--- type: language-specification source-path: approved feature requests and expert design plan ingested-at: 2026-11-07 version: 0.1-draft --- # Nova128 Language Specification Status: approved version 1 baseline. Changes to visible syntax or semantics require a recorded decision. `Nova128` is the approved project name. ## 1. Design Goals Nova128 is a small, predictable, statically typed object-oriented language that can be compiled on a Commodore 128 with bounded memory. Version 1 prioritizes: - understandable generated 8502 code; - deterministic memory and execution costs; - useful encapsulation and interface polymorphism; - source that is comfortable to enter on a Commodore keyboard; - diagnostics that remain readable on a 40-column display; - a compiler architecture that fits within the C128's banked memory. Compactness is a product constraint. A feature is not part of version 1 unless this specification defines its syntax, semantics, diagnostics, and tests. ## 2. Source Profile ### 2.1 Physical format - Source files are PETSCII sequential files. - Logical lines end in carriage return (`$0D`). - The compiler accepts a missing final carriage return. - A source file may contain both PETSCII letter ranges `$41-$5A` and `$C1-$DA`; the lexer normalizes them for comparison. - The version 1 editor profile permits at most 80 bytes per logical line, 18,999 source bytes, and 599 logical lines. - A source base name should contain at most 12 characters so `.n8`, `.err`, and `.map` names fit the Commodore DOS filename limit. - One source file produces one program in version 1. ### 2.2 Lexical rules - Keywords and identifiers are case-insensitive. - Diagnostics and map files preserve the spelling of the first declaration. - Identifiers begin with a letter and continue with letters, digits, or `_`. - At most 16 identifier characters are significant. Longer identifiers are an error rather than being silently truncated. - Spaces and tabs separate tokens. - A newline terminates a statement or declaration. Version 1 does not continue expressions across lines and does not allow multiple statements on one line. - `//` begins a comment that continues through the end of the logical line. - Decimal numbers use digits. `$` introduces hexadecimal and `%` introduces binary. - String literals use double quotes. Version 1 escape sequences are `\"`, `\\`, `\n`, and `\r`. - Character literals use single quotes and contain exactly one PETSCII character or one supported escape: `\'`, `\\`, `\n`, or `\r`. - Reserved punctuation is `(`, `)`, `[`, `]`, `,`, `.`, `:`, `=`, `+`, `-`, `*`, `/`, `<`, `>`, `<>`, `<=`, `>=`, and the single quote that delimits character literals. (`<=` and `>=` were missing from this list although section 8 defines them; corrected in E02-S02.) - `\n` and `\r` both produce PETSCII `$0D`, the Commodore line end (D119, proposed). - Boolean operators are the words `and`, `or`, and `not`; `&&` and `||` are not aliases. ## 3. Types and Values ### 3.1 Primitive types | Type | Size | Values | |---|---:|---| | `boolean` | 1 byte | `false` (`0`) or `true` (`1`) | | `byte` | 1 byte | `0` through `255` | | `char` | 1 byte | One PETSCII byte | | `integer` | 2 bytes | Signed two's-complement `-32768` through `32767` | | `word` | 2 bytes | Unsigned `0` through `65535` | String literals are length-address pairs in generated read-only data. General mutable string variables are deferred. `print` accepts a string literal or a supported scalar value. ### 3.2 Reference types - Each class name defines a concrete object-reference type. - Each interface name defines an interface-reference type. - A concrete object reference is logically an address or `null`. - An interface reference identifies both an object and its implementation of a particular interface, or is `null`. - Reference assignment copies identity; it does not copy object fields. - Equality and inequality compare object identity. ### 3.3 Arrays - Version 1 supports fixed-size arrays with a compile-time positive length, written as `type[length]`. - Arrays may be fields or local variables. - Array indexing is zero-based. - Bounds checking is controlled by the compiler's debug/release option. - Arrays of interface values use the interface representation defined by the ABI; language code does not observe its byte layout. ### 3.4 Conversions - `byte` widens implicitly to `word` or `integer`. - A character literal has type `char`. `char` does not silently become text or a number in version 1. - `boolean` never converts to or from a numeric type implicitly. - `word` and `integer` do not convert implicitly in either direction. - A class reference converts implicitly to an interface reference only when the class implements that interface. - An interface reference never converts implicitly to a concrete class. - `null` is assignable to any class or interface reference. ## 4. Program Structure A file contains zero or more interface declarations, zero or more class declarations, and exactly one program declaration. Declarations may refer to types declared later in the file. Simplified grammar uses `{x}` for repetition and `[x]` for an optional part: ```text file = { interface-decl | class-decl }, program-decl, EOF ; interface-decl = "interface", identifier, EOL, { method-signature }, "end", "interface", EOL ; class-decl = "class", identifier, [ "implements", type-list ], EOL, { field-decl | method-decl }, "end", "class", EOL ; program-decl = "program", identifier, EOL, { variable-decl | statement }, "end", "program", EOL ; method-signature = "method", identifier, "(", [ parameter-list ], ")", [ "as", type ], EOL ; method-decl = [ access ], "method", identifier, "(", [ parameter-list ], ")", [ "as", type ], EOL, { variable-decl }, { statement }, "end", "method", EOL ; field-decl = [ access ], type, identifier, EOL ; variable-decl = "var", identifier, "as", type, EOL ; access = "public" | "private" ; type-list = identifier, { ",", identifier } ; ``` The complete parser may use equivalent productions, but accepted and rejected programs must follow the behavior in this specification. ## 5. Interfaces ### 5.1 Declaration An interface contains method signatures only: ```text interface Printable method print() end interface ``` Interfaces cannot declare fields, constructors, destructors, method bodies, or access modifiers. Interface inheritance and default methods are deferred. ### 5.2 Implementation A class implements one or more interfaces with a comma-separated list: ```text class Counter implements Printable, Resettable ``` For every interface method, the class must provide exactly one public method with the same case-insensitive name, parameter count, parameter types, and return type. Version 1 has no method overloading, so a class cannot declare two methods with the same name. If two implemented interfaces require the same signature, one class method may satisfy both. If they use the same name with incompatible signatures, the class declaration is an error. ### 5.3 Interface calls Calls made through a concrete class type are statically bound. Calls made through an interface type are dynamically dispatched to the object's class implementation. ```text var output as Printable output = new Counter(10) output.print() ``` Calling a method on a `null` interface reference is a runtime fault in debug mode. Release behavior must still terminate through the defined runtime-error path rather than jumping through address zero. ## 6. Classes and Objects ### 6.1 Fields and access - Fields default to `private`. - Methods default to `public`. - Private members are accessible only inside methods of their declaring class. - Version 1 has no class inheritance, protected access, static fields, or static methods. ### 6.2 Methods - An instance method has an implicit `self` reference. - Parameters are passed by value. Passing a reference copies the reference. - Local variables must be declared before executable statements in a method. - A non-returning method omits `as type`. - A returning method must return a compatible value on every reachable path. - Recursion is permitted by the language but bounded by the generated program's hardware and software stacks. ### 6.3 Construction and destruction - `init` is the optional constructor name. A class may declare at most one. - `destroy` is the optional destructor name. It takes no explicit parameters and returns no value. - `new Class(args)` allocates zero-initialized storage and then invokes `init`. - If allocation fails, the runtime follows the defined out-of-memory path and does not call `init`. - `delete expression` invokes `destroy` when present and then releases storage. - Deleting `null` is a no-op. - Deleting through an interface reference acts on the underlying object. - Using a reference after deletion is erroneous; version 1 does not attempt complete alias or lifetime proof. ## 7. Statements Version 1 statements are: ```text assignment target = expression call expression.method(arguments) if if expression statements [else statements] end if while while expression statements end while for for name = expression to expression [step expression] statements end for return return [expression] print print expression delete delete expression ``` Conditions must have type `boolean`; numeric values are not truth values. `and` and `or` short-circuit from left to right. A `for` loop evaluates its end and step expressions once before the first iteration. ## 8. Expressions From highest to lowest precedence: 1. Parentheses, member access, calls, and indexing 2. Unary `not` and unary `-` 3. `*` and `/` 4. `+` and `-` 5. `<`, `<=`, `>`, and `>=` 6. `=`, `<>` 7. `and` 8. `or` Arithmetic is performed in the operand type after permitted widening. Version 1 arithmetic overflow always wraps in the destination's two's-complement or unsigned representation. Debug and release builds have identical overflow semantics. Division by zero follows the runtime-error path. ## 9. Names and Scopes - Top-level interface and class names share one type namespace. - Field and method names occupy separate namespaces within a class. - Parameters and locals share a method-local namespace. - A local or parameter may shadow a field; `self.field` selects the field. - Duplicate declarations in the same namespace are errors. - Forward references to top-level types and methods are permitted because the compiler's declaration pass records signatures before bodies are compiled. ## 10. Built-in Operations Version 1 reserves these operations: - `print value` — output a string literal, `char`, `byte`, `word`, `integer`, or `boolean` through the runtime's KERNAL-compatible text layer. - `new Class(args)` — allocate and construct an object. - `delete reference` — destroy and release an object. Additional machine and KERNAL facilities will be introduced through explicit runtime APIs rather than unrestricted inline assembly in version 1. ## 11. Diagnostics Diagnostics have stable codes and source positions. The screen form must fit a 40-column display: ```text E014 TEST.N8 23 7 BYTE EXPECTED; WORD FOUND ``` The compiler writes the complete diagnostic list to `TEST.ERR`. It stops after 20 errors by default so a malformed file cannot exhaust compiler memory. Required diagnostic categories include: - invalid PETSCII or malformed token; - line, file, or identifier limit exceeded; - duplicate or unknown name; - syntax error with expected token; - type mismatch or illegal conversion; - access violation; - incomplete or conflicting interface implementation; - missing return; - resource limit, disk error, or output collision. ## 12. Complete Example ```text interface Printable method print() end interface interface Resettable method reset() end interface class Counter implements Printable, Resettable private word value public method init(start as word) self.value = start end method public method increment() self.value = self.value + 1 end method public method print() print self.value end method public method reset() self.value = 0 end method end class program Main var count as Counter var output as Printable count = new Counter(10) count.increment() output = count output.print() delete output end program ``` ## Open Questions - Which additional KERNAL-backed library operations belong in the first standard runtime after the compiler core is proven. - Which runtime-fault messages fit after the generated-program memory map is measured; stable fault codes and a map-resolvable address are required.