Compare commits

..

No commits in common. "ca1d67a54a028cf4f423d15d48e4e4ae68f4f48a" and "7b879d30be1475c95a855945b8f3e84d81ffd191" have entirely different histories.

7 changed files with 142 additions and 203 deletions

View File

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

View File

@ -48,8 +48,6 @@ 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 { AnnoView, Expr, Stmt } from "./ast.ts";
import { 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,15 +111,9 @@ class MonoFnLowerer {
for (const { ident } of stmt.kind.params) {
this.locals.allocSym(ident);
}
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")) {
if (stmt.kind.anno?.ident === "builtin") {
this.lowerFnBuiltinBody(stmt.kind.anno.values);
} else if (stmt.kind.anno?.ident === "remainder") {
this.program.add(Ops.Remainder);
} else {
this.lowerExpr(stmt.kind.body);

View File

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

View File

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

View File

@ -1,54 +1,33 @@
// stdlib.slg
#[builtin(Exit)]
fn exit(status_code: int) {}
fn exit(status_code: int) #[builtin(Exit)] {}
#[builtin(Print)]
fn print(msg: string) {}
msg + "\n"
fn println(msg: string) { print) }
fn print(msg: string) #[builtin(Print)] {}
fn println(msg: string) { print(msg + "\n") }
#[builtin(IntToString)]
fn int_to_string(number: int) -> string {}
fn int_to_string(number: int) -> string #[builtin(IntToString)] {}
#[builtin(StringPushChar)]
fn string_push_char(str: string, value: int) -> string {}
#[builtin(StringCharAt)]
fn string_char_at(str: string, index: int) -> int {}
#[builtin(StringLength)]
fn string_length(str: string) -> int {}
#[builtin(StringToInt)]
fn string_to_int(str: string) -> int {}
fn string_push_char(str: string, value: int) -> string #[builtin(StringPushChar)] {}
fn string_char_at(str: string, index: int) -> int #[builtin(StringCharAt)] {}
fn string_length(str: string) -> int #[builtin(StringLength)] {}
fn string_to_int(str: string) -> int #[builtin(StringToInt)] {}
#[builtin(ArrayNew)]
fn array_new<T>() -> [T] {}
#[builtin(ArrayPush)]
fn array_push<T>(array: [T], value: T) {}
#[builtin(ArrayLength)]
fn array_length<T>(array: [T]) -> int {}
#[builtin(ArrayAt)]
fn array_at<T>(array: [T], index: int) -> T {}
fn array_new<T>() -> [T] #[builtin(ArrayNew)] {}
fn array_push<T>(array: [T], value: T) #[builtin(ArrayPush)] {}
fn array_length<T>(array: [T]) -> int #[builtin(ArrayLength)] {}
fn array_at<T>(array: [T], index: int) -> T #[builtin(ArrayAt)] {}
#[builtin(FileOpen)]
fn file_open(filename: string, mode: string) -> int {}
#[builtin(FileClose)]
fn file_close(file: int) {}
#[builtin(FileWriteString)]
fn file_write_string(file: int, content: string) -> int {}
#[builtin(FileReadChar)]
fn file_read_char(file: int) -> int {}
#[builtin(FileReadToString)]
fn file_read_to_string(file: int) -> string {}
#[builtin(FileFlush)]
fn file_flush(file: int) {}
#[builtin(FileEof)]
fn file_eof(file: int) -> bool {}
fn file_open(filename: string, mode: string) -> int #[builtin(FileOpen)] {}
fn file_close(file: int) #[builtin(FileClose)] {}
fn file_write_string(file: int, content: string) -> int #[builtin(FileWriteString)] {}
fn file_read_char(file: int) -> int #[builtin(FileReadChar)] {}
fn file_read_to_string(file: int) -> string #[builtin(FileReadToString)] {}
fn file_flush(file: int) #[builtin(FileFlush)] {}
fn file_eof(file: int) -> bool #[builtin(FileEof)] {}
#[builtin(IntToString)]
fn itos(number: int) -> string {}
#[builtin(StringToInt)]
fn stoi(str: string) -> int {}
fn itos(number: int) -> string #[builtin(IntToString)] {}
fn stoi(str: string) -> int #[builtin(StringToInt)] {}
fn stdin() -> int { 0 }
fn stdout() -> int { 1 }