diff --git a/FORTH.py b/FORTH.py new file mode 100644 index 0000000..a136738 --- /dev/null +++ b/FORTH.py @@ -0,0 +1,451 @@ +#PYTHON name +# +# forth.py +# +import sys, math +import hpprime +import graphic +import urandom + +if sys.version > '3' : raw_input = input # for both 2.7 and 3.0+ + +ds = [] # The data stack +cStack = [] # The control struct stack +heap = [0]*2048 # The data heap +heapNext = 0 # Next avail slot in heap +words = [] # The input stream of tokens +colour = 0x0000ff +background = 0xffffff +initCode = """: cr 10 emit ; : abs dup 0 < if 0 swap - then ; : constant create , does> @ ; : variable create 1 allot ; : +! DUP @ ROT + SWAP ! ; +: 2DUP OVER OVER ; : 2DROP DROP DROP ; : NIP SWAP DROP ; : 2NIP 2SWAP 2DROP ; : TUCK SWAP OVER ; + : BL 32 ; : CR 10 EMIT ; : SPACE BL EMIT ; : NEGATE 0 SWAP - ; : DNEGATE 0. 2SWAP D- ; : CELLS CELL * ; : TRUE -1 ; : FALSE 0 ; + : 0= 0 = ; : 0< 0 < ; : 0> 0 > ; : <= > 0= ; : >= < 0= ; : 0<= 0 <= ; : 0>= 0 >= ; : 1- 1 - ; +: 2+ 2 + ; : 2- 2 - ; : 2/ 2 / ; : 2* 2 * ; : MIN 2DUP < IF DROP ELSE NIP THEN ; : MAX 2DUP > IF DROP ELSE NIP THEN ; : D0= OR 0= ; 1 constant CELL +""" + +def main() : + global words, initCode + if len(sys.argv) > 1 : + initCode = open(sys.argv[1]).read() # load start file + hpprime.eval("PRINT") # clear terminal screen + print("Prime FORTH 1.0") + while True : + pcode = compile() # compile/run from user + if pcode == None : print(""); return + execute(pcode) + +#============================== Lexical Parsing + +def getWord (prompt="... ") : + global words, initCode + while not words : + try : + if initCode : lin = initCode; initCode="" + else : + lin = raw_input(prompt)+" " + print(lin) + except : return None + tokenizeWords(lin) + + word = words[0] + if word == "bye" : return None + words = words[1:] + return word + +def tokenizeWords(s) : + global words # clip comments, split to list of words + words += s.lower().split() # Use "#" for comment to end of line + +#================================= Runtime operation + +def execute (code) : + p = 0 + while p < len(code) : + func = code[p] + p += 1 + newP = func(code,p) + if newP != None : p = newP + +def rAdd (cod,p) : b=ds.pop(); a=ds.pop(); ds.append(a+b) +def rFloor (cod,p) : a=ds.pop(); ds.append(math.floor(a)) +def rMod (cod,p) : b=ds.pop(); a=ds.pop(); ds.append(a%b) +def rOneplus (cod,p) : a=ds.pop(); ds.append(a+1) +def rOr (cod,p) : b=ds.pop(); a=ds.pop(); ds.append(a|b) +def rAnd (cod,p) : b=ds.pop(); a=ds.pop(); ds.append(a&b) +def rMul (cod,p) : b=ds.pop(); a=ds.pop(); ds.append(a*b) +def rSub (cod,p) : b=ds.pop(); a=ds.pop(); ds.append(a-b) +def rDiv (cod,p) : b=ds.pop(); a=ds.pop(); ds.append(a/b) +def rEq (cod,p) : b=ds.pop(); a=ds.pop(); ds.append(int(a==b)) +def rPixon (cod,p) : y=ds.pop(); x=ds.pop(); hpprime.pixon(0,x,y,colour) +def rPixon2 (cod,p) : + y=ds.pop(); + if y > 0 : y = y * 2 + 1 + x=ds.pop() + if x > 0 : x = x * 2 + 1 + hpprime.pixon(0,x,y,colour); + hpprime.pixon(0,x+1,y,colour); + hpprime.pixon(0,x,y+1,colour); + hpprime.pixon(0,x+1,y+1,colour) + +def rPixon4 (cod,p) : + y=ds.pop(); + if y > 0 : y = y * 4 + 1 + x=ds.pop() + if x > 0 : x = x * 4 + 1 + hpprime.fillrect(0,x,y,4, 4, colour, colour); + +def rGetpix (cod,p) : y=ds.pop(); x=ds.pop(); ds.append(hpprime.eval("getpix_p({},{})".format(x,y))) + +def rGetpix2 (cod,p) : + y=ds.pop(); + if y > 0 : y = y * 2 + 1 + x=ds.pop() + if x > 0 : x = x * 2 + 1 + ds.append(hpprime.eval("getpix_p({},{})".format(x,y))) + +def rGetpix4 (cod,p) : + y=ds.pop(); + if y > 0 : y = y * 4 + 1 + x=ds.pop() + if x > 0 : x = x * 4 + 1 + ds.append(hpprime.eval("getpix_p({},{})".format(x,y))) + +def rLastkey (cod,p) : ds.append(int(hpprime.eval("getkey"))) + +def rKey (cod,p) : + while 1: + k = hpprime.eval("getkey") + if k != -1 : break + + ds.append(int(k)) +def rTicks (cod,p) : ds.append(int(hpprime.eval("ticks"))) +def rLine (cod,p) : y2=ds.pop(); x2=ds.pop(); y1=ds.pop(); x1=ds.pop(); hpprime.line(0,x1,y1,x2, y2, colour) +def rRect (cod,p) : h=ds.pop(); w=ds.pop(); y=ds.pop(); x=ds.pop(); hpprime.rect(0,x,y,w, h, colour) +def rFillrect (cod,p) : h=ds.pop(); w=ds.pop(); y=ds.pop(); x=ds.pop(); hpprime.fillrect(0,x,y,w, h, colour, colour) +def rCircle (cod,p) : rad=ds.pop(); y=ds.pop(); x=ds.pop(); hpprime.circle(0,x,y,rad, colour) +def rCol (cod,p) : global colour ; colour = ds.pop() +def rGetcol (cod,p) : global colour; ds.append(colour) +def rBg (cod,p) : global background; background = ds.pop() +def rShow (cod,p) : graphic.show() +def rList (cod,p) : + fname = getWord()+".fth"; + try: + f = open(fname, "r") + print(f.read(), end=''); f.close() + except: + print(fname+": does not exist") + +def rLoad (cod,p) : + global initCode + fname = getWord()+".fth" + try: + f = open(fname, "r") + initCode = f.read() + f.close() + except: + print(fname+": does not exist") + +def rSleep (cod,p) : a=ds.pop()/1000; hpprime.eval("wait({})".format(a)) +def rNeq (cod,p) : b=ds.pop(); a=ds.pop(); ds.append(int(a!=b)) +def rGt (cod,p) : b=ds.pop(); a=ds.pop(); ds.append(int(a>b)) +def rLt (cod,p) : b=ds.pop(); a=ds.pop(); ds.append(int(a' : rGt, '<': rLt, + ',' : rComa,'@' : rAt, '!' : rBang,'allot': rAllot, + + 'create': rCreate, 'does>': rDoes, +'or' : rOr, +'and' : rAnd, +'emit' : rEmit, +'<>' : rNeq, +'here' : rHere, +'rot' : rRot, +'pixon' : rPixon, +'pixon2' : rPixon2, +'pixon4' : rPixon4, +'getpix' : rGetpix, +'getpix2' : rGetpix2, +'getpix4' : rGetpix4, +'key' : rKey, +'lastkey' : rLastkey, +'ticks' : rTicks, +'line' : rLine, +'rect' : rRect, +'fillrect' : rFillrect, +'circle' : rCircle, +'sleep' : rSleep, +'cls' : rCls, +'col' : rCol, +'getcol' : rGetcol, +'bg' : rBg, +'show' : rShow, +'list' : rList, +'load' : rLoad, +'<>' : rNeq, +'here' : rHere, +'rot' : rRot, +'sin' : rSin, +'cos' : rCos, +'random' : rRandom, +'tan' : rTan, +'asin' : rAsin, +'acos' : rAcos, +'atan' : rAtan, +'sqrt' : rSqrt, +'words' : rWords, +'R@' : rRat, +'1+' : rOneplus, +'>r' : rgtR, +'r>' : rRgt, +'j' : rJ, +'type' : rType, +'word' : rWord, +'ddump' : rDdump, +'idump' : rIdump, +'immediate' : rImmediate, +'lte' : rLtE, +} +#================================= Compile time + +def compile() : + global ds + pcode = []; prompt = "Ok " if len(ds)==0 else "Ok: " + while 1 : + word = getWord(prompt) # get next word + if word == None : return None + cAct = cDict.get(word) # Is there a compile time action ? + rAct = rDict.get(word) # Is there a runtime action ? + + if cAct : cAct(pcode) # run at compile time + elif rAct : + if type(rAct) == type([]) : + pcode.append(rRun) # Compiled word. + pcode.append(word) # for now do dynamic lookup + else : pcode.append(rAct) # push builtin for runtime + else : + # Number to be pushed onto ds at runtime + pcode.append(rPush) + try : pcode.append(int(word)) + except : + try: pcode.append(float(word)) + except : + pcode[-1] = rRun # Change rPush to rRun + pcode.append(word) # Assume word will be defined + if not cStack : return pcode + prompt = "... " + +def fatal (mesg) : raise mesg + +def cColon (pcode) : + if cStack : fatal(": inside Control stack: %s" % cStack) + label = getWord() + cStack.append(("COLON",label)) # flag for following ";" + +def cSemi (pcode) : + if not cStack : fatal("No : for ; to match") + code,label = cStack.pop() + if code != "COLON" : fatal(": not balanced with ;") + rDict[label] = pcode[:] # Save word definition in rDict + while pcode : pcode.pop() + +def cBegin (pcode) : + cStack.append(("BEGIN",len(pcode))) # flag for following UNTIL + +def cUntil (pcode) : + if not cStack : fatal("No BEGIN for UNTIL to match") + code,slot = cStack.pop() + if code != "BEGIN" : fatal("UNTIL preceded by %s (not BEGIN)" % code) + pcode.append(rJz) + pcode.append(slot) + +def cWhile (pcode) : + if not cStack : fatal("No BEGIN for WHILE to match") + pcode.append(rJz) + +def cRepeat (pcode) : + if not cStack : fatal("No BEGIN for REPEAT to match") + +def cDo (pcode) : + cStack.append(("DO",len(pcode))) # flag for following UNTIL + pcode.append(rSwap) + pcode.append(rgtR) + pcode.append(rgtR) + +def cLoop (pcode) : + if not cStack : fatal("No DO for LOOP to match") + code,slot = cStack.pop() + if code != "DO" : fatal("LOOP preceded by %s (not DO)" % code) + pcode.append(rRgt) + pcode.append(rRgt) + pcode.append(rSwap) + pcode.append(rOneplus) + pcode.append(rOver) + pcode.append(rOver) + pcode.append(rEq) + pcode.append(rJz) + pcode.append(slot) + pcode.append(rDrop) + pcode.append(rDrop) + +def cLoopPlus (pcode) : + if not cStack : fatal("No DO for LOOP to match") + code,slot = cStack.pop() + if code != "DO" : fatal("+LOOP preceded by %s (not DO)" % code) + pcode.append(rRgt) + pcode.append(rRgt) + pcode.append(rSwap) + pcode.append(rRot) + pcode.append(rAdd) + pcode.append(rOver) + pcode.append(rOver) + pcode.append(rSwap) + pcode.append(rLt) + pcode.append(rJz) + pcode.append(slot) + pcode.append(rDrop) + pcode.append(rDrop) + +def cI (pcode) : + pcode.append(rRat) + +def cJ (pcode) : + pcode.append(rRgt) + pcode.append(rRgt) + pcode.append(rRgt) + pcode.append(rDup) + pcode.append(rgtR) + pcode.append(rSwap) + pcode.append(rgtR) + pcode.append(rSwap) + pcode.append(rgtR) + +def cIf (pcode) : + pcode.append(rJz) + cStack.append(("IF",len(pcode))) # flag for following Then or Else + pcode.append(0) # slot to be filled in + +def cElse (pcode) : + if not cStack : fatal("No IF for ELSE to match") + code,slot = cStack.pop() + if code != "IF" : fatal("ELSE preceded by %s (not IF)" % code) + pcode.append(rJmp) + cStack.append(("ELSE",len(pcode))) # flag for following THEN + pcode.append(0) # slot to be filled in + pcode[slot] = len(pcode) # close JZ for IF + +def cThen (pcode) : + if not cStack : fatal("No IF or ELSE for THEN to match") + code,slot = cStack.pop() + if code not in ("IF","ELSE") : fatal("THEN preceded by %s (not IF or ELSE)" % code) + pcode[slot] = len(pcode) # close JZ for IF or JMP for ELSE + +cDict = { + ':' : cColon, ';' : cSemi, 'if': cIf, 'else': cElse, 'then': cThen, + 'begin': cBegin, 'until': cUntil, +'do': cDo, 'loop': cLoop, '+loop' : cLoopPlus, 'i' : cI , 'j' : cJ , 'while' : cWhile, 'repeat' : cRepeat, +} + +if __name__ == "__main__" : main() +#END +EXPORT FORTH() +BEGIN +PYTHON(name); +END;