mirror of
https://github.com/Mercantec-GHC/h4-projekt-gruppe-0-sm.git
synced 2025-04-28 08:44:06 +02:00
compiler: finish example ast lower
This commit is contained in:
parent
e8c666412c
commit
f4886e045c
@ -48,7 +48,13 @@ export class Checker {
|
|||||||
|
|
||||||
const exprTy = kind.expr && Ok(this.exprTy(kind.expr));
|
const exprTy = kind.expr && Ok(this.exprTy(kind.expr));
|
||||||
const tyTy = kind.ty && Ok(this.tyTy(kind.ty));
|
const tyTy = kind.ty && Ok(this.tyTy(kind.ty));
|
||||||
const ty = exprTy && tyTy && this.resolveTys(exprTy.val, tyTy.val);
|
const ty = exprTy !== undefined
|
||||||
|
? tyTy !== undefined
|
||||||
|
? this.resolveTys(exprTy.val, tyTy.val)
|
||||||
|
: exprTy
|
||||||
|
: exprTy;
|
||||||
|
|
||||||
|
this.stmtChecked.add(stmt.id);
|
||||||
|
|
||||||
if (ty === undefined) {
|
if (ty === undefined) {
|
||||||
this.report("type amfibious, specify type or value", stmt.span);
|
this.report("type amfibious, specify type or value", stmt.span);
|
||||||
@ -65,8 +71,6 @@ export class Checker {
|
|||||||
this.report(res.val, stmt.span);
|
this.report(res.val, stmt.span);
|
||||||
return Ty({ tag: "error" });
|
return Ty({ tag: "error" });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.stmtChecked.add(stmt.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private assignPatTy(pat: ast.Pat, ty: Ty): Res<void, string> {
|
private assignPatTy(pat: ast.Pat, ty: Ty): Res<void, string> {
|
||||||
@ -108,9 +112,9 @@ export class Checker {
|
|||||||
case "path":
|
case "path":
|
||||||
return this.checkPathExpr(expr, k, expected);
|
return this.checkPathExpr(expr, k, expected);
|
||||||
case "null":
|
case "null":
|
||||||
return todo();
|
return Ty({ tag: "null" });
|
||||||
case "int":
|
case "int":
|
||||||
return todo();
|
return Ty({ tag: "int" });
|
||||||
case "bool":
|
case "bool":
|
||||||
return todo();
|
return todo();
|
||||||
case "str":
|
case "str":
|
||||||
@ -134,7 +138,7 @@ export class Checker {
|
|||||||
case "index":
|
case "index":
|
||||||
return todo();
|
return todo();
|
||||||
case "call":
|
case "call":
|
||||||
return todo();
|
return this.checkCallExpr(expr, k, expected);
|
||||||
case "unary":
|
case "unary":
|
||||||
return todo();
|
return todo();
|
||||||
case "binary":
|
case "binary":
|
||||||
@ -188,6 +192,35 @@ export class Checker {
|
|||||||
exhausted(res.kind);
|
exhausted(res.kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private checkCallExpr(
|
||||||
|
expr: ast.Expr,
|
||||||
|
kind: ast.CallExpr,
|
||||||
|
expected: Ty,
|
||||||
|
): Ty {
|
||||||
|
const fnTy = this.exprTy(kind.expr);
|
||||||
|
if (fnTy.kind.tag !== "fn") {
|
||||||
|
if (fnTy.kind.tag === "error") {
|
||||||
|
return fnTy;
|
||||||
|
}
|
||||||
|
const ty = tyToString(this.ctx, fnTy);
|
||||||
|
console.log(kind.expr.span);
|
||||||
|
this.report(`type '${ty}' not fucking callable`, kind.expr.span);
|
||||||
|
return Ty({ tag: "error" });
|
||||||
|
}
|
||||||
|
const paramTys = fnTy.kind.params;
|
||||||
|
if (paramTys.length !== kind.args.length) {
|
||||||
|
this.report(
|
||||||
|
"not enough/too many fucking arguments",
|
||||||
|
kind.expr.span,
|
||||||
|
);
|
||||||
|
return Ty({ tag: "error" });
|
||||||
|
}
|
||||||
|
const _args = kind.args.map((arg, i) =>
|
||||||
|
this.checkExpr(arg, paramTys[i])
|
||||||
|
);
|
||||||
|
return fnTy.kind.returnTy;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
@ -227,10 +260,29 @@ export class Checker {
|
|||||||
return todo();
|
return todo();
|
||||||
case "bind": {
|
case "bind": {
|
||||||
switch (patRes.kind.tag) {
|
switch (patRes.kind.tag) {
|
||||||
case "fn_param":
|
case "fn_param": {
|
||||||
return todo();
|
const fnTy = this.fnItemTy(
|
||||||
case "let":
|
patRes.kind.item,
|
||||||
return todo();
|
patRes.kind.kind,
|
||||||
|
);
|
||||||
|
if (fnTy.kind.tag !== "fn") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const paramTy = fnTy.kind.params[patRes.kind.paramIdx];
|
||||||
|
this.assignPatTy(
|
||||||
|
patRes.kind.kind.params[patRes.kind.paramIdx].pat,
|
||||||
|
paramTy,
|
||||||
|
);
|
||||||
|
const ty = this.patTy(pat);
|
||||||
|
this.patTys.set(pat.id, ty);
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
|
case "let": {
|
||||||
|
this.checkLetStmtTy(patRes.kind.stmt, patRes.kind.kind);
|
||||||
|
const ty = this.patTy(pat);
|
||||||
|
this.patTys.set(pat.id, ty);
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exhausted(patRes.kind);
|
exhausted(patRes.kind);
|
||||||
return todo();
|
return todo();
|
||||||
@ -242,6 +294,12 @@ export class Checker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private resolveTys(a: Ty, b: Ty): Res<Ty, string> {
|
private resolveTys(a: Ty, b: Ty): Res<Ty, string> {
|
||||||
|
if (a.kind.tag === "error" || b.kind.tag === "error") {
|
||||||
|
return Res.Ok(a);
|
||||||
|
}
|
||||||
|
if (b.kind.tag === "unknown") {
|
||||||
|
return Res.Ok(a);
|
||||||
|
}
|
||||||
const as = tyToString(this.ctx, a);
|
const as = tyToString(this.ctx, a);
|
||||||
const bs = tyToString(this.ctx, b);
|
const bs = tyToString(this.ctx, b);
|
||||||
const incompat = () =>
|
const incompat = () =>
|
||||||
@ -249,10 +307,8 @@ export class Checker {
|
|||||||
`type '${as}' not compatible with type '${bs}'`,
|
`type '${as}' not compatible with type '${bs}'`,
|
||||||
);
|
);
|
||||||
switch (a.kind.tag) {
|
switch (a.kind.tag) {
|
||||||
case "error":
|
|
||||||
return Res.Ok(b);
|
|
||||||
case "unknown":
|
case "unknown":
|
||||||
return Res.Ok(b);
|
return this.resolveTys(b, a);
|
||||||
case "null": {
|
case "null": {
|
||||||
if (b.kind.tag !== "null") {
|
if (b.kind.tag !== "null") {
|
||||||
return incompat();
|
return incompat();
|
||||||
|
@ -25,6 +25,10 @@ export type Report = {
|
|||||||
pos?: Pos;
|
pos?: Pos;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ReportLocation =
|
||||||
|
| { file: File; span: Span }
|
||||||
|
| { file: File; pos: Pos };
|
||||||
|
|
||||||
function severityColor(severity: "fatal" | "error" | "warning" | "info") {
|
function severityColor(severity: "fatal" | "error" | "warning" | "info") {
|
||||||
switch (severity) {
|
switch (severity) {
|
||||||
case "fatal":
|
case "fatal":
|
||||||
@ -40,6 +44,13 @@ function severityColor(severity: "fatal" | "error" | "warning" | "info") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function prettyPrintReport(ctx: Ctx, rep: Report) {
|
export function prettyPrintReport(ctx: Ctx, rep: Report) {
|
||||||
|
if (rep.span && rep.span.begin.idx === rep.span.end.idx) {
|
||||||
|
return prettyPrintReport(ctx, {
|
||||||
|
...rep,
|
||||||
|
span: undefined,
|
||||||
|
pos: rep.span.begin,
|
||||||
|
});
|
||||||
|
}
|
||||||
const { severity, msg } = rep;
|
const { severity, msg } = rep;
|
||||||
const origin = rep.origin ? `\x1b[1m${rep.origin}:\x1b[0m ` : "";
|
const origin = rep.origin ? `\x1b[1m${rep.origin}:\x1b[0m ` : "";
|
||||||
console.error(
|
console.error(
|
||||||
@ -47,71 +58,72 @@ export function prettyPrintReport(ctx: Ctx, rep: Report) {
|
|||||||
severityColor(severity)
|
severityColor(severity)
|
||||||
}${severity}:\x1b[0m \x1b[37m${msg}\x1b[0m`,
|
}${severity}:\x1b[0m \x1b[37m${msg}\x1b[0m`,
|
||||||
);
|
);
|
||||||
if (rep.file && (rep.span || rep.pos)) {
|
if (!rep.file) {
|
||||||
const errorLineOffset = 2;
|
return;
|
||||||
const { absPath: path } = ctx.fileInfo(rep.file);
|
}
|
||||||
const { line, col } = rep.span?.begin ?? rep.pos!;
|
const errorLineOffset = 2;
|
||||||
console.error(` --> ./${path}:${line}:${col}`);
|
const { absPath: path } = ctx.fileInfo(rep.file);
|
||||||
if (rep.span) {
|
const { line, col } = rep.span?.begin ?? rep.pos!;
|
||||||
const spanLines = ctx.fileSpanText(rep.file, rep.span).split("\n");
|
console.error(` --> ./${path}:${line}:${col}`);
|
||||||
spanLines.pop();
|
if (rep.span) {
|
||||||
if (spanLines.length == 1) {
|
const spanLines = ctx.fileSpanText(rep.file, rep.span).split("\n");
|
||||||
console.error(
|
spanLines.pop();
|
||||||
`${rep.span.begin.line.toString().padStart(4, " ")}| ${
|
if (spanLines.length == 1) {
|
||||||
spanLines[0]
|
|
||||||
}`,
|
|
||||||
);
|
|
||||||
console.error(
|
|
||||||
` | ${severityColor(severity)}${
|
|
||||||
" ".repeat(rep.span.begin.col)
|
|
||||||
}${
|
|
||||||
"~".repeat(rep.span.end.col - rep.span.begin.col)
|
|
||||||
}\x1b[0m`,
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < spanLines.length; i++) {
|
|
||||||
console.error(
|
|
||||||
`${
|
|
||||||
(rep.span.begin.line + i).toString().padStart(4, " ")
|
|
||||||
}| ${spanLines[i]}`,
|
|
||||||
);
|
|
||||||
if (i == 0) {
|
|
||||||
console.error(
|
|
||||||
` | ${" ".repeat(rep.span.begin.col - 1)}${
|
|
||||||
severityColor(severity)
|
|
||||||
}${
|
|
||||||
"~".repeat(
|
|
||||||
spanLines[i].length - (rep.span.begin.col - 1),
|
|
||||||
)
|
|
||||||
}\x1b[0m`,
|
|
||||||
);
|
|
||||||
} else if (i == spanLines.length - 1) {
|
|
||||||
console.error(
|
|
||||||
` | ${severityColor(severity)}${
|
|
||||||
"~".repeat(rep.span.end.col)
|
|
||||||
}\x1b[0m`,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
console.error(
|
|
||||||
` | ${severityColor(severity)}${
|
|
||||||
"~".repeat(spanLines[i].length)
|
|
||||||
}\x1b[0m`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (rep.pos) {
|
|
||||||
console.error(
|
console.error(
|
||||||
`${rep.pos.line.toString().padStart(4, " ")}| ${
|
`${rep.span.begin.line.toString().padStart(4, " ")}| ${
|
||||||
ctx.filePosLineText(rep.file, rep.pos)
|
spanLines[0]
|
||||||
}`,
|
}`,
|
||||||
);
|
);
|
||||||
console.error(
|
console.error(
|
||||||
` | ${severityColor(severity)}${
|
` | ${severityColor(severity)}${
|
||||||
" ".repeat(rep.pos.col)
|
" ".repeat(rep.span.begin.col - 1)
|
||||||
}^\x1b[0m`,
|
}${
|
||||||
|
"~".repeat(rep.span.end.col - rep.span.begin.col + 1)
|
||||||
|
}\x1b[0m`,
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
for (let i = 0; i < spanLines.length; i++) {
|
||||||
|
console.error(
|
||||||
|
`${(rep.span.begin.line + i).toString().padStart(4, " ")}| ${
|
||||||
|
spanLines[i]
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
if (i == 0) {
|
||||||
|
console.error(
|
||||||
|
` | ${" ".repeat(rep.span.begin.col - 1)}${
|
||||||
|
severityColor(severity)
|
||||||
|
}${
|
||||||
|
"~".repeat(
|
||||||
|
spanLines[i].length - (rep.span.begin.col - 1),
|
||||||
|
)
|
||||||
|
}\x1b[0m`,
|
||||||
|
);
|
||||||
|
} else if (i == spanLines.length - 1) {
|
||||||
|
console.error(
|
||||||
|
` | ${severityColor(severity)}${
|
||||||
|
"~".repeat(rep.span.end.col)
|
||||||
|
}\x1b[0m`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
` | ${severityColor(severity)}${
|
||||||
|
"~".repeat(spanLines[i].length)
|
||||||
|
}\x1b[0m`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (rep.pos) {
|
||||||
|
console.error(
|
||||||
|
`${rep.pos.line.toString().padStart(4, " ")}| ${
|
||||||
|
ctx.filePosLineText(rep.file, rep.pos)
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
console.error(
|
||||||
|
` | ${severityColor(severity)}${
|
||||||
|
" ".repeat(rep.pos.col - 1)
|
||||||
|
}^\x1b[0m`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,24 @@
|
|||||||
{
|
{
|
||||||
"workspace": ["./ast", "./check", "./middle", "./parse", "./resolve", "./ty", "./common"],
|
"workspace": [
|
||||||
|
"./ast",
|
||||||
|
"./check",
|
||||||
|
"./middle",
|
||||||
|
"./parse",
|
||||||
|
"./resolve",
|
||||||
|
"./ty",
|
||||||
|
"./common",
|
||||||
|
"./stringify"
|
||||||
|
],
|
||||||
"lint": {
|
"lint": {
|
||||||
"rules": {
|
"rules": {
|
||||||
"tags": ["recommended"],
|
"tags": ["recommended"],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"verbatim-module-syntax",
|
"verbatim-module-syntax",
|
||||||
"no-unused-vars"
|
"no-unused-vars"
|
||||||
],
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fmt": {
|
"fmt": {
|
||||||
"indentWidth": 4
|
"indentWidth": 4
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import { Ctx, File } from "@slige/common";
|
|||||||
import { Resolver } from "./resolve/resolver.ts";
|
import { Resolver } from "./resolve/resolver.ts";
|
||||||
import { Checker } from "./check/checker.ts";
|
import { Checker } from "./check/checker.ts";
|
||||||
import { AstLowerer } from "./middle/ast_lower.ts";
|
import { AstLowerer } from "./middle/ast_lower.ts";
|
||||||
|
import { HirStringifyer } from "@slige/stringify";
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const filePath = Deno.args[0];
|
const filePath = Deno.args[0];
|
||||||
@ -43,7 +44,17 @@ export class PackCompiler {
|
|||||||
.collect();
|
.collect();
|
||||||
const resols = new Resolver(this.ctx, entryFileAst).resolve();
|
const resols = new Resolver(this.ctx, entryFileAst).resolve();
|
||||||
const checker = new Checker(this.ctx, entryFileAst, resols);
|
const checker = new Checker(this.ctx, entryFileAst, resols);
|
||||||
new AstLowerer(this.ctx, resols, checker, entryFileAst).lower();
|
console.log(
|
||||||
|
"=== HIR ===\n" + new HirStringifyer(checker).file(entryFileAst),
|
||||||
|
);
|
||||||
|
const astLowerer = new AstLowerer(
|
||||||
|
this.ctx,
|
||||||
|
resols,
|
||||||
|
checker,
|
||||||
|
entryFileAst,
|
||||||
|
);
|
||||||
|
astLowerer.lower();
|
||||||
|
console.log("=== MIR ===\n" + astLowerer.mirString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public enableDebug() {
|
public enableDebug() {
|
||||||
|
@ -3,10 +3,20 @@ import { Checker } from "@slige/check";
|
|||||||
import { AstId, Ctx, exhausted, IdMap, Ids, Res, todo } from "@slige/common";
|
import { AstId, Ctx, exhausted, IdMap, Ids, Res, todo } from "@slige/common";
|
||||||
import { Resols } from "@slige/resolve";
|
import { Resols } from "@slige/resolve";
|
||||||
import { Ty } from "@slige/ty";
|
import { Ty } from "@slige/ty";
|
||||||
import { BinaryType, Operand, StmtKind, TerKind } from "./mir.ts";
|
import {
|
||||||
|
BinaryType,
|
||||||
|
Operand,
|
||||||
|
Place,
|
||||||
|
ProjElem,
|
||||||
|
StmtKind,
|
||||||
|
TerKind,
|
||||||
|
} from "./mir.ts";
|
||||||
import { Block, BlockId, Fn, Local, LocalId, RVal, Stmt, Ter } from "./mir.ts";
|
import { Block, BlockId, Fn, Local, LocalId, RVal, Stmt, Ter } from "./mir.ts";
|
||||||
|
import { MirFnStringifyer } from "@slige/stringify";
|
||||||
|
|
||||||
export class AstLowerer implements ast.Visitor {
|
export class AstLowerer implements ast.Visitor {
|
||||||
|
private fns = new IdMap<AstId, Fn>();
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
private ctx: Ctx,
|
private ctx: Ctx,
|
||||||
private re: Resols,
|
private re: Resols,
|
||||||
@ -19,7 +29,18 @@ export class AstLowerer implements ast.Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitFnItem(item: ast.Item, kind: ast.FnItem): ast.VisitRes {
|
visitFnItem(item: ast.Item, kind: ast.FnItem): ast.VisitRes {
|
||||||
new FnLowerer(this.ctx, this.re, this.ch, item, kind).lower();
|
const fn = new FnLowerer(this.ctx, this.re, this.ch, item, kind)
|
||||||
|
.lower();
|
||||||
|
if (fn.ok) {
|
||||||
|
this.fns.set(item.id, fn.val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public mirString(): string {
|
||||||
|
return this.fns.values()
|
||||||
|
.map((fn) => new MirFnStringifyer(this.ctx).fn(fn))
|
||||||
|
.toArray()
|
||||||
|
.join("\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +67,10 @@ export class FnLowerer {
|
|||||||
const entry = this.pushBlock();
|
const entry = this.pushBlock();
|
||||||
|
|
||||||
const fnTy = this.ch.fnItemTy(this.item, this.kind);
|
const fnTy = this.ch.fnItemTy(this.item, this.kind);
|
||||||
const returnPlace = this.local(fnTy);
|
if (fnTy.kind.tag !== "fn") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const returnPlace = this.local(fnTy.kind.returnTy);
|
||||||
const returnVal = this.lowerBlock(this.kind.body!);
|
const returnVal = this.lowerBlock(this.kind.body!);
|
||||||
|
|
||||||
this.addStmt({
|
this.addStmt({
|
||||||
@ -81,12 +105,68 @@ export class FnLowerer {
|
|||||||
case "error":
|
case "error":
|
||||||
return { tag: "error" };
|
return { tag: "error" };
|
||||||
case "item":
|
case "item":
|
||||||
|
return todo(k.tag);
|
||||||
case "let":
|
case "let":
|
||||||
|
return this.lowerLetStmt(stmt, k);
|
||||||
case "return":
|
case "return":
|
||||||
case "break":
|
case "break":
|
||||||
case "continue":
|
case "continue":
|
||||||
case "assign":
|
case "assign":
|
||||||
case "expr":
|
case "expr":
|
||||||
|
return todo(k.tag);
|
||||||
|
}
|
||||||
|
exhausted(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
private lowerLetStmt(_stmt: ast.Stmt, kind: ast.LetStmt) {
|
||||||
|
const val = kind.expr && this.lowerExpr(kind.expr);
|
||||||
|
this.allocatePat(kind.pat);
|
||||||
|
if (val) {
|
||||||
|
const local = this.local(this.ch.patTy(kind.pat));
|
||||||
|
this.addStmt({
|
||||||
|
tag: "assign",
|
||||||
|
place: { local: local, proj: [] },
|
||||||
|
rval: val,
|
||||||
|
});
|
||||||
|
this.assignPat(kind.pat, local, []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private allocatePat(pat: ast.Pat) {
|
||||||
|
const k = pat.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "error":
|
||||||
|
return;
|
||||||
|
case "bind": {
|
||||||
|
const ty = this.ch.patTy(pat);
|
||||||
|
const local = this.local(ty);
|
||||||
|
this.reLocals.set(pat.id, local);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "path":
|
||||||
|
return todo();
|
||||||
|
}
|
||||||
|
exhausted(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
private assignPat(pat: ast.Pat, local: LocalId, proj: ProjElem[]) {
|
||||||
|
const k = pat.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "error":
|
||||||
|
return;
|
||||||
|
case "bind": {
|
||||||
|
const patLocal = this.reLocals.get(pat.id)!;
|
||||||
|
this.addStmt({
|
||||||
|
tag: "assign",
|
||||||
|
place: { local: patLocal, proj: [] },
|
||||||
|
rval: {
|
||||||
|
tag: "use",
|
||||||
|
operand: { tag: "move", place: { local, proj } },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case "path":
|
||||||
return todo();
|
return todo();
|
||||||
}
|
}
|
||||||
exhausted(k);
|
exhausted(k);
|
||||||
@ -100,7 +180,18 @@ export class FnLowerer {
|
|||||||
case "path":
|
case "path":
|
||||||
return this.lowerPathExpr(expr, k);
|
return this.lowerPathExpr(expr, k);
|
||||||
case "null":
|
case "null":
|
||||||
|
return {
|
||||||
|
tag: "use",
|
||||||
|
operand: { tag: "const", val: { tag: "null" } },
|
||||||
|
};
|
||||||
case "int":
|
case "int":
|
||||||
|
return {
|
||||||
|
tag: "use",
|
||||||
|
operand: {
|
||||||
|
tag: "const",
|
||||||
|
val: { tag: "int", value: k.value },
|
||||||
|
},
|
||||||
|
};
|
||||||
case "bool":
|
case "bool":
|
||||||
case "str":
|
case "str":
|
||||||
case "group":
|
case "group":
|
||||||
@ -112,7 +203,9 @@ export class FnLowerer {
|
|||||||
case "elem":
|
case "elem":
|
||||||
case "field":
|
case "field":
|
||||||
case "index":
|
case "index":
|
||||||
|
return todo(k.tag);
|
||||||
case "call":
|
case "call":
|
||||||
|
return this.lowerCallExpr(expr, k);
|
||||||
case "unary":
|
case "unary":
|
||||||
return todo(k.tag);
|
return todo(k.tag);
|
||||||
case "binary":
|
case "binary":
|
||||||
@ -133,8 +226,14 @@ export class FnLowerer {
|
|||||||
switch (re.kind.tag) {
|
switch (re.kind.tag) {
|
||||||
case "error":
|
case "error":
|
||||||
return { tag: "error" };
|
return { tag: "error" };
|
||||||
case "fn":
|
case "fn": {
|
||||||
return todo();
|
const ty = this.ch.fnItemTy(re.kind.item, re.kind.kind);
|
||||||
|
const local = this.local(ty);
|
||||||
|
return {
|
||||||
|
tag: "use",
|
||||||
|
operand: { tag: "move", place: { local, proj: [] } },
|
||||||
|
};
|
||||||
|
}
|
||||||
case "local": {
|
case "local": {
|
||||||
const ty = this.ch.exprTy(expr);
|
const ty = this.ch.exprTy(expr);
|
||||||
const local = this.local(ty);
|
const local = this.local(ty);
|
||||||
@ -164,6 +263,17 @@ export class FnLowerer {
|
|||||||
exhausted(re.kind);
|
exhausted(re.kind);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lowerCallExpr(expr: ast.Expr, kind: ast.CallExpr): RVal {
|
||||||
|
const args = kind.args.map((arg) =>
|
||||||
|
this.rvalAsOperand(this.lowerExpr(arg), this.ch.exprTy(arg))
|
||||||
|
);
|
||||||
|
const func = this.rvalAsOperand(
|
||||||
|
this.lowerExpr(kind.expr),
|
||||||
|
this.ch.exprTy(expr),
|
||||||
|
);
|
||||||
|
return { tag: "call", func, args };
|
||||||
|
}
|
||||||
|
|
||||||
private lowerBinaryExpr(expr: ast.Expr, kind: ast.BinaryExpr): RVal {
|
private lowerBinaryExpr(expr: ast.Expr, kind: ast.BinaryExpr): RVal {
|
||||||
const left = this.rvalAsOperand(
|
const left = this.rvalAsOperand(
|
||||||
this.lowerExpr(kind.left),
|
this.lowerExpr(kind.left),
|
||||||
|
@ -51,8 +51,7 @@ export type TerKind =
|
|||||||
}
|
}
|
||||||
| { tag: "return" }
|
| { tag: "return" }
|
||||||
| { tag: "unreachable" }
|
| { tag: "unreachable" }
|
||||||
| { tag: "drop"; place: Place; target: BlockId }
|
| { tag: "drop"; place: Place; target: BlockId };
|
||||||
| { tag: "call"; func: Operand; args: Operand[]; dest: Operand };
|
|
||||||
|
|
||||||
export type SwitchTarget = {
|
export type SwitchTarget = {
|
||||||
value: number;
|
value: number;
|
||||||
@ -67,7 +66,6 @@ export type Place = {
|
|||||||
// https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/type.PlaceElem.html
|
// https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/mir/type.PlaceElem.html
|
||||||
export type ProjElem =
|
export type ProjElem =
|
||||||
| { tag: "deref" }
|
| { tag: "deref" }
|
||||||
| { tag: "repeat" }
|
|
||||||
| { tag: "field"; fieldIdx: number }
|
| { tag: "field"; fieldIdx: number }
|
||||||
| { tag: "index"; local: LocalId }
|
| { tag: "index"; local: LocalId }
|
||||||
| { tag: "downcast"; variantIdx: number };
|
| { tag: "downcast"; variantIdx: number };
|
||||||
@ -80,7 +78,8 @@ export type RVal =
|
|||||||
| { tag: "ref"; place: Place; mut: boolean }
|
| { tag: "ref"; place: Place; mut: boolean }
|
||||||
| { tag: "ptr"; place: Place; mut: boolean }
|
| { tag: "ptr"; place: Place; mut: boolean }
|
||||||
| { tag: "binary"; binaryType: BinaryType; left: Operand; right: Operand }
|
| { tag: "binary"; binaryType: BinaryType; left: Operand; right: Operand }
|
||||||
| { tag: "unary"; unaryType: UnaryType; operand: Operand };
|
| { tag: "unary"; unaryType: UnaryType; operand: Operand }
|
||||||
|
| { tag: "call"; func: Operand; args: Operand[] };
|
||||||
|
|
||||||
export type BinaryType =
|
export type BinaryType =
|
||||||
| "add"
|
| "add"
|
||||||
|
2
slige/compiler/middle/mod.ts
Normal file
2
slige/compiler/middle/mod.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./mir.ts";
|
||||||
|
export * from "./ast_lower.ts";
|
@ -203,7 +203,7 @@ export class Lexer implements TokenIter {
|
|||||||
let val = this.current();
|
let val = this.current();
|
||||||
this.step();
|
this.step();
|
||||||
while (this.test(tailPat)) {
|
while (this.test(tailPat)) {
|
||||||
end = begin;
|
end = this.pos();
|
||||||
val += this.current();
|
val += this.current();
|
||||||
this.step();
|
this.step();
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ export type PatResolve = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type PatResolveKind =
|
export type PatResolveKind =
|
||||||
| { tag: "fn_param"; paramIdx: number }
|
| { tag: "fn_param"; item: ast.Item; kind: ast.FnItem; paramIdx: number }
|
||||||
| { tag: "let"; stmt: ast.Stmt; kind: ast.LetStmt };
|
| { tag: "let"; stmt: ast.Stmt; kind: ast.LetStmt };
|
||||||
|
|
||||||
export const ResolveError = (ident: ast.Ident): Resolve => ({
|
export const ResolveError = (ident: ast.Ident): Resolve => ({
|
||||||
|
@ -118,12 +118,17 @@ export class Resolver implements ast.Visitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private popAndVisitFnBodies() {
|
private popAndVisitFnBodies() {
|
||||||
for (const [_item, kind] of this.fnBodiesToCheck.at(-1)!) {
|
for (const [item, kind] of this.fnBodiesToCheck.at(-1)!) {
|
||||||
const outerSyms = this.syms;
|
const outerSyms = this.syms;
|
||||||
this.syms = new FnSyms(this.syms);
|
this.syms = new FnSyms(this.syms);
|
||||||
this.syms = new LocalSyms(this.syms);
|
this.syms = new LocalSyms(this.syms);
|
||||||
for (const [paramIdx, param] of kind.params.entries()) {
|
for (const [paramIdx, param] of kind.params.entries()) {
|
||||||
this.patResolveStack.push({ tag: "fn_param", paramIdx });
|
this.patResolveStack.push({
|
||||||
|
tag: "fn_param",
|
||||||
|
item,
|
||||||
|
kind,
|
||||||
|
paramIdx,
|
||||||
|
});
|
||||||
ast.visitParam(this, param);
|
ast.visitParam(this, param);
|
||||||
this.patResolveStack.pop();
|
this.patResolveStack.pop();
|
||||||
}
|
}
|
||||||
|
4
slige/compiler/stringify/deno.jsonc
Normal file
4
slige/compiler/stringify/deno.jsonc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"name": "@slige/stringify",
|
||||||
|
"exports": "./mod.ts",
|
||||||
|
}
|
178
slige/compiler/stringify/hir.ts
Normal file
178
slige/compiler/stringify/hir.ts
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
import * as ast from "@slige/ast";
|
||||||
|
import { exhausted, todo } from "@slige/common";
|
||||||
|
import { Checker } from "@slige/check";
|
||||||
|
import { Ty } from "@slige/ty";
|
||||||
|
|
||||||
|
export class HirStringifyer {
|
||||||
|
public constructor(
|
||||||
|
private ch: Checker,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public file(file: ast.File, depth = 0): string {
|
||||||
|
return file.stmts.map((stmt) =>
|
||||||
|
indent(depth) + this.stmt(stmt, depth + 1)
|
||||||
|
).join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
public stmt(stmt: ast.Stmt, depth = 0): string {
|
||||||
|
const k = stmt.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "error":
|
||||||
|
return "<error>;";
|
||||||
|
case "item":
|
||||||
|
return this.item(k.item);
|
||||||
|
case "let":
|
||||||
|
return `let ${this.pat(k.pat)}${
|
||||||
|
k.expr && ` = ${this.expr(k.expr)}` || ""
|
||||||
|
};`;
|
||||||
|
case "return":
|
||||||
|
return `return${k.expr && ` ${this.expr(k.expr)}` || ""};`;
|
||||||
|
case "break":
|
||||||
|
return `break${k.expr && ` ${this.expr(k.expr)}` || ""};`;
|
||||||
|
case "continue":
|
||||||
|
return `continue;`;
|
||||||
|
case "assign":
|
||||||
|
return `${this.expr(k.subject)} = ${this.expr(k.value)};`;
|
||||||
|
case "expr":
|
||||||
|
return `${this.expr(k.expr)};`;
|
||||||
|
}
|
||||||
|
exhausted(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
public item(item: ast.Item, depth = 0): string {
|
||||||
|
const ident = item.ident.text;
|
||||||
|
const pub = item.pub ? "pub " : "";
|
||||||
|
const k = item.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "error":
|
||||||
|
return "<error>;";
|
||||||
|
case "mod_block":
|
||||||
|
return `${pub}mod ${ident} ${this.block(k.block, depth)}`;
|
||||||
|
case "mod_file":
|
||||||
|
return `${pub}mod ${ident} {\n${
|
||||||
|
this.file(k.ast!, depth + 1)
|
||||||
|
}\n}`;
|
||||||
|
case "enum":
|
||||||
|
return todo();
|
||||||
|
case "struct":
|
||||||
|
return todo();
|
||||||
|
case "fn": {
|
||||||
|
const ty = this.ch.fnItemTy(item, k);
|
||||||
|
if (ty.kind.tag !== "fn") {
|
||||||
|
throw new Error();
|
||||||
|
}
|
||||||
|
const params = k.params
|
||||||
|
.map((param) => this.pat(param.pat))
|
||||||
|
.join(", ");
|
||||||
|
return `${pub}fn ${ident}(${params}) -> ${
|
||||||
|
this.ty(ty.kind.returnTy)
|
||||||
|
} ${this.block(k.body!, depth)}`;
|
||||||
|
}
|
||||||
|
case "use":
|
||||||
|
return todo();
|
||||||
|
case "type_alias":
|
||||||
|
return todo();
|
||||||
|
}
|
||||||
|
exhausted(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
public expr(expr: ast.Expr, depth = 0): string {
|
||||||
|
const k = expr.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "error":
|
||||||
|
return "<error>";
|
||||||
|
case "path":
|
||||||
|
return this.path(k.path);
|
||||||
|
case "null":
|
||||||
|
return "null";
|
||||||
|
case "int":
|
||||||
|
return `${k.value}`;
|
||||||
|
case "bool":
|
||||||
|
return `${k.value}`;
|
||||||
|
case "str":
|
||||||
|
return `"${k.value}"`;
|
||||||
|
case "group":
|
||||||
|
case "array":
|
||||||
|
case "repeat":
|
||||||
|
case "struct":
|
||||||
|
case "ref":
|
||||||
|
case "deref":
|
||||||
|
case "elem":
|
||||||
|
case "field":
|
||||||
|
case "index":
|
||||||
|
return todo(k.tag);
|
||||||
|
case "call":
|
||||||
|
return `${this.expr(k.expr)}(${
|
||||||
|
k.args.map((arg) => this.expr(arg)).join(", ")
|
||||||
|
})`;
|
||||||
|
case "unary":
|
||||||
|
return todo(k.tag);
|
||||||
|
case "binary":
|
||||||
|
return `${this.expr(k.left)} ${k.binaryType} ${
|
||||||
|
this.expr(k.right)
|
||||||
|
}`;
|
||||||
|
case "block":
|
||||||
|
case "if":
|
||||||
|
case "loop":
|
||||||
|
case "while":
|
||||||
|
case "for":
|
||||||
|
case "c_for":
|
||||||
|
return todo(k.tag);
|
||||||
|
}
|
||||||
|
exhausted(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
public pat(pat: ast.Pat, depth = 0): string {
|
||||||
|
const k = pat.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "error":
|
||||||
|
return "<error>";
|
||||||
|
case "bind":
|
||||||
|
return `${k.mut ? "mut " : ""}${k.ident.text}: ${
|
||||||
|
this.ty(this.ch.patTy(pat))
|
||||||
|
}`;
|
||||||
|
case "path":
|
||||||
|
return todo();
|
||||||
|
}
|
||||||
|
exhausted(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
public block(block: ast.Block, depth = 0): string {
|
||||||
|
return `{\n${
|
||||||
|
[
|
||||||
|
...block.stmts
|
||||||
|
.map((stmt) => this.stmt(stmt, depth + 1)),
|
||||||
|
...(block.expr ? [this.expr(block.expr)] : []),
|
||||||
|
]
|
||||||
|
.map((str) => indent(depth + 1) + str)
|
||||||
|
.join("\n")
|
||||||
|
}\n${indent(depth)}}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public path(path: ast.Path): string {
|
||||||
|
return path.segments.map((seg) => seg.ident.text).join("::");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ty(ty: Ty): string {
|
||||||
|
const k = ty.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "error":
|
||||||
|
return "<error>";
|
||||||
|
case "unknown":
|
||||||
|
return "<unknown>";
|
||||||
|
case "null":
|
||||||
|
return "null";
|
||||||
|
case "int":
|
||||||
|
return "int";
|
||||||
|
case "fn":
|
||||||
|
return `fn ${k.item.ident}(${
|
||||||
|
k.params.map((param) => this.ty(param)).join(", ")
|
||||||
|
}) -> ${this.ty(k.returnTy)}`;
|
||||||
|
}
|
||||||
|
exhausted(k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function indent(depth: number): string {
|
||||||
|
return " ".repeat(depth);
|
||||||
|
}
|
168
slige/compiler/stringify/mir.ts
Normal file
168
slige/compiler/stringify/mir.ts
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
import {
|
||||||
|
Block,
|
||||||
|
BlockId,
|
||||||
|
Const,
|
||||||
|
Fn,
|
||||||
|
Local,
|
||||||
|
LocalId,
|
||||||
|
Operand,
|
||||||
|
Place,
|
||||||
|
ProjElem,
|
||||||
|
RVal,
|
||||||
|
Stmt,
|
||||||
|
StmtKind,
|
||||||
|
Ter,
|
||||||
|
} from "@slige/middle";
|
||||||
|
import { Ctx, exhausted, IdMap, todo } from "@slige/common";
|
||||||
|
import { Checker } from "@slige/check";
|
||||||
|
import { Ty, tyToString } from "@slige/ty";
|
||||||
|
|
||||||
|
export class MirFnStringifyer {
|
||||||
|
private blockIds = new IdMap<BlockId, number>();
|
||||||
|
private localIds = new IdMap<LocalId, number>();
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
private ctx: Ctx,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
public fn(fn: Fn): string {
|
||||||
|
return `fn ${fn.label} {\n${
|
||||||
|
fn.locals.values().toArray()
|
||||||
|
.map((local) => this.localDef(local))
|
||||||
|
.join("\n")
|
||||||
|
}\n${
|
||||||
|
fn.blocks.values().toArray()
|
||||||
|
.map((block) => this.block(block))
|
||||||
|
.join("\n")
|
||||||
|
}\n}`.replaceAll("#", " ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private localDef(local: Local): string {
|
||||||
|
const id = this.localIds.size;
|
||||||
|
this.localIds.set(local.id, id);
|
||||||
|
return `#let %${id}: ${tyToString(this.ctx, local.ty)}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private block(block: Block): string {
|
||||||
|
const id = this.blockIds.size;
|
||||||
|
this.blockIds.set(block.id, id);
|
||||||
|
return `#.b${id}: {\n${
|
||||||
|
block.stmts
|
||||||
|
.map((stmt) => this.stmt(stmt))
|
||||||
|
.join("\n")
|
||||||
|
}\n${this.ter(block.terminator)}\n#}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private stmt(stmt: Stmt): string {
|
||||||
|
const k = stmt.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "error":
|
||||||
|
return "##<error>;";
|
||||||
|
case "assign":
|
||||||
|
return `##${this.place(k.place)} = ${this.rval(k.rval)}`;
|
||||||
|
case "fake_read":
|
||||||
|
case "deinit":
|
||||||
|
case "live":
|
||||||
|
case "dead":
|
||||||
|
case "mention":
|
||||||
|
return todo();
|
||||||
|
}
|
||||||
|
exhausted(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ter(ter: Ter): string {
|
||||||
|
const k = ter.kind;
|
||||||
|
switch (k.tag) {
|
||||||
|
case "unset":
|
||||||
|
return "##<unset>;";
|
||||||
|
case "goto":
|
||||||
|
case "switch":
|
||||||
|
return todo(k.tag);
|
||||||
|
case "return":
|
||||||
|
return `##return;`;
|
||||||
|
case "unreachable":
|
||||||
|
case "drop":
|
||||||
|
return todo(k.tag);
|
||||||
|
}
|
||||||
|
exhausted(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
private place(place: Place): string {
|
||||||
|
return this.placeWithProj(place.local, place.proj);
|
||||||
|
}
|
||||||
|
|
||||||
|
private placeWithProj(local: LocalId, elems: ProjElem[]): string {
|
||||||
|
if (elems.length === 0) {
|
||||||
|
return this.local(local);
|
||||||
|
}
|
||||||
|
const elem = elems[0];
|
||||||
|
const tail = elems.slice(1);
|
||||||
|
switch (elem.tag) {
|
||||||
|
case "deref":
|
||||||
|
return `*${this.placeWithProj(local, tail)}`;
|
||||||
|
case "field":
|
||||||
|
return `${this.placeWithProj(local, tail)}.${elem.fieldIdx}`;
|
||||||
|
case "index":
|
||||||
|
return `${this.placeWithProj(local, tail)}[${
|
||||||
|
this.local(elem.local)
|
||||||
|
}]`;
|
||||||
|
case "downcast":
|
||||||
|
return todo();
|
||||||
|
}
|
||||||
|
exhausted(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private rval(rval: RVal): string {
|
||||||
|
switch (rval.tag) {
|
||||||
|
case "error":
|
||||||
|
return "<error>";
|
||||||
|
case "use":
|
||||||
|
return `${this.operand(rval.operand)}`;
|
||||||
|
case "repeat":
|
||||||
|
case "ref":
|
||||||
|
case "ptr":
|
||||||
|
return todo(rval.tag);
|
||||||
|
case "binary":
|
||||||
|
return `${this.operand(rval.left)} ${rval.binaryType} ${
|
||||||
|
this.operand(rval.right)
|
||||||
|
}`;
|
||||||
|
case "unary":
|
||||||
|
return todo(rval.tag);
|
||||||
|
case "call":
|
||||||
|
return `${this.operand(rval.func)}(${
|
||||||
|
rval.args.map((arg) => this.operand(arg)).join(", ")
|
||||||
|
})`;
|
||||||
|
}
|
||||||
|
exhausted(rval);
|
||||||
|
}
|
||||||
|
|
||||||
|
private operand(operand: Operand): string {
|
||||||
|
switch (operand.tag) {
|
||||||
|
case "copy":
|
||||||
|
return `copy ${this.place(operand.place)}`;
|
||||||
|
case "move":
|
||||||
|
return `move ${this.place(operand.place)}`;
|
||||||
|
case "const":
|
||||||
|
return `${this.constVal(operand.val)}`;
|
||||||
|
}
|
||||||
|
exhausted(operand);
|
||||||
|
}
|
||||||
|
|
||||||
|
private constVal(val: Const): string {
|
||||||
|
switch (val.tag) {
|
||||||
|
case "null":
|
||||||
|
return `null`;
|
||||||
|
case "int":
|
||||||
|
return `${val.value}`;
|
||||||
|
case "bool":
|
||||||
|
return `${val.value}`;
|
||||||
|
case "string":
|
||||||
|
return `"${val.value}"`;
|
||||||
|
}
|
||||||
|
exhausted(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
private local(local: LocalId): string {
|
||||||
|
return `%${this.localIds.get(local)!}`;
|
||||||
|
}
|
||||||
|
}
|
2
slige/compiler/stringify/mod.ts
Normal file
2
slige/compiler/stringify/mod.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from "./hir.ts";
|
||||||
|
export * from "./mir.ts";
|
Loading…
x
Reference in New Issue
Block a user