Elvelt

Frontends

MPL Compiler Frontends — Overview

MPL Compiler Frontends — Overview

MPL_Compiler ships 9 first-class language frontends that share a single target: a unified EMPL IR (Extended Multi-Platform Language Intermediate Representation) defined in core/ir/EMPLNode.h. From that IR the only currently-shipped backend (backends/cpp/) lowers the program to C++20.

Source language Frontend file File extensions Document
JavaScript / TypeScript frontends/javascript/JSFrontend.h .js .mjs .cjs .ts .tsx .mts .cts .jsx javascript.md
Python frontends/python/PythonFrontend.h .py python.md
C# frontends/csharp/CSharpFrontend.h .cs csharp.md
Go frontends/go/GoFrontend.h .go go.md
Rust frontends/rust/RustFrontend.h .rs rust.md
PHP frontends/php/PHPFrontend.h .php php.md
Ruby frontends/ruby/RubyFrontend.h .rb ruby.md
Java frontends/java/JavaFrontend.h .java java.md
EMPLS (built-in) frontends/empls/EMPLSFrontend.h .empls empls.md

Two additional frontends exist (Swift, Kotlin) but are not covered here.


Architectural map

              ┌──────────────────────────────────────────────────────┐
              │              ILanguageFrontend (interface)           │
              │    core/plugin/ILanguageFrontend.h:15                │
              ├──────────────────────────────────────────────────────┤
              │ languageName()                                       │
              │ fileExtensions()                                     │
              │ parse(source, filename) -> ModuleNode                │
              └──────────────────────────────────────────────────────┘
                                ▲        ▲
        ┌───────────────────────┘        └───────────────────────────┐
        │                                                        │
┌───────┴───────────┐                                ┌────────────┴─────────────┐
│ FrontendBase      │                                │ BraceFrontendBase        │
│ core/plugin/      │                                │ core/plugin/             │
│ FrontendBase.h    │                                │ BraceFrontendBase.h      │
│ (line-based,      │                                │ (brace-delimited langs:  │
│  simple print +   │                                │  JS, C#, Go, Rust, PHP,  │
│  var declarations)│                                │  Java, EMPLS, …)         │
└───────────────────┘                                └──────────────────────────┘

PluginRegistry (core/plugin/PluginRegistry.h:12) is a process-wide singleton that holds an unordered map of language-name → frontend and an extension → language-name map. The CLI uses it to dispatch by file extension or by explicit --target argument.


Quick start: how a source file becomes C++

  1. CLI dispatch (MPL_Compiler.cpp:32-47): registerPlugins() registers nine frontend instances and one backend (CppBackend). The CLI then reads argv[1], finds the matching frontend via PluginRegistry::getFrontend (extension lookup), and calls frontend->parse(source, filename).
  2. Frontend parsing: each frontend is a BraceFrontendBase subclass (or FrontendBase for line-oriented languages like Python and Ruby). It produces an ir::ModuleNode.
  3. EMPL IR: every frontend yields the same node types: ModuleNode, FunctionDeclNode, ClassDeclNode, InterfaceDeclNode, EnumDeclNode, VariableDeclNode, BlockNode, IfNode, ForNode, ForEachNode, WhileNode, SwitchNode, TryCatchNode, BinaryOpNode, UnaryOpNode, TernaryNode, FunctionCallNode, MethodCallNode, MemberAccessNode, IndexAccessNode, LiteralNode, LambdaNode, AwaitNode, YieldNode, ImportNode, ExportNode, AssignmentNode, BreakNode, ContinueNode, ReturnNode, StandardLibraryCallNode, UnsupportedNode, DestructuringPatternNode, InterfaceDeclNode, TypeAliasDeclNode, and a few more.
  4. C++ backend: backends/cpp/CppBackend.h walks the IR and emits C++ that links against MPL_Standard/System/empl.h (and the language-specific runtime under frontends/<lang>/runtime/ for JavaScript).

The IR is language-neutral

core/ir/EMPLNode.h is the contract every frontend and every backend shares. Critical language-neutral rules enforced by all frontends:

Rule Source line convention
metadata keys MUST be language-neutral decl_origin, async, generator, using_decl, using_await, exported, export_name, global_using, static_using, etc. — never js_*, csharp_*, etc.
decl_origin values var, let, const, :=, pub, final, require, import, import_eq, using, etc.
Standard library calls One node: StandardLibraryCallNode(moduleName, functionName, args) with moduleName from {Console, Math, String, IO, Process, Environment, DateTime, Json, Convert, Collection, …}.
Brace-only languages Inherit BraceFrontendBase. The base owns parseBlock, parseStatement, expression parsing, and print patterns.
Indentation-only languages Inherit FrontendBase or implement the parse() entry directly (Python and Ruby both do the latter, building their own indent-stack scope tracker).

See architecture.md for the full lex/parse/IR pipeline.


