Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- const enum Op {
- IF,
- MUL, DIV, MOD, ADD, MIN,
- EQ, NE, GT, LT, GE, LE, IN,
- AND, OR,
- IMM,
- UNARY_MIN, NOT,
- MAKE_EMPTY_LIST,
- MAKE_SINGLETON_LIST,
- LIST_APPEND,
- LIST_ADD_TAIL,
- RET,
- RET0,
- JUMP,
- CALL_BI,
- CALL_VERB,
- FORK,
- FORK_WITH_ID,
- SUSPEND,
- EXTENDED,
- POP,
- tinyIntStart,
- lastOpCode = 255
- }
- const tinyIntLow = -10;
- const tinyIntHi = Op.lastOpCode - Op.tinyIntStart + tinyIntLow;
- function isTinyIntOpCode(op: Op): boolean {
- return op >= Op.tinyIntStart;
- }
- function opCodeToTinyInt(op: Op): number {
- return op - Op.tinyIntStart + tinyIntLow;
- }
- function tinyIntToOpCode(i: number): Op {
- return Op.tinyIntStart + i - tinyIntLow;
- }
- function inTinyIntRange(i: number): boolean {
- return i >= tinyIntLow && i <= tinyIntHi;
- }
- interface Program {
- main: Buffer;
- forks: Buffer[];
- literals: any[];
- }
- class Frame {
- program: Program;
- caller: Frame;
- vp: number;
- stack = [];
- ip = 0;
- env = {};
- constructor(program: Program, caller: Frame = null, vp = -1) {
- this.program = program;
- this.caller = caller;
- this.vp = vp;
- }
- }
- class DelayedFrame {
- frame: Frame;
- delay: number;
- constructor(frame: Frame, delay: number) {
- this.frame = frame;
- this.delay = delay;
- }
- }
- function toInt(bytes: Buffer): number {
- var n = 0;
- var len = bytes.length;
- for (var i = 0; i < len; i++) {
- var shift = (len - i - 1) * 8;
- n += bytes[i] * (1 << shift);
- }
- return n;
- }
- class VM {
- private objects: (id: number) => any;
- constructor(objects: (id: number) => any) {
- this.objects = objects;
- }
- run(f: Frame): [any, DelayedFrame[]] {
- let top = f;
- while (top) {
- let [r, cont, delayed] = this.exec(top);
- if (cont) {
- top = cont;
- top.stack.push(r);
- }
- else {
- return [r, delayed];
- }
- }
- }
- exec(f: Frame): [any, Frame, DelayedFrame[]] {
- var y, x, z, r, s, d, t;
- let forks = [];
- let code = f.vp == -1 ? f.program.main : f.program.forks[f.vp];
- while(f.ip < code.length) {
- let op = code[f.ip];
- switch(op) {
- case Op.ADD:
- y = f.stack.pop();
- x = f.stack.pop();
- r = x + y;
- f.stack.push(r);
- break;
- case Op.MIN:
- y = f.stack.pop();
- x = f.stack.pop();
- r = x - y;
- f.stack.push(r);
- break;
- case Op.MUL:
- y = f.stack.pop();
- x = f.stack.pop();
- r = x * y;
- f.stack.push(r);
- break;
- case Op.DIV:
- y = f.stack.pop();
- x = f.stack.pop();
- r = x / y;
- f.stack.push(r);
- break;
- case Op.MOD:
- y = f.stack.pop();
- x = f.stack.pop();
- r = x % y;
- f.stack.push(r);
- break;
- case Op.IN:
- y = f.stack.pop();
- x = f.stack.pop();
- r = y.includes(x);
- f.stack.push(r);
- break;
- case Op.IF:
- x = f.stack.pop(); // cond
- break;
- case Op.IMM:
- s = code[f.ip];
- r = f.program.literals[s];
- f.stack.push(r);
- break;
- case Op.CALL_BI:
- y = f.stack.pop(); // args
- x = f.stack.pop(); // fname
- // We can't call verbs from inside builtin functions so
- // we can just execute this inline, push the result to
- // the current stack and continue.
- r = this[x](y);
- f.stack.push(r);
- break;
- case Op.CALL_VERB:
- z = f.stack.pop(); // args
- y = f.stack.pop(); // vname
- x = f.stack.pop(); // objid
- let id = +(x.substring(1));
- let obj = this.objects(id);
- let prog = obj[y];
- let cont = new Frame(prog, f);
- return [0, cont, forks];
- case Op.FORK:
- y = f.stack.pop(); // delay
- x = f.stack.pop(); // index
- t = new Frame(f.program, null, x);
- d = new DelayedFrame(t, y);
- forks.push(d);
- break;
- case Op.SUSPEND:
- // We'll continue on this frame after `delay` seconds
- // but because we are returning here we'll have to
- // manually progress the instruction pointer to point
- // at the first instruction following `suspend`.
- f.ip += 1;
- x = f.stack.pop(); // delay
- d = new DelayedFrame(f, x);
- return [0, null, forks.concat(d)];
- case Op.RET:
- r = f.stack.pop();
- return [r, null, forks];
- case Op.RET0:
- return [0, null, forks];
- case Op.MAKE_EMPTY_LIST:
- f.stack.push([]);
- break;
- case Op.MAKE_SINGLETON_LIST:
- x = f.stack.pop();
- f.stack.push([x]);
- break;
- case Op.LIST_ADD_TAIL:
- y = f.stack.pop();
- x = f.stack.pop();
- r = x.concat(y);
- f.stack.push(r);
- break;
- case Op.LIST_APPEND:
- y = f.stack.pop();
- x = f.stack.pop();
- r = x.concat(y);
- f.stack.push(r);
- break;
- case Op.POP:
- f.stack.pop();
- break;
- default:
- if (isTinyIntOpCode(op)) {
- f.stack.push(opCodeToTinyInt(op));
- }
- else {
- throw new Error('E_INVOP');
- }
- }
- f.ip += 1;
- }
- throw new Error('E_NORET');
- }
- private log(args): number {
- console.log(args);
- return 0;
- }
- }
- export {
- tinyIntToOpCode,
- Op,
- Object,
- Program,
- Frame,
- DelayedFrame,
- VM
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement