UNPKG

forth

Version:

Forth programming environment

291 lines (258 loc) 10.1 kB
'use strict'; // stack-only words; using only .dpop .dpush // TODO add manipulations, double, float, return stack words var def = require('./def'), Long = require('long'); function stack (cxt) { cxt.DS = []; cxt.RS = []; cxt.dtop = function () { return this.DS[this.DS.length - 1]; }, cxt.dnext = function () { return this.DS[this.DS.length - 2]; }, cxt.dpop = function () { return this.DS.pop(); }, cxt.dpush = function (e) { this.DS.push(e); }; cxt.rtop = function () { return this.RS[this.RS.length - 1]; }, cxt.rnext = function () { return this.RS[this.RS.length - 2]; }, cxt.rpick2 = function () { return this.RS[this.RS.length - 3]; }, cxt.rpop = function () { return this.RS.pop(); }, cxt.rpush = function (e) { this.RS.push(e); }; cxt.plusloop = function () { var n1 = this.rpop(); var limit = this.rtop(); var delta = n1 - limit; var n = this.dpop(); var n2 = (n1 + n) | 0; this.rpush(n2); var res = ((delta ^ (delta + n)) & (delta ^ n)) < 0; return res ? -1 : 0; }; // stack primitives var core = { drop: function () { this.dpop(); }, dup: function () { var t = this.dtop(); this.dpush(t); }, over: function () { var n = this.dnext(); this.dpush(n); }, swap: function () { var t = this.dpop(); var n = this.dpop(); this.dpush(t); this.dpush(n); }, '>r': function () { this.rpush(this.dpop()); }, 'r>': function () { this.dpush(this.rpop()); }, 'r@': function () { var rtop = this.rtop(); this.dpush(rtop); }, // 6.1.0100 */ ( n1 n2 n3 -- n4 ) “star-slash” (CORE) // Multiply n1 by n2 producing the intermediate double-cell result d. // Divide d by n3 giving the single-cell quotient n4. An ambiguous // condition exists if n3 is zero or if the quotient n4 lies outside // the range of a signed number. If d and n3 differ in sign, the // implementation-defined result returned will be the same as that // returned by either the phrase >R M* R> FM/MOD SWAP DROP or // the phrase >R M* R> SM/REM SWAP DROP. '*/': function () { var t = this.dpop() | 0; var n = this.dpop() | 0; var m = this.dpop() | 0; var ddd = Long.fromInt(m); ddd = ddd.mul(Long.fromInt(n)); ddd = ddd.div(Long.fromInt(t)); ddd = ddd.toInt(); this.dpush(ddd | 0); }, // 6.1.0110 */MOD “star-slash-mod” (CORE) // ( n1 n2 n3 -- n4 n5 ) // Multiply n1 by n2 producing the intermediate double-cell result d. // Divide d by n3 producing the single-cell remainder n4 and the // single-cell quotient n5. An ambiguous condition exists if n3 is zero, // or if the quotient n5 lies outside the range of a single-cell signed // integer. If d and n3 differ in sign, the implementation-defined // result returned will be the same as that returned by either // the phrase >R M* R> FM/MOD or the phrase >R M* R> SM/REM. '*/mod': function () { var t = this.dpop() | 0; var n = this.dpop() | 0; var m = this.dpop() | 0; var ddd = Long.fromInt(m); ddd = ddd.mul(Long.fromInt(n)); var r0 = ddd.div(Long.fromInt(t)); r0 = r0.toInt(); var r1 = ddd.mod(Long.fromInt(t)); r1 = r1.toInt(); this.dpush(r1 | 0); this.dpush(r0 | 0); }, '/mod': function () { var t = this.dpop() | 0; var n = this.dpop() | 0; this.dpush((n % t) | 0); this.dpush((n / t) | 0); }, nip: function () { 'swap'(); 'drop'(); }, // rot ( x1 x2 x3 -- x2 x3 x1 ) rot: function () { var x3 = this.dpop(); var x2 = this.dpop(); var x1 = this.dpop(); this.dpush(x2); this.dpush(x3); this.dpush(x1); }, // 2drop ( w1 w2 -- ) '2drop': function () { 'drop'(); 'drop'(); }, // 2dup ( w1 w2 -- w1 w2 w1 w2 ) '2dup': function () { 'over'(); 'over'(); }, // 2over ( w1 w2 w3 w4 -- w1 w2 w3 w4 w1 w2 ) '2over': function () { '>r'(); '>r'(); 'over'(); 'over'(); 'r>'(); 'rot'(); 'rot'(); 'r>'(); 'rot'(); 'rot'(); }, // 2swap ( w1 w2 w3 w4 -- w3 w4 w1 w2 ) '2swap': function () { 'rot'(); '>r'(); 'rot'(); 'r>'(); }, 'depth': function () { this.dpush(this.DS.length); }, // 6.1.1810 M* “m-star” (CORE) // ( n1 n2 -- d ) // d is the signed product of n1 times n2 . 'm*': function () { var t = this.dpop() | 0; var n = this.dpop() | 0; var ddd = Long.fromInt(n); ddd = ddd.mul(Long.fromInt(t)); this.dpush(ddd.low); this.dpush(ddd.high); }, // 6.1.2360 UM* “u-m-star” (CORE) // ( u1 u2 -- ud ) // Multiply u 1 by u 2, giving the unsigned double-cell product ud. // All values and arithmetic are unsigned. 'um*': function () { var t = this.dpop() >>> 0; var n = this.dpop() >>> 0; var nn = Long.fromValue(n, true); var tt = Long.fromValue(t, true); var ddd = nn.mul(tt); this.dpush(ddd.low); this.dpush(ddd.high); }, // 6.1.2214 SM/REM “s-m-slash-rem” (CORE) // ( d1 n1 -- n2 n3 ) // Divide d1 by n1, giving the symmetric quotient n3 and the remainder n2. // Input and output stack arguments are signed. An ambiguous condition // exists if n1 is zero or if the quotient lies outside the range of a // single-cell signed integer. 'sm/rem': function () { var t = this.dpop() | 0; var n = this.dpop() | 0; var s = this.dpop() | 0; var ddd = new Long(s, n); var ttt = Long.fromInt(t); var r0 = ddd.div(ttt); r0 = r0.toInt(); var r1 = ddd.mod(ttt); r1 = r1.toInt(); this.dpush(r1); this.dpush(r0); }, // 6.1.2370 UM/MOD “u-m-slash-mod” (CORE) // ( ud u1 -- u2 u3 ) // Divide ud by u1, giving the quotient u3 and the remainder u2. // All values and arithmetic are unsigned. An ambiguous condition exists // if u1 is zero or if the qukrusaotient lies outside the range of // a single-cell unsigned integer. 'um/mod': function () { var t = this.dpop() >>> 0; var n = this.dpop() >>> 0; var s = this.dpop() >>> 0; var ddd = new Long(s, n, true); var ttt = Long.fromInt(t); var r0 = ddd.div(ttt); r0 = r0.toInt(); var r1 = ddd.mod(ttt); r1 = r1.toInt(); this.dpush(r1); this.dpush(r0); }, // fm/mod // 6.1.1561 FM/MOD “f-m-slash-mod” (CORE) // ( d1 n1 -- n2 n3 ) // Divide d1 by n1, giving the floored quotient n3 and the remainder n2. // Input and output stack arguments are signed. An ambiguous condition // exists if n is zero or if the quotient lies outside 1the range of a // single-cell signed integer. 'fm/mod': function () { var t = this.dpop() | 0; var n = this.dpop() | 0; var s = this.dpop() | 0; var ddd = new Long(s, n); var ttt = Long.fromInt(t); var r0 = ddd.div(ttt); r0 = r0.toInt(); var r1 = ddd.mod(ttt); r1 = r1.toInt(); this.dpush(r1); this.dpush(r0); }, // '?dup': null, // 6.1.2170 S>D “s-to-d” (CORE) // ( n -- d ) // Convert the number n to the double-cell number d with the same numerical value. 's>d': function () { var t = this.dpop() | 0; var ttt = Long.fromInt(t); this.dpush(ttt.low); this.dpush(ttt.high); } // sign }; Object.keys(core).forEach(function (key) { def(key, core[key], cxt); }); // x1 -- x2 ' 0< # ((t < 0) ? -1 : 0) ; 0<> # ((t !== 0) ? -1 : 0) ; 0> # ((t > 0) ? -1 : 0) ; 0= # ((t === 0) ? -1 : 0) ; 1+ # (t + 1) | 0 ; 1- # (t - 1) | 0 ; 2* # (t << 1) ; 2/ # (t >> 1) ; abs # Math.abs(t) | 0 ; cell+ # (t + 4) | 0 ; cells # (t * 4) | 0 ; char+ # (t + 1) | 0 ; chars # t ; invert # (t ^ -1) ; negate # ((t ^ -1) + 1) | 0 ' .split(';') .map(function (e) { return e.split('#'); }) .forEach(function (e) { var fn; eval('fn = function () { var t = this.dpop() | 0; this.dpush(' + e[1] + '); };'); def(e[0].trim(), fn, cxt); }); // x1 x2 -- x3 ' + # (n + t) | 0 ; - # (n - t) | 0 ; * # (n * t) | 0 ; / # (n / t) | 0 ; <> # ((t !== n) ? -1 : 0) ; and # (n & t) ; = # ((n === t) ? -1 : 0) ; > # ((n > t) ? -1 : 0) ; < # ((n < t) ? -1 : 0) ; lshift # (n << t) ; max # (Math.max(t, n)) ; min # (Math.min(t, n)) ; mod # (n % t) | 0; or # (n | t) ; rshift # (n >>> t) ; u< # (((n >>> 0) < (t >>> 0)) ? -1 : 0) ; xor # (n ^ t) ; u> # (((n >>> 0) > (t >>> 0)) ? -1 : 0) ' .split(';') .map(function (e) { return e.split('#'); }) .forEach(function (e) { var fn; eval('fn = function () { var t = this.dpop() | 0; var n = this.dpop() | 0; this.dpush(' + e[1] + '); };'); def(e[0].trim(), fn, cxt); }); } module.exports = stack;