Replace three identical error structs (ParserError, ScopeError,
ParsedError) with a single shared ParseError type. Since all three
had the same fields (message, line, column), having separate types
only added verbose field-by-field copying at each boundary.
Now errors flow directly from parser/scope collector into
ParsedProgram without conversion.
Move regex compilation out of the parsing hot path. Both the C++ and
Rust parsers now collect raw regex pattern+flags strings during parsing
and batch-compile them after parsing completes.
This is a prerequisite for moving the Rust parser to a background
thread, since LibRegex is thread-unsafe and FFI calls during parsing
prevent parallelization.
Flag validation remains in the parser since it's trivial string
checking with no LibRegex dependency.
Cache failed arrow function attempts by token offset. Once we
determine that '(' at offset N is not the start of an arrow
function, skip re-attempting at the same offset.
Without memoization, nested expressions like (a=(b=(c=(d=0))))
cause exponential work: each failed arrow attempt at an outer '('
re-parses all inner '(' positions during grouping expression
re-parse, and each inner position triggers its own arrow
attempts. With n nesting levels, the innermost position is
processed O(2^n) times.
The C++ parser already has this optimization (via the
try_parse_arrow_function_expression_failed_at_position()
memoization cache).
Implement a complete Rust reimplementation of the LibJS frontend:
lexer, parser, AST, scope collector, and bytecode code generator.
The Rust pipeline is built via Corrosion (CMake-Cargo bridge) and
linked into LibJS as a static library. It is gated behind a build
flag (ENABLE_RUST, on by default except on Windows) and two runtime
environment variables:
- LIBJS_CPP: Use the C++ pipeline instead of Rust
- LIBJS_COMPARE_PIPELINES=1: Run both pipelines in lockstep,
aborting on any difference in AST or bytecode generated.
The C++ side communicates with Rust through a C FFI layer
(RustIntegration.cpp/h) that passes source text to Rust and receives
a populated Executable back via a BytecodeFactory interface.