add some checker

This commit is contained in:
SimonFJ20 2023-05-04 01:06:09 +02:00
parent 0e46ac7bf5
commit 4fb445f272
2 changed files with 209 additions and 4 deletions

View File

@ -4,6 +4,7 @@ import { Position } from "./token.ts";
export type CheckedExpr = export type CheckedExpr =
& { & {
pos: Position; pos: Position;
valueType: CheckedType;
} }
& ({ exprType: "error"; message: string } | { & ({ exprType: "error"; message: string } | {
exprType: "unit"; exprType: "unit";
@ -13,7 +14,6 @@ export type CheckedExpr =
} | { } | {
exprType: "int"; exprType: "int";
value: number; value: number;
valueType: CheckedType;
} | { } | {
exprType: "if"; exprType: "if";
condition: CheckedExpr; condition: CheckedExpr;
@ -23,7 +23,6 @@ export type CheckedExpr =
exprType: "block"; exprType: "block";
body: CheckedExpr[]; body: CheckedExpr[];
value: CheckedExpr | null; value: CheckedExpr | null;
valueType: CheckedType;
} | { } | {
exprType: "call"; exprType: "call";
subject: CheckedExpr; subject: CheckedExpr;

View File

@ -60,25 +60,117 @@ export class Checker {
private searchTopLevelFn(statement: ParsedExpr<"fn">) { private searchTopLevelFn(statement: ParsedExpr<"fn">) {
} }
private checkStatement(expr: ParsedExpr): CheckedExpr {
switch (expr.exprType) {
case "if":
return this.checkIfStatement(expr);
case "let":
return this.checkLetStatement(expr);
case "fn":
return this.checkFnStatement(expr);
case "return":
return this.checkReturnStatement(expr);
case "loop":
return this.checkLoopStatement(expr);
case "while":
return this.checkWhileStatement(expr);
case "break":
return this.checkBreakStatement(expr);
case "continue":
return this.checkContinueStatement(expr);
default:
return this.checkExpr(expr);
}
}
private checkIfStatement(expr: ParsedExpr<"if">): CheckedExpr {
const condition = this.checkExpr(expr.condition);
const truthy = this.checkExpr(expr.truthy);
const falsy = expr.falsy ? this.checkExpr(expr.falsy) : null;
return {
pos: expr.pos,
exprType: "if",
condition,
truthy,
falsy,
valueType: { pos: expr.pos, typeType: "unit" },
};
}
private checkLetStatement(expr: ParsedExpr<"let">): CheckedExpr {
throw new Error("not implemented");
}
private checkFnStatement(expr: ParsedExpr<"fn">): CheckedExpr {
throw new Error("not implemented");
}
private checkReturnStatement(expr: ParsedExpr<"return">): CheckedExpr {
throw new Error("not implemented");
}
private checkLoopStatement(expr: ParsedExpr<"loop">): CheckedExpr {
const body = this.checkExpr(expr.body);
return {
pos: expr.pos,
exprType: "loop",
body,
valueType: { pos: expr.pos, typeType: "unit" },
};
}
private checkWhileStatement(expr: ParsedExpr<"while">): CheckedExpr {
const condition = this.checkExpr(expr.condition);
const body = this.checkExpr(expr.body);
return {
pos: expr.pos,
exprType: "while",
condition,
body,
valueType: { pos: expr.pos, typeType: "unit" },
};
}
private checkBreakStatement(expr: ParsedExpr<"break">): CheckedExpr {
return { ...expr, valueType: { pos: expr.pos, typeType: "unit" } };
}
private checkContinueStatement(expr: ParsedExpr<"continue">): CheckedExpr {
return { ...expr, valueType: { pos: expr.pos, typeType: "unit" } };
}
private checkExpr(expr: ParsedExpr): CheckedExpr { private checkExpr(expr: ParsedExpr): CheckedExpr {
switch (expr.exprType) { switch (expr.exprType) {
case "error": case "error":
return this.errorExpr(expr.message, expr.pos); return this.errorExpr(expr.message, expr.pos);
case "unit": case "unit":
return { pos: expr.pos, exprType: "unit" }; return {
pos: expr.pos,
exprType: "unit",
valueType: { pos: expr.pos, typeType: "unit" },
};
case "id": case "id":
throw new Error("not implemented"); return this.checkId(expr);
case "int": case "int":
return this.checkInt(expr); return this.checkInt(expr);
case "if": case "if":
return this.checkIf(expr);
case "block": case "block":
return this.checkBlock(expr);
case "call": case "call":
return this.checkCall(expr);
case "index": case "index":
return this.checkIndex(expr);
case "increment": case "increment":
return this.checkIncrement(expr as ParsedExpr<"increment">);
case "decrement": case "decrement":
return this.checkDecrement(expr as ParsedExpr<"decrement">);
case "unary": case "unary":
return this.checkUnary(expr);
case "binary": case "binary":
return this.checkBinary(expr);
case "assign": case "assign":
return this.checkAssign(expr);
default: default:
return this.errorExpr( return this.errorExpr(
`expected expression, got '${expr.exprType}' statement`, `expected expression, got '${expr.exprType}' statement`,
@ -87,7 +179,14 @@ export class Checker {
} }
} }
private checkId(_expr: ParsedExpr<"id">): CheckedExpr {
throw new Error("not implemented");
}
private checkInt(expr: ParsedExpr<"int">): CheckedExpr { private checkInt(expr: ParsedExpr<"int">): CheckedExpr {
if (expr.value >= 1 << 15) {
return this.errorExpr("16-bit literal too large", expr.pos);
}
return { return {
pos: expr.pos, pos: expr.pos,
exprType: "int", exprType: "int",
@ -96,11 +195,118 @@ export class Checker {
}; };
} }
private checkIf(expr: ParsedExpr<"if">): CheckedExpr {
const condition = this.checkExpr(expr.condition);
const truthy = this.checkExpr(expr.truthy);
if (!expr.falsy) {
this.errors.push({
pos: expr.pos,
message: "expected else-block in if expression",
});
return {
pos: expr.pos,
exprType: "if",
condition,
truthy,
falsy: null,
valueType: { pos: expr.pos, typeType: "unit" },
};
}
const falsy = this.checkExpr(expr.falsy);
const valueType = this.compatibleType(
truthy.valueType,
falsy.valueType,
);
return {
pos: expr.pos,
exprType: "if",
condition,
truthy,
falsy,
valueType,
};
}
private checkBlock(expr: ParsedExpr<"block">): CheckedExpr {
const body = expr.body.map((statement) =>
this.checkStatement(statement)
);
if (expr.value) {
const value = this.checkExpr(expr.value);
return {
pos: expr.pos,
exprType: "block",
body,
value,
valueType: value.valueType,
};
} else {
return {
pos: expr.pos,
exprType: "block",
body,
value: null,
valueType: { pos: expr.pos, typeType: "unit" },
};
}
}
private checkCall(expr: ParsedExpr<"call">): CheckedExpr {
throw new Error("not implemented");
}
private checkIndex(expr: ParsedExpr<"index">): CheckedExpr {
throw new Error("not implemented");
}
private checkIncrement(expr: ParsedExpr<"increment">): CheckedExpr {
throw new Error("not implemented");
}
private checkDecrement(expr: ParsedExpr<"decrement">): CheckedExpr {
throw new Error("not implemented");
}
private checkUnary(expr: ParsedExpr<"unary">): CheckedExpr {
throw new Error("not implemented");
}
private checkBinary(expr: ParsedExpr<"binary">): CheckedExpr {
throw new Error("not implemented");
}
private checkAssign(expr: ParsedExpr<"assign">): CheckedExpr {
throw new Error("not implemented");
}
private compatibleType(
a: CheckedType,
b: CheckedType,
reversed = false,
): CheckedType {
if (a.typeType === "error") {
return a;
} else if (b.typeType === "error") {
return b;
} else if (a.typeType === "unit" && b.typeType === "unit") {
return a;
} else if (a.typeType === "u16" && b.typeType === "u16") {
return a;
} else if (a.typeType === "u16" && b.typeType === "i16") {
return a;
} else if (!reversed) {
return this.compatibleType(b, a, true);
} else {
throw new Error("not implemented");
}
}
private errorExpr(message: string, pos: Position): CheckedExpr { private errorExpr(message: string, pos: Position): CheckedExpr {
this.errors.push({ message, pos }); this.errors.push({ message, pos });
return { return {
pos, pos,
exprType: "error", exprType: "error",
valueType: { pos, typeType: "unit" },
message, message,
}; };
} }