Adapted from the structure of the Java Language Specification (JLS) to describe the subset of LPC currently accepted and generated by the LPC2J toolchain.
This specification documents the LPC surface language as implemented by LPC2J. It mirrors the major sections of the JLS where they have a clear LPC counterpart and omits areas (modules, threads, class loading, generics, annotations, etc.) that are not applicable to the current compiler.
LPC2J is an ahead-of-time compiler that turns one LPC source file into a single Java classfile. The specification focuses on the grammar and semantics that are enforced today; experimental or commented-out parser hooks are called out as “reserved” where appropriate.
Compilation starts with preprocessing. The preprocessor performs:
#include "path" / #include <path> with caller-provided search paths.#define / #undef macros.#if, #elif, #else, #endif, plus #ifdef / #ifndef.\\ + newline before tokenization.Preprocessing occurs before scanning; any attempt to include without a resolver is rejected. Undefined or unknown directives are skipped while preserving newlines so diagnostics stay aligned with the original source. 【F:src/io/github/protasm/lpc2j/preproc/Preprocessor.java†L32-L204】
After preprocessing, the scanner also recognizes:
// and ending at newline./* and */; nesting is rejected and unterminated blocks are errors.【F:src/io/github/protasm/lpc2j/scanner/Scanner.java†L118-L187】
Single-character separators are () { } [ ] , ;. Multi-character operators include ==, !=, &&, ||, +=, -=, *=, /=, --, ++, and ->. Angle-bracket comparators (<=, >=) are tokenized but not currently wired into expression precedence, so they are reserved for future support. 【F:src/io/github/protasm/lpc2j/scanner/Scanner.java†L40-L115】【F:src/io/github/protasm/lpc2j/parser/PrattParser.java†L36-L89】
Identifiers begin with ASCII letters or _ and continue with letters, digits, or _. The following words are reserved as keywords or type names:
if, else, for, while, return, inherit.true, false.nil (reserved; not yet parsed into an expression node).int, float, mapping, mixed, object, status, string, void.Keywords are not available for use as identifiers. 【F:src/io/github/protasm/lpc2j/scanner/Scanner.java†L45-L112】【F:src/io/github/protasm/lpc2j/token/TokenType.java†L8-L43】
- operator. 【F:src/io/github/protasm/lpc2j/scanner/Scanner.java†L189-L236】" delimiters; backslash escapes are copied verbatim by the scanner. 【F:src/io/github/protasm/lpc2j/scanner/Scanner.java†L238-L252】true and false. 【F:src/io/github/protasm/lpc2j/parser/parselet/PrefixLiteral.java†L10-L30】nil but not lowered to an AST; use explicit return null; for reference types instead. 【F:src/io/github/protasm/lpc2j/parser/parselet/PrefixLiteral.java†L18-L26】The type keywords map to JVM types as follows:
int → intstatus → booleanstring → java.lang.Stringobject → java.lang.Objectmixed → java.lang.Object with boxing/unboxing of primitives where neededvoid → method return onlyfloat → tokenized but not compiled in expressions (reserved)mapping → tokenized only (no code generation yet)Type inference propagates declared types across assignments, returns, and parameter use to validate expressions and keep symbol metadata consistent. 【F:src/io/github/protasm/lpc2j/parser/type/LPCType.java†L8-L34】【F:src/io/github/protasm/lpc2j/parser/ast/visitor/TypeInferenceVisitor.java†L17-L118】
Logical operators and conditionals treat values as truthy when non-null and, for numbers, non-zero. This is enforced at runtime by Truth.isTruthy(Object). 【F:src/io/github/protasm/lpc2j/runtime/Truth.java†L9-L41】
Variables can be object fields, method parameters, or locals. Locals are block-scoped and must be declared with an explicit type keyword when using the current parser defaults; redeclaration in the same scope is rejected. Assignments to object or string targets treat literal zero as null to preserve traditional LPC semantics. 【F:src/io/github/protasm/lpc2j/parser/Parser.java†L164-L243】【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L248-L314】【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L380-L430】
Each source file defines exactly one LPC object. An optional leading inherit "path"; sets the superclass name used during bytecode emission; if absent, the generated class extends java/lang/Object. Only a single parent may be named. 【F:src/io/github/protasm/lpc2j/parser/Parser.java†L71-L134】【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L151-L207】
int a = 1, b;). Initializers are evaluated in the constructor. 【F:src/io/github/protasm/lpc2j/parser/Parser.java†L141-L215】【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L482-L519】void methods must end with a correctly typed return statement; void methods may fall through with an implicit return;. Methods are compiled as public instance methods. 【F:src/io/github/protasm/lpc2j/parser/Parser.java†L141-L239】【F:src/io/github/protasm/lpc2j/parser/Parser.java†L309-L471】【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L402-L470】The statement forms currently accepted are:
{ ... } with optional trailing implicit return; insertion for void methods only (see §8.2).if (expr) stmt [else stmt].return; or return expr;.;.Loops (for, while) are tokenized but not parsed yet. 【F:src/io/github/protasm/lpc2j/parser/Parser.java†L361-L370】
nil is reserved and not a usable primary. 【F:src/io/github/protasm/lpc2j/parser/parselet/PrefixLiteral.java†L18-L26】
Locals and fields support =, +=, and -=. Assignment returns the stored value, allowing it in expression statements. Field invocation via -> is reserved; only locals containing objects can be invoked with -> (see §6.4). 【F:src/io/github/protasm/lpc2j/parser/parselet/PrefixIdentifier.java†L14-L81】
Expression parsing uses Pratt precedence with these levels (low to high): assignment (restricted to valid targets), ||, &&, equality (==, !=), relational (<, >; <=/>= reserved), additive (+, -), multiplicative (*, /), unary (!, unary -), call/primary. Logical operators short-circuit; relational and equality operators return status (int-compatible) values. String concatenation uses + when either operand or context is string. 【F:src/io/github/protasm/lpc2j/parser/PrattParser.java†L12-L109】【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L316-L380】
foo(a, b) resolves to a method defined on the current object by name.efun_name(args) resolve via the efun registry; unknown efuns raise an error at runtime after a null-check.expr->method(args) is supported when expr is a local variable of object type; it uses reflection to find and invoke the method, applying primitive boxing/unboxing to match LPC types.Field arrow invocation is reserved. 【F:src/io/github/protasm/lpc2j/parser/parselet/PrefixIdentifier.java†L14-L69】【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L35-L165】【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L166-L247】
Unary minus negates numeric values (ints and boxed numbers); logical not coerces the operand to boolean using LPC truthiness rules. 【F:src/io/github/protasm/lpc2j/parser/parselet/PrefixUnaryOp.java†L10-L33】【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L316-L379】
The console preloads several efuns; they are callable from any LPC object when registered in EfunRegistry: add_action, add_verb, call_other, destruct, foo, environment, this_player, this_object, set_heart_beat, set_light, say, and write. Implementations are Java classes under io.github.protasm.lpc2j.console.efuns. 【F:src/io/github/protasm/lpc2j/console/LPCConsole.java†L33-L90】
Additional efuns can be added at runtime by registering them before parsing or compiling; efun symbols carry name, arity, and Java implementation.
Each LPC object becomes a public Java class with private fields and public methods. The constructor calls the parent constructor and evaluates field initializers. Missing parent names default to java/lang/Object. 【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L151-L247】【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L482-L519】
void methods may fall off the end of their bodies; the parser inserts an implicit return; and the compiler emits a RETURN opcode. Methods with any other declared return type must end with an explicit, correctly typed return statement. 【F:src/io/github/protasm/lpc2j/parser/Parser.java†L309-L471】【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L402-L470】
if, logical &&, ||, and unary ! all coerce operands using the truthiness rules in §3.2. Short-circuiting is implemented in the emitted bytecode for && and ||. 【F:src/io/github/protasm/lpc2j/compiler/Compiler.java†L334-L379】【F:src/io/github/protasm/lpc2j/runtime/Truth.java†L9-L41】
The LPCLS omits JLS chapters that do not yet map to LPC2J features, including packages, imports, nested types, generics, exceptions, threads, modules, records, sealed types, annotations, and serialization. Loop constructs, floating-point expressions, mappings, and the nil literal are reserved for future expansion as the compiler grows.