this commit kinda sucks

This commit is contained in:
sfja 2024-12-30 21:12:28 +01:00
parent 852df09ac9
commit 7ca1ff1e25
12 changed files with 221 additions and 156 deletions

View File

@ -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;
};

View File

@ -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;

View File

@ -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);
}

View File

@ -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";
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -48,6 +48,7 @@ export class Lexer {
"for",
"in",
"mod",
"pub",
];
if (keywords.includes(value)) {
return this.token(value, pos);

View File

@ -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;
}

View File

@ -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, ">", ",");
}

View File

@ -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);

View File

@ -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] };
}

View File

@ -1,4 +1,6 @@
fn print(msg: string) #[builtin(print)] {
#[builtin(Print)]
fn print(msg: string) {
"hello" + 0
}