add type checking
This commit is contained in:
parent
d4ea73de1d
commit
8deda48ca6
@ -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: {
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user