Compare commits

...

5 Commits

7 changed files with 140 additions and 37 deletions

View File

@ -238,11 +238,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,36 +64,26 @@ describe('compiler', () => {
expect('sum = 2 + 3; sum').toEvaluateTo(5) expect('sum = 2 + 3; sum').toEvaluateTo(5)
}) })
test('compound assignment +=', () => { test('array destructuring with two variables', () => {
expect('x = 10; x += 5; x').toEvaluateTo(15) expect('[ a b ] = [ 1 2 3 4 ]; a').toEvaluateTo(1)
expect('[ a b ] = [ 1 2 3 4 ]; b').toEvaluateTo(2)
}) })
test('compound assignment -= ', () => { test('array destructuring with one variable', () => {
expect('x = 10; x -= 3; x').toEvaluateTo(7) expect('[ x ] = [ 42 ]; x').toEvaluateTo(42)
}) })
test('compound assignment *=', () => { test('array destructuring with missing elements assigns null', () => {
expect('x = 5; x *= 3; x').toEvaluateTo(15) expect('[ a b c ] = [ 1 2 ]; c').toEvaluateTo(null)
}) })
test('compound assignment /=', () => { test('array destructuring returns the original array', () => {
expect('x = 20; x /= 4; x').toEvaluateTo(5) expect('[ a b ] = [ 1 2 3 4 ]').toEvaluateTo([1, 2, 3, 4])
}) })
test('compound assignment %=', () => { test('array destructuring with emoji identifiers', () => {
expect('x = 17; x %= 5; x').toEvaluateTo(2) expect('[ 🚀 💎 ] = [ 1 2 ]; 🚀').toEvaluateTo(1)
}) expect('[ 🚀 💎 ] = [ 1 2 ]; 💎').toEvaluateTo(2)
test('compound assignment with expression', () => {
expect('x = 10; x += 2 + 3; x').toEvaluateTo(15)
})
test('compound assignment returns value', () => {
expect('x = 5; x += 10; x').toEvaluateTo(15)
})
test('compound assignment fails on undefined variable', () => {
expect('undefined-var += 5').toFailEvaluation()
}) })
test('parentheses', () => { test('parentheses', () => {

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

@ -75,6 +75,12 @@ export const trackScope = new ContextTracker<TrackerContext>({
return new TrackerContext(context.scope, [...context.pendingIds, text]) return new TrackerContext(context.scope, [...context.pendingIds, text])
} }
// Track identifiers in array destructuring: [ a b ] = ...
if (!inParams && term === terms.Identifier && isArrayDestructuring(input)) {
const text = readIdentifierText(input, input.pos, stack.pos)
return new TrackerContext(Scope.add(context.scope, text), context.pendingIds)
}
return context return context
}, },
@ -98,3 +104,26 @@ export const trackScope = new ContextTracker<TrackerContext>({
hash: (context) => context.scope.hash(), hash: (context) => context.scope.hash(),
}) })
// Check if we're parsing array destructuring: [ a b ] = ...
const isArrayDestructuring = (input: InputStream): boolean => {
let pos = 0
// Find closing bracket
while (pos < 200 && input.peek(pos) !== 93 /* ] */) {
if (input.peek(pos) === -1) return false // EOF
pos++
}
if (input.peek(pos) !== 93 /* ] */) return false
pos++
// Skip whitespace
while (input.peek(pos) === 32 /* space */ ||
input.peek(pos) === 9 /* tab */ ||
input.peek(pos) === 10 /* \n */) {
pos++
}
return input.peek(pos) === 61 /* = */
}

View File

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

View File

