writeonce/compiler/bin/main.ml
shoney.arickathil 012562290d feat(compiler): Tasks 1-4 — scaffold, diagnostics, lexer, declaration parser
- Task 1: OCaml/dune scaffold with woc CLI (exit codes 0/1/2), just woc-build/woc-test
- Task 2: Diagnostics module — WO-E coded diagnostics with excerpts, related sites, ordered dedup collector, exit-code decision
- Task 3: Newline-significant lexer mirroring rt conventions (self/me/subscribe/insert stay idents), --dump-tokens, golden test framework with bless mode
- Task 4: Declaration parser — class/type/interface/fn signatures, @gc/@table annotations, brace-depth skip-on-block (service/policy/on), decl-level recovery, --dump-ast goldens
- 13 golden fixtures (tokens + AST) with WOC_BLESS=1 support
- just woc-test green (14 diag + 93 runner checks)
2026-08-10 09:00:08 +02:00

110 lines
4 KiB
OCaml

(* woc — the writeonce OCaml compiler front end.
Task 1 scaffolded the CLI's exit-code contract with no compiler
stage behind it. Task 2 added diagnostics (compiler/src/diag.ml).
Task 3 added the lexer (compiler/src/{token,lexer}.ml) behind
--dump-tokens. Task 4 adds the declaration parser
(compiler/src/{ast,parser}.ml) behind --dump-ast, printing a stable,
golden-diffed AST dump (compiler/src/dump.ml) to stdout. Tasks 5-7
add statement/expression parsing, the typechecker, and the ownership
pass behind their own --dump-* flags; Task 8 adds directory
discovery and multi-file programs, at which point the bare
`woc <path>` form below starts actually compiling instead of just
checking existence.
0 = clean compile
1 = diagnostics reported (reachable now: --dump-tokens on source
with an unknown character, or --dump-ast on source with a
broken declaration, reports WO-E0xx/WO-E1xx and exits 1)
2 = usage or IO failure
With no arguments, this prints usage to stderr and exits 2. Given a
single path argument (no flag), it exits 0 if the path exists and 2
(with a stderr message) if it does not — this bare-path form
predates any real compilation and stays as-is until Task 8. *)
let usage_msg =
"usage: woc <path>\n\
usage: woc --dump-tokens <file.wo>\n\
usage: woc --dump-ast <file.wo>\n\
\n\
Compiles writeonce (.wo) source. <path> is a single .wo file or a\n\
directory to discover .wo files under (directory discovery lands in\n\
a later task; compilation itself has not landed yet either).\n\
\n\
--dump-tokens prints one line per lexed token to stdout, in source\n\
order (\"LINE:COL KIND\" or \"LINE:COL KIND(payload)\"), ending with\n\
EOF; lexing diagnostics, if any, print to stderr.\n\
\n\
--dump-ast prints the declaration- and body-level AST as an indented\n\
tree to stdout (class/type/interface/fn declarations, with real\n\
statement/expression parsing inside method and free-fn bodies);\n\
parsing diagnostics, if any, print to stderr.\n\
\n\
Exit codes: 0 clean, 1 diagnostics reported, 2 usage/IO failure.\n"
let read_source path =
try
let ic = open_in_bin path in
let n = in_channel_length ic in
let s = really_input_string ic n in
close_in ic;
Ok s
with Sys_error msg -> Error msg
let dump_tokens path =
if not (Sys.file_exists path) then begin
Printf.eprintf "woc: no such file or directory: %s\n" path;
exit 2
end;
match read_source path with
| Error msg ->
Printf.eprintf "woc: %s\n" msg;
exit 2
| Ok src ->
let collector = Woc_lib.Diag.Collector.create () in
let toks = Woc_lib.Lexer.tokenize collector ~file:path src in
print_string (Woc_lib.Dump.dump_tokens toks);
if Woc_lib.Diag.Collector.has_error collector then begin
let lookup f = if f = path then Some src else None in
prerr_string (Woc_lib.Diag.Collector.render_all collector lookup);
prerr_newline ();
exit 1
end
else exit 0
let dump_ast path =
if not (Sys.file_exists path) then begin
Printf.eprintf "woc: no such file or directory: %s\n" path;
exit 2
end;
match read_source path with
| Error msg ->
Printf.eprintf "woc: %s\n" msg;
exit 2
| Ok src ->
let collector = Woc_lib.Diag.Collector.create () in
let toks = Woc_lib.Lexer.tokenize collector ~file:path src in
let prog = Woc_lib.Parser.parse collector ~file:path toks in
print_string (Woc_lib.Dump.dump_ast prog);
if Woc_lib.Diag.Collector.has_error collector then begin
let lookup f = if f = path then Some src else None in
prerr_string (Woc_lib.Diag.Collector.render_all collector lookup);
prerr_newline ();
exit 1
end
else exit 0
let () =
match Sys.argv with
| [| _; "--dump-tokens"; path |] -> dump_tokens path
| [| _; "--dump-ast"; path |] -> dump_ast path
| [| _; path |] ->
if Sys.file_exists path then exit 0
else begin
Printf.eprintf "woc: no such file or directory: %s\n" path;
exit 2
end
| _ ->
prerr_string usage_msg;
exit 2