diff --git a/src/lang/abstractSyntaxTree.test.ts b/src/lang/abstractSyntaxTree.test.ts index 371881ae9..267a4b99f 100644 --- a/src/lang/abstractSyntaxTree.test.ts +++ b/src/lang/abstractSyntaxTree.test.ts @@ -1214,4 +1214,140 @@ describe('testing pipe operator special', () => { }, ]) }) + test('nested object expression ast', () => { + const code = `const yo = {key: { + key2: 'value' +}}` + const tokens = lexer(code) + const { body } = abstractSyntaxTree(tokens) + expect(body).toEqual([ + { + type: 'VariableDeclaration', + start: 0, + end: 37, + kind: 'const', + declarations: [ + { + type: 'VariableDeclarator', + start: 6, + end: 37, + id: { + type: 'Identifier', + start: 6, + end: 8, + name: 'yo', + }, + init: { + type: 'ObjectExpression', + start: 11, + end: 37, + properties: [ + { + type: 'ObjectProperty', + start: 12, + end: 36, + key: { + type: 'Identifier', + start: 12, + end: 15, + name: 'key', + }, + value: { + type: 'ObjectExpression', + start: 17, + end: 36, + properties: [ + { + type: 'ObjectProperty', + start: 21, + end: 34, + key: { + type: 'Identifier', + start: 21, + end: 25, + name: 'key2', + }, + value: { + type: 'Literal', + start: 27, + end: 34, + value: 'value', + raw: "'value'", + }, + }, + ], + }, + }, + ], + }, + }, + ], + }, + ]) + }) + test('object expression with array ast', () => { + const code = `const yo = {key: [1, '2']}` + const tokens = lexer(code) + const { body } = abstractSyntaxTree(tokens) + expect(body).toEqual([ + { + type: 'VariableDeclaration', + start: 0, + end: 26, + kind: 'const', + declarations: [ + { + type: 'VariableDeclarator', + start: 6, + end: 26, + id: { + type: 'Identifier', + start: 6, + end: 8, + name: 'yo', + }, + init: { + type: 'ObjectExpression', + start: 11, + end: 26, + properties: [ + { + type: 'ObjectProperty', + start: 12, + end: 25, + key: { + type: 'Identifier', + start: 12, + end: 15, + name: 'key', + }, + value: { + type: 'ArrayExpression', + start: 17, + end: 25, + elements: [ + { + type: 'Literal', + start: 18, + end: 19, + value: 1, + raw: '1', + }, + { + type: 'Literal', + start: 21, + end: 24, + value: '2', + raw: "'2'", + }, + ], + }, + }, + ], + }, + }, + ], + }, + ]) + }) }) diff --git a/src/lang/abstractSyntaxTree.ts b/src/lang/abstractSyntaxTree.ts index f5d9c2c9a..cac1295af 100644 --- a/src/lang/abstractSyntaxTree.ts +++ b/src/lang/abstractSyntaxTree.ts @@ -297,7 +297,8 @@ function makeValue( ): { value: Value; lastIndex: number } { const currentToken = tokens[index] const { token: nextToken } = nextMeaningfulToken(tokens, index) - if (nextToken.type === 'brace' && nextToken.value === '(') { + // nextToken might be empty if it's at the end of the file + if (nextToken?.type === 'brace' && nextToken.value === '(') { const { expression, lastIndex } = makeCallExpression(tokens, index) return { value: expression, @@ -305,8 +306,10 @@ function makeValue( } } if ( - (currentToken.type === 'word' || currentToken.type === 'number') && - nextToken.type === 'operator' + (currentToken.type === 'word' || + currentToken.type === 'number' || + currentToken.type === 'string') && + nextToken?.type === 'operator' ) { const { expression, lastIndex } = makeBinaryExpression(tokens, index) return { @@ -314,6 +317,23 @@ function makeValue( lastIndex, } } + if (currentToken.type === 'brace' && currentToken.value === '{') { + const objExp = makeObjectExpression(tokens, index) + return { + value: objExp.expression, + lastIndex: objExp.lastIndex, + } + } + if (currentToken.type === 'brace' && currentToken.value === '[') { + const arrExp = makeArrayExpression(tokens, index) + return { + value: arrExp.expression, + lastIndex: arrExp.lastIndex, + } + } + if (currentToken.type === 'word' && nextToken.type === 'period') { + // TODO object access + } if (currentToken.type === 'word') { const identifier = makeIdentifier(tokens, index) return { @@ -328,6 +348,23 @@ function makeValue( lastIndex: index, } } + if (currentToken.type === 'brace' && currentToken.value === '(') { + const closingBraceIndex = findClosingBrace(tokens, index) + const arrowToken = nextMeaningfulToken(tokens, closingBraceIndex) + if ( + arrowToken.token.type === 'operator' && + arrowToken.token.value === '=>' + ) { + const { expression, lastIndex: arrowFunctionLastIndex } = + makeFunctionExpression(tokens, index) + return { + value: expression, + lastIndex: arrowFunctionLastIndex, + } + } else { + throw new Error('TODO - handle expression with braces') + } + } throw new Error('Expected a previous Value if statement to match') } @@ -349,7 +386,6 @@ function makeVariableDeclarators( const assignmentToken = nextMeaningfulToken(tokens, index) const declarationToken = previousMeaningfulToken(tokens, index) const contentsStartToken = nextMeaningfulToken(tokens, assignmentToken.index) - const nextAfterInit = nextMeaningfulToken(tokens, contentsStartToken.index) const pipeStartIndex = assignmentToken?.token?.type === 'operator' ? contentsStartToken.index @@ -364,33 +400,6 @@ function makeVariableDeclarators( ) init = expression lastIndex = pipeLastIndex - } else if ( - contentsStartToken.token.type === 'brace' && - contentsStartToken.token.value === '(' - ) { - const closingBraceIndex = findClosingBrace(tokens, contentsStartToken.index) - const arrowToken = nextMeaningfulToken(tokens, closingBraceIndex) - if ( - arrowToken.token.type === 'operator' && - arrowToken.token.value === '=>' - ) { - const { expression, lastIndex: arrowFunctionLastIndex } = - makeFunctionExpression(tokens, contentsStartToken.index) - init = expression - lastIndex = arrowFunctionLastIndex - } else { - throw new Error('TODO - handle expression with braces') - } - } else if ( - contentsStartToken.token.type === 'brace' && - contentsStartToken.token.value === '{' - ) { - const objectExpression = makeObjectExpression( - tokens, - contentsStartToken.index - ) - init = objectExpression.expression - lastIndex = objectExpression.lastIndex } else if ( declarationToken.token.type === 'word' && declarationToken.token.value === 'sketch' @@ -398,29 +407,13 @@ function makeVariableDeclarators( const sketchExp = makeSketchExpression(tokens, assignmentToken.index) init = sketchExp.expression lastIndex = sketchExp.lastIndex - } else if (nextAfterInit.token?.type === 'operator') { - const binExp = makeBinaryExpression(tokens, contentsStartToken.index) - init = binExp.expression - lastIndex = binExp.lastIndex - } else if ( - nextAfterInit.token?.type === 'brace' && - nextAfterInit.token.value === '(' - ) { - const callExInfo = makeCallExpression(tokens, contentsStartToken.index) - init = callExInfo.expression - lastIndex = callExInfo.lastIndex - } else if ( - contentsStartToken.token.type === 'brace' && - contentsStartToken.token.value === '[' - ) { - const arrayExpression = makeArrayExpression( + } else { + const { value, lastIndex: valueLastIndex } = makeValue( tokens, contentsStartToken.index ) - init = arrayExpression.expression - lastIndex = arrayExpression.lastIndex - } else { - init = makeLiteral(tokens, contentsStartToken.index) + init = value + lastIndex = valueLastIndex } const currentDeclarator: VariableDeclarator = { type: 'VariableDeclarator', @@ -594,14 +587,18 @@ function makeObjectProperties( } const colonToken = nextMeaningfulToken(tokens, index) const valueStartToken = nextMeaningfulToken(tokens, colonToken.index) - const value = makeValue(tokens, valueStartToken.index) - const commaOrClosingBraceToken = nextMeaningfulToken(tokens, value.lastIndex) + + const val = makeValue(tokens, valueStartToken.index) + + const value = val.value + const valueLastIndex = val.lastIndex + const commaOrClosingBraceToken = nextMeaningfulToken(tokens, valueLastIndex) let objectProperty: ObjectProperty = { type: 'ObjectProperty', start: propertyKeyToken.start, - end: value.value.end, + end: value.end, key: makeIdentifier(tokens, index), - value: value.value, + value, } const nextKeyToken = nextMeaningfulToken( tokens, diff --git a/src/lang/executor.test.ts b/src/lang/executor.test.ts index 218ecd7ee..3982b7eed 100644 --- a/src/lang/executor.test.ts +++ b/src/lang/executor.test.ts @@ -18,7 +18,7 @@ const newVar = myVar + 1` const { root } = exe(code) expect(root.myVar).toBe('a str') }) - it('test assigning a var by cont concatenating two strings string', () => { + it('test assigning a var by cont concatenating two strings string execute', () => { const code = fs.readFileSync( './src/lang/testExamples/variableDeclaration.cado', 'utf-8' @@ -40,7 +40,7 @@ log(5, myVar)` expect(root.myVar).toBe('hello') expect(programMemoryOverride.log).toHaveBeenCalledWith(5, 'hello') }) - it('fn funcN = () => {}', () => { + it('fn funcN = () => {} execute', () => { const { root } = exe( [ 'fn funcN = (a, b) => {',