kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

run.sh (10693B)


      1 #!/bin/sh
      2 # test/gram/run.sh — gram subsystem functional + diagnostic tests.
      3 #
      4 # Drives `kit gram` to generate parsers/lexers/machines, compiles the generated
      5 # C plus the test drivers against libkit.a, runs them, and checks the error
      6 # fixtures (a grammar must be rejected with the expected diagnostic substring).
      7 # Run from the repo root (some drivers fopen() their grammar by relative path).
      8 #
      9 # Env (set by the Makefile target; sensible defaults for ad-hoc runs):
     10 #   KIT              path to the kit binary                (required)
     11 #   KIT_GRAM_CC      host C compiler                       (default: cc)
     12 #   KIT_GRAM_CFLAGS  compile flags (sysroot, sanitizers, -Iinclude, ...)
     13 #   KIT_GRAM_LIB     libkit.a                              (default: build/libkit.a)
     14 #   KIT_GRAM_BUILD   scratch dir for generated C + binaries
     15 set -u
     16 
     17 KIT=${KIT:?set KIT to the kit binary}
     18 CC=${KIT_GRAM_CC:-cc}
     19 CFLAGS=${KIT_GRAM_CFLAGS:--std=c11 -Wall -Wextra -Werror -Iinclude}
     20 LIB=${KIT_GRAM_LIB:-build/libkit.a}
     21 DIR=${KIT_GRAM_BUILD:-build/test/gram}
     22 SRC=test/gram
     23 
     24 mkdir -p "$DIR"
     25 pass=0
     26 fail=0
     27 fails=""
     28 
     29 # Table-free link checks compile sanitizer-free with section GC + dead-strip so
     30 # the unused table-parser glue (which references the parser runtime) is dropped,
     31 # proving the standalone scanner alone needs nothing from libkit.
     32 case "$(uname)" in
     33 Darwin) DEADSTRIP="-Wl,-dead_strip" ;;
     34 *) DEADSTRIP="-Wl,--gc-sections" ;;
     35 esac
     36 SA_CFLAGS="-std=c11 -O2 -ffunction-sections -fdata-sections -Iinclude ${KIT_GRAM_SYSROOT:-}"
     37 
     38 note_fail() {
     39   fail=$((fail + 1))
     40   fails="$fails $1"
     41 }
     42 
     43 # gen <prefix> <out-base> <grammar> [extra `kit gram` flags...]
     44 # Always passes --prefix (even when empty, for %machine codegen-only output).
     45 gen() {
     46   _pfx=$1
     47   _base=$2
     48   _gram=$3
     49   shift 3
     50   if "$KIT" gram --prefix "$_pfx" "$@" -o "$DIR/$_base.c" \
     51       --header "$DIR/$_base.h" "$_gram" >"$DIR/$_base.gen.out" \
     52       2>"$DIR/$_base.gen.err"; then
     53     return 0
     54   fi
     55   printf '  FAIL gen %s\n' "$_base"
     56   sed 's/^/      /' "$DIR/$_base.gen.err"
     57   note_fail "gen:$_base"
     58   return 1
     59 }
     60 
     61 # build <name> <yes|no link libkit> <cc args: flags + sources...>
     62 build() {
     63   _name=$1
     64   _link=$2
     65   shift 2
     66   set -- $CFLAGS -I"$DIR" -I"$SRC" "$@"
     67   if [ "$_link" = yes ]; then set -- "$@" "$LIB"; fi
     68   if "$CC" "$@" -o "$DIR/$_name" 2>"$DIR/$_name.cc.err"; then
     69     return 0
     70   fi
     71   printf '  FAIL compile %s\n' "$_name"
     72   sed 's/^/      /' "$DIR/$_name.cc.err" | tail -15
     73   note_fail "cc:$_name"
     74   return 1
     75 }
     76 
     77 # build_linkcheck <name> <sources...> : sanitizer-free, dead-stripped, NO libkit.
     78 build_linkcheck() {
     79   _name=$1
     80   shift
     81   if "$CC" $SA_CFLAGS -I"$DIR" -I"$SRC" $DEADSTRIP -o "$DIR/$_name" "$@" \
     82       2>"$DIR/$_name.cc.err"; then
     83     return 0
     84   fi
     85   printf '  FAIL link-check %s\n' "$_name"
     86   sed 's/^/      /' "$DIR/$_name.cc.err" | tail -15
     87   note_fail "cc:$_name"
     88   return 1
     89 }
     90 
     91 # run <name> <binary>
     92 run() {
     93   if "$2" >"$DIR/$1.out" 2>"$DIR/$1.err"; then
     94     pass=$((pass + 1))
     95     printf '  ok   %s\n' "$1"
     96   else
     97     _rc=$?
     98     printf '  FAIL %s (exit %d)\n' "$1" "$_rc"
     99     sed 's/^/      /' "$DIR/$1.err" | tail -10
    100     note_fail "run:$1"
    101   fi
    102 }
    103 
    104 echo "== gram functional tests =="
    105 
    106 # calc: hand-written tables, then generated tables (same driver, -D switch).
    107 build calc_hand yes "$SRC/calc.c" "$SRC/calc_test.c" && run calc_hand "$DIR/calc_hand"
    108 gen calc_ generated_calc "$SRC/calc.ebnf" &&
    109   build calc_gen yes -DKIT_GRAM_GENERATED "$DIR/generated_calc.c" "$SRC/calc_test.c" &&
    110   run calc_gen "$DIR/calc_gen"
    111 
    112 # features grammar: parser actions + the table lexer over the same tables.
    113 if gen features_ generated_features "$SRC/features.ebnf"; then
    114   build features yes "$DIR/generated_features.c" "$SRC/features_test.c" && run features "$DIR/features"
    115   build lexer yes "$DIR/generated_features.c" "$SRC/lexer_test.c" && run lexer "$DIR/lexer"
    116 fi
    117 
    118 gen desugar_ generated_desugar "$SRC/desugar.ebnf" &&
    119   build desugar yes "$DIR/generated_desugar.c" "$SRC/desugar_test.c" && run desugar "$DIR/desugar"
    120 gen pratt_calc_ generated_pratt_calc "$SRC/pratt_calc.ebnf" &&
    121   build pratt_calc yes "$DIR/generated_pratt_calc.c" "$SRC/pratt_calc_test.c" && run pratt_calc "$DIR/pratt_calc"
    122 gen pratt_mixfix_ generated_pratt_mixfix "$SRC/pratt_mixfix.ebnf" &&
    123   build pratt_mixfix yes "$DIR/generated_pratt_mixfix.c" "$SRC/pratt_mixfix_test.c" && run pratt_mixfix "$DIR/pratt_mixfix"
    124 gen keywords_ generated_keywords "$SRC/keywords.ebnf" &&
    125   build keywords yes "$DIR/generated_keywords.c" "$SRC/keywords_test.c" && run keywords "$DIR/keywords"
    126 gen unicode_ generated_unicode "$SRC/unicode.ebnf" &&
    127   build unicode_lexer yes "$DIR/generated_unicode.c" "$SRC/unicode_lexer_test.c" && run unicode_lexer "$DIR/unicode_lexer"
    128 gen utf8_runtime_ generated_utf8_runtime "$SRC/utf8_runtime.ebnf" &&
    129   build utf8_runtime yes "$DIR/generated_utf8_runtime.c" "$SRC/utf8_runtime_test.c" && run utf8_runtime "$DIR/utf8_runtime"
    130 
    131 # Realistic JSON / C-like, generated with --parser-codegen (one file carries the
    132 # table parser + table lexer + the generated pull parser).
    133 gen json_ generated_rjson "$SRC/realistic/json.ebnf" --parser-codegen &&
    134   build rjson yes "$DIR/generated_rjson.c" "$SRC/realistic/test_json.c" && run rjson "$DIR/rjson"
    135 gen clike_ generated_clike "$SRC/realistic/clike.ebnf" --parser-codegen &&
    136   build rclike yes "$DIR/generated_clike.c" "$SRC/realistic/test_clike.c" && run rclike "$DIR/rclike"
    137 
    138 # Pure in-process gramgen API / runtime tests (no codegen step).
    139 build gramgen_api yes "$SRC/gramgen_api_test.c" && run gramgen_api "$DIR/gramgen_api"
    140 build match yes "$SRC/match_test.c" && run match "$DIR/match"
    141 build sublexer_table yes "$SRC/sublexer_table_test.c" && run sublexer_table "$DIR/sublexer_table"
    142 build json_frontend yes "$SRC/realistic/test_json_frontend.c" && run json_frontend "$DIR/json_frontend"
    143 # unicode_support reaches the private generated property header.
    144 build unicode_support yes -Isrc/gram "$SRC/unicode_support_test.c" && run unicode_support "$DIR/unicode_support"
    145 
    146 # Token machines (%machine): codegen-only, linked with NO libkit (table-free).
    147 gen "" generated_machine "$SRC/machine.ebnf" &&
    148   build machine no "$DIR/generated_machine.c" "$SRC/machine_test.c" && run machine "$DIR/machine"
    149 
    150 # Pull recursive-descent parser codegen (RD + error-recovery variants).
    151 if gen jsonrd_ jsonrd "$SRC/realistic/json.ebnf" --parser-codegen &&
    152   gen clikerd_ clikerd "$SRC/realistic/clike.ebnf" --parser-codegen &&
    153   gen mixfixrd_ mixfixrd "$SRC/mixfix.ebnf" --parser-codegen &&
    154   gen clikerec_ clikerec "$SRC/realistic/clike.ebnf" --parser-codegen --parser-recover &&
    155   gen mixfixrec_ mixfixrec "$SRC/mixfix.ebnf" --parser-codegen --parser-recover; then
    156   build parser_codegen yes "$DIR/jsonrd.c" "$DIR/clikerd.c" "$DIR/mixfixrd.c" \
    157     "$DIR/clikerec.c" "$DIR/mixfixrec.c" "$SRC/parser_codegen_test.c" &&
    158     run parser_codegen "$DIR/parser_codegen"
    159 fi
    160 
    161 # Standalone table-free scanners (--lexer-standalone), incl. fold-keywords,
    162 # utf8, anchors, match, and the fused parser+standalone-scanner variants.
    163 if gen jsonsa_ jsonsa "$SRC/realistic/json.ebnf" --lexer-standalone &&
    164   gen clikesa_ clikesa "$SRC/realistic/clike.ebnf" --lexer-standalone &&
    165   gen clikesaf_ clikesaf "$SRC/realistic/clike.ebnf" --lexer-standalone --fold-keywords &&
    166   gen kwutf8sa_ kwutf8sa "$SRC/keywords_utf8.ebnf" --lexer-standalone &&
    167   gen anchstartsa_ anchstartsa "$SRC/anchors_start.ebnf" --lexer-standalone --multiline &&
    168   gen anchsa_ anchsa "$SRC/anchors.ebnf" --lexer-standalone --multiline &&
    169   gen possa_ possa "$SRC/standalone/pos.ebnf" --lexer-standalone --multiline &&
    170   gen matchsa_ matchsa "$SRC/standalone/match.ebnf" --lexer-standalone --multiline &&
    171   gen jsonsr_ jsonsr "$SRC/realistic/json.ebnf" --lexer-standalone --parser-codegen &&
    172   gen clikesr_ clikesr "$SRC/realistic/clike.ebnf" --lexer-standalone --parser-codegen; then
    173   # The driver deliberately type-puns the generated per-grammar scanner entry
    174   # points to one local signature, so drop UBSan's -fsanitize=function check
    175   # (fatal under -fno-sanitize-recover) for this one binary.
    176   build lexer_standalone yes -fno-sanitize=function "$DIR/jsonsa.c" "$DIR/clikesa.c" \
    177     "$DIR/clikesaf.c" "$DIR/kwutf8sa.c" "$DIR/anchstartsa.c" "$DIR/anchsa.c" \
    178     "$DIR/possa.c" "$DIR/matchsa.c" "$DIR/jsonsr.c" "$DIR/clikesr.c" \
    179     "$SRC/lexer_standalone_test.c" &&
    180     run lexer_standalone "$DIR/lexer_standalone"
    181 fi
    182 
    183 # Table-free link check: a standalone scanner + a tiny driver, NO libkit
    184 # (dead-strip drops the unused table-parser glue).
    185 build_linkcheck sa_link "$DIR/jsonsa.c" "$SRC/standalone/link.c" && run sa_link "$DIR/sa_link"
    186 
    187 # Sub-lexers from one standalone grammar, NO libkit.
    188 gen sublex_ sublexersa "$SRC/standalone/sublexer.ebnf" --lexer-standalone &&
    189   build_linkcheck sublexer "$DIR/sublexersa.c" "$SRC/standalone/test_sublexer.c" && run sublexer "$DIR/sublexer"
    190 
    191 # Lazy line/col positions (grampos.h) over normal + --position-lazy scanners.
    192 if gen posb_ posb "$SRC/pos_byte.ebnf" --lexer-standalone &&
    193   gen posbl_ posbl "$SRC/pos_byte.ebnf" --lexer-standalone --position-lazy &&
    194   gen posu_ posu "$SRC/pos_utf8.ebnf" --lexer-standalone &&
    195   gen posul_ posul "$SRC/pos_utf8.ebnf" --lexer-standalone --position-lazy; then
    196   build position yes "$DIR/posb.c" "$DIR/posbl.c" "$DIR/posu.c" "$DIR/posul.c" \
    197     "$SRC/position_test.c" && run position "$DIR/position"
    198 fi
    199 
    200 # --- diagnostic fixtures: each grammar must be rejected with the expected msg ---
    201 check_errors() {
    202   _label=$1
    203   _glob=$2
    204   for ebnf in $_glob; do
    205     [ -e "$ebnf" ] || continue
    206     _b=$(basename "$ebnf" .ebnf)
    207     # Prefer the precise meta-parser message (.meta_expect) when present; the
    208     # meta grammar is the only parse path now, so it is the message produced.
    209     _exp="${ebnf%.ebnf}.meta_expect"
    210     [ -f "$_exp" ] || _exp="${ebnf%.ebnf}.expect"
    211     [ -f "$_exp" ] || continue
    212     if "$KIT" gram -o "$DIR/$_b.bad.c" --header "$DIR/$_b.bad.h" "$ebnf" \
    213         >"$DIR/$_b.err.out" 2>"$DIR/$_b.err.err"; then
    214       printf '  FAIL %s/%s: accepted invalid grammar\n' "$_label" "$_b"
    215       note_fail "$_label:$_b"
    216     elif grep -F -f "$_exp" "$DIR/$_b.err.err" >/dev/null 2>&1; then
    217       pass=$((pass + 1))
    218       printf '  ok   %s/%s\n' "$_label" "$_b"
    219     else
    220       printf '  FAIL %s/%s: diagnostic mismatch\n' "$_label" "$_b"
    221       printf '    expected substring: '; cat "$_exp"; echo
    222       printf '    actual:             '; cat "$DIR/$_b.err.err"
    223       note_fail "$_label:$_b"
    224     fi
    225   done
    226 }
    227 
    228 echo "== gram diagnostic fixtures =="
    229 check_errors errors "$SRC/errors/*.ebnf"
    230 check_errors machine_errors "$SRC/machine_errors/*.ebnf"
    231 
    232 echo
    233 echo "== gram: $pass passed, $fail failed =="
    234 if [ "$fail" -ne 0 ]; then
    235   echo "FAILURES:$fails"
    236   exit 1
    237 fi
    238 exit 0