From 7ca1ff1e25ed085359180538428e9a43b5c1c850 Mon Sep 17 00:00:00 2001 From: sfja Date: Mon, 30 Dec 2024 21:12:28 +0100 Subject: [PATCH] this commit kinda sucks --- compiler/ast.ts | 15 ++- compiler/ast_visitor.ts | 27 ++++- compiler/checker.ts | 51 ++++++--- compiler/compiler.ts | 33 +++--- compiler/desugar/compound_assign.ts | 8 +- compiler/desugar/special_loop.ts | 8 +- compiler/lexer.ts | 1 + compiler/mono.ts | 22 ++-- compiler/parser.ts | 169 ++++++++++++++-------------- compiler/resolver.ts | 28 +++-- compiler/resolver_syms.ts | 11 +- examples/annos.slg | 4 +- 12 files changed, 221 insertions(+), 156 deletions(-) diff --git a/compiler/ast.ts b/compiler/ast.ts index e86b9a7..8386c45 100644 --- a/compiler/ast.ts +++ b/compiler/ast.ts @@ -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; }; diff --git a/compiler/ast_visitor.ts b/compiler/ast_visitor.ts index 80b69bd..2055378 100644 --- a/compiler/ast_visitor.ts +++ b/compiler/ast_visitor.ts @@ -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 { + 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 { 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( + items: Item[], + v: AstVisitor, + ...args: Args +) { + if (v.visitItems?.(items, ...args) === "stop") return; + items.map((item) => visitItem(item, v, ...args)); +} + +export function visitItem( + item: Item, + v: AstVisitor, + ...args: Args +) { + if (v.visitItem?.(item, ...args) == "stop") return; + visitStmt(item.stmt, v, ...args); } export function visitStmts( @@ -75,11 +94,11 @@ export function visitStmt( 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; diff --git a/compiler/checker.ts b/compiler/checker.ts index 02eb85f..dc357ad 100644 --- a/compiler/checker.ts +++ b/compiler/checker.ts @@ -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); } diff --git a/compiler/compiler.ts b/compiler/compiler.ts index a018798..45d7a2b 100644 --- a/compiler/compiler.ts +++ b/compiler/compiler.ts @@ -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"; } } diff --git a/compiler/desugar/compound_assign.ts b/compiler/desugar/compound_assign.ts index 1ae6920..d69f456 100644 --- a/compiler/desugar/compound_assign.ts +++ b/compiler/desugar/compound_assign.ts @@ -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 { diff --git a/compiler/desugar/special_loop.ts b/compiler/desugar/special_loop.ts index 425e062..758e587 100644 --- a/compiler/desugar/special_loop.ts +++ b/compiler/desugar/special_loop.ts @@ -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 { diff --git a/compiler/lexer.ts b/compiler/lexer.ts index dea631a..8f8f726 100644 --- a/compiler/lexer.ts +++ b/compiler/lexer.ts @@ -48,6 +48,7 @@ export class Lexer { "for", "in", "mod", + "pub", ]; if (keywords.includes(value)) { return this.token(value, pos); diff --git a/compiler/mono.ts b/compiler/mono.ts index 9ccc45b..e1bcfc0 100644 --- a/compiler/mono.ts +++ b/compiler/mono.ts @@ -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; - private entryFn: Stmt; + private allFns: Map; + 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(); + private allFns = new Map(); - public collect(ast: Stmt[]): Map { - visitStmts(ast, this); + public collect(ast: Item[]): Map { + visitItems(ast, this); return this.allFns; } diff --git a/compiler/parser.ts b/compiler/parser.ts index 67c1004..3bc5fa1 100644 --- a/compiler/parser.ts +++ b/compiler/parser.ts @@ -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 { - 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, ">", ","); } diff --git a/compiler/resolver.ts b/compiler/resolver.ts index 2d06de4..d141c18 100644 --- a/compiler/resolver.ts +++ b/compiler/resolver.ts @@ -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); diff --git a/compiler/resolver_syms.ts b/compiler/resolver_syms.ts index 2ac04c9..d9a901c 100644 --- a/compiler/resolver_syms.ts +++ b/compiler/resolver_syms.ts @@ -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] }; } diff --git a/examples/annos.slg b/examples/annos.slg index 3f1bb76..6bf1884 100644 --- a/examples/annos.slg +++ b/examples/annos.slg @@ -1,4 +1,6 @@ -fn print(msg: string) #[builtin(print)] { + +#[builtin(Print)] +fn print(msg: string) { "hello" + 0 }