diff --git a/02-go-forth/forth.py b/02-go-forth/forth.py new file mode 100644 index 0000000..710b754 --- /dev/null +++ b/02-go-forth/forth.py @@ -0,0 +1,172 @@ +#!/usr/local/bin/python +# +# f o r t h . p y +# Author: Chris Meyers @ +# http://openbookproject.net/py4fun/forth/forth.html +# +import sys, re + +ds = [] # The data stack +cStack = [] # The control struct stack +heap = [0]*2000 # The data heap +heapNext = 0 # Next avail slot in heap +words = [] # The input stream of tokens + +def main() : + while 1 : + pcode = compile() # compile/run from user + if pcode == None : print; return + execute(pcode) + +#============================== Lexical Parsing + +def getWord (prompt="... ") : + global words + while not words : + try : lin = raw_input(prompt)+"\n" + except : return None + if lin[0:1] == "@" : lin = open(lin[1:-1]).read() + tokenizeWords(lin) + word = words[0] + words = words[1:] + return word + +def tokenizeWords(s) : + global words # clip comments, split to list of words + words += re.sub("#.*\n","\n",s+"\n").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 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 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 " + 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 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, +} + +if __name__ == "__main__" : main()