- Add `WrenErrorFn` typedef in wren.h accepting module, line, and message parameters
- Include `errorFn` slot in `WrenConfiguration` struct with documentation comment
- Initialize `errorFn` to NULL in `wrenInitConfiguration()` in wren_vm.c
- Replace direct `fprintf(stderr, ...)` in wren_compiler.c with call to `parser->vm->config.errorFn()`
- Add guard in wren_compiler.c to skip error reporting when `errorFn` is NULL
- Implement `reportError()` in cli/vm.c that writes formatted errors to stderr
- Wire `reportError` into config during `initVM()` in cli/vm.c
Store the line number of first use in implicitly declared variables as a numeric value, then synthesize a token at that line when reporting the error, ensuring bounded error message length even for pathologically long variable names.
Add tracking of static and instance method names during class compilation to detect and report duplicate method definitions. Introduce `hasMethod` helper function and `staticMethods`/`instanceMethods` buffers in the `ClassCompiler` struct, along with a `name` field for identification. Include a new test case `duplicate_methods.wren` verifying error generation for duplicate instance and static methods, while allowing same-named methods across static/instance boundaries.
Replace the generic `wrenIntBufferFind` function with a specialized `hasMethod` helper that performs exact method symbol matching in the compiler. This change removes the generic `BufferFind` macro from the buffer utility system and introduces a focused implementation in `wren_compiler.c` that directly compares method symbols using `memcmp`. The error message for duplicate methods is also simplified to exclude the module name. Additionally, a new test file `duplicate_methods.wren` is added to verify error handling for duplicate instance and static method definitions.
Replace multiple fprintf calls in lexError and compileError with a unified printError function that formats the entire error message into a fixed-size buffer before outputting it as a single fprintf call. This centralizes error formatting logic and prepares for future integration of a host-provided error callback by eliminating scattered fprintf invocations.
Remove CODE_LOAD_MODULE and CODE_IMPORT_VARIABLE opcodes, replacing them with calls to System.importModule(_) and System.getModuleVariable(_,_) primitives. This simplifies the interpreter loop, improves performance by reducing bytecode dispatch, and prepares for moving module loading logic into Wren source code for embedder flexibility.
The block() function duplicated the curly-block parsing logic that statement() already handled for single-expression bodies. By removing block() and replacing its single call site in loopBody() with statement(), the compiler eliminates unnecessary indirection and simplifies the control flow parsing. The statement() comment is updated to clarify that it excludes variable binding statements like "var" and "class" which are not allowed in if-branch positions.
- Add missing `:::wren` code block marker before the `removeAt` example in the list documentation to ensure proper syntax highlighting.
- Simplify the `parens` array increment in `readString` by combining the assignment and post-increment into a single expression.
Introduce a `methods` symbol table and `name` field to `ClassCompiler` struct to track method signatures during compilation. When a method is defined, check for existing entries in the table and emit a compile error if a duplicate is found, preventing silent method overrides. Initialize the class name string before class definition processing to support error messages referencing the class name.
Add a MAX_JUMP constant set to 2^16 and validate jump distance in patchJump to prevent silent overflow when bytecode exceeds the maximum representable offset. Also remove the TODO comment in endLoop since the forward jump error will be caught by patchJump. Include test cases jump_too_far.wren and loop_too_far.wren to verify the compiler correctly reports "Too much code to jump over." for both conditional and loop constructs.
Replace the integer `classSlot` parameter with a `Variable` struct in both `defineMethod()` and `method()` functions, removing duplicate code that handled top-level vs local class loading by delegating to the new `loadVariable()` helper.
In both declareVariable and resolveLocal functions within wren_compiler.c,
the string comparison using strncmp is replaced with memcmp since the
length parameter is already known and null-termination checking is
unnecessary for these fixed-length identifier comparisons. This avoids
the overhead of scanning for null terminators when comparing local
variable names against token or parameter strings.
Replace the ad-hoc output parameter pattern in resolveNonmodule with a proper Variable struct that bundles the variable's index and scope (local, upvalue, or module). This makes variable references a first-class concept in the compiler, eliminating the need to pass around a separate Code pointer for load instructions and improving code clarity throughout the compilation pipeline.
Change the map key parsing precedence from PREC_PRIMARY to PREC_UNARY in
wren_compiler.c, enabling method calls (e.g., name.count), subscript
access (name[0]), and unary operators (-1, ~3) as valid map keys.
Add two test files: precedence.wren verifying the new key types work
correctly, and bad_key_precedence.wren confirming that binary operators
like addition still produce a parse error.
- Fix debug dump to print human-readable instruction names instead of internal enum names (e.g., "RETURN" instead of "CODE_RETURN")
- Remove `ClassCompiler*` parameter from `method()` function, accessing class state through `compiler->enclosingClass` instead
- Change `ClassCompiler.fields` from pointer to embedded struct, updating field lookup to use address-of operator
Add `numSlots` and `maxSlots` fields to `Compiler` struct to track current and maximum stack usage during compilation. Introduce `stackEffects` array mapping each opcode to its net stack delta, defined via updated `OPCODE` macro in `wren_opcodes.h` now taking a second argument. Modify `initCompiler` to initialize slot tracking from `numLocals`, update `emitByte` and related emit functions to adjust `numSlots` and update `maxSlots` when a new peak is reached. Extend `ObjFn` with `maxSlots` field, pass it through `wrenNewFunction` calls (including `makeCallStub`), and fix a null-check in `wrenDumpCode` for core module name.
Add calls to ignoreNewlines in the import compiler function to allow
newlines between the import keyword and the module string, and between
the module string and the for clause. Create a new test case
test/language/module/newlines/ that verifies multiline import syntax
works correctly with both bare imports and imports with variable
bindings.
The error message for a missing '(' after '%' in string interpolation
contained an unescaped '%' character, which could be misinterpreted
by format string functions. Changed the literal '%' to '%%' to ensure
proper display of the percent sign in the error output.
Remove the deferred function compilation for interpolation parts and instead evaluate them directly during string parsing. This simplifies the compiler by eliminating the need for String.interpolate_() and the associated closure creation, replacing it with a direct list construction and join() call.
Add lexer support for `TOKEN_INTERPOLATION` to split string literals at interpolation points, introduce `MAX_INTERPOLATION_NESTING` limit of 8, and compile interpolated strings by emitting calls to `String.interpolate_()`. Optimize list and map literal construction with new `addCore_` primitives to reduce stack churn. Update `String`, `List`, and `Map` `toString` methods in core library to use interpolation syntax, and migrate all benchmark and test files from explicit concatenation to interpolated strings.
Rename `wrenMarkValue` to `wrenGrayValue`, `wrenMarkBuffer` to `wrenGrayBuffer`, and `wrenDarkenObjs` to `wrenBlackenObjects` to make the tri-color garbage collection abstraction explicit. Introduce `wrenGrayObj` as a new helper that handles adding objects to the gray stack with cycle detection via the `isDark` flag. Update all call sites in the compiler, value, and VM modules to use the new names. Fix a typo in the VM header comment ("garbace" → "garbage").
Extend the compiler's string parser to handle `\U` escapes with 8 hex digits
for codepoints above U+FFFF, replacing the previous TODO comment. The
`readUnicodeEscape` helper now accepts a variable digit count (4 or 8) and
the `readString` function dispatches `\u` with length 4 and `\U` with length 8.
New test cases verify correct encoding of emoji and ancient scripts, plus
error handling for incomplete long escapes and values that fit in 4 digits.
Add fallback empty UNREACHABLE() definition for GCC < 4.5 and non-MSVC compilers, and insert return/break statements after all UNREACHABLE() calls to prevent -Wreturn-type warnings when the macro expands to nothing. Also cast malloc result to char* in io.c to suppress C++ compilation warning.
The sourcePath field on ObjModule was redundant since it always matched the module name except for the main script. This change removes the parameter from wrenInterpret, wrenInterpretInModule, and wrenNewModule, and replaces all references to module->sourcePath with module->name in error reporting and debug output.
Remove the `sourcePath` field from `FnDebug` struct and the `Parser` struct, and instead store it directly in `ObjModule`. This eliminates duplication of the debug path across all functions in a module and simplifies the compiler interface by removing the `sourcePath` parameter from `wrenCompile()` and `wrenNewFunction()`. Update all call sites to reference `module->sourcePath` instead, including stack trace printing, code dumping, and the `meta_eval` primitive.
When a method references a name that exists as a local variable outside the method boundary, the compiler now correctly treats it as a self send rather than closing over the outer local. This fixes the closure resolution logic in `findUpvalue` to stop searching for upvalues when hitting a method boundary, unless the name starts with '_' indicating a static field access. The change removes two incorrect test cases that expected outer local closure behavior and adds a new test verifying that both instance and static methods resolve local names to their own methods.
Remove the compiler check that prevented 'super' from being used inside static methods, and fix the runtime binding logic in bindMethod to correctly resolve the superclass for static method dispatch. The key change moves the metaclass lookup (classObj->obj.classObj) to occur before wrenBindMethodCode so that bytecode patching uses the actual class rather than the metaclass, while preserving the original class name for foreign method resolution.
Introduce a Keyword struct and static keywords array mapping reserved word strings to their TokenType, replacing the previous isKeyword() function that performed repeated strncmp comparisons. The new table-driven approach simplifies lexing by enabling direct binary search or linear scan over known identifiers, reducing per-token branching and making the set of keywords explicit and easier to maintain.
Previously, inheriting from null would silently fall back to Object superclass. Now validates superclass and raises runtime error if null is provided. Refactored defineClass to remove implicit null-to-Object fallback, added loadCoreVariable helper for module-level core imports, and updated CLASS/FOREIGN_CLASS opcode comments to reflect that null is no longer a valid superclass. Added test case for inherit_from_null.
Refactor emitConstant to accept a Value parameter, removing the implicit dependency on compiler->parser->value. Update all call sites to pass the value explicitly, improving encapsulation and making the function's data flow clearer.
Add wrenNewStringFromRange function to wren_value.c that decodes UTF-8 code points from source string and re-encodes them into result, enabling correct range subscripts on multi-byte characters. Rename wrenUtf8NumBytes to wrenUtf8EncodeNumBytes and make wrenUtf8Encode return written byte count for use in range construction. Add wrenUtf8DecodeNumBytes utility to skip continuation bytes when iterating by byte index. Remove all skip directives from test files and add UTF-8 specific test cases verifying correct slicing across code point boundaries.
The compiler no longer auto-generates a default `new()` constructor for classes. All classes must now explicitly define `construct new() {}` to be instantiable. This change removes the `createDefaultConstructor` function from the compiler, eliminates the `return_this` primitive used by Object's default initializer, and adds explicit constructors to all test and example classes. Core metaclasses (Bool, Class, Null, Num, Object, Range, Sequence) now correctly reject `new()` calls with runtime errors.
The ignoreNewlines() call following list instantiation via callMethod(compiler, 0, "new()", 5) was redundant because the subsequent do-while loop for compiling list elements already handles newline skipping internally. Removing this duplicate call eliminates unnecessary parser state manipulation without affecting list compilation behavior.
- Convert list() and map() compiler functions from if/do-while to do-while loops
- Remove redundant empty literal handling by breaking on right bracket/brace
- Add new test files for duplicate comma, duplicate trailing comma, and empty list/map with comma
- Expand newlines test to cover trailing comma and empty list/map cases
- Simplify trailing_comma tests by removing inline syntax error checks
- Update Test.fail() to handle variable arguments without kwargs
Add support for trailing commas in list literals by checking for TOKEN_RIGHT_BRACKET
after parsing the last element in the list() function. When a trailing comma is
followed by the closing bracket, the loop breaks immediately instead of attempting
to parse another element.
Include a test case verifying that lists with trailing commas parse correctly and
elements are accessible by index.
Add a check in the map parsing function to break when encountering a closing brace after a comma, enabling trailing commas in map literals. Include a test case verifying that maps with trailing commas parse and execute correctly.
Replace the separate `string` ByteBuffer and `number` double fields in the Parser struct with a single `value` field of type Value. This unifies literal storage, simplifies the parser's state, and eliminates the need for separate buffers and conversion logic for different literal types.
The change modifies `makeNumber` to store parsed numbers as `NUM_VAL` directly into `parser->value`, and adds a guard in `addConstant` to return -1 early if the parser has encountered an error.