interpreter
This commit is contained in:
parent
1298b69388
commit
7741896a98
@ -1,5 +1,5 @@
|
||||
|
||||
(def nand (a b vin) (r) (
|
||||
(def nand (a b) (r) (
|
||||
(let (a_and_b) (relay_default_off a b))
|
||||
(set r (relay_default_on a_and_b vin))
|
||||
))
|
||||
@ -45,3 +45,4 @@
|
||||
(set carry_out e1)
|
||||
))
|
||||
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ export function tokenize(text: string): Tok[] {
|
||||
)[0]
|
||||
.map(({ tok, line }) => ({ ty: tok, text: tok, line }))
|
||||
.map((tok) => {
|
||||
if (/^[a-zA-Z_][a-zA-Z_0-9]*$/.test(tok.text)) {
|
||||
if (/^[a-zA-Z_0-9]+$/.test(tok.text)) {
|
||||
tok.ty = "ident";
|
||||
}
|
||||
return tok;
|
||||
@ -263,6 +263,8 @@ class DefResolver {
|
||||
private syms = new Map<string, Sym>([
|
||||
["relay_default_off", { tag: "Builtin" }],
|
||||
["relay_default_on", { tag: "Builtin" }],
|
||||
["gnd", { tag: "Builtin" }],
|
||||
["vin", { tag: "Builtin" }],
|
||||
]);
|
||||
public resols = new Map<Expr, Sym>();
|
||||
|
||||
|
||||
228
src/main.ts
228
src/main.ts
@ -1,6 +1,31 @@
|
||||
import * as front from "./front.ts";
|
||||
import * as ast from "./ast.ts";
|
||||
|
||||
class ColorPrinter {
|
||||
private process: Deno.ChildProcess;
|
||||
private writer: WritableStreamDefaultWriter<Uint8Array<ArrayBufferLike>>;
|
||||
|
||||
constructor(language: string, theme = "github-dark") {
|
||||
this.process = new Deno.Command("pygmentize", {
|
||||
args: ["-l", language, "-O", `style=${theme}`],
|
||||
stdin: "piped",
|
||||
stdout: "inherit",
|
||||
})
|
||||
.spawn();
|
||||
this.writer = this.process.stdin.getWriter();
|
||||
}
|
||||
|
||||
async print(text: string): Promise<void> {
|
||||
await this.writer.ready;
|
||||
await this.writer.write(new TextEncoder().encode(text));
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this.writer.close();
|
||||
await this.process.output();
|
||||
}
|
||||
}
|
||||
|
||||
class Ins {
|
||||
constructor(
|
||||
public line: number,
|
||||
@ -9,26 +34,33 @@ class Ins {
|
||||
}
|
||||
|
||||
type InsKind =
|
||||
| { tag: "Gnd" | "Vin" }
|
||||
| { tag: "Input"; idx: number }
|
||||
| { tag: "Set"; idx: number; value: Ins }
|
||||
| { tag: "RelayDefaultOff" | "RelayDefaultOn"; args: Ins[] }
|
||||
| { tag: "RelayDefaultOff" | "RelayDefaultOn"; a: Ins; b: Ins }
|
||||
| { tag: "Call"; def: ast.Def; args: Ins[] }
|
||||
| { tag: "Elem"; value: Ins; idx: number };
|
||||
|
||||
class InsCx {
|
||||
public insts: Ins[] = [];
|
||||
|
||||
makeGnd(line: number): Ins {
|
||||
return this.make(line, { tag: "Gnd" });
|
||||
}
|
||||
makeVin(line: number): Ins {
|
||||
return this.make(line, { tag: "Vin" });
|
||||
}
|
||||
makeInput(line: number, idx: number): Ins {
|
||||
return this.make(line, { tag: "Input", idx });
|
||||
}
|
||||
makeSet(line: number, idx: number, value: Ins): Ins {
|
||||
return this.make(line, { tag: "Set", idx, value });
|
||||
}
|
||||
makeRelayDefaultOff(line: number, args: Ins[]): Ins {
|
||||
return this.make(line, { tag: "RelayDefaultOff", args });
|
||||
makeRelayDefaultOff(line: number, a: Ins, b: Ins): Ins {
|
||||
return this.make(line, { tag: "RelayDefaultOff", a, b });
|
||||
}
|
||||
makeRelayDefaultOn(line: number, args: Ins[]): Ins {
|
||||
return this.make(line, { tag: "RelayDefaultOn", args });
|
||||
makeRelayDefaultOn(line: number, a: Ins, b: Ins): Ins {
|
||||
return this.make(line, { tag: "RelayDefaultOn", a, b });
|
||||
}
|
||||
makeCall(line: number, def: ast.Def, args: Ins[]): Ins {
|
||||
return this.make(line, { tag: "Call", def, args });
|
||||
@ -42,9 +74,16 @@ class InsCx {
|
||||
this.insts.push(ins);
|
||||
return ins;
|
||||
}
|
||||
}
|
||||
|
||||
class Component {
|
||||
constructor(
|
||||
public def: ast.Def,
|
||||
public insts: Ins[],
|
||||
) {}
|
||||
|
||||
pretty(): string {
|
||||
let result = "";
|
||||
let result = `${this.def.ident}:\n`;
|
||||
let regIds = 0;
|
||||
const insRegs = new Map<Ins, number>();
|
||||
|
||||
@ -57,6 +96,12 @@ class InsCx {
|
||||
|
||||
for (const ins of this.insts) {
|
||||
switch (ins.kind.tag) {
|
||||
case "Gnd":
|
||||
result += ` ${r(ins)} = gnd\n`;
|
||||
break;
|
||||
case "Vin":
|
||||
result += ` ${r(ins)} = vin\n`;
|
||||
break;
|
||||
case "Input":
|
||||
result += ` ${r(ins)} = input ${ins.kind.idx}\n`;
|
||||
break;
|
||||
@ -65,24 +110,26 @@ class InsCx {
|
||||
break;
|
||||
case "RelayDefaultOff":
|
||||
result += ` ${r(ins)} = relay_default_off ${
|
||||
r(ins.kind.args[0])
|
||||
} ${r(ins.kind.args[1])}\n`;
|
||||
r(ins.kind.a)
|
||||
}, ${r(ins.kind.b)}\n`;
|
||||
break;
|
||||
case "RelayDefaultOn":
|
||||
result += ` ${r(ins)} = relay_default_on ${
|
||||
r(ins.kind.args[0])
|
||||
} ${r(ins.kind.args[1])}\n`;
|
||||
r(ins.kind.a)
|
||||
}, ${r(ins.kind.b)}\n`;
|
||||
break;
|
||||
case "Call":
|
||||
result += ` ${r(ins)} = call @${ins.kind.def.ident} ${
|
||||
r(ins.kind.args[0])
|
||||
} ${r(ins.kind.args[1])}\n`;
|
||||
result += ` ${r(ins)} = call @${ins.kind.def.ident}, ${
|
||||
ins.kind.args.map((a) => r(a)).join(", ")
|
||||
}\n`;
|
||||
break;
|
||||
case "Elem":
|
||||
result += ` ${r(ins)} = elem ${
|
||||
r(ins.kind.value)
|
||||
} ${ins.kind.idx}\n`;
|
||||
}, ${ins.kind.idx}\n`;
|
||||
break;
|
||||
default:
|
||||
ins.kind satisfies never;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -90,19 +137,19 @@ class InsCx {
|
||||
}
|
||||
|
||||
class DefLowerer {
|
||||
private symIns = new Map<front.Sym, Ins>();
|
||||
private cx = new InsCx();
|
||||
private letIns = new Map<ast.Stmt, Ins[]>();
|
||||
|
||||
constructor(
|
||||
private cx: InsCx,
|
||||
private def: ast.Def,
|
||||
private resols: Map<ast.Expr, front.Sym>,
|
||||
) {}
|
||||
|
||||
lower() {
|
||||
lower(): Component {
|
||||
for (const stmt of this.def.stmts) {
|
||||
this.lowerStmt(stmt);
|
||||
}
|
||||
return new Component(this.def, this.cx.insts);
|
||||
}
|
||||
|
||||
lowerStmt(stmt: ast.Stmt) {
|
||||
@ -114,7 +161,7 @@ class DefLowerer {
|
||||
this.cx.makeSet(
|
||||
stmt.line,
|
||||
re.idx,
|
||||
this.lowerExpr(stmt.kind.expr),
|
||||
this.lowerExprToVal(stmt.kind.expr),
|
||||
);
|
||||
break;
|
||||
case "Builtin":
|
||||
@ -140,6 +187,17 @@ class DefLowerer {
|
||||
}
|
||||
}
|
||||
|
||||
lowerExprToVal(expr: ast.Expr): Ins {
|
||||
if (expr.kind.tag === "Call") {
|
||||
return this.cx.makeElem(
|
||||
expr.line,
|
||||
this.lowerExpr(expr),
|
||||
0,
|
||||
);
|
||||
}
|
||||
return this.lowerExpr(expr);
|
||||
}
|
||||
|
||||
lowerExpr(expr: ast.Expr): Ins {
|
||||
switch (expr.kind.tag) {
|
||||
case "Ident": {
|
||||
@ -150,7 +208,15 @@ class DefLowerer {
|
||||
case "Node": {
|
||||
return this.letIns.get(re.stmt)![re.idx];
|
||||
}
|
||||
case "Builtin":
|
||||
case "Builtin": {
|
||||
switch (expr.kind.ident) {
|
||||
case "gnd":
|
||||
return this.cx.makeGnd(expr.line);
|
||||
case "vin":
|
||||
return this.cx.makeVin(expr.line);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "Output":
|
||||
case "Def":
|
||||
throw new Error();
|
||||
@ -168,16 +234,14 @@ class DefLowerer {
|
||||
case "relay_default_off":
|
||||
return this.cx.makeRelayDefaultOff(
|
||||
expr.line,
|
||||
expr.kind.args.map((expr) =>
|
||||
this.lowerExpr(expr)
|
||||
),
|
||||
this.lowerExprToVal(expr.kind.args[0]),
|
||||
this.lowerExprToVal(expr.kind.args[1]),
|
||||
);
|
||||
case "relay_default_on":
|
||||
return this.cx.makeRelayDefaultOn(
|
||||
expr.line,
|
||||
expr.kind.args.map((expr) =>
|
||||
this.lowerExpr(expr)
|
||||
),
|
||||
this.lowerExprToVal(expr.kind.args[0]),
|
||||
this.lowerExprToVal(expr.kind.args[1]),
|
||||
);
|
||||
default:
|
||||
throw new Error();
|
||||
@ -188,7 +252,9 @@ class DefLowerer {
|
||||
return this.cx.makeCall(
|
||||
expr.line,
|
||||
re.def,
|
||||
expr.kind.args.map((expr) => this.lowerExpr(expr)),
|
||||
expr.kind.args.map((expr) =>
|
||||
this.lowerExprToVal(expr)
|
||||
),
|
||||
);
|
||||
break;
|
||||
}
|
||||
@ -206,15 +272,117 @@ class DefLowerer {
|
||||
}
|
||||
}
|
||||
|
||||
class IrInterpreter {
|
||||
constructor(
|
||||
private components: Map<ast.Def, Component>,
|
||||
) {}
|
||||
|
||||
eval(com: Component, inputs: boolean[]): boolean[] {
|
||||
if (inputs.length !== com.def.inputs.length) {
|
||||
throw new Error(
|
||||
`incorrect arguments to component '${com.def.ident}'. expected ${com.def.inputs.length}, got ${inputs.length}`,
|
||||
);
|
||||
}
|
||||
const outputs = new Array(com.def.outputs.length).fill(false);
|
||||
const tups = new Map<Ins, boolean[]>();
|
||||
const vals = new Map<Ins, boolean>();
|
||||
for (const ins of com.insts) {
|
||||
switch (ins.kind.tag) {
|
||||
case "Gnd":
|
||||
vals.set(ins, false);
|
||||
break;
|
||||
case "Vin":
|
||||
vals.set(ins, true);
|
||||
break;
|
||||
case "Input":
|
||||
vals.set(ins, inputs[ins.kind.idx]);
|
||||
break;
|
||||
case "Set":
|
||||
outputs[ins.kind.idx] = vals.get(ins.kind.value);
|
||||
break;
|
||||
case "RelayDefaultOff":
|
||||
tups.set(
|
||||
ins,
|
||||
[
|
||||
vals.get(ins.kind.a)! &&
|
||||
vals.get(ins.kind.b)!,
|
||||
],
|
||||
);
|
||||
break;
|
||||
case "RelayDefaultOn":
|
||||
tups.set(
|
||||
ins,
|
||||
[
|
||||
!vals.get(ins.kind.a)! &&
|
||||
vals.get(ins.kind.b)!,
|
||||
],
|
||||
);
|
||||
break;
|
||||
case "Call":
|
||||
tups.set(
|
||||
ins,
|
||||
this.eval(
|
||||
this.components.get(ins.kind.def)!,
|
||||
ins.kind.args.map((arg) => vals.get(arg)!),
|
||||
),
|
||||
);
|
||||
break;
|
||||
case "Elem":
|
||||
vals.set(
|
||||
ins,
|
||||
tups.get(ins.kind.value)![ins.kind.idx],
|
||||
);
|
||||
break;
|
||||
default:
|
||||
ins.kind satisfies never;
|
||||
}
|
||||
}
|
||||
return outputs;
|
||||
}
|
||||
}
|
||||
|
||||
const text = await Deno.readTextFile(Deno.args[0]);
|
||||
const toks = front.tokenize(text);
|
||||
const sexprs = front.parseSExprs(toks);
|
||||
const defs = front.parseAst(sexprs);
|
||||
const resols = front.resolve(defs);
|
||||
|
||||
const printer = new ColorPrinter("llvm");
|
||||
|
||||
const components = new Map<ast.Def, Component>();
|
||||
for (const def of defs) {
|
||||
console.log(`${def.ident}:`);
|
||||
const cx = new InsCx();
|
||||
new DefLowerer(cx, def, resols.get(def)!).lower();
|
||||
console.log(cx.pretty());
|
||||
const component = new DefLowerer(def, resols.get(def)!).lower();
|
||||
components.set(def, component);
|
||||
await printer.print(component.pretty());
|
||||
}
|
||||
await printer.close();
|
||||
|
||||
const interpreter = new IrInterpreter(components);
|
||||
|
||||
while (true) {
|
||||
const line = prompt(">");
|
||||
if (!line) {
|
||||
break;
|
||||
}
|
||||
const s = front.parseSExprs(front.tokenize("(" + line + ")"))[0];
|
||||
|
||||
const ident = s.exprs?.[0].ident;
|
||||
const args = s.exprs
|
||||
?.slice(1)
|
||||
?.map((s) =>
|
||||
s.ident && /^[01]$/.test(s.ident) ? s.ident === "1" : null
|
||||
);
|
||||
|
||||
if (!ident || !args || args.some((a) => a === null)) {
|
||||
console.error(`error: malformed expression`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const com = components.values().find((v) => v.def.ident === ident);
|
||||
if (!com) {
|
||||
throw new Error(`no component '${ident}'`);
|
||||
}
|
||||
const output = interpreter.eval(com, args as boolean[]);
|
||||
const result = output.map((v) => v ? "1" : "0").join(" ");
|
||||
console.log(`= ${result}`);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user