This commit is contained in:
parent
27b4e89694
commit
cf36151115
11
src/ast.ts
11
src/ast.ts
@ -88,6 +88,9 @@ export class Node {
|
|||||||
return visit(k.left, k.right);
|
return visit(k.left, k.right);
|
||||||
case "IdentTy":
|
case "IdentTy":
|
||||||
return visit();
|
return visit();
|
||||||
|
case "PtrTy":
|
||||||
|
case "PtrMutTy":
|
||||||
|
return visit(k.ty);
|
||||||
}
|
}
|
||||||
k satisfies never;
|
k satisfies never;
|
||||||
}
|
}
|
||||||
@ -115,11 +118,15 @@ export type NodeKind =
|
|||||||
| { tag: "CallExpr"; expr: Node; args: Node[] }
|
| { tag: "CallExpr"; expr: Node; args: Node[] }
|
||||||
| { tag: "UnaryExpr"; op: UnaryOp; expr: Node; tok: string }
|
| { 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 }
|
||||||
|
| { tag: "PtrTy" | "PtrMutTy"; ty: Node };
|
||||||
|
|
||||||
export type UnaryOp =
|
export type UnaryOp =
|
||||||
| "Not"
|
| "Not"
|
||||||
| "Negate";
|
| "Negate"
|
||||||
|
| "Ref"
|
||||||
|
| "RefMut"
|
||||||
|
| "Deref";
|
||||||
|
|
||||||
export type BinaryOp =
|
export type BinaryOp =
|
||||||
| "Or"
|
| "Or"
|
||||||
|
|||||||
@ -85,16 +85,27 @@ export class Checker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (node.is("UnaryExpr")) {
|
if (node.is("UnaryExpr")) {
|
||||||
const expr = this.check(node.kind.expr);
|
const exprTy = this.check(node.kind.expr);
|
||||||
if (node.kind.op === "Negate" && expr.compatibleWith(Ty.Int)) {
|
if (node.kind.op === "Negate" && exprTy.compatibleWith(Ty.Int)) {
|
||||||
return Ty.Int;
|
return Ty.Int;
|
||||||
}
|
}
|
||||||
if (node.kind.op === "Not" && expr.compatibleWith(Ty.Bool)) {
|
if (node.kind.op === "Not" && exprTy.compatibleWith(Ty.Bool)) {
|
||||||
return Ty.Bool;
|
return Ty.Bool;
|
||||||
}
|
}
|
||||||
|
if (node.kind.op === "Ref") {
|
||||||
|
return Ty.create("Ptr", { ty: exprTy });
|
||||||
|
}
|
||||||
|
if (node.kind.op === "RefMut") {
|
||||||
|
return Ty.create("PtrMut", { ty: exprTy });
|
||||||
|
}
|
||||||
|
if (node.kind.op === "Deref") {
|
||||||
|
if (exprTy.is("Ptr") || exprTy.is("PtrMut")) {
|
||||||
|
return exprTy.kind.ty;
|
||||||
|
}
|
||||||
|
}
|
||||||
this.error(
|
this.error(
|
||||||
node.line,
|
node.line,
|
||||||
`operator '${node.kind.tok}' cannot be applied to type '${expr.pretty()}'`,
|
`operator '${node.kind.tok}' cannot be applied to type '${exprTy.pretty()}'`,
|
||||||
);
|
);
|
||||||
this.fail();
|
this.fail();
|
||||||
}
|
}
|
||||||
@ -131,6 +142,15 @@ export class Checker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (node.is("PtrTy")) {
|
||||||
|
const ty = this.check(node.kind.ty);
|
||||||
|
return Ty.create("Ptr", { ty });
|
||||||
|
}
|
||||||
|
if (node.is("PtrMutTy")) {
|
||||||
|
const ty = this.check(node.kind.ty);
|
||||||
|
return Ty.create("PtrMut", { ty });
|
||||||
|
}
|
||||||
|
|
||||||
throw new Error(`'${k.tag}' not unhandled`);
|
throw new Error(`'${k.tag}' not unhandled`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -185,6 +185,7 @@ export class Parser {
|
|||||||
const ops: [Tok["type"], ast.UnaryOp][] = [
|
const ops: [Tok["type"], ast.UnaryOp][] = [
|
||||||
["not", "Not"],
|
["not", "Not"],
|
||||||
["-", "Negate"],
|
["-", "Negate"],
|
||||||
|
["*", "Deref"],
|
||||||
];
|
];
|
||||||
for (const [tok, op] of ops) {
|
for (const [tok, op] of ops) {
|
||||||
if (this.eat(tok)) {
|
if (this.eat(tok)) {
|
||||||
@ -192,6 +193,12 @@ export class Parser {
|
|||||||
return ast.Node.create(loc, "UnaryExpr", { op, expr, tok });
|
return ast.Node.create(loc, "UnaryExpr", { op, expr, tok });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (this.eat("&")) {
|
||||||
|
const op: ast.UnaryOp = this.eat("mut") ? "RefMut" : "Ref";
|
||||||
|
const expr = this.parsePrefix();
|
||||||
|
const tok = op === "Ref" ? "&" : "&mut";
|
||||||
|
return ast.Node.create(loc, "UnaryExpr", { op, expr, tok });
|
||||||
|
}
|
||||||
return this.parsePostfix();
|
return this.parsePostfix();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,6 +252,10 @@ export class Parser {
|
|||||||
const ident = this.current.value;
|
const ident = this.current.value;
|
||||||
this.step();
|
this.step();
|
||||||
return ast.Node.create(loc, "IdentTy", { ident });
|
return ast.Node.create(loc, "IdentTy", { ident });
|
||||||
|
} else if (this.eat("*")) {
|
||||||
|
const mutable = this.eat("mut");
|
||||||
|
const ty = this.parseTy();
|
||||||
|
return ast.Node.create(loc, mutable ? "PtrMutTy" : "PtrTy", { ty });
|
||||||
} else {
|
} else {
|
||||||
this.mustEat("<type>");
|
this.mustEat("<type>");
|
||||||
throw new Error();
|
throw new Error();
|
||||||
@ -305,7 +316,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)|(?:not)$/;
|
/^(?:fn)|(?:return)|(?:let)|(?:if)|(?:else)|(?:while)|(?:break)|(?:or)|(?:and)|(?:not)|(?:mut)$/;
|
||||||
const operatorPattern =
|
const operatorPattern =
|
||||||
/((?:\->)|(?:==)|(?:!=)|(?:<=)|(?:>=)|(?:<<)|(?:>>)|[\n\(\)\{\}\,\.\;\:\!\=\<\>\&\^\|\+\-\*\/\%])/g;
|
/((?:\->)|(?:==)|(?:!=)|(?:<=)|(?:>=)|(?:<<)|(?:>>)|[\n\(\)\{\}\,\.\;\:\!\=\<\>\&\^\|\+\-\*\/\%])/g;
|
||||||
|
|
||||||
|
|||||||
@ -21,6 +21,7 @@ export class MiddleLowerer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FnLowerer {
|
class FnLowerer {
|
||||||
|
private allocs: Inst[] = [];
|
||||||
private bbs: BasicBlock[] = [new BasicBlock()];
|
private bbs: BasicBlock[] = [new BasicBlock()];
|
||||||
private localMap = new Map<number, Inst>();
|
private localMap = new Map<number, Inst>();
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ class FnLowerer {
|
|||||||
const ty = this.checker.check(this.stmt);
|
const ty = this.checker.check(this.stmt);
|
||||||
this.lowerBlock(this.stmt.kind.body.as("Block"));
|
this.lowerBlock(this.stmt.kind.body.as("Block"));
|
||||||
this.pushInst(Ty.Void, "Return", { source: this.makeVoid() });
|
this.pushInst(Ty.Void, "Return", { source: this.makeVoid() });
|
||||||
|
this.bbs[0].insts.unshift(...this.allocs);
|
||||||
return new Fn(this.stmt, ty, this.bbs);
|
return new Fn(this.stmt, ty, this.bbs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,8 +50,9 @@ class FnLowerer {
|
|||||||
if (stmt.is("LetStmt")) {
|
if (stmt.is("LetStmt")) {
|
||||||
const ty = this.checker.check(stmt.kind.param);
|
const ty = this.checker.check(stmt.kind.param);
|
||||||
const expr = this.lowerExpr(stmt.kind.expr);
|
const expr = this.lowerExpr(stmt.kind.expr);
|
||||||
const local = this.pushInst(ty, "AllocLocal", {});
|
const local = new Inst(ty, { tag: "Alloca" });
|
||||||
this.pushInst(Ty.Void, "LocalStore", {
|
this.allocs.push(local);
|
||||||
|
this.pushInst(Ty.Void, "Store", {
|
||||||
target: local,
|
target: local,
|
||||||
source: expr,
|
source: expr,
|
||||||
});
|
});
|
||||||
@ -103,8 +106,8 @@ class FnLowerer {
|
|||||||
}
|
}
|
||||||
if (stmt.is("AssignStmt")) {
|
if (stmt.is("AssignStmt")) {
|
||||||
const source = this.lowerExpr(stmt.kind.expr);
|
const source = this.lowerExpr(stmt.kind.expr);
|
||||||
const target = this.lowerAssignPlace(stmt.kind.place);
|
const target = this.lowerPlace(stmt.kind.place);
|
||||||
this.pushInst(Ty.Void, "LocalStore", { target, source });
|
this.pushInst(Ty.Void, "Store", { target, source });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (stmt.is("ExprStmt")) {
|
if (stmt.is("ExprStmt")) {
|
||||||
@ -114,7 +117,7 @@ class FnLowerer {
|
|||||||
throw new Error(`'${stmt.kind.tag}' not handled`);
|
throw new Error(`'${stmt.kind.tag}' not handled`);
|
||||||
}
|
}
|
||||||
|
|
||||||
private lowerAssignPlace(place: ast.Node): Inst {
|
private lowerPlace(place: ast.Node): Inst {
|
||||||
if (place.is("IdentExpr")) {
|
if (place.is("IdentExpr")) {
|
||||||
const sym = this.resols.get(place);
|
const sym = this.resols.get(place);
|
||||||
if (sym.tag === "Let") {
|
if (sym.tag === "Let") {
|
||||||
@ -126,6 +129,11 @@ class FnLowerer {
|
|||||||
}
|
}
|
||||||
throw new Error(`'${sym.tag}' not handled`);
|
throw new Error(`'${sym.tag}' not handled`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (place.is("UnaryExpr") && place.kind.op === "Deref") {
|
||||||
|
return this.lowerExpr(place.kind.expr);
|
||||||
|
}
|
||||||
|
|
||||||
throw new Error(`'${place.kind.tag}' not handled`);
|
throw new Error(`'${place.kind.tag}' not handled`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -148,7 +156,7 @@ class FnLowerer {
|
|||||||
if (!local) {
|
if (!local) {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
return this.pushInst(local.ty, "LocalLoad", { source: local });
|
return this.pushInst(local.ty, "Load", { source: local });
|
||||||
}
|
}
|
||||||
if (sym.tag === "Bool") {
|
if (sym.tag === "Bool") {
|
||||||
return this.pushInst(Ty.Bool, "Bool", { value: sym.value });
|
return this.pushInst(Ty.Bool, "Bool", { value: sym.value });
|
||||||
@ -199,6 +207,29 @@ class FnLowerer {
|
|||||||
const operand = this.lowerExpr(expr.kind.expr);
|
const operand = this.lowerExpr(expr.kind.expr);
|
||||||
return this.pushInst(Ty.Bool, "Not", { source: operand });
|
return this.pushInst(Ty.Bool, "Not", { source: operand });
|
||||||
}
|
}
|
||||||
|
if (expr.kind.op === "Ref" || expr.kind.op === "RefMut") {
|
||||||
|
const place = expr.kind.expr;
|
||||||
|
if (place.is("IdentExpr")) {
|
||||||
|
const sym = this.resols.get(place);
|
||||||
|
if (sym.tag === "Let") {
|
||||||
|
const local = this.localMap.get(sym.param.id);
|
||||||
|
if (!local) {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
return local;
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`${expr.kind.op} with sym ${sym.tag} not handled`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`${expr.kind.op} with place ${place.kind.tag} not handled`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (expr.kind.op === "Deref") {
|
||||||
|
const source = this.lowerExpr(expr.kind.expr);
|
||||||
|
return this.pushInst(resultTy, "Load", { source });
|
||||||
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`'${expr.kind.op}' with '${resultTy.pretty()}' not handled`,
|
`'${expr.kind.op}' with '${resultTy.pretty()}' not handled`,
|
||||||
);
|
);
|
||||||
@ -266,6 +297,12 @@ const binaryOpPatterns: BinaryOpPattern[] = [
|
|||||||
{ op: "Gte", tag: "Gte", result: Ty.Bool, left: Ty.Int },
|
{ op: "Gte", tag: "Gte", result: Ty.Bool, left: Ty.Int },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export interface Visitor {
|
||||||
|
visitFn?(fn: Fn): void;
|
||||||
|
visitBasicBlock?(bb: BasicBlock): void;
|
||||||
|
visitInst?(inst: Inst): void;
|
||||||
|
}
|
||||||
|
|
||||||
export class Fn {
|
export class Fn {
|
||||||
constructor(
|
constructor(
|
||||||
public stmt: ast.FnStmt,
|
public stmt: ast.FnStmt,
|
||||||
@ -273,6 +310,13 @@ export class Fn {
|
|||||||
public bbs: BasicBlock[],
|
public bbs: BasicBlock[],
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
visit(v: Visitor) {
|
||||||
|
v.visitFn?.(this);
|
||||||
|
for (const bb of this.bbs) {
|
||||||
|
bb.visit(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pretty(): string {
|
pretty(): string {
|
||||||
const fnTy = this.ty.is("FnStmt") && this.ty.kind.ty.is("Fn")
|
const fnTy = this.ty.is("FnStmt") && this.ty.kind.ty.is("Fn")
|
||||||
? this.ty.kind.ty
|
? this.ty.kind.ty
|
||||||
@ -320,6 +364,13 @@ class PrettyCx {
|
|||||||
export class BasicBlock {
|
export class BasicBlock {
|
||||||
public insts: Inst[] = [];
|
public insts: Inst[] = [];
|
||||||
|
|
||||||
|
visit(v: Visitor) {
|
||||||
|
v.visitBasicBlock?.(this);
|
||||||
|
for (const inst of this.insts) {
|
||||||
|
inst.visit(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pretty(cx: PrettyCx): string {
|
pretty(cx: PrettyCx): string {
|
||||||
return `bb${cx.bbId(this)}:\n${
|
return `bb${cx.bbId(this)}:\n${
|
||||||
this.insts
|
this.insts
|
||||||
@ -336,8 +387,12 @@ export class Inst {
|
|||||||
public kind: InstKind,
|
public kind: InstKind,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
visit(v: Visitor) {
|
||||||
|
v.visitInst?.(this);
|
||||||
|
}
|
||||||
|
|
||||||
pretty(cx: PrettyCx): string {
|
pretty(cx: PrettyCx): string {
|
||||||
const r = (v: Inst) => `_${cx.regId(v)}`;
|
const r = (v: Inst) => `%${cx.regId(v)}`;
|
||||||
|
|
||||||
return `${`${r(this)}:`.padEnd(4, " ")} ${
|
return `${`${r(this)}:`.padEnd(4, " ")} ${
|
||||||
this.ty.pretty().padEnd(4, " ")
|
this.ty.pretty().padEnd(4, " ")
|
||||||
@ -358,18 +413,18 @@ export class Inst {
|
|||||||
return ` ${k.idx}`;
|
return ` ${k.idx}`;
|
||||||
case "Call":
|
case "Call":
|
||||||
return ` ${r(k.callee)} (${k.args.map(r).join(", ")})`;
|
return ` ${r(k.callee)} (${k.args.map(r).join(", ")})`;
|
||||||
case "AllocLocal":
|
case "Alloca":
|
||||||
return "";
|
return "";
|
||||||
case "LocalLoad":
|
case "Load":
|
||||||
return ` ${r(k.source)}`;
|
return ` ${r(k.source)}`;
|
||||||
case "LocalStore":
|
case "Store":
|
||||||
return ` ${r(k.target)} = ${r(k.source)}`;
|
return ` ${r(k.target)} = ${r(k.source)}`;
|
||||||
case "Jump":
|
case "Jump":
|
||||||
return ` bb${cx.bbId(k.target)}`;
|
return ` bb${cx.bbId(k.target)}`;
|
||||||
case "Branch":
|
case "Branch":
|
||||||
return ` ${r(k.cond)} ? bb${cx.bbId(k.truthy)} : bb${
|
return ` if ${r(k.cond)} then bb${
|
||||||
cx.bbId(k.falsy)
|
cx.bbId(k.truthy)
|
||||||
}`;
|
} else bb${cx.bbId(k.falsy)}`;
|
||||||
case "Return":
|
case "Return":
|
||||||
return ` ${r(k.source)}`;
|
return ` ${r(k.source)}`;
|
||||||
case "Not":
|
case "Not":
|
||||||
@ -409,9 +464,9 @@ export type InstKind =
|
|||||||
| { tag: "Fn"; fn: Fn }
|
| { tag: "Fn"; fn: Fn }
|
||||||
| { tag: "Param"; idx: number }
|
| { tag: "Param"; idx: number }
|
||||||
| { tag: "Call"; callee: Inst; args: Inst[] }
|
| { tag: "Call"; callee: Inst; args: Inst[] }
|
||||||
| { tag: "AllocLocal" }
|
| { tag: "Alloca" }
|
||||||
| { tag: "LocalLoad"; source: Inst }
|
| { tag: "Load"; source: Inst }
|
||||||
| { tag: "LocalStore"; target: Inst; source: Inst }
|
| { tag: "Store"; target: Inst; source: Inst }
|
||||||
| { 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 }
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import * as mir from "./middle.ts";
|
|||||||
export class FnInterpreter {
|
export class FnInterpreter {
|
||||||
private regs = new Map<mir.Inst, Val>();
|
private regs = new Map<mir.Inst, Val>();
|
||||||
private locals: (Val | null)[] = [];
|
private locals: (Val | null)[] = [];
|
||||||
private localMap = new Map<mir.Inst, number>();
|
|
||||||
private bb: mir.BasicBlock;
|
private bb: mir.BasicBlock;
|
||||||
private instIdx = 0;
|
private instIdx = 0;
|
||||||
|
|
||||||
@ -42,30 +41,36 @@ export class FnInterpreter {
|
|||||||
this.regs.set(inst, val);
|
this.regs.set(inst, val);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "AllocLocal":
|
case "Alloca": {
|
||||||
this.localMap.set(inst, this.locals.length);
|
const localIdx = this.locals.length;
|
||||||
this.locals.push(null);
|
this.locals.push(null);
|
||||||
break;
|
|
||||||
case "LocalLoad":
|
|
||||||
if (!this.localMap.has(k.source)) {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
if (this.locals[this.localMap.get(k.source)!] === null) {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
this.regs.set(
|
this.regs.set(
|
||||||
inst,
|
inst,
|
||||||
this.locals[this.localMap.get(k.source)!]!,
|
new Val({ tag: "LocalPtr", localIdx, mutable: true }),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "LocalStore":
|
}
|
||||||
if (!this.localMap.has(k.target)) {
|
case "Load": {
|
||||||
|
const source = this.regs.get(k.source);
|
||||||
|
if (!source || source.kind.tag !== "LocalPtr") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
this.locals[this.localMap.get(k.target)!] = this.regs.get(
|
const value = this.locals[source.kind.localIdx];
|
||||||
k.source,
|
if (!value) {
|
||||||
)!;
|
throw new Error();
|
||||||
|
}
|
||||||
|
this.regs.set(inst, value);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
case "Store": {
|
||||||
|
const target = this.regs.get(k.target)!;
|
||||||
|
if (target.kind.tag !== "LocalPtr") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const source = this.regs.get(k.source)!;
|
||||||
|
this.locals[target.kind.localIdx] = source;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case "Jump": {
|
case "Jump": {
|
||||||
this.bb = k.target;
|
this.bb = k.target;
|
||||||
this.instIdx = 0;
|
this.instIdx = 0;
|
||||||
@ -207,6 +212,8 @@ class Val {
|
|||||||
case "Int":
|
case "Int":
|
||||||
case "Bool":
|
case "Bool":
|
||||||
return `${k.value}`;
|
return `${k.value}`;
|
||||||
|
case "LocalPtr":
|
||||||
|
return `<pointer>`;
|
||||||
case "Fn":
|
case "Fn":
|
||||||
return `<${k.fn.ty.pretty()}>`;
|
return `<${k.fn.ty.pretty()}>`;
|
||||||
default:
|
default:
|
||||||
@ -219,4 +226,5 @@ type ValKind =
|
|||||||
| { tag: "Void" }
|
| { tag: "Void" }
|
||||||
| { tag: "Int"; value: number }
|
| { tag: "Int"; value: number }
|
||||||
| { tag: "Bool"; value: boolean }
|
| { tag: "Bool"; value: boolean }
|
||||||
|
| { tag: "LocalPtr"; mutable: boolean; localIdx: number }
|
||||||
| { tag: "Fn"; fn: mir.Fn };
|
| { tag: "Fn"; fn: mir.Fn };
|
||||||
|
|||||||
@ -102,6 +102,12 @@ export class Ty {
|
|||||||
if (this.is("Bool")) {
|
if (this.is("Bool")) {
|
||||||
return "bool";
|
return "bool";
|
||||||
}
|
}
|
||||||
|
if (this.is("Ptr")) {
|
||||||
|
return `*${this.kind.ty.pretty()}`;
|
||||||
|
}
|
||||||
|
if (this.is("PtrMut")) {
|
||||||
|
return `*mut ${this.kind.ty.pretty()}`;
|
||||||
|
}
|
||||||
if (this.is("Fn")) {
|
if (this.is("Fn")) {
|
||||||
return `fn (${
|
return `fn (${
|
||||||
this.kind.params.map((param) => param.pretty()).join(", ")
|
this.kind.params.map((param) => param.pretty()).join(", ")
|
||||||
@ -124,5 +130,7 @@ export type TyKind =
|
|||||||
| { tag: "Void" }
|
| { tag: "Void" }
|
||||||
| { tag: "Int" }
|
| { tag: "Int" }
|
||||||
| { tag: "Bool" }
|
| { tag: "Bool" }
|
||||||
|
| { tag: "Ptr"; ty: Ty }
|
||||||
|
| { tag: "PtrMut"; ty: Ty }
|
||||||
| { tag: "Fn"; params: Ty[]; retTy: Ty }
|
| { tag: "Fn"; params: Ty[]; retTy: Ty }
|
||||||
| { tag: "FnStmt"; ty: Ty; stmt: ast.NodeWithKind<"FnStmt"> };
|
| { tag: "FnStmt"; ty: Ty; stmt: ast.NodeWithKind<"FnStmt"> };
|
||||||
|
|||||||
23
tests/pointer.ethlang
Normal file
23
tests/pointer.ethlang
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
fn main()
|
||||||
|
{
|
||||||
|
let a = 1;
|
||||||
|
let b: *int = &a;
|
||||||
|
|
||||||
|
// expect: 1
|
||||||
|
print_int(*b);
|
||||||
|
|
||||||
|
a = 2;
|
||||||
|
|
||||||
|
// expect: 2
|
||||||
|
print_int(*b);
|
||||||
|
|
||||||
|
let c: *mut int = &mut a;
|
||||||
|
*c = 3;
|
||||||
|
|
||||||
|
// expect: 3
|
||||||
|
print_int(a);
|
||||||
|
// expect: 3
|
||||||
|
print_int(*c);
|
||||||
|
}
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ run_test_file() {
|
|||||||
then
|
then
|
||||||
if grep -q '// expect:' $file
|
if grep -q '// expect:' $file
|
||||||
then
|
then
|
||||||
expected=$(grep '// expect:' $file | sed -E 's/\/\/ expect: (.*?)/\1/g')
|
expected=$(grep '// expect:' $file | sed -E 's/\s*\/\/\s+expect: (.*?)/\1/g')
|
||||||
if [[ $output != $expected ]]
|
if [[ $output != $expected ]]
|
||||||
then
|
then
|
||||||
echo "-- failed: incorrect output --"
|
echo "-- failed: incorrect output --"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user