aboutsummaryrefslogtreecommitdiff
path: root/tinyvm.sh
blob: 488b1ec5723131bd0b37c6e2d95507bd96a40646 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#!/usr/bin/env bash
# interactive build and run menu, with the same commands as tinyvm.bat.
set -u

tinyvm_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) || exit 1
tinyvm_caller=$PWD
cd -- "$tinyvm_root" || exit 1

usage() {
    cat <<'HELP'
usage: tinyvm.sh [command]
  build [all|core|micro|tools|lsp]  build executables
  run FILE.zc                     run on the native VM
  run-micro FILE.zc               run on the portable microcore
  js FILE.zc                      run on the JavaScript VM
  lsp FILE.zc                     check a source file
  test [test options]             run the full test suite
  check                          run the minimal test suite
  genome                         rebuild the compiler and verify the fixpoint
  repl                           start the resident compiler REPL
  clean                          remove generated build output
  help                           show this help
with no command, open the interactive menu.
HELP
}

build() {
    case "${1:-all}" in
        all) make ;;
        core) make build/bin/zy ;;
        micro) make build/bin/zym ;;
        tools) make build/bin/zc0 build/bin/mkgenome build/bin/genmap ;;
        lsp) make lsp ;;
        *) printf 'Unknown build target: %s\n' "$1" >&2; return 2 ;;
    esac
}

source_path() {
    local input=$1
    if [[ $input != /* ]]; then input="$tinyvm_caller/$input"; fi
    [[ -f $input ]] || { printf 'Source file not found: %s\n' "$input" >&2; return 2; }
    printf '%s\n' "$input"
}

run_file() {
    local mode=$1 input exe
    [[ $# == 2 ]] || { printf 'Usage: tinyvm.sh %s FILE.zc\n' "$mode" >&2; return 2; }
    input=$(source_path "$2") || return
    case $mode in
        run) exe=build/bin/zy ;;
        run-micro) exe=build/bin/zym ;;
        lsp) exe=build/bin/zclsp ;;
        js) command -v node >/dev/null || { echo 'node.js is required.' >&2; return 1; }; make js || return; node build/js/tinyvm.js "$input"; return ;;
    esac
    make "$exe" || return
    "$exe" < "$input"
}

repl() {
    make build/bin/zy || return
    echo 'Enter statements or fn definitions, ending each chunk with a backtick. Ctrl-D exits.'
    { cat examples/repl.zc; printf '`'; cat; } | build/bin/zy
}

dispatch() {
    local action=${1:-help}
    [[ $# == 0 ]] || shift
    case $action in
        build) build "$@" ;;
        run|run-micro|js|lsp) run_file "$action" "$@" ;;
        test) bash tests/test.sh "$@" ;;
        check) make check ;;
        genome) make genome ;;
        repl) repl ;;
        clean) make clean ;;
        help|-h|--help) usage ;;
        *) printf 'Unknown command: %s\n' "$action" >&2; usage >&2; return 2 ;;
    esac
}

menu() {
    local choice input status
    while true; do
        printf '\nTinyVM — build and run\n'
        printf '  1 build everything\n  2 run a .zc file\n  3 run on the microcore\n  4 run in JavaScript\n  5 check with the language server\n  6 run tests\n  7 rebuild genome\n  R REPL\n  C clean build output\n  Q quit\n'
        read -r -p 'select an option: ' choice || return 0
        status=0
        case $choice in
            1) build || status=$? ;;
            2|3|4|5)
                read -r -p 'source file: ' input || return 0
                case $choice in 2) choice=run ;; 3) choice=run-micro ;; 4) choice=js ;; 5) choice=lsp ;; esac
                run_file "$choice" "$input" || status=$?
                ;;
            6) dispatch test || status=$? ;;
            7) dispatch genome || status=$? ;;
            r|R) repl || status=$? ;;
            c|C) dispatch clean || status=$? ;;
            q|Q) return 0 ;;
            *) echo 'Invalid option.'; continue ;;
        esac
        [[ $status == 0 ]] || printf 'Command failed with status %d.\n' "$status" >&2
    done
}

if [[ $# == 0 ]]; then menu; else dispatch "$@"; fi