From fb651529011dc5e46a4ae49f0fad96ca679551fc Mon Sep 17 00:00:00 2001 From: Sebastian Jeong Date: Mon, 17 Aug 2026 03:33:23 +0900 Subject: [PATCH] refactor: keep the front end, drop everything downstream of it The milestone structure had stopped describing the compiler and started shaping it: m7.c, check_m7.c, tests/m2..m9, and a checker and emitter that had each grown past 2,500 lines because there was nowhere else to put anything. Restart from the pipeline instead. What is left is the front end -- lexer, parser, types, ownership, semantic analysis -- and the fixtures that describe it. The C backend, the DOSBox-X runner, the milestone registry and the batch build are removed. The driver now stops after semantic analysis; a code generator attaches where emit_c did. Fixtures move from milestone directories to what they check: parse/ grammar own/ ownership and borrowing types/ type rules optional/ optionals and error unions format/ formatting, try units/ units and visibility generic/ generics pending-backend/ pending-backend/ holds the three fixtures that can only be checked by running a program -- that the bounds check traps, that --no-checks removes it, and that drops and defers actually fire, verified through a fake allocator. Those are not front-end tests and are not pretending to be; they come back first when there is a code generator. tests/run.py replaces the DOSBox-X harness. It builds the front end with the pinned Watcom's Windows-hosted driver and runs every fixture in about two seconds, and it does something the old runner structurally could not: it reads the `// ERROR:line:text` marker each fixture carries and checks the diagnostic against it. Those markers have been in the tree all along, unverified, because DOS could not redirect the compiler's stderr and only the exit code was ever compared. 133/188 pass. The 55 failures are not regressions -- they are what was already true and invisible: - units (27) and generic (23): `import`, `comptime` and generic declarations parse and are then dropped on the floor. No pass looks at them. The old registry did not list these fixtures at all, so nothing said so. - five in own/, optional/ and generic/: a marker disagrees with the diagnostic about the line or the wording. Each is either a wrong marker or a wrong diagnostic and has to be read individually. Everything removed is in git history. --- .gitignore | 3 + fec/build-dos.bat | 49 - fec/src/driver.c | 25 +- fec/src/emit_c.c | 2428 ----------------- fec/src/emit_c.h | 28 - fec/src/lower.c | 227 -- fec/src/lower.h | 61 - fec/tests/{m4 => format}/bad-ari.fe | 0 fec/tests/{m4 => format}/bad-bufw.fe | 0 fec/tests/{m4 => format}/bad-cls.fe | 0 fec/tests/{m4 => format}/bad-many.fe | 0 fec/tests/{m4 => format}/bad-open.fe | 0 fec/tests/{m4 => format}/bad-run.fe | 0 fec/tests/{m4 => format}/bad-try.fe | 0 fec/tests/{m4 => format}/bad-type.fe | 0 fec/tests/{m4 => format}/bad-verb.fe | 0 fec/tests/{m4 => format}/bad-writ.fe | 0 .../{m4/format.fe => format/ok-format.fe} | 0 fec/tests/{m4/prop.fe => format/ok-prop.fe} | 0 .../{m4/try-fpr.fe => format/ok-try-fpr.fe} | 0 fec/tests/{m9 => generic}/README.md | 0 fec/tests/{m9 => generic}/badarg.fe | 0 fec/tests/{m9 => generic}/badarity.fe | 0 fec/tests/{m9 => generic}/badbody.fe | 0 fec/tests/{m9 => generic}/baddepth.fe | 0 fec/tests/{m9 => generic}/baddist.fe | 0 fec/tests/{m9 => generic}/badfew.fe | 0 fec/tests/{m9 => generic}/badinfer.fe | 0 fec/tests/{m9 => generic}/badop.fe | 0 fec/tests/{m9 => generic}/badtype.fe | 0 fec/tests/{m9 => generic}/badvalue.fe | 0 fec/tests/{m9 => generic}/defscope/lib.fe | 0 fec/tests/{m9 => generic}/defscope/main.fe | 0 fec/tests/{m9 => generic}/okalias.fe | 0 fec/tests/{m9 => generic}/okbox.fe | 0 fec/tests/{m9 => generic}/okdedup.fe | 0 fec/tests/{m9 => generic}/okid.fe | 0 fec/tests/{m9 => generic}/okisint.fe | 0 fec/tests/{m9 => generic}/okmulti.fe | 0 fec/tests/{m9 => generic}/oknested.fe | 0 fec/tests/{m9 => generic}/okpair.fe | 0 fec/tests/{m9 => generic}/oksamrec.fe | 0 fec/tests/{m9 => generic}/okscope/lib.fe | 0 fec/tests/{m9 => generic}/okscope/main.fe | 0 fec/tests/{m9 => generic}/okskip.fe | 0 fec/tests/{m9 => generic}/oktypeeq.fe | 0 fec/tests/{m7 => optional}/README.md | 0 fec/tests/{m7 => optional}/badcatch.fe | 0 fec/tests/{m7 => optional}/baddef.fe | 0 fec/tests/{m7 => optional}/baddir.fe | 0 fec/tests/{m7 => optional}/badercod.fe | 0 fec/tests/{m7 => optional}/badernam.fe | 0 fec/tests/{m7 => optional}/badetype.fe | 0 fec/tests/{m7 => optional}/badnull.fe | 0 fec/tests/{m7 => optional}/badoref.fe | 0 fec/tests/{m7 => optional}/badorel.fe | 0 fec/tests/{m7 => optional}/badproj.fe | 0 fec/tests/{m7 => optional}/badqmark.fe | 0 fec/tests/{m7 => optional}/badret.fe | 0 fec/tests/{m7 => optional}/badsome.fe | 0 fec/tests/{m7 => optional}/badtry.fe | 0 fec/tests/{m7 => optional}/badzero.fe | 0 fec/tests/{m7 => optional}/okcatch.fe | 0 fec/tests/{m7 => optional}/okcatmov.fe | 0 fec/tests/{m7 => optional}/okcvoid.fe | 0 fec/tests/{m7 => optional}/okdeflt.fe | 0 fec/tests/{m7 => optional}/okiflet.fe | 0 fec/tests/{m7 => optional}/okmatch.fe | 0 fec/tests/{m7 => optional}/oknull.fe | 0 fec/tests/{m7 => optional}/okorelse.fe | 0 fec/tests/{m7 => optional}/okpatvw.fe | 0 fec/tests/{m7 => optional}/okproj.fe | 0 fec/tests/{m7 => optional}/okrepl.fe | 0 fec/tests/{m7 => optional}/oktrdef.fe | 0 fec/tests/{m7 => optional}/oktry.fe | 0 fec/tests/{m6 => own}/README.md | 0 fec/tests/{m6 => own}/badarg.fe | 0 fec/tests/{m6 => own}/badbinit.fe | 0 fec/tests/{m6 => own}/badbrmov.fe | 0 fec/tests/{m6 => own}/baddefer.fe | 0 fec/tests/{m6 => own}/badfld.fe | 0 fec/tests/{m6 => own}/badglob.fe | 0 fec/tests/{m6 => own}/badgmut.fe | 0 fec/tests/{m6 => own}/badinv.fe | 0 fec/tests/{m6 => own}/badlocsl.fe | 0 fec/tests/{m6 => own}/badloop.fe | 0 fec/tests/{m6 => own}/badmove.fe | 0 fec/tests/{m6 => own}/badmut.fe | 0 fec/tests/{m6 => own}/badmut2.fe | 0 fec/tests/{m6 => own}/badptr.fe | 0 fec/tests/{m6 => own}/badret.fe | 0 fec/tests/{m6 => own}/badrfld.fe | 0 fec/tests/{m6 => own}/badridx.fe | 0 fec/tests/{m6 => own}/badscop.fe | 0 fec/tests/{m6 => own}/badself.fe | 0 fec/tests/{m6 => own}/badshwr.fe | 0 fec/tests/{m6 => own}/badslfld.fe | 0 fec/tests/{m6 => own}/badtwo.fe | 0 fec/tests/{m6 => own}/badup.fe | 0 fec/tests/{m6 => own}/badweak.fe | 0 fec/tests/{m5/defer.fe => own/ok-defer.fe} | 0 fec/tests/{m5/owned.fe => own/ok-owned.fe} | 0 fec/tests/{m6 => own}/okbranch.fe | 0 fec/tests/{m6 => own}/okdefer.fe | 0 fec/tests/{m6 => own}/okglobcp.fe | 0 fec/tests/{m6 => own}/oklast.fe | 0 fec/tests/{m6 => own}/okr8free.fe | 0 fec/tests/{m6 => own}/okr8join.fe | 0 fec/tests/{m6 => own}/okr8meth.fe | 0 fec/tests/{m6 => own}/okr8stat.fe | 0 fec/tests/{m6 => own}/okrebor.fe | 0 fec/tests/{m6 => own}/okrtlast.fe | 0 fec/tests/{m6 => own}/okshare.fe | 0 fec/tests/{m6 => own}/okslreb.fe | 0 fec/tests/{m6 => own}/okstatic.fe | 0 fec/tests/{m6 => own}/oktemp.fe | 0 fec/tests/{m6 => own}/oktrim.fe | 0 fec/tests/{m6 => own}/okwcall.fe | 0 .../{m5/bad-clos.fe => own/own-bad-clos.fe} | 0 .../{m5/bad-cond.fe => own/own-bad-cond.fe} | 0 .../{m5/bad-dbl.fe => own/own-bad-dbl.fe} | 0 .../{m5/bad-dest.fe => own/own-bad-dest.fe} | 0 .../{m5/bad-drop.fe => own/own-bad-drop.fe} | 0 .../{m5/bad-loop.fe => own/own-bad-loop.fe} | 0 .../{m5/bad-move.fe => own/own-bad-move.fe} | 0 .../{m5/bad-proj.fe => own/own-bad-proj.fe} | 0 fec/tests/{pass => parse}/basic.fe | 0 fec/tests/{pass => parse}/keybuilt.fe | 0 fec/tests/{pass => parse}/literals.fe | 0 fec/tests/{fail => parse}/logical.fe | 0 fec/tests/{fail => parse}/misssemi.fe | 0 fec/tests/{fail => parse}/unclcomm.fe | 0 fec/tests/{pass => parse}/v012form.fe | 0 .../bounds-nocheck.fe} | 0 .../bounds-trap.fe} | 0 .../format-prop.c} | 0 .../ownership-drop.c} | 0 .../ownership-drop.fe} | 0 .../slice-bounds-trap.fe} | 0 fec/tests/{m2 => types}/bad-ari.fe | 0 fec/tests/{m2 => types}/bad-asgn.fe | 0 fec/tests/{m2 => types}/bad-cast.fe | 0 fec/tests/{m2 => types}/bad-cond.fe | 0 fec/tests/{m3 => types}/bad-mlet.fe | 0 fec/tests/{m2 => types}/bad-ret.fe | 0 fec/tests/{m3 => types}/bad-shwr.fe | 0 fec/tests/{m2 => types}/bad-type.fe | 0 fec/tests/{m2 => types}/bad-unit.fe | 0 fec/tests/{m2 => types}/bad-unk.fe | 0 fec/tests/{m2 => types}/bad-void.fe | 0 fec/tests/{m3 => types}/badarr.fe | 0 fec/tests/{m3 => types}/badchar.fe | 0 fec/tests/{m3 => types}/badcycle.fe | 0 fec/tests/{m3 => types}/badfield.fe | 0 fec/tests/{m3 => types}/badfld.fe | 0 fec/tests/{m3 => types}/badindex.fe | 0 fec/tests/{m3 => types}/badmat.fe | 0 fec/tests/{m3 => types}/badstr.fe | 0 fec/tests/{m3/array.fe => types/ok-array.fe} | 0 .../{m3/arrayctx.fe => types/ok-arrayctx.fe} | 0 .../{m2/castwhil.fe => types/ok-castwhil.fe} | 0 fec/tests/{m3/char.fe => types/ok-char.fe} | 0 fec/tests/{m3/enum.fe => types/ok-enum.fe} | 0 fec/tests/{m3/for.fe => types/ok-for.fe} | 0 fec/tests/{m2/hello.fe => types/ok-hello.fe} | 0 .../{m3/mutable.fe => types/ok-mutable.fe} | 0 .../{m3/nested.fe => types/ok-nested.fe} | 0 .../{m2/scopes.fe => types/ok-scopes.fe} | 0 fec/tests/{m3/str.fe => types/ok-str.fe} | 0 .../{m3/struct.fe => types/ok-struct.fe} | 0 fec/tests/{m8 => units}/README.md | 0 fec/tests/{m8 => units}/alias/acme/math.fe | 0 fec/tests/{m8 => units}/alias/main.fe | 0 fec/tests/{m8 => units}/badlong/main.fe | 0 fec/tests/{m8 => units}/badupper/main.fe | 0 fec/tests/{m8 => units}/basic/main.fe | 0 fec/tests/{m8 => units}/basic/util.fe | 0 fec/tests/{m8 => units}/bindconf/alpha/net.fe | 0 fec/tests/{m8 => units}/bindconf/beta/net.fe | 0 fec/tests/{m8 => units}/bindconf/main.fe | 0 fec/tests/{m8 => units}/cycle/a.fe | 0 fec/tests/{m8 => units}/cycle/b.fe | 0 fec/tests/{m8 => units}/dotpriv/game/bar.fe | 0 fec/tests/{m8 => units}/dotpriv/game/foo.fe | 0 fec/tests/{m8 => units}/dotpriv/main.fe | 0 fec/tests/{m8 => units}/dotted/acme/math.fe | 0 fec/tests/{m8 => units}/dotted/main.fe | 0 fec/tests/{m8 => units}/errdet/alpha.fe | 0 fec/tests/{m8 => units}/errdet/beta.fe | 0 fec/tests/{m8 => units}/errdet/main.fe | 0 fec/tests/{m8 => units}/errnom/lib.fe | 0 fec/tests/{m8 => units}/errnom/main.fe | 0 fec/tests/{m8 => units}/errsame/alpha.fe | 0 fec/tests/{m8 => units}/errsame/beta.fe | 0 fec/tests/{m8 => units}/errsame/main.fe | 0 fec/tests/{m8 => units}/missing/main.fe | 0 fec/tests/{m8 => units}/privfld/data.fe | 0 fec/tests/{m8 => units}/privfld/main.fe | 0 fec/tests/{m8 => units}/privfn/main.fe | 0 fec/tests/{m8 => units}/privfn/util.fe | 0 fec/tests/{m8 => units}/pubfld/data.fe | 0 fec/tests/{m8 => units}/pubfld/main.fe | 0 fec/tests/{m8 => units}/pubpriv/lib.fe | 0 fec/tests/{m8 => units}/pubpriv/main.fe | 0 fec/tests/{m8 => units}/unitbad/main.fe | 0 src/ferrolang_vm/__init__.py | 1 - src/ferrolang_vm/dos_cli.py | 80 - src/ferrolang_vm/dosboxx.py | 332 --- src/ferrolang_vm/paths.py | 5 - src/ferrolang_vm/registry.py | 260 -- src/ferrolang_vm/suite.py | 16 - src/ferrolang_vm/test_cli.py | 90 - src/node-types.json | 121 - src/parser.c | 1744 ------------ src/tree_sitter/parser.h | 55 - tests/run.py | 140 + tools/README.md | 55 - tools/tests/test_dos_names.py | 72 - tools/tests/test_host_syntax.py | 113 - tools/tests/test_milestones_dosboxx.py | 55 - tools/toolchains/dosboxx.lock.json | 23 - 221 files changed, 149 insertions(+), 5834 deletions(-) delete mode 100644 fec/build-dos.bat delete mode 100644 fec/src/emit_c.c delete mode 100644 fec/src/emit_c.h delete mode 100644 fec/src/lower.c delete mode 100644 fec/src/lower.h rename fec/tests/{m4 => format}/bad-ari.fe (100%) rename fec/tests/{m4 => format}/bad-bufw.fe (100%) rename fec/tests/{m4 => format}/bad-cls.fe (100%) rename fec/tests/{m4 => format}/bad-many.fe (100%) rename fec/tests/{m4 => format}/bad-open.fe (100%) rename fec/tests/{m4 => format}/bad-run.fe (100%) rename fec/tests/{m4 => format}/bad-try.fe (100%) rename fec/tests/{m4 => format}/bad-type.fe (100%) rename fec/tests/{m4 => format}/bad-verb.fe (100%) rename fec/tests/{m4 => format}/bad-writ.fe (100%) rename fec/tests/{m4/format.fe => format/ok-format.fe} (100%) rename fec/tests/{m4/prop.fe => format/ok-prop.fe} (100%) rename fec/tests/{m4/try-fpr.fe => format/ok-try-fpr.fe} (100%) rename fec/tests/{m9 => generic}/README.md (100%) rename fec/tests/{m9 => generic}/badarg.fe (100%) rename fec/tests/{m9 => generic}/badarity.fe (100%) rename fec/tests/{m9 => generic}/badbody.fe (100%) rename fec/tests/{m9 => generic}/baddepth.fe (100%) rename fec/tests/{m9 => generic}/baddist.fe (100%) rename fec/tests/{m9 => generic}/badfew.fe (100%) rename fec/tests/{m9 => generic}/badinfer.fe (100%) rename fec/tests/{m9 => generic}/badop.fe (100%) rename fec/tests/{m9 => generic}/badtype.fe (100%) rename fec/tests/{m9 => generic}/badvalue.fe (100%) rename fec/tests/{m9 => generic}/defscope/lib.fe (100%) rename fec/tests/{m9 => generic}/defscope/main.fe (100%) rename fec/tests/{m9 => generic}/okalias.fe (100%) rename fec/tests/{m9 => generic}/okbox.fe (100%) rename fec/tests/{m9 => generic}/okdedup.fe (100%) rename fec/tests/{m9 => generic}/okid.fe (100%) rename fec/tests/{m9 => generic}/okisint.fe (100%) rename fec/tests/{m9 => generic}/okmulti.fe (100%) rename fec/tests/{m9 => generic}/oknested.fe (100%) rename fec/tests/{m9 => generic}/okpair.fe (100%) rename fec/tests/{m9 => generic}/oksamrec.fe (100%) rename fec/tests/{m9 => generic}/okscope/lib.fe (100%) rename fec/tests/{m9 => generic}/okscope/main.fe (100%) rename fec/tests/{m9 => generic}/okskip.fe (100%) rename fec/tests/{m9 => generic}/oktypeeq.fe (100%) rename fec/tests/{m7 => optional}/README.md (100%) rename fec/tests/{m7 => optional}/badcatch.fe (100%) rename fec/tests/{m7 => optional}/baddef.fe (100%) rename fec/tests/{m7 => optional}/baddir.fe (100%) rename fec/tests/{m7 => optional}/badercod.fe (100%) rename fec/tests/{m7 => optional}/badernam.fe (100%) rename fec/tests/{m7 => optional}/badetype.fe (100%) rename fec/tests/{m7 => optional}/badnull.fe (100%) rename fec/tests/{m7 => optional}/badoref.fe (100%) rename fec/tests/{m7 => optional}/badorel.fe (100%) rename fec/tests/{m7 => optional}/badproj.fe (100%) rename fec/tests/{m7 => optional}/badqmark.fe (100%) rename fec/tests/{m7 => optional}/badret.fe (100%) rename fec/tests/{m7 => optional}/badsome.fe (100%) rename fec/tests/{m7 => optional}/badtry.fe (100%) rename fec/tests/{m7 => optional}/badzero.fe (100%) rename fec/tests/{m7 => optional}/okcatch.fe (100%) rename fec/tests/{m7 => optional}/okcatmov.fe (100%) rename fec/tests/{m7 => optional}/okcvoid.fe (100%) rename fec/tests/{m7 => optional}/okdeflt.fe (100%) rename fec/tests/{m7 => optional}/okiflet.fe (100%) rename fec/tests/{m7 => optional}/okmatch.fe (100%) rename fec/tests/{m7 => optional}/oknull.fe (100%) rename fec/tests/{m7 => optional}/okorelse.fe (100%) rename fec/tests/{m7 => optional}/okpatvw.fe (100%) rename fec/tests/{m7 => optional}/okproj.fe (100%) rename fec/tests/{m7 => optional}/okrepl.fe (100%) rename fec/tests/{m7 => optional}/oktrdef.fe (100%) rename fec/tests/{m7 => optional}/oktry.fe (100%) rename fec/tests/{m6 => own}/README.md (100%) rename fec/tests/{m6 => own}/badarg.fe (100%) rename fec/tests/{m6 => own}/badbinit.fe (100%) rename fec/tests/{m6 => own}/badbrmov.fe (100%) rename fec/tests/{m6 => own}/baddefer.fe (100%) rename fec/tests/{m6 => own}/badfld.fe (100%) rename fec/tests/{m6 => own}/badglob.fe (100%) rename fec/tests/{m6 => own}/badgmut.fe (100%) rename fec/tests/{m6 => own}/badinv.fe (100%) rename fec/tests/{m6 => own}/badlocsl.fe (100%) rename fec/tests/{m6 => own}/badloop.fe (100%) rename fec/tests/{m6 => own}/badmove.fe (100%) rename fec/tests/{m6 => own}/badmut.fe (100%) rename fec/tests/{m6 => own}/badmut2.fe (100%) rename fec/tests/{m6 => own}/badptr.fe (100%) rename fec/tests/{m6 => own}/badret.fe (100%) rename fec/tests/{m6 => own}/badrfld.fe (100%) rename fec/tests/{m6 => own}/badridx.fe (100%) rename fec/tests/{m6 => own}/badscop.fe (100%) rename fec/tests/{m6 => own}/badself.fe (100%) rename fec/tests/{m6 => own}/badshwr.fe (100%) rename fec/tests/{m6 => own}/badslfld.fe (100%) rename fec/tests/{m6 => own}/badtwo.fe (100%) rename fec/tests/{m6 => own}/badup.fe (100%) rename fec/tests/{m6 => own}/badweak.fe (100%) rename fec/tests/{m5/defer.fe => own/ok-defer.fe} (100%) rename fec/tests/{m5/owned.fe => own/ok-owned.fe} (100%) rename fec/tests/{m6 => own}/okbranch.fe (100%) rename fec/tests/{m6 => own}/okdefer.fe (100%) rename fec/tests/{m6 => own}/okglobcp.fe (100%) rename fec/tests/{m6 => own}/oklast.fe (100%) rename fec/tests/{m6 => own}/okr8free.fe (100%) rename fec/tests/{m6 => own}/okr8join.fe (100%) rename fec/tests/{m6 => own}/okr8meth.fe (100%) rename fec/tests/{m6 => own}/okr8stat.fe (100%) rename fec/tests/{m6 => own}/okrebor.fe (100%) rename fec/tests/{m6 => own}/okrtlast.fe (100%) rename fec/tests/{m6 => own}/okshare.fe (100%) rename fec/tests/{m6 => own}/okslreb.fe (100%) rename fec/tests/{m6 => own}/okstatic.fe (100%) rename fec/tests/{m6 => own}/oktemp.fe (100%) rename fec/tests/{m6 => own}/oktrim.fe (100%) rename fec/tests/{m6 => own}/okwcall.fe (100%) rename fec/tests/{m5/bad-clos.fe => own/own-bad-clos.fe} (100%) rename fec/tests/{m5/bad-cond.fe => own/own-bad-cond.fe} (100%) rename fec/tests/{m5/bad-dbl.fe => own/own-bad-dbl.fe} (100%) rename fec/tests/{m5/bad-dest.fe => own/own-bad-dest.fe} (100%) rename fec/tests/{m5/bad-drop.fe => own/own-bad-drop.fe} (100%) rename fec/tests/{m5/bad-loop.fe => own/own-bad-loop.fe} (100%) rename fec/tests/{m5/bad-move.fe => own/own-bad-move.fe} (100%) rename fec/tests/{m5/bad-proj.fe => own/own-bad-proj.fe} (100%) rename fec/tests/{pass => parse}/basic.fe (100%) rename fec/tests/{pass => parse}/keybuilt.fe (100%) rename fec/tests/{pass => parse}/literals.fe (100%) rename fec/tests/{fail => parse}/logical.fe (100%) rename fec/tests/{fail => parse}/misssemi.fe (100%) rename fec/tests/{fail => parse}/unclcomm.fe (100%) rename fec/tests/{pass => parse}/v012form.fe (100%) rename fec/tests/{m3/nochk.fe => pending-backend/bounds-nocheck.fe} (100%) rename fec/tests/{m3/bounds.fe => pending-backend/bounds-trap.fe} (100%) rename fec/tests/{m4/proptest.c => pending-backend/format-prop.c} (100%) rename fec/tests/{m5/runtime.c => pending-backend/ownership-drop.c} (100%) rename fec/tests/{m5/runtime.fe => pending-backend/ownership-drop.fe} (100%) rename fec/tests/{m3/slcbound.fe => pending-backend/slice-bounds-trap.fe} (100%) rename fec/tests/{m2 => types}/bad-ari.fe (100%) rename fec/tests/{m2 => types}/bad-asgn.fe (100%) rename fec/tests/{m2 => types}/bad-cast.fe (100%) rename fec/tests/{m2 => types}/bad-cond.fe (100%) rename fec/tests/{m3 => types}/bad-mlet.fe (100%) rename fec/tests/{m2 => types}/bad-ret.fe (100%) rename fec/tests/{m3 => types}/bad-shwr.fe (100%) rename fec/tests/{m2 => types}/bad-type.fe (100%) rename fec/tests/{m2 => types}/bad-unit.fe (100%) rename fec/tests/{m2 => types}/bad-unk.fe (100%) rename fec/tests/{m2 => types}/bad-void.fe (100%) rename fec/tests/{m3 => types}/badarr.fe (100%) rename fec/tests/{m3 => types}/badchar.fe (100%) rename fec/tests/{m3 => types}/badcycle.fe (100%) rename fec/tests/{m3 => types}/badfield.fe (100%) rename fec/tests/{m3 => types}/badfld.fe (100%) rename fec/tests/{m3 => types}/badindex.fe (100%) rename fec/tests/{m3 => types}/badmat.fe (100%) rename fec/tests/{m3 => types}/badstr.fe (100%) rename fec/tests/{m3/array.fe => types/ok-array.fe} (100%) rename fec/tests/{m3/arrayctx.fe => types/ok-arrayctx.fe} (100%) rename fec/tests/{m2/castwhil.fe => types/ok-castwhil.fe} (100%) rename fec/tests/{m3/char.fe => types/ok-char.fe} (100%) rename fec/tests/{m3/enum.fe => types/ok-enum.fe} (100%) rename fec/tests/{m3/for.fe => types/ok-for.fe} (100%) rename fec/tests/{m2/hello.fe => types/ok-hello.fe} (100%) rename fec/tests/{m3/mutable.fe => types/ok-mutable.fe} (100%) rename fec/tests/{m3/nested.fe => types/ok-nested.fe} (100%) rename fec/tests/{m2/scopes.fe => types/ok-scopes.fe} (100%) rename fec/tests/{m3/str.fe => types/ok-str.fe} (100%) rename fec/tests/{m3/struct.fe => types/ok-struct.fe} (100%) rename fec/tests/{m8 => units}/README.md (100%) rename fec/tests/{m8 => units}/alias/acme/math.fe (100%) rename fec/tests/{m8 => units}/alias/main.fe (100%) rename fec/tests/{m8 => units}/badlong/main.fe (100%) rename fec/tests/{m8 => units}/badupper/main.fe (100%) rename fec/tests/{m8 => units}/basic/main.fe (100%) rename fec/tests/{m8 => units}/basic/util.fe (100%) rename fec/tests/{m8 => units}/bindconf/alpha/net.fe (100%) rename fec/tests/{m8 => units}/bindconf/beta/net.fe (100%) rename fec/tests/{m8 => units}/bindconf/main.fe (100%) rename fec/tests/{m8 => units}/cycle/a.fe (100%) rename fec/tests/{m8 => units}/cycle/b.fe (100%) rename fec/tests/{m8 => units}/dotpriv/game/bar.fe (100%) rename fec/tests/{m8 => units}/dotpriv/game/foo.fe (100%) rename fec/tests/{m8 => units}/dotpriv/main.fe (100%) rename fec/tests/{m8 => units}/dotted/acme/math.fe (100%) rename fec/tests/{m8 => units}/dotted/main.fe (100%) rename fec/tests/{m8 => units}/errdet/alpha.fe (100%) rename fec/tests/{m8 => units}/errdet/beta.fe (100%) rename fec/tests/{m8 => units}/errdet/main.fe (100%) rename fec/tests/{m8 => units}/errnom/lib.fe (100%) rename fec/tests/{m8 => units}/errnom/main.fe (100%) rename fec/tests/{m8 => units}/errsame/alpha.fe (100%) rename fec/tests/{m8 => units}/errsame/beta.fe (100%) rename fec/tests/{m8 => units}/errsame/main.fe (100%) rename fec/tests/{m8 => units}/missing/main.fe (100%) rename fec/tests/{m8 => units}/privfld/data.fe (100%) rename fec/tests/{m8 => units}/privfld/main.fe (100%) rename fec/tests/{m8 => units}/privfn/main.fe (100%) rename fec/tests/{m8 => units}/privfn/util.fe (100%) rename fec/tests/{m8 => units}/pubfld/data.fe (100%) rename fec/tests/{m8 => units}/pubfld/main.fe (100%) rename fec/tests/{m8 => units}/pubpriv/lib.fe (100%) rename fec/tests/{m8 => units}/pubpriv/main.fe (100%) rename fec/tests/{m8 => units}/unitbad/main.fe (100%) delete mode 100644 src/ferrolang_vm/__init__.py delete mode 100644 src/ferrolang_vm/dos_cli.py delete mode 100644 src/ferrolang_vm/dosboxx.py delete mode 100644 src/ferrolang_vm/paths.py delete mode 100644 src/ferrolang_vm/registry.py delete mode 100644 src/ferrolang_vm/suite.py delete mode 100644 src/ferrolang_vm/test_cli.py delete mode 100644 src/node-types.json delete mode 100644 src/parser.c delete mode 100644 src/tree_sitter/parser.h create mode 100644 tests/run.py delete mode 100644 tools/README.md delete mode 100644 tools/tests/test_dos_names.py delete mode 100644 tools/tests/test_host_syntax.py delete mode 100644 tools/tests/test_milestones_dosboxx.py delete mode 100644 tools/toolchains/dosboxx.lock.json diff --git a/.gitignore b/.gitignore index 6979fd4..2ad4b9f 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ __pycache__/ node_modules/ .npm/ .cache/ + +# host build output of the front end +.build/ diff --git a/fec/build-dos.bat b/fec/build-dos.bat deleted file mode 100644 index e8c1178..0000000 --- a/fec/build-dos.bat +++ /dev/null @@ -1,49 +0,0 @@ -@echo off -rem Open Watcom C89 build. The runner's generated RUN.BAT calls this from C:\FEC. -C: -cd \FEC -if exist BUILD.OK del BUILD.OK -if exist BUILD.FAIL del BUILD.FAIL -if exist fec.exe del fec.exe -if exist __wcl__.lnk del __wcl__.lnk -if exist *.obj del *.obj - -if "%WATCOM%"=="" set WATCOM=C:\DEVEL\WATCOMC -if not exist %WATCOM%\BINW\WCL.EXE goto build_fail -set PATH=%WATCOM%\BINW;%WATCOM%\BINP;%PATH% -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=arena.obj src\arena.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=diag.obj src\diag.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=lexer.obj src\lexer.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=ast.obj src\ast.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=parser.obj src\parser.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=types.obj src\types.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=m7.obj src\m7.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=own.obj src\own.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=check.obj src\check.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=lower.obj src\lower.c -if errorlevel 1 goto build_fail -rem Use an unambiguous short object name for the emitter source. -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=emitc.obj src\emit_c.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -c -fo=driver.obj src\driver.c -if errorlevel 1 goto build_fail -wcl -q -za -wx -bt=dos -ml -k32768 -fe=fec.exe *.obj -if errorlevel 1 goto build_fail -if not exist fec.exe goto build_fail -echo OK>BUILD.OK -cd C:\FEC -goto build_done - -:build_fail -echo FAIL>BUILD.FAIL - -:build_done diff --git a/fec/src/driver.c b/fec/src/driver.c index 90903d5..7a5c55f 100644 --- a/fec/src/driver.c +++ b/fec/src/driver.c @@ -1,6 +1,5 @@ #include "parser.h" #include "check.h" -#include "emit_c.h" #include #include #include @@ -37,25 +36,20 @@ static void dump_tokens(const char *src, unsigned long n, const char *file, int main(int argc, char **argv) { - int i,dump=0,dump_tok=0,check_only=0,emit=0,no_checks=0; - const char *file=0,*outname=0; + int i,dump=0,dump_tok=0,check_only=0,no_checks=0; + const char *file=0; unsigned long n; char *src; FeDiags d; FeAst ast; FeParser p; FeCheck check; - FeEmitter emitter; - FILE *out; unsigned pointer_bits=32; if(argc<2){usage();return 2;} for(i=1;i=argc){fprintf(fe_diag_stream(),"fec: -o needs a path\n");return 2;}outname=argv[++i];} - else if(strncmp(argv[i],"-o",2)==0 && argv[i][2]) outname=argv[i]+2; else if(strncmp(argv[i],"--target=bits16",15)==0) pointer_bits=16; else if(strncmp(argv[i],"--target=bits32",15)==0) pointer_bits=32; else if(strcmp(argv[i],"--no-checks")==0) no_checks=1; @@ -64,7 +58,7 @@ int main(int argc, char **argv) else if(strcmp(argv[i],"--help")==0){usage();return 0;} else {fprintf(fe_diag_stream(),"fec: unknown option %s\n",argv[i]);return 2;} } - if((dump?1:0)+(dump_tok?1:0)+(check_only?1:0)+(emit?1:0)>1){ + if((dump?1:0)+(dump_tok?1:0)+(check_only?1:0)>1){ fprintf(fe_diag_stream(),"fec: choose only one output mode\n"); return 2; } @@ -92,16 +86,9 @@ int main(int argc, char **argv) free(src); return 1; } - if(check_only){ - fe_ast_destroy(&ast); - free(src); - return 0; - } - out=outname?fopen(outname,"w"):stdout; - if(!out){fprintf(fe_diag_stream(),"fec: cannot create %s\n",outname);fe_ast_destroy(&ast);free(src);return 2;} - fe_emit_c_init(&emitter,out,&check,pointer_bits,no_checks); - fe_emit_c_program(&emitter); - if(outname)fclose(out); + /* Semantic analysis is the last pass there is. A code generator attaches + here; until then --check and the default path are the same thing. */ + (void)check_only; fe_ast_destroy(&ast); free(src); return d.errors?1:0; diff --git a/fec/src/emit_c.c b/fec/src/emit_c.c deleted file mode 100644 index 1fc185b..0000000 --- a/fec/src/emit_c.c +++ /dev/null @@ -1,2428 +0,0 @@ -#include "emit_c.h" -#include -#include - -static int type_needs_drop(FeType *t); -static void emit_lvalue(FeEmitter *e, FeNode *n); - -static void emit_expr_core(FeEmitter *e, FeNode *n); -static void emit_stmt_core(FeEmitter *e, FeNode *n); -static void emit_block(FeEmitter *e, FeNode *n); - -static void pad(FeEmitter *e) -{ - int i; - for (i = 0; i < e->indent; ++i) fputs(" ", e->out); -} - -static const char *ctype(FeEmitter *e, FeNode *n) -{ - FeType *t; - if (n && n->sem_type) t = n->sem_type; - else if (n) t = fe_type_from_ast(&e->check->types, n); - else t = fe_type_intern(&e->check->types, "i32"); - return fe_type_c_name(t, e->pointer_bits); -} - -static const char *cname(FeNode *n, const char *fallback) -{ - return n && n->cname ? n->cname : fallback; -} - -static void emit_one_type(FeEmitter *e, FeType *t); - - -static void emit_type_deps(FeEmitter *e, FeType *t) -{ - unsigned i,j; - if (!t) return; - if (t->kind == FE_TYPE_ARRAY) emit_one_type(e,t->elem); - if (t->kind == FE_TYPE_STRUCT) - for (i=0;ifield_count;i++) emit_one_type(e,t->fields[i].type); - if (t->kind == FE_TYPE_ENUM) - for (i=0;ivariant_count;i++) - for (j=0;jvariants[i].field_count;j++) - emit_one_type(e,t->variants[i].fields[j].type); - if (t->kind == FE_TYPE_ERROR_UNION && t->error_value) - emit_one_type(e,t->error_value); -} - -static void emit_one_type(FeEmitter *e, FeType *t) -{ - unsigned i,j; - if (t && strcmp(t->name,"io.Writer")==0) return; - if (!t || t->emit_state || - (t->kind != FE_TYPE_STRUCT && t->kind != FE_TYPE_ENUM && - t->kind != FE_TYPE_ARRAY && t->kind != FE_TYPE_SLICE && - !(t->kind == FE_TYPE_OWNED && t->elem && - t->elem->kind == FE_TYPE_SLICE) && - t->kind != FE_TYPE_ERROR_UNION) || - (t->kind == FE_TYPE_ERROR_UNION && - (!t->error_value || t->error_value->kind == FE_TYPE_VOID))) return; - t->emit_state=1; - emit_type_deps(e,t); - if(t->kind==FE_TYPE_STRUCT) { - fputs(t->cname,e->out); fputs(" {\n",e->out); - for(i=0;ifield_count;i++) { fputs(" ",e->out); fputs(fe_type_c_name(t->fields[i].type,e->pointer_bits),e->out); fputc(' ',e->out); fputs(t->fields[i].name,e->out); fputs(";\n",e->out); } - fputs("};\n",e->out); - } else if(t->kind==FE_TYPE_ARRAY) { - fputs(t->cname,e->out); fputs(" { ",e->out); fputs(fe_type_c_name(t->elem,e->pointer_bits),e->out); fputs(" a[",e->out); fprintf(e->out,"%lu",t->length); fputs("]; };\n",e->out); - } else if(t->kind==FE_TYPE_SLICE && t->cname) { - fputs("typedef struct { ",e->out); - if(!t->ref_mut) fputs("const ",e->out); - fputs(fe_type_c_name(t->elem,e->pointer_bits),e->out); fputs(" *p; unsigned long n; } ",e->out); fputs(t->cname,e->out); fputs(";\n",e->out); - fprintf(e->out,"static %s %s(%s%s *p, unsigned long n) { %s s; s.p=p; s.n=n; return s; }\n",t->cname,t->maker,t->ref_mut ? "" : "const ",fe_type_c_name(t->elem,e->pointer_bits),t->cname); - } else if(t->kind==FE_TYPE_OWNED && t->elem && - t->elem->kind==FE_TYPE_SLICE) { - FeType *item=t->elem->elem; - fputs("typedef struct { ",e->out); - fputs(fe_type_c_name(item,e->pointer_bits),e->out); - fputs(" *p; unsigned long n; } ",e->out); fputs(t->cname,e->out); - fputs(";\n",e->out); - fprintf(e->out,"static %s %s(%s *p, unsigned long n) { %s s; s.p=p; s.n=n; return s; }\n", - t->cname,t->maker,fe_type_c_name(item,e->pointer_bits),t->cname); - } else if(t->kind==FE_TYPE_ERROR_UNION) { - fputs(t->cname,e->out); fputs(" { unsigned short e; ",e->out); - fputs(fe_type_c_name(t->error_value,e->pointer_bits),e->out); - fputs(" v; } ;\n",e->out); - } else if(t->kind==FE_TYPE_ENUM) { - for(i=0;ivariant_count;i++) if(t->variants[i].field_count>1) { - fprintf(e->out,"struct fe_payload_%s_%s {",t->name,t->variants[i].name); - for(j=0;jvariants[i].field_count;j++) { fputs(" ",e->out); fputs(fe_type_c_name(t->variants[i].fields[j].type,e->pointer_bits),e->out); fputc(' ',e->out); fputs(t->variants[i].fields[j].name,e->out); fputc(';',e->out); } - fputs(" };\n",e->out); - } - fputs(t->cname,e->out); fputs(" { ",e->out); fputs(t->bits>8 ? "unsigned short" : "unsigned char",e->out); fputs(" tag; union { ",e->out); - for(i=0;ivariant_count;i++) { if(t->variants[i].field_count==0) fputs("unsigned char",e->out); else if(t->variants[i].field_count==1) fputs(fe_type_c_name(t->variants[i].fields[0].type,e->pointer_bits),e->out); else fprintf(e->out,"struct fe_payload_%s_%s",t->name,t->variants[i].name); fputc(' ',e->out); fputs(t->variants[i].name,e->out); fputc(';',e->out); } - fputs(" } payload; };\n",e->out); - } - t->emit_state=2; -} - - -static FeNode *find_drop_method(FeEmitter *e, const char *name) -{ - FeNode *n; - FeNode *m; - for (n=e->check->ast->root ? e->check->ast->root->children : 0; n; n=n->next) - if (n->kind==FE_N_STRUCT && n->text && name && strcmp(n->text,name)==0) - for (m=n->children; m; m=m->next) - if (m->kind==FE_N_FN && m->text && strcmp(m->text,"drop")==0) - return m; - return 0; -} - - - -/* SPEC 12.3 lists `trim` among the built-in alias methods on `str`, called as - `line.trim()`. The checker accepts it; this emits the lowering. Only the - helper for slice types actually reached by a trim call is emitted, so a - program that never trims does not carry it. */ -static int node_uses_trim(FeNode *n) -{ - FeNode *x; - if (!n) return 0; - if (n->kind==FE_N_CALL && n->a && n->a->kind==FE_N_MEMBER && - n->a->b && n->a->b->text && strcmp(n->a->b->text,"trim")==0) return 1; - if (node_uses_trim(n->a) || node_uses_trim(n->b) || node_uses_trim(n->c)) - return 1; - for (x=n->children; x; x=x->next) if (node_uses_trim(x)) return 1; - return 0; -} - - -static void emit_m4_runtime(FeEmitter *e) -{ - fputs("typedef struct { unsigned char *p; unsigned long n; } fe_m4_slice;\n",e->out); - fputs("typedef struct { unsigned char tag; unsigned short handle; } fe_writer;\n",e->out); - fputs("unsigned short fe_m4_error;\n",e->out); - fputs("fe_writer fe_m4_writer(unsigned char tag, unsigned short handle) { fe_writer w; w.tag=tag; w.handle=handle; return w; }\n",e->out); - fputs("fe_writer fe_m4_stdout_writer(void) { return fe_m4_writer(0,1); }\n",e->out); - fputs("fe_writer fe_m4_stderr_writer(void) { return fe_m4_writer(1,2); }\n",e->out); - fputs("fe_writer fe_m4_null_writer(void) { return fe_m4_writer(3,0); }\n",e->out); - fputs("/* bounded sprint stack; overflow traps instead of corrupting an outer call */\n#define FE_M4_SPRINT_DEPTH 8\n",e->out); - fputs("typedef struct { fe_m4_slice b; unsigned long start_n; } fe_m4_sprint_frame;\n",e->out); - fputs("static fe_m4_sprint_frame fe_m4_sprint_stack[FE_M4_SPRINT_DEPTH];\n",e->out); - fputs("static unsigned fe_m4_sprint_depth;\n",e->out); - fputs("void fe_m4_sprint_begin(fe_m4_slice *b) { if (fe_m4_sprint_depth>=FE_M4_SPRINT_DEPTH) abort(); fe_m4_sprint_stack[fe_m4_sprint_depth].b=*b; fe_m4_sprint_stack[fe_m4_sprint_depth].start_n=b->n; ++fe_m4_sprint_depth; }\n",e->out); - fputs("fe_writer fe_m4_sprint_writer(void) { return fe_m4_writer(4,(unsigned short)(fe_m4_sprint_depth-1)); }\n",e->out); - fputs("unsigned long fe_m4_sprint_finish(void) { unsigned long result; if (!fe_m4_sprint_depth) abort(); --fe_m4_sprint_depth; result=fe_m4_sprint_stack[fe_m4_sprint_depth].start_n-fe_m4_sprint_stack[fe_m4_sprint_depth].b.n; return result; }\n",e->out); - fputs("unsigned short fe_m4_write_bytes(fe_writer w, const unsigned char *p, unsigned long n) { if(w.tag==0) return fwrite(p,1,(size_t)n,stdout)==(size_t)n?0:1; if(w.tag==1) return fwrite(p,1,(size_t)n,stderr)==(size_t)n?0:1; if(w.tag==3) return 0; if(w.tag==4 && w.handlen?n:b->n; if(k) memcpy(b->p,p,(size_t)k); b->p+=k; b->n-=k; return 0; } return 1; }\n",e->out); - fputs("unsigned short fe_m4_write_cstr(fe_writer w, const char *p) { return fe_m4_write_bytes(w,(const unsigned char*)p,(unsigned long)strlen(p)); }\n",e->out); - fputs("#define fe_m4_write_slice(w,s) fe_m4_write_bytes((w),(s).p,(s).n)\n",e->out); - fputs("unsigned short fe_m4_write_int(fe_writer w, long v) { char b[40]; sprintf(b,\"%ld\",v); return fe_m4_write_cstr(w,b); }\n",e->out); - fputs("unsigned short fe_m4_write_hex(fe_writer w, unsigned long v) { char b[40]; sprintf(b,\"%lx\",v); return fe_m4_write_cstr(w,b); }\n",e->out); - fputs("unsigned short fe_m4_write_char(fe_writer w, unsigned char v) { return fe_m4_write_bytes(w,&v,1); }\n",e->out); - fputs("unsigned short fe_m4_write_bool(fe_writer w, unsigned char v) { return fe_m4_write_cstr(w,v ? \"true\" : \"false\"); }\n",e->out); - fputs("unsigned short fe_m4_write_error(fe_writer w, unsigned long v) { char b[40]; sprintf(b,\"error#%lu\",v); return fe_m4_write_cstr(w,b); }\n",e->out); -} - -static int node_uses_m4(FeNode *n) -{ - FeNode *x; - if (!n) return 0; - if (n->kind==FE_N_CALL && n->text && - (strcmp(n->text,"@print")==0 || strcmp(n->text,"@fprint")==0 || - strcmp(n->text,"@sprint")==0)) return 1; - if (n->kind==FE_N_CALL && n->a && n->a->kind==FE_N_MEMBER && - n->a->a && n->a->a->text && strcmp(n->a->a->text,"io")==0) return 1; - if (node_uses_m4(n->a) || node_uses_m4(n->b) || node_uses_m4(n->c)) return 1; - for (x=n->children; x; x=x->next) if (node_uses_m4(x)) return 1; - return 0; -} - -static void emit_expr(FeEmitter *e, FeNode *n); -static void emit_stmt(FeEmitter *e, FeNode *n); -static void emit_owned_live(FeEmitter *e, FeNode *n, int value); -static void emit_cleanup_all(FeEmitter *e); -static void emit_value_drop(FeEmitter *e, FeNode *n); -static void emit_cleanup_block(FeEmitter *e, FeNode *n); -static void emit_cleanup_to(FeEmitter *e, unsigned floor); -static void emit_param_cleanup(FeEmitter *e); -static void emit_error_return(FeEmitter *e, const char *error_expr); -static void emit_fn(FeEmitter *e, FeNode *fn, int prototype); -static void emit_main_wrapper(FeEmitter *e, FeNode *fn); -static void emit_type_defs(FeEmitter *e); - -static int stmt_definitely_returns(FeNode *n); - -static int match_is_exhaustive(FeNode *n) -{ - FeType *t; - FeNode *arm; - unsigned i; - int found; - if (!n || !n->a) return 0; - t=n->a->sem_type; - if (!t || t->kind!=FE_TYPE_ENUM) return 0; - for (arm=n->children; arm; arm=arm->next) - if (arm->text && strcmp(arm->text,"_")==0) return 1; - for (i=0; ivariant_count; ++i) { - found=0; - for (arm=n->children; arm; arm=arm->next) - if (arm->text && strcmp(arm->text,t->variants[i].name)==0) { - found=1; - break; - } - if (!found) return 0; - } - return 1; -} - -static int match_definitely_returns(FeNode *n) -{ - FeNode *arm; - if (!match_is_exhaustive(n)) return 0; - for (arm=n->children; arm; arm=arm->next) - if (!stmt_definitely_returns(arm->a)) return 0; - return 1; -} - -static int stmt_definitely_returns(FeNode *n) -{ - FeNode *last; - if (!n) return 0; - if (n->kind==FE_N_RETURN) return 1; - if (n->kind==FE_N_MATCH) return match_definitely_returns(n); - if (n->kind==FE_N_BLOCK) { - last=n->children; - if (!last) return 0; - while (last->next) last=last->next; - return stmt_definitely_returns(last); - } - if (n->kind==FE_N_IF) - return n->b && n->c && stmt_definitely_returns(n->b) && - stmt_definitely_returns(n->c); - return 0; -} - -static int hex_value(int c) -{ - if (c>='0' && c<='9') return c-'0'; - if (c>='a' && c<='f') return c-'a'+10; - if (c>='A' && c<='F') return c-'A'+10; - return -1; -} - -static void emit_byte(FILE *out, unsigned value) -{ - fprintf(out,"\\%03o",value & 255U); -} - -static void emit_codepoint(FILE *out, unsigned long cp) -{ - if (cp<=0x7fUL) emit_byte(out,(unsigned)cp); - else if (cp<=0x7ffUL) { - emit_byte(out,(unsigned)(0xc0UL | (cp>>6))); - emit_byte(out,(unsigned)(0x80UL | (cp&0x3fUL))); - } else if (cp<=0xffffUL) { - emit_byte(out,(unsigned)(0xe0UL | (cp>>12))); - emit_byte(out,(unsigned)(0x80UL | ((cp>>6)&0x3fUL))); - emit_byte(out,(unsigned)(0x80UL | (cp&0x3fUL))); - } else { - emit_byte(out,(unsigned)(0xf0UL | (cp>>18))); - emit_byte(out,(unsigned)(0x80UL | ((cp>>12)&0x3fUL))); - emit_byte(out,(unsigned)(0x80UL | ((cp>>6)&0x3fUL))); - emit_byte(out,(unsigned)(0x80UL | (cp&0x3fUL))); - } -} - -static void emit_c_literal(FILE *out, const char *text, int string) -{ - unsigned long i; - unsigned long cp; - int h0,h1,h2,h3; - int c; - char quote=string ? '"' : '\''; - if (!text) { fputs(string ? "\"\"" : "'\\000'",out); return; } - fputc(quote,out); - for(i=1;text[i] && text[i]!=quote;i++) { - c=(unsigned char)text[i]; - if(c!='\\') { - if(c==quote || c=='\\') fputc('\\',out); - fputc(c,out); - continue; - } - ++i; c=(unsigned char)text[i]; - if(c=='u' && text[i+1] && text[i+2] && text[i+3] && text[i+4]) { - h0=hex_value(text[i+1]); h1=hex_value(text[i+2]); - h2=hex_value(text[i+3]); h3=hex_value(text[i+4]); - if(h0>=0 && h1>=0 && h2>=0 && h3>=0) { - cp=(unsigned long)((h0<<12)|(h1<<8)|(h2<<4)|h3); - emit_codepoint(out,cp); i+=4; continue; - } - } - if(c=='x' && text[i+1] && text[i+2]) { - h0=hex_value(text[i+1]); h1=hex_value(text[i+2]); - if(h0>=0 && h1>=0) { emit_byte(out,(unsigned)((h0<<4)|h1)); i+=2; continue; } - } - if(c=='n') emit_byte(out,10U); - else if(c=='r') emit_byte(out,13U); - else if(c=='t') emit_byte(out,9U); - else if(c=='0') emit_byte(out,0U); - else emit_byte(out,(unsigned char)c); - } - fputc(quote,out); -} - -static void emit_m4_piece(FILE *out, const char *fmt, unsigned long begin, - unsigned long end) -{ - unsigned long i; - unsigned long cp; - int h0,h1,h2,h3; - int c; - fputc('"',out); - for(i=begin;i=0 && - (h1=hex_value((unsigned char)fmt[i+2]))>=0) { - emit_byte(out,(unsigned)((h0<<4)|h1)); i+=2; - } else if(c=='u' && i+4=0 && - (h1=hex_value((unsigned char)fmt[i+2]))>=0 && - (h2=hex_value((unsigned char)fmt[i+3]))>=0 && - (h3=hex_value((unsigned char)fmt[i+4]))>=0) { - cp=(unsigned long)((h0<<12)|(h1<<8)|(h2<<4)|h3); - emit_codepoint(out,cp); i+=4; - } - else if(c=='\\' || c=='"') { fputc('\\',out); fputc(c,out); } - else emit_byte(out,(unsigned)c); - continue; - } - if(c=='"' || c=='\\') fputc('\\',out); - fputc(c,out); - } - fputc('"',out); -} - -static void emit_m4_writer(FeEmitter *e, FeNode *arg, int buffer) -{ - (void)buffer; - if (arg && arg->kind==FE_N_UNARY && arg->text && - (strcmp(arg->text,"&")==0 || strcmp(arg->text,"&mut")==0)) { - emit_expr(e,arg->a); - } else if (arg && arg->kind==FE_N_CALL && arg->a && - arg->a->kind==FE_N_MEMBER) { - emit_expr(e,arg); - } else if (arg && arg->sem_type && arg->sem_type->kind==FE_TYPE_REF) { - fputs("(*",e->out); emit_expr(e,arg); fputc(')',e->out); - } else { - emit_expr(e,arg); - } -} - -static void emit_m4_writer_value(FeEmitter *e, FeNode *writer, int buffer) -{ - if (!writer) fputs("fe_m4_stdout_writer()",e->out); - else if (buffer) fputs("fe_m4_sprint_writer()",e->out); - else emit_m4_writer(e,writer,buffer); -} - -static void emit_m4_arg(FeEmitter *e, FeNode *arg, int verb, - FeNode *writer, int buffer, int error_value) -{ - FeType *t=arg ? arg->sem_type : 0; - if (verb=='x') { - fputs("fe_m4_write_hex(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (unsigned long)",e->out); emit_expr(e,arg); fputc(')',e->out); return; - } - if (verb=='c') { - fputs("fe_m4_write_char(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (unsigned char)",e->out); emit_expr(e,arg); fputc(')',e->out); return; - } - if (verb=='b') { - fputs("fe_m4_write_bool(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", ",e->out); emit_expr(e,arg); fputc(')',e->out); return; - } - if (verb=='s' || (verb==' ' && t && t->kind==FE_TYPE_SLICE)) { - fputs("fe_m4_write_slice(",e->out); - emit_m4_writer_value(e,writer,buffer); fputs(", ",e->out); emit_expr(e,arg); fputc(')',e->out); return; - } - if (error_value) { - fputs("fe_m4_write_error(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (unsigned long)",e->out); emit_expr(e,arg); fputs(".tag)",e->out); return; - } - if (verb==' ' && t && t->kind==FE_TYPE_BOOL) { - fputs("fe_m4_write_bool(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", ",e->out); emit_expr(e,arg); fputc(')',e->out); return; - } - if (verb==' ' && t && t->kind==FE_TYPE_CHAR) { - fputs("fe_m4_write_char(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (unsigned char)",e->out); emit_expr(e,arg); fputc(')',e->out); return; - } - fputs("fe_m4_write_int(",e->out); emit_m4_writer_value(e,writer,buffer); fputs(", (long)",e->out); emit_expr(e,arg); fputc(')',e->out); -} - -static void emit_m4_builtin(FeEmitter *e, FeNode *n) -{ - const char *fmt=n->aux_text; - FeNode *fmt_node=n->children; - FeNode *arg; - FeNode *writer_arg=0; - FeNode *buffer_arg=0; - unsigned long i,j,last=1; - unsigned count=0; - int verb; - int error_value; - int first=1; - int is_print=strcmp(n->text,"@print")==0; - int is_sprint=strcmp(n->text,"@sprint")==0; - if (!fmt) { fputs("0",e->out); return; } - if (is_print) writer_arg=0; - else if (is_sprint) { buffer_arg=fmt_node; fmt_node=fmt_node ? fmt_node->next : 0; } - else { writer_arg=fmt_node; fmt_node=fmt_node ? fmt_node->next : 0; } - arg=fmt_node ? fmt_node->next : 0; - error_value=0; - fputc('(',e->out); - if (!is_print && !is_sprint) { - fputs("fe_m4_error=0",e->out); - first=0; - } - if (is_sprint) { - fputs("fe_m4_sprint_begin((fe_m4_slice*)&",e->out); emit_expr(e,buffer_arg); fputs(")",e->out); - first=0; - } - i=1; - while(fmt[i] && fmt[i]!='"') { - if(fmt[i]=='\\') { ++i; if(fmt[i]) ++i; continue; } - if(fmt[i]=='{' && fmt[i+1]=='{') { i+=2; continue; } - if(fmt[i]=='}' && fmt[i+1]=='}') { i+=2; continue; } - if(fmt[i]=='{') { - j=i+1; while(fmt[j] && fmt[j]!='}') ++j; - if(!fmt[j]) break; - if(i>last) { - if(!first) fputs(", ",e->out); - if (!is_print && !is_sprint) - fputs("fe_m4_error ? fe_m4_error : (fe_m4_error = ",e->out); - if(is_print) { fputs("fe_m4_write_cstr(fe_m4_stdout_writer(), ",e->out); emit_m4_piece(e->out,fmt,last,i); fputc(')',e->out); } - else { fputs("fe_m4_write_cstr(",e->out); if(is_sprint) emit_m4_writer_value(e,buffer_arg,1); else emit_m4_writer(e,writer_arg,0); fputs(", ",e->out); emit_m4_piece(e->out,fmt,last,i); fputc(')',e->out); } - if (!is_print && !is_sprint) fputc(')',e->out); - first=0; - } - verb=(j==i+1) ? ' ' : (j==i+2 ? (unsigned char)fmt[i+1] : '?'); - if(arg) { - error_value=arg->sem_type && arg->sem_type->kind==FE_TYPE_ENUM && - arg->sem_type->is_error; - if(!first) fputs(", ",e->out); - if (!is_print && !is_sprint) - fputs("fe_m4_error ? fe_m4_error : (fe_m4_error = ",e->out); - if(is_print) emit_m4_arg(e,arg,verb,0,0,error_value); - else { - /* The writer expression is repeated intentionally; it is - a value wrapper and does not re-evaluate source args. */ - emit_m4_arg(e,arg,verb, is_sprint ? buffer_arg : writer_arg, - is_sprint,error_value); - } - if (!is_print && !is_sprint) fputc(')',e->out); - first=0; arg=arg->next; ++count; - } - last=j+1; i=j+1; continue; - } - ++i; - } - if(fmt[i]=='"' && i>last) { - if(!first) fputs(", ",e->out); - if (!is_print && !is_sprint) - fputs("fe_m4_error ? fe_m4_error : (fe_m4_error = ",e->out); - if(is_print) { fputs("fe_m4_write_cstr(fe_m4_stdout_writer(), ",e->out); emit_m4_piece(e->out,fmt,last,i); fputc(')',e->out); } - else { fputs("fe_m4_write_cstr(",e->out); if(is_sprint) emit_m4_writer_value(e,buffer_arg,1); else emit_m4_writer(e,writer_arg,0); fputs(", ",e->out); emit_m4_piece(e->out,fmt,last,i); fputc(')',e->out); } - if (!is_print && !is_sprint) fputc(')',e->out); - first=0; - } - if(first) fputs("0",e->out); - if(is_print) fputs(", (void)0",e->out); - else if(is_sprint) { fputs(", fe_m4_sprint_finish()",e->out); } - fputc(')',e->out); - (void)count; -} - - -static void emit_destroy_expr(FeEmitter *e, FeNode *n) -{ - fputs("(free(",e->out); emit_expr(e,n); - if(n && n->sem_type && n->sem_type->kind==FE_TYPE_OWNED && - n->sem_type->elem && n->sem_type->elem->kind==FE_TYPE_SLICE) - fputs(".p",e->out); - fputs(")",e->out); - if (n && n->kind==FE_N_IDENT) { - fputs(", ",e->out); emit_lvalue(e,n); - if(n->sem_type && n->sem_type->elem && - n->sem_type->elem->kind==FE_TYPE_SLICE) fputs(".p=0",e->out); - else fputs("=0",e->out); - fputs(", fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs("=0",e->out); - } else { - fputs(", ",e->out); emit_lvalue(e,n); fputs("=0",e->out); - } - fputs(", 0)",e->out); -} - -static FeNode *init_field(FeNode *n, const char *name) -{ - FeNode *f; - for(f=n ? n->children : 0;f;f=f->next) - if(f->kind==FE_N_FIELD && f->text && name && strcmp(f->text,name)==0) return f; - return 0; -} - -static void emit_slice_call(FeEmitter *e, FeNode *n) -{ - FeType *bt=n->a ? n->a->sem_type : 0; - const char *maker=n->sem_type && n->sem_type->maker ? n->sem_type->maker : - (bt && bt->slicer ? bt->slicer : "fe_missing_slice"); - if (bt && (bt->kind==FE_TYPE_ARRAY || bt->kind==FE_TYPE_SLICE)) { - fputs(n->sem_type && n->sem_type->slicer ? - n->sem_type->slicer : "fe_missing_slicer",e->out); - fputc('(',e->out); fputs(maker,e->out); fputc('(',e->out); - if (bt->kind==FE_TYPE_ARRAY) { - fputs("(&",e->out); emit_lvalue(e,n->a); fputs(")->a",e->out); - } else { - emit_expr(e,n->a); fputs(".p",e->out); - } - fputs(", ",e->out); - if(bt->kind==FE_TYPE_ARRAY) fprintf(e->out,"%lu",bt->length); - else { emit_expr(e,n->a); fputs(".n",e->out); } - fputs("), ",e->out); - if(n->b) emit_expr(e,n->b); else fputs("0",e->out); - fputs(", ",e->out); - if(n->c) emit_expr(e,n->c); - else if(bt->kind==FE_TYPE_ARRAY) fprintf(e->out,"%lu",bt->length); - else { emit_expr(e,n->a); fputs(".n",e->out); } - fputc(')',e->out); - return; - } - if (!n->b && !n->c && bt && bt->full_slicer) { - fputs(bt->full_slicer,e->out); - if (bt->kind==FE_TYPE_ARRAY) { fputs("(&",e->out); emit_lvalue(e,n->a); } - else { fputc('(',e->out); emit_expr(e,n->a); } - fputc(')',e->out); return; - } - if (!n->c && bt && bt->tail_slicer) { - fputs(bt->tail_slicer,e->out); - if (bt->kind==FE_TYPE_ARRAY) { fputs("(&",e->out); emit_lvalue(e,n->a); } - else { fputc('(',e->out); emit_expr(e,n->a); } - fputs(", ",e->out); - if (n->b) emit_expr(e,n->b); else fputs("0",e->out); - fputc(')',e->out); return; - } - fputs(maker,e->out); - if (bt && bt->kind==FE_TYPE_ARRAY) { fputs("(&",e->out); emit_lvalue(e,n->a); } - else { fputc('(',e->out); emit_expr(e,n->a); } - fputs(", ",e->out); - if(n->b) emit_expr(e,n->b); else fputs("0",e->out); - fputs(", ",e->out); - if(n->c) emit_expr(e,n->c); - else if(bt && bt->kind==FE_TYPE_ARRAY) fprintf(e->out,"%lu",bt->length); - else fputs("((unsigned long)",e->out), emit_expr(e,n->a), fputs(".n)",e->out); - fputc(')',e->out); -} - -static void emit_slice(FeEmitter *e, FeNode *n) -{ - emit_slice_call(e,n); -} - -static void emit_expr_core(FeEmitter *e, FeNode *n) -{ - FeNode *x; - const char *op; - if (!n) { - fputs("0", e->out); - return; - } - switch (n->kind) { - case FE_N_IDENT: - if((n->flags & 0x100U) && n->sem_type && - type_needs_drop(n->sem_type)) { - fputs("(fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs("=0, ",e->out); fputs(cname(n,"fe_missing"),e->out); - fputc(')',e->out); - } else fputs(cname(n, "fe_missing"), e->out); - break; - case FE_N_LITERAL: - if (n->text && strcmp(n->text, "true") == 0) fputs("1", e->out); - else if (n->text && strcmp(n->text, "false") == 0) fputs("0", e->out); - else if (n->text && n->text[0]=='"') { - fputs(n->sem_type && n->sem_type->maker ? - n->sem_type->maker : "fe_missing_str",e->out); - fputs("((const unsigned char*)",e->out); - emit_c_literal(e->out,n->text,1); fputs(", sizeof(",e->out); - emit_c_literal(e->out,n->text,1); fputs(")-1)",e->out); - } - else if (n->text && n->text[0]=='\'') emit_c_literal(e->out,n->text,0); - else fputs(n->text ? n->text : "0", e->out); - break; - case FE_N_STRUCT_INIT: { - FeVariantType *v; - FeNode *f; - unsigned i; - if(n->sem_type && n->a && n->a->kind==FE_N_MEMBER) { - v=fe_type_variant(n->sem_type,n->a->b ? n->a->b->text : ""); - if(v) { fputs(v->maker,e->out); fputc('(',e->out); for(i=0;ifield_count;i++){f=init_field(n,v->fields[i].name);if(i)fputs(", ",e->out);if(f)emit_expr(e,f->a);else fputs("0",e->out);} fputc(')',e->out); } - else fputs("0",e->out); - } else if(n->sem_type && n->sem_type->maker) { - fputs(n->sem_type->maker,e->out); fputc('(',e->out); - if(n->sem_type->kind==FE_TYPE_STRUCT) { for(i=0;isem_type->field_count;i++){f=init_field(n,n->sem_type->fields[i].name);if(i)fputs(", ",e->out);if(f)emit_expr(e,f->a);else fputs("0",e->out);} } - fputc(')',e->out); - } else fputs("0",e->out); - break; - } - case FE_N_ARRAY_INIT: { - int first=1; - if(n->sem_type && n->sem_type->maker) { fputs(n->sem_type->maker,e->out); fputc('(',e->out); for(x=n->children;x;x=x->next){if(!first)fputs(", ",e->out);emit_expr(e,x);first=0;} fputc(')',e->out); } else fputs("0",e->out); - break; - } - case FE_N_INDEX: { - FeType *bt; - bt=n->a ? n->a->sem_type : 0; - if(n->c || !n->b) { - emit_slice(e,n); - } else { - if (bt && bt->indexer) { - fputs(bt->indexer,e->out); fputc('(',e->out); - emit_expr(e,n->a); fputs(", ",e->out); emit_expr(e,n->b); fputc(')',e->out); - } else fputs("0",e->out); - } - break; - } - case FE_N_UNARY: - op = n->text ? n->text : ""; - if (strcmp(op, "try") == 0) { - if (n->a && n->a->sem_type && - n->a->sem_type->kind==FE_TYPE_ERROR_UNION && - n->a->sem_type->error_value && - n->a->sem_type->error_value->kind!=FE_TYPE_VOID) { - fputc('(',e->out); emit_expr(e,n->a); fputs(").v",e->out); - } else emit_expr(e,n->a); - break; - } - if (strcmp(op, "not") == 0) fputs("(!", e->out); - else { - fputc('(', e->out); - fputs(strcmp(op,"&mut")==0 ? "&" : op, e->out); - } - /* Borrowing needs a place, not a value. emit_expr lowers an index to - the bounds-checking accessor, and the address of that call is not an - lvalue -- `&s[0]` became `&fe_idx_slice_type_2(s, 0)`, which C - rejects. emit_lvalue spells the same element as `s.p[0]`. */ - if (strcmp(op,"&")==0 || strcmp(op,"&mut")==0) emit_lvalue(e, n->a); - else emit_expr(e, n->a); - fputc(')', e->out); - break; - case FE_N_BINARY: - op = n->text ? n->text : "+"; - fputc('(', e->out); - emit_expr(e, n->a); - if (strcmp(op, "and") == 0) fputs(" && ", e->out); - else if (strcmp(op, "or") == 0) fputs(" || ", e->out); - else fputs(op, e->out); - emit_expr(e, n->b); - fputc(')', e->out); - break; - case FE_N_TYPE: - if (n->text && strcmp(n->text, "as") == 0) { - fputs("((", e->out); - fputs(ctype(e, n->b), e->out); - fputc(')', e->out); - emit_expr(e, n->a); - fputc(')', e->out); - } else emit_expr(e, n->a); - break; - case FE_N_CALL: { - FeVariantType *v; - FeNode *call_param=0; - int special=0; - if(n->text && (strcmp(n->text,"@print")==0 || strcmp(n->text,"@fprint")==0 || strcmp(n->text,"@sprint")==0)) { emit_m4_builtin(e,n); special=1; } - else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && - n->a->b->text && strcmp(n->a->b->text,"destroy")==0 && - n->children) { - emit_destroy_expr(e,n->children); - special=1; - } - else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && - n->a->b->text && strcmp(n->a->b->text,"create")==0 && - n->children) { - FeType *created=n->children->sem_type; - FeType *owned=fe_type_owned(&e->check->types,created); - FeType *result=fe_type_error_union(&e->check->types,owned); - if (result->alloc_cname) fputs(result->alloc_cname,e->out); - else fputs("fe_bad_alloc",e->out); - fputc('(',e->out); emit_expr(e,n->children); fputc(')',e->out); - special=1; - } - else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && - n->a->b->text && strcmp(n->a->b->text,"alloc_slice")==0 && - n->children && n->children->next) { - FeType *result=n->sem_type; - if(result && result->alloc_cname) fputs(result->alloc_cname,e->out); - else fputs("fe_bad_slice_alloc",e->out); - fputc('(',e->out); emit_expr(e,n->children->next); fputc(')',e->out); - special=1; - } - else if(n->a && n->a->kind==FE_N_MEMBER && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"trim")==0 && !n->children && - n->a->a && n->a->a->sem_type && - n->a->a->sem_type->kind==FE_TYPE_SLICE && - n->a->a->sem_type->cname) { - fputs("fe_trim_",e->out); fputs(n->a->a->sem_type->cname,e->out); - fputc('(',e->out); emit_expr(e,n->a->a); fputc(')',e->out); - special=1; - } - else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && - n->a->b->text && strcmp(n->a->b->text,"replace")==0 && - n->children && n->children->next && n->sem_type) { - fputs(n->sem_type->replace_cname ? n->sem_type->replace_cname : - "fe_bad_replace",e->out); - fputc('(',e->out); emit_expr(e,n->children); fputs(", ",e->out); - emit_expr(e,n->children->next); fputc(')',e->out); - special=1; - } - else if(n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"null_writer")==0) { fputs("fe_m4_null_writer()",e->out); special=1; } - else if(n->a && n->a->kind==FE_N_MEMBER && n->sem_decl && - n->sem_decl->kind==FE_N_FN) { - FeNode *mp=n->sem_decl->a ? n->sem_decl->a->children : 0; - FeNode *ma; - fputs(cname(n->sem_decl,"fe_method"),e->out); fputc('(',e->out); - if(mp && mp->sem_type && mp->sem_type->kind==FE_TYPE_REF) { - fputc('&',e->out); emit_lvalue(e,n->a->a); - } else emit_expr(e,n->a->a); - for(ma=n->children; ma; ma=ma->next) { - fputs(", ",e->out); emit_expr(e,ma); - } - fputc(')',e->out); - special=1; - } - else if(!n->a && n->text && strcmp(n->text,"@size_of")==0 && n->children && n->children->kind==FE_N_IDENT) { fprintf(e->out,"%lu",fe_type_size(fe_type_intern(&e->check->types,n->children->text))); special=1; } - else if(!n->a && n->text && strcmp(n->text,"@align_of")==0 && n->children && n->children->kind==FE_N_IDENT) { fprintf(e->out,"%u",fe_type_align(fe_type_intern(&e->check->types,n->children->text))); special=1; } - else if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && n->a->a->sem_type && n->a->a->sem_type->kind==FE_TYPE_ENUM) { - v=fe_type_variant(n->a->a->sem_type,n->a->b ? n->a->b->text : ""); - if(v) fputs(v->maker,e->out); else fputs("fe_bad_variant",e->out); - } else if (n->a) emit_expr(e, n->a); - else fputs(n->text ? n->text : "fe_builtin", e->out); - if(!special) { - if(n->sem_decl && n->sem_decl->kind==FE_N_FN && n->sem_decl->a) - call_param=n->sem_decl->a->children; - fputc('(', e->out); - for (x = n->children; x; x = x->next) { - FeType *want=call_param && call_param->a ? - fe_type_from_ast(&e->check->types,call_param->a) : 0; - if (x != n->children) fputs(", ", e->out); - if(want && want->kind==FE_TYPE_SLICE && !want->ref_mut && - x->sem_type && x->sem_type->kind==FE_TYPE_SLICE && - x->sem_type->ref_mut) { - fputs(want->maker,e->out); fputc('(',e->out); - emit_expr(e,x); fputs(".p, ",e->out); - emit_expr(e,x); fputs(".n)",e->out); - } else if ((x->flags & 0x100U) && x->kind==FE_N_IDENT && - x->sem_type && x->sem_type->kind==FE_TYPE_OWNED) { - fputs("(fe_live_",e->out); fputs(cname(x,"owned"),e->out); - fputs("=0, ",e->out); emit_expr(e,x); fputc(')',e->out); - } else emit_expr(e, x); - if(call_param) call_param=call_param->next; - } - fputc(')', e->out); - } - break; - } - case FE_N_MEMBER: { - FeVariantType *v; - if(n->a && n->a->kind==FE_N_IDENT && n->a->text && - strcmp(n->a->text,"io")==0 && n->b && n->b->text && - (strcmp(n->b->text,"stdout")==0 || strcmp(n->b->text,"stderr")==0)) - fputs(strcmp(n->b->text,"stderr")==0 ? - "fe_m4_stderr_writer()" : "fe_m4_stdout_writer()",e->out); - else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_REF && - n->b && n->b->text && strcmp(n->b->text,"^")==0) { - fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out); - } else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OWNED && - n->b && n->b->text && strcmp(n->b->text,"^")==0) { - fputs("(*",e->out); emit_expr(e,n->a); fputs(")",e->out); - } else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_ENUM) { v=fe_type_variant(n->a->sem_type,n->b ? n->b->text : ""); if(v) fputs(v->maker,e->out); else fputs("0",e->out); if(v)fputs("()",e->out); } - else if(n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_REF) { - emit_expr(e,n->a); fputs("->",e->out); - if(n->b) fputs(n->b->text ? n->b->text : "member",e->out); - } else { emit_expr(e, n->a); fputc('.', e->out); if (n->b) fputs(n->b->text ? n->b->text : "member", e->out); } - break; - } - default: - fputs("0", e->out); - break; - } -} - - - - - - - - - - -static void emit_match(FeEmitter *e, FeNode *n, int value_context) -{ - FeNode *arm; - FeType *t=n->a ? n->a->sem_type : 0; - FeVariantType *v; - FeNode *b; - unsigned i; - char temp[32]; - sprintf(temp,"fe_match_%u",e->temp_serial++); - pad(e); fputs("{\n",e->out); ++e->indent; - pad(e); fputs(fe_type_c_name(t,e->pointer_bits),e->out); fputc(' ',e->out); - fputs(temp,e->out); fputs(" = ",e->out); emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("switch (",e->out); fputs(temp,e->out); fputs(".tag) {\n",e->out); ++e->indent; - for(arm=n->children;arm;arm=arm->next) { - if(arm->text && strcmp(arm->text,"_")==0) { pad(e); fputs("default: ",e->out); } - else { v=t && t->kind==FE_TYPE_ENUM ? fe_type_variant(t,arm->text) : 0; if(!v) continue; fprintf(e->out,"case %u: ",v->tag); } - fputs("{\n",e->out); ++e->indent; - v=t && t->kind==FE_TYPE_ENUM ? fe_type_variant(t,arm->text) : 0; - if(v) for(i=0,b=arm->children;ifield_count && b;i++,b=b->next) { - pad(e); fputs(fe_type_c_name(v->fields[i].type,e->pointer_bits),e->out); fputc(' ',e->out); fputs(cname(b,"fe_match"),e->out); fputs(" = ",e->out); fputs(temp,e->out); fputs(".payload.",e->out); fputs(v->name,e->out); if(v->field_count>1){fputc('.',e->out);fputs(v->fields[i].name,e->out);} fputs(";\n",e->out); - } - if(arm->a && arm->a->kind==FE_N_BLOCK) emit_stmt(e,arm->a); else { pad(e); emit_expr(e,arm->a); fputs(";\n",e->out); } - pad(e); fputs("break;\n",e->out); --e->indent; pad(e); fputs("}\n",e->out); - } - --e->indent; pad(e); fputs("}\n",e->out); - --e->indent; pad(e); fputs("}\n",e->out); - if (value_context || match_definitely_returns(n)) { - pad(e); fputs("fe_trap_bounds();\n",e->out); - pad(e); fputs("return 0;\n",e->out); - } -} - -static int try_has_value(FeNode *n) -{ - return n && n->kind==FE_N_UNARY && n->text && strcmp(n->text,"try")==0 && - n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_ERROR_UNION && - n->a->sem_type->error_value && n->a->sem_type->error_value->kind!=FE_TYPE_VOID; -} - -static void emit_try_statement(FeEmitter *e, FeNode *try_node, FeNode *target) -{ - char temp[40]; - char error[48]; - FeType *result=try_node->a->sem_type; - sprintf(temp,"fe_try_%u",e->temp_serial++); - sprintf(error,"%s.e",temp); - pad(e); fputs("{ ",e->out); fputs(fe_type_c_name(result,e->pointer_bits),e->out); - fputc(' ',e->out); fputs(temp,e->out); fputs(" = ",e->out); - emit_expr(e,try_node->a); fputs("; if (",e->out); fputs(temp,e->out); - fputs(".e) {\n",e->out); ++e->indent; - emit_cleanup_all(e); - pad(e); emit_error_return(e,error); - --e->indent; pad(e); fputs("} ",e->out); - if (target) { - if (target->kind==FE_N_LET || target->kind==FE_N_VAR) - fputs(cname(target,"fe_local"),e->out); - else - emit_lvalue(e,target); - fputs(" = ",e->out); fputs(temp,e->out); fputs(".v;\n",e->out); - } - fputs("}\n",e->out); -} - -static void emit_stmt_core(FeEmitter *e, FeNode *n) -{ - if (!n) return; - switch (n->kind) { - case FE_N_BLOCK: - emit_block(e, n); - fputc('\n', e->out); - break; - case FE_N_LET: - case FE_N_VAR: - if (n->b) { - if (try_has_value(n->b)) emit_try_statement(e,n->b,n); - else { - pad(e); - fputs(cname(n, "fe_local"), e->out); - fputs(" = ", e->out); - emit_expr(e, n->b); - fputs(";\n", e->out); - } - emit_owned_live(e,n,1); - } - break; - case FE_N_ASSIGN: - if (n->a && n->a->kind==FE_N_IDENT && n->a->sem_type && - n->a->sem_type->kind==FE_TYPE_OWNED) { - pad(e); fputs("if (fe_live_",e->out); fputs(cname(n->a,"owned"),e->out); - fputs(") { ",e->out); - if(n->a->sem_type->elem && - n->a->sem_type->elem->kind==FE_TYPE_SLICE) { - fputs("free(",e->out); fputs(cname(n->a,"owned"),e->out); - fputs(".p); ",e->out); - } else if (n->a->sem_type->elem && type_needs_drop(n->a->sem_type->elem) && - n->a->sem_type->elem->drop_cname) - fprintf(e->out,"%s(%s); ",n->a->sem_type->elem->drop_cname,cname(n->a,"owned")); - if(!(n->a->sem_type->elem && - n->a->sem_type->elem->kind==FE_TYPE_SLICE)) { - fputs("free(",e->out); fputs(cname(n->a,"owned"),e->out); fputs("); ",e->out); - } - fputs("fe_live_",e->out); fputs(cname(n->a,"owned"),e->out); - fputs("=0; }\n",e->out); - } else if(n->a && n->a->kind==FE_N_IDENT && n->a->sem_type && - type_needs_drop(n->a->sem_type) && - n->a->sem_type->drop_cname) { - pad(e); fputs("if (fe_live_",e->out); fputs(cname(n->a,"local"),e->out); - fprintf(e->out,") { %s(&%s); fe_live_", - n->a->sem_type->drop_cname,cname(n->a,"local")); - fputs(cname(n->a,"local"),e->out); fputs("=0; }\n",e->out); - } - pad(e); - emit_lvalue(e, n->a); - fputc(' ', e->out); - fputs(n->text ? n->text : "=", e->out); - fputs(" ", e->out); - if (n->b && (n->b->flags & 0x100U) && n->b->kind==FE_N_IDENT && - n->b->sem_type && n->b->sem_type->kind==FE_TYPE_OWNED) { - fputs("(fe_live_",e->out); fputs(cname(n->b,"owned"),e->out); - fputs("=0, ",e->out); emit_expr(e,n->b); fputc(')',e->out); - } else emit_expr(e, n->b); - fputs(";\n", e->out); - if (n->a && n->a->kind==FE_N_IDENT) emit_owned_live(e,n->a,1); - break; - case FE_N_EXPR_STMT: - if (try_has_value(n->a)) { - emit_try_statement(e,n->a,0); - } else { - pad(e); - if (n->a && n->a->kind==FE_N_UNARY && n->a->text && - strcmp(n->a->text,"try")==0 && n->a->a) { - fputs("if ((fe_error_temp = ",e->out); - emit_expr(e,n->a->a); - fputs(") != 0) {\n",e->out); - ++e->indent; - emit_cleanup_all(e); - pad(e); fputs("return fe_error_temp;\n",e->out); - --e->indent; - pad(e); fputs("}\n",e->out); - } else { - emit_expr(e, n->a); - fputs(";\n", e->out); - } - } - break; - case FE_N_BREAK: - case FE_N_CONTINUE: - if (e->loop_depth) { - emit_cleanup_to(e,e->loop_floor[e->loop_depth-1]); - pad(e); fputs(n->kind==FE_N_BREAK ? "break;\n" : "continue;\n",e->out); - } - break; - case FE_N_RETURN: - if (n->a && n->a->kind == FE_N_MATCH) { - emit_match(e,n->a,1); - break; - } - /* Evaluate before cleanup: `return p.^` must not dereference p after - its owned cleanup has run. The block-local temporary is declared - before all statements to retain C89 declaration ordering. */ - if (n->a && e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - pad(e); fputs("fe_return_value = ",e->out); - if ((n->a->flags & 0x100U) && n->a->kind==FE_N_IDENT && - n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OWNED) { - fputs("(fe_live_",e->out); fputs(cname(n->a,"owned"),e->out); - fputs("=0, ",e->out); emit_expr(e,n->a); fputc(')',e->out); - } else emit_expr(e,n->a); - fputs(";\n",e->out); - } - emit_cleanup_all(e); - pad(e); fputs("return",e->out); - if (n->a) fputs(" fe_return_value",e->out); - fputs(";\n",e->out); - break; - case FE_N_IF: - pad(e); - fputs("if (", e->out); - emit_expr(e, n->a); - fputs(") ", e->out); - if (n->b && n->b->kind == FE_N_BLOCK) emit_block(e, n->b); - else emit_block(e, 0); - if (n->c) { - fputs(" else ", e->out); - if (n->c->kind == FE_N_IF) emit_stmt(e, n->c); - else emit_block(e, n->c); - } - fputc('\n', e->out); - break; - case FE_N_WHILE: - pad(e); - fputs("while (", e->out); - emit_expr(e, n->a); - fputs(") ", e->out); - if (e->loop_depth<16U) e->loop_floor[e->loop_depth++]=e->block_depth; - if (n->b && n->b->kind == FE_N_BLOCK) emit_block(e, n->b); - else emit_block(e, 0); - if (e->loop_depth) --e->loop_depth; - fputc('\n', e->out); - break; - case FE_N_FOR: - pad(e); fputs("{\n",e->out); ++e->indent; - if (e->loop_depth<16U) e->loop_floor[e->loop_depth++]=e->block_depth; - if (n->c) { - pad(e); fputs("unsigned long ",e->out); fputs(cname(n,"fe_index"),e->out); fputs(";\n",e->out); - pad(e); fputs(cname(n,"fe_index"),e->out); fputs(" = ",e->out); emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("for (; ",e->out); fputs(cname(n,"fe_index"),e->out); fputs(" < ",e->out); emit_expr(e,n->c); fputs("; ++",e->out); fputs(cname(n,"fe_index"),e->out); fputs(") ",e->out); emit_block(e,n->b); fputc('\n',e->out); - } else { - FeType *bt=n->a ? n->a->sem_type : 0; - FeType *et=bt ? bt->elem : 0; - char temp[32]; - int mutable_iter=(n->flags & 4U) != 0; - sprintf(temp,"fe_iter_%u",e->temp_serial++); - pad(e); fputs(fe_type_c_name(bt,e->pointer_bits),e->out); if (mutable_iter) fputs(" *",e->out); fputc(' ',e->out); fputs(temp,e->out); fputs(" = ",e->out); if (mutable_iter) fputc('&',e->out); emit_expr(e,n->a); fputs(";\n",e->out); - if (n->aux_text) { - pad(e); fputs("unsigned long ",e->out); fputs(cname(n,"fe_index"),e->out); fputs(";\n",e->out); - } else { - pad(e); fputs(fe_type_c_name(n->sem_type ? n->sem_type : fe_type_ref(&e->check->types,et,0),e->pointer_bits),e->out); fputc(' ',e->out); fputs(cname(n,"fe_item"),e->out); fputs(";\n",e->out); - } - pad(e); fputs("{ unsigned long fe_i; for (fe_i = 0; fe_i < ",e->out); - if(bt && bt->kind==FE_TYPE_ARRAY) fprintf(e->out,"%lu",bt->length); else { if(mutable_iter) fputs("(*",e->out); fputs(temp,e->out); if(mutable_iter) fputs(").n",e->out); else fputs(".n",e->out); } - fputs("; ++fe_i) { ",e->out); - if(n->aux_text) { - fputs(fe_type_c_name(fe_type_ref(&e->check->types,et,(n->flags & 4U) != 0),e->pointer_bits),e->out); fputc(' ',e->out); fputs(n->aux_cname ? n->aux_cname : "fe_item",e->out); fputs("; ",e->out); - fputs(cname(n,"fe_index"),e->out); fputs(" = fe_i; ",e->out); - fputs(n->aux_cname ? n->aux_cname : "fe_item",e->out); fputs(" = ",e->out); - } else { fputs(cname(n,"fe_item"),e->out); fputs(" = ",e->out); } - fputc('&',e->out); if(mutable_iter) fputs("(*",e->out); fputs(temp,e->out); if(mutable_iter) fputs(")",e->out); if(bt && bt->kind==FE_TYPE_ARRAY) fputs(".a[fe_i]",e->out); else fputs(".p[fe_i]",e->out); fputs("; ",e->out); - emit_block(e,n->b); fputs(" } }\n",e->out); - } - --e->indent; - if (e->loop_depth) --e->loop_depth; - pad(e); fputs("}\n",e->out); break; - case FE_N_MATCH: - emit_match(e,n,0); break; - default: - break; - } -} - - -static void emit_main_wrapper_core(FeEmitter *e, FeNode *fn) -{ - fputs("int main(void) {\n ", e->out); - if (fn->sem_type && fn->sem_type->kind == FE_TYPE_VOID) { - fputs(cname(fn, "fe_main"), e->out); - fputs("();\n return 0;\n", e->out); - } else { - fputs("return ", e->out); - fputs(cname(fn, "fe_main"), e->out); - fputs("();\n", e->out); - } - fputs("}\n", e->out); -} - -void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check, - unsigned pointer_bits, int no_checks) -{ - e->out = out; - e->check = check; - e->pointer_bits = pointer_bits; - e->indent = 0; - e->no_checks = no_checks; - e->temp_serial = 0; - e->fallthrough_block = 0; - e->block_depth = 0; - e->loop_depth = 0; - e->current_ret = 0; - e->current_fn = 0; -} - - -#include "m7.h" -#include "lower.h" - -static void emit_expr(FeEmitter *e, FeNode *n); -static void emit_stmt(FeEmitter *e, FeNode *n); -static void emit_block(FeEmitter *e, FeNode *n); - -static int type_needs_drop(FeType *t) -{ - return fe_lower_type_needs_drop(t); -} - -static const char *m7_c_type(FeEmitter *e, FeType *t) -{ - if (!t) return "long"; - if ((t->kind==FE_TYPE_ENUM && t->is_error) || - strcmp(t->name,"core.Error")==0) - return "unsigned short"; - return fe_type_c_name(t,e->pointer_bits); -} - -static char *m7_temp_name(FeEmitter *e) -{ - char number[24]; - char *p; - unsigned long len; - sprintf(number,"%u",e->temp_serial++); - len=(unsigned long)strlen("fe_m7_tmp_")+ - (unsigned long)strlen(number)+1UL; - p=(char *)fe_arena_alloc(&e->check->ast->arena,len); - if (!p) return 0; - strcpy(p,"fe_m7_tmp_"); - strcat(p,number); - return p; -} - -static int m7_needs_temp(FeNode *n) -{ - if (!n) return 0; - if (fe_m7_is_try(n)) return 1; - if (n->kind==FE_N_BINARY && fe_m7_lazy_kind(n)!=FE_M7_LAZY_NONE) - return 1; - if (n->kind==FE_N_IF && n->text && strcmp(n->text,"if let")==0) - return 1; - if (n->kind==FE_N_MATCH && n->a && n->a->sem_type && - n->a->sem_type->kind==FE_TYPE_OPTIONAL) - return 1; - return 0; -} - -static FeType *m7_temp_type(FeNode *n) -{ - if (!n) return 0; - if (fe_m7_is_try(n)) return n->a ? n->a->sem_type : 0; - if (n->kind==FE_N_BINARY) return n->a ? n->a->sem_type : 0; - if ((n->kind==FE_N_IF || n->kind==FE_N_MATCH) && n->a) - return n->a->sem_type; - return 0; -} - -static void m7_prepare_temps(FeEmitter *e, FeNode *n) -{ - FeNode *x; - if (!n) return; - if (m7_needs_temp(n) && !n->aux_cname) - n->aux_cname=m7_temp_name(e); - m7_prepare_temps(e,n->a); - m7_prepare_temps(e,n->b); - m7_prepare_temps(e,n->c); - for (x=n->children;x;x=x->next) m7_prepare_temps(e,x); -} - -static void m7_emit_temp_decls(FeEmitter *e, FeNode *n) -{ - FeNode *x; - FeType *t; - if (!n) return; - if (m7_needs_temp(n) && n->aux_cname) { - t=m7_temp_type(n); - if (t) { - pad(e); fputs(m7_c_type(e,t),e->out); fputc(' ',e->out); - fputs(n->aux_cname,e->out); fputs(";\n",e->out); - } - } - m7_emit_temp_decls(e,n->a); - m7_emit_temp_decls(e,n->b); - m7_emit_temp_decls(e,n->c); - for (x=n->children;x;x=x->next) m7_emit_temp_decls(e,x); -} - -static void m7_emit_type(FeEmitter *e, FeType *t) -{ - unsigned i; - unsigned j; - if (!t || t->emit_state) return; - if (t->kind==FE_TYPE_OPTIONAL) { - t->emit_state=1; - m7_emit_type(e,t->elem); - if (!fe_m7_optional_uses_niche(t->elem) && t->cname) { - fputs(t->cname,e->out); fputs(" { unsigned char has; ",e->out); - fputs(m7_c_type(e,t->elem),e->out); - fputs(" v; };\n",e->out); - } - t->emit_state=2; - return; - } - if (t->kind==FE_TYPE_ARRAY) m7_emit_type(e,t->elem); - if (t->kind==FE_TYPE_SLICE) m7_emit_type(e,t->elem); - if (t->kind==FE_TYPE_OWNED) m7_emit_type(e,t->elem); - if (t->kind==FE_TYPE_STRUCT) - for (i=0;ifield_count;++i) m7_emit_type(e,t->fields[i].type); - if (t->kind==FE_TYPE_ENUM) - for (i=0;ivariant_count;++i) - for (j=0;jvariants[i].field_count;++j) - m7_emit_type(e,t->variants[i].fields[j].type); - if (t->kind==FE_TYPE_ERROR_UNION) { - m7_emit_type(e,t->elem); - m7_emit_type(e,t->error_value); - } - emit_one_type(e,t); -} - -static void emit_type_defs(FeEmitter *e) -{ - FeType *t; - for (t=e->check->types.types;t;t=t->next) - if (t->kind==FE_TYPE_ARRAY) - fe_type_slice(&e->check->types,t->elem); - for (t=e->check->types.types;t;t=t->next) m7_emit_type(e,t); -} - -static void m7_emit_drop_access(FeEmitter *e, FeType *t, - const char *access) -{ - if (!t || !access || !type_needs_drop(t)) return; - if (t->kind==FE_TYPE_OWNED) { - if (t->elem && t->elem->kind==FE_TYPE_SLICE) { - fprintf(e->out,"if ((%s).p) { free((%s).p); (%s).p=0; } ", - access,access,access); - } else { - fprintf(e->out,"if (%s) { ",access); - if (t->elem && type_needs_drop(t->elem) && t->elem->drop_cname) - fprintf(e->out,"%s(%s); ",t->elem->drop_cname,access); - fprintf(e->out,"free(%s); %s=0; } ",access,access); - } - return; - } - if (t->drop_cname) - fprintf(e->out,"%s(&(%s)); ",t->drop_cname,access); -} - -static void m7_emit_drop_helpers(FeEmitter *e) -{ - FeType *t; - FeNode *method; - unsigned i; - char access[256]; - for (t=e->check->types.types;t;t=t->next) - if (type_needs_drop(t) && t->drop_cname && - (t->kind==FE_TYPE_STRUCT || t->kind==FE_TYPE_ARRAY || - t->kind==FE_TYPE_OPTIONAL || t->kind==FE_TYPE_ERROR_UNION)) - fprintf(e->out,"static void %s(%s *self);\n", - t->drop_cname,m7_c_type(e,t)); - for (t=e->check->types.types;t;t=t->next) { - if (!type_needs_drop(t) || !t->drop_cname) continue; - if (t->kind==FE_TYPE_STRUCT) { - fprintf(e->out,"static void %s(%s *self) { ", - t->drop_cname,m7_c_type(e,t)); - method=find_drop_method(e,t->name); - if (method) fprintf(e->out,"%s(self); ",cname(method,"fe_drop_method")); - for (i=t->field_count;i>0;--i) { - sprintf(access,"self->%s",t->fields[i-1U].name); - m7_emit_drop_access(e,t->fields[i-1U].type,access); - } - fputs("}\n",e->out); - } else if (t->kind==FE_TYPE_ARRAY) { - fprintf(e->out,"static void %s(%s *self) { unsigned long i; for (i=0; i<%lu; ++i) { ", - t->drop_cname,m7_c_type(e,t),t->length); - strcpy(access,"self->a[i]"); - m7_emit_drop_access(e,t->elem,access); - fputs("} }\n",e->out); - } else if (t->kind==FE_TYPE_OPTIONAL) { - fprintf(e->out,"static void %s(%s *self) { ", - t->drop_cname,m7_c_type(e,t)); - if (fe_m7_optional_uses_niche(t->elem)) { - fputs("if (*self) { ",e->out); - m7_emit_drop_access(e,t->elem,"*self"); - fputs("} ",e->out); - } else { - fputs("if (self->has) { ",e->out); - m7_emit_drop_access(e,t->elem,"self->v"); - fputs("self->has=0; } ",e->out); - } - fputs("}\n",e->out); - } else if (t->kind==FE_TYPE_ERROR_UNION && t->error_value && - t->error_value->kind!=FE_TYPE_VOID) { - fprintf(e->out,"static void %s(%s *self) { if (!self->e) { ", - t->drop_cname,m7_c_type(e,t)); - m7_emit_drop_access(e,t->error_value,"self->v"); - fputs("self->e=1; } }\n",e->out); - } - } - /* Error enums are scalar codes, so their enum payload helper functions - from M3 are deliberately not emitted in the M7 path. */ -} - -static void m7_emit_type_helpers(FeEmitter *e) -{ - FeType *t; - FeType *st; - unsigned i; - unsigned j; - const char *ct; - FeVariantType *v; - for (t=e->check->types.types;t;t=t->next) { - if (t->kind==FE_TYPE_OPTIONAL) { - if (!fe_m7_optional_uses_niche(t->elem)) { - fprintf(e->out,"static %s %s(%s v) { %s r; r.has=1; r.v=v; return r; }\n", - m7_c_type(e,t),t->maker,m7_c_type(e,t->elem),m7_c_type(e,t)); - fprintf(e->out,"static %s %s(void) { %s r; memset(&r,0,sizeof(r)); return r; }\n", - m7_c_type(e,t),t->none_cname,m7_c_type(e,t)); - fprintf(e->out,"static %s %s(%s x) { ", - m7_c_type(e,t->elem),t->unwrap_cname,m7_c_type(e,t)); - if (!e->no_checks) fputs("if (!x.has) fe_trap_bounds(); ",e->out); - fputs("return x.v; }\n",e->out); - } else { - fprintf(e->out,"static %s %s(%s x) { ", - m7_c_type(e,t->elem),t->unwrap_cname,m7_c_type(e,t)); - if (!e->no_checks) fputs("if (!x) fe_trap_bounds(); ",e->out); - fputs("return x; }\n",e->out); - } - } - if (t->kind==FE_TYPE_ERROR_UNION && t->error_value && - t->error_value->kind!=FE_TYPE_VOID) { - fprintf(e->out,"static %s %s(unsigned short e, %s v) { %s r; r.e=e; r.v=v; return r; }\n", - m7_c_type(e,t),t->maker,m7_c_type(e,t->error_value),m7_c_type(e,t)); - if (t->none_cname) - fprintf(e->out,"static %s %s(unsigned short e) { %s r; memset(&r,0,sizeof(r)); r.e=e; return r; }\n", - m7_c_type(e,t),t->none_cname,m7_c_type(e,t)); - if (t->error_value->kind==FE_TYPE_OWNED && - t->error_value->elem && t->error_value->elem->kind==FE_TYPE_SLICE) { - FeType *item=t->error_value->elem->elem; - fprintf(e->out,"static %s %s(unsigned long n) { %s r; r.v.p=(%s*)malloc(sizeof(%s)*n); r.v.n=n; r.e=(r.v.p || !n) ? 0 : 1; return r; }\n", - m7_c_type(e,t),t->alloc_cname,m7_c_type(e,t), - m7_c_type(e,item),m7_c_type(e,item)); - } else if (t->error_value->kind==FE_TYPE_OWNED) { - fprintf(e->out,"static %s %s(%s v) { %s r; r.v=(%s)malloc(sizeof(%s)); if(r.v) *r.v=v; r.e=r.v ? 0 : 1; return r; }\n", - m7_c_type(e,t),t->alloc_cname, - m7_c_type(e,t->error_value->elem),m7_c_type(e,t), - m7_c_type(e,t->error_value), - m7_c_type(e,t->error_value->elem)); - } - } - } - for (t=e->check->types.types;t;t=t->next) { - if (t->replace_cname) { - ct=m7_c_type(e,t); - fprintf(e->out,"static %s %s(%s *dst, %s value) { %s old=*dst; *dst=value; return old; }\n", - ct,t->replace_cname,ct,ct,ct); - } - if (t->kind==FE_TYPE_STRUCT && t->maker) { - fprintf(e->out,"static %s %s(",m7_c_type(e,t),t->maker); - for (i=0;ifield_count;i++) { - if (i) fputs(", ",e->out); - fputs(m7_c_type(e,t->fields[i].type),e->out); - fprintf(e->out," p%u",i); - } - fprintf(e->out,") { %s v;",m7_c_type(e,t)); - for (i=0;ifield_count;i++) - fprintf(e->out," v.%s=p%u;",t->fields[i].name,i); - fputs(" return v; }\n",e->out); - } else if (t->kind==FE_TYPE_ARRAY && t->maker) { - fprintf(e->out,"static %s %s(",m7_c_type(e,t),t->maker); - for (i=0;ilength;i++) { - if (i) fputs(", ",e->out); - fputs(m7_c_type(e,t->elem),e->out); - fprintf(e->out," p%u",i); - } - fprintf(e->out,") { %s v;",m7_c_type(e,t)); - for (i=0;ilength;i++) fprintf(e->out," v.a[%u]=p%u;",i,i); - fputs(" return v; }\n",e->out); - } else if (t->kind==FE_TYPE_ENUM && !t->is_error) { - for (i=0;ivariant_count;i++) { - v=&t->variants[i]; - fprintf(e->out,"static %s %s(",m7_c_type(e,t),v->maker); - for (j=0;jfield_count;j++) { - if (j) fputs(", ",e->out); - fputs(m7_c_type(e,v->fields[j].type),e->out); - fprintf(e->out," p%u",j); - } - fprintf(e->out,") { %s x; x.tag=%u;",m7_c_type(e,t),v->tag); - for (j=0;jfield_count;j++) { - if (v->field_count==1) - fprintf(e->out," x.payload.%s=p%u;",v->name,j); - else - fprintf(e->out," x.payload.%s.%s=p%u;",v->name, - v->fields[j].name,j); - } - fputs(" return x; }\n",e->out); - } - } - } - m7_emit_drop_helpers(e); - /* Reuse the mature M3 index/slice helper generator. It does not depend - on M7 drop policy and all wrapper dependencies are already emitted. */ - for (t=e->check->types.types;t;t=t->next) { - if (t->kind==FE_TYPE_ARRAY && t->indexer) { - fprintf(e->out,"static %s %s(%s x, unsigned long i) { ", - m7_c_type(e,t->elem),t->indexer,m7_c_type(e,t)); - if (!e->no_checks) - fprintf(e->out,"if (i >= %lu) fe_trap_bounds(); ",t->length); - fputs("return x.a[i]; }\n",e->out); - if (t->slicer) { - st=fe_type_slice(&e->check->types,t->elem); - fprintf(e->out,"static %s %s(%s *x, unsigned long a, unsigned long b) { ", - m7_c_type(e,st),t->slicer,m7_c_type(e,t)); - if (!e->no_checks) - fprintf(e->out,"if (a > b || b > %lu) fe_trap_bounds(); ",t->length); - fprintf(e->out,"return %s(x->a+a,b-a); }\n",st->maker); - fprintf(e->out,"static %s %s(%s *x) { return %s(x,0,%lu); }\n", - m7_c_type(e,st),t->full_slicer,m7_c_type(e,t),t->slicer,t->length); - fprintf(e->out,"static %s %s(%s *x, unsigned long a) { return %s(x,a,%lu); }\n", - m7_c_type(e,st),t->tail_slicer,m7_c_type(e,t),t->slicer,t->length); - } - } else if (t->kind==FE_TYPE_SLICE && t->indexer) { - fprintf(e->out,"static %s %s(%s x, unsigned long i) { ", - m7_c_type(e,t->elem),t->indexer,m7_c_type(e,t)); - if (!e->no_checks) fputs("if (i >= x.n) fe_trap_bounds(); ",e->out); - fputs("return x.p[i]; }\n",e->out); - if (t->slicer) { - fprintf(e->out,"static %s %s(%s x, unsigned long a, unsigned long b) { ", - m7_c_type(e,t),t->slicer,m7_c_type(e,t)); - if (!e->no_checks) - fputs("if (a > b || b > x.n) fe_trap_bounds(); ",e->out); - fprintf(e->out,"return %s(x.p+a,b-a); }\n",t->maker); - fprintf(e->out,"static %s %s(%s x) { return %s(x,0,x.n); }\n", - m7_c_type(e,t),t->full_slicer,m7_c_type(e,t),t->slicer); - fprintf(e->out,"static %s %s(%s x, unsigned long a) { return %s(x,a,x.n); }\n", - m7_c_type(e,t),t->tail_slicer,m7_c_type(e,t),t->slicer); - if (node_uses_trim(e->check->ast->root) && !t->ref_mut) { - fprintf(e->out, - "static %s fe_trim_%s(%s s) { unsigned long a=0; unsigned long b=s.n;" - " while (aa && (s.p[b-1]==' '||s.p[b-1]=='\\t'||s.p[b-1]=='\\r'||s.p[b-1]=='\\n')) --b;" - " return %s(s.p+a,b-a); }\n", - t->cname,t->cname,t->cname,t->maker); - } - } - } - } -} - -static void m7_emit_present(FeEmitter *e, FeType *opt, const char *name) -{ - if (fe_m7_optional_uses_niche(opt->elem)) { - fputs("(",e->out); fputs(name,e->out); fputs(" != 0)",e->out); - } else { - fputs(name,e->out); fputs(".has",e->out); - } -} - -static void m7_emit_payload_var(FeEmitter *e, FeType *opt, const char *name) -{ - (void)e; - fputs(name,e->out); - if (!fe_m7_optional_uses_niche(opt->elem)) fputs(".v",e->out); -} - -static void m7_emit_error_member(FeEmitter *e, FeNode *n) -{ - FeVariantType *v; - FeType *t; - t=n && n->a ? n->a->sem_type : 0; - v=t && t->kind==FE_TYPE_ENUM ? - fe_type_variant(t,n->b ? n->b->text : "") : 0; - if (v) fprintf(e->out,"%u",v->tag); - else fputs("0",e->out); -} - -static void m7_emit_raw_expr(FeEmitter *e, FeNode *n); - -static void m7_emit_contextual(FeEmitter *e, FeNode *n) -{ - FeType *ctx; - FeType *actual; - FeType *error_type; - ctx=n ? n->sem_context : 0; - actual=n ? n->sem_type : 0; - if (!ctx) { m7_emit_raw_expr(e,n); return; } - if (ctx->kind==FE_TYPE_OPTIONAL) { - if (fe_m7_is_null(n)) { - if (fe_m7_optional_uses_niche(ctx->elem)) fputs("0",e->out); - else { fputs(ctx->none_cname,e->out); fputs("()",e->out); } - return; - } - if (fe_m7_optional_uses_niche(ctx->elem)) { - m7_emit_raw_expr(e,n); - } else { - fputs(ctx->maker,e->out); fputc('(',e->out); - m7_emit_raw_expr(e,n); fputc(')',e->out); - } - return; - } - if (ctx->kind==FE_TYPE_ERROR_UNION) { - error_type=ctx->elem; - if (!error_type) error_type=fe_type_intern(&e->check->types,"core.Error"); - if (actual && fe_type_equal(actual,ctx->error_value)) { - if (ctx->error_value->kind==FE_TYPE_VOID) fputs("0",e->out); - else { - fputs(ctx->maker,e->out); fputs("(0, ",e->out); - m7_emit_raw_expr(e,n); fputc(')',e->out); - } - return; - } - if (actual && fe_type_equal(actual,error_type)) { - if (ctx->error_value->kind==FE_TYPE_VOID) - m7_emit_raw_expr(e,n); - else { - fputs(ctx->none_cname,e->out); fputc('(',e->out); - m7_emit_raw_expr(e,n); fputc(')',e->out); - } - return; - } - } - m7_emit_raw_expr(e,n); -} - -static void emit_expr(FeEmitter *e, FeNode *n) -{ - if (!n) { fputs("0",e->out); return; } - if (n->sem_context) m7_emit_contextual(e,n); - else m7_emit_raw_expr(e,n); -} - -static void emit_lvalue(FeEmitter *e, FeNode *n) -{ - FeType *bt; - if (!n) { fputs("fe_bad_lvalue",e->out); return; } - if (n->kind==FE_N_IDENT) { - fputs(cname(n,"fe_local"),e->out); - return; - } - /* A declaration names its own storage. The initializer for `let`/`var` is - emitted as a separate assignment statement, so the declaration node is - handed here as the target; without this it falls through to the raw - expression path, which emits a declaration as "0" and produces `0 = ...`. */ - if (n->kind==FE_N_LET || n->kind==FE_N_VAR || n->kind==FE_N_CONST) { - fputs(cname(n,"fe_local"),e->out); - return; - } - if (n->kind==FE_N_MEMBER) { - bt=n->a ? n->a->sem_type : 0; - if (n->text && strcmp(n->text,".?")==0) { - emit_expr(e,n); - return; - } - if ((bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) && - n->b && n->b->text && strcmp(n->b->text,"^")==0) { - fputs("(*",e->out); emit_expr(e,n->a); fputc(')',e->out); - } else if (bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) { - emit_expr(e,n->a); fputs("->",e->out); - fputs(n->b && n->b->text ? n->b->text : "member",e->out); - } else { - emit_lvalue(e,n->a); fputc('.',e->out); - fputs(n->b && n->b->text ? n->b->text : "member",e->out); - } - return; - } - if (n->kind==FE_N_INDEX) { - bt=n->a ? n->a->sem_type : 0; - emit_lvalue(e,n->a); - fputs(bt && bt->kind==FE_TYPE_ARRAY ? ".a[" : ".p[",e->out); - emit_expr(e,n->b); fputc(']',e->out); - return; - } - m7_emit_raw_expr(e,n); -} - -static void m7_emit_call(FeEmitter *e, FeNode *n) -{ - FeNode *x; - FeNode *call_param; - FeVariantType *v; - int special; - call_param=0; - special=0; - if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"destroy")==0 && n->children) { - FeNode *arg=n->children; - fputs("(free(",e->out); emit_expr(e,arg); - if (arg->sem_type && arg->sem_type->kind==FE_TYPE_OWNED && - arg->sem_type->elem && arg->sem_type->elem->kind==FE_TYPE_SLICE) - fputs(".p",e->out); - fputc(')',e->out); - if (arg->kind==FE_N_IDENT) { - fputs(", ",e->out); emit_lvalue(e,arg); - if (arg->sem_type && arg->sem_type->elem && - arg->sem_type->elem->kind==FE_TYPE_SLICE) fputs(".p=0",e->out); - else fputs("=0",e->out); - fputs(", fe_live_",e->out); fputs(cname(arg,"owned"),e->out); - fputs("=0",e->out); - } - fputs(", 0)",e->out); - return; - } - if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"replace")==0 && n->children && - n->children->next && n->sem_type) { - fputs(n->sem_type->replace_cname ? n->sem_type->replace_cname : - "fe_bad_replace",e->out); - fputc('(',e->out); emit_expr(e,n->children); fputs(", ",e->out); - emit_expr(e,n->children->next); fputc(')',e->out); - return; - } - if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"create")==0 && n->children) { - FeType *created=n->children->sem_type; - FeType *owned=fe_type_owned(&e->check->types,created); - FeType *result=fe_type_error_union(&e->check->types,owned); - fputs(result->alloc_cname ? result->alloc_cname : "fe_bad_alloc",e->out); - fputc('(',e->out); emit_expr(e,n->children); fputc(')',e->out); - return; - } - if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"mem")==0 && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"alloc_slice")==0 && n->children && - n->children->next) { - FeType *result=n->sem_type; - fputs(result && result->alloc_cname ? result->alloc_cname : - "fe_bad_slice_alloc",e->out); - fputc('(',e->out); emit_expr(e,n->children->next); fputc(')',e->out); - return; - } - if (n->text && (strcmp(n->text,"@print")==0 || - strcmp(n->text,"@fprint")==0 || strcmp(n->text,"@sprint")==0)) { - emit_m4_builtin(e,n); - return; - } - /* Every other builtin -- @size_of, @align_of and friends -- is lowered by - the core emitter. Without this the generic path below emits the call - verbatim, which is not C. */ - if (!n->a && n->text && n->text[0]=='@') { - emit_expr_core(e,n); - return; - } - /* Same for the built-in alias methods on str: the core emitter knows how to - lower `line.trim()`, the generic member path would emit `.trim()`. */ - if (n->a && n->a->kind==FE_N_MEMBER && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"trim")==0 && !n->children) { - emit_expr_core(e,n); - return; - } - if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->kind==FE_N_IDENT && n->a->a->text && - strcmp(n->a->a->text,"io")==0 && n->a->b && n->a->b->text && - strcmp(n->a->b->text,"null_writer")==0) { - fputs("fe_m4_null_writer()",e->out); - return; - } - if (n->a && n->a->kind==FE_N_MEMBER && n->a->a && - n->a->a->sem_type && n->a->a->sem_type->kind==FE_TYPE_ENUM && - !n->a->a->sem_type->is_error) { - v=fe_type_variant(n->a->a->sem_type,n->a->b ? n->a->b->text : ""); - fputs(v ? v->maker : "fe_bad_variant",e->out); - } else if (n->a && n->a->kind==FE_N_MEMBER && n->sem_decl && - n->sem_decl->kind==FE_N_FN) { - FeNode *mp=n->sem_decl->a ? n->sem_decl->a->children : 0; - FeNode *ma; - fputs(cname(n->sem_decl,"fe_method"),e->out); fputc('(',e->out); - if (mp && mp->sem_type && mp->sem_type->kind==FE_TYPE_REF) { - fputc('&',e->out); emit_lvalue(e,n->a->a); - } else emit_expr(e,n->a->a); - for (ma=n->children;ma;ma=ma->next) { - fputs(", ",e->out); emit_expr(e,ma); - } - fputc(')',e->out); - return; - } else if (n->a) emit_expr(e,n->a); - else fputs(n->text ? n->text : "fe_builtin",e->out); - if (!special) { - if (n->sem_decl && n->sem_decl->kind==FE_N_FN && n->sem_decl->a) - call_param=n->sem_decl->a->children; - fputc('(',e->out); - for (x=n->children;x;x=x->next) { - FeType *want=call_param && call_param->a ? - fe_type_from_ast(&e->check->types,call_param->a) : 0; - if (x!=n->children) fputs(", ",e->out); - if (want && want->kind==FE_TYPE_SLICE && !want->ref_mut && - x->sem_type && x->sem_type->kind==FE_TYPE_SLICE && - x->sem_type->ref_mut) { - fputs(want->maker,e->out); fputc('(',e->out); - emit_expr(e,x); fputs(".p, ",e->out); - emit_expr(e,x); fputs(".n)",e->out); - } else emit_expr(e,x); - if (call_param) call_param=call_param->next; - } - fputc(')',e->out); - } -} - -/* `dst = ;` as a statement, avoiding a comma expression on the right. - - A consumed identifier lowers to `(fe_live_x=0, x)`. When dst is a struct, - Watcom crashes on a struct assignment whose right side is a comma expression - -- hard enough to take DOSBox-X down with it -- so clear the move flag as its - own statement and assign the plain name. */ -static void m7_emit_assign_stmt(FeEmitter *e, const char *dst, FeNode *src) -{ - if (src && src->kind==FE_N_IDENT && (src->flags & FE_OWN_NODE_CONSUMED) && - src->sem_type && type_needs_drop(src->sem_type)) { - pad(e); fputs("fe_live_",e->out); fputs(cname(src,"owned"),e->out); - fputs("=0;\n",e->out); - pad(e); fputs(dst,e->out); fputs(" = ",e->out); - fputs(cname(src,"fe_missing"),e->out); fputs(";\n",e->out); - return; - } - pad(e); fputs(dst,e->out); fputs(" = ",e->out); - emit_expr(e,src); fputs(";\n",e->out); -} - -static void m7_emit_raw_expr(FeEmitter *e, FeNode *n) -{ - FeType *bt; - FeVariantType *v; - const char *op; - FeM7LazyKind lazy; - if (!n) { fputs("0",e->out); return; } - /* No feature scan: the switch below handles the node kinds this emitter - changes and its default hands everything else to emit_expr_core, so the - same path serves a unit whether or not it mentions optionals. */ - switch (n->kind) { - case FE_N_IDENT: - if ((n->flags & FE_OWN_NODE_CONSUMED) && n->sem_type && - type_needs_drop(n->sem_type)) { - fputs("(fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs("=0, ",e->out); fputs(cname(n,"fe_missing"),e->out); - fputc(')',e->out); - } else fputs(cname(n,"fe_missing"),e->out); - break; - case FE_N_LITERAL: - if (fe_m7_is_null(n)) fputs("0",e->out); - else emit_expr_core(e,n); - break; - case FE_N_UNARY: - op=n->text ? n->text : ""; - if (strcmp(op,"try")==0) { - if (n->a && n->a->sem_type && - n->a->sem_type->kind==FE_TYPE_ERROR_UNION && - n->a->sem_type->error_value && - n->a->sem_type->error_value->kind!=FE_TYPE_VOID) { - fputs("(",e->out); fputs(n->aux_cname,e->out); - fputs(" = ",e->out); emit_expr(e,n->a); fputs(", ",e->out); - fputs(n->aux_cname,e->out); fputs(".v)",e->out); - } else emit_expr(e,n->a); - } else if (strcmp(op,"&")==0 || strcmp(op,"&mut")==0) { - fputs("(&",e->out); emit_lvalue(e,n->a); fputc(')',e->out); - } else if (strcmp(op,"not")==0) { - fputs("(!",e->out); emit_expr(e,n->a); fputc(')',e->out); - } else { - fputc('(',e->out); fputs(op,e->out); emit_expr(e,n->a); - fputc(')',e->out); - } - break; - case FE_N_BINARY: - lazy=fe_m7_lazy_kind(n); - if (lazy==FE_M7_LAZY_ORELSE) { - FeType *opt=n->a ? n->a->sem_type : 0; - fputs("((",e->out); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs("), ",e->out); - m7_emit_present(e,opt,n->aux_cname); fputs(" ? ",e->out); - if (fe_m7_optional_uses_niche(opt->elem)) fputs(n->aux_cname,e->out); - else { fputs(n->aux_cname,e->out); fputs(".v",e->out); } - fputs(" : ",e->out); emit_expr(e,n->b); fputc(')',e->out); - } else if (lazy==FE_M7_LAZY_CATCH && !n->c) { - FeType *res=n->a ? n->a->sem_type : 0; - fputs("((",e->out); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs("), ",e->out); - if (res && res->error_value && res->error_value->kind==FE_TYPE_VOID) { - fputs(n->aux_cname,e->out); fputs(" ? ",e->out); - emit_expr(e,n->b); fputs(" : 0)",e->out); - } else { - fputs(n->aux_cname,e->out); fputs(".e ? ",e->out); - emit_expr(e,n->b); fputs(" : ",e->out); - fputs(n->aux_cname,e->out); fputs(".v)",e->out); - } - } else if (lazy==FE_M7_LAZY_CATCH && n->c) { - fputs("0",e->out); - } else if ((n->text && (strcmp(n->text,"==")==0 || - strcmp(n->text,"!=")==0)) && - (fe_m7_is_null(n->a) || fe_m7_is_null(n->b))) { - FeNode *value=fe_m7_is_null(n->a) ? n->b : n->a; - FeType *opt=value ? value->sem_type : 0; - if (opt && opt->kind==FE_TYPE_OPTIONAL && - !fe_m7_optional_uses_niche(opt->elem)) { - fputs("(!",e->out); emit_expr(e,value); fputs(".has)",e->out); - if (strcmp(n->text,"!=")==0) { - fputs(" == 0",e->out); - } - } else { - fputc('(',e->out); emit_expr(e,value); - fputs(strcmp(n->text,"==")==0 ? " == 0)" : " != 0)",e->out); - } - } else { - op=n->text ? n->text : "+"; - fputc('(',e->out); emit_expr(e,n->a); - if (strcmp(op,"and")==0) fputs(" && ",e->out); - else if (strcmp(op,"or")==0) fputs(" || ",e->out); - else fputs(op,e->out); - emit_expr(e,n->b); fputc(')',e->out); - } - break; - case FE_N_MEMBER: - bt=n->a ? n->a->sem_type : 0; - if (n->text && strcmp(n->text,".?")==0 && bt && - bt->kind==FE_TYPE_OPTIONAL) { - fputs(bt->unwrap_cname,e->out); fputc('(',e->out); - emit_expr(e,n->a); fputc(')',e->out); - } else if (bt && bt->kind==FE_TYPE_ENUM && bt->is_error) { - m7_emit_error_member(e,n); - } else if ((bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) && - n->b && n->b->text && strcmp(n->b->text,"^")==0) { - fputs("(*",e->out); emit_expr(e,n->a); fputc(')',e->out); - } else if (bt && (bt->kind==FE_TYPE_REF || bt->kind==FE_TYPE_OWNED)) { - emit_expr(e,n->a); fputs("->",e->out); - fputs(n->b && n->b->text ? n->b->text : "member",e->out); - } else if (bt && bt->kind==FE_TYPE_ENUM && !bt->is_error) { - v=fe_type_variant(bt,n->b ? n->b->text : ""); - if (v) { fputs(v->maker,e->out); fputs("()",e->out); } - else fputs("0",e->out); - } else { - emit_expr(e,n->a); fputc('.',e->out); - if (n->b) fputs(n->b->text ? n->b->text : "member",e->out); - } - break; - case FE_N_CALL: - m7_emit_call(e,n); - break; - case FE_N_TYPE: - if (n->text && strcmp(n->text,"as")==0) { - fputs("((",e->out); fputs(m7_c_type(e,n->sem_type),e->out); - fputc(')',e->out); emit_expr(e,n->a); fputc(')',e->out); - } else emit_expr(e,n->a); - break; - case FE_N_INDEX: - bt=n->a ? n->a->sem_type : 0; - if (n->c || !n->b) { - emit_expr_core(e,n); - } else if (bt && bt->indexer) { - fputs(bt->indexer,e->out); fputc('(',e->out); - emit_expr(e,n->a); fputs(", ",e->out); emit_expr(e,n->b); - fputc(')',e->out); - } else fputs("0",e->out); - break; - case FE_N_STRUCT_INIT: - case FE_N_ARRAY_INIT: - emit_expr_core(e,n); - break; - default: - emit_expr_core(e,n); - break; - } -} - -/* Emit the initializer for a `const` declaration. - - A string literal normally lowers to a maker call, but C89 requires the - initializer of an aggregate -- at file scope and for automatics alike -- to be - a constant expression, and the build runs with -za. Emit the slice braced - instead. Returns non-zero when it handled the initializer. */ -static int m7_emit_const_init(FeEmitter *e, FeNode *n) -{ - if (n->kind!=FE_N_CONST || !n->b || n->b->kind!=FE_N_LITERAL || - !n->b->text || n->b->text[0]!='"') return 0; - fputs("{ (const unsigned char*)",e->out); - emit_c_literal(e->out,n->b->text,1); - fputs(", sizeof(",e->out); - emit_c_literal(e->out,n->b->text,1); - fputs(")-1 }",e->out); - return 1; -} - -static void emit_decl(FeEmitter *e, FeNode *n) -{ - pad(e); fputs(m7_c_type(e,n->sem_type),e->out); fputc(' ',e->out); - fputs(cname(n,"fe_local"),e->out); - if (n->kind==FE_N_CONST && n->b) { - fputs(" = ",e->out); - if (!m7_emit_const_init(e,n)) emit_expr(e,n->b); - } - fputs(";\n",e->out); - if ((n->kind==FE_N_LET || n->kind==FE_N_VAR) && n->sem_type && - type_needs_drop(n->sem_type)) { - pad(e); fputs("unsigned char fe_live_",e->out); - fputs(cname(n,"owned"),e->out); fputs("=0;\n",e->out); - } -} - -static void emit_owned_live(FeEmitter *e, FeNode *n, int value) -{ - if (n && n->sem_type && type_needs_drop(n->sem_type)) { - pad(e); fputs("fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fprintf(e->out,"=%d;\n",value); - } -} - -static void emit_value_drop(FeEmitter *e, FeNode *n) -{ - FeType *t; - t=n ? n->sem_type : 0; - if (!n || !t || !type_needs_drop(t) || - (n->flags & FE_OWN_NODE_CONSUMED) || - (n->flags & FE_OWN_NODE_DEFER_CAPTURE)) return; - pad(e); fputs("if (fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs(") { ",e->out); - if (t->kind==FE_TYPE_OWNED) { - if (t->elem && t->elem->kind==FE_TYPE_SLICE) { - fputs("free(",e->out); fputs(cname(n,"owned"),e->out); - fputs(".p); ",e->out); - } else { - if (t->elem && type_needs_drop(t->elem) && t->elem->drop_cname) - fprintf(e->out,"%s(%s); ",t->elem->drop_cname,cname(n,"owned")); - fputs("free(",e->out); fputs(cname(n,"owned"),e->out); - fputs("); ",e->out); - } - } else if (t->drop_cname) { - fprintf(e->out,"%s(&%s); ",t->drop_cname,cname(n,"local")); - } - fputs("fe_live_",e->out); fputs(cname(n,"owned"),e->out); - fputs("=0; }\n",e->out); -} - -static void emit_cleanup_block(FeEmitter *e, FeNode *n) -{ - FeNode *x; - unsigned count; - unsigned index; - unsigned seen; - unsigned depth; - count=0; - seen=0xffffffffU; - for (depth=0;depthblock_depth;++depth) - if (e->block_stack[depth]==n) { - seen=e->block_seen[depth]; - break; - } - for (x=n ? n->children : 0,index=0;x;x=x->next,++index) - if (indexkind==FE_N_DEFER || x->kind==FE_N_LET || - x->kind==FE_N_VAR)) ++count; - while (count) { - index=0; - for (x=n->children;x;x=x->next) - if ((x->kind==FE_N_DEFER || x->kind==FE_N_LET || - x->kind==FE_N_VAR) && index++==count-1U) { - if (x->kind==FE_N_DEFER) emit_stmt(e,x->a); - else emit_value_drop(e,x); - break; - } - --count; - } -} - -static void emit_cleanup_to(FeEmitter *e, unsigned floor) -{ - unsigned i; - for (i=e->block_depth;i>floor;--i) - emit_cleanup_block(e,e->block_stack[i-1U]); -} - -static void emit_param_cleanup(FeEmitter *e) -{ - FeNode *p; - if (!e->current_fn || !e->current_fn->a) return; - for (p=e->current_fn->a->children;p;p=p->next) emit_value_drop(e,p); -} - -static void emit_cleanup_all(FeEmitter *e) -{ - emit_cleanup_to(e,0); - emit_param_cleanup(e); -} - -static void emit_error_return(FeEmitter *e, const char *error_expr) -{ - FeType *ret=e->current_ret; - if (ret && ret->kind==FE_TYPE_ERROR_UNION && ret->error_value && - ret->error_value->kind!=FE_TYPE_VOID) { - fputs("return ",e->out); fputs(ret->none_cname,e->out); - fputc('(',e->out); fputs(error_expr,e->out); fputs(");\n",e->out); - } else { - fputs("return ",e->out); fputs(error_expr,e->out); fputs(";\n",e->out); - } -} - -static void m7_emit_try_error_check(FeEmitter *e, FeNode *n) -{ - FeType *result=n->a ? n->a->sem_type : 0; - pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("if (",e->out); fputs(n->aux_cname,e->out); - if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(") {\n",e->out); ++e->indent; - emit_cleanup_all(e); - pad(e); - if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) { - char error[192]; - sprintf(error,"%s.e",n->aux_cname); - emit_error_return(e,error); - } else emit_error_return(e,n->aux_cname); - --e->indent; pad(e); fputs("}\n",e->out); -} - -static void m7_emit_catch_block(FeEmitter *e, FeNode *n, - FeNode *target) -{ - FeType *result=n->a ? n->a->sem_type : 0; - FeNode *binding=n->b; - pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("if (",e->out); fputs(n->aux_cname,e->out); - if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(") {\n",e->out); ++e->indent; - if (binding && binding->cname) { - pad(e); fputs("unsigned short ",e->out); fputs(binding->cname,e->out); - fputs(" = ",e->out); fputs(n->aux_cname,e->out); - if (result && result->error_value && result->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(";\n",e->out); - } - emit_stmt(e,n->c); - --e->indent; pad(e); fputs("}",e->out); - if (target && result && result->error_value && - result->error_value->kind!=FE_TYPE_VOID) { - fputs(" else {\n",e->out); ++e->indent; - pad(e); emit_lvalue(e,target); fputs(" = ",e->out); - fputs(n->aux_cname,e->out); fputs(".v;\n",e->out); - emit_owned_live(e,target,1); - --e->indent; pad(e); fputs("}",e->out); - } - fputc('\n',e->out); -} - -static void m7_emit_optional_match(FeEmitter *e, FeNode *n) -{ - FeType *opt=n->a ? n->a->sem_type : 0; - FeNode *arm; - FeNode *binding; - int first; - pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - first=1; - for (arm=n->children;arm;arm=arm->next) { - if (arm->text && strcmp(arm->text,"Some")==0) { - pad(e); if (!first) fputs("else ",e->out); - fputs("if (",e->out); m7_emit_present(e,opt,n->aux_cname); - fputs(") {\n",e->out); ++e->indent; - binding=arm->children; - if (binding && binding->cname) { - pad(e); fputs(m7_c_type(e,binding->sem_type),e->out); - fputc(' ',e->out); fputs(binding->cname,e->out); fputs(" = ",e->out); - m7_emit_payload_var(e,opt,n->aux_cname); fputs(";\n",e->out); - } - if (arm->a) emit_stmt(e,arm->a); - --e->indent; pad(e); fputs("}\n",e->out); - first=0; - } else if (arm->text && strcmp(arm->text,"None")==0) { - pad(e); if (!first) fputs("else ",e->out); - fputs("if (!",e->out); m7_emit_present(e,opt,n->aux_cname); - fputs(") {\n",e->out); ++e->indent; - if (arm->a) emit_stmt(e,arm->a); - --e->indent; pad(e); fputs("}\n",e->out); - first=0; - } else if (arm->text && strcmp(arm->text,"_")==0) { - pad(e); if (!first) fputs("else ",e->out); - fputs("{\n",e->out); ++e->indent; - if (arm->a) emit_stmt(e,arm->a); - --e->indent; pad(e); fputs("}\n",e->out); - first=0; - } - } -} - -static void m7_emit_if_let(FeEmitter *e, FeNode *n) -{ - FeType *opt=n->a ? n->a->sem_type : 0; - FeNode *binding=n->children; - int some=n->aux_text && strcmp(n->aux_text,"Some")==0; - pad(e); fputs(n->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - pad(e); fputs("if (",e->out); - if (!some) fputc('!',e->out); - m7_emit_present(e,opt,n->aux_cname); fputs(") {\n",e->out); - ++e->indent; - if (some && binding && binding->cname) { - pad(e); fputs(m7_c_type(e,binding->sem_type),e->out); fputc(' ',e->out); - fputs(binding->cname,e->out); fputs(" = ",e->out); - m7_emit_payload_var(e,opt,n->aux_cname); fputs(";\n",e->out); - } - if (n->b) emit_stmt(e,n->b); - --e->indent; pad(e); fputs("}",e->out); - if (n->c) { - fputs(" else ",e->out); - emit_stmt(e,n->c); - } - fputc('\n',e->out); -} - -static void emit_block(FeEmitter *e, FeNode *n) -{ - FeNode *x; - unsigned seen; - if (!n) { - pad(e); fputs("{}",e->out); return; - } - pad(e); fputs("{\n",e->out); ++e->indent; - if (e->block_depth<32U) { - e->block_stack[e->block_depth]=n; - e->block_seen[e->block_depth]=0; - ++e->block_depth; - } - for (x=n->children;x;x=x->next) - if (x->kind==FE_N_LET || x->kind==FE_N_VAR || x->kind==FE_N_CONST) - emit_decl(e,x); - if (e->current_fn && e->current_fn->c==n) { - if (e->current_fn->a) { - FeNode *p; - for (p=e->current_fn->a->children;p;p=p->next) - if (p->sem_type && type_needs_drop(p->sem_type)) { - pad(e); fputs("unsigned char fe_live_",e->out); - fputs(cname(p,"owned"),e->out); fputs("=1;\n",e->out); - } - } - m7_emit_temp_decls(e,n); - } - if (e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - pad(e); fputs(m7_c_type(e,e->current_ret),e->out); - fputs(" fe_return_value;\n",e->out); - } - seen=0; - for (x=n->children;x;x=x->next) { - ++seen; - if (e->block_depth) e->block_seen[e->block_depth-1U]=seen; - emit_stmt(e,x); - } - --e->indent; - emit_cleanup_block(e,n); - if (e->current_fn && e->current_fn->c==n) emit_param_cleanup(e); - if (e->block_depth) --e->block_depth; - if (e->fallthrough_block==n) { - pad(e); fputs("return 0;\n",e->out); - e->fallthrough_block=0; - } - pad(e); fputc('}',e->out); -} - -static void emit_stmt(FeEmitter *e, FeNode *n) -{ - FeType *result; - if (!n) return; - switch (n->kind) { - case FE_N_BLOCK: - emit_block(e,n); fputc('\n',e->out); break; - case FE_N_LET: - case FE_N_VAR: - if (n->b) { - if (fe_m7_is_try(n->b)) { - m7_emit_try_error_check(e,n->b); - pad(e); emit_lvalue(e,n); fputs(" = ",e->out); - fputs(n->b->aux_cname,e->out); - result=n->b->a ? n->b->a->sem_type : 0; - if (result && result->error_value && - result->error_value->kind!=FE_TYPE_VOID) fputs(".v",e->out); - fputs(";\n",e->out); emit_owned_live(e,n,1); - } else if (n->b->kind==FE_N_BINARY && n->b->c && - fe_m7_lazy_kind(n->b)==FE_M7_LAZY_CATCH) { - m7_emit_catch_block(e,n->b,n); - } else { - pad(e); emit_lvalue(e,n); fputs(" = ",e->out); - emit_expr(e,n->b); fputs(";\n",e->out); - emit_owned_live(e,n,1); - } - } - break; - case FE_N_ASSIGN: - if (n->a && n->a->kind==FE_N_IDENT) emit_value_drop(e,n->a); - pad(e); emit_lvalue(e,n->a); fputc(' ',e->out); - fputs(n->text ? n->text : "=",e->out); fputc(' ',e->out); - emit_expr(e,n->b); fputs(";\n",e->out); - if (n->a && n->a->kind==FE_N_IDENT) emit_owned_live(e,n->a,1); - break; - case FE_N_EXPR_STMT: - if (fe_m7_is_try(n->a)) { - m7_emit_try_error_check(e,n->a); - } else if (n->a && n->a->kind==FE_N_BINARY && n->a->c && - fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH) { - m7_emit_catch_block(e,n->a,0); - } else { - pad(e); emit_expr(e,n->a); fputs(";\n",e->out); - } - break; - case FE_N_DEFER: - break; - case FE_N_RETURN: - if (n->a && fe_m7_is_try(n->a)) { - FeNode *tr=n->a; - FeType *res=tr->a ? tr->a->sem_type : 0; - m7_emit_try_error_check(e,tr); - if (e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - pad(e); fputs("fe_return_value = ",e->out); - if (e->current_ret->kind==FE_TYPE_ERROR_UNION && - e->current_ret->error_value && - e->current_ret->error_value->kind!=FE_TYPE_VOID) { - fputs(e->current_ret->maker,e->out); fputs("(0, ",e->out); - fputs(tr->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".v",e->out); - fputc(')',e->out); - } else { - fputs(tr->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".v",e->out); - } - fputs(";\n",e->out); - } - emit_cleanup_all(e); - pad(e); fputs("return fe_return_value;\n",e->out); - } else if (n->a && n->a->kind==FE_N_BINARY && n->a->c && - fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH) { - /* A value catch-block is lowered as a temporary local success - assignment; the handler is required by the checker to exit. */ - FeNode *cx=n->a; - FeType *res=cx->a ? cx->a->sem_type : 0; - pad(e); fputs(cx->aux_cname,e->out); fputs(" = ",e->out); - emit_expr(e,cx->a); fputs(";\n",e->out); - pad(e); fputs("if (",e->out); fputs(cx->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(") {\n",e->out); ++e->indent; - if (cx->b && cx->b->cname) { - pad(e); fputs("unsigned short ",e->out); fputs(cx->b->cname,e->out); - fputs(" = ",e->out); fputs(cx->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".e",e->out); - fputs(";\n",e->out); - } - emit_stmt(e,cx->c); - --e->indent; pad(e); fputs("}\n",e->out); - pad(e); fputs("fe_return_value = ",e->out); - fputs(cx->aux_cname,e->out); - if (res && res->error_value && res->error_value->kind!=FE_TYPE_VOID) - fputs(".v",e->out); - fputs(";\n",e->out); - emit_cleanup_all(e); - pad(e); fputs("return fe_return_value;\n",e->out); - } else if (n->a && n->a->kind==FE_N_BINARY && !n->a->c && - fe_m7_lazy_kind(n->a)==FE_M7_LAZY_CATCH && - e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - /* Short catch in return position. As an expression this lowers to - `((tmp = X), tmp.e ? fallback : tmp.v)`, and when X carries a move - it becomes a struct assignment whose right side is itself a comma - expression -- which crashes wcc386 hard enough to take DOSBox-X - down with it. The same lowering as statements is also plainer. */ - FeNode *cx=n->a; - FeType *res=cx->a ? cx->a->sem_type : 0; - int has_value=res && res->error_value && - res->error_value->kind!=FE_TYPE_VOID; - m7_emit_assign_stmt(e,cx->aux_cname,cx->a); - pad(e); fputs("if (",e->out); fputs(cx->aux_cname,e->out); - if (has_value) fputs(".e",e->out); - fputs(") {\n",e->out); ++e->indent; - pad(e); fputs("fe_return_value = ",e->out); - emit_expr(e,cx->b); fputs(";\n",e->out); - --e->indent; pad(e); fputs("} else {\n",e->out); ++e->indent; - pad(e); fputs("fe_return_value = ",e->out); - fputs(cx->aux_cname,e->out); - if (has_value) fputs(".v",e->out); - fputs(";\n",e->out); - --e->indent; pad(e); fputs("}\n",e->out); - emit_cleanup_all(e); - pad(e); fputs("return fe_return_value;\n",e->out); - } else { - if (n->a && e->current_ret && e->current_ret->kind!=FE_TYPE_VOID) { - pad(e); fputs("fe_return_value = ",e->out); - emit_expr(e,n->a); fputs(";\n",e->out); - } - emit_cleanup_all(e); - pad(e); fputs("return",e->out); - if (n->a) fputs(" fe_return_value",e->out); - fputs(";\n",e->out); - } - break; - case FE_N_IF: - if (n->text && strcmp(n->text,"if let")==0) { - m7_emit_if_let(e,n); - } else { - pad(e); fputs("if (",e->out); emit_expr(e,n->a); fputs(") ",e->out); - emit_block(e,n->b); - if (n->c) { - fputs(" else ",e->out); - if (n->c->kind==FE_N_IF) emit_stmt(e,n->c); - else emit_block(e,n->c); - } - fputc('\n',e->out); - } - break; - case FE_N_MATCH: - if (n->a && n->a->sem_type && n->a->sem_type->kind==FE_TYPE_OPTIONAL) - m7_emit_optional_match(e,n); - else emit_match(e,n,0); - break; - case FE_N_BREAK: - case FE_N_CONTINUE: - if (e->loop_depth) { - emit_cleanup_to(e,e->loop_floor[e->loop_depth-1U]); - pad(e); fputs(n->kind==FE_N_BREAK ? "break;\n" : "continue;\n",e->out); - } - break; - case FE_N_WHILE: - pad(e); fputs("while (",e->out); emit_expr(e,n->a); fputs(") ",e->out); - if (e->loop_depth<16U) e->loop_floor[e->loop_depth++]=e->block_depth; - emit_block(e,n->b); - if (e->loop_depth) --e->loop_depth; - fputc('\n',e->out); - break; - case FE_N_FOR: - emit_stmt_core(e,n); - break; - default: - emit_stmt_core(e,n); - break; - } -} - -static void emit_fn(FeEmitter *e, FeNode *fn, int prototype) -{ - FeNode *p; - FeType *old_ret; - FeNode *old_fn; - fputs(m7_c_type(e,fn->sem_type ? fn->sem_type : - (fn->b ? fe_type_from_ast(&e->check->types,fn->b) : - fe_type_intern(&e->check->types,"void"))),e->out); - fputc(' ',e->out); fputs(cname(fn,"fe_fn"),e->out); fputc('(',e->out); - p=fn->a ? fn->a->children : 0; - if (!p) fputs("void",e->out); - while (p) { - if (p!=fn->a->children) fputs(", ",e->out); - fputs(m7_c_type(e,p->sem_type ? p->sem_type : - fe_type_from_ast(&e->check->types,p->a)),e->out); - fputc(' ',e->out); fputs(cname(p,"fe_arg"),e->out); - p=p->next; - } - fputc(')',e->out); - if (prototype) { fputs(";\n",e->out); return; } - old_ret=e->current_ret; - old_fn=e->current_fn; - e->current_ret=fn->sem_type; - e->current_fn=fn; - m7_prepare_temps(e,fn->c); - fputc(' ',e->out); - if (fn->sem_type && fn->sem_type->kind==FE_TYPE_ERROR_UNION && - fn->sem_type->error_value && fn->sem_type->error_value->kind==FE_TYPE_VOID) - e->fallthrough_block=fn->c; - emit_block(e,fn->c); - e->current_ret=old_ret; - e->current_fn=old_fn; - fputc('\n',e->out); -} - -static void emit_main_wrapper(FeEmitter *e, FeNode *fn) -{ - FeType *ret=fn->sem_type; - if (ret && ret->kind==FE_TYPE_ERROR_UNION && ret->error_value && - ret->error_value->kind!=FE_TYPE_VOID) { - fputs("int main(void) { ",e->out); fputs(m7_c_type(e,ret),e->out); - fputs(" r = ",e->out); fputs(cname(fn,"fe_main"),e->out); - fputs("(); return r.e ? 1 : 0; }\n",e->out); - } else emit_main_wrapper_core(e,fn); -} - -void fe_emit_c_program(FeEmitter *e) -{ - FeNode *n; - FeNode *main_fn; - FeType *type; - int need_m4; - main_fn=0; - need_m4=node_uses_m4(e->check->ast->root); - for (type=e->check->types.types;type;type=type->next) - if (strcmp(type->name,"io.Writer")==0) need_m4=1; - /* See emit_c.c: stdio only comes in with the M4 writer runtime. */ - fputs("/* generated by fec M7 */\n#include \n#include \n#include \n",e->out); - if (need_m4) fputs("#include \n",e->out); - fputs("typedef char fe_assert_u8[(sizeof(unsigned char)==1) ? 1 : -1];\ntypedef char fe_assert_u16[(sizeof(unsigned short)==2) ? 1 : -1];\ntypedef char fe_assert_u32[(sizeof(unsigned long)==4) ? 1 : -1];\n",e->out); - if (e->pointer_bits==16) - fputs("typedef char fe_assert_usize[(sizeof(unsigned short)==2) ? 1 : -1];\n",e->out); - else - fputs("typedef char fe_assert_usize[(sizeof(unsigned long)==4) ? 1 : -1];\n",e->out); - fputs("static void fe_trap_bounds(void) { abort(); }\nstatic unsigned short fe_error_temp;\n\n",e->out); - emit_type_defs(e); - if (need_m4) emit_m4_runtime(e); - m7_emit_type_helpers(e); - for (n=e->check->ast->root ? e->check->ast->root->children : 0;n;n=n->next) { - if (n->kind==FE_N_GLOBAL || n->kind==FE_N_CONST) { - fputs(m7_c_type(e,n->sem_type),e->out); fputc(' ',e->out); - fputs(cname(n,"fe_global"),e->out); - if (n->b) { - fputs(" = ",e->out); - if (!m7_emit_const_init(e,n)) emit_expr(e,n->b); - } - fputs(";\n",e->out); - } - } - for (n=e->check->ast->root ? e->check->ast->root->children : 0;n;n=n->next) - if (n->kind==FE_N_FN) { - emit_fn(e,n,1); - if (n->text && strcmp(n->text,"main")==0) main_fn=n; - } - for (n=e->check->ast->root ? e->check->ast->root->children : 0;n;n=n->next) - if (n->kind==FE_N_STRUCT) { - FeNode *m; - for (m=n->children;m;m=m->next) - if (m->kind==FE_N_FN) emit_fn(e,m,1); - } - fputc('\n',e->out); - for (n=e->check->ast->root ? e->check->ast->root->children : 0;n;n=n->next) - if (n->kind==FE_N_FN) emit_fn(e,n,0); - for (n=e->check->ast->root ? e->check->ast->root->children : 0;n;n=n->next) - if (n->kind==FE_N_STRUCT) { - FeNode *m; - for (m=n->children;m;m=m->next) - if (m->kind==FE_N_FN) emit_fn(e,m,0); - } - if (main_fn) { fputc('\n',e->out); emit_main_wrapper(e,main_fn); } -} diff --git a/fec/src/emit_c.h b/fec/src/emit_c.h deleted file mode 100644 index 8be10e6..0000000 --- a/fec/src/emit_c.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef FE_EMIT_C_H -#define FE_EMIT_C_H - -#include "check.h" -#include "own.h" - -typedef struct FeEmitter { - FILE *out; - FeCheck *check; - unsigned pointer_bits; - int indent; - int no_checks; - unsigned temp_serial; - FeNode *fallthrough_block; - FeNode *block_stack[32]; - unsigned block_seen[32]; - unsigned block_depth; - unsigned loop_floor[16]; - unsigned loop_depth; - FeType *current_ret; - FeNode *current_fn; -} FeEmitter; - -void fe_emit_c_init(FeEmitter *e, FILE *out, FeCheck *check, - unsigned pointer_bits, int no_checks); -void fe_emit_c_program(FeEmitter *e); - -#endif diff --git a/fec/src/lower.c b/fec/src/lower.c deleted file mode 100644 index abd6942..0000000 --- a/fec/src/lower.c +++ /dev/null @@ -1,227 +0,0 @@ -#include "lower.h" -#include - -static int lower_grow_scopes(FeLowerPlan *plan) -{ - FeLowerScope *items; - unsigned capacity; - if (plan->scope_count < plan->scope_capacity) return 1; - capacity = plan->scope_capacity ? plan->scope_capacity * 2U : 8U; - items = (FeLowerScope *)fe_arena_alloc(plan->arena, - capacity * sizeof(FeLowerScope)); - if (!items) return 0; - if (plan->scopes) - memcpy(items, plan->scopes, - plan->scope_count * sizeof(FeLowerScope)); - plan->scopes = items; - plan->scope_capacity = capacity; - return 1; -} - -static int lower_grow_cleanups(FeLowerPlan *plan) -{ - FeLowerCleanup *items; - unsigned capacity; - if (plan->cleanup_count < plan->cleanup_capacity) return 1; - capacity = plan->cleanup_capacity ? plan->cleanup_capacity * 2U : 16U; - items = (FeLowerCleanup *)fe_arena_alloc(plan->arena, - capacity * sizeof(FeLowerCleanup)); - if (!items) return 0; - if (plan->cleanups) - memcpy(items, plan->cleanups, - plan->cleanup_count * sizeof(FeLowerCleanup)); - plan->cleanups = items; - plan->cleanup_capacity = capacity; - return 1; -} - -static unsigned lower_add_scope(FeLowerPlan *plan, FeNode *block, - unsigned parent) -{ - FeLowerScope *scope; - unsigned index; - if (!lower_grow_scopes(plan)) return FE_LOWER_NO_SCOPE; - index = plan->scope_count++; - scope = &plan->scopes[index]; - scope->block = block; - scope->parent = parent; - scope->ordinal = plan->next_ordinal++; - return index; -} - -static int lower_add_cleanup(FeLowerPlan *plan, unsigned scope, - FeLowerCleanupKind kind, FeNode *node, - FeNode *decl, FeType *type) -{ - FeLowerCleanup *cleanup; - if (!lower_grow_cleanups(plan)) return 0; - cleanup = &plan->cleanups[plan->cleanup_count++]; - cleanup->kind = kind; - cleanup->scope = scope; - cleanup->ordinal = plan->next_ordinal++; - cleanup->node = node; - cleanup->decl = decl; - cleanup->type = type; - return 1; -} - -int fe_lower_type_needs_drop(const FeType *type) -{ - unsigned i; - unsigned j; - if (!type) return 0; - if (type->kind == FE_TYPE_OWNED) return 1; - if (type->kind == FE_TYPE_OPTIONAL) - return fe_lower_type_needs_drop(type->elem); - if (type->kind == FE_TYPE_ERROR_UNION) - return fe_lower_type_needs_drop(type->error_value); - if (type->kind == FE_TYPE_ARRAY) - return fe_lower_type_needs_drop(type->elem); - if (type->kind == FE_TYPE_STRUCT) { - if (type->has_drop) return 1; - for (i = 0; i < type->field_count; ++i) - if (fe_lower_type_needs_drop(type->fields[i].type)) return 1; - return 0; - } - if (type->kind == FE_TYPE_ENUM) { - for (i = 0; i < type->variant_count; ++i) - for (j = 0; j < type->variants[i].field_count; ++j) - if (fe_lower_type_needs_drop(type->variants[i].fields[j].type)) - return 1; - } - return 0; -} - -static int lower_build_node(FeLowerPlan *plan, FeNode *node, - unsigned scope); - -static int lower_build_list(FeLowerPlan *plan, FeNode *node, - unsigned scope) -{ - while (node) { - if (!lower_build_node(plan, node, scope)) return 0; - node = node->next; - } - return 1; -} - -static int lower_build_block(FeLowerPlan *plan, FeNode *block, - unsigned parent) -{ - unsigned scope; - if (!block || block->kind != FE_N_BLOCK) return 1; - scope = lower_add_scope(plan, block, parent); - if (scope == FE_LOWER_NO_SCOPE) return 0; - return lower_build_list(plan, block->children, scope); -} - -static int lower_build_node(FeLowerPlan *plan, FeNode *node, - unsigned scope) -{ - FeNode *child; - FeType *type; - if (!node) return 1; - if (node->kind == FE_N_BLOCK) - return lower_build_block(plan, node, scope); - if (node->kind == FE_N_DEFER) { - if (!lower_add_cleanup(plan, scope, FE_LOWER_CLEANUP_DEFER, - node->a, node, 0)) - return 0; - return lower_build_node(plan, node->a, scope); - } - if (node->kind == FE_N_LET || node->kind == FE_N_VAR || - node->kind == FE_N_CONST) { - type = node->sem_type; - if (fe_lower_type_needs_drop(type)) - if (!lower_add_cleanup(plan, scope, FE_LOWER_CLEANUP_DROP, - node, node, type)) - return 0; - } - if (!lower_build_node(plan, node->a, scope)) return 0; - if (!lower_build_node(plan, node->b, scope)) return 0; - if (!lower_build_node(plan, node->c, scope)) return 0; - for (child = node->children; child; child = child->next) - if (!lower_build_node(plan, child, scope)) return 0; - return 1; -} - -void fe_lower_plan_init(FeLowerPlan *plan, FeArena *arena) -{ - if (!plan) return; - plan->arena = arena; - plan->fn = 0; - plan->scopes = 0; - plan->scope_count = 0; - plan->scope_capacity = 0; - plan->cleanups = 0; - plan->cleanup_count = 0; - plan->cleanup_capacity = 0; - plan->next_ordinal = 0; -} - -int fe_lower_plan_build(FeLowerPlan *plan, FeNode *fn) -{ - if (!plan || !plan->arena || !fn || fn->kind != FE_N_FN) return 0; - plan->fn = fn; - plan->scopes = 0; - plan->scope_count = 0; - plan->scope_capacity = 0; - plan->cleanups = 0; - plan->cleanup_count = 0; - plan->cleanup_capacity = 0; - plan->next_ordinal = 0; - if (!fn->c) return 1; - return lower_build_block(plan, fn->c, FE_LOWER_NO_SCOPE); -} - -unsigned fe_lower_scope_for_block(const FeLowerPlan *plan, - const FeNode *block) -{ - unsigned i; - if (!plan || !block) return FE_LOWER_NO_SCOPE; - for (i = 0; i < plan->scope_count; ++i) - if (plan->scopes[i].block == block) return i; - return FE_LOWER_NO_SCOPE; -} - -unsigned fe_lower_collect_cleanups(const FeLowerPlan *plan, - const FeNode *from_block, - const FeNode *stop_block, - const FeLowerCleanup **out, - unsigned out_capacity) -{ - unsigned scope; - unsigned stop; - unsigned i; - unsigned count; - if (!plan || !from_block) return 0; - scope = fe_lower_scope_for_block(plan, from_block); - stop = stop_block ? fe_lower_scope_for_block(plan, stop_block) : - FE_LOWER_NO_SCOPE; - count = 0; - while (scope != FE_LOWER_NO_SCOPE && scope != stop) { - for (i = plan->cleanup_count; i > 0; --i) { - if (plan->cleanups[i - 1U].scope != scope) continue; - if (out && count < out_capacity) - out[count] = &plan->cleanups[i - 1U]; - ++count; - } - scope = plan->scopes[scope].parent; - } - return count; -} - -int fe_lower_exit_runs_cleanup(FeLowerExitKind kind) -{ - return kind == FE_LOWER_EXIT_FALLTHROUGH || - kind == FE_LOWER_EXIT_RETURN || - kind == FE_LOWER_EXIT_ERROR_RETURN || - kind == FE_LOWER_EXIT_BREAK || - kind == FE_LOWER_EXIT_CONTINUE; -} - -int fe_lower_exit_leaves_function(FeLowerExitKind kind) -{ - return kind == FE_LOWER_EXIT_RETURN || - kind == FE_LOWER_EXIT_ERROR_RETURN; -} diff --git a/fec/src/lower.h b/fec/src/lower.h deleted file mode 100644 index bb2e544..0000000 --- a/fec/src/lower.h +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef FE_LOWER_H -#define FE_LOWER_H - -#include "types.h" - -#define FE_LOWER_NO_SCOPE ((unsigned)~0U) - -typedef enum FeLowerExitKind { - FE_LOWER_EXIT_FALLTHROUGH = 0, - FE_LOWER_EXIT_RETURN, - FE_LOWER_EXIT_ERROR_RETURN, - FE_LOWER_EXIT_BREAK, - FE_LOWER_EXIT_CONTINUE -} FeLowerExitKind; - -typedef enum FeLowerCleanupKind { - FE_LOWER_CLEANUP_DROP = 0, - FE_LOWER_CLEANUP_DEFER -} FeLowerCleanupKind; - -typedef struct FeLowerScope { - FeNode *block; - unsigned parent; - unsigned ordinal; -} FeLowerScope; - -typedef struct FeLowerCleanup { - FeLowerCleanupKind kind; - unsigned scope; - unsigned ordinal; - FeNode *node; - FeNode *decl; - FeType *type; -} FeLowerCleanup; - -typedef struct FeLowerPlan { - FeArena *arena; - FeNode *fn; - FeLowerScope *scopes; - unsigned scope_count; - unsigned scope_capacity; - FeLowerCleanup *cleanups; - unsigned cleanup_count; - unsigned cleanup_capacity; - unsigned next_ordinal; -} FeLowerPlan; - -void fe_lower_plan_init(FeLowerPlan *plan, FeArena *arena); -int fe_lower_plan_build(FeLowerPlan *plan, FeNode *fn); -unsigned fe_lower_scope_for_block(const FeLowerPlan *plan, - const FeNode *block); -unsigned fe_lower_collect_cleanups(const FeLowerPlan *plan, - const FeNode *from_block, - const FeNode *stop_block, - const FeLowerCleanup **out, - unsigned out_capacity); -int fe_lower_type_needs_drop(const FeType *type); -int fe_lower_exit_runs_cleanup(FeLowerExitKind kind); -int fe_lower_exit_leaves_function(FeLowerExitKind kind); - -#endif diff --git a/fec/tests/m4/bad-ari.fe b/fec/tests/format/bad-ari.fe similarity index 100% rename from fec/tests/m4/bad-ari.fe rename to fec/tests/format/bad-ari.fe diff --git a/fec/tests/m4/bad-bufw.fe b/fec/tests/format/bad-bufw.fe similarity index 100% rename from fec/tests/m4/bad-bufw.fe rename to fec/tests/format/bad-bufw.fe diff --git a/fec/tests/m4/bad-cls.fe b/fec/tests/format/bad-cls.fe similarity index 100% rename from fec/tests/m4/bad-cls.fe rename to fec/tests/format/bad-cls.fe diff --git a/fec/tests/m4/bad-many.fe b/fec/tests/format/bad-many.fe similarity index 100% rename from fec/tests/m4/bad-many.fe rename to fec/tests/format/bad-many.fe diff --git a/fec/tests/m4/bad-open.fe b/fec/tests/format/bad-open.fe similarity index 100% rename from fec/tests/m4/bad-open.fe rename to fec/tests/format/bad-open.fe diff --git a/fec/tests/m4/bad-run.fe b/fec/tests/format/bad-run.fe similarity index 100% rename from fec/tests/m4/bad-run.fe rename to fec/tests/format/bad-run.fe diff --git a/fec/tests/m4/bad-try.fe b/fec/tests/format/bad-try.fe similarity index 100% rename from fec/tests/m4/bad-try.fe rename to fec/tests/format/bad-try.fe diff --git a/fec/tests/m4/bad-type.fe b/fec/tests/format/bad-type.fe similarity index 100% rename from fec/tests/m4/bad-type.fe rename to fec/tests/format/bad-type.fe diff --git a/fec/tests/m4/bad-verb.fe b/fec/tests/format/bad-verb.fe similarity index 100% rename from fec/tests/m4/bad-verb.fe rename to fec/tests/format/bad-verb.fe diff --git a/fec/tests/m4/bad-writ.fe b/fec/tests/format/bad-writ.fe similarity index 100% rename from fec/tests/m4/bad-writ.fe rename to fec/tests/format/bad-writ.fe diff --git a/fec/tests/m4/format.fe b/fec/tests/format/ok-format.fe similarity index 100% rename from fec/tests/m4/format.fe rename to fec/tests/format/ok-format.fe diff --git a/fec/tests/m4/prop.fe b/fec/tests/format/ok-prop.fe similarity index 100% rename from fec/tests/m4/prop.fe rename to fec/tests/format/ok-prop.fe diff --git a/fec/tests/m4/try-fpr.fe b/fec/tests/format/ok-try-fpr.fe similarity index 100% rename from fec/tests/m4/try-fpr.fe rename to fec/tests/format/ok-try-fpr.fe diff --git a/fec/tests/m9/README.md b/fec/tests/generic/README.md similarity index 100% rename from fec/tests/m9/README.md rename to fec/tests/generic/README.md diff --git a/fec/tests/m9/badarg.fe b/fec/tests/generic/badarg.fe similarity index 100% rename from fec/tests/m9/badarg.fe rename to fec/tests/generic/badarg.fe diff --git a/fec/tests/m9/badarity.fe b/fec/tests/generic/badarity.fe similarity index 100% rename from fec/tests/m9/badarity.fe rename to fec/tests/generic/badarity.fe diff --git a/fec/tests/m9/badbody.fe b/fec/tests/generic/badbody.fe similarity index 100% rename from fec/tests/m9/badbody.fe rename to fec/tests/generic/badbody.fe diff --git a/fec/tests/m9/baddepth.fe b/fec/tests/generic/baddepth.fe similarity index 100% rename from fec/tests/m9/baddepth.fe rename to fec/tests/generic/baddepth.fe diff --git a/fec/tests/m9/baddist.fe b/fec/tests/generic/baddist.fe similarity index 100% rename from fec/tests/m9/baddist.fe rename to fec/tests/generic/baddist.fe diff --git a/fec/tests/m9/badfew.fe b/fec/tests/generic/badfew.fe similarity index 100% rename from fec/tests/m9/badfew.fe rename to fec/tests/generic/badfew.fe diff --git a/fec/tests/m9/badinfer.fe b/fec/tests/generic/badinfer.fe similarity index 100% rename from fec/tests/m9/badinfer.fe rename to fec/tests/generic/badinfer.fe diff --git a/fec/tests/m9/badop.fe b/fec/tests/generic/badop.fe similarity index 100% rename from fec/tests/m9/badop.fe rename to fec/tests/generic/badop.fe diff --git a/fec/tests/m9/badtype.fe b/fec/tests/generic/badtype.fe similarity index 100% rename from fec/tests/m9/badtype.fe rename to fec/tests/generic/badtype.fe diff --git a/fec/tests/m9/badvalue.fe b/fec/tests/generic/badvalue.fe similarity index 100% rename from fec/tests/m9/badvalue.fe rename to fec/tests/generic/badvalue.fe diff --git a/fec/tests/m9/defscope/lib.fe b/fec/tests/generic/defscope/lib.fe similarity index 100% rename from fec/tests/m9/defscope/lib.fe rename to fec/tests/generic/defscope/lib.fe diff --git a/fec/tests/m9/defscope/main.fe b/fec/tests/generic/defscope/main.fe similarity index 100% rename from fec/tests/m9/defscope/main.fe rename to fec/tests/generic/defscope/main.fe diff --git a/fec/tests/m9/okalias.fe b/fec/tests/generic/okalias.fe similarity index 100% rename from fec/tests/m9/okalias.fe rename to fec/tests/generic/okalias.fe diff --git a/fec/tests/m9/okbox.fe b/fec/tests/generic/okbox.fe similarity index 100% rename from fec/tests/m9/okbox.fe rename to fec/tests/generic/okbox.fe diff --git a/fec/tests/m9/okdedup.fe b/fec/tests/generic/okdedup.fe similarity index 100% rename from fec/tests/m9/okdedup.fe rename to fec/tests/generic/okdedup.fe diff --git a/fec/tests/m9/okid.fe b/fec/tests/generic/okid.fe similarity index 100% rename from fec/tests/m9/okid.fe rename to fec/tests/generic/okid.fe diff --git a/fec/tests/m9/okisint.fe b/fec/tests/generic/okisint.fe similarity index 100% rename from fec/tests/m9/okisint.fe rename to fec/tests/generic/okisint.fe diff --git a/fec/tests/m9/okmulti.fe b/fec/tests/generic/okmulti.fe similarity index 100% rename from fec/tests/m9/okmulti.fe rename to fec/tests/generic/okmulti.fe diff --git a/fec/tests/m9/oknested.fe b/fec/tests/generic/oknested.fe similarity index 100% rename from fec/tests/m9/oknested.fe rename to fec/tests/generic/oknested.fe diff --git a/fec/tests/m9/okpair.fe b/fec/tests/generic/okpair.fe similarity index 100% rename from fec/tests/m9/okpair.fe rename to fec/tests/generic/okpair.fe diff --git a/fec/tests/m9/oksamrec.fe b/fec/tests/generic/oksamrec.fe similarity index 100% rename from fec/tests/m9/oksamrec.fe rename to fec/tests/generic/oksamrec.fe diff --git a/fec/tests/m9/okscope/lib.fe b/fec/tests/generic/okscope/lib.fe similarity index 100% rename from fec/tests/m9/okscope/lib.fe rename to fec/tests/generic/okscope/lib.fe diff --git a/fec/tests/m9/okscope/main.fe b/fec/tests/generic/okscope/main.fe similarity index 100% rename from fec/tests/m9/okscope/main.fe rename to fec/tests/generic/okscope/main.fe diff --git a/fec/tests/m9/okskip.fe b/fec/tests/generic/okskip.fe similarity index 100% rename from fec/tests/m9/okskip.fe rename to fec/tests/generic/okskip.fe diff --git a/fec/tests/m9/oktypeeq.fe b/fec/tests/generic/oktypeeq.fe similarity index 100% rename from fec/tests/m9/oktypeeq.fe rename to fec/tests/generic/oktypeeq.fe diff --git a/fec/tests/m7/README.md b/fec/tests/optional/README.md similarity index 100% rename from fec/tests/m7/README.md rename to fec/tests/optional/README.md diff --git a/fec/tests/m7/badcatch.fe b/fec/tests/optional/badcatch.fe similarity index 100% rename from fec/tests/m7/badcatch.fe rename to fec/tests/optional/badcatch.fe diff --git a/fec/tests/m7/baddef.fe b/fec/tests/optional/baddef.fe similarity index 100% rename from fec/tests/m7/baddef.fe rename to fec/tests/optional/baddef.fe diff --git a/fec/tests/m7/baddir.fe b/fec/tests/optional/baddir.fe similarity index 100% rename from fec/tests/m7/baddir.fe rename to fec/tests/optional/baddir.fe diff --git a/fec/tests/m7/badercod.fe b/fec/tests/optional/badercod.fe similarity index 100% rename from fec/tests/m7/badercod.fe rename to fec/tests/optional/badercod.fe diff --git a/fec/tests/m7/badernam.fe b/fec/tests/optional/badernam.fe similarity index 100% rename from fec/tests/m7/badernam.fe rename to fec/tests/optional/badernam.fe diff --git a/fec/tests/m7/badetype.fe b/fec/tests/optional/badetype.fe similarity index 100% rename from fec/tests/m7/badetype.fe rename to fec/tests/optional/badetype.fe diff --git a/fec/tests/m7/badnull.fe b/fec/tests/optional/badnull.fe similarity index 100% rename from fec/tests/m7/badnull.fe rename to fec/tests/optional/badnull.fe diff --git a/fec/tests/m7/badoref.fe b/fec/tests/optional/badoref.fe similarity index 100% rename from fec/tests/m7/badoref.fe rename to fec/tests/optional/badoref.fe diff --git a/fec/tests/m7/badorel.fe b/fec/tests/optional/badorel.fe similarity index 100% rename from fec/tests/m7/badorel.fe rename to fec/tests/optional/badorel.fe diff --git a/fec/tests/m7/badproj.fe b/fec/tests/optional/badproj.fe similarity index 100% rename from fec/tests/m7/badproj.fe rename to fec/tests/optional/badproj.fe diff --git a/fec/tests/m7/badqmark.fe b/fec/tests/optional/badqmark.fe similarity index 100% rename from fec/tests/m7/badqmark.fe rename to fec/tests/optional/badqmark.fe diff --git a/fec/tests/m7/badret.fe b/fec/tests/optional/badret.fe similarity index 100% rename from fec/tests/m7/badret.fe rename to fec/tests/optional/badret.fe diff --git a/fec/tests/m7/badsome.fe b/fec/tests/optional/badsome.fe similarity index 100% rename from fec/tests/m7/badsome.fe rename to fec/tests/optional/badsome.fe diff --git a/fec/tests/m7/badtry.fe b/fec/tests/optional/badtry.fe similarity index 100% rename from fec/tests/m7/badtry.fe rename to fec/tests/optional/badtry.fe diff --git a/fec/tests/m7/badzero.fe b/fec/tests/optional/badzero.fe similarity index 100% rename from fec/tests/m7/badzero.fe rename to fec/tests/optional/badzero.fe diff --git a/fec/tests/m7/okcatch.fe b/fec/tests/optional/okcatch.fe similarity index 100% rename from fec/tests/m7/okcatch.fe rename to fec/tests/optional/okcatch.fe diff --git a/fec/tests/m7/okcatmov.fe b/fec/tests/optional/okcatmov.fe similarity index 100% rename from fec/tests/m7/okcatmov.fe rename to fec/tests/optional/okcatmov.fe diff --git a/fec/tests/m7/okcvoid.fe b/fec/tests/optional/okcvoid.fe similarity index 100% rename from fec/tests/m7/okcvoid.fe rename to fec/tests/optional/okcvoid.fe diff --git a/fec/tests/m7/okdeflt.fe b/fec/tests/optional/okdeflt.fe similarity index 100% rename from fec/tests/m7/okdeflt.fe rename to fec/tests/optional/okdeflt.fe diff --git a/fec/tests/m7/okiflet.fe b/fec/tests/optional/okiflet.fe similarity index 100% rename from fec/tests/m7/okiflet.fe rename to fec/tests/optional/okiflet.fe diff --git a/fec/tests/m7/okmatch.fe b/fec/tests/optional/okmatch.fe similarity index 100% rename from fec/tests/m7/okmatch.fe rename to fec/tests/optional/okmatch.fe diff --git a/fec/tests/m7/oknull.fe b/fec/tests/optional/oknull.fe similarity index 100% rename from fec/tests/m7/oknull.fe rename to fec/tests/optional/oknull.fe diff --git a/fec/tests/m7/okorelse.fe b/fec/tests/optional/okorelse.fe similarity index 100% rename from fec/tests/m7/okorelse.fe rename to fec/tests/optional/okorelse.fe diff --git a/fec/tests/m7/okpatvw.fe b/fec/tests/optional/okpatvw.fe similarity index 100% rename from fec/tests/m7/okpatvw.fe rename to fec/tests/optional/okpatvw.fe diff --git a/fec/tests/m7/okproj.fe b/fec/tests/optional/okproj.fe similarity index 100% rename from fec/tests/m7/okproj.fe rename to fec/tests/optional/okproj.fe diff --git a/fec/tests/m7/okrepl.fe b/fec/tests/optional/okrepl.fe similarity index 100% rename from fec/tests/m7/okrepl.fe rename to fec/tests/optional/okrepl.fe diff --git a/fec/tests/m7/oktrdef.fe b/fec/tests/optional/oktrdef.fe similarity index 100% rename from fec/tests/m7/oktrdef.fe rename to fec/tests/optional/oktrdef.fe diff --git a/fec/tests/m7/oktry.fe b/fec/tests/optional/oktry.fe similarity index 100% rename from fec/tests/m7/oktry.fe rename to fec/tests/optional/oktry.fe diff --git a/fec/tests/m6/README.md b/fec/tests/own/README.md similarity index 100% rename from fec/tests/m6/README.md rename to fec/tests/own/README.md diff --git a/fec/tests/m6/badarg.fe b/fec/tests/own/badarg.fe similarity index 100% rename from fec/tests/m6/badarg.fe rename to fec/tests/own/badarg.fe diff --git a/fec/tests/m6/badbinit.fe b/fec/tests/own/badbinit.fe similarity index 100% rename from fec/tests/m6/badbinit.fe rename to fec/tests/own/badbinit.fe diff --git a/fec/tests/m6/badbrmov.fe b/fec/tests/own/badbrmov.fe similarity index 100% rename from fec/tests/m6/badbrmov.fe rename to fec/tests/own/badbrmov.fe diff --git a/fec/tests/m6/baddefer.fe b/fec/tests/own/baddefer.fe similarity index 100% rename from fec/tests/m6/baddefer.fe rename to fec/tests/own/baddefer.fe diff --git a/fec/tests/m6/badfld.fe b/fec/tests/own/badfld.fe similarity index 100% rename from fec/tests/m6/badfld.fe rename to fec/tests/own/badfld.fe diff --git a/fec/tests/m6/badglob.fe b/fec/tests/own/badglob.fe similarity index 100% rename from fec/tests/m6/badglob.fe rename to fec/tests/own/badglob.fe diff --git a/fec/tests/m6/badgmut.fe b/fec/tests/own/badgmut.fe similarity index 100% rename from fec/tests/m6/badgmut.fe rename to fec/tests/own/badgmut.fe diff --git a/fec/tests/m6/badinv.fe b/fec/tests/own/badinv.fe similarity index 100% rename from fec/tests/m6/badinv.fe rename to fec/tests/own/badinv.fe diff --git a/fec/tests/m6/badlocsl.fe b/fec/tests/own/badlocsl.fe similarity index 100% rename from fec/tests/m6/badlocsl.fe rename to fec/tests/own/badlocsl.fe diff --git a/fec/tests/m6/badloop.fe b/fec/tests/own/badloop.fe similarity index 100% rename from fec/tests/m6/badloop.fe rename to fec/tests/own/badloop.fe diff --git a/fec/tests/m6/badmove.fe b/fec/tests/own/badmove.fe similarity index 100% rename from fec/tests/m6/badmove.fe rename to fec/tests/own/badmove.fe diff --git a/fec/tests/m6/badmut.fe b/fec/tests/own/badmut.fe similarity index 100% rename from fec/tests/m6/badmut.fe rename to fec/tests/own/badmut.fe diff --git a/fec/tests/m6/badmut2.fe b/fec/tests/own/badmut2.fe similarity index 100% rename from fec/tests/m6/badmut2.fe rename to fec/tests/own/badmut2.fe diff --git a/fec/tests/m6/badptr.fe b/fec/tests/own/badptr.fe similarity index 100% rename from fec/tests/m6/badptr.fe rename to fec/tests/own/badptr.fe diff --git a/fec/tests/m6/badret.fe b/fec/tests/own/badret.fe similarity index 100% rename from fec/tests/m6/badret.fe rename to fec/tests/own/badret.fe diff --git a/fec/tests/m6/badrfld.fe b/fec/tests/own/badrfld.fe similarity index 100% rename from fec/tests/m6/badrfld.fe rename to fec/tests/own/badrfld.fe diff --git a/fec/tests/m6/badridx.fe b/fec/tests/own/badridx.fe similarity index 100% rename from fec/tests/m6/badridx.fe rename to fec/tests/own/badridx.fe diff --git a/fec/tests/m6/badscop.fe b/fec/tests/own/badscop.fe similarity index 100% rename from fec/tests/m6/badscop.fe rename to fec/tests/own/badscop.fe diff --git a/fec/tests/m6/badself.fe b/fec/tests/own/badself.fe similarity index 100% rename from fec/tests/m6/badself.fe rename to fec/tests/own/badself.fe diff --git a/fec/tests/m6/badshwr.fe b/fec/tests/own/badshwr.fe similarity index 100% rename from fec/tests/m6/badshwr.fe rename to fec/tests/own/badshwr.fe diff --git a/fec/tests/m6/badslfld.fe b/fec/tests/own/badslfld.fe similarity index 100% rename from fec/tests/m6/badslfld.fe rename to fec/tests/own/badslfld.fe diff --git a/fec/tests/m6/badtwo.fe b/fec/tests/own/badtwo.fe similarity index 100% rename from fec/tests/m6/badtwo.fe rename to fec/tests/own/badtwo.fe diff --git a/fec/tests/m6/badup.fe b/fec/tests/own/badup.fe similarity index 100% rename from fec/tests/m6/badup.fe rename to fec/tests/own/badup.fe diff --git a/fec/tests/m6/badweak.fe b/fec/tests/own/badweak.fe similarity index 100% rename from fec/tests/m6/badweak.fe rename to fec/tests/own/badweak.fe diff --git a/fec/tests/m5/defer.fe b/fec/tests/own/ok-defer.fe similarity index 100% rename from fec/tests/m5/defer.fe rename to fec/tests/own/ok-defer.fe diff --git a/fec/tests/m5/owned.fe b/fec/tests/own/ok-owned.fe similarity index 100% rename from fec/tests/m5/owned.fe rename to fec/tests/own/ok-owned.fe diff --git a/fec/tests/m6/okbranch.fe b/fec/tests/own/okbranch.fe similarity index 100% rename from fec/tests/m6/okbranch.fe rename to fec/tests/own/okbranch.fe diff --git a/fec/tests/m6/okdefer.fe b/fec/tests/own/okdefer.fe similarity index 100% rename from fec/tests/m6/okdefer.fe rename to fec/tests/own/okdefer.fe diff --git a/fec/tests/m6/okglobcp.fe b/fec/tests/own/okglobcp.fe similarity index 100% rename from fec/tests/m6/okglobcp.fe rename to fec/tests/own/okglobcp.fe diff --git a/fec/tests/m6/oklast.fe b/fec/tests/own/oklast.fe similarity index 100% rename from fec/tests/m6/oklast.fe rename to fec/tests/own/oklast.fe diff --git a/fec/tests/m6/okr8free.fe b/fec/tests/own/okr8free.fe similarity index 100% rename from fec/tests/m6/okr8free.fe rename to fec/tests/own/okr8free.fe diff --git a/fec/tests/m6/okr8join.fe b/fec/tests/own/okr8join.fe similarity index 100% rename from fec/tests/m6/okr8join.fe rename to fec/tests/own/okr8join.fe diff --git a/fec/tests/m6/okr8meth.fe b/fec/tests/own/okr8meth.fe similarity index 100% rename from fec/tests/m6/okr8meth.fe rename to fec/tests/own/okr8meth.fe diff --git a/fec/tests/m6/okr8stat.fe b/fec/tests/own/okr8stat.fe similarity index 100% rename from fec/tests/m6/okr8stat.fe rename to fec/tests/own/okr8stat.fe diff --git a/fec/tests/m6/okrebor.fe b/fec/tests/own/okrebor.fe similarity index 100% rename from fec/tests/m6/okrebor.fe rename to fec/tests/own/okrebor.fe diff --git a/fec/tests/m6/okrtlast.fe b/fec/tests/own/okrtlast.fe similarity index 100% rename from fec/tests/m6/okrtlast.fe rename to fec/tests/own/okrtlast.fe diff --git a/fec/tests/m6/okshare.fe b/fec/tests/own/okshare.fe similarity index 100% rename from fec/tests/m6/okshare.fe rename to fec/tests/own/okshare.fe diff --git a/fec/tests/m6/okslreb.fe b/fec/tests/own/okslreb.fe similarity index 100% rename from fec/tests/m6/okslreb.fe rename to fec/tests/own/okslreb.fe diff --git a/fec/tests/m6/okstatic.fe b/fec/tests/own/okstatic.fe similarity index 100% rename from fec/tests/m6/okstatic.fe rename to fec/tests/own/okstatic.fe diff --git a/fec/tests/m6/oktemp.fe b/fec/tests/own/oktemp.fe similarity index 100% rename from fec/tests/m6/oktemp.fe rename to fec/tests/own/oktemp.fe diff --git a/fec/tests/m6/oktrim.fe b/fec/tests/own/oktrim.fe similarity index 100% rename from fec/tests/m6/oktrim.fe rename to fec/tests/own/oktrim.fe diff --git a/fec/tests/m6/okwcall.fe b/fec/tests/own/okwcall.fe similarity index 100% rename from fec/tests/m6/okwcall.fe rename to fec/tests/own/okwcall.fe diff --git a/fec/tests/m5/bad-clos.fe b/fec/tests/own/own-bad-clos.fe similarity index 100% rename from fec/tests/m5/bad-clos.fe rename to fec/tests/own/own-bad-clos.fe diff --git a/fec/tests/m5/bad-cond.fe b/fec/tests/own/own-bad-cond.fe similarity index 100% rename from fec/tests/m5/bad-cond.fe rename to fec/tests/own/own-bad-cond.fe diff --git a/fec/tests/m5/bad-dbl.fe b/fec/tests/own/own-bad-dbl.fe similarity index 100% rename from fec/tests/m5/bad-dbl.fe rename to fec/tests/own/own-bad-dbl.fe diff --git a/fec/tests/m5/bad-dest.fe b/fec/tests/own/own-bad-dest.fe similarity index 100% rename from fec/tests/m5/bad-dest.fe rename to fec/tests/own/own-bad-dest.fe diff --git a/fec/tests/m5/bad-drop.fe b/fec/tests/own/own-bad-drop.fe similarity index 100% rename from fec/tests/m5/bad-drop.fe rename to fec/tests/own/own-bad-drop.fe diff --git a/fec/tests/m5/bad-loop.fe b/fec/tests/own/own-bad-loop.fe similarity index 100% rename from fec/tests/m5/bad-loop.fe rename to fec/tests/own/own-bad-loop.fe diff --git a/fec/tests/m5/bad-move.fe b/fec/tests/own/own-bad-move.fe similarity index 100% rename from fec/tests/m5/bad-move.fe rename to fec/tests/own/own-bad-move.fe diff --git a/fec/tests/m5/bad-proj.fe b/fec/tests/own/own-bad-proj.fe similarity index 100% rename from fec/tests/m5/bad-proj.fe rename to fec/tests/own/own-bad-proj.fe diff --git a/fec/tests/pass/basic.fe b/fec/tests/parse/basic.fe similarity index 100% rename from fec/tests/pass/basic.fe rename to fec/tests/parse/basic.fe diff --git a/fec/tests/pass/keybuilt.fe b/fec/tests/parse/keybuilt.fe similarity index 100% rename from fec/tests/pass/keybuilt.fe rename to fec/tests/parse/keybuilt.fe diff --git a/fec/tests/pass/literals.fe b/fec/tests/parse/literals.fe similarity index 100% rename from fec/tests/pass/literals.fe rename to fec/tests/parse/literals.fe diff --git a/fec/tests/fail/logical.fe b/fec/tests/parse/logical.fe similarity index 100% rename from fec/tests/fail/logical.fe rename to fec/tests/parse/logical.fe diff --git a/fec/tests/fail/misssemi.fe b/fec/tests/parse/misssemi.fe similarity index 100% rename from fec/tests/fail/misssemi.fe rename to fec/tests/parse/misssemi.fe diff --git a/fec/tests/fail/unclcomm.fe b/fec/tests/parse/unclcomm.fe similarity index 100% rename from fec/tests/fail/unclcomm.fe rename to fec/tests/parse/unclcomm.fe diff --git a/fec/tests/pass/v012form.fe b/fec/tests/parse/v012form.fe similarity index 100% rename from fec/tests/pass/v012form.fe rename to fec/tests/parse/v012form.fe diff --git a/fec/tests/m3/nochk.fe b/fec/tests/pending-backend/bounds-nocheck.fe similarity index 100% rename from fec/tests/m3/nochk.fe rename to fec/tests/pending-backend/bounds-nocheck.fe diff --git a/fec/tests/m3/bounds.fe b/fec/tests/pending-backend/bounds-trap.fe similarity index 100% rename from fec/tests/m3/bounds.fe rename to fec/tests/pending-backend/bounds-trap.fe diff --git a/fec/tests/m4/proptest.c b/fec/tests/pending-backend/format-prop.c similarity index 100% rename from fec/tests/m4/proptest.c rename to fec/tests/pending-backend/format-prop.c diff --git a/fec/tests/m5/runtime.c b/fec/tests/pending-backend/ownership-drop.c similarity index 100% rename from fec/tests/m5/runtime.c rename to fec/tests/pending-backend/ownership-drop.c diff --git a/fec/tests/m5/runtime.fe b/fec/tests/pending-backend/ownership-drop.fe similarity index 100% rename from fec/tests/m5/runtime.fe rename to fec/tests/pending-backend/ownership-drop.fe diff --git a/fec/tests/m3/slcbound.fe b/fec/tests/pending-backend/slice-bounds-trap.fe similarity index 100% rename from fec/tests/m3/slcbound.fe rename to fec/tests/pending-backend/slice-bounds-trap.fe diff --git a/fec/tests/m2/bad-ari.fe b/fec/tests/types/bad-ari.fe similarity index 100% rename from fec/tests/m2/bad-ari.fe rename to fec/tests/types/bad-ari.fe diff --git a/fec/tests/m2/bad-asgn.fe b/fec/tests/types/bad-asgn.fe similarity index 100% rename from fec/tests/m2/bad-asgn.fe rename to fec/tests/types/bad-asgn.fe diff --git a/fec/tests/m2/bad-cast.fe b/fec/tests/types/bad-cast.fe similarity index 100% rename from fec/tests/m2/bad-cast.fe rename to fec/tests/types/bad-cast.fe diff --git a/fec/tests/m2/bad-cond.fe b/fec/tests/types/bad-cond.fe similarity index 100% rename from fec/tests/m2/bad-cond.fe rename to fec/tests/types/bad-cond.fe diff --git a/fec/tests/m3/bad-mlet.fe b/fec/tests/types/bad-mlet.fe similarity index 100% rename from fec/tests/m3/bad-mlet.fe rename to fec/tests/types/bad-mlet.fe diff --git a/fec/tests/m2/bad-ret.fe b/fec/tests/types/bad-ret.fe similarity index 100% rename from fec/tests/m2/bad-ret.fe rename to fec/tests/types/bad-ret.fe diff --git a/fec/tests/m3/bad-shwr.fe b/fec/tests/types/bad-shwr.fe similarity index 100% rename from fec/tests/m3/bad-shwr.fe rename to fec/tests/types/bad-shwr.fe diff --git a/fec/tests/m2/bad-type.fe b/fec/tests/types/bad-type.fe similarity index 100% rename from fec/tests/m2/bad-type.fe rename to fec/tests/types/bad-type.fe diff --git a/fec/tests/m2/bad-unit.fe b/fec/tests/types/bad-unit.fe similarity index 100% rename from fec/tests/m2/bad-unit.fe rename to fec/tests/types/bad-unit.fe diff --git a/fec/tests/m2/bad-unk.fe b/fec/tests/types/bad-unk.fe similarity index 100% rename from fec/tests/m2/bad-unk.fe rename to fec/tests/types/bad-unk.fe diff --git a/fec/tests/m2/bad-void.fe b/fec/tests/types/bad-void.fe similarity index 100% rename from fec/tests/m2/bad-void.fe rename to fec/tests/types/bad-void.fe diff --git a/fec/tests/m3/badarr.fe b/fec/tests/types/badarr.fe similarity index 100% rename from fec/tests/m3/badarr.fe rename to fec/tests/types/badarr.fe diff --git a/fec/tests/m3/badchar.fe b/fec/tests/types/badchar.fe similarity index 100% rename from fec/tests/m3/badchar.fe rename to fec/tests/types/badchar.fe diff --git a/fec/tests/m3/badcycle.fe b/fec/tests/types/badcycle.fe similarity index 100% rename from fec/tests/m3/badcycle.fe rename to fec/tests/types/badcycle.fe diff --git a/fec/tests/m3/badfield.fe b/fec/tests/types/badfield.fe similarity index 100% rename from fec/tests/m3/badfield.fe rename to fec/tests/types/badfield.fe diff --git a/fec/tests/m3/badfld.fe b/fec/tests/types/badfld.fe similarity index 100% rename from fec/tests/m3/badfld.fe rename to fec/tests/types/badfld.fe diff --git a/fec/tests/m3/badindex.fe b/fec/tests/types/badindex.fe similarity index 100% rename from fec/tests/m3/badindex.fe rename to fec/tests/types/badindex.fe diff --git a/fec/tests/m3/badmat.fe b/fec/tests/types/badmat.fe similarity index 100% rename from fec/tests/m3/badmat.fe rename to fec/tests/types/badmat.fe diff --git a/fec/tests/m3/badstr.fe b/fec/tests/types/badstr.fe similarity index 100% rename from fec/tests/m3/badstr.fe rename to fec/tests/types/badstr.fe diff --git a/fec/tests/m3/array.fe b/fec/tests/types/ok-array.fe similarity index 100% rename from fec/tests/m3/array.fe rename to fec/tests/types/ok-array.fe diff --git a/fec/tests/m3/arrayctx.fe b/fec/tests/types/ok-arrayctx.fe similarity index 100% rename from fec/tests/m3/arrayctx.fe rename to fec/tests/types/ok-arrayctx.fe diff --git a/fec/tests/m2/castwhil.fe b/fec/tests/types/ok-castwhil.fe similarity index 100% rename from fec/tests/m2/castwhil.fe rename to fec/tests/types/ok-castwhil.fe diff --git a/fec/tests/m3/char.fe b/fec/tests/types/ok-char.fe similarity index 100% rename from fec/tests/m3/char.fe rename to fec/tests/types/ok-char.fe diff --git a/fec/tests/m3/enum.fe b/fec/tests/types/ok-enum.fe similarity index 100% rename from fec/tests/m3/enum.fe rename to fec/tests/types/ok-enum.fe diff --git a/fec/tests/m3/for.fe b/fec/tests/types/ok-for.fe similarity index 100% rename from fec/tests/m3/for.fe rename to fec/tests/types/ok-for.fe diff --git a/fec/tests/m2/hello.fe b/fec/tests/types/ok-hello.fe similarity index 100% rename from fec/tests/m2/hello.fe rename to fec/tests/types/ok-hello.fe diff --git a/fec/tests/m3/mutable.fe b/fec/tests/types/ok-mutable.fe similarity index 100% rename from fec/tests/m3/mutable.fe rename to fec/tests/types/ok-mutable.fe diff --git a/fec/tests/m3/nested.fe b/fec/tests/types/ok-nested.fe similarity index 100% rename from fec/tests/m3/nested.fe rename to fec/tests/types/ok-nested.fe diff --git a/fec/tests/m2/scopes.fe b/fec/tests/types/ok-scopes.fe similarity index 100% rename from fec/tests/m2/scopes.fe rename to fec/tests/types/ok-scopes.fe diff --git a/fec/tests/m3/str.fe b/fec/tests/types/ok-str.fe similarity index 100% rename from fec/tests/m3/str.fe rename to fec/tests/types/ok-str.fe diff --git a/fec/tests/m3/struct.fe b/fec/tests/types/ok-struct.fe similarity index 100% rename from fec/tests/m3/struct.fe rename to fec/tests/types/ok-struct.fe diff --git a/fec/tests/m8/README.md b/fec/tests/units/README.md similarity index 100% rename from fec/tests/m8/README.md rename to fec/tests/units/README.md diff --git a/fec/tests/m8/alias/acme/math.fe b/fec/tests/units/alias/acme/math.fe similarity index 100% rename from fec/tests/m8/alias/acme/math.fe rename to fec/tests/units/alias/acme/math.fe diff --git a/fec/tests/m8/alias/main.fe b/fec/tests/units/alias/main.fe similarity index 100% rename from fec/tests/m8/alias/main.fe rename to fec/tests/units/alias/main.fe diff --git a/fec/tests/m8/badlong/main.fe b/fec/tests/units/badlong/main.fe similarity index 100% rename from fec/tests/m8/badlong/main.fe rename to fec/tests/units/badlong/main.fe diff --git a/fec/tests/m8/badupper/main.fe b/fec/tests/units/badupper/main.fe similarity index 100% rename from fec/tests/m8/badupper/main.fe rename to fec/tests/units/badupper/main.fe diff --git a/fec/tests/m8/basic/main.fe b/fec/tests/units/basic/main.fe similarity index 100% rename from fec/tests/m8/basic/main.fe rename to fec/tests/units/basic/main.fe diff --git a/fec/tests/m8/basic/util.fe b/fec/tests/units/basic/util.fe similarity index 100% rename from fec/tests/m8/basic/util.fe rename to fec/tests/units/basic/util.fe diff --git a/fec/tests/m8/bindconf/alpha/net.fe b/fec/tests/units/bindconf/alpha/net.fe similarity index 100% rename from fec/tests/m8/bindconf/alpha/net.fe rename to fec/tests/units/bindconf/alpha/net.fe diff --git a/fec/tests/m8/bindconf/beta/net.fe b/fec/tests/units/bindconf/beta/net.fe similarity index 100% rename from fec/tests/m8/bindconf/beta/net.fe rename to fec/tests/units/bindconf/beta/net.fe diff --git a/fec/tests/m8/bindconf/main.fe b/fec/tests/units/bindconf/main.fe similarity index 100% rename from fec/tests/m8/bindconf/main.fe rename to fec/tests/units/bindconf/main.fe diff --git a/fec/tests/m8/cycle/a.fe b/fec/tests/units/cycle/a.fe similarity index 100% rename from fec/tests/m8/cycle/a.fe rename to fec/tests/units/cycle/a.fe diff --git a/fec/tests/m8/cycle/b.fe b/fec/tests/units/cycle/b.fe similarity index 100% rename from fec/tests/m8/cycle/b.fe rename to fec/tests/units/cycle/b.fe diff --git a/fec/tests/m8/dotpriv/game/bar.fe b/fec/tests/units/dotpriv/game/bar.fe similarity index 100% rename from fec/tests/m8/dotpriv/game/bar.fe rename to fec/tests/units/dotpriv/game/bar.fe diff --git a/fec/tests/m8/dotpriv/game/foo.fe b/fec/tests/units/dotpriv/game/foo.fe similarity index 100% rename from fec/tests/m8/dotpriv/game/foo.fe rename to fec/tests/units/dotpriv/game/foo.fe diff --git a/fec/tests/m8/dotpriv/main.fe b/fec/tests/units/dotpriv/main.fe similarity index 100% rename from fec/tests/m8/dotpriv/main.fe rename to fec/tests/units/dotpriv/main.fe diff --git a/fec/tests/m8/dotted/acme/math.fe b/fec/tests/units/dotted/acme/math.fe similarity index 100% rename from fec/tests/m8/dotted/acme/math.fe rename to fec/tests/units/dotted/acme/math.fe diff --git a/fec/tests/m8/dotted/main.fe b/fec/tests/units/dotted/main.fe similarity index 100% rename from fec/tests/m8/dotted/main.fe rename to fec/tests/units/dotted/main.fe diff --git a/fec/tests/m8/errdet/alpha.fe b/fec/tests/units/errdet/alpha.fe similarity index 100% rename from fec/tests/m8/errdet/alpha.fe rename to fec/tests/units/errdet/alpha.fe diff --git a/fec/tests/m8/errdet/beta.fe b/fec/tests/units/errdet/beta.fe similarity index 100% rename from fec/tests/m8/errdet/beta.fe rename to fec/tests/units/errdet/beta.fe diff --git a/fec/tests/m8/errdet/main.fe b/fec/tests/units/errdet/main.fe similarity index 100% rename from fec/tests/m8/errdet/main.fe rename to fec/tests/units/errdet/main.fe diff --git a/fec/tests/m8/errnom/lib.fe b/fec/tests/units/errnom/lib.fe similarity index 100% rename from fec/tests/m8/errnom/lib.fe rename to fec/tests/units/errnom/lib.fe diff --git a/fec/tests/m8/errnom/main.fe b/fec/tests/units/errnom/main.fe similarity index 100% rename from fec/tests/m8/errnom/main.fe rename to fec/tests/units/errnom/main.fe diff --git a/fec/tests/m8/errsame/alpha.fe b/fec/tests/units/errsame/alpha.fe similarity index 100% rename from fec/tests/m8/errsame/alpha.fe rename to fec/tests/units/errsame/alpha.fe diff --git a/fec/tests/m8/errsame/beta.fe b/fec/tests/units/errsame/beta.fe similarity index 100% rename from fec/tests/m8/errsame/beta.fe rename to fec/tests/units/errsame/beta.fe diff --git a/fec/tests/m8/errsame/main.fe b/fec/tests/units/errsame/main.fe similarity index 100% rename from fec/tests/m8/errsame/main.fe rename to fec/tests/units/errsame/main.fe diff --git a/fec/tests/m8/missing/main.fe b/fec/tests/units/missing/main.fe similarity index 100% rename from fec/tests/m8/missing/main.fe rename to fec/tests/units/missing/main.fe diff --git a/fec/tests/m8/privfld/data.fe b/fec/tests/units/privfld/data.fe similarity index 100% rename from fec/tests/m8/privfld/data.fe rename to fec/tests/units/privfld/data.fe diff --git a/fec/tests/m8/privfld/main.fe b/fec/tests/units/privfld/main.fe similarity index 100% rename from fec/tests/m8/privfld/main.fe rename to fec/tests/units/privfld/main.fe diff --git a/fec/tests/m8/privfn/main.fe b/fec/tests/units/privfn/main.fe similarity index 100% rename from fec/tests/m8/privfn/main.fe rename to fec/tests/units/privfn/main.fe diff --git a/fec/tests/m8/privfn/util.fe b/fec/tests/units/privfn/util.fe similarity index 100% rename from fec/tests/m8/privfn/util.fe rename to fec/tests/units/privfn/util.fe diff --git a/fec/tests/m8/pubfld/data.fe b/fec/tests/units/pubfld/data.fe similarity index 100% rename from fec/tests/m8/pubfld/data.fe rename to fec/tests/units/pubfld/data.fe diff --git a/fec/tests/m8/pubfld/main.fe b/fec/tests/units/pubfld/main.fe similarity index 100% rename from fec/tests/m8/pubfld/main.fe rename to fec/tests/units/pubfld/main.fe diff --git a/fec/tests/m8/pubpriv/lib.fe b/fec/tests/units/pubpriv/lib.fe similarity index 100% rename from fec/tests/m8/pubpriv/lib.fe rename to fec/tests/units/pubpriv/lib.fe diff --git a/fec/tests/m8/pubpriv/main.fe b/fec/tests/units/pubpriv/main.fe similarity index 100% rename from fec/tests/m8/pubpriv/main.fe rename to fec/tests/units/pubpriv/main.fe diff --git a/fec/tests/m8/unitbad/main.fe b/fec/tests/units/unitbad/main.fe similarity index 100% rename from fec/tests/m8/unitbad/main.fe rename to fec/tests/units/unitbad/main.fe diff --git a/src/ferrolang_vm/__init__.py b/src/ferrolang_vm/__init__.py deleted file mode 100644 index a63f890..0000000 --- a/src/ferrolang_vm/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Reproducible DOS development tools for the Ferro compiler.""" diff --git a/src/ferrolang_vm/dos_cli.py b/src/ferrolang_vm/dos_cli.py deleted file mode 100644 index 02a0567..0000000 --- a/src/ferrolang_vm/dos_cli.py +++ /dev/null @@ -1,80 +0,0 @@ -"""General-purpose disposable DOSBox-X/Open Watcom environment CLI.""" -from __future__ import annotations - -import argparse -import subprocess -import sys -from pathlib import Path - -from .dosboxx import DosboxError, run_suite, setup -from .paths import ROOT -from .suite import Case - - -def _run(command: str | None, *, keep: bool, show_dos: bool) -> int: - cases = [] if command is None else [Case("command", 0, command, True)] - run = run_suite(cases, keep=keep, show_dos=show_dos, trace_dos=show_dos) - try: - if run.result() != "PASS": - print(run.log(), file=sys.stderr) - return 1 - if cases and run.result(cases[0]) != "PASS": - print(run.log(cases[0]), file=sys.stderr) - return 1 - if cases: - output = run.log(cases[0]) - if output: - print(output, end="" if output.endswith("\n") else "\n") - if keep: - print(f"DOS workspace: {run.root}") - return 0 - finally: - run.cleanup() - - -def main() -> int: - parser = argparse.ArgumentParser( - prog="ferro-dos", - description="Disposable directory-backed DOSBox-X/Open Watcom environment.", - ) - commands = parser.add_subparsers(dest="action", required=True) - prepare = commands.add_parser("setup", help="install the pinned DOSBox-X and Open Watcom tools") - prepare.add_argument("--accept-watcom-license", action="store_true") - for name, help_text in ( - ("build", "build the current FEC source inside DOS"), - ("exec", "build FEC and execute one DOS command"), - ("batch", "build FEC and call a repository DOS batch"), - ("shell", "build FEC and open an interactive DOS shell"), - ): - command = commands.add_parser(name, help=help_text) - command.add_argument("--keep", action="store_true", help="preserve the temporary DOS workspace") - command.add_argument("--show-dos", action="store_true", help="show and pause the DOS window") - if name == "exec": - command.add_argument("dos_command") - elif name == "batch": - command.add_argument("path", type=Path) - args = parser.parse_args() - try: - if args.action == "setup": - dosbox, watcom = setup(accept_watcom_license=args.accept_watcom_license) - print(f"DOSBox-X: {dosbox}") - print(f"Open Watcom: {watcom}") - return 0 - if args.action == "build": - return _run(None, keep=args.keep, show_dos=args.show_dos) - if args.action == "exec": - return _run(args.dos_command, keep=args.keep, show_dos=args.show_dos) - if args.action == "shell": - return _run("COMMAND.COM", keep=args.keep, show_dos=True) - path = (ROOT / args.path).resolve() - if (path != ROOT and ROOT not in path.parents) or not path.is_file(): - raise DosboxError("batch path must be an existing file inside the repository") - relative = path.relative_to(ROOT).as_posix().replace("/", "\\").upper() - return _run(f"CALL R:\\{relative}", keep=args.keep, show_dos=args.show_dos) - except (DosboxError, subprocess.SubprocessError) as exc: - print(f"ferro-dos: {exc}", file=sys.stderr) - return 2 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/src/ferrolang_vm/dosboxx.py b/src/ferrolang_vm/dosboxx.py deleted file mode 100644 index 0dcc2b3..0000000 --- a/src/ferrolang_vm/dosboxx.py +++ /dev/null @@ -1,332 +0,0 @@ -"""Reproducible DOSBox-X/Open Watcom development test backend.""" -from __future__ import annotations - -import hashlib -import json -import os -import shutil -import subprocess -import tempfile -import urllib.request -import zipfile -from dataclasses import dataclass -from pathlib import Path - -from .paths import ROOT -from .suite import Case - - -CACHE = ROOT / ".dosboxx" -LOCK_PATH = ROOT / "tools" / "toolchains" / "dosboxx.lock.json" -RUNS = CACHE / "runs" - - -class DosboxError(RuntimeError): - pass - - -def _lock() -> dict[str, object]: - return json.loads(LOCK_PATH.read_text(encoding="utf-8")) - - -def _sha256(path: Path) -> str: - digest = hashlib.sha256() - with path.open("rb") as source: - while chunk := source.read(1024 * 1024): - digest.update(chunk) - return digest.hexdigest() - - -def _download(name: str, spec: dict[str, object]) -> Path: - downloads = CACHE / "downloads" - downloads.mkdir(parents=True, exist_ok=True) - target = downloads / Path(str(spec["url"])).name - expected = str(spec["sha256"]).lower() - if target.is_file() and _sha256(target) == expected: - return target - target.unlink(missing_ok=True) - partial = target.with_suffix(target.suffix + ".part") - partial.unlink(missing_ok=True) - print(f"ferro-test: downloading {name} {spec['version']}...") - try: - with urllib.request.urlopen(str(spec["url"])) as response, partial.open("wb") as output: - shutil.copyfileobj(response, output, length=1024 * 1024) - except Exception: - partial.unlink(missing_ok=True) - raise - actual = _sha256(partial) - if actual != expected: - partial.unlink(missing_ok=True) - raise DosboxError(f"{name} SHA-256 mismatch: expected {expected}, got {actual}") - partial.replace(target) - return target - - -def _safe_extract(archive: Path, destination: Path) -> None: - destination.parent.mkdir(parents=True, exist_ok=True) - temporary = Path(tempfile.mkdtemp(prefix=f".{destination.name}-", dir=destination.parent)) - try: - with zipfile.ZipFile(archive) as bundle: - root = temporary.resolve() - for member in bundle.infolist(): - target = (temporary / member.filename).resolve() - if target != root and root not in target.parents: - raise DosboxError(f"unsafe archive member: {member.filename}") - bundle.extractall(temporary) - if destination.exists(): - shutil.rmtree(destination) - temporary.replace(destination) - except Exception: - shutil.rmtree(temporary, ignore_errors=True) - raise - - -def _required_paths(lock: dict[str, object]) -> tuple[Path, Path, list[Path]]: - dosbox_spec = lock["dosboxx"] - watcom_spec = lock["open_watcom"] - assert isinstance(dosbox_spec, dict) and isinstance(watcom_spec, dict) - dosbox = CACHE / "dosbox-x" / str(dosbox_spec["executable"]) - watcom = CACHE / "watcom" - required = [watcom / str(item) for item in watcom_spec["required"]] - return dosbox, watcom, required - - -def setup(*, accept_watcom_license: bool = False) -> tuple[Path, Path]: - if os.name != "nt": - raise DosboxError("the DOSBox-X development backend currently supports Windows only") - lock = _lock() - dosbox, watcom, watcom_required = _required_paths(lock) - tools_ready = dosbox.is_file() and all(path.is_file() for path in watcom_required) - if not tools_ready and not accept_watcom_license: - raise DosboxError( - "Open Watcom is distributed under the Sybase Open Watcom Public License. " - "Review tools/toolchains/dosboxx.lock.json and rerun " - "`uv run ferro-dos setup --accept-watcom-license`." - ) - if not tools_ready: - dosbox_spec = lock["dosboxx"] - watcom_spec = lock["open_watcom"] - assert isinstance(dosbox_spec, dict) and isinstance(watcom_spec, dict) - _safe_extract(_download("DOSBox-X", dosbox_spec), CACHE / "dosbox-x") - _safe_extract(_download("Open Watcom", watcom_spec), watcom) - dosbox, watcom, watcom_required = _required_paths(lock) - missing = [str(path.relative_to(CACHE)) for path in [dosbox, *watcom_required] - if not path.is_file()] - if missing: - raise DosboxError("toolchain archive is missing: " + ", ".join(missing)) - (CACHE / "SETUP.OK").write_text(_sha256(LOCK_PATH) + "\n", encoding="ascii") - return dosbox, watcom - - -def resolve_tools() -> tuple[Path, Path]: - lock = _lock() - dosbox, watcom, required = _required_paths(lock) - if not dosbox.is_file() or not all(path.is_file() for path in required): - raise DosboxError("toolchain is not installed; run `uv run ferro-dos setup`") - return dosbox, watcom - - -# ``if errorlevel N`` in DOS tests ``>= N``, so an exact code needs a descending -# ladder. Small values get their own rung because they carry the meaning -- 1 is -# an ordinary compiler error, 3 is Watcom's abort() -- while anything above 8 is -# bucketed to a lower bound, which is enough to tell a crash from a diagnostic. -_RC_LADDER = (255, 128, 64, 32, 16, 8, 7, 6, 5, 4, 3, 2, 1) - - -def _rc_batch() -> str: - """Batch helper that records the previous command's exit code. - - Called as ``call RC.BAT `` right after a case command, since anything - else -- including writing a file -- would clobber ERRORLEVEL first. Note the - space before each ``>``: ``echo 0>FILE`` would parse as a redirect of handle - 0 rather than an echo of "0", so the value is written with a trailing space - and stripped on the host. - """ - lines = ["@echo off"] - lines.extend(f"if errorlevel {value} goto R{value}" for value in _RC_LADDER) - lines.extend(["echo 0 >RESULTS\\%1.RC", "goto END"]) - for value in _RC_LADDER: - lines.extend([f":R{value}", f"echo {value} >RESULTS\\%1.RC", "goto END"]) - lines.extend([":END", ""]) - return "\r\n".join(lines) - - -def _batch(cases: list[Case], *, show_dos: bool, trace_dos: bool, - prebuilt: bool = False) -> str: - # The compiler build is the step that fails first and blocks everything after - # it, so its output is captured exactly like a case command's. - build = "call BUILD.BAT" if trace_dos else "call BUILD.BAT > RESULTS\\BUILD.LOG" - if prebuilt: - # FEC.EXE was restored from cache; BUILD.BAT would delete and rebuild it. - # It also puts the Watcom binaries on PATH, which the case commands need - # after it, so that line has to be reproduced rather than skipped. - build = ("set PATH=%WATCOM%\\BINW;%WATCOM%\\BINP;%PATH%\r\n" - "echo OK>BUILD.OK") - lines = [ - "@echo off", "if not exist RESULTS md RESULTS", "if not exist OUT md OUT", - "set WATCOM=W:", "set INCLUDE=W:\\H", - "set LIB=W:\\LIB286\\DOS;W:\\LIB286;W:\\LIB386\\DOS;W:\\LIB386", - # COMMAND.COM can only redirect handle 1, so fec diagnostics written to - # stderr never reach RESULTS\.LOG. Ask it for stdout instead. - "set FE_DIAG_STDOUT=1", - build, "if not exist BUILD.OK goto BUILDFAIL", - "echo PASS>RESULTS\\BUILD.RES", - ] - for index, case in enumerate(cases): - key = f"C{index:03d}" - command = case.command - # Watcom writes diagnostics into the current directory. Isolate each - # case so a later pytest item never sees stale diagnostics. - lines.extend([ - "if exist *.ERR del *.ERR > NUL", - f"if exist RESULTS\\{key}.ERR del RESULTS\\{key}.ERR > NUL", - ]) - if not trace_dos: - command += f" > RESULTS\\{key}.LOG" - lines.extend([ - command, - f"call RC.BAT {key}", - f"if exist *.ERR type *.ERR > RESULTS\\{key}.ERR", - f"if not exist RESULTS\\{key}.ERR type NUL > RESULTS\\{key}.ERR", - ]) - lines.extend([ - "goto FINISH", ":BUILDFAIL", "echo FAIL>RESULTS\\BUILD.RES", ":FINISH", - "echo DONE>RUN.OK", - *(["pause"] if show_dos else []), "exit", "", - ]) - return "\r\n".join(lines) - - -@dataclass -class SuiteRun: - root: Path - cases: list[Case] - keep: bool = False - - @property - def fec(self) -> Path: - return self.root / "FEC" - - def _key(self, case: Case) -> str: - return f"C{self.cases.index(case):03d}" - - def rc(self, case: Case) -> int | None: - """Exit code the DOS command reported, or None if it was never recorded. - - Values above 8 are a lower bound; see ``_RC_LADDER``. - """ - path = self.fec / "RESULTS" / f"{self._key(case)}.RC" - if not path.is_file(): - return None - text = path.read_text(encoding="ascii", errors="replace").strip() - return int(text) if text.isdigit() else None - - def result(self, case: Case | None = None) -> str: - if case is None: - path = self.fec / "RESULTS" / "BUILD.RES" - return path.read_text(encoding="ascii").strip() if path.is_file() else "MISSING" - code = self.rc(case) - if code is None: - return "MISSING" - return "PASS" if (code == 0) == case.expect_success else "FAIL" - - def log(self, case: Case | None = None) -> str: - name = "BUILD" if case is None else self._key(case) - path = self.fec / "RESULTS" / f"{name}.LOG" - content = path.read_text(encoding="utf-8", errors="replace") if path.is_file() else "" - if content.strip(): - return content - errors = sorted(self.fec.glob("*.ERR")) - joined = "\n".join(p.read_text(encoding="utf-8", errors="replace") for p in errors) - if joined.strip(): - return joined - # Deliberately not falling back to CONSOLE.LOG: that is the emulator's own - # log (display enumeration, INT15 chatter) and burying one useful line in - # it reads as output when there was none. Use --dos-log to see it. - return "(no DOS output captured; the command wrote nothing before exiting)" - - def err(self, case: Case) -> str: - path = self.fec / "RESULTS" / f"{self._key(case)}.ERR" - return path.read_text(encoding="utf-8", errors="replace") if path.is_file() else "" - - def cleanup(self) -> None: - if not self.keep: - shutil.rmtree(self.root, ignore_errors=True) - - -def _compiler_key() -> str: - """Hash of everything the compiler build reads. - - Sources and the build batch only; the toolchain itself is pinned by - dosboxx.lock.json, so it cannot drift underneath a cache hit. - """ - digest = hashlib.sha256() - paths = sorted((ROOT / "fec" / "src").rglob("*")) - paths.append(ROOT / "fec" / "build-dos.bat") - for path in paths: - if not path.is_file(): - continue - digest.update(path.name.encode("utf-8")) - digest.update(path.read_bytes()) - return digest.hexdigest()[:16] - - -def run_suite(cases: list[Case], *, keep: bool = False, show_dos: bool = False, - trace_dos: bool = False) -> SuiteRun: - dosbox, watcom = resolve_tools() - RUNS.mkdir(parents=True, exist_ok=True) - run_root = Path(tempfile.mkdtemp(prefix="suite-", dir=RUNS)) - result = SuiteRun(run_root, cases, keep) - fec = result.fec - cached = CACHE / "compilers" / f"{_compiler_key()}.exe" - try: - shutil.copytree(ROOT / "fec" / "src", fec / "SRC") - shutil.copytree(ROOT / "fec" / "std", fec / "STD") - shutil.copytree(ROOT / "fec" / "tests", fec / "TESTS") - shutil.copy2(ROOT / "fec" / "build-dos.bat", fec / "BUILD.BAT") - console = run_root / "CONSOLE.LOG" - config = run_root / "DOSBOX.CON" - config.write_text( - # core=auto leaves real mode on the interpreter, which is where the - # 16-bit compiler build spends its time. Nothing here is timing - # sensitive -- a compiler and a batch file -- so ask for the - # recompiler explicitly. The M3 bounds cases confirm abort() still - # reports its exit status under it. - f"[log]\nlogfile={console}\n" - f"[dosbox]\nlog console=quiet\n" - f"[cpu]\ncore=dynamic\ncycles=max\n", - encoding="ascii", - ) - if cached.is_file(): - shutil.copy2(cached, fec / "FEC.EXE") - (fec / "RUN.BAT").write_text( - _batch(cases, show_dos=show_dos, trace_dos=trace_dos, - prebuilt=cached.is_file()), - encoding="ascii", newline="", - ) - (fec / "RC.BAT").write_text(_rc_batch(), encoding="ascii", newline="") - command = [str(dosbox)] - if not show_dos: - command.append("-silent") - command.extend([ - "-fastlaunch", "-conf", str(config), - "-c", f'mount C "{run_root}"', "-c", f'mount R "{ROOT}" -ro', - "-c", f'mount W "{watcom}" -ro', - "-c", "C:", "-c", "cd \\FEC", "-c", "RUN.BAT", - ]) - # Every case pays a DOS process spawn, and the compile-only checks spawn - # wcc386 once each, so the whole-suite run is minutes rather than the - # under-a-minute a single milestone takes. - completed = subprocess.run(command, check=False, timeout=1800) - if completed.returncode != 0: - raise DosboxError(f"DOSBox-X exited with status {completed.returncode}") - if not (fec / "RUN.OK").is_file(): - raise DosboxError("DOSBox-X did not complete the test batch") - built = fec / "FEC.EXE" - if not cached.is_file() and built.is_file() and result.result() == "PASS": - cached.parent.mkdir(parents=True, exist_ok=True) - shutil.copy2(built, cached) - return result - except Exception: - result.keep = True - raise diff --git a/src/ferrolang_vm/paths.py b/src/ferrolang_vm/paths.py deleted file mode 100644 index 87ffbbd..0000000 --- a/src/ferrolang_vm/paths.py +++ /dev/null @@ -1,5 +0,0 @@ -"""Repository and local cache paths shared by Ferro developer tools.""" -from pathlib import Path - - -ROOT = Path(__file__).resolve().parents[2] diff --git a/src/ferrolang_vm/registry.py b/src/ferrolang_vm/registry.py deleted file mode 100644 index 5a47497..0000000 --- a/src/ferrolang_vm/registry.py +++ /dev/null @@ -1,260 +0,0 @@ -"""Milestone case registry: DOS commands and their expected exit status. - -Only commands live here; the ``.fe`` fixtures stay under ``fec/tests`` and are -copied into the disposable DOS filesystem by the runner. Case order is load -bearing -- ``emit`` must precede ``build`` must precede ``run`` for the same -fixture, because each step consumes the previous step's output. -""" -from __future__ import annotations - -from .suite import Case - -PASS = "TESTS\\PASS" -FAIL = "TESTS\\FAIL" -STD = "STD" -M2 = "TESTS\\M2" -M3 = "TESTS\\M3" -M4 = "TESTS\\M4" -M5 = "TESTS\\M5" -M6 = "TESTS\\M6" -M7 = "TESTS\\M7" -OUT = "OUT" - -# Emitted-C basenames that were hand-shortened for DOS 8.3. Keyed by milestone -# because the same fixture name maps to different outputs across milestones -# (``bad-type`` is BAD-TY in M2 but BAD-TYP in M4). The shortenings are not -# consistent -- M2 cut to six characters, M4 to seven, and several were never -# required at all since BAD-COND is already a legal 8.3 name. Preserved verbatim; -# changing one renames a file inside the DOS run, so re-verify if you touch it. -_OUT83 = { - (2, "bad-cond"): "BAD-CO", - (2, "bad-cast"): "BAD-CA", - (2, "bad-asgn"): "BAD-AS", - (2, "bad-unk"): "BAD-UN", - (2, "bad-ari"): "BAD-AR", - (2, "bad-type"): "BAD-TY", - (2, "bad-ret"): "BAD-RE", - (2, "bad-unit"): "BAD-UI", - (2, "bad-void"): "BAD-VO", - (4, "bad-type"): "BAD-TYP", - (4, "bad-writ"): "BAD-WRI", - (5, "bad-dest"): "BAD-DES", -} - - -def _case(milestone: int, name: str, command: str, ok: bool = True) -> Case: - return Case(f"m{milestone}-{name}", milestone, command, ok) - - -def _fe(directory: str, name: str) -> str: - return f"{directory}\\{name.upper()}.FE" - - -def _emit(source: str, output: str, *, target: str = "bits32", - flags: tuple[str, ...] = (), output_first: bool = False) -> str: - """``fec`` invocation that translates ``source`` to C at ``output``. - - ``output_first`` reproduces the M6 cases, which pass ``-o`` before the input - file while every other milestone passes it after. - """ - parts = ["FEC.EXE", f"--target={target}", *flags, "--emit-c"] - parts += ["-o", output, source] if output_first else [source, "-o", output] - return " ".join(parts) - - -def _wcl(exe: str, *sources: str, bits: int = 32, strict: bool = False, - defines: tuple[str, ...] = ()) -> str: - """Open Watcom invocation. ``strict`` is the M4 ``-wx -wcd=202`` pairing: - warnings are errors except W202, which the generated C trips on unused - helpers (see AGENTS.md).""" - parts = ["WCL386" if bits == 32 else "WCL", "-q", "-za"] - if strict: - parts += ["-wx", "-wcd=202"] - parts += ["-bt=dos", *defines, f"-fe={exe}", *sources] - return " ".join(parts) - - -def _wcc(source: str, obj: str) -> str: - """Compile the generated C without linking. - - A fixture with no ``main`` cannot be run, so this is the floor for it: the - backend's output has to survive the compiler the project actually ships - with. It catches a malformed emission -- an unnamed assignment target, a - helper that is called but never defined, an initializer C89 rejects -- which - otherwise sits unnoticed in a case that only ever emitted text. - - This asserts nothing about the C itself; it is a conformance check on the - backend's output, so a future non-C backend swaps the command rather than - the intent. Never grep the generated C to prove a language feature -- write a - fixture whose exit code differs instead, as TESTS\\M3\\NOCHK.FE does. - """ - # Through the wcl386 driver with -c rather than calling wcc386 directly: - # wcc386 writes its diagnostics to stderr, which COMMAND.COM cannot - # redirect, so a failure would report a count and no messages. The driver - # leaves an .ERR file, which the runner already collects. - return f"WCL386 -q -za -wx -wcd=202 -bt=dos -c -fo={obj} {source}" - - -def _accepts(milestone: int, directory: str, names: tuple[str, ...], *, - output: str = OUT, output_first: bool = True) -> list[Case]: - """Fixtures that must compile: emit the C, then build it.""" - cases: list[Case] = [] - for name in names: - cfile = f"{output}\\{name.upper()}.C" - cases.append(_case(milestone, name, - _emit(_fe(directory, name), cfile, - output_first=output_first))) - cases.append(_case(milestone, f"{name}-cc", - _wcc(cfile, f"{OUT}\\{name.upper()}.OBJ"))) - return cases - - -def _dump_ast(milestone: int, directory: str, names: tuple[str, ...], *, - suffix: str, ok: bool = True, prefix: str = "") -> list[Case]: - return [ - _case(milestone, f"{prefix}{name}-{suffix}", - f"FEC.EXE --dump-ast {_fe(directory, name)}", ok) - for name in names - ] - - -def _rejects(milestone: int, directory: str, names: tuple[str, ...], *, - suffix: str = "") -> list[Case]: - """Fixtures that must fail to compile. The emitted-C path is still spelled - out because ``fec`` needs an ``-o`` even when it is expected to bail.""" - return [ - _case(milestone, f"{name}-{suffix}" if suffix else name, - _emit(_fe(directory, name), - f"{directory}\\{_OUT83.get((milestone, name), name.upper())}.C"), - False) - for name in names - ] - - -def _triple(milestone: int, name: str, directory: str, *, stem: str | None = None, - target: str = "bits32", bits: int = 32, strict: bool = False, - build_source: str | None = None, emit_suffix: str | None = "emit", - run_suffix: str = "run", run_ok: bool = True) -> list[Case]: - """emit -> build -> run for one fixture. - - ``stem`` renames the C/EXE pair when the fixture name does not fit 8.3 or - collides (M2 castwhil emits CAST16). ``build_source`` compiles a different - file than the one emitted (M4 prop emits PROP.C but builds PROPTEST.C, which - ``#include``s it). - """ - stem = stem or name.upper() - cfile = f"{directory}\\{stem}.C" - exe = f"{directory}\\{stem}.EXE" - emit_id = f"{name}-{emit_suffix}" if emit_suffix else name - return [ - _case(milestone, emit_id, _emit(_fe(directory, name), cfile, target=target)), - _case(milestone, f"{name}-build", - _wcl(exe, build_source or cfile, bits=bits, strict=strict)), - _case(milestone, f"{name}-{run_suffix}", exe, run_ok), - ] - - -CASES: list[Case] = [ - # -- M1: parse only ------------------------------------------------------- - *_dump_ast(1, PASS, ("basic", "literals", "keybuilt", "v012form"), suffix="parse"), - *_dump_ast(1, STD, ("core", "fmt", "io", "list", "map", "mem", "str", "sys"), - suffix="parse", prefix="std-"), - *_dump_ast(1, FAIL, ("misssemi", "unclcomm", "logical"), suffix="reject", ok=False), - - # -- M2: first generated C ------------------------------------------------ - *_triple(2, "hello", M2), - *_triple(2, "scopes", M2), - *_triple(2, "castwhil", M2, stem="CAST16", target="bits16", bits=16), - *_rejects(2, M2, ("bad-cond", "bad-cast", "bad-asgn", "bad-unk", "bad-ari", - "bad-type", "bad-ret", "bad-unit", "bad-void"), suffix="reject"), - - # -- M3: aggregates, strings, bounds checks ------------------------------- - *_triple(3, "struct", M3), - *_triple(3, "enum", M3), - *_triple(3, "array", M3), - *_triple(3, "mutable", M3), - *_rejects(3, M3, ("bad-mlet", "bad-shwr"), suffix="reject"), - *_triple(3, "str", M3), - *_triple(3, "for", M3), - *_triple(3, "nested", M3), - *_triple(3, "char", M3), - *_triple(3, "arrayctx", M3), - # These two must trap at runtime: the bounds check is the feature under test. - *_triple(3, "bounds", M3, run_suffix="trap", run_ok=False), - *_triple(3, "slcbound", M3, run_suffix="trap", run_ok=False), - # --no-checks is proved by a differential on one source. NOCHK.FE reads one - # element past a [2]i32 and returns x - x, which is 0 whatever garbage the - # unchecked read produced: compiled with checks it must trap, compiled with - # --no-checks it must run to completion. BOUNDS.FE cannot serve as the - # unchecked half because it returns the out-of-bounds value directly, so its - # exit code would be whatever happens to sit past the array on the stack. - *_triple(3, "nochk", M3, run_suffix="trap", run_ok=False), - _case(3, "nochk-off-emit", - _emit(_fe(M3, "nochk"), f"{M3}\\NOCHK-N.C", flags=("--no-checks",))), - _case(3, "nochk-off-build", - _wcl(f"{M3}\\NOCHK-N.EXE", f"{M3}\\NOCHK-N.C")), - _case(3, "nochk-off-run", f"{M3}\\NOCHK-N.EXE"), - *_rejects(3, M3, ("badfld", "badmat", "badarr", "badcycle", "badstr", "badchar", - "badfield", "badindex"), suffix="reject"), - - # -- M4: formatting and error propagation --------------------------------- - *_triple(4, "format", M4, strict=True, emit_suffix=None), - *_triple(4, "try-fpr", M4, strict=True, emit_suffix=None), - *_triple(4, "prop", M4, strict=True, emit_suffix=None, - build_source=f"{M4}\\PROPTEST.C"), - *_rejects(4, M4, ("bad-ari", "bad-verb", "bad-run", "bad-type", "bad-try", - "bad-writ", "bad-bufw", "bad-many", "bad-open", "bad-cls")), - - # -- M5: defer and ownership ---------------------------------------------- - *_accepts(5, M5, ("defer", "owned"), output=M5, output_first=False), - *_rejects(5, M5, ("bad-move", "bad-dest", "bad-drop", "bad-dbl", "bad-cond", - "bad-proj", "bad-clos", "bad-loop")), - # The runtime case links the generated C against a hand-written allocator - # shim, so malloc/free are redirected at compile time. - _case(5, "runtime", _emit(_fe(M5, "runtime"), f"{M5}\\RUNT-G.C")), - _case(5, "runtime-build", - _wcl(f"{M5}\\RUNTIME.EXE", f"{M5}\\RUNT-G.C", f"{M5}\\RUNTIME.C", - defines=("-dmalloc=m5_malloc", "-dfree=m5_free"))), - _case(5, "runtime-run", f"{M5}\\RUNTIME.EXE"), - - # -- M6: borrow checking (R1--R8) ----------------------------------------- - *[_case(6, name, f"FEC.EXE --check {_fe(M6, name)}", False) for name in ( - "badarg", "badbinit", "badbrmov", "baddefer", "badfld", "badglob", "badgmut", - "badinv", "badlocsl", "badloop", "badmove", "badmut", "badmut2", "badptr", - "badret", "badrfld", "badridx", "badscop", "badself", "badshwr", "badslfld", - "badtwo", "badup", "badweak")], - *_accepts(6, M6, ( - "okbranch", "okdefer", "okglobcp", "oklast", "okr8free", "okr8join", - "okr8meth", "okr8stat", "okrebor", "okrtlast", "okshare", "okslreb", - "okstatic", "oktemp", "oktrim", "okwcall")), - - # -- M7: optionals and error unions --------------------------------------- - *[_case(7, name, f"FEC.EXE --check {_fe(M7, name)}", False) for name in ( - "badcatch", "baddef", "baddir", "badercod", "badernam", "badetype", - "badnull", "badoref", "badorel", "badproj", "badqmark", "badret", - "badsome", "badtry", "badzero")], - *_accepts(7, M7, ( - "okcatch", "okcatmov", "okcvoid", "okdeflt", "okiflet", "okmatch", - "oknull", "okorelse", "okpatvw", "okproj", "okrepl", "oktrdef", - "oktry")), -] - -MAX_MILESTONE: int = max(case.milestone for case in CASES) -MILESTONES: tuple[str, ...] = tuple(f"m{number}" - for number in range(1, MAX_MILESTONE + 1)) - - -def milestone_number(name: str) -> int: - """Parse an ``mN`` selector against the milestones the registry knows about.""" - if not name.startswith("m") or not name[1:].isdigit(): - raise ValueError(f"invalid milestone: {name}") - value = int(name[1:]) - if value not in range(1, MAX_MILESTONE + 1): - raise ValueError(f"unsupported milestone: {name}") - return value - - -def all_cases(*, through: int = MAX_MILESTONE, only: int | None = None) -> list[Case]: - if only is not None: - return [case for case in CASES if case.milestone == only] - return [case for case in CASES if case.milestone <= through] diff --git a/src/ferrolang_vm/suite.py b/src/ferrolang_vm/suite.py deleted file mode 100644 index b412298..0000000 --- a/src/ferrolang_vm/suite.py +++ /dev/null @@ -1,16 +0,0 @@ -"""The one type shared by the case registry and the DOSBox-X runner. - -Kept separate from ``registry`` so that ``dosboxx`` can depend on the type -without importing the case data. -""" -from __future__ import annotations - -from dataclasses import dataclass - - -@dataclass(frozen=True) -class Case: - id: str - milestone: int - command: str - expect_success: bool diff --git a/src/ferrolang_vm/test_cli.py b/src/ferrolang_vm/test_cli.py deleted file mode 100644 index d82b2ee..0000000 --- a/src/ferrolang_vm/test_cli.py +++ /dev/null @@ -1,90 +0,0 @@ -"""Developer test entry point backed by a disposable DOSBox-X run.""" -from __future__ import annotations - -import argparse -import os -import sys -from pathlib import Path - -from .dosboxx import DosboxError, setup -from .registry import MAX_MILESTONE, MILESTONES - - -def main() -> int: - parser = argparse.ArgumentParser( - prog="ferro-test", - description="Fast Ferro development tests in DOSBox-X/Open Watcom.", - ) - commands = parser.add_subparsers(dest="command", required=True) - prepare = commands.add_parser("setup", help="download and verify pinned development tools") - prepare.add_argument("--accept-watcom-license", action="store_true", - help="confirm acceptance of the Sybase Open Watcom Public License") - run = commands.add_parser("run", help="build once and run milestone pytest cases") - selection = run.add_mutually_exclusive_group() - selection.add_argument("--through", choices=MILESTONES, default=f"m{MAX_MILESTONE}", - help="run cumulatively through this milestone " - f"(default: m{MAX_MILESTONE})") - selection.add_argument("--only", choices=MILESTONES, - help="run only this milestone's cases") - run.add_argument("-v", "--verbose", action="store_true", help="show every pytest case") - run.add_argument("--keep-failed", action="store_true", - help="keep the disposable DOS filesystem after failures") - run.add_argument("--show-dos", action="store_true", - help="show DOSBox-X and wait for a key before closing") - run.add_argument("--dos-log", action="store_true", - help="print the captured DOS console after the run") - run.add_argument("--trace-dos", action="store_true", - help="do not redirect case command output") - run.add_argument("-k", dest="select", metavar="EXPR", - help="run only cases whose id matches this pytest -k expression") - args, extra = parser.parse_known_args() - try: - if args.command == "setup": - dosbox, watcom = setup(accept_watcom_license=args.accept_watcom_license) - print(f"DOSBox-X: {dosbox}") - print(f"Open Watcom: {watcom}") - return 0 - if args.only: - os.environ["FERRO_TEST_ONLY"] = args.only - else: - os.environ["FERRO_TEST_THROUGH"] = args.through - for enabled, name in ( - (args.keep_failed, "FERRO_TEST_KEEP_FAILED"), - (args.show_dos, "FERRO_TEST_SHOW_DOS"), - (args.trace_dos, "FERRO_TEST_TRACE_DOS"), - (args.dos_log, "FERRO_TEST_DOS_LOG"), - ): - if enabled: - os.environ[name] = "1" - import pytest - from .paths import ROOT - # The package can be imported from a different checkout than the one the - # shell is sitting in -- an editable install plus a git worktree is enough - # to silently build and test the wrong tree. Say which tree this is. - print(f"ferro-test: building {ROOT}", file=sys.stderr) - tests = ROOT / "tools" / "tests" - # The host gates run first and take under a second. A missing declaration - # or an 8.3-illegal name would otherwise be found only after a DOSBox-X - # boot and a full compiler build, and the DOS-side message for either is - # unhelpful. They do not replace the DOS run; they precede it. - gates = [os.fspath(tests / name) for name in - ("test_host_syntax.py", "test_dos_names.py")] - if pytest.main([*gates, "-q", "--no-header"]) != 0: - print("ferro-test: host gates failed; not starting DOSBox-X", - file=sys.stderr) - return 1 - test_file = os.fspath(tests / "test_milestones_dosboxx.py") - pytest_args = [test_file, "--tb=short", "-v" if args.verbose else "-q"] - if args.select: - pytest_args.extend(["-k", args.select]) - if args.dos_log: - pytest_args.append("-s") - pytest_args.extend(extra) - return int(pytest.main(pytest_args)) - except (DosboxError, ValueError) as exc: - print(f"ferro-test: {exc}", file=sys.stderr) - return 2 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/src/node-types.json b/src/node-types.json deleted file mode 100644 index ae9616e..0000000 --- a/src/node-types.json +++ /dev/null @@ -1,121 +0,0 @@ -[ - { - "type": "block_comment", - "named": true, - "fields": {}, - "children": { - "multiple": true, - "required": false, - "types": [ - { - "type": "block_comment", - "named": true - } - ] - } - }, - { - "type": "source_file", - "named": true, - "root": true, - "fields": {}, - "children": { - "multiple": true, - "required": false, - "types": [ - { - "type": "block_comment", - "named": true - }, - { - "type": "builtin", - "named": true - }, - { - "type": "builtin_type", - "named": true - }, - { - "type": "char_literal", - "named": true - }, - { - "type": "identifier", - "named": true - }, - { - "type": "integer_literal", - "named": true - }, - { - "type": "keyword", - "named": true - }, - { - "type": "line_comment", - "named": true - }, - { - "type": "operator", - "named": true - }, - { - "type": "punctuation", - "named": true - }, - { - "type": "string_literal", - "named": true - } - ] - } - }, - { - "type": "*/", - "named": false - }, - { - "type": "/*", - "named": false - }, - { - "type": "builtin", - "named": true - }, - { - "type": "builtin_type", - "named": true - }, - { - "type": "char_literal", - "named": true - }, - { - "type": "identifier", - "named": true - }, - { - "type": "integer_literal", - "named": true - }, - { - "type": "keyword", - "named": true - }, - { - "type": "line_comment", - "named": true - }, - { - "type": "operator", - "named": true - }, - { - "type": "punctuation", - "named": true - }, - { - "type": "string_literal", - "named": true - } -] \ No newline at end of file diff --git a/src/parser.c b/src/parser.c deleted file mode 100644 index 9173d00..0000000 --- a/src/parser.c +++ /dev/null @@ -1,1744 +0,0 @@ -/* Automatically @generated by tree-sitter */ - -#include "tree_sitter/parser.h" - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic ignored "-Wmissing-field-initializers" -#endif - -#define LANGUAGE_VERSION 14 -#define STATE_COUNT 14 -#define LARGE_STATE_COUNT 6 -#define SYMBOL_COUNT 20 -#define ALIAS_COUNT 0 -#define TOKEN_COUNT 16 -#define EXTERNAL_TOKEN_COUNT 0 -#define FIELD_COUNT 0 -#define MAX_ALIAS_SEQUENCE_LENGTH 3 -#define MAX_RESERVED_WORD_SET_SIZE 0 -#define PRODUCTION_ID_COUNT 1 -#define SUPERTYPE_COUNT 0 - -enum ts_symbol_identifiers { - sym_line_comment = 1, - anon_sym_SLASH_STAR = 2, - aux_sym_block_comment_token1 = 3, - aux_sym_block_comment_token2 = 4, - aux_sym_block_comment_token3 = 5, - anon_sym_STAR_SLASH = 6, - sym_string_literal = 7, - sym_char_literal = 8, - sym_integer_literal = 9, - sym_builtin = 10, - sym_builtin_type = 11, - sym_keyword = 12, - sym_identifier = 13, - sym_operator = 14, - sym_punctuation = 15, - sym_source_file = 16, - sym_block_comment = 17, - aux_sym_source_file_repeat1 = 18, - aux_sym_block_comment_repeat1 = 19, -}; - -static const char * const ts_symbol_names[] = { - [ts_builtin_sym_end] = "end", - [sym_line_comment] = "line_comment", - [anon_sym_SLASH_STAR] = "/*", - [aux_sym_block_comment_token1] = "block_comment_token1", - [aux_sym_block_comment_token2] = "block_comment_token2", - [aux_sym_block_comment_token3] = "block_comment_token3", - [anon_sym_STAR_SLASH] = "*/", - [sym_string_literal] = "string_literal", - [sym_char_literal] = "char_literal", - [sym_integer_literal] = "integer_literal", - [sym_builtin] = "builtin", - [sym_builtin_type] = "builtin_type", - [sym_keyword] = "keyword", - [sym_identifier] = "identifier", - [sym_operator] = "operator", - [sym_punctuation] = "punctuation", - [sym_source_file] = "source_file", - [sym_block_comment] = "block_comment", - [aux_sym_source_file_repeat1] = "source_file_repeat1", - [aux_sym_block_comment_repeat1] = "block_comment_repeat1", -}; - -static const TSSymbol ts_symbol_map[] = { - [ts_builtin_sym_end] = ts_builtin_sym_end, - [sym_line_comment] = sym_line_comment, - [anon_sym_SLASH_STAR] = anon_sym_SLASH_STAR, - [aux_sym_block_comment_token1] = aux_sym_block_comment_token1, - [aux_sym_block_comment_token2] = aux_sym_block_comment_token2, - [aux_sym_block_comment_token3] = aux_sym_block_comment_token3, - [anon_sym_STAR_SLASH] = anon_sym_STAR_SLASH, - [sym_string_literal] = sym_string_literal, - [sym_char_literal] = sym_char_literal, - [sym_integer_literal] = sym_integer_literal, - [sym_builtin] = sym_builtin, - [sym_builtin_type] = sym_builtin_type, - [sym_keyword] = sym_keyword, - [sym_identifier] = sym_identifier, - [sym_operator] = sym_operator, - [sym_punctuation] = sym_punctuation, - [sym_source_file] = sym_source_file, - [sym_block_comment] = sym_block_comment, - [aux_sym_source_file_repeat1] = aux_sym_source_file_repeat1, - [aux_sym_block_comment_repeat1] = aux_sym_block_comment_repeat1, -}; - -static const TSSymbolMetadata ts_symbol_metadata[] = { - [ts_builtin_sym_end] = { - .visible = false, - .named = true, - }, - [sym_line_comment] = { - .visible = true, - .named = true, - }, - [anon_sym_SLASH_STAR] = { - .visible = true, - .named = false, - }, - [aux_sym_block_comment_token1] = { - .visible = false, - .named = false, - }, - [aux_sym_block_comment_token2] = { - .visible = false, - .named = false, - }, - [aux_sym_block_comment_token3] = { - .visible = false, - .named = false, - }, - [anon_sym_STAR_SLASH] = { - .visible = true, - .named = false, - }, - [sym_string_literal] = { - .visible = true, - .named = true, - }, - [sym_char_literal] = { - .visible = true, - .named = true, - }, - [sym_integer_literal] = { - .visible = true, - .named = true, - }, - [sym_builtin] = { - .visible = true, - .named = true, - }, - [sym_builtin_type] = { - .visible = true, - .named = true, - }, - [sym_keyword] = { - .visible = true, - .named = true, - }, - [sym_identifier] = { - .visible = true, - .named = true, - }, - [sym_operator] = { - .visible = true, - .named = true, - }, - [sym_punctuation] = { - .visible = true, - .named = true, - }, - [sym_source_file] = { - .visible = true, - .named = true, - }, - [sym_block_comment] = { - .visible = true, - .named = true, - }, - [aux_sym_source_file_repeat1] = { - .visible = false, - .named = false, - }, - [aux_sym_block_comment_repeat1] = { - .visible = false, - .named = false, - }, -}; - -static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT][MAX_ALIAS_SEQUENCE_LENGTH] = { - [0] = {0}, -}; - -static const uint16_t ts_non_terminal_alias_map[] = { - 0, -}; - -static const TSStateId ts_primary_state_ids[STATE_COUNT] = { - [0] = 0, - [1] = 1, - [2] = 2, - [3] = 3, - [4] = 4, - [5] = 5, - [6] = 6, - [7] = 7, - [8] = 8, - [9] = 6, - [10] = 7, - [11] = 4, - [12] = 5, - [13] = 13, -}; - -static bool ts_lex(TSLexer *lexer, TSStateId state) { - START_LEXER(); - eof = lexer->eof(lexer); - switch (state) { - case 0: - if (eof) ADVANCE(21); - ADVANCE_MAP( - '!', 155, - '"', 1, - '%', 155, - '&', 155, - '\'', 6, - '*', 157, - '+', 157, - '-', 159, - '.', 161, - '/', 153, - '0', 31, - '<', 154, - '=', 158, - '>', 156, - '@', 20, - 'S', 69, - '^', 155, - 'a', 110, - 'b', 115, - 'c', 48, - 'd', 71, - 'e', 98, - 'f', 50, - 'i', 44, - 'l', 76, - 'm', 49, - 'n', 118, - 'o', 126, - 'p', 51, - 'r', 78, - 's', 68, - 't', 127, - 'u', 45, - 'v', 53, - 'w', 86, - '|', 155, - '?', 152, - '~', 152, - '(', 160, - ')', 160, - ',', 160, - ':', 160, - ';', 160, - '[', 160, - ']', 160, - '{', 160, - '}', 160, - ); - if (('\t' <= lookahead && lookahead <= '\r') || - lookahead == ' ' || - lookahead == 0x200b || - lookahead == 0x2060 || - lookahead == 0xfeff) SKIP(0); - if (('1' <= lookahead && lookahead <= '9')) ADVANCE(34); - if (('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('g' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 1: - if (lookahead == '"') ADVANCE(29); - if (lookahead == '\\') ADVANCE(7); - if (lookahead != 0 && - lookahead != '\n' && - lookahead != '\r' && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(1); - END_STATE(); - case 2: - if (lookahead == '\'') ADVANCE(30); - END_STATE(); - case 3: - if (lookahead == '*') ADVANCE(23); - if (lookahead != 0 && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(27); - END_STATE(); - case 4: - if (lookahead == '*') ADVANCE(5); - if (lookahead == '/') ADVANCE(3); - if (('\t' <= lookahead && lookahead <= '\r') || - lookahead == ' ' || - lookahead == 0x200b || - lookahead == 0x2060 || - lookahead == 0xfeff) ADVANCE(24); - if (lookahead != 0 && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(25); - END_STATE(); - case 5: - if (lookahead == '/') ADVANCE(28); - if (lookahead != 0 && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(26); - END_STATE(); - case 6: - if (lookahead == '\\') ADVANCE(8); - if (lookahead != 0 && - lookahead != '\n' && - lookahead != '\r' && - lookahead != '\'' && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(2); - END_STATE(); - case 7: - ADVANCE_MAP( - 'u', 17, - 'x', 13, - '"', 1, - '\'', 1, - '0', 1, - '\\', 1, - 'n', 1, - 'r', 1, - 't', 1, - ); - END_STATE(); - case 8: - ADVANCE_MAP( - 'u', 18, - 'x', 14, - '"', 2, - '\'', 2, - '0', 2, - '\\', 2, - 'n', 2, - 'r', 2, - 't', 2, - ); - END_STATE(); - case 9: - if (lookahead == '0' || - lookahead == '1' || - lookahead == '_') ADVANCE(32); - END_STATE(); - case 10: - if (('0' <= lookahead && lookahead <= '7') || - lookahead == '_') ADVANCE(33); - END_STATE(); - case 11: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(1); - END_STATE(); - case 12: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(2); - END_STATE(); - case 13: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(11); - END_STATE(); - case 14: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(12); - END_STATE(); - case 15: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(13); - END_STATE(); - case 16: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(14); - END_STATE(); - case 17: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(15); - END_STATE(); - case 18: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(16); - END_STATE(); - case 19: - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(35); - END_STATE(); - case 20: - if (('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(36); - END_STATE(); - case 21: - ACCEPT_TOKEN(ts_builtin_sym_end); - END_STATE(); - case 22: - ACCEPT_TOKEN(sym_line_comment); - if (lookahead != 0 && - lookahead != '\n' && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(22); - END_STATE(); - case 23: - ACCEPT_TOKEN(anon_sym_SLASH_STAR); - END_STATE(); - case 24: - ACCEPT_TOKEN(aux_sym_block_comment_token1); - if (('\t' <= lookahead && lookahead <= '\r') || - lookahead == ' ' || - lookahead == 0x200b || - lookahead == 0x2060 || - lookahead == 0xfeff) ADVANCE(24); - if (lookahead != 0 && - lookahead != '*' && - lookahead != '/' && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(25); - END_STATE(); - case 25: - ACCEPT_TOKEN(aux_sym_block_comment_token1); - if (lookahead != 0 && - lookahead != '*' && - lookahead != '/' && - lookahead != 0x17f && - lookahead != 0x212a) ADVANCE(25); - END_STATE(); - case 26: - ACCEPT_TOKEN(aux_sym_block_comment_token2); - END_STATE(); - case 27: - ACCEPT_TOKEN(aux_sym_block_comment_token3); - END_STATE(); - case 28: - ACCEPT_TOKEN(anon_sym_STAR_SLASH); - END_STATE(); - case 29: - ACCEPT_TOKEN(sym_string_literal); - END_STATE(); - case 30: - ACCEPT_TOKEN(sym_char_literal); - END_STATE(); - case 31: - ACCEPT_TOKEN(sym_integer_literal); - if (lookahead == 'B' || - lookahead == 'b') ADVANCE(9); - if (lookahead == 'O' || - lookahead == 'o') ADVANCE(10); - if (lookahead == 'X' || - lookahead == 'x') ADVANCE(19); - if (('0' <= lookahead && lookahead <= '9') || - lookahead == '_') ADVANCE(34); - END_STATE(); - case 32: - ACCEPT_TOKEN(sym_integer_literal); - if (lookahead == '0' || - lookahead == '1' || - lookahead == '_') ADVANCE(32); - END_STATE(); - case 33: - ACCEPT_TOKEN(sym_integer_literal); - if (('0' <= lookahead && lookahead <= '7') || - lookahead == '_') ADVANCE(33); - END_STATE(); - case 34: - ACCEPT_TOKEN(sym_integer_literal); - if (('0' <= lookahead && lookahead <= '9') || - lookahead == '_') ADVANCE(34); - END_STATE(); - case 35: - ACCEPT_TOKEN(sym_integer_literal); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'F') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'f')) ADVANCE(35); - END_STATE(); - case 36: - ACCEPT_TOKEN(sym_builtin); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(36); - END_STATE(); - case 37: - ACCEPT_TOKEN(sym_builtin_type); - END_STATE(); - case 38: - ACCEPT_TOKEN(sym_builtin_type); - if (lookahead == 'u') ADVANCE(63); - END_STATE(); - case 39: - ACCEPT_TOKEN(sym_keyword); - END_STATE(); - case 40: - ACCEPT_TOKEN(sym_keyword); - if (lookahead == '_') ADVANCE(135); - END_STATE(); - case 41: - ACCEPT_TOKEN(sym_keyword); - if (lookahead == 'e') ADVANCE(100); - END_STATE(); - case 42: - ACCEPT_TOKEN(sym_keyword); - if (lookahead == 'm') ADVANCE(39); - END_STATE(); - case 43: - ACCEPT_TOKEN(sym_keyword); - if (lookahead == 't') ADVANCE(80); - END_STATE(); - case 44: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == '1') ADVANCE(47); - if (lookahead == '3') ADVANCE(46); - if (lookahead == '8') ADVANCE(37); - if (lookahead == 'f') ADVANCE(39); - if (lookahead == 'm') ADVANCE(120); - if (lookahead == 'n') ADVANCE(43); - if (lookahead == 's') ADVANCE(87); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 45: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == '1') ADVANCE(47); - if (lookahead == '3') ADVANCE(46); - if (lookahead == '8') ADVANCE(37); - if (lookahead == 'n') ADVANCE(67); - if (lookahead == 's') ADVANCE(87); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 46: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == '2') ADVANCE(37); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 47: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == '6') ADVANCE(37); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 48: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(140); - if (lookahead == 'h') ADVANCE(54); - if (lookahead == 'o') ADVANCE(107); - if (lookahead == 'r') ADVANCE(93); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 49: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(140); - if (lookahead == 'u') ADVANCE(137); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 50: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(99); - if (lookahead == 'n') ADVANCE(39); - if (lookahead == 'o') ADVANCE(124); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 51: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(61); - if (lookahead == 'u') ADVANCE(59); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 52: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(96); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 53: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(124); - if (lookahead == 'o') ADVANCE(89); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 54: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(125); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 55: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(101); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 56: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(133); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 57: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(83); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 58: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'a') ADVANCE(141); - if (lookahead == 'r') ADVANCE(38); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('b' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 59: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'b') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 60: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'c') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 61: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'c') ADVANCE(97); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 62: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'c') ADVANCE(85); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 63: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'c') ADVANCE(137); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 64: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'c') ADVANCE(55); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 65: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'd') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 66: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'd') ADVANCE(37); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 67: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'd') ADVANCE(77); - if (lookahead == 'i') ADVANCE(137); - if (lookahead == 's') ADVANCE(57); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 68: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(103); - if (lookahead == 'h') ADVANCE(56); - if (lookahead == 't') ADVANCE(58); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 69: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(103); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 70: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(65); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 71: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(82); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 72: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 73: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(37); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 74: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(52); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 75: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(124); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 76: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(137); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 77: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(84); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 78: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(139); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 79: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(128); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 80: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'e') ADVANCE(132); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 81: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'f') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 82: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'f') ADVANCE(75); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 83: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'f') ADVANCE(72); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 84: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'f') ADVANCE(91); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 85: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'h') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 86: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'h') ADVANCE(95); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 87: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(150); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 88: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(112); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 89: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(66); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 90: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(109); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 91: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(113); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 92: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(60); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 93: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(143); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 94: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(64); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 95: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'i') ADVANCE(105); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 96: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'k') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 97: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'k') ADVANCE(70); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 98: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(136); - if (lookahead == 'n') ADVANCE(145); - if (lookahead == 'r') ADVANCE(129); - if (lookahead == 'x') ADVANCE(142); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 99: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(136); - if (lookahead == 'r') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 100: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(136); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 101: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 102: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(37); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 103: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(81); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 104: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(101); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 105: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'l') ADVANCE(72); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 106: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'm') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 107: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'm') ADVANCE(123); - if (lookahead == 'n') ADVANCE(134); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 108: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'm') ADVANCE(92); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 109: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'm') ADVANCE(72); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 110: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'n') ADVANCE(65); - if (lookahead == 's') ADVANCE(42); - if (lookahead == 't') ADVANCE(114); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 111: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'n') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 112: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'n') ADVANCE(147); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 113: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'n') ADVANCE(70); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 114: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'o') ADVANCE(108); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 115: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'o') ADVANCE(117); - if (lookahead == 'r') ADVANCE(74); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 116: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'o') ADVANCE(124); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 117: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'o') ADVANCE(102); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 118: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'o') ADVANCE(137); - if (lookahead == 'u') ADVANCE(104); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 119: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'o') ADVANCE(130); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 120: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'p') ADVANCE(119); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 121: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'p') ADVANCE(72); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 122: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'p') ADVANCE(138); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 123: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'p') ADVANCE(144); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 124: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 125: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(37); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 126: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(41); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 127: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(146); - if (lookahead == 'y') ADVANCE(121); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 128: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(111); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 129: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(116); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 130: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(137); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 131: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(149); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 132: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(131); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 133: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'r') ADVANCE(70); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 134: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 's') ADVANCE(137); - if (lookahead == 't') ADVANCE(88); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 135: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 's') ADVANCE(57); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 136: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 's') ADVANCE(72); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 137: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 138: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(40); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 139: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(148); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 140: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(62); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 141: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(92); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 142: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(79); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 143: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(94); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 144: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 't') ADVANCE(90); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 145: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'u') ADVANCE(106); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 146: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'u') ADVANCE(72); - if (lookahead == 'y') ADVANCE(39); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 147: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'u') ADVANCE(72); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 148: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'u') ADVANCE(128); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 149: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'u') ADVANCE(122); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 150: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == 'z') ADVANCE(73); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'y')) ADVANCE(151); - END_STATE(); - case 151: - ACCEPT_TOKEN(sym_identifier); - if (('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(151); - END_STATE(); - case 152: - ACCEPT_TOKEN(sym_operator); - END_STATE(); - case 153: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '*') ADVANCE(23); - if (lookahead == '/') ADVANCE(22); - if (lookahead == '=') ADVANCE(152); - END_STATE(); - case 154: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '<') ADVANCE(155); - if (lookahead == '=') ADVANCE(152); - END_STATE(); - case 155: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '=') ADVANCE(152); - END_STATE(); - case 156: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '=') ADVANCE(152); - if (lookahead == '>') ADVANCE(155); - END_STATE(); - case 157: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '%' || - lookahead == '=') ADVANCE(152); - END_STATE(); - case 158: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '=' || - lookahead == '>') ADVANCE(152); - END_STATE(); - case 159: - ACCEPT_TOKEN(sym_operator); - if (lookahead == '%' || - lookahead == '=' || - lookahead == '>') ADVANCE(152); - END_STATE(); - case 160: - ACCEPT_TOKEN(sym_punctuation); - END_STATE(); - case 161: - ACCEPT_TOKEN(sym_punctuation); - if (lookahead == '.') ADVANCE(152); - END_STATE(); - default: - return false; - } -} - -static const TSLexMode ts_lex_modes[STATE_COUNT] = { - [0] = {.lex_state = 0}, - [1] = {.lex_state = 0}, - [2] = {.lex_state = 0}, - [3] = {.lex_state = 0}, - [4] = {.lex_state = 0}, - [5] = {.lex_state = 0}, - [6] = {.lex_state = 4}, - [7] = {.lex_state = 4}, - [8] = {.lex_state = 4}, - [9] = {.lex_state = 4}, - [10] = {.lex_state = 4}, - [11] = {.lex_state = 4}, - [12] = {.lex_state = 4}, - [13] = {.lex_state = 0}, -}; - -static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = { - [STATE(0)] = { - [ts_builtin_sym_end] = ACTIONS(1), - [sym_line_comment] = ACTIONS(1), - [anon_sym_SLASH_STAR] = ACTIONS(1), - [sym_string_literal] = ACTIONS(1), - [sym_char_literal] = ACTIONS(1), - [sym_integer_literal] = ACTIONS(1), - [sym_builtin] = ACTIONS(1), - [sym_builtin_type] = ACTIONS(1), - [sym_keyword] = ACTIONS(1), - [sym_identifier] = ACTIONS(1), - [sym_operator] = ACTIONS(1), - [sym_punctuation] = ACTIONS(1), - }, - [STATE(1)] = { - [sym_source_file] = STATE(13), - [sym_block_comment] = STATE(2), - [aux_sym_source_file_repeat1] = STATE(2), - [ts_builtin_sym_end] = ACTIONS(3), - [sym_line_comment] = ACTIONS(5), - [anon_sym_SLASH_STAR] = ACTIONS(7), - [sym_string_literal] = ACTIONS(5), - [sym_char_literal] = ACTIONS(5), - [sym_integer_literal] = ACTIONS(5), - [sym_builtin] = ACTIONS(5), - [sym_builtin_type] = ACTIONS(9), - [sym_keyword] = ACTIONS(5), - [sym_identifier] = ACTIONS(9), - [sym_operator] = ACTIONS(9), - [sym_punctuation] = ACTIONS(9), - }, - [STATE(2)] = { - [sym_block_comment] = STATE(3), - [aux_sym_source_file_repeat1] = STATE(3), - [ts_builtin_sym_end] = ACTIONS(11), - [sym_line_comment] = ACTIONS(13), - [anon_sym_SLASH_STAR] = ACTIONS(7), - [sym_string_literal] = ACTIONS(13), - [sym_char_literal] = ACTIONS(13), - [sym_integer_literal] = ACTIONS(13), - [sym_builtin] = ACTIONS(13), - [sym_builtin_type] = ACTIONS(15), - [sym_keyword] = ACTIONS(13), - [sym_identifier] = ACTIONS(15), - [sym_operator] = ACTIONS(15), - [sym_punctuation] = ACTIONS(15), - }, - [STATE(3)] = { - [sym_block_comment] = STATE(3), - [aux_sym_source_file_repeat1] = STATE(3), - [ts_builtin_sym_end] = ACTIONS(17), - [sym_line_comment] = ACTIONS(19), - [anon_sym_SLASH_STAR] = ACTIONS(22), - [sym_string_literal] = ACTIONS(19), - [sym_char_literal] = ACTIONS(19), - [sym_integer_literal] = ACTIONS(19), - [sym_builtin] = ACTIONS(19), - [sym_builtin_type] = ACTIONS(25), - [sym_keyword] = ACTIONS(19), - [sym_identifier] = ACTIONS(25), - [sym_operator] = ACTIONS(25), - [sym_punctuation] = ACTIONS(25), - }, - [STATE(4)] = { - [ts_builtin_sym_end] = ACTIONS(28), - [sym_line_comment] = ACTIONS(28), - [anon_sym_SLASH_STAR] = ACTIONS(28), - [sym_string_literal] = ACTIONS(28), - [sym_char_literal] = ACTIONS(28), - [sym_integer_literal] = ACTIONS(28), - [sym_builtin] = ACTIONS(28), - [sym_builtin_type] = ACTIONS(30), - [sym_keyword] = ACTIONS(28), - [sym_identifier] = ACTIONS(30), - [sym_operator] = ACTIONS(30), - [sym_punctuation] = ACTIONS(30), - }, - [STATE(5)] = { - [ts_builtin_sym_end] = ACTIONS(32), - [sym_line_comment] = ACTIONS(32), - [anon_sym_SLASH_STAR] = ACTIONS(32), - [sym_string_literal] = ACTIONS(32), - [sym_char_literal] = ACTIONS(32), - [sym_integer_literal] = ACTIONS(32), - [sym_builtin] = ACTIONS(32), - [sym_builtin_type] = ACTIONS(34), - [sym_keyword] = ACTIONS(32), - [sym_identifier] = ACTIONS(34), - [sym_operator] = ACTIONS(34), - [sym_punctuation] = ACTIONS(34), - }, -}; - -static const uint16_t ts_small_parse_table[] = { - [0] = 5, - ACTIONS(36), 1, - anon_sym_SLASH_STAR, - ACTIONS(38), 1, - aux_sym_block_comment_token1, - ACTIONS(42), 1, - anon_sym_STAR_SLASH, - ACTIONS(40), 2, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - STATE(7), 2, - sym_block_comment, - aux_sym_block_comment_repeat1, - [18] = 5, - ACTIONS(36), 1, - anon_sym_SLASH_STAR, - ACTIONS(44), 1, - aux_sym_block_comment_token1, - ACTIONS(48), 1, - anon_sym_STAR_SLASH, - ACTIONS(46), 2, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - STATE(8), 2, - sym_block_comment, - aux_sym_block_comment_repeat1, - [36] = 5, - ACTIONS(50), 1, - anon_sym_SLASH_STAR, - ACTIONS(53), 1, - aux_sym_block_comment_token1, - ACTIONS(59), 1, - anon_sym_STAR_SLASH, - ACTIONS(56), 2, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - STATE(8), 2, - sym_block_comment, - aux_sym_block_comment_repeat1, - [54] = 5, - ACTIONS(36), 1, - anon_sym_SLASH_STAR, - ACTIONS(61), 1, - aux_sym_block_comment_token1, - ACTIONS(65), 1, - anon_sym_STAR_SLASH, - ACTIONS(63), 2, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - STATE(10), 2, - sym_block_comment, - aux_sym_block_comment_repeat1, - [72] = 5, - ACTIONS(36), 1, - anon_sym_SLASH_STAR, - ACTIONS(44), 1, - aux_sym_block_comment_token1, - ACTIONS(67), 1, - anon_sym_STAR_SLASH, - ACTIONS(46), 2, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - STATE(8), 2, - sym_block_comment, - aux_sym_block_comment_repeat1, - [90] = 2, - ACTIONS(28), 1, - aux_sym_block_comment_token1, - ACTIONS(30), 4, - anon_sym_SLASH_STAR, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - anon_sym_STAR_SLASH, - [100] = 2, - ACTIONS(32), 1, - aux_sym_block_comment_token1, - ACTIONS(34), 4, - anon_sym_SLASH_STAR, - aux_sym_block_comment_token2, - aux_sym_block_comment_token3, - anon_sym_STAR_SLASH, - [110] = 1, - ACTIONS(69), 1, - ts_builtin_sym_end, -}; - -static const uint32_t ts_small_parse_table_map[] = { - [SMALL_STATE(6)] = 0, - [SMALL_STATE(7)] = 18, - [SMALL_STATE(8)] = 36, - [SMALL_STATE(9)] = 54, - [SMALL_STATE(10)] = 72, - [SMALL_STATE(11)] = 90, - [SMALL_STATE(12)] = 100, - [SMALL_STATE(13)] = 110, -}; - -static const TSParseActionEntry ts_parse_actions[] = { - [0] = {.entry = {.count = 0, .reusable = false}}, - [1] = {.entry = {.count = 1, .reusable = false}}, RECOVER(), - [3] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_source_file, 0, 0, 0), - [5] = {.entry = {.count = 1, .reusable = true}}, SHIFT(2), - [7] = {.entry = {.count = 1, .reusable = true}}, SHIFT(6), - [9] = {.entry = {.count = 1, .reusable = false}}, SHIFT(2), - [11] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_source_file, 1, 0, 0), - [13] = {.entry = {.count = 1, .reusable = true}}, SHIFT(3), - [15] = {.entry = {.count = 1, .reusable = false}}, SHIFT(3), - [17] = {.entry = {.count = 1, .reusable = true}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), - [19] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(3), - [22] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(6), - [25] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_source_file_repeat1, 2, 0, 0), SHIFT_REPEAT(3), - [28] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_block_comment, 2, 0, 0), - [30] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_block_comment, 2, 0, 0), - [32] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_block_comment, 3, 0, 0), - [34] = {.entry = {.count = 1, .reusable = false}}, REDUCE(sym_block_comment, 3, 0, 0), - [36] = {.entry = {.count = 1, .reusable = false}}, SHIFT(9), - [38] = {.entry = {.count = 1, .reusable = true}}, SHIFT(7), - [40] = {.entry = {.count = 1, .reusable = false}}, SHIFT(7), - [42] = {.entry = {.count = 1, .reusable = false}}, SHIFT(4), - [44] = {.entry = {.count = 1, .reusable = true}}, SHIFT(8), - [46] = {.entry = {.count = 1, .reusable = false}}, SHIFT(8), - [48] = {.entry = {.count = 1, .reusable = false}}, SHIFT(5), - [50] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_block_comment_repeat1, 2, 0, 0), SHIFT_REPEAT(9), - [53] = {.entry = {.count = 2, .reusable = true}}, REDUCE(aux_sym_block_comment_repeat1, 2, 0, 0), SHIFT_REPEAT(8), - [56] = {.entry = {.count = 2, .reusable = false}}, REDUCE(aux_sym_block_comment_repeat1, 2, 0, 0), SHIFT_REPEAT(8), - [59] = {.entry = {.count = 1, .reusable = false}}, REDUCE(aux_sym_block_comment_repeat1, 2, 0, 0), - [61] = {.entry = {.count = 1, .reusable = true}}, SHIFT(10), - [63] = {.entry = {.count = 1, .reusable = false}}, SHIFT(10), - [65] = {.entry = {.count = 1, .reusable = false}}, SHIFT(11), - [67] = {.entry = {.count = 1, .reusable = false}}, SHIFT(12), - [69] = {.entry = {.count = 1, .reusable = true}}, ACCEPT_INPUT(), -}; - -#ifdef __cplusplus -extern "C" { -#endif -#ifdef TREE_SITTER_HIDE_SYMBOLS -#define TS_PUBLIC -#elif defined(_WIN32) -#define TS_PUBLIC __declspec(dllexport) -#else -#define TS_PUBLIC __attribute__((visibility("default"))) -#endif - -TS_PUBLIC const TSLanguage *tree_sitter_ferro(void) { - static const TSLanguage language = { - .abi_version = LANGUAGE_VERSION, - .symbol_count = SYMBOL_COUNT, - .alias_count = ALIAS_COUNT, - .token_count = TOKEN_COUNT, - .external_token_count = EXTERNAL_TOKEN_COUNT, - .state_count = STATE_COUNT, - .large_state_count = LARGE_STATE_COUNT, - .production_id_count = PRODUCTION_ID_COUNT, - .field_count = FIELD_COUNT, - .max_alias_sequence_length = MAX_ALIAS_SEQUENCE_LENGTH, - .parse_table = &ts_parse_table[0][0], - .small_parse_table = ts_small_parse_table, - .small_parse_table_map = ts_small_parse_table_map, - .parse_actions = ts_parse_actions, - .symbol_names = ts_symbol_names, - .symbol_metadata = ts_symbol_metadata, - .public_symbol_map = ts_symbol_map, - .alias_map = ts_non_terminal_alias_map, - .alias_sequences = &ts_alias_sequences[0][0], - .lex_modes = (const void*)ts_lex_modes, - .lex_fn = ts_lex, - .primary_state_ids = ts_primary_state_ids, - }; - return &language; -} -#ifdef __cplusplus -} -#endif diff --git a/src/tree_sitter/parser.h b/src/tree_sitter/parser.h deleted file mode 100644 index 5f37997..0000000 --- a/src/tree_sitter/parser.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef TREE_SITTER_PARSER_H_ -#define TREE_SITTER_PARSER_H_ -#ifdef __cplusplus -extern "C" { -#endif -#include -#include -#include -#define ts_builtin_sym_error ((TSSymbol)-1) -#define ts_builtin_sym_end 0 -#define TREE_SITTER_SERIALIZATION_BUFFER_SIZE 1024 -#ifndef TREE_SITTER_API_H_ -typedef uint16_t TSStateId; -typedef uint16_t TSSymbol; -typedef uint16_t TSFieldId; -typedef struct TSLanguage TSLanguage; -typedef struct TSLanguageMetadata { uint8_t major_version; uint8_t minor_version; uint8_t patch_version; } TSLanguageMetadata; -#endif -typedef struct { TSFieldId field_id; uint8_t child_index; bool inherited; } TSFieldMapEntry; -typedef struct { uint16_t index; uint16_t length; } TSMapSlice; -typedef struct { bool visible; bool named; bool supertype; } TSSymbolMetadata; -typedef struct TSLexer TSLexer; -struct TSLexer { int32_t lookahead; TSSymbol result_symbol; void (*advance)(TSLexer *, bool); void (*mark_end)(TSLexer *); uint32_t (*get_column)(TSLexer *); bool (*is_at_included_range_start)(const TSLexer *); bool (*eof)(const TSLexer *); void (*log)(const TSLexer *, const char *, ...); }; -typedef enum { TSParseActionTypeShift, TSParseActionTypeReduce, TSParseActionTypeAccept, TSParseActionTypeRecover } TSParseActionType; -typedef union { struct { uint8_t type; TSStateId state; bool extra; bool repetition; } shift; struct { uint8_t type; uint8_t child_count; TSSymbol symbol; int16_t dynamic_precedence; uint16_t production_id; } reduce; uint8_t type; } TSParseAction; -typedef struct { uint16_t lex_state; uint16_t external_lex_state; } TSLexMode; -typedef struct { uint16_t lex_state; uint16_t external_lex_state; uint16_t reserved_word_set_id; } TSLexerMode; -typedef union { TSParseAction action; struct { uint8_t count; bool reusable; } entry; } TSParseActionEntry; -typedef struct { int32_t start; int32_t end; } TSCharacterRange; -struct TSLanguage { uint32_t abi_version; uint32_t symbol_count; uint32_t alias_count; uint32_t token_count; uint32_t external_token_count; uint32_t state_count; uint32_t large_state_count; uint32_t production_id_count; uint32_t field_count; uint16_t max_alias_sequence_length; const uint16_t *parse_table; const uint16_t *small_parse_table; const uint32_t *small_parse_table_map; const TSParseActionEntry *parse_actions; const char * const *symbol_names; const char * const *field_names; const TSMapSlice *field_map_slices; const TSFieldMapEntry *field_map_entries; const TSSymbolMetadata *symbol_metadata; const TSSymbol *public_symbol_map; const uint16_t *alias_map; const TSSymbol *alias_sequences; const TSLexerMode *lex_modes; bool (*lex_fn)(TSLexer *, TSStateId); bool (*keyword_lex_fn)(TSLexer *, TSStateId); TSSymbol keyword_capture_token; struct { const bool *states; const TSSymbol *symbol_map; void *(*create)(void); void (*destroy)(void *); bool (*scan)(void *, TSLexer *, const bool *); unsigned (*serialize)(void *, char *); void (*deserialize)(void *, const char *, unsigned); } external_scanner; const TSStateId *primary_state_ids; const char *name; const TSSymbol *reserved_words; uint16_t max_reserved_word_set_size; uint32_t supertype_count; const TSSymbol *supertype_symbols; const TSMapSlice *supertype_map_slices; const TSSymbol *supertype_map_entries; TSLanguageMetadata metadata; }; -static inline bool set_contains(const TSCharacterRange *ranges, uint32_t len, int32_t lookahead) { uint32_t index=0,size=len; while(size>1){uint32_t half=size/2,mid=index+half; const TSCharacterRange *r=&ranges[mid]; if(lookahead>=r->start&&lookahead<=r->end)return true; else if(lookahead>r->end)index=mid; size-=half;} const TSCharacterRange *r=&ranges[index]; return lookahead>=r->start&&lookahead<=r->end; } -#ifdef _MSC_VER -#define UNUSED __pragma(warning(suppress : 4101)) -#else -#define UNUSED __attribute__((unused)) -#endif -#define START_LEXER() bool result=false; bool skip=false; UNUSED bool eof=false; int32_t lookahead; goto start; next_state: lexer->advance(lexer,skip); start: skip=false; lookahead=lexer->lookahead; -#define ADVANCE(state_value) { state=state_value; goto next_state; } -#define ADVANCE_MAP(...) { static const uint16_t map[]={__VA_ARGS__}; for(uint32_t i=0;iresult_symbol=symbol_value; lexer->mark_end(lexer); -#define END_STATE() return result; -#define SMALL_STATE(id) ((id)-LARGE_STATE_COUNT) -#define STATE(id) id -#define ACTIONS(id) id -#define SHIFT(state_value) {{.shift={.type=TSParseActionTypeShift,.state=(state_value)}}} -#define SHIFT_REPEAT(state_value) {{.shift={.type=TSParseActionTypeShift,.state=(state_value),.repetition=true}}} -#define SHIFT_EXTRA() {{.shift={.type=TSParseActionTypeShift,.extra=true}}} -#define REDUCE(symbol_name,children,precedence,prod_id) {{.reduce={.type=TSParseActionTypeReduce,.symbol=symbol_name,.child_count=children,.dynamic_precedence=precedence,.production_id=prod_id},}} -#define RECOVER() {{.type=TSParseActionTypeRecover}} -#define ACCEPT_INPUT() {{.type=TSParseActionTypeAccept}} -#ifdef __cplusplus -} -#endif -#endif diff --git a/tests/run.py b/tests/run.py new file mode 100644 index 0000000..20700dd --- /dev/null +++ b/tests/run.py @@ -0,0 +1,140 @@ +"""Run every fixture through the front end and check what it reports. + +The compiler is a front end now -- lexer, parser, types, ownership -- so a +fixture is checked by running `fec` on it and looking at two things: whether it +was accepted, and, when it was rejected, whether the diagnostic is the one the +fixture asked for. + +A fixture states its expectation in its first line: + + // ERROR:8:self rejected at line 8, with "self" in the message + // ERROR:expected ';' rejected, message only -- the parse fixtures, where + the line is not the interesting part + +A fixture with no marker whose name starts with `bad` must be rejected but does +not pin the message yet. Anything else must be accepted. + +Fixtures under `parse/` are checked with --dump-ast rather than --check: they +exercise the grammar, and several are deliberately not well-typed. + +This runs on the host in about a second. There is no VM: nothing here executes +generated code, because there is no code generator. +""" +from __future__ import annotations + +import argparse +import os +import re +import subprocess +import sys +from dataclasses import dataclass +from pathlib import Path + +ROOT = Path(__file__).resolve().parent.parent +FIXTURES = ROOT / "fec" / "tests" +WATCOM = ROOT / ".dosboxx" / "watcom" +SOURCES = ("arena", "diag", "lexer", "ast", "parser", "types", "m7", "own", + "check", "driver") +# Fixtures live here until there is a code generator to run them against. +QUARANTINE = "pending-backend" + +MARKER = re.compile(r"^//\s*ERROR:(?:(\d+):)?(.*)$") + + +@dataclass +class Expectation: + rejected: bool + line: int | None = None + text: str | None = None + + +def expectation(path: Path) -> Expectation: + first = path.read_text(encoding="utf-8", errors="replace").split("\n", 1)[0] + m = MARKER.match(first.strip()) + if m: + line = int(m.group(1)) if m.group(1) else None + return Expectation(True, line, m.group(2).strip()) + name = path.stem + return Expectation(name.startswith("bad") or "-bad-" in name or name.startswith("own-bad")) + + +def build(out: Path) -> Path: + """Build the front end with the pinned toolchain, hosted.""" + wcl = WATCOM / "binnt" / "wcl386.exe" + if not wcl.is_file(): + sys.exit(f"pinned Open Watcom not found at {WATCOM}") + out.mkdir(parents=True, exist_ok=True) + env = dict(os.environ) + env.update(WATCOM=str(WATCOM), INCLUDE=f"{WATCOM / 'h'};{WATCOM / 'h' / 'nt'}", + PATH=f"{WATCOM / 'binnt'}{os.pathsep}{env.get('PATH', '')}") + src = ROOT / "fec" / "src" + cmd = [str(wcl), "-q", "-za", "-wx", "-bt=nt", "-fe=fec.exe", f"-i={src}"] + cmd += [str(src / f"{n}.c") for n in SOURCES] + done = subprocess.run(cmd, cwd=out, capture_output=True, text=True, env=env) + if done.returncode != 0 or (done.stdout + done.stderr).strip(): + sys.exit("front end does not build clean:\n" + done.stdout + done.stderr) + return out / "fec.exe" + + +def run_case(fec: Path, path: Path) -> tuple[bool, str]: + want = expectation(path) + # The grammar fixtures are not all well-typed; stop after parsing. + mode = "--dump-ast" if path.parent.name == "parse" else "--check" + done = subprocess.run([str(fec), mode, str(path)], + capture_output=True, text=True, timeout=30) + output = (done.stdout + done.stderr).strip() + rejected = done.returncode != 0 + + if want.rejected != rejected: + verb = "accepted" if rejected else "rejected" + return False, f"expected to be {'rejected' if want.rejected else verb}" + if not want.rejected: + return True, "" + if want.line is None: + if want.text and want.text.lower() not in output.lower(): + got = output.splitlines()[0] if output else "(silent)" + return False, f"marker wants {want.text!r}\n {got}" + return True, "" + # The marker pins where and roughly what, so a rule can be moved or reworded + # only deliberately. + first = output.split("\n", 1)[0] if output else "" + at = re.search(r":(\d+):\d+: error:", first) + if not at: + return False, f"no diagnostic to match marker\n got: {first or '(silent)'}" + if int(at.group(1)) != want.line: + return False, f"marker says line {want.line}, diagnostic is line {at.group(1)}\n {first}" + if want.text and want.text.lower() not in output.lower(): + return False, f"marker wants {want.text!r}\n {first}" + return True, "" + + +def main() -> int: + ap = argparse.ArgumentParser(description="run the front-end fixtures") + ap.add_argument("-k", dest="select", help="only fixtures whose path contains this") + ap.add_argument("-v", dest="verbose", action="store_true") + args = ap.parse_args() + + fec = build(ROOT / ".build") + cases = sorted(p for p in FIXTURES.rglob("*.fe") if QUARANTINE not in p.parts) + if args.select: + cases = [p for p in cases if args.select in p.as_posix()] + + failed = [] + for path in cases: + ok, why = run_case(fec, path) + rel = path.relative_to(FIXTURES).as_posix() + if ok: + if args.verbose: + print(f" ok {rel}") + else: + failed.append((rel, why)) + for rel, why in failed: + print(f"FAIL {rel}: {why}") + marked = sum(1 for p in cases if expectation(p).line is not None) + print(f"\n{len(cases) - len(failed)}/{len(cases)} passed " + f"({marked} pin a line and message)") + return 1 if failed else 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/tools/README.md b/tools/README.md deleted file mode 100644 index 97b141e..0000000 --- a/tools/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# Development tools - -## Host support - -The automated DOS development environment currently supports Windows 10/11. -The host only needs `uv`. The setup command downloads the pinned DOSBox-X and -Open Watcom DOS archives, verifies their SHA-256 hashes, and installs them in the -ignored `.dosboxx/` cache. - -```powershell -uv run ferro-dos setup --accept-watcom-license -``` - -Review the Open Watcom license referenced by -`tools/toolchains/dosboxx.lock.json` before accepting it. Neither downloaded -archives nor installed tools are committed. - -## General DOS environment - -`ferro-dos` provides the development entry points: - -```powershell -uv run ferro-dos build -uv run ferro-dos exec "FEC.EXE --check TESTS\M6\OKLAST.FE" -uv run ferro-dos batch fec\test-dos.bat -uv run ferro-dos shell -uv run ferro-dos --help -``` - -Every invocation creates an isolated host directory under `.dosboxx/runs/` and -mounts it as writable `C:`. The repository is mounted read-only as `R:` and the -pinned Open Watcom installation as read-only `W:`. Current compiler sources, -the standard library, and fixtures are copied to `C:\FEC`; all compilation and -execution happen there inside DOSBox-X. Successful runs are removed by default. -Use `--keep` to retain a workspace and `--show-dos` to display the DOS window. - -This directory-backed layout deliberately has no QEMU, disk-image, TCP-agent, -or OCR dependency. A future disk-image backend can be added without changing -the command interface. - -## Pytest regression suite - -`ferro-test` uses the same isolated DOSBox-X/Open Watcom environment, builds -`FEC.EXE` once, and executes all selected cases sequentially in that one DOS -instance. Pytest still reports each registered case separately. - -```powershell -uv run ferro-test run --through m6 -v -uv run ferro-test run --only m6 --dos-log -uv run ferro-test --help -``` - -`--keep-failed` preserves a failed workspace, `--dos-log` prints captured DOS -output, `--trace-dos` disables per-command redirection, and `--show-dos` displays -the GUI. Working rules and DOS/Open Watcom build traps are in `AGENTS.md`. diff --git a/tools/tests/test_dos_names.py b/tools/tests/test_dos_names.py deleted file mode 100644 index 2daf1d5..0000000 --- a/tools/tests/test_dos_names.py +++ /dev/null @@ -1,72 +0,0 @@ -"""Host-side checks for constraints the DOS toolchain enforces far too late. - -Everything here runs without DOSBox-X. The point is to fail in a tenth of a -second with the offending name, instead of after a DOSBox-X boot and ten -successful object builds -- and with a message that says what is actually wrong. -A 9-character source name reaches the DOS build as ``Unable to open "src\\x.c"``, -which reads as a missing file rather than a name that cannot be represented. -""" -from __future__ import annotations - -from pathlib import Path - -import pytest - -from ferrolang_vm.paths import ROOT -from ferrolang_vm.registry import CASES - -# The runner copies these onto a FAT filesystem, where a name is at most eight -# characters plus a three-character extension. -COPIED_TREES = ("fec/src", "fec/std", "fec/tests") - - -def _offenders(root: Path) -> list[str]: - bad = [] - for path in sorted(root.rglob("*")): - name = path.name - if name.startswith("."): - continue - stem, _, suffix = name.rpartition(".") if "." in name else (name, "", "") - if len(stem) > 8 or len(suffix) > 3: - bad.append(f"{path.relative_to(ROOT).as_posix()} (stem {len(stem)}, ext {len(suffix)})") - return bad - - -@pytest.mark.parametrize("tree", COPIED_TREES) -def test_copied_files_fit_dos_8_3(tree: str) -> None: - root = ROOT / tree - if not root.is_dir(): - pytest.skip(f"{tree} is absent") - bad = _offenders(root) - assert not bad, ( - f"{len(bad)} name(s) under {tree} cannot be represented on the DOS side.\n" - "The DOS build will report them as missing files, not as long names:\n " - + "\n ".join(bad) - ) - - -def test_registry_paths_exist_on_the_host() -> None: - """Every ``.FE`` a case names must exist, matched case-insensitively. - - DOS is case-insensitive, so a registry typo survives until the command runs - inside the VM and fails with a message about the wrong thing. - """ - available = { - path.relative_to(ROOT / "fec").as_posix().upper() - for path in (ROOT / "fec").rglob("*.fe") - } - available |= { - path.relative_to(ROOT / "fec").as_posix().upper() - for path in (ROOT / "fec").rglob("*.FE") - } - missing = [] - for case in CASES: - for token in case.command.split(): - if not token.upper().endswith(".FE"): - continue - wanted = token.replace("\\", "/").upper() - if wanted.startswith("STD/"): - wanted = f"STD/{wanted[4:]}" - if not any(entry.endswith(wanted) for entry in available): - missing.append(f"{case.id}: {token}") - assert not missing, "registry names fixtures that do not exist:\n " + "\n ".join(missing) diff --git a/tools/tests/test_host_syntax.py b/tools/tests/test_host_syntax.py deleted file mode 100644 index 69a3e2b..0000000 --- a/tools/tests/test_host_syntax.py +++ /dev/null @@ -1,113 +0,0 @@ -"""Compile the compiler's own sources on the host, as a syntax gate. - -This is not verification. AGENTS.md is explicit that a host compiler's result -does not count, and it still does not: the DOS build and the milestone suite -decide whether anything works. What this buys is the turnaround. A missing -declaration or a signature that disagrees with its definition used to surface -only after a DOSBox-X boot and a full compiler build; here it surfaces in about -a second, with the line number. - -It runs the pinned toolchain's Windows-hosted 16-bit driver with the exact -command ``fec/build-dos.bat`` uses, so the diagnostics match what the DOS build -sees. A 32-bit compile is not equivalent: it misses warnings that only the -16-bit large model reports, which is how a dead function survived the M7 -unification with a clean 32-bit check. - -What it still cannot see is the DOS environment itself -- memory limits, the -command line length, the filesystem. The DOS build and the milestone suite -remain the gate. - -It uses the pinned toolchain only. There is no environment override and no -skip: a system-wide Watcom is a different version reporting different things, -and a gate that quietly skips is not a gate. -""" -from __future__ import annotations - -import os -import re -import subprocess -from pathlib import Path - -import pytest - -from ferrolang_vm.paths import ROOT - -SRC = ROOT / "fec" / "src" -# Mirrors the compile order in fec/build-dos.bat. -SOURCES = ("arena", "diag", "lexer", "ast", "parser", "types", "m7", "own", - "check", "lower", "emit_c", "driver") - - -@pytest.fixture(scope="session") -def watcom() -> Path: - """The pinned toolchain, and nothing else. - - Deliberately no environment override and no skip. A system-wide Open Watcom - is a different version with different diagnostics, and a gate that skips is - a gate that is not running -- which is the failure mode this file exists to - close. dosboxx.py fails the same way when the toolchain is missing. - """ - base = ROOT / ".dosboxx" / "watcom" - if not (base / "binnt" / "wcl.exe").is_file(): - raise AssertionError( - f"the pinned Open Watcom is not at {base}; " - "run `uv run ferro-dos setup --accept-watcom-license`") - return base - - -@pytest.fixture(scope="session") -def objdir(tmp_path_factory: pytest.TempPathFactory) -> Path: - return tmp_path_factory.mktemp("wcc") - - -@pytest.mark.parametrize("name", SOURCES) -def test_source_compiles_clean(name: str, watcom: Path, objdir: Path) -> None: - source = SRC / f"{name}.c" - if not source.is_file(): - pytest.fail(f"{source} is missing but build-dos.bat compiles it") - env = dict(os.environ) - env["WATCOM"] = os.fspath(watcom) - env["INCLUDE"] = os.fspath(watcom / "h") - env["PATH"] = os.pathsep.join( - [os.fspath(watcom / "binnt"), env.get("PATH", "")]) - # The same command build-dos.bat runs, minus the object name. - completed = subprocess.run( - [os.fspath(watcom / "binnt" / "wcl.exe"), "-q", "-za", "-wx", - "-bt=dos", "-ml", "-k32768", "-c", f"-i={SRC}", os.fspath(source)], - cwd=objdir, capture_output=True, text=True, env=env, timeout=120, - ) - output = (completed.stdout + completed.stderr).strip() - # -wx keeps warnings meaningful, so treat any diagnostic as a failure. The - # DOS build prints them to a screen nobody reads, which is how they - # accumulate unnoticed. - assert completed.returncode == 0 and not output, ( - f"{name}.c does not compile clean\n{output}" - ) - - -def test_build_scripts_agree_on_sources() -> None: - """The two build files and this test must name the same translation units. - - They drifted apart while the M7 wrapper existed, which is how a source could - stop being compiled without anyone noticing. - """ - batch = (ROOT / "fec" / "build-dos.bat").read_text(encoding="utf-8", errors="replace") - makefile = (ROOT / "fec" / "Makefile").read_text(encoding="utf-8", errors="replace") - in_batch = set(re.findall(r"src\\(\w+)\.c", batch)) - srcline = next(line for line in makefile.splitlines() if line.startswith("SRC =")) - in_make = set(re.findall(r"src/(\w+)\.c", srcline)) - assert in_batch == set(SOURCES), f"build-dos.bat compiles {sorted(in_batch)}" - assert in_make == set(SOURCES), f"Makefile compiles {sorted(in_make)}" - - -def test_no_source_is_orphaned() -> None: - """Every .c under fec/src must be compiled by something. - - check_m7.c and emitcm7.c hid check.c and emit_c.c from the build by - including them textually; nothing flagged that they had stopped being - translation units of their own. - """ - on_disk = {p.stem for p in SRC.glob("*.c")} - assert on_disk == set(SOURCES), ( - f"fec/src has {sorted(on_disk - set(SOURCES))} that no build step compiles" - ) diff --git a/tools/tests/test_milestones_dosboxx.py b/tools/tests/test_milestones_dosboxx.py deleted file mode 100644 index 8b8ae3f..0000000 --- a/tools/tests/test_milestones_dosboxx.py +++ /dev/null @@ -1,55 +0,0 @@ -from __future__ import annotations - -import os -import warnings - -import pytest - -from ferrolang_vm.dosboxx import SuiteRun, run_suite -from ferrolang_vm.registry import MAX_MILESTONE, all_cases, milestone_number -from ferrolang_vm.suite import Case - -ONLY = os.environ.get("FERRO_TEST_ONLY") -CASES = all_cases( - through=milestone_number(os.environ.get("FERRO_TEST_THROUGH", f"m{MAX_MILESTONE}")), - only=milestone_number(ONLY) if ONLY else None, -) - - -@pytest.fixture(scope="session") -def suite_run() -> SuiteRun: - run = run_suite( - CASES, - keep=os.environ.get("FERRO_TEST_KEEP_FAILED") == "1", - show_dos=os.environ.get("FERRO_TEST_SHOW_DOS") == "1", - trace_dos=os.environ.get("FERRO_TEST_TRACE_DOS") == "1", - ) - yield run - if os.environ.get("FERRO_TEST_DOS_LOG") == "1": - console = run.root / "CONSOLE.LOG" - if console.is_file(): - print(console.read_text(encoding="utf-8", errors="replace")) - run.cleanup() - - -def test_compiler_build(suite_run: SuiteRun) -> None: - assert suite_run.result() == "PASS", suite_run.log() - - -@pytest.mark.parametrize("case", CASES, ids=lambda case: case.id) -def test_milestone_case(case: Case, suite_run: SuiteRun) -> None: - if suite_run.result() != "PASS": - pytest.skip("compiler build failed") - result = suite_run.result(case) - err = suite_run.err(case) - if result == "PASS" and err: - warning_lines = [line for line in err.splitlines() if "warning" in line.lower()] - if warning_lines: - warnings.warn("\n".join(warning_lines), stacklevel=1) - code = suite_run.rc(case) - assert result == "PASS", ( - f"DOS command: {case.command}\n" - f"Expected success: {case.expect_success}\n" - f"Exit code: {'not recorded' if code is None else code}\n" - f"{suite_run.log(case)}\n{err}" - ) diff --git a/tools/toolchains/dosboxx.lock.json b/tools/toolchains/dosboxx.lock.json deleted file mode 100644 index 08b1123..0000000 --- a/tools/toolchains/dosboxx.lock.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "schema": 1, - "dosboxx": { - "version": "2026.08.02", - "url": "https://github.com/joncampbell123/dosbox-x/releases/download/dosbox-x-v2026.08.02/dosbox-x-vsbuild-win64-2026.08.02-portable.zip", - "sha256": "ca28208f5fee25a74caf3a02cc0189c7f7943a42ce37c02848f5fc03450a96cf", - "executable": "bin/x64/Release/dosbox-x.exe" - }, - "open_watcom": { - "version": "2026-08-01-Build", - "url": "https://github.com/open-watcom/open-watcom-v2/releases/download/2026-08-01-Build/open-watcom-2_0-c-dos.exe", - "sha256": "80db4ab340f382e59bf3d396280576ec837964c2ef00e8ddd3b2b3724ab63edf", - "required": [ - "binw/wcl.exe", - "binw/wcl386.exe", - "binp/wlink.exe", - "h/stdio.h", - "lib286/dos/clibl.lib", - "lib386/dos/clib3r.lib", - "license.txt" - ] - } -}