LPC Language Specification (LPCLS)

Adapted from the structure of the Java Language Specification (JLS) to describe the subset of LPC currently accepted and generated by the LPC2J toolchain.

1. Introduction and Scope

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.

2. Lexical Structure

2.1 Source text and preprocessing

Compilation starts with preprocessing. The preprocessor performs:

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】

2.2 Comments and whitespace

After preprocessing, the scanner also recognizes:

【F:src/io/github/protasm/lpc2j/scanner/Scanner.java†L118-L187】

2.3 Tokens and separators

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】

2.4 Identifiers and keywords

Identifiers begin with ASCII letters or _ and continue with letters, digits, or _. The following words are reserved as keywords or type names:

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】

2.5 Literals

3. Types, Values, and Variables

3.1 Primitive and object types

The type keywords map to JVM types as follows:

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】

3.2 Truthiness and boolean contexts

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】

3.3 Variables

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】

4. Program Structure

4.1 Translation units and inheritance

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】

4.2 Declarations

5. Statements

The statement forms currently accepted are:

Loops (for, while) are tokenized but not parsed yet. 【F:src/io/github/protasm/lpc2j/parser/Parser.java†L361-L370】

6. Expressions and Operators

6.1 Primary expressions

nil is reserved and not a usable primary. 【F:src/io/github/protasm/lpc2j/parser/parselet/PrefixLiteral.java†L18-L26】

6.2 Assignment and mutation

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】

6.3 Operators and precedence

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】

6.4 Calls and invocation

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】

6.5 Unary operators

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】

7. Standard and External Functions (Efuns)

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.

8. Runtime Behavior

8.1 Class generation

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】

8.2 Returns and fallthrough

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】

8.3 Truthiness and logical control flow

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】

9. Omitted JLS Sections

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.