Compare commits
1 Commits
019f7d84b1
...
336dc12082
| Author | SHA1 | Date | |
|---|---|---|---|
| 336dc12082 |
|
|
@ -274,13 +274,31 @@ export class Compiler {
|
||||||
const { identifier, operator, right } = getCompoundAssignmentParts(node)
|
const { identifier, operator, right } = getCompoundAssignmentParts(node)
|
||||||
const identifierName = input.slice(identifier.from, identifier.to)
|
const identifierName = input.slice(identifier.from, identifier.to)
|
||||||
const instructions: ProgramItem[] = []
|
const instructions: ProgramItem[] = []
|
||||||
|
const opValue = input.slice(operator.from, operator.to)
|
||||||
|
|
||||||
// will throw if undefined
|
// Special handling for ??= since it needs conditional evaluation
|
||||||
|
if (opValue === '??=') {
|
||||||
instructions.push(['LOAD', identifierName])
|
instructions.push(['LOAD', identifierName])
|
||||||
|
|
||||||
|
const rightInstructions = this.#compileNode(right, input)
|
||||||
|
|
||||||
|
instructions.push(['DUP'])
|
||||||
|
instructions.push(['PUSH', null])
|
||||||
|
instructions.push(['NEQ'])
|
||||||
|
instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1])
|
||||||
|
instructions.push(['POP'])
|
||||||
|
instructions.push(...rightInstructions)
|
||||||
|
|
||||||
|
instructions.push(['DUP'])
|
||||||
|
instructions.push(['STORE', identifierName])
|
||||||
|
|
||||||
|
return instructions
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard compound assignments: evaluate both sides, then operate
|
||||||
|
instructions.push(['LOAD', identifierName]) // will throw if undefined
|
||||||
instructions.push(...this.#compileNode(right, input))
|
instructions.push(...this.#compileNode(right, input))
|
||||||
|
|
||||||
const opValue = input.slice(operator.from, operator.to)
|
|
||||||
switch (opValue) {
|
switch (opValue) {
|
||||||
case '+=':
|
case '+=':
|
||||||
instructions.push(['ADD'])
|
instructions.push(['ADD'])
|
||||||
|
|
@ -587,6 +605,18 @@ export class Compiler {
|
||||||
|
|
||||||
break
|
break
|
||||||
|
|
||||||
|
case '??':
|
||||||
|
// Nullish coalescing: return left if not null, else right
|
||||||
|
instructions.push(...leftInstructions)
|
||||||
|
instructions.push(['DUP'])
|
||||||
|
instructions.push(['PUSH', null])
|
||||||
|
instructions.push(['NEQ'])
|
||||||
|
instructions.push(['JUMP_IF_TRUE', rightInstructions.length + 1])
|
||||||
|
instructions.push(['POP'])
|
||||||
|
instructions.push(...rightInstructions)
|
||||||
|
|
||||||
|
break
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new CompilerError(`Unsupported conditional operator: ${opValue}`, op.from, op.to)
|
throw new CompilerError(`Unsupported conditional operator: ${opValue}`, op.from, op.to)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -313,3 +313,118 @@ describe('default params', () => {
|
||||||
).toEvaluateTo({ name: 'Jon', age: 21 })
|
).toEvaluateTo({ name: 'Jon', age: 21 })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Nullish coalescing operator (??)', () => {
|
||||||
|
test('returns left side when not null', () => {
|
||||||
|
expect('5 ?? 10').toEvaluateTo(5)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns right side when left is null', () => {
|
||||||
|
expect('null ?? 10').toEvaluateTo(10)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns left side when left is false', () => {
|
||||||
|
expect('false ?? 10').toEvaluateTo(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns left side when left is 0', () => {
|
||||||
|
expect('0 ?? 10').toEvaluateTo(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns left side when left is empty string', () => {
|
||||||
|
expect(`'' ?? 'default'`).toEvaluateTo('')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chains left to right', () => {
|
||||||
|
expect('null ?? null ?? 42').toEvaluateTo(42)
|
||||||
|
expect('null ?? 10 ?? 20').toEvaluateTo(10)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('short-circuits evaluation', () => {
|
||||||
|
const throwError = () => { throw new Error('Should not evaluate') }
|
||||||
|
expect('5 ?? throw-error').toEvaluateTo(5, { 'throw-error': throwError })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('works with variables', () => {
|
||||||
|
expect('x = null; x ?? 5').toEvaluateTo(5)
|
||||||
|
expect('y = 3; y ?? 5').toEvaluateTo(3)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('works with function calls', () => {
|
||||||
|
const getValue = () => null
|
||||||
|
const getDefault = () => 42
|
||||||
|
// Note: identifiers without parentheses refer to the function, not call it
|
||||||
|
// Use explicit call syntax to invoke the function
|
||||||
|
expect('(get-value) ?? (get-default)').toEvaluateTo(42, {
|
||||||
|
'get-value': getValue,
|
||||||
|
'get-default': getDefault
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Nullish coalescing assignment (??=)', () => {
|
||||||
|
test('assigns when variable is null', () => {
|
||||||
|
expect('x = null; x ??= 5; x').toEvaluateTo(5)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('does not assign when variable is not null', () => {
|
||||||
|
expect('x = 3; x ??= 10; x').toEvaluateTo(3)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('does not assign when variable is false', () => {
|
||||||
|
expect('x = false; x ??= true; x').toEvaluateTo(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('does not assign when variable is 0', () => {
|
||||||
|
expect('x = 0; x ??= 100; x').toEvaluateTo(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('does not assign when variable is empty string', () => {
|
||||||
|
expect(`x = ''; x ??= 'default'; x`).toEvaluateTo('')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('returns the final value', () => {
|
||||||
|
expect('x = null; x ??= 5').toEvaluateTo(5)
|
||||||
|
expect('y = 3; y ??= 10').toEvaluateTo(3)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('short-circuits evaluation when not null', () => {
|
||||||
|
const throwError = () => { throw new Error('Should not evaluate') }
|
||||||
|
expect('x = 5; x ??= throw-error; x').toEvaluateTo(5, { 'throw-error': throwError })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('works with expressions', () => {
|
||||||
|
expect('x = null; x ??= 2 + 3; x').toEvaluateTo(5)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('works with function calls', () => {
|
||||||
|
const getDefault = () => 42
|
||||||
|
expect('x = null; x ??= (get-default); x').toEvaluateTo(42, { 'get-default': getDefault })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('throws when variable is undefined', () => {
|
||||||
|
expect(() => expect('undefined-var ??= 5').toEvaluateTo(null)).toThrow()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Compound assignment operators', () => {
|
||||||
|
test('+=', () => {
|
||||||
|
expect('x = 5; x += 3; x').toEvaluateTo(8)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('-=', () => {
|
||||||
|
expect('x = 10; x -= 4; x').toEvaluateTo(6)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('*=', () => {
|
||||||
|
expect('x = 3; x *= 4; x').toEvaluateTo(12)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('/=', () => {
|
||||||
|
expect('x = 20; x /= 5; x').toEvaluateTo(4)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('%=', () => {
|
||||||
|
expect('x = 10; x %= 3; x').toEvaluateTo(1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,16 @@ const operators: Array<Operator> = [
|
||||||
{ str: '==', tokenName: 'EqEq' },
|
{ str: '==', tokenName: 'EqEq' },
|
||||||
|
|
||||||
// Compound assignment operators (must come before single-char operators)
|
// Compound assignment operators (must come before single-char operators)
|
||||||
|
{ str: '??=', tokenName: 'NullishEq' },
|
||||||
{ str: '+=', tokenName: 'PlusEq' },
|
{ str: '+=', tokenName: 'PlusEq' },
|
||||||
{ str: '-=', tokenName: 'MinusEq' },
|
{ str: '-=', tokenName: 'MinusEq' },
|
||||||
{ str: '*=', tokenName: 'StarEq' },
|
{ str: '*=', tokenName: 'StarEq' },
|
||||||
{ str: '/=', tokenName: 'SlashEq' },
|
{ str: '/=', tokenName: 'SlashEq' },
|
||||||
{ str: '%=', tokenName: 'ModuloEq' },
|
{ str: '%=', tokenName: 'ModuloEq' },
|
||||||
|
|
||||||
|
// Nullish coalescing (must come before it could be mistaken for other tokens)
|
||||||
|
{ str: '??', tokenName: 'NullishCoalesce' },
|
||||||
|
|
||||||
// Single-char operators
|
// Single-char operators
|
||||||
{ str: '*', tokenName: 'Star' },
|
{ str: '*', tokenName: 'Star' },
|
||||||
{ str: '=', tokenName: 'Eq' },
|
{ str: '=', tokenName: 'Eq' },
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
@top Program { item* }
|
@top Program { item* }
|
||||||
|
|
||||||
@external tokens operatorTokenizer from "./operatorTokenizer" { Star, Slash, Plus, Minus, And, Or, Eq, EqEq, Neq, Lt, Lte, Gt, Gte, Modulo, PlusEq, MinusEq, StarEq, SlashEq, ModuloEq }
|
@external tokens operatorTokenizer from "./operatorTokenizer" { Star, Slash, Plus, Minus, And, Or, Eq, EqEq, Neq, Lt, Lte, Gt, Gte, Modulo, PlusEq, MinusEq, StarEq, SlashEq, ModuloEq, NullishCoalesce, NullishEq }
|
||||||
|
|
||||||
@tokens {
|
@tokens {
|
||||||
@precedence { Number Regex }
|
@precedence { Number Regex }
|
||||||
|
|
@ -44,6 +44,7 @@ null { @specialize[@name=Null]<Identifier, "null"> }
|
||||||
pipe @left,
|
pipe @left,
|
||||||
or @left,
|
or @left,
|
||||||
and @left,
|
and @left,
|
||||||
|
nullish @left,
|
||||||
comparison @left,
|
comparison @left,
|
||||||
multiplicative @left,
|
multiplicative @left,
|
||||||
additive @left,
|
additive @left,
|
||||||
|
|
@ -160,7 +161,8 @@ ConditionalOp {
|
||||||
expression !comparison Gt expression |
|
expression !comparison Gt expression |
|
||||||
expression !comparison Gte expression |
|
expression !comparison Gte expression |
|
||||||
(expression | ConditionalOp) !and And (expression | ConditionalOp) |
|
(expression | ConditionalOp) !and And (expression | ConditionalOp) |
|
||||||
(expression | ConditionalOp) !or Or (expression | ConditionalOp)
|
(expression | ConditionalOp) !or Or (expression | ConditionalOp) |
|
||||||
|
(expression | ConditionalOp) !nullish NullishCoalesce (expression | ConditionalOp)
|
||||||
}
|
}
|
||||||
|
|
||||||
Params {
|
Params {
|
||||||
|
|
@ -176,7 +178,7 @@ Assign {
|
||||||
}
|
}
|
||||||
|
|
||||||
CompoundAssign {
|
CompoundAssign {
|
||||||
AssignableIdentifier (PlusEq | MinusEq | StarEq | SlashEq | ModuloEq) consumeToTerminator
|
AssignableIdentifier (PlusEq | MinusEq | StarEq | SlashEq | ModuloEq | NullishEq) consumeToTerminator
|
||||||
}
|
}
|
||||||
|
|
||||||
BinOp {
|
BinOp {
|
||||||
|
|
|
||||||
|
|
@ -19,48 +19,50 @@ export const
|
||||||
StarEq = 17,
|
StarEq = 17,
|
||||||
SlashEq = 18,
|
SlashEq = 18,
|
||||||
ModuloEq = 19,
|
ModuloEq = 19,
|
||||||
Identifier = 20,
|
NullishCoalesce = 20,
|
||||||
AssignableIdentifier = 21,
|
NullishEq = 21,
|
||||||
Word = 22,
|
Identifier = 22,
|
||||||
IdentifierBeforeDot = 23,
|
AssignableIdentifier = 23,
|
||||||
Do = 24,
|
Word = 24,
|
||||||
Comment = 25,
|
IdentifierBeforeDot = 25,
|
||||||
Program = 26,
|
Do = 26,
|
||||||
PipeExpr = 27,
|
Comment = 27,
|
||||||
WhileExpr = 29,
|
Program = 28,
|
||||||
keyword = 70,
|
PipeExpr = 29,
|
||||||
ConditionalOp = 31,
|
WhileExpr = 31,
|
||||||
ParenExpr = 32,
|
keyword = 72,
|
||||||
IfExpr = 33,
|
ConditionalOp = 33,
|
||||||
FunctionCall = 35,
|
ParenExpr = 34,
|
||||||
DotGet = 36,
|
IfExpr = 35,
|
||||||
Number = 37,
|
FunctionCall = 37,
|
||||||
PositionalArg = 38,
|
DotGet = 38,
|
||||||
FunctionDef = 39,
|
Number = 39,
|
||||||
Params = 40,
|
PositionalArg = 40,
|
||||||
NamedParam = 41,
|
FunctionDef = 41,
|
||||||
NamedArgPrefix = 42,
|
Params = 42,
|
||||||
String = 43,
|
NamedParam = 43,
|
||||||
StringFragment = 44,
|
NamedArgPrefix = 44,
|
||||||
Interpolation = 45,
|
String = 45,
|
||||||
EscapeSeq = 46,
|
StringFragment = 46,
|
||||||
Boolean = 47,
|
Interpolation = 47,
|
||||||
Null = 48,
|
EscapeSeq = 48,
|
||||||
colon = 49,
|
Boolean = 49,
|
||||||
CatchExpr = 50,
|
Null = 50,
|
||||||
Block = 52,
|
colon = 51,
|
||||||
FinallyExpr = 53,
|
CatchExpr = 52,
|
||||||
Underscore = 56,
|
Block = 54,
|
||||||
NamedArg = 57,
|
FinallyExpr = 55,
|
||||||
ElseIfExpr = 58,
|
Underscore = 58,
|
||||||
ElseExpr = 60,
|
NamedArg = 59,
|
||||||
FunctionCallOrIdentifier = 61,
|
ElseIfExpr = 60,
|
||||||
BinOp = 62,
|
ElseExpr = 62,
|
||||||
Regex = 63,
|
FunctionCallOrIdentifier = 63,
|
||||||
Dict = 64,
|
BinOp = 64,
|
||||||
Array = 65,
|
Regex = 65,
|
||||||
FunctionCallWithBlock = 66,
|
Dict = 66,
|
||||||
TryExpr = 67,
|
Array = 67,
|
||||||
Throw = 69,
|
FunctionCallWithBlock = 68,
|
||||||
CompoundAssign = 71,
|
TryExpr = 69,
|
||||||
Assign = 72
|
Throw = 71,
|
||||||
|
CompoundAssign = 73,
|
||||||
|
Assign = 74
|
||||||
|
|
|
||||||
|
|
@ -4,24 +4,24 @@ import {operatorTokenizer} from "./operatorTokenizer"
|
||||||
import {tokenizer, specializeKeyword} from "./tokenizer"
|
import {tokenizer, specializeKeyword} from "./tokenizer"
|
||||||
import {trackScope} from "./parserScopeContext"
|
import {trackScope} from "./parserScopeContext"
|
||||||
import {highlighting} from "./highlight"
|
import {highlighting} from "./highlight"
|
||||||
const spec_Identifier = {__proto__:null,while:60, if:68, null:96, catch:102, finally:108, end:110, else:118, try:136, throw:140}
|
const spec_Identifier = {__proto__:null,while:64, if:72, null:100, catch:106, finally:112, end:114, else:122, try:140, throw:144}
|
||||||
export const parser = LRParser.deserialize({
|
export const parser = LRParser.deserialize({
|
||||||
version: 14,
|
version: 14,
|
||||||
states: "9UQYQbOOO!dOpO'#DQO!iOSO'#DXO$_QcO'#DkO&rQcO'#EYOOQ`'#Eh'#EhO'uQRO'#DlO)[QcO'#EWO)lQbO'#C|OOQa'#Dn'#DnO+nQbO'#DoOOQa'#EY'#EYO+uQcO'#EYO+|QcO'#EXO,yQcO'#EWO-TQRO'#DuOOQ`'#EW'#EWO-iQbO'#EWO-pQQO'#EVOOQ`'#EV'#EVOOQ`'#Dw'#DwQYQbOOO-{QbO'#DTO.WQbO'#C}O.{QbO'#CyO/pQQO'#DqO.{QbO'#DsO/uObO,59lO0QQbO'#DZO0YQWO'#D[OOOO'#E`'#E`OOOO'#D|'#D|O0nOSO,59sOOQa,59s,59sOOQ`'#DS'#DSO0|QbO'#DgOOQ`'#E^'#E^OOQ`'#Dy'#DyO1WQbO,59kOOQa'#EX'#EXO.{QbO,5:WO.{QbO,5:WO.{QbO,5:WO.{QbO,59gO.{QbO,59gO.{QbO,59gO2QQRO,59hO2^QQO,59hO2fQQO,59hO2qQRO,59hO3[QRO,59hO3jQQO'#CwOOQ`'#EP'#EPO3oQbO,5:ZO3vQQO,5:YOOQa,5:Z,5:ZO4RQbO,5:ZO)lQbO,5:bO)lQbO,5:aO4]QbO,5:[O4dQbO,59cOOQ`,5:q,5:qO)lQbO'#DxOOQ`-E7u-E7uOOQ`'#Dz'#DzO5OQbO'#DUO5ZQbO'#DVOOQO'#D{'#D{O5RQQO'#DUO5iQQO,59oO5nQcO'#EXO5uQRO'#E[O6lQRO'#E[OOQO'#E['#E[O6sQQO,59iO6xQRO,59eO7PQRO,59eO4]QbO,5:]O7[QcO,5:_O8dQcO,5:_O8tQcO,5:_OOQa1G/W1G/WOOOO,59u,59uOOOO,59v,59vOOOO-E7z-E7zOOQa1G/_1G/_OOQ`,5:R,5:ROOQ`-E7w-E7wOOQa1G/r1G/rO9sQcO1G/rO9}QcO1G/rO:XQcO1G/rOOQa1G/R1G/RO<TQcO1G/RO<[QcO1G/RO<cQcO1G/ROOQa1G/S1G/SOOQ`-E7}-E7}O<jQQO1G/tOOQa1G/u1G/uO<uQbO1G/uOOQO'#EQ'#EQO<jQQO1G/tOOQa1G/t1G/tOOQ`'#ER'#ERO<uQbO1G/uO=PQbO1G/|O=kQbO1G/{O>VQbO'#DbO>hQbO'#DbO>{QbO1G/vOOQ`-E7v-E7vOOQ`,5:d,5:dOOQ`-E7x-E7xO?WQQO,59pOOQO,59q,59qOOQO-E7y-E7yO?`QbO1G/ZO4]QbO1G/TO4]QbO1G/PO?gQbO1G/wO?rQQO7+%`OOQa7+%`7+%`O?}QbO7+%aOOQa7+%a7+%aOOQO-E8O-E8OOOQ`-E8P-E8POOQ`'#D}'#D}O@XQQO'#D}O@aQbO'#EgOOQ`,59|,59|O@tQbO'#D`O@yQQO'#DcOOQ`7+%b7+%bOAOQbO7+%bOATQbO7+%bOA]QbO7+$uOAkQbO7+$uOA{QbO7+$oOBTQbO7+$kOOQ`7+%c7+%cOBYQbO7+%cOB_QbO7+%cOOQa<<Hz<<HzOOQa<<H{<<H{OOQ`,5:i,5:iOOQ`-E7{-E7{OBgQQO,59zO4]QbO,59}OOQ`<<H|<<H|OBlQbO<<H|OOQ`<<Ha<<HaOBqQbO<<HaOBvQbO<<HaOCOQbO<<HaOOQ`'#EO'#EOOCZQbO<<HZOCcQbO'#DjOOQ`<<HZ<<HZOCkQbO<<HZOOQ`<<HV<<HVOOQ`<<H}<<H}OCpQbO<<H}O4]QbO1G/fOOQ`1G/i1G/iOOQ`AN>hAN>hOOQ`AN={AN={OCuQbOAN={OCzQbOAN={OOQ`-E7|-E7|OOQ`AN=uAN=uODSQbOAN=uO.WQbO,5:SO4]QbO,5:UOOQ`AN>iAN>iOOQ`7+%Q7+%QOOQ`G23gG23gODXQbOG23gPD^QbO'#DhOOQ`G23aG23aODcQQO1G/nOOQ`1G/p1G/pOOQ`LD)RLD)RO4]QbO7+%YOOQ`<<Ht<<Ht",
|
states: "9hQYQbOOO!dOpO'#DSO!iOSO'#DZO$bQcO'#DmO&xQcO'#E[OOQ`'#Ej'#EjO'{QRO'#DnO)eQcO'#EYO)uQbO'#DOOOQa'#Dp'#DpO+wQbO'#DqOOQa'#E['#E[O,OQcO'#E[O,VQcO'#EZO-VQcO'#EYO-dQRO'#DwOOQ`'#EY'#EYO-{QbO'#EYO.SQQO'#EXOOQ`'#EX'#EXOOQ`'#Dy'#DyQYQbOOO._QbO'#DVO.jQbO'#DPO/_QbO'#C{O0SQQO'#DsO/_QbO'#DuO0XObO,59nO0dQbO'#D]O0lQWO'#D^OOOO'#Eb'#EbOOOO'#EO'#EOO1QOSO,59uOOQa,59u,59uOOQ`'#DU'#DUO1`QbO'#DiOOQ`'#E`'#E`OOQ`'#D{'#D{O1jQbO,59mOOQa'#EZ'#EZO/_QbO,5:YO/_QbO,5:YO/_QbO,5:YO/_QbO,59iO/_QbO,59iO/_QbO,59iO/_QbO,59iO2dQRO,59jO2pQQO,59jO2xQQO,59jO3TQRO,59jO3nQRO,59jO4PQQO'#CyOOQ`'#ER'#ERO4UQbO,5:]O4]QQO,5:[OOQa,5:],5:]O4hQbO,5:]O)uQbO,5:dO)uQbO,5:cO4rQbO,5:^O4yQbO,59eOOQ`,5:s,5:sO)uQbO'#DzOOQ`-E7w-E7wOOQ`'#D|'#D|O5eQbO'#DWO5pQbO'#DXOOQO'#D}'#D}O5hQQO'#DWO6OQQO,59qO6TQcO'#EZO6[QRO'#E^O7XQRO'#E^OOQO'#E^'#E^O7`QQO,59kO7eQRO,59gO7lQRO,59gO4rQbO,5:_O7zQcO,5:aO9SQcO,5:aO9dQcO,5:aOOQa1G/Y1G/YOOOO,59w,59wOOOO,59x,59xOOOO-E7|-E7|OOQa1G/a1G/aOOQ`,5:T,5:TOOQ`-E7y-E7yOOQa1G/t1G/tO:fQcO1G/tO:pQcO1G/tO:zQcO1G/tOOQa1G/T1G/TO<yQcO1G/TO=QQcO1G/TO=XQcO1G/TO>WQcO1G/TO=`QcO1G/TOOQa1G/U1G/UOOQ`-E8P-E8PO>nQQO1G/vOOQa1G/w1G/wO>yQbO1G/wOOQO'#ES'#ESO>nQQO1G/vOOQa1G/v1G/vOOQ`'#ET'#ETO>yQbO1G/wO?TQbO1G0OO?oQbO1G/}O@ZQbO'#DdO@lQbO'#DdOAPQbO1G/xOOQ`-E7x-E7xOOQ`,5:f,5:fOOQ`-E7z-E7zOA[QQO,59rOOQO,59s,59sOOQO-E7{-E7{OAdQbO1G/]O4rQbO1G/VO4rQbO1G/ROAkQbO1G/yOAvQQO7+%bOOQa7+%b7+%bOBRQbO7+%cOOQa7+%c7+%cOOQO-E8Q-E8QOOQ`-E8R-E8ROOQ`'#EP'#EPOB]QQO'#EPOBeQbO'#EiOOQ`,5:O,5:OOBxQbO'#DbOB}QQO'#DeOOQ`7+%d7+%dOCSQbO7+%dOCXQbO7+%dOCaQbO7+$wOCoQbO7+$wODPQbO7+$qODXQbO7+$mOOQ`7+%e7+%eOD^QbO7+%eODcQbO7+%eOOQa<<H|<<H|OOQa<<H}<<H}OOQ`,5:k,5:kOOQ`-E7}-E7}ODkQQO,59|O4rQbO,5:POOQ`<<IO<<IOODpQbO<<IOOOQ`<<Hc<<HcODuQbO<<HcODzQbO<<HcOESQbO<<HcOOQ`'#EQ'#EQOE_QbO<<H]OEgQbO'#DlOOQ`<<H]<<H]OEoQbO<<H]OOQ`<<HX<<HXOOQ`<<IP<<IPOEtQbO<<IPO4rQbO1G/hOOQ`1G/k1G/kOOQ`AN>jAN>jOOQ`AN=}AN=}OEyQbOAN=}OFOQbOAN=}OOQ`-E8O-E8OOOQ`AN=wAN=wOFWQbOAN=wO.jQbO,5:UO4rQbO,5:WOOQ`AN>kAN>kOOQ`7+%S7+%SOOQ`G23iG23iOF]QbOG23iPFbQbO'#DjOOQ`G23cG23cOFgQQO1G/pOOQ`1G/r1G/rOOQ`LD)TLD)TO4rQbO7+%[OOQ`<<Hv<<Hv",
|
||||||
stateData: "Dk~O!xOSiOS~OdROe_OfZOgPOhfOnhOrgOuZO!PZO!QZO!aZO!fiO!hjO!}WO#RQO#YcO#^XO#_YO~O#PkO~O|nO#RqO#TlO#UmO~OdwOfZOgPOhfOuZOzsO!PZO!QZO!YrO!aZO!}WO#RQO#^XO#_YOT!{XU!{XW!{XX!{XY!{XZ!{X[!{X]!{X~OP!{XQ!{XR!{XS!{X^!{Xl!_X!R!_X#Y!_X#a!_X#]!_X!T!_X!W!_X!X!_X!]!_X~P!wOP!|XQ!|XR!|XS!|XT!|XU!|XW!|XX!|XY!|XZ!|X[!|X]!|X^!|Xl!|X#Y!|X#a!|X#]!|X!T!|X!W!|X!X!|X!]!|X~OdwOfZOgPOhfOuZOzsO!PZO!QZO!YrO!aZO!}WO#RQO#^XO#_YO!R!|X~P%_OPyOQyORzOSzOT|OU}OW{OX{OY{OZ{O[{O]{O^xO~Ol!zX#Y!zX#a!zX!T!zX!W!zX!X!zX#]!zX!]!zX~OPyOQyORzOSzO~P(pOdROe_OfZOgPOhfOnhOrgOuZO!PZO!QZO!aZO!fiO!hjO!}WO#RQO#^XO#_YO~OdwOfZOgPOuZOzsO!PZO!QZO!aZO!}WO#RQO#Y!UO#^XO#_YO~O#`!XO~P*sOV!ZO~P%_OP!{XQ!{XR!{XS!{XT!{XU!{XW!{XX!{XY!{XZ!{X[!{X]!{X^!{X~P(pOT|OU}O~P(pOV!ZO_![O`![Oa![Ob![Oc![O~O!R!]O~P(pOl!`O#Y!_O#a!_O~Od!bOz!dO!RxP~Od!hOfZOgPOuZO!PZO!QZO!aZO!}WO#RQO#^XO#_YO~OdwOfZOgPOuZO!PZO!QZO!aZO!}WO#RQO#^XO#_YO~O!R!oO~Od!sOu!sO!}WO~Od!tO!}WO~O#R!uO#T!uO#U!uO#V!uO#W!uO#X!uO~O|nO#R!wO#TlO#UmO~OhfO!Y!xO~P.{OhfOzsO!YrOlsa!Rsa#Ysa#asa#]sa!Tsa!Wsa!Xsa!]sa~P.{OPyOQyORzOSzO#]#SOl!zX~O!R!]O#]#SOl!zX~O#]#SOP!{XQ!{XR!{XS!{X^!{Xl!zX~P#sOT|OU}O#]#SOl!zX~Ol!`O~O#`#VO~P*sOzsO#Y#XO#`#ZO~O#Y#[O#`#VO~P.{O#Y#aO~P)lOl!`O#Yka#aka#]ka!Tka!Wka!Xka!]ka~Od!bOz!dO!RxX~Ou#gO!P#gO!Q#gO#RQO~O!R#iO~O!R!{X~P!wOT|OU}O!R#OX~OT|OU}OW{OX{OY{OZ{O[{O]{O~O!R#OX~P6QO!R#jO~O!R#kO~P6QOT|OU}O!R#kO~Ol!ga#Y!ga#a!ga!T!ga!W!ga!X!ga#]!ga!]!ga~P'uOl!ga#Y!ga#a!ga!T!ga!W!ga!X!ga#]!ga!]!ga~OPyOQyORzOSzO~P7xOT|OU}O~P7xO^xOR!`iS!`il!`i#Y!`i#a!`i#]!`i!T!`i!W!`i!X!`i!]!`i~OP!`iQ!`i~P9OOPyOQyO~P9OOPyOQyOR!`iS!`il!`i#Y!`i#a!`i#]!`i!T!`i!W!`i!X!`i!]!`i~OW{OX{OY{OZ{O[{O]{OToiloi#Yoi#aoi#]oi!Roi!Toi!Woi!Xoi!]oi~OU}O~P;POU}O~P;cOUoi~P;POzsO#Y#XO#`#nO~O#Y#[O#`#pO~P.{Ol!`O#Y!ji#a!ji!T!ji!W!ji!X!ji#]!ji!]!ji~Ol!`O#Y!ii#a!ii!T!ii!W!ii!X!ii#]!ii!]!ii~Ol!`O!T!UX!W!UX!X!UX!]!UX~O#Y#sO!T#ZP!W#ZP!X#ZP!]#ZP~P)lO!T#wO!W#xO!X#yO~Oz!dO!Rxa~O#Y#}O~P)lO!T#wO!W#xO!X$QO~OzsO#Y#XO#`$TO~O#Y#[O#`$UO~P.{Ol!`O#Y$VO~O#Y#sO!T#ZX!W#ZX!X#ZX!]#ZX~P)lOd$XO~O!R$YO~O!X$ZO~O!W#xO!X$ZO~Ol!`O!T#wO!W#xO!X$]O~O#Y#sO!T#ZP!W#ZP!X#ZP~P)lO!X$dO!]$cO~O!X$fO~O!X$gO~O!W#xO!X$gO~O!R$iO~O!X$kO~O!X$lO~O!W#xO!X$lO~O!T#wO!W#xO!X$lO~O!X$pO!]$cO~Or$rO!R$sO~O!X$pO~O!X$tO~O!X$vO~O!W#xO!X$vO~O!X$yO~O!X$|O~Or$rO~O!R$}O~Ou!a~",
|
stateData: "Fo~O!zOSkOS~OfROg_OhZOiPOjfOphOtgOwZO!RZO!SZO!cZO!hiO!jjO#PWO#TQO#[cO#`XO#aYO~O#RkO~O!OnO#TqO#VlO#WmO~OfwOhZOiPOjfOwZO|sO!RZO!SZO![rO!cZO#PWO#TQO#`XO#aYOT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}Xd!}X~OP!}XQ!}XR!}XS!}X^!}Xn!aX!T!aX#[!aX#c!aX#_!aX!V!aX!Y!aX!Z!aX!_!aX~P!wOP#OXQ#OXR#OXS#OXT#OXU#OXW#OXX#OXY#OXZ#OX[#OX]#OX^#OXd#OXn#OX#[#OX#c#OX#_#OX!V#OX!Y#OX!Z#OX!_#OX~OfwOhZOiPOjfOwZO|sO!RZO!SZO![rO!cZO#PWO#TQO#`XO#aYO!T#OX~P%bOPyOQyORzOSzOT|OU}OW{OX{OY{OZ{O[{O]{O^xOd!OO~On!|X#[!|X#c!|X!V!|X!Y!|X!Z!|X#_!|X!_!|X~OPyOQyORzOSzO~P(yOfROg_OhZOiPOjfOphOtgOwZO!RZO!SZO!cZO!hiO!jjO#PWO#TQO#`XO#aYO~OfwOhZOiPOwZO|sO!RZO!SZO!cZO#PWO#TQO#[!VO#`XO#aYO~O#b!YO~P*|OV![O~P%bOP!}XQ!}XR!}XS!}XT!}XU!}XW!}XX!}XY!}XZ!}X[!}X]!}X^!}Xd!}X~P(yOT|OU}Od!OO~P(yOV![O_!]O`!]Oa!]Ob!]Oc!]Oe!]O~O!T!^O~P(yOn!aO#[!`O#c!`O~Of!cO|!eO!TzP~Of!iOhZOiPOwZO!RZO!SZO!cZO#PWO#TQO#`XO#aYO~OfwOhZOiPOwZO!RZO!SZO!cZO#PWO#TQO#`XO#aYO~O!T!pO~Of!tOw!tO#PWO~Of!uO#PWO~O#T!vO#V!vO#W!vO#X!vO#Y!vO#Z!vO~O!OnO#T!xO#VlO#WmO~OjfO![!yO~P/_OjfO|sO![rOnua!Tua#[ua#cua#_ua!Vua!Yua!Zua!_ua~P/_OPyOQyORzOSzO#_#VOn!|X~O!T!^O#_#VOn!|X~O#_#VOP!}XQ!}XR!}XS!}X^!}Xn!|X~P#sOT|OU}Od!OO#_#VOn!|X~On!aO~O#b#YO~P*|O|sO#[#[O#b#^O~O#[#_O#b#YO~P/_O#[#dO~P)uOn!aO#[ma#cma#_ma!Vma!Yma!Zma!_ma~Of!cO|!eO!TzX~Ow#jO!R#jO!S#jO#TQO~O!T#lO~O!T!}X~P!wOT|OU}Od!OO!T#QX~OT|OU}OW{OX{OY{OZ{O[{O]{Od!OO~O!T#QX~P6jO!T#mO~O!T#nO~P6jOT|OU}Od!OO!T#nO~On!ia#[!ia#c!ia!V!ia!Y!ia!Z!ia#_!ia!_!ia~P'{On!ia#[!ia#c!ia!V!ia!Y!ia!Z!ia#_!ia!_!ia~OPyOQyORzOSzO~P8hOT|OU}Od!OO~P8hO^xOR!biS!bin!bi#[!bi#c!bi#_!bi!V!bi!Y!bi!Z!bi!_!bi~OP!biQ!bi~P9qOPyOQyO~P9qOPyOQyOR!biS!bin!bi#[!bi#c!bi#_!bi!V!bi!Y!bi!Z!bi!_!bi~OW{OX{OY{OZ{O[{O]{OTqidqinqi#[qi#cqi#_qi!Tqi!Vqi!Yqi!Zqi!_qi~OU}O~P;rOU}O~P<UOUqi~P;rOT|OU}Odqinqi#[qi#cqi#_qi!Tqi!Vqi!Yqi!Zqi!_qi~OW{OX{OY{OZ{O[{O]{O~P=`O|sO#[#[O#b#qO~O#[#_O#b#sO~P/_On!aO#[!li#c!li!V!li!Y!li!Z!li#_!li!_!li~On!aO#[!ki#c!ki!V!ki!Y!ki!Z!ki#_!ki!_!ki~On!aO!V!WX!Y!WX!Z!WX!_!WX~O#[#vO!V#]P!Y#]P!Z#]P!_#]P~P)uO!V#zO!Y#{O!Z#|O~O|!eO!Tza~O#[$QO~P)uO!V#zO!Y#{O!Z$TO~O|sO#[#[O#b$WO~O#[#_O#b$XO~P/_On!aO#[$YO~O#[#vO!V#]X!Y#]X!Z#]X!_#]X~P)uOf$[O~O!T$]O~O!Z$^O~O!Y#{O!Z$^O~On!aO!V#zO!Y#{O!Z$`O~O#[#vO!V#]P!Y#]P!Z#]P~P)uO!Z$gO!_$fO~O!Z$iO~O!Z$jO~O!Y#{O!Z$jO~O!T$lO~O!Z$nO~O!Z$oO~O!Y#{O!Z$oO~O!V#zO!Y#{O!Z$oO~O!Z$sO!_$fO~Ot$uO!T$vO~O!Z$sO~O!Z$wO~O!Z$yO~O!Y#{O!Z$yO~O!Z$|O~O!Z%PO~Ot$uO~O!T%QO~Ow!c~",
|
||||||
goto: "4l#]PPPPPPPPPPPPPPPPPPPPPPPPPPP#^P#tP$Y%Q#^P&T&mP'l'r(c(fP(lP)j)jPPP)nP)z*dPPP*z+^P+b+h+|P,m-h#t#tP#tP#t#t.e.k.w/P/V/a/g/n/t/z0UPPP0`0d1W2oP3nP3tP3zPPPPPP4O4Ur`Oe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}R!PWu`OWe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}r^Oe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}Q!SWS!ig$rQ!nhQ!rjQ#O}R#Q|xSOWeg!Z![!]!`!o#a#i#j#k#u#}$Y$i$r$s$}vZRSYhjsvxyz{|}!V!Y!h#W#]#oQ!skR!tltTOWe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}T!kg$rtROWe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}vwRSYhjsvxyz{|}!V!Y!h#W#]#oT!hg$rXtRSv!hr`Oe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}WrRSv!hQ!PWR!xsR!gfX!ef!c!f#f!pZORSWYeghjsvxyz{|}!V!Y!Z![!]!`!h!o#W#]#a#i#j#k#o#u#}$Y$i$r$s$}R#g!dTnQpQ#{#bQ$S#lQ$_#|R$n$`Q#b!]Q#l!oQ$O#jQ$P#kQ$j$YQ$u$iQ${$sR%O$}Q#z#bQ$R#lQ$[#{Q$^#|Q$h$SS$m$_$`R$w$nWtRSv!hQ!WYQ#U!VX#X!W#U#Y#mT$a$O$bQ$e$OR$q$buTOWe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}rVOe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}Q!OWQ!qjQ!zyR!}z!qZORSWYeghjsvxyz{|}!V!Y!Z![!]!`!h!o#W#]#a#i#j#k#o#u#}$Y$i$r$s$}zZRSYghjsvxyz{|}!V!Y!h#W#]#o$ru[OWe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}QeOR!ae^!^b!T#^#_#`#t#|R#c!^UvRS!hR!yvQ!cfR#e!cQ!ffQ#f!cT#h!f#fQpQR!vpS#u#a#}R$W#uQ$b$OR$o$bQ!VYR#T!VQ#Y!WQ#m#UT#q#Y#mQ#]!YQ#o#WT#r#]#oTdOeSbOeQ!TWQ#^!ZQ#_![`#`!]!o#j#k$Y$i$s$}Q#d!`U#t#a#u#}R#|#itUOWe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}WrRSv!hQ!YYS!jg$rQ!mhQ!pjQ!xsQ!zxQ!{yQ!|zQ#O{Q#P|Q#R}Q#W!VX#[!Y#W#]#or]Oe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}zwRSYghjsvxyz{|}!V!Y!h#W#]#o$rR!RWQ!lgR$z$rXuRSv!hToQpQ#v#aR$`#}raOe!Z![!]!`!o#a#i#j#k#u#}$Y$i$s$}R!QW",
|
goto: "4z#_PPPPPPPPPPPPPPPPPPPPPPPPPPPPP#`P#vP$[%V#`P&Z&sP's'y(j(mP(sP)r)rPPP)vP*S*lPPP+S+fP+j+p,UP,u-q#v#vP#vP#v#v.o.u/R/Z/a/k/q/x0O0U0`PPP0j0n1b2|P3|P4SP4YPPPPPP4^4dr`Oe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QR!QWu`OWe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%Qr^Oe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QQ!TWS!jg$uQ!ohQ!sjQ#P}Q#R|R#U!OxSOWeg![!]!^!a!p#d#l#m#n#x$Q$]$l$u$v%QxZRSYhjsvxyz{|}!O!W!Z!i#Z#`#rQ!tkR!ultTOWe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QT!lg$utROWe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QxwRSYhjsvxyz{|}!O!W!Z!i#Z#`#rT!ig$uXtRSv!ir`Oe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QWrRSv!iQ!QWR!ysR!hfX!ff!d!g#i!rZORSWYeghjsvxyz{|}!O!W!Z![!]!^!a!i!p#Z#`#d#l#m#n#r#x$Q$]$l$u$v%QR#j!eTnQpQ$O#eQ$V#oQ$b$PR$q$cQ#e!^Q#o!pQ$R#mQ$S#nQ$m$]Q$x$lQ%O$vR%R%QQ#}#eQ$U#oQ$_$OQ$a$PQ$k$VS$p$b$cR$z$qWtRSv!iQ!XYQ#X!WX#[!X#X#]#pT$d$R$eQ$h$RR$t$euTOWe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QrVOe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QQ!PWQ!rjQ!{yR#Oz!sZORSWYeghjsvxyz{|}!O!W!Z![!]!^!a!i!p#Z#`#d#l#m#n#r#x$Q$]$l$u$v%Q|ZRSYghjsvxyz{|}!O!W!Z!i#Z#`#r$uu[OWe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QQeOR!be^!_b!U#a#b#c#w$PR#f!_UvRS!iR!zvQ!dfR#h!dQ!gfQ#i!dT#k!g#iQpQR!wpS#x#d$QR$Z#xQ$e$RR$r$eQ!WYR#W!WQ#]!XQ#p#XT#t#]#pQ#`!ZQ#r#ZT#u#`#rTdOeSbOeQ!UWQ#a![Q#b!]`#c!^!p#m#n$]$l$v%QQ#g!aU#w#d#x$QR$P#ltUOWe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QWrRSv!iQ!ZYS!kg$uQ!nhQ!qjQ!ysQ!{xQ!|yQ!}zQ#P{Q#Q|Q#S}Q#T!OQ#Z!WX#_!Z#Z#`#rr]Oe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%Q|wRSYghjsvxyz{|}!O!W!Z!i#Z#`#r$uR!SWQ!mgR$}$uXuRSv!iToQpQ#y#dR$c$QraOe![!]!^!a!p#d#l#m#n#x$Q$]$l$v%QR!RW",
|
||||||
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 Comment Program PipeExpr operator WhileExpr keyword ConditionalOp ParenExpr IfExpr keyword FunctionCall DotGet Number PositionalArg FunctionDef Params NamedParam NamedArgPrefix String StringFragment Interpolation EscapeSeq Boolean Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore NamedArg ElseIfExpr keyword ElseExpr FunctionCallOrIdentifier BinOp Regex Dict Array FunctionCallWithBlock TryExpr keyword Throw keyword CompoundAssign Assign",
|
nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq NullishCoalesce NullishEq Identifier AssignableIdentifier Word IdentifierBeforeDot Do Comment Program PipeExpr operator WhileExpr keyword ConditionalOp ParenExpr IfExpr keyword FunctionCall DotGet Number PositionalArg FunctionDef Params NamedParam NamedArgPrefix String StringFragment Interpolation EscapeSeq Boolean Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore NamedArg ElseIfExpr keyword ElseExpr FunctionCallOrIdentifier BinOp Regex Dict Array FunctionCallWithBlock TryExpr keyword Throw keyword CompoundAssign Assign",
|
||||||
maxTerm: 109,
|
maxTerm: 111,
|
||||||
context: trackScope,
|
context: trackScope,
|
||||||
nodeProps: [
|
nodeProps: [
|
||||||
["closedBy", 49,"end"]
|
["closedBy", 51,"end"]
|
||||||
],
|
],
|
||||||
propSources: [highlighting],
|
propSources: [highlighting],
|
||||||
skippedNodes: [0,25],
|
skippedNodes: [0,27],
|
||||||
repeatNodeCount: 11,
|
repeatNodeCount: 11,
|
||||||
tokenData: "C_~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'Vuw#{wx'[xy'ayz'zz{#{{|(e|}#{}!O+X!O!P#{!P!Q-n!Q![)S![!]6Z!]!^%T!^!}#{!}#O6t#O#P8j#P#Q8o#Q#R#{#R#S9Y#S#T#{#T#Y,Y#Y#Z9s#Z#b,Y#b#c>q#c#f,Y#f#g?n#g#h,Y#h#i@k#i#o,Y#o#p#{#p#qBo#q;'S#{;'S;=`$d<%l~#{~O#{~~CYS$QU|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qU|S!xYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[U|S#YQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%uZiY|SOY%nYZ#{Zt%ntu&huw%nwx&hx#O%n#O#P&h#P;'S%n;'S;=`'P<%lO%nY&mSiYOY&hZ;'S&h;'S;=`&y<%lO&hY&|P;=`<%l&h^'SP;=`<%l%n~'[O#T~~'aO#R~U'hU|S!}QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(RU|S#]QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(jW|SOt#{uw#{x!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U)ZY|SuQOt#{uw#{x!O#{!O!P)y!P!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U*OW|SOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U*oW|SuQOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U+^^|SOt#{uw#{x}#{}!O,Y!O!Q#{!Q![)S![!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{U,_[|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{U-[UzQ|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U-sW|SOt#{uw#{x!P#{!P!Q.]!Q#O#{#P;'S#{;'S;=`$d<%lO#{U.b^|SOY/^YZ#{Zt/^tu0auw/^wx0ax!P/^!P!Q#{!Q!}/^!}#O5S#O#P2o#P;'S/^;'S;=`6T<%lO/^U/e^|S!aQOY/^YZ#{Zt/^tu0auw/^wx0ax!P/^!P!Q3U!Q!}/^!}#O5S#O#P2o#P;'S/^;'S;=`6T<%lO/^Q0fX!aQOY0aZ!P0a!P!Q1R!Q!}0a!}#O1p#O#P2o#P;'S0a;'S;=`3O<%lO0aQ1UP!P!Q1XQ1^U!aQ#Z#[1X#]#^1X#a#b1X#g#h1X#i#j1X#m#n1XQ1sVOY1pZ#O1p#O#P2Y#P#Q0a#Q;'S1p;'S;=`2i<%lO1pQ2]SOY1pZ;'S1p;'S;=`2i<%lO1pQ2lP;=`<%l1pQ2rSOY0aZ;'S0a;'S;=`3O<%lO0aQ3RP;=`<%l0aU3ZW|SOt#{uw#{x!P#{!P!Q3s!Q#O#{#P;'S#{;'S;=`$d<%lO#{U3zb|S!aQOt#{uw#{x#O#{#P#Z#{#Z#[3s#[#]#{#]#^3s#^#a#{#a#b3s#b#g#{#g#h3s#h#i#{#i#j3s#j#m#{#m#n3s#n;'S#{;'S;=`$d<%lO#{U5X[|SOY5SYZ#{Zt5Stu1puw5Swx1px#O5S#O#P2Y#P#Q/^#Q;'S5S;'S;=`5}<%lO5SU6QP;=`<%l5SU6WP;=`<%l/^U6bU|S!RQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U6{W#_Q|SOt#{uw#{x!_#{!_!`7e!`#O#{#P;'S#{;'S;=`$d<%lO#{U7jV|SOt#{uw#{x#O#{#P#Q8P#Q;'S#{;'S;=`$d<%lO#{U8WU#^Q|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~8oO#U~U8vU#`Q|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U9aU|S!YQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U9x]|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#U:q#U#o,Y#o;'S#{;'S;=`$d<%lO#{U:v^|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#`,Y#`#a;r#a#o,Y#o;'S#{;'S;=`$d<%lO#{U;w^|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#g,Y#g#h<s#h#o,Y#o;'S#{;'S;=`$d<%lO#{U<x^|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#X,Y#X#Y=t#Y#o,Y#o;'S#{;'S;=`$d<%lO#{U={[!PQ|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^>x[#VW|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^?u[#XW|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^@r^#WW|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#f,Y#f#gAn#g#o,Y#o;'S#{;'S;=`$d<%lO#{UAs^|SOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#i,Y#i#j<s#j#o,Y#o;'S#{;'S;=`$d<%lO#{UBvUlQ|SOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~C_O#a~",
|
tokenData: "C_~R|OX#{XY$jYZ%TZp#{pq$jqs#{st%ntu'Vuw#{wx'[xy'ayz'zz{#{{|(e|}#{}!O+X!O!P#{!P!Q-n!Q![)S![!]6Z!]!^%T!^!}#{!}#O6t#O#P8j#P#Q8o#Q#R#{#R#S9Y#S#T#{#T#Y,Y#Y#Z9s#Z#b,Y#b#c>q#c#f,Y#f#g?n#g#h,Y#h#i@k#i#o,Y#o#p#{#p#qBo#q;'S#{;'S;=`$d<%l~#{~O#{~~CYS$QU!OSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{S$gP;=`<%l#{^$qU!OS!zYOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U%[U!OS#[QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{^%uZkY!OSOY%nYZ#{Zt%ntu&huw%nwx&hx#O%n#O#P&h#P;'S%n;'S;=`'P<%lO%nY&mSkYOY&hZ;'S&h;'S;=`&y<%lO&hY&|P;=`<%l&h^'SP;=`<%l%n~'[O#V~~'aO#T~U'hU!OS#PQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(RU!OS#_QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U(jW!OSOt#{uw#{x!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U)ZY!OSwQOt#{uw#{x!O#{!O!P)y!P!Q#{!Q![)S![#O#{#P;'S#{;'S;=`$d<%lO#{U*OW!OSOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U*oW!OSwQOt#{uw#{x!Q#{!Q![*h![#O#{#P;'S#{;'S;=`$d<%lO#{U+^^!OSOt#{uw#{x}#{}!O,Y!O!Q#{!Q![)S![!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{U,_[!OSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{U-[U|Q!OSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U-sW!OSOt#{uw#{x!P#{!P!Q.]!Q#O#{#P;'S#{;'S;=`$d<%lO#{U.b^!OSOY/^YZ#{Zt/^tu0auw/^wx0ax!P/^!P!Q#{!Q!}/^!}#O5S#O#P2o#P;'S/^;'S;=`6T<%lO/^U/e^!OS!cQOY/^YZ#{Zt/^tu0auw/^wx0ax!P/^!P!Q3U!Q!}/^!}#O5S#O#P2o#P;'S/^;'S;=`6T<%lO/^Q0fX!cQOY0aZ!P0a!P!Q1R!Q!}0a!}#O1p#O#P2o#P;'S0a;'S;=`3O<%lO0aQ1UP!P!Q1XQ1^U!cQ#Z#[1X#]#^1X#a#b1X#g#h1X#i#j1X#m#n1XQ1sVOY1pZ#O1p#O#P2Y#P#Q0a#Q;'S1p;'S;=`2i<%lO1pQ2]SOY1pZ;'S1p;'S;=`2i<%lO1pQ2lP;=`<%l1pQ2rSOY0aZ;'S0a;'S;=`3O<%lO0aQ3RP;=`<%l0aU3ZW!OSOt#{uw#{x!P#{!P!Q3s!Q#O#{#P;'S#{;'S;=`$d<%lO#{U3zb!OS!cQOt#{uw#{x#O#{#P#Z#{#Z#[3s#[#]#{#]#^3s#^#a#{#a#b3s#b#g#{#g#h3s#h#i#{#i#j3s#j#m#{#m#n3s#n;'S#{;'S;=`$d<%lO#{U5X[!OSOY5SYZ#{Zt5Stu1puw5Swx1px#O5S#O#P2Y#P#Q/^#Q;'S5S;'S;=`5}<%lO5SU6QP;=`<%l5SU6WP;=`<%l/^U6bU!OS!TQOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U6{W#aQ!OSOt#{uw#{x!_#{!_!`7e!`#O#{#P;'S#{;'S;=`$d<%lO#{U7jV!OSOt#{uw#{x#O#{#P#Q8P#Q;'S#{;'S;=`$d<%lO#{U8WU#`Q!OSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~8oO#W~U8vU#bQ!OSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U9aU!OS![QOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{U9x]!OSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#U:q#U#o,Y#o;'S#{;'S;=`$d<%lO#{U:v^!OSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#`,Y#`#a;r#a#o,Y#o;'S#{;'S;=`$d<%lO#{U;w^!OSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#g,Y#g#h<s#h#o,Y#o;'S#{;'S;=`$d<%lO#{U<x^!OSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#X,Y#X#Y=t#Y#o,Y#o;'S#{;'S;=`$d<%lO#{U={[!RQ!OSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^>x[#XW!OSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^?u[#ZW!OSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#o,Y#o;'S#{;'S;=`$d<%lO#{^@r^#YW!OSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#f,Y#f#gAn#g#o,Y#o;'S#{;'S;=`$d<%lO#{UAs^!OSOt#{uw#{x}#{}!O,Y!O!_#{!_!`-T!`#O#{#P#T#{#T#i,Y#i#j<s#j#o,Y#o;'S#{;'S;=`$d<%lO#{UBvUnQ!OSOt#{uw#{x#O#{#P;'S#{;'S;=`$d<%lO#{~C_O#c~",
|
||||||
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#P~~", 11)],
|
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#R~~", 11)],
|
||||||
topRules: {"Program":[0,26]},
|
topRules: {"Program":[0,28]},
|
||||||
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: 22, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 22, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
|
||||||
tokenPrec: 1634
|
tokenPrec: 1730
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -595,6 +595,87 @@ describe('CompoundAssign', () => {
|
||||||
PositionalArg
|
PositionalArg
|
||||||
Number 3`)
|
Number 3`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('parses ??= operator', () => {
|
||||||
|
expect('x ??= 5').toMatchTree(`
|
||||||
|
CompoundAssign
|
||||||
|
AssignableIdentifier x
|
||||||
|
NullishEq ??=
|
||||||
|
Number 5`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses ??= with expression', () => {
|
||||||
|
expect('config ??= get-default').toMatchTree(`
|
||||||
|
CompoundAssign
|
||||||
|
AssignableIdentifier config
|
||||||
|
NullishEq ??=
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
Identifier get-default`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Nullish coalescing operator', () => {
|
||||||
|
test('? can still end an identifier', () => {
|
||||||
|
expect('what?').toMatchTree(`
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
Identifier what?`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('?? can still end an identifier', () => {
|
||||||
|
expect('what??').toMatchTree(`
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
Identifier what??`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('?? can still be in a word', () => {
|
||||||
|
expect('what??the').toMatchTree(`
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
Identifier what??the`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('?? can still start a word', () => {
|
||||||
|
expect('??what??the').toMatchTree(`
|
||||||
|
Word ??what??the`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses ?? operator', () => {
|
||||||
|
expect('x ?? 5').toMatchTree(`
|
||||||
|
ConditionalOp
|
||||||
|
Identifier x
|
||||||
|
NullishCoalesce ??
|
||||||
|
Number 5`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses chained ?? operators', () => {
|
||||||
|
expect('a ?? b ?? c').toMatchTree(`
|
||||||
|
ConditionalOp
|
||||||
|
ConditionalOp
|
||||||
|
Identifier a
|
||||||
|
NullishCoalesce ??
|
||||||
|
Identifier b
|
||||||
|
NullishCoalesce ??
|
||||||
|
Identifier c`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses ?? with expressions', () => {
|
||||||
|
expect('get-value ?? default-value').toMatchTree(`
|
||||||
|
ConditionalOp
|
||||||
|
Identifier get-value
|
||||||
|
NullishCoalesce ??
|
||||||
|
Identifier default-value`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('parses ?? with parenthesized function call', () => {
|
||||||
|
expect('get-value ?? (default 10)').toMatchTree(`
|
||||||
|
ConditionalOp
|
||||||
|
Identifier get-value
|
||||||
|
NullishCoalesce ??
|
||||||
|
ParenExpr
|
||||||
|
FunctionCall
|
||||||
|
Identifier default
|
||||||
|
PositionalArg
|
||||||
|
Number 10`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('DotGet whitespace sensitivity', () => {
|
describe('DotGet whitespace sensitivity', () => {
|
||||||
|
|
|
||||||
|
|
@ -193,6 +193,15 @@ const chooseIdentifierToken = (input: InputStream, stack: Stack): number => {
|
||||||
|
|
||||||
const nextCh = getFullCodePoint(input, peekPos)
|
const nextCh = getFullCodePoint(input, peekPos)
|
||||||
const nextCh2 = getFullCodePoint(input, peekPos + 1)
|
const nextCh2 = getFullCodePoint(input, peekPos + 1)
|
||||||
|
const nextCh3 = getFullCodePoint(input, peekPos + 2)
|
||||||
|
|
||||||
|
// Check for ??= (three-character compound operator)
|
||||||
|
if (nextCh === 63 /* ? */ && nextCh2 === 63 /* ? */ && nextCh3 === 61 /* = */) {
|
||||||
|
const charAfterOp = getFullCodePoint(input, peekPos + 3)
|
||||||
|
if (isWhiteSpace(charAfterOp) || charAfterOp === -1 /* EOF */) {
|
||||||
|
return AssignableIdentifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for compound assignment operators: +=, -=, *=, /=, %=
|
// Check for compound assignment operators: +=, -=, *=, /=, %=
|
||||||
if (
|
if (
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user