Elvelt

EMPL IR

NodeKind — exhaustive enumeration

NodeKind — exhaustive enumeration

NodeKind is the closed enumeration (enum class) that discriminates every concrete IR node. It is declared in core/ir/EMPLNode.h:19-70 and currently contains 41 values grouped into seven semantic families.

// MPL_Compiler/core/ir/EMPLNode.h:19
enum class NodeKind {
    Program,
    Module,
    Import,
    Export,

    FunctionDecl,
    VariableDecl,
    DestructuringPattern,
    ClassDecl,
    InterfaceDecl,
    EnumDecl,
    TypeAliasDecl,

    Block,
    If,
    For,
    ForEach,
    While,
    DoWhile,
    Switch,
    Throw,
    Return,
    Break,
    Continue,
    TryCatch,

    Assignment,
    BinaryOp,
    UnaryOp,
    FunctionCall,
    MethodCall,
    MemberAccess,
    IndexAccess,
    Literal,
    Identifier,
    ArrayLiteral,
    ObjectLiteral,
    RecordLiteral,
    TupleLiteral,
    Lambda,
    Await,
    Yield,
    Cast,
    Ternary,
    StandardLibraryCall,

    // Legacy aliases (deprecated — prefer StandardLibraryCall over PrintCall)
    PrintCall,
    FuncDecl,
    Unsupported,
};

Complete table