@ -7,9 +7,9 @@ import {highlighting} from "./highlight"
const spec_Identifier = {__proto__:null,catch:92, finally:98, end:100, null:106, try:116, throw:120, if:124, elseif:132, else:136} const spec_Identifier = {__proto__:null,catch:92, finally:98, end:100, null:106, try:116, throw:120, if:124, elseif:132, else:136}
export const parser = LRParser.deserialize({ export const parser = LRParser.deserialize({
version: 14, version: 14,
states: "9UQYQbOOO#tQcO'#C{O$qOSO'#C}O%PQbO'#EfOOQ`'#DW'#DWOOQa'#DT'#DTO&SQbO'#DbO'XQcO'#EZOOQa'#EZ'#EZO)cQcO'#EYO)vQRO'#C|O+SQcO'#EUO+dQcO'#EUO+nQbO'#CzO,fOpO'#CxOOQ`'#EV'#EVO,kQbO'#EUO,rQQO'#ElOOQ`'#Dg'#DgO,wQbO'#DiO,wQbO'#EnOOQ`'#Dk'#DkO-lQRO'#DsOOQ`'#EU'#EUO.QQQO'#ETOOQ`'#ET'#ETOOQ`'#Du'#DuQYQbOOO.YQbO'#DUOOQa'#EY'#EYOOQ`'#De'#DeOOQ`'#Ek'#EkOOQ`'#D|'#D|O.dQbO,59cO.}QbO'#DPO/VQWO'#DQOOOO'#E]'#E]OOOO'#Dv'#DvO/kOSO,59iOOQa,59i,59iOOQ`'#Dx'#DxO/yQbO'#DXO0RQQO,5;QOOQ`'#Dw'#DwO0WQbO,59|O0_QQO,59oOOQa,59|,59|O0jQbO,59|O,wQbO,59hO,wQbO,59hO,wQbO,59hO,wQbO,5:OO,wQbO,5:OO,wQbO,5:OO0tQRO,59fO0{QRO,59fO1^QRO,59fO1XQQO,59fO1iQQO,59fO1qObO,59dO1|QbO'#D}O2XQbO,59bO2pQbO,5;WO3TQcO,5:TO3yQcO,5:TO4ZQcO,5:TO5PQRO,5;YO5WQRO,5;YO5cQbO,5:_O5cQbO,5:`OOQ`,5:o,5:oOOQ`-E7s-E7sOOQ`,59p,59pOOQ`-E7z-E7zOOOO,59k,59kOOOO,59l,59lOOOO-E7t-E7tOOQa1G/T1G/TOOQ`-E7v-E7vO5sQbO1G0lOOQ`-E7u-E7uO6WQQO1G/ZOOQa1G/h1G/hO6cQbO1G/hOOQO'#Dz'#DzO6WQQO1G/ZOOQa1G/Z1G/ZOOQ`'#D{'#D{O6cQbO1G/hOOQa1G/S1G/SO7[QcO1G/SO7fQcO1G/SO7pQcO1G/SOOQa1G/j1G/jO9`QcO1G/jO9gQcO1G/jO9nQcO1G/jOOQa1G/Q1G/QOOQa1G/O1G/OO!aQbO'#C{O&ZQbO'#CwOOQ`,5:i,5:iOOQ`-E7{-E7{O9uQbO1G0rO:QQbO1G0sO:nQbO1G0tOOQ`1G/y1G/yOOQ`1G/z1G/zO;RQbO7+&WO:QQbO7+&YO;^QQO7+$uOOQa7+$u7+$uO;iQbO7+%SOOQa7+%S7+%SOOQO-E7x-E7xOOQ`-E7y-E7yO;sQbO'#DZO;xQQO'#D^OOQ`7+&^7+&^O;}QbO7+&^O<SQbO7+&^OOQ`'#Dy'#DyO<[QQO'#DyO<aQbO'#EgOOQ`'#D]'#D]O=TQbO7+&_OOQ`'#Dm'#DmO=`QbO7+&`O=eQbO7+&aOOQ`<<Ir<<IrO>RQbO<<IrO>WQbO<<IrO>`QbO<<ItOOQa<<Ha<<HaOOQa<<Hn<<HnO>kQQO,59uO>pQbO,59xOOQ`<<Ix<<IxO?TQbO<<IxOOQ`,5:e,5:eOOQ`-E7w-E7wOOQ`<<Iy<<IyO?YQbO<<IyO?_QbO<<IyOOQ`<<Iz<<IzOOQ`'#Dn'#DnO?gQbO<<I{OOQ`AN?^AN?^O?rQbOAN?^OOQ`AN?`AN?`O?wQbOAN?`O?|QbOAN?`O@UQbO1G/aO@iQbO1G/dOOQ`1G/d1G/dOOQ`AN?dAN?dOOQ`AN?eAN?eOAPQbOAN?eO,wQbO'#DoOOQ`'#EO'#EOOAUQbOAN?gOAaQQO'#DqOOQ`AN?gAN?gOAfQbOAN?gOOQ`G24xG24xOOQ`G24zG24zOAkQbOG24zOApQbO7+${OOQ`7+${7+${OOQ`7+%O7+%OOOQ`G25PG25POBZQRO,5:ZOBbQRO,5:ZOOQ`-E7|-E7|OOQ`G25RG25ROBmQbOG25ROBrQQO,5:]OOQ`LD*fLD*fOOQ`<<Hg<<HgOBwQQO1G/uOOQ`LD*mLD*mO@iQbO1G/wO=eQbO7+%aOOQ`7+%c7+%cOOQ`<<H{<<H{", states: "9[QYQbOOO#tQcO'#C{O$qOSO'#C}O%PQbO'#EfOOQ`'#DW'#DWOOQa'#DT'#DTO&SQbO'#DbO'eQcO'#EZOOQa'#EZ'#EZO(hQcO'#EZO)jQcO'#EYO)}QRO'#C|O+ZQcO'#EUO+kQcO'#EUO+uQbO'#CzO,mOpO'#CxOOQ`'#EV'#EVO,rQbO'#EUO,yQQO'#ElOOQ`'#Dg'#DgO-OQbO'#DiO-OQbO'#EnOOQ`'#Dk'#DkO-sQRO'#DsOOQ`'#EU'#EUO.XQQO'#ETOOQ`'#ET'#ETOOQ`'#Du'#DuQYQbOOO.aQbO'#DUOOQa'#EY'#EYOOQ`'#De'#DeOOQ`'#Ek'#EkOOQ`'#D|'#D|O.kQbO,59cO/_QbO'#DPO/gQWO'#DQOOOO'#E]'#E]OOOO'#Dv'#DvO/{OSO,59iOOQa,59i,59iOOQ`'#Dx'#DxO0ZQbO'#DXO0cQQO,5;QOOQ`'#Dw'#DwO0hQbO,59|O0oQQO,59oOOQa,59|,59|O0zQbO,59|O1UQbO,5:`O-OQbO,59hO-OQbO,59hO-OQbO,59hO-OQbO,5:OO-OQbO,5:OO-OQbO,5:OO1fQRO,59fO1mQRO,59fO2OQRO,59fO1yQQO,59fO2ZQQO,59fO2cObO,59dO2nQbO'#D}O2yQbO,59bO3bQbO,5;WO3uQcO,5:TO4kQcO,5:TO4{QcO,5:TO5qQRO,5;YO5xQRO,5;YO1UQbO,5:_OOQ`,5:o,5:oOOQ`-E7s-E7sOOQ`,59p,59pOOQ`-E7z-E7zOOOO,59k,59kOOOO,59l,59lOOOO-E7t-E7tOOQa1G/T1G/TOOQ`-E7v-E7vO6TQbO1G0lOOQ`-E7u-E7uO6hQQO1G/ZOOQa1G/h1G/hO6sQbO1G/hOOQO'#Dz'#DzO6hQQO1G/ZOOQa1G/Z1G/ZOOQ`'#D{'#D{O6sQbO1G/hOOQ`1G/z1G/zOOQa1G/S1G/SO7lQcO1G/SO7vQcO1G/SO8QQcO1G/SOOQa1G/j1G/jO9pQcO1G/jO9wQcO1G/jO:OQcO1G/jOOQa1G/Q1G/QOOQa1G/O1G/OO!aQbO'#C{O:VQbO'#CwOOQ`,5:i,5:iOOQ`-E7{-E7{O:dQbO1G0rO:oQbO1G0sO;]QbO1G0tOOQ`1G/y1G/yO;pQbO7+&WO:oQbO7+&YO;{QQO7+$uOOQa7+$u7+$uO<WQbO7+%SOOQa7+%S7+%SOOQO-E7x-E7xOOQ`-E7y-E7yO<bQbO'#DZO<gQQO'#D^OOQ`7+&^7+&^O<lQbO7+&^O<qQbO7+&^OOQ`'#Dy'#DyO<yQQO'#DyO=OQbO'#EgOOQ`'#D]'#D]O=rQbO7+&_OOQ`'#Dm'#DmO=}QbO7+&`O>SQbO7+&aOOQ`<<Ir<<IrO>pQbO<<IrO>uQbO<<IrO>}QbO<<ItOOQa<<Ha<<HaOOQa<<Hn<<HnO?YQQO,59uO?_QbO,59xOOQ`<<Ix<<IxO?rQbO<<IxOOQ`,5:e,5:eOOQ`-E7w-E7wOOQ`<<Iy<<IyO?wQbO<<IyO?|QbO<<IyOOQ`<<Iz<<IzOOQ`'#Dn'#DnO@UQbO<<I{OOQ`AN?^AN?^O@aQbOAN?^OOQ`AN?`AN?`O@fQbOAN?`O@kQbOAN?`O@sQbO1G/aOAWQbO1G/dOOQ`1G/d1G/dOOQ`AN?dAN?dOOQ`AN?eAN?eOAnQbOAN?eO-OQbO'#DoOOQ`'#EO'#EOOAsQbOAN?gOBOQQO'#DqOOQ`AN?gAN?gOBTQbOAN?gOOQ`G24xG24xOOQ`G24zG24zOBYQbOG24zOB_QbO7+${OOQ`7+${7+${OOQ`7+%O7+%OOOQ`G25PG25POBxQRO,5:ZOCPQRO,5:ZOOQ`-E7|-E7|OOQ`G25RG25ROC[QbOG25ROCaQQO,5:]OOQ`LD*fLD*fOOQ`<<Hg<<HgOCfQQO1G/uOOQ`LD*mLD*mOAWQbO1G/wO>SQbO7+%aOOQ`7+%c7+%cOOQ`<<H{<<H{",
stateData: "CP~O!uOS!vOS~OdPOefOfWOg^OhROmWOuWOvWO!VWO![aO!^cO!`dO!{]O#OQO#VTO#WUO#XiO~OdmOfWOg^OhROmWOuWOvWOylO!TnO!VWO!{]O#OQO#VTO#WUO!YoX#XoX#doX#^oX!OoX!RoX!SoX~OP!|XQ!|XR!|XS!|XT!|XU!|XW!|XX!|XY!|XZ!|X[!|X]!|X^!|X~P!aOrtO#OwO#QrO#RsO~OdxO|{P~OdmOfWOg^OmWOuWOvWOylO!VWO!{]O#OQO#VTO#WUO#X{O~O#]!OO~P%XOdmOfWOg^OhROmWOuWOvWOylO!TnO!VWO!{]O#OQO#VTO#WUO~OP!}XQ!}XR!}XS!}XT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}X^!}X#X!}X#d!}X#^!}X!O!}X!R!}X!S!}X~P&ZOP!|XQ!|XR!|XS!|XT!|XU!|XW!|XX!|XY!|XZ!|X[!|X]!|X^!|X~O#X!xX#d!xX!O!xX!R!xX!S!xX~P(hOP!ROQ!ROR!SOS!SOT!UOU!VOW!TOX!TOY!TOZ!TO[!TO]!TO^!QO~O#X!xX#d!xX!O!xX!R!xX!S!xX~OP!ROQ!ROR!SOS!SO~P*qOT!UOU!VO~P*qOdPOfWOg^OhROmWOuWOvWO!VWO!{]O#OQO#VTO#WUO~O!z!]O~O!Y!^O~P*qO|!`O~OdmOfWOg^OmWOuWOvWO!VWO!{]O#OQO#VTO#WUO~OV!gO_!fO`!fOa!fOb!fOc!fO~O#X!hO#d!hO~OhRO!T!jO~P,wO!Yka#Xka#dka#^ka!Oka!Rka!Ska~P&ZOd!lO!{]O~O#O!mO#Q!mO#R!mO#S!mO#T!mO#U!mO~OrtO#O!oO#QrO#RsO~OdxO|{X~O|!qO~O#]!tO~P%XOylO#X!vO#]!xO~O#X!yO#]!tO~P,wO#^#TO~P(hOP!ROQ!ROR!SOS!SO#^#TO~OT!UOU!VO#^#TO~O!Y!^O#^#TO~Od#UOm#UO!{]O~Od#VOg^O!{]O~O!Y!^O#Xja#dja#^ja!Oja!Rja!Sja~OefO![aO!^cO!`dO#X#[O~P+nO#X!]a#d!]a!O!]a!R!]a!S!]a~P)vO#X!]a#d!]a!O!]a!R!]a!S!]a~OP!ROQ!ROR!SOS!SO~P3hOT!UOU!VO~P3hOT!UOU!VOW!TOX!TOY!TOZ!TO[!TO]!TO~O|#]O~P4eOT!UOU!VO|#]O~OefO![aO!^cO!`dO~P+nOefO![aO!^cO!`dO#X#aO~P+nOylO#X!vO#]#cO~O#X!yO#]#eO~P,wO^!QORpiSpi#Xpi#dpi#^pi!Opi!Rpi!Spi~OPpiQpi~P6mOP!ROQ!RO~P6mOP!ROQ!RORpiSpi#Xpi#dpi#^pi!Opi!Rpi!Spi~OW!TOX!TOY!TOZ!TO[!TO]!TOT!Wi#X!Wi#d!Wi#^!Wi|!Wi!O!Wi!R!Wi!S!Wi~OU!VO~P8bOU!VO~P8tOU!Wi~P8bO!O#hO!R#iO!S#jO~OefO![aO!^cO!`dO#X#mO!O#ZP!R#ZP!S#ZP~P+nOefO![aO!^cO!`dO#X#tO~P+nO!O#hO!R#iO!S#uO~OylO#X!vO#]#yO~O#X!yO#]#zO~P,wOd#{O~O|#|O~O!S#}O~O!R#iO!S#}O~O#X$PO~OefO![aO!^cO!`dO#X#mO!O#ZX!R#ZX!S#ZX!d#ZX!f#ZX~P+nO!O#hO!R#iO!S$RO~O!S$UO~OefO![aO!^cO!`dO#X#mO!S#ZP!d#ZP!f#ZP~P+nO!S$XO~O!R#iO!S$XO~O!O#hO!R#iO!S$ZO~O|$^O~OefO![aO!^cO!`dO#X$_O~P+nO!S$aO~O!S$bO~O!R#iO!S$bO~O!S$hO!d$dO!f$gO~O!S$jO~O!S$kO~O!R#iO!S$kO~OefO![aO!^cO!`dO#X$mO~P+nOefO![aO!^cO!`dO#X#mO!S#ZP~P+nO!S$pO~O!S$tO!d$dO!f$gO~O|$vO~O!S$tO~O!S$wO~OefO![aO!^cO!`dO#X#mO!R#ZP!S#ZP~P+nO|$yO~P4eOT!UOU!VO|$yO~O!S$zO~O#X${O~O#X$|O~Omv~", stateData: "Cn~O!uOS!vOS~OdPOegOfWOg_OhROmWOuWOvWO!VWO![bO!^dO!`eO!{^O#OQO#VTO#WUO#XjO~OdnOfWOg_OhROmWOuWOvWOymO!ToO!VWO!{^O#OQO#VTO#WUO!YoX#XoX#doX#^oX!OoX!RoX!SoX~OP!|XQ!|XR!|XS!|XT!|XU!|XW!|XX!|XY!|XZ!|X[!|X]!|X^!|X~P!aOruO#OxO#QsO#RtO~OdyO|{P~OdnOfWOg_OmWOuWOvWOymO!VWO!{^O#OQO#VTO#WUO#X|O~O#]!PO~P%XOP!}XQ!}XR!}XS!}XT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}X^!}X#X!}X#d!}X!O!}X!R!}X!S!}X~OdnOfWOg_OhROmWOuWOvWOymO!ToO!VWO!{^O#OQO#VTO#WUO#^!}X~P&ZOV!RO~P&ZOP!|XQ!|XR!|XS!|XT!|XU!|XW!|XX!|XY!|XZ!|X[!|X]!|X^!|X~O#X!xX#d!xX!O!xX!R!xX!S!xX~P(oOP!TOQ!TOR!UOS!UOT!WOU!XOW!VOX!VOY!VOZ!VO[!VO]!VO^!SO~O#X!xX#d!xX!O!xX!R!xX!S!xX~OP!TOQ!TOR!UOS!UO~P*xOT!WOU!XO~P*xOdPOfWOg_OhROmWOuWOvWO!VWO!{^O#OQO#VTO#WUO~O!z!_O~O!Y!`O~P*xO|!bO~OdnOfWOg_OmWOuWOvWO!VWO!{^O#OQO#VTO#WUO~OV!RO_!hO`!hOa!hOb!hOc!hO~O#X!iO#d!iO~OhRO!T!kO~P-OOhROymO!ToO!Yka#Xka#dka#^ka!Oka!Rka!Ska~P-OOd!mO!{^O~O#O!nO#Q!nO#R!nO#S!nO#T!nO#U!nO~OruO#O!pO#QsO#RtO~OdyO|{X~O|!rO~O#]!uO~P%XOymO#X!wO#]!yO~O#X!zO#]!uO~P-OOegO![bO!^dO!`eO~P+uO#^#VO~P(oOP!TOQ!TOR!UOS!UO#^#VO~OT!WOU!XO#^#VO~O!Y!`O#^#VO~Od#WOm#WO!{^O~Od#XOg_O!{^O~O!Y!`O#Xja#dja#^ja!Oja!Rja!Sja~OegO![bO!^dO!`eO#X#^O~P+uO#X!]a#d!]a!O!]a!R!]a!S!]a~P)}O#X!]a#d!]a!O!]a!R!]a!S!]a~OP!TOQ!TOR!UOS!UO~P4YOT!WOU!XO~P4YOT!WOU!XOW!VOX!VOY!VOZ!VO[!VO]!VO~O|#_O~P5VOT!WOU!XO|#_O~OegO![bO!^dO!`eO#X#bO~P+uOymO#X!wO#]#dO~O#X!zO#]#fO~P-OO^!SORpiSpi#Xpi#dpi#^pi!Opi!Rpi!Spi~OPpiQpi~P6}OP!TOQ!TO~P6}OP!TOQ!TORpiSpi#Xpi#dpi#^pi!Opi!Rpi!Spi~OW!VOX!VOY!VOZ!VO[!VO]!VOT!Wi#X!Wi#d!Wi#^!Wi|!Wi!O!Wi!R!Wi!S!Wi~OU!XO~P8rOU!XO~P9UOU!Wi~P8rOhROymO!ToO~P-OO!O#iO!R#jO!S#kO~OegO![bO!^dO!`eO#X#nO!O#ZP!R#ZP!S#ZP~P+uOegO![bO!^dO!`eO#X#uO~P+uO!O#iO!R#jO!S#vO~OymO#X!wO#]#zO~O#X!zO#]#{O~P-OOd#|O~O|#}O~O!S$OO~O!R#jO!S$OO~O#X$QO~OegO![bO!^dO!`eO#X#nO!O#ZX!R#ZX!S#ZX!d#ZX!f#ZX~P+uO!O#iO!R#jO!S$SO~O!S$VO~OegO![bO!^dO!`eO#X#nO!S#ZP!d#ZP!f#ZP~P+uO!S$YO~O!R#jO!S$YO~O!O#iO!R#jO!S$[O~O|$_O~OegO![bO!^dO!`eO#X$`O~P+uO!S$bO~O!S$cO~O!R#jO!S$cO~O!S$iO!d$eO!f$hO~O!S$kO~O!S$lO~O!R#jO!S$lO~OegO![bO!^dO!`eO#X$nO~P+uOegO![bO!^dO!`eO#X#nO!S#ZP~P+uO!S$qO~O!S$uO!d$eO!f$hO~O|$wO~O!S$uO~O!S$xO~OegO![bO!^dO!`eO#X#nO!R#ZP!S#ZP~P+uO|$zO~P5VOT!WOU!XO|$zO~O!S${O~O#X$|O~O#X$}O~Omv~",
goto: "3n#dPPPPPPPPPPPPPPPPPPPPPPPPPP#e#{$bP%b#{&h'XP(S(SPP'X(WP(k)]P)`P)l)uPPP'XP*_+UP+]P+]P+]P+p+s+|P,QP+]+],W,^,d,j,p,|-W-b-k-rPPPP-x-|.qPP/[0sP1rPPPPPPPP1v2b1vPP2o2v2v3Z3ZrgOk!`!f!g!q#[#]#a#o#t#|$^$_$m${$|R!Z]w_O]k!^!`!f!g!q#[#]#a#o#t#|$^$_$m${$|tPO]k!`!f!g!q#[#]#a#o#t#|$^$_$m${$|zmPUVcdlq|!P!Q!R!S!T!U!V!u!z#V#W#d$dR#V!^tVO]k!`!f!g!q#[#]#a#o#t#|$^$_$m${$|zWPUVcdlq|!P!Q!R!S!T!U!V!u!z#V#W#d$dQ!lrQ#U!]R#W!^rZOk!`!f!g!q#[#]#a#o#t#|$^$_$m${$|Q!X]Q!bcQ!{!RR#O!S!qWOPUV]cdklq|!P!Q!R!S!T!U!V!`!f!g!q!u!z#V#W#[#]#a#d#o#t#|$^$_$d$m${$|TtQvYoPVq#V#WQ}UQ!s|X!v}!s!w#brgOk!`!f!g!q#[#]#a#o#t#|$^$_$m${$|YnPVq#V#WQ!Z]R!jlRzRQ#l#ZQ#w#`Q$T#qR$]#xQ#q#[Q$o$_R$x$mQ#k#ZQ#v#`Q$O#lQ$S#qQ$Y#wQ$[#xQ$c$TR$l$]r[Ok!`!f!g!q#[#]#a#o#t#|$^$_$m${$|Q!Y]Q!ccQ!edQ#P!VQ#R!UR$r$dZoPVq#V#WsgOk!`!f!g!q#[#]#a#o#t#|$^$_$m${$|R#s#]Q$W#tQ$}${R%O$|T$e$W$fQ$i$WR$u$fQkOR!ikQvQR!nvQ|UR!r|QyRR!py^#o#[#a#t$_$m${$|R$Q#oQ!w}Q#b!sT#f!w#bQ!z!PQ#d!uT#g!z#dWqPV#V#WR!kqS!_`![R#Y!_Q$f$WR$s$fTjOkShOkQ#Z!`Q#^!fQ#_!gQ#`!q`#n#[#a#o#t$_$m${$|Q#r#]Q$`#|R$n$^r`Ok!`!f!g!q#[#]#a#o#t#|$^$_$m${$|Q![]R#X!^tYO]k!`!f!g!q#[#]#a#o#t#|$^$_$m${$|YnPVq#V#WQ!PUQ!acQ!ddQ!jlQ!u|W!y!P!u!z#dQ!{!QQ!|!RQ!}!SQ#P!TQ#Q!UQ#S!VR$q$drXOk!`!f!g!q#[#]#a#o#t#|$^$_$m${$|zmPUVcdlq|!P!Q!R!S!T!U!V!u!z#V#W#d$dR!W]TuQv!RSOPV]klq!`!f!g!q#V#W#[#]#a#o#t#|$^$_$m${$|U#p#[$_$mQ#x#aV$V#t${$|ZpPVq#V#WsbOk!`!f!g!q#[#]#a#o#t#|$^$_$m${$|seOk!`!f!g!q#[#]#a#o#t#|$^$_$m${$|", goto: "4k#dPPPPPPPPPPPPPPPPPPPPPPPPPP#e#{$bP%b#{&h'XP(S(SPP'X(WP(k)]P)`P)l)uPPP*_P+[,RP,YP,YP,YP,m,p,yP,}P,Y,Y-T-Z-a-g-m-y.T._.h.oPPPP.u.y/nPP0X1pP2oPPPPPPPP2s3_2sPP3l3s3s4W4WrhOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}R!]^w`O^l!R!`!b!h!r#^#_#b#p#u#}$_$`$n$|$}tPO^l!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}znPUVdemr}!Q!S!T!U!V!W!X!v!{#X#Y#e$eR#X!`tVO^l!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}zWPUVdemr}!Q!S!T!U!V!W!X!v!{#X#Y#e$eQ!msQ#W!_R#Y!`r[Ol!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}Q!Z^Q!ddQ!}!TR#Q!U!qWOPUV^delmr}!Q!R!S!T!U!V!W!X!b!h!r!v!{#X#Y#^#_#b#e#p#u#}$_$`$e$n$|$}TuQwYpPVr#X#YQ!OUQ!t}X!w!O!t!x#crhOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}YoPVr#X#YQ!]^R!kmR{RQ#m#]Q#x#aQ$U#rR$^#yQ#r#^Q$p$`R$y$nQ#l#]Q#w#aQ$P#mQ$T#rQ$Z#xQ$]#yQ$d$UR$m$^|WPUV^demr}!Q!S!T!U!V!W!X!v!{#X#Y#e$esXOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}r]Ol!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}Q![^Q!edQ!geQ#R!XQ#T!WR$s$eZpPVr#X#YshOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}R#t#_Q$X#uQ%O$|R%P$}T$f$X$gQ$j$XR$v$gQlOR!jlQwQR!owQ}UR!s}QzRR!qz^#p#^#b#u$`$n$|$}R$R#pQ!x!OQ#c!tT#g!x#cQ!{!QQ#e!vT#h!{#eWrPV#X#YR!lrS!aa!^R#[!aQ$g$XR$t$gTkOlSiOlQ!|!RQ#]!bQ#`!hQ#a!r`#o#^#b#p#u$`$n$|$}Q#s#_Q$a#}R$o$_raOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}Q!^^R#Z!`tZO^l!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}YoPVr#X#YQ!QUQ!cdQ!feQ!kmQ!v}W!z!Q!v!{#eQ!}!SQ#O!TQ#P!UQ#R!VQ#S!WQ#U!XR$r$erYOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}znPUVdemr}!Q!S!T!U!V!W!X!v!{#X#Y#e$eR!Y^TvQw!RSOPV^lmr!R!b!h!r#X#Y#^#_#b#p#u#}$_$`$n$|$}U#q#^$`$nQ#y#bV$W#u$|$}ZqPVr#X#YscOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}sfOl!R!b!h!r#^#_#b#p#u#}$_$`$n$|$}",
nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq 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 CompoundAssign Assign", nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq 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 CompoundAssign Assign",
maxTerm: 112, maxTerm: 112,
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!z~~", 11)], tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO!z~~", 11)],
topRules: {"Program":[0,25]}, topRules: {"Program":[0,25]},
specialized: [{term: 20, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 20, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], specialized: [{term: 20, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 20, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
tokenPrec: 1562 tokenPrec: 1591
}) })

View File

@ -660,6 +660,62 @@ 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`)
})
test('works with dotget', () => {
expect('[ a ] = [ [1 2 3] ]; a.1').toMatchTree(`
Assign
Array
Identifier a
Eq =
Array
Array
Number 1
Number 2
Number 3
FunctionCallOrIdentifier
DotGet
IdentifierBeforeDot a
Number 1`)
})
})
describe('Conditional ops', () => { describe('Conditional ops', () => {
test('or can be chained', () => { test('or can be chained', () => {
expect(` expect(`