Compare commits
No commits in common. "f3523e486b4cf6ecd58be8f608005b46dfcd139c" and "916c2376a0723a8777634649cd3096931944bcdc" have entirely different histories.
f3523e486b
...
916c2376a0
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
out.slgbc
|
|
@ -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}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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" } },
|
||||||
];
|
];
|
||||||
|
@ -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,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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));
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
|
|
||||||
fn main() -> int {
|
|
||||||
+ 1 2
|
|
||||||
}
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
|||||||
|
|
||||||
fn main() -> int {
|
|
||||||
let a = 5;
|
|
||||||
let b = 3;
|
|
||||||
+ a b
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
|
|
||||||
fn main() {}
|
|
||||||
|
|
@ -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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)));
|
||||||
}
|
}
|
||||||
|
@ -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; }
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -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;
|
||||||
|
12
slige-run.sh
12
slige-run.sh
@ -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
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user