From 304b07494ba77aee009b276038f5006b0bc932e1 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 10:00:36 -0500 Subject: [PATCH 01/28] feat(lexer): add brackets --- apps/parser/src/lexer.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/parser/src/lexer.ts b/apps/parser/src/lexer.ts index c35268e..93329c6 100644 --- a/apps/parser/src/lexer.ts +++ b/apps/parser/src/lexer.ts @@ -210,6 +210,8 @@ export const LPAREN: TokenType = createToken({ name: 'LPAREN', pattern: '(' }); export const RPAREN: TokenType = createToken({ name: 'RPAREN', pattern: ')' }); export const LCURLY: TokenType = createToken({ name: 'LCURLY', pattern: '{' }); export const RCURLY: TokenType = createToken({ name: 'RCURLY', pattern: '}' }); +export const LBRACK: TokenType = createToken({ name: 'LBRACK', pattern: '[' }); +export const RBRACK: TokenType = createToken({ name: 'RBRACK', pattern: ']' }); export const SEMI: TokenType = createToken({ name: 'SEMI', pattern: ';' }); export const COLON: TokenType = createToken({ name: 'COLON', pattern: ':' }); /* Keywords */ @@ -269,6 +271,8 @@ export const allTokens: TokenType[] = [ RPAREN, LCURLY, RCURLY, + LBRACK, + RBRACK, SEMI, COLON, ERROR, From 66c3a63488075c3bcc02198b75c81b6a3bb484c8 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 10:25:18 -0500 Subject: [PATCH 02/28] feat(parser): add array index and slice --- apps/parser/src/parser.ts | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/apps/parser/src/parser.ts b/apps/parser/src/parser.ts index 787c42c..f1155c3 100644 --- a/apps/parser/src/parser.ts +++ b/apps/parser/src/parser.ts @@ -129,7 +129,7 @@ export class CalvinParser extends CstParser { }); private expression = this.RULE('expression', () => { - this.SUBRULE(this.value); + this.SUBRULE(this.chainValue); this.OR([ { ALT: () => this.CONSUME(Tokens.PostFix), @@ -153,12 +153,32 @@ export class CalvinParser extends CstParser { ]); }); + private chainValue = this.RULE('chainValue', () => { + this.SUBRULE(this.value); + this.MANY(() => { + this.CONSUME(Tokens.LBRACK); + // This allows for expressions of the form arr[] to be syntactically valid + // I don't like it, but I guess this can be handled on the semantic level??? + // This was the best way I could fix the common lookahead prefix error + this.OPTION(() => this.SUBRULE(this.expression)); + this.OPTION1(() => { + this.CONSUME(Tokens.COLON); + this.OPTION2(() => this.SUBRULE1(this.expression)); + this.OPTION3(() => { + this.CONSUME1(Tokens.COLON); + this.SUBRULE2(this.expression); + }); + }); + this.CONSUME(Tokens.RBRACK); + }); + }); + private value = this.RULE('value', () => { this.OR([ { ALT: () => { this.CONSUME(Tokens.UnOp); - this.SUBRULE1(this.value); + this.SUBRULE1(this.chainValue); }, }, { From 41d7a24461d950e589bde7adac83a3fa1754d130 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 10:29:28 -0500 Subject: [PATCH 03/28] feat(lexer): add comma --- apps/parser/src/lexer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/parser/src/lexer.ts b/apps/parser/src/lexer.ts index 93329c6..067d2a4 100644 --- a/apps/parser/src/lexer.ts +++ b/apps/parser/src/lexer.ts @@ -214,6 +214,7 @@ export const LBRACK: TokenType = createToken({ name: 'LBRACK', pattern: '[' }); export const RBRACK: TokenType = createToken({ name: 'RBRACK', pattern: ']' }); export const SEMI: TokenType = createToken({ name: 'SEMI', pattern: ';' }); export const COLON: TokenType = createToken({ name: 'COLON', pattern: ':' }); +export const COMMA: TokenType = createToken({ name: 'COMMA', pattern: ',' }); /* Keywords */ export const LET: TokenType = createToken({ name: 'LET', pattern: 'let', longer_alt: ID }); // Selection @@ -275,6 +276,7 @@ export const allTokens: TokenType[] = [ RBRACK, SEMI, COLON, + COMMA, ERROR, ]; From b47b6ef56033f9ccb99636c4530c5bd809eb627c Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 10:30:31 -0500 Subject: [PATCH 04/28] feat(parser): add array literals --- apps/parser/src/parser.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/parser/src/parser.ts b/apps/parser/src/parser.ts index f1155c3..aa6a52b 100644 --- a/apps/parser/src/parser.ts +++ b/apps/parser/src/parser.ts @@ -198,7 +198,19 @@ export class CalvinParser extends CstParser { }); private constant = this.RULE('constant', () => - this.OR(Tokens.literals.map((t) => ({ ALT: () => this.CONSUME(t) }))), + this.OR([ + ...Tokens.literals.map((t) => ({ ALT: () => this.CONSUME(t) })), + { + ALT: () => { + this.CONSUME(Tokens.LBRACK); + this.MANY_SEP({ + SEP: () => this.CONSUME(Tokens.COMMA), + DEF: () => this.SUBRULE(this.expression), + }); + this.CONSUME(Tokens.RBRACK); + }, + }, + ]), ); private type = this.RULE('type', () => this.CONSUME(Tokens.BASIC_TYPE)); From 75eae9a76f98b59832bc975a29c91713f496685e Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 10:31:27 -0500 Subject: [PATCH 05/28] chore(docs): update syntax diagrams --- apps/parser/generated/syntax-diagrams.html | 1279 +++++++++++--------- 1 file changed, 700 insertions(+), 579 deletions(-) diff --git a/apps/parser/generated/syntax-diagrams.html b/apps/parser/generated/syntax-diagrams.html index 111d915..ae9a1fe 100644 --- a/apps/parser/generated/syntax-diagrams.html +++ b/apps/parser/generated/syntax-diagrams.html @@ -1,694 +1,815 @@ + - - - - - + + + + + + -
+
From a8849614108c88671cc1140ef2c0d930a2c48375 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 10:31:42 -0500 Subject: [PATCH 06/28] chore(parser): update cst-types --- apps/parser/generated/cst-types.ts | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/apps/parser/generated/cst-types.ts b/apps/parser/generated/cst-types.ts index 89c0d6c..bfc82e0 100644 --- a/apps/parser/generated/cst-types.ts +++ b/apps/parser/generated/cst-types.ts @@ -76,13 +76,26 @@ export interface ExpressionCstNode extends CstNode { } export type ExpressionCstChildren = { - value: ValueCstNode[]; + chainValue: ChainValueCstNode[]; PostFix?: IToken[]; CmpAsgn?: IToken[]; BinOp?: IToken[]; expression?: ExpressionCstNode[]; }; +export interface ChainValueCstNode extends CstNode { + name: 'chainValue'; + children: ChainValueCstChildren; +} + +export type ChainValueCstChildren = { + value: ValueCstNode[]; + LBRACK?: IToken[]; + expression?: ExpressionCstNode[]; + COLON?: IToken[]; + RBRACK?: IToken[]; +}; + export interface ValueCstNode extends CstNode { name: 'value'; children: ValueCstChildren; @@ -90,7 +103,7 @@ export interface ValueCstNode extends CstNode { export type ValueCstChildren = { UnOp?: IToken[]; - value?: ValueCstNode[]; + chainValue?: ChainValueCstNode[]; constant?: ConstantCstNode[]; ID?: IToken[]; LPAREN?: IToken[]; @@ -107,9 +120,13 @@ export type ConstantCstChildren = { STRING?: IToken[]; BOOL?: IToken[]; BIN?: IToken[]; - INT?: IToken[]; CMPX?: IToken[]; REAL?: IToken[]; + INT?: IToken[]; + LBRACK?: IToken[]; + expression?: ExpressionCstNode[]; + SEP?: IToken[]; + RBRACK?: IToken[]; }; export interface TypeCstNode extends CstNode { @@ -128,6 +145,7 @@ export interface ICstNodeVisitor extends ICstVisitor { body(children: BodyCstChildren, param?: IN): OUT; declaration(children: DeclarationCstChildren, param?: IN): OUT; expression(children: ExpressionCstChildren, param?: IN): OUT; + chainValue(children: ChainValueCstChildren, param?: IN): OUT; value(children: ValueCstChildren, param?: IN): OUT; constant(children: ConstantCstChildren, param?: IN): OUT; type(children: TypeCstChildren, param?: IN): OUT; From d616f0c891b083cbc8eab9329b1613d9780b2dc4 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 10:35:23 -0500 Subject: [PATCH 07/28] chore(parser): check off yacc-reference --- apps/parser/yacc-reference/lex.l | 6 +++--- apps/parser/yacc-reference/parser.y | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/parser/yacc-reference/lex.l b/apps/parser/yacc-reference/lex.l index d8eb686..605269d 100644 --- a/apps/parser/yacc-reference/lex.l +++ b/apps/parser/yacc-reference/lex.l @@ -165,15 +165,15 @@ using return yy::parser::make_USING (loc); "}" return yy::parser::make_RBRACE (loc); // DONE "(" return yy::parser::make_LPAREN (loc); // DONE ")" return yy::parser::make_RPAREN (loc); // DONE -"[" return yy::parser::make_LBRACK (loc); -"]" return yy::parser::make_RBRACK (loc); +"[" return yy::parser::make_LBRACK (loc); // DONE +"]" return yy::parser::make_RBRACK (loc); // DONE ";" return yy::parser::make_SEMI (loc); // DONE "_" return yy::parser::make_USCORE (loc); "?" return yy::parser::make_QUE (loc); "!" return yy::parser::make_BANG (loc); ":" return yy::parser::make_COLON (loc); // DONE "??" return yy::parser::make_N_COAL (loc); // DONE -"," return yy::parser::make_COMMA (loc); +"," return yy::parser::make_COMMA (loc); // DONE "." return yy::parser::make_DOT (loc); . { throw yy::parser::syntax_error(loc, "invalid character: " + std::string(yytext));} <> return yy::parser::make_YYEOF (loc); diff --git a/apps/parser/yacc-reference/parser.y b/apps/parser/yacc-reference/parser.y index 14905ec..697d43c 100644 --- a/apps/parser/yacc-reference/parser.y +++ b/apps/parser/yacc-reference/parser.y @@ -369,7 +369,7 @@ compound_assign: // DONE | value RS_EQU expression | value AS_EQU expression | value NC_EQU expression; -value: // 2/15 +value: // 5/15 constant // DONE | ID // DONE | GLOBAL ID @@ -380,10 +380,10 @@ value: // 2/15 | function_call | value optional_chain DOT ID | value optional_chain DOT function_call - | value optional_chain LBRACK expression RBRACK - | value optional_chain LBRACK slice RBRACK + | value optional_chain LBRACK expression RBRACK // DONE + | value optional_chain LBRACK slice RBRACK // DONE | LBRACE list_expression RBRACE - | LPAREN expression RPAREN + | LPAREN expression RPAREN // DONE | type_signature LPAREN expression RPAREN; constant: // DONE BOOL { debug::log(drv.trace_parsing) << std::endl << "Parser push boolean: " << $1 << std::endl << std::endl; } @@ -398,7 +398,7 @@ optional_chain: QUE | BANG |; -slice: +slice: // DONE optional_expression COLON optional_expression | optional_expression COLON optional_expression COLON expression; From c9c6475825b15c24611193e97dfb538a43798be2 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 18:11:02 -0500 Subject: [PATCH 08/28] fix(parser): break index or slice into subrule --- apps/parser/src/parser.ts | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/apps/parser/src/parser.ts b/apps/parser/src/parser.ts index aa6a52b..9767a09 100644 --- a/apps/parser/src/parser.ts +++ b/apps/parser/src/parser.ts @@ -156,21 +156,25 @@ export class CalvinParser extends CstParser { private chainValue = this.RULE('chainValue', () => { this.SUBRULE(this.value); this.MANY(() => { - this.CONSUME(Tokens.LBRACK); - // This allows for expressions of the form arr[] to be syntactically valid - // I don't like it, but I guess this can be handled on the semantic level??? - // This was the best way I could fix the common lookahead prefix error - this.OPTION(() => this.SUBRULE(this.expression)); - this.OPTION1(() => { - this.CONSUME(Tokens.COLON); - this.OPTION2(() => this.SUBRULE1(this.expression)); - this.OPTION3(() => { - this.CONSUME1(Tokens.COLON); - this.SUBRULE2(this.expression); - }); + this.SUBRULE(this.indexOrSlice); + }); + }); + + private indexOrSlice = this.RULE('indexOrSlice', () => { + this.CONSUME(Tokens.LBRACK); + // This allows for expressions of the form arr[] to be syntactically valid + // I don't like it, but I guess this can be handled on the semantic level??? + // This was the best way I could fix the common lookahead prefix error + this.OPTION(() => this.SUBRULE(this.expression)); + this.OPTION1(() => { + this.CONSUME(Tokens.COLON); + this.OPTION2(() => this.SUBRULE1(this.expression)); + this.OPTION3(() => { + this.CONSUME1(Tokens.COLON); + this.SUBRULE2(this.expression); }); - this.CONSUME(Tokens.RBRACK); }); + this.CONSUME(Tokens.RBRACK); }); private value = this.RULE('value', () => { From 839ba631dbd1936a12925b1e9b941435d7f1bd7f Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 18:11:14 -0500 Subject: [PATCH 09/28] chore(docs): update syntax diagrams --- apps/parser/generated/syntax-diagrams.html | 94 ++++++++++++---------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/apps/parser/generated/syntax-diagrams.html b/apps/parser/generated/syntax-diagrams.html index ae9a1fe..67aa0c1 100644 --- a/apps/parser/generated/syntax-diagrams.html +++ b/apps/parser/generated/syntax-diagrams.html @@ -523,75 +523,87 @@ { "type": "Repetition", "idx": 0, + "definition": [ + { + "type": "NonTerminal", + "name": "indexOrSlice", + "idx": 0 + } + ] + } + ] + }, + { + "type": "Rule", + "name": "indexOrSlice", + "orgText": "", + "definition": [ + { + "type": "Terminal", + "name": "LBRACK", + "label": "LBRACK", + "idx": 0, + "pattern": "[" + }, + { + "type": "Option", + "idx": 0, + "definition": [ + { + "type": "NonTerminal", + "name": "expression", + "idx": 0 + } + ] + }, + { + "type": "Option", + "idx": 1, "definition": [ { "type": "Terminal", - "name": "LBRACK", - "label": "LBRACK", + "name": "COLON", + "label": "COLON", "idx": 0, - "pattern": "[" + "pattern": ":" }, { "type": "Option", - "idx": 0, + "idx": 2, "definition": [ { "type": "NonTerminal", "name": "expression", - "idx": 0 + "idx": 1 } ] }, { "type": "Option", - "idx": 1, + "idx": 3, "definition": [ { "type": "Terminal", "name": "COLON", "label": "COLON", - "idx": 0, + "idx": 1, "pattern": ":" }, { - "type": "Option", - "idx": 2, - "definition": [ - { - "type": "NonTerminal", - "name": "expression", - "idx": 1 - } - ] - }, - { - "type": "Option", - "idx": 3, - "definition": [ - { - "type": "Terminal", - "name": "COLON", - "label": "COLON", - "idx": 1, - "pattern": ":" - }, - { - "type": "NonTerminal", - "name": "expression", - "idx": 2 - } - ] + "type": "NonTerminal", + "name": "expression", + "idx": 2 } ] - }, - { - "type": "Terminal", - "name": "RBRACK", - "label": "RBRACK", - "idx": 0, - "pattern": "]" } ] + }, + { + "type": "Terminal", + "name": "RBRACK", + "label": "RBRACK", + "idx": 0, + "pattern": "]" } ] }, From 0cd9a06ba7ff8ee576d8acc55b50437452ff431b Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 18:11:37 -0500 Subject: [PATCH 10/28] chore(parser): update cst-types --- apps/parser/generated/cst-types.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/parser/generated/cst-types.ts b/apps/parser/generated/cst-types.ts index bfc82e0..e501e0b 100644 --- a/apps/parser/generated/cst-types.ts +++ b/apps/parser/generated/cst-types.ts @@ -90,10 +90,19 @@ export interface ChainValueCstNode extends CstNode { export type ChainValueCstChildren = { value: ValueCstNode[]; - LBRACK?: IToken[]; + indexOrSlice?: IndexOrSliceCstNode[]; +}; + +export interface IndexOrSliceCstNode extends CstNode { + name: 'indexOrSlice'; + children: IndexOrSliceCstChildren; +} + +export type IndexOrSliceCstChildren = { + LBRACK: IToken[]; expression?: ExpressionCstNode[]; COLON?: IToken[]; - RBRACK?: IToken[]; + RBRACK: IToken[]; }; export interface ValueCstNode extends CstNode { @@ -146,6 +155,7 @@ export interface ICstNodeVisitor extends ICstVisitor { declaration(children: DeclarationCstChildren, param?: IN): OUT; expression(children: ExpressionCstChildren, param?: IN): OUT; chainValue(children: ChainValueCstChildren, param?: IN): OUT; + indexOrSlice(children: IndexOrSliceCstChildren, param?: IN): OUT; value(children: ValueCstChildren, param?: IN): OUT; constant(children: ConstantCstChildren, param?: IN): OUT; type(children: TypeCstChildren, param?: IN): OUT; From 704f919fee41f50071706dff7e46ba90997b06c5 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 18:29:59 -0500 Subject: [PATCH 11/28] fix(parser): fix comma separation --- apps/parser/src/parser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/parser/src/parser.ts b/apps/parser/src/parser.ts index 9767a09..0b80265 100644 --- a/apps/parser/src/parser.ts +++ b/apps/parser/src/parser.ts @@ -208,7 +208,7 @@ export class CalvinParser extends CstParser { ALT: () => { this.CONSUME(Tokens.LBRACK); this.MANY_SEP({ - SEP: () => this.CONSUME(Tokens.COMMA), + SEP: Tokens.COMMA, DEF: () => this.SUBRULE(this.expression), }); this.CONSUME(Tokens.RBRACK); From 054c627eb99beb8fd08fa4ea7df0f9968c539c8d Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 18:30:18 -0500 Subject: [PATCH 12/28] chore(docs): update syntax diagrams --- apps/parser/generated/syntax-diagrams.html | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/parser/generated/syntax-diagrams.html b/apps/parser/generated/syntax-diagrams.html index 67aa0c1..034ffca 100644 --- a/apps/parser/generated/syntax-diagrams.html +++ b/apps/parser/generated/syntax-diagrams.html @@ -779,9 +779,10 @@ "idx": 0, "separator": { "type": "Terminal", - "name": "SEP", - "label": "SEP", - "idx": 1 + "name": "COMMA", + "label": "COMMA", + "idx": 1, + "pattern": "," }, "definition": [ { From 2176a01a3c1c19aca683a5f22379f6f3361c2ad8 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 18:30:31 -0500 Subject: [PATCH 13/28] chore(parser): update cst-types --- apps/parser/generated/cst-types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/parser/generated/cst-types.ts b/apps/parser/generated/cst-types.ts index e501e0b..327e7eb 100644 --- a/apps/parser/generated/cst-types.ts +++ b/apps/parser/generated/cst-types.ts @@ -134,7 +134,7 @@ export type ConstantCstChildren = { INT?: IToken[]; LBRACK?: IToken[]; expression?: ExpressionCstNode[]; - SEP?: IToken[]; + COMMA?: IToken[]; RBRACK?: IToken[]; }; From 20dfd6a82492a1c2a40b48bc9e6f56d34b08ea64 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 18:31:28 -0500 Subject: [PATCH 14/28] chore(tests): add input file for arrays --- apps/parser/test/end-to-end/fixtures/arrays.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 apps/parser/test/end-to-end/fixtures/arrays.txt diff --git a/apps/parser/test/end-to-end/fixtures/arrays.txt b/apps/parser/test/end-to-end/fixtures/arrays.txt new file mode 100644 index 0000000..0222fc7 --- /dev/null +++ b/apps/parser/test/end-to-end/fixtures/arrays.txt @@ -0,0 +1,6 @@ +let arr = [1, 2]; +let empty = []; +let sliced = arr[1:2]; +let indexed = arr[0]; +let indexOfSliced = sliced[0]; +let check = indexed == indexOfSliced; \ No newline at end of file From abb2972acdc34ed28a90479e8ba8e593da8a4c59 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 18:32:10 -0500 Subject: [PATCH 15/28] feat(printer): handle index/slice --- apps/parser/src/visitors/printer.ts | 54 ++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/apps/parser/src/visitors/printer.ts b/apps/parser/src/visitors/printer.ts index 109e687..72942b1 100644 --- a/apps/parser/src/visitors/printer.ts +++ b/apps/parser/src/visitors/printer.ts @@ -1,12 +1,14 @@ import type { CstNode, IToken } from 'chevrotain'; import type { BodyCstChildren, + ChainValueCstChildren, ConstantCstChildren, DeclarationCstChildren, ExpressionCstChildren, FileCstChildren, ICstNodeVisitor, IfPredBodyCstChildren, + IndexOrSliceCstChildren, StatementCstChildren, StatementCstNode, TypeCstChildren, @@ -131,13 +133,45 @@ export class CalvinPrinter extends BaseCstVisitor implements ICstNodeVisitor 'tokenType' in v[0]) as IToken[]; tree(`(${op[0].image}!`, indent); - this.value(val.value[0].children, indent + 2); + this.chainValue(val.chainValue[0].children, indent + 2); tree(`)`, indent); } else { throw new Error(`TypeInference: unhandled value type ${JSON.stringify(val)}`); @@ -158,7 +192,19 @@ export class CalvinPrinter extends BaseCstVisitor implements ICstNodeVisitor { + this.expression(e.children, indent + 2); + }); + tree(']', indent); + } else if (c.LBRACK) { + // empty list + tree('[]', indent); + } else { + tree((Object.values(c)[0][0] as IToken).image, indent); + } } type(t: TypeCstChildren, indent: number) { From 378941a3141bb570bec99db685d804b979fbfada Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 18:34:19 -0500 Subject: [PATCH 16/28] chore(printer): fix visit to include new methods --- apps/parser/src/visitors/printer.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/apps/parser/src/visitors/printer.ts b/apps/parser/src/visitors/printer.ts index 72942b1..1e3d0c1 100644 --- a/apps/parser/src/visitors/printer.ts +++ b/apps/parser/src/visitors/printer.ts @@ -231,6 +231,12 @@ export class CalvinPrinter extends BaseCstVisitor implements ICstNodeVisitor Date: Thu, 30 Oct 2025 18:40:56 -0500 Subject: [PATCH 17/28] chore(tests): add multi-dimensional arrays to test file --- apps/parser/test/end-to-end/fixtures/arrays.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/parser/test/end-to-end/fixtures/arrays.txt b/apps/parser/test/end-to-end/fixtures/arrays.txt index 0222fc7..5b86cd3 100644 --- a/apps/parser/test/end-to-end/fixtures/arrays.txt +++ b/apps/parser/test/end-to-end/fixtures/arrays.txt @@ -3,4 +3,6 @@ let empty = []; let sliced = arr[1:2]; let indexed = arr[0]; let indexOfSliced = sliced[0]; -let check = indexed == indexOfSliced; \ No newline at end of file +let check = indexed == indexOfSliced; +let multi = [[1, 2], [3, 4]]; +let badMulti = [[1, 2], 3]; // should have semantic error \ No newline at end of file From 94f688193dedbcef6c7235a2203c343a61289292 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 19:47:14 -0500 Subject: [PATCH 18/28] fix(parser): parse input *before* checking if errors exist --- apps/parser/main.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/parser/main.ts b/apps/parser/main.ts index b4a0f94..97f2631 100644 --- a/apps/parser/main.ts +++ b/apps/parser/main.ts @@ -37,6 +37,9 @@ export async function main(): Promise { const lexingResult = CalvinLexer.tokenize(inputFile); // "input" is a setter which will reset the parser's state. parser.input = lexingResult.tokens; + + const output = parser.file(); + if (parser.errors.length > 0) { throw new AggregateError( parser.errors, @@ -44,8 +47,6 @@ export async function main(): Promise { ); } - const output = parser.file(); - printer.visit(output); } From ab8bdda7247e291717593cab1bdbca0091a6380f Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 19:55:58 -0500 Subject: [PATCH 19/28] feat(parser): add explicit array type --- apps/parser/src/parser.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/parser/src/parser.ts b/apps/parser/src/parser.ts index 0b80265..3e72d9e 100644 --- a/apps/parser/src/parser.ts +++ b/apps/parser/src/parser.ts @@ -217,7 +217,18 @@ export class CalvinParser extends CstParser { ]), ); - private type = this.RULE('type', () => this.CONSUME(Tokens.BASIC_TYPE)); + private type = this.RULE('type', () => { + this.CONSUME(Tokens.BASIC_TYPE); + this.MANY(() => { + this.SUBRULE(this.arrayType); + }); + }); + + private arrayType = this.RULE('arrayType', () => { + this.CONSUME(Tokens.LBRACK); + this.OPTION(() => this.CONSUME(Tokens.INT)); + this.CONSUME(Tokens.RBRACK); + }); } export const parser: CalvinParser = new CalvinParser(); From 7342ab1f1ec6ac45cdbc228204075a636dc149a1 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 19:56:09 -0500 Subject: [PATCH 20/28] chore(docs): update syntax diagrams --- apps/parser/generated/syntax-diagrams.html | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/apps/parser/generated/syntax-diagrams.html b/apps/parser/generated/syntax-diagrams.html index 034ffca..1936af5 100644 --- a/apps/parser/generated/syntax-diagrams.html +++ b/apps/parser/generated/syntax-diagrams.html @@ -816,6 +816,51 @@ "label": "BASIC_TYPE", "idx": 0, "pattern": "bool|(i|u|b)(8|16|32|64)|(r|x)(32|64)|string" + }, + { + "type": "Repetition", + "idx": 0, + "definition": [ + { + "type": "NonTerminal", + "name": "arrayType", + "idx": 0 + } + ] + } + ] + }, + { + "type": "Rule", + "name": "arrayType", + "orgText": "", + "definition": [ + { + "type": "Terminal", + "name": "LBRACK", + "label": "LBRACK", + "idx": 0, + "pattern": "[" + }, + { + "type": "Option", + "idx": 0, + "definition": [ + { + "type": "Terminal", + "name": "INT", + "label": "INT", + "idx": 0, + "pattern": "(\\+|-)?(0|[1-9]([\\d_]+\\d|\\d)?)" + } + ] + }, + { + "type": "Terminal", + "name": "RBRACK", + "label": "RBRACK", + "idx": 0, + "pattern": "]" } ] } From debdd4851ebc81822f0d7cb95e1a0a99b947e9e9 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 19:56:19 -0500 Subject: [PATCH 21/28] chore(parser): update cst-types --- apps/parser/generated/cst-types.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/parser/generated/cst-types.ts b/apps/parser/generated/cst-types.ts index 327e7eb..eb62d55 100644 --- a/apps/parser/generated/cst-types.ts +++ b/apps/parser/generated/cst-types.ts @@ -145,6 +145,18 @@ export interface TypeCstNode extends CstNode { export type TypeCstChildren = { BASIC_TYPE: IToken[]; + arrayType?: ArrayTypeCstNode[]; +}; + +export interface ArrayTypeCstNode extends CstNode { + name: 'arrayType'; + children: ArrayTypeCstChildren; +} + +export type ArrayTypeCstChildren = { + LBRACK: IToken[]; + INT?: IToken[]; + RBRACK: IToken[]; }; export interface ICstNodeVisitor extends ICstVisitor { @@ -159,4 +171,5 @@ export interface ICstNodeVisitor extends ICstVisitor { value(children: ValueCstChildren, param?: IN): OUT; constant(children: ConstantCstChildren, param?: IN): OUT; type(children: TypeCstChildren, param?: IN): OUT; + arrayType(children: ArrayTypeCstChildren, param?: IN): OUT; } From 945cee517b72d2a46ca83009434acf6170c1b358 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 19:56:35 -0500 Subject: [PATCH 22/28] feat(printer): handle array types --- apps/parser/src/visitors/printer.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/apps/parser/src/visitors/printer.ts b/apps/parser/src/visitors/printer.ts index 1e3d0c1..1bc4f72 100644 --- a/apps/parser/src/visitors/printer.ts +++ b/apps/parser/src/visitors/printer.ts @@ -1,5 +1,6 @@ import type { CstNode, IToken } from 'chevrotain'; import type { + ArrayTypeCstChildren, BodyCstChildren, ChainValueCstChildren, ConstantCstChildren, @@ -209,6 +210,15 @@ export class CalvinPrinter extends BaseCstVisitor implements ICstNodeVisitor { + this.arrayType(at.children, indent + 2); + }); + } + } + + arrayType(a: ArrayTypeCstChildren, indent: number) { + tree(`[${a.INT?.at(0)?.image ?? ''}]`, indent); } override visit(node: CstNode, indent: number = 0) { @@ -246,6 +256,9 @@ export class CalvinPrinter extends BaseCstVisitor implements ICstNodeVisitor Date: Thu, 30 Oct 2025 19:56:51 -0500 Subject: [PATCH 23/28] chore(tests): add array types to test file --- apps/parser/test/end-to-end/fixtures/arrays.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/parser/test/end-to-end/fixtures/arrays.txt b/apps/parser/test/end-to-end/fixtures/arrays.txt index 5b86cd3..fee1b02 100644 --- a/apps/parser/test/end-to-end/fixtures/arrays.txt +++ b/apps/parser/test/end-to-end/fixtures/arrays.txt @@ -1,8 +1,8 @@ let arr = [1, 2]; -let empty = []; +let empty: i32[] = []; // otherwise type is unknown[] let sliced = arr[1:2]; let indexed = arr[0]; let indexOfSliced = sliced[0]; let check = indexed == indexOfSliced; -let multi = [[1, 2], [3, 4]]; +let multi: i32[2][] = [[1, 2], [3, 4]]; let badMulti = [[1, 2], 3]; // should have semantic error \ No newline at end of file From 323bfed722576313242f193a36a4b537bb9bf90e Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 20:59:41 -0500 Subject: [PATCH 24/28] fix(printer): missing indexOrSlice call --- apps/parser/src/visitors/printer.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/parser/src/visitors/printer.ts b/apps/parser/src/visitors/printer.ts index 1bc4f72..61d6d4f 100644 --- a/apps/parser/src/visitors/printer.ts +++ b/apps/parser/src/visitors/printer.ts @@ -143,6 +143,11 @@ export class CalvinPrinter extends BaseCstVisitor implements ICstNodeVisitor { + this.indexOrSlice(ios.children, indent); + }); + } } indexOrSlice(ios: IndexOrSliceCstChildren, indent: number) { From cd19daa5ce7e9daf45a1327b491ae1f528a0bf35 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 21:07:22 -0500 Subject: [PATCH 25/28] chore(tests): add input for precedence handler --- apps/parser/test/end-to-end/fixtures/arrays.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/parser/test/end-to-end/fixtures/arrays.txt b/apps/parser/test/end-to-end/fixtures/arrays.txt index fee1b02..75feea4 100644 --- a/apps/parser/test/end-to-end/fixtures/arrays.txt +++ b/apps/parser/test/end-to-end/fixtures/arrays.txt @@ -1,7 +1,7 @@ let arr = [1, 2]; let empty: i32[] = []; // otherwise type is unknown[] let sliced = arr[1:2]; -let indexed = arr[0]; +let indexed = arr[0 + 1 - 1]; let indexOfSliced = sliced[0]; let check = indexed == indexOfSliced; let multi: i32[2][] = [[1, 2], [3, 4]]; From 543f23963e5a7ca914eb061ffd2ca925f497fe04 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Thu, 30 Oct 2025 21:07:59 -0500 Subject: [PATCH 26/28] chore(precedence): update precedence handler --- apps/parser/src/visitors/precedence.ts | 88 ++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/apps/parser/src/visitors/precedence.ts b/apps/parser/src/visitors/precedence.ts index 77701a2..7a973e2 100644 --- a/apps/parser/src/visitors/precedence.ts +++ b/apps/parser/src/visitors/precedence.ts @@ -1,17 +1,20 @@ import type { CstNode, TokenType } from 'chevrotain'; import type { + ArrayTypeCstChildren, BodyCstChildren, + ChainValueCstChildren, + ChainValueCstNode, ConstantCstChildren, DeclarationCstChildren, ExpressionCstChildren, ExpressionCstNode, FileCstChildren, IfPredBodyCstChildren, + IndexOrSliceCstChildren, StatementCstChildren, StatementCstNode, TypeCstChildren, ValueCstChildren, - ValueCstNode, } from '@/generated/cst-types.ts'; import * as Tokens from '../lexer.ts'; import { BaseCstVisitor } from '../parser.ts'; @@ -94,19 +97,26 @@ export class PrecedenceHandler extends BaseCstVisitor { // tree is now tree.right tree = { ...right }; // old tree.right is now tree.right.left - const left = tree.value[0]; + const left = tree.chainValue[0]; old.expression = [ { ...left, - children: { value: [{ ...left }] }, + children: { chainValue: [{ ...left }] }, name: 'expression', } satisfies ExpressionCstNode, ]; // new tree.left is now old tree - tree.value[0] = { - children: { expression: [{ name: 'expression', children: old }] }, - name: 'value', - } satisfies ValueCstNode; + tree.chainValue[0] = { + children: { + value: [ + { + name: 'value', + children: { expression: [{ name: 'expression', children: old }] }, + }, + ], + }, + name: 'chainValue', + } satisfies ChainValueCstNode; } } } @@ -115,16 +125,61 @@ export class PrecedenceHandler extends BaseCstVisitor { expression(expr: ExpressionCstNode) { expr.children = this.reorder(expr.children); - this.value(expr.children.value[0].children); + this.chainValue(expr.children.chainValue[0].children); + } + + chainValue(cval: ChainValueCstChildren) { + this.value(cval.value[0].children); + if (cval.indexOrSlice) { + cval.indexOrSlice.forEach((ios) => { + this.indexOrSlice(ios.children); + }); + } + } + + indexOrSlice(ios: IndexOrSliceCstChildren) { + if (ios.LBRACK && ios.RBRACK) { + if (ios.COLON) { + let exprCount = 0; + if (ios.expression?.at(exprCount)) { + // start + this.expression(ios.expression[exprCount++]); + } + if (ios.expression?.at(exprCount)) { + // end + this.expression(ios.expression[exprCount++]); + } + if (ios.COLON.at(1)) { + if (ios.expression?.at(exprCount)) { + // direction + this.expression(ios.expression[exprCount++]); + } + } + } else if (ios.expression?.at(0)) { + this.expression(ios.expression[0]); + } + } } value(val: ValueCstChildren) { if (val.expression) { // nested expression this.expression(val.expression[0]); - } else if (!val.constant && !val.ID && val.value) { + } else if (val.constant) { + // possible array literal + this.constant(val.constant[0].children); + } else if (!val.ID && val.chainValue) { // Unop - this.value(val.value[0].children); + this.chainValue(val.chainValue[0].children); + } + } + + constant(c: ConstantCstChildren) { + if (c.expression) { + // list + c.expression.forEach((e) => { + this.expression(e); + }); } } @@ -199,10 +254,10 @@ export class PrecedenceHandler extends BaseCstVisitor { } } - constant(_c: ConstantCstChildren) {} - type(_t: TypeCstChildren) {} + arrayType(_at: ArrayTypeCstChildren) {} + override visit(node: CstNode) { switch (node.name) { case 'file': @@ -223,6 +278,12 @@ export class PrecedenceHandler extends BaseCstVisitor { case 'expression': this.expression(node as ExpressionCstNode); break; + case 'chainValue': + this.chainValue(node.children as ChainValueCstChildren); + break; + case 'indexOrSlice': + this.indexOrSlice(node.children as IndexOrSliceCstChildren); + break; case 'value': this.value(node.children as ValueCstChildren); break; @@ -232,6 +293,9 @@ export class PrecedenceHandler extends BaseCstVisitor { case 'type': this.type(node.children as TypeCstChildren); break; + case 'arrayType': + this.arrayType(node.children as ArrayTypeCstChildren); + break; } } } From d1d842d6fdd96dabf127e82c06e76957d60aa460 Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Sat, 1 Nov 2025 15:00:22 -0500 Subject: [PATCH 27/28] feat(semantics): initial look at array indexing --- apps/parser/src/visitors/semantics.ts | 187 +++++++++++++++++++++----- 1 file changed, 153 insertions(+), 34 deletions(-) diff --git a/apps/parser/src/visitors/semantics.ts b/apps/parser/src/visitors/semantics.ts index 43baa7a..a446bfc 100644 --- a/apps/parser/src/visitors/semantics.ts +++ b/apps/parser/src/visitors/semantics.ts @@ -1,13 +1,16 @@ import { assert } from '@std/assert'; import type { CstNode, IToken } from 'chevrotain'; import type { + ArrayTypeCstChildren, BodyCstChildren, + ChainValueCstChildren, ConstantCstChildren, DeclarationCstChildren, ExpressionCstChildren, FileCstChildren, ICstNodeVisitor, IfPredBodyCstChildren, + IndexOrSliceCstChildren, StatementCstChildren, StatementCstNode, TypeCstChildren, @@ -17,7 +20,7 @@ import { Globals } from '../globals.ts'; import { debug, error, prefix, warn } from '../logging.ts'; import { BaseCstVisitor } from '../parser.ts'; -export enum TypeClasses { +export enum TypeClass { Unknown, Integral, Real, @@ -25,32 +28,55 @@ export enum TypeClasses { Boolean, Binary, String, + Container, Never, } -export function printType(t: TypeClasses): string { - switch (t) { - case TypeClasses.Unknown: +export type Type = + | { + class: TypeClass.Container; + type: Type; + size: number | null; + } + | { + class: Exclude; + type?: never; + size?: never; + }; + +export function printType(t: Type): string { + switch (t.class) { + case TypeClass.Unknown: return 'unknown'; - case TypeClasses.Integral: + case TypeClass.Integral: return 'integer'; - case TypeClasses.Real: + case TypeClass.Real: return 'real'; - case TypeClasses.Complex: + case TypeClass.Complex: return 'complex'; - case TypeClasses.Boolean: + case TypeClass.Boolean: return 'boolean'; - case TypeClasses.Binary: + case TypeClass.Binary: return 'binary'; - case TypeClasses.String: + case TypeClass.String: return 'string'; - case TypeClasses.Never: + case TypeClass.Container: + return `${printType(t.type)}[${t.size ?? '?'}]`; + case TypeClass.Never: return 'never'; } } +export function compareType(a: Type, b: Type): boolean { + if (a.class === TypeClass.Container && b.class === TypeClass.Container) { + return a.size === b.size && compareType(a.type, b.type); + } else { + return a.class === b.class; + } +} + export type Meta = { - returnType: TypeClasses; + returnType: Type; source: IToken; }; @@ -144,7 +170,7 @@ export class Scope { export class CalvinTypeAnalyzer extends BaseCstVisitor - implements ICstNodeVisitor + implements ICstNodeVisitor { private counts; private _errors; @@ -230,6 +256,12 @@ export class CalvinTypeAnalyzer case 'expression': this.expression(node.children as ExpressionCstChildren); break; + case 'chainValue': + this.chainValue(node.children as ChainValueCstChildren); + break; + case 'indexOrSlice': + this.indexOrSlice(node.children as IndexOrSliceCstChildren); + break; case 'value': this.value(node.children as ValueCstChildren); break; @@ -239,6 +271,9 @@ export class CalvinTypeAnalyzer case 'type': this.type(node.children as TypeCstChildren); break; + case 'arrayType': + this.arrayType(node.children as ArrayTypeCstChildren); + break; } } @@ -319,7 +354,8 @@ export class CalvinTypeAnalyzer const id = decl.ID[0]; const t = decl.type ? this.type(decl.type[0].children) : null; const expr = decl.expression ? this.expression(decl.expression[0].children) : null; - const meta = t ?? expr ?? ({ source: id, returnType: TypeClasses.Unknown } satisfies Meta); + const meta = + t ?? expr ?? ({ source: id, returnType: { class: TypeClass.Unknown } } satisfies Meta); const search = this.scope.search(id.image); const existing = this.scope === search?.scope && search.found; if (search) { @@ -334,7 +370,7 @@ export class CalvinTypeAnalyzer } } if (t && expr) { - if (t.returnType !== expr.returnType) { + if (!compareType(t.returnType, expr.returnType)) { // TODO type resolution algorithm this.error( `type declaration on line ${t.source.startLine} does not match assignment on line ${expr.source.startLine}`, @@ -361,12 +397,62 @@ export class CalvinTypeAnalyzer if (op) { // TODO value operator mismatch } - const val = this.value(expr.value[0].children); + const val = this.chainValue(expr.chainValue[0].children); // TODO handle type mismatch const exprMeta = expr.expression ? this.expression(expr.expression[0].children) : null; return exprMeta ?? val; } + chainValue(cval: ChainValueCstChildren): Meta { + const val = this.value(cval.value[0].children); + if (cval.indexOrSlice) { + return cval.indexOrSlice.reduce((val: Meta, ios) => { + this.indexOrSlice(ios.children); + const isSlice = !!ios.children.COLON; + if (val.returnType.class === TypeClass.Container) { + if (isSlice) { + val.source = ios.children.LBRACK[0]; + return val; + } else { + return { source: ios.children.LBRACK[0], returnType: val.returnType.type }; + } + } else { + this.error(`Bad array indexing at ${val.source.image} on line ${val.source.startLine}!`); + return { + source: ios.children.LBRACK[0], + returnType: { class: TypeClass.Never }, + } satisfies Meta; + } + }, val); + } else { + return val; + } + } + + indexOrSlice(ios: IndexOrSliceCstChildren): undefined { + if (ios.LBRACK && ios.RBRACK) { + if (ios.COLON) { + let exprCount = 0; + if (ios.expression?.at(exprCount)) { + // start + this.expression(ios.expression[exprCount++].children); + } + if (ios.expression?.at(exprCount)) { + // end + this.expression(ios.expression[exprCount++].children); + } + if (ios.COLON.at(1)) { + if (ios.expression?.at(exprCount)) { + // direction + this.expression(ios.expression[exprCount++].children); + } + } + } else if (ios.expression?.at(0)) { + this.expression(ios.expression[0].children); + } + } + } + value(val: ValueCstChildren): Meta { if (val.expression) { return this.expression(val.expression[0].children); @@ -380,47 +466,80 @@ export class CalvinTypeAnalyzer } const meta = existing?.found?.meta; - return meta ?? ({ source: id, returnType: TypeClasses.Never } satisfies Meta); - } else if (val.value) { + return meta ?? ({ source: id, returnType: { class: TypeClass.Never } } satisfies Meta); + } else if (val.chainValue) { //const op = Object.values(val).find((v) => 'tokenType' in v[0]) as IToken[]; // TODO value operator mismatch - return this.value(val.value[0].children); + return this.chainValue(val.chainValue[0].children); } throw new Error(`TypeInference: unhandled value type ${JSON.stringify(val)}`); } constant(c: ConstantCstChildren): Meta { - if (c.BIN) return { source: c.BIN[0], returnType: TypeClasses.Binary }; - else if (c.BOOL) return { source: c.BOOL[0], returnType: TypeClasses.Boolean }; - else if (c.CMPX) return { source: c.CMPX[0], returnType: TypeClasses.Complex }; - else if (c.REAL) return { source: c.REAL[0], returnType: TypeClasses.Real }; - else if (c.INT) return { source: c.INT[0], returnType: TypeClasses.Integral }; - else if (c.STRING) return { source: c.STRING[0], returnType: TypeClasses.String }; + if (c.BIN) return { source: c.BIN[0], returnType: { class: TypeClass.Binary } }; + else if (c.BOOL) return { source: c.BOOL[0], returnType: { class: TypeClass.Boolean } }; + else if (c.CMPX) return { source: c.CMPX[0], returnType: { class: TypeClass.Complex } }; + else if (c.REAL) return { source: c.REAL[0], returnType: { class: TypeClass.Real } }; + else if (c.INT) return { source: c.INT[0], returnType: { class: TypeClass.Integral } }; + else if (c.STRING) return { source: c.STRING[0], returnType: { class: TypeClass.String } }; + else if (c.LBRACK && c.expression) { + // TODO handle type mismatch + return { + source: c.LBRACK[0], + returnType: { + class: TypeClass.Container, + type: this.expression(c.expression[0].children).returnType, + size: c.expression.length, + }, + }; + } else if (c.LBRACK) + return { + source: c.LBRACK[0], + returnType: { class: TypeClass.Container, type: { class: TypeClass.Unknown }, size: 0 }, + }; // this should never be reached, except maybe when adding a new literal type throw new Error(`Could not get type from constant token ${JSON.stringify(c, null, 4)}`); } type(t: TypeCstChildren): Meta { const source = t.BASIC_TYPE[0]; - return { - source, - returnType: [source.image].map((t) => { + const tClass = { + class: [source.image].map((t) => { switch (t[0]) { case 'i': case 'u': - return TypeClasses.Integral; + return TypeClass.Integral; case 'x': - return TypeClasses.Complex; + return TypeClass.Complex; case 'r': - return TypeClasses.Real; + return TypeClass.Real; case 'b': - return t[1] === 'o' ? TypeClasses.Boolean : TypeClasses.Binary; + return t[1] === 'o' ? TypeClass.Boolean : TypeClass.Binary; case 's': - return TypeClasses.String; + return TypeClass.String; default: - return TypeClasses.Unknown; + return TypeClass.Unknown; } })[0], + } satisfies Type; + return { + source, + returnType: + t.arrayType?.reduce((p: Type, c) => ({ ...this.arrayType(c.children), type: p }), tClass) ?? + tClass, + }; + } + + arrayType(a: ArrayTypeCstChildren): { + class: TypeClass.Container; + type: Type; + size: number | null; + } { + const size = parseInt(a.INT?.at(0)?.image ?? 'NaN', 10); + return { + class: TypeClass.Container, + type: { class: TypeClass.Unknown }, + size: Number.isNaN(size) ? null : size, }; } } From de2dff62752b8530c6beaa81c978ea0c22e1622b Mon Sep 17 00:00:00 2001 From: Aidan Jones Date: Sat, 1 Nov 2025 15:00:38 -0500 Subject: [PATCH 28/28] chore(tests): update test input for bad indexing --- apps/parser/test/end-to-end/fixtures/arrays.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/parser/test/end-to-end/fixtures/arrays.txt b/apps/parser/test/end-to-end/fixtures/arrays.txt index 75feea4..f6bb36d 100644 --- a/apps/parser/test/end-to-end/fixtures/arrays.txt +++ b/apps/parser/test/end-to-end/fixtures/arrays.txt @@ -5,4 +5,5 @@ let indexed = arr[0 + 1 - 1]; let indexOfSliced = sliced[0]; let check = indexed == indexOfSliced; let multi: i32[2][] = [[1, 2], [3, 4]]; -let badMulti = [[1, 2], 3]; // should have semantic error \ No newline at end of file +let badMulti = [[1, 2], 3]; // should have semantic error +let badIndex = arr[1][1]; \ No newline at end of file