Repository navigation
Conversation
jow-
force-pushed
the
feat/syntax-destruct-assignment
branch
from
October 19, 2026 16:03
5883414 to
24273c2
Compare
jow-
force-pushed
the
feat/build-flags
branch
from
October 19, 2026 16:27
7aa19e5 to
0dc287e
Compare
jow-
force-pushed
the
feat/syntax-destruct-assignment
branch
2 times, most recently
from
October 23, 2026 21:09
2619713 to
76e6408
Compare
jow-
force-pushed
the
feat/syntax-destruct-assignment
branch
from
October 23, 2026 22:19
76e6408 to
5dd1957
Compare
jow-
force-pushed
the
feat/stcall-typeof-throw
branch
from
October 23, 2026 22:35
1980f8c to
3b87b29
Compare
jow-
force-pushed
the
feat/syntax-destruct-assignment
branch
from
October 23, 2026 22:35
5dd1957 to
693e679
Compare
jow-
force-pushed
the
feat/stcall-typeof-throw
branch
from
October 23, 2026 22:37
3b87b29 to
a357bc0
Compare
jow-
force-pushed
the
feat/syntax-destruct-assignment
branch
from
October 23, 2026 22:37
693e679 to
6b77e1f
Compare
Add uc_lexer_skip_balanced() which skips over the text up to the bracket closing the one that ends at a given source offset, and tells whether `=` or `=>` follows it. It also reports which of the enclosed brackets are followed by one of them, so that callers can avoid skipping the same text repeatedly for nested expressions. The procedure looks at the raw text and only recognizes what can hide brackets: strings, template literals including their placeholders, regular expressions and comments. Whether a slash starts a regular expression or is a division is guessed from the preceding character. Lexer and parser state are not affected, which allows deciding about the nature of an expression like a destructuring pattern or the parameter list of an arrow function ahead of compiling it, without having to backtrack. To support this, sources gain uc_source_read_at() to look at text at arbitrary offsets using a small read-ahead cache, and uc_source_resync() to continue sequential reading afterwards. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Support array and object destructuring in `let` and `const`
declarations and in assignment expressions, including nested patterns,
default values, holes, computed keys, member targets and rest elements:
let [a, {b, c: [d = 1]}, ...rest] = src;
const {x, y: z, w = 5, ...others} = obj;
[arr[i], o.k, ...o.tail] = src;
({a, [key]: b = 3, ...others} = obj);
Assignments evaluate to the assigned value and may appear wherever an
assignment expression can, e.g. `x = [a, b] = y` or as call argument.
Defaults apply for null values. The initializer is evaluated first,
followed by the targets and the loaded values of each item in turn.
An array or object literal that starts an assignment expression is a
pattern if the matching closing bracket is followed by `=`, which is
determined using uc_lexer_skip_balanced() without compiling anything
(literals enclosed in already examined ones reuse that result). Patterns
of declarations are known by the keyword and do not need this.
The initializer of a pattern follows the pattern in the source text but
has to be evaluated first. To generate code in source order nevertheless,
the code of the pattern is emitted ahead of the code of the initializer
and jumps are used to run them in the right order:
JMP init
pat: <pattern>
JMP end
init: <initializer>
JMP pat
end:
Nested patterns emit their default value behind their code in the same
fashion. As a consequence, declared variables are inaccessible while
the initializer and the defaults of nested patterns are compiled, which
yields the usual temporal dead zone errors, source positions stay
monotonic and runtime errors point into the pattern. Syntax errors in
patterns are reported at the offending target.
Rest elements need no instruction of their own: they copy the container
with a literal spread and take the items already bound off the copy, with
splice() for an array and rawdelete() for an object, both called
internally, see I_STCALL. The copy is created below the container so that
the merge consumes the loaded value and the stack stays as balanced as it
does for an ordinary item, and the merge is what reports an unsuitable
container, the way a literal spread does.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
jow-
force-pushed
the
feat/syntax-destruct-assignment
branch
from
October 23, 2026 22:52
6b77e1f to
e3e6656
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Support array and object destructuring in
let/constdeclarations and in assignment expressions, including nested patterns, default values, holes, computed keys, member targets and rest elements:Assignments evaluate to the assigned value and may appear wherever an assignment expression can. The initializer is evaluated first, then the targets and the loaded values of each item in turn; declared variables are inaccessible while the initializer and defaults are compiled, yielding the usual temporal dead zone errors.
An array/object literal that starts an assignment expression is a pattern if the matching closing bracket is followed by
=. This is determined by the newuc_lexer_skip_balanced(), which skips raw text up to the closing bracket (recognizing only strings, template literals, regexps and comments), without compiling anything or affecting lexer/parser state — so the nature of the expression is known ahead of compiling it, without backtracking. Sources gainuc_source_read_at()(read-ahead peek at arbitrary offsets) anduc_source_resync()(resume sequential reading).To generate code in source order despite the initializer-evaluates-first semantics, pattern code is emitted ahead of initializer code with jumps to run them in the right order; nested pattern defaults use the same scheme.
Rest elements reuse existing machinery instead of adding instructions: the container is copied with
I_NARR/I_NOBJ, merged withI_MARR/I_MOBJexactly like a literal spread (so an unsuitable container is reported the same way), and the already-bound items are taken off the copy with internalsplice()/rawdelete()calls emitted asI_STCALL. Both work on plain copies, so no metamethods are dispatched. No new VM instruction is introduced.Stacked on #509 (
vm: invoke standard library functions internally, add typeof and throw), which providesI_STCALL; merge that first.