mirror of
https://git.sfja.dk/Mikkel/slige.git
synced 2025-01-18 10:06:31 +00:00
everything
This commit is contained in:
parent
f3523e486b
commit
072e44eaa8
@ -42,6 +42,7 @@ export const Builtins = {
|
||||
StringEqual: 0x11,
|
||||
ArraySet: 0x20,
|
||||
StructSet: 0x30,
|
||||
Print: 0x40,
|
||||
} as const;
|
||||
|
||||
export function opToString(op: number): string {
|
||||
@ -119,6 +120,8 @@ export function builtinToString(builtin: number): string {
|
||||
return "ArraySet";
|
||||
case Builtins.StructSet:
|
||||
return "StructSet";
|
||||
case Builtins.Print:
|
||||
return "Print";
|
||||
default:
|
||||
return `<unknown Builtin ${builtin}>`;
|
||||
}
|
||||
|
@ -49,17 +49,10 @@ export class Assembler {
|
||||
const locs: { [key: string]: number } = {};
|
||||
const refs: { [key: number]: string } = {};
|
||||
|
||||
const debugLines: {
|
||||
startIp: number;
|
||||
endIp: number;
|
||||
insString: string;
|
||||
}[] = [];
|
||||
|
||||
for (const line of this.lines) {
|
||||
for (const label of line.labels ?? []) {
|
||||
locs[label] = ip;
|
||||
}
|
||||
const startIp = ip;
|
||||
for (const lit of line.ins as Lit[]) {
|
||||
if (typeof lit === "number") {
|
||||
output.push(lit);
|
||||
@ -68,6 +61,8 @@ export class Assembler {
|
||||
output.push(lit ? 1 : 0);
|
||||
ip += 1;
|
||||
} else if (typeof lit === "string") {
|
||||
output.push(lit.length);
|
||||
ip += 1;
|
||||
for (let i = 0; i < lit.length; ++i) {
|
||||
output.push(lit.charCodeAt(i));
|
||||
ip += 1;
|
||||
@ -78,11 +73,6 @@ export class Assembler {
|
||||
ip += 1;
|
||||
}
|
||||
}
|
||||
debugLines.push({
|
||||
startIp,
|
||||
endIp: ip,
|
||||
insString: this.insToString(line.ins),
|
||||
});
|
||||
}
|
||||
for (let i = 0; i < output.length; ++i) {
|
||||
if (!(i in refs)) {
|
||||
@ -96,21 +86,14 @@ export class Assembler {
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
public printProgram() {
|
||||
let ip = 0;
|
||||
for (const line of this.lines) {
|
||||
for (const label of line.labels ?? []) {
|
||||
console.log(`${label}:`);
|
||||
console.log(` ${label}:`);
|
||||
}
|
||||
const op = opToString(line.ins[0] as number)
|
||||
.padEnd(13, " ");
|
||||
@ -129,28 +112,10 @@ export class Assembler {
|
||||
return lit.label;
|
||||
}
|
||||
}).join(", ");
|
||||
console.log(` ${op} ${args}`);
|
||||
console.log(`${ip.toString().padStart(8, " ")}: ${op} ${args}`);
|
||||
ip += line.ins.map((lit) =>
|
||||
typeof lit === "string" ? lit.length : 1
|
||||
).reduce((acc, curr) => acc + curr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
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}`;
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ export type StmtKind =
|
||||
params: Param[];
|
||||
returnType?: EType;
|
||||
body: Expr;
|
||||
annos?: Anno[];
|
||||
vtype?: VType;
|
||||
}
|
||||
| { type: "let"; param: Param; value: Expr }
|
||||
@ -85,7 +86,7 @@ export type SymKind =
|
||||
| { type: "fn"; stmt: Stmt }
|
||||
| { type: "fn_param"; param: Param }
|
||||
| { type: "closure"; inner: Sym }
|
||||
| { type: "builtin" };
|
||||
| { type: "builtin"; builtinId: number };
|
||||
|
||||
export type EType = {
|
||||
kind: ETypeKind;
|
||||
@ -98,3 +99,9 @@ export type ETypeKind =
|
||||
| { type: "ident"; value: string }
|
||||
| { type: "array"; inner: EType }
|
||||
| { type: "struct"; fields: Param[] };
|
||||
|
||||
export type Anno = {
|
||||
ident: string;
|
||||
values: Expr[];
|
||||
pos: Pos;
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { StmtKind } from "./ast.ts";
|
||||
import { Builtins } from "./arch.ts";
|
||||
import { EType, Expr, Stmt } from "./ast.ts";
|
||||
import { printStackTrace, Reporter } from "./info.ts";
|
||||
import { Pos } from "./token.ts";
|
||||
@ -356,6 +356,7 @@ export class Checker {
|
||||
}
|
||||
const pos = expr.pos;
|
||||
const subject = this.checkExpr(expr.kind.subject);
|
||||
console.log(expr);
|
||||
if (subject.type !== "fn") {
|
||||
this.report("cannot call non-fn", pos);
|
||||
return { type: "error" };
|
||||
|
@ -91,7 +91,7 @@ export class Lexer {
|
||||
this.step();
|
||||
return { ...this.token("string", pos), stringValue: value };
|
||||
}
|
||||
if (this.test(/[\+\{\};=\-\*\(\)\.,:;\[\]><!0]/)) {
|
||||
if (this.test(/[\+\{\};=\-\*\(\)\.,:;\[\]><!0#]/)) {
|
||||
const first = this.current();
|
||||
this.step();
|
||||
if (first === "=" && !this.done() && this.test("=")) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Builtins } from "./arch.ts";
|
||||
import { BinaryType, Expr, Stmt } from "./ast.ts";
|
||||
import { Expr, Stmt } from "./ast.ts";
|
||||
import { LocalLeaf, Locals, LocalsFnRoot } from "./lowerer_locals.ts";
|
||||
import { Ops } from "./mod.ts";
|
||||
import { Assembler, Label } from "./assembler.ts";
|
||||
import { VType, vtypeToString } from "./vtype.ts";
|
||||
import { vtypeToString } from "./vtype.ts";
|
||||
|
||||
export class Lowerer {
|
||||
private program = new Assembler();
|
||||
@ -134,25 +134,13 @@ export class Lowerer {
|
||||
for (const { ident } of stmt.kind.params) {
|
||||
this.locals.allocSym(ident);
|
||||
}
|
||||
this.locals.allocSym("::return");
|
||||
this.program.add(Ops.PushNull);
|
||||
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
|
||||
) {
|
||||
const localAmount = fnRoot.stackReserved() -
|
||||
stmt.kind.params.length;
|
||||
for (let i = 0; i < localAmount; ++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);
|
||||
@ -334,6 +322,8 @@ export class Lowerer {
|
||||
|
||||
if (expr.kind.falsy) {
|
||||
this.lowerExpr(expr.kind.falsy!);
|
||||
} else {
|
||||
this.program.add(Ops.PushNull);
|
||||
}
|
||||
|
||||
this.program.setLabel(doneLabel);
|
||||
|
@ -1,14 +1,5 @@
|
||||
import { Stmt } from "./ast.ts";
|
||||
import { Lexer } from "./lexer.ts";
|
||||
import { Parser } from "./parser.ts";
|
||||
|
||||
export * from "./parser.ts";
|
||||
export * from "./ast.ts";
|
||||
export * from "./arch.ts";
|
||||
export * from "./lexer.ts";
|
||||
export * from "./token.ts";
|
||||
|
||||
export async function compileWithDebug(filepath: string): Promise<Stmt[]> {
|
||||
const text = await Deno.readTextFile(filepath);
|
||||
return new Parser(new Lexer(text)).parseStmts();
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {
|
||||
Anno,
|
||||
BinaryType,
|
||||
EType,
|
||||
ETypeKind,
|
||||
@ -203,6 +204,11 @@ export class Parser {
|
||||
this.step();
|
||||
returnType = this.parseEType();
|
||||
}
|
||||
|
||||
let annos: Anno[] | null = null;
|
||||
if (this.test("#")) {
|
||||
annos = this.parseAnnoArgs();
|
||||
}
|
||||
if (!this.test("{")) {
|
||||
this.report("expected block");
|
||||
return this.stmt({ type: "error" }, pos);
|
||||
@ -214,6 +220,62 @@ export class Parser {
|
||||
return this.stmt({ type: "fn", ident, params, returnType, body }, pos);
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
|
||||
public parseAnnos(): Anno[] {
|
||||
this.step();
|
||||
if (!this.test("[")) {
|
||||
this.report("expected '['");
|
||||
return [];
|
||||
}
|
||||
this.step();
|
||||
const annoArgs: Expr[] = [];
|
||||
if (!this.test(")")) {
|
||||
if (!this.test("ident")) {
|
||||
this.report("expected identifier");
|
||||
return [];
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
public parseFnParams(): Param[] {
|
||||
this.step();
|
||||
if (this.test(")")) {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Builtins } from "./arch.ts";
|
||||
import { Expr, Stmt } from "./ast.ts";
|
||||
import { Reporter } from "./info.ts";
|
||||
import { printStackTrace, Reporter } from "./info.ts";
|
||||
import {
|
||||
FnSyms,
|
||||
GlobalSyms,
|
||||
@ -12,7 +13,13 @@ import { Pos } from "./token.ts";
|
||||
export class Resolver {
|
||||
private root = new GlobalSyms();
|
||||
|
||||
public constructor(private reporter: Reporter) {}
|
||||
public constructor(private reporter: Reporter) {
|
||||
this.root.define("print", {
|
||||
type: "builtin",
|
||||
ident: "print",
|
||||
builtinId: Builtins.Print,
|
||||
});
|
||||
}
|
||||
|
||||
public resolve(stmts: Stmt[]) {
|
||||
const scopeSyms = new StaticSyms(this.root);
|
||||
@ -208,6 +215,7 @@ export class Resolver {
|
||||
msg: `use of undefined symbol '${ident}'`,
|
||||
pos,
|
||||
});
|
||||
printStackTrace();
|
||||
}
|
||||
|
||||
private reportAlreadyDefined(ident: string, pos: Pos, syms: Syms) {
|
||||
@ -228,5 +236,6 @@ export class Resolver {
|
||||
msg: `previous definition of '${ident}'`,
|
||||
pos: prev.sym.pos,
|
||||
});
|
||||
printStackTrace();
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
|
||||
|
||||
fn main() -> int {
|
||||
add(1, 2)
|
||||
}
|
||||
@ -11,5 +10,5 @@ fn add(a: int, b: int) -> int {
|
||||
let a = c;
|
||||
+ a d
|
||||
}
|
||||
//+ a b
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,4 @@
|
||||
|
||||
fn println(str: string) {
|
||||
}
|
||||
fn print(msg: string) #[builtin(print)] {}
|
||||
|
||||
fn sum(a: int, b: int) -> int {
|
||||
+ a b
|
||||
@ -13,13 +11,13 @@ fn main() {
|
||||
|
||||
let b = "world";
|
||||
|
||||
println(+ + + a " " b "!"); // -> "Hello world!"
|
||||
print(+ + + a " " b "!\n"); // -> "Hello world!"
|
||||
|
||||
if == a b {
|
||||
println("whaaaat");
|
||||
print("whaaaat\n");
|
||||
}
|
||||
else {
|
||||
println(":o");
|
||||
print(":o\n");
|
||||
}
|
||||
|
||||
loop {
|
||||
|
@ -1,14 +1,17 @@
|
||||
fn add(a, b) {
|
||||
fn add(a: int, b: int) -> int {
|
||||
+ a b
|
||||
}
|
||||
|
||||
let result = 0;
|
||||
let i = 0;
|
||||
loop {
|
||||
if >= i 10 {
|
||||
break;
|
||||
fn main() -> int {
|
||||
let result = 0;
|
||||
let i = 0;
|
||||
loop {
|
||||
if >= i 10 {
|
||||
break;
|
||||
}
|
||||
result = add(result, 5);
|
||||
i = + i 1;
|
||||
}
|
||||
result = add(result, 5);
|
||||
i = + i 1;
|
||||
result
|
||||
}
|
||||
|
||||
|
@ -10,6 +10,30 @@
|
||||
|
||||
namespace sliger {
|
||||
|
||||
inline auto escape_string(std::string str) -> std::string
|
||||
{
|
||||
auto result = std::string();
|
||||
for (auto ch : str) {
|
||||
switch (ch) {
|
||||
case '\n':
|
||||
result += "\\n";
|
||||
break;
|
||||
case '\t':
|
||||
result += "\\t";
|
||||
break;
|
||||
case '\0':
|
||||
result += "\\0";
|
||||
break;
|
||||
case '\\':
|
||||
result += "\\\\";
|
||||
break;
|
||||
default:
|
||||
result += ch;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
enum class ValueType {
|
||||
Null,
|
||||
Int,
|
||||
@ -137,7 +161,7 @@ public:
|
||||
case ValueType::Bool:
|
||||
return as_bool().value ? "true" : "false";
|
||||
case ValueType::String:
|
||||
return std::format("\"{}\"", as_string().value);
|
||||
return std::format("\"{}\"", escape_string(as_string().value));
|
||||
case ValueType::Ptr:
|
||||
return std::to_string(as_ptr().value);
|
||||
}
|
||||
|
@ -13,56 +13,37 @@ using namespace sliger;
|
||||
inline auto maybe_op_to_string(uint32_t value) -> std::string
|
||||
{
|
||||
switch (static_cast<Op>(value)) {
|
||||
case Op::Nop:
|
||||
return "Nop";
|
||||
case Op::PushNull:
|
||||
return "PushNull";
|
||||
case Op::PushInt:
|
||||
return "PushInt";
|
||||
case Op::PushBool:
|
||||
return "PushBool";
|
||||
case Op::PushString:
|
||||
return "PushString";
|
||||
case Op::PushPtr:
|
||||
return "PushPtr";
|
||||
case Op::Pop:
|
||||
return "Pop";
|
||||
case Op::LoadLocal:
|
||||
return "LoadLocal";
|
||||
case Op::StoreLocal:
|
||||
return "StoreLocal";
|
||||
case Op::Call:
|
||||
return "Call";
|
||||
case Op::Return:
|
||||
return "Return";
|
||||
case Op::Jump:
|
||||
return "Jump";
|
||||
case Op::JumpIfTrue:
|
||||
return "JumpIfTrue";
|
||||
case Op::Add:
|
||||
return "Add";
|
||||
case Op::Subtract:
|
||||
return "Subtract";
|
||||
case Op::Multiply:
|
||||
return "Multiply";
|
||||
case Op::Divide:
|
||||
return "Divide";
|
||||
case Op::Remainder:
|
||||
return "Remainder";
|
||||
case Op::Equal:
|
||||
return "Equal";
|
||||
case Op::LessThan:
|
||||
return "LessThan";
|
||||
case Op::And:
|
||||
return "And";
|
||||
case Op::Or:
|
||||
return "Or";
|
||||
case Op::Xor:
|
||||
return "Xor";
|
||||
case Op::Not:
|
||||
return "Not";
|
||||
case Op::SourceMap:
|
||||
return "SourceMap";
|
||||
/* clang-format off */
|
||||
case Op::Nop: return "Nop";
|
||||
case Op::PushNull: return "PushNull";
|
||||
case Op::PushInt: return "PushInt";
|
||||
case Op::PushBool: return "PushBool";
|
||||
case Op::PushString: return "PushString";
|
||||
case Op::PushPtr: return "PushPtr";
|
||||
case Op::Pop: return "Pop";
|
||||
case Op::ReserveStatic: return "ReserveStatic";
|
||||
case Op::LoadStatic: return "LoadStatic";
|
||||
case Op::StoreStatic: return "StoreStatic";
|
||||
case Op::LoadLocal: return "LoadLocal";
|
||||
case Op::StoreLocal: return "StoreLocal";
|
||||
case Op::Call: return "Call";
|
||||
case Op::Return: return "Return";
|
||||
case Op::Jump: return "Jump";
|
||||
case Op::JumpIfTrue: return "JumpIfTrue";
|
||||
case Op::Builtin: return "Builtin";
|
||||
case Op::Add: return "Add";
|
||||
case Op::Subtract: return "Subtract";
|
||||
case Op::Multiply: return "Multiply";
|
||||
case Op::Divide: return "Divide";
|
||||
case Op::Remainder: return "Remainder";
|
||||
case Op::Equal: return "Equal";
|
||||
case Op::LessThan: return "LessThan";
|
||||
case Op::And: return "And";
|
||||
case Op::Or: return "Or";
|
||||
case Op::Xor: return "Xor";
|
||||
case Op::Not: return "Not";
|
||||
case Op::SourceMap: return "SourceMap";
|
||||
/* clang-format on */
|
||||
default:
|
||||
return std::to_string(value);
|
||||
}
|
||||
@ -88,7 +69,7 @@ void VM::run_n_instructions(size_t amount)
|
||||
void VM::run_instruction()
|
||||
{
|
||||
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(8));
|
||||
auto op = eat_op();
|
||||
switch (op) {
|
||||
case Op::Nop:
|
||||
@ -179,12 +160,11 @@ void VM::run_instruction()
|
||||
}
|
||||
stack_push(Ptr { .value = this->pc });
|
||||
stack_push(Ptr { .value = this->bp });
|
||||
this->pc = fn_ptr.as_ptr().value;
|
||||
this->bp = static_cast<uint32_t>(this->stack.size());
|
||||
for (size_t i = arguments.size(); i > 0; --i) {
|
||||
stack_push(std::move(arguments.at(i - 1)));
|
||||
}
|
||||
this->pc = fn_ptr.as_ptr().value;
|
||||
this->bp
|
||||
= static_cast<uint32_t>(this->stack.size() - arguments.size());
|
||||
if (this->opts.flame_graph) {
|
||||
this->flame_graph.report_call(
|
||||
fn_ptr.as_ptr().value, this->instruction_counter);
|
||||
@ -194,6 +174,9 @@ void VM::run_instruction()
|
||||
case Op::Return: {
|
||||
assert_stack_has(3);
|
||||
auto ret_val = stack_pop();
|
||||
while (this->stack.size() > this->bp) {
|
||||
stack_pop();
|
||||
}
|
||||
auto bp_val = stack_pop();
|
||||
auto pc_val = stack_pop();
|
||||
this->bp = bp_val.as_ptr().value;
|
||||
@ -331,16 +314,16 @@ 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();
|
||||
auto right = 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();
|
||||
auto right = stack_pop();
|
||||
stack_push(Bool(right.as_string().value == left.as_string().value));
|
||||
break;
|
||||
}
|
||||
|
@ -2,11 +2,14 @@
|
||||
|
||||
set -e
|
||||
|
||||
echo Text:
|
||||
cat $1
|
||||
|
||||
echo Compiling $1...
|
||||
|
||||
deno run --allow-read --allow-write compiler/main.ts $1
|
||||
|
||||
echo "Running out.slgbc..."
|
||||
echo Running out.slgbc...
|
||||
|
||||
./runtime/build/sliger run out.slgbc
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user