. "$BST_LIB" require bison bison/present # Sized to the runner's 60s per-suite bound (BRINTOS-172). Every bison run that # emits a parser costs ~2s on this target -- it forks m4 and expands a 75 KB # skeleton -- so cases share a generated file wherever the assertion allows it, # and options whose coverage another case already implies are not run twice. tmp=${TMPDIR:-/tmp}/bst-bison.$$ mkdir -p "$tmp" || { bst_notok bison/tmpdir "cannot create $tmp"; return 2>/dev/null || exit 0; } cd "$tmp" || { bst_notok bison/chdir "cannot cd $tmp"; return 2>/dev/null || exit 0; } # ---------------------------------------------------------------- fixtures # clean.y is conflict-free: %left gives PLUS an associativity, so the # `expr PLUS expr` rule is unambiguous and bison emits no diagnostic at all. cat > clean.y <<'YACC' %{ #include #define BISON_SELFTEST_MARKER 1 int yylex(void); void yyerror(const char *s); %} %token NUM %token PLUS %left PLUS %% input : /* empty */ | input line ; line : expr { printf("%d\n", $1); } ; expr : NUM { $$ = $1; } | expr PLUS expr { $$ = $1 + $3; } ; %% void yyerror(const char *s) { fprintf(stderr, "%s\n", s); } YACC # Same grammar with the %left line dropped: exactly one shift/reduce conflict. sed '/^%left PLUS$/d' clean.y > conflict.y cat > bad.y <<'YACC' %% input : @@@ ; %% YACC # ------------------------------------------------- 1. shipped payload check_out bison/version "bison (GNU Bison) 3.8.2" bison --version check_out bison/help "Usage: bison" bison --help check bison/yacc-present test -x /usr/bin/yacc check bison/m4-present test -x /usr/bin/m4 # bison is not self-contained at run time: the parser it emits is an m4 # expansion of skeleton files that must be on the image beside the binary. check bison/skel-dir test -d /usr/share/bison/skeletons check bison/skel-yacc-c test -s /usr/share/bison/skeletons/yacc.c check bison/skel-bison-m4 test -s /usr/share/bison/skeletons/bison.m4 check bison/skel-glr-c test -s /usr/share/bison/skeletons/glr.c check bison/m4sugar test -s /usr/share/bison/m4sugar/m4sugar.m4 # ------------------------------------------------- 2. parser generation rm -f clean.tab.c check bison/gen-default bison clean.y check bison/gen-default-file test -s clean.tab.c # One run, three assertions: the exit status, the file, and that a conflict-free # grammar draws no diagnostic whatsoever. rm -f out.c out=$(bison -o out.c clean.y 2>&1); rc=$? check_eq bison/gen-outfile 0 "$rc" check bison/gen-outfile-file test -s out.c if [ -z "$out" ]; then bst_ok bison/gen-clean-silent else bst_notok bison/gen-clean-silent "$(_bst_1line "$out")"; fi # ------------------------------------------------- 3. generated content # the parser really encodes THIS grammar, not merely some parser check_out bison/gen-yyparse "yyparse" cat out.c check_out bison/gen-yytable "yytable" cat out.c check_out bison/gen-cblock "BISON_SELFTEST_MARKER" cat out.c check_out bison/gen-action "printf" cat out.c check_out bison/gen-token "NUM" cat out.c # ------------------------------------------- 4. m4 expansion completeness # The skeleton is written in m4 and bison's own macros are b4_-prefixed; # any residue means the forked m4 did not expand the skeleton to the end. n=$(grep -c 'b4_' out.c 2>/dev/null || true) check_eq bison/gen-no-b4-residue 0 "$n" n=$(grep -c 'm4_' out.c 2>/dev/null || true) check_eq bison/gen-no-m4-residue 0 "$n" # ------------------------------------------- 5. the m4 subprocess (fork/exec) # bison cannot emit a parser without starting m4 and reading it back, so a # bogus $M4 must break the run. This is what proves the subprocess is real # rather than short-circuited into the binary -- and it is a live regression # guard, not a formality: bison reaches m4 through gnulib's spawn-pipe, and # the libc posix_spawn on this target cannot start a process at all (it wants # clone(CLONE_VM|CLONE_VFORK)), so the port turns on gnulib's fork()-based # replacement instead. If that ever regresses, every case below goes red. rm -f m4bad.c out=$(M4=/nonexistent-m4-xyz bison -o m4bad.c clean.y 2>&1); rc=$? if [ "$rc" -ne 0 ] || [ ! -s m4bad.c ]; then bst_ok bison/m4-required else bst_notok bison/m4-required "rc=$rc with M4=/nonexistent-m4-xyz, parser still produced"; fi case "$out" in *"m4 subprocess failed"*|*"No such file"*) bst_ok bison/m4-required-msg ;; *) bst_notok bison/m4-required-msg "$(_bst_1line "$out")" ;; esac rm -f m4ok.c check bison/m4-env-honored sh -c 'M4=/usr/bin/m4 bison -o m4ok.c clean.y' check bison/m4-env-output test -s m4ok.c # the skeleton data dir is read at run time too -- point it somewhere empty # and the run must fail rather than silently emit a parser rm -f skelbad.c out=$(BISON_PKGDATADIR=/nonexistent-skel-xyz bison -o skelbad.c clean.y 2>&1); rc=$? if [ "$rc" -ne 0 ] || [ ! -s skelbad.c ]; then bst_ok bison/skeldata-required else bst_notok bison/skeldata-required "rc=$rc with a bogus BISON_PKGDATADIR, parser still produced"; fi # ------------------------------------------------- 6. determinism # Two runs writing the SAME output name must be byte-identical. Comparing two # runs writing DIFFERENT names would be a false red: the #line directives # embed the output filename, so those lines legitimately differ. bison -o same.c clean.y 2>/dev/null; cp same.c d1.c bison -o same.c clean.y 2>/dev/null; cp same.c d2.c check bison/gen-deterministic cmp -s d1.c d2.c # The directives naming the INPUT grammar are m4's, passed through untouched; # those naming the OUTPUT are rewritten by bison. Only their presence and # count are pinned, never their numeric value -- flex's port (BRINTOS-180) # showed the self-referencing counter can sit one behind on this target. n=$(grep -c '^#line [0-9][0-9]* "clean.y"$' same.c 2>/dev/null || true) check_eq bison/line-names-input 5 "$n" n=$(grep -c '^#line [0-9][0-9]* "same.c"$' same.c 2>/dev/null || true) check_eq bison/line-names-output 5 "$n" # -l suppresses them entirely bison -l -o nl.c clean.y 2>/dev/null n=$(grep -c '^#line' nl.c 2>/dev/null || true) check_eq bison/opt-no-line 0 "$n" # ------------------------------------------------- 7. option surface rm -f hh.c hh.h check bison/opt-defines bison -d -o hh.c clean.y check bison/opt-defines-file test -s hh.h check_out bison/opt-defines-enum "YYTOKENTYPE" cat hh.h check_out bison/opt-defines-token "NUM" cat hh.h check_out bison/opt-defines-decl "yyparse" cat hh.h rm -f v.c v.output check bison/opt-report bison -v -o v.c clean.y check bison/opt-report-file test -s v.output check_out bison/opt-report-states "State 0" cat v.output check_out bison/opt-debug "YYDEBUG" sh -c 'bison -t -o td.c clean.y && cat td.c' check_out bison/opt-prefix "zzparse" sh -c 'bison -p zz -o zz.c clean.y && cat zz.c' rm -f xx.xml xx.c check bison/opt-xml sh -c 'bison --xml=xx.xml -o xx.c clean.y' check_out bison/opt-xml-content "bison-xml-report" cat xx.xml # The shipped /usr/bin/yacc wrapper execs `bison -y`, so this covers the POSIX # yacc mode and the wrapper script in one run: fixed y.tab.c / y.tab.h names. rm -f y.tab.c y.tab.h check bison/yacc-wrapper sh -c 'yacc -d clean.y' check bison/yacc-wrapper-c test -s y.tab.c check bison/yacc-wrapper-h test -s y.tab.h # alternate skeletons drive different back ends through the same m4 pipeline check_out bison/skel-glr "GLRStack" sh -c 'bison -S glr.c -o gl.c clean.y && cat gl.c' check_out bison/skel-cxx "parser::parser" sh -c 'bison -S lalr1.cc -o cc.cc clean.y && cat cc.cc' # ---------------------------------------- 8. the conflict contract # bison reports a shift/reduce conflict as a WARNING, exits 0, and still # writes the parser. (Contrast lemon, whose exit status is nonzero for # conflicts as well as errors.) A caller must not read nonzero as # "nothing was generated" -- nor zero as "no conflict". rm -f cf.c out=$(bison -o cf.c conflict.y 2>&1); rc=$? check_eq bison/conflict-rc 0 "$rc" case "$out" in *"shift/reduce conflict"*) bst_ok bison/conflict-warned ;; *) bst_notok bison/conflict-warned "$(_bst_1line "$out")" ;; esac check bison/conflict-still-generated test -s cf.c # ...and -Werror promotes that warning to a failure rm -f cfe.c out=$(bison -Werror=conflicts-sr -o cfe.c conflict.y 2>&1); rc=$? if [ "$rc" -ne 0 ]; then bst_ok bison/conflict-werror else bst_notok bison/conflict-werror "-Werror=conflicts-sr exited 0 on a conflicting grammar"; fi # ------------------------------------------------- 9. exit-status contracts out=$(bison -o miss.c nosuchfile.y 2>&1); rc=$? check_eq bison/rc-missing-input 1 "$rc" case "$out" in *"cannot open"*|*"No such file"*) bst_ok bison/msg-missing-input ;; *) bst_notok bison/msg-missing-input "$(_bst_1line "$out")" ;; esac out=$(bison -Z clean.y 2>&1); rc=$? check_eq bison/rc-bad-option 1 "$rc" case "$out" in *"invalid option"*|*nrecognized*|*nknown*) bst_ok bison/msg-bad-option ;; *) bst_notok bison/msg-bad-option "$(_bst_1line "$out")" ;; esac out=$(bison -o badout.c bad.y 2>&1); rc=$? check_eq bison/rc-syntax-error 1 "$rc" case "$out" in *error*) bst_ok bison/msg-syntax-error ;; *) bst_notok bison/msg-syntax-error "$(_bst_1line "$out")" ;; esac cd / rm -rf "$tmp"