ast lowering :sob
This commit is contained in:
parent
b70ba48e0a
commit
abb4298437
@ -264,5 +264,6 @@ export type PathSegment = {
|
||||
|
||||
export type Ident = {
|
||||
id: IdentId;
|
||||
text: string;
|
||||
span: Span;
|
||||
};
|
||||
|
@ -141,7 +141,24 @@ export class Checker {
|
||||
}
|
||||
|
||||
private checkTy(ty: ast.Ty): Ty {
|
||||
return todo();
|
||||
const k = ty.kind;
|
||||
switch (k.tag) {
|
||||
case "error":
|
||||
return Ty({ tag: "error" });
|
||||
case "null":
|
||||
case "int":
|
||||
return Ty({ tag: "int" });
|
||||
case "bool":
|
||||
case "str":
|
||||
case "path":
|
||||
case "ref":
|
||||
case "ptr":
|
||||
case "slice":
|
||||
case "array":
|
||||
case "anon_struct":
|
||||
return todo(k);
|
||||
}
|
||||
exhausted(k);
|
||||
}
|
||||
|
||||
private report(msg: string, span: Span) {
|
||||
@ -171,6 +188,12 @@ export class Checker {
|
||||
}
|
||||
return Res.Ok(a);
|
||||
}
|
||||
case "int": {
|
||||
if (b.kind.tag !== "int") {
|
||||
return incompat();
|
||||
}
|
||||
return Res.Ok(a);
|
||||
}
|
||||
case "fn": {
|
||||
if (b.kind.tag !== "fn") {
|
||||
return incompat();
|
||||
|
@ -4,6 +4,7 @@ import * as ast from "./ast/mod.ts";
|
||||
import { Ctx, File } from "./ctx.ts";
|
||||
import { Resolver } from "./resolve/resolver.ts";
|
||||
import { Checker } from "./check/checker.ts";
|
||||
import { AstLowerer } from "./middle/ast_lower.ts";
|
||||
|
||||
async function main() {
|
||||
const filePath = Deno.args[0];
|
||||
@ -42,6 +43,7 @@ export class PackCompiler {
|
||||
.collect();
|
||||
const resols = new Resolver(this.ctx, entryFileAst).resolve();
|
||||
const checker = new Checker(this.ctx, entryFileAst, resols);
|
||||
new AstLowerer(this.ctx, resols, checker, entryFileAst).lower();
|
||||
}
|
||||
|
||||
public enableDebug() {
|
||||
|
245
compiler/middle/ast_lower.ts
Normal file
245
compiler/middle/ast_lower.ts
Normal file
@ -0,0 +1,245 @@
|
||||
import * as ast from "../ast/mod.ts";
|
||||
import { Checker } from "../check/checker.ts";
|
||||
import { Ctx } from "../ctx.ts";
|
||||
import { IdMap, Ids } from "../ids.ts";
|
||||
import { LocalId as ReLocalId, Resols } from "../resolve/resolver.ts";
|
||||
import { Ty } from "../ty/ty.ts";
|
||||
import { exhausted, Res, todo } from "../util.ts";
|
||||
import { BinaryType, Operand, StmtKind, TerKind } from "./mir.ts";
|
||||
import { Block, BlockId, Fn, Local, LocalId, RVal, Stmt, Ter } from "./mir.ts";
|
||||
|
||||
export class AstLowerer implements ast.Visitor {
|
||||
public constructor(
|
||||
private ctx: Ctx,
|
||||
private re: Resols,
|
||||
private ch: Checker,
|
||||
private ast: ast.File,
|
||||
) {}
|
||||
|
||||
public lower() {
|
||||
ast.visitFile(this, this.ast);
|
||||
}
|
||||
|
||||
visitFnItem(item: ast.Item, kind: ast.FnItem): ast.VisitRes {
|
||||
new FnLowerer(this.ctx, this.re, this.ch, item, kind).lower();
|
||||
}
|
||||
}
|
||||
|
||||
export class FnLowerer {
|
||||
private localIds = new Ids<LocalId>();
|
||||
private locals = new IdMap<LocalId, Local>();
|
||||
|
||||
private blockIds = new Ids<BlockId>();
|
||||
private blocks = new IdMap<BlockId, Block>();
|
||||
|
||||
private currentBlock?: Block;
|
||||
|
||||
private reLocals = new IdMap<ReLocalId, LocalId>();
|
||||
|
||||
public constructor(
|
||||
private ctx: Ctx,
|
||||
private re: Resols,
|
||||
private ch: Checker,
|
||||
private item: ast.Item,
|
||||
private kind: ast.FnItem,
|
||||
) {}
|
||||
|
||||
public lower(): Res<Fn, string> {
|
||||
const entry = this.pushBlock();
|
||||
|
||||
const fnTy = this.ch.fnItemTy(this.item, this.kind);
|
||||
const returnPlace = this.local(fnTy);
|
||||
const returnVal = this.lowerBlock(this.kind.body!);
|
||||
|
||||
this.addStmt({
|
||||
tag: "assign",
|
||||
place: { local: returnPlace, proj: [] },
|
||||
rval: returnVal,
|
||||
});
|
||||
|
||||
this.setTer({ tag: "return" });
|
||||
|
||||
return Res.Ok({
|
||||
label: this.ctx.identText(this.item.ident.id),
|
||||
locals: this.locals,
|
||||
blocks: this.blocks,
|
||||
entry,
|
||||
});
|
||||
}
|
||||
|
||||
private lowerBlock(block: ast.Block): RVal {
|
||||
for (const stmt of block.stmts) {
|
||||
this.lowerStmt(stmt);
|
||||
}
|
||||
return block.expr && this.lowerExpr(block.expr) || {
|
||||
tag: "use",
|
||||
operand: { tag: "const", val: { tag: "null" } },
|
||||
};
|
||||
}
|
||||
|
||||
private lowerStmt(stmt: ast.Stmt) {
|
||||
const k = stmt.kind;
|
||||
switch (k.tag) {
|
||||
case "error":
|
||||
return { tag: "error" };
|
||||
case "item":
|
||||
case "let":
|
||||
case "return":
|
||||
case "break":
|
||||
case "continue":
|
||||
case "assign":
|
||||
case "expr":
|
||||
return todo();
|
||||
}
|
||||
exhausted(k);
|
||||
}
|
||||
|
||||
private lowerExpr(expr: ast.Expr): RVal {
|
||||
const k = expr.kind;
|
||||
switch (k.tag) {
|
||||
case "error":
|
||||
return { tag: "error" };
|
||||
case "path":
|
||||
return this.lowerPathExpr(expr, k);
|
||||
case "null":
|
||||
case "int":
|
||||
case "bool":
|
||||
case "str":
|
||||
case "group":
|
||||
case "array":
|
||||
case "repeat":
|
||||
case "struct":
|
||||
case "ref":
|
||||
case "deref":
|
||||
case "elem":
|
||||
case "field":
|
||||
case "index":
|
||||
case "call":
|
||||
case "unary":
|
||||
return todo(k.tag);
|
||||
case "binary":
|
||||
return this.lowerBinaryExpr(expr, k);
|
||||
case "block":
|
||||
case "if":
|
||||
case "loop":
|
||||
case "while":
|
||||
case "for":
|
||||
case "c_for":
|
||||
return todo(k.tag);
|
||||
}
|
||||
exhausted(k);
|
||||
}
|
||||
|
||||
private lowerPathExpr(expr: ast.Expr, kind: ast.PathExpr): RVal {
|
||||
const re = this.re.exprRes(expr.id);
|
||||
switch (re.kind.tag) {
|
||||
case "error":
|
||||
return { tag: "error" };
|
||||
case "fn":
|
||||
return todo();
|
||||
case "local": {
|
||||
const ty = this.ch.exprTy(expr);
|
||||
const local = this.local(ty);
|
||||
this.reLocals.set(re.kind.id, local);
|
||||
const isCopyable = (() => {
|
||||
switch (ty.kind.tag) {
|
||||
case "error":
|
||||
case "unknown":
|
||||
return false;
|
||||
case "null":
|
||||
case "int":
|
||||
return true;
|
||||
case "fn":
|
||||
return false;
|
||||
}
|
||||
exhausted(ty.kind);
|
||||
})();
|
||||
return {
|
||||
tag: "use",
|
||||
operand: {
|
||||
tag: isCopyable ? "copy" : "move",
|
||||
place: { local, proj: [] },
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
exhausted(re.kind);
|
||||
}
|
||||
|
||||
private lowerBinaryExpr(expr: ast.Expr, kind: ast.BinaryExpr): RVal {
|
||||
const left = this.rvalAsOperand(
|
||||
this.lowerExpr(kind.left),
|
||||
this.ch.exprTy(kind.left),
|
||||
);
|
||||
const right = this.rvalAsOperand(
|
||||
this.lowerExpr(kind.right),
|
||||
this.ch.exprTy(kind.right),
|
||||
);
|
||||
const binaryType = ((kind): BinaryType => {
|
||||
switch (kind.binaryType) {
|
||||
case "+":
|
||||
return "add";
|
||||
case "-":
|
||||
return "sub";
|
||||
case "*":
|
||||
return "mul";
|
||||
case "/":
|
||||
return "div";
|
||||
case "==":
|
||||
return "eq";
|
||||
case "!=":
|
||||
return "ne";
|
||||
case "<":
|
||||
return "lt";
|
||||
case ">":
|
||||
return "lte";
|
||||
case "<=":
|
||||
return "lte";
|
||||
case ">=":
|
||||
return "gte";
|
||||
case "or":
|
||||
return "or";
|
||||
case "and":
|
||||
return "and";
|
||||
}
|
||||
return todo(kind.binaryType);
|
||||
})(kind);
|
||||
return { tag: "binary", binaryType, left, right };
|
||||
}
|
||||
|
||||
private rvalAsOperand(rval: RVal, ty: Ty): Operand {
|
||||
const local = this.local(ty);
|
||||
this.addStmt({ tag: "assign", place: { local, proj: [] }, rval });
|
||||
return { tag: "move", place: { local, proj: [] } };
|
||||
}
|
||||
|
||||
private local(ty: Ty): LocalId {
|
||||
const id = this.localIds.nextThenStep();
|
||||
this.locals.set(id, { id, ty });
|
||||
return id;
|
||||
}
|
||||
|
||||
private pushBlock(): BlockId {
|
||||
const id = this.blockIds.nextThenStep();
|
||||
const block: Block = {
|
||||
id,
|
||||
stmts: [],
|
||||
terminator: { kind: { tag: "unset" } },
|
||||
};
|
||||
this.blocks.set(id, block);
|
||||
this.currentBlock = block;
|
||||
return id;
|
||||
}
|
||||
|
||||
private setTer(kind: TerKind) {
|
||||
this.currentBlock!.terminator = { kind };
|
||||
}
|
||||
|
||||
private addStmt(kind: StmtKind) {
|
||||
this.currentBlock!.stmts.push({ kind });
|
||||
}
|
||||
}
|
||||
|
||||
function unpack(blocks: Block[], val: [Block[], RVal]): [Block[], RVal] {
|
||||
return [[...blocks, ...val[0]], val[1]];
|
||||
}
|
@ -1,10 +1,27 @@
|
||||
import { Span } from "../diagnostics.ts";
|
||||
import { IdBase } from "../ids.ts";
|
||||
import { IdBase, IdMap } from "../ids.ts";
|
||||
import { Ty } from "../ty/ty.ts";
|
||||
|
||||
export type Fn = {
|
||||
label: string;
|
||||
locals: IdMap<LocalId, Local>;
|
||||
blocks: IdMap<BlockId, Block>;
|
||||
entry: BlockId;
|
||||
};
|
||||
|
||||
export type LocalId = IdBase & { readonly _: unique symbol };
|
||||
|
||||
export type Local = {
|
||||
id: LocalId;
|
||||
ty: Ty;
|
||||
};
|
||||
|
||||
export type BlockId = IdBase & { readonly _: unique symbol };
|
||||
|
||||
export type Block = {
|
||||
id: BlockId;
|
||||
stmts: Stmt[];
|
||||
terminator?: Terminator;
|
||||
terminator: Ter;
|
||||
};
|
||||
|
||||
export type Stmt = {
|
||||
@ -20,11 +37,12 @@ export type StmtKind =
|
||||
| { tag: "dead"; local: LocalId }
|
||||
| { tag: "mention"; place: Place };
|
||||
|
||||
export type Terminator = {
|
||||
kind: TerminatorKind;
|
||||
export type Ter = {
|
||||
kind: TerKind;
|
||||
};
|
||||
|
||||
export type TerminatorKind =
|
||||
export type TerKind =
|
||||
| { tag: "unset" }
|
||||
| { tag: "goto"; target: BlockId }
|
||||
| {
|
||||
tag: "switch";
|
||||
@ -57,6 +75,7 @@ export type ProjElem =
|
||||
|
||||
// https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/enum.Rvalue.html
|
||||
export type RVal =
|
||||
| { tag: "error" }
|
||||
| { tag: "use"; operand: Operand }
|
||||
| { tag: "repeat"; operand: Operand; length: Const }
|
||||
| { tag: "ref"; place: Place; mut: boolean }
|
||||
@ -94,6 +113,3 @@ export type Const =
|
||||
| { tag: "int"; value: number }
|
||||
| { tag: "bool"; value: boolean }
|
||||
| { tag: "string"; value: string };
|
||||
|
||||
export type LocalId = IdBase & { readonly _: unique symbol };
|
||||
export type BlockId = IdBase & { readonly _: unique symbol };
|
||||
|
@ -38,6 +38,7 @@ export class Lexer implements TokenIter {
|
||||
: this.token("ident", span, {
|
||||
type: "ident",
|
||||
identId: this.ctx.internIdent(val),
|
||||
identText: val,
|
||||
});
|
||||
},
|
||||
/[a-zA-Z_]/,
|
||||
|
@ -1207,7 +1207,7 @@ export class Parser {
|
||||
private parseIdent(): Ident {
|
||||
const tok = this.current();
|
||||
this.step();
|
||||
return { id: tok.identId!, span: tok.span };
|
||||
return { id: tok.identId!, text: tok.identText!, span: tok.span };
|
||||
}
|
||||
|
||||
private step() {
|
||||
|
@ -6,6 +6,7 @@ export type Token = {
|
||||
span: Span;
|
||||
length: number;
|
||||
identId?: IdentId;
|
||||
identText?: string;
|
||||
intValue?: number;
|
||||
stringValue?: string;
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as ast from "../ast/mod.ts";
|
||||
import { IdentId, IdMap } from "../ids.ts";
|
||||
import { IdBase, IdentId, IdMap } from "../ids.ts";
|
||||
import { Res } from "../util.ts";
|
||||
|
||||
export interface Syms {
|
||||
@ -15,10 +15,12 @@ export type Resolve = {
|
||||
kind: ResolveKind;
|
||||
};
|
||||
|
||||
export type LocalId = IdBase & { readonly _: unique symbol };
|
||||
|
||||
export type ResolveKind =
|
||||
| { tag: "error" }
|
||||
| { tag: "fn"; item: ast.Item; kind: ast.FnItem }
|
||||
| { tag: "local" };
|
||||
| { tag: "local"; id: LocalId };
|
||||
|
||||
export const ResolveError = (ident: ast.Ident): Resolve => ({
|
||||
ident,
|
||||
|
@ -1,15 +1,17 @@
|
||||
import * as ast from "../ast/mod.ts";
|
||||
import { Ctx, File } from "../ctx.ts";
|
||||
import { AstId, IdMap } from "../ids.ts";
|
||||
import { AstId, IdMap, Ids } from "../ids.ts";
|
||||
import { exhausted, todo } from "../util.ts";
|
||||
import {
|
||||
FnSyms,
|
||||
LocalId,
|
||||
LocalSyms,
|
||||
Resolve,
|
||||
ResolveError,
|
||||
RootSyms,
|
||||
Syms,
|
||||
} from "./cx.ts";
|
||||
export { type LocalId } from "./cx.ts";
|
||||
|
||||
export class Resols {
|
||||
public constructor(
|
||||
@ -31,6 +33,8 @@ export class Resolver implements ast.Visitor {
|
||||
|
||||
private exprResols = new IdMap<AstId, Resolve>();
|
||||
|
||||
private localIds = new Ids<LocalId>();
|
||||
|
||||
public constructor(
|
||||
private ctx: Ctx,
|
||||
private entryFileAst: ast.File,
|
||||
@ -47,6 +51,7 @@ export class Resolver implements ast.Visitor {
|
||||
this.currentFile = file.file;
|
||||
ast.visitStmts(this, file.stmts);
|
||||
this.visitFnBodies();
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitLetStmt(stmt: ast.Stmt, kind: ast.LetStmt): ast.VisitRes {
|
||||
@ -54,6 +59,7 @@ export class Resolver implements ast.Visitor {
|
||||
kind.expr && ast.visitExpr(this, kind.expr);
|
||||
this.syms = new LocalSyms(this.syms);
|
||||
ast.visitPat(this, kind.pat);
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitModBlockItem(item: ast.Item, kind: ast.ModBlockItem): ast.VisitRes {
|
||||
@ -133,7 +139,10 @@ export class Resolver implements ast.Visitor {
|
||||
}
|
||||
|
||||
visitBindPat(pat: ast.Pat, kind: ast.BindPat): ast.VisitRes {
|
||||
const res = this.syms.defVal(kind.ident, { tag: "local" });
|
||||
const res = this.syms.defVal(kind.ident, {
|
||||
tag: "local",
|
||||
id: this.localIds.nextThenStep(),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const text = this.ctx.identText(kind.ident.id);
|
||||
this.ctx.report({
|
||||
@ -143,6 +152,7 @@ export class Resolver implements ast.Visitor {
|
||||
msg: `redefinition of value '${text}'`,
|
||||
});
|
||||
}
|
||||
return "stop";
|
||||
}
|
||||
|
||||
visitPathPat(pat: ast.Pat, kind: ast.PathPat): ast.VisitRes {
|
||||
@ -153,6 +163,7 @@ export class Resolver implements ast.Visitor {
|
||||
ast.visitStmts(this, block.stmts);
|
||||
this.visitFnBodies();
|
||||
block.expr && ast.visitExpr(this, block.expr);
|
||||
return "stop";
|
||||
}
|
||||
|
||||
private resolveInnerPath(path: ast.Path): Resolve {
|
||||
@ -183,20 +194,4 @@ export class Resolver implements ast.Visitor {
|
||||
}, this.syms.getTy(path.segments[0].ident));
|
||||
return res;
|
||||
}
|
||||
|
||||
// const text = this.ctx.identText(ident.id);
|
||||
// this.ctx.report({
|
||||
// severity: "error",
|
||||
// file: this.currentFile,
|
||||
// span: ident.span,
|
||||
// msg: `redefinition of type '${text}'`,
|
||||
// });
|
||||
//
|
||||
// const text = this.ctx.identText(ident.id);
|
||||
// this.ctx.report({
|
||||
// severity: "error",
|
||||
// file: this.currentFile,
|
||||
// span: ident.span,
|
||||
// msg: `redefinition of value '${text}'`,
|
||||
// });
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ export function tyToString(ctx: Ctx, ty: Ty): string {
|
||||
return `<unknown>`;
|
||||
case "null":
|
||||
return `null`;
|
||||
case "int":
|
||||
return `int`;
|
||||
case "fn": {
|
||||
const identText = ctx.identText(k.item.ident.id);
|
||||
const params = k.params
|
||||
|
@ -10,6 +10,7 @@ export type TyKind =
|
||||
| { tag: "error" }
|
||||
| { tag: "unknown" }
|
||||
| { tag: "null" }
|
||||
| { tag: "int" }
|
||||
| {
|
||||
tag: "fn";
|
||||
item: ast.Item;
|
||||
|
@ -1,10 +1,22 @@
|
||||
export function todo<T>(msg?: string): T {
|
||||
class NotImplemented extends Error {}
|
||||
throw new NotImplemented(msg);
|
||||
export function todo<T>(...args: unknown[]): T {
|
||||
const argsStr = args.map((a) => JSON.stringify(a)).join(", ");
|
||||
class NotImplemented extends Error {
|
||||
constructor() {
|
||||
super(`todo(${argsStr})`);
|
||||
this.name = "NotImplemented";
|
||||
}
|
||||
}
|
||||
throw new NotImplemented();
|
||||
}
|
||||
|
||||
export function exhausted<T>(_: never): T {
|
||||
class Unexhausted extends Error {}
|
||||
export function exhausted<T>(...args: never[]): T {
|
||||
const argsStr = args.map((a) => JSON.stringify(a)).join(", ");
|
||||
class Unexhausted extends Error {
|
||||
constructor() {
|
||||
super(`exhausted(${argsStr})`);
|
||||
this.name = "Unexhausted";
|
||||
}
|
||||
}
|
||||
throw new Unexhausted();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user