From 4c4b0da2384d20edeb44eec427231ca505f6b50a Mon Sep 17 00:00:00 2001 From: sfja Date: Sat, 22 Feb 2025 02:26:01 +0100 Subject: [PATCH] compiler: add tuple structs --- slige/compiler/check/checker.ts | 77 +++++++++++++++++++++++++++++- slige/compiler/middle/ast_lower.ts | 36 +++++++++++++- slige/compiler/parse/parser.ts | 3 ++ slige/compiler/program.slg | 12 ++++- slige/compiler/resolve/resolver.ts | 34 ++++++++++++- slige/compiler/stringify/mir.ts | 35 +++++++++----- 6 files changed, 179 insertions(+), 18 deletions(-) diff --git a/slige/compiler/check/checker.ts b/slige/compiler/check/checker.ts index ce23c1c..a707785 100644 --- a/slige/compiler/check/checker.ts +++ b/slige/compiler/check/checker.ts @@ -351,8 +351,29 @@ export class Checker { case "error": return Ty({ tag: "error" }); case "enum": - case "struct": - return todo("return a ctor here"); + return todo("return enum type here"); + case "struct": { + const data = res.kind.kind.data; + switch (data.kind.tag) { + case "error": + return Ty({ tag: "error" }); + case "struct": + this.report( + "expected value, got struct type", + expr.span, + ); + return Ty({ tag: "error" }); + case "unit": + return this.structItemTy(res.kind.item, res.kind.kind); + case "tuple": + this.report( + "expected value, got struct type", + expr.span, + ); + return Ty({ tag: "error" }); + } + return exhausted(data.kind); + } case "variant": return todo("return a ctor here"); case "field": @@ -450,6 +471,9 @@ export class Checker { kind: ast.CallExpr, expected: Ty, ): Ty { + if (this.callExprIsTupleStructCtor(kind)) { + return this.checkCallExprTupleStructCtor(expr, kind, expected); + } const fnTy = this.exprTy(kind.expr); if (fnTy.kind.tag !== "fn") { if (fnTy.kind.tag === "error") { @@ -477,6 +501,55 @@ export class Checker { return ty; } + private callExprIsTupleStructCtor(kind: ast.CallExpr): boolean { + if (kind.expr.kind.tag !== "path") { + return false; + } + const res = this.re.exprRes(kind.expr.id); + return res.kind.tag === "struct"; + } + + private checkCallExprTupleStructCtor( + expr: ast.Expr, + kind: ast.CallExpr, + expected: Ty, + ): Ty { + if (kind.expr.kind.tag !== "path") { + throw new Error(); + } + const res = this.re.exprRes(kind.expr.id); + if (res.kind.tag !== "struct") { + throw new Error(); + } + const ty = this.structItemTy(res.kind.item, res.kind.kind); + this.exprTys.set(expr.id, ty); + if (ty.kind.tag === "error") { + return ty; + } + if (ty.kind.tag !== "struct") { + throw new Error(); + } + const data = ty.kind.data; + if (data.tag !== "tuple") { + this.report( + "struct data not a tuple", + kind.expr.kind.path.span, + ); + const ty = Ty({ tag: "error" }); + this.exprTys.set(expr.id, ty); + return ty; + } + for (const [i, arg] of kind.args.entries()) { + const argTy = this.exprTy(arg); + const tyRes = this.resolveTys(argTy, data.elems[i].ty); + if (!tyRes.ok) { + this.report(tyRes.val, arg.span); + return ty; + } + } + return ty; + } + private checkBinaryExpr( expr: ast.Expr, kind: ast.BinaryExpr, diff --git a/slige/compiler/middle/ast_lower.ts b/slige/compiler/middle/ast_lower.ts index 144141e..6d1220a 100644 --- a/slige/compiler/middle/ast_lower.ts +++ b/slige/compiler/middle/ast_lower.ts @@ -332,11 +332,32 @@ export class FnLowerer { } private lowerCallExpr(expr: ast.Expr, kind: ast.CallExpr): RVal { + if (this.callExprIsTupleStructCtor(kind)) { + return this.lowerCallExprTupleStructCtor(expr, kind); + } const args = kind.args.map((arg) => this.lowerExprToOperand(arg)); const func = this.lowerExprToOperand(kind.expr); return { tag: "call", func, args }; } + private callExprIsTupleStructCtor(kind: ast.CallExpr): boolean { + if (kind.expr.kind.tag !== "path") { + return false; + } + const res = this.re.exprRes(kind.expr.id); + return res.kind.tag === "struct"; + } + + private lowerCallExprTupleStructCtor( + expr: ast.Expr, + kind: ast.CallExpr, + ): RVal { + const ty = this.ch.exprTy(expr); + const fields = kind.args + .map((arg) => this.lowerExprToOperand(arg)); + return { tag: "struct", ty, fields }; + } + private lowerBinaryExpr(expr: ast.Expr, kind: ast.BinaryExpr): RVal { const left = this.lowerExprToOperand(kind.left); const right = this.lowerExprToOperand(kind.right); @@ -632,7 +653,20 @@ export class FnLowerer { case "error": return { tag: "error" }; case "enum": - case "struct": + return todo(); + case "struct": { + const data = re.kind.kind.data; + switch (data.kind.tag) { + case "error": + return { tag: "error" }; + case "struct": + case "unit": + return todo(data.kind.tag); + case "tuple": + return todo(data.kind.tag); + } + return exhausted(data.kind); + } case "variant": case "field": return todo(); diff --git a/slige/compiler/parse/parser.ts b/slige/compiler/parse/parser.ts index 80399e2..19a2e4d 100644 --- a/slige/compiler/parse/parser.ts +++ b/slige/compiler/parse/parser.ts @@ -394,6 +394,9 @@ export class Parser { } const ident = this.parseIdent(); const data = this.parseVariantData(); + if (data.kind.tag === "unit" || data.kind.tag === "tuple") { + this.eatSemicolon(); + } const span = Span.fromto(begin, data.span); return this.stmt({ tag: "item", diff --git a/slige/compiler/program.slg b/slige/compiler/program.slg index 1bdc8c0..e31f6a6 100644 --- a/slige/compiler/program.slg +++ b/slige/compiler/program.slg @@ -1,10 +1,18 @@ -struct A { +struct A(int); +struct B(int, int); +struct C { + a: A, v: int, } fn main() { - let a: A = A { v: 123 }; + let a: A = A(123); + let b = B(1, 2); + let c = C { + a: a, + v: 123, + }; } diff --git a/slige/compiler/resolve/resolver.ts b/slige/compiler/resolve/resolver.ts index 7cb6f5a..7b552d7 100644 --- a/slige/compiler/resolve/resolver.ts +++ b/slige/compiler/resolve/resolver.ts @@ -241,11 +241,31 @@ export class Resolver implements ast.Visitor { return "stop"; } + visitCallExpr(expr: ast.Expr, kind: ast.CallExpr): ast.VisitRes { + if ( + kind.expr.kind.tag === "path" && + kind.expr.kind.path.segments.length === 1 + ) { + const res = this.resolveTyPath(kind.expr.kind.path); + if (res.kind.tag === "struct") { + this.exprResols.set(kind.expr.id, res); + for (const arg of kind.args) { + ast.visitExpr(this, arg); + } + return "stop"; + } + } + // otherwise, just continue as usual + } + visitStructExpr(expr: ast.Expr, kind: ast.StructExpr): ast.VisitRes { if (!kind.path) { return todo(); } - this.resolveValPath(kind.path); + this.resolveTyPath(kind.path); + for (const field of kind.fields) { + ast.visitExpr(this, field.expr); + } return "stop"; } @@ -321,7 +341,7 @@ export class Resolver implements ast.Visitor { private resolveValPath(path: ast.Path): Resolve { let res: Resolve; - if (path.segments.length === 0) { + if (path.segments.length === 1) { res = this.syms.getVal(path.segments[0].ident); } else { res = path.segments @@ -343,6 +363,16 @@ export class Resolver implements ast.Visitor { exhausted(); }, this.syms.getTy(path.segments[0].ident)); } + if (res.kind.tag === "error") { + if (path.segments.length === 1) { + this.report( + `could not resolve symbol '${path.segments[0].ident.text}'`, + path.span, + ); + } else { + this.report(`could not path`, path.span); + } + } this.pathResols.set(path.id, res); return res; } diff --git a/slige/compiler/stringify/mir.ts b/slige/compiler/stringify/mir.ts index 380a0cd..2bd5a8e 100644 --- a/slige/compiler/stringify/mir.ts +++ b/slige/compiler/stringify/mir.ts @@ -169,18 +169,31 @@ export class MirFnStringifyer { const tyk = rval.ty.kind; if (tyk.tag === "struct") { const datak = tyk.kind.data.kind; - if (datak.tag !== "struct") { - throw new Error(); + switch (datak.tag) { + case "error": + return ""; + case "unit": + return todo(); + case "tuple": { + const name = tyk.item.ident.text; + const fields = rval.fields + .map((field, idx) => this.operand(field)) + .join(", "); + return `${name}(${fields})`; + } + case "struct": { + const name = tyk.item.ident.text; + const fields = rval.fields + .map((field, idx) => + `${datak.fields[idx].ident!.text}: ${ + this.operand(field) + }` + ) + .join(", "); + return `${name} { ${fields} }`; + } } - const name = tyk.item.ident.text; - const fields = rval.fields - .map((field, idx) => - `${datak.fields[idx].ident!.text}: ${ - this.operand(field) - }` - ) - .join(", "); - return `${name} { ${fields} }`; + return exhausted(datak); } else { return todo(); }