@venezia is on PowPing!

PowPing is a place where you can earn Bitcoin simply by socializing, for FREE.
Never tried Bitcoin? It's OK! Just come, socialize, and earn Bitcoin.
Check out venezia's activities
Total Economy: 0.44 USD

PEG.js grammar for MiniForth

/*
Parsing the MiniForth code into the ASM string of bitcoin opcodes.
Copyright (C) 2020  Venezia(390@moneybutton.com)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

{
	function alias(op) {
    	op = op.replace(/^WORD:/g, '');
    	let full = {
        	tas: 'toaltstack',
            fas: 'fromaltstack',
            '+': 'add',
            '-': 'sub',
            '*': 'mul',
            '/': 'div'
        }[op];
        if(full) {
        	return 'OP_' + full.toUpperCase()
        }
    }

	function toASM(dict) {
    	let main = dict.get("WORD:main");
        main.reverse();
        let asm = '';
        // console.log(main)
        while(main.length > 0) {
        	let h = main.pop();
            let s;
            if(Number.isInteger(h)) {
            	s = minimal_encode(h);
            } else if(alias(h)){
            	s = alias(h);
            } else if(h.startsWith('WORD:')) {
            	s = h.replace(/^WORD:/g, 'OP_').toUpperCase();
            } else {
            	s = h;
            }
            asm = asm + ' ' + s;
        }
        console.log(asm);
        return asm.trimStart();
    }
	function unroll(dict) {
    	let main = dict.get("WORD:main");
        if(!main) {
        	error("Must have a 'main' word defined")
        }
       	main = main.reverse();
        var m1 = [];
        while(main.length > 0) {
        	let h = main.pop();
            if(h == 'WORD:do') {
            	let start = m1.pop();
                let stop = m1.pop();
				if(!(Number.isInteger(start)&&Number.isInteger(stop))) {
                	error('The start and stop params of loop must be known at compile time')
                }
                var loop = [];
                var step = 1;
                while(main.length > 0) {
                	let h = main.pop();
                    if(h == 'WORD:loop') {
                        break;
                    } else {
                    	loop.push(h);
                    }
                }
                for(let i = start; i < stop; i++) {
                	let loop1 = JSON.parse(JSON.stringify(loop));
                	for(let j = 0; j < loop.length; j++) {
                        if(loop[j] == 'WORD:i') {
                        	loop1[j] = i;
                        }
                    }
                	m1 = m1.concat(loop1);
                }
            } else {
            	m1.push(h);
            }
        }
        
        dict.set('WORD:main', m1);
        
        return dict
    }
    
	function replace(dict) {
    	var modified = false;
       
    	// do replace
        dict.forEach((v, k) => {
        	for(var i = 0; i < v.length; i++) {
            	let a = dict.get(v[i]);
                if(a) {
                	v[i] = a;
             		v = v.flat();
                    dict.set(k, v);
                    modified = true;
                }
            }
        })
	
    	if(!modified) {
        	return dict;
        } else {
        	return replace(dict);
        }
    }

	function stringToHex(str) {
      var result = '';
      for (var i=0; i<str.length; i++) {
        result += str.charCodeAt(i).toString(16);
      }
      return result;
    }

	function minimal_encode(v) {
    	if(v >= 0 && v <=16) {
        	return 'OP_' + v;
        }
    	let signBit = v > 0 ? 0 : 0x80;
        v = Math.abs(v);
        var result = '';
        for (var i = 0; i < 1000; i ++) {
           let e;
           if(v < 128) {
               e = v|signBit
               v = 'end';
           } else {
               e = (v % 256);
               v = Math.floor(v/256);
           }
           result += e.toString(16).padStart(2, '0');
           if(v == 'end') {
           	return result;
           }
        }
   	 	error('The number is too large: ' + v);
    }
    
    function num2bin(v, bits, BE, unsigned) { 
        if(bits % 8) {
            error('The bits size should be multiples of 8')
        }
        let max = 2**bits - 1;
        if(v > max || v < -max) {
            error('Impossible encoding for number ' + v)
        }
        let signBit = v > 0 ? 0 : 0x80;
        v = Math.abs(v);
        let bytes = bits / 8;
        var result = '';
        for (var i = 0; i < bytes; i ++) {
           let e;
           if(!unsigned && i == bytes-1) {
               e = (v % 128)|signBit
           } else {
               e = (v % 256);
               v = Math.floor(v/256);
           }
           if(!BE) {
           	result += e.toString(16).padStart(2, '0');
           } else {
           	result = e.toString(16).padStart(2, '0') + result;
           }
        }
        return result;
    }
}

start
	= ws head:block tail:blockTail* ws {
    	tail.unshift(head);
        let r = new Map();
        for (var i = 0; i < tail.length; i++) {
        	if(tail[i]) {
        		let k = tail[i].name;
            	r.set(k, tail[i].body);
            }
        }
        // console.log(r)
        r = replace(r)
        // console.log(r)
        r = unroll(r)
        // console.log(r)
        r = toASM(r)
        return r;
      }

word "word"
	= chars:nameChar+ { return 'WORD:' + chars.join('') }
    
nameChar "nameChar"
    = [a-z0-9*\+\-\/]i

block
	= definition
    / comment
    
blockTail
	= ws b:block { return b }

elements
	= tail:elementTail* {
    	var r = [];
    	for(var i = 0; i < tail.length; i++) {
        	if(tail[i] != null) {
            	r.push(tail[i])
            }
        }
        return r;
      }
    
definition
	= ':' ws w:word e:elements ws ';' { return { name: w, body: e } }
    
LineTerminator
 	= [\n\r\u2028\u2029]

element
	= bytes
    / number
    / string
    / word
    / comment
    
elementTail
	= ws e:element { return e; }

string "string"
  = quotation_mark chars:char* quotation_mark { return stringToHex(chars.join("")); }

char
  = unescaped
  / escape
    sequence:(
        '"'
      / "\\"
      / "/"
      / "b" { return "\b"; }
      / "f" { return "\f"; }
      / "n" { return "\n"; }
      / "r" { return "\r"; }
      / "t" { return "\t"; }
      / "u" digits:$(HEXDIG HEXDIG HEXDIG HEXDIG) {
          return String.fromCharCode(parseInt(digits, 16));
        }
    )
    { return sequence; }

escape
  = "\\"

quotation_mark
  = '"'

unescaped
  = [^\0-\x1F\x22\x5C]
  
HEXDIG = [0-9a-f]i

comment
	= bracketsComment
    / singleLineComment

singleLineComment
	= '\\' (!LineTerminator .)*  { return null }

bracketsComment
	= '(' (!')' .)* ')' { return null }

bytes
    = '<<' ws head:segment tail:segmentTail* '>>' {
    	tail.unshift(head);
        return tail.reduce(
        	(acc, x) => {
        		return acc + num2bin(x.value, x.size, true, true)
        	}, ''	
        );
      }

segmentTail
    = ws ',' ws seg:segment { return seg; }

segment
    = v:number size:size ?
      { return {value: v, size: size || 8}; }

hexDigit
  = [0-9a-fA-F]

number
	= posNumber
    / negNumber

posNumber
    = hexNumber
    / '0' { return 0; }
    / head:[1-9] tail:[0-9]* {
    	var x;
    	x = parseInt(head + tail.join(''));
        return x;
      }
      
negNumber
	= '-' n:posNumber { return -n }
    
hexNumber
    = '0x' head:hexDigit tail:hexDigit* { return parseInt(head + tail.join(''), 16); }

size
    = ':' num:number { return num; }

ws = [ \t\n]*

Try it on [https://pegjs.org/online](https://pegjs.org/online). We got an interactive compiler for free ( I'm too lazy to write oneπŸ˜…).

 Not all features of MiniForth are supported( I want to keep it concise). But you can start with this:

: power ( a b -- a^b )
    1 swap max 0 do
        dup if
            tas over * fas 1sub
        endif
    loop drop nip
;

\ helpers
: max 10 ;

: main 4 3 power ;
powered by powpress
link Tip
Share
tx
translate
joe tipped:
0.16 USD
1 year ago
maw tipped:
0.02 USD
1 year ago
unwriter tipped:
0.1 USD
1 year ago
kapil tipped:
0.07 USD
1 year ago
musiq tipped:
0.05 USD
1 year ago
adonsats tipped:
0.04 USD
1 year ago
xhliu tipped:
0.02 USD
1 year ago
Fixed a paring error about the words start with numbers, like '2dup'. The latest version can be found here: https://github.com/Ljzn/mini_forth/blob/master/j/mini.pegjs
πŸ˜‚πŸ˜‚πŸ˜‚ really. miniforth can go anywhere Pomp it Move it ....... Do it ! 😱