mirror of
				https://git.sfja.dk/Mikkel/slige.git
				synced 2025-11-04 09:58:15 +00:00 
			
		
		
		
	this commit kinda sucks
This commit is contained in:
		
							parent
							
								
									852df09ac9
								
							
						
					
					
						commit
						7ca1ff1e25
					
				@ -4,7 +4,14 @@ import { GenericArgsMap, VType } from "./vtype.ts";
 | 
			
		||||
 | 
			
		||||
export type Mod = {
 | 
			
		||||
    filePath: string;
 | 
			
		||||
    ast: Stmt[];
 | 
			
		||||
    items: Item[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type Item = {
 | 
			
		||||
    stmt: Stmt;
 | 
			
		||||
    pub: boolean;
 | 
			
		||||
    annos?: Anno[];
 | 
			
		||||
    pos: Pos;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type Stmt = {
 | 
			
		||||
@ -15,9 +22,10 @@ export type Stmt = {
 | 
			
		||||
 | 
			
		||||
export type StmtKind =
 | 
			
		||||
    | { type: "error" }
 | 
			
		||||
    | { type: "mod_block"; ident: string; stmts: Stmt[] }
 | 
			
		||||
    | { type: "mod_block"; ident: string; items: Item[] }
 | 
			
		||||
    | { type: "mod_file"; ident: string; filePath: string }
 | 
			
		||||
    | { type: "mod"; ident: string; mod: Mod }
 | 
			
		||||
    | { type: "item"; item: Item }
 | 
			
		||||
    | { type: "break"; expr?: Expr }
 | 
			
		||||
    | { type: "return"; expr?: Expr }
 | 
			
		||||
    | {
 | 
			
		||||
@ -27,7 +35,6 @@ export type StmtKind =
 | 
			
		||||
        params: Param[];
 | 
			
		||||
        returnType?: EType;
 | 
			
		||||
        body: Expr;
 | 
			
		||||
        anno?: Anno;
 | 
			
		||||
        vtype?: VType;
 | 
			
		||||
    }
 | 
			
		||||
    | { type: "let"; param: Param; value: Expr }
 | 
			
		||||
@ -147,7 +154,7 @@ export type GenericParam = {
 | 
			
		||||
 | 
			
		||||
export type Anno = {
 | 
			
		||||
    ident: string;
 | 
			
		||||
    values: Expr[];
 | 
			
		||||
    args: Expr[];
 | 
			
		||||
    pos: Pos;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,10 @@
 | 
			
		||||
import { EType, Expr, Param, Stmt } from "./ast.ts";
 | 
			
		||||
import { EType, Expr, Item, Param, Stmt } from "./ast.ts";
 | 
			
		||||
 | 
			
		||||
export type VisitRes = "stop" | void;
 | 
			
		||||
 | 
			
		||||
export interface AstVisitor<Args extends unknown[] = []> {
 | 
			
		||||
    visitItems?(items: Item[], ...args: Args): VisitRes;
 | 
			
		||||
    visitItem?(item: Item, ...args: Args): VisitRes;
 | 
			
		||||
    visitStmts?(stmts: Stmt[], ...args: Args): VisitRes;
 | 
			
		||||
    visitStmt?(stmt: Stmt, ...args: Args): VisitRes;
 | 
			
		||||
    visitErrorStmt?(stmt: Stmt, ...args: Args): VisitRes;
 | 
			
		||||
@ -48,7 +50,24 @@ export interface AstVisitor<Args extends unknown[] = []> {
 | 
			
		||||
    visitSymEType?(etype: EType, ...args: Args): VisitRes;
 | 
			
		||||
    visitArrayEType?(etype: EType, ...args: Args): VisitRes;
 | 
			
		||||
    visitStructEType?(etype: EType, ...args: Args): VisitRes;
 | 
			
		||||
    visitAnno?(etype: EType, ...args: Args): VisitRes;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function visitItems<Args extends unknown[] = []>(
 | 
			
		||||
    items: Item[],
 | 
			
		||||
    v: AstVisitor<Args>,
 | 
			
		||||
    ...args: Args
 | 
			
		||||
) {
 | 
			
		||||
    if (v.visitItems?.(items, ...args) === "stop") return;
 | 
			
		||||
    items.map((item) => visitItem(item, v, ...args));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function visitItem<Args extends unknown[] = []>(
 | 
			
		||||
    item: Item,
 | 
			
		||||
    v: AstVisitor<Args>,
 | 
			
		||||
    ...args: Args
 | 
			
		||||
) {
 | 
			
		||||
    if (v.visitItem?.(item, ...args) == "stop") return;
 | 
			
		||||
    visitStmt(item.stmt, v, ...args);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function visitStmts<Args extends unknown[] = []>(
 | 
			
		||||
@ -75,11 +94,11 @@ export function visitStmt<Args extends unknown[] = []>(
 | 
			
		||||
            break;
 | 
			
		||||
        case "mod_block":
 | 
			
		||||
            if (v.visitModBlockStmt?.(stmt, ...args) == "stop") return;
 | 
			
		||||
            visitStmts(stmt.kind.stmts, v, ...args);
 | 
			
		||||
            visitItems(stmt.kind.items, v, ...args);
 | 
			
		||||
            break;
 | 
			
		||||
        case "mod":
 | 
			
		||||
            if (v.visitModStmt?.(stmt, ...args) == "stop") return;
 | 
			
		||||
            visitStmts(stmt.kind.mod.ast, v, ...args);
 | 
			
		||||
            visitItems(stmt.kind.mod.items, v, ...args);
 | 
			
		||||
            break;
 | 
			
		||||
        case "break":
 | 
			
		||||
            if (v.visitBreakStmt?.(stmt, ...args) == "stop") return;
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { EType, Expr, Stmt, Sym } from "./ast.ts";
 | 
			
		||||
import { EType, Expr, Item, Stmt, Sym } from "./ast.ts";
 | 
			
		||||
import { printStackTrace, Reporter } from "./info.ts";
 | 
			
		||||
import { Pos } from "./token.ts";
 | 
			
		||||
import {
 | 
			
		||||
@ -19,15 +19,16 @@ export class Checker {
 | 
			
		||||
 | 
			
		||||
    public constructor(private reporter: Reporter) {}
 | 
			
		||||
 | 
			
		||||
    public check(stmts: Stmt[]) {
 | 
			
		||||
        this.checkFnHeaders(stmts);
 | 
			
		||||
        for (const stmt of stmts) {
 | 
			
		||||
            this.checkStmt(stmt);
 | 
			
		||||
    public check(items: Item[]) {
 | 
			
		||||
        this.scoutItems(items);
 | 
			
		||||
        for (const item of items) {
 | 
			
		||||
            this.checkItem(item);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private checkFnHeaders(stmts: Stmt[]) {
 | 
			
		||||
        for (const stmt of stmts) {
 | 
			
		||||
    private scoutItems(items: Item[]) {
 | 
			
		||||
        for (const item of items) {
 | 
			
		||||
            const { stmt } = item;
 | 
			
		||||
            if (stmt.kind.type !== "fn") {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
@ -65,6 +66,15 @@ export class Checker {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private checkItem(item: Item) {
 | 
			
		||||
        switch (item.stmt.kind.type) {
 | 
			
		||||
            case "fn":
 | 
			
		||||
                return this.checkFnItem(item);
 | 
			
		||||
            default:
 | 
			
		||||
                return this.checkStmt(item.stmt);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public checkStmt(stmt: Stmt) {
 | 
			
		||||
        switch (stmt.kind.type) {
 | 
			
		||||
            case "error":
 | 
			
		||||
@ -79,7 +89,7 @@ export class Checker {
 | 
			
		||||
            case "return":
 | 
			
		||||
                return this.checkReturnStmt(stmt);
 | 
			
		||||
            case "fn":
 | 
			
		||||
                return this.checkFnStmt(stmt);
 | 
			
		||||
                throw new Error("item, not stmt");
 | 
			
		||||
            case "let":
 | 
			
		||||
                return this.checkLetStmt(stmt);
 | 
			
		||||
            case "assign":
 | 
			
		||||
@ -93,10 +103,10 @@ export class Checker {
 | 
			
		||||
        if (stmt.kind.type !== "mod") {
 | 
			
		||||
            throw new Error();
 | 
			
		||||
        }
 | 
			
		||||
        const { ast } = stmt.kind.mod;
 | 
			
		||||
        this.checkFnHeaders(ast);
 | 
			
		||||
        for (const stmt of ast) {
 | 
			
		||||
            this.checkStmt(stmt);
 | 
			
		||||
        const { items } = stmt.kind.mod;
 | 
			
		||||
        this.scoutItems(items);
 | 
			
		||||
        for (const item of items) {
 | 
			
		||||
            this.checkItem(item);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -153,7 +163,8 @@ export class Checker {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public checkFnStmt(stmt: Stmt) {
 | 
			
		||||
    public checkFnItem(item: Item) {
 | 
			
		||||
        const { stmt } = item;
 | 
			
		||||
        if (stmt.kind.type !== "fn") {
 | 
			
		||||
            throw new Error();
 | 
			
		||||
        }
 | 
			
		||||
@ -163,8 +174,9 @@ export class Checker {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (
 | 
			
		||||
            stmt.kind.anno?.ident === "remainder" ||
 | 
			
		||||
            stmt.kind.anno?.ident === "builtin"
 | 
			
		||||
            item.annos?.some((anno) =>
 | 
			
		||||
                ["remainder", "builtin"].includes(anno.ident)
 | 
			
		||||
            ) ?? false
 | 
			
		||||
        ) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
@ -910,7 +922,14 @@ export class Checker {
 | 
			
		||||
        if (expr.kind.type !== "block") {
 | 
			
		||||
            throw new Error();
 | 
			
		||||
        }
 | 
			
		||||
        this.checkFnHeaders(expr.kind.stmts);
 | 
			
		||||
        this.scoutItems(
 | 
			
		||||
            expr.kind.stmts
 | 
			
		||||
                .filter((stmt) => stmt.kind.type === "item")
 | 
			
		||||
                .map((stmt) =>
 | 
			
		||||
                    stmt.kind.type === "item" ? stmt.kind.item : undefined
 | 
			
		||||
                )
 | 
			
		||||
                .filter((item) => item !== undefined),
 | 
			
		||||
        );
 | 
			
		||||
        for (const stmt of expr.kind.stmts) {
 | 
			
		||||
            this.checkStmt(stmt);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { AstCreator, Mod, Stmt } from "./ast.ts";
 | 
			
		||||
import { AstCreator, Item, Mod, Stmt } from "./ast.ts";
 | 
			
		||||
import { Checker } from "./checker.ts";
 | 
			
		||||
import { CompoundAssignDesugarer } from "./desugar/compound_assign.ts";
 | 
			
		||||
import { SpecialLoopDesugarer } from "./desugar/special_loop.ts";
 | 
			
		||||
@ -8,10 +8,10 @@ import { Monomorphizer } from "./mono.ts";
 | 
			
		||||
import { FnNamesMap, Lowerer } from "./lowerer.ts";
 | 
			
		||||
import { Parser } from "./parser.ts";
 | 
			
		||||
import { Resolver } from "./resolver.ts";
 | 
			
		||||
import { AstVisitor, VisitRes, visitStmts } from "./ast_visitor.ts";
 | 
			
		||||
import { AstVisitor, visitItems, VisitRes } from "./ast_visitor.ts";
 | 
			
		||||
import { Pos } from "./token.ts";
 | 
			
		||||
 | 
			
		||||
import * as path from "jsr:@std/path";
 | 
			
		||||
import { Pos } from "./token.ts";
 | 
			
		||||
 | 
			
		||||
export type CompileResult = {
 | 
			
		||||
    program: number[];
 | 
			
		||||
@ -33,20 +33,21 @@ export class Compiler {
 | 
			
		||||
            this.reporter,
 | 
			
		||||
        ).resolve();
 | 
			
		||||
 | 
			
		||||
        new SpecialLoopDesugarer(this.astCreator).desugar(mod.ast);
 | 
			
		||||
        new SpecialLoopDesugarer(this.astCreator).desugar(mod.items);
 | 
			
		||||
 | 
			
		||||
        new Resolver(this.reporter).resolve(mod.ast);
 | 
			
		||||
        new Resolver(this.reporter).resolve(mod.items);
 | 
			
		||||
 | 
			
		||||
        new CompoundAssignDesugarer(this.astCreator).desugar(mod.ast);
 | 
			
		||||
        new CompoundAssignDesugarer(this.astCreator).desugar(mod.items);
 | 
			
		||||
 | 
			
		||||
        new Checker(this.reporter).check(mod.ast);
 | 
			
		||||
        new Checker(this.reporter).check(mod.items);
 | 
			
		||||
 | 
			
		||||
        if (this.reporter.errorOccured()) {
 | 
			
		||||
            console.error("Errors occurred, stopping compilation.");
 | 
			
		||||
            Deno.exit(1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const { monoFns, callMap } = new Monomorphizer(mod.ast).monomorphize();
 | 
			
		||||
        const { monoFns, callMap } = new Monomorphizer(mod.items)
 | 
			
		||||
            .monomorphize();
 | 
			
		||||
 | 
			
		||||
        const lastPos = await lastPosInTextFile(this.startFilePath);
 | 
			
		||||
 | 
			
		||||
@ -68,12 +69,12 @@ export class ModTree implements AstVisitor<[string]> {
 | 
			
		||||
    public resolve(): Mod {
 | 
			
		||||
        const entryAst = this.parseFile(this.entryFilePath);
 | 
			
		||||
 | 
			
		||||
        visitStmts(entryAst, this, this.entryFilePath);
 | 
			
		||||
        visitItems(entryAst, this, this.entryFilePath);
 | 
			
		||||
 | 
			
		||||
        return { filePath: this.entryFilePath, ast: entryAst };
 | 
			
		||||
        return { filePath: this.entryFilePath, items: entryAst };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseFile(filePath: string): Stmt[] {
 | 
			
		||||
    private parseFile(filePath: string): Item[] {
 | 
			
		||||
        const text = Deno.readTextFileSync(filePath);
 | 
			
		||||
 | 
			
		||||
        const lexer = new Lexer(text, this.reporter);
 | 
			
		||||
@ -88,13 +89,13 @@ export class ModTree implements AstVisitor<[string]> {
 | 
			
		||||
        if (stmt.kind.type !== "mod_block") {
 | 
			
		||||
            throw new Error();
 | 
			
		||||
        }
 | 
			
		||||
        const { ident, stmts: ast } = stmt.kind;
 | 
			
		||||
        const { ident, items } = stmt.kind;
 | 
			
		||||
        stmt.kind = {
 | 
			
		||||
            type: "mod",
 | 
			
		||||
            ident,
 | 
			
		||||
            mod: { filePath, ast },
 | 
			
		||||
            mod: { filePath, items },
 | 
			
		||||
        };
 | 
			
		||||
        visitStmts(ast, this, filePath);
 | 
			
		||||
        visitItems(items, this, filePath);
 | 
			
		||||
        return "stop";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -109,9 +110,9 @@ export class ModTree implements AstVisitor<[string]> {
 | 
			
		||||
        stmt.kind = {
 | 
			
		||||
            type: "mod",
 | 
			
		||||
            ident,
 | 
			
		||||
            mod: { filePath, ast },
 | 
			
		||||
            mod: { filePath, items: ast },
 | 
			
		||||
        };
 | 
			
		||||
        visitStmts(ast, this, filePath);
 | 
			
		||||
        visitItems(ast, this, filePath);
 | 
			
		||||
        return "stop";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,11 @@
 | 
			
		||||
import { AstCreator, Stmt } from "../ast.ts";
 | 
			
		||||
import { AstVisitor, VisitRes, visitStmt, visitStmts } from "../ast_visitor.ts";
 | 
			
		||||
import { AstCreator, Item, Stmt } from "../ast.ts";
 | 
			
		||||
import { AstVisitor, visitItems, VisitRes, visitStmt } from "../ast_visitor.ts";
 | 
			
		||||
 | 
			
		||||
export class CompoundAssignDesugarer implements AstVisitor {
 | 
			
		||||
    public constructor(private astCreator: AstCreator) {}
 | 
			
		||||
 | 
			
		||||
    public desugar(stmts: Stmt[]) {
 | 
			
		||||
        visitStmts(stmts, this);
 | 
			
		||||
    public desugar(items: Item[]) {
 | 
			
		||||
        visitItems(items, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    visitAssignStmt(stmt: Stmt): VisitRes {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
import { AstCreator, Expr, ExprKind, Stmt, StmtKind } from "../ast.ts";
 | 
			
		||||
import { AstVisitor, visitExpr, VisitRes, visitStmts } from "../ast_visitor.ts";
 | 
			
		||||
import { AstCreator, Expr, ExprKind, Item, StmtKind } from "../ast.ts";
 | 
			
		||||
import { AstVisitor, visitExpr, visitItems, VisitRes } from "../ast_visitor.ts";
 | 
			
		||||
import { Pos } from "../token.ts";
 | 
			
		||||
 | 
			
		||||
export class SpecialLoopDesugarer implements AstVisitor {
 | 
			
		||||
@ -7,8 +7,8 @@ export class SpecialLoopDesugarer implements AstVisitor {
 | 
			
		||||
        private astCreator: AstCreator,
 | 
			
		||||
    ) {}
 | 
			
		||||
 | 
			
		||||
    public desugar(stmts: Stmt[]) {
 | 
			
		||||
        visitStmts(stmts, this);
 | 
			
		||||
    public desugar(items: Item[]) {
 | 
			
		||||
        visitItems(items, this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    visitWhileExpr(expr: Expr): VisitRes {
 | 
			
		||||
 | 
			
		||||
@ -48,6 +48,7 @@ export class Lexer {
 | 
			
		||||
                "for",
 | 
			
		||||
                "in",
 | 
			
		||||
                "mod",
 | 
			
		||||
                "pub",
 | 
			
		||||
            ];
 | 
			
		||||
            if (keywords.includes(value)) {
 | 
			
		||||
                return this.token(value, pos);
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,15 @@
 | 
			
		||||
import { Expr, Stmt } from "./ast.ts";
 | 
			
		||||
import { AstVisitor, visitExpr, VisitRes, visitStmts } from "./ast_visitor.ts";
 | 
			
		||||
import { Expr, Item, Stmt } from "./ast.ts";
 | 
			
		||||
import { AstVisitor, visitExpr, visitItems, VisitRes } from "./ast_visitor.ts";
 | 
			
		||||
import { GenericArgsMap, VType } from "./vtype.ts";
 | 
			
		||||
 | 
			
		||||
export class Monomorphizer {
 | 
			
		||||
    private fnIdCounter = 0;
 | 
			
		||||
    private fns: MonoFnsMap = {};
 | 
			
		||||
    private callMap: MonoCallNameGenMap = {};
 | 
			
		||||
    private allFns: Map<number, Stmt>;
 | 
			
		||||
    private entryFn: Stmt;
 | 
			
		||||
    private allFns: Map<number, Item>;
 | 
			
		||||
    private entryFn: Item;
 | 
			
		||||
 | 
			
		||||
    constructor(private ast: Stmt[]) {
 | 
			
		||||
    constructor(private ast: Item[]) {
 | 
			
		||||
        this.allFns = new AllFnsCollector().collect(this.ast);
 | 
			
		||||
        this.entryFn = findMain(this.allFns);
 | 
			
		||||
    }
 | 
			
		||||
@ -20,7 +20,7 @@ export class Monomorphizer {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private monomorphizeFn(
 | 
			
		||||
        stmt: Stmt,
 | 
			
		||||
        item: Item,
 | 
			
		||||
        genericArgs?: GenericArgsMap,
 | 
			
		||||
    ): MonoFn {
 | 
			
		||||
        const id = this.fnIdCounter;
 | 
			
		||||
@ -29,7 +29,7 @@ export class Monomorphizer {
 | 
			
		||||
        if (nameGen in this.fns) {
 | 
			
		||||
            return this.fns[nameGen];
 | 
			
		||||
        }
 | 
			
		||||
        const monoFn = { id, nameGen, stmt, genericArgs };
 | 
			
		||||
        const monoFn: MonoFn = { id, nameGen, stmt, genericArgs };
 | 
			
		||||
        this.fns[nameGen] = monoFn;
 | 
			
		||||
        const calls = new CallCollector().collect(stmt);
 | 
			
		||||
        for (const call of calls) {
 | 
			
		||||
@ -127,7 +127,7 @@ export type MonoFnsMap = { [nameGen: string]: MonoFn };
 | 
			
		||||
export type MonoFn = {
 | 
			
		||||
    id: number;
 | 
			
		||||
    nameGen: string;
 | 
			
		||||
    stmt: Stmt;
 | 
			
		||||
    item: Item;
 | 
			
		||||
    genericArgs?: GenericArgsMap;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -182,10 +182,10 @@ function vtypeNameGenPart(vtype: VType): string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class AllFnsCollector implements AstVisitor {
 | 
			
		||||
    private allFns = new Map<number, Stmt>();
 | 
			
		||||
    private allFns = new Map<number, Item>();
 | 
			
		||||
 | 
			
		||||
    public collect(ast: Stmt[]): Map<number, Stmt> {
 | 
			
		||||
        visitStmts(ast, this);
 | 
			
		||||
    public collect(ast: Item[]): Map<number, Item> {
 | 
			
		||||
        visitItems(ast, this);
 | 
			
		||||
        return this.allFns;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ import {
 | 
			
		||||
    Expr,
 | 
			
		||||
    ExprKind,
 | 
			
		||||
    GenericParam,
 | 
			
		||||
    Item,
 | 
			
		||||
    Param,
 | 
			
		||||
    Stmt,
 | 
			
		||||
    StmtKind,
 | 
			
		||||
@ -30,39 +31,92 @@ export class Parser {
 | 
			
		||||
        this.currentToken = lexer.next();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public parse(): Stmt[] {
 | 
			
		||||
        return this.parseStmts();
 | 
			
		||||
    public parse(): Item[] {
 | 
			
		||||
        return this.parseItems();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseStmts(): Stmt[] {
 | 
			
		||||
        const stmts: Stmt[] = [];
 | 
			
		||||
    private parseItems(): Item[] {
 | 
			
		||||
        const items: Item[] = [];
 | 
			
		||||
        while (!this.done()) {
 | 
			
		||||
            stmts.push(this.parseModStmt());
 | 
			
		||||
            items.push(this.parseItem());
 | 
			
		||||
        }
 | 
			
		||||
        return stmts;
 | 
			
		||||
        return items;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseModStmt(): Stmt {
 | 
			
		||||
        if (this.test("mod")) {
 | 
			
		||||
            return (this.parseMod());
 | 
			
		||||
        } else if (this.test("fn")) {
 | 
			
		||||
            return (this.parseFn());
 | 
			
		||||
        } else if (
 | 
			
		||||
            this.test("let") || this.test("return") || this.test("break")
 | 
			
		||||
        ) {
 | 
			
		||||
            const expr = this.parseSingleLineBlockStmt();
 | 
			
		||||
            this.eatSemicolon();
 | 
			
		||||
            return expr;
 | 
			
		||||
        } else if (
 | 
			
		||||
            ["{", "if", "loop", "while", "for"].some((tt) => this.test(tt))
 | 
			
		||||
        ) {
 | 
			
		||||
            const expr = this.parseMultiLineBlockExpr();
 | 
			
		||||
            return (this.stmt({ type: "expr", expr }, expr.pos));
 | 
			
		||||
        } else {
 | 
			
		||||
            const expr = this.parseAssign();
 | 
			
		||||
            this.eatSemicolon();
 | 
			
		||||
            return expr;
 | 
			
		||||
    private parseItem(): Item {
 | 
			
		||||
        const pos = this.pos();
 | 
			
		||||
        return this.parseItemAnnos(pos, []);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseItemAnnos(itemPos: Pos, annos: Anno[]): Item {
 | 
			
		||||
        const pos = this.pos();
 | 
			
		||||
        if (!this.test("#")) {
 | 
			
		||||
            return this.parseItemPub(itemPos, annos);
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
        if (!this.test("[")) {
 | 
			
		||||
            this.report("expected '['");
 | 
			
		||||
            return this.errorItem(pos);
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
        if (!this.test("ident")) {
 | 
			
		||||
            this.report("expected 'ident'");
 | 
			
		||||
            return this.errorItem(pos);
 | 
			
		||||
        }
 | 
			
		||||
        const ident = this.current().identValue!;
 | 
			
		||||
        this.step();
 | 
			
		||||
        if (!this.test("(")) {
 | 
			
		||||
            this.report("expected '('");
 | 
			
		||||
            return this.errorItem(pos);
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
        const args: Expr[] = [];
 | 
			
		||||
        if (!this.done() && !this.test(")")) {
 | 
			
		||||
            args.push(this.parseExpr());
 | 
			
		||||
            while (this.test(",")) {
 | 
			
		||||
                this.step();
 | 
			
		||||
                args.push(this.parseExpr());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!this.test(")")) {
 | 
			
		||||
            this.report("expected ')'");
 | 
			
		||||
            return this.errorItem(pos);
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
        return this.parseItemAnnos(itemPos, [...annos, { ident, args, pos }]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseItemPub(itemPos: Pos, annos: Anno[]): Item {
 | 
			
		||||
        if (!this.test("pub")) {
 | 
			
		||||
            return this.parseItemInner(itemPos, false, annos);
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
        return this.parseItemInner(itemPos, true, annos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseItemInner(pos: Pos, pub: boolean, annos: Anno[]): Item {
 | 
			
		||||
        const stmt = this.parseItemStmt();
 | 
			
		||||
        return { stmt, pub, annos, pos };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseItemStmt(): Stmt {
 | 
			
		||||
        const pos = this.pos();
 | 
			
		||||
        if (this.test("mod")) {
 | 
			
		||||
            return this.parseMod();
 | 
			
		||||
        } else if (this.test("fn")) {
 | 
			
		||||
            return this.parseFn();
 | 
			
		||||
        } else if (this.test("let")) {
 | 
			
		||||
            const stmt = this.parseLet();
 | 
			
		||||
            this.eatSemicolon();
 | 
			
		||||
            return stmt;
 | 
			
		||||
        } else {
 | 
			
		||||
            this.report("expected item");
 | 
			
		||||
            return this.stmt({ type: "error" }, pos);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private errorItem(pos: Pos): Item {
 | 
			
		||||
        return { stmt: this.stmt({ type: "error" }, pos), pub: false, pos };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseMod(): Stmt {
 | 
			
		||||
@ -87,9 +141,9 @@ export class Parser {
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
 | 
			
		||||
        const stmts: Stmt[] = [];
 | 
			
		||||
        const items: Item[] = [];
 | 
			
		||||
        while (!this.done() && !this.test("}")) {
 | 
			
		||||
            stmts.push(this.parseModStmt());
 | 
			
		||||
            items.push(this.parseItem());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this.test("}")) {
 | 
			
		||||
@ -98,7 +152,7 @@ export class Parser {
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
 | 
			
		||||
        return this.stmt({ type: "mod_block", ident, stmts }, pos);
 | 
			
		||||
        return this.stmt({ type: "mod_block", ident, items }, pos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseMultiLineBlockExpr(): Expr {
 | 
			
		||||
@ -234,14 +288,6 @@ export class Parser {
 | 
			
		||||
            returnType = this.parseEType();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let anno: Anno | undefined;
 | 
			
		||||
        if (this.test("#")) {
 | 
			
		||||
            const result = this.parseAnno();
 | 
			
		||||
            if (!result.ok) {
 | 
			
		||||
                return this.stmt({ type: "error" }, pos);
 | 
			
		||||
            }
 | 
			
		||||
            anno = result.value;
 | 
			
		||||
        }
 | 
			
		||||
        if (!this.test("{")) {
 | 
			
		||||
            this.report("expected block");
 | 
			
		||||
            return this.stmt({ type: "error" }, pos);
 | 
			
		||||
@ -255,60 +301,11 @@ export class Parser {
 | 
			
		||||
                params,
 | 
			
		||||
                returnType,
 | 
			
		||||
                body,
 | 
			
		||||
                anno,
 | 
			
		||||
            },
 | 
			
		||||
            pos,
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseAnnoArgs(): Expr[] {
 | 
			
		||||
        this.step();
 | 
			
		||||
        if (!this.test("(")) {
 | 
			
		||||
            this.report("expected '('");
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
        const annoArgs: Expr[] = [];
 | 
			
		||||
        if (!this.test(")")) {
 | 
			
		||||
            annoArgs.push(this.parseExpr());
 | 
			
		||||
            while (this.test(",")) {
 | 
			
		||||
                this.step();
 | 
			
		||||
                if (this.test(")")) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
                annoArgs.push(this.parseExpr());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (!this.test(")")) {
 | 
			
		||||
            this.report("expected ')'");
 | 
			
		||||
            return [];
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
        return annoArgs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseAnno(): Res<Anno> {
 | 
			
		||||
        const pos = this.pos();
 | 
			
		||||
        this.step();
 | 
			
		||||
        if (!this.test("[")) {
 | 
			
		||||
            this.report("expected '['");
 | 
			
		||||
            return { ok: false };
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
        if (!this.test("ident")) {
 | 
			
		||||
            this.report("expected identifier");
 | 
			
		||||
            return { ok: false };
 | 
			
		||||
        }
 | 
			
		||||
        const ident = this.current().identValue!;
 | 
			
		||||
        const values = this.parseAnnoArgs();
 | 
			
		||||
        if (!this.test("]")) {
 | 
			
		||||
            this.report("expected ']'");
 | 
			
		||||
            return { ok: false };
 | 
			
		||||
        }
 | 
			
		||||
        this.step();
 | 
			
		||||
        return { ok: true, value: { ident, pos, values } };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private parseFnETypeParams(): GenericParam[] {
 | 
			
		||||
        return this.parseDelimitedList(this.parseETypeParam, ">", ",");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,9 @@
 | 
			
		||||
import { EType, Expr, Stmt } from "./ast.ts";
 | 
			
		||||
import { EType, Expr, Item, Stmt } from "./ast.ts";
 | 
			
		||||
import {
 | 
			
		||||
    AstVisitor,
 | 
			
		||||
    visitEType,
 | 
			
		||||
    visitExpr,
 | 
			
		||||
    visitItems,
 | 
			
		||||
    visitParam,
 | 
			
		||||
    VisitRes,
 | 
			
		||||
    visitStmt,
 | 
			
		||||
@ -22,10 +23,10 @@ export class Resolver implements AstVisitor<[Syms]> {
 | 
			
		||||
    public constructor(private reporter: Reporter) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public resolve(stmts: Stmt[]): VisitRes {
 | 
			
		||||
    public resolve(items: Item[]): VisitRes {
 | 
			
		||||
        const syms = new EntryModSyms();
 | 
			
		||||
        this.scoutFnStmts(stmts, syms);
 | 
			
		||||
        visitStmts(stmts, this, syms);
 | 
			
		||||
        this.scoutItems(items, syms);
 | 
			
		||||
        visitItems(items, this, syms);
 | 
			
		||||
        return "stop";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -35,8 +36,8 @@ export class Resolver implements AstVisitor<[Syms]> {
 | 
			
		||||
        }
 | 
			
		||||
        const modSyms = new ModSyms(syms);
 | 
			
		||||
        const { mod, ident } = stmt.kind;
 | 
			
		||||
        this.scoutFnStmts(mod.ast, modSyms);
 | 
			
		||||
        visitStmts(mod.ast, this, modSyms);
 | 
			
		||||
        this.scoutItems(mod.items, modSyms);
 | 
			
		||||
        visitItems(mod.items, this, modSyms);
 | 
			
		||||
 | 
			
		||||
        if (syms.definedLocally(ident)) {
 | 
			
		||||
            this.reportAlreadyDefined(ident, stmt.pos, syms);
 | 
			
		||||
@ -72,8 +73,9 @@ export class Resolver implements AstVisitor<[Syms]> {
 | 
			
		||||
        return "stop";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private scoutFnStmts(stmts: Stmt[], syms: Syms) {
 | 
			
		||||
        for (const stmt of stmts) {
 | 
			
		||||
    private scoutItems(items: Item[], syms: Syms) {
 | 
			
		||||
        for (const item of items) {
 | 
			
		||||
            const { stmt } = item;
 | 
			
		||||
            if (stmt.kind.type !== "fn") {
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
@ -186,7 +188,15 @@ export class Resolver implements AstVisitor<[Syms]> {
 | 
			
		||||
            throw new Error();
 | 
			
		||||
        }
 | 
			
		||||
        const childSyms = new LeafSyms(syms);
 | 
			
		||||
        this.scoutFnStmts(expr.kind.stmts, childSyms);
 | 
			
		||||
        this.scoutItems(
 | 
			
		||||
            expr.kind.stmts
 | 
			
		||||
                .filter((stmt) => stmt.kind.type === "item")
 | 
			
		||||
                .map((stmt) =>
 | 
			
		||||
                    stmt.kind.type === "item" ? stmt.kind.item : undefined
 | 
			
		||||
                )
 | 
			
		||||
                .filter((item) => item !== undefined),
 | 
			
		||||
            childSyms,
 | 
			
		||||
        );
 | 
			
		||||
        visitStmts(expr.kind.stmts, this, childSyms);
 | 
			
		||||
        if (expr.kind.expr) {
 | 
			
		||||
            visitExpr(expr.kind.expr, this, childSyms);
 | 
			
		||||
 | 
			
		||||
@ -13,7 +13,13 @@ export interface Syms {
 | 
			
		||||
export class EntryModSyms implements Syms {
 | 
			
		||||
    private syms: SymMap = {};
 | 
			
		||||
 | 
			
		||||
    public constructor() {}
 | 
			
		||||
    public constructor() {
 | 
			
		||||
        this.syms["crate"] = {
 | 
			
		||||
            type: "mod",
 | 
			
		||||
            ident: "crate",
 | 
			
		||||
            syms: this,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public define(ident: string, sym: Sym) {
 | 
			
		||||
        if (sym.type === "let") {
 | 
			
		||||
@ -65,6 +71,9 @@ export class ModSyms implements Syms {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public get(ident: string): GetRes {
 | 
			
		||||
        if (ident === "crate") {
 | 
			
		||||
            return this.parent.get(ident);
 | 
			
		||||
        }
 | 
			
		||||
        if (ident in this.syms) {
 | 
			
		||||
            return { ok: true, sym: this.syms[ident] };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,6 @@
 | 
			
		||||
fn print(msg: string) #[builtin(print)] {
 | 
			
		||||
 | 
			
		||||
#[builtin(Print)]
 | 
			
		||||
fn print(msg: string) {
 | 
			
		||||
    "hello" + 0    
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user