maiac
v1.2.13
Published
MaiaC (WebC): C89-subset to WebAssembly compiler in JavaScript.
Maintainers
Readme
MaiaC (WebC)
MaiaC is a C-to-WebAssembly compiler focused on a practical C89 subset and a browser/Node execution workflow.
Process Documentation
Mandatory cross-repository synchronization workflow:
Overview
MaiaC currently provides:
- C source parsing from EBNF-generated parser artifacts.
- Semantic lowering to a WAT module model.
- WAT emission through template-based backend code.
- Validation/assembly through MaiaWASM.
- Runtime execution support in Node and browser wrappers.
The project uses two local submodules:
- MaiaCC: grammar-to-parser generation.
- MaiaWASM: WAT assembler and validation pipeline.
Repository Structure
compiler/: parser integration, semantic analysis, lowering, WAT generation, tests.grammar/: C grammar source files.tools/: parser generation scripts, runtime helpers, browser runner.bin/: convenience wrappers (webc.sh,run-test-node.sh,run-wasm-browser.sh).docs/: project documentation.maiacc/: MaiaCC submodule.maiawasm/: MaiaWASM submodule.
Quick Start
1) Build the C parser from EBNF
bash tools/build-c-parser.sh2) Compile a C file to WAT and validate
bash bin/webc.sh --file ./compiler/examples/test.c --wat-out ./out/test.wat3) Run a compiled program in Node
bash bin/run-test-node.sh compiler/examples/test.c4) Run in browser (development runner)
bash bin/run-wasm-browser.shThen open:
http://127.0.0.1:8080/tools/browser/run-wasm.html
Runtime Tooling (Consistent Flow)
tools/webc.js (compile + wrapper + optional run + dist)
webc compiles a C source and emits:
<out>.wasm<out>.jswrapper (createImports()+run())- optional
<out>.wat
It can also package a complete distributable folder (--dist) containing browser and Node runners.
Example:
node tools/webc.js compiler/examples/test.c -o out/test --watRun immediately:
node tools/webc.js compiler/examples/test.c -o out/test --runOptional (experimental include expansion):
node tools/webc.js compiler/examples/test.c -o out/test --run --resolve-system-includesCreate distribution package (browser + node):
node tools/webc.js compiler/examples/test.c --dist --out-dir dist --name test --watCreate dist and run generated Node runner in one step:
node tools/webc.js compiler/examples/test.c --dist-run --out-dir dist --name testDist outputs include:
dist/test.wasmdist/test.jsdist/test.wat(when--watis used)dist/manifest.jsondist/browser-runner.htmldist/browser-memory-file-store.jsdist/node-runner.jsdist/node-runner.sh
Run from dist in Node:
node dist/node-runner.js
# or
bash dist/node-runner.shRun from dist in browser: serve the repo root and open dist/browser-runner.html.
tools/create-dist.js (compatibility wrapper)
create-dist.js remains available for backward compatibility and delegates to webc --dist internally:
node tools/create-dist.js compiler/examples/test.c -o dist --name test --wattools/host-env-builder.js (extern __host__path bridge)
host-env-builder converts compiler hostImports metadata into imports.env functions.
C example:
extern void __console__log(char *msg);
extern double __Math__sin(double x);At runtime this maps to console.log(...) and Math.sin(...), with automatic C string dereference for char * parameters.
Recommended Validation Flow
Use this sequence to validate compiler/runtime behavior quickly and consistently.
1) Core large example (compiler/examples/test.c)
node tools/webc.js compiler/examples/test.c -o out/test --run
node tools/run-test-node.js compiler/examples/test.c
node tools/webc.js compiler/examples/test.c --dist --out-dir dist --name test
node dist/node-runner.jsExpected result: all commands complete and return 0.
2) Host extern bridge (compiler/examples/test-extern.c)
node tools/webc.js compiler/examples/test-extern.c -o out/test-extern --run
node tools/run-test-node.js compiler/examples/test-extern.cExpected result: host calls such as __console__log and __Math__sqrt execute correctly and return 0.
3) About --resolve-system-includes
- This flag is optional and currently experimental for complex sources.
- It is useful when you explicitly want inline expansion of system headers.
- The default validation path for
test.candtest-extern.cshould run without this flag.
setjmp/longjmp Runtime Semantics (Current)
- Current implementation is host-assisted and emulated resume.
longjmprestores__stack_ptr/__frame_ptr, signals unwind, and runtime re-enters the program entrypoint.- The next
setjmpcapture for the samejmp_bufreturns the pending value (longjmp(..., 0)normalizes to1). - This is now handled consistently in:
tools/run-test-node.jstools/webc.js --run- generated wrapper
run() webc --distbrowser runner pagewebc --distnode runner scripts
Compiler CLI
Main CLI entrypoint:
compiler/c-compiler.js
Typical options:
--code <c-code>: compile inline source.--file <path>: compile source from file.--ast: print parse tree.--print-json,--print-xml: print parse outputs.--json-out,--xml-out: write parse outputs.--wat-out: write generated WAT.--wasm-out: write assembled WASM.--no-wat: suppress WAT stdout.--no-validate: skip validation/assembly.
Example:
node compiler/c-compiler.js --file ./compiler/examples/test.c --wat-out ./out/test.wat --wasm-out ./out/test.wasmTest Strategy
Run the full bundle:
node compiler/tests/test-all.jsRun full regression (bundle + additional focused diagnostics):
bash tools/run-full-tests.sh
# or
npm run test:fullIncludes:
- Preprocessor tests.
- C89 mini-suite runtime validation.
- Rich C89 example-suite dist/runtime validation (
compiler/examples/suite/). - Large end-to-end example test.
stdargend-to-end tests.setjmp/longjmpbootstrap + emulated-resume tests.
The full-regression script also executes focused tests that are intentionally kept
outside test-all.js (debug/diagnostic pointer and WAT checks) to provide a
broader post-change safety net.
Optional JSON report:
node compiler/tests/test-all.js --json-out compiler/tests/outputs/test-report.jsonFor mini-suite artifacts (disabled by default):
MAIAC_WRITE_TEST_OUTPUTS=1 node compiler/tests/test-c89-mini-suite.jsFor the rich example suite directly:
bash compiler/examples/suite/build_all.sh
bash compiler/examples/suite/run_all.shExamples (Large + Small)
Large Example (test.c) in Node
node tools/examples/run-test-node-example.jsLarge Example (test.c) in Browser
- Build wasm:
node tools/examples/build-test-wasm.js- Serve the repository root:
bash bin/run-wasm-browser.sh- Open:
http://127.0.0.1:8080/tools/browser/test-large.html
Small Example (simple_add.c) in Node
node tools/examples/build-simple-add-wasm.js
node tools/examples/run-simple-add-node.jsSmall Example (simple_add.c) in Browser
After starting the same browser server (bin/run-wasm-browser.sh), open:
http://127.0.0.1:8080/tools/browser/simple-add.html
Architecture Documentation
For system design, data flow, and diagrams, see:
Bootstrap details:
Consistency Rules for Submodules
maiaccin MaiaC and MaiaWASM should reference the same canonical MaiaCC commit.- Parser changes should be made in grammar sources and regenerated, not edited directly in generated parser files.
- When MaiaWASM parser changes are required, update
grammar/WAT.ebnf, regenerateassembler/wat-parser.js, and then update MaiaC submodule pointer.
Current Scope and Notes
- Current target is a practical C89 subset with strong runtime validation.
- Some advanced C/library features remain intentionally out of scope.
- Keep generated test artifacts out of version control unless explicitly needed.
