UNPKG

functasy

Version:

An esoteric Turing complete programming language

209 lines (170 loc) 5.49 kB
'use strict'; const fs = require('fs'); const path = require('path'); const O = require('./framework'); const parser = require('./parser'); class Engine{ constructor(src){ this.src = parser.parse(Buffer.from(src)); this.meta = new Closure(parser.meta()); } run(){ var chain = this.src; var closure = new Closure(chain); var stack = [new StackFrame(closure)]; while(stack.length !== 0){ var frame = O.last(stack); if(frame.len() === 0){ // The current chain is finished if(frame.val === null){ // Meta chain is called // When called, meta chain returns itself frame.val = this.meta; continue; } // Pop the last stack frame stack.pop(); // If this was not the last chain, put the value on the previous frame if(stack.length !== 0) O.last(stack).val = frame.val; continue; } // Get the next element from the current closure var elem = frame.next(); var len = frame.len(); // val - old value, v - new value var val = frame.val; var v = frame.eval(elem); if(v.isMeta()){ // Meta chain /** * Meta chain is special. Its behavior depends on the * situation it appears in. There are 6 different cases */ var next = frame.next(); var n = frame.eval(next); if(val === null){ if(next === null){ // Case 1: [empty] [empty] // Initialize the stack frame with the meta chain frame.val = v; }else if(next.isIdent()){ // Case 2: [empty] [ident] // Output 1 this.write(1); // Evaluate the next element and save it in the frame value frame.val = n; }else{ // Case 3: [empty] [chain] // Read the next bit if(this.read()){ // If the next bit is 1, call the chain call(n, v); }else{ // If the next bit is 0, return the chain frame.val = n; } } }else{ if(next === null){ // Case 4: [value] [empty] // Output 0 this.write(0); }else if(next.isIdent()){ // Case 5: [value] [ident] // Update the value associated with the given identifier frame.set(next.id, val); }else{ // Case 6: [value] [chain] // Perform the call in the reversed order call(n, val); } } continue; } if(val === null){ // There is nothing on the current stack frame // Initialize the stack frame value frame.val = v; continue; } call(val, v); } // Call the given chain with the given argument function call(chain, arg){ // Create a new stack frame var newFrame = chain.call(arg); if(O.last(stack).len() === 0){ /** * In case this was the last call from the current chain, * replace the current stack frame with the new one */ stack[stack.length - 1] = newFrame; }else{ // Otherwise just push the new frame to the stack stack.push(newFrame); } } } }; module.exports = Engine; class StackFrame{ constructor(closure, val=null){ this.closure = closure; this.val = val; } eval(elem){ return this.closure.eval(elem); } next(){ return this.closure.next(); } len(){ return this.closure.len(); } get(ident){ return this.closure.get(ident); } set(ident, val){ this.closure.set(ident, val); } toString(){ var {closure} = this; var str = this.val !== null ? '[V]' : '[.]'; str += ' ' + closure; str = str.padEnd(50); for(var ident in closure.idents){ ident |= 0; var val = this.get(ident); if(val === null) continue; str += parser.toName(ident) + ': '; str += `(${val.toString(1)})`; str += ' '.repeat(3); } return str; } }; class Closure{ constructor(from, idents=null, arg=null){ this.elems = from.elems; var depth = from.depth; this.depth = depth; this.idents = O.obj(); for(var ident in from.idents){ ident |= 0; if(ident <= depth - 2) this.idents[ident] = idents[ident]; } if((this.depth - 1) in from.idents) this.idents[this.depth - 1] = new Reference(arg); this.index = 0; } call(arg){ var closure = new Closure(this, this.idents, arg); return new StackFrame(closure); } eval(elem){ if(elem === null) return null; if(elem.isIdent()) return this.get(elem.id); return new Closure(elem, this.idents); } next(){ if(this.len() === 0) return null; return this.elems[this.index++]; } len(){ return this.elems.length - this.index; } get(ident){ return this.idents[ident].get(); } set(ident, val){ this.idents[ident].set(val); } isMeta(){ return this.len() === 0; } toString(full=0){ var elems = this.elems; if(!full) elems = elems.slice(this.index); return elems.join(''); } }; class Reference{ constructor(val){ this.val = val; } get(){ return this.val; } set(val){ this.val = val; } };