OJaml / docsProject documentation

Explanation

Thesis and Design Contract

Understand the design decisions, alternatives, and limits.

OJaml is built around one constraint: the browser demo must be the real language implementation. There is no server compiler and no interpreter shortcut hidden behind the editor. The same source pipeline supports the reusable editor, the route embedded in the website, the command-line interface, the tests, and the generated WebAssembly output.

SourceLexerTokensParserASTCheckerTyped metadataWAT emitterWebAssemblymain()token/span metadatachecked symbols, tokens, main typeWABT conversionbrowser instantiate and execute
Whole-system pipeline. The checker consumes the parser's AST as its semantic input. Token and span metadata from lexing travels on the dashed path for diagnostics, hovers, and source ranges.
run⁡(s)=instantiate⁡(wabt⁡(emit⁡(check⁡(parse⁡(lex⁡(s))))))\operatorname{run}(s) = \operatorname{instantiate}(\operatorname{wabt}(\operatorname{emit}(\operatorname{check}(\operatorname{parse}(\operatorname{lex}(s))))))
Compiler contract. Runtime behavior is downstream of syntax and type checking; invalid programs do not reach WebAssembly emission.

OJaml keeps the source language narrow enough for the whole compiler to fit in a TypeScript codebase, while covering the mechanisms that distinguish an ML-family compiler from a syntax demo: inference, recursion, closures, high-arity function values, heap allocation, indirect calls, polymorphic containers, and browser-native tooling. Syntax is OCaml-like, values cross WebAssembly function boundaries through a uniform i32 slot, static checks run before emission, and every standard-library function has an explicit type scheme.

The implementation keeps stage boundaries visible. Lexing decides what tokens exist. Parsing decides the tree shape. Checking decides whether names, calls, branches, patterns, and standard-library uses are valid. Code generation decides memory layout and call shape. Runtime execution only runs programs that survived those prior stages.

The main boundary is between the language surface and the representation the machine runs. The source language has ints, floats, tuples, records, functions, arrays, lists, sets, maps, strings, and pattern matching. The emitted WebAssembly mostly sees immediate integers and heap pointers. The type checker records the meaning that the backend erases from the raw i32 signatures.

  • The editor, examples, tests, and CLI exercise the same language stages.
  • The checker owns static validity; the runtime assumes checked programs.
  • The backend targets portable WebAssembly text instead of JavaScript evaluation.
  • The current scope is finite: top-level and nested modules, local type declarations, abstract and concrete type signatures, and value signatures are supported, but file imports, functors, exceptions, and garbage collection are not.