Skip to content

Instantly share code, notes, and snippets.

@cheery
Created December 26, 2019 00:06
Show Gist options
  • Save cheery/74b83a4cb46d7612876a4655e173a3c2 to your computer and use it in GitHub Desktop.
Save cheery/74b83a4cb46d7612876a4655e173a3c2 to your computer and use it in GitHub Desktop.
Interaction & arithmetic in linear abstract machine
function main() {
interaction_example();
arithmetic_example();
}
function interaction_example() {
var plug = make_terminal_plug();
var code = [
SEL, 1,
SEL, 0,
SEL, 1,
SEL, 2,
RET ];
function vector(op, code, pc, sp) {
return null;
}
plug.then(function(sp){
spawn(vector, code, 0, sp).then(function(value){
console.log("result is:", value);
});
});
}
function arithmetic_example() {
var code = [
INSL,
SPLIT, 20, 4, 13,
IND, 0, IND, 1, IND, 1, IND, 1, RET,
IND, 0, IND, 1, IND, 1, RET,
// Addition of two numbers:
SPLIT, 26, 4, 25,
ALT, 3, 8, // Decompose first number
CUR, 4, 3, DELL, RET,
CUR, 13, 3,
SPLIT, 9, 4, 6,
JUMP, 113,
IND, 1, RET,
APP, RET,
APP,
RET ];
function vector(op, code, pc, sp) {
// return {code, pc, sp} otherwise.
return null;
}
spawn(vector, code, 0, null).then(function(value) {
console.log("3+2 is", value);
});
}
function make_terminal_plug() {
var INP = 0;
var OUTP = 1;
var CLOSP = 2;
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: false
});
function vector(op, code, pc, sp) {
if (op == INP) {
var resp = new Promise(function(resolve, reject) {
try {
rl.once('line', function(line) {
resolve(sp);
});
} catch (e) {
reject(e);
}
});
return {code:code, pc:pc, sp:resp};
}
if (op == OUTP) {
console.log("HELLOOO");
return {code:code, pc:pc, sp:sp};
}
if (op == CLOSP) {
rl.close();
return {code:code, pc:pc, sp:sp};
}
return null;
}
var code = [
EXP, 3, 0,
OPT, 14, 5, 8, 11,
INST, SYS+INP, RET,
INST, SYS+OUTP, RET,
DROP, SYS+CLOSP, RET,
RET ];
return spawn(vector, code, 0, null);
}
var SPLIT = 1,
ASSL = 2,
ASSR = 3,
INSL = 4,
DELL = 5,
EXCH = 6,
APP = 7,
SEL = 8,
IND = 9,
INST = 10,
DUP = 11,
DROP = 12,
ALT = 13,
OPT = 14,
CUR = 15,
EXP = 16,
RET = 17,
JUMP = 18,
UNR = 19,
SYS = 20;
function opname(op) {
switch (op) {
case SPLIT: return "SPLIT";
case ASSL: return "ASSL";
case ASSR: return "ASSR";
case INSL: return "INSL";
case DELL: return "DELL";
case EXCH: return "EXCH";
case APP: return "APP";
case SEL: return "SEL";
case IND: return "IND";
case INST: return "INST";
case DUP: return "DUP";
case DROP: return "DROP";
case ALT: return "ALT";
case OPT: return "OPT";
case CUR: return "CUR";
case EXP: return "EXP";
case RET: return "RET";
case JUMP: return "JUMP";
case UNR: return "UNR";
case SYS: return "SYS";
}
}
/* Data structures */
function cons(a, b) {
return [a, b];
}
function is_cons(sp) {
return (sp instanceof Array);
}
function Comp(sp, com) {
this.sp = sp;
this.com = com;
}
function comp(sp, env, code, pc) {
/* Exponents a bit different from other structures. */
if (!(sp instanceof Comp) || is_comp(sp, EXP)) {
sp = new Comp(sp, [{env:env, code:code, pc:pc}]);
}
else {
sp.com.push({env:env, code:code, pc:pc});
}
return sp;
}
function is_comp(sp, op) {
if (sp instanceof Comp) {
var ref = sp.com[sp.com.length-1];
return (ref.code[ref.pc] === op);
}
return false;
}
function uncomp(sp) {
if (sp.com.length == 1) {
var ref = sp.com[0];
return {env:ref.env, code:ref.code, pc:ref.pc, sp:sp.sp};
}
else {
var ref = sp.com.pop();
ref.sp = sp;
return ref;
}
}
function is_susp(sp) {
return (sp instanceof Promise);
}
/* Virtual machine */
function spawn(env, code, pc, sp) {
return new Promise(function(resolve, reject) {
try {
run(resolve, env, code, pc, sp);
} catch (e) {
reject(e);
}
});
}
/* Bytecode access */
function getnum(code, pc) {
var octet = code[pc];
pc += 1;
sign = octet & 64;
num = (octet & 63) - (octet & sign);
while (octet >= 128) {
octet = code[pc];
pc += 1;
num = (num << 7) | (octet & 127);
}
return {num:num, code:code, pc:pc};
}
/* Interpreter */
function run(resolve, env, code, pc, sp) {
if (is_susp(sp)) {
return sp.then(function(sp) { run(resolve, env, code, pc, sp); });
}
while (true) {
//console.log("AT", pc + ":", opname(code[pc]), "SP", sp);
switch(code[pc]) {
case SPLIT:
if (is_cons(sp)) {
var p1 = getnum(code, pc+1);
var p2 = getnum(code, p1.pc);
var p3 = getnum(code, p2.pc);
var pc3 = p1.num + pc;
var pc1 = p2.num + pc;
var pc2 = p3.num + pc;
sp = cons(spawn(env, code, pc1, sp[0]),
spawn(env, code, pc2, sp[1]));
pc = pc3;
break;
} else {
throw "a type error";
}
case ASSL:
if (is_cons(sp) && is_cons(sp[1])) {
sp = cons(cons(sp[0], sp[1][0]), sp[1][1]);
pc += 1;
break;
} else if (is_cons(sp) && is_susp(sp[1])) {
return sp[1].then(function(sp1) {
run(resolve, env, code, pc, cons(sp[0], sp1));
});
} else {
throw "b type error";
}
case ASSR:
if (is_cons(sp) && is_cons(sp[0])) {
sp = cons(sp[0][0], cons(sp[0][1], sp[1]));
pc += 1;
break;
} else if (is_cons(sp) && is_susp(sp[0])) {
return sp[0].then(function(sp0) {
run(resolve, env, code, pc, cons(sp0, sp[1]));
});
} else {
throw "c type error";
}
case INSL:
sp = cons(null, sp);
pc += 1;
break;
case DELL:
if (is_cons(sp) && (sp[0] === null)) {
sp = sp[1];
pc += 1;
if (is_susp(sp)) {
return sp.then(function(sp) {
run(resolve, env, code, pc, sp);
});
}
break;
} else if (is_cons(sp) && is_susp(sp[0])) {
sp[0].then(function(sp0) {
if (sp0 !== null) {
throw "d type error";
}
});
sp = sp[1];
pc += 1;
if (is_susp(sp)) {
return sp.then(function(sp) {
run(resolve, env, code, pc, sp);
});
}
break;
} else {
throw "e type error";
}
case EXCH:
if (is_cons(sp)) {
sp = cons(sp[1], sp[0]);
pc += 1;
break;
} else {
throw "f type error";
}
case APP:
if (is_cons(sp) && is_comp(sp[0], CUR)) {
var a = sp[1];
var ref = uncomp(sp[0]);
var jump_pc = getnum(ref.code, getnum(ref.code, ref.pc+1).pc).num + ref.pc;
return spawn(ref.env, ref.code, jump_pc, cons(ref.sp, a)).then(function(sp) {
run(resolve, env, code, pc + 1, sp);
});
} else if (is_cons(sp) && is_susp(sp[0])) {
return sp[0].then(function(sp0) {
run(resolve, env, code, pc, cons(sp0, sp[1]));
});
} else {
throw "g type error";
}
case SEL:
if (is_comp(sp, OPT)) {
var n = getnum(code, pc+1);
var ref = uncomp(sp);
sp = ref.sp;
var size = getnum(ref.code, ref.pc+1).pc - ref.pc - 1;
var jump_pc = getnum(ref.code, ref.pc+1+size+size * n.num).num + ref.pc;
// console.log("size is", size)
// console.log("numeric selection", n.num);
// console.log("selection is", jump_pc);
return spawn(ref.env, ref.code, jump_pc, sp).then(function(sp) {
run(resolve, env, code, n.pc, sp);
});
} else {
throw "h type error";
}
case IND:
sp = comp(sp, env, code, pc);
pc = getnum(code, pc+1).pc;
break;
case INST:
if (is_comp(sp, EXP)) {
var ref = uncomp(sp);
var jump_pc = getnum(ref.code, getnum(ref.code, ref.pc+1).pc).num + ref.pc;
return spawn(ref.env, ref.code, jump_pc, ref.sp).then(function(sp) {
run(resolve, env, code, pc+1, sp);
});
} else {
throw "i type error";
}
case DUP:
if (is_comp(sp, EXP)) {
sp = cons(sp, sp);
pc += 1;
break;
} else {
throw "j type error";
}
case DROP:
if (is_comp(sp, EXP)) {
sp = null;
pc += 1;
break;
} else {
throw "l type error";
}
case ALT:
if (is_comp(sp, IND)) {
var ref = uncomp(sp);
var n = getnum(ref.code, ref.pc+1);
var size = getnum(code, pc+1).pc - pc - 1;
var jump_pc = getnum(code, pc+1+size * n.num).num + pc;
sp = ref.sp;
pc = jump_pc;
} else {
throw "m type error";
}
case OPT: case CUR: case EXP:
sp = comp(sp, env, code, pc);
pc = getnum(code, pc+1).num + pc;
break;
case RET:
return resolve(sp);
case JUMP:
pc = getnum(code, pc+1).num + pc;
break;
case UNR:
throw "n type error";
default: {
var n = getnum(code, pc);
var resp = env(n.num - SYS, code, n.pc, sp);
if (resp === null) {
throw "o type error";
} else {
code = resp.code;
pc = resp.pc;
sp = resp.sp;
if (is_susp(sp)) {
return sp.then(function(sp) {
run(resolve, env, code, pc, sp);
});
}
}
}
}
}
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment