Compare commits
1 Commits
415470b43d
...
6ca64d362c
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ca64d362c |
|
|
@ -211,16 +211,37 @@ export class Compiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
case terms.DotGet: {
|
case terms.DotGet: {
|
||||||
|
// DotGet is parsed into a nested tree because it's hard to parse it into a flat one.
|
||||||
|
// However, we want a flat tree - so we're going to pretend like we are getting one from the parser.
|
||||||
|
//
|
||||||
|
// This: DotGet(config, DotGet(script, name))
|
||||||
|
// Becomes: DotGet(config, script, name)
|
||||||
const { objectName, property } = getDotGetParts(node, input)
|
const { objectName, property } = getDotGetParts(node, input)
|
||||||
const instructions: ProgramItem[] = []
|
const instructions: ProgramItem[] = []
|
||||||
|
|
||||||
instructions.push(['TRY_LOAD', objectName])
|
instructions.push(['TRY_LOAD', objectName])
|
||||||
if (property.type.id === terms.ParenExpr) {
|
|
||||||
instructions.push(...this.#compileNode(property, input))
|
const flattenProperty = (prop: SyntaxNode): void => {
|
||||||
} else {
|
if (prop.type.id === terms.DotGet) {
|
||||||
const propertyValue = input.slice(property.from, property.to)
|
const nestedParts = getDotGetParts(prop, input)
|
||||||
instructions.push(['PUSH', propertyValue])
|
|
||||||
|
const nestedObjectValue = input.slice(nestedParts.object.from, nestedParts.object.to)
|
||||||
|
instructions.push(['PUSH', nestedObjectValue])
|
||||||
|
instructions.push(['DOT_GET'])
|
||||||
|
|
||||||
|
flattenProperty(nestedParts.property)
|
||||||
|
} else {
|
||||||
|
if (prop.type.id === terms.ParenExpr) {
|
||||||
|
instructions.push(...this.#compileNode(prop, input))
|
||||||
|
} else {
|
||||||
|
const propertyValue = input.slice(prop.from, prop.to)
|
||||||
|
instructions.push(['PUSH', propertyValue])
|
||||||
|
}
|
||||||
|
instructions.push(['DOT_GET'])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
instructions.push(['DOT_GET'])
|
|
||||||
|
flattenProperty(property)
|
||||||
return instructions
|
return instructions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -284,6 +284,43 @@ describe('dot get', () => {
|
||||||
test('use parens expr with dot-get', () => {
|
test('use parens expr with dot-get', () => {
|
||||||
expect(`a = 1; arr = array 'a' 'b' 'c'; arr.(1 + a)`).toEvaluateTo('c', { array })
|
expect(`a = 1; arr = array 'a' 'b' 'c'; arr.(1 + a)`).toEvaluateTo('c', { array })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('chained dot get: two levels', () => {
|
||||||
|
expect(`obj = [inner=[value=42]]; obj.inner.value`).toEvaluateTo(42)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chained dot get: three levels', () => {
|
||||||
|
expect(`obj = [a=[b=[c=123]]]; obj.a.b.c`).toEvaluateTo(123)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chained dot get: four levels', () => {
|
||||||
|
expect(`obj = [w=[x=[y=[z='deep']]]]; obj.w.x.y.z`).toEvaluateTo('deep')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chained dot get with numeric index', () => {
|
||||||
|
expect(`obj = [items=[1 2 3]]; obj.items.0`).toEvaluateTo(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chained dot get in expression', () => {
|
||||||
|
expect(`config = [server=[port=3000]]; config.server.port + 1`).toEvaluateTo(3001)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chained dot get as function argument', () => {
|
||||||
|
const double = (x: number) => x * 2
|
||||||
|
expect(`obj = [val=[num=21]]; double obj.val.num`).toEvaluateTo(42, { double })
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chained dot get in binary operation', () => {
|
||||||
|
expect(`a = [x=[y=10]]; b = [x=[y=20]]; a.x.y + b.x.y`).toEvaluateTo(30)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chained dot get with parens at end', () => {
|
||||||
|
expect(`idx = 1; obj = [items=[10 20 30]]; obj.items.(idx)`).toEvaluateTo(20)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('mixed chained and simple dot get', () => {
|
||||||
|
expect(`obj = [a=1 b=[c=2]]; obj.a + obj.b.c`).toEvaluateTo(3)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('default params', () => {
|
describe('default params', () => {
|
||||||
|
|
|
||||||
|
|
@ -301,9 +301,9 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (![terms.Identifier, terms.Number, terms.ParenExpr].includes(property.type.id)) {
|
if (![terms.Identifier, terms.Number, terms.ParenExpr, terms.DotGet].includes(property.type.id)) {
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
`DotGet property must be an Identifier or Number, got ${property.type.name}`,
|
`DotGet property must be an Identifier, Number, ParenExpr, or DotGet, got ${property.type.name}`,
|
||||||
property.from,
|
property.from,
|
||||||
property.to
|
property.to
|
||||||
)
|
)
|
||||||
|
|
@ -311,7 +311,7 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => {
|
||||||
|
|
||||||
const objectName = input.slice(object.from, object.to)
|
const objectName = input.slice(object.from, object.to)
|
||||||
|
|
||||||
return { objectName, property }
|
return { object, objectName, property }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getTryExprParts = (node: SyntaxNode, input: string) => {
|
export const getTryExprParts = (node: SyntaxNode, input: string) => {
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ expression {
|
||||||
|
|
||||||
@skip {} {
|
@skip {} {
|
||||||
DotGet {
|
DotGet {
|
||||||
IdentifierBeforeDot dot (Number | Identifier | ParenExpr)
|
IdentifierBeforeDot dot (DotGet | Number | Identifier | ParenExpr)
|
||||||
}
|
}
|
||||||
|
|
||||||
String {
|
String {
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ import {highlighting} from "./highlight"
|
||||||
const spec_Identifier = {__proto__:null,while:78, null:112, catch:118, finally:124, end:126, if:134, else:140, try:158, throw:162, import:166}
|
const spec_Identifier = {__proto__:null,while:78, null:112, catch:118, finally:124, end:126, if:134, else:140, try:158, throw:162, import:166}
|
||||||
export const parser = LRParser.deserialize({
|
export const parser = LRParser.deserialize({
|
||||||
version: 14,
|
version: 14,
|
||||||
states: "=|QYQbOOO!mOpO'#DXO!rOSO'#D`OOQa'#D`'#D`O%mQcO'#DvO(mQcO'#EiOOQ`'#Ew'#EwO)WQRO'#DwO+]QcO'#EgO+vQbO'#DVOOQa'#Dy'#DyO.[QbO'#DzOOQa'#Ei'#EiO.cQcO'#EiO0aQcO'#EhO1fQcO'#EgO1sQRO'#ESOOQ`'#Eg'#EgO2[QbO'#EgO2cQQO'#EfOOQ`'#Ef'#EfOOQ`'#EU'#EUQYQbOOO2nQbO'#D[O2yQbO'#DpO3tQbO'#DSO4oQQO'#D|O3tQbO'#EOO4tQbO'#EQO4|ObO,59sO5XQbO'#DbO5aQWO'#DcOOOO'#Eo'#EoOOOO'#EZ'#EZO5uOSO,59zOOQa,59z,59zOOQ`'#DZ'#DZO6TQbO'#DoOOQ`'#Em'#EmOOQ`'#E^'#E^O6_QbO,5:^OOQa'#Eh'#EhO3tQbO,5:cO3tQbO,5:cO3tQbO,5:cO3tQbO,5:cO3tQbO,59pO3tQbO,59pO3tQbO,59pO3tQbO,59pOOQ`'#EW'#EWO+vQbO,59qO7XQcO'#DvO7`QcO'#EiO7gQRO,59qO7qQQO,59qO7vQQO,59qO8OQQO,59qO8ZQRO,59qO8sQRO,59qO8zQQO'#DQO9PQbO,5:fO9WQQO,5:eOOQa,5:f,5:fO9cQbO,5:fO9mQbO,5:oO9mQbO,5:nO:}QbO,5:gO;UQbO,59lOOQ`,5;Q,5;QO9mQbO'#EVOOQ`-E8S-E8SOOQ`'#EX'#EXO;pQbO'#D]O;{QbO'#D^OOQO'#EY'#EYO;sQQO'#D]O<aQQO,59vO<fQcO'#EhO=cQRO'#EvO>`QRO'#EvOOQO'#Ev'#EvO>gQQO,5:[O>lQRO,59nO>sQRO,59nO:}QbO,5:hO?RQcO,5:jO@aQcO,5:jO@}QcO,5:jOArQbO,5:lOOQ`'#Eb'#EbO4tQbO,5:lOOQa1G/_1G/_OOOO,59|,59|OOOO,59},59}OOOO-E8X-E8XOOQa1G/f1G/fOOQ`,5:Z,5:ZOOQ`-E8[-E8[OOQa1G/}1G/}OCkQcO1G/}OCuQcO1G/}OETQcO1G/}OE_QcO1G/}OElQcO1G/}OOQa1G/[1G/[OF}QcO1G/[OGUQcO1G/[OG]QcO1G/[OH[QcO1G/[OGdQcO1G/[OOQ`-E8U-E8UOHrQRO1G/]OH|QQO1G/]OIRQQO1G/]OIZQQO1G/]OIfQRO1G/]OImQRO1G/]OItQbO,59rOJOQQO1G/]OOQa1G/]1G/]OJWQQO1G0POOQa1G0Q1G0QOJcQbO1G0QOOQO'#E`'#E`OJWQQO1G0POOQa1G0P1G0POOQ`'#Ea'#EaOJcQbO1G0QOJmQbO1G0ZOKXQbO1G0YOKsQbO'#DjOLUQbO'#DjOLiQbO1G0ROOQ`-E8T-E8TOOQ`,5:q,5:qOOQ`-E8V-E8VOLtQQO,59wOOQO,59x,59xOOQO-E8W-E8WOL|QbO1G/bO:}QbO1G/vO:}QbO1G/YOMTQbO1G0SOM`QbO1G0WOM}QbO1G0WOOQ`-E8`-E8`ONUQQO7+$wOOQa7+$w7+$wON^QQO1G/^ONfQQO7+%kOOQa7+%k7+%kONqQbO7+%lOOQa7+%l7+%lOOQO-E8^-E8^OOQ`-E8_-E8_OOQ`'#E['#E[ON{QQO'#E[O! TQbO'#EuOOQ`,5:U,5:UO! hQbO'#DhO! mQQO'#DkOOQ`7+%m7+%mO! rQbO7+%mO! wQbO7+%mO!!PQbO7+$|O!!_QbO7+$|O!!oQbO7+%bO!!wQbO7+$tOOQ`7+%n7+%nO!!|QbO7+%nO!#RQbO7+%nO!#ZQbO7+%rOOQa<<Hc<<HcO!#xQbO7+$xO!$VQQO7+$xOOQa<<IV<<IVOOQa<<IW<<IWOOQ`,5:v,5:vOOQ`-E8Y-E8YO!$_QQO,5:SO:}QbO,5:VOOQ`<<IX<<IXO!$dQbO<<IXOOQ`<<Hh<<HhO!$iQbO<<HhO!$nQbO<<HhO!$vQbO<<HhOOQ`'#E_'#E_O!%RQbO<<H|O!%ZQbO'#DuOOQ`<<H|<<H|O!%cQbO<<H|OOQ`<<H`<<H`OOQ`<<IY<<IYO!%hQbO<<IYOOQO,5:w,5:wO!%mQbO<<HdOOQO-E8Z-E8ZO:}QbO1G/nOOQ`1G/q1G/qOOQ`AN>sAN>sOOQ`AN>SAN>SO!%zQbOAN>SO!&PQbOAN>SOOQ`-E8]-E8]OOQ`AN>hAN>hO!&XQbOAN>hO2yQbO,5:_O:}QbO,5:aOOQ`AN>tAN>tPItQbO'#EWOOQ`7+%Y7+%YOOQ`G23nG23nO!&^QbOG23nP!%^QbO'#DsOOQ`G24SG24SO!&cQQO1G/yOOQ`1G/{1G/{OOQ`LD)YLD)YO:}QbO7+%eOOQ`<<IP<<IP",
|
states: "=|QYQbOOO!mOpO'#DXO!rOSO'#D`OOQa'#D`'#D`O%mQcO'#DvO(mQcO'#EiOOQ`'#Ew'#EwO)WQRO'#DwO+]QcO'#EgO+vQbO'#DVOOQa'#Dy'#DyO.[QbO'#DzOOQa'#Ei'#EiO.cQcO'#EiO0aQcO'#EhO1fQcO'#EgO1sQRO'#ESOOQ`'#Eg'#EgO2[QbO'#EgO2cQQO'#EfOOQ`'#Ef'#EfOOQ`'#EU'#EUQYQbOOO2nQbO'#D[O2yQbO'#DpO3tQbO'#DSO4oQQO'#D|O3tQbO'#EOO4tQbO'#EQO4|ObO,59sO5[QbO'#DbO5dQWO'#DcOOOO'#Eo'#EoOOOO'#EZ'#EZO5xOSO,59zOOQa,59z,59zOOQ`'#DZ'#DZO6WQbO'#DoOOQ`'#Em'#EmOOQ`'#E^'#E^O6bQbO,5:^OOQa'#Eh'#EhO3tQbO,5:cO3tQbO,5:cO3tQbO,5:cO3tQbO,5:cO3tQbO,59pO3tQbO,59pO3tQbO,59pO3tQbO,59pOOQ`'#EW'#EWO+vQbO,59qO7[QcO'#DvO7cQcO'#EiO7jQRO,59qO7tQQO,59qO7yQQO,59qO8RQQO,59qO8^QRO,59qO8vQRO,59qO8}QQO'#DQO9SQbO,5:fO9ZQQO,5:eOOQa,5:f,5:fO9fQbO,5:fO9pQbO,5:oO9pQbO,5:nO;QQbO,5:gO;XQbO,59lOOQ`,5;Q,5;QO9pQbO'#EVOOQ`-E8S-E8SOOQ`'#EX'#EXO;sQbO'#D]O<OQbO'#D^OOQO'#EY'#EYO;vQQO'#D]O<dQQO,59vO<iQcO'#EhO=fQRO'#EvO>cQRO'#EvOOQO'#Ev'#EvO>jQQO,5:[O>oQRO,59nO>vQRO,59nO;QQbO,5:hO?UQcO,5:jO@dQcO,5:jOAQQcO,5:jOAuQbO,5:lOOQ`'#Eb'#EbO4tQbO,5:lOOQa1G/_1G/_OOOO,59|,59|OOOO,59},59}OOOO-E8X-E8XOOQa1G/f1G/fOOQ`,5:Z,5:ZOOQ`-E8[-E8[OOQa1G/}1G/}OCnQcO1G/}OCxQcO1G/}OEWQcO1G/}OEbQcO1G/}OEoQcO1G/}OOQa1G/[1G/[OGQQcO1G/[OGXQcO1G/[OG`QcO1G/[OH_QcO1G/[OGgQcO1G/[OOQ`-E8U-E8UOHuQRO1G/]OIPQQO1G/]OIUQQO1G/]OI^QQO1G/]OIiQRO1G/]OIpQRO1G/]OIwQbO,59rOJRQQO1G/]OOQa1G/]1G/]OJZQQO1G0POOQa1G0Q1G0QOJfQbO1G0QOOQO'#E`'#E`OJZQQO1G0POOQa1G0P1G0POOQ`'#Ea'#EaOJfQbO1G0QOJpQbO1G0ZOK[QbO1G0YOKvQbO'#DjOLXQbO'#DjOLlQbO1G0ROOQ`-E8T-E8TOOQ`,5:q,5:qOOQ`-E8V-E8VOLwQQO,59wOOQO,59x,59xOOQO-E8W-E8WOMPQbO1G/bO;QQbO1G/vO;QQbO1G/YOMWQbO1G0SOMcQbO1G0WONQQbO1G0WOOQ`-E8`-E8`ONXQQO7+$wOOQa7+$w7+$wONaQQO1G/^ONiQQO7+%kOOQa7+%k7+%kONtQbO7+%lOOQa7+%l7+%lOOQO-E8^-E8^OOQ`-E8_-E8_OOQ`'#E['#E[O! OQQO'#E[O! WQbO'#EuOOQ`,5:U,5:UO! kQbO'#DhO! pQQO'#DkOOQ`7+%m7+%mO! uQbO7+%mO! zQbO7+%mO!!SQbO7+$|O!!bQbO7+$|O!!rQbO7+%bO!!zQbO7+$tOOQ`7+%n7+%nO!#PQbO7+%nO!#UQbO7+%nO!#^QbO7+%rOOQa<<Hc<<HcO!#{QbO7+$xO!$YQQO7+$xOOQa<<IV<<IVOOQa<<IW<<IWOOQ`,5:v,5:vOOQ`-E8Y-E8YO!$bQQO,5:SO;QQbO,5:VOOQ`<<IX<<IXO!$gQbO<<IXOOQ`<<Hh<<HhO!$lQbO<<HhO!$qQbO<<HhO!$yQbO<<HhOOQ`'#E_'#E_O!%UQbO<<H|O!%^QbO'#DuOOQ`<<H|<<H|O!%fQbO<<H|OOQ`<<H`<<H`OOQ`<<IY<<IYO!%kQbO<<IYOOQO,5:w,5:wO!%pQbO<<HdOOQO-E8Z-E8ZO;QQbO1G/nOOQ`1G/q1G/qOOQ`AN>sAN>sOOQ`AN>SAN>SO!%}QbOAN>SO!&SQbOAN>SOOQ`-E8]-E8]OOQ`AN>hAN>hO!&[QbOAN>hO2yQbO,5:_O;QQbO,5:aOOQ`AN>tAN>tPIwQbO'#EWOOQ`7+%Y7+%YOOQ`G23nG23nO!&aQbOG23nP!%aQbO'#DsOOQ`G24SG24SO!&fQQO1G/yOOQ`1G/{1G/{OOQ`LD)YLD)YO;QQbO7+%eOOQ`<<IP<<IP",
|
||||||
stateData: "!&k~O#XOSrOS~OlSOm`On[OoPOpROqgOwiO|[O!WRO!X[O!Y[O!ehO!l[O!qjO!skO!ulO#^XO#_dO#bQO#mYO#nZO~O#`mO~O!TpO#bsO#dnO#eoO~OlyOn[OoPOpROqgO|[O!RuO!WRO!X[O!Y[O!btO!l[O#^XO#bQO#mYO#nZOP#[XQ#[XR#[XS#[XT#[XU#[XW#[XX#[XY#[XZ#[X[#[X]#[X^#[Xd#[Xe#[Xf#[Xg#[Xh#[Xi#[Xj#[Xu!jX!Z!jX#l!jX~O#_!jX#p!jX!]!jX!`!jX!a!jX!h!jX~P#QOlyOn[OoPOpROqgO|[O!RuO!WRO!X[O!Y[O!btO!l[O#^XO#bQO#mYO#nZOP#]XQ#]XR#]XS#]XT#]XU#]XW#]XX#]XY#]XZ#]X[#]X]#]X^#]Xd#]Xe#]Xf#]Xg#]Xh#]Xi#]Xj#]Xu#]X#l#]X~O#_#]X#p#]X!Z#]X!]#]X!`#]X!a#]X!h#]X~P&TOP{OQ{OR|OS|OT!POU!QOW!OOX!OOY!OOZ!OO[!OO]!OO^zOd}Oe}Of}Og}Oh}Oi}Oj!RO~OP{OQ{OR|OS|Od}Oe}Of}Og}Oh}Oi}Ou#ZX~O#_#ZX#p#ZX!]#ZX!`#ZX!a#ZX#l#ZX!h#ZX~P*hOl!UOm`On[OoPOpROqgOwiO|[O!WRO!X[O!Y[O!ehO!l[O!qjO!skO!ulO#^XO#_!SO#bQO#mYO#nZO~OlyOn[OoPOpRO|[O!RuO!WRO!X[O!Y[O!l[O#^XO#_!SO#bQO#mYO#nZO~O#o!aO~P-ZOV!cO#_#]X#p#]X!]#]X!`#]X!a#]X!h#]X~P'VOP#[XQ#[XR#[XS#[XT#[XU#[XW#[XX#[XY#[XZ#[X[#[X]#[X^#[Xd#[Xe#[Xf#[Xg#[Xh#[Xi#[Xj#[Xu#ZX~O#_#ZX#p#ZX!]#ZX!`#ZX!a#ZX#l#ZX!h#ZX~P.|Ou#ZX#_#ZX#p#ZX!]#ZX!`#ZX!a#ZX#l#ZX!h#ZX~OT!POU!QOj!RO~P0zOV!cO_!dO`!dOa!dOb!dOc!dOk!dO~O!Z!eO~P0zOu!hO#_!gO#p!gO~Ol!jO!R!lO!Z!PP~Ol!pOn[OoPOpRO|[O!WRO!X[O!Y[O!l[O#^XO#bQO#mYO#nZO~OlyOn[OoPOpRO|[O!WRO!X[O!Y[O!l[O#^XO#bQO#mYO#nZO~O!Z!wO~Ol!jO!RuO~Ol#OO|#OO#^XO~Ol#PO#^XO~O#b#QO#d#QO#e#QO#f#QO#g#QO#h#QO~O!TpO#b#SO#dnO#eoO~OqgO!b#TO~P3tOqgO!RuO!btOu!fa!Z!fa#_!fa#p!fa#l!fa!]!fa!`!fa!a!fa!h!fa~P3tO#_!SO~P#QO#_!SO~P&TO#_!SO#l#lO~P*hO#l#lO~O#l#lOu#ZX~O!Z!eO#l#lOu#ZX~O#l#lO~P.|OT!POU!QOj!RO#_!SOu#ZX~O#l#lO~P8bOu!hO~O#o#nO~P-ZO!RuO#_#pO#o#rO~O#_#sO#o#nO~P3tOlSOm`On[OoPOpROqgOwiO|[O!WRO!X[O!Y[O!ehO!l[O!qjO!skO!ulO#^XO#bQO#mYO#nZO~O#_#xO~P9mOu!hO#_ta#pta#lta!]ta!`ta!ata!hta~Ol!jO!R!lO!Z!PX~OpRO|$OO!WRO!X$OO!Y$OO#bQO~O!Z$QO~OqgO!RuO!btOT#[XU#[XW#[XX#[XY#[XZ#[X[#[X]#[Xj#[X!Z#[X~P3tOT!POU!QOj!RO!Z#jX~OT!POU!QOW!OOX!OOY!OOZ!OO[!OO]!OOj!RO~O!Z#jX~P=qO!Z$RO~O!Z$SO~P=qOT!POU!QOj!RO!Z$SO~Ou!ra#_!ra#p!ra!]!ra!`!ra!a!ra#l!ra!h!ra~P)WOP{OQ{OR|OS|Od}Oe}Of}Og}Oh}Oi}O~Ou!ra#_!ra#p!ra!]!ra!`!ra!a!ra#l!ra!h!ra~P?oOT!POU!QOj!ROu!ra#_!ra#p!ra!]!ra!`!ra!a!ra#l!ra!h!ra~Ol!jO!RuOu!ta#_!ta#p!ta!]!ta!`!ta!a!ta#l!ta!h!ta~O^zOR!kiS!kid!kie!kif!kig!kih!kii!kiu!ki#_!ki#p!ki#l!ki!]!ki!`!ki!a!ki!h!ki~OP!kiQ!ki~PBdOP{OQ{O~PBdOP{OQ{Od!kie!kif!kig!kih!kii!kiu!ki#_!ki#p!ki#l!ki!]!ki!`!ki!a!ki!h!ki~OR!kiS!ki~PDPOR|OS|O^zO~PDPOR|OS|O~PDPOW!OOX!OOY!OOZ!OO[!OO]!OOTxijxiuxi#_xi#pxi#lxi!Zxi!]xi!`xi!axi!hxi~OU!QO~PEvOU!QO~PFYOUxi~PEvOT!POU!QOjxiuxi#_xi#pxi#lxi!Zxi!]xi!`xi!axi!hxi~OW!OOX!OOY!OOZ!OO[!OO]!OO~PGdO#_!SO#l$YO~P*hO#l$YO~O#l$YOu#ZX~O!Z!eO#l$YOu#ZX~O#l$YO~P.|O#l$YO~P8bOqgO!btO~P-ZO#_!SO#l$YO~O!RuO#_#pO#o$]O~O#_#sO#o$_O~P3tOu!hO#_!wi#p!wi!]!wi!`!wi!a!wi#l!wi!h!wi~Ou!hO#_!vi#p!vi!]!vi!`!vi!a!vi#l!vi!h!vi~Ou!hO!]!^X!`!^X!a!^X!h!^X~O#_$bO!]#iP!`#iP!a#iP!h#iP~P9mO!]$fO!`$gO!a$hO~O!R!lO!Z!Pa~O#_$lO~P9mO!]$fO!`$gO!a$oO~O!RuOu!ti#_!ti#p!ti!]!ti!`!ti!a!ti#l!ti!h!ti~Ol!jO~PM`O#_!SO#l$sO~O#_!SO#lzi~O!RuO#_#pO#o$vO~O#_#sO#o$wO~P3tOu!hO#_$xO~O#_$bO!]#iX!`#iX!a#iX!h#iX~P9mOl$zO~O!Z${O~O!a$|O~O!`$gO!a$|O~Ou!hO!]$fO!`$gO!a%OO~O#_$bO!]#iP!`#iP!a#iP~P9mO!a%VO!h%UO~O!a%XO~O!a%YO~O!`$gO!a%YO~O!RuOu!tq#_!tq#p!tq!]!tq!`!tq!a!tq#l!tq!h!tq~OqgO!btO#lzq~P-ZO#_!SO#lzq~O!Z%_O~O!a%aO~O!a%bO~O!`$gO!a%bO~O!]$fO!`$gO!a%bO~O!a%fO!h%UO~O!Z%iO!e%hO~O!a%fO~O!a%jO~OqgO!btO#lzy~P-ZO!a%mO~O!`$gO!a%mO~O!a%pO~O!a%sO~O!Z%tO~O|!l~",
|
stateData: "!&n~O#XOSrOS~OlSOm`On[OoPOpROqgOwiO|[O!WRO!X[O!Y[O!ehO!l[O!qjO!skO!ulO#^XO#_dO#bQO#mYO#nZO~O#`mO~O!TpO#bsO#dnO#eoO~OlyOn[OoPOpROqgO|[O!RuO!WRO!X[O!Y[O!btO!l[O#^XO#bQO#mYO#nZOP#[XQ#[XR#[XS#[XT#[XU#[XW#[XX#[XY#[XZ#[X[#[X]#[X^#[Xd#[Xe#[Xf#[Xg#[Xh#[Xi#[Xj#[Xu!jX!Z!jX#l!jX~O#_!jX#p!jX!]!jX!`!jX!a!jX!h!jX~P#QOlyOn[OoPOpROqgO|[O!RuO!WRO!X[O!Y[O!btO!l[O#^XO#bQO#mYO#nZOP#]XQ#]XR#]XS#]XT#]XU#]XW#]XX#]XY#]XZ#]X[#]X]#]X^#]Xd#]Xe#]Xf#]Xg#]Xh#]Xi#]Xj#]Xu#]X#l#]X~O#_#]X#p#]X!Z#]X!]#]X!`#]X!a#]X!h#]X~P&TOP{OQ{OR|OS|OT!POU!QOW!OOX!OOY!OOZ!OO[!OO]!OO^zOd}Oe}Of}Og}Oh}Oi}Oj!RO~OP{OQ{OR|OS|Od}Oe}Of}Og}Oh}Oi}Ou#ZX~O#_#ZX#p#ZX!]#ZX!`#ZX!a#ZX#l#ZX!h#ZX~P*hOl!UOm`On[OoPOpROqgOwiO|[O!WRO!X[O!Y[O!ehO!l[O!qjO!skO!ulO#^XO#_!SO#bQO#mYO#nZO~OlyOn[OoPOpRO|[O!RuO!WRO!X[O!Y[O!l[O#^XO#_!SO#bQO#mYO#nZO~O#o!aO~P-ZOV!cO#_#]X#p#]X!]#]X!`#]X!a#]X!h#]X~P'VOP#[XQ#[XR#[XS#[XT#[XU#[XW#[XX#[XY#[XZ#[X[#[X]#[X^#[Xd#[Xe#[Xf#[Xg#[Xh#[Xi#[Xj#[Xu#ZX~O#_#ZX#p#ZX!]#ZX!`#ZX!a#ZX#l#ZX!h#ZX~P.|Ou#ZX#_#ZX#p#ZX!]#ZX!`#ZX!a#ZX#l#ZX!h#ZX~OT!POU!QOj!RO~P0zOV!cO_!dO`!dOa!dOb!dOc!dOk!dO~O!Z!eO~P0zOu!hO#_!gO#p!gO~Ol!jO!R!lO!Z!PP~Ol!pOn[OoPOpRO|[O!WRO!X[O!Y[O!l[O#^XO#bQO#mYO#nZO~OlyOn[OoPOpRO|[O!WRO!X[O!Y[O!l[O#^XO#bQO#mYO#nZO~O!Z!wO~Ol!jO!RuO~Ol#OOoPO|#OO#^XO~Ol#PO#^XO~O#b#QO#d#QO#e#QO#f#QO#g#QO#h#QO~O!TpO#b#SO#dnO#eoO~OqgO!b#TO~P3tOqgO!RuO!btOu!fa!Z!fa#_!fa#p!fa#l!fa!]!fa!`!fa!a!fa!h!fa~P3tO#_!SO~P#QO#_!SO~P&TO#_!SO#l#lO~P*hO#l#lO~O#l#lOu#ZX~O!Z!eO#l#lOu#ZX~O#l#lO~P.|OT!POU!QOj!RO#_!SOu#ZX~O#l#lO~P8eOu!hO~O#o#nO~P-ZO!RuO#_#pO#o#rO~O#_#sO#o#nO~P3tOlSOm`On[OoPOpROqgOwiO|[O!WRO!X[O!Y[O!ehO!l[O!qjO!skO!ulO#^XO#bQO#mYO#nZO~O#_#xO~P9pOu!hO#_ta#pta#lta!]ta!`ta!ata!hta~Ol!jO!R!lO!Z!PX~OpRO|$OO!WRO!X$OO!Y$OO#bQO~O!Z$QO~OqgO!RuO!btOT#[XU#[XW#[XX#[XY#[XZ#[X[#[X]#[Xj#[X!Z#[X~P3tOT!POU!QOj!RO!Z#jX~OT!POU!QOW!OOX!OOY!OOZ!OO[!OO]!OOj!RO~O!Z#jX~P=tO!Z$RO~O!Z$SO~P=tOT!POU!QOj!RO!Z$SO~Ou!ra#_!ra#p!ra!]!ra!`!ra!a!ra#l!ra!h!ra~P)WOP{OQ{OR|OS|Od}Oe}Of}Og}Oh}Oi}O~Ou!ra#_!ra#p!ra!]!ra!`!ra!a!ra#l!ra!h!ra~P?rOT!POU!QOj!ROu!ra#_!ra#p!ra!]!ra!`!ra!a!ra#l!ra!h!ra~Ol!jO!RuOu!ta#_!ta#p!ta!]!ta!`!ta!a!ta#l!ta!h!ta~O^zOR!kiS!kid!kie!kif!kig!kih!kii!kiu!ki#_!ki#p!ki#l!ki!]!ki!`!ki!a!ki!h!ki~OP!kiQ!ki~PBgOP{OQ{O~PBgOP{OQ{Od!kie!kif!kig!kih!kii!kiu!ki#_!ki#p!ki#l!ki!]!ki!`!ki!a!ki!h!ki~OR!kiS!ki~PDSOR|OS|O^zO~PDSOR|OS|O~PDSOW!OOX!OOY!OOZ!OO[!OO]!OOTxijxiuxi#_xi#pxi#lxi!Zxi!]xi!`xi!axi!hxi~OU!QO~PEyOU!QO~PF]OUxi~PEyOT!POU!QOjxiuxi#_xi#pxi#lxi!Zxi!]xi!`xi!axi!hxi~OW!OOX!OOY!OOZ!OO[!OO]!OO~PGgO#_!SO#l$YO~P*hO#l$YO~O#l$YOu#ZX~O!Z!eO#l$YOu#ZX~O#l$YO~P.|O#l$YO~P8eOqgO!btO~P-ZO#_!SO#l$YO~O!RuO#_#pO#o$]O~O#_#sO#o$_O~P3tOu!hO#_!wi#p!wi!]!wi!`!wi!a!wi#l!wi!h!wi~Ou!hO#_!vi#p!vi!]!vi!`!vi!a!vi#l!vi!h!vi~Ou!hO!]!^X!`!^X!a!^X!h!^X~O#_$bO!]#iP!`#iP!a#iP!h#iP~P9pO!]$fO!`$gO!a$hO~O!R!lO!Z!Pa~O#_$lO~P9pO!]$fO!`$gO!a$oO~O!RuOu!ti#_!ti#p!ti!]!ti!`!ti!a!ti#l!ti!h!ti~Ol!jO~PMcO#_!SO#l$sO~O#_!SO#lzi~O!RuO#_#pO#o$vO~O#_#sO#o$wO~P3tOu!hO#_$xO~O#_$bO!]#iX!`#iX!a#iX!h#iX~P9pOl$zO~O!Z${O~O!a$|O~O!`$gO!a$|O~Ou!hO!]$fO!`$gO!a%OO~O#_$bO!]#iP!`#iP!a#iP~P9pO!a%VO!h%UO~O!a%XO~O!a%YO~O!`$gO!a%YO~O!RuOu!tq#_!tq#p!tq!]!tq!`!tq!a!tq#l!tq!h!tq~OqgO!btO#lzq~P-ZO#_!SO#lzq~O!Z%_O~O!a%aO~O!a%bO~O!`$gO!a%bO~O!]$fO!`$gO!a%bO~O!a%fO!h%UO~O!Z%iO!e%hO~O!a%fO~O!a%jO~OqgO!btO#lzy~P-ZO!a%mO~O!`$gO!a%mO~O!a%pO~O!a%sO~O!Z%tO~O|!l~",
|
||||||
goto: "9Z#lPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#mP$WP$m%k&y'PP(Z(g)a)dP)jP*q*qPPPP*uP+R+kPPP,R#mP,s-^P-b-h-}P.t/x$W$WP$WP$WP$W$W1O1U1b2U2d2n2t2{3R3]3c3m3wPPP4V4Z5O6tPPP8OP8`PPPPP8d8j8praOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQ!YXR#f!TwaOXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tr_Of!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQ!]XS!qh%hQ!viQ!zkQ#]!QQ#_!PQ#b!RR#i!TvTOfh!c!d!e!h!w#x$Q$R$S$d$l${%_%h%i%t!W[STZikuxz{|}!O!P!Q!R!U!V!_!b!p#j#o#t$^$t%]%kS!VX!TQ#OmR#PnQ!XXR#e!TrSOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%t!WySTZikuxz{|}!O!P!Q!R!U!V!_!b!p#j#o#t$^$t%]%kS!UX!TT!ph%hevSTx!U!V!p#j$t%]%kraOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tdtSTx!U!V!p#j$t%]%kQ!YXQ#TuR#f!TR!ogX!mg!k!n#}#S[OSTXZfhikuxz{|}!O!P!Q!R!T!U!V!_!b!c!d!e!h!p!w#j#o#t#x$Q$R$S$^$d$l$t${%]%_%h%i%k%tR$O!lTpQrQ$j#yQ$q$TQ%Q$kR%d%RQ#y!eQ$T!wQ$m$RQ$n$SQ%`${Q%l%_Q%r%iR%u%tQ$i#yQ$p$TQ$}$jQ%P$kQ%Z$qS%c%Q%RR%n%ddvSTx!U!V!p#j$t%]%kQ!`Z[!|l!{!}$U$V$rQ#m!_X#p!`#m#q$[vUOXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tT!sh%hT%S$m%TQ%W$mR%g%TwUOXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%trWOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQ!WXQ!ykQ#V{Q#Y|Q#[}R#d!T#T[OSTXZfhikuxz{|}!O!P!Q!R!T!U!V!_!b!c!d!e!h!p!w#j#o#t#x$Q$R$S$^$d$l$t${%]%_%h%i%k%t![[STZhikuxz{|}!O!P!Q!R!U!V!_!b!p#j#o#t$^$t%]%h%kw]OXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQfOR!if^!fc!^#u#v#w$c$kR#z!fQ!TXQ!_Z`#c!T!_#j#k$X$t%]%kS#j!U!VS#k!W!]S$X#d#iQ$t$ZR%]$uQ!kgQ!{lU#|!k!{$VR$V!}Q!ngQ#}!kT$P!n#}QrQR#RrS$d#x$lR$y$dQ$u$ZR%^$uYxST!U!V!pR#UxQ%T$mR%e%TQ#q!`Q$[#mT$`#q$[Q#t!bQ$^#oT$a#t$^Q!}lQ$U!{U$W!}$U$rR$r$VTeOfScOfS!^X!TQ#u!cQ#v!d`#w!e!w$R$S${%_%i%tQ#{!hU$c#x$d$lR$k$QvVOXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tdtSTx!U!V!p#j$t%]%kQ!bZS!rh%hQ!uiQ!xkQ#TuQ#VzQ#W{Q#X|Q#Z}Q#]!OQ#^!PQ#`!QQ#a!RQ#o!_X#s!b#o#t$^r^Of!c!d!e!h!w#x$Q$R$S$d$l${%_%i%t![ySTZhikuxz{|}!O!P!Q!R!U!V!_!b!p#j#o#t$^$t%]%h%kQ![XR#h!T[wSTx!U!V!pQ$Z#jV%[$t%]%kTqQrQ$e#xR%R$lQ!thR%q%hrbOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQ!ZXR#g!T",
|
goto: "9^#lPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPP#mP$WP$m%k&y'PP(^(j)d)gP)mP*t*tPPPP*xP+U+nPPP,U#mP,v-aP-e-k.QP.w/{$W$WP$WP$WP$W$W1R1X1e2X2g2q2w3O3U3`3f3p3zPPP4Y4^5R6wPPP8RP8cPPPPP8g8m8sraOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQ!YXR#f!TwaOXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tr_Of!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQ!]XS!qh%hQ!viQ!zkQ#]!QQ#_!PQ#b!RR#i!TvTOfh!c!d!e!h!w#x$Q$R$S$d$l${%_%h%i%t!W[STZikuxz{|}!O!P!Q!R!U!V!_!b!p#j#o#t$^$t%]%kS!VX!TQ#OmR#PnQ!XXR#e!TrSOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%t!WySTZikuxz{|}!O!P!Q!R!U!V!_!b!p#j#o#t$^$t%]%kS!UX!TS!ph%hR#OmevSTx!U!V!p#j$t%]%kraOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tdtSTx!U!V!p#j$t%]%kQ!YXQ#TuR#f!TR!ogX!mg!k!n#}#S[OSTXZfhikuxz{|}!O!P!Q!R!T!U!V!_!b!c!d!e!h!p!w#j#o#t#x$Q$R$S$^$d$l$t${%]%_%h%i%k%tR$O!lTpQrQ$j#yQ$q$TQ%Q$kR%d%RQ#y!eQ$T!wQ$m$RQ$n$SQ%`${Q%l%_Q%r%iR%u%tQ$i#yQ$p$TQ$}$jQ%P$kQ%Z$qS%c%Q%RR%n%ddvSTx!U!V!p#j$t%]%kQ!`Z[!|l!{!}$U$V$rQ#m!_X#p!`#m#q$[vUOXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tT!sh%hT%S$m%TQ%W$mR%g%TwUOXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%trWOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQ!WXQ!ykQ#V{Q#Y|Q#[}R#d!T#T[OSTXZfhikuxz{|}!O!P!Q!R!T!U!V!_!b!c!d!e!h!p!w#j#o#t#x$Q$R$S$^$d$l$t${%]%_%h%i%k%t![[STZhikuxz{|}!O!P!Q!R!U!V!_!b!p#j#o#t$^$t%]%h%kw]OXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQfOR!if^!fc!^#u#v#w$c$kR#z!fQ!TXQ!_Z`#c!T!_#j#k$X$t%]%kS#j!U!VS#k!W!]S$X#d#iQ$t$ZR%]$uQ!kgQ!{lU#|!k!{$VR$V!}Q!ngQ#}!kT$P!n#}QrQR#RrS$d#x$lR$y$dQ$u$ZR%^$uYxST!U!V!pR#UxQ%T$mR%e%TQ#q!`Q$[#mT$`#q$[Q#t!bQ$^#oT$a#t$^Q!}lQ$U!{U$W!}$U$rR$r$VTeOfScOfS!^X!TQ#u!cQ#v!d`#w!e!w$R$S${%_%i%tQ#{!hU$c#x$d$lR$k$QvVOXf!T!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tdtSTx!U!V!p#j$t%]%kQ!bZS!rh%hQ!uiQ!xkQ#TuQ#VzQ#W{Q#X|Q#Z}Q#]!OQ#^!PQ#`!QQ#a!RQ#o!_X#s!b#o#t$^r^Of!c!d!e!h!w#x$Q$R$S$d$l${%_%i%t![ySTZhikuxz{|}!O!P!Q!R!U!V!_!b!p#j#o#t$^$t%]%h%kQ![XR#h!T[wSTx!U!V!pQ$Z#jV%[$t%]%kTqQrQ$e#xR%R$lQ!thR%q%hrbOf!c!d!e!h!w#x$Q$R$S$d$l${%_%i%tQ!ZXR#g!T",
|
||||||
nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq Band Bor Bxor Shl Shr Ushr NullishCoalesce NullishEq Identifier AssignableIdentifier Word IdentifierBeforeDot CurlyString Do Comment Program PipeExpr operator WhileExpr keyword ConditionalOp ParenExpr FunctionCall DotGet Number PositionalArg FunctionDef Params NamedParam NamedArgPrefix String StringFragment Interpolation EscapeSeq DoubleQuote Boolean Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore NamedArg IfExpr keyword FunctionCall ElseIfExpr keyword ElseExpr FunctionCallOrIdentifier BinOp Regex Dict Array FunctionCallWithBlock TryExpr keyword Throw keyword Import keyword CompoundAssign Assign",
|
nodeNames: "⚠ Star Slash Plus Minus And Or Eq EqEq Neq Lt Lte Gt Gte Modulo PlusEq MinusEq StarEq SlashEq ModuloEq Band Bor Bxor Shl Shr Ushr NullishCoalesce NullishEq Identifier AssignableIdentifier Word IdentifierBeforeDot CurlyString Do Comment Program PipeExpr operator WhileExpr keyword ConditionalOp ParenExpr FunctionCall DotGet Number PositionalArg FunctionDef Params NamedParam NamedArgPrefix String StringFragment Interpolation EscapeSeq DoubleQuote Boolean Null colon CatchExpr keyword Block FinallyExpr keyword keyword Underscore NamedArg IfExpr keyword FunctionCall ElseIfExpr keyword ElseExpr FunctionCallOrIdentifier BinOp Regex Dict Array FunctionCallWithBlock TryExpr keyword Throw keyword Import keyword CompoundAssign Assign",
|
||||||
maxTerm: 124,
|
maxTerm: 124,
|
||||||
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#`~~", 11)],
|
tokenizers: [operatorTokenizer, 1, 2, 3, tokenizer, new LocalTokenGroup("[~RP!O!PU~ZO#`~~", 11)],
|
||||||
topRules: {"Program":[0,35]},
|
topRules: {"Program":[0,35]},
|
||||||
specialized: [{term: 28, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 28, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
|
specialized: [{term: 28, get: (value: any, stack: any) => (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 28, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}],
|
||||||
tokenPrec: 2370
|
tokenPrec: 2373
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -298,4 +298,154 @@ end`).toMatchTree(`
|
||||||
Number 2
|
Number 2
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// NOTE: these are parsed as DotGet(meta, DotGet(script, name)) because that's easiest,
|
||||||
|
// but the compiler flattens them
|
||||||
|
test('chained dot get: meta.script.name', () => {
|
||||||
|
expect('meta = 42; meta.script.name').toMatchTree(`
|
||||||
|
Assign
|
||||||
|
AssignableIdentifier meta
|
||||||
|
Eq =
|
||||||
|
Number 42
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot meta
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot script
|
||||||
|
Identifier name
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chained dot get: a.b.c.d', () => {
|
||||||
|
expect('a = 1; a.b.c.d').toMatchTree(`
|
||||||
|
Assign
|
||||||
|
AssignableIdentifier a
|
||||||
|
Eq =
|
||||||
|
Number 1
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot a
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot b
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot c
|
||||||
|
Identifier d
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chained dot get in function call', () => {
|
||||||
|
expect('config = 1; echo config.db.host').toMatchTree(`
|
||||||
|
Assign
|
||||||
|
AssignableIdentifier config
|
||||||
|
Eq =
|
||||||
|
Number 1
|
||||||
|
FunctionCall
|
||||||
|
Identifier echo
|
||||||
|
PositionalArg
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot config
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot db
|
||||||
|
Identifier host
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chained dot get with numeric index at end', () => {
|
||||||
|
expect('obj = 1; obj.items.0').toMatchTree(`
|
||||||
|
Assign
|
||||||
|
AssignableIdentifier obj
|
||||||
|
Eq =
|
||||||
|
Number 1
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot obj
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot items
|
||||||
|
Number 0
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chained dot get with ParenExpr at end', () => {
|
||||||
|
expect('obj = 1; obj.items.(i)').toMatchTree(`
|
||||||
|
Assign
|
||||||
|
AssignableIdentifier obj
|
||||||
|
Eq =
|
||||||
|
Number 1
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot obj
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot items
|
||||||
|
ParenExpr
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
Identifier i
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('not in scope remains Word with chained dots', () => {
|
||||||
|
expect('readme.md.bak').toMatchTree(`Word readme.md.bak`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('chained dot get in nested functions', () => {
|
||||||
|
expect(`do cfg:
|
||||||
|
do inner:
|
||||||
|
cfg.db.host
|
||||||
|
end
|
||||||
|
end`).toMatchTree(`
|
||||||
|
FunctionDef
|
||||||
|
Do do
|
||||||
|
Params
|
||||||
|
Identifier cfg
|
||||||
|
colon :
|
||||||
|
FunctionDef
|
||||||
|
Do do
|
||||||
|
Params
|
||||||
|
Identifier inner
|
||||||
|
colon :
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot cfg
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot db
|
||||||
|
Identifier host
|
||||||
|
keyword end
|
||||||
|
keyword end
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('mixed simple and chained dot get', () => {
|
||||||
|
expect('obj = 1; obj.a; obj.b.c').toMatchTree(`
|
||||||
|
Assign
|
||||||
|
AssignableIdentifier obj
|
||||||
|
Eq =
|
||||||
|
Number 1
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot obj
|
||||||
|
Identifier a
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot obj
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot b
|
||||||
|
Identifier c
|
||||||
|
`)
|
||||||
|
})
|
||||||
|
|
||||||
|
test.skip('chained numeric dot get: row.2.1.b', () => {
|
||||||
|
expect('row = []; row.2.1').toMatchTree(`
|
||||||
|
Assign
|
||||||
|
AssignableIdentifier row
|
||||||
|
Eq =
|
||||||
|
Array []
|
||||||
|
FunctionCallOrIdentifier
|
||||||
|
DotGet
|
||||||
|
IdentifierBeforeDot row
|
||||||
|
DotGet
|
||||||
|
Number 2
|
||||||
|
DotGet
|
||||||
|
Number 1
|
||||||
|
Identifier b
|
||||||
|
`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { ExternalTokenizer, InputStream, Stack } from '@lezer/lr'
|
import { ExternalTokenizer, InputStream, Stack } from '@lezer/lr'
|
||||||
import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do, CurlyString } from './shrimp.terms'
|
import { Identifier, AssignableIdentifier, Word, IdentifierBeforeDot, Do, CurlyString, DotGet } from './shrimp.terms'
|
||||||
|
|
||||||
// doobie doobie do (we need the `do` keyword to know when we're defining params)
|
// doobie doobie do (we need the `do` keyword to know when we're defining params)
|
||||||
export function specializeKeyword(ident: string) {
|
export function specializeKeyword(ident: string) {
|
||||||
|
|
@ -187,11 +187,21 @@ const checkForDotGet = (input: InputStream, stack: Stack, pos: number): number |
|
||||||
const identifierText = buildIdentifierText(input, pos)
|
const identifierText = buildIdentifierText(input, pos)
|
||||||
const context = stack.context as { scope: { has(name: string): boolean } } | undefined
|
const context = stack.context as { scope: { has(name: string): boolean } } | undefined
|
||||||
|
|
||||||
// If identifier is in scope, this is property access (e.g., obj.prop)
|
// Check if identifier is in scope (lexical scope or globals)
|
||||||
// If not in scope, it should be consumed as a Word (e.g., file.txt)
|
const inScope = context?.scope.has(identifierText) || globals.includes(identifierText)
|
||||||
return context?.scope.has(identifierText) || globals.includes(identifierText)
|
|
||||||
? IdentifierBeforeDot
|
// property access
|
||||||
: null
|
if (inScope) return IdentifierBeforeDot
|
||||||
|
|
||||||
|
// Not in scope - check if we're inside a DotGet chain
|
||||||
|
// Inside the @skip {} block where DotGet is defined, Word cannot be shifted
|
||||||
|
// but Identifier can be. This tells us we're at the RHS of a DotGet.
|
||||||
|
const canShiftIdentifier = stack.canShift(Identifier)
|
||||||
|
const canShiftWord = stack.canShift(Word)
|
||||||
|
const inDotGetChain = canShiftIdentifier && !canShiftWord
|
||||||
|
|
||||||
|
// continue if we're inside a DotGet
|
||||||
|
return inDotGetChain ? IdentifierBeforeDot : null
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decide between AssignableIdentifier and Identifier using grammar state + peek-ahead
|
// Decide between AssignableIdentifier and Identifier using grammar state + peek-ahead
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user