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 UnaryType = "not" | "-";
export type BinaryType = export type BinaryType =
| "+" | "+"
| "*"
| "=="
| "-" | "-"
| "*"
| "/" | "/"
| "=="
| "!=" | "!="
| "<" | "<"
| ">" | ">"

View File

@ -141,58 +141,12 @@ export class Checker {
return this.checkCallExpr(expr, k, expected); return this.checkCallExpr(expr, k, expected);
case "unary": case "unary":
return todo(); return todo();
case "binary": { case "binary":
const res = this.resolveTys( return this.checkBinaryExpr(expr, k, expected);
this.exprTy(k.left), case "block":
this.exprTy(k.right), return this.checkBlock(k.block, expected);
); case "if":
if (!res.ok) { return this.checkIfExpr(expr, k, expected);
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 "loop": case "loop":
return todo(); return todo();
case "while": case "while":
@ -261,15 +215,110 @@ export class Checker {
); );
return Ty({ tag: "error" }); return Ty({ tag: "error" });
} }
const _args = kind.args.map((arg, i) => for (const [i, arg] of kind.args.entries()) {
this.checkExpr(arg, paramTys[i]) this.checkExpr(arg, paramTys[i]);
); }
const ty = fnTy.kind.returnTy; const ty = fnTy.kind.returnTy;
this.exprTys.set(expr.id, ty); this.exprTys.set(expr.id, ty);
return 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 { private tyTy(ty: ast.Ty): Ty {
return this.tyTys.get(ty.id) || return this.tyTys.get(ty.id) ||
this.checkTy(ty); this.checkTy(ty);

View File

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

View File

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