UNPKG

forth

Version:

Forth programming environment

387 lines (313 loc) 9.77 kB
'use strict'; var def = require('./def'), Long = require('long'), colors = require('colors/safe'), expect = require('chai').expect; function core (cxt) { cxt.setBase(10); var stash = {}; def('\\', function () { this.io.parse('\\\n'); }, cxt, { immediate: true }); def('(', function () { this.io.parse('\\)'); }, cxt, { immediate: true }); def('.(', function () { var str = this.io.parse('\\)'); this.log(str); }, cxt, { immediate: true }); // 6.1.0990 CR “c-r” (CORE) // ( -- ) // Cause subsequent output to appear at the beginning of the next line. def('cr', function () { cxt.log('\n'); }, cxt); // 6.1.2220 SPACE def('space', function () { cxt.log(' '); }, cxt); // 6.1.2230 SPACES def('spaces', function () { cxt.log(' '.repeat(this.dpop())); }, cxt); def('bl', function () { this.dpush(32); }, cxt); def('.', function () { cxt.log(' ' + this.dpop()); }, cxt); def('.s', function () { cxt.DS.forEach(function (e) { cxt.log(' ' + e); }); }, cxt); // 15.6.1.1280 DUMP (TOOLS) // ( addr u -- ) // Display the contents of u consecutive addresses starting at addr. // The format of the display is implementation dependent. def('dump', function () { var ilen = this.dpop(); var addr = this.dpop(); var i; for (i = 0; i < ilen; i++) { cxt.log(' ' + this.MEMi8[addr + i]); } }, cxt); // 6.1.0895 CHAR “char” (CORE) // ( “<spaces>name” -- char ) // Skip leading space delimiters. Parse name delimited by a space. // Put the value of its first character onto the stack. def('char', function () { var w = this.io.word('\\s'); this.dpush(w.charCodeAt(0)); }, cxt); // 6.1.0630 ?DUP “question-dupe” (CORE) // ( x -- 0 | x x ) // Duplicate x if it is non-zero. def('?dup', function () { var t = this.dpop(); if (t !== 0) { this.dpush(t); } this.dpush(t); }, cxt); def('hex', function () { this.setBase(16); }, cxt); def('decimal', function () { this.setBase(10); }, cxt); // 6.1.0490 <# “less-number-sign” // Initialize the pictured numeric output conversion process. def('<#', function () { // ( -- ) this.picturedNumericOutputAddress = this.here + 128; this.picturedNumericOutputLength = 0; }, cxt); // 6.1.0040 #> def('#>', function () { this.dpop(); this.dpop(); this.dpush(this.picturedNumericOutputAddress + 1); this.dpush(this.picturedNumericOutputLength); }, cxt); // 6.1.1670 HOLD def('hold', function () { var char = this.dpop() >>> 0; this.MEMi8[this.picturedNumericOutputAddress] = char; this.picturedNumericOutputAddress -= 1; this.picturedNumericOutputLength += 1; }, cxt); // 6.1.2210 SIGN def('sign', function () { // ( n -- ) var t = this.dpop() | 0; if (t < 0) { this.MEMi8[this.picturedNumericOutputAddress] = 45; this.picturedNumericOutputAddress -= 1; this.picturedNumericOutputLength += 1; } }, cxt); // 6.1.0030 # “number-sign” // Divide ud1 by the number in BASE giving the quotient ud2 and the remainder n. // (n is the least-significant digit of ud1.) Convert n to external form // and add the resulting character to the beginning of the pictured numeric // output string. def('#', function () { // ( ud1 -- ud2 ) var t = this.dpop() >>> 0; var n = this.dpop() >>> 0; var dd = new Long(n, t, true); var base = this.getBase(); var r1 = dd.mod(base); var char = r1.toInt(); if (char < 10) { char += 48; } else { char += 55; } this.MEMi8[this.picturedNumericOutputAddress] = char; this.picturedNumericOutputAddress -= 1; this.picturedNumericOutputLength += 1; dd = dd.div(base); this.dpush(dd.low); this.dpush(dd.high); }, cxt); // 6.1.0050 #S def('#s', function () { var t = this.dpop() >>> 0; var n = this.dpop() >>> 0; var dd = new Long(n, t, true); var base = this.getBase(); do { var r1 = dd.mod(base); var char = r1.toInt(); if (char < 10) { char += 48; } else { char += 55; } this.MEMi8[this.picturedNumericOutputAddress] = char; this.picturedNumericOutputAddress -= 1; this.picturedNumericOutputLength += 1; dd = dd.div(base, true); } while (!dd.eq(0)); this.dpush(dd.low); this.dpush(dd.high); }, cxt); // 6.1.0560 >IN def('>in', function () { this.dpush(this.toinAddr()); }, cxt); // 6.1.0570 >NUMBER def('>number', function () { }, cxt); // 6.1.0670 ABORT def('abort', function () { }, cxt); // 6.1.0680 ABORT" def('abort"', function () { }, cxt); // 6.1.0695 ACCEPT def('accept', function () { }, cxt); // 6.1.0750 BASE def('base', function () { this.dpush(this.baseAddr()); }, cxt); // 6.1.0980 COUNT def('count', function () { var addr = this.dpop(); var len = this.MEMi8[addr]; this.dpush(addr + 1); this.dpush(len); }, cxt); // 6.1.1320 EMIT ( x -- ) def('emit', function () { this.log(String.fromCharCode(this.dpop() >>> 0)); }, cxt); // 6.1.1360 EVALUATE ( i*x c-addr u -- j*x ) def('evaluate', function () { var str = ''; var len = this.dpop(); var addr = this.dpop(); // Save the current input source specification var buf = this.io.buf; var ptr = this.io.ptr; var last = this.io.last; // Make the string described by c-addr and u both the input // source and input buffer, set >IN to zero var i; for (i = 0; i < len; i++) { str += String.fromCharCode(this.MEMi8[addr + i]); } // , and interpret this.interpret(str); // When the parse area is empty, restore the prior input // source specification. this.io.buf = buf; this.io.ptr = ptr; this.io.last = last; }, cxt); // 6.1.1380 EXIT def('exit', function () { }, cxt); // 6.1.1540 FILL def('fill', function () { // ( c-addr u char -- ) var char = this.dpop() >>> 0; var len = this.dpop() >>> 0; var addr = this.dpop() >>> 0; var i; for (i = 0; i < len; i++) { this.MEMi8[addr + i] = char; } }, cxt); // 6.1.1550 FIND def('find', function () { 'count'(); var len = this.dpop(); var addr = this.dpop(); var str = ''; var i; for (i = 0; i < len; i++) { str += String.fromCharCode(this.MEMi8[addr + i]); } str = str.toLowerCase(); var wordLink = this.O[str]; if (wordLink) { this.dpush(wordLink.index); if (wordLink.immediate) { this.dpush(1); } else { this.dpush(-1); } } else { this.dpush(addr); this.dpush(0); } }, cxt); // 6.1.1750 KEY def('key', function () { this.dpush(42); // TODO }, cxt); // 6.1.1900 MOVE def('move', function () { // ( addr1 addr2 u -- ) var len = this.dpop() >>> 0; var dst = this.dpop() >>> 0; var src = this.dpop() >>> 0; var i; if (src > dst) { for (i = 0; i < len; i++) { this.MEMi8[dst + i] = this.MEMi8[src + i]; } } else { for (i = len - 1; i >= 0; i--) { this.MEMi8[dst + i] = this.MEMi8[src + i]; } } }, cxt); // 6.1.2050 QUIT def('quit', function () { }, cxt); // 6.1.2216 SOURCE def('source', function () { this.dpush(this.here); this.dpush(this.io.last - this.io.ptr); }, cxt); // 6.1.2310 TYPE def('type', function () { // ( c-addr u -- ) var len = this.dpop(); var addr = this.dpop(); var i, char; for (i = 0; i < len; i++) { char = this.MEMi8[addr + i]; cxt.log(String.fromCharCode(char)); } }, cxt); // 6.1.2320 U. def('u.', function () { cxt.log(' ' + (this.dpop() >>> 0)); }, cxt); // 6.1.2450 WORD def('word', function () { var char = this.dpop(); var w = this.io.word(String.fromCharCode(char)); var i, ilen, here; here = this.here; for (i = 0, ilen = w.length; i < ilen; i++) { this.MEMi8[here + i] = w.charCodeAt(i); } this.MEMi8[here + ilen] = 0; this.here += (ilen + 1); this.dpush(here); }, cxt); // tester def('testing', function () { this.io.parse('\\\n'); }, cxt); def('t{', function () { this.DS = []; }, cxt); def('->', function () { stash.DS = this.DS; this.DS = []; }, cxt); def('}t', function () { var lines, l0, l1; try { expect(stash.DS).deep.equal(this.DS); } catch (err) { lines = this.io.buf.toString().slice(0, this.io.ptr).split('\n'); cxt.log(colors.red('Error:') + ' in line: ' + (lines.length - 1) + ' '); l0 = lines.pop(); l1 = lines.pop(); cxt.log(l1 + ' ' + l0 + ' '); cxt.log(err.message); } cxt.log('.\n'); }, cxt); } module.exports = core;