compiler: equality

This commit is contained in:
SimonFJ20 2025-02-10 18:23:56 +01:00
parent aa35b6f35d
commit 245ed06a3b
4 changed files with 117 additions and 75 deletions

View File

@ -179,10 +179,10 @@ export type RefType = "ref" | "ptr";
export type UnaryType = "not" | "-";
export type BinaryType =
| "+"
| "*"
| "=="
| "-"
| "*"
| "/"
| "=="
| "!="
| "<"
| ">"

View File

@ -141,58 +141,12 @@ export class Checker {
return this.checkCallExpr(expr, k, expected);
case "unary":
return todo();
case "binary": {
const res = this.resolveTys(
this.exprTy(k.left),
this.exprTy(k.right),
);
if (!res.ok) {
this.exprTys.set(expr.id, Ty({ tag: "error" }));
this.report(res.val, expr.span);
return Ty({ tag: "error" });
}
this.exprTys.set(expr.id, res.val);
return res.val;
}
case "block": {
const ty = this.checkBlock(k.block, expected);
return ty;
}
case "if": {
const cond = this.exprTy(k.cond);
const condRes = this.resolveTys(cond, Ty({ tag: "bool" }));
if (!condRes.ok) {
this.exprTys.set(expr.id, Ty({ tag: "error" }));
this.report("if-condition must be a boolean", k.cond.span);
return Ty({ tag: "error" });
}
const truthy = this.exprTy(k.truthy);
if (!k.falsy) {
const truthyRes = this.resolveTys(
truthy,
Ty({ tag: "null" }),
);
if (!truthyRes.ok) {
this.exprTys.set(expr.id, Ty({ tag: "error" }));
this.report(
"if there isn't a falsy-clause, then the truthy clause must evaluate to null",
k.truthy.span,
);
return Ty({ tag: "error" });
}
this.exprTys.set(expr.id, Ty({ tag: "null" }));
return Ty({ tag: "null" });
}
const falsy = this.exprTy(k.falsy);
const bothRes = this.resolveTys(truthy, falsy);
if (!bothRes.ok) {
this.exprTys.set(expr.id, Ty({ tag: "error" }));
this.report(bothRes.val, k.truthy.span);
return Ty({ tag: "error" });
}
this.exprTys.set(expr.id, bothRes.val);
return bothRes.val;
}
case "binary":
return this.checkBinaryExpr(expr, k, expected);
case "block":
return this.checkBlock(k.block, expected);
case "if":
return this.checkIfExpr(expr, k, expected);
case "loop":
return todo();
case "while":
@ -261,15 +215,110 @@ export class Checker {
);
return Ty({ tag: "error" });
}
const _args = kind.args.map((arg, i) =>
this.checkExpr(arg, paramTys[i])
);
for (const [i, arg] of kind.args.entries()) {
this.checkExpr(arg, paramTys[i]);
}
const ty = fnTy.kind.returnTy;
this.exprTys.set(expr.id, ty);
return ty;
}
private checkBinaryExpr(
expr: ast.Expr,
kind: ast.BinaryExpr,
expected: Ty,
): Ty {
switch (kind.binaryType) {
case "+":
case "-":
case "*":
case "/": {
const operandRes = this.resolveTys(
this.exprTy(kind.left),
this.exprTy(kind.right),
);
if (!operandRes.ok) {
this.exprTys.set(expr.id, Ty({ tag: "error" }));
this.report(operandRes.val, expr.span);
return Ty({ tag: "error" });
}
const operatorRes = this.resolveTys(
operandRes.val,
Ty({ tag: "int" }),
);
if (!operatorRes.ok) {
this.exprTys.set(expr.id, Ty({ tag: "error" }));
this.report(operatorRes.val, expr.span);
return Ty({ tag: "error" });
}
this.exprTys.set(expr.id, operatorRes.val);
return operandRes.val;
}
case "==":
case "!=":
case "<":
case ">":
case "<=":
case ">=":
case "or":
case "and": {
const operandRes = this.resolveTys(
this.exprTy(kind.left),
this.exprTy(kind.right),
);
if (!operandRes.ok) {
this.exprTys.set(expr.id, Ty({ tag: "error" }));
this.report(operandRes.val, expr.span);
return Ty({ tag: "error" });
}
const ty = Ty({ tag: "bool" });
this.exprTys.set(expr.id, ty);
return ty;
}
}
}
private checkIfExpr(
expr: ast.Expr,
kind: ast.IfExpr,
expected: Ty,
): Ty {
const cond = this.exprTy(kind.cond);
const condRes = this.resolveTys(cond, Ty({ tag: "bool" }));
if (!condRes.ok) {
this.exprTys.set(expr.id, Ty({ tag: "error" }));
this.report("if-condition must be a boolean", kind.cond.span);
return Ty({ tag: "error" });
}
const truthy = this.exprTy(kind.truthy);
if (!kind.falsy) {
const truthyRes = this.resolveTys(
truthy,
Ty({ tag: "null" }),
);
if (!truthyRes.ok) {
this.exprTys.set(expr.id, Ty({ tag: "error" }));
this.report(
"if there isn't a falsy-clause, then the truthy clause must evaluate to null",
kind.truthy.span,
);
return Ty({ tag: "error" });
}
this.exprTys.set(expr.id, Ty({ tag: "null" }));
return Ty({ tag: "null" });
}
const falsy = this.exprTy(kind.falsy);
const bothRes = this.resolveTys(truthy, falsy);
if (!bothRes.ok) {
this.exprTys.set(expr.id, Ty({ tag: "error" }));
this.report(bothRes.val, kind.truthy.span);
return Ty({ tag: "error" });
}
this.exprTys.set(expr.id, bothRes.val);
return bothRes.val;
}
private tyTy(ty: ast.Ty): Ty {
return this.tyTys.get(ty.id) ||
this.checkTy(ty);

View File

@ -24,7 +24,7 @@ import {
TyKind,
UnaryType,
} from "@slige/ast";
import { Ctx, File as CtxFile, Pos, Res, Span, todo } from "@slige/common";
import { Ctx, File as CtxFile, Res, Span } from "@slige/common";
import { Lexer } from "./lexer.ts";
import { TokenIter } from "./token.ts";
import { SigFilter } from "./token.ts";
@ -726,11 +726,10 @@ export class Parser {
}
private parseOr(rs: ExprRestricts): Expr {
const pos = this.span();
let left = this.parseAnd(rs);
while (true) {
if (this.test("or")) {
left = this.parBinTail(left, pos, rs, this.parseAnd, "or");
left = this.parBinTail(left, rs, this.parseAnd, "or");
} else {
break;
}
@ -739,11 +738,10 @@ export class Parser {
}
private parseAnd(rs: ExprRestricts): Expr {
const pos = this.span();
let left = this.parseEq(rs);
let left = this.parseEquality(rs);
while (true) {
if (this.test("and")) {
left = this.parBinTail(left, pos, rs, this.parseEq, "and");
left = this.parBinTail(left, rs, this.parseEquality, "and");
} else {
break;
}
@ -751,36 +749,33 @@ export class Parser {
return left;
}
private parseEq(rs: ExprRestricts): Expr {
const pos = this.span();
private parseEquality(rs: ExprRestricts): Expr {
const left = this.parseComparison(rs);
if (this.test("==") || this.test("!=")) {
const op = this.current().type as BinaryType;
return this.parBinTail(left, pos, rs, this.parseComparison, op);
return this.parBinTail(left, rs, this.parseComparison, op);
}
return left;
}
private parseComparison(rs: ExprRestricts): Expr {
const pos = this.span();
const left = this.parseAddSub(rs);
if (
this.test("<") || this.test(">") || this.test("<=") ||
this.test(">=")
) {
const op = this.current().type as BinaryType;
return this.parBinTail(left, pos, rs, this.parseAddSub, op);
return this.parBinTail(left, rs, this.parseAddSub, op);
}
return left;
}
private parseAddSub(rs: ExprRestricts): Expr {
const pos = this.span();
let left = this.parseMulDiv(rs);
while (true) {
if (this.test("+") || this.test("-")) {
const op = this.current().type as BinaryType;
left = this.parBinTail(left, pos, rs, this.parseMulDiv, op);
left = this.parBinTail(left, rs, this.parseMulDiv, op);
continue;
}
break;
@ -789,12 +784,11 @@ export class Parser {
}
private parseMulDiv(rs: ExprRestricts): Expr {
const pos = this.span();
let left = this.parsePrefix(rs);
while (true) {
if (this.test("*") || this.test("/")) {
const op = this.current().type as BinaryType;
left = this.parBinTail(left, pos, rs, this.parsePrefix, op);
left = this.parBinTail(left, rs, this.parsePrefix, op);
continue;
}
break;
@ -804,7 +798,6 @@ export class Parser {
private parBinTail(
left: Expr,
span: Span,
rs: ExprRestricts,
parseRight: (this: Parser, rs: ExprRestricts) => Expr,
binaryType: BinaryType,
@ -813,7 +806,7 @@ export class Parser {
const right = parseRight.call(this, rs);
return this.expr(
{ tag: "binary", binaryType, left, right },
span,
Span.fromto(left.span, right.span),
);
}

View File

@ -7,7 +7,7 @@ fn main() {
let foo = 5;
let bar = 7 + foo;
if foo {} else {}
if foo == 5 {} else {}
let c = add(foo, bar);
}