Merge branch 'array-destructuring' into bang

This commit is contained in:
Chris Wanstrath 2025-10-29 19:19:31 -07:00
commit bc62987abb
9 changed files with 123 additions and 21 deletions

View File

@ -144,19 +144,33 @@ async function repl() {
return return
} }
codeHistory.push(trimmed)
try { try {
const compiler = new Compiler(trimmed, Object.keys(globals)) const compiler = new Compiler(trimmed, Object.keys(globals))
vm.appendBytecode(compiler.bytecode) // Save VM state before appending bytecode, in case execution fails
const savedInstructions = [...vm.instructions]
const savedConstants = [...vm.constants]
const savedPc = vm.pc
const savedScope = vm.scope
const savedStopped = vm.stopped
const result = await vm.continue() try {
vm.appendBytecode(compiler.bytecode)
const result = await vm.continue()
console.log(`${colors.dim}=>${colors.reset} ${formatValue(result)}`) codeHistory.push(trimmed)
console.log(`${colors.dim}=>${colors.reset} ${formatValue(result)}`)
} catch (error: any) {
vm.instructions = savedInstructions
vm.constants = savedConstants
vm.pc = savedPc
vm.scope = savedScope
vm.stopped = savedStopped
console.log(`\n${colors.red}Error:${colors.reset} ${error.message}`)
}
} catch (error: any) { } catch (error: any) {
console.log(`\n${colors.red}Error:${colors.reset} ${error.message}`) console.log(`\n${colors.red}Error:${colors.reset} ${error.message}`)
codeHistory.pop()
} }
rl.prompt() rl.prompt()

View File

@ -62,7 +62,7 @@
"hono": ["hono@4.9.8", "", {}, "sha512-JW8Bb4RFWD9iOKxg5PbUarBYGM99IcxFl2FPBo2gSJO11jjUDqlP1Bmfyqt8Z/dGhIQ63PMA9LdcLefXyIasyg=="], "hono": ["hono@4.9.8", "", {}, "sha512-JW8Bb4RFWD9iOKxg5PbUarBYGM99IcxFl2FPBo2gSJO11jjUDqlP1Bmfyqt8Z/dGhIQ63PMA9LdcLefXyIasyg=="],
"reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#9618dd64148ccf6f0cdfd8a80a0f58efe3e0819d", { "peerDependencies": { "typescript": "^5" } }, "9618dd64148ccf6f0cdfd8a80a0f58efe3e0819d"], "reefvm": ["reefvm@git+https://git.nose.space/defunkt/reefvm#c69b172c78853756ec8acba5bc33d93eb6a571c6", { "peerDependencies": { "typescript": "^5" } }, "c69b172c78853756ec8acba5bc33d93eb6a571c6"],
"style-mod": ["style-mod@4.1.2", "", {}, "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="], "style-mod": ["style-mod@4.1.2", "", {}, "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="],

View File

