Compare commits
	
		
			3 Commits
		
	
	
		
			76fff577b1
			...
			99532d37d6
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 99532d37d6 | |||
| 8367399a2c | |||
| 6351bc1e71 | 
@ -21,20 +21,23 @@ 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: "fn";
 | 
					 | 
				
			||||||
        ident: string;
 | 
					 | 
				
			||||||
        genericParams?: GenericParam[];
 | 
					 | 
				
			||||||
        params: Param[];
 | 
					 | 
				
			||||||
        returnType?: EType;
 | 
					 | 
				
			||||||
        body: Expr;
 | 
					 | 
				
			||||||
        vtype?: VType;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    | { type: "let"; param: Param; value: Expr }
 | 
					    | { type: "let"; param: Param; value: Expr }
 | 
				
			||||||
    | { type: "type_alias"; param: Param }
 | 
					    | { type: "type_alias"; param: Param }
 | 
				
			||||||
    | { type: "assign"; assignType: AssignType; subject: Expr; value: Expr }
 | 
					    | { type: "assign"; assignType: AssignType; subject: Expr; value: Expr }
 | 
				
			||||||
    | { type: "expr"; expr: Expr };
 | 
					    | { type: "expr"; expr: Expr };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type FnStmtKind = {
 | 
				
			||||||
 | 
					    type: "fn";
 | 
				
			||||||
 | 
					    ident: string;
 | 
				
			||||||
 | 
					    genericParams?: GenericParam[];
 | 
				
			||||||
 | 
					    params: Param[];
 | 
				
			||||||
 | 
					    returnType?: EType;
 | 
				
			||||||
 | 
					    body: Expr;
 | 
				
			||||||
 | 
					    sym?: Sym;
 | 
				
			||||||
 | 
					    vtype?: VType;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type AssignType = "=" | "+=" | "-=";
 | 
					export type AssignType = "=" | "+=" | "-=";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type StmtDetails = {
 | 
					export type StmtDetails = {
 | 
				
			||||||
@ -116,11 +119,13 @@ 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, vtype });
 | 
					                    params.push({ ident: param.ident, mut: true, vtype });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                const returnType: VType = stmt.kind.returnType
 | 
					                const returnType: VType = stmt.kind.returnType
 | 
				
			||||||
                    ? this.checkEType(stmt.kind.returnType)
 | 
					                    ? this.checkEType(stmt.kind.returnType)
 | 
				
			||||||
@ -426,7 +426,11 @@ export class Checker {
 | 
				
			|||||||
            throw new Error();
 | 
					            throw new Error();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const fields: VTypeParam[] = expr.kind.fields
 | 
					        const fields: VTypeParam[] = expr.kind.fields
 | 
				
			||||||
            .map(({ ident, expr }) => ({ ident, vtype: this.checkExpr(expr) }));
 | 
					            .map(({ ident, expr }): VTypeParam => ({
 | 
				
			||||||
 | 
					                ident,
 | 
				
			||||||
 | 
					                mut: true,
 | 
				
			||||||
 | 
					                vtype: this.checkExpr(expr),
 | 
				
			||||||
 | 
					            }));
 | 
				
			||||||
        return { type: "struct", fields };
 | 
					        return { type: "struct", fields };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -489,6 +493,9 @@ 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,
 | 
				
			||||||
@ -1023,10 +1030,12 @@ 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.map((param): VTypeParam => ({
 | 
					            const fields = etype.kind.fields
 | 
				
			||||||
                ident: param.ident,
 | 
					                .map((param): VTypeParam => ({
 | 
				
			||||||
                vtype: this.checkEType(param.etype!),
 | 
					                    ident: param.ident,
 | 
				
			||||||
            }));
 | 
					                    mut: true,
 | 
				
			||||||
 | 
					                    vtype: this.checkEType(param.etype!),
 | 
				
			||||||
 | 
					                }));
 | 
				
			||||||
            return { type: "struct", fields };
 | 
					            return { type: "struct", fields };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (etype.kind.type === "type_of") {
 | 
					        if (etype.kind.type === "type_of") {
 | 
				
			||||||
 | 
				
			|||||||
@ -12,10 +12,16 @@ 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 { AstLowerer } from "./middle/lower_ast.ts";
 | 
					import { lowerAst } from "./middle/lower_ast.ts";
 | 
				
			||||||
import { printMir } from "./middle/mir.ts";
 | 
					import { eliminateUnusedLocals } from "./middle/elim_unused_local.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[];
 | 
				
			||||||
@ -47,8 +53,28 @@ export class Compiler {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        new Checker(this.reporter).check(ast);
 | 
					        new Checker(this.reporter).check(ast);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const mir = new AstLowerer(ast).lower();
 | 
					        const mir = lowerAst(ast);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        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" | "note";
 | 
					    type: "error" | "warning" | "note";
 | 
				
			||||||
    reporter: string;
 | 
					    reporter: string;
 | 
				
			||||||
    pos?: Pos;
 | 
					    pos?: Pos;
 | 
				
			||||||
    msg: string;
 | 
					    msg: string;
 | 
				
			||||||
@ -23,6 +23,11 @@ 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}${
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										103
									
								
								compiler/middle/cfg.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								compiler/middle/cfg.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,103 @@
 | 
				
			|||||||
 | 
					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]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										59
									
								
								compiler/middle/elim_blocks.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								compiler/middle/elim_blocks.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,59 @@
 | 
				
			|||||||
 | 
					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);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										111
									
								
								compiler/middle/elim_transient_locals.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								compiler/middle/elim_transient_locals.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,111 @@
 | 
				
			|||||||
 | 
					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 });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										99
									
								
								compiler/middle/elim_transient_vals.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								compiler/middle/elim_transient_vals.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,99 @@
 | 
				
			|||||||
 | 
					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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										81
									
								
								compiler/middle/elim_unused_local.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								compiler/middle/elim_unused_local.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					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,13 +8,17 @@ import {
 | 
				
			|||||||
    Local,
 | 
					    Local,
 | 
				
			||||||
    LocalId,
 | 
					    LocalId,
 | 
				
			||||||
    Mir,
 | 
					    Mir,
 | 
				
			||||||
    Op,
 | 
					 | 
				
			||||||
    OpKind,
 | 
					    OpKind,
 | 
				
			||||||
 | 
					    RValue,
 | 
				
			||||||
    Ter,
 | 
					    Ter,
 | 
				
			||||||
    TerKind,
 | 
					    TerKind,
 | 
				
			||||||
} from "./mir.ts";
 | 
					} from "./mir.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export class AstLowerer {
 | 
					export function lowerAst(ast: Ast.Stmt[]): Mir {
 | 
				
			||||||
 | 
					    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 {
 | 
				
			||||||
@ -29,9 +33,15 @@ export class AstLowerer {
 | 
				
			|||||||
class LocalAllocator {
 | 
					class LocalAllocator {
 | 
				
			||||||
    private locals: Local[] = [];
 | 
					    private locals: Local[] = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public alloc(vtype: VType): LocalId {
 | 
					    public alloc(vtype: VType, sym?: Ast.Sym): LocalId {
 | 
				
			||||||
        const id = this.locals.length;
 | 
					        const id = this.locals.length;
 | 
				
			||||||
        this.locals.push({ id, vtype });
 | 
					        this.locals.push({ id, mut: false, vtype, sym });
 | 
				
			||||||
 | 
					        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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -63,19 +73,21 @@ class FnAstLowerer {
 | 
				
			|||||||
            throw new Error();
 | 
					            throw new Error();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.locals.alloc(stmt.kind.vtype!);
 | 
					        const rLoc = this.locals.alloc(vtype.returnType);
 | 
				
			||||||
        for (const param of stmt.kind.params) {
 | 
					        for (const param of stmt.kind.params) {
 | 
				
			||||||
            const id = this.locals.alloc(param.vtype!);
 | 
					            const id = this.locals.allocMut(param.vtype!);
 | 
				
			||||||
            this.fnParamIndexLocals.set(param.index!, id);
 | 
					            this.fnParamIndexLocals.set(param.index!, id);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.pushBlock("entry");
 | 
					        const entry = this.pushBlock();
 | 
				
			||||||
        this.lowerBlockExpr(stmt.kind.body);
 | 
					        const rVal = this.lowerBlockExpr(stmt.kind.body);
 | 
				
			||||||
        this.pushBlock("exit");
 | 
					        this.addOp({ type: "assign", dst: rLoc, src: local(rVal) });
 | 
				
			||||||
 | 
					        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 };
 | 
					        return { stmt, locals, blocks, entry, exit };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private lowerStmt(stmt: Ast.Stmt) {
 | 
					    private lowerStmt(stmt: Ast.Stmt) {
 | 
				
			||||||
@ -86,12 +98,12 @@ class FnAstLowerer {
 | 
				
			|||||||
            case "mod":
 | 
					            case "mod":
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
            case "break": {
 | 
					            case "break": {
 | 
				
			||||||
                const { local, block } = this.breakStack.at(-1)!;
 | 
					                const { local: dst, 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: local, src: val });
 | 
					                    this.addOp({ type: "assign", dst, src: local(val) });
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    this.addOp({ type: "assign_null", dst: local });
 | 
					                    this.addOp({ type: "assign_null", dst });
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                this.setTer({ type: "jump", target: block });
 | 
					                this.setTer({ type: "jump", target: block });
 | 
				
			||||||
                this.pushBlock();
 | 
					                this.pushBlock();
 | 
				
			||||||
@ -110,8 +122,7 @@ class FnAstLowerer {
 | 
				
			|||||||
            case "assign":
 | 
					            case "assign":
 | 
				
			||||||
                return this.lowerAssign(stmt);
 | 
					                return this.lowerAssign(stmt);
 | 
				
			||||||
            case "expr": {
 | 
					            case "expr": {
 | 
				
			||||||
                const val = this.lowerExpr(stmt.kind.expr);
 | 
					                this.lowerExpr(stmt.kind.expr);
 | 
				
			||||||
                this.addOp({ type: "drop", val });
 | 
					 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -125,19 +136,19 @@ class FnAstLowerer {
 | 
				
			|||||||
        if (stmt.kind.assignType !== "=") {
 | 
					        if (stmt.kind.assignType !== "=") {
 | 
				
			||||||
            throw new Error("incomplete desugar");
 | 
					            throw new Error("incomplete desugar");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const src = this.lowerExpr(stmt.kind.value);
 | 
					        const src = local(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 = this.lowerExpr(s.kind.subject);
 | 
					                const subject = local(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 = this.lowerExpr(s.kind.subject);
 | 
					                const subject = local(this.lowerExpr(s.kind.subject));
 | 
				
			||||||
                const index = this.lowerExpr(s.kind.value);
 | 
					                const index = local(this.lowerExpr(s.kind.value));
 | 
				
			||||||
                this.addOp({ type: "assign_field", subject, index, src });
 | 
					                this.addOp({ type: "assign_index", subject, index, src });
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case "sym": {
 | 
					            case "sym": {
 | 
				
			||||||
@ -167,9 +178,12 @@ class FnAstLowerer {
 | 
				
			|||||||
        if (stmt.kind.type !== "let") {
 | 
					        if (stmt.kind.type !== "let") {
 | 
				
			||||||
            throw new Error();
 | 
					            throw new Error();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const src = this.lowerExpr(stmt.kind.value);
 | 
					        const srcId = this.lowerExpr(stmt.kind.value);
 | 
				
			||||||
        const dst = this.locals.alloc(stmt.kind.param.vtype!);
 | 
					        const dst = this.locals.allocMut(
 | 
				
			||||||
        this.addOp({ type: "assign", dst, src });
 | 
					            stmt.kind.param.vtype!,
 | 
				
			||||||
 | 
					            stmt.kind.param.sym!,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        this.addOp({ type: "assign", dst, src: local(srcId) });
 | 
				
			||||||
        this.letStmtIdLocals.set(stmt.id, dst);
 | 
					        this.letStmtIdLocals.set(stmt.id, dst);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -177,30 +191,34 @@ 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_error", dst });
 | 
					                this.addOp({ type: "assign", dst, src: { type: "error" } });
 | 
				
			||||||
                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_null", dst });
 | 
					                this.addOp({ type: "assign", dst, src: { type: "null" } });
 | 
				
			||||||
                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_bool", dst, val });
 | 
					                this.addOp({ type: "assign", dst, src: { type: "bool", 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_int", dst, val });
 | 
					                this.addOp({ type: "assign", dst, src: { type: "int", 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({ type: "assign_string", dst, val });
 | 
					                this.addOp({
 | 
				
			||||||
 | 
					                    type: "assign",
 | 
				
			||||||
 | 
					                    dst,
 | 
				
			||||||
 | 
					                    src: { type: "string", val },
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
                return dst;
 | 
					                return dst;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            case "ident":
 | 
					            case "ident":
 | 
				
			||||||
@ -274,7 +292,7 @@ class FnAstLowerer {
 | 
				
			|||||||
            throw new Error();
 | 
					            throw new Error();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const ident = expr.kind.ident;
 | 
					        const ident = expr.kind.ident;
 | 
				
			||||||
        const subject = this.lowerExpr(expr.kind.subject);
 | 
					        const subject = local(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") {
 | 
				
			||||||
@ -296,8 +314,8 @@ class FnAstLowerer {
 | 
				
			|||||||
        if (expr.kind.type !== "index") {
 | 
					        if (expr.kind.type !== "index") {
 | 
				
			||||||
            throw new Error();
 | 
					            throw new Error();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const subject = this.lowerExpr(expr.kind.subject);
 | 
					        const subject = local(this.lowerExpr(expr.kind.subject));
 | 
				
			||||||
        const index = this.lowerExpr(expr.kind.value);
 | 
					        const index = local(this.lowerExpr(expr.kind.value));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const dstVType = ((): VType => {
 | 
					        const dstVType = ((): VType => {
 | 
				
			||||||
            const outer = expr.kind.subject.vtype!;
 | 
					            const outer = expr.kind.subject.vtype!;
 | 
				
			||||||
@ -320,9 +338,9 @@ class FnAstLowerer {
 | 
				
			|||||||
            throw new Error();
 | 
					            throw new Error();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const args = expr.kind.args.map((arg) => this.lowerExpr(arg));
 | 
					        const args = expr.kind.args.map((arg) => local(this.lowerExpr(arg)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const subject = this.lowerExpr(expr.kind.subject);
 | 
					        const subject = local(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") {
 | 
				
			||||||
@ -346,8 +364,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 = this.lowerExpr(expr.kind.left);
 | 
					        const left = local(this.lowerExpr(expr.kind.left));
 | 
				
			||||||
        const right = this.lowerExpr(expr.kind.right);
 | 
					        const right = local(this.lowerExpr(expr.kind.right));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const dst = this.locals.alloc(expr.vtype!);
 | 
					        const dst = this.locals.alloc(expr.vtype!);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -364,19 +382,19 @@ class FnAstLowerer {
 | 
				
			|||||||
            throw new Error();
 | 
					            throw new Error();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const condBlock = this.currentBlock();
 | 
					        const condBlock = this.currentBlock();
 | 
				
			||||||
        const cond = this.lowerExpr(expr.kind.cond);
 | 
					        const cond = local(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 = this.lowerExpr(expr.kind.truthy);
 | 
					        const truthyVal = local(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 = this.lowerExpr(expr.kind.falsy);
 | 
					            const falsyVal = local(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 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -385,6 +403,8 @@ 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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -397,8 +417,9 @@ 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.setTer({ type: "jump", target: body });
 | 
					        this.setTerOn(before, { 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 });
 | 
				
			||||||
@ -457,13 +478,18 @@ 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 } from "../ast.ts";
 | 
					import { BinaryType, Stmt, Sym } from "../ast.ts";
 | 
				
			||||||
import { VType, vtypeToString } from "../vtype.ts";
 | 
					import { VType, vtypeToString } from "../vtype.ts";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type Mir = {
 | 
					export type Mir = {
 | 
				
			||||||
@ -9,13 +9,17 @@ 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;
 | 
				
			||||||
@ -32,24 +36,17 @@ export type Op = {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type L = LocalId;
 | 
					type L = LocalId;
 | 
				
			||||||
 | 
					type R = RValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type OpKind =
 | 
					export type OpKind =
 | 
				
			||||||
    | { type: "error" }
 | 
					    | { type: "error" }
 | 
				
			||||||
    | { type: "return" }
 | 
					    | { type: "assign"; dst: L; src: R }
 | 
				
			||||||
    | { type: "drop"; val: L }
 | 
					    | { type: "field"; dst: L; subject: R; ident: string }
 | 
				
			||||||
    | { type: "assign"; dst: L; src: L }
 | 
					    | { type: "assign_field"; subject: R; ident: string; src: R }
 | 
				
			||||||
    | { type: "assign_error"; dst: L }
 | 
					    | { type: "index"; dst: L; subject: R; index: R }
 | 
				
			||||||
    | { type: "assign_null"; dst: L }
 | 
					    | { type: "assign_index"; subject: R; index: R; src: R }
 | 
				
			||||||
    | { type: "assign_bool"; dst: L; val: boolean }
 | 
					    | { type: "call_val"; dst: L; subject: R; args: R[] }
 | 
				
			||||||
    | { type: "assign_int"; dst: L; val: number }
 | 
					    | { type: "binary"; binaryType: BinaryType; dst: L; left: R; right: R };
 | 
				
			||||||
    | { 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;
 | 
				
			||||||
@ -57,8 +54,158 @@ 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: L; truthy: BlockId; falsy: BlockId };
 | 
					    | { type: "if"; cond: R; 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) {
 | 
				
			||||||
@ -66,7 +213,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.ident;
 | 
					        const name = stmt.kind.sym!.fullPath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        const vtype = stmt.kind.vtype;
 | 
					        const vtype = stmt.kind.vtype;
 | 
				
			||||||
        if (vtype?.type !== "fn") {
 | 
					        if (vtype?.type !== "fn") {
 | 
				
			||||||
@ -75,78 +222,106 @@ 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(({ vtype }, i) =>
 | 
					            .map(({ mut, vtype }, i) =>
 | 
				
			||||||
                `_${fn.locals[i + 1].id}: ${vtypeToString(vtype)}`
 | 
					                `${mut && "mut" || ""} _${fn.locals[i + 1].id}: ${
 | 
				
			||||||
 | 
					                    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) {
 | 
					
 | 
				
			||||||
            console.log(`    let _${id}: ${vtypeToString(vtype)};`);
 | 
					        const paramIndices = vtype.params.map((_v, i) => i + 1);
 | 
				
			||||||
 | 
					        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) {
 | 
				
			||||||
            console.log(`.${block.label ?? block.id}:`);
 | 
					            const l = (msg: string) => console.log(`        ${msg}`);
 | 
				
			||||||
 | 
					            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":
 | 
				
			||||||
                        console.log(`    <error>;`);
 | 
					                        l(`<error>;`);
 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    case "return":
 | 
					 | 
				
			||||||
                        console.log(`    return;`);
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    case "drop":
 | 
					 | 
				
			||||||
                        console.log(`    drop _${k.val};`);
 | 
					 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    case "assign":
 | 
					                    case "assign":
 | 
				
			||||||
                        console.log(`    _${k.dst} = _${k.src};`);
 | 
					                        l(`_${k.dst} = ${r(k.src)};`);
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    case "assign_error":
 | 
					 | 
				
			||||||
                        console.log(`    _${k.dst} = <error>;`);
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    case "assign_null":
 | 
					 | 
				
			||||||
                        console.log(`    _${k.dst} = null;`);
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    case "assign_bool":
 | 
					 | 
				
			||||||
                        console.log(`    _${k.dst} = ${k.val};`);
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    case "assign_int":
 | 
					 | 
				
			||||||
                        console.log(`    _${k.dst} = ${k.val};`);
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    case "assign_string":
 | 
					 | 
				
			||||||
                        console.log(`    _${k.dst} = "${k.val}";`);
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    case "assign_fn": {
 | 
					 | 
				
			||||||
                        const stmt = k.stmt;
 | 
					 | 
				
			||||||
                        if (stmt.kind.type !== "fn") {
 | 
					 | 
				
			||||||
                            throw new Error();
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        console.log(`    _${k.dst} = ${stmt.kind.ident};`);
 | 
					 | 
				
			||||||
                        break;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    case "field":
 | 
					                    case "field":
 | 
				
			||||||
                        console.log(
 | 
					                        l(`_${k.dst} = ${r(k.subject)}.${k.ident};`);
 | 
				
			||||||
                            `    _${k.dst} = _${k.subject}.${k.ident};`,
 | 
					                        break;
 | 
				
			||||||
                        );
 | 
					                    case "assign_field":
 | 
				
			||||||
 | 
					                        l(`${r(k.subject)}.${k.ident} = ${r(k.src)};`);
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    case "index":
 | 
					                    case "index":
 | 
				
			||||||
                        console.log(
 | 
					                        l(`_${k.dst} = ${r(k.subject)}[${r(k.index)}];`);
 | 
				
			||||||
                            `    _${k.dst} = _${k.subject}[_${k.index}];`,
 | 
					                        break;
 | 
				
			||||||
                        );
 | 
					                    case "assign_index":
 | 
				
			||||||
 | 
					                        l(`${r(k.subject)}[${r(k.index)}] = ${r(k.src)};`);
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    case "call_val": {
 | 
					                    case "call_val": {
 | 
				
			||||||
                        const args = k.args.map((arg) => `_${arg}`).join(", ");
 | 
					                        const args = k.args.map((arg) => r(arg)).join(", ");
 | 
				
			||||||
                        console.log(`    _${k.dst} = _${k.subject}(${args});`);
 | 
					                        l(`_${k.dst} = call ${r(k.subject)}(${args});`);
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    case "binary": {
 | 
					                    case "binary": {
 | 
				
			||||||
                        console.log(
 | 
					                        l(`_${k.dst} = ${r(k.left)} ${k.binaryType} ${
 | 
				
			||||||
                            `    _${k.dst} = _${k.left} ${k.binaryType} _${k.right};`,
 | 
					                            r(k.right)
 | 
				
			||||||
                        );
 | 
					                        };`);
 | 
				
			||||||
                        break;
 | 
					                        break;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					            const tk = block.ter.kind;
 | 
				
			||||||
 | 
					            switch (tk.type) {
 | 
				
			||||||
 | 
					                case "error":
 | 
				
			||||||
 | 
					                    l(`<error>;`);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case "return":
 | 
				
			||||||
 | 
					                    l(`return;`);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case "jump":
 | 
				
			||||||
 | 
					                    l(`jump bb${tk.target};`);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case "if":
 | 
				
			||||||
 | 
					                    l(`if ${
 | 
				
			||||||
 | 
					                        r(tk.cond)
 | 
				
			||||||
 | 
					                    }, true: bb${tk.truthy}, false: bb${tk.falsy};`);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            console.log("    }");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        console.log("}");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function rvalueToString(rvalue: RValue): string {
 | 
				
			||||||
 | 
					    switch (rvalue.type) {
 | 
				
			||||||
 | 
					        case "error":
 | 
				
			||||||
 | 
					            return `<error>`;
 | 
				
			||||||
 | 
					        case "local":
 | 
				
			||||||
 | 
					            return `_${rvalue.id}`;
 | 
				
			||||||
 | 
					        case "null":
 | 
				
			||||||
 | 
					            return "null";
 | 
				
			||||||
 | 
					        case "bool":
 | 
				
			||||||
 | 
					            return `${rvalue.val}`;
 | 
				
			||||||
 | 
					        case "int":
 | 
				
			||||||
 | 
					            return `${rvalue.val}`;
 | 
				
			||||||
 | 
					        case "string":
 | 
				
			||||||
 | 
					            return `"${rvalue.val}"`;
 | 
				
			||||||
 | 
					        case "fn": {
 | 
				
			||||||
 | 
					            const stmt = rvalue.stmt;
 | 
				
			||||||
 | 
					            if (stmt.kind.type !== "fn") {
 | 
				
			||||||
 | 
					                throw new Error();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return stmt.kind.sym!.fullPath;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -365,6 +365,7 @@ 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)) {
 | 
				
			||||||
@ -375,6 +376,7 @@ 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();
 | 
					        const syms = new EntryModSyms("root");
 | 
				
			||||||
        this.scout(stmts, syms);
 | 
					        this.scout(stmts, syms);
 | 
				
			||||||
        visitStmts(stmts, this, syms);
 | 
					        visitStmts(stmts, this, syms);
 | 
				
			||||||
        return "stop";
 | 
					        return "stop";
 | 
				
			||||||
@ -37,9 +37,10 @@ export class Resolver implements AstVisitor<[Syms]> {
 | 
				
			|||||||
                    return;
 | 
					                    return;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                const ident = stmt.kind.ident;
 | 
					                const ident = stmt.kind.ident;
 | 
				
			||||||
                syms.define(ident, {
 | 
					                stmt.kind.sym = 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,
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
@ -52,6 +53,7 @@ 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,
 | 
				
			||||||
@ -64,7 +66,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);
 | 
					        const modSyms = new ModSyms(syms, stmt.kind.ident);
 | 
				
			||||||
        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);
 | 
				
			||||||
@ -76,6 +78,7 @@ 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,
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
@ -93,9 +96,10 @@ export class Resolver implements AstVisitor<[Syms]> {
 | 
				
			|||||||
            this.reportAlreadyDefined(ident, stmt.pos, syms);
 | 
					            this.reportAlreadyDefined(ident, stmt.pos, syms);
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        syms.define(ident, {
 | 
					        stmt.kind.param.sym = 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,
 | 
				
			||||||
@ -123,6 +127,7 @@ 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,
 | 
				
			||||||
@ -137,6 +142,7 @@ 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,25 +5,28 @@ 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): void;
 | 
					    define(ident: string, sym: Sym): Sym;
 | 
				
			||||||
    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() {}
 | 
					    public constructor(private modName: string) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public define(ident: string, sym: Sym) {
 | 
					    public define(ident: string, sym: Sym): Sym {
 | 
				
			||||||
        if (sym.type === "let") {
 | 
					        if (sym.type === "let") {
 | 
				
			||||||
            this.define(ident, {
 | 
					            return 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 {
 | 
				
			||||||
@ -36,28 +39,48 @@ 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) {
 | 
					    public constructor(private parent: Syms, private modName: string) {
 | 
				
			||||||
        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) {
 | 
					    public define(ident: string, sym: Sym): Sym {
 | 
				
			||||||
        if (sym.type === "let") {
 | 
					        if (sym.type === "let") {
 | 
				
			||||||
            this.define(ident, {
 | 
					            return this.define(ident, {
 | 
				
			||||||
                ...sym,
 | 
					                ...sym,
 | 
				
			||||||
                type: "let_static",
 | 
					                type: "let_static",
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        this.syms[ident] = sym;
 | 
					        return this.syms[ident] = sym;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public definedLocally(ident: string): boolean {
 | 
					    public definedLocally(ident: string): boolean {
 | 
				
			||||||
@ -70,6 +93,21 @@ 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 {
 | 
				
			||||||
@ -77,16 +115,16 @@ export class FnSyms implements Syms {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public constructor(private parent: Syms) {}
 | 
					    public constructor(private parent: Syms) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public define(ident: string, sym: Sym) {
 | 
					    public define(ident: string, sym: Sym): Sym {
 | 
				
			||||||
        if (sym.type === "let") {
 | 
					        if (sym.type === "let") {
 | 
				
			||||||
            this.define(ident, {
 | 
					            return 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 {
 | 
				
			||||||
@ -99,6 +137,21 @@ 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 {
 | 
				
			||||||
@ -106,8 +159,9 @@ export class LeafSyms implements Syms {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public constructor(private parent: Syms) {}
 | 
					    public constructor(private parent: Syms) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public define(ident: string, sym: Sym) {
 | 
					    public define(ident: string, sym: Sym): Sym {
 | 
				
			||||||
        this.syms[ident] = sym;
 | 
					        this.syms[ident] = sym;
 | 
				
			||||||
 | 
					        return sym;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public definedLocally(ident: string): boolean {
 | 
					    public definedLocally(ident: string): boolean {
 | 
				
			||||||
@ -120,4 +174,19 @@ 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,6 +23,7 @@ export type VType =
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export type VTypeParam = {
 | 
					export type VTypeParam = {
 | 
				
			||||||
    ident: string;
 | 
					    ident: string;
 | 
				
			||||||
 | 
					    mut: boolean;
 | 
				
			||||||
    vtype: VType;
 | 
					    vtype: VType;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										22
									
								
								examples/transient_variable.slg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								examples/transient_variable.slg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					// 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);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										6
									
								
								examples/unused_variable.slg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								examples/unused_variable.slg
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					// mod std;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fn main() {
 | 
				
			||||||
 | 
					    let a = 5;
 | 
				
			||||||
 | 
					    let b = a;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user