Created
December 26, 2019 00:06
-
-
Save cheery/74b83a4cb46d7612876a4655e173a3c2 to your computer and use it in GitHub Desktop.
Interaction & arithmetic in linear abstract machine
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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