@ -7,7 +7,7 @@
"dev": "bun generate-parser && bun --hot src/server/server.tsx", "dev": "bun generate-parser && bun --hot src/server/server.tsx",
"generate-parser": "lezer-generator src/parser/shrimp.grammar --typeScript -o src/parser/shrimp.ts", "generate-parser": "lezer-generator src/parser/shrimp.grammar --typeScript -o src/parser/shrimp.ts",
"repl": "bun generate-parser && bun bin/repl", "repl": "bun generate-parser && bun bin/repl",
"update-reef": "cd packages/ReefVM && git pull origin main" "update-reef": "rm -rf ~/.bun/install/cache/ && bun update reefvm"
}, },
"dependencies": { "dependencies": {
"@codemirror/view": "^6.38.3", "@codemirror/view": "^6.38.3",

View File

@ -237,11 +237,31 @@ export class Compiler {
} }
case terms.Assign: { case terms.Assign: {
const { identifier, right } = getAssignmentParts(node) const assignParts = getAssignmentParts(node)
const instructions: ProgramItem[] = [] const instructions: ProgramItem[] = []
instructions.push(...this.#compileNode(right, input))
instructions.push(['DUP']) // Keep a copy on the stack after storing // right-hand side
const identifierName = input.slice(identifier.from, identifier.to) instructions.push(...this.#compileNode(assignParts.right, input))
// array destructuring: [ a b ] = [ 1 2 3 4 ]
if ('arrayPattern' in assignParts) {
const identifiers = assignParts.arrayPattern ?? []
if (identifiers.length === 0) return instructions
for (let i = 0; i < identifiers.length; i++) {
instructions.push(['DUP'])
instructions.push(['PUSH', i])
instructions.push(['DOT_GET'])
instructions.push(['STORE', input.slice(identifiers[i]!.from, identifiers[i]!.to)])
}
// original array still on stack as the return value
return instructions
}
// simple assignment: x = value
instructions.push(['DUP'])
const identifierName = input.slice(assignParts.identifier.from, assignParts.identifier.to)
instructions.push(['STORE', identifierName]) instructions.push(['STORE', identifierName])
return instructions return instructions

View File

@ -64,6 +64,28 @@ describe('compiler', () => {
expect('sum = 2 + 3; sum').toEvaluateTo(5) expect('sum = 2 + 3; sum').toEvaluateTo(5)
}) })
test('array destructuring with two variables', () => {
expect('[ a b ] = [ 1 2 3 4 ]; a').toEvaluateTo(1)
expect('[ a b ] = [ 1 2 3 4 ]; b').toEvaluateTo(2)
})
test('array destructuring with one variable', () => {
expect('[ x ] = [ 42 ]; x').toEvaluateTo(42)
})
test('array destructuring with missing elements assigns null', () => {
expect('[ a b c ] = [ 1 2 ]; c').toEvaluateTo(null)
})
test('array destructuring returns the original array', () => {
expect('[ a b ] = [ 1 2 3 4 ]').toEvaluateTo([1, 2, 3, 4])
})
test('array destructuring with emoji identifiers', () => {
expect('[ 🚀 💎 ] = [ 1 2 ]; 🚀').toEvaluateTo(1)
expect('[ 🚀 💎 ] = [ 1 2 ]; 💎').toEvaluateTo(2)
})
test('parentheses', () => { test('parentheses', () => {
expect('(2 + 3) * 4').toEvaluateTo(20) expect('(2 + 3) * 4').toEvaluateTo(20)
}) })

View File

@ -40,15 +40,23 @@ export const getAssignmentParts = (node: SyntaxNode) => {
const children = getAllChildren(node) const children = getAllChildren(node)
const [left, equals, right] = children const [left, equals, right] = children
if (!left || left.type.id !== terms.AssignableIdentifier) { if (!equals || !right) {
throw new CompilerError( throw new CompilerError(
`Assign left child must be an AssignableIdentifier, got ${left ? left.type.name : 'none'}`, `Assign expected 3 children, got ${children.length}`,
node.from, node.from,
node.to node.to
) )
} else if (!equals || !right) { }
// array destructuring
if (left && left.type.id === terms.Array) {
const identifiers = getAllChildren(left).filter(child => child.type.id === terms.Identifier)
return { arrayPattern: identifiers, right }
}
if (!left || left.type.id !== terms.AssignableIdentifier) {
throw new CompilerError( throw new CompilerError(
`Assign expected 3 children, got ${children.length}`, `Assign left child must be an AssignableIdentifier or Array, got ${left ? left.type.name : 'none'}`,
node.from, node.from,
node.to node.to
) )

View File

@ -178,7 +178,7 @@ Params {
} }
Assign { Assign {
AssignableIdentifier Eq consumeToTerminator (AssignableIdentifier | Array) Eq consumeToTerminator
} }
BinOp { BinOp {

View File

@ -7,9 +7,9 @@ import {highlighting} from "./highlight"
const spec_Identifier = {__proto__:null,catch:82, finally:88, end:90, null:96, try:106, throw:110, if:114, elseif:122, else:126} const spec_Identifier = {__proto__:null,catch:82, finally:88, end:90, null:96, try:106, throw:110, if:114, elseif:122, else:126}
export const parser = LRParser.deserialize({ export const parser = LRParser.deserialize({
version: 14, version: 14,
states: "8xQYQbOOO#tQcO'#CvO$qOSO'#CxO%PQbO'#E`OOQ`'#DR'#DROOQa'#DO'#DOO&SQbO'#D]O'XQcO'#ETOOQa'#ET'#ETO)cQcO'#ESO)vQRO'#CwO+SQcO'#EOO+dQcO'#EOO+nQbO'#CuO,fOpO'#CsOOQ`'#EP'#EPO,kQbO'#EOO,rQQO'#EfOOQ`'#Db'#DbO,wQbO'#DdO,wQbO'#EhOOQ`'#Df'#DfO-lQRO'#DnOOQ`'#EO'#EOO-qQQO'#D}OOQ`'#D}'#D}OOQ`'#Do'#DoQYQbOOO-yQbO'#DPOOQa'#ES'#ESOOQ`'#D`'#D`OOQ`'#Ee'#EeOOQ`'#Dv'#DvO.TQbO,59^O.nQbO'#CzO.vQWO'#C{OOOO'#EV'#EVOOOO'#Dp'#DpO/[OSO,59dOOQa,59d,59dOOQ`'#Dr'#DrO/jQbO'#DSO/rQQO,5:zOOQ`'#Dq'#DqO/wQbO,59wO0OQQO,59jOOQa,59w,59wO0ZQbO,59wO,wQbO,59cO,wQbO,59cO,wQbO,59cO,wQbO,59yO,wQbO,59yO,wQbO,59yO0eQRO,59aO0lQRO,59aO0}QRO,59aO0xQQO,59aO1YQQO,59aO1bObO,59_O1mQbO'#DwO1xQbO,59]O2aQbO,5;QO2tQcO,5:OO3jQcO,5:OO3zQcO,5:OO4pQRO,5;SO4wQRO,5;SO5SQbO,5:YOOQ`,5:i,5:iOOQ`-E7m-E7mOOQ`,59k,59kOOQ`-E7t-E7tOOOO,59f,59fOOOO,59g,59gOOOO-E7n-E7nOOQa1G/O1G/OOOQ`-E7p-E7pO5dQbO1G0fOOQ`-E7o-E7oO5wQQO1G/UOOQa1G/c1G/cO6SQbO1G/cOOQO'#Dt'#DtO5wQQO1G/UOOQa1G/U1G/UOOQ`'#Du'#DuO6SQbO1G/cOOQa1G.}1G.}O6{QcO1G.}O7VQcO1G.}O7aQcO1G.}OOQa1G/e1G/eO9PQcO1G/eO9WQcO1G/eO9_QcO1G/eOOQa1G.{1G.{OOQa1G.y1G.yO!aQbO'#CvO&ZQbO'#CrOOQ`,5:c,5:cOOQ`-E7u-E7uO9fQbO1G0lO9qQbO1G0mO:_QbO1G0nOOQ`1G/t1G/tO:rQbO7+&QO9qQbO7+&SO:}QQO7+$pOOQa7+$p7+$pO;YQbO7+$}OOQa7+$}7+$}OOQO-E7r-E7rOOQ`-E7s-E7sO;dQbO'#DUO;iQQO'#DXOOQ`7+&W7+&WO;nQbO7+&WO;sQbO7+&WOOQ`'#Ds'#DsO;{QQO'#DsO<QQbO'#EaOOQ`'#DW'#DWO<tQbO7+&XOOQ`'#Dh'#DhO=PQbO7+&YO=UQbO7+&ZOOQ`<<Il<<IlO=rQbO<<IlO=wQbO<<IlO>PQbO<<InOOQa<<H[<<H[OOQa<<Hi<<HiO>[QQO,59pO>aQbO,59sOOQ`<<Ir<<IrO>tQbO<<IrOOQ`,5:_,5:_OOQ`-E7q-E7qOOQ`<<Is<<IsO>yQbO<<IsO?OQbO<<IsOOQ`<<It<<ItOOQ`'#Di'#DiO?WQbO<<IuOOQ`AN?WAN?WO?cQbOAN?WOOQ`AN?YAN?YO?hQbOAN?YO?mQbOAN?YO?uQbO1G/[O@YQbO1G/_OOQ`1G/_1G/_OOQ`AN?^AN?^OOQ`AN?_AN?_O@pQbOAN?_O,wQbO'#DjOOQ`'#Dx'#DxO@uQbOAN?aOAQQQO'#DlOOQ`AN?aAN?aOAVQbOAN?aOOQ`G24rG24rOOQ`G24tG24tOA[QbOG24tOAaQbO7+$vOOQ`7+$v7+$vOOQ`7+$y7+$yOOQ`G24yG24yOAzQRO,5:UOBRQRO,5:UOOQ`-E7v-E7vOOQ`G24{G24{OB^QbOG24{OBcQQO,5:WOOQ`LD*`LD*`OOQ`<<Hb<<HbOBhQQO1G/pOOQ`LD*gLD*gO@YQbO1G/rO=UQbO7+%[OOQ`7+%^7+%^OOQ`<<Hv<<Hv", states: "9OQYQbOOO#tQcO'#CvO$qOSO'#CxO%PQbO'#E`OOQ`'#DR'#DROOQa'#DO'#DOO&SQbO'#D]O'eQcO'#ETOOQa'#ET'#ETO(hQcO'#ETO)jQcO'#ESO)}QRO'#CwO+ZQcO'#EOO+kQcO'#EOO+uQbO'#CuO,mOpO'#CsOOQ`'#EP'#EPO,rQbO'#EOO,yQQO'#EfOOQ`'#Db'#DbO-OQbO'#DdO-OQbO'#EhOOQ`'#Df'#DfO-sQRO'#DnOOQ`'#EO'#EOO-xQQO'#D}OOQ`'#D}'#D}OOQ`'#Do'#DoQYQbOOO.QQbO'#DPOOQa'#ES'#ESOOQ`'#D`'#D`OOQ`'#Ee'#EeOOQ`'#Dv'#DvO.[QbO,59^O/OQbO'#CzO/WQWO'#C{OOOO'#EV'#EVOOOO'#Dp'#DpO/lOSO,59dOOQa,59d,59dOOQ`'#Dr'#DrO/zQbO'#DSO0SQQO,5:zOOQ`'#Dq'#DqO0XQbO,59wO0`QQO,59jOOQa,59w,59wO0kQbO,59wO0uQbO,5:YO-OQbO,59cO-OQbO,59cO-OQbO,59cO-OQbO,59yO-OQbO,59yO-OQbO,59yO1VQRO,59aO1^QRO,59aO1oQRO,59aO1jQQO,59aO1zQQO,59aO2SObO,59_O2_QbO'#DwO2jQbO,59]O3RQbO,5;QO3fQcO,5:OO4[QcO,5:OO4lQcO,5:OO5bQRO,5;SO5iQRO,5;SOOQ`,5:i,5:iOOQ`-E7m-E7mOOQ`,59k,59kOOQ`-E7t-E7tOOOO,59f,59fOOOO,59g,59gOOOO-E7n-E7nOOQa1G/O1G/OOOQ`-E7p-E7pO5tQbO1G0fOOQ`-E7o-E7oO6XQQO1G/UOOQa1G/c1G/cO6dQbO1G/cOOQO'#Dt'#DtO6XQQO1G/UOOQa1G/U1G/UOOQ`'#Du'#DuO6dQbO1G/cOOQ`1G/t1G/tOOQa1G.}1G.}O7]QcO1G.}O7gQcO1G.}O7qQcO1G.}OOQa1G/e1G/eO9aQcO1G/eO9hQcO1G/eO9oQcO1G/eOOQa1G.{1G.{OOQa1G.y1G.yO!aQbO'#CvO9vQbO'#CrOOQ`,5:c,5:cOOQ`-E7u-E7uO:TQbO1G0lO:`QbO1G0mO:|QbO1G0nO;aQbO7+&QO:`QbO7+&SO;lQQO7+$pOOQa7+$p7+$pO;wQbO7+$}OOQa7+$}7+$}OOQO-E7r-E7rOOQ`-E7s-E7sO<RQbO'#DUO<WQQO'#DXOOQ`7+&W7+&WO<]QbO7+&WO<bQbO7+&WOOQ`'#Ds'#DsO<jQQO'#DsO<oQbO'#EaOOQ`'#DW'#DWO=cQbO7+&XOOQ`'#Dh'#DhO=nQbO7+&YO=sQbO7+&ZOOQ`<<Il<<IlO>aQbO<<IlO>fQbO<<IlO>nQbO<<InOOQa<<H[<<H[OOQa<<Hi<<HiO>yQQO,59pO?OQbO,59sOOQ`<<Ir<<IrO?cQbO<<IrOOQ`,5:_,5:_OOQ`-E7q-E7qOOQ`<<Is<<IsO?hQbO<<IsO?mQbO<<IsOOQ`<<It<<ItOOQ`'#Di'#DiO?uQbO<<IuOOQ`AN?WAN?WO@QQbOAN?WOOQ`AN?YAN?YO@VQbOAN?YO@[QbOAN?YO@dQbO1G/[O@wQbO1G/_OOQ`1G/_1G/_OOQ`AN?^AN?^OOQ`AN?_AN?_OA_QbOAN?_O-OQbO'#DjOOQ`'#Dx'#DxOAdQbOAN?aOAoQQO'#DlOOQ`AN?aAN?aOAtQbOAN?aOOQ`G24rG24rOOQ`G24tG24tOAyQbOG24tOBOQbO7+$vOOQ`7+$v7+$vOOQ`7+$y7+$yOOQ`G24yG24yOBiQRO,5:UOBpQRO,5:UOOQ`-E7v-E7vOOQ`G24{G24{OB{QbOG24{OCQQQO,5:WOOQ`LD*`LD*`OOQ`<<Hb<<HbOCVQQO1G/pOOQ`LD*gLD*gO@wQbO1G/rO=sQbO7+%[OOQ`7+%^7+%^OOQ`<<Hv<<Hv",
stateData: "Bp~O!oOS!pOS~O_PO`fOaWOb^OcROhWOpWOqWO!QWO!VaO!XcO!ZdO!u]O!xQO#PTO#QUO#RiO~O_mOaWOb^OcROhWOpWOqWOtlO!OnO!QWO!u]O!xQO#PTO#QUO!TjX#RjX#^jX#WjXyjX|jX}jX~OP!vXQ!vXR!vXS!vXT!vXU!vXW!vXX!vXY!vXZ!vX[!vX]!vX^!vX~P!aOmtO!xwO!zrO!{sO~O_xOwvP~O_mOaWOb^OhWOpWOqWOtlO!QWO!u]O!xQO#PTO#QUO#R{O~O#V!OO~P%XO_mOaWOb^OcROhWOpWOqWOtlO!OnO!QWO!u]O!xQO#PTO#QUO~OP!wXQ!wXR!wXS!wXT!wXU!wXW!wXX!wXY!wXZ!wX[!wX]!wX^!wX#R!wX#^!wX#W!wXy!wX|!wX}!wX~P&ZOP!vXQ!vXR!vXS!vXT!vXU!vXW!vXX!vXY!vXZ!vX[!vX]!vX^!vX~O#R!rX#^!rXy!rX|!rX}!rX~P(hOP!ROQ!ROR!SOS!SOT!UOU!VOW!TOX!TOY!TOZ!TO[!TO]!TO^!QO~O#R!rX#^!rXy!rX|!rX}!rX~OP!ROQ!ROR!SOS!SO~P*qOT!UOU!VO~P*qO_POaWOb^OcROhWOpWOqWO!QWO!u]O!xQO#PTO#QUO~O!t!]O~O!T!^O~P*qOw!`O~O_mOaWOb^OhWOpWOqWO!QWO!u]O!xQO#PTO#QUO~OV!fO~O#R!gO#^!gO~OcRO!O!iO~P,wO!Tfa#Rfa#^fa#Wfayfa|fa}fa~P&ZO_!kO!u]O~O!x!lO!z!lO!{!lO!|!lO!}!lO#O!lO~OmtO!x!nO!zrO!{sO~O_xOwvX~Ow!pO~O#V!sO~P%XOtlO#R!uO#V!wO~O#R!xO#V!sO~P,wO#W#SO~P(hOP!ROQ!ROR!SOS!SO#W#SO~OT!UOU!VO#W#SO~O!T!^O#W#SO~O_#TOh#TO!u]O~O_#UOb^O!u]O~O!T!^O#Rea#^ea#Weayea|ea}ea~O`fO!VaO!XcO!ZdO#R#ZO~P+nO#R!Wa#^!Way!Wa|!Wa}!Wa~P)vO#R!Wa#^!Way!Wa|!Wa}!Wa~OP!ROQ!ROR!SOS!SO~P3XOT!UOU!VO~P3XOT!UOU!VOW!TOX!TOY!TOZ!TO[!TO]!TO~Ow#[O~P4UOT!UOU!VOw#[O~O`fO!VaO!XcO!ZdO~P+nO`fO!VaO!XcO!ZdO#R#_O~P+nOtlO#R!uO#V#aO~O#R!xO#V#cO~P,wO^!QORkiSki#Rki#^ki#Wkiyki|ki}ki~OPkiQki~P6^OP!ROQ!RO~P6^OP!ROQ!RORkiSki#Rki#^ki#Wkiyki|ki}ki~OW!TOX!TOY!TOZ!TO[!TO]!TOT!Ri#R!Ri#^!Ri#W!Riw!Riy!Ri|!Ri}!Ri~OU!VO~P8ROU!VO~P8eOU!Ri~P8ROy#fO|#gO}#hO~O`fO!VaO!XcO!ZdO#R#kOy#TP|#TP}#TP~P+nO`fO!VaO!XcO!ZdO#R#rO~P+nOy#fO|#gO}#sO~OtlO#R!uO#V#wO~O#R!xO#V#xO~P,wO_#yO~Ow#zO~O}#{O~O|#gO}#{O~O#R#}O~O`fO!VaO!XcO!ZdO#R#kOy#TX|#TX}#TX!_#TX!a#TX~P+nOy#fO|#gO}$PO~O}$SO~O`fO!VaO!XcO!ZdO#R#kO}#TP!_#TP!a#TP~P+nO}$VO~O|#gO}$VO~Oy#fO|#gO}$XO~Ow$[O~O`fO!VaO!XcO!ZdO#R$]O~P+nO}$_O~O}$`O~O|#gO}$`O~O}$fO!_$bO!a$eO~O}$hO~O}$iO~O|#gO}$iO~O`fO!VaO!XcO!ZdO#R$kO~P+nO`fO!VaO!XcO!ZdO#R#kO}#TP~P+nO}$nO~O}$rO!_$bO!a$eO~Ow$tO~O}$rO~O}$uO~O`fO!VaO!XcO!ZdO#R#kO|#TP}#TP~P+nOw$wO~P4UOT!UOU!VOw$wO~O}$xO~O#R$yO~O#R$zO~Ohq~", stateData: "C_~O!oOS!pOS~O_PO`gOaWOb_OcROhWOpWOqWO!QWO!VbO!XdO!ZeO!u^O!xQO#PTO#QUO#RjO~O_nOaWOb_OcROhWOpWOqWOtmO!OoO!QWO!u^O!xQO#PTO#QUO!TjX#RjX#^jX#WjXyjX|jX}jX~OP!vXQ!vXR!vXS!vXT!vXU!vXW!vXX!vXY!vXZ!vX[!vX]!vX^!vX~P!aOmuO!xxO!zsO!{tO~O_yOwvP~O_nOaWOb_OhWOpWOqWOtmO!QWO!u^O!xQO#PTO#QUO#R|O~O#V!PO~P%XOP!wXQ!wXR!wXS!wXT!wXU!wXW!wXX!wXY!wXZ!wX[!wX]!wX^!wX#R!wX#^!wXy!wX|!wX}!wX~O_nOaWOb_OcROhWOpWOqWOtmO!OoO!QWO!u^O!xQO#PTO#QUO#W!wX~P&ZOV!RO~P&ZOP!vXQ!vXR!vXS!vXT!vXU!vXW!vXX!vXY!vXZ!vX[!vX]!vX^!vX~O#R!rX#^!rXy!rX|!rX}!rX~P(oOP!TOQ!TOR!UOS!UOT!WOU!XOW!VOX!VOY!VOZ!VO[!VO]!VO^!SO~O#R!rX#^!rXy!rX|!rX}!rX~OP!TOQ!TOR!UOS!UO~P*xOT!WOU!XO~P*xO_POaWOb_OcROhWOpWOqWO!QWO!u^O!xQO#PTO#QUO~O!t!_O~O!T!`O~P*xOw!bO~O_nOaWOb_OhWOpWOqWO!QWO!u^O!xQO#PTO#QUO~OV!RO~O#R!hO#^!hO~OcRO!O!jO~P-OOcROtmO!OoO!Tfa#Rfa#^fa#Wfayfa|fa}fa~P-OO_!lO!u^O~O!x!mO!z!mO!{!mO!|!mO!}!mO#O!mO~OmuO!x!oO!zsO!{tO~O_yOwvX~Ow!qO~O#V!tO~P%XOtmO#R!vO#V!xO~O#R!yO#V!tO~P-OO`gO!VbO!XdO!ZeO~P+uO#W#UO~P(oOP!TOQ!TOR!UOS!UO#W#UO~OT!WOU!XO#W#UO~O!T!`O#W#UO~O_#VOh#VO!u^O~O_#WOb_O!u^O~O!T!`O#Rea#^ea#Weayea|ea}ea~O`gO!VbO!XdO!ZeO#R#]O~P+uO#R!Wa#^!Way!Wa|!Wa}!Wa~P)}O#R!Wa#^!Way!Wa|!Wa}!Wa~OP!TOQ!TOR!UOS!UO~P3yOT!WOU!XO~P3yOT!WOU!XOW!VOX!VOY!VOZ!VO[!VO]!VO~Ow#^O~P4vOT!WOU!XOw#^O~O`gO!VbO!XdO!ZeO#R#`O~P+uOtmO#R!vO#V#bO~O#R!yO#V#dO~P-OO^!SORkiSki#Rki#^ki#Wkiyki|ki}ki~OPkiQki~P6nOP!TOQ!TO~P6nOP!TOQ!TORkiSki#Rki#^ki#Wkiyki|ki}ki~OW!VOX!VOY!VOZ!VO[!VO]!VOT!Ri#R!Ri#^!Ri#W!Riw!Riy!Ri|!Ri}!Ri~OU!XO~P8cOU!XO~P8uOU!Ri~P8cOcROtmO!OoO~P-OOy#gO|#hO}#iO~O`gO!VbO!XdO!ZeO#R#lOy#TP|#TP}#TP~P+uO`gO!VbO!XdO!ZeO#R#sO~P+uOy#gO|#hO}#tO~OtmO#R!vO#V#xO~O#R!yO#V#yO~P-OO_#zO~Ow#{O~O}#|O~O|#hO}#|O~O#R$OO~O`gO!VbO!XdO!ZeO#R#lOy#TX|#TX}#TX!_#TX!a#TX~P+uOy#gO|#hO}$QO~O}$TO~O`gO!VbO!XdO!ZeO#R#lO}#TP!_#TP!a#TP~P+uO}$WO~O|#hO}$WO~Oy#gO|#hO}$YO~Ow$]O~O`gO!VbO!XdO!ZeO#R$^O~P+uO}$`O~O}$aO~O|#hO}$aO~O}$gO!_$cO!a$fO~O}$iO~O}$jO~O|#hO}$jO~O`gO!VbO!XdO!ZeO#R$lO~P+uO`gO!VbO!XdO!ZeO#R#lO}#TP~P+uO}$oO~O}$sO!_$cO!a$fO~Ow$uO~O}$sO~O}$vO~O`gO!VbO!XdO!ZeO#R#lO|#TP}#TP~P+uOw$xO~P4vOT!WOU!XOw$xO~O}$yO~O#R$zO~O#R${O~Ohq~",
goto: "3U#^PPPPPPPPPPPPPPPPPPPPP#_#t$YP%X#t&^&|P'v'vPP&|'zP(_)OP)RP)_)hPPP&|P*Q*vP*}P*}P*}P+a+d+mP+qP*}+w+},T,Z,a,m,w-R-[-cPPPP-i-m._PP.w0_P1]PPPPPPPP1a1z1aPP2X2`2`2r2rpgOk!`!f!p#Z#[#_#m#r#z$[$]$k$y$zR!Z]u_O]k!^!`!f!p#Z#[#_#m#r#z$[$]$k$y$zrPO]k!`!f!p#Z#[#_#m#r#z$[$]$k$y$zzmPUVcdlq|!P!Q!R!S!T!U!V!t!y#U#V#b$bR#U!^rVO]k!`!f!p#Z#[#_#m#r#z$[$]$k$y$zzWPUVcdlq|!P!Q!R!S!T!U!V!t!y#U#V#b$bQ!krQ#T!]R#V!^pZOk!`!f!p#Z#[#_#m#r#z$[$]$k$y$zQ!X]Q!bcQ!z!RR!}!S!oWOPUV]cdklq|!P!Q!R!S!T!U!V!`!f!p!t!y#U#V#Z#[#_#b#m#r#z$[$]$b$k$y$zTtQvYoPVq#U#VQ}UQ!r|X!u}!r!v#`pgOk!`!f!p#Z#[#_#m#r#z$[$]$k$y$zYnPVq#U#VQ!Z]R!ilRzRQ#j#YQ#u#^Q$R#oR$Z#vQ#o#ZQ$m$]R$v$kQ#i#YQ#t#^Q#|#jQ$Q#oQ$W#uQ$Y#vQ$a$RR$j$Zp[Ok!`!f!p#Z#[#_#m#r#z$[$]$k$y$zQ!Y]Q!ccQ!edQ#O!VQ#Q!UR$p$bZoPVq#U#VqgOk!`!f!p#Z#[#_#m#r#z$[$]$k$y$zR#q#[Q$U#rQ${$yR$|$zT$c$U$dQ$g$UR$s$dQkOR!hkQvQR!mvQ|UR!q|QyRR!oy^#m#Z#_#r$]$k$y$zR$O#mQ!v}Q#`!rT#d!v#`Q!y!PQ#b!tT#e!y#bWqPV#U#VR!jqS!_`![R#X!_Q$d$UR$q$dTjOkShOkQ#Y!`Q#]!fQ#^!p`#l#Z#_#m#r$]$k$y$zQ#p#[Q$^#zR$l$[p`Ok!`!f!p#Z#[#_#m#r#z$[$]$k$y$zQ![]R#W!^rYO]k!`!f!p#Z#[#_#m#r#z$[$]$k$y$zYnPVq#U#VQ!PUQ!acQ!ddQ!ilQ!t|W!x!P!t!y#bQ!z!QQ!{!RQ!|!SQ#O!TQ#P!UQ#R!VR$o$bpXOk!`!f!p#Z#[#_#m#r#z$[$]$k$y$zzmPUVcdlq|!P!Q!R!S!T!U!V!t!y#U#V#b$bR!W]TuQv!PSOPV]klq!`!f!p#U#V#Z#[#_#m#r#z$[$]$k$y$zU#n#Z$]$kQ#v#_V$T#r$y$zZpPVq#U#VqbOk!`!f!p#Z#[#_#m#r#z$[$]$k$y$zqeOk!`!f!p#Z#[#_#m#r#z$[$]$k$y$z", goto: "4Q#^PPPPPPPPPPPPPPPPPPPPP#_#t$YP%X#t&^&|P'v'vPP&|'zP(_)OP)RP)_)hPPP*QP*|+rP+yP+yP+yP,],`,iP,mP+y,s,y-P-V-]-i-s-}.W._PPPP.e.i/ZPP/s1ZP2XPPPPPPPP2]2v2]PP3T3[3[3n3nphOl!R!b!q#]#^#`#n#s#{$]$^$l$z${R!]^u`O^l!R!`!b!q#]#^#`#n#s#{$]$^$l$z${rPO^l!R!b!q#]#^#`#n#s#{$]$^$l$z${znPUVdemr}!Q!S!T!U!V!W!X!u!z#W#X#c$cR#W!`rVO^l!R!b!q#]#^#`#n#s#{$]$^$l$z${zWPUVdemr}!Q!S!T!U!V!W!X!u!z#W#X#c$cQ!lsQ#V!_R#X!`p[Ol!R!b!q#]#^#`#n#s#{$]$^$l$z${Q!Z^Q!ddQ!|!TR#P!U!oWOPUV^delmr}!Q!R!S!T!U!V!W!X!b!q!u!z#W#X#]#^#`#c#n#s#{$]$^$c$l$z${TuQwYpPVr#W#XQ!OUQ!s}X!v!O!s!w#aphOl!R!b!q#]#^#`#n#s#{$]$^$l$z${YoPVr#W#XQ!]^R!jmR{RQ#k#[Q#v#_Q$S#pR$[#wQ#p#]Q$n$^R$w$lQ#j#[Q#u#_Q#}#kQ$R#pQ$X#vQ$Z#wQ$b$SR$k$[|WPUV^demr}!Q!S!T!U!V!W!X!u!z#W#X#c$cqXOl!R!b!q#]#^#`#n#s#{$]$^$l$z${p]Ol!R!b!q#]#^#`#n#s#{$]$^$l$z${Q![^Q!edQ!geQ#Q!XQ#S!WR$q$cZpPVr#W#XqhOl!R!b!q#]#^#`#n#s#{$]$^$l$z${R#r#^Q$V#sQ$|$zR$}${T$d$V$eQ$h$VR$t$eQlOR!ilQwQR!nwQ}UR!r}QzRR!pz^#n#]#`#s$^$l$z${R$P#nQ!w!OQ#a!sT#e!w#aQ!z!QQ#c!uT#f!z#cWrPV#W#XR!krS!aa!^R#Z!aQ$e$VR$r$eTkOlSiOlQ!{!RQ#[!bQ#_!q`#m#]#`#n#s$^$l$z${Q#q#^Q$_#{R$m$]paOl!R!b!q#]#^#`#n#s#{$]$^$l$z${Q!^^R#Y!`rZO^l!R!b!q#]#^#`#n#s#{$]$^$l$z${YoPVr#W#XQ!QUQ!cdQ!feQ!jmQ!u}W!y!Q!u!z#cQ!|!SQ!}!TQ#O!UQ#Q!VQ#R!WQ#T!XR$p$cpYOl!R!b!q#]#^#`#n#s#{$]$^$l$z${znPUVdemr}!Q!S!T!U!V!W!X!u!z#W#X#c$cR!Y^TvQw!PSOPV^lmr!R!b!q#W#X#]#^#`#n#s#{$]$^$l$z${U#o#]$^$lQ#w#`V$U#s$z${ZqPVr#W#XqcOl!R!b!q#]#^#`#n#s#{$]$^$l$z${qfOl!R!b!q#]#^#`#n#s#{$]$^$l$z${",
nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo Identifier AssignableIdentifier Word IdentifierBeforeDot Do Program PipeExpr FunctionCall DotGet Number ParenExpr FunctionCallOrIdentifier BinOp String StringFragment Interpolation EscapeSeq Boolean Regex Dict NamedArg NamedArgPrefix FunctionDef Params colon CatchExpr keyword TryBlock FinallyExpr keyword keyword Underscore Array Null ConditionalOp PositionalArg operator TryExpr keyword Throw keyword IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword Assign", nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo Identifier AssignableIdentifier Word IdentifierBeforeDot Do Program PipeExpr FunctionCall DotGet Number ParenExpr FunctionCallOrIdentifier BinOp String StringFragment Interpolation EscapeSeq Boolean Regex Dict NamedArg NamedArgPrefix FunctionDef Params colon CatchExpr keyword TryBlock FinallyExpr keyword keyword Underscore Array Null ConditionalOp PositionalArg operator TryExpr keyword Throw keyword IfExpr keyword SingleLineThenBlock ThenBlock ElseIfExpr keyword ElseExpr keyword Assign",
maxTerm: 106, maxTerm: 106,
context: trackScope, context: trackScope,
@ -23,5 +23,5 @@ export const parser = LRParser.deserialize({
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!t~~", 11)], tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!t~~", 11)],
topRules: {"Program":[0,20]}, topRules: {"Program":[0,20]},
specialized: [{term: 15, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 15, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], specialized: [{term: 15, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 15, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
tokenPrec: 1547 tokenPrec: 1576
}) })

View File

@ -594,6 +594,44 @@ describe('Comments', () => {
}) })
}) })
describe('Array destructuring', () => {
test('parses array pattern with two variables', () => {
expect('[ a b ] = [ 1 2 3 4]').toMatchTree(`
Assign
Array
Identifier a
Identifier b
Eq =
Array
Number 1
Number 2
Number 3
Number 4`)
})
test('parses array pattern with one variable', () => {
expect('[ x ] = [ 42 ]').toMatchTree(`
Assign
Array
Identifier x
Eq =
Array
Number 42`)
})
test('parses array pattern with emoji identifiers', () => {
expect('[ 🚀 💎 ] = [ 1 2 ]').toMatchTree(`
Assign
Array
Identifier 🚀
Identifier 💎
Eq =
Array
Number 1
Number 2`)
})
})
describe('Conditional ops', () => { describe('Conditional ops', () => {
test('or can be chained', () => { test('or can be chained', () => {
expect(` expect(`