Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

FORTH in Python (py4th.py)

1,356 views
Skip to first unread message

Nick Seidenman

unread,
Dec 6, 1994, 9:38:26 PM12/6/94
to
Just for fun I sat down and started writing a forth interpreter in
python. I got as far as putting in control structs and decided I';d
better get back to my real job. It does make a nice little example of
many of the cooler features of the language. I hearby bequeath its
current incodation to the net.

No arguments, now! ;)

---------------<cut here>-------------
#!/usr/Util/bin/python
#
# @(#)py4th.py 1.1 94/12/06
#
# Forth in Python (py4th).
#
## This module implements a postfix interpreter class that
## can be instantiated as the inner interpreter or as a forth-ish
## interactive interpreter. The inner interpreter has two methods
## called p_compile and p_interp that are the core methods. Compile
## takes a string argument and returns a list that is executable by
## the p_interp method.
##
## As of this version (12/6/94) there are a few important features
## that need to be added, namely if-else-then and do-loop structures.
## Doing so may require that the "for" construct used in p_interp
## be replaced by a while loop that iterates over the program with
## a program counter instead of the nice, clean, pc-less way it's done
## now. I had thought about implementing these as follows:
##
## for IF-ELSE-THEN:
##
## given -- IF wordlist1 ELSE wordlist2 THEN
## wordlist1 and wordlist2 would be compiled as embedded
## lists within the current list. For example:
##
## a @ if dup * else dup dup * * then
##
## would compile into
##
## ['a', '@', 'if', ['dup', '*'], 'else', [ 'dup', 'dup',
## '*', '*']]
##
## so that p_interp could then treat the wordlists as single
## words and pass then to recursive invocations of itself.
##
## I have a similar scheme in mind for DO-LOOPs.
##
## 10 0 do i . cr loop
##
## would become
##
## ['10', '0', 'do', ['i', '.', 'cr', 'loop']]
##
## One way to do it might be to have a prepass before
## p_interp and after p_compile. Since these control structures
## are only allowed inside definitions, perhaps p_semicolon
## could be where this happens. It could then build the
## sublists and add the code required for loop increments
## and proper skipping (else over if) and handling of omitted
## parts (if without else or then).
##
## Loops use the variable name 'I' for the reference count.
## The prepass mentioned above would generate code to store
## the current value of 'I' (if any) as some internally known
## variable (e.g., '__I__2371') and restore it once the loop
## was finished.
##
## I have already put the skeleton code in for doing this. It's a
## bit of a hack at this point but you can get the gist of what I have
## in mind from in.
##
## Author: Nick Seidenman
## SAIC
## ni...@osg.saic.com

import string
import math
import sys
import stack

StackUnderflow = 'StackUnderflow'
ExitNonError = 'ExitNonError'
InnerInterpreterError = 'InnerInterpreterError'


# InnerInterpreter takes a postfix expression in the form of
# a python list object and 'executes it'. It has it's own
# dictionary, which is initialized with the py4thon primatives and a few

# Operational modes.
Execution = 'Execution'
Definition = 'Definition'
Forgetting = 'Forgetting'
Comment = 'Comment'
Variable = 'Variable'

class InnerInterpreter:

def __init__(self):
# Create a new stack and dictionary for this interpreter instance.
self.__stack = stack.Stack()
self.__rstack = stack.Stack()
self.__vocabulary = {}
self.__ulist = []
self.__vars = {}
self.__vlist = []
self.__mode = Execution
self.__lastmode = Execution
self.__runlevel = 0

# Initialize the new dictionary with words for the primitive
# functions.
self.__vocabulary['.'] = self.p_dot
self.__vocabulary['cr'] = self.p_cr
self.__vocabulary['+'] = self.p_plus
self.__vocabulary['-'] = self.p_minus
self.__vocabulary['*'] = self.p_multiply
self.__vocabulary['/'] = self.p_divide
self.__vocabulary['uminus'] = self.p_uminus
self.__vocabulary['^'] = self.p_exponent
self.__vocabulary['variable'] = self.p_variable
self.__vocabulary['!'] = self.p_assign
self.__vocabulary['@'] = self.p_dereference
self.__vocabulary['dup'] = self.p_dup
self.__vocabulary['swap'] = self.p_swap
self.__vocabulary['bye'] = self.p_bye
self.__vocabulary['forget'] = self.p_forget
self.__vocabulary[':'] = self.p_colon
self.__vocabulary[';'] = self.p_semicolon
self.__vocabulary['('] = self.p_lparen
self.__vocabulary[')'] = self.p_rparen
self.__vocabulary['vlist'] = self.p_vlist

# Initialize dictionary with control structures.

self.__vocabulary['if'] = self.c_if
self.__vocabulary['else'] = self.c_else
self.__vocabulary['then'] = self.c_then
self.__vocabulary['do'] = self.c_do
self.__vocabulary['loop'] = self.c_loop
self.__vocabulary['+loop'] = self.c_plusloop

# Initialize the control structures prepass table.

self.__ctl_struct = {}
self.__ctl_struct['do'] = self.c_pp_do
self.__ctl_struct['loop'] = self.c_pp_loop
self.__ctl_struct['+loop'] = self.c_pp_plusloop
self.__ctl_struct['if'] = self.c_pp_if
self.__ctl_struct['else'] = self.c_pp_else
self.__ctl_struct['then'] = self.c_pp_then

# Primitive functions (all begin with 'p_'. Primitive
# is defined as a function that must directly manipulate
# the interpreter stack. Defined functions do not do this.

def p_dot(self):
result = self.__stack.pop()
sys.stdout.write (result + ' ')

def p_cr (self):
print

def p_plus(self):
y = string.atof(self.__stack.pop())
x = string.atof(self.__stack.pop())
self.__stack.push (`y + x`)

def p_minus (self):
y = string.atof(self.__stack.pop())
x = string.atof(self.__stack.pop())
self.__stack.push(`y - x`)

def p_multiply (self):
y= string.atof(self.__stack.pop())
x = string.atof(self.__stack.pop())
self.__stack.push(`y * x`)

def p_divide (self):
y = string.atof(self.__stack.pop())
x = string.atof(self.__stack.pop())
self.__stack.push( `b / a`)

def p_exponent (self):
y = string.atof(self.__stack.pop())
x = string.atof(self.__stack.pop())
self.__stack.push( `math.pow(x, y)`)

def p_uminus (self):
x = string.atof(self.__stack.pop())
self.__stack.push (`- x`)

def p_assign (self):
word = self.__stack.pop()
value = self.__stack.pop()
if self.__vars.has_key (word):
self.__vars[word] = value
else:
raise InnerInterpreterError, word

def p_dereference (self):
word = self.__stack.pop()
try:
self.__stack.push(self.__vars[word])
except KeyError, x:
raise InnerInterpreterError, word

def p_dup(self):
val = self.__stack.pop()
self.__stack.push(val)
self.__stack.push(val)

def p_swap(self):
a = self.__stack.pop()
b = self.__stack.pop()
self.__stack.push(a)
self.__stack.push(b)

def p_def (self):
word = self.__stack.pop()
prog = self.__stack.pop()
## print 'defining', word, 'as', prog
self.__vocabulary[word] = prog
self.__ulist.append(word)

def p_colon (self):
if self.__mode == Execution:
self.__mode = Definition
self.__colon = []
else:
raise InnerInterpreterError, 'nested colon'

def p_semicolon (self):
if self.__mode == Definition:
# Perhaps put prepass in here to scan for
# IF-ELSE-THEN and DO-LOOP and create sublists
# from these?
prog = self.__colon[1:]
self.__stack.push(prog)
self.__stack.push(self.__colon[0])
del self.__colon
self.p_def()
self.__mode = Execution
else:
raise InnerInterpreterError, 'nested semicolon'

def p_forget (self):
self.__mode = Forgetting

def p_bye (self):
raise ExitNonError

def p_compile (self, text):
return string.split(text)

def p_lparen (self):
if self.__mode != Comment:
self.__lastmode = self.__mode
self.__mode = Comment

def p_rparen (self):
if self.__mode == Comment:
self.__mode = self.__lastmode
else:
raise InnerInterpreterError, ')'

def do_forget (self, word):
if self.__vocabulary.has_key(word) or self.__vars.has_key(word):
i = self.__ulist.index(word) ## Should be in here.
ul = len(self.__ulist)
for j in range (i, ul):
if self.__vocabulary.has_key(self.__ulist[i]):
del self.__vocabulary[self.__ulist[i]]
elif self.__vars.has_key(self.__ulist[i]):
del self.__vars[self.__ulist[i]]
else:
raise InnerInterpreterError, 'system error'
del self.__ulist[i]
else:
raise InnerInterpreterError, 'system error'

self.__mode = Execution

def p_variable (self):
self.__lastmode = self.__mode
self.__mode = Variable

def do_variable (self, varName):
self.__vars[varName] = self.__stack.pop()
self.__ulist.append(varName)
self.__mode = self.__lastmode

def p_vlist (self):
vlist = self.__vocabulary.keys()
vlist.sort()
for k in vlist:
sys.stdout.write (k + ' ')

###
### Control structures (if then else, do loop, etc).
###

def c_if (self):
if self.__runlevel == 0:
raise InnerInterpreterError, 'if'

def c_else (self):
if self.__runlevel == 0:
raise InnerInterpreterError, 'else'

def c_then (self):
if self.__runlevel == 0:
raise InnerInterpreterError, 'then'

def c_do (self):
if self.__runlevel == 0:
raise InnerInterpreterError, 'do'

def c_pp_do (self, scan):
self.__rstack.push(scan[0:])
scan = []
scan.append ('do')
return scan

def c_pp_if (self, scan):
self.__rstack.push(scan[0:])
scan = []
scan.append ('if')
return scan

def c_loop (self):
if self.__runlevel == 0:
raise InnerInterpreterError, 'loop'

def c_pp_loop (self, scan):
scan.append('loop')
result = self.__rstack.pop()
result.append(scan)
return result

def c_pp_plusloop (self, scan):
scan.append('+loop')
result = self.__rstack.pop()
result.append(scan)
return result

def c_pp_else (self, scan):
scan.append('else')
result = self.__rstack.pop()
result.append(scan)
return result

def c_pp_then (self, scan):
scan.append('then')
result = self.__rstack.pop()
result.append(scan)
return result

def c_plusloop (self):
if self.__runlevel == 0:
raise InnerInterpreterError, '+loop'

def c_prepass (self, prog):
self.__rstack.flush()
scan = []
for word in prog:
if self.__ctl_struct.has_key(word):
scan = self.__ctl_struct[word](scan)
else:
scan.append(word)

return scan

# This is the inner interpreter itself. It will execute the
# code body passed in the form of a python list as 'prog'.
def p_interp (self, prog):
for word in prog:
# Are we in comment mode?
if self.__mode == Comment:
if word == ')':
self.p_rparen()
continue

# Are we in execution mode or definition mode?
elif self.__mode == Definition:
if word == ';':
self.p_semicolon()
else:
self.__colon.append(word)
continue

elif self.__mode == Variable:
self.do_variable (word)
continue

# See if this is a word we are supposed to forget.
elif self.__mode == Forgetting:
self.do_forget (word)
continue

# If it isn't in the dictionary to begin with, then it must
# be a constant: push it on the stack
if type(word) != type([]) and not self.__vocabulary.has_key(word):
self.__stack.push(word)
continue

# It is in the dictionary, but it may be a defined word
# rather than a primitive. Chase it down to either a
# primitive, a constant, or another code body. In the
# latter case, recurse with the new code body.
else:
current_word = word
try:
while self.__vocabulary.has_key (self.__vocabulary[current_word]):
current_word = self.__vocabulary[current_word]
except TypeError, x:
pass # Ok, since this is probably because the
# it's a list, or a primative.

# If what we have is another program (non-primative word)
# then we run interp recursively to execute the word's text.
if type(current_word) == type([]):
self.__runlevel = self.__runlevel + 1
self.p_interp(current_word)
self.__runlevel = self.__runlevel - 1

elif type(self.__vocabulary[current_word]) == type([]):
self.__runlevel = self.__runlevel + 1
self.p_interp(self.__vocabulary[current_word])
self.__runlevel = self.__runlevel - 1

elif type(self.__vocabulary[current_word]) == type (self.p_def):
self.__vocabulary[current_word]()

# Whatever it is at this point just gets pushed onto
# the stack. It should be some sort of constant.
else:
self.__stack.push(self.__vocabulary[current_word])

# Simple outter interpreter for Py4th. I envision this as being
# augmented with thread support to allow multiple instances of
# the interpreter to provide a multitasking "forth" environment.

class Py4th:
def __init__(self, input=sys.stdin):
self.input = input
self.interpreter = InnerInterpreter ()

def go (self):
try:
while 1:
try:
input = self.input.readline ()
code = self.interpreter.p_compile (input)
self.interpreter.p_interp (code)
if self.input.isatty () and self.interpreter.__mode == Execution:
print 'OK'
except InnerInterpreterError, err_str:
if err_str != 'stack underflow':
print err_str, '?'
else:
print err_str
self.interpreter.__stack.flush()

except ExitNonError:
if self.input.isatty ():
print 'goodbye'
pass

# ----------------------------------------------------------
# Test driver. Add to this as functionality is augmented.
# ----------------------------------------------------------
def test ():
## # Compile and run a simple program.
##
## print '***** Testing simple postfix program'
## s = '2 3 + . 3 4 ^ .'
f = InnerInterpreter()
## t = f.p_compile (s)
## print s, '->', t
## f.p_interp (t)
##
#### This section predated the implementation of ':-;' and is no longer
#### needed.
#### ------------------------------
#### # Now add program as a new word to the dictionary, then invoke it.
####
#### f.__stack.push(t)
#### f.__stack.push('junk')
#### f.p_def()
#### f.p_interp (['junk'])
##
## # Test assignment (!) word.
##
## print '***** Testing VARIABLE ! and @'
## s = '19 variable a 3 a @ * . cr'
## t = f.p_compile(s)
## print s, '->', t
## f.p_interp(t)
##
## try:
## s = 'b @ . cr'
## t = f.p_compile(s)
## f.p_interp(t)
## except InnerInterpreterError, x:
## print 'This should fail'
##
## # Test dup and swap
##
## print '***** Testing dup and swap'
## s = '20 dup . cr . cr'
## t = f.p_compile(s)
## print s, '->', t
## print 'should see 20\\n20\\n'
## f.p_interp(t)
##
## s = '5 10 swap . cr . cr'
## t = f.p_compile(s)
## print s, '->', t
## print 'should see \\n5\\n10\\n'
## f.p_interp(t)
##
## # Test : , ;, and forget
##
## print '***** Testing colon definitions and FORGET'
## s = ': sq dup * ; 2 sq 3 sq 100 sq . cr . cr . cr'
## t = f.p_compile(s)
## print s, '->', t
## print 'Should see 10000\\n9\\n4\\n'
## f.p_interp(t)
##
## print 'forgetting sq'
## f.p_interp(f.p_compile('4 variable d 5 variable e'))
## f.p_interp(f.p_compile('d @ e @ . cr . cr'))
## f.p_interp(f.p_compile('forget sq'))
## try:
## print f.__vocabulary['sq'] # It better not find this.
## except KeyError, k:
## print 'sq forgotten' # Exception is ok.
##
## try:
## print f.__vars['d'] # It better not find this.
## except KeyError, k:
## pass # Exception is ok.
##
## try:
## print f.__vars['e'] # It better not find this.
## except KeyError, k:
## print 'FORGET works' # Exception is ok.
##
## # Everything defined since sq is also forgotten - good!

s = ': nestor 10 variable i 10 0 do i @ if . cr else dup 2 * loop 1 2 3 10 5 do . cr 2 +loop + + . cr ;'
t = f.p_compile (s)
print t
u = f.c_prepass (t)
print u
f.p_interp(u)

## print f.__vocabulary

f.p_interp(f.c_prepass(f.p_compile('nestor')))

# Run the test program when called as a script
if __name__ == '__main__':
test()


recai...@gmail.com

unread,
Jul 18, 2012, 6:05:24 PM7/18/12
to ni...@osg.saic.com
7 Aralık 1994 Çarşamba 05:04:22 UTC+2 tarihinde Nick Seidenman yazdı:
> Just for fun I sat down and started writing a forth interpreter in
> python. I got as far as putting in control structs and decided I&#39;;d
> better get back to my real job. It does make a nice little example of
> many of the cooler features of the language. I hearby bequeath its
> current incodation to the net.
>
> No arguments, now! ;)
>
> ---------------&lt;cut here&gt;-------------
> #!/usr/Util/bin/python
> #
> # @(#)py4th.py 1.1 94/12/06
> #
> # Forth in Python (py4th).
> #
> ## This module implements a postfix interpreter class that
> ## can be instantiated as the inner interpreter or as a forth-ish
> ## interactive interpreter. The inner interpreter has two methods
> ## called p_compile and p_interp that are the core methods. Compile
> ## takes a string argument and returns a list that is executable by
> ## the p_interp method.
> ##
> ## As of this version (12/6/94) there are a few important features
> ## that need to be added, namely if-else-then and do-loop structures.
> ## Doing so may require that the &quot;for&quot; construct used in p_interp
> ## be replaced by a while loop that iterates over the program with
> ## a program counter instead of the nice, clean, pc-less way it&#39;s done
> ## now. I had thought about implementing these as follows:
> ##
> ## for IF-ELSE-THEN:
> ##
> ## given -- IF wordlist1 ELSE wordlist2 THEN
> ## wordlist1 and wordlist2 would be compiled as embedded
> ## lists within the current list. For example:
> ##
> ## a @ if dup * else dup dup * * then
> ##
> ## would compile into
> ##
> ## [&#39;a&#39;, &#39;@&#39;, &#39;if&#39;, [&#39;dup&#39;, &#39;*&#39;], &#39;else&#39;, [ &#39;dup&#39;, &#39;dup&#39;,
> ## &#39;*&#39;, &#39;*&#39;]]
> ##
> ## so that p_interp could then treat the wordlists as single
> ## words and pass then to recursive invocations of itself.
> ##
> ## I have a similar scheme in mind for DO-LOOPs.
> ##
> ## 10 0 do i . cr loop
> ##
> ## would become
> ##
> ## [&#39;10&#39;, &#39;0&#39;, &#39;do&#39;, [&#39;i&#39;, &#39;.&#39;, &#39;cr&#39;, &#39;loop&#39;]]
> ##
> ## One way to do it might be to have a prepass before
> ## p_interp and after p_compile. Since these control structures
> ## are only allowed inside definitions, perhaps p_semicolon
> ## could be where this happens. It could then build the
> ## sublists and add the code required for loop increments
> ## and proper skipping (else over if) and handling of omitted
> ## parts (if without else or then).
> ##
> ## Loops use the variable name &#39;I&#39; for the reference count.
> ## The prepass mentioned above would generate code to store
> ## the current value of &#39;I&#39; (if any) as some internally known
> ## variable (e.g., &#39;__I__2371&#39;) and restore it once the loop
> ## was finished.
> ##
> ## I have already put the skeleton code in for doing this. It&#39;s a
> ## bit of a hack at this point but you can get the gist of what I have
> ## in mind from in.
> ##
> ## Author: Nick Seidenman
> ## SAIC
> ## ni...@osg.saic.com
>
> import string
> import math
> import sys
> import stack
>
> StackUnderflow = &#39;StackUnderflow&#39;
> ExitNonError = &#39;ExitNonError&#39;
> InnerInterpreterError = &#39;InnerInterpreterError&#39;
>
>
> # InnerInterpreter takes a postfix expression in the form of
> # a python list object and &#39;executes it&#39;. It has it&#39;s own
> # dictionary, which is initialized with the py4thon primatives and a few
>
> # Operational modes.
> Execution = &#39;Execution&#39;
> Definition = &#39;Definition&#39;
> Forgetting = &#39;Forgetting&#39;
> Comment = &#39;Comment&#39;
> Variable = &#39;Variable&#39;
>
> class InnerInterpreter:
>
> def __init__(self):
> # Create a new stack and dictionary for this interpreter instance.
> self.__stack = stack.Stack()
> self.__rstack = stack.Stack()
> self.__vocabulary = {}
> self.__ulist = []
> self.__vars = {}
> self.__vlist = []
> self.__mode = Execution
> self.__lastmode = Execution
> self.__runlevel = 0
>
> # Initialize the new dictionary with words for the primitive
> # functions.
> self.__vocabulary[&#39;.&#39;] = self.p_dot
> self.__vocabulary[&#39;cr&#39;] = self.p_cr
> self.__vocabulary[&#39;+&#39;] = self.p_plus
> self.__vocabulary[&#39;-&#39;] = self.p_minus
> self.__vocabulary[&#39;*&#39;] = self.p_multiply
> self.__vocabulary[&#39;/&#39;] = self.p_divide
> self.__vocabulary[&#39;uminus&#39;] = self.p_uminus
> self.__vocabulary[&#39;^&#39;] = self.p_exponent
> self.__vocabulary[&#39;variable&#39;] = self.p_variable
> self.__vocabulary[&#39;!&#39;] = self.p_assign
> self.__vocabulary[&#39;@&#39;] = self.p_dereference
> self.__vocabulary[&#39;dup&#39;] = self.p_dup
> self.__vocabulary[&#39;swap&#39;] = self.p_swap
> self.__vocabulary[&#39;bye&#39;] = self.p_bye
> self.__vocabulary[&#39;forget&#39;] = self.p_forget
> self.__vocabulary[&#39;:&#39;] = self.p_colon
> self.__vocabulary[&#39;;&#39;] = self.p_semicolon
> self.__vocabulary[&#39;(&#39;] = self.p_lparen
> self.__vocabulary[&#39;)&#39;] = self.p_rparen
> self.__vocabulary[&#39;vlist&#39;] = self.p_vlist
>
> # Initialize dictionary with control structures.
>
> self.__vocabulary[&#39;if&#39;] = self.c_if
> self.__vocabulary[&#39;else&#39;] = self.c_else
> self.__vocabulary[&#39;then&#39;] = self.c_then
> self.__vocabulary[&#39;do&#39;] = self.c_do
> self.__vocabulary[&#39;loop&#39;] = self.c_loop
> self.__vocabulary[&#39;+loop&#39;] = self.c_plusloop
>
> # Initialize the control structures prepass table.
>
> self.__ctl_struct = {}
> self.__ctl_struct[&#39;do&#39;] = self.c_pp_do
> self.__ctl_struct[&#39;loop&#39;] = self.c_pp_loop
> self.__ctl_struct[&#39;+loop&#39;] = self.c_pp_plusloop
> self.__ctl_struct[&#39;if&#39;] = self.c_pp_if
> self.__ctl_struct[&#39;else&#39;] = self.c_pp_else
> self.__ctl_struct[&#39;then&#39;] = self.c_pp_then
>
>
> # Primitive functions (all begin with &#39;p_&#39;. Primitive
> # is defined as a function that must directly manipulate
> # the interpreter stack. Defined functions do not do this.
>
> def p_dot(self):
> result = self.__stack.pop()
> sys.stdout.write (result + &#39; &#39;)
> ## print &#39;defining&#39;, word, &#39;as&#39;, prog
> self.__vocabulary[word] = prog
> self.__ulist.append(word)
>
> def p_colon (self):
> if self.__mode == Execution:
> self.__mode = Definition
> self.__colon = []
> else:
> raise InnerInterpreterError, &#39;nested colon&#39;
>
> def p_semicolon (self):
> if self.__mode == Definition:
> # Perhaps put prepass in here to scan for
> # IF-ELSE-THEN and DO-LOOP and create sublists
> # from these?
> prog = self.__colon[1:]
> self.__stack.push(prog)
> self.__stack.push(self.__colon[0])
> del self.__colon
> self.p_def()
> self.__mode = Execution
> else:
> raise InnerInterpreterError, &#39;nested semicolon&#39;
>
> def p_forget (self):
> self.__mode = Forgetting
>
> def p_bye (self):
> raise ExitNonError
>
> def p_compile (self, text):
> return string.split(text)
>
> def p_lparen (self):
> if self.__mode != Comment:
> self.__lastmode = self.__mode
> self.__mode = Comment
>
> def p_rparen (self):
> if self.__mode == Comment:
> self.__mode = self.__lastmode
> else:
> raise InnerInterpreterError, &#39;)&#39;
>
> def do_forget (self, word):
> if self.__vocabulary.has_key(word) or self.__vars.has_key(word):
> i = self.__ulist.index(word) ## Should be in here.
> ul = len(self.__ulist)
> for j in range (i, ul):
> if self.__vocabulary.has_key(self.__ulist[i]):
> del self.__vocabulary[self.__ulist[i]]
> elif self.__vars.has_key(self.__ulist[i]):
> del self.__vars[self.__ulist[i]]
> else:
> raise InnerInterpreterError, &#39;system error&#39;
> del self.__ulist[i]
> else:
> raise InnerInterpreterError, &#39;system error&#39;
>
> self.__mode = Execution
>
> def p_variable (self):
> self.__lastmode = self.__mode
> self.__mode = Variable
>
> def do_variable (self, varName):
> self.__vars[varName] = self.__stack.pop()
> self.__ulist.append(varName)
> self.__mode = self.__lastmode
>
> def p_vlist (self):
> vlist = self.__vocabulary.keys()
> vlist.sort()
> for k in vlist:
> sys.stdout.write (k + &#39; &#39;)
>
> ###
> ### Control structures (if then else, do loop, etc).
> ###
>
> def c_if (self):
> if self.__runlevel == 0:
> raise InnerInterpreterError, &#39;if&#39;
>
> def c_else (self):
> if self.__runlevel == 0:
> raise InnerInterpreterError, &#39;else&#39;
>
> def c_then (self):
> if self.__runlevel == 0:
> raise InnerInterpreterError, &#39;then&#39;
>
> def c_do (self):
> if self.__runlevel == 0:
> raise InnerInterpreterError, &#39;do&#39;
>
> def c_pp_do (self, scan):
> self.__rstack.push(scan[0:])
> scan = []
> scan.append (&#39;do&#39;)
> return scan
>
> def c_pp_if (self, scan):
> self.__rstack.push(scan[0:])
> scan = []
> scan.append (&#39;if&#39;)
> return scan
>
> def c_loop (self):
> if self.__runlevel == 0:
> raise InnerInterpreterError, &#39;loop&#39;
>
> def c_pp_loop (self, scan):
> scan.append(&#39;loop&#39;)
> result = self.__rstack.pop()
> result.append(scan)
> return result
>
> def c_pp_plusloop (self, scan):
> scan.append(&#39;+loop&#39;)
> result = self.__rstack.pop()
> result.append(scan)
> return result
>
> def c_pp_else (self, scan):
> scan.append(&#39;else&#39;)
> result = self.__rstack.pop()
> result.append(scan)
> return result
>
> def c_pp_then (self, scan):
> scan.append(&#39;then&#39;)
> result = self.__rstack.pop()
> result.append(scan)
> return result
>
> def c_plusloop (self):
> if self.__runlevel == 0:
> raise InnerInterpreterError, &#39;+loop&#39;
>
> def c_prepass (self, prog):
> self.__rstack.flush()
> scan = []
> for word in prog:
> if self.__ctl_struct.has_key(word):
> scan = self.__ctl_struct[word](scan)
> else:
> scan.append(word)
>
> return scan
>
> # This is the inner interpreter itself. It will execute the
> # code body passed in the form of a python list as &#39;prog&#39;.
> def p_interp (self, prog):
> for word in prog:
> # Are we in comment mode?
> if self.__mode == Comment:
> if word == &#39;)&#39;:
> self.p_rparen()
> continue
>
> # Are we in execution mode or definition mode?
> elif self.__mode == Definition:
> if word == &#39;;&#39;:
> self.p_semicolon()
> else:
> self.__colon.append(word)
> continue
>
> elif self.__mode == Variable:
> self.do_variable (word)
> continue
>
> # See if this is a word we are supposed to forget.
> elif self.__mode == Forgetting:
> self.do_forget (word)
> continue
>
> # If it isn&#39;t in the dictionary to begin with, then it must
> # be a constant: push it on the stack
> if type(word) != type([]) and not self.__vocabulary.has_key(word):
> self.__stack.push(word)
> continue
>
> # It is in the dictionary, but it may be a defined word
> # rather than a primitive. Chase it down to either a
> # primitive, a constant, or another code body. In the
> # latter case, recurse with the new code body.
> else:
> current_word = word
> try:
> while self.__vocabulary.has_key (self.__vocabulary[current_word]):
> current_word = self.__vocabulary[current_word]
> except TypeError, x:
> pass # Ok, since this is probably because the
> # it&#39;s a list, or a primative.
>
> # If what we have is another program (non-primative word)
> # then we run interp recursively to execute the word&#39;s text.
> if type(current_word) == type([]):
> self.__runlevel = self.__runlevel + 1
> self.p_interp(current_word)
> self.__runlevel = self.__runlevel - 1
>
> elif type(self.__vocabulary[current_word]) == type([]):
> self.__runlevel = self.__runlevel + 1
> self.p_interp(self.__vocabulary[current_word])
> self.__runlevel = self.__runlevel - 1
>
> elif type(self.__vocabulary[current_word]) == type (self.p_def):
> self.__vocabulary[current_word]()
>
> # Whatever it is at this point just gets pushed onto
> # the stack. It should be some sort of constant.
> else:
> self.__stack.push(self.__vocabulary[current_word])
>
> # Simple outter interpreter for Py4th. I envision this as being
> # augmented with thread support to allow multiple instances of
> # the interpreter to provide a multitasking &quot;forth&quot; environment.
>
> class Py4th:
> def __init__(self, input=sys.stdin):
> self.input = input
> self.interpreter = InnerInterpreter ()
>
> def go (self):
> try:
> while 1:
> try:
> input = self.input.readline ()
> code = self.interpreter.p_compile (input)
> self.interpreter.p_interp (code)
> if self.input.isatty () and self.interpreter.__mode == Execution:
> print &#39;OK&#39;
> except InnerInterpreterError, err_str:
> if err_str != &#39;stack underflow&#39;:
> print err_str, &#39;?&#39;
> else:
> print err_str
> self.interpreter.__stack.flush()
>
> except ExitNonError:
> if self.input.isatty ():
> print &#39;goodbye&#39;
> pass
>
> # ----------------------------------------------------------
> # Test driver. Add to this as functionality is augmented.
> # ----------------------------------------------------------
> def test ():
> ## # Compile and run a simple program.
> ##
> ## print &#39;***** Testing simple postfix program&#39;
> ## s = &#39;2 3 + . 3 4 ^ .&#39;
> f = InnerInterpreter()
> ## t = f.p_compile (s)
> ## print s, &#39;-&gt;&#39;, t
> ## f.p_interp (t)
> ##
> #### This section predated the implementation of &#39;:-;&#39; and is no longer
> #### needed.
> #### ------------------------------
> #### # Now add program as a new word to the dictionary, then invoke it.
> ####
> #### f.__stack.push(t)
> #### f.__stack.push(&#39;junk&#39;)
> #### f.p_def()
> #### f.p_interp ([&#39;junk&#39;])
> ##
> ## # Test assignment (!) word.
> ##
> ## print &#39;***** Testing VARIABLE ! and @&#39;
> ## s = &#39;19 variable a 3 a @ * . cr&#39;
> ## t = f.p_compile(s)
> ## print s, &#39;-&gt;&#39;, t
> ## f.p_interp(t)
> ##
> ## try:
> ## s = &#39;b @ . cr&#39;
> ## t = f.p_compile(s)
> ## f.p_interp(t)
> ## except InnerInterpreterError, x:
> ## print &#39;This should fail&#39;
> ##
> ## # Test dup and swap
> ##
> ## print &#39;***** Testing dup and swap&#39;
> ## s = &#39;20 dup . cr . cr&#39;
> ## t = f.p_compile(s)
> ## print s, &#39;-&gt;&#39;, t
> ## print &#39;should see 20\\n20\\n&#39;
> ## f.p_interp(t)
> ##
> ## s = &#39;5 10 swap . cr . cr&#39;
> ## t = f.p_compile(s)
> ## print s, &#39;-&gt;&#39;, t
> ## print &#39;should see \\n5\\n10\\n&#39;
> ## f.p_interp(t)
> ##
> ## # Test : , ;, and forget
> ##
> ## print &#39;***** Testing colon definitions and FORGET&#39;
> ## s = &#39;: sq dup * ; 2 sq 3 sq 100 sq . cr . cr . cr&#39;
> ## t = f.p_compile(s)
> ## print s, &#39;-&gt;&#39;, t
> ## print &#39;Should see 10000\\n9\\n4\\n&#39;
> ## f.p_interp(t)
> ##
> ## print &#39;forgetting sq&#39;
> ## f.p_interp(f.p_compile(&#39;4 variable d 5 variable e&#39;))
> ## f.p_interp(f.p_compile(&#39;d @ e @ . cr . cr&#39;))
> ## f.p_interp(f.p_compile(&#39;forget sq&#39;))
> ## try:
> ## print f.__vocabulary[&#39;sq&#39;] # It better not find this.
> ## except KeyError, k:
> ## print &#39;sq forgotten&#39; # Exception is ok.
> ##
> ## try:
> ## print f.__vars[&#39;d&#39;] # It better not find this.
> ## except KeyError, k:
> ## pass # Exception is ok.
> ##
> ## try:
> ## print f.__vars[&#39;e&#39;] # It better not find this.
> ## except KeyError, k:
> ## print &#39;FORGET works&#39; # Exception is ok.
> ##
> ## # Everything defined since sq is also forgotten - good!
>
> s = &#39;: nestor 10 variable i 10 0 do i @ if . cr else dup 2 * loop 1 2 3 10 5 do . cr 2 +loop + + . cr ;&#39;
> t = f.p_compile (s)
> print t
> u = f.c_prepass (t)
> print u
> f.p_interp(u)
>
> ## print f.__vocabulary
>
> f.p_interp(f.c_prepass(f.p_compile(&#39;nestor&#39;)))
>
> # Run the test program when called as a script
> if __name__ == &#39;__main__&#39;:
> test()

Thank you for the code. How can i import stack? Where is the code for it?

Ian Kelly

unread,
Jul 18, 2012, 6:43:20 PM7/18/12
to Python
On Wed, Jul 18, 2012 at 4:05 PM, <recai...@gmail.com> wrote:
> 7 Aralık 1994 Çarşamba 05:04:22 UTC+2 tarihinde Nick Seidenman yazdı:

You realize that you're replying to a post from 1994? This code was
written for Python 1.0. I'll be pretty impressed if it still works in
2.7.

> Thank you for the code. How can i import stack? Where is the code for it?

Google turns up this post, also from 1994:

https://groups.google.com/forum/?fromgroups#!topic/comp.lang.python/5qBmi7K-mo4

Cheers,
Ian

MRAB

unread,
Jul 18, 2012, 7:22:11 PM7/18/12
to pytho...@python.org
On 18/07/2012 23:43, Ian Kelly wrote:
> On Wed, Jul 18, 2012 at 4:05 PM, <recai...@gmail.com> wrote:
>> 7 Aral�k 1994 �ar�amba 05:04:22 UTC+2 tarihinde Nick Seidenman yazd�:
>
> You realize that you're replying to a post from 1994? This code was
> written for Python 1.0. I'll be pretty impressed if it still works in
> 2.7.
>
You'll need to create a Stack class and replace the string exceptions,
but once those are fixed, it appears to work OK in Python 2.7.
0 new messages