details, not trailing anno

This commit is contained in:
sfja 2024-12-31 00:12:34 +01:00
parent 94a57029c0
commit f2b1323337
6 changed files with 161 additions and 121 deletions

View File

@ -10,6 +10,7 @@ export type Mod = {
export type Stmt = { export type Stmt = {
kind: StmtKind; kind: StmtKind;
pos: Pos; pos: Pos;
details?: StmtDetails;
id: number; id: number;
}; };
@ -27,7 +28,6 @@ export type StmtKind =
params: Param[]; params: Param[];
returnType?: EType; returnType?: EType;
body: Expr; body: Expr;
anno?: Anno;
vtype?: VType; vtype?: VType;
} }
| { type: "let"; param: Param; value: Expr } | { type: "let"; param: Param; value: Expr }
@ -36,6 +36,11 @@ export type StmtKind =
export type AssignType = "=" | "+=" | "-="; export type AssignType = "=" | "+=" | "-=";
export type StmtDetails = {
pub: boolean;
annos: Anno[];
};
export type Expr = { export type Expr = {
kind: ExprKind; kind: ExprKind;
pos: Pos; pos: Pos;
@ -147,17 +152,17 @@ export type GenericParam = {
export type Anno = { export type Anno = {
ident: string; ident: string;
values: Expr[]; args: Expr[];
pos: Pos; pos: Pos;
}; };
export class AstCreator { export class AstCreator {
private nextNodeId = 0; private nextNodeId = 0;
public stmt(kind: StmtKind, pos: Pos): Stmt { public stmt(kind: StmtKind, pos: Pos, details?: StmtDetails): Stmt {
const id = this.nextNodeId; const id = this.nextNodeId;
this.nextNodeId += 1; this.nextNodeId += 1;
return { kind, pos, id }; return { kind, pos, details, id };
} }
public expr(kind: ExprKind, pos: Pos): Expr { public expr(kind: ExprKind, pos: Pos): Expr {
@ -172,3 +177,21 @@ export class AstCreator {
return { kind, pos, id }; return { kind, pos, id };
} }
} }
export class AnnoView {
public constructor(private details?: StmtDetails) {}
public has(...idents: string[]): boolean {
return this.details?.annos.some((anno) =>
idents.some((ident) => anno.ident === ident)
) ?? false;
}
public get(ident: string): Anno {
const anno = this.details?.annos.find((anno) => anno.ident === ident);
if (!anno) {
throw new Error();
}
return anno;
}
}

View File

@ -1,4 +1,4 @@
import { EType, Expr, Stmt, Sym } from "./ast.ts"; import { AnnoView, EType, Expr, Stmt, Sym } from "./ast.ts";
import { printStackTrace, Reporter } from "./info.ts"; import { printStackTrace, Reporter } from "./info.ts";
import { Pos } from "./token.ts"; import { Pos } from "./token.ts";
import { import {
@ -162,12 +162,12 @@ export class Checker {
throw new Error(); throw new Error();
} }
if ( const annos = new AnnoView(stmt.details);
stmt.kind.anno?.ident === "remainder" || if (annos.has("builtin", "remainder")) {
stmt.kind.anno?.ident === "builtin" // NOTE: handled in lowerer
) {
return; return;
} }
const { returnType } = stmt.kind.vtype!; const { returnType } = stmt.kind.vtype!;
if (returnType.type === "error") return returnType; if (returnType.type === "error") return returnType;
this.fnReturnStack.push(returnType); this.fnReturnStack.push(returnType);

View File

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

View File

@ -1,6 +1,6 @@
import { Builtins, Ops } from "./arch.ts"; import { Builtins, Ops } from "./arch.ts";
import { Assembler, Label } from "./assembler.ts"; import { Assembler, Label } from "./assembler.ts";
import { Expr, Stmt } from "./ast.ts"; import { AnnoView, Expr, Stmt } from "./ast.ts";
import { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts"; import { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts";
import { MonoCallNameGenMap, MonoFn, MonoFnsMap } from "./mono.ts"; import { MonoCallNameGenMap, MonoFn, MonoFnsMap } from "./mono.ts";
import { Pos } from "./token.ts"; import { Pos } from "./token.ts";
@ -111,9 +111,15 @@ class MonoFnLowerer {
for (const { ident } of stmt.kind.params) { for (const { ident } of stmt.kind.params) {
this.locals.allocSym(ident); this.locals.allocSym(ident);
} }
if (stmt.kind.anno?.ident === "builtin") {
this.lowerFnBuiltinBody(stmt.kind.anno.values); const annos = new AnnoView(stmt.details);
} else if (stmt.kind.anno?.ident === "remainder") { if (annos.has("builtin")) {
const anno = annos.get("builtin");
if (!anno) {
throw new Error();
}
this.lowerFnBuiltinBody(anno.args);
} else if (annos.has("remainder")) {
this.program.add(Ops.Remainder); this.program.add(Ops.Remainder);
} else { } else {
this.lowerExpr(stmt.kind.body); this.lowerExpr(stmt.kind.body);

View File

@ -10,6 +10,7 @@ import {
GenericParam, GenericParam,
Param, Param,
Stmt, Stmt,
StmtDetails,
StmtKind, StmtKind,
UnaryType, UnaryType,
} from "./ast.ts"; } from "./ast.ts";
@ -37,18 +38,18 @@ export class Parser {
private parseStmts(): Stmt[] { private parseStmts(): Stmt[] {
const stmts: Stmt[] = []; const stmts: Stmt[] = [];
while (!this.done()) { while (!this.done()) {
stmts.push(this.parseModStmt()); stmts.push(this.parseStmt());
} }
return stmts; return stmts;
} }
private parseModStmt(): Stmt { private parseStmt(): Stmt {
if (this.test("mod")) { if (
return (this.parseMod()); ["#", "pub", "mod", "fn"].some((tt) => this.test(tt))
} else if (this.test("fn")) { ) {
return (this.parseFn()); return this.parseItemStmt();
} else if ( } else if (
this.test("let") || this.test("return") || this.test("break") ["let", "return", "break"].some((tt) => this.test(tt))
) { ) {
const expr = this.parseSingleLineBlockStmt(); const expr = this.parseSingleLineBlockStmt();
this.eatSemicolon(); this.eatSemicolon();
@ -65,42 +66,6 @@ export class Parser {
} }
} }
private parseMod(): Stmt {
const pos = this.pos();
this.step();
if (!this.test("ident")) {
this.report("expected 'ident'");
return this.stmt({ type: "error" }, pos);
}
const ident = this.current().identValue!;
this.step();
if (this.test("string")) {
const filePath = this.current().stringValue!;
this.step();
this.eatSemicolon();
return this.stmt({ type: "mod_file", ident, filePath }, pos);
}
if (!this.test("{")) {
this.report("expected '{' or 'string'");
return this.stmt({ type: "error" }, pos);
}
this.step();
const stmts: Stmt[] = [];
while (!this.done() && !this.test("}")) {
stmts.push(this.parseModStmt());
}
if (!this.test("}")) {
this.report("expected '}'");
return this.stmt({ type: "error" }, pos);
}
this.step();
return this.stmt({ type: "mod_block", ident, stmts }, pos);
}
private parseMultiLineBlockExpr(): Expr { private parseMultiLineBlockExpr(): Expr {
const pos = this.pos(); const pos = this.pos();
if (this.test("{")) { if (this.test("{")) {
@ -160,13 +125,14 @@ export class Parser {
this.step(); this.step();
return this.expr({ type: "block", stmts }, pos); return this.expr({ type: "block", stmts }, pos);
} else if ( } else if (
this.test("return") || this.test("break") || this.test("let") ["#", "pub", "mod", "fn"].some((tt) => this.test(tt))
) {
stmts.push(this.parseItemStmt());
} else if (
["let", "return", "break"].some((tt) => this.test(tt))
) { ) {
stmts.push(this.parseSingleLineBlockStmt()); stmts.push(this.parseSingleLineBlockStmt());
this.eatSemicolon(); this.eatSemicolon();
} else if (this.test("fn")) {
stmts.push(this.parseSingleLineBlockStmt());
stmts.push(this.parseFn());
} else if ( } else if (
["{", "if", "loop", "while", "for"].some((tt) => this.test(tt)) ["{", "if", "loop", "while", "for"].some((tt) => this.test(tt))
) { ) {
@ -210,7 +176,103 @@ export class Parser {
return this.expr({ type: "error" }, pos); return this.expr({ type: "error" }, pos);
} }
private parseFn(): Stmt { private parseItemStmt(
pos = this.pos(),
details: StmtDetails = {
pub: false,
annos: [],
},
): Stmt {
const spos = this.pos();
if (this.test("#") && !details.pub) {
this.step();
if (!this.test("[")) {
this.report("expected '['");
return this.stmt({ type: "error" }, spos);
}
this.step();
if (!this.test("ident")) {
this.report("expected 'ident'");
return this.stmt({ type: "error" }, spos);
}
const ident = this.current().identValue!;
this.step();
const args: Expr[] = [];
if (this.test("(")) {
this.step();
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.stmt({ type: "error" }, spos);
}
this.step();
}
if (!this.test("]")) {
this.report("expected ']'");
return this.stmt({ type: "error" }, spos);
}
this.step();
const anno = { ident, args, pos: spos };
return this.parseItemStmt(pos, {
...details,
annos: [...details.annos, anno],
});
} else if (this.test("pub") && !details.pub) {
this.step();
return this.parseItemStmt(pos, { ...details, pub: true });
} else if (this.test("mod")) {
return this.parseMod(details);
} else if (this.test("fn")) {
return this.parseFn(details);
} else {
this.report("expected item statement");
return this.stmt({ type: "error" }, pos);
}
}
private parseMod(details: StmtDetails): Stmt {
const pos = this.pos();
this.step();
if (!this.test("ident")) {
this.report("expected 'ident'");
return this.stmt({ type: "error" }, pos);
}
const ident = this.current().identValue!;
this.step();
if (this.test("string")) {
const filePath = this.current().stringValue!;
this.step();
this.eatSemicolon();
return this.stmt({ type: "mod_file", ident, filePath }, pos);
}
if (!this.test("{")) {
this.report("expected '{' or 'string'");
return this.stmt({ type: "error" }, pos);
}
this.step();
const stmts: Stmt[] = [];
while (!this.done() && !this.test("}")) {
stmts.push(this.parseStmt());
}
if (!this.test("}")) {
this.report("expected '}'");
return this.stmt({ type: "error" }, pos);
}
this.step();
return this.stmt({ type: "mod_block", ident, stmts }, pos, details);
}
private parseFn(details: StmtDetails): Stmt {
const pos = this.pos(); const pos = this.pos();
this.step(); this.step();
if (!this.test("ident")) { if (!this.test("ident")) {
@ -234,14 +296,6 @@ export class Parser {
returnType = this.parseEType(); 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("{")) { if (!this.test("{")) {
this.report("expected block"); this.report("expected block");
return this.stmt({ type: "error" }, pos); return this.stmt({ type: "error" }, pos);
@ -255,60 +309,12 @@ export class Parser {
params, params,
returnType, returnType,
body, body,
anno,
}, },
pos, pos,
details,
); );
} }
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[] { private parseFnETypeParams(): GenericParam[] {
return this.parseDelimitedList(this.parseETypeParam, ">", ","); return this.parseDelimitedList(this.parseETypeParam, ">", ",");
} }
@ -920,8 +926,8 @@ export class Parser {
printStackTrace(); printStackTrace();
} }
private stmt(kind: StmtKind, pos: Pos): Stmt { private stmt(kind: StmtKind, pos: Pos, details?: StmtDetails): Stmt {
return this.astCreator.stmt(kind, pos); return this.astCreator.stmt(kind, pos, details);
} }
private expr(kind: ExprKind, pos: Pos): Expr { private expr(kind: ExprKind, pos: Pos): Expr {

View File

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