mirror of
https://github.com/diemheych/PrimeFORTH
synced 2024-12-26 09:58:13 +01:00
Python source for HP Prime FORTH
HP Prime FORTH in Python code including required wrapper to run on HP Prime
This commit is contained in:
parent
7c856800c3
commit
b63d35514d
1 changed files with 451 additions and 0 deletions
451
FORTH.py
Normal file
451
FORTH.py
Normal file
|
@ -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<b))
|
||||
def rLtE (cod,p) : b=ds.pop(); a=ds.pop(); ds.append(int(a<=b))
|
||||
def rSqr (cod,p) : a=ds.pop(); ds.append(math.sqrt(a))
|
||||
def rCos (cod,p) : a=ds.pop(); ds.append(math.cos(a))
|
||||
def rRandom (cod,p) : a=ds.pop(); ds.append(urandom.randint(0,a-1))
|
||||
def rSwap(cod,p) : a=ds.pop(); b=ds.pop(); ds.append(a); ds.append(b)
|
||||
def rRot(cod,p) : a=ds.pop(); b=ds.pop(); c=ds.pop(); ds.append(b); ds.append(a); ds.append(c)
|
||||
def rDup (cod,p) : ds.append(ds[-1])
|
||||
def rDrop(cod,p) : ds.pop()
|
||||
def rOver(cod,p) : ds.append(ds[-2])
|
||||
def rDump(cod,p) : print("ds = %s" % ds)
|
||||
def rDdump(cod,p) : print(rDict)
|
||||
def rIdump(cod,p) : print(imDict)
|
||||
def rDot (cod,p) : print(ds.pop(),end=' ')
|
||||
def rEmit (cod,p) : print(chr(ds.pop()&255),end='')
|
||||
def rHere(cod,p) : ds.append(heapNext)
|
||||
def rJmp (cod,p) : return cod[p]
|
||||
def rJnz (cod,p) : return (cod[p],p+1)[ds.pop()]
|
||||
def rJz (cod,p) : return (p+1,cod[p])[ds.pop()==0]
|
||||
def rRun (cod,p) :
|
||||
if cod[p] in rDict:
|
||||
execute(rDict[cod[p]])
|
||||
else:
|
||||
print(cod[p]," unknown")
|
||||
return p+1
|
||||
|
||||
def rPush(cod,p) : ds.append(cod[p]) ; return p+1
|
||||
def rCls(cod,p) : graphic.clear_screen(background); hpprime.eval("print")
|
||||
def rSin(cod,p) : a=ds.pop(); ds.append(math.sin(a))
|
||||
def rCos(cod,p) : a=ds.pop(); ds.append(math.cos(a))
|
||||
def rTan(cod,p) : a=ds.pop(); ds.append(math.tan(a))
|
||||
def rAsin(cod,p) : a=ds.pop(); ds.append(math.asin(a))
|
||||
def rAcos(cod,p) : a=ds.pop(); ds.append(math.acos(a))
|
||||
def rAtan(cod,p) : a=ds.pop(); ds.append(math.atan(a))
|
||||
def rSqrt(cod,p) : a=ds.pop(); ds.append(math.sqrt(a))
|
||||
|
||||
def rgtR(cod,p) :
|
||||
a=ds.pop()
|
||||
cStack.append(("NUMBER",a))
|
||||
|
||||
def rRgt(cod,p) :
|
||||
name,value=cStack.pop()
|
||||
ds.append(value)
|
||||
|
||||
def rRat(cod,p) :
|
||||
name,value = cStack.pop()
|
||||
cStack.append((name,value))
|
||||
ds.append(value)
|
||||
|
||||
def rJ (cod,p) :
|
||||
c1,t1 = cStack.pop()
|
||||
c2,t2 = cStack.pop()
|
||||
c3,t3 = cStack.pop()
|
||||
cStack.append((c3,t3))
|
||||
cStack.append((c2,t2))
|
||||
cStack.append((c1,t1))
|
||||
ds.append(t3)
|
||||
|
||||
def rWord (cod,p) :
|
||||
word=getWord()
|
||||
for c in word :
|
||||
ds.append(ord(c))
|
||||
ds.append(len(word))
|
||||
|
||||
def rType (cod,p) :
|
||||
len = ds.pop()
|
||||
addr = ds.pop()
|
||||
print("addr ",addr,len)
|
||||
for i in range(0, len) :
|
||||
print(chr(heap[addr+i]&0x7f),end="")
|
||||
|
||||
def rImmediate(cod,p) :
|
||||
imDict[list(rDict.keys())[-1]] = 1
|
||||
|
||||
def rWords(cod,p) :
|
||||
for k in sorted(list(rDict.keys())): print(k,end=' ')
|
||||
|
||||
def rCreate (pcode,p) :
|
||||
global heapNext, lastCreate
|
||||
lastCreate = label = getWord() # match next word (input) to next heap address
|
||||
rDict[label] = [rPush, heapNext] # when created word is run, pushes its address
|
||||
|
||||
def rDoes (cod,p) :
|
||||
rDict[lastCreate] += cod[p:] # rest of words belong to created words runtime
|
||||
return len(cod) # jump p over these
|
||||
|
||||
def rAllot (cod,p) :
|
||||
global heapNext
|
||||
heapNext += ds.pop() # reserve n words for last create
|
||||
|
||||
def rAt (cod,p) : ds.append(heap[ds.pop()]) # get heap @ address
|
||||
def rBang(cod,p) : a=ds.pop(); heap[a] = ds.pop() # set heap @ address
|
||||
def rComa(cod,p) : # push tos into heap
|
||||
global heapNext
|
||||
heap[heapNext]=ds.pop()
|
||||
heapNext += 1
|
||||
|
||||
rDict = {
|
||||
'+' : rAdd, '-' : rSub, '/' : rDiv, '*' : rMul, 'over': rOver,
|
||||
'dup': rDup, 'swap': rSwap, '.': rDot, 'dump' : rDump, 'drop': rDrop,
|
||||
'=' : rEq, '>' : 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;
|
Loading…
Reference in a new issue