details, not trailing anno

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

View File

@ -10,6 +10,7 @@ export type Mod = {
export type Stmt = {
kind: StmtKind;
pos: Pos;
details?: StmtDetails;
id: number;
};
@ -27,7 +28,6 @@ export type StmtKind =
params: Param[];
returnType?: EType;
body: Expr;
anno?: Anno;
vtype?: VType;
}
| { type: "let"; param: Param; value: Expr }
@ -36,6 +36,11 @@ export type StmtKind =
export type AssignType = "=" | "+=" | "-=";
export type StmtDetails = {
pub: boolean;
annos: Anno[];
};
export type Expr = {
kind: ExprKind;
pos: Pos;
@ -147,17 +152,17 @@ export type GenericParam = {
export type Anno = {
ident: string;
values: Expr[];
args: Expr[];
pos: Pos;
};
export class AstCreator {
private nextNodeId = 0;
public stmt(kind: StmtKind, pos: Pos): Stmt {
public stmt(kind: StmtKind, pos: Pos, details?: StmtDetails): Stmt {
const id = this.nextNodeId;
this.nextNodeId += 1;
return { kind, pos, id };
return { kind, pos, details, id };
}
public expr(kind: ExprKind, pos: Pos): Expr {
@ -172,3 +177,21 @@ export class AstCreator {
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 { Pos } from "./token.ts";
import {
@ -162,12 +162,12 @@ export class Checker {
throw new Error();
}
if (
stmt.kind.anno?.ident === "remainder" ||
stmt.kind.anno?.ident === "builtin"
) {
const annos = new AnnoView(stmt.details);
if (annos.has("builtin", "remainder")) {
// NOTE: handled in lowerer
return;
}
const { returnType } = stmt.kind.vtype!;
if (returnType.type === "error") return returnType;
this.fnReturnStack.push(returnType);

View File

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

View File

@ -1,6 +1,6 @@
import { Builtins, Ops } from "./arch.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 { MonoCallNameGenMap, MonoFn, MonoFnsMap } from "./mono.ts";
import { Pos } from "./token.ts";
@ -111,9 +111,15 @@ class MonoFnLowerer {
for (const { ident } of stmt.kind.params) {
this.locals.allocSym(ident);
}
if (stmt.kind.anno?.ident === "builtin") {
this.lowerFnBuiltinBody(stmt.kind.anno.values);
} else if (stmt.kind.anno?.ident === "remainder") {
const annos = new AnnoView(stmt.details);
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);
} else {
this.lowerExpr(stmt.kind.body);

View File

@ -10,6 +10,7 @@ import {
GenericParam,
Param,
Stmt,
StmtDetails,
StmtKind,
UnaryType,
} from "./ast.ts";
@ -37,18 +38,18 @@ export class Parser {
private parseStmts(): Stmt[] {
const stmts: Stmt[] = [];
while (!this.done()) {
stmts.push(this.parseModStmt());
stmts.push(this.parseStmt());
}
return stmts;
}
private parseModStmt(): Stmt {
if (this.test("mod")) {
return (this.parseMod());
} else if (this.test("fn")) {
return (this.parseFn());
private parseStmt(): Stmt {
if (
["#", "pub", "mod", "fn"].some((tt) => this.test(tt))
) {
return this.parseItemStmt();
} else if (
this.test("let") || this.test("return") || this.test("break")
["let", "return", "break"].some((tt) => this.test(tt))
) {
const expr = this.parseSingleLineBlockStmt();
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 {
const pos = this.pos();
if (this.test("{")) {
@ -160,13 +125,14 @@ export class Parser {
this.step();
return this.expr({ type: "block", stmts }, pos);
} 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());
this.eatSemicolon();
} else if (this.test("fn")) {
stmts.push(this.parseSingleLineBlockStmt());
stmts.push(this.parseFn());
} else if (
["{", "if", "loop", "while", "for"].some((tt) => this.test(tt))
) {
@ -210,7 +176,103 @@ export class Parser {
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();
this.step();
if (!this.test("ident")) {
@ -234,14 +296,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 +309,12 @@ export class Parser {
params,
returnType,
body,
anno,
},
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[] {
return this.parseDelimitedList(this.parseETypeParam, ">", ",");
}
@ -920,8 +926,8 @@ export class Parser {
printStackTrace();
}
private stmt(kind: StmtKind, pos: Pos): Stmt {
return this.astCreator.stmt(kind, pos);
private stmt(kind: StmtKind, pos: Pos, details?: StmtDetails): Stmt {
return this.astCreator.stmt(kind, pos, details);
}
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
}