Write a .lvm script that defines your language's tokens, grammar, semantics, and code generation. LangVM executes it and becomes a working implementation.
// A complete language in one .lvm script language SayLang version "1.0"; tokens { casesensitive = true; token keyword.say = "say"; token delimiter.semicolon = ";"; token string.default = "\""; } grammar { rule stmt.say { expect keyword.say; let text: string = currentText(); advance(); setAttr(getResultNode(), "message", text); requireToken("delimiter.semicolon"); } } emitters { on stmt.say { let msg: string = getAttr(node, "message"); mirString("msg_0", msg); mirInsn("call", "p_printf", "printf", "_", "msg_0"); } }
$ lvm -l say.lvm -s hello.say hello world // hello.say contained: // say "hello"; // say "world"; // // Output: a native PE executable
LangVM scripts are Turing-complete. These examples show the scripting language that powers the pipeline.
// tokens{} -- define the lexer at runtime // LangVM's generic lexer configures itself from these declarations tokens { casesensitive = true; // Keywords token keyword.say = "say"; token keyword.let = "let"; token keyword.if = "if"; token keyword.while = "while"; token keyword.routine = "routine"; // Operators and delimiters token operator.assign = "="; token operator.plus = "+"; token operator.less = "<"; token delimiter.semi = ";"; token delimiter.lparen = "("; token delimiter.rparen = ")"; // Literals and comments token string.default = "\""; token comment.line = "//"; }
// grammar{} -- define Pratt parser rules // prefix, infix, and statement rules parse YOUR language's source grammar { // Statement rule: say "text"; rule stmt.say { expect keyword.say; let text: string = currentText(); advance(); let nd: handle = getResultNode(); setAttr(nd, "message", text); requireToken("delimiter.semi"); } // Statement rule: let x = expr; rule stmt.var_decl { expect keyword.let; let name: string = currentText(); advance(); requireToken("operator.assign"); parseExpr(); requireToken("delimiter.semi"); } // Infix rule: a + b rule infix.add { trigger operator.plus; precedence = 50; parseExpr(precedence); } }
// semantics{} -- analysis passes over the AST // Scope management, symbol declaration, type checking semantics { on stmt.var_decl { let name: string = getAttr(node, "name"); // Check for duplicate declaration if scopeHas(name) { diagError("duplicate: " + name, node); } // Declare in current scope scopeDeclare(name, "variable"); // Walk child nodes (the initializer expression) walkChildren(node); } on expr.identifier { let name: string = getAttr(node, "name"); if !scopeHas(name) { diagError("undeclared: " + name, node); } } on stmt.say { // Validate: no-op for this simple language } }
// emitters{} -- walk the AST, emit output // For native compilation, emit MIR instructions emitters { on stmt.say { let msg: string = getAttr(node, "message"); // Strip surrounding quotes let text: string = substr(msg, 1, len(msg) - 2); let text_nl: string = text + "\n"; // Emit string data into MIR let str_name: string = "msg_" + toString(g_str_count); mirString(str_name, text_nl); g_str_count = g_str_count + 1; // Emit: call printf with the string mirInsn("call", "p_printf", "printf", "_", str_name); } }
// MIR -- a virtual CPU assembly language // Written inline or built programmatically by emitters mir { m0: module import printf, ExitProcess export main p_printf: proto void, p:fmt, ... p_exit: proto void, i32:code msg: string "Hello MIR!\n" main: func i64 local i64:n mov n, 0 add n, n, 1 call p_printf, printf, msg call p_exit, ExitProcess, 0 ret n endfunc endmodule } // on-handlers lower each MIR event to real x64: mir { on module { g_ib = ib_create(); g_cb = cb_create(1024); g_db = db_create(); } on func { cb_emit_sub_rsp_imm8(g_cb, 0x28); } on insn { if opcode == "call" { cb_emit_call_rip_disp32(g_cb); } } on endfunc { cb_emit_add_rsp_imm8(g_cb, 0x28); cb_emit_ret(g_cb); } on endmodule { let image: map = pe_build_exe(...); bufSave(image, exe_path); ExitCode = runPE(exe_path); } }
// Buffer builtins make binary data construction possible // This is how PE/ELF images are built from script let buf: any = buffer(4096); // Write PE DOS header bufWriteU16(buf, 0, 0x5A4D); // MZ signature bufWriteU32(buf, 0x3C, 0x80); // PE header offset // Write PE signature bufWriteU32(buf, 0x80, 0x00004550); // "PE\0\0" // Write machine code directly bufWriteU8(buf, offset, 0x48); // REX.W prefix bufWriteU8(buf, offset+1, 0x83); // sub r/m64, imm8 bufWriteU8(buf, offset+2, 0xEC); // ModRM: rsp bufWriteU8(buf, offset+3, 0x28); // 40 bytes shadow // Copy between buffers bufCopyBytes(dst, dst_off, src, src_off, count); // Save to disk and execute bufSave(buf, "output.exe"); let code: int = runPE("output.exe");
// The .lvm scripting language is Turing-complete // Variables, control flow, routines, data structures let count: int = 0; let names: list = ["alpha", "beta", "gamma"]; let config: map = {"debug": true, "level": 3}; // For loop with range for i = 0 to len(names) - 1 { println(names[i]); } // While loop while count < 10 { count = count + 1; if count == 5 { continue; } println(toString(count)); } // Routines with return values routine fib(n: int): int { if n <= 1 { return n; } return fib(n - 1) + fib(n - 2); } println("fib(10) = " + toString(fib(10)));
Everything needed to go from language idea to native binary -- lexer, parser, semantic analysis, code generation, and backend -- all scriptable.
tokens{}, grammar{}, semantics{}, emitters{}, and mir{} blocks. LangVM configures its generic lexer and Pratt parser at runtime from your definitions.mirInsn, mirString, mirBeginFunc -- build programs in MIR, lower them to x64.LangVM.pas into your Delphi project. Create a TLangVM, load a .lvm script, call Run('main'). Your application gains language-implementation capabilities.bufWriteU8, bufWriteU32, bufCopyBytes, bufSave -- construct arbitrary binary data from script. PE headers, ELF segments, machine code, all from .lvm.Each pipeline stage is defined by a block in your .lvm script. LangVM's runtime engine executes them in sequence.
prefix, infix, and statement -- parse your language's source into an AST. Each rule calls parseExpr(), advance(), setAttr() to build nodes.on stmt.var_decl, on expr.call) walk the AST for scope management, symbol declaration, type checking, and attribute stamping.mirInsn(), mirString(), mirBeginFunc() to build a MIR program.on module, on func, on insn, on endmodule) lower MIR events to machine code using encoder, ABI, and PE/ELF scripts -- all written in .lvm.Whether you are designing a DSL, writing a compiler, or teaching how they work.
Infographic, walkthroughs, and a deep dive into the pipeline architecture.
No SDK, no package manager, no setup wizard.