From 6620dbcaceb2b5b57958473c4aa256789f927cca Mon Sep 17 00:00:00 2001 From: sfja Date: Thu, 12 Mar 2026 00:13:33 +0100 Subject: [PATCH] add array syntax --- src/ast.ts | 3 +++ src/front/check.ts | 18 ++++++++++++++++++ src/front/parse.ts | 15 ++++++++++++++- src/ty.ts | 1 + tests/_array.ethlang | 6 ++++++ tests/test.sh | 2 +- 6 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 tests/_array.ethlang diff --git a/src/ast.ts b/src/ast.ts index 57a84be..4ece706 100644 --- a/src/ast.ts +++ b/src/ast.ts @@ -80,6 +80,8 @@ export class Node { return visit(); case "IntExpr": return visit(); + case "ArrayExpr": + return visit(...k.values); case "CallExpr": return visit(k.expr, ...k.args); case "UnaryExpr": @@ -115,6 +117,7 @@ export type NodeKind = | { tag: "Param"; ident: string; ty: Node | null } | { tag: "IdentExpr"; ident: string } | { tag: "IntExpr"; value: number } + | { tag: "ArrayExpr"; values: Node[] } | { tag: "CallExpr"; expr: Node; args: Node[] } | { tag: "UnaryExpr"; op: UnaryOp; expr: Node; tok: string } | { tag: "BinaryExpr"; op: BinaryOp; left: Node; right: Node; tok: string } diff --git a/src/front/check.ts b/src/front/check.ts index 9323b70..113eb85 100644 --- a/src/front/check.ts +++ b/src/front/check.ts @@ -80,6 +80,24 @@ export class Checker { return Ty.Int; } + if (node.is("ArrayExpr")) { + let ty: Ty | null = null; + for (const value of node.kind.values) { + const valueTy = this.check(value); + if (ty) { + this.assertCompatible(ty, valueTy, value.line); + } else { + ty = valueTy; + } + } + if (!ty) { + this.error(node.line, `could not infer type of empty array`); + this.fail(); + } + const length = node.kind.values.length; + return Ty.create("Array", { ty, length }); + } + if (node.is("CallExpr")) { return this.checkCall(node); } diff --git a/src/front/parse.ts b/src/front/parse.ts index 7a83ab1..575b4f5 100644 --- a/src/front/parse.ts +++ b/src/front/parse.ts @@ -240,6 +240,19 @@ export class Parser { const expr = this.parseExpr(); this.mustEat(")"); return expr; + } else if (this.eat("[")) { + const values: ast.Node[] = []; + if (!this.done && !this.test("]")) { + values.push(this.parseExpr()); + while (this.eat(",")) { + if (this.test("]")) { + break; + } + values.push(this.parseExpr()); + } + } + this.mustEat("]"); + return ast.Node.create(loc, "ArrayExpr", { values }); } else { this.mustEat(""); throw new Error(); @@ -318,7 +331,7 @@ export type Tok = { type: string; value: string; line: number }; const keywordPattern = /^(?:fn)|(?:return)|(?:let)|(?:if)|(?:else)|(?:while)|(?:break)|(?:or)|(?:and)|(?:not)|(?:mut)$/; const operatorPattern = - /((?:\->)|(?:==)|(?:!=)|(?:<=)|(?:>=)|(?:<<)|(?:>>)|[\n\(\)\{\}\,\.\;\:\!\=\<\>\&\^\|\+\-\*\/\%])/g; + /((?:\->)|(?:==)|(?:!=)|(?:<=)|(?:>=)|(?:<<)|(?:>>)|[\n\(\)\{\}\[\]\,\.\;\:\!\=\<\>\&\^\|\+\-\*\/\%])/g; export function tokenize(text: string): Tok[] { return text diff --git a/src/ty.ts b/src/ty.ts index 831b71e..55ee356 100644 --- a/src/ty.ts +++ b/src/ty.ts @@ -132,5 +132,6 @@ export type TyKind = | { tag: "Bool" } | { tag: "Ptr"; ty: Ty } | { tag: "PtrMut"; ty: Ty } + | { tag: "Array"; ty: Ty; length: number } | { tag: "Fn"; params: Ty[]; retTy: Ty } | { tag: "FnStmt"; ty: Ty; stmt: ast.NodeWithKind<"FnStmt"> }; diff --git a/tests/_array.ethlang b/tests/_array.ethlang new file mode 100644 index 0000000..ffe0489 --- /dev/null +++ b/tests/_array.ethlang @@ -0,0 +1,6 @@ + +fn main() +{ + let array = [1, 2, 3]; +} + diff --git a/tests/test.sh b/tests/test.sh index 0b42ad5..5830a78 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -5,7 +5,7 @@ set -e TEST_DIR=$(dirname $0) SRC_DIR=$TEST_DIR/../src -TEST_SRC=$(find $TEST_DIR -name '*.ethlang') +TEST_SRC=$(find $TEST_DIR -name '*.ethlang' -and -not -name "_*") count_total=0 count_succeeded=0