add some checker
This commit is contained in:
parent
0e46ac7bf5
commit
4fb445f272
@ -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;
|
||||||
|
210
checker.ts
210
checker.ts
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user