Standard library — single source of truth

All language-specific standard library mapping tables live in frontends/<lang>/<Lang>StdLib.h / .cpp. They each map the source language's qualified name (e.g. console.log, math.sqrt, fmt.Println, println!) to a language-neutral MPL module+function (e.g. Console.WriteLine, Math.Sqrt). The canonical spec lives at MPL_Standard/Specification.json; the C++ implementations are in MPL_Standard/System/CppImpl.h.

File Mappings
JsStdLib.cpp console.log/info/warn/error/debug, Math.*, process.exit/cwd, JSON.stringify/parse
PythonStdLib.h print, input, len, abs, round, min, max, int, float, str, math.sqrt/floor/ceil/pow/log/sin/cos/tan, os.path.*, sys.exit, json.loads/dumps
CSharpStdLib.cpp Two large tables: typeMap (~570 entries, Console → System.Console, etc.) and methodMap (~470 entries)
GoStdLib.h fmt.Println/Printf/Print/Sprintf, math., os., strings.*
RustStdLib.h println!/print!/eprintln!/eprint!, std::process::exit, std::env::var, std::env::current_dir, std::fs::read_to_string, std::fs::write
PhpStdLib.h echo, print, var_dump, print_r, abs, floor, ceil, round, sqrt, pow, max, min, log, sin, cos, rand, strlen, strtolower, strtoupper, trim, explode, str_starts_with, str_ends_with, exit, die, getenv, json_encode, json_decode, file_get_contents, file_put_contents, file_exists
RubyStdLib.h puts, print, p, gets, exit, abort, rand
JavaStdLib.h System.out.println/print, System.err.println/print, Math.*, System.exit/getenv/getProperty, Integer.parseInt, Double.parseDouble, String.valueOf

EMPLS does not own a StdLib table — it routes through the shared brace-level print-pattern handling in BraceFrontendBase.


File layout

frontends/
├── javascript/        — JSFrontend.h + Lexer/Parser + 5 runtime headers (~32 K LOC)
├── python/            — PythonFrontend.h + PythonStdLib.h (header-only Python header)
├── csharp/            — CSharpFrontend.cpp + Lexer/Parser/Converter/Sema + StdLib
├── go/                — GoFrontend.h + Lexer/Parser + GoStdLib
├── rust/              — RustFrontend.h (single header with brace language transforms)
├── php/               — PHPFrontend.h (single header with many static transforms)
├── ruby/              — RubyFrontend.h (single header, line-based parser)
├── java/              — JavaFrontend.h (single header with many static transforms)
├── empls/             — EMPLSFrontend.h (single header; brace-based)
└── (swift, kotlin)    — not documented here

Plugin registration (pointers)

// MPL_Compiler.cpp:32-47
void registerPlugins() {
    auto& reg = plugin::PluginRegistry::instance();
    reg.registerFrontend(std::make_unique<frontends::JSFrontend>());
    reg.registerFrontend(std::make_unique<frontends::PythonFrontend>());
    reg.registerFrontend(std::make_unique<frontends::CSharpFrontend>());
    reg.registerFrontend(std::make_unique<frontends::GoFrontend>());
    reg.registerFrontend(std::make_unique<frontends::RustFrontend>());
    reg.registerFrontend(std::make_unique<frontends::PHPFrontend>());
    reg.registerFrontend(std::make_unique<frontends::RubyFrontend>());
    reg.registerFrontend(std::make_unique<frontends::JavaFrontend>());
    reg.registerFrontend(std::make_unique<frontends::EMPLSFrontend>());
    reg.registerBackend(std::make_unique<backends::CppBackend>());
}

See registration.md for the full registration mechanism and how to add a 10th language.


Per-language docs

  • javascript.md — ES2025/Node 26/TS 6 features, JSX, npm packages
  • python.md — Decorators, comprehensions, walrus, match
  • csharp.md — Records, init setter, field keyword, LINQ
  • go.md — Channels, select, go/defer, for-range, composite literals
  • rust.md — Traits, lifetimes, let-else, if-let chains, gen blocks
  • php.md — Named args, match, attributes, enums, fibers, readonly classes
  • ruby.md — String interpolation, blocks, modules, refinements
  • java.md — Text blocks, records, sealed, pattern matching, virtual threads
  • empls.md — var::, func::, class, struct, range, pipeline |>

Also see:


Cross-references

  • core/ir/EMPLNode.h — the IR contract (see also EMPLPrinter.h, EMPLValidator.h, EMPLCanonicalizer.h)
  • core/plugin/ILanguageFrontend.h — the contract every frontend obeys
  • core/plugin/PluginRegistry.h — the registry
  • MPL_Compiler.cpp:32registerPlugins()
  • MPL_Standard/Specification.json — canonical language-neutral stdlib spec
  • MPL_Standard/System/empl.h — language-neutral runtime umbrella header