Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #25587

Re: FORTH in Python (py4th.py)

From recaialkan@gmail.com
Newsgroups comp.lang.python
Subject Re: FORTH in Python (py4th.py)
Date 2012-07-18 15:05 -0700
Organization http://groups.google.com
Message-ID <b34d488d-a8f1-4758-8a3b-5839d29640e6@googlegroups.com> (permalink)
References <3c3772$21u@typhoon.osg.saic.com>

Show all headers | View raw


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
> ##            nick@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;)
> 
>     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 &#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?

Back to comp.lang.python | Previous | NextNext in thread | Find similar | Unroll thread


Thread

Re: FORTH in Python (py4th.py) recaialkan@gmail.com - 2012-07-18 15:05 -0700
  Re: FORTH in Python (py4th.py) Ian Kelly <ian.g.kelly@gmail.com> - 2012-07-18 16:43 -0600
  Re: FORTH in Python (py4th.py) MRAB <python@mrabarnett.plus.com> - 2012-07-19 00:22 +0100

csiph-web