add some checker
This commit is contained in:
parent
0e46ac7bf5
commit
4fb445f272
@ -4,6 +4,7 @@ import { Position } from "./token.ts";
|
||||
export type CheckedExpr =
|
||||
& {
|
||||
pos: Position;
|
||||
valueType: CheckedType;
|
||||
}
|
||||
& ({ exprType: "error"; message: string } | {
|
||||
exprType: "unit";
|
||||
@ -13,7 +14,6 @@ export type CheckedExpr =
|
||||
} | {
|
||||
exprType: "int";
|
||||
value: number;
|
||||
valueType: CheckedType;
|
||||
} | {
|
||||
exprType: "if";
|
||||
condition: CheckedExpr;
|
||||
@ -23,7 +23,6 @@ export type CheckedExpr =
|
||||
exprType: "block";
|
||||
body: CheckedExpr[];
|
||||
value: CheckedExpr | null;
|
||||
valueType: CheckedType;
|
||||
} | {
|
||||
exprType: "call";
|
||||
subject: CheckedExpr;
|
||||
|
210
checker.ts
210
checker.ts
@ -60,25 +60,117 @@ export class Checker {
|
||||
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 {
|
||||
switch (expr.exprType) {
|
||||
case "error":
|
||||
return this.errorExpr(expr.message, expr.pos);
|
||||
case "unit":
|
||||
return { pos: expr.pos, exprType: "unit" };
|
||||
return {
|
||||
pos: expr.pos,
|
||||
exprType: "unit",
|
||||
valueType: { pos: expr.pos, typeType: "unit" },
|
||||
};
|
||||
case "id":
|
||||
throw new Error("not implemented");
|
||||
return this.checkId(expr);
|
||||
case "int":
|
||||
return this.checkInt(expr);
|
||||
case "if":
|
||||
return this.checkIf(expr);
|
||||
case "block":
|
||||
return this.checkBlock(expr);
|
||||
case "call":
|
||||
return this.checkCall(expr);
|
||||
case "index":
|
||||
return this.checkIndex(expr);
|
||||
case "increment":
|
||||
return this.checkIncrement(expr as ParsedExpr<"increment">);
|
||||
case "decrement":
|
||||
return this.checkDecrement(expr as ParsedExpr<"decrement">);
|
||||
case "unary":
|
||||
return this.checkUnary(expr);
|
||||
case "binary":
|
||||
return this.checkBinary(expr);
|
||||
case "assign":
|
||||
return this.checkAssign(expr);
|
||||
default:
|
||||
return this.errorExpr(
|
||||
`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 {
|
||||
if (expr.value >= 1 << 15) {
|
||||
return this.errorExpr("16-bit literal too large", expr.pos);
|
||||
}
|
||||
return {
|
||||
pos: expr.pos,
|
||||
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 {
|
||||
this.errors.push({ message, pos });
|
||||
return {
|
||||
pos,
|
||||
exprType: "error",
|
||||
valueType: { pos, typeType: "unit" },
|
||||
message,
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user