Compare commits
No commits in common. "99532d37d6fa47f78b13e5c30a17e1ad5327a49f" and "76fff577b1e72481cdccbe659e120357afda9379" have entirely different histories.
99532d37d6
...
76fff577b1
@ -21,22 +21,19 @@ export type StmtKind =
|
|||||||
| { type: "mod"; ident: string; mod: Mod }
|
| { type: "mod"; ident: string; mod: Mod }
|
||||||
| { type: "break"; expr?: Expr }
|
| { type: "break"; expr?: Expr }
|
||||||
| { type: "return"; expr?: Expr }
|
| { type: "return"; expr?: Expr }
|
||||||
| FnStmtKind
|
| {
|
||||||
| { type: "let"; param: Param; value: Expr }
|
|
||||||
| { type: "type_alias"; param: Param }
|
|
||||||
| { type: "assign"; assignType: AssignType; subject: Expr; value: Expr }
|
|
||||||
| { type: "expr"; expr: Expr };
|
|
||||||
|
|
||||||
export type FnStmtKind = {
|
|
||||||
type: "fn";
|
type: "fn";
|
||||||
ident: string;
|
ident: string;
|
||||||
genericParams?: GenericParam[];
|
genericParams?: GenericParam[];
|
||||||
params: Param[];
|
params: Param[];
|
||||||
returnType?: EType;
|
returnType?: EType;
|
||||||
body: Expr;
|
body: Expr;
|
||||||
sym?: Sym;
|
|
||||||
vtype?: VType;
|
vtype?: VType;
|
||||||
};
|
}
|
||||||
|
| { type: "let"; param: Param; value: Expr }
|
||||||
|
| { type: "type_alias"; param: Param }
|
||||||
|
| { type: "assign"; assignType: AssignType; subject: Expr; value: Expr }
|
||||||
|
| { type: "expr"; expr: Expr };
|
||||||
|
|
||||||
export type AssignType = "=" | "+=" | "-=";
|
export type AssignType = "=" | "+=" | "-=";
|
||||||
|
|
||||||
@ -119,13 +116,11 @@ export type Param = {
|
|||||||
ident: string;
|
ident: string;
|
||||||
etype?: EType;
|
etype?: EType;
|
||||||
pos: Pos;
|
pos: Pos;
|
||||||
sym?: Sym;
|
|
||||||
vtype?: VType;
|
vtype?: VType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Sym = {
|
export type Sym = {
|
||||||
ident: string;
|
ident: string;
|
||||||
fullPath: string;
|
|
||||||
pos?: Pos;
|
pos?: Pos;
|
||||||
} & SymKind;
|
} & SymKind;
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ export class Checker {
|
|||||||
}
|
}
|
||||||
const vtype = this.checkEType(param.etype!);
|
const vtype = this.checkEType(param.etype!);
|
||||||
param.vtype = vtype;
|
param.vtype = vtype;
|
||||||
params.push({ ident: param.ident, mut: true, vtype });
|
params.push({ ident: param.ident, vtype });
|
||||||
}
|
}
|
||||||
const returnType: VType = stmt.kind.returnType
|
const returnType: VType = stmt.kind.returnType
|
||||||
? this.checkEType(stmt.kind.returnType)
|
? this.checkEType(stmt.kind.returnType)
|
||||||
@ -426,11 +426,7 @@ export class Checker {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const fields: VTypeParam[] = expr.kind.fields
|
const fields: VTypeParam[] = expr.kind.fields
|
||||||
.map(({ ident, expr }): VTypeParam => ({
|
.map(({ ident, expr }) => ({ ident, vtype: this.checkExpr(expr) }));
|
||||||
ident,
|
|
||||||
mut: true,
|
|
||||||
vtype: this.checkExpr(expr),
|
|
||||||
}));
|
|
||||||
return { type: "struct", fields };
|
return { type: "struct", fields };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -493,9 +489,6 @@ export class Checker {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
const args = expr.kind.args.map((arg) => this.checkExpr(arg));
|
const args = expr.kind.args.map((arg) => this.checkExpr(arg));
|
||||||
if (args.some((arg) => arg.type === "error")) {
|
|
||||||
return { type: "error" };
|
|
||||||
}
|
|
||||||
if (subject.genericParams === undefined) {
|
if (subject.genericParams === undefined) {
|
||||||
return this.checkCallExprNoGenericsTail(
|
return this.checkCallExprNoGenericsTail(
|
||||||
expr,
|
expr,
|
||||||
@ -1030,10 +1023,8 @@ export class Checker {
|
|||||||
this.report(`field ${declaredTwiceTest[2]} defined twice`, pos);
|
this.report(`field ${declaredTwiceTest[2]} defined twice`, pos);
|
||||||
return { type: "error" };
|
return { type: "error" };
|
||||||
}
|
}
|
||||||
const fields = etype.kind.fields
|
const fields = etype.kind.fields.map((param): VTypeParam => ({
|
||||||
.map((param): VTypeParam => ({
|
|
||||||
ident: param.ident,
|
ident: param.ident,
|
||||||
mut: true,
|
|
||||||
vtype: this.checkEType(param.etype!),
|
vtype: this.checkEType(param.etype!),
|
||||||
}));
|
}));
|
||||||
return { type: "struct", fields };
|
return { type: "struct", fields };
|
||||||
|
@ -12,16 +12,10 @@ import { Resolver } from "./resolver.ts";
|
|||||||
import { AstVisitor, VisitRes, visitStmts } from "./ast_visitor.ts";
|
import { AstVisitor, VisitRes, visitStmts } from "./ast_visitor.ts";
|
||||||
import { Pos } from "./token.ts";
|
import { Pos } from "./token.ts";
|
||||||
import { ArrayLiteralDesugarer } from "./desugar/array_literal.ts";
|
import { ArrayLiteralDesugarer } from "./desugar/array_literal.ts";
|
||||||
import { mirOpCount, printMir } from "./middle/mir.ts";
|
|
||||||
|
|
||||||
import * as path from "jsr:@std/path";
|
import * as path from "jsr:@std/path";
|
||||||
import { lowerAst } from "./middle/lower_ast.ts";
|
import { AstLowerer } from "./middle/lower_ast.ts";
|
||||||
import { eliminateUnusedLocals } from "./middle/elim_unused_local.ts";
|
import { printMir } from "./middle/mir.ts";
|
||||||
import {
|
|
||||||
eliminateOnlyChildsBlocks,
|
|
||||||
eliminateUnreachableBlocks,
|
|
||||||
} from "./middle/elim_blocks.ts";
|
|
||||||
import { eliminateTransientVals } from "./middle/elim_transient_vals.ts";
|
|
||||||
|
|
||||||
export type CompileResult = {
|
export type CompileResult = {
|
||||||
program: number[];
|
program: number[];
|
||||||
@ -53,28 +47,8 @@ export class Compiler {
|
|||||||
|
|
||||||
new Checker(this.reporter).check(ast);
|
new Checker(this.reporter).check(ast);
|
||||||
|
|
||||||
const mir = lowerAst(ast);
|
const mir = new AstLowerer(ast).lower();
|
||||||
|
|
||||||
console.log("Before optimizations:");
|
|
||||||
printMir(mir);
|
|
||||||
|
|
||||||
const mirHistory = [mirOpCount(mir)];
|
|
||||||
for (let i = 0; i < 1; ++i) {
|
|
||||||
eliminateUnusedLocals(mir, this.reporter, mirHistory.length === 1);
|
|
||||||
eliminateOnlyChildsBlocks(mir);
|
|
||||||
eliminateUnreachableBlocks(mir);
|
|
||||||
eliminateTransientVals(mir);
|
|
||||||
|
|
||||||
const opCount = mirOpCount(mir);
|
|
||||||
const histOccurence = mirHistory
|
|
||||||
.filter((v) => v === opCount).length;
|
|
||||||
if (histOccurence >= 2) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mirHistory.push(opCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("After optimizations:");
|
|
||||||
printMir(mir);
|
printMir(mir);
|
||||||
|
|
||||||
if (this.reporter.errorOccured()) {
|
if (this.reporter.errorOccured()) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Pos } from "./token.ts";
|
import { Pos } from "./token.ts";
|
||||||
|
|
||||||
export type Report = {
|
export type Report = {
|
||||||
type: "error" | "warning" | "note";
|
type: "error" | "note";
|
||||||
reporter: string;
|
reporter: string;
|
||||||
pos?: Pos;
|
pos?: Pos;
|
||||||
msg: string;
|
msg: string;
|
||||||
@ -23,11 +23,6 @@ export class Reporter {
|
|||||||
this.errorSet = true;
|
this.errorSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public reportWarning(report: Omit<Report, "type">) {
|
|
||||||
this.reports.push({ ...report, type: "warning" });
|
|
||||||
this.printReport({ ...report, type: "warning" });
|
|
||||||
}
|
|
||||||
|
|
||||||
private printReport({ reporter, type, pos, msg }: Report) {
|
private printReport({ reporter, type, pos, msg }: Report) {
|
||||||
console.error(
|
console.error(
|
||||||
`${reporter} ${type}: ${msg}${
|
`${reporter} ${type}: ${msg}${
|
||||||
|
@ -1,103 +0,0 @@
|
|||||||
import { Block, BlockId, Fn, Mir } from "./mir.ts";
|
|
||||||
|
|
||||||
export function createCfg(fn: Fn): Cfg {
|
|
||||||
return new CfgBuilder(fn).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Cfg {
|
|
||||||
public constructor(
|
|
||||||
private graph: Map<BlockId, CfgNode>,
|
|
||||||
private entry: BlockId,
|
|
||||||
private exit: BlockId,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public parents(block: Block): Block[] {
|
|
||||||
return this.graph
|
|
||||||
.get(block.id)!.parents
|
|
||||||
.map((id) => this.graph.get(id)!.block);
|
|
||||||
}
|
|
||||||
|
|
||||||
public children(block: Block): Block[] {
|
|
||||||
return this.graph
|
|
||||||
.get(block.id)!.children
|
|
||||||
.map((id) => this.graph.get(id)!.block);
|
|
||||||
}
|
|
||||||
|
|
||||||
public index(block: Block): number {
|
|
||||||
return this.graph.get(block.id)!.index;
|
|
||||||
}
|
|
||||||
|
|
||||||
public print() {
|
|
||||||
for (const [id, node] of this.graph.entries()) {
|
|
||||||
const l = <T>(v: T[]) => v.map((v) => `${v}`).join(", ");
|
|
||||||
|
|
||||||
console.log(`graph[${id}] = {`);
|
|
||||||
console.log(` id: ${node.block.id},`);
|
|
||||||
console.log(` index: ${node.index},`);
|
|
||||||
console.log(` parents: [${l(node.parents)}],`);
|
|
||||||
console.log(` children: [${l(node.children)}],`);
|
|
||||||
console.log(`}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type CfgNode = {
|
|
||||||
block: Block;
|
|
||||||
index: number;
|
|
||||||
parents: BlockId[];
|
|
||||||
children: BlockId[];
|
|
||||||
};
|
|
||||||
|
|
||||||
class CfgBuilder {
|
|
||||||
private nodes: [Block, number][] = [];
|
|
||||||
private edges: [BlockId, BlockId][] = [];
|
|
||||||
|
|
||||||
public constructor(private fn: Fn) {}
|
|
||||||
|
|
||||||
public build(): Cfg {
|
|
||||||
for (
|
|
||||||
const [block, index] of this.fn.blocks
|
|
||||||
.map((v, i) => [v, i] as const)
|
|
||||||
) {
|
|
||||||
this.addNode(block, index);
|
|
||||||
|
|
||||||
const tk = block.ter.kind;
|
|
||||||
switch (tk.type) {
|
|
||||||
case "error":
|
|
||||||
break;
|
|
||||||
case "return":
|
|
||||||
break;
|
|
||||||
case "jump":
|
|
||||||
this.addEdge(block.id, tk.target);
|
|
||||||
break;
|
|
||||||
case "if":
|
|
||||||
this.addEdge(block.id, tk.truthy);
|
|
||||||
this.addEdge(block.id, tk.falsy);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const graph = new Map<BlockId, CfgNode>();
|
|
||||||
for (const [block, index] of this.nodes) {
|
|
||||||
const parents = this.edges
|
|
||||||
.filter(([_from, to]) => to === block.id)
|
|
||||||
.map(([from, _to]) => from);
|
|
||||||
|
|
||||||
const children = this.edges
|
|
||||||
.filter(([from, _to]) => from === block.id)
|
|
||||||
.map(([_from, to]) => to);
|
|
||||||
|
|
||||||
graph.set(block.id, { block, index, parents, children });
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Cfg(graph, this.fn.entry, this.fn.exit);
|
|
||||||
}
|
|
||||||
|
|
||||||
private addNode(block: Block, index: number) {
|
|
||||||
this.nodes.push([block, index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private addEdge(from: BlockId, to: BlockId) {
|
|
||||||
this.edges.push([from, to]);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
import { createCfg } from "./cfg.ts";
|
|
||||||
import { Block, Mir } from "./mir.ts";
|
|
||||||
|
|
||||||
export function eliminateOnlyChildsBlocks(mir: Mir) {
|
|
||||||
for (const fn of mir.fns) {
|
|
||||||
const cfg = createCfg(fn);
|
|
||||||
|
|
||||||
const candidates: { parent: Block; child: Block }[] = [];
|
|
||||||
|
|
||||||
for (const block of fn.blocks) {
|
|
||||||
const children = cfg.children(block);
|
|
||||||
if (children.length !== 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (cfg.parents(children[0]).length !== 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
candidates.push({ parent: block, child: children[0] });
|
|
||||||
}
|
|
||||||
|
|
||||||
const elimIndices: number[] = [];
|
|
||||||
|
|
||||||
for (const { parent, child } of candidates) {
|
|
||||||
parent.ops.push(...child.ops);
|
|
||||||
parent.ter = child.ter;
|
|
||||||
elimIndices.push(cfg.index(child));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const i of elimIndices.toReversed()) {
|
|
||||||
fn.blocks.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function eliminateUnreachableBlocks(mir: Mir) {
|
|
||||||
for (const fn of mir.fns) {
|
|
||||||
const cfg = createCfg(fn);
|
|
||||||
|
|
||||||
const candidates: Block[] = [];
|
|
||||||
|
|
||||||
for (const block of fn.blocks) {
|
|
||||||
if (block.id === fn.entry) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (cfg.parents(block).length !== 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
candidates.push(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (
|
|
||||||
const i of candidates
|
|
||||||
.map((block) => cfg.index(block))
|
|
||||||
.toReversed()
|
|
||||||
) {
|
|
||||||
fn.blocks.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
import { FnStmtKind } from "../ast.ts";
|
|
||||||
import {
|
|
||||||
Block,
|
|
||||||
Fn,
|
|
||||||
Local,
|
|
||||||
LocalId,
|
|
||||||
Mir,
|
|
||||||
replaceBlockSrcs,
|
|
||||||
RValue,
|
|
||||||
} from "./mir.ts";
|
|
||||||
|
|
||||||
export function eliminateTransientLocals(mir: Mir) {
|
|
||||||
for (const fn of mir.fns) {
|
|
||||||
const otherLocals = fn.locals
|
|
||||||
.slice(1 + (fn.stmt.kind as FnStmtKind).params.length)
|
|
||||||
.map((local) => local.id);
|
|
||||||
|
|
||||||
for (const block of fn.blocks) {
|
|
||||||
new EliminateTransientLocalsBlockPass(block, otherLocals)
|
|
||||||
.pass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Candidate = {
|
|
||||||
dst: LocalId;
|
|
||||||
src: LocalId;
|
|
||||||
};
|
|
||||||
|
|
||||||
class EliminateTransientLocalsBlockPass {
|
|
||||||
private candidates: Candidate[] = [];
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
private block: Block,
|
|
||||||
private readonly locals: LocalId[],
|
|
||||||
) {
|
|
||||||
}
|
|
||||||
|
|
||||||
public pass() {
|
|
||||||
this.findCandidatesInBlock(this.block);
|
|
||||||
|
|
||||||
this.candidates = this.candidates
|
|
||||||
.filter((cand) => this.locals.includes(cand.dst));
|
|
||||||
|
|
||||||
this.eliminateCandidatesInBlock(this.block);
|
|
||||||
}
|
|
||||||
|
|
||||||
private eliminateCandidatesInBlock(block: Block) {
|
|
||||||
replaceBlockSrcs(block, (src) => this.replaceMaybe(src));
|
|
||||||
}
|
|
||||||
|
|
||||||
private replaceMaybe(src: RValue): RValue {
|
|
||||||
if (src.type !== "local") {
|
|
||||||
return src;
|
|
||||||
}
|
|
||||||
const candidate = this.candidates
|
|
||||||
.find((cand) => cand.dst === src.id)?.src;
|
|
||||||
return candidate !== undefined ? { type: "local", id: candidate } : src;
|
|
||||||
}
|
|
||||||
|
|
||||||
private findCandidatesInBlock(block: Block) {
|
|
||||||
for (const op of block.ops) {
|
|
||||||
const ok = op.kind;
|
|
||||||
switch (ok.type) {
|
|
||||||
case "error":
|
|
||||||
break;
|
|
||||||
case "assign":
|
|
||||||
this.markDst(ok.dst, ok.src);
|
|
||||||
break;
|
|
||||||
case "assign_error":
|
|
||||||
case "assign_null":
|
|
||||||
case "assign_bool":
|
|
||||||
case "assign_int":
|
|
||||||
case "assign_string":
|
|
||||||
case "assign_fn":
|
|
||||||
case "field":
|
|
||||||
case "assign_field":
|
|
||||||
case "index":
|
|
||||||
case "assign_index":
|
|
||||||
case "call_val":
|
|
||||||
case "binary":
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const tk = block.ter.kind;
|
|
||||||
switch (tk.type) {
|
|
||||||
case "error":
|
|
||||||
break;
|
|
||||||
case "return":
|
|
||||||
break;
|
|
||||||
case "jump":
|
|
||||||
break;
|
|
||||||
case "if":
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private markDst(dst: LocalId, src: RValue) {
|
|
||||||
if (src.type !== "local") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.candidates = this.candidates
|
|
||||||
.filter((cand) => cand.dst !== dst);
|
|
||||||
this.candidates = this.candidates
|
|
||||||
.filter((cand) => cand.src !== dst);
|
|
||||||
this.candidates.push({ dst, src: src.id });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
import { Mir, Op, RValue, visitBlockSrcs } from "./mir.ts";
|
|
||||||
|
|
||||||
export function eliminateTransientVals(mir: Mir) {
|
|
||||||
for (const fn of mir.fns) {
|
|
||||||
for (const block of fn.blocks) {
|
|
||||||
const cands: { src: RValue; consumer: Op; definition: Op }[] = [];
|
|
||||||
|
|
||||||
visitBlockSrcs(block, (src, op, i) => {
|
|
||||||
if (src.type !== "local") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const found = block.ops.find((op, fi) =>
|
|
||||||
op.kind.type === "assign" &&
|
|
||||||
op.kind.dst === src.id &&
|
|
||||||
fi < i!
|
|
||||||
);
|
|
||||||
if (!found) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cands.push({ src, consumer: op!, definition: found! });
|
|
||||||
});
|
|
||||||
|
|
||||||
//console.log(cands);
|
|
||||||
|
|
||||||
for (const { src: oldsrc, consumer, definition } of cands) {
|
|
||||||
if (oldsrc.type !== "local") {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
if (definition.kind.type !== "assign") {
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
const src = definition.kind.src;
|
|
||||||
|
|
||||||
const k = consumer.kind;
|
|
||||||
switch (k.type) {
|
|
||||||
case "error":
|
|
||||||
break;
|
|
||||||
case "assign":
|
|
||||||
k.src = src;
|
|
||||||
break;
|
|
||||||
case "field":
|
|
||||||
if (same(k.subject, oldsrc)) {
|
|
||||||
k.subject = src;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "assign_field":
|
|
||||||
if (same(k.subject, oldsrc)) {
|
|
||||||
k.subject = src;
|
|
||||||
}
|
|
||||||
if (same(k.src, oldsrc)) {
|
|
||||||
k.src = src;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "index":
|
|
||||||
if (same(k.subject, oldsrc)) {
|
|
||||||
k.subject = src;
|
|
||||||
}
|
|
||||||
if (same(k.index, oldsrc)) {
|
|
||||||
k.index = src;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "assign_index":
|
|
||||||
if (same(k.subject, oldsrc)) {
|
|
||||||
k.subject = src;
|
|
||||||
}
|
|
||||||
if (same(k.index, oldsrc)) {
|
|
||||||
k.index = src;
|
|
||||||
}
|
|
||||||
if (same(k.src, oldsrc)) {
|
|
||||||
k.src = src;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "call_val":
|
|
||||||
if (same(k.subject, oldsrc)) {
|
|
||||||
k.subject = src;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < k.args.length; ++i) {
|
|
||||||
if (same(k.args[i], oldsrc)) {
|
|
||||||
k.args[i] = src;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "binary":
|
|
||||||
if (same(k.left, oldsrc)) {
|
|
||||||
k.left = src;
|
|
||||||
}
|
|
||||||
if (same(k.right, oldsrc)) {
|
|
||||||
k.right = src;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function same(a: RValue, b: RValue): boolean {
|
|
||||||
return a.type === "local" && a.type === b.type && a.id === b.id;
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
import { FnStmtKind } from "../ast.ts";
|
|
||||||
import { Reporter } from "../info.ts";
|
|
||||||
import {
|
|
||||||
Block,
|
|
||||||
Fn,
|
|
||||||
LocalId,
|
|
||||||
Mir,
|
|
||||||
RValue,
|
|
||||||
visitBlockDsts,
|
|
||||||
visitBlockSrcs,
|
|
||||||
} from "./mir.ts";
|
|
||||||
|
|
||||||
export function eliminateUnusedLocals(
|
|
||||||
mir: Mir,
|
|
||||||
reporter: Reporter,
|
|
||||||
isPassOne: boolean,
|
|
||||||
) {
|
|
||||||
for (const fn of mir.fns) {
|
|
||||||
new EliminateUnusedLocalsFnPass(fn, reporter, isPassOne).pass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EliminateUnusedLocalsFnPass {
|
|
||||||
private locals: LocalId[];
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
private fn: Fn,
|
|
||||||
private reporter: Reporter,
|
|
||||||
private isPassOne: boolean,
|
|
||||||
) {
|
|
||||||
this.locals = this.fn.locals
|
|
||||||
.slice(1 + (fn.stmt.kind as FnStmtKind).params.length)
|
|
||||||
.map((local) => local.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public pass() {
|
|
||||||
for (const block of this.fn.blocks) {
|
|
||||||
this.markLocalsInBlock(block);
|
|
||||||
}
|
|
||||||
for (const local of this.locals) {
|
|
||||||
for (const block of this.fn.blocks) {
|
|
||||||
this.eliminateLocalInBlock(block, local);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const id of this.locals) {
|
|
||||||
const local = this.fn.locals.find((local) => local.id === id)!;
|
|
||||||
if (local.sym?.type === "let" && this.isPassOne) {
|
|
||||||
this.reporter.reportWarning({
|
|
||||||
reporter: "analysis mf'er",
|
|
||||||
msg: `unused let symbol '${local.sym.ident}'`,
|
|
||||||
pos: local.sym.pos,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.fn.locals = this.fn.locals
|
|
||||||
.filter((local) => !this.locals.includes(local.id));
|
|
||||||
}
|
|
||||||
|
|
||||||
private eliminateLocalInBlock(block: Block, local: LocalId) {
|
|
||||||
const elimIndices: number[] = [];
|
|
||||||
visitBlockDsts(block, (dst, i) => {
|
|
||||||
if (dst === local) {
|
|
||||||
elimIndices.push(i);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
for (const i of elimIndices.toReversed()) {
|
|
||||||
block.ops.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private markLocalsInBlock(block: Block) {
|
|
||||||
visitBlockSrcs(block, (src) => this.markUsed(src));
|
|
||||||
}
|
|
||||||
|
|
||||||
private markUsed(local: RValue) {
|
|
||||||
if (local.type !== "local") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.locals = this.locals.filter((lid) => lid !== local.id);
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,17 +8,13 @@ import {
|
|||||||
Local,
|
Local,
|
||||||
LocalId,
|
LocalId,
|
||||||
Mir,
|
Mir,
|
||||||
|
Op,
|
||||||
OpKind,
|
OpKind,
|
||||||
RValue,
|
|
||||||
Ter,
|
Ter,
|
||||||
TerKind,
|
TerKind,
|
||||||
} from "./mir.ts";
|
} from "./mir.ts";
|
||||||
|
|
||||||
export function lowerAst(ast: Ast.Stmt[]): Mir {
|
export class AstLowerer {
|
||||||
return new AstLowerer(ast).lower();
|
|
||||||
}
|
|
||||||
|
|
||||||
class AstLowerer {
|
|
||||||
public constructor(private ast: Ast.Stmt[]) {}
|
public constructor(private ast: Ast.Stmt[]) {}
|
||||||
|
|
||||||
public lower(): Mir {
|
public lower(): Mir {
|
||||||
@ -33,15 +29,9 @@ class AstLowerer {
|
|||||||
class LocalAllocator {
|
class LocalAllocator {
|
||||||
private locals: Local[] = [];
|
private locals: Local[] = [];
|
||||||
|
|
||||||
public alloc(vtype: VType, sym?: Ast.Sym): LocalId {
|
public alloc(vtype: VType): LocalId {
|
||||||
const id = this.locals.length;
|
const id = this.locals.length;
|
||||||
this.locals.push({ id, mut: false, vtype, sym });
|
this.locals.push({ id, vtype });
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public allocMut(vtype: VType, sym?: Ast.Sym): LocalId {
|
|
||||||
const id = this.locals.length;
|
|
||||||
this.locals.push({ id, mut: true, vtype, sym });
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,21 +63,19 @@ class FnAstLowerer {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
const rLoc = this.locals.alloc(vtype.returnType);
|
this.locals.alloc(stmt.kind.vtype!);
|
||||||
for (const param of stmt.kind.params) {
|
for (const param of stmt.kind.params) {
|
||||||
const id = this.locals.allocMut(param.vtype!);
|
const id = this.locals.alloc(param.vtype!);
|
||||||
this.fnParamIndexLocals.set(param.index!, id);
|
this.fnParamIndexLocals.set(param.index!, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const entry = this.pushBlock();
|
this.pushBlock("entry");
|
||||||
const rVal = this.lowerBlockExpr(stmt.kind.body);
|
this.lowerBlockExpr(stmt.kind.body);
|
||||||
this.addOp({ type: "assign", dst: rLoc, src: local(rVal) });
|
this.pushBlock("exit");
|
||||||
this.setTer({ type: "return" });
|
|
||||||
const exit = this.currentBlock();
|
|
||||||
|
|
||||||
const locals = this.locals.finish();
|
const locals = this.locals.finish();
|
||||||
const blocks = this.blocks.values().toArray();
|
const blocks = this.blocks.values().toArray();
|
||||||
return { stmt, locals, blocks, entry, exit };
|
return { stmt, locals, blocks };
|
||||||
}
|
}
|
||||||
|
|
||||||
private lowerStmt(stmt: Ast.Stmt) {
|
private lowerStmt(stmt: Ast.Stmt) {
|
||||||
@ -98,12 +86,12 @@ class FnAstLowerer {
|
|||||||
case "mod":
|
case "mod":
|
||||||
break;
|
break;
|
||||||
case "break": {
|
case "break": {
|
||||||
const { local: dst, block } = this.breakStack.at(-1)!;
|
const { local, block } = this.breakStack.at(-1)!;
|
||||||
if (stmt.kind.expr) {
|
if (stmt.kind.expr) {
|
||||||
const val = this.lowerExpr(stmt.kind.expr);
|
const val = this.lowerExpr(stmt.kind.expr);
|
||||||
this.addOp({ type: "assign", dst, src: local(val) });
|
this.addOp({ type: "assign", dst: local, src: val });
|
||||||
} else {
|
} else {
|
||||||
this.addOp({ type: "assign_null", dst });
|
this.addOp({ type: "assign_null", dst: local });
|
||||||
}
|
}
|
||||||
this.setTer({ type: "jump", target: block });
|
this.setTer({ type: "jump", target: block });
|
||||||
this.pushBlock();
|
this.pushBlock();
|
||||||
@ -122,7 +110,8 @@ class FnAstLowerer {
|
|||||||
case "assign":
|
case "assign":
|
||||||
return this.lowerAssign(stmt);
|
return this.lowerAssign(stmt);
|
||||||
case "expr": {
|
case "expr": {
|
||||||
this.lowerExpr(stmt.kind.expr);
|
const val = this.lowerExpr(stmt.kind.expr);
|
||||||
|
this.addOp({ type: "drop", val });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,19 +125,19 @@ class FnAstLowerer {
|
|||||||
if (stmt.kind.assignType !== "=") {
|
if (stmt.kind.assignType !== "=") {
|
||||||
throw new Error("incomplete desugar");
|
throw new Error("incomplete desugar");
|
||||||
}
|
}
|
||||||
const src = local(this.lowerExpr(stmt.kind.value));
|
const src = this.lowerExpr(stmt.kind.value);
|
||||||
const s = stmt.kind.subject;
|
const s = stmt.kind.subject;
|
||||||
switch (s.kind.type) {
|
switch (s.kind.type) {
|
||||||
case "field": {
|
case "field": {
|
||||||
const subject = local(this.lowerExpr(s.kind.subject));
|
const subject = this.lowerExpr(s.kind.subject);
|
||||||
const ident = s.kind.ident;
|
const ident = s.kind.ident;
|
||||||
this.addOp({ type: "assign_field", subject, ident, src });
|
this.addOp({ type: "assign_field", subject, ident, src });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case "index": {
|
case "index": {
|
||||||
const subject = local(this.lowerExpr(s.kind.subject));
|
const subject = this.lowerExpr(s.kind.subject);
|
||||||
const index = local(this.lowerExpr(s.kind.value));
|
const index = this.lowerExpr(s.kind.value);
|
||||||
this.addOp({ type: "assign_index", subject, index, src });
|
this.addOp({ type: "assign_field", subject, index, src });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
case "sym": {
|
case "sym": {
|
||||||
@ -178,12 +167,9 @@ class FnAstLowerer {
|
|||||||
if (stmt.kind.type !== "let") {
|
if (stmt.kind.type !== "let") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const srcId = this.lowerExpr(stmt.kind.value);
|
const src = this.lowerExpr(stmt.kind.value);
|
||||||
const dst = this.locals.allocMut(
|
const dst = this.locals.alloc(stmt.kind.param.vtype!);
|
||||||
stmt.kind.param.vtype!,
|
this.addOp({ type: "assign", dst, src });
|
||||||
stmt.kind.param.sym!,
|
|
||||||
);
|
|
||||||
this.addOp({ type: "assign", dst, src: local(srcId) });
|
|
||||||
this.letStmtIdLocals.set(stmt.id, dst);
|
this.letStmtIdLocals.set(stmt.id, dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,34 +177,30 @@ class FnAstLowerer {
|
|||||||
switch (expr.kind.type) {
|
switch (expr.kind.type) {
|
||||||
case "error": {
|
case "error": {
|
||||||
const dst = this.locals.alloc({ type: "error" });
|
const dst = this.locals.alloc({ type: "error" });
|
||||||
this.addOp({ type: "assign", dst, src: { type: "error" } });
|
this.addOp({ type: "assign_error", dst });
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
case "null": {
|
case "null": {
|
||||||
const dst = this.locals.alloc({ type: "null" });
|
const dst = this.locals.alloc({ type: "null" });
|
||||||
this.addOp({ type: "assign", dst, src: { type: "null" } });
|
this.addOp({ type: "assign_null", dst });
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
case "bool": {
|
case "bool": {
|
||||||
const val = expr.kind.value;
|
const val = expr.kind.value;
|
||||||
const dst = this.locals.alloc({ type: "bool" });
|
const dst = this.locals.alloc({ type: "bool" });
|
||||||
this.addOp({ type: "assign", dst, src: { type: "bool", val } });
|
this.addOp({ type: "assign_bool", dst, val });
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
case "int": {
|
case "int": {
|
||||||
const val = expr.kind.value;
|
const val = expr.kind.value;
|
||||||
const dst = this.locals.alloc({ type: "int" });
|
const dst = this.locals.alloc({ type: "int" });
|
||||||
this.addOp({ type: "assign", dst, src: { type: "int", val } });
|
this.addOp({ type: "assign_int", dst, val });
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
case "string": {
|
case "string": {
|
||||||
const val = expr.kind.value;
|
const val = expr.kind.value;
|
||||||
const dst = this.locals.alloc({ type: "string" });
|
const dst = this.locals.alloc({ type: "string" });
|
||||||
this.addOp({
|
this.addOp({ type: "assign_string", dst, val });
|
||||||
type: "assign",
|
|
||||||
dst,
|
|
||||||
src: { type: "string", val },
|
|
||||||
});
|
|
||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
case "ident":
|
case "ident":
|
||||||
@ -292,7 +274,7 @@ class FnAstLowerer {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const ident = expr.kind.ident;
|
const ident = expr.kind.ident;
|
||||||
const subject = local(this.lowerExpr(expr.kind.subject));
|
const subject = this.lowerExpr(expr.kind.subject);
|
||||||
|
|
||||||
const subjectVType = expr.kind.subject.vtype!;
|
const subjectVType = expr.kind.subject.vtype!;
|
||||||
if (subjectVType.type !== "struct") {
|
if (subjectVType.type !== "struct") {
|
||||||
@ -314,8 +296,8 @@ class FnAstLowerer {
|
|||||||
if (expr.kind.type !== "index") {
|
if (expr.kind.type !== "index") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const subject = local(this.lowerExpr(expr.kind.subject));
|
const subject = this.lowerExpr(expr.kind.subject);
|
||||||
const index = local(this.lowerExpr(expr.kind.value));
|
const index = this.lowerExpr(expr.kind.value);
|
||||||
|
|
||||||
const dstVType = ((): VType => {
|
const dstVType = ((): VType => {
|
||||||
const outer = expr.kind.subject.vtype!;
|
const outer = expr.kind.subject.vtype!;
|
||||||
@ -338,9 +320,9 @@ class FnAstLowerer {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
|
|
||||||
const args = expr.kind.args.map((arg) => local(this.lowerExpr(arg)));
|
const args = expr.kind.args.map((arg) => this.lowerExpr(arg));
|
||||||
|
|
||||||
const subject = local(this.lowerExpr(expr.kind.subject));
|
const subject = this.lowerExpr(expr.kind.subject);
|
||||||
|
|
||||||
const subjectVType = expr.kind.subject.vtype!;
|
const subjectVType = expr.kind.subject.vtype!;
|
||||||
if (subjectVType.type !== "fn") {
|
if (subjectVType.type !== "fn") {
|
||||||
@ -364,8 +346,8 @@ class FnAstLowerer {
|
|||||||
//const vtype = leftVType.type === "error" && rightVType || leftVType;
|
//const vtype = leftVType.type === "error" && rightVType || leftVType;
|
||||||
|
|
||||||
const binaryType = expr.kind.binaryType;
|
const binaryType = expr.kind.binaryType;
|
||||||
const left = local(this.lowerExpr(expr.kind.left));
|
const left = this.lowerExpr(expr.kind.left);
|
||||||
const right = local(this.lowerExpr(expr.kind.right));
|
const right = this.lowerExpr(expr.kind.right);
|
||||||
|
|
||||||
const dst = this.locals.alloc(expr.vtype!);
|
const dst = this.locals.alloc(expr.vtype!);
|
||||||
|
|
||||||
@ -382,19 +364,19 @@ class FnAstLowerer {
|
|||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const condBlock = this.currentBlock();
|
const condBlock = this.currentBlock();
|
||||||
const cond = local(this.lowerExpr(expr.kind.cond));
|
const cond = this.lowerExpr(expr.kind.cond);
|
||||||
const end = this.reserveBlock();
|
const end = this.reserveBlock();
|
||||||
|
|
||||||
const val = this.locals.alloc(expr.vtype!);
|
const val = this.locals.alloc(expr.vtype!);
|
||||||
|
|
||||||
const truthy = this.pushBlock();
|
const truthy = this.pushBlock();
|
||||||
const truthyVal = local(this.lowerExpr(expr.kind.truthy));
|
const truthyVal = this.lowerExpr(expr.kind.truthy);
|
||||||
this.addOp({ type: "assign", dst: val, src: truthyVal });
|
this.addOp({ type: "assign", dst: val, src: truthyVal });
|
||||||
this.setTer({ type: "jump", target: end });
|
this.setTer({ type: "jump", target: end });
|
||||||
|
|
||||||
if (expr.kind.falsy) {
|
if (expr.kind.falsy) {
|
||||||
const falsy = this.pushBlock();
|
const falsy = this.pushBlock();
|
||||||
const falsyVal = local(this.lowerExpr(expr.kind.falsy));
|
const falsyVal = this.lowerExpr(expr.kind.falsy);
|
||||||
this.addOp({ type: "assign", dst: val, src: falsyVal });
|
this.addOp({ type: "assign", dst: val, src: falsyVal });
|
||||||
this.setTer({ type: "jump", target: end });
|
this.setTer({ type: "jump", target: end });
|
||||||
|
|
||||||
@ -403,8 +385,6 @@ class FnAstLowerer {
|
|||||||
this.setTerOn(condBlock, { type: "if", cond, truthy, falsy: end });
|
this.setTerOn(condBlock, { type: "if", cond, truthy, falsy: end });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pushBlockWithId(end);
|
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,9 +397,8 @@ class FnAstLowerer {
|
|||||||
const breakBlock = this.reserveBlock();
|
const breakBlock = this.reserveBlock();
|
||||||
this.breakStack.push({ local: val, block: breakBlock });
|
this.breakStack.push({ local: val, block: breakBlock });
|
||||||
|
|
||||||
const before = this.currentBlock();
|
|
||||||
const body = this.pushBlock();
|
const body = this.pushBlock();
|
||||||
this.setTerOn(before, { type: "jump", target: body });
|
this.setTer({ type: "jump", target: body });
|
||||||
|
|
||||||
this.lowerExpr(expr.kind.body);
|
this.lowerExpr(expr.kind.body);
|
||||||
this.setTer({ type: "jump", target: body });
|
this.setTer({ type: "jump", target: body });
|
||||||
@ -478,18 +457,13 @@ class FnAstLowerer {
|
|||||||
this.blockIdCounter += 1;
|
this.blockIdCounter += 1;
|
||||||
const ter: Ter = { kind: { type: "error" } };
|
const ter: Ter = { kind: { type: "error" } };
|
||||||
this.blocks.set(id, { id, ops: [], ter, label });
|
this.blocks.set(id, { id, ops: [], ter, label });
|
||||||
this.currentBlockId = id;
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
private pushBlockWithId(id: BlockId): BlockId {
|
private pushBlockWithId(id: BlockId): BlockId {
|
||||||
|
this.blockIdCounter += 1;
|
||||||
const ter: Ter = { kind: { type: "error" } };
|
const ter: Ter = { kind: { type: "error" } };
|
||||||
this.blocks.set(id, { id, ops: [], ter });
|
this.blocks.set(id, { id, ops: [], ter });
|
||||||
this.currentBlockId = id;
|
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function local(id: LocalId): RValue {
|
|
||||||
return { type: "local", id };
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { BinaryType, Stmt, Sym } from "../ast.ts";
|
import { BinaryType, Stmt } from "../ast.ts";
|
||||||
import { VType, vtypeToString } from "../vtype.ts";
|
import { VType, vtypeToString } from "../vtype.ts";
|
||||||
|
|
||||||
export type Mir = {
|
export type Mir = {
|
||||||
@ -9,17 +9,13 @@ export type Fn = {
|
|||||||
stmt: Stmt;
|
stmt: Stmt;
|
||||||
locals: Local[];
|
locals: Local[];
|
||||||
blocks: Block[];
|
blocks: Block[];
|
||||||
entry: BlockId;
|
|
||||||
exit: BlockId;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LocalId = number;
|
export type LocalId = number;
|
||||||
|
|
||||||
export type Local = {
|
export type Local = {
|
||||||
id: LocalId;
|
id: LocalId;
|
||||||
mut: boolean;
|
|
||||||
vtype: VType;
|
vtype: VType;
|
||||||
sym?: Sym;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BlockId = number;
|
export type BlockId = number;
|
||||||
@ -36,17 +32,24 @@ export type Op = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type L = LocalId;
|
type L = LocalId;
|
||||||
type R = RValue;
|
|
||||||
|
|
||||||
export type OpKind =
|
export type OpKind =
|
||||||
| { type: "error" }
|
| { type: "error" }
|
||||||
| { type: "assign"; dst: L; src: R }
|
| { type: "return" }
|
||||||
| { type: "field"; dst: L; subject: R; ident: string }
|
| { type: "drop"; val: L }
|
||||||
| { type: "assign_field"; subject: R; ident: string; src: R }
|
| { type: "assign"; dst: L; src: L }
|
||||||
| { type: "index"; dst: L; subject: R; index: R }
|
| { type: "assign_error"; dst: L }
|
||||||
| { type: "assign_index"; subject: R; index: R; src: R }
|
| { type: "assign_null"; dst: L }
|
||||||
| { type: "call_val"; dst: L; subject: R; args: R[] }
|
| { type: "assign_bool"; dst: L; val: boolean }
|
||||||
| { type: "binary"; binaryType: BinaryType; dst: L; left: R; right: R };
|
| { type: "assign_int"; dst: L; val: number }
|
||||||
|
| { type: "assign_string"; dst: L; val: string }
|
||||||
|
| { type: "assign_fn"; dst: L; stmt: Stmt }
|
||||||
|
| { type: "field"; dst: L; subject: L; ident: string }
|
||||||
|
| { type: "assign_field"; subject: L; ident: string; src: L }
|
||||||
|
| { type: "index"; dst: L; subject: L; index: number }
|
||||||
|
| { type: "assign_field"; subject: L; index: L; src: L }
|
||||||
|
| { type: "call_val"; dst: L; subject: L; args: L[] }
|
||||||
|
| { type: "binary"; binaryType: BinaryType; dst: L; left: L; right: L };
|
||||||
|
|
||||||
export type Ter = {
|
export type Ter = {
|
||||||
kind: TerKind;
|
kind: TerKind;
|
||||||
@ -54,158 +57,8 @@ export type Ter = {
|
|||||||
|
|
||||||
export type TerKind =
|
export type TerKind =
|
||||||
| { type: "error" }
|
| { type: "error" }
|
||||||
| { type: "return" }
|
|
||||||
| { type: "jump"; target: BlockId }
|
| { type: "jump"; target: BlockId }
|
||||||
| { type: "if"; cond: R; truthy: BlockId; falsy: BlockId };
|
| { type: "if"; cond: L; truthy: BlockId; falsy: BlockId };
|
||||||
|
|
||||||
export type RValue =
|
|
||||||
| { type: "error" }
|
|
||||||
| { type: "local"; id: BlockId }
|
|
||||||
| { type: "null" }
|
|
||||||
| { type: "bool"; val: boolean }
|
|
||||||
| { type: "int"; val: number }
|
|
||||||
| { type: "string"; val: string }
|
|
||||||
| { type: "fn"; stmt: Stmt };
|
|
||||||
|
|
||||||
export function visitBlockDsts(
|
|
||||||
block: Block,
|
|
||||||
visit: (local: LocalId, index: number) => void,
|
|
||||||
) {
|
|
||||||
for (const [op, i] of block.ops.map((v, i) => [v, i] as const)) {
|
|
||||||
const ok = op.kind;
|
|
||||||
switch (ok.type) {
|
|
||||||
case "error":
|
|
||||||
break;
|
|
||||||
case "assign":
|
|
||||||
case "field":
|
|
||||||
case "index":
|
|
||||||
case "call_val":
|
|
||||||
case "binary":
|
|
||||||
visit(ok.dst, i);
|
|
||||||
break;
|
|
||||||
case "assign_field":
|
|
||||||
case "assign_index":
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function replaceBlockSrcs(
|
|
||||||
block: Block,
|
|
||||||
replace: (src: RValue) => RValue,
|
|
||||||
) {
|
|
||||||
for (const op of block.ops) {
|
|
||||||
const ok = op.kind;
|
|
||||||
switch (ok.type) {
|
|
||||||
case "error":
|
|
||||||
break;
|
|
||||||
case "assign":
|
|
||||||
ok.src = replace(ok.src);
|
|
||||||
break;
|
|
||||||
case "field":
|
|
||||||
ok.subject = replace(ok.subject);
|
|
||||||
break;
|
|
||||||
case "assign_field":
|
|
||||||
ok.subject = replace(ok.subject);
|
|
||||||
ok.src = replace(ok.src);
|
|
||||||
break;
|
|
||||||
case "index":
|
|
||||||
ok.subject = replace(ok.subject);
|
|
||||||
ok.index = replace(ok.index);
|
|
||||||
break;
|
|
||||||
case "assign_index":
|
|
||||||
ok.subject = replace(ok.subject);
|
|
||||||
ok.index = replace(ok.index);
|
|
||||||
ok.src = replace(ok.src);
|
|
||||||
break;
|
|
||||||
case "call_val":
|
|
||||||
ok.subject = replace(ok.subject);
|
|
||||||
ok.args = ok.args.map((arg) => replace(arg));
|
|
||||||
break;
|
|
||||||
case "binary":
|
|
||||||
ok.left = replace(ok.left);
|
|
||||||
ok.right = replace(ok.right);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const tk = block.ter.kind;
|
|
||||||
switch (tk.type) {
|
|
||||||
case "error":
|
|
||||||
break;
|
|
||||||
case "return":
|
|
||||||
break;
|
|
||||||
case "jump":
|
|
||||||
break;
|
|
||||||
case "if":
|
|
||||||
tk.cond = replace(tk.cond);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function visitBlockSrcs(
|
|
||||||
block: Block,
|
|
||||||
visitor: (src: RValue, op?: Op, index?: number, ter?: Ter) => void,
|
|
||||||
) {
|
|
||||||
for (const [op, i] of block.ops.map((v, i) => [v, i] as const)) {
|
|
||||||
const ok = op.kind;
|
|
||||||
switch (ok.type) {
|
|
||||||
case "error":
|
|
||||||
break;
|
|
||||||
case "assign":
|
|
||||||
visitor(ok.src, op, i);
|
|
||||||
break;
|
|
||||||
case "field":
|
|
||||||
visitor(ok.subject, op, i);
|
|
||||||
break;
|
|
||||||
case "assign_field":
|
|
||||||
visitor(ok.subject, op, i);
|
|
||||||
visitor(ok.src, op, i);
|
|
||||||
break;
|
|
||||||
case "index":
|
|
||||||
visitor(ok.subject, op, i);
|
|
||||||
visitor(ok.index, op, i);
|
|
||||||
break;
|
|
||||||
case "assign_index":
|
|
||||||
visitor(ok.subject, op, i);
|
|
||||||
visitor(ok.index, op, i);
|
|
||||||
visitor(ok.src, op, i);
|
|
||||||
break;
|
|
||||||
case "call_val":
|
|
||||||
visitor(ok.subject, op, i);
|
|
||||||
ok.args.map((arg) => visitor(arg, op, i));
|
|
||||||
break;
|
|
||||||
case "binary":
|
|
||||||
visitor(ok.left, op, i);
|
|
||||||
visitor(ok.right, op, i);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const tk = block.ter.kind;
|
|
||||||
switch (tk.type) {
|
|
||||||
case "error":
|
|
||||||
break;
|
|
||||||
case "return":
|
|
||||||
break;
|
|
||||||
case "jump":
|
|
||||||
break;
|
|
||||||
case "if":
|
|
||||||
visitor(tk.cond, undefined, undefined, block.ter);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mirOpCount(mir: Mir): number {
|
|
||||||
return mir.fns
|
|
||||||
.reduce((acc, fn) =>
|
|
||||||
acc + fn.blocks
|
|
||||||
.reduce((acc, block) => acc + block.ops.length + 1, 0), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function printMir(mir: Mir) {
|
export function printMir(mir: Mir) {
|
||||||
for (const fn of mir.fns) {
|
for (const fn of mir.fns) {
|
||||||
@ -213,7 +66,7 @@ export function printMir(mir: Mir) {
|
|||||||
if (stmt.kind.type !== "fn") {
|
if (stmt.kind.type !== "fn") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
const name = stmt.kind.sym!.fullPath;
|
const name = stmt.kind.ident;
|
||||||
|
|
||||||
const vtype = stmt.kind.vtype;
|
const vtype = stmt.kind.vtype;
|
||||||
if (vtype?.type !== "fn") {
|
if (vtype?.type !== "fn") {
|
||||||
@ -222,106 +75,78 @@ export function printMir(mir: Mir) {
|
|||||||
const generics = vtype.genericParams
|
const generics = vtype.genericParams
|
||||||
?.map(({ ident }) => `${ident}`).join(", ") ?? "";
|
?.map(({ ident }) => `${ident}`).join(", ") ?? "";
|
||||||
const params = vtype.params
|
const params = vtype.params
|
||||||
.map(({ mut, vtype }, i) =>
|
.map(({ vtype }, i) =>
|
||||||
`${mut && "mut" || ""} _${fn.locals[i + 1].id}: ${
|
`_${fn.locals[i + 1].id}: ${vtypeToString(vtype)}`
|
||||||
vtypeToString(vtype)
|
|
||||||
}`
|
|
||||||
)
|
)
|
||||||
.join(", ");
|
.join(", ");
|
||||||
const returnType = vtypeToString(vtype.returnType);
|
const returnType = vtypeToString(vtype.returnType);
|
||||||
console.log(`${name}${generics}(${params}) -> ${returnType} {`);
|
console.log(`${name}${generics}(${params}) -> ${returnType}:`);
|
||||||
|
for (const { id, vtype } of fn.locals) {
|
||||||
const paramIndices = vtype.params.map((_v, i) => i + 1);
|
console.log(` let _${id}: ${vtypeToString(vtype)};`);
|
||||||
for (
|
|
||||||
const { id, vtype, mut } of fn.locals
|
|
||||||
.filter((_v, i) => !paramIndices.includes(i))
|
|
||||||
) {
|
|
||||||
const m = mut ? "mut" : "";
|
|
||||||
const v = vtypeToString(vtype);
|
|
||||||
console.log(` let ${m} _${id}: ${v};`);
|
|
||||||
}
|
}
|
||||||
for (const block of fn.blocks) {
|
for (const block of fn.blocks) {
|
||||||
const l = (msg: string) => console.log(` ${msg}`);
|
console.log(`.${block.label ?? block.id}:`);
|
||||||
const r = rvalueToString;
|
|
||||||
|
|
||||||
console.log(` ${block.label ?? "bb" + block.id}: {`);
|
|
||||||
for (const op of block.ops) {
|
for (const op of block.ops) {
|
||||||
const k = op.kind;
|
const k = op.kind;
|
||||||
switch (k.type) {
|
switch (k.type) {
|
||||||
case "error":
|
case "error":
|
||||||
l(`<error>;`);
|
console.log(` <error>;`);
|
||||||
break;
|
|
||||||
case "assign":
|
|
||||||
l(`_${k.dst} = ${r(k.src)};`);
|
|
||||||
break;
|
|
||||||
case "field":
|
|
||||||
l(`_${k.dst} = ${r(k.subject)}.${k.ident};`);
|
|
||||||
break;
|
|
||||||
case "assign_field":
|
|
||||||
l(`${r(k.subject)}.${k.ident} = ${r(k.src)};`);
|
|
||||||
break;
|
|
||||||
case "index":
|
|
||||||
l(`_${k.dst} = ${r(k.subject)}[${r(k.index)}];`);
|
|
||||||
break;
|
|
||||||
case "assign_index":
|
|
||||||
l(`${r(k.subject)}[${r(k.index)}] = ${r(k.src)};`);
|
|
||||||
break;
|
|
||||||
case "call_val": {
|
|
||||||
const args = k.args.map((arg) => r(arg)).join(", ");
|
|
||||||
l(`_${k.dst} = call ${r(k.subject)}(${args});`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "binary": {
|
|
||||||
l(`_${k.dst} = ${r(k.left)} ${k.binaryType} ${
|
|
||||||
r(k.right)
|
|
||||||
};`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const tk = block.ter.kind;
|
|
||||||
switch (tk.type) {
|
|
||||||
case "error":
|
|
||||||
l(`<error>;`);
|
|
||||||
break;
|
break;
|
||||||
case "return":
|
case "return":
|
||||||
l(`return;`);
|
console.log(` return;`);
|
||||||
break;
|
break;
|
||||||
case "jump":
|
case "drop":
|
||||||
l(`jump bb${tk.target};`);
|
console.log(` drop _${k.val};`);
|
||||||
break;
|
break;
|
||||||
case "if":
|
case "assign":
|
||||||
l(`if ${
|
console.log(` _${k.dst} = _${k.src};`);
|
||||||
r(tk.cond)
|
|
||||||
}, true: bb${tk.truthy}, false: bb${tk.falsy};`);
|
|
||||||
break;
|
break;
|
||||||
}
|
case "assign_error":
|
||||||
console.log(" }");
|
console.log(` _${k.dst} = <error>;`);
|
||||||
}
|
break;
|
||||||
console.log("}");
|
case "assign_null":
|
||||||
}
|
console.log(` _${k.dst} = null;`);
|
||||||
}
|
break;
|
||||||
|
case "assign_bool":
|
||||||
export function rvalueToString(rvalue: RValue): string {
|
console.log(` _${k.dst} = ${k.val};`);
|
||||||
switch (rvalue.type) {
|
break;
|
||||||
case "error":
|
case "assign_int":
|
||||||
return `<error>`;
|
console.log(` _${k.dst} = ${k.val};`);
|
||||||
case "local":
|
break;
|
||||||
return `_${rvalue.id}`;
|
case "assign_string":
|
||||||
case "null":
|
console.log(` _${k.dst} = "${k.val}";`);
|
||||||
return "null";
|
break;
|
||||||
case "bool":
|
case "assign_fn": {
|
||||||
return `${rvalue.val}`;
|
const stmt = k.stmt;
|
||||||
case "int":
|
|
||||||
return `${rvalue.val}`;
|
|
||||||
case "string":
|
|
||||||
return `"${rvalue.val}"`;
|
|
||||||
case "fn": {
|
|
||||||
const stmt = rvalue.stmt;
|
|
||||||
if (stmt.kind.type !== "fn") {
|
if (stmt.kind.type !== "fn") {
|
||||||
throw new Error();
|
throw new Error();
|
||||||
}
|
}
|
||||||
return stmt.kind.sym!.fullPath;
|
console.log(` _${k.dst} = ${stmt.kind.ident};`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "field":
|
||||||
|
console.log(
|
||||||
|
` _${k.dst} = _${k.subject}.${k.ident};`,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "index":
|
||||||
|
console.log(
|
||||||
|
` _${k.dst} = _${k.subject}[_${k.index}];`,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case "call_val": {
|
||||||
|
const args = k.args.map((arg) => `_${arg}`).join(", ");
|
||||||
|
console.log(` _${k.dst} = _${k.subject}(${args});`);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "binary": {
|
||||||
|
console.log(
|
||||||
|
` _${k.dst} = _${k.left} ${k.binaryType} _${k.right};`,
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -365,7 +365,6 @@ export class Parser {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
elems.push(elemRes.value);
|
elems.push(elemRes.value);
|
||||||
i += 1;
|
|
||||||
while (this.test(delimiter)) {
|
while (this.test(delimiter)) {
|
||||||
this.step();
|
this.step();
|
||||||
if (this.test(endToken)) {
|
if (this.test(endToken)) {
|
||||||
@ -376,7 +375,6 @@ export class Parser {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
elems.push(elemRes.value);
|
elems.push(elemRes.value);
|
||||||
i += 1;
|
|
||||||
}
|
}
|
||||||
if (!this.test(endToken)) {
|
if (!this.test(endToken)) {
|
||||||
this.report(`expected '${endToken}'`);
|
this.report(`expected '${endToken}'`);
|
||||||
|
@ -23,7 +23,7 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public resolve(stmts: Stmt[]): VisitRes {
|
public resolve(stmts: Stmt[]): VisitRes {
|
||||||
const syms = new EntryModSyms("root");
|
const syms = new EntryModSyms();
|
||||||
this.scout(stmts, syms);
|
this.scout(stmts, syms);
|
||||||
visitStmts(stmts, this, syms);
|
visitStmts(stmts, this, syms);
|
||||||
return "stop";
|
return "stop";
|
||||||
@ -37,10 +37,9 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const ident = stmt.kind.ident;
|
const ident = stmt.kind.ident;
|
||||||
stmt.kind.sym = syms.define(ident, {
|
syms.define(ident, {
|
||||||
ident: stmt.kind.ident,
|
ident: stmt.kind.ident,
|
||||||
type: "fn",
|
type: "fn",
|
||||||
fullPath: `${syms.pathString()}::${ident}`,
|
|
||||||
pos: stmt.pos,
|
pos: stmt.pos,
|
||||||
stmt,
|
stmt,
|
||||||
});
|
});
|
||||||
@ -53,7 +52,6 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
syms.define(ident, {
|
syms.define(ident, {
|
||||||
ident,
|
ident,
|
||||||
type: "type_alias",
|
type: "type_alias",
|
||||||
fullPath: `${syms.pathString()}::${ident}`,
|
|
||||||
pos: stmt.kind.param.pos,
|
pos: stmt.kind.param.pos,
|
||||||
stmt,
|
stmt,
|
||||||
param: stmt.kind.param,
|
param: stmt.kind.param,
|
||||||
@ -66,7 +64,7 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
if (stmt.kind.type !== "mod") {
|
if (stmt.kind.type !== "mod") {
|
||||||
throw new Error("expected let statement");
|
throw new Error("expected let statement");
|
||||||
}
|
}
|
||||||
const modSyms = new ModSyms(syms, stmt.kind.ident);
|
const modSyms = new ModSyms(syms);
|
||||||
const { mod, ident } = stmt.kind;
|
const { mod, ident } = stmt.kind;
|
||||||
this.scout(mod.ast, modSyms);
|
this.scout(mod.ast, modSyms);
|
||||||
visitStmts(mod.ast, this, modSyms);
|
visitStmts(mod.ast, this, modSyms);
|
||||||
@ -78,7 +76,6 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
syms.define(ident, {
|
syms.define(ident, {
|
||||||
type: "mod",
|
type: "mod",
|
||||||
ident,
|
ident,
|
||||||
fullPath: `${syms.pathString()}::${ident}`,
|
|
||||||
pos: stmt.pos,
|
pos: stmt.pos,
|
||||||
syms: modSyms,
|
syms: modSyms,
|
||||||
});
|
});
|
||||||
@ -96,10 +93,9 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
this.reportAlreadyDefined(ident, stmt.pos, syms);
|
this.reportAlreadyDefined(ident, stmt.pos, syms);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stmt.kind.param.sym = syms.define(ident, {
|
syms.define(ident, {
|
||||||
ident,
|
ident,
|
||||||
type: "let",
|
type: "let",
|
||||||
fullPath: ident,
|
|
||||||
pos: stmt.kind.param.pos,
|
pos: stmt.kind.param.pos,
|
||||||
stmt,
|
stmt,
|
||||||
param: stmt.kind.param,
|
param: stmt.kind.param,
|
||||||
@ -127,7 +123,6 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
fnScopeSyms.define(param.ident, {
|
fnScopeSyms.define(param.ident, {
|
||||||
ident: param.ident,
|
ident: param.ident,
|
||||||
type: "generic",
|
type: "generic",
|
||||||
fullPath: param.ident,
|
|
||||||
pos: param.pos,
|
pos: param.pos,
|
||||||
stmt,
|
stmt,
|
||||||
genericParam: param,
|
genericParam: param,
|
||||||
@ -142,7 +137,6 @@ export class Resolver implements AstVisitor<[Syms]> {
|
|||||||
fnScopeSyms.define(param.ident, {
|
fnScopeSyms.define(param.ident, {
|
||||||
ident: param.ident,
|
ident: param.ident,
|
||||||
type: "fn_param",
|
type: "fn_param",
|
||||||
fullPath: param.ident,
|
|
||||||
pos: param.pos,
|
pos: param.pos,
|
||||||
param,
|
param,
|
||||||
});
|
});
|
||||||
|
@ -5,28 +5,25 @@ export type SymMap = { [ident: string]: Sym };
|
|||||||
type GetRes = { ok: true; sym: Sym } | { ok: false };
|
type GetRes = { ok: true; sym: Sym } | { ok: false };
|
||||||
|
|
||||||
export interface Syms {
|
export interface Syms {
|
||||||
define(ident: string, sym: Sym): Sym;
|
define(ident: string, sym: Sym): void;
|
||||||
definedLocally(ident: string): boolean;
|
definedLocally(ident: string): boolean;
|
||||||
get(ident: string): GetRes;
|
get(ident: string): GetRes;
|
||||||
getPub(ident: string): GetRes;
|
|
||||||
rootMod(): Sym;
|
|
||||||
pathString(): string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EntryModSyms implements Syms {
|
export class EntryModSyms implements Syms {
|
||||||
private syms: SymMap = {};
|
private syms: SymMap = {};
|
||||||
|
|
||||||
public constructor(private modName: string) {}
|
public constructor() {}
|
||||||
|
|
||||||
public define(ident: string, sym: Sym): Sym {
|
public define(ident: string, sym: Sym) {
|
||||||
if (sym.type === "let") {
|
if (sym.type === "let") {
|
||||||
return this.define(ident, {
|
this.define(ident, {
|
||||||
...sym,
|
...sym,
|
||||||
type: "let_static",
|
type: "let_static",
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
this.syms[ident] = sym;
|
this.syms[ident] = sym;
|
||||||
return sym;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public definedLocally(ident: string): boolean {
|
public definedLocally(ident: string): boolean {
|
||||||
@ -39,48 +36,28 @@ export class EntryModSyms implements Syms {
|
|||||||
}
|
}
|
||||||
return { ok: false };
|
return { ok: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPub(ident: string): GetRes {
|
|
||||||
if (ident in this.syms) {
|
|
||||||
return { ok: true, sym: this.syms[ident] };
|
|
||||||
}
|
|
||||||
return { ok: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
public rootMod(): Sym {
|
|
||||||
return {
|
|
||||||
type: "mod",
|
|
||||||
ident: this.modName,
|
|
||||||
fullPath: this.modName,
|
|
||||||
syms: this,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public pathString(): string {
|
|
||||||
return this.modName;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ModSyms implements Syms {
|
export class ModSyms implements Syms {
|
||||||
private syms: SymMap = {};
|
private syms: SymMap = {};
|
||||||
|
|
||||||
public constructor(private parent: Syms, private modName: string) {
|
public constructor(private parent: Syms) {
|
||||||
this.syms["super"] = {
|
this.syms["super"] = {
|
||||||
type: "mod",
|
type: "mod",
|
||||||
ident: "super",
|
ident: "super",
|
||||||
fullPath: this.pathString(),
|
|
||||||
syms: this.parent,
|
syms: this.parent,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public define(ident: string, sym: Sym): Sym {
|
public define(ident: string, sym: Sym) {
|
||||||
if (sym.type === "let") {
|
if (sym.type === "let") {
|
||||||
return this.define(ident, {
|
this.define(ident, {
|
||||||
...sym,
|
...sym,
|
||||||
type: "let_static",
|
type: "let_static",
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return this.syms[ident] = sym;
|
this.syms[ident] = sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
public definedLocally(ident: string): boolean {
|
public definedLocally(ident: string): boolean {
|
||||||
@ -93,21 +70,6 @@ export class ModSyms implements Syms {
|
|||||||
}
|
}
|
||||||
return { ok: false };
|
return { ok: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPub(ident: string): GetRes {
|
|
||||||
if (ident in this.syms) {
|
|
||||||
return { ok: true, sym: this.syms[ident] };
|
|
||||||
}
|
|
||||||
return { ok: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
public rootMod(): Sym {
|
|
||||||
return this.parent.rootMod();
|
|
||||||
}
|
|
||||||
|
|
||||||
public pathString(): string {
|
|
||||||
return `${this.parent.pathString()}::${this.modName}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FnSyms implements Syms {
|
export class FnSyms implements Syms {
|
||||||
@ -115,16 +77,16 @@ export class FnSyms implements Syms {
|
|||||||
|
|
||||||
public constructor(private parent: Syms) {}
|
public constructor(private parent: Syms) {}
|
||||||
|
|
||||||
public define(ident: string, sym: Sym): Sym {
|
public define(ident: string, sym: Sym) {
|
||||||
if (sym.type === "let") {
|
if (sym.type === "let") {
|
||||||
return this.define(ident, {
|
this.define(ident, {
|
||||||
...sym,
|
...sym,
|
||||||
type: "closure",
|
type: "closure",
|
||||||
inner: sym,
|
inner: sym,
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
this.syms[ident] = sym;
|
this.syms[ident] = sym;
|
||||||
return sym;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public definedLocally(ident: string): boolean {
|
public definedLocally(ident: string): boolean {
|
||||||
@ -137,21 +99,6 @@ export class FnSyms implements Syms {
|
|||||||
}
|
}
|
||||||
return this.parent.get(ident);
|
return this.parent.get(ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPub(ident: string): GetRes {
|
|
||||||
if (ident in this.syms) {
|
|
||||||
return { ok: true, sym: this.syms[ident] };
|
|
||||||
}
|
|
||||||
return { ok: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
public rootMod(): Sym {
|
|
||||||
return this.parent.rootMod();
|
|
||||||
}
|
|
||||||
|
|
||||||
public pathString(): string {
|
|
||||||
return this.parent.pathString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LeafSyms implements Syms {
|
export class LeafSyms implements Syms {
|
||||||
@ -159,9 +106,8 @@ export class LeafSyms implements Syms {
|
|||||||
|
|
||||||
public constructor(private parent: Syms) {}
|
public constructor(private parent: Syms) {}
|
||||||
|
|
||||||
public define(ident: string, sym: Sym): Sym {
|
public define(ident: string, sym: Sym) {
|
||||||
this.syms[ident] = sym;
|
this.syms[ident] = sym;
|
||||||
return sym;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public definedLocally(ident: string): boolean {
|
public definedLocally(ident: string): boolean {
|
||||||
@ -174,19 +120,4 @@ export class LeafSyms implements Syms {
|
|||||||
}
|
}
|
||||||
return this.parent.get(ident);
|
return this.parent.get(ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getPub(ident: string): GetRes {
|
|
||||||
if (ident in this.syms) {
|
|
||||||
return { ok: true, sym: this.syms[ident] };
|
|
||||||
}
|
|
||||||
return { ok: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
public rootMod(): Sym {
|
|
||||||
return this.parent.rootMod();
|
|
||||||
}
|
|
||||||
|
|
||||||
public pathString(): string {
|
|
||||||
return this.parent.pathString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ export type VType =
|
|||||||
|
|
||||||
export type VTypeParam = {
|
export type VTypeParam = {
|
||||||
ident: string;
|
ident: string;
|
||||||
mut: boolean;
|
|
||||||
vtype: VType;
|
vtype: VType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
// mod std;
|
|
||||||
|
|
||||||
fn black_box(v: int) { }
|
|
||||||
|
|
||||||
fn add(a: int, b: int) -> int {
|
|
||||||
let s = a + b;
|
|
||||||
if false {}
|
|
||||||
s
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let a = 5;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
a = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
let b = a;
|
|
||||||
let c = b;
|
|
||||||
|
|
||||||
black_box(b);
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
// mod std;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let a = 5;
|
|
||||||
let b = a;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user