Enable the -Wextra compiler flag in the wren.mk build configuration, which was previously commented out with a TODO. To handle the new unused parameter warnings triggered by -Wextra (particularly for WrenVM* vm parameters in several functions), add -Wno-unused-parameter to suppress them. Also fix a declaration ordering issue in wren_vm.c where the 'static' keyword was placed after the return type in loadIntoCore, moving it to the correct position before the type specifier.
The guard checks if the VM pointer is NULL or if its methodNames count is zero,
which serves as a heuristic to detect an already-freed VM instance. This prevents
a segmentation fault or memory corruption when wrenFreeVM is called multiple times
on the same VM object.
Add a check in the interpreter's class creation path that rejects attempts to subclass any of the seven core built-in types (Class, Fiber, Fn, List, Map, Range, String). When such a subclass is declared, a runtime error is raised with a message like "Class 'SubName' may not subclass a built-in". Also add corresponding test files for each forbidden subclass scenario.
The `signatureSymbol` function previously built a method name string as a side effect before hashing it. This change separates the stringification logic into a new `signatureToString` function that writes the full signature name into a provided buffer and updates its length. All callers now use the new helper, making the code clearer and allowing the stringified signature to be reused independently of symbol lookup.
The finishParameterList function no longer needs to consume the closing delimiter
or produce error messages, as that responsibility has been moved to the callers.
The parameterList wrapper function has been inlined into its single call site,
simplifying the parameter parsing flow.
- Compile empty `()` as a valid method signature distinct from no parentheses
- Update `call()`, `clear()`, `run()`, `try()`, and `yield()` to use empty argument lists
- Revise documentation across multiple files to reflect new signature semantics
The method signature format has been changed from space-counted arity (e.g., "== ") to explicit parenthesized parameter lists (e.g., "==(_)"). This required updating the MAX_METHOD_SIGNATURE calculation to account for parentheses and parameter separators, replacing the old copyName helper with a new Signature struct and SignatureType enum, and rewriting the methodNotFound error message to display the full signature string instead of reconstructing arity from trailing spaces. All core method registrations in wren_core.c, wren_io.c, and corresponding test expectations have been updated to use the new signature format.
The wrenDefineMethod and wrenDefineStaticMethod functions now accept a single `signature` string parameter instead of separate `methodName` and `arity` arguments. This simplifies the API by letting callers specify the full method signature (including arity spaces) directly, removing the internal arity-to-spaces conversion logic in defineMethod. The change updates the header declaration, the internal defineMethod helper, and all call sites in wren_io.c to pass pre-formatted signatures.
Add a check in the interpreter's class creation path that detects when a user-defined class attempts to inherit from any of the seven built-in types (Class, Fiber, Fn, List, Map, Range, String). When such an inheritance is detected, a runtime error is raised with a descriptive message indicating the class name and that it may not subclass a built-in. This implements the restriction discussed in issue #70.
Also adds test files for each built-in type to verify the runtime error is correctly triggered, and includes a commented-out test for closure subclassing that currently causes a segfault.
Add new Precedence enum values (PREC_BITWISE_OR, PREC_BITWISE_XOR, PREC_BITWISE_AND, PREC_BITWISE_SHIFT) and a PREC_TERNARY level between assignment and logical operators. Update and_(), or_(), and conditional() to use the refined precedence constants. Include a test file verifying correct associativity and precedence ordering among bitwise operators (|, &, ^, <<) in expressions.
Previously, `Num.fromString` accepted strings like `"1.2prefix"` and returned the
leading numeric portion (`1.2`). This change makes parsing stricter by requiring
that the entire string be consumed after `strtod`, skipping only trailing
whitespace. If any non-whitespace characters remain after the number, the
function now returns `null` instead of a partial result.
The fix also adds an explicit empty-string check and updates the test suite to
reflect the new behavior, replacing the old `"1.2prefix"` success test with a
`"1.2suffix"` null-return test.
Split the former PREC_LOGIC into PREC_LOGICAL_OR and PREC_LOGICAL_AND, inserting PREC_TERNARY between them, and updated all parsePrecedence calls and grammar rule entries to use the new precedence levels.
Implement a new `Num.fromString(value)` method that attempts to parse a string as a decimal literal and returns the corresponding `Num` instance, or `null` if parsing fails. The implementation uses `strtod` for conversion, validates the argument is a string at runtime, and includes tests for valid numbers, edge cases like leading/trailing whitespace and partial suffix parsing, non-number inputs returning null, and a runtime error when a non-string argument is passed. Also fixes a missing semicolon in the `toString` native registration and removes the hex literal TODO from the number literals test.
- Change `int` to `uint32_t` for iterator indices in `map_iterate` and `string_iterate` to match capacity/length types
- Update `String.length` field type from `int` to `uint32_t` in `wren_utils.h`
- Modify `wrenStringCodePointAt` parameter type from `int` to `uint32_t` and remove redundant negative index assertion
- Convert loop variable in `markMap` from `int` to `uint32_t` to match `map->capacity` type
- Add `-Wsign-compare`, `-Wtype-limits`, and `-Wuninitialized` compiler flags to catch future mismatches
The UINT32_MAX macro is defined in <stdint.h> but was not included, causing
compilation errors on platforms where it is not implicitly pulled in by other
headers. This commit adds the missing include to ensure the constant is
available for use in the source file.
Remove conditional getcwd logic for MSVC and POSIX, replacing it with a simple
empty root directory string. This simplifies the import path resolution by
always using relative paths from the current working directory instead of
attempting to resolve an absolute root path at startup.
- Add platform-specific getcwd header includes for MSVC and POSIX
- Replace malloc-based rootDirectory assignment with a static 2048-byte buffer
- Initialize rootDirectory to point to the static buffer instead of an empty string
- Update runFile to use const char* for lastSlash and copy path directly into buffer
- Implement TODO in runRepl by calling getcwd to set rootDirectory to current working directory
Initialize vm->modules to NULL in wrenNewVM and create a module registry map to track loaded modules. In loadModule, check if a module has already been loaded via wrenMapFind; if found, reuse the existing module object instead of creating a new one, enabling successive wrenInterpret calls to execute in the same module context and preserve variable bindings across REPL invocations.
Replace the TODO placeholder for rootDirectory initialization in the REPL
with a call to getcwd (with _getcwd fallback for MSVC), enabling module
imports to resolve relative to the user's working directory at startup.
Implements a new `fromString` static method on the `Num` class that attempts to parse a string as a decimal number literal using `strtod`. Returns the parsed `Num` value on success, or `null` if the string does not represent a valid number. Includes runtime validation that the argument is a string, and adds test coverage for positive, negative, zero, and decimal inputs as well as non-numeric strings.
Extract `callMethodInstruction` and `callMethod` helper functions from the
existing `methodCall` function to reduce code duplication and improve
readability. Also fix comment formatting for argument arity overloading
and block argument inclusion.
Extract the duplicated argument list parsing logic from methodCall and
subscript into a shared finishArgumentList function. This unifies the
comma-separated expression parsing, arity tracking, and newline handling
into a single location. Also add a new test case verifying that newlines
after commas and before closing delimiters are correctly handled in both
method calls and subscript expressions.
When a script path contains no '/' character, strrchr returns NULL, causing a crash in the subsequent malloc and memcpy calls. This change adds a NULL check before attempting to extract the root directory, leaving rootDirectory as an empty string when no separator is present.
The monolithic Makefile is replaced with a thin wrapper that invokes
script/wren.mk for each configuration. Build artifacts are now placed
in bin/ (executables) and lib/ (static/shared libraries) instead of
the project root. The .gitignore is updated accordingly, and the
getting-started documentation reflects the new output layout.
Add three new bitwise operators (^, <<, >>) to the Wren compiler and runtime,
including tokenization, parsing with correct precedence, and native method
implementations operating on 32-bit unsigned integers. New test files verify
correct behavior for edge cases including max u32 values and operand type errors.
Add token definitions for `<<`, `>>`, and `^` in the lexer with proper two-character parsing for shift operators. Register new infix operators at the BITWISE precedence level in the parser grammar. Implement native C functions for bitwise XOR, left shift, and right shift on 32-bit unsigned integers in the core runtime, and bind them as methods on the Num class. Include test files covering edge cases like max u32, overflow, and non-numeric operand errors.
Make the `for` clause in import statements optional, and support comma-separated lists of imported names when present. This removes the previous restriction of exactly one imported variable per import statement.
- Refactor `import()` in compiler to handle optional `for` clause with loop over comma-separated names
- Update existing tests to use comma-separated imports and omit `for` clause where appropriate
- Add new tests: `inside_block`, `name_collision`, `no_variable`
Add map_numeric and map_string benchmark scripts in Lua, Python, Ruby, and Wren to compare hash map insertion/lookup/deletion performance across languages. Register both new benchmarks in the benchmark runner script and fix its language executable paths to use WREN_DIR. Expose a new `IO.time` static method in the C IO module returning `time(NULL)` as a double. In the Makefile, split the monolithic `clean` target into separate removals for build artifacts, wren executables, and library variants to avoid accidental deletion of unrelated files. Remove hardcoded hash singleton constants from wren_value.c as they are no longer used.
Replace the runtime string-based `import_` method call with a new `CODE_IMPORT_VARIABLE` bytecode instruction that directly loads variables from other modules. This eliminates the overhead of calling a native method and string-based module lookup during import, moving the logic into the VM interpreter loop with a dedicated `importVariable` helper function. The change includes adding the opcode to the compiler's `import` function, implementing the runtime lookup in `wren_vm.c`, updating the debug disassembler, and removing the now-unnecessary `string_import` native from `wren_core.c`.
Replace the previous approach of calling a native `loadModule_` method on string objects with a new `CODE_LOAD_MODULE` bytecode instruction. This eliminates the need for the `string_loadModule` native function and the indirect fiber invocation through `PRIM_RUN_FIBER`. The new implementation directly handles module loading in the VM by compiling the module source, creating a fiber, and switching execution contexts. The compiler now emits `CODE_LOAD_MODULE` followed by a `CODE_POP` to discard the unused fiber result, instead of the previous `CODE_CONSTANT` + `CODE_CALL_0` sequence.
Renamed the native function identifiers for left and right bitwise shift operations
in wren_core.c to use the more explicit names num_bitwiseLeftShift and
num_bitwiseRightShift, replacing the abbreviated Lsh and Rsh suffixes. Updated
both the DEF_NATIVE macro definitions and the corresponding NATIVE registrations
in wrenInitializeCore to use the new names.
Rename the token enum values and all references in the compiler to use the more concise
TOKEN_LTLT and TOKEN_GTGT names for the '<<' and '>>' operators, updating both the
token type definitions and the grammar rule table entries.
The import_ method on String was a builtin that combined module loading and variable lookup. This change removes that method and instead emits separate bytecode instructions in the compiler: first calling loadModule_ on the module, then calling import_ on the module with the variable name. The native function string_lookUpVariable is renamed to string_import to match the new import_ method name on String's metaclass.
The `loadModule_` primitive now returns a fiber when a module is loaded, and the C runtime handles calling it via `PRIM_RUN_FIBER` instead of the Wren code manually invoking the result. This removes the conditional `if (result != null) result.call` from the `import_` method in both the builtin source and the embedded libSource string, and adds `IS_FIBER` macro to `wren_value.h` for type checking in the native implementation.
The loadModule_ primitive now returns NULL for already-loaded modules and an error string for missing modules, shifting the error handling from Wren's Fiber.abort to C's native string return. The Wren import_ method simplifies to only call the result when non-null, removing the false/true branching logic.
Implement a new `IO.time` static method in the Wren IO module that returns the current Unix timestamp as a double by calling `time(NULL)`. This provides a simple way for Wren scripts to obtain wall-clock time, complementing the existing `IO.clock` method which measures CPU time. The method is registered in `wrenLoadIOLibrary` alongside the other IO primitives.
Replace the named constants HASH_FALSE, HASH_NAN, HASH_NULL, and HASH_TRUE
with inline integer literals in the hashValue function, eliminating the
unused HASH_NAN constant and the associated TODO comment about tuning.
Implement three new native methods on the Num class: `^` (bitwise XOR), `<<` (left shift), and `>>` (right shift). Each operates on 32-bit unsigned integers, matching the existing bitwise AND/OR convention. The new primitives are registered in `wrenInitializeCore` alongside the existing bitwise operators.
Add TOKEN_LEFT_SHIFT, TOKEN_RIGHT_SHIFT, and TOKEN_CARET to the token enum, implement lexing for '^' as single-char token and '<<'/'>>' as two-char tokens in nextToken(), and register them as infix operators at PREC_BITWISE precedence in the grammar rules table.
Add TOKEN_IMPORT to the token enum and wire it into the keyword parser in readName(). Refactor string literal parsing by extracting stringConstant() helper that returns the constant index, allowing the new import parser to reuse it. Update all module test files to replace the old .import_() method calls with the new import "module" for Var syntax, and add new error test cases for missing string and missing for clause after import.
Replace the previous deletion strategy that required rehashing all entries after removal with a tombstone approach. When an entry is removed, its slot is marked with UNDEFINED_VAL key and TRUE_VAL value, allowing linear probing to continue past deleted slots during lookups. This prevents deletion from degrading to near-linear time in the presence of large clusters, matching the approach used by Python and Lua. Also refactor hashValue to hash raw bits of unboxed values under WREN_NAN_TAGGING, and update MapEntry documentation to clarify tombstone semantics.
Extract core module loading into separate `loadIntoCore` function and `getCoreModule` helper, replacing the old `getMainModule`. Implement `loadModule` that automatically imports all core module variables into every newly loaded module, enabling implicit visibility of core types (Bool, Class, Fiber, etc.) across all user modules. Add `wrenImportModule` public API for module loading with circular import detection. Increase `WREN_MAX_TEMP_ROOTS` from 4 to 5 to accommodate additional GC roots during module loading. Add test cases for cyclic imports (a.wren/b.wren) and implicit core imports (implicitly_imports_core.wren/module.wren).
The readModule function now concatenates ".wren" to the module path when constructing the full file path, increasing the path buffer by 5 bytes. All test files have been updated to use bare module names (without ".wren") in import_ calls, and the expected error messages in unknown_module and unknown_variable tests are adjusted accordingly. A trailing blank line is also removed from wren_vm.c.
Implement initial module system allowing Wren code to import other modules via a temporary `String.import_` method. The embedder must supply a `WrenLoadModuleFn` callback that returns source code for a given module name; the VM caches loaded modules and returns a fiber to execute the module body. Add `wrenImportModule` to the VM API, wire `loadModuleFn` into `WrenConfiguration`, and update the CLI interpreter to resolve imports relative to the entry script's directory. Include test cases for importing multiple variables, shared imports across modules, and verifying that variable bindings are independent across import sites.
Add Makefile targets for building shared libraries (.so/.dylib) alongside existing static libraries, with platform-specific linker flags for macOS and Linux. Refactor the internal string representation from raw `char*` to a `String` struct that tracks both buffer and length, updating all symbol table operations, debug printing, and string concatenation calls to use the new type. Fix a null-termination bug in `copyName` and add support for the `\0` escape sequence in string literals.
Instead of storing the main module directly in a dedicated field on WrenVM,
store it as a null-keyed entry in the new modules map. This eliminates the
redundant `vm->main` pointer and paves the way for supporting multiple named
modules. The change also adds `wrenFindVariable` as a public helper to look up
top-level variables in the main module, and updates `wrenNewFunction` to accept
an explicit module parameter instead of always using `vm->main`.
The wrenStringFind function previously returned the haystack length to indicate
a missing needle, which was ambiguous when the needle could legitimately appear
at the end of the string. This commit changes the sentinel value to UINT32_MAX,
updates all callers (string_contains, string_indexOf) to check against the new
sentinel, and adds explicit handling for empty needle and empty haystack edge
cases. The Boyer-Moore-Horspool implementation is also cleaned up with clearer
variable names and comments.
Implement wrenStringFind function using Boyer-Moore-Horspool algorithm with 256-entry shift table for efficient substring matching. Update string_contains, string_indexOf, and string_count native methods to use the new implementation, fixing the empty string corner case to always return true when search string is empty.
Break long wrenStringConcat calls across multiple lines in validateFn,
validateNum, validateIntValue, and validateString functions in
src/wren_core.c to stay within the 80-column limit. Also fix a comment
typo in src/wren_value.h: change "of" to "with" in the wrenStringConcat
documentation.
Add handling for the null character escape sequence '\0' in the readString function within wren_compiler.c. This extends the existing escape sequence handling to include the null character, allowing strings to contain embedded null bytes when explicitly escaped.
Replace all uses of strcpy() and strncpy() with memcpy() followed by explicit null-termination to avoid potential buffer over-reads and undefined behavior from non-null-terminated source strings. This change affects copyName() in wren_compiler.c, wrenSymbolTableAdd() in wren_utils.c, wrenNewFunction() and wrenNewString() in wren_value.c, and defineMethod() in wren_vm.c. Also converts the static "new" string initialization in new_() from strcpy to a C99 compound literal. Additionally fixes a minor whitespace alignment issue in string_endsWith() in wren_core.c.
The FNV-1a hash initializer constant 2166136261 exceeds the maximum value
for a signed 32-bit integer, causing GCC to emit a constant unsigned error
when compiling as C++. Adding the 'u' suffix explicitly marks the literal
as unsigned, resolving the compilation issue without changing behavior.
Previously, the corner case for empty strings only returned true when both the source and search strings were empty. This fix changes the condition to return true whenever the search string is empty, as the empty string is always contained within any string.
The function signature was changed from accepting null-terminated C strings to accepting explicit length parameters, with -1 indicating automatic strlen calculation. All call sites were updated to pass -1 for both lengths, and internal implementation switched from strcpy to memcpy to handle embedded null bytes correctly.
Implement wrenStringFind function in wren_value.c that uses the Boyer-Moore-Horspool algorithm with a 256-entry shift table for full 8-bit character support. Update string_contains and string_indexOf native methods in wren_core.c to call the new function instead of strstr, fixing substring searches on strings containing null bytes or extended ASCII characters.
The length field in ObjString struct is updated to use uint32_t instead of int,
ensuring consistent unsigned semantics for string size representation across
the codebase and preventing potential issues with negative length values.
Add handling for the null character escape sequence '\0' in the
readString function within the Wren compiler. This allows string
literals to include null bytes via the standard C escape syntax,
matching the existing support for other escape sequences like '\a',
'\b', and '\f'.
The previous implementation of hashValue had UNREACHABLE() placed after the switch statement, which could be reached when using NaN-tagging mode. This fix moves UNREACHABLE() into default cases within each switch branch, ensuring it's only triggered for truly unreachable paths.
Additionally, the Value struct is refactored to use an anonymous union for num and obj fields, preventing undefined behavior from accessing inactive union members. All related macros and inline functions (AS_OBJ, wrenValuesSame, wrenObjectToValue, wrenValueToNum, wrenNumToValue, and singleton value definitions) are updated to use the new `as` union member access pattern.
The macro now takes a main type, array element type, and element count, computing the total allocation as `sizeof(mainType) + sizeof(arrayType) * count`. This improves type safety and clarity at all call sites, which are updated to pass the element type (Upvalue*, Value, char) instead of manually computing byte sizes.
Replace the ObjFlags bitfield approach with a simple bool `marked` field on Obj, removing the FLAG_MARKED constant and all bitwise operations. Also add a guard in wrenMapFind to return UINT32_MAX when map capacity is zero, preventing a crash on empty map subscript lookups. Fix the validateKey function to not wrap the error string in OBJ_VAL.
The debug instruction printer was dereferencing string arrays directly instead of accessing their .buffer member, causing incorrect symbol name display for LOAD_GLOBAL, STORE_GLOBAL, CALL, SUPER, METHOD_INSTANCE, and METHOD_STATIC instructions. Updated all six printf calls to use vm->globalNames.data[global].buffer and vm->methodNames.data[symbol].buffer respectively.
Replace the `char*` buffer element type with a `String` struct that holds both the buffer pointer and its length. This eliminates O(n) `strlen()` calls in `wrenSymbolTableFind` and ensures length is always available without scanning for the null terminator. Update `wrenSymbolTableAdd` to populate the new `length` field, `wrenSymbolTableClear` to free via `symbol.buffer`, and all call sites (e.g., `methodNotFound` in `wren_vm.c`) to access `.buffer` explicitly.
Remove the FLAG_MARKED enum and ObjFlags bitfield, replacing them with a dedicated bool `marked` member in the Obj struct. Update all references in wren_value.c and wren_vm.c to use direct boolean assignment and checks instead of bitwise flag operations. This simplifies the GC marking logic and eliminates the need for manual bit manipulation.
Add early return of UINT32_MAX when map capacity is zero in wrenMapFind,
preventing a crash from modulo operation on zero during hash index calculation.
Includes regression test verifying subscript access on empty map returns null.
Replace the embedded `Module main` field in WrenVM with a pointer to a dynamically allocated `ObjModule`, adding a new OBJ_MODULE object type. Introduce `wrenNewModule()` constructor and `markModule()` GC tracer, update all references from `&vm->main` to `vm->main`, and remove the old `initModule`/`freeModule` static helpers.
Replace the single-bit FLAG_MARKED enum and ObjFlags bitfield with a direct bool 'marked' field on the Obj struct. This simplifies the garbage collector's mark-sweep logic by removing unnecessary bitwise operations (|, &, ~) and the associated enum definition, making the intent clearer and reducing cognitive overhead when reading GC-related code paths.
Remove dedicated string equality and inequality native methods (string_eqeq, string_bangeq) and their registration in wrenInitializeCore, since wrenValuesEqual now handles string comparison correctly using memcmp instead of strncmp. Also add a new test file for range equality and inequality checks.
Functions now hold a reference to the module where they were defined and load module-level variables from there instead of from the VM's global table. This introduces a new Module struct with its own variable buffer and symbol table, replacing the VM-level globals and globalNames. The main module is stored directly in the VM as a temporary measure. Performance regresses slightly due to the indirection through the function's module pointer.
Remove the dedicated string equality operators (string_eqeq, string_bangeq) and their NATIVE registrations, switching object_eqeq and object_bangeq to use the generic wrenValuesEqual instead of wrenValuesSame. Also fix wrenValuesEqual to use memcmp instead of strncmp for string comparison. Add a new test file test/range/equality.wren verifying inclusive and exclusive range equality and inequality.
Eliminate the LIST opcode and its interpreter case, debug printing, and enum entry. Instead compile list literals by loading the List class, calling its constructor, then emitting DUP, element expression, CALL_1 for add(), and POP for each element. This removes interpreter loop code and lifts the previous 255-element limit on list literals.
Implement equality comparison for Range objects by defining `range_eqeq` and `range_bangeq` native methods that delegate to `wrenValuesEqual`. Register both operators on the Range class in `wrenInitializeCore` and add a comprehensive test suite in `test/range/equality.wren` covering inclusive/exclusive ranges and cross-type inequality checks.
Implement hexadecimal number literal support in the Wren compiler by adding a `number` field to the Parser struct, a `readHexDigit` helper that returns -1 for invalid hex chars, and a `readHexNumber` function that skips the 'x' prefix, accumulates hex digits, converts via strtol, and emits a TOKEN_NUMBER. Update `readNumber` to remove the TODO placeholder for hex support. Include test cases for valid hex literals (0xFF, 0x09, negative -0xFF) and an invalid hex literal (2xFF) that triggers a lex error.
Remove the TOKEN_HEXADECIMAL token type and isHexDigit helper, replacing them with a readHexDigit function and readHexNumber parser that directly converts hex literals to numeric values during lexing. Add a `number` field to the Parser struct to hold the parsed value, and include a test for invalid hex literal error handling.
Implement `containsKey()` native method on Map that returns a boolean indicating whether the given key exists in the map. Add `validateKey()` helper to reject non-value-type keys (objects, lists, fibers, etc.) with a clear runtime error. Refactor `wrenMapGet()` signature to return success/failure via bool and output the value through an out-parameter, enabling both `containsKey()` and the existing subscript operator to share the same key validation and lookup logic. Update `map_subscript` to return `null` instead of crashing when a key is not found. Add test cases for `containsKey` success/failure, and runtime error tests for invalid key types in subscript getter, setter, and containsKey.
Add MapKeySequence and MapValueSequence classes in core.wren to expose iterable key and value views on Map instances. Implement native iterate_, keyIteratorValue_, and valueIteratorValue_ primitives in wren_core.c to support these sequences. Replace the stub Map.toString with a full implementation that iterates keys and formats each entry as "key: value", handling empty maps and nested maps correctly. Include comprehensive test suites for key iteration, value iteration, iterator type validation, and toString output across multiple orderings.
Add DEF_NATIVE(map_clear) that frees the underlying entries array and resets
capacity and count to zero, returning null. Register the "clear" method on
the Map class in wrenInitializeCore and include a test verifying the map is
emptied and the return value is null.
Add support for map literals using `{}` syntax in the compiler, replacing the previous `new Map` constructor pattern. This introduces a new `map()` grammar function that loads the Map class, instantiates it, and compiles key-value pairs using subscript setter calls. A new `CODE_DUP` bytecode is added to duplicate the map reference on the stack for each element insertion. The change includes updated test files to use literal syntax and new error tests for edge cases like EOF after colon, comma, key, or value.
Implement the core Map data structure in Wren, including the ObjMap type with
hash table storage, native methods for instantiation, subscript get/set, and
count. Add Map class declaration to core.wren with a basic toString stub.
Introduce MapEntry struct and hash table growth logic with configurable load
factor. Add test files for map creation, count tracking, key type support
(including null, bool, num, string, range, and class keys), and growth
behavior. Refactor list capacity constants into shared MIN_CAPACITY and
GROW_FACTOR macros. Change object equality operators to use wrenValuesSame
instead of wrenValuesEqual.
Add TOKEN_HEXADECIMAL token type and isHexDigit helper function to lexer.
Modify readNumber to detect 'x' prefix after '0' and lex hex digits.
Implement emitNumericConstant for hex values in compiler.
Add test file test/number/hex_literals.wren verifying basic hex parsing,
arithmetic, type checks, and negative hex literals.
Add `join` and `join(sep)` methods to the `Sequence` class, allowing any sequence to produce a concatenated string of its elements with an optional separator. Refactor `List.toString` to delegate to `join(", ")` with bracket wrapping, removing the manual iteration logic from List. Include new test files for List, Range, and String join behavior, plus runtime error tests for non-string separators.
Add a public `arity` getter to the Fn class that returns the number of required arguments for a function. Internally rename the `numParams` field to `arity` across the codebase for consistency, update the C API parameter name from `numParams` to `arity` in `wrenDefineMethod` and `wrenDefineStaticMethod`, and adjust the error message in `callFunction` to use the new field name. Also update documentation to clarify that extra arguments are ignored rather than causing an error, and add a new test file `test/function/arity.wren` verifying arity values from 0 to 4.
Add Visual Studio 2013 solution and project files under project/msvc2013/ directory, enabling native Windows builds without MinGW. Include Marco Lizza in AUTHORS for the contribution. Fix C89 compatibility issues for MSVC: disable computed goto via _MSC_VER check, define inline as _inline for non-C++ mode, change flexible array members from empty brackets to [0] syntax, and fix char to int type for readHexDigit return value. Refactor single-line if/while/for bodies in wren_compiler.c to use braces to satisfy MSVC stricter parsing.
Remove the entire wren_math.c and wren_math.h implementation files, delete all math test scripts under test/math/, and strip the WREN_USE_LIB_MATH compile flag from wren_common.h. Also remove the wrenLoadMathLibrary call from wren_vm.c and delete the wren_math.c/h entries from both the MSVC project file and its filters file.
The diff reorders includes across multiple source files to place standard library headers before project headers, removes the `_CRT_SECURE_NO_WARNINGS` define from `wren_common.h`, and adds it as a preprocessor definition in both Debug and Release configurations of the MSVC 2013 project file.
Add String as a subclass of Sequence in core.wren and implement the iterator protocol for strings in wren_core.c. The string_iterate native advances through UTF-8 code point boundaries, while string_iteratorValue extracts the full code point at a given byte index. Includes comprehensive test coverage for iteration, edge cases (empty strings, mid-sequence positions), and error handling for invalid iterator types and out-of-bounds indices. Also updates documentation for String, List, and Sequence classes with iterator protocol details and usage examples.
Add wren_math.c and wren_math.h to the MSVC 2013 project files, including both the .vcxproj and .vcxproj.filters. Also fix the indentation of the WREN_COMPUTED_GOTO comment in wren_common.h and correct a conditional guard bug in wren_vm.c where wrenLoadMathLibrary was guarded by WREN_USE_LIB_IO instead of WREN_USE_LIB_MATH.
Implement the Math standard library module for Wren, including absolute value, ceiling, floor, integer/fractional decomposition, trigonometric functions (sin, cos, tan), degree/radian conversion, and a seeded xorshift random number generator. Add WREN_USE_LIB_MATH compile-time flag to wren_common.h and provide comprehensive test suite under test/math/.
Implement a new optional Math module for the Wren standard library, gated behind the WREN_USE_LIB_MATH flag (default on). The module provides double-precision math functions (abs, ceil, floor, int, frac, sin, cos, tan, deg, rad) and a xorshift-based pseudo-random number generator with srand/rand methods. Includes comprehensive test files for each function and an all_tests runner.
Add runtime type validation to wrenGetArgumentBool, wrenGetArgumentDouble, and
wrenGetArgumentString in src/wren_vm.c, replacing TODO comments with actual
checks that return safe defaults (false, 0.0, NULL) on type mismatch.
Remove the unused wrenReturnNull function entirely from the VM implementation.
Update include/wren.h documentation for argument accessor functions to clarify
indexing semantics (including receiver at index 0) and document the new
type-checking behavior.
The modulo operator was incorrectly assigned PREC_TERM precedence, making it
evaluate at the same level as + and -. This caused expressions like "13 + 1 % 7"
to parse as "(13 + 1) % 7" instead of the correct "13 + (1 % 7)".
Changed the grammar rule for TOKEN_PERCENT to use PREC_FACTOR, matching the
precedence of * and / operators. Added a precedence test case in mod.wren to
verify the fix.
Rename all instances of "pinned" objects to "temporary roots" in wren_vm.c, wren_vm.h, and wren_common.h to better describe their stack-based lifecycle. Update the WREN_MAX_PINNED constant to WREN_MAX_TEMP_ROOTS, rename the vm->pinned array to vm->tempRoots, and rename vm->numPinned to vm->numTempRoots. Adjust all related comments and assertions to reflect the new terminology, including the GC stress test comment in wren_common.h.
Disable optimization for wrenPinObj and wrenUnpinObj functions under Microsoft Visual Studio 2013 to prevent the compiler from incorrectly inlining them, which triggers a memory access violation. The pragma directives bracket only these two functions to minimize the impact on the rest of the compilation unit.
Enforce a consistent include ordering convention across the codebase: wren_common.h (as configuration header) first, followed by the module's own header, then other wren_*.h headers in lexicographic order, and finally standard library includes. Also add missing wren_common.h include to main.c and wren_io.c, and add _CRT_SECURE_NO_WARNINGS define for MSVC in wren_common.h.
The WREN_COMPUTED_GOTO macro is now conditionally set to 0 when
compiling with _MSC_VER, as Microsoft's compiler does not support
the computed-goto extension used by the VM dispatch loop.
In `src/wren_value.h`, change three flexible array members (`char value[]`, `Upvalue* upvalues[]`, `Value fields[]`) to explicit zero-sized arrays (`char value[0]`, `Upvalue* upvalues[0]`, `Value fields[0]`) to resolve compiler warnings about non-standard zero-sized array usage in structs `ObjString`, `ObjClosure`, and `ObjInstance`.
Refactor parameter list parsing to support bracket-delimited parameters for
subscript operators. Extract finishParameterList from parameterList to handle
the common closing logic, and add TOKEN_RIGHT_BRACKET case for error messages.
Add comprehensive test suite for subscript getters and setters with 1-3
parameters in test/method/subscript_operators.wren.
The AS_OBJ macro on NaN-tagged architectures was performing a direct cast from
uint64_t to Obj* which triggers a pointer-to-integer cast warning on 32-bit
MinGW where sizeof(Obj*) is 4 bytes. Adding an intermediate cast to uintptr_t
ensures the value is properly truncated before conversion to the pointer type.
Also replaces IS_SINGLETON macro usage with direct NULL_VAL/UNDEFINED_VAL
comparisons and removes the now-unused IS_SINGLETON definition.
The CalculatedRange struct is removed from the header and calculateRange now returns the start index directly while using pointer parameters for length and step, simplifying the API and reducing struct overhead.
Extract range validation logic into a reusable `calculateRange` function that handles inclusive/exclusive bounds, negative indices, and direction stepping. Add `CalculatedRange` struct to `wren_core.h` and implement string subscript tests covering forward, backward, negative, and half-negative ranges along with comprehensive error cases for out-of-bounds and non-integer range endpoints.
Replace signed `int` with unsigned `uint8_t` for field indices in LOAD_FIELD_THIS, STORE_FIELD_THIS, LOAD_FIELD, and STORE_FIELD opcodes, and with `uint16_t` for jump offsets in JUMP, LOOP, and JUMP_IF opcodes. This eliminates sign-extension overhead and enables better code generation, yielding 1-7% performance gains across binary_trees, delta_blue, fib, for, and method_call benchmarks.
The consume() function previously returned a pointer to the consumed token,
and consumeLine() returned a boolean indicating success. Both return values
were never used by any callers. This change converts both functions to void,
eliminating dead code and simplifying the interface.
Implement the wrenGetArgumentBool function in wren_vm.c, which retrieves a boolean value from the foreign call argument slot at the given index. The function includes assertions for valid call context, non-negative index, and argument bounds. Also add the corresponding declaration to the public wren.h header to expose the new API for foreign method implementations.
Add a new public API function `wrenReturnBool` to the Wren C API, enabling
foreign method implementations to return boolean values. The implementation
stores the boolean as a Wren BOOL_VAL in the foreign call slot and clears
the slot pointer, following the same pattern as existing `wrenReturnString`.
Also add the missing `prep` target to the .PHONY list in the Makefile and
include `<stdbool.h>` in the public header to support the `bool` type used
in the new function signature.
The `is` operator was placed at `PREC_IS` which had lower precedence than `PREC_EQUALITY`, causing expressions like `true == 10 is Num` to parse incorrectly. Moved `PREC_IS` above `PREC_EQUALITY` in the precedence enum to give `is` higher binding power than `==` and `!=`. Added precedence tests in new `test/precedence.wren` file and removed TODO comments about precedence from `test/is/is.wren`.
Add a new public API function `wrenReturnBool` that allows foreign function
implementations to return boolean values to Wren. The function validates
that it is called within a foreign call context using an assertion, then
writes the boolean value into the foreign call slot and clears the slot
pointer. This completes the set of return value helpers alongside the
existing `wrenReturnNull` and `wrenReturnString` functions.
Remove the Linux-specific `_GNU_SOURCE` macro and `getline()` usage in the REPL loop, replacing them with a fixed-size `fgets()` buffer of 1024 bytes. Also switch `snprintf` to `sprintf` in `wren_vm.c` for the field limit error message, eliminating the need for `_GNU_SOURCE` entirely.
Extract range validation logic from list_subscript into a reusable calculateRange function that handles inclusive/exclusive bounds, negative indices, and direction stepping. Add CalculatedRange struct to wren_core.h and implement string subscripting via the new helper, including comprehensive test coverage for forward, backward, negative, and half-negative ranges with proper error messages for out-of-bounds and non-integer values.
Replace the C99 designated initializer syntax for `WrenConfiguration config` with explicit field assignments to avoid potential compiler compatibility issues. The change preserves the same configuration values: `reallocateFn` set to NULL, `heapGrowthPercent` and `minHeapSize` set to 0, and `initialHeapSize` set to 100 MB.
Replace the previous slice-based single-argument reduce implementation with an explicit iterator loop that seeds from the first element. This avoids the out-of-bounds range error on single-element lists and adds a Fiber.abort for empty sequences. Update the test to verify correct behavior for single-element and empty sequences.
Implement `reduce` on the `Sequence` class in core.wren with two overloads: one taking an initial accumulator and a function, and another using the first element as the initial accumulator. Add three test files covering normal reduction, single-element edge case, and wrong arity error.
Add native `!` methods to Null, Object, Num, String, and List classes so that `!` works reliably on any value. Null returns `true`, all other types return `false` since they are considered truthy. Update documentation to describe the operator as returning the logical complement and add comprehensive tests for each type. Also fix the `all` method test to use a truthy string instead of expecting a runtime error.
Add C++98 compatibility by introducing explicit casts, replacing C99-specific constructs with macros, and updating the build system to produce C++ object files. Refactor the Value union to a plain uint64_t when WREN_NAN_TAGGING is enabled, adding helper functions for num-to-value and value-to-num conversions. Update the compiler to split emit into emit and emitShort for cleaner bytecode emission, and rename emitByte/emitShort to emitByteArg/emitShortArg.
- Replace Value union with plain uint64_t typedef under WREN_NAN_TAGGING, removing num/bits field access
- Add wrenValueToNum and wrenNumToValue helper functions for double conversion
- Remove wrenValuesEqual function from wren_value.c as bitwise comparison is now direct
- Update Makefile with CPPFLAGS for C++98 compilation and add release-cpp build target
- Add wren-cpp and libwren-cpp.a to .gitignore for C++ build artifacts
Remove the single-purpose `allocate` helper and introduce three type-safe allocation macros (`ALLOCATE`, `ALLOCATE_FLEX`, `ALLOCATE_ARRAY`) that directly invoke `wrenReallocate`. Update all call sites in `wren_value.c` — including `wrenNewSingleClass`, `wrenNewClosure`, `wrenNewFiber`, and `wrenNewFunction` — to use the appropriate macro, eliminating the need for manual sizeof calculations and improving compile-time type checking when compiling as C++.
Add explicit casts to `(Code)` in `debugPrintInstruction` for bytecode indexing, to `(char*)` in `wrenSymbolTableAdd` for heap string allocation, and to `(type*)` in the `DEFINE_BUFFER` macro's reallocation call to ensure C++ compilation compatibility without implicit conversion errors.
- Cast malloc return to char* in src/main.c to avoid C++ implicit void* conversion
- Rename emit to emitByteArg and emitShort to emitShortArg in src/wren_compiler.c to avoid conflict with new emit and emitShort helpers
- Add new emit function taking uint8_t byte and emitShort helper for 16-bit big-endian emission
- Remove stray semicolon in script/test.py
Implement `all(f)` method on Sequence class that returns true if all elements pass the predicate function. Add corresponding documentation entry for `forall(predicate)` in core-library.markdown. Include three test files covering basic functionality, non-boolean return handling, and non-function argument error cases.
Add a new singleton value `VAL_UNDEFINED` to represent globals that have been implicitly declared via forward reference but not yet explicitly defined. Introduce `wrenDeclareGlobal()` to create such entries in the global table, and modify `wrenDefineGlobal()` to check for and resolve undefined slots. Update the compiler's `findUpvalue()` to stop closure capture at method boundaries, preventing methods from closing over local variables. Refactor `wrenSymbolTableAdd()` to always add symbols without duplicate checking, moving that logic to callers. Adjust error messages in the compiler to handle EOF tokens and improve newline formatting. Fix the delta_blue benchmark by renaming the global `planner` variable to `ThePlanner` to avoid shadowing the forward-declared function.
Add support for forward references to top-level variables by implicitly
declaring unresolved capitalized names as globals during compilation.
If a real definition is not found later, a compile-time error is raised.
This enables mutual recursion at the top level and fixes issues #101 and #106.
Introduce an `UNDEFINED` singleton value to mark implicitly declared globals
that have not yet been explicitly defined. Update `wrenDeclareGlobal` to add
such entries, and modify `wrenDefineGlobal` to check for and replace undefined
placeholders. Adjust the symbol table API to allow duplicate additions for
implicit declarations. Add test cases for mutual recursion, forward references
in functions and methods, and undefined variable errors.
Add test case verifying that `forall` returns true for an empty list regardless of predicate, and add two new test files covering runtime errors when `forall` receives a non-boolean-returning function or a non-function argument. Also refactor the `forall` implementation to use logical negation (`!`) instead of explicit `!= true` comparison.
Replace sequential `if` statements with `else if` chains in `readName` so that once a keyword matches (e.g. "break"), the remaining keyword checks are skipped, fixing issue #110 where later keywords could incorrectly override an already-matched token type.
Implement a `forall` method on the Sequence class that takes a predicate function and returns true only if every element in the sequence satisfies the predicate. The method iterates through all elements, returning false immediately upon encountering any element where the predicate does not return exactly true. Includes core library documentation and a test file verifying behavior with numeric comparisons and non-boolean predicate returns.
Modify `findUpvalue` in the compiler to return -1 when the enclosing function is a method, preventing closures from capturing outer local variables. Add a new `nonlocal` variable resolution path that treats capitalized identifiers as global variables rather than method receivers. Update existing test files to use capitalized global names and add new test cases for nonlocal assignment, duplicate nonlocal declarations, block scoping, and method-local variable shadowing.
The DECLARE_BUFFER macro in src/wren_utils.h had an extraneous semicolon at the end of the wren##name##BufferWrite function declaration. This caused a double-semicolon issue when the macro was expanded in source files, as the macro invocation sites already added their own semicolons. Removing the trailing semicolon from the macro template ensures consistent and correct C syntax across all buffer type declarations.
The commit adds missing semicolons to three DEFINE_BUFFER calls in wren_utils.c and wren_value.c, and also adds a semicolon to the DECLARE_BUFFER macro definition in wren_utils.h to ensure proper syntax termination. Additionally, it removes trailing whitespace on two blank lines in wren_value.c.
Add string type check for IO.read() prompt argument with Fiber.abort on non-string input. Implement stdin piping in test runner via // stdin: comments, allowing tests to provide input lines. Update stack trace filtering to omit built-in library frames with empty source paths. Add io/read.wren and io/read_arg_not_string.wren test cases.
Implement a new static `IO.read(prompt)` method in the IO class that writes a prompt string to stdout and reads a line from stdin into a fixed 1024-byte buffer. The C implementation adds a `MAX_READ_LEN` constant, an `ioRead` function using `fgets`, and registers the method via `wrenDefineStaticMethod`. The builtin source string is updated to include the new Wren method definition.
The nextToken function in wren_compiler.c previously only skipped spaces and carriage returns when advancing past whitespace, causing tab characters to be treated as non-whitespace tokens. This change adds '\t' to the whitespace check in both the initial case statement and the peekChar loop, ensuring tabs are properly skipped like other whitespace characters. A new test file test/whitespace.wren is added to verify correct handling of tabs in various indentation patterns and inline code positions.
Add a `contains(element)` method to the built-in `List` class in `builtin/core.wren` and its embedded source in `src/wren_core.c`, enabling linear search for an element. Introduce a new `Set` class example in `example/set.wren` implementing set operations (union, intersection, difference) using the new `contains` method. Update `doc/site/classes.markdown` with a warning against inheriting from built-in types due to internal bit representation constraints.
Change `fopen` mode from `"r"` to `"rb"` in `readFile()` to prevent CRLF-to-LF conversion on Windows, and add `'\r'` as a skipped whitespace character in `nextToken()` to handle carriage returns embedded in source files. This fixes parsing errors when Wren source files use Windows-style line endings.
Add a complete Set class implementation in example/set.wren with union, intersection, and set minus operations. Implement List.contains method in builtin/core.wren and its C source to support element existence checks via iteration.
When `super` is invoked at the top level without an explicit method name, the compiler previously dereferenced a NULL `enclosingClass` pointer while trying to copy the enclosing method's signature. This change adds a NULL check for `enclosingClass` in the `super_` function, setting an empty method name and zero length when the pointer is NULL, preventing the segmentation fault. Also extends the test file `super_at_top_level.wren` with additional cases for bare `super`, `super.foo("bar")`, and `super("foo")` to verify error handling for these top-level super invocations.
Rename indexOf.wren and startsWith.wren test files to index_of.wren and
starts_with.wren respectively, fix missing trailing newlines, update
string class documentation to clarify UTF-8 storage and move the
note about incorrect UTF-8 handling in the index operator, and
remove unnecessary parentheses in string_indexOf native return.
Previously, when `super` was used at the top level or outside any method, the compiler would error but still attempt to read `enclosingClass->methodLength` and `enclosingClass->methodName` from a NULL pointer, leading to undefined behavior. This restructures the error handling in `super_()` to always call `loadThis()` and guard the unnamed super call path with a NULL check on `enclosingClass`, setting an empty method signature when no enclosing class exists. Two additional test cases for `super.foo("bar")` and `super("foo")` are added to verify the compiler continues past the error without crashing.
Add a `contains(element)` method to the `List` class in the core library,
enabling linear search for an element across all list items. Update the
`example/set.wren` file to use the new Wren operator syntax for `|`, `+`,
`&`, and `-` methods, replacing old function-style parameter definitions
with block-style closures. Also fix trailing whitespace throughout the
Set example and embed the new `contains` method into the C source string
in `src/wren_core.c` for the built-in List class.
When `super` is called at top-level with no arguments, `enclosingClass` is null, causing a segfault when trying to access its method name. This fix wraps the unnamed super call logic inside an else branch that only executes when `enclosingClass` is non-null, and adds a test case for the unnamed `super` call at top-level.
The `string_subscript` native was allocating a 2-byte buffer for single-character
results, causing a one-byte over-allocation. Changed to allocate exactly 1 byte
for the character plus the null terminator handled by `wrenNewUninitializedString`.
Also fixed `wrenNewUninitializedString` to properly cast the `length` parameter
to `int` when assigning to `ObjString->length`, resolving a potential
truncation warning on 64-bit systems.
Added a test case to verify that subscript access returns a properly formed
string that compares equal to the expected character.
The lexer previously treated a leading minus sign followed by a digit as a single negative number token, which caused incorrect parsing of expressions like `1-1` and broke method calls on negative numbers. This change removes that special case, making the minus sign always a separate token, and updates all test expectations to use explicit parentheses for negative number method calls.
Previously, the lexer greedily consumed a `-` followed by a digit as a single negative number token, causing "1 -1" to be lexed as two number tokens instead of a minus operator and a positive literal. This broke parsing of subtraction expressions without spaces.
Now `-` always emits a TOKEN_MINUS, and the parser handles unary negation. This is a breaking change: `-16.sqrt` is now parsed as `-(16.sqrt)` instead of `(-16).sqrt`, requiring parentheses for negative method receivers. All number method tests (abs, ceil, floor, sqrt, toString) are updated to use explicit parentheses for negative receivers.
Introduce a `methodNotFound` helper in `wren_vm.c` that parses the symbol name to determine the number of arguments and generates a detailed error message including the argument count. Update all runtime error calls and test expectations across fiber, inheritance, method, and super test files to reflect the new format, and add new test cases for method-not-found with 1, 2, and 11 arguments.
Add writeObject_ helper that checks if obj.toString returns a String before
writing it, falling back to "[invalid toString]" otherwise. Update print,
write, and printList_ to use the new helper instead of calling toString
directly. Include test cases for both IO.print and IO.write with a class
whose toString returns an integer.
Use the integer length returned by sprintf directly instead of calling strlen on the buffer, eliminating redundant string length calculations in two native functions for number and range string conversion.
The length field in ObjString struct now uses int instead of size_t, saving memory
and aligning with other string length storage conventions throughout the VM.
This eliminates signed/unsigned conversion warnings and matches the type used
elsewhere for string lengths.
Replace strlen(buffer) calls with the length returned directly by sprintf in the
num_toString and range_toString native functions, eliminating unnecessary string
length calculations after formatting.
Replace separate strlen(buffer) calls with the length returned directly by sprintf in both
num_toString and range_toString native functions. This avoids an unnecessary pass over the
formatted string buffer, improving performance for these hot-path number and range conversions.
Refactor string_contains, string_count, string_eqeq, string_bangeq, and string_subscript to use the precomputed ObjString->length field instead of calling strlen() on the C string. This avoids O(n) length calculations on every string comparison and count operation.
Also update markString memory accounting to use the stored length plus null terminator instead of recalculating with strlen().
Add a `length` field to the `ObjString` struct in `wren_value.h` and initialize it in `wrenNewUninitializedString()` in `wren_value.c`. This stores the string's byte count excluding the null terminator, enabling O(1) length retrieval while preserving null-terminated C string compatibility.
Remove separate heap allocation for string text by converting ObjString.value from a char pointer to a C99 flexible array member. This eliminates the extra allocation in wrenNewUninitializedString and the corresponding free in wrenFreeObj, reducing memory fragmentation and pointer indirection for all string objects.
The previous implementation relied on sprintf's "%.14g" format for NaN values,
which produces platform-dependent results (some libc versions add a sign prefix).
This change adds an explicit NaN check using the self-comparison idiom (value != value)
and returns a lowercase "nan" string directly, ensuring consistent behavior across
all platforms. The buffer size comment is also updated to remove the now-inaccurate
reference to NaN handling.
Remove separate heap allocation for string text by storing it directly in the ObjString struct using a C99 flexible array member. This eliminates the need for an extra pointer indirection and a separate memory allocation call in wrenNewUninitializedString, while also simplifying the cleanup logic in wrenFreeObj by removing the explicit deallocation of the string's value buffer.
- Open source files in binary mode to prevent OS-level newline translation
- Treat '\r' character as equivalent to '\n' in the tokenizer
- Handle '\r' followed by '\n' as a single line break via twoCharToken
The `error()` function in the compiler now returns early when the offending token
is of type `TOKEN_ERROR`, preventing the lexer's already-reported error from
being emitted a second time to stderr. This eliminates redundant error messages
for malformed tokens.
Add special handling in the lexer's nextToken function to detect a '#' character followed by '!' on the first line (currentLine == 1) and skip the entire line as a comment. If the shebang appears on any other line, or if a '#' is not followed by '!', emit a lex error for invalid character. Include test cases for valid shebang execution, shebang at EOF, shebang on a non-first line (expected error), and an invalid shebang pattern (expected error).
Remove the empty-line check in runRepl that skipped interpretation for blank input, and refactor the main compilation loop in wrenCompile to use a while loop with TOKEN_EOF matching instead of a for loop with redundant EOF break. This allows the compiler to process files containing only comments or whitespace without prematurely terminating, fixing issue #57. Add test fixtures for comment-only source files.
Add helper functions `canonicalSymbol` and `methodArgumentsString` to extract and format the argument count from symbol names. Update the runtime error message in `runInterpreter` to append the arity (e.g., "with 0 arguments", "with 2 arguments", "with 1 argument") when a class does not implement a method. Add new test cases for missing methods with one, two, and eleven arguments, and update all existing test expectations to include the arity suffix.
Add a strcmp check against "\n" before invoking wrenInterpret in the
runRepl function, preventing an unnecessary interpretation attempt and
potential error handling for blank user input.
Remove the `;` case from the `nextToken` function in `wren_compiler.c` so that semicolons are no longer lexed as `TOKEN_LINE`. Update the documentation in `doc/site/syntax.markdown` to describe newlines as the sole statement separator, and adjust all test files (`test/limit/many_constants.wren`, `test/limit/many_globals.wren`, `test/limit/too_many_constants.wren`, `test/semicolon.wren`) to reflect that semicolons now produce an error instead of being treated as line breaks.
The anonymous union inside the Method struct was previously unnamed, which is not allowed in strict C99 mode. This change names the union `fn` and updates all references to its members (`.primitive`, `.foreign`, `.obj`) to use the qualified syntax (`.fn.primitive`, `.fn.foreign`, `.fn.obj`). The NATIVE macro parameter was also renamed from `fn` to `function` to avoid shadowing the new union field name.
Add explicit case for '\t' alongside space character in the nextToken function's whitespace-skipping logic. Previously, only spaces were consumed during whitespace skipping, causing tab characters to be incorrectly treated as unexpected tokens. The fix ensures tabs are properly skipped by first matching the tab case and then falling through to the existing space-handling loop.
Add static `IO.read(prompt)` method to the IO class that writes the prompt to stdout
and reads a line from stdin into a 1024-byte buffer. The C implementation uses
`fgets` for input and returns the string via `wrenReturnString`. The Wren wrapper
calls `IO.write` for the prompt before delegating to the native `IO.read` method.
The getline function's return value was previously ignored, causing gcc to emit a
warning that was treated as an error during build. This commit captures the return
value in a ssize_t variable and adds a check for -1 (indicating read failure) to
the existing feof(stdin) condition, ensuring both EOF and error conditions are
properly handled.
- Inserted a TODO comment before the toString native binding in the
function class initialization to remind about implementing an arity
getter for Wren functions.
Implements a new `Fiber.abort()` static method that allows a fiber to terminate with a custom error message string. The method validates that the argument is a string, then returns a PRIM_ERROR to abort the current fiber. Includes three test cases: basic abort with error message retrieval via `fiber.try`, aborting the main fiber with a runtime error, and type checking that non-string arguments produce an appropriate error message.
Consolidate the two separate conditional branches for empty range on zero-length list into a single expression using ternary on range->isInclusive, removing the TODO comment and reducing code duplication.
Migrate fiber stack management from an integer `stackSize` counter to a direct `Value* stackTop` pointer, eliminating repeated `stack[stackSize - 1]` calculations. Update all stack slot accesses, iteration loops, and the `CallFrame::stackStart` field to use pointer arithmetic, simplifying the code and reducing index arithmetic overhead.
Store the start of the current frame's stack in a local `stackStart` variable
during LOAD_FRAME() macro execution, replacing repeated calculations of
`fiber->stack[frame->stackStart + offset]` with `stackStart[offset]` across
LOAD_LOCAL and LOAD_FIELD opcodes. Also remove unused `upvalues` register
variable and its assignments from closure handling paths.
Move the class pointer from ObjInstance into the base Obj struct and remove the separate metaclass field from ObjClass, allowing all heap objects to directly reference their class. Add an inlined wrenGetClassInline function in the header to eliminate function call overhead during method dispatch, yielding 10-43% performance gains across benchmarks.
Introduce nine new bytecode instructions (CODE_LOAD_LOCAL_0 through CODE_LOAD_LOCAL_8) that directly encode the local slot number in the opcode itself, eliminating the need to read and decode a separate argument byte for the most frequently accessed local variables. This optimization targets the hottest path in the interpreter, as LOAD_LOCAL is the most executed instruction across benchmarks (e.g., 3.75M times in delta_blue). The compiler's `loadLocal` helper emits the specialized opcode when the slot index is ≤8, falling back to the generic `CODE_LOAD_LOCAL` with an argument byte otherwise. The VM dispatch table and debug printer are updated accordingly. Benchmarks show consistent speedups: fib +7.3%, method_call +5.9%, binary_trees +1.9%, for +2.8%, delta_blue +1.0%.
In `getNumArguments`, add a `return 0` after the `UNREACHABLE()` default case to prevent
undefined behavior when assertions are disabled. In `wrenGetClass`, reorder the
`UNREACHABLE()` macro before the `return NULL` so the unreachable annotation is
always emitted. In `runInterpreter`, replace the `ASSERT(0, ...)` with an explicit
`UNREACHABLE()` and `return false` to guarantee a return value on all control paths
when the interpreter exits unexpectedly.
Add -lm flag to both debug and release linker commands in Makefile to resolve
undefined math library references. Define _GNU_SOURCE in main.c to make getline()
available under GCC. Add default cases to switch statements in runFile and
getNumArguments to suppress -Wswitch warnings. Fix u_int8_t to uint8_t type in
wrenNewFunction declaration and definition for POSIX compliance.
Implement a new `Fiber.try()` method that allows a calling fiber to catch runtime errors from a called fiber. When a fiber is invoked with `try()` instead of `call()`, any runtime error that occurs in the called fiber is returned as a string to the caller rather than terminating the program with a stack trace. The `callerIsTrying` flag on `ObjFiber` tracks this state, and the `runtimeError` function now checks this flag to redirect the error to the caller's stack. Also adds `Fiber.error` getter to retrieve the error string from a failed fiber, changes `Fiber.isDone` to return `true` for errored fibers, and migrates the fiber error field from a raw `char*` to an `ObjString*` for proper memory management. Includes comprehensive test cases for try behavior, re-entry prevention, and error state queries.
The crash occurred in captureUpvalue() when an upvalue for an earlier local
variable was captured after a later one. The function walked to the end of the
open upvalue list but then dereferenced a NULL pointer when comparing
upvalue->value against the target local slot.
Added a NULL guard before the equality check so that if no existing upvalue
is found, the function correctly proceeds to create a new one instead of
dereferencing a null pointer.
Also fixed a typo in the comment ("existsing" -> "existing").
Added regression test close_over_later_variable.wren that exercises the
scenario of capturing upvalues for locals in reverse declaration order.
Add a 64-character maximum identifier length with compile-time error reporting, and enhance the runtime field overflow error to include the class name and remove the TODO comment. Update test expectations for both limit scenarios.
Add a new test case `shadow_closure_with_local.wren` that verifies a closed-over
variable can be shadowed by a local variable in an inner scope within the same
closure. The test ensures that the outer closure variable retains its value
outside the inner scope after the shadowing local goes out of scope.
Also remove an incomplete TODO comment from `reuse_closure_slot.wren` and add
a detailed TODO comment in `wren_compiler.c` discussing the design trade-off
between treating capitalized names as globals versus implicit `this` calls
when walking the scope chain.
Add a static `print` method with no arguments that writes only a newline, enabling blank-line output. Remove the `ignoreNewlines` calls before closing parentheses in parameter lists and method calls, tightening syntax rules. Add a `strlen` TODO comment in symbol table lookup for future optimization. Move the `Foo.toString` test from `test/io/write.wren` to `test/io/print.wren` and delete the old write test file.
Add new `fiber.call` method that remembers the calling fiber for symmetric transfer, while `fiber.run` now performs tail-call style switching without preserving the caller. Introduce re-entrancy guards preventing a fiber from being called multiple times, update all test fixtures to use the new API, and add comprehensive test coverage for call semantics including direct/indirect re-entrancy, value passing, and finished fiber errors.
Add 15 static overloads of IO.print in builtin/io.wren and src/wren_io.c, each delegating to a new private printList_ method that iterates over a list of objects. Update all benchmark and test files to use the new multi-argument syntax, removing explicit .toString calls and string concatenation. Add a comprehensive test/io/print.wren verifying single-argument return value, multi-argument null return, and concatenation of up to 16 arguments.
- Replace silent null return with explicit string type check in string_plus native
- Remove outdated TODO comments about string coercion from both num_plus and string_plus
- Add test for basic string concatenation ("a" + "b")
- Add test verifying runtime error when concatenating string with non-string ("a" + 123)
Enforce syntactic requirement for parentheses in operator method signatures (e.g., `+(other)` instead of `+ other`) and setter definitions (e.g., `bar=(value)` instead of `bar = value`). Update the compiler's `infixSignature`, `mixedSignature`, and `namedSignature` functions to consume explicit `(` and `)` tokens around parameter names. Migrate all built-in core library operator definitions and test fixtures to the new parenthesized syntax.
Add `list_instantiate` native to create empty lists, bind it to List's metaclass for `new` support, and remove the related TODO. Also change three `inline` functions in `wren_value.h` to `static inline` to prevent linker errors. Include a new test `test/list/new.wren` verifying `new List` yields an empty list that supports `add`.
Move the wrenIsBool and wrenIsObjType function definitions from wren_value.c
into wren_value.h as inline functions, eliminating function call overhead.
Also relocate OBJ_VAL macro definition to appear before its first use in the
header and add inline for wrenObjectToValue. This yields 3-13% speedup across
benchmarks (binary_trees, delta_blue, fib, for, method_call).
The internal PinnedObj type was renamed to WrenPinnedObj across the codebase to make it publicly accessible, as the WREN_PIN macro expands to stack allocations of this type that must be visible to external consumers. Updated all typedefs, function signatures, and variable declarations in wren_vm.c and wren_vm.h to use the new prefixed name, and removed the TODO comment about moving the struct into wren_vm.c.
Rename boolean defines from `true`/`false` to `1`/`0` for consistency, prefix debug flags with `WREN_DEBUG_`, add `WREN_USE_LIB_IO` flag to guard IO library loading, and wrap IO module includes and function declarations in conditional compilation blocks.
Replace the nested `SymbolTable` struct (containing a `StringBuffer names` field) with a direct `typedef StringBuffer SymbolTable`, updating all internal accesses from `symbols->names.data` to `symbols->data` and `symbols->names.count` to `symbols->count`. Rename `vm->methods` to `vm->methodNames` and `vm->globalSymbols` to `vm->globalNames` across the codebase, including debug printing, compiler symbol resolution, core initialization, and VM interpreter error messages. Remove the stale TODO comment about error codes in `wren.h` and adjust the `instantiate` method name to include a leading space to prevent direct user invocation.
Replace the static `Value globals[MAX_GLOBALS]` array in WrenVM with a dynamic `ValueBuffer` that grows on demand, removing the 256-global limit. Introduce `wrenDefineGlobal()` helper that adds a named global to the symbol table and stores its value in the buffer, returning -2 when the 65536 limit is reached. Update all global access sites (compiler emit, runtime interpreter, GC marking, debug disassembly, core class definition) to use the buffer's data pointer and short (16-bit) opcode arguments instead of byte-sized ones. Add a regression test `many_globals.wren` that defines 8200 globals to verify the new capacity.
Add `addAll(other)` method to the List class in both the Wren core library source and its embedded C string representation. The method iterates over the given `other` sequence, appends each element to the list using the existing `add()` method, and returns the original `other` argument to match the documented return behavior. Include a new test file `test/list/add_all.wren` covering basic appending, empty iterable, range input, and return value verification.
Refactor fiber creation from a static `Fiber.create` method to a constructor-based `new Fiber` pattern. This introduces a `fiber_instantiate` native that returns the Fiber class itself, allowing `new Fiber` to invoke `fiber_new` for validation and creation. A shared `validateFn` helper is added to centralize function argument checking, replacing inline validation in both `fiber_new` and `fn_new`. All test files are updated to use the new syntax, and the old `create_wrong_arg_type` test is replaced with `new_wrong_arg_type`.
Add type checking to the `fn_new` native function to ensure the argument
passed to `Fn.new` is either a function or closure. Previously, passing
an invalid type like an integer would silently succeed. Now it returns
a clear runtime error message: "Argument must be a function."
Also remove two stale TODO comments about newline handling in the
compiler that were no longer relevant to current parsing logic.
This change shifts newline tokenization from the lexer's readRawToken into the parser's nextToken, allowing the grammar to selectively ignore newlines in specific contexts (e.g., after operators like "|", "is", "for", "if", "while") while treating them as errors in others (e.g., after "var", "new", "class", "static", before commas in lists). The lexer now emits TOKEN_LINE for semicolons and newlines uniformly, and the parser decides which to elide. Test files are updated to reflect the new error expectations and to remove tests for newlines that are now grammatically allowed.
Refactor `finishBlock` in `wren_compiler.c` to detect single-expression bodies (no newline after `{`) and leave a value on the stack, enabling implicit returns for methods and functions. Add `finishBody` wrapper to handle constructor flag. Update all benchmark and test `.wren` files to remove explicit `return` keywords from getters and single-expression methods, relying on the new implicit return behavior. Remove old `return_null_if_brace.wren` test, add `newline_body.wren`, `newline_in_expression_block.wren`, and `no_newline_before_close.wren` tests for edge cases.
Remove the TOKEN_FN enum value, its keyword mapping in readName, and the
associated function syntax handling from the tokenizer. Also clean up
unnecessary operator tokens from the newline-skipping switch in nextToken
to simplify the token processing logic.
Replace all occurrences of the old `fn` function literal syntax with the new `new Fn {|params| body}` block syntax across benchmark, test, and source files. Update the Wren compiler to handle the pipe operator as a block argument delimiter by removing `TOKEN_PIPE` from the newline-swallowing token list and adding explicit newline matching in `infixOp`. Adjust test expectations in `conditional/precedence.wren` to remove `fn`-based expressions and update `test/assignment/local.wren` to use the new syntax.
Refactor the 'new' operator compilation to emit a method call to 'instantiate' on the class instead of using a dedicated bytecode instruction. This allows built-in types like Fn to override instantiation behavior, enabling `new Fn { ... }` to return the block argument directly. Remove the CODE_NEW opcode from the VM, compiler, and debug printer, and add native methods `class_instantiate`, `fn_instantiate`, `fn_new`, and `object_instantiate` to handle the new dispatch. Update test files to reflect the renamed `Function` class to `Fn`.
Update all fiber test files to replace `Fiber.create(fn { ... })` with `Fiber.create { ... }` block syntax, and add compiler support for parsing block bodies without requiring a newline after the opening brace. The compiler changes include removing `TOKEN_LEFT_BRACE` from the newline-discarding token list and adding `match(compiler, TOKEN_LINE)` calls in `finishBlock`, `methodCall`, `function`, and `block` to handle empty block bodies and inline expressions.
Replace two static error-reporting functions with a single RUNTIME_ERROR macro that stores the fiber frame before calling runtimeError and returning false. This consolidates error handling logic and prepares for catching runtime errors from other fibers by ensuring consistent frame preservation.
In the interpreter's NEW opcode handler, check if the top-of-stack value is a class before attempting construction. If not, raise a runtime error with a descriptive message instead of silently failing or crashing.
Also add a test case (test/new_on_nonclass.wren) that verifies the error is correctly triggered when calling new on a non-class value like an integer.
Implement num_bitwiseAnd and num_bitwiseOr native methods that cast operands to uint32_t before applying bitwise operations, matching the existing bitwiseNot pattern. Register both operators on the number class and add test files covering basic cases, max u32 boundary, overflow behavior, and non-numeric operand error handling.
The error message for unimplemented methods previously included the word "Receiver"
before the class name, which was redundant and inconsistent with other error messages.
Updated the format string in `methodNotFound()` to drop "Receiver" and use the class
name directly. Added a new test case for static method not found errors and updated
existing test expectations to match the new message format.
Add ObjClass* parameter to methodNotFound and pass classObj from all call sites in runInterpreter, so the error message displays the receiver's class name instead of a generic "Receiver does not implement method".
Add INFIX_OPERATOR entries for TOKEN_PIPE and TOKEN_AMP with PREC_BITWISE precedence in the compiler's grammar rules, replacing the previous UNUSED entries. This allows user-defined classes to implement custom `|` and `&` operator methods.
Include a new test file `test/custom_operators/bitwise.wren` that defines Amp and Pipe classes with their respective operator methods to validate the feature works correctly.
Extract the common `map` and `where` methods from `List` into a new `Sequence` base class, making `List is Sequence` and `Range is Sequence`. Remove duplicate native method registrations for Range in wren_core.c and add new test files for Range map/where operations.
Add TOKEN_QUESTION token type and implement conditional() parsing function in wren_compiler.c to support the ternary conditional operator (condition ? then : else). Register fn_toString native in wren_core.c to return "<fn>" string for function objects. Include comprehensive test suite covering precedence, short-circuit evaluation, and error cases for missing tokens.
Implement a `where` method on the List class that takes a function `f` and returns a new list containing only elements for which `f.call(element)` returns true. The implementation is added both in the builtin core.wren source and embedded in the C library source string in wren_core.c. Also includes a new test file `test/list/where.wren` verifying filtering with matching and non-matching predicates.
Implement a `where` method on the List class that takes a predicate function and returns a new list containing only elements for which the predicate returns true. The implementation iterates over the list, calls the predicate on each element, and collects matching elements into a new list. Includes test cases verifying filtering with both matching and non-matching predicates.
Implement a `map` method on the List class that applies a given function `f` to each element, returning a new list with the transformed values. Includes a test case verifying incrementing each element by 1.
In classDefinition in wren_compiler.c, add a check for TOKEN_RIGHT_BRACE before
requiring a newline after the last method definition, so that a closing brace
immediately following a method is accepted. Update test/class/syntax.wren with
a test case for this syntax.
Add special case in list_subscript native to return a new empty list when
the source list is empty and the requested range is [0..-1] (inclusive) or
[0...0] (exclusive). This enables using list[0..-1] as a universal copy
idiom that works even for empty lists. Update the List.+ operator to use
this idiom instead of manual iteration. Add test cases for both range
forms on empty lists.
The previous implementation of List.+ used conditional checks (`this.count > 0`, `that.count > 0`) before iterating, which caused the loop body to be skipped entirely for empty lists. This commit removes those guards, allowing the for-in loops to run unconditionally — the Wren VM's iterate/iterateValue protocol already handles the empty case correctly at the native level. Additionally, the `list_iterate` native in `wren_core.c` now returns `false` immediately when the list count is zero and the iterator argument is null, preventing an out-of-bounds index of 0 from being returned for empty lists. The test file `test/list/concat.wren` is renamed to `test/list/plus.wren` and extended with a check that the original list `a` remains unmodified after concatenation. A new test in `test/list/iterate.wren` verifies that iterating over an empty list returns `false`.
Add `+` method to List class in core.wren that creates a new list by concatenating elements from both operands. Supports concatenation with Range objects and handles empty lists on either side. Includes comprehensive test cases in test/list/concat.wren covering list-list, list-range, and empty list combinations.
Move upvalue closing logic from the else branch to before the fiber completion check
to ensure upvalues are properly closed when a fiber finishes. Add test cases for
closure behavior across fiber yields and for Fiber.create argument type validation.
In the compiler, when a bare name is encountered inside a class definition and it does not resolve to a local variable or global, emit an implicit `this.` load before the call, enabling getter, setter, and method calls without explicit receiver. Update all benchmark, builtin, and test files to remove redundant `this.` qualifiers, and add comprehensive test suites for implicit receiver behavior across instance, inherited, static, nested, and shadowing scenarios.
When shrinking a list's capacity, wrenReallocate may return a different
memory address. The previous code ignored the return value, leaving
list->elements pointing to freed memory. This fix assigns the result
back to list->elements and adds a regression test for grow/shrink
behavior.
The previous comparison `classObj->methods.count < symbol` incorrectly allowed
access when `symbol` equals `count`, which is out of bounds for zero-indexed
arrays. Changed both occurrences in `runInterpreter` to use `symbol >= count`
to properly guard against out-of-bounds access.
Add two new public API functions to the Wren VM for foreign function interface
(FFI) return values. wrenReturnNull sets the foreign call slot to a null value,
while wrenReturnString copies the provided text into a new Wren heap string,
supporting both explicit length and strlen-based length calculation. Both
functions include assertions to ensure they are called within a valid foreign
call context.
Add `numParams` field to `ObjFn` and `Compiler` structs to track expected parameter count. Refactor `parameterList` to return count and store it in compiler. Modify `endCompiler` to pass `numParams` to `wrenNewFunction`. Replace single `fn_call` native with 17 arity-specific `fn_call0` through `fn_call16` natives that validate argument count against `fn->numParams` via `callFunction` helper, returning error on missing args. Remove stale TODO comment in VM interpreter. Add test files `call_extra_arguments.wren` and `call_missing_arguments.wren`.
Migrate the clock timing functionality from the OS class to the IO class across all benchmark scripts and the core library. Remove the os_clock native function and its associated OS class definition from wren_core.c, while adding a new ioClock native in wren_io.c that uses the same clock()/CLOCKS_PER_SEC logic. Update all benchmark files (binary_trees, fib, for, method_call) to reference IO.clock instead of OS.clock for both start time and elapsed time calculations.
Replace monolithic corelib.wren with per-class builtin/*.wren files and generate_builtins.py script. Move IO class definition and its native writeString_ method from wren_core.c to new wren_io.c, adding wrenGetArgumentString and wrenDefineStaticMethod to the public API. Update Makefile target from corelib to builtin and remove generate_corelib.py.
Replace the TODO placeholder for error codes with a proper WrenInterpretResult enum
containing WREN_RESULT_SUCCESS, WREN_RESULT_COMPILE_ERROR, and
WREN_RESULT_RUNTIME_ERROR. Update wrenInterpret signature to return the new type
and refactor callers in main.c and wren_vm.c to use the enum values instead of
magic exit codes. Also add a startup banner to the REPL.
Replace TODO comments with proper error reporting for unterminated strings, incomplete Unicode escapes, and invalid escape sequences. Add five new test files covering each error case.
Replace all (void) POP() calls with a dedicated DROP() macro that decrements
fiber->stackSize directly, eliminating unused-value compiler warnings and
making the intent of discarding the stack top explicit throughout the
interpreter loop, logical operators, upvalue closing, and object construction.
Extend the list subscript operator to accept Range arguments in addition to plain numbers. When a Range is provided, the operator returns a new list containing the elements within that range, supporting inclusive (..) and exclusive (...) bounds, negative indices that count from the end, and automatic reversal for backwards ranges. Added validation functions for integer values and range bounds, with dedicated error messages for non-integer range endpoints and out-of-bounds conditions. Updated the test suite with comprehensive range subscript tests and removed the old test that only checked for non-numeric subscripts.
In the compiler's classDefinition function, the class name constant is now emitted as a separate CODE_CONSTANT instruction before CODE_CLASS, instead of being passed as a short argument to CODE_CLASS. The VM's CLASS case handler is updated to pop the class name string from the stack using PEEK2() instead of reading it from the constant table via READ_SHORT(). The comment in wren_vm.h is revised to document that the class name is now on the stack below the superclass.
Store the class name string in `ObjClass` during compilation and class creation, and expose it through a new `class_name` native method. Also update `object_toString` to return the class name for class instances and "instance of <name>" for regular instances.
Eliminate the dedicated CODE_SUBCLASS opcode and instead push a null value to indicate implicit inheritance from Object. This simplifies the bytecode interpreter by using a single CODE_CLASS instruction that checks the top of stack for a null sentinel to determine whether to use the implicit Object superclass or a user-provided superclass. The change also consolidates argument counting and debug printing logic, removing the now-unnecessary case branches for CODE_SUBCLASS across the compiler, VM, and debug modules.
The getNumArguments function was incorrectly returning the number of upvalues
as a direct count instead of accounting for the two bytes consumed per upvalue
in the bytecode stream. This caused the patching loop to exit prematurely when
processing closures with multiple upvalues, leading to corrupted bytecode offsets
and runtime crashes.
Add a check for feof(stdin) after fgets in the REPL input loop in src/main.c. When the user presses Ctrl-D, stdin is closed and fgets returns NULL without reading input. The new code detects this condition, prints a newline for clean terminal output, and returns 0 to exit the REPL gracefully instead of continuing with an empty or uninitialized line buffer. This resolves issue #6 where Ctrl-D caused undefined behavior or an infinite loop.
The for-in loop in Wren already increments the loop variable automatically, so the explicit `i = i + 1` inside the loop body was causing every other element to be skipped when building the string representation of a List. This fix removes the unnecessary increment from both the corelib.wren source and the embedded string in wren_core.c.
Replace six separate type-check functions (wrenIsClosure, wrenIsFiber, wrenIsFn, wrenIsInstance, wrenIsRange, wrenIsString) with a single wrenIsObjType that takes an ObjType parameter. Add IS_CLASS macro and runtime validation in the IS bytecode handler to ensure the right operand is a class, producing a clear error message when it is not. Add test case for non-class right operand.
Replace silent RETURN_NULL for non-numeric right operands with explicit runtime error via validateNum in four comparison primitives (num_lt, num_gt, num_lte, num_gte). Also remove redundant IS_NUM check in num_mod. Add four new test files verifying runtime error messages for each comparison operator when given a boolean operand.
Update the VM interpreter to treat null as falsey alongside false in if, while, for, and logical AND/OR operations. Remove the TODO comment and adjust short-circuit logic in OP_JUMP_IF and OP_JUMP_IF_NOT branches. Refactor existing test files into subdirectories (if/, logical_operator/, while/) and add dedicated truthiness tests for each construct. Remove old test/if.wren and update and/or tests to exclude null/0/"" truthiness checks now that null is falsey.
Extract the common pattern of checking and setting the FLAG_MARKED bit on an object into a dedicated static helper function. This eliminates repeated inline code across markInstance, markList, markUpvalue, markFiber, and other marking functions, improving maintainability and reducing the risk of inconsistencies in cycle detection.
Add isInclusive boolean to ObjRange struct to distinguish between inclusive (..) and exclusive (...) ranges. Update wrenNewRange signature to accept isInclusive parameter. Implement correct iteration logic for both ascending and descending ranges, handling empty exclusive ranges and floating-point iteration. Add type validation for range RHS operands. Update min/max/to properties to return raw endpoint values instead of adjusted ones. Add comprehensive test coverage for inclusive/exclusive ranges, negative ranges, empty ranges, floating-point iteration, isInclusive property, toString, and type error cases.
Remove the Range class definition from corelib.wren and implement it as a native C type with ObjRange struct, adding native methods for from, to, min, max, iterate, and iteratorValue. Move range operator handling from Num class to native num_dotDot and num_dotDotDot functions. Update VM to support OBJ_RANGE type in marking, printing, and type checking. Add comprehensive test suite for range operations including ordered, backwards, and exclusive ranges.
Refactor the compiler to treat static methods as local variables defined in an implicit scope surrounding the class, eliminating the need for VM-level static method support. This changes `declareVariable` to use the previously consumed token, adds `declareNamedVariable` for explicit token consumption, and updates `parameterList` to use the new function. Adjusts VM bytecode ordering for `CODE_METHOD_INSTANCE` and `CODE_METHOD_STATIC` to pop the class before the function body. Adds comprehensive test coverage for static fields including closures, nested classes, default null values, instance method access, and error cases for field usage outside classes or in static methods.
Move fields, isStaticMethod, methodName, and methodLength from Compiler struct into a new ClassCompiler struct, replacing them with a single enclosingClass pointer. This consolidates class-level bookkeeping and simplifies the Compiler struct by grouping related fields.
Add a new token type TOKEN_STATIC_FIELD to the compiler's token enum and
update the lexer to distinguish between single underscore (TOKEN_FIELD)
and double underscore (TOKEN_STATIC_FIELD) prefixed names. The new token
is registered in the grammar rules table as UNUSED with a TODO comment
for future implementation.
Remove the metaclass inheritance chain that mirrored the class hierarchy, instead always binding metaclasses directly to Class. This prevents static methods from being inherited by subclasses. Also add a compile-time error when `super` is used inside a static method, since it would always delegate to Class without useful effect. Update the `isInsideMethod` check to use `compiler->fields` instead of walking the parent chain for correctness. Add test cases for the new restrictions and remove the old test that verified static method inheritance.
Add isStaticMethod flag to Compiler struct to track whether the current method is static. When compiling a field access, check this flag and emit a compile error if an instance field is used inside a static method. Initialize the flag to false for top-level code and propagate it from parent to child compilers for nested functions and methods.
Add a MAX_FIELDS constant (255) to wren_common.h and implement field overflow
validation in the compiler and VM. The compiler now emits an error when a class
declares more than 255 fields, and the VM checks for inherited field overflow
at class creation time, producing a descriptive runtime error. Also add four
test files (many_fields, many_inherited_fields, too_many_fields,
too_many_inherited_fields) to verify both valid and invalid field counts.
The error message emitted when a left brace is expected after a class body
incorrectly referenced a closing brace '}' instead of the required opening
brace '{'. This fix updates the error string in the `classDefinition` function
within `src/wren_compiler.c` to accurately describe the expected token.
Add a new `isDone` native method to the Fiber class that returns true when the fiber has no remaining frames. Replace TODO comments with proper runtime error handling in `fiber_run`, `fiber_run1`, `fiber_yield`, and `fiber_yield1` to prevent running finished fibers or yielding from the main fiber. Include test cases verifying the new property and error conditions.
Add iteration over the VM's object linked list in wrenFreeVM to properly
deallocate all allocated GC objects before freeing the VM memory,
replacing the previous TODO placeholder comment.
In the compiler, when a `return` token is followed by a newline or right brace, emit CODE_NULL instead of parsing an expression. This enables bare `return` statements to implicitly return null. Also removes a stale TODO comment about setter parameters and fixes a typo in a comment in wren_core.c. Adds three test cases: one for explicit return in a function, one for bare return before a brace, and one for bare return before a newline.
Implement the remaining standard C escape codes in the readString function
of the Wren compiler, removing the TODO comment about missing escapes.
Adds handling for alert (a), backspace (b), form feed (f), carriage return (r),
and vertical tab (v) escape sequences.
Add core fiber functionality including Fiber.create, Fiber.run, Fiber.yield
primitives with value passing between fibers. Introduce PRIM_RUN_FIBER result
type for interpreter loop, add caller field to ObjFiber struct for yield
resumption, and update GC marking to traverse fiber caller chains. Modify
wrenNewFiber to accept a function/closure argument and initialize the first
call frame. Add debug stack printing with fiber pointer. Include test suite
covering fiber creation, run, yield, value passing, type checking, and
caller resumption.
Implement five new Num methods (ceil, cos, sin, sqrt, isNan) in wren_core.c and register them on the Num class. Add corresponding test files for ceil, floor, is_nan, and sqrt. Extend divide.wren with division-by-zero edge cases (inf, -inf, nan). Remove stale TODO comments from minus, multiply, and plus tests.
Replace single-byte jump offset emission and reading with two-byte (uint16) handling across all jump instructions (JUMP, LOOP, JUMP_IF, AND, OR). Add new emitJump helper that reserves a 16-bit placeholder, update patchJump to write the offset as big-endian short, and change VM opcode handlers to use READ_SHORT instead of READ_BYTE for jump offsets. This increases maximum jump distance from 255 to 65535 bytes, supporting larger generated bytecode.
Implement the `floor` native method on the `Num` class in `wren_core.c`, which delegates to C's `floor()` function to round a number down to the nearest integer value. Register the new method in `wrenInitializeCore` alongside the existing `abs` method.
Replace the single `loopBody` index with a `Loop` struct that tracks scope depth, enabling the compiler to emit correct scope-exit code when a `break` statement is encountered inside nested blocks. Also add `WREN_DUMP_COMPILED_CODE` debug flag and new test cases for closures capturing loop variables after break, return inside loops, and nested loop scoping.
The previous buffer size of 21 bytes was insufficient to hold the longest possible
double representation formatted with "%.14g", which can reach 24 bytes including
the null terminator. Updated the buffer to 24 bytes and extracted the numeric
value into a local variable for clarity.
Rename the IO.write method to IO.print, which now prints without a trailing newline, and add a new IO.print method that appends a newline after output. Update all benchmark, example, and test files to use IO.print instead of IO.write. Additionally, implement Unicode escape sequence parsing in the compiler's string tokenizer, supporting \uXXXX and \u{...} syntax with proper UTF-8 encoding for code points up to U+10FFFF.
Replace the fixed-size MAX_SYMBOLS array with a dynamically growing StringBuffer for symbol names, update all instruction encodings to use 16-bit operands for constants and method symbols, and refactor method binding to use a dynamic MethodBuffer per class. Remove the constant deduplication logic in addConstant and the commented-out struct fields in initCompiler.
Introduce a new `WREN_TRACE_GC` compile-time flag in `wren_common.h` to enable
dedicated GC tracing independently from general memory tracing. When either
`WREN_TRACE_MEMORY` or `WREN_TRACE_GC` is set, include `<time.h>` and modify
`collectGarbage()` to measure elapsed wall-clock time using `clock()`. The GC
trace now reports bytes before and after collection, bytes collected, the next
GC threshold, and the duration in seconds. Also move the `-- gc --` header
print and the `nextGC` calculation inside the tracing guard to avoid
side-effects when tracing is disabled.
Remove the deprecated validateIndexOld function and migrate all callers to the new validateIndex that reports runtime errors. Add validateString helper for string argument checking. Introduce comprehensive runtime error tests for list and string subscript operations, removeAt, and contains methods, replacing old silent null returns with proper error halting.
Replace the old validateIndex function with validateNum and validateInt helpers that produce descriptive runtime error messages. Add new test files covering invalid index types, out-of-bounds indices, and iterator validation for list insert, iterate, and iteratorValue methods. Rename existing number operator error tests to use consistent "must be a number" phrasing.
Move bytecode and debug source line buffers out of Compiler struct to
simplify initialization, and add inline comments for source path, source
code, constants, locals, upvalues, and fields to improve readability of
the compiler's internal state layout.
Remove the single-purpose Buffer struct from wren_compiler.c and replace it with type-specific ByteBuffer and IntBuffer types generated via a DECLARE_BUFFER macro in wren_utils.h, with shared buffer logic implemented in wren_utils.c using a void* Buffer helper struct. This enables a growable line-number buffer for debug source lines.
Move SymbolTable struct and its associated functions (initSymbolTable, clearSymbolTable, addSymbol, findSymbol, ensureSymbol, getSymbolName) from wren_vm.c and wren_value.h into new wren_utils.c and wren_utils.h files. Rename all functions with wrenSymbolTable prefix for consistent naming convention, update all call sites across compiler, core, debug, and VM files, and relocate MAX_SYMBOLS constant to wren_common.h.
Consolidate memory management by relocating the static freeObj function into wren_value.c as the public wrenFreeObj API, keeping allocation and deallocation co-located for better code organization.
Introduce primitive error signaling, runtime error handling with callstack printing, and fiber termination on error. Add source file path and method name tracking to functions, along with debug line number information. Update arithmetic operators to error on non-numeric operands and implement proper "method not found" runtime errors. Refactor the test runner to expect runtime errors with exit code 70 (EX_SOFTWARE) and update the C API to accept source paths in wrenInterpret and wrenCompile. Rename WrenNativeMethodFn to WrenForeignMethodFn and adjust related typedefs. Add debug source line arrays to compilers and functions, and replace the old debug dump functions with new print-based variants that display line numbers.
In `wren_core.c`, `defineClass` and `wrenInitializeCore` now call `addSymbol` before allocating new class objects (`wrenNewClass`, `wrenNewSingleClass`), ensuring unpinned objects are not live when a GC-triggering symbol insertion occurs. In `wren_compiler.c`, `freeBuffer` now passes zero capacity to `wrenReallocate` and reuses `initBuffer` instead of manually zeroing fields, fixing a potential double-free or stale pointer on buffer reuse.
Add support for empty block bodies in the Wren compiler by early-returning
from `finishBlock` when a right brace is immediately matched. This permits
syntactically valid constructs like `{}`, `if (true) {}`, and empty function
or method bodies.
Fix a copy-paste error in `wrenGetArgumentDouble` where the assertion
checked `nativeCallSlot` and `nativeCallNumArgs` instead of the correct
`foreignCallSlot` and `foreignCallNumArgs` fields.
Add test cases covering standalone empty blocks, empty blocks in if/else
statements, and empty function and method bodies returning null.
Rename the fields `nativeCallSlot` and `nativeCallNumArgs` to `foreignCallSlot` and
`foreignCallNumArgs` across `wren_vm.c` and `wren_vm.h` to accurately reflect that
these fields are used exclusively during foreign function calls, not native method
invocations. Update all references in `callForeign`, `wrenGetArgumentDouble`, and
`wrenReturnDouble` accordingly.
Introduce the `WrenNativeMethodFn` callback type and `METHOD_FOREIGN` enum value to allow host applications to define C-implemented methods on Wren classes. Add `wrenDefineMethod`, `wrenGetArgumentDouble`, and `wrenReturnDouble` API functions, along with `nativeCallSlot` and `nativeCallNumArgs` VM fields to manage foreign call state. Implement `callForeign` in the interpreter loop to invoke native methods and handle stack cleanup. Move `MAX_PARAMETERS`, `MAX_METHOD_NAME`, and `MAX_METHOD_SIGNATURE` constants from `wren_compiler.c` to `wren_common.h` for shared use across the VM.
Pass WrenVM pointer to addSymbol, ensureSymbol, and clearSymbolTable to use
wrenReallocate for memory management instead of raw free/malloc, and remove
truncateSymbolTable and MAX_STRING constant as part of the cleanup.
Replace the static MAX_STRING-sized char array and fixed bytecode array in the compiler with a dynamic Buffer type that supports reallocation via wrenReallocate. Add initBuffer, ensureBufferCapacity, writeBuffer, and freeBuffer helpers to manage memory. This allows the compiler to handle arbitrarily long string literals and large function bodies without hitting hard-coded limits. Add test/long_function.wren (1013 lines) and test/long_string.wren to verify the fix.
Refactor ObjFn creation in endCompiler to pass all data (constants, bytecode, upvalues) directly to wrenNewFunction, eliminating the temporary post-construction assignment loop and memcpy. Update wrenNewFunction signature to accept these parameters, copy constants into a new owned array, and take ownership of the bytecode buffer. Adjust ObjFn struct to use dynamic bytecode pointer and bytecodeLength field instead of fixed-size array. Update memory tracking in markFn and cleanup in freeObj to handle the new dynamic bytecode allocation.
Restructure Compiler to use dynamic bytecode and constant buffers instead of
relying on ObjFn's fixed-size arrays. Move ObjFn creation to the end of
compilation, add wrenListAdd/Insert/RemoveAt helpers to wren_value, relocate
list capacity macros and ensureListCapacity into wren_value.c, and store a
Compiler pointer in WrenVM for GC safety during compilation.
Migrate bytecode pointer types from `unsigned char*` to `uint8_t*` in `wren_debug.c`, `wren_value.h`, and `wren_vm.c` for consistent fixed-width integer usage. Also expand `ObjFn` bytecode array from 1024 to 2048 elements and update corresponding memory allocation in `markFn` to reflect the doubled capacity.
Remove the dynamically allocated `bytecode` pointer from `ObjFn` and replace it with a fixed-size `unsigned char bytecode[1024]` array embedded directly in the struct. This eliminates the need for a separate `allocate` call in `wrenNewFunction`, the associated `deallocate` in `freeObj`, and the pointer indirection, simplifying memory management and improving cache locality for function bytecode access.
Replace the `fn` field in `CallFrame` and `Method` structs from `Value` to `Obj*`, since it always holds an ObjFn or ObjClosure. Update all callers to use `AS_OBJ` and `markObj` instead of `markValue`. Fix a bug in `freeObj` where `OBJ_CLOSURE` case incorrectly freed the upvalues flexible array instead of falling through to the no-op case.
Implement `..` and `...` infix operators on Num returning Range objects that support the iterator protocol, enabling concise numeric for loops like `for (i in 1..10)`. Add Range class with min/max properties and iterate/iteratorValue methods. Introduce TOKEN_DOTDOT and TOKEN_DOTDOTDOT lexer tokens with PREC_RANGE precedence in the compiler. Unroll method_call benchmark loops across all languages to stress dynamic dispatch instead of loop overhead, reducing iteration count from 1M to 100K with 10x unrolling. Update Wren benchmarks (binary_trees, fib, for) to use new range syntax. Add benchmark/README.md documenting each benchmark's purpose.
Remove the large switch statement that manually handled instruction argument counts for each opcode in wrenBindMethod(). Instead, use the existing getNumArguments() helper function to determine how many bytes each instruction consumes, reducing code duplication and making the method more maintainable when new instructions are added.
Add `for (variable in sequence)` syntax to the Wren language, introducing the `in` keyword token and compiler support for iterating over sequences using `iterate` and `iteratorValue` methods. Includes new benchmark files for Lua, Python, Ruby, and Wren, plus test cases covering single-expression bodies, block bodies, closures over loop variables, and break statements. Also fixes a missing POP() in CLOSE_UPVALUE and moves the while test file to a subdirectory.
Replace the block() function's expression-only body with a call to definition(), enabling flow control statements like `if (foo) return bar` and `if (i > 2) break` without requiring curly braces. Add new test files for return statements after if, else, while, and inside functions/methods, and update existing break tests to use the simplified syntax.
Add TOKEN_BREAK keyword recognition and a loopBody field to the compiler
to track the innermost loop's first instruction index. The break
statement emits a jump to the end of the current loop, and compile-time
errors are reported when break appears outside a loop body, inside a
function defined within a loop, or inside a method defined within a
loop. New test files verify correct behavior for break in while loops,
nested loops, and error cases for invalid break usage.
- Remove two TODO comments in wren_compiler.c about parameter length overflow check and assignment in named call
- Remove TODO about interning bool_toString and null_toString strings in wren_core.c
- Replace oversized 100-char temp buffer with precise 21-char buffer in num_toString, referencing Lua implementation
- Rename io_write native to io_writeString and remove TODO about calling toString on argument
Add a dedicated lexError function that marks compilation as failed and formats
error messages consistently with a trailing newline. Move line counting from
readRawToken into nextChar so that newlines are tracked during all lexer phases,
including block comment scanning. Replace the TODO placeholder in
skipBlockComment with a proper lexError call for unterminated block comments,
and add test cases for unterminated block and nested block comments.
Remove the CODE_DUP opcode from the compiler, debug printer, VM dispatch table, and enum definition. Also simplify several case blocks in the computed-goto interpreter by removing unnecessary braces and consolidating local variable declarations, and add a critical ordering comment to the dispatch table.
Introduce CODE_LOAD_FIELD_THIS and CODE_STORE_FIELD_THIS opcodes that bypass receiver stack operations when field access occurs directly within a method body. Refactor field() compiler to emit these faster instructions when compiler->methodName is set, and extract loadThis() helper to correctly resolve 'this' in nested functions. Update VM interpreter with dedicated dispatch cases that read receiver from frame->stackStart instead of popping, and add debug disassembly support. Add test cases for field closure capture and nested class field isolation, removing related TODO comments.