diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index e6b006b..72b0f23 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -96,7 +96,6 @@ export class Compiler { #compileNode(node: SyntaxNode, input: string): ProgramItem[] { const value = input.slice(node.from, node.to) - if (DEBUG) console.log(`🫦 ${node.name}: ${value}`) switch (node.type.id) { @@ -192,10 +191,15 @@ export class Compiler { } case terms.DotGet: { - const { objectName, propertyName } = getDotGetParts(node, input) + const { objectName, property } = getDotGetParts(node, input) const instructions: ProgramItem[] = [] instructions.push(['TRY_LOAD', objectName]) - instructions.push(['PUSH', propertyName]) + if (property.type.id === terms.ParenExpr) { + instructions.push(...this.#compileNode(property, input)) + } else { + const propertyValue = input.slice(property.from, property.to) + instructions.push(['PUSH', propertyValue]) + } instructions.push(['DOT_GET']) return instructions } @@ -270,8 +274,9 @@ export class Compiler { } case terms.FunctionCallOrIdentifier: { - if (node.firstChild?.name === 'DotGet') + if (node.firstChild?.type.id === terms.DotGet) { return this.#compileNode(node.firstChild, input) + } return [['TRY_CALL', value]] } diff --git a/src/compiler/tests/compiler.test.ts b/src/compiler/tests/compiler.test.ts index ef02035..4d662c3 100644 --- a/src/compiler/tests/compiler.test.ts +++ b/src/compiler/tests/compiler.test.ts @@ -102,8 +102,7 @@ describe('compiler', () => { end abc - `) - .toEvaluateTo(true) + `).toEvaluateTo(true) }) test('simple conditionals', () => { @@ -244,3 +243,20 @@ describe('native functions', () => { expect(`add 5 9`).toEvaluateTo(14, { add }) }) }) + +describe('dot get', () => { + const array = (...items: any) => items + const dict = (atNamed: any) => atNamed + + test('access array element', () => { + expect(`arr = array 'a' 'b' 'c'; arr.1`).toEvaluateTo('b', { array }) + }) + + test('access dict element', () => { + expect(`dict = dict a=1 b=2; dict.a`).toEvaluateTo(1, { dict }) + }) + + test('use parens expr with dot-get', () => { + expect(`a = 1; arr = array 'a' 'b' 'c'; arr.(1 + a)`).toEvaluateTo('c', { array }) + }) +}) diff --git a/src/compiler/utils.ts b/src/compiler/utils.ts index bcf0587..82b4025 100644 --- a/src/compiler/utils.ts +++ b/src/compiler/utils.ts @@ -203,7 +203,7 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => { const children = getAllChildren(node) const [object, property] = children - if (children.length !== 2) { + if (!object || !property) { throw new CompilerError( `DotGet expected 2 identifier children, got ${children.length}`, node.from, @@ -219,7 +219,7 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => { ) } - if (property.type.id !== terms.Identifier && property.type.id !== terms.Number) { + if (![terms.Identifier, terms.Number, terms.ParenExpr].includes(property.type.id)) { throw new CompilerError( `DotGet property must be an Identifier or Number, got ${property.type.name}`, property.from, @@ -228,7 +228,6 @@ export const getDotGetParts = (node: SyntaxNode, input: string) => { } const objectName = input.slice(object.from, object.to) - const propertyName = input.slice(property.from, property.to) - return { objectName, propertyName } + return { objectName, property } } diff --git a/src/editor/editor.tsx b/src/editor/editor.tsx index 9206fce..ce7e0b1 100644 --- a/src/editor/editor.tsx +++ b/src/editor/editor.tsx @@ -32,7 +32,6 @@ export const Editor = () => { }) multilineModeSignal.connect((isMultiline) => { - console.log(`🌭 hey babe`, isMultiline) view.dispatch({ effects: lineNumbersCompartment.reconfigure(isMultiline ? lineNumbers() : []), }) diff --git a/src/parser/shrimp.grammar b/src/parser/shrimp.grammar index f74f5ea..635035a 100644 --- a/src/parser/shrimp.grammar +++ b/src/parser/shrimp.grammar @@ -174,7 +174,7 @@ expression { @skip {} { DotGet { - IdentifierBeforeDot dot (Number | Identifier) + IdentifierBeforeDot dot (Number | Identifier | ParenExpr) } String { "'" stringContent* "'" } diff --git a/src/parser/shrimp.ts b/src/parser/shrimp.ts index da34621..6e19a7f 100644 --- a/src/parser/shrimp.ts +++ b/src/parser/shrimp.ts @@ -7,9 +7,9 @@ import {highlighting} from "./highlight" const spec_Identifier = {__proto__:null,end:80, null:86, if:96, elseif:104, else:108} export const parser = LRParser.deserialize({ version: 14, - states: "3UQYQbOOO!ZOpO'#CsO#mQcO'#CvO$jOSO'#CxO$xQbO'#EVOOQ`'#DR'#DROOQa'#DO'#DOO%{QbO'#DWO'QQcO'#DzOOQa'#Dz'#DzO)UQcO'#DyO)}QRO'#CwO*bQcO'#DuO*yQcO'#DuO+[QbO'#CuOOQ`'#Dv'#DvO,SQbO'#DuO,bQbO'#E]OOQ`'#D]'#D]O-VQRO'#DeOOQ`'#Du'#DuO-[QQO'#DtOOQ`'#Dt'#DtOOQ`'#Df'#DfQYQbOOO-dObO,59_O-lQbO'#DPOOQa'#Dy'#DyOOQ`'#DZ'#DZOOQ`'#E['#E[OOQ`'#Dm'#DmO-vQbO,59^O.ZQbO'#CzO.cQWO'#C{OOOO'#D|'#D|OOOO'#Dg'#DgO.wOSO,59dOOQa,59d,59dOOQ`'#Di'#DiO/VQbO'#DSO/_QQO,5:qOOQ`'#Dh'#DhO/dQbO,59rO/kQQO,59jOOQa,59r,59rO/vQbO,59rO,bQbO,59cO,bQbO,59cO,bQbO,59cO,bQbO,59tO,bQbO,59tO,bQbO,59tO0QQRO,59aO0XQRO,59aO0jQRO,59aO0eQQO,59aO0uQQO,59aO0}QbO'#DnO1YQbO,59]O1kQRO,5:wO1rQRO,5:wO1}QbO,5:POOQ`,5:`,5:`OOQ`-E7d-E7dOOQa1G.y1G.yOOQ`,59k,59kOOQ`-E7k-E7kOOOO,59f,59fOOOO,59g,59gOOOO-E7e-E7eOOQa1G/O1G/OOOQ`-E7g-E7gO2XQbO1G0]OOQ`-E7f-E7fO2fQQO1G/UOOQa1G/^1G/^O2qQbO1G/^OOQO'#Dk'#DkO2fQQO1G/UOOQa1G/U1G/UOOQ`'#Dl'#DlO2qQbO1G/^OOQa1G.}1G.}O3dQcO1G.}O3nQcO1G.}O3xQcO1G.}OOQa1G/`1G/`O5[QcO1G/`O5cQcO1G/`O5jQcO1G/`OOQa1G.{1G.{O!`QbO'#CvO&SQbO'#CrOOQ`,5:Y,5:YOOQ`-E7l-E7lO5qQbO1G0cOOQ`1G/k1G/kO6OQbO7+%wO6TQbO7+%xO6eQQO7+$pOOQa7+$p7+$pO6pQbO7+$xOOQa7+$x7+$xOOQO-E7i-E7iOOQ`-E7j-E7jOOQ`'#D_'#D_O6zQbO7+%}O7PQbO7+&OOOQ`< (specializeKeyword(value, stack) << 1), external: specializeKeyword},{term: 15, get: (value: keyof typeof spec_Identifier) => spec_Identifier[value] || -1}], - tokenPrec: 1132 + tokenPrec: 1135 }) diff --git a/src/parser/tests/dot-get.test.ts b/src/parser/tests/dot-get.test.ts index bcec05f..f781937 100644 --- a/src/parser/tests/dot-get.test.ts +++ b/src/parser/tests/dot-get.test.ts @@ -274,4 +274,28 @@ end`).toMatchTree(` Identifier heya `) }) + + test('can use the result of a parens expression as the property of dot get', () => { + expect('obj = list 1 2 3; obj.(1 + 2)').toMatchTree(` + Assign + AssignableIdentifier obj + Eq = + FunctionCall + Identifier list + PositionalArg + Number 1 + PositionalArg + Number 2 + PositionalArg + Number 3 + FunctionCallOrIdentifier + DotGet + IdentifierBeforeDot obj + ParenExpr + BinOp + Number 1 + Plus + + Number 2 + `) + }) }) diff --git a/src/testSetup.ts b/src/testSetup.ts index f47218a..d2c1652 100644 --- a/src/testSetup.ts +++ b/src/testSetup.ts @@ -60,7 +60,7 @@ expect.extend({ } }, - toFailParse(received: unknown) { + toFailParse(received) { assert(typeof received === 'string', 'toFailParse can only be used with string values') try { @@ -121,7 +121,7 @@ expect.extend({ } }, - async toFailEvaluation(received: unknown) { + async toFailEvaluation(received) { assert(typeof received === 'string', 'toFailEvaluation can only be used with string values') try {