Index NodeKind Class Family Source:line Description
0 Program ProgramNode Module EMPLNode.h:103 Root: graph of ModuleEntry with dependency edges and topological ordering.
1 Module ModuleNode Module EMPLNode.h:88 A single compilation unit (one source file, one library, …).
2 Import ImportNode Module EMPLNode.h:634 Cross-module import (ESM, CJS, Python import, etc.).
3 Export ExportNode Module EMPLNode.h:656 Cross-module export.
4 FunctionDecl FunctionDeclNode Declaration EMPLNode.h:162 Function or method declaration.
5 VariableDecl VariableDeclNode Declaration EMPLNode.h:446 let, const, var, etc.
6 DestructuringPattern DestructuringPatternNode Declaration EMPLNode.h:427 Object or array destructuring pattern.
7 ClassDecl ClassDeclNode Declaration EMPLNode.h:183 Class (or struct, depending on isStruct).
8 InterfaceDecl InterfaceDeclNode Declaration EMPLNode.h:199 Interface (TS / Java / C# interface).
9 EnumDecl EnumDeclNode Declaration EMPLNode.h:212 Enumeration.
10 TypeAliasDecl TypeAliasDeclNode Declaration EMPLNode.h:228 Type alias (TS type).
11 Block BlockNode Block EMPLNode.h:153 A { … } sequence of statements.
12 If IfNode Control flow EMPLNode.h:239 if (cond) { … } else { … }.
13 For ForNode Control flow EMPLNode.h:250 C-style for (init; cond; incr).
14 ForEach ForEachNode Control flow EMPLNode.h:262 for (target : iterable).
15 While WhileNode Control flow EMPLNode.h:273 while (cond).
16 DoWhile DoWhileNode Control flow EMPLNode.h:283 do { … } while (cond).
17 Switch SwitchNode Control flow EMPLNode.h:293 switch (expr) { case … }.
18 Throw ThrowNode Jump EMPLNode.h:329 throw expr.
19 Return ReturnNode Jump EMPLNode.h:320 return [expr].
20 Break BreakNode Jump EMPLNode.h:338 break.
21 Continue ContinueNode Jump EMPLNode.h:345 continue.
22 TryCatch TryCatchNode Control flow EMPLNode.h:309 try { … } catch (…) { … } finally { … }.
23 Assignment AssignmentNode Expression EMPLNode.h:460 lhs = rhs (or +=, -=, …).
24 BinaryOp BinaryOpNode Expression EMPLNode.h:471 a + b, a && b, a == b, …
25 UnaryOp UnaryOpNode Expression EMPLNode.h:482 !x, -x, ++x, typeof x, …
26 FunctionCall FunctionCallNode Expression EMPLNode.h:493 f(arg1, arg2, …).
27 MethodCall MethodCallNode Expression EMPLNode.h:352 obj.method(arg1, …).
28 MemberAccess MemberAccessNode Expression EMPLNode.h:363 obj.field.
29 IndexAccess IndexAccessNode Expression EMPLNode.h:373 arr[i], map[key].
30 Literal LiteralNode Literal EMPLNode.h:393 Numeric, string, boolean, or null literal.
31 Identifier IdentifierNode Literal EMPLNode.h:383 A bare name reference (no obj. prefix).
32 ArrayLiteral ArrayLiteralNode Literal EMPLNode.h:529 [1, 2, 3].
33 ObjectLiteral ObjectLiteralNode Literal EMPLNode.h:538 { a: 1, b: 2 }.
34 RecordLiteral RecordLiteralNode Literal EMPLNode.h:547 Struct-shaped literal (named keys, fixed shape).
35 TupleLiteral TupleLiteralNode Literal EMPLNode.h:556 [1, "x"] where types are heterogeneous and fixed.
36 Lambda LambdaNode Expression EMPLNode.h:565 Anonymous function ((x) => x, function(x){}, lambda x: x).
37 Await AwaitNode Expression EMPLNode.h:581 await expr.
38 Yield YieldNode Expression EMPLNode.h:590 yield expr / yield* expr.
39 Cast CastNode Expression EMPLNode.h:600 Explicit type cast.
40 Ternary TernaryNode Expression EMPLNode.h:610 cond ? a : b.
41 StandardLibraryCall StandardLibraryCallNode Literal EMPLNode.h:517 Language-neutral stdlib call (Console.WriteLine, Math.Sqrt, …).
42 PrintCall PrintCallNode Legacy EMPLNode.h:503 Deprecated; prefer StandardLibraryCall.
43 FuncDecl (alias) Legacy EMPLNode.h:181 Deprecated alias for FunctionDecl.
44 Unsupported UnsupportedNode Legacy EMPLNode.h:621 Frontend hit a construct it cannot represent.

Note. The enum class is non-sparse — EMPLValidator::validate walks the value space and reports unknown kinds as errors. The legacy entries (PrintCall, FuncDecl, Unsupported) are retained for source compatibility but new code MUST emit the canonical equivalents.

Family overview

Module / program (4 values)

These nodes form the top-level structure emitted by every frontend.

Kind Purpose
Program Top-level; only one ever exists per compilation. Holds ModuleEntry[] (id, ModuleNode, dependencies, isEntry). Exposes findModule(id) and topologicalOrder().
Module One compilation unit. Holds name, sourceLanguage, imports, declarations, statements.
Import Cross-module dependency. ImportBinding { importedName, localName, isDefault, isNamespace } plus module specifier.
Export Either named bindings, a default value, or a re-export.

Declarations (7 values)

All produce a top-level binding in the containing module's scope.

Kind Carrier
FunctionDecl FunctionDeclNode with body BlockNode and optional flags (isAsync, isGenerator, isConstructor, isStatic).
VariableDecl VariableDeclNode; carries name, pattern, type, initializer, plus isConst/isMutable.
DestructuringPattern DestructuringPatternNode; PatternKind::Object or PatternKind::Array.
ClassDecl ClassDeclNode; holds name, optional baseClass, constructor, methods[], members[], staticBlocks[], decorators[].
InterfaceDecl InterfaceDeclNode; name + extends + members.
EnumDecl EnumDeclNode; name + Member[] (each with name + optional initializer).
TypeAliasDecl TypeAliasDeclNode; name + free-form typeExpr text.

Blocks & control flow (11 values)

Kind Carrier
Block BlockNode { statements[] }.
If IfNode { condition, thenBranch, elseBranch }.
For ForNode { init, condition, increment, body } — any of init/cond/incr may be null.
ForEach ForEachNode { target, iterable, body }.
While WhileNode { condition, body }.
DoWhile DoWhileNode { body, condition } — body first.
Switch SwitchNode { expr, cases[], defaultCase }; each case has label, body, fallthrough.
TryCatch TryCatchNode { tryBlock, catchBlocks[], finallyBlock }.
Return / Throw / Break / Continue Single-purpose jumps.

Expressions (15 values)

Kind Carrier
Assignment AssignmentNode { target, value, op }.
BinaryOp BinaryOpNode { op, left, right }.
UnaryOp UnaryOpNode { op, operand, isPrefix }.
FunctionCall / MethodCall FunctionCallNode { name, args[] } / MethodCallNode { object, methodName, args[] }.
MemberAccess / IndexAccess Member / index read or write (target context determined by parent).
Cast CastNode { targetType, expression }.
Ternary TernaryNode { condition, whenTrue, whenFalse }.
Lambda LambdaNode { parameters, body, expressionBody, isAsync, capturesLexicalThis, isConstructible, hasOwnArguments }.
Await AwaitNode { expression }.
Yield YieldNode { expression, delegate }.

Literals (7 values)

Kind Carrier
Literal LiteralNode { value: variant<int64, double, string, bool, nullptr>, type }.
Identifier IdentifierNode { name }.
ArrayLiteral / ObjectLiteral / RecordLiteral / TupleLiteral List- or property-shaped literal nodes.
StandardLibraryCall StandardLibraryCallNode { moduleName, functionName, arguments[], argumentNames[] }.

Legacy (3 values)

PrintCall, FuncDecl, Unsupported — see comments at EMPLNode.h:66-69. New code MUST NOT emit them.

See also

  • index.md — IR overview and design philosophy
  • types.mdTypeKind enum and UIRType struct
  • nodes/ — per-node documentation

Numeric stability

The NodeKind enum is the IR's stable contract. Backends switch on the integer value of the kind to decide lowering. The numeric values are part of the IR ABI and MUST NOT be reordered or renumbered — only appended to. The current highest value is Unsupported at 44.

When reading EMPLNode.h (EMPLNode.h:19), the enum is laid out in source order. The integer values are:

Index range Values
0–3 Module / program family
4–10 Declaration family
11 Block
12–17, 22 Control-flow family (excluding jumps)
18–21 Jump family
23–41 Expression / literal family
42–44 Legacy family

Frontends that need to round-trip IR through a binary format should serialize the integer value directly (with appropriate endian conversion). Frontends that need to persist IR to JSON / text should use the symbolic name (e.g. "FunctionDecl") for forward compatibility.

Reserved values

There are no reserved NodeKind values. New kinds are simply appended. The validator emits a warning (not an error) for unknown kinds from older IRs, which lets new compilers load older IR files.

Convention summary

When adding a new node:

  1. Append the new enum value to EMPLNode.h (do not insert in the middle).
  2. Declare the new class deriving from EMPLNode with the correct EMPLNode(NodeKind::New) constructor call.
  3. Add an accept(EMPLVisitor& v) override { v.visit(*this); } method.
  4. Add a corresponding visit(NewNode& node) method to the EMPLVisitor base class. Existing visitors will fail to compile until they implement the new visit method — this is intentional, forcing all visitors to handle the new kind.
  5. Add a case to EMPLPrinter::printNode for human-readable dump.
  6. Add structural rules to EMPLValidator if the kind has invariants.
  7. Update this document.
  8. Update ../standard-library/index.md if the new kind affects stdlib emission.

See also (post-summary)