summaryrefslogtreecommitdiff
path: root/godel/js/compiler.js
diff options
context:
space:
mode:
authorElizabeth (Lizzy) Hunt <elizabeth.hunt@simponic.xyz>2023-11-17 12:13:30 -0700
committerGitHub <noreply@github.com>2023-11-17 12:13:30 -0700
commiteaca9073ebf8a438ec8474f15171a62082fa141b (patch)
tree371b06e288a85c14fccd785008c7abfe586a6471 /godel/js/compiler.js
parent1aefa0f9b1da1c7bb99f7605c334eaf691ba2fda (diff)
parent57a4d439847bf3d63513b2443dfdf1eca5ecbb02 (diff)
downloadsimponic.xyz-eaca9073ebf8a438ec8474f15171a62082fa141b.tar.gz
simponic.xyz-eaca9073ebf8a438ec8474f15171a62082fa141b.zip
Merge pull request #3 from Simponic/godel
L-Program and Godel Numbers
Diffstat (limited to 'godel/js/compiler.js')
-rw-r--r--godel/js/compiler.js182
1 files changed, 182 insertions, 0 deletions
diff --git a/godel/js/compiler.js b/godel/js/compiler.js
new file mode 100644
index 0000000..e978244
--- /dev/null
+++ b/godel/js/compiler.js
@@ -0,0 +1,182 @@
+class StringBuilder {
+ constructor() {
+ this.stringPieces = [];
+ }
+ add(s) {
+ this.stringPieces.push(s);
+ }
+ build() {
+ return this.stringPieces.join("");
+ }
+}
+
+const compileGoto = (gotoNode, stringBuilder) => {
+ stringBuilder.add(`this.followGoto("${gotoNode.label.symbol}");\nreturn;\n`);
+};
+
+const compileConditional = (conditionalNode, stringBuilder) => {
+ const {
+ variable: { symbol: variable },
+ goto: gotoNode,
+ } = conditionalNode;
+
+ stringBuilder.add(`if (this.get("${variable}") != 0) {\n`);
+ compileGoto(gotoNode, stringBuilder);
+ stringBuilder.add(`}\n`);
+};
+
+const compileAssignment = (assignmentNode, stringBuilder) => {
+ const {
+ variable: { symbol: variable },
+ expr,
+ } = assignmentNode;
+ if (expr.opr) {
+ if (expr.opr == "+") stringBuilder.add(`this.addOne("${variable}");\n`);
+ else if (expr.opr == "-")
+ stringBuilder.add(`this.subtractOne("${variable}");\n`);
+ } else {
+ stringBuilder.add("// noop \n");
+ }
+};
+
+const compileInstruction = (instruction, stringBuilder) => {
+ if (instruction.goto) {
+ compileGoto(instruction.goto, stringBuilder);
+ return; // ignore unreachable addition to instructionPointer
+ }
+
+ if (instruction.conditional) {
+ compileConditional(instruction.conditional, stringBuilder);
+ } else if (instruction.assignment) {
+ compileAssignment(instruction.assignment, stringBuilder);
+ }
+
+ stringBuilder.add("this.instructionPointer++;\n");
+};
+
+const compile = (ast) => {
+ const godelSequence = [];
+
+ const stringBuilder = new StringBuilder();
+ stringBuilder.add(`
+ class Program {
+ constructor() {
+ this.variables = new Map(); // variable -> natural number val
+ this.labelInstructions = new Map(); // labels to instruction indices
+ this.instructions = new Map(); // instruction indices to procedures
+
+ this.instructions.set(0, () => this.main());
+ this.instructionPointer = 0;
+ this.variables.set("Y", 0);
+
+ // -- program-specific state init --
+ this.finalInstruction = ${
+ ast.instructions.length + 1
+ }; // instruction of the implied "exit" label
+ // "E1" is the exit label
+ this.labelInstructions.set("E1", this.finalInstruction);
+ }
+
+ get(variable) {
+ if (!this.variables.has(variable)) {
+ this.variables.set(variable, 0);
+ }
+ return this.variables.get(variable);
+ }
+
+ addOne(variable) {
+ const val = this.get(variable);
+ this.variables.set(variable, val + 1);
+ }
+
+ subtractOne(variable) {
+ const val = this.get(variable);
+ this.variables.set(variable, val - 1);
+ }
+
+ followGoto(label) {
+ this.instructionPointer = this.labelInstructions.get(label);
+ }
+
+ step() {
+ if (!this.isCompleted()) {
+ const procedure = this.instructions.get(this.instructionPointer);
+ procedure();
+
+ }
+ return this.instructionPointer;
+ }
+
+ isCompleted() {
+ return this.instructionPointer == this.finalInstruction;
+ }
+
+ getResult() {
+ return this.variables.get("Y");
+ }
+
+ run(maxIter=500_000) {
+ let iter = 0;
+ while (!this.isCompleted() && (++iter) < maxIter) this.step();
+ if (iter < maxIter) {
+ return this.getResult();
+ }
+ throw new Error("Too many iterations. To resolve, please ask"
+ + " Turing how we can find if a program will halt during compilation.");
+ }
+
+ main() {
+`);
+
+ stringBuilder.add("// -- build label -> instruction map --\n");
+ for (let i = 0; i < ast.instructions.length; i++) {
+ const instruction = ast.instructions[i];
+ godelSequence.push(instruction.godel);
+
+ const line = instruction.instruction;
+ const instructionIdx = i + 1;
+ if (line.label) {
+ const symbol = line.label.symbol;
+ stringBuilder.add(
+ `this.instructions.set(${instructionIdx}, () => this.${symbol}());\n`
+ );
+ stringBuilder.add(
+ `this.labelInstructions.set("${symbol}", ${instructionIdx});\n`
+ );
+ }
+ }
+
+ stringBuilder.add("// -- compiled instructions --\n");
+ for (let i = 0; i < ast.instructions.length; i++) {
+ let instruction = ast.instructions[i].instruction;
+ const instructionIdx = i + 1;
+ if (instruction.label) {
+ const symbol = instruction.label.symbol;
+
+ stringBuilder.add(
+ ` this.followGoto("${symbol}");\n}\n\n${symbol}() {\n`
+ );
+ stringBuilder.add(`this.instructionPointer = ${instructionIdx};\n`);
+ instruction = instruction.instruction;
+ }
+
+ compileInstruction(instruction, stringBuilder);
+ }
+
+ stringBuilder.add(` }\n}\n`);
+ stringBuilder.add("// -- \n");
+ stringBuilder.add("const program = new Program();\n\n");
+ stringBuilder.add("// !! set the initial Snapshot here !!\n");
+ stringBuilder.add('// program.variables.set("X1", 2);\n\n');
+ stringBuilder.add("program.run();\n");
+ stringBuilder.add("console.log(program.variables);\n");
+ stringBuilder.add("program.getResult();\n");
+
+ return {
+ js: js_beautify(stringBuilder.build(), {
+ indent_size: 2,
+ wrap_line_length: 100,
+ }),
+ godelSequence,
+ };
+};