Shell 150 lines
# Flash shell helpers. Source from ~/.zshrc:
# [[ -f /Users/antonhahn/Flash/flash.env.zsh ]] && source /Users/antonhahn/Flash/flash.env.zsh
#
# Public interface — one verb dispatcher:
# flash build|test|gates|run|emit|tree|fmt|tokens|lsp|version|clean|help
#
# flash build build flashc (stage0 seed) into zig-out/bin
# flash test [suite] run the host suite, or one of:
# std | selfhost | flash | lsp | driver
# flash gates the full pre-commit gate: test + fixpoint + diff-corpus
# flash run <file> transpile a .flash file to stdout
# flash emit <file> <out> transpile a .flash file into <out>.zig
# flash tree <src> <out> transpile a whole tree, mirroring relative paths
# flash fmt [--check] <file> reformat a .flash file in place
# flash tokens <file> dump the token stream for a .flash file
# flash lsp build flashd, the language server
# flash version print the flashc version
# flash clean remove .zig-cache and zig-out
# flash help this list
#
# run/emit/tree/fmt/tokens go through flashc-stage1 — the live, self-hosted
# compiler. The stage0 seed in zig-out/bin/flashc is frozen at the v0.5
# bootstrap surface and rejects newer grammar, so it is the wrong binary for
# day-to-day source work.
# Directory of this file, captured at source time (%x still points at the
# file being sourced here; inside a function it would name the function).
typeset -g _FLASH_DIR="${${(%):-%x}:A:h}"
typeset -g _FLASH_RED=$'\033[0;31m'
typeset -g _FLASH_GREEN=$'\033[0;32m'
typeset -g _FLASH_NC=$'\033[0m'
_flash_err() { print -u2 -- "${_FLASH_RED}$*${_FLASH_NC}"; }
_flash_ok() { print -- "${_FLASH_GREEN}$*${_FLASH_NC}"; }
# Run argv from the project root in a subshell, leaving the caller's cwd alone.
_flash_root() { ( cd "$_FLASH_DIR" && "$@" ); }
# Build stage1 (quiet on success, build errors still reach stderr) and exec
# flashc-stage1 with the given arguments.
_flash_stage1() {
_flash_root sh -c 'zig build stage1 >/dev/null && exec ./zig-out/bin/flashc-stage1 "$@"' _ "$@"
}
# Absolutize every non-flag argument, so file paths survive the cd to the
# project root. Result in $reply.
_flash_abs() {
reply=()
local a
for a in "$@"; do
if [[ "$a" == -* ]]; then reply+=("$a"); else reply+=("${a:A}"); fi
done
}
_flash_help() {
print -- "flash — dev helpers for the Flash compiler"
print -- ""
print -- " flash build build flashc (stage0 seed) into zig-out/bin"
print -- " flash test [suite] host suite, or: std|selfhost|flash|lsp|driver"
print -- " flash gates test + fixpoint + diff-corpus"
print -- " flash run <file> transpile a .flash file to stdout"
print -- " flash emit <file> <out> transpile a .flash file into <out>.zig"
print -- " flash tree <src> <out> transpile a whole tree (mirrors paths)"
print -- " flash fmt [--check] <file> reformat a .flash file in place"
print -- " flash tokens <file> dump the token stream"
print -- " flash lsp build flashd, the language server"
print -- " flash version print the flashc version"
print -- " flash clean remove .zig-cache and zig-out"
print -- " flash help this list"
print -- ""
print -- " run/emit/tree/fmt/tokens use flashc-stage1 (the live compiler);"
print -- " --anchors / --plain-diagnostics pass through to it."
}
flash() {
emulate -L zsh
local verb="${1:-help}"
(( $# )) && shift
case "$verb" in
build) _flash_root zig build "$@" ;;
test)
case "${1:-}" in
std|selfhost|flash|lsp|driver)
local suite="$1"; shift
_flash_root zig build "test-$suite" "$@"
;;
*) _flash_root zig build test "$@" ;;
esac
;;
gates)
_flash_root zig build test &&
_flash_root zig build fixpoint &&
_flash_root zig build diff-corpus &&
_flash_ok "gates green: test + fixpoint + diff-corpus"
;;
run)
[[ -z "$1" ]] && { _flash_err "usage: flash run <file.flash> [flags]"; return 1; }
_flash_abs "$@"; _flash_stage1 "${reply[@]}"
;;
emit)
(( $# < 2 )) && { _flash_err "usage: flash emit <file.flash> <out.zig> [flags]"; return 1; }
_flash_abs "$@"; _flash_stage1 "${reply[1]}" -o "${reply[2]}" "${reply[@]:2}"
;;
tree)
(( $# < 2 )) && { _flash_err "usage: flash tree <srcdir> <outdir> [flags]"; return 1; }
_flash_abs "$@"; _flash_stage1 build "${reply[@]}"
;;
fmt)
[[ -z "$1" ]] && { _flash_err "usage: flash fmt [--check] <file.flash>"; return 1; }
_flash_abs "$@"; _flash_stage1 fmt "${reply[@]}"
;;
tokens)
[[ -z "$1" ]] && { _flash_err "usage: flash tokens <file.flash>"; return 1; }
_flash_abs "$@"; _flash_stage1 --dump-tokens "${reply[@]}"
;;
lsp) _flash_root zig build lsp && _flash_ok "flashd built into zig-out/bin" ;;
version)
_flash_root sh -c 'zig build >/dev/null && exec ./zig-out/bin/flashc --version'
;;
clean) _flash_root rm -rf .zig-cache zig-out && _flash_ok "cleaned .zig-cache + zig-out" ;;
help|-h|--help) _flash_help ;;
*) _flash_err "unknown verb: $verb"; _flash_help; return 1 ;;
esac
}
# Warn once at source time when the installed Zig disagrees with the pin —
# build.zig would reject it anyway, this just says so up front.
() {
local want have
[[ -r "$_FLASH_DIR/.zigversion" ]] || return 0
want="$(<"$_FLASH_DIR/.zigversion")"
have="$(command zig version 2>/dev/null)" || return 0
[[ "$have" == "$want" ]] ||
print -u2 -- "${_FLASH_RED}flash: zig $have installed, but the project pins $want (.zigversion)${_FLASH_NC}"
}
# Verb completion (only when the completion system is initialized).
if (( $+functions[compdef] )); then
_flash_verbs() {
if (( CURRENT == 2 )); then
compadd -- build test gates run emit tree fmt tokens lsp version clean help
elif [[ "${words[2]}" == "test" ]] && (( CURRENT == 3 )); then
compadd -- std selfhost flash lsp driver
else
_files
fi
}
compdef _flash_verbs flash
fi