forked from ls1248659692/python_guide
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathforth.py
More file actions
177 lines (150 loc) · 6.07 KB
/
forth.py
File metadata and controls
177 lines (150 loc) · 6.07 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
#!/usr/local/bin/python
#
# f o r t h . p y
# Author: Chris Meyers @
# http://openbookproject.net/py4fun/forth/forth.html
#
import re
try:
raw_input # Python 2
except NameError:
raw_input = input # Python 3
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<b))
def rSwap(cod,p) : a=ds.pop(); b=ds.pop(); ds.append(a); ds.append(b)
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 = ", ds)
def rDot (cod,p) : print(ds.pop())
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) : execute(rDict[cod[p]]); return p+1
def rPush(cod,p) : ds.append(cod[p]) ; return p+1
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,
}
#================================= Compile time
def compile() :
pcode = []; prompt = "Forth> "
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()