add type checking

This commit is contained in:
SimonFJ20 2024-12-09 10:52:05 +01:00
parent d4ea73de1d
commit 8deda48ca6
2 changed files with 76 additions and 63 deletions

View File

@ -91,66 +91,12 @@ export class Checker {
return this.checkBinaryExpr(expr); return this.checkBinaryExpr(expr);
case "group": case "group":
return this.checkExpr(expr.kind.expr); return this.checkExpr(expr.kind.expr);
case "field": { case "field":
const subject = this.checkExpr(expr.kind.subject); return this.checkFieldExpr(expr);
if (subject.type !== "struct") { case "index":
this.report("cannot use field on non-struct", pos); return this.checkIndexExpr(expr);
return { type: "error" }; case "call":
} return this.checkCallExpr(expr);
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 "unary": case "unary":
case "if": case "if":
case "loop": case "loop":
@ -170,10 +116,10 @@ export class Checker {
} }
public checkBinaryExpr(expr: Expr): VType { public checkBinaryExpr(expr: Expr): VType {
const pos = expr.pos;
if (expr.kind.type !== "binary") { if (expr.kind.type !== "binary") {
throw new Error(); throw new Error();
} }
const pos = expr.pos;
const left = this.checkExpr(expr.kind.left); const left = this.checkExpr(expr.kind.left);
const right = this.checkExpr(expr.kind.right); const right = this.checkExpr(expr.kind.right);
for (const operation of simpleBinaryOperations) { for (const operation of simpleBinaryOperations) {
@ -197,6 +143,73 @@ export class Checker {
); );
return { type: "error" }; 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: { const simpleBinaryOperations: {

View File

@ -20,7 +20,7 @@ export function vtypesEqual(a: VType, b: VType): boolean {
return false; return false;
} }
if ( if (
["error", "unknown", "null", "int", "string", "bool", "struct"] ["error", "unknown", "null", "int", "string", "bool"]
.includes(a.type) .includes(a.type)
) { ) {
return true; return true;
@ -44,7 +44,7 @@ export function vtypesEqual(a: VType, b: VType): boolean {
export function vtypeToString(vtype: VType): string { export function vtypeToString(vtype: VType): string {
if ( if (
["error", "unknown", "null", "int", "string", "bool", "struct"] ["error", "unknown", "null", "int", "string", "bool"]
.includes(vtype.type) .includes(vtype.type)
) { ) {
return vtype.type; return vtype.type;