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';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
> ##            n...@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()

Thank you for the code. How can i import stack? Where is the code for it?
-- 
http://mail.python.org/mailman/listinfo/python-list

Reply via email to