Compare commits

..

No commits in common. "f3523e486b4cf6ecd58be8f608005b46dfcd139c" and "916c2376a0723a8777634649cd3096931944bcdc" have entirely different histories.

23 changed files with 186 additions and 524 deletions

1
.gitignore vendored
View File

@ -1 +0,0 @@
out.slgbc

View File

@ -43,23 +43,14 @@ export class Assembler {
} }
public assemble(): number[] { public assemble(): number[] {
console.log("Assembling...");
let ip = 0; let ip = 0;
const output: number[] = []; const output: number[] = [];
const locs: { [key: string]: number } = {}; const locs: { [key: string]: number } = {};
const refs: { [key: number]: string } = {}; const refs: { [key: number]: string } = {};
const debugLines: {
startIp: number;
endIp: number;
insString: string;
}[] = [];
for (const line of this.lines) { for (const line of this.lines) {
for (const label of line.labels ?? []) { for (const label of line.labels ?? []) {
locs[label] = ip; locs[label] = ip;
} }
const startIp = ip;
for (const lit of line.ins as Lit[]) { for (const lit of line.ins as Lit[]) {
if (typeof lit === "number") { if (typeof lit === "number") {
output.push(lit); output.push(lit);
@ -78,11 +69,6 @@ export class Assembler {
ip += 1; ip += 1;
} }
} }
debugLines.push({
startIp,
endIp: ip,
insString: this.insToString(line.ins),
});
} }
for (let i = 0; i < output.length; ++i) { for (let i = 0; i < output.length; ++i) {
if (!(i in refs)) { if (!(i in refs)) {
@ -96,14 +82,6 @@ export class Assembler {
} }
output[i] = locs[refs[i]]; output[i] = locs[refs[i]];
} }
for (const line of debugLines) {
console.log(
line.startIp.toString().padStart(3, " ") + " " +
output.slice(line.startIp, line.endIp).join(", ") + "\n" +
line.insString + "\n",
);
}
return output; return output;
} }
@ -132,25 +110,4 @@ export class Assembler {
console.log(` ${op} ${args}`); console.log(` ${op} ${args}`);
} }
} }
private insToString(ins: Ins): string {
const op = opToString(ins[0] as number)
.padEnd(13, " ");
const args = (ins.slice(1) as Lit[]).map((lit) => {
if (typeof lit === "number") {
return lit;
} else if (typeof lit === "boolean") {
return lit.toString();
} else if (typeof lit === "string") {
return '"' +
lit.replaceAll("\\", "\\\\").replaceAll("\0", "\\0")
.replaceAll("\n", "\\n").replaceAll("\t", "\\t")
.replaceAll("\r", "\\r") +
'"';
} else {
return lit.label;
}
}).join(", ");
return ` ${op} ${args}`;
}
} }

View File

@ -1,6 +1,5 @@
import { StmtKind } from "./ast.ts"; import { StmtKind } from "./ast.ts";
import { EType, Expr, Stmt } from "./ast.ts"; import { EType, Expr, Stmt } from "./ast.ts";
import { printStackTrace, Reporter } from "./info.ts";
import { Pos } from "./token.ts"; import { Pos } from "./token.ts";
import { VType, VTypeParam, vtypesEqual, vtypeToString } from "./vtype.ts"; import { VType, VTypeParam, vtypesEqual, vtypeToString } from "./vtype.ts";
@ -8,37 +7,12 @@ export class Checker {
private fnReturnStack: VType[] = []; private fnReturnStack: VType[] = [];
private loopBreakStack: VType[][] = []; private loopBreakStack: VType[][] = [];
public constructor(private reporter: Reporter) {}
public check(stmts: Stmt[]) { public check(stmts: Stmt[]) {
this.checkFnHeaders(stmts);
for (const stmt of stmts) { for (const stmt of stmts) {
this.checkStmt(stmt); this.checkStmt(stmt);
} }
} }
private checkFnHeaders(stmts: Stmt[]) {
for (const stmt of stmts) {
if (stmt.kind.type !== "fn") {
continue;
}
const returnType: VType = stmt.kind.returnType
? this.checkEType(stmt.kind.returnType)
: { type: "null" };
const params: VTypeParam[] = [];
for (const param of stmt.kind.params) {
if (param.etype === undefined) {
this.report("parameter types must be defined", param.pos);
stmt.kind.vtype = { type: "error" };
}
const vtype = this.checkEType(param.etype!);
param.vtype = vtype;
params.push({ ident: param.ident, vtype });
}
stmt.kind.vtype = { type: "fn", params, returnType };
}
}
public checkStmt(stmt: Stmt) { public checkStmt(stmt: Stmt) {
switch (stmt.kind.type) { switch (stmt.kind.type) {
case "error": case "error":
@ -116,18 +90,28 @@ export class Checker {
throw new Error(); throw new Error();
} }
const pos = stmt.pos; const pos = stmt.pos;
if (stmt.kind.vtype!.type !== "fn") { const returnType: VType = stmt.kind.returnType
throw new Error(); ? this.checkEType(stmt.kind.returnType)
: { type: "null" };
const params: VTypeParam[] = [];
for (const param of stmt.kind.params) {
if (param.etype === undefined) {
this.report("parameter types must be defined", param.pos);
stmt.kind.vtype = { type: "error" };
} }
const { returnType } = stmt.kind.vtype!; const vtype = this.checkEType(param.etype!);
param.vtype = vtype;
params.push({ ident: param.ident, vtype });
}
stmt.kind.vtype = { type: "fn", params, returnType };
this.fnReturnStack.push(returnType); this.fnReturnStack.push(returnType);
const body = this.checkExpr(stmt.kind.body); const body = this.checkExpr(stmt.kind.body);
this.fnReturnStack.pop(); this.fnReturnStack.pop();
if (!vtypesEqual(returnType, body)) { if (!vtypesEqual(returnType, body)) {
this.report( this.report(
`incompatible return type` + `incompatible return type` +
`, expected '${vtypeToString(returnType)}'` + `, got ${body}` +
`, got '${vtypeToString(body)}'`, `, expected ${returnType}`,
pos, pos,
); );
} }
@ -222,8 +206,7 @@ export class Checker {
this.report( this.report(
`cannot assign to incompatible type` + `cannot assign to incompatible type` +
`, got '${vtypeToString(value)}'` + `, got '${vtypeToString(value)}'` +
`, expected '${ `, expected '${vtypeToString(
vtypeToString(
stmt.kind.subject.kind.sym.param.vtype!, stmt.kind.subject.kind.sym.param.vtype!,
) )
}'`, }'`,
@ -426,8 +409,7 @@ export class Checker {
} }
this.report( this.report(
`cannot apply binary operation '${expr.kind.binaryType}' ` + `cannot apply binary operation '${expr.kind.binaryType}' ` +
`on types '${vtypeToString(left)}' and '${ `on types '${vtypeToString(left)}' and '${vtypeToString(right)
vtypeToString(right)
}'`, }'`,
pos, pos,
); );
@ -519,7 +501,7 @@ export class Checker {
if (expr.kind.type !== "block") { if (expr.kind.type !== "block") {
throw new Error(); throw new Error();
} }
this.checkFnHeaders(expr.kind.stmts); const pos = expr.pos;
for (const stmt of expr.kind.stmts) { for (const stmt of expr.kind.stmts) {
this.checkStmt(stmt); this.checkStmt(stmt);
} }
@ -592,8 +574,20 @@ export class Checker {
} }
private report(msg: string, pos: Pos) { private report(msg: string, pos: Pos) {
this.reporter.reportError({ reporter: "Checker", msg, pos }); console.error(`Checker: ${msg} at ${pos.line}:${pos.col}`);
printStackTrace(); class ReportNotAnError extends Error {
constructor() {
super("ReportNotAnError");
}
}
try {
throw new ReportNotAnError();
} catch (error) {
if (!(error instanceof ReportNotAnError)) {
throw error;
}
console.log(error);
}
} }
} }
@ -603,7 +597,7 @@ const simpleUnaryOperations: {
result?: VType; result?: VType;
}[] = [ }[] = [
{ unaryType: "not", operand: { type: "bool" } }, { unaryType: "not", operand: { type: "bool" } },
]; ];
const simpleBinaryOperations: { const simpleBinaryOperations: {
binaryType: string; binaryType: string;
@ -633,4 +627,4 @@ const simpleBinaryOperations: {
{ binaryType: ">", operand: { type: "int" }, result: { type: "bool" } }, { binaryType: ">", operand: { type: "int" }, result: { type: "bool" } },
{ binaryType: "<=", operand: { type: "int" }, result: { type: "bool" } }, { binaryType: "<=", operand: { type: "int" }, result: { type: "bool" } },
{ binaryType: ">=", operand: { type: "int" }, result: { type: "bool" } }, { binaryType: ">=", operand: { type: "int" }, result: { type: "bool" } },
]; ];

View File

@ -1,55 +0,0 @@
import { Pos } from "./token.ts";
export type Report = {
type: "error" | "note";
reporter: string;
pos?: Pos;
msg: string;
};
export class Reporter {
private reports: Report[] = [];
private errorSet = false;
public reportError(report: Omit<Report, "type">) {
this.reports.push({ ...report, type: "error" });
this.printReport({ ...report, type: "error" });
this.errorSet = true;
}
private printReport({ reporter, type, pos, msg }: Report) {
console.error(
`${reporter}: ${type}: ${msg}${
pos ? ` at ${pos.line}:${pos.col}` : ""
}`,
);
}
public addNote(report: Omit<Report, "type">) {
this.reports.push({ ...report, type: "note" });
this.printReport({ ...report, type: "note" });
}
public errorOccured(): boolean {
return this.errorSet;
}
}
export function printStackTrace() {
class ReportNotAnError extends Error {
constructor() {
super("ReportNotAnError");
}
}
try {
throw new ReportNotAnError();
} catch (error) {
if (!(error instanceof ReportNotAnError)) {
throw error;
}
console.log(
error.stack?.replace("Error: ReportNotAnError", "Stack trace:") ??
error,
);
}
}

View File

@ -1,4 +1,3 @@
import { Reporter } from "./info.ts";
import { Pos, Token } from "./token.ts"; import { Pos, Token } from "./token.ts";
export class Lexer { export class Lexer {
@ -6,7 +5,7 @@ export class Lexer {
private line = 1; private line = 1;
private col = 1; private col = 1;
public constructor(private text: string, private reporter: Reporter) {} public constructor(private text: string) {}
public next(): Token | null { public next(): Token | null {
if (this.done()) { if (this.done()) {
@ -59,7 +58,10 @@ export class Lexer {
if (this.test("0")) { if (this.test("0")) {
this.step(); this.step();
if (!this.done() && this.test(/[0-9]/)) { if (!this.done() && this.test(/[0-9]/)) {
this.report("invalid number", pos); console.error(
`Lexer: invalid number` +
` at ${pos.line}:${pos.col}`,
);
return this.token("error", pos); return this.token("error", pos);
} }
return { ...this.token("int", pos), intValue: 0 }; return { ...this.token("int", pos), intValue: 0 };
@ -85,7 +87,10 @@ export class Lexer {
this.step(); this.step();
} }
if (this.done() || !this.test('"')) { if (this.done() || !this.test('"')) {
this.report("unclosed/malformed string", pos); console.error(
`Lexer: unclosed/malformed string` +
` at ${pos.line}:${pos.col}`,
);
return this.token("error", pos); return this.token("error", pos);
} }
this.step(); this.step();
@ -184,7 +189,9 @@ export class Lexer {
this.step(); this.step();
return this.token("return", pos); return this.token("return", pos);
} }
this.report(`illegal character '${this.current()}'`, pos); console.error(
`Lexer: illegal character '${this.current()}' at ${pos.line}:${pos.col}`,
);
this.step(); this.step();
return this.next(); return this.next();
} }
@ -234,12 +241,4 @@ export class Lexer {
return pattern.test(this.current()); return pattern.test(this.current());
} }
} }
private report(msg: string, pos: Pos) {
this.reporter.reportError({
msg,
pos,
reporter: "Lexer",
});
}
} }

View File

@ -12,34 +12,15 @@ export class Lowerer {
private breakStack: Label[] = []; private breakStack: Label[] = [];
public lower(stmts: Stmt[]) { public lower(stmts: Stmt[]) {
this.program.add(Ops.PushPtr, { label: "_start" });
this.program.add(Ops.Jump);
this.scoutFnHeaders(stmts);
for (const stmt of stmts) { for (const stmt of stmts) {
this.lowerStaticStmt(stmt); this.lowerStaticStmt(stmt);
} }
this.program.setLabel({ label: "_start" });
this.program.add(Ops.PushPtr, { label: "main" });
this.program.add(Ops.Call, 0);
this.program.add(Ops.Pop);
} }
public finish(): number[] { public finish(): number[] {
return this.program.assemble(); return this.program.assemble();
} }
private scoutFnHeaders(stmts: Stmt[]) {
for (const stmt of stmts) {
if (stmt.kind.type !== "fn") {
continue;
}
const label = stmt.kind.ident === "main"
? "main"
: `${stmt.kind.ident}_${stmt.id}`;
this.fnStmtIdLabelMap[stmt.id] = label;
}
}
private lowerStaticStmt(stmt: Stmt) { private lowerStaticStmt(stmt: Stmt) {
switch (stmt.kind.type) { switch (stmt.kind.type) {
case "fn": case "fn":
@ -120,43 +101,26 @@ export class Lowerer {
if (stmt.kind.type !== "fn") { if (stmt.kind.type !== "fn") {
throw new Error(); throw new Error();
} }
const label = stmt.kind.ident === "main" const label = `${stmt.kind.ident}_${stmt.id}`;
? "main" this.fnStmtIdLabelMap[stmt.id] = label;
: `${stmt.kind.ident}_${stmt.id}`;
this.program.setLabel({ label }); this.program.setLabel({ label });
const outerLocals = this.locals; const outerLocals = this.locals;
const fnRoot = new LocalsFnRoot(outerLocals); this.locals = new LocalsFnRoot(outerLocals);
const outerProgram = this.program; const outerProgram = this.program;
this.program = new Assembler(); this.program = new Assembler();
this.locals = fnRoot;
for (const { ident } of stmt.kind.params) { for (const { ident } of stmt.kind.params) {
this.locals.allocSym(ident); this.locals.allocSym(ident);
this.program.add(
Ops.StoreLocal,
this.locals.symId(ident),
);
} }
this.locals.allocSym("::return");
this.program.add(Ops.PushNull);
this.lowerExpr(stmt.kind.body); this.lowerExpr(stmt.kind.body);
this.program.add(Ops.StoreLocal, this.locals.symId("::return"));
this.locals = outerLocals;
for (
let i = 0;
i < fnRoot.stackReserved() - 1 - stmt.kind.params.length;
++i
) {
outerProgram.add(Ops.PushNull);
this.program.add(Ops.Pop);
}
if (stmt.kind.params.length >= 1) {
this.program.add(Ops.StoreLocal, 0);
}
for (let i = 0; i < stmt.kind.params.length - 1; ++i) {
this.program.add(Ops.Pop);
}
this.program.add(Ops.Return); this.program.add(Ops.Return);
this.locals = outerLocals;
outerProgram.concat(this.program); outerProgram.concat(this.program);
this.program = outerProgram; this.program = outerProgram;
} }
@ -231,7 +195,7 @@ export class Lowerer {
} }
if (expr.kind.sym.type === "fn") { if (expr.kind.sym.type === "fn") {
const label = this.fnStmtIdLabelMap[expr.kind.sym.stmt.id]; const label = this.fnStmtIdLabelMap[expr.kind.sym.stmt.id];
this.program.add(Ops.PushPtr, { label }); this.program.add(Ops.PushPtr, label);
return; return;
} }
throw new Error(`unhandled sym type '${expr.kind.sym.type}'`); throw new Error(`unhandled sym type '${expr.kind.sym.type}'`);
@ -308,7 +272,6 @@ export class Lowerer {
this.lowerExpr(arg); this.lowerExpr(arg);
} }
this.lowerExpr(expr.kind.subject); this.lowerExpr(expr.kind.subject);
this.program.add(Ops.Call, expr.kind.args.length);
} }
private lowerIfExpr(expr: Expr) { private lowerIfExpr(expr: Expr) {
@ -322,13 +285,11 @@ export class Lowerer {
this.lowerExpr(expr.kind.cond); this.lowerExpr(expr.kind.cond);
this.program.add(Ops.Not); this.program.add(Ops.Not);
this.program.add(Ops.PushPtr, falseLabel); this.program.add(Ops.JumpIfTrue, falseLabel);
this.program.add(Ops.JumpIfTrue);
this.lowerExpr(expr.kind.truthy); this.lowerExpr(expr.kind.truthy);
this.program.add(Ops.PushPtr, doneLabel); this.program.add(Ops.Jump, doneLabel);
this.program.add(Ops.Jump);
this.program.setLabel(falseLabel); this.program.setLabel(falseLabel);
@ -350,8 +311,7 @@ export class Lowerer {
this.program.setLabel(contineLabel); this.program.setLabel(contineLabel);
this.lowerExpr(expr.kind.body); this.lowerExpr(expr.kind.body);
this.program.add(Ops.PushPtr, breakLabel); this.program.add(Ops.Jump, breakLabel);
this.program.add(Ops.Jump);
this.program.setLabel(breakLabel); this.program.setLabel(breakLabel);
if (expr.vtype!.type === "null") { if (expr.vtype!.type === "null") {
this.program.add(Ops.PushNull); this.program.add(Ops.PushNull);
@ -366,7 +326,6 @@ export class Lowerer {
} }
const outerLocals = this.locals; const outerLocals = this.locals;
this.locals = new LocalLeaf(this.locals); this.locals = new LocalLeaf(this.locals);
this.scoutFnHeaders(expr.kind.stmts);
for (const stmt of expr.kind.stmts) { for (const stmt of expr.kind.stmts) {
this.lowerStmt(stmt); this.lowerStmt(stmt);
} }

View File

@ -1,28 +1,25 @@
export interface Locals { export interface Locals {
reserveAmount(id: number): void; reserveId(id: number): void;
allocSym(ident: string): void; allocSym(ident: string): void;
symId(ident: string): number; symId(ident: string): number;
currentLocalIdCounter(): number;
} }
export class LocalsFnRoot implements Locals { export class LocalsFnRoot implements Locals {
private localsAmount = 0; private localsAmount = 0;
private localIdCounter = 0;
private symLocalMap: { [key: string]: number } = {}; private symLocalMap: { [key: string]: number } = {};
private localIdCounter: number;
constructor(private parent?: Locals) { constructor(private parent?: Locals) {
this.localIdCounter = parent?.currentLocalIdCounter() ?? 0;
} }
public reserveAmount(amount: number): void { public reserveId(id: number): void {
this.localsAmount = Math.max(amount, this.localsAmount); this.localsAmount = Math.max(id + 1, this.localsAmount);
} }
public allocSym(ident: string) { public allocSym(ident: string) {
this.symLocalMap[ident] = this.localIdCounter; this.symLocalMap[ident] = this.localIdCounter;
this.localIdCounter++; this.localIdCounter++;
this.reserveAmount(this.localIdCounter); this.reserveId(this.localIdCounter);
} }
public symId(ident: string): number { public symId(ident: string): number {
@ -34,32 +31,23 @@ export class LocalsFnRoot implements Locals {
} }
throw new Error(`undefined symbol '${ident}'`); throw new Error(`undefined symbol '${ident}'`);
} }
public stackReserved(): number {
return this.localsAmount;
}
public currentLocalIdCounter(): number {
return this.localIdCounter;
}
} }
export class LocalLeaf implements Locals { export class LocalLeaf implements Locals {
private localIdCounter = 0;
private symLocalMap: { [key: string]: number } = {}; private symLocalMap: { [key: string]: number } = {};
private localIdCounter: number;
constructor(private parent: Locals) { constructor(private parent: Locals) {
this.localIdCounter = parent.currentLocalIdCounter();
} }
public reserveAmount(amount: number): void { public reserveId(id: number): void {
this.parent.reserveAmount(amount); this.parent.reserveId(id);
} }
public allocSym(ident: string) { public allocSym(ident: string) {
this.symLocalMap[ident] = this.localIdCounter; this.symLocalMap[ident] = this.localIdCounter;
this.localIdCounter++; this.localIdCounter++;
this.reserveAmount(this.localIdCounter); this.reserveId(this.localIdCounter);
} }
public symId(ident: string): number { public symId(ident: string): number {
@ -68,8 +56,4 @@ export class LocalLeaf implements Locals {
} }
return this.parent.symId(ident); return this.parent.symId(ident);
} }
public currentLocalIdCounter(): number {
return this.localIdCounter;
}
} }

View File

@ -1,33 +1,21 @@
import { Checker } from "./checker.ts"; import { Checker } from "./checker.ts";
import { Reporter } from "./info.ts";
import { Lexer } from "./lexer.ts"; import { Lexer } from "./lexer.ts";
import { Lowerer } from "./lowerer.ts"; import { Lowerer } from "./lowerer.ts";
import { Parser } from "./parser.ts"; import { Parser } from "./parser.ts";
import { Resolver } from "./resolver.ts"; import { Resolver } from "./resolver.ts";
//const text = await Deno.readTextFile("example.slg"); const text = await Deno.readTextFile("example.slg");
const text = await Deno.readTextFile(Deno.args[0]);
const reporter = new Reporter(); const lexer = new Lexer(text);
const lexer = new Lexer(text, reporter); const parser = new Parser(lexer);
const parser = new Parser(lexer, reporter);
const ast = parser.parseStmts(); const ast = parser.parseStmts();
new Resolver(reporter).resolve(ast); new Resolver().resolve(ast);
new Checker(reporter).check(ast); new Checker().check(ast);
// console.log(JSON.stringify(ast, null, 4)) // console.log(JSON.stringify(ast, null, 4))
if (reporter.errorOccured()) {
console.error("Errors occurred, stopping compilation.");
Deno.exit(1);
}
const lowerer = new Lowerer(); const lowerer = new Lowerer();
lowerer.lower(ast); lowerer.lower(ast);
lowerer.printProgram(); lowerer.printProgram();
const program = lowerer.finish(); const program = lowerer.finish();
//console.log(JSON.stringify(program, null, 4)); //console.log(JSON.stringify(program, null, 4));
console.log(JSON.stringify(program)); console.log(JSON.stringify(program));
await Deno.writeTextFile("out.slgbc", JSON.stringify(program));

View File

@ -8,7 +8,6 @@ import {
Stmt, Stmt,
StmtKind, StmtKind,
} from "./ast.ts"; } from "./ast.ts";
import { printStackTrace, Reporter } from "./info.ts";
import { Lexer } from "./lexer.ts"; import { Lexer } from "./lexer.ts";
import { Pos, Token } from "./token.ts"; import { Pos, Token } from "./token.ts";
@ -16,7 +15,7 @@ export class Parser {
private currentToken: Token | null; private currentToken: Token | null;
private nextNodeId = 0; private nextNodeId = 0;
public constructor(private lexer: Lexer, private reporter: Reporter) { public constructor(private lexer: Lexer) {
this.currentToken = lexer.next(); this.currentToken = lexer.next();
} }
@ -42,12 +41,19 @@ export class Parser {
private report(msg: string, pos = this.pos()) { private report(msg: string, pos = this.pos()) {
console.log(`Parser: ${msg} at ${pos.line}:${pos.col}`); console.log(`Parser: ${msg} at ${pos.line}:${pos.col}`);
this.reporter.reportError({ class ReportNotAnError extends Error {
msg, constructor() {
pos, super("ReportNotAnError");
reporter: "Parser", }
}); }
printStackTrace(); try {
throw new ReportNotAnError();
} catch (error) {
if (!(error instanceof ReportNotAnError)) {
throw error;
}
console.log(error);
}
} }
private stmt(kind: StmtKind, pos: Pos): Stmt { private stmt(kind: StmtKind, pos: Pos): Stmt {

View File

@ -1,5 +1,4 @@
import { Expr, Stmt } from "./ast.ts"; import { Expr, Stmt } from "./ast.ts";
import { Reporter } from "./info.ts";
import { import {
FnSyms, FnSyms,
GlobalSyms, GlobalSyms,
@ -12,8 +11,6 @@ import { Pos } from "./token.ts";
export class Resolver { export class Resolver {
private root = new GlobalSyms(); private root = new GlobalSyms();
public constructor(private reporter: Reporter) {}
public resolve(stmts: Stmt[]) { public resolve(stmts: Stmt[]) {
const scopeSyms = new StaticSyms(this.root); const scopeSyms = new StaticSyms(this.root);
this.scoutFnStmts(stmts, scopeSyms); this.scoutFnStmts(stmts, scopeSyms);
@ -203,19 +200,15 @@ export class Resolver {
} }
private reportUseOfUndefined(ident: string, pos: Pos, _syms: Syms) { private reportUseOfUndefined(ident: string, pos: Pos, _syms: Syms) {
this.reporter.reportError({ console.error(
reporter: "Resolver", `use of undefined symbol '${ident}' at ${pos.line}${pos.col}`,
msg: `use of undefined symbol '${ident}'`, );
pos,
});
} }
private reportAlreadyDefined(ident: string, pos: Pos, syms: Syms) { private reportAlreadyDefined(ident: string, pos: Pos, syms: Syms) {
this.reporter.reportError({ console.error(
reporter: "Resolver", `symbol already defined '${ident}', at ${pos.line}${pos.col}`,
msg: `symbol already defined '${ident}'`, );
pos,
});
const prev = syms.get(ident); const prev = syms.get(ident);
if (!prev.ok) { if (!prev.ok) {
throw new Error("expected to be defined"); throw new Error("expected to be defined");
@ -223,10 +216,9 @@ export class Resolver {
if (!prev.sym.pos) { if (!prev.sym.pos) {
return; return;
} }
this.reporter.addNote({ const { line: prevLine, col: prevCol } = prev.sym.pos;
reporter: "Resolver", console.error(
msg: `previous definition of '${ident}'`, `previous definition of '${ident}' at ${prevLine}:${prevCol}`,
pos: prev.sym.pos, );
});
} }
} }

View File

@ -1,15 +0,0 @@
fn main() -> int {
add(1, 2)
}
fn add(a: int, b: int) -> int {
let c = a;
let d = b;
{
let a = c;
+ a d
}
//+ a b
}

View File

@ -1,5 +0,0 @@
fn main() -> int {
+ 1 2
}

View File

@ -1,7 +0,0 @@
fn main() -> int {
let a = 5;
let b = 3;
+ a b
}

View File

@ -1,3 +0,0 @@
fn main() {}

View File

@ -7,42 +7,31 @@ namespace sliger {
// NOTICE: keep up to date with src/arch.ts // NOTICE: keep up to date with src/arch.ts
enum class Op : uint32_t { enum class Op : uint32_t {
Nop = 0x00, Nop = 0,
PushNull = 0x01, PushNull = 1,
PushInt = 0x02, PushInt = 2,
PushBool = 0x03, PushBool = 3,
PushString = 0x04, PushString = 4,
PushPtr = 0x05, PushPtr = 5,
Pop = 0x06, Pop = 6,
ReserveStatic = 0x07, LoadLocal = 7,
LoadStatic = 0x08, StoreLocal = 8,
StoreStatic = 0x09, Call = 9,
LoadLocal = 0x0a, Return = 10,
StoreLocal = 0x0b, Jump = 11,
Call = 0x0c, JumpIfFalse = 12,
Return = 0x0d, Add = 13,
Jump = 0x0e, Subtract = 14,
JumpIfTrue = 0x0f, Multiply = 15,
Builtin = 0x10, Divide = 16,
Add = 0x20, Remainder = 17,
Subtract = 0x21, Equal = 18,
Multiply = 0x22, LessThan = 19,
Divide = 0x23, And = 20,
Remainder = 0x24, Or = 21,
Equal = 0x25, Xor = 22,
LessThan = 0x26, Not = 23,
And = 0x27, SourceMap = 24,
Or = 0x28,
Xor = 0x29,
Not = 0x2a,
SourceMap = 0x30,
};
enum class Builtin : uint32_t {
StringConcat = 0x10,
StringEqual = 0x11,
ArraySet = 0x20,
StructSet = 0x30,
}; };
} }

View File

@ -1,7 +1,6 @@
#include "json.hpp" #include "json.hpp"
#include <cstdlib> #include <cstdlib>
#include <format> #include <format>
#include <iostream>
#include <memory> #include <memory>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@ -166,7 +165,7 @@ auto Parser::parse_val() -> Res<std::unique_ptr<Value>>
} }
step(); step();
auto value = parse_val(); auto value = parse_val();
if (not value.ok()) { if (value.ok()) {
return value.err(); return value.err();
} }
fields.insert_or_assign(key, std::move(value.val())); fields.insert_or_assign(key, std::move(value.val()));
@ -184,7 +183,7 @@ auto Parser::parse_val() -> Res<std::unique_ptr<Value>>
} }
step(); step();
auto value = parse_val(); auto value = parse_val();
if (not value.ok()) { if (value.ok()) {
return value.err(); return value.err();
} }
fields.insert_or_assign(key, std::move(value.val())); fields.insert_or_assign(key, std::move(value.val()));
@ -193,32 +192,30 @@ auto Parser::parse_val() -> Res<std::unique_ptr<Value>>
if (curtyp() != TokTyp::RBrace) { if (curtyp() != TokTyp::RBrace) {
return unexpected_tok_err(TokTyp::RBrace, "malformed object"); return unexpected_tok_err(TokTyp::RBrace, "malformed object");
} }
step();
return Res<std::unique_ptr<Value>>( return Res<std::unique_ptr<Value>>(
std::make_unique<Object>(std::move(fields))); std::make_unique<Object>(std::move(fields)));
} }
case TokTyp::LBracket: { case TokTyp::LBracket: {
step(); step();
ArrayValues values; ArrayValues values;
if (curtyp() != TokTyp::RBracket) { if (curtyp() != TokTyp::RBrace) {
auto value = parse_val(); auto value = parse_val();
if (not value.ok()) { if (value.ok()) {
return value.err(); return value.err();
} }
values.push_back(std::move(value.val())); values.push_back(std::move(value.val()));
while (curtyp() == TokTyp::Comma) { while (curtyp() == TokTyp::Comma) {
step(); step();
auto value = parse_val(); auto value = parse_val();
if (not value.ok()) { if (value.ok()) {
return value.err(); return value.err();
} }
values.push_back(std::move(value.val())); values.push_back(std::move(value.val()));
} }
} }
if (curtyp() != TokTyp::RBracket) { if (curtyp() != TokTyp::RBrace) {
return unexpected_tok_err(TokTyp::RBracket, "malformed array"); return unexpected_tok_err(TokTyp::RBrace, "malformed object");
} }
step();
return Res<std::unique_ptr<Value>>( return Res<std::unique_ptr<Value>>(
std::make_unique<Array>(std::move(values))); std::make_unique<Array>(std::move(values)));
} }

View File

@ -311,7 +311,6 @@ private:
auto tok = this->lexer.next(); auto tok = this->lexer.next();
if (not tok.ok()) if (not tok.ok())
return tok.err(); return tok.err();
this->cur = tok;
return {}; return {};
} }
inline auto curtyp() const -> TokTyp { return this->cur.val().typ; } inline auto curtyp() const -> TokTyp { return this->cur.val().typ; }

View File

@ -1,56 +1,17 @@
#include "actions.hpp" #include "actions.hpp"
#include "json.hpp" #include "json.hpp"
#include "rpc_server.hpp" #include "rpc_server.hpp"
#include "vm.hpp"
#include "vm_provider.hpp" #include "vm_provider.hpp"
#include <cstdint>
#include <format>
#include <fstream>
#include <string>
#include <vector>
int execute_file_and_exit(std::string filename) int main()
{ {
auto file = std::ifstream();
file.open(filename.c_str());
if (!file) {
std::cout << std::format("error: could not open file '{}'\n", filename);
return 1;
}
auto text = std::string(std::istreambuf_iterator<char> { file }, {});
auto parsed = sliger::json::parse_json(text);
if (not parsed.ok()) {
std::cout << std::format("error: {} at {}:{}\n", parsed.err().msg,
parsed.err().pos.line, parsed.err().pos.col);
return 1;
}
auto program = std::vector<uint32_t>();
for (auto& v : parsed.val()->as<sliger::json::Array>().values) {
program.push_back(
static_cast<uint32_t>(v->as<sliger::json::Number>().value));
}
auto vm = sliger::VM(program,
{
.flame_graph = false,
.code_coverage = false,
});
vm.run_until_done();
return 0;
}
int main(int argc, char** argv)
{
if (argc >= 3 && std::string(argv[1]) == "run") {
return execute_file_and_exit(argv[2]);
}
auto state = sliger::rpc::vm_provider::VmProvider(); auto state = sliger::rpc::vm_provider::VmProvider();
auto rpc = sliger::rpc::RpcServer( auto rpc = sliger::rpc::RpcServer(
[&](std::unique_ptr<sliger::json::Value> req, [&](std::unique_ptr<sliger::json::Value> req,
std::unique_ptr<sliger::rpc::BufferedWriter> writer) { std::unique_ptr<sliger::rpc::BufferedWriter> writer) {
auto action = sliger::rpc::action::action_from_json(std::move(req)); auto action = sliger::rpc::action::action_from_json(std::move(req));
action->perform_action(std::move(writer), state); action->perform_action(std::move(writer));
}); });
rpc.listen(); rpc.listen();
} }

View File

@ -37,8 +37,8 @@ inline auto maybe_op_to_string(uint32_t value) -> std::string
return "Return"; return "Return";
case Op::Jump: case Op::Jump:
return "Jump"; return "Jump";
case Op::JumpIfTrue: case Op::JumpIfFalse:
return "JumpIfTrue"; return "JumpIfFalse";
case Op::Add: case Op::Add:
return "Add"; return "Add";
case Op::Subtract: case Op::Subtract:
@ -88,7 +88,7 @@ void VM::run_n_instructions(size_t amount)
void VM::run_instruction() void VM::run_instruction()
{ {
std::cout << std::format(" {:>4}: {:<12}{}\n", this->pc, std::cout << std::format(" {:>4}: {:<12}{}\n", this->pc,
maybe_op_to_string(this->program[this->pc]), stack_repr_string(6)); maybe_op_to_string(this->program[this->pc]), stack_repr_string(4));
auto op = eat_op(); auto op = eat_op();
switch (op) { switch (op) {
case Op::Nop: case Op::Nop:
@ -132,28 +132,7 @@ void VM::run_instruction()
this->stack.pop_back(); this->stack.pop_back();
break; break;
} }
case Op::ReserveStatic: {
assert_program_has(1);
auto value = eat_uint32();
this->statics.reserve(value);
break;
}
case Op::LoadStatic: {
assert_program_has(1);
auto loc = eat_uint32();
auto value = this->statics.at(loc);
stack_push(value);
break;
}
case Op::StoreStatic: {
assert_program_has(1);
auto loc = eat_uint32();
auto value = stack_pop();
this->statics.at(loc) = value;
break;
}
case Op::LoadLocal: { case Op::LoadLocal: {
assert_program_has(1);
auto loc = eat_uint32(); auto loc = eat_uint32();
assert_fn_stack_has(loc); assert_fn_stack_has(loc);
auto value = fn_stack_at(loc); auto value = fn_stack_at(loc);
@ -161,7 +140,6 @@ void VM::run_instruction()
break; break;
} }
case Op::StoreLocal: { case Op::StoreLocal: {
assert_program_has(1);
auto loc = eat_uint32(); auto loc = eat_uint32();
assert_fn_stack_has(loc + 1); assert_fn_stack_has(loc + 1);
auto value = stack_pop(); auto value = stack_pop();
@ -183,8 +161,7 @@ void VM::run_instruction()
stack_push(std::move(arguments.at(i - 1))); stack_push(std::move(arguments.at(i - 1)));
} }
this->pc = fn_ptr.as_ptr().value; this->pc = fn_ptr.as_ptr().value;
this->bp this->bp = static_cast<uint32_t>(this->stack.size());
= static_cast<uint32_t>(this->stack.size() - arguments.size());
if (this->opts.flame_graph) { if (this->opts.flame_graph) {
this->flame_graph.report_call( this->flame_graph.report_call(
fn_ptr.as_ptr().value, this->instruction_counter); fn_ptr.as_ptr().value, this->instruction_counter);
@ -210,21 +187,15 @@ void VM::run_instruction()
this->pc = addr.as_ptr().value; this->pc = addr.as_ptr().value;
break; break;
} }
case Op::JumpIfTrue: { case Op::JumpIfFalse: {
assert_stack_has(2); assert_stack_has(2);
auto addr = stack_pop(); auto addr = stack_pop();
auto cond = stack_pop(); auto cond = stack_pop();
if (cond.as_bool().value) { if (!cond.as_bool().value) {
this->pc = addr.as_ptr().value; this->pc = addr.as_ptr().value;
} }
break; break;
} }
case Op::Builtin: {
assert_program_has(1);
auto builtin_id = eat_uint32();
run_builtin(static_cast<Builtin>(builtin_id));
break;
}
case Op::Add: { case Op::Add: {
assert_stack_has(2); assert_stack_has(2);
auto right = stack_pop().as_int().value; auto right = stack_pop().as_int().value;
@ -325,36 +296,3 @@ void VM::run_instruction()
} }
this->instruction_counter += 1; this->instruction_counter += 1;
} }
void VM::run_builtin(Builtin builtin_id)
{
switch (builtin_id) {
case Builtin::StringConcat: {
assert_stack_has(2);
auto right = stack_pop();
auto left = stack_pop();
stack_push(
String(right.as_string().value + left.as_string().value));
break;
}
case Builtin::StringEqual: {
assert_stack_has(2);
auto right = stack_pop();
auto left = stack_pop();
stack_push(Bool(right.as_string().value == left.as_string().value));
break;
}
case Builtin::ArraySet: {
assert_stack_has(2);
std::cerr << std::format("not implemented\n");
std::exit(1);
break;
}
case Builtin::StructSet: {
assert_stack_has(2);
std::cerr << std::format("not implemented\n");
std::exit(1);
break;
}
}
}

View File

@ -191,8 +191,6 @@ public:
} }
private: private:
void run_builtin(Builtin builtin_id);
inline void step() { this->pc += 1; } inline void step() { this->pc += 1; }
inline auto eat_op() -> Op inline auto eat_op() -> Op
@ -274,7 +272,6 @@ private:
const uint32_t* program; const uint32_t* program;
size_t program_size; size_t program_size;
std::vector<Value> stack; std::vector<Value> stack;
std::vector<Value> statics;
heap::Heap heap; heap::Heap heap;
SourcePos current_pos = { 0, 1, 1 }; SourcePos current_pos = { 0, 1, 1 };
int64_t instruction_counter = 0; int64_t instruction_counter = 0;

View File

@ -1,12 +0,0 @@
#!/bin/bash
set -e
echo Compiling $1...
deno run --allow-read --allow-write compiler/main.ts $1
echo "Running out.slgbc..."
./runtime/build/sliger run out.slgbc