diff --git a/compiler/ast.ts b/compiler/ast.ts index 8386c45..e86b9a7 100644 --- a/compiler/ast.ts +++ b/compiler/ast.ts @@ -4,14 +4,7 @@ import { GenericArgsMap, VType } from "./vtype.ts"; export type Mod = { filePath: string; - items: Item[]; -}; - -export type Item = { - stmt: Stmt; - pub: boolean; - annos?: Anno[]; - pos: Pos; + ast: Stmt[]; }; export type Stmt = { @@ -22,10 +15,9 @@ export type Stmt = { export type StmtKind = | { type: "error" } - | { type: "mod_block"; ident: string; items: Item[] } + | { type: "mod_block"; ident: string; stmts: Stmt[] } | { 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 } | { @@ -35,6 +27,7 @@ export type StmtKind = params: Param[]; returnType?: EType; body: Expr; + anno?: Anno; vtype?: VType; } | { type: "let"; param: Param; value: Expr } @@ -154,7 +147,7 @@ export type GenericParam = { export type Anno = { ident: string; - args: Expr[]; + values: Expr[]; pos: Pos; }; diff --git a/compiler/ast_visitor.ts b/compiler/ast_visitor.ts index 2055378..80b69bd 100644 --- a/compiler/ast_visitor.ts +++ b/compiler/ast_visitor.ts @@ -1,10 +1,8 @@ -import { EType, Expr, Item, Param, Stmt } from "./ast.ts"; +import { EType, Expr, 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; @@ -50,24 +48,7 @@ export interface AstVisitor { visitSymEType?(etype: EType, ...args: Args): VisitRes; visitArrayEType?(etype: EType, ...args: Args): VisitRes; visitStructEType?(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); + visitAnno?(etype: EType, ...args: Args): VisitRes; } export function visitStmts( @@ -94,11 +75,11 @@ export function visitStmt( break; case "mod_block": if (v.visitModBlockStmt?.(stmt, ...args) == "stop") return; - visitItems(stmt.kind.items, v, ...args); + visitStmts(stmt.kind.stmts, v, ...args); break; case "mod": if (v.visitModStmt?.(stmt, ...args) == "stop") return; - visitItems(stmt.kind.mod.items, v, ...args); + visitStmts(stmt.kind.mod.ast, v, ...args); break; case "break": if (v.visitBreakStmt?.(stmt, ...args) == "stop") return; diff --git a/compiler/checker.ts b/compiler/checker.ts index dc357ad..02eb85f 100644 --- a/compiler/checker.ts +++ b/compiler/checker.ts @@ -1,4 +1,4 @@ -import { EType, Expr, Item, Stmt, Sym } from "./ast.ts"; +import { EType, Expr, Stmt, Sym } from "./ast.ts"; import { printStackTrace, Reporter } from "./info.ts"; import { Pos } from "./token.ts"; import { @@ -19,16 +19,15 @@ export class Checker { public constructor(private reporter: Reporter) {} - public check(items: Item[]) { - this.scoutItems(items); - for (const item of items) { - this.checkItem(item); + public check(stmts: Stmt[]) { + this.checkFnHeaders(stmts); + for (const stmt of stmts) { + this.checkStmt(stmt); } } - private scoutItems(items: Item[]) { - for (const item of items) { - const { stmt } = item; + private checkFnHeaders(stmts: Stmt[]) { + for (const stmt of stmts) { if (stmt.kind.type !== "fn") { continue; } @@ -66,15 +65,6 @@ 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": @@ -89,7 +79,7 @@ export class Checker { case "return": return this.checkReturnStmt(stmt); case "fn": - throw new Error("item, not stmt"); + return this.checkFnStmt(stmt); case "let": return this.checkLetStmt(stmt); case "assign": @@ -103,10 +93,10 @@ export class Checker { if (stmt.kind.type !== "mod") { throw new Error(); } - const { items } = stmt.kind.mod; - this.scoutItems(items); - for (const item of items) { - this.checkItem(item); + const { ast } = stmt.kind.mod; + this.checkFnHeaders(ast); + for (const stmt of ast) { + this.checkStmt(stmt); } } @@ -163,8 +153,7 @@ export class Checker { } } - public checkFnItem(item: Item) { - const { stmt } = item; + public checkFnStmt(stmt: Stmt) { if (stmt.kind.type !== "fn") { throw new Error(); } @@ -174,9 +163,8 @@ export class Checker { } if ( - item.annos?.some((anno) => - ["remainder", "builtin"].includes(anno.ident) - ) ?? false + stmt.kind.anno?.ident === "remainder" || + stmt.kind.anno?.ident === "builtin" ) { return; } @@ -922,14 +910,7 @@ export class Checker { if (expr.kind.type !== "block") { throw new Error(); } - 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), - ); + this.checkFnHeaders(expr.kind.stmts); for (const stmt of expr.kind.stmts) { this.checkStmt(stmt); } diff --git a/compiler/compiler.ts b/compiler/compiler.ts index 45d7a2b..a018798 100644 --- a/compiler/compiler.ts +++ b/compiler/compiler.ts @@ -1,4 +1,4 @@ -import { AstCreator, Item, Mod, Stmt } from "./ast.ts"; +import { AstCreator, 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, visitItems, VisitRes } from "./ast_visitor.ts"; -import { Pos } from "./token.ts"; +import { AstVisitor, VisitRes, visitStmts } from "./ast_visitor.ts"; import * as path from "jsr:@std/path"; +import { Pos } from "./token.ts"; export type CompileResult = { program: number[]; @@ -33,21 +33,20 @@ export class Compiler { this.reporter, ).resolve(); - new SpecialLoopDesugarer(this.astCreator).desugar(mod.items); + new SpecialLoopDesugarer(this.astCreator).desugar(mod.ast); - new Resolver(this.reporter).resolve(mod.items); + new Resolver(this.reporter).resolve(mod.ast); - new CompoundAssignDesugarer(this.astCreator).desugar(mod.items); + new CompoundAssignDesugarer(this.astCreator).desugar(mod.ast); - new Checker(this.reporter).check(mod.items); + new Checker(this.reporter).check(mod.ast); if (this.reporter.errorOccured()) { console.error("Errors occurred, stopping compilation."); Deno.exit(1); } - const { monoFns, callMap } = new Monomorphizer(mod.items) - .monomorphize(); + const { monoFns, callMap } = new Monomorphizer(mod.ast).monomorphize(); const lastPos = await lastPosInTextFile(this.startFilePath); @@ -69,12 +68,12 @@ export class ModTree implements AstVisitor<[string]> { public resolve(): Mod { const entryAst = this.parseFile(this.entryFilePath); - visitItems(entryAst, this, this.entryFilePath); + visitStmts(entryAst, this, this.entryFilePath); - return { filePath: this.entryFilePath, items: entryAst }; + return { filePath: this.entryFilePath, ast: entryAst }; } - private parseFile(filePath: string): Item[] { + private parseFile(filePath: string): Stmt[] { const text = Deno.readTextFileSync(filePath); const lexer = new Lexer(text, this.reporter); @@ -89,13 +88,13 @@ export class ModTree implements AstVisitor<[string]> { if (stmt.kind.type !== "mod_block") { throw new Error(); } - const { ident, items } = stmt.kind; + const { ident, stmts: ast } = stmt.kind; stmt.kind = { type: "mod", ident, - mod: { filePath, items }, + mod: { filePath, ast }, }; - visitItems(items, this, filePath); + visitStmts(ast, this, filePath); return "stop"; } @@ -110,9 +109,9 @@ export class ModTree implements AstVisitor<[string]> { stmt.kind = { type: "mod", ident, - mod: { filePath, items: ast }, + mod: { filePath, ast }, }; - visitItems(ast, this, filePath); + visitStmts(ast, this, filePath); return "stop"; } } diff --git a/compiler/desugar/compound_assign.ts b/compiler/desugar/compound_assign.ts index d69f456..1ae6920 100644 --- a/compiler/desugar/compound_assign.ts +++ b/compiler/desugar/compound_assign.ts @@ -1,11 +1,11 @@ -import { AstCreator, Item, Stmt } from "../ast.ts"; -import { AstVisitor, visitItems, VisitRes, visitStmt } from "../ast_visitor.ts"; +import { AstCreator, Stmt } from "../ast.ts"; +import { AstVisitor, VisitRes, visitStmt, visitStmts } from "../ast_visitor.ts"; export class CompoundAssignDesugarer implements AstVisitor { public constructor(private astCreator: AstCreator) {} - public desugar(items: Item[]) { - visitItems(items, this); + public desugar(stmts: Stmt[]) { + visitStmts(stmts, this); } visitAssignStmt(stmt: Stmt): VisitRes { diff --git a/compiler/desugar/special_loop.ts b/compiler/desugar/special_loop.ts index 758e587..425e062 100644 --- a/compiler/desugar/special_loop.ts +++ b/compiler/desugar/special_loop.ts @@ -1,5 +1,5 @@ -import { AstCreator, Expr, ExprKind, Item, StmtKind } from "../ast.ts"; -import { AstVisitor, visitExpr, visitItems, VisitRes } from "../ast_visitor.ts"; +import { AstCreator, Expr, ExprKind, Stmt, StmtKind } from "../ast.ts"; +import { AstVisitor, visitExpr, VisitRes, visitStmts } 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(items: Item[]) { - visitItems(items, this); + public desugar(stmts: Stmt[]) { + visitStmts(stmts, this); } visitWhileExpr(expr: Expr): VisitRes { diff --git a/compiler/lexer.ts b/compiler/lexer.ts index 8f8f726..dea631a 100644 --- a/compiler/lexer.ts +++ b/compiler/lexer.ts @@ -48,7 +48,6 @@ 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 e1bcfc0..9ccc45b 100644 --- a/compiler/mono.ts +++ b/compiler/mono.ts @@ -1,15 +1,15 @@ -import { Expr, Item, Stmt } from "./ast.ts"; -import { AstVisitor, visitExpr, visitItems, VisitRes } from "./ast_visitor.ts"; +import { Expr, Stmt } from "./ast.ts"; +import { AstVisitor, visitExpr, VisitRes, visitStmts } 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: Item; + private allFns: Map; + private entryFn: Stmt; - constructor(private ast: Item[]) { + constructor(private ast: Stmt[]) { this.allFns = new AllFnsCollector().collect(this.ast); this.entryFn = findMain(this.allFns); } @@ -20,7 +20,7 @@ export class Monomorphizer { } private monomorphizeFn( - item: Item, + stmt: Stmt, 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: MonoFn = { id, nameGen, stmt, genericArgs }; + const 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; - item: Item; + stmt: Stmt; 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: Item[]): Map { - visitItems(ast, this); + public collect(ast: Stmt[]): Map { + visitStmts(ast, this); return this.allFns; } diff --git a/compiler/parser.ts b/compiler/parser.ts index 3bc5fa1..67c1004 100644 --- a/compiler/parser.ts +++ b/compiler/parser.ts @@ -8,7 +8,6 @@ import { Expr, ExprKind, GenericParam, - Item, Param, Stmt, StmtKind, @@ -31,94 +30,41 @@ export class Parser { this.currentToken = lexer.next(); } - public parse(): Item[] { - return this.parseItems(); + public parse(): Stmt[] { + return this.parseStmts(); } - private parseItems(): Item[] { - const items: Item[] = []; + private parseStmts(): Stmt[] { + const stmts: Stmt[] = []; while (!this.done()) { - items.push(this.parseItem()); + stmts.push(this.parseModStmt()); } - return items; + return stmts; } - 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(); + private parseModStmt(): Stmt { if (this.test("mod")) { - return this.parseMod(); + return (this.parseMod()); } else if (this.test("fn")) { - return this.parseFn(); - } else if (this.test("let")) { - const stmt = this.parseLet(); + return (this.parseFn()); + } else if ( + this.test("let") || this.test("return") || this.test("break") + ) { + const expr = this.parseSingleLineBlockStmt(); this.eatSemicolon(); - return stmt; + 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 { - this.report("expected item"); - return this.stmt({ type: "error" }, pos); + const expr = this.parseAssign(); + this.eatSemicolon(); + return expr; } } - private errorItem(pos: Pos): Item { - return { stmt: this.stmt({ type: "error" }, pos), pub: false, pos }; - } - private parseMod(): Stmt { const pos = this.pos(); this.step(); @@ -141,9 +87,9 @@ export class Parser { } this.step(); - const items: Item[] = []; + const stmts: Stmt[] = []; while (!this.done() && !this.test("}")) { - items.push(this.parseItem()); + stmts.push(this.parseModStmt()); } if (!this.test("}")) { @@ -152,7 +98,7 @@ export class Parser { } this.step(); - return this.stmt({ type: "mod_block", ident, items }, pos); + return this.stmt({ type: "mod_block", ident, stmts }, pos); } private parseMultiLineBlockExpr(): Expr { @@ -288,6 +234,14 @@ 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); @@ -301,11 +255,60 @@ 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 d141c18..2d06de4 100644 --- a/compiler/resolver.ts +++ b/compiler/resolver.ts @@ -1,9 +1,8 @@ -import { EType, Expr, Item, Stmt } from "./ast.ts"; +import { EType, Expr, Stmt } from "./ast.ts"; import { AstVisitor, visitEType, visitExpr, - visitItems, visitParam, VisitRes, visitStmt, @@ -23,10 +22,10 @@ export class Resolver implements AstVisitor<[Syms]> { public constructor(private reporter: Reporter) { } - public resolve(items: Item[]): VisitRes { + public resolve(stmts: Stmt[]): VisitRes { const syms = new EntryModSyms(); - this.scoutItems(items, syms); - visitItems(items, this, syms); + this.scoutFnStmts(stmts, syms); + visitStmts(stmts, this, syms); return "stop"; } @@ -36,8 +35,8 @@ export class Resolver implements AstVisitor<[Syms]> { } const modSyms = new ModSyms(syms); const { mod, ident } = stmt.kind; - this.scoutItems(mod.items, modSyms); - visitItems(mod.items, this, modSyms); + this.scoutFnStmts(mod.ast, modSyms); + visitStmts(mod.ast, this, modSyms); if (syms.definedLocally(ident)) { this.reportAlreadyDefined(ident, stmt.pos, syms); @@ -73,9 +72,8 @@ export class Resolver implements AstVisitor<[Syms]> { return "stop"; } - private scoutItems(items: Item[], syms: Syms) { - for (const item of items) { - const { stmt } = item; + private scoutFnStmts(stmts: Stmt[], syms: Syms) { + for (const stmt of stmts) { if (stmt.kind.type !== "fn") { continue; } @@ -188,15 +186,7 @@ export class Resolver implements AstVisitor<[Syms]> { throw new Error(); } const childSyms = new LeafSyms(syms); - 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, - ); + this.scoutFnStmts(expr.kind.stmts, 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 d9a901c..2ac04c9 100644 --- a/compiler/resolver_syms.ts +++ b/compiler/resolver_syms.ts @@ -13,13 +13,7 @@ export interface Syms { export class EntryModSyms implements Syms { private syms: SymMap = {}; - public constructor() { - this.syms["crate"] = { - type: "mod", - ident: "crate", - syms: this, - }; - } + public constructor() {} public define(ident: string, sym: Sym) { if (sym.type === "let") { @@ -71,9 +65,6 @@ 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 6bf1884..3f1bb76 100644 --- a/examples/annos.slg +++ b/examples/annos.slg @@ -1,6 +1,4 @@ - -#[builtin(Print)] -fn print(msg: string) { +fn print(msg: string) #[builtin(print)] { "hello" + 0 }