forthtranspiler
Version:
Forth to Javascript Transpiler
330 lines (329 loc) • 13.2 kB
JavaScript
function ext(vm) {
//////////////////////////////////////////////////////////////////////////////////////// tools
vm.equal=function equal(tag,value,expected){ var t; // asure value is exactly equal to expected
vm.tests++;
if(value===expected)
vm.passed++, vm.showTst.apply(vm,[tag+' ok']);
else{
var tv=typeof value, te=typeof expected;
t='??? '+tag+' value:'+value+' not equal to expected:'+expected
vm.showErr.apply(vm,[t]);
if(tv==='string')
t='val len '+value.length+': '+value.split('').map(function(c){
return c.charCodeAt(0).toString(16);
}).join(' '), vm.showErr.apply(vm,[t]);
if(te==='string')
t='exp len '+expected.length+': '+expected.split('').map(function(c){
return c.charCodeAt(0).toString(16);
}).join(' '), vm.showErr.apply(vm,[t]);
}
}
vm.trm=function trm(x){ // ignore all space, \t, or \n in string x
var y='';
for(var i=0;i<x.length;i++){
var c=x.charAt(i);
if(c!==' '&&c!=='\t'&&c!=='\n')y+=c;
}
return y;
}
///////////////////////////////////////////////////////////////////////////////////////////////
vm.showWords=function(){
var nw=vm.words.length;
var primitives=[], colons=[];
vm.words.forEach(function(w,i){
if(w){ var type=typeof w.xt, name=i+' '+w.name;
if(type==='function') primitives.push(name);
else if(type==='number') colons.push(name);
}
});
var np=primitives.length, nc=colons.length, ni=nw-np-nc;
vm.cr(nw+' words ('+
np+' primitives '+
nc+' colons '+
ni+' ignores');
vm.type.apply(vm,['primitives:']);
primitives.forEach(function(w){
if(vm.tob.length+w.length+1>80)vm.cr.apply(vm,[]);
vm.type.apply(vm,[' '+w]);
});
if(vm.tob)vm.cr.apply(vm,[]);
vm.type.apply(vm,['colons:']);
colons.forEach(function(w){
if(vm.tob.length+w.length+1>80)vm.cr.apply(vm,[]);
vm.type.apply(vm,[' '+w]);
});
if(vm.tob)vm.cr.apply(vm,[]);
};
vm.seeColon=function seeColon(addr){
var ip=addr,prevName='',codeLimit=0;
do {
var s=ip, w=vm.cArea[ip++];
s+=': ', n=typeof w==='object'?w.name:'';
if(n){ var x=w.xt, t=typeof x;
s+=n.replace(/</g,'<')+(t==='function'?' primitive':t==='number'?(' colon at '+x):'');
} else {
if((prevName==='branch' || prevName==='zBranch')){
if(w>0)
codeLimit=Math.max(codeLimit,ip+w);
s+='(to '+(ip+w)+') ';
}
s+=w;
}
vm.cr.apply(vm,[s]);
prevName=n;
} while((codeLimit && ip<codeLimit) || n!=='exit');
};
vm.seeWord=function seeWord(w){
var o= typeof o==='string'?vm.nameWord[w]:w;
if(typeof o==='object'){
var n=o.name, x=o.xt, t=typeof x, i=o.immediate?'immediate':'';
if(t==='function'){
vm.cr.apply(vm,[n+' primitive '+i]),vm.cr.apply(vm,[x.toString().replace(/</g,'<')]);
} else if(t==='number' && x%1===0){
vm.cr.apply(vm,[n+' colon '+i]),vm.seeColon.apply(vm,[x]);
}else{
vm.cr.apply(vm,[n+' xt='+x+' ?????']);
}
}else{
vm.cr.apply(vm,[w+' ?????']);
}
};
vm.seeArray=function seeArray(arr){
var old=vm.cArea; addr=old.length;
vm.cArea=vm.cArea.concat(arr);
vm.seeColon.apply(vm,[addr]);
vm.cArea=old;
};
vm.see=function see(x){
var o=x||vm.nextToken.apply(vm,[]);
var t=typeof o;
if(t==='number' && o%1===0){
vm.seeColon.apply(vm,[o]);
} else if(t==='object'){
vm.seeWord.apply(vm,[o]);
} else if(t==='string'){
vm.seeWord.apply(vm,[vm.nameWord[o]]);
} else {
vm.cr.apply(vm,[o+' ?????']);
}
};
//////////////////////////////////////////////////////////////////////////////////////// tools
vm.addWord.apply(vm,['code' ,vm.code]);
vm.addWord.apply(vm,['doLit' ,vm.doLit]); // v2
vm.addWord.apply(vm,['exit' ,vm.exit ]); // v2
vm.addWord.apply(vm,['words' ,vm.showWords]);
vm.addWord.apply(vm,['see' ,vm.see]);
vm.addWord.apply(vm,['type' ,vm.type]);
vm.addWord.apply(vm,['cr' ,vm.cr]);
//////////////////////////////////////////////////////////////////////////////////////////// v1
vm.addWord.apply(vm,[ '.' ,function(){vm.type.call(this),vm.type.apply(this,[" "]);}]);
vm.addWord.apply(vm,[ '+' ,function(){var b=vm.dStack.pop();vm.dStack.push(vm.dStack.pop()+b);}]);
vm.addWord.apply(vm,[ '-' ,function(){var b=vm.dStack.pop();vm.dStack.push(vm.dStack.pop()-b);}]);
vm.addWord.apply(vm,[ '*' ,function(){var b=vm.dStack.pop();vm.dStack.push(vm.dStack.pop()*b);}]);
vm.addWord.apply(vm,[ '/' ,function(){var b=vm.dStack.pop();vm.dStack.push(vm.dStack.pop()/b);}]);
vm.addWord.apply(vm,[ '1+' ,function(){var s=vm.dStack; s[s.length-1]++;}]);
vm.addWord.apply(vm,[ '1-' ,function(){var s=vm.dStack; s[s.length-1]--;}]);
vm.addWord.apply(vm,[ '2+' ,function(){var s=vm.dStack; s[s.length-1]+=2;}]);
vm.addWord.apply(vm,[ '2-' ,function(){var s=vm.dStack; s[s.length-1]-=2;}]);
vm.addWord.apply(vm,[ '2*' ,function(){var s=vm.dStack; s[s.length-1]*=2;}]);
vm.addWord.apply(vm,[ '2/' ,function(){var s=vm.dStack; s[s.length-1]/=2;}]);
vm.addWord.apply(vm,[ '2%' ,function(){var s=vm.dStack; s[s.length-1]%=2;}]);
vm.addWord.apply(vm,[ 'mod' ,function(){var s=vm.dStack, d=s.pop(); s[s.length-1]%=d;}]);
vm.addWord.apply(vm,['/mod' ,function(){
var s=vm.dStack, t=s.length-1,n=t-1,sn=s[n],st=s[t],r=s[n]=sn%st; s[t]=(sn-r)/st;}]);
vm.addWord.apply(vm,['and' ,function(){vm.dStack.push(vm.dStack.pop()&vm.dStack.pop());}]);
vm.addWord.apply(vm,['or' ,function(){vm.dStack.push(vm.dStack.pop()|vm.dStack.pop());}]);
vm.addWord.apply(vm,['xor' ,function(){vm.dStack.push(vm.dStack.pop()^vm.dStack.pop());}]);
vm.addWord.apply(vm,['hex' ,function(){vm.base=16;}]);
vm.addWord.apply(vm,['decimal',function(){vm.base=10;}]);
vm.addWord.apply(vm,['binary' ,function(){vm.base= 2;}]);
vm.addWord.apply(vm,['.r' ,function(){
var m=vm.dStack.pop(),n=""+vm.dStack.pop();vm.type.apply(vm,[" ".substr(0,m-n.length)+n]);}]);
//////////////////////////////////////////////////////////////////////////////////////////// v2
vm.addWord.apply(vm,[':' ,function(){
vm.newName=vm.nextToken.apply(vm,[]),vm.newXt=vm.cArea.length,vm.compiling=1;}]);
vm.addWord.apply(vm,['immediate',function(){vm.words[vm.words.length-1].immediate=1;}]);
vm.addWord.apply(vm,[';' ,function(){
vm.compileCode.apply(vm,["exit"]),vm.compiling=0;vm.addWord.apply(vm,[vm.newName,vm.newXt]);},'immediate']);
vm.addWord.apply(vm,['r@' ,function(){vm.dStack.push(vm.rStack[vm.rStack.length-1]);}]);
vm.addWord.apply(vm,['i' ,function(){vm.dStack.push(vm.rStack[vm.rStack.length-1].i);}]);
vm.addWord.apply(vm,['drop' ,function(){vm.dStack.pop();}]);
vm.addWord.apply(vm,['dup' ,function(){vm.dStack.push(vm.dStack[vm.dStack.length-1]);}]);
vm.addWord.apply(vm,['over' ,function(){vm.dStack.push(vm.dStack[vm.dStack.length-2]);}]);
vm.addWord.apply(vm,['emit' ,function(){vm.type.apply(vm,[String.fromCharCode(vm.dStack.pop())]);}]);
vm.addWord.apply(vm,['>r' ,function(){vm.rStack.push(vm.dStack.pop());}]);
vm.addWord.apply(vm,['?dup' ,function () {var s=vm.dStack, d=s[s.length-1]; if(d)s.push(d);}]);
vm.addWord.apply(vm,['0=' ,function () {var s=vm.dStack,m=s.length-1; s[m]=!s[m];}]);
vm.addWord.apply(vm,['for' ,function(){
if(vm.compiling){
vm.compileCode.apply(vm,[">r"]);
vm.dStack.push({name:"for",at:vm.cArea.length}); return;
}
var nTib=vm.nTib,i=vm.dStack.pop();vm.rStack.push({name:"for",nTib:nTib,i:i});
},'immediate']);
vm.addWord.apply(vm,['doNext',function(){
var i=vm.rStack.pop();
if(i){vm.rStack.push(i-1),vm.ip+=vm.cArea[vm.ip];}
else vm.ip++;}]);
vm.addWord.apply(vm,[ 'next',function(){ var o;
if(vm.compiling) o=vm.dStack.pop();
else o=vm.rStack[vm.rStack.length-1];
var t=typeof o;
if(t!=="object" || o.name!=="for"){
vm.panic.apply(vm,["missing for to match next"]); return;
}
if(vm.compiling){
vm.compileCode.apply(vm,["doNext",o.at-vm.cArea.length-1]); return;
}
if(--o.i>=0)vm.nTib=o.nTib;
else vm.rStack.pop();
},'immediate']);
vm.addWord.apply(vm,['branch' ,function(){vm.ip+=vm.cArea[vm.ip];}]);
vm.addWord.apply(vm,['zBranch' ,function(){
if(vm.dStack.pop())vm.ip++; else vm.ip+=vm.cArea[vm.ip];}]);
vm.addWord.apply(vm,['if',function(){
if(vm.compiling){
vm.compileCode.apply(vm,["zBranch",0]);
vm.dStack.push({name:"if",at:vm.cArea.length-1});return;
}
if(vm.dStack.pop())return; // 20141215 sam fixed
var e=vm.tib.substr(vm.nTib).indexOf("else");
var t=vm.tib.substr(vm.nTib).indexOf("then");
if(e>=0){
if(t && t<e)
vm.nTib+=t+4; // zbranch to then
else
vm.nTib+=e+4; // zbranch to else
} else if(t>=0)
vm.nTib+=t+4; // zbranch to then
else
vm.panic.apply(vm,["no else or then to match if"]);
},'immediate']);
vm.addWord.apply(vm,['else',function () {var t;
if(vm.compiling){
var o=vm.dStack.pop();t=typeof o;
if(t!=="object" || o.name!="if"){
vm.panic.apply(vm,["there is no if to match else"]);return;
}
var i=o.at; vm.compileCode.apply(vm,["branch",0]);
vm.dStack.push({name:"else",at:vm.cArea.length-1});
vm.cArea[i]=vm.cArea.length-i;return;
}
t=vm.tib.substr(vm.nTib).indexOf("then");
if(t>=0) vm.nTib+=t+4; // branch to then
else vm.panic.apply(vm,["there is no then to match else"]);
},'immediate']);
vm.addWord.apply(vm,['then',function () {
if(!vm.compiling) return;
var o=vm.dStack.pop(),t=typeof o, n=o.name;
if(t!=="object" || (n!="if" && n!="else" && n!="aft")){
vm.panic.apply(vm,["no if, else, aft to match then"]);return;
}
var i=o.at; vm.cArea[i]=vm.cArea.length-i;
},'immediate']);
vm.addWord.apply(vm,['aft',function () {var t;
if(vm.compiling){
var s=vm.dStack,o=s[s.length-1];t=typeof o;
if(t!=="object" || o.name!=="for"){
vm.panic.apply(vm,["no for to match aft"]);return;
}
var i=o.at;
vm.compileCode.apply(vm,["zBranch",0]);
vm.dStack.push({name:"aft",at:vm.cArea.length-1});
return;
}
t=vm.tib.substr(vm.nTib).indexOf("then");
if(t>=0) vm.nTib+=t+4; // branch to then
else vm.panic.apply(vm,["there is no then to match aft"]);
},'immediate']);
vm.addWord.apply(vm,['begin',function () {
if(vm.compiling){
vm.dStack.push({name:"begin",at:vm.cArea.length-1});
return;
}
vm.rStack.push({name:"begin",nTib:vm.nTib});
},'immediate']);
vm.addWord.apply(vm,['again',function () { var o;
if(vm.compiling)
o=vm.dStack.pop();
else
o=vm.rStack[vm.rStack.length-1];
var t=typeof o;
if(t!=="object" || o.name!=="begin"){
vm.panic.apply(vm,["no begin to match again"]);
return;
}
if(vm.compiling){
var i=o.at;
vm.compileCode.apply(vm,[ "branch", i-vm.cArea.length]);
return;
}
vm.nTib=o.nTib;
},'immediate']);
vm.addWord.apply(vm,['until',function () { var o;
if(vm.compiling)
o=vm.dStack.pop();
else
o=vm.rStack[vm.rStack.length-1];
var t=typeof o;
if(t!=="object" || o.name!=="begin"){
vm.panic.apply(vm,["no begin to match until"]);
return;
}
if(vm.compiling){
var i=o.at;
vm.compileCode.apply(vm,[ "zBranch", i-vm.cArea.length]);
return;
}
if(vm.dStack.pop()) vm.rStack.pop();
else vm.nTib=o.nTib;
},'immediate']);
vm.addWord.apply(vm,['while',function () { var s,o,t;
s=vm.dStack,o=s[s.length-1],t=typeof o;
if(t!=="object" || o.name!=="begin"){
vm.panic.apply(vm,["no begin to match while"]);return;
}
var i=o.at; vm.compileCode.apply(vm,["zBranch",0]);
vm.dStack.push({name:"while",at:vm.cArea.length-1});
},'immediate']);
vm.addWord.apply(vm,['repeat',function () {
var o=vm.dStack.pop(),t=typeof o;
if(t!=="object" || o.name!=="while"){
vm.panic.apply(vm,["no while to match repeat"]);return;
}
var i=o.at; o=vm.dStack.pop(),t=typeof o;
if(t!=="object" || o.name!=="begin"){
vm.panic.apply(vm,["no begin to match repeat"]);return;
}
vm.compileCode.apply(vm,["branch",o.at-vm.cArea.length]);
vm.cArea[i]=vm.cArea.length-i;
},'immediate']);
//////////////////////////////////////////////////////////////////////////////////////////// v3
vm.msTime=[];
var doWakeup=function(){
var time=new Date().getTime()-vm.startTime, T=vm.msTime, n=T.length, i, t;
for(var i=n-1; i>=0; i--) { t=T[i];
if(time>=t.wakeup){ T.splice(i,1); break; }
}
var msg=t.tib.substr(t.nTib,3);
vm.tib=t.tib,vm.nTib=t.nTib,vm.tob=t.tob,vm.uob=t.uob,
vm.dStack=t.dStack,vm.rStack=t.rStack;
// msgJef.innerHTML+=new Date().getTime()-vm.startTime+' '+msg+' ';
vm.resumeExec.apply(vm,[vm.waiting===1?0:vm.waiting]);
}
vm.startTime=new Date().getTime();
vm.addWord.apply(vm,['ms',function (n) {
var time=new Date().getTime()-vm.startTime;
var m= n===undefined ? vm.dStack.pop() : n;
var t={tib:vm.tib,nTib:vm.nTib,tob:vm.tob,uob:vm.uob,
dStack:vm.dStack,rStack:vm.rStack.slice(0), // clone a copy of rStack
wakeup:time+m
};
vm.waiting=1; t.timeout=setTimeout(doWakeup,m);
vm.msTime.push(t);
}]);
};
if(typeof module!='undefined')
module.exports=ext;
else
ext(vm);