ast lowering :sob

This commit is contained in:
SimonFJ20 2025-02-04 15:06:19 +01:00
parent b70ba48e0a
commit abb4298437
13 changed files with 336 additions and 35 deletions

View File

@ -264,5 +264,6 @@ export type PathSegment = {
export type Ident = {
id: IdentId;
text: string;
span: Span;
};

View File

@ -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();

View File

@ -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() {

View 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]];
}

View File

@ -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 };

View File

@ -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_]/,

View File

@ -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() {

View File

@ -6,6 +6,7 @@ export type Token = {
span: Span;
length: number;
identId?: IdentId;
identText?: string;
intValue?: number;
stringValue?: string;
};

View File

@ -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,

View File

@ -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}'`,
// });
}

View File

@ -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

View File

@ -10,6 +10,7 @@ export type TyKind =
| { tag: "error" }
| { tag: "unknown" }
| { tag: "null" }
| { tag: "int" }
| {
tag: "fn";
item: ast.Item;

View File

@ -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();
}