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))
|
(let (a_and_b) (relay_default_off a b))
|
||||||
(set r (relay_default_on a_and_b vin))
|
(set r (relay_default_on a_and_b vin))
|
||||||
))
|
))
|
||||||
@ -45,3 +45,4 @@
|
|||||||
(set carry_out e1)
|
(set carry_out e1)
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -26,7 +26,7 @@ export function tokenize(text: string): Tok[] {
|
|||||||
)[0]
|
)[0]
|
||||||
.map(({ tok, line }) => ({ ty: tok, text: tok, line }))
|
.map(({ tok, line }) => ({ ty: tok, text: tok, line }))
|
||||||
.map((tok) => {
|
.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";
|
tok.ty = "ident";
|
||||||
}
|
}
|
||||||
return tok;
|
return tok;
|
||||||
@ -263,6 +263,8 @@ class DefResolver {
|
|||||||
private syms = new Map<string, Sym>([
|
private syms = new Map<string, Sym>([
|
||||||
["relay_default_off", { tag: "Builtin" }],
|
["relay_default_off", { tag: "Builtin" }],
|
||||||
["relay_default_on", { tag: "Builtin" }],
|
["relay_default_on", { tag: "Builtin" }],
|
||||||
|
["gnd", { tag: "Builtin" }],
|
||||||
|
["vin", { tag: "Builtin" }],
|
||||||
]);
|
]);
|
||||||
public resols = new Map<Expr, Sym>();
|
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 front from "./front.ts";
|
||||||
import * as ast from "./ast.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 {
|
class Ins {
|
||||||
constructor(
|
constructor(
|
||||||
public line: number,
|
public line: number,
|
||||||
@ -9,26 +34,33 @@ class Ins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type InsKind =
|
type InsKind =
|
||||||
|
| { tag: "Gnd" | "Vin" }
|
||||||
| { tag: "Input"; idx: number }
|
| { tag: "Input"; idx: number }
|
||||||
| { tag: "Set"; idx: number; value: Ins }
|
| { 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: "Call"; def: ast.Def; args: Ins[] }
|
||||||
| { tag: "Elem"; value: Ins; idx: number };
|
| { tag: "Elem"; value: Ins; idx: number };
|
||||||
|
|
||||||
class InsCx {
|
class InsCx {
|
||||||
public insts: Ins[] = [];
|
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 {
|
makeInput(line: number, idx: number): Ins {
|
||||||
return this.make(line, { tag: "Input", idx });
|
return this.make(line, { tag: "Input", idx });
|
||||||
}
|
}
|
||||||
makeSet(line: number, idx: number, value: Ins): Ins {
|
makeSet(line: number, idx: number, value: Ins): Ins {
|
||||||
return this.make(line, { tag: "Set", idx, value });
|
return this.make(line, { tag: "Set", idx, value });
|
||||||
}
|
}
|
||||||
makeRelayDefaultOff(line: number, args: Ins[]): Ins {
|
makeRelayDefaultOff(line: number, a: Ins, b: Ins): Ins {
|
||||||
return this.make(line, { tag: "RelayDefaultOff", args });
|
return this.make(line, { tag: "RelayDefaultOff", a, b });
|
||||||
}
|
}
|
||||||
makeRelayDefaultOn(line: number, args: Ins[]): Ins {
|
makeRelayDefaultOn(line: number, a: Ins, b: Ins): Ins {
|
||||||
return this.make(line, { tag: "RelayDefaultOn", args });
|
return this.make(line, { tag: "RelayDefaultOn", a, b });
|
||||||
}
|
}
|
||||||
makeCall(line: number, def: ast.Def, args: Ins[]): Ins {
|
makeCall(line: number, def: ast.Def, args: Ins[]): Ins {
|
||||||
return this.make(line, { tag: "Call", def, args });
|
return this.make(line, { tag: "Call", def, args });
|
||||||
@ -42,9 +74,16 @@ class InsCx {
|
|||||||
this.insts.push(ins);
|
this.insts.push(ins);
|
||||||
return ins;
|
return ins;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Component {
|
||||||
|
constructor(
|
||||||
|
public def: ast.Def,
|
||||||
|
public insts: Ins[],
|
||||||
|
) {}
|
||||||
|
|
||||||
pretty(): string {
|
pretty(): string {
|
||||||
let result = "";
|
let result = `${this.def.ident}:\n`;
|
||||||
let regIds = 0;
|
let regIds = 0;
|
||||||
const insRegs = new Map<Ins, number>();
|
const insRegs = new Map<Ins, number>();
|
||||||
|
|
||||||
@ -57,6 +96,12 @@ class InsCx {
|
|||||||
|
|
||||||
for (const ins of this.insts) {
|
for (const ins of this.insts) {
|
||||||
switch (ins.kind.tag) {
|
switch (ins.kind.tag) {
|
||||||
|
case "Gnd":
|
||||||
|
result += ` ${r(ins)} = gnd\n`;
|
||||||
|
break;
|
||||||
|
case "Vin":
|
||||||
|
result += ` ${r(ins)} = vin\n`;
|
||||||
|
break;
|
||||||
case "Input":
|
case "Input":
|
||||||
result += ` ${r(ins)} = input ${ins.kind.idx}\n`;
|
result += ` ${r(ins)} = input ${ins.kind.idx}\n`;
|
||||||
break;
|
break;
|
||||||
@ -65,24 +110,26 @@ class InsCx {
|
|||||||
break;
|
break;
|
||||||
case "RelayDefaultOff":
|
case "RelayDefaultOff":
|
||||||
result += ` ${r(ins)} = relay_default_off ${
|
result += ` ${r(ins)} = relay_default_off ${
|
||||||
r(ins.kind.args[0])
|
r(ins.kind.a)
|
||||||
} ${r(ins.kind.args[1])}\n`;
|
}, ${r(ins.kind.b)}\n`;
|
||||||
break;
|
break;
|
||||||
case "RelayDefaultOn":
|
case "RelayDefaultOn":
|
||||||
result += ` ${r(ins)} = relay_default_on ${
|
result += ` ${r(ins)} = relay_default_on ${
|
||||||
r(ins.kind.args[0])
|
r(ins.kind.a)
|
||||||
} ${r(ins.kind.args[1])}\n`;
|
}, ${r(ins.kind.b)}\n`;
|
||||||
break;
|
break;
|
||||||
case "Call":
|
case "Call":
|
||||||
result += ` ${r(ins)} = call @${ins.kind.def.ident} ${
|
result += ` ${r(ins)} = call @${ins.kind.def.ident}, ${
|
||||||
r(ins.kind.args[0])
|
ins.kind.args.map((a) => r(a)).join(", ")
|
||||||
} ${r(ins.kind.args[1])}\n`;
|
}\n`;
|
||||||
break;
|
break;
|
||||||
case "Elem":
|
case "Elem":
|
||||||
result += ` ${r(ins)} = elem ${
|
result += ` ${r(ins)} = elem ${
|
||||||
r(ins.kind.value)
|
r(ins.kind.value)
|
||||||
} ${ins.kind.idx}\n`;
|
}, ${ins.kind.idx}\n`;
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
ins.kind satisfies never;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -90,19 +137,19 @@ class InsCx {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class DefLowerer {
|
class DefLowerer {
|
||||||
private symIns = new Map<front.Sym, Ins>();
|
private cx = new InsCx();
|
||||||
private letIns = new Map<ast.Stmt, Ins[]>();
|
private letIns = new Map<ast.Stmt, Ins[]>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private cx: InsCx,
|
|
||||||
private def: ast.Def,
|
private def: ast.Def,
|
||||||
private resols: Map<ast.Expr, front.Sym>,
|
private resols: Map<ast.Expr, front.Sym>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
lower() {
|
lower(): Component {
|
||||||
for (const stmt of this.def.stmts) {
|
for (const stmt of this.def.stmts) {
|
||||||
this.lowerStmt(stmt);
|
this.lowerStmt(stmt);
|
||||||
}
|
}
|
||||||
|
return new Component(this.def, this.cx.insts);
|
||||||
}
|
}
|
||||||
|
|
||||||
lowerStmt(stmt: ast.Stmt) {
|
lowerStmt(stmt: ast.Stmt) {
|
||||||
@ -114,7 +161,7 @@ class DefLowerer {
|
|||||||
this.cx.makeSet(
|
this.cx.makeSet(
|
||||||
stmt.line,
|
stmt.line,
|
||||||
re.idx,
|
re.idx,
|
||||||
this.lowerExpr(stmt.kind.expr),
|
this.lowerExprToVal(stmt.kind.expr),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case "Builtin":
|
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 {
|
lowerExpr(expr: ast.Expr): Ins {
|
||||||
switch (expr.kind.tag) {
|
switch (expr.kind.tag) {
|
||||||
case "Ident": {
|
case "Ident": {
|
||||||
@ -150,7 +208,15 @@ class DefLowerer {
|
|||||||
case "Node": {
|
case "Node": {
|
||||||
return this.letIns.get(re.stmt)![re.idx];
|
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 "Output":
|
||||||
case "Def":
|
case "Def":
|
||||||
throw new Error();
|
throw new Error();
|
||||||
@ -168,16 +234,14 @@ class DefLowerer {
|
|||||||
case "relay_default_off":
|
case "relay_default_off":
|
||||||
return this.cx.makeRelayDefaultOff(
|
return this.cx.makeRelayDefaultOff(
|
||||||
expr.line,
|
expr.line,
|
||||||
expr.kind.args.map((expr) =>
|
this.lowerExprToVal(expr.kind.args[0]),
|
||||||
this.lowerExpr(expr)
|
this.lowerExprToVal(expr.kind.args[1]),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
case "relay_default_on":
|
case "relay_default_on":
|
||||||
return this.cx.makeRelayDefaultOn(
|
return this.cx.makeRelayDefaultOn(
|
||||||
expr.line,
|
expr.line,
|
||||||
expr.kind.args.map((expr) =>
|
this.lowerExprToVal(expr.kind.args[0]),
|
||||||
this.lowerExpr(expr)
|
this.lowerExprToVal(expr.kind.args[1]),
|
||||||
),
|
|
||||||
);
|
);
|
||||||
default:
|
default:
|
||||||
throw new Error();
|
throw new Error();
|
||||||
@ -188,7 +252,9 @@ class DefLowerer {
|
|||||||
return this.cx.makeCall(
|
return this.cx.makeCall(
|
||||||
expr.line,
|
expr.line,
|
||||||
re.def,
|
re.def,
|
||||||
expr.kind.args.map((expr) => this.lowerExpr(expr)),
|
expr.kind.args.map((expr) =>
|
||||||
|
this.lowerExprToVal(expr)
|
||||||
|
),
|
||||||
);
|
);
|
||||||
break;
|
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 text = await Deno.readTextFile(Deno.args[0]);
|
||||||
const toks = front.tokenize(text);
|
const toks = front.tokenize(text);
|
||||||
const sexprs = front.parseSExprs(toks);
|
const sexprs = front.parseSExprs(toks);
|
||||||
const defs = front.parseAst(sexprs);
|
const defs = front.parseAst(sexprs);
|
||||||
const resols = front.resolve(defs);
|
const resols = front.resolve(defs);
|
||||||
|
|
||||||
|
const printer = new ColorPrinter("llvm");
|
||||||
|
|
||||||
|
const components = new Map<ast.Def, Component>();
|
||||||
for (const def of defs) {
|
for (const def of defs) {
|
||||||
console.log(`${def.ident}:`);
|
const component = new DefLowerer(def, resols.get(def)!).lower();
|
||||||
const cx = new InsCx();
|
components.set(def, component);
|
||||||
new DefLowerer(cx, def, resols.get(def)!).lower();
|
await printer.print(component.pretty());
|
||||||
console.log(cx.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