From 8deda48ca6d5c59da5750e6c8e502cc8e6e74b0f Mon Sep 17 00:00:00 2001 From: SimonFJ20 Date: Mon, 9 Dec 2024 10:52:05 +0100 Subject: [PATCH] add type checking --- compiler/Checker.ts | 135 ++++++++++++++++++++++++-------------------- compiler/vtypes.ts | 4 +- 2 files changed, 76 insertions(+), 63 deletions(-) diff --git a/compiler/Checker.ts b/compiler/Checker.ts index 28b780b..40836be 100644 --- a/compiler/Checker.ts +++ b/compiler/Checker.ts @@ -91,66 +91,12 @@ export class Checker { return this.checkBinaryExpr(expr); case "group": return this.checkExpr(expr.kind.expr); - case "field": { - const subject = this.checkExpr(expr.kind.subject); - if (subject.type !== "struct") { - this.report("cannot use field on non-struct", pos); - return { type: "error" }; - } - const value = expr.kind.value; - const found = subject.fields.find((param) => - param.ident === value - ); - if (!found) { - this.report( - `no field named '${expr.kind.value}' on struct`, - pos, - ); - return { type: "error" }; - } - return found.vtype; - } - case "index": { - const subject = this.checkExpr(expr.kind.subject); - if (subject.type !== "array") { - this.report("cannot index on non-array", pos); - return { type: "error" }; - } - return subject.inner; - } - case "call": { - const subject = this.checkExpr(expr.kind.subject); - if (subject.type !== "fn") { - this.report("cannot call non-fn", pos); - return { type: "error" }; - } - const args = expr.kind.args.map((arg) => - this.checkExpr(arg) - ); - if (args.length !== subject.params.length) { - this.report( - `incorrect number of arguments` + - `, expected ${subject.params.length}`, - pos, - ); - } - for (let i = 0; i < args.length; ++i) { - if (!vtypesEqual(args[i], subject.params[i].vtype)) { - this.report( - `incorrect argument ${i} '${ - subject.params[i].ident - }'` + - `, expected ${ - vtypeToString(subject.params[i].vtype) - }` + - `, got ${vtypeToString(args[i])}`, - pos, - ); - break; - } - } - return subject.returnType; - } + case "field": + return this.checkFieldExpr(expr); + case "index": + return this.checkIndexExpr(expr); + case "call": + return this.checkCallExpr(expr); case "unary": case "if": case "loop": @@ -170,10 +116,10 @@ export class Checker { } public checkBinaryExpr(expr: Expr): VType { - const pos = expr.pos; if (expr.kind.type !== "binary") { throw new Error(); } + const pos = expr.pos; const left = this.checkExpr(expr.kind.left); const right = this.checkExpr(expr.kind.right); for (const operation of simpleBinaryOperations) { @@ -197,6 +143,73 @@ export class Checker { ); return { type: "error" }; } + + public checkFieldExpr(expr: Expr): VType { + if (expr.kind.type !== "field") { + throw new Error(); + } + const pos = expr.pos; + const subject = this.checkExpr(expr.kind.subject); + if (subject.type !== "struct") { + this.report("cannot use field on non-struct", pos); + return { type: "error" }; + } + const value = expr.kind.value; + const found = subject.fields.find((param) => param.ident === value); + if (!found) { + this.report( + `no field named '${expr.kind.value}' on struct`, + pos, + ); + return { type: "error" }; + } + return found.vtype; + } + + public checkIndexExpr(expr: Expr): VType { + if (expr.kind.type !== "index") { + throw new Error(); + } + const pos = expr.pos; + const subject = this.checkExpr(expr.kind.subject); + if (subject.type !== "array") { + this.report("cannot index on non-array", pos); + return { type: "error" }; + } + return subject.inner; + } + + public checkCallExpr(expr: Expr): VType { + if (expr.kind.type !== "call") { + throw new Error(); + } + const pos = expr.pos; + const subject = this.checkExpr(expr.kind.subject); + if (subject.type !== "fn") { + this.report("cannot call non-fn", pos); + return { type: "error" }; + } + const args = expr.kind.args.map((arg) => this.checkExpr(arg)); + if (args.length !== subject.params.length) { + this.report( + `incorrect number of arguments` + + `, expected ${subject.params.length}`, + pos, + ); + } + for (let i = 0; i < args.length; ++i) { + if (!vtypesEqual(args[i], subject.params[i].vtype)) { + this.report( + `incorrect argument ${i} '${subject.params[i].ident}'` + + `, expected ${vtypeToString(subject.params[i].vtype)}` + + `, got ${vtypeToString(args[i])}`, + pos, + ); + break; + } + } + return subject.returnType; + } } const simpleBinaryOperations: { diff --git a/compiler/vtypes.ts b/compiler/vtypes.ts index 9f617ed..4567c0c 100644 --- a/compiler/vtypes.ts +++ b/compiler/vtypes.ts @@ -20,7 +20,7 @@ export function vtypesEqual(a: VType, b: VType): boolean { return false; } if ( - ["error", "unknown", "null", "int", "string", "bool", "struct"] + ["error", "unknown", "null", "int", "string", "bool"] .includes(a.type) ) { return true; @@ -44,7 +44,7 @@ export function vtypesEqual(a: VType, b: VType): boolean { export function vtypeToString(vtype: VType): string { if ( - ["error", "unknown", "null", "int", "string", "bool", "struct"] + ["error", "unknown", "null", "int", "string", "bool"] .includes(vtype.type) ) { return vtype.type;