This commit is contained in:
parent
2dff17144c
commit
856618d113
@ -82,6 +82,8 @@ export class Node {
|
|||||||
return visit();
|
return visit();
|
||||||
case "CallExpr":
|
case "CallExpr":
|
||||||
return visit(k.expr, ...k.args);
|
return visit(k.expr, ...k.args);
|
||||||
|
case "UnaryExpr":
|
||||||
|
return visit(k.expr);
|
||||||
case "BinaryExpr":
|
case "BinaryExpr":
|
||||||
return visit(k.left, k.right);
|
return visit(k.left, k.right);
|
||||||
case "IdentTy":
|
case "IdentTy":
|
||||||
@ -111,9 +113,14 @@ export type NodeKind =
|
|||||||
| { tag: "IdentExpr"; ident: string }
|
| { tag: "IdentExpr"; ident: string }
|
||||||
| { tag: "IntExpr"; value: number }
|
| { tag: "IntExpr"; value: number }
|
||||||
| { tag: "CallExpr"; expr: Node; args: Node[] }
|
| { tag: "CallExpr"; expr: Node; args: Node[] }
|
||||||
|
| { tag: "UnaryExpr"; op: UnaryOp; expr: Node; tok: string }
|
||||||
| { tag: "BinaryExpr"; op: BinaryOp; left: Node; right: Node; tok: string }
|
| { tag: "BinaryExpr"; op: BinaryOp; left: Node; right: Node; tok: string }
|
||||||
| { tag: "IdentTy"; ident: string };
|
| { tag: "IdentTy"; ident: string };
|
||||||
|
|
||||||
|
export type UnaryOp =
|
||||||
|
| "Not"
|
||||||
|
| "Negate";
|
||||||
|
|
||||||
export type BinaryOp =
|
export type BinaryOp =
|
||||||
| "Or"
|
| "Or"
|
||||||
| "And"
|
| "And"
|
||||||
|
|||||||
@ -84,6 +84,21 @@ export class Checker {
|
|||||||
return this.checkCall(node);
|
return this.checkCall(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node.is("UnaryExpr")) {
|
||||||
|
const expr = this.check(node.kind.expr);
|
||||||
|
if (node.kind.op === "Negate" && expr.compatibleWith(Ty.Int)) {
|
||||||
|
return Ty.Int;
|
||||||
|
}
|
||||||
|
if (node.kind.op === "Not" && expr.compatibleWith(Ty.Bool)) {
|
||||||
|
return Ty.Bool;
|
||||||
|
}
|
||||||
|
this.error(
|
||||||
|
node.line,
|
||||||
|
`operator '${node.kind.tok}' cannot be applied to type '${expr.pretty()}'`,
|
||||||
|
);
|
||||||
|
this.fail();
|
||||||
|
}
|
||||||
|
|
||||||
if (node.is("BinaryExpr")) {
|
if (node.is("BinaryExpr")) {
|
||||||
const left = this.check(node.kind.left);
|
const left = this.check(node.kind.left);
|
||||||
const right = this.check(node.kind.right);
|
const right = this.check(node.kind.right);
|
||||||
|
|||||||
@ -136,7 +136,7 @@ export class Parser {
|
|||||||
parseBinary(prec = 7): ast.Node {
|
parseBinary(prec = 7): ast.Node {
|
||||||
const loc = this.loc();
|
const loc = this.loc();
|
||||||
if (prec == 0) {
|
if (prec == 0) {
|
||||||
return this.parsePrefixE();
|
return this.parsePrefix();
|
||||||
}
|
}
|
||||||
const ops: [Tok["type"], ast.BinaryOp, number][] = [
|
const ops: [Tok["type"], ast.BinaryOp, number][] = [
|
||||||
["or", "Or", 9],
|
["or", "Or", 9],
|
||||||
@ -180,7 +180,18 @@ export class Parser {
|
|||||||
return left;
|
return left;
|
||||||
}
|
}
|
||||||
|
|
||||||
parsePrefixE() {
|
parsePrefix(): ast.Node {
|
||||||
|
const loc = this.loc();
|
||||||
|
const ops: [Tok["type"], ast.UnaryOp][] = [
|
||||||
|
["not", "Not"],
|
||||||
|
["-", "Negate"],
|
||||||
|
];
|
||||||
|
for (const [tok, op] of ops) {
|
||||||
|
if (this.eat(tok)) {
|
||||||
|
const expr = this.parsePrefix();
|
||||||
|
return ast.Node.create(loc, "UnaryExpr", { op, expr, tok });
|
||||||
|
}
|
||||||
|
}
|
||||||
return this.parsePostfix();
|
return this.parsePostfix();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +301,7 @@ export class Parser {
|
|||||||
export type Tok = { type: string; value: string; line: number };
|
export type Tok = { type: string; value: string; line: number };
|
||||||
|
|
||||||
const keywordPattern =
|
const keywordPattern =
|
||||||
/^(?:fn)|(?:return)|(?:let)|(?:if)|(?:else)|(?:while)|(?:break)|(?:or)|(?:and)$/;
|
/^(?:fn)|(?:return)|(?:let)|(?:if)|(?:else)|(?:while)|(?:break)|(?:or)|(?:and)|(?:not)$/;
|
||||||
const operatorPattern =
|
const operatorPattern =
|
||||||
/((?:\->)|(?:==)|(?:!=)|(?:<=)|(?:>=)|(?:<<)|(?:>>)|[\n\(\)\{\}\,\.\;\:\!\=\<\>\&\^\|\+\-\*\/\%])/g;
|
/((?:\->)|(?:==)|(?:!=)|(?:<=)|(?:>=)|(?:<<)|(?:>>)|[\n\(\)\{\}\,\.\;\:\!\=\<\>\&\^\|\+\-\*\/\%])/g;
|
||||||
|
|
||||||
|
|||||||
@ -180,6 +180,29 @@ class FnLowerer {
|
|||||||
const callee = this.lowerExpr(expr.kind.expr);
|
const callee = this.lowerExpr(expr.kind.expr);
|
||||||
return this.pushInst(ty, "Call", { callee, args });
|
return this.pushInst(ty, "Call", { callee, args });
|
||||||
}
|
}
|
||||||
|
if (expr.is("UnaryExpr")) {
|
||||||
|
const resultTy = this.checker.check(expr);
|
||||||
|
const operandTy = this.checker.check(expr.kind.expr);
|
||||||
|
if (
|
||||||
|
expr.kind.op === "Negate" &&
|
||||||
|
operandTy.compatibleWith(Ty.Int) &&
|
||||||
|
resultTy.compatibleWith(Ty.Int)
|
||||||
|
) {
|
||||||
|
const operand = this.lowerExpr(expr.kind.expr);
|
||||||
|
return this.pushInst(Ty.Int, "Negate", { source: operand });
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
expr.kind.op === "Not" &&
|
||||||
|
operandTy.compatibleWith(Ty.Bool) &&
|
||||||
|
resultTy.compatibleWith(Ty.Bool)
|
||||||
|
) {
|
||||||
|
const operand = this.lowerExpr(expr.kind.expr);
|
||||||
|
return this.pushInst(Ty.Bool, "Not", { source: operand });
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`'${expr.kind.op}' with '${resultTy.pretty()}' not handled`,
|
||||||
|
);
|
||||||
|
}
|
||||||
if (expr.is("BinaryExpr")) {
|
if (expr.is("BinaryExpr")) {
|
||||||
const resultTy = this.checker.check(expr);
|
const resultTy = this.checker.check(expr);
|
||||||
const leftTy = this.checker.check(expr.kind.left);
|
const leftTy = this.checker.check(expr.kind.left);
|
||||||
@ -349,6 +372,9 @@ export class Inst {
|
|||||||
}`;
|
}`;
|
||||||
case "Return":
|
case "Return":
|
||||||
return ` ${r(k.source)}`;
|
return ` ${r(k.source)}`;
|
||||||
|
case "Not":
|
||||||
|
case "Negate":
|
||||||
|
return ` ${r(k.source)}`;
|
||||||
case "Eq":
|
case "Eq":
|
||||||
case "Ne":
|
case "Ne":
|
||||||
case "Lt":
|
case "Lt":
|
||||||
@ -389,6 +415,8 @@ export type InstKind =
|
|||||||
| { tag: "Jump"; target: BasicBlock }
|
| { tag: "Jump"; target: BasicBlock }
|
||||||
| { tag: "Branch"; cond: Inst; truthy: BasicBlock; falsy: BasicBlock }
|
| { tag: "Branch"; cond: Inst; truthy: BasicBlock; falsy: BasicBlock }
|
||||||
| { tag: "Return"; source: Inst }
|
| { tag: "Return"; source: Inst }
|
||||||
|
| { tag: "Not"; source: Inst }
|
||||||
|
| { tag: "Negate"; source: Inst }
|
||||||
| { tag: BinaryOp; left: Inst; right: Inst }
|
| { tag: BinaryOp; left: Inst; right: Inst }
|
||||||
| { tag: "DebugPrint"; args: Inst[] };
|
| { tag: "DebugPrint"; args: Inst[] };
|
||||||
|
|
||||||
|
|||||||
@ -82,6 +82,28 @@ export class FnInterpreter {
|
|||||||
}
|
}
|
||||||
case "Return":
|
case "Return":
|
||||||
return this.regs.get(k.source)!;
|
return this.regs.get(k.source)!;
|
||||||
|
case "Not": {
|
||||||
|
const source = this.regs.get(k.source)!;
|
||||||
|
if (source.kind.tag !== "Bool") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
this.regs.set(
|
||||||
|
inst,
|
||||||
|
new Val({ tag: "Bool", value: !source.kind.value }),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "Negate": {
|
||||||
|
const source = this.regs.get(k.source)!;
|
||||||
|
if (source.kind.tag !== "Int") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
this.regs.set(
|
||||||
|
inst,
|
||||||
|
new Val({ tag: "Int", value: -source.kind.value }),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "Eq":
|
case "Eq":
|
||||||
case "Ne":
|
case "Ne":
|
||||||
case "Lt":
|
case "Lt":
|
||||||
|
|||||||
@ -42,6 +42,11 @@ export class Ty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
compatibleWith(other: Ty): boolean {
|
compatibleWith(other: Ty): boolean {
|
||||||
|
// types are interned--we can just do this
|
||||||
|
if (this === other) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.is("Error")) {
|
if (this.is("Error")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
// expect: 15
|
// expect: 15
|
||||||
// expect: 7
|
// expect: 7
|
||||||
// expect: 2
|
// expect: 2
|
||||||
|
// expect: -5
|
||||||
|
// expect: 123
|
||||||
|
|
||||||
fn main()
|
fn main()
|
||||||
{
|
{
|
||||||
@ -14,5 +16,12 @@ fn main()
|
|||||||
print_int(a * b);
|
print_int(a * b);
|
||||||
print_int(a * b / 2);
|
print_int(a * b / 2);
|
||||||
print_int(a % b);
|
print_int(a % b);
|
||||||
|
print_int(-a);
|
||||||
|
|
||||||
|
let c = false;
|
||||||
|
|
||||||
|
if not c {
|
||||||
|
print_int(123);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user