Why don't people like lisp?
Andrew Dalke
adalke at mindspring.com
Wed Oct 22 05:07:25 EDT 2003
Me:
> I then chose
> a problem which could be done that way and implemented it
> in the canonical tokenizer/parser/translater/compiler framework
> Kaz Kylheku (to whom I was responding) wanted, when it's
> inappropriate for this case, hence complicating my original code.
Here's what the code would look like without that strict
partitioning. It can be made a bit smaller (I don't really need the
'Stack' class; it just helps simplify dealing with line numbers,
and the is_float make the if/elif/else structure nice, compared
to using a try/except.)
The point though is that Python allows the same sorts of
domain language -> AST -> "native" AST -> native code
steps that Kaz Kylheku wanted.
Andrew
dalke at dalkescientific.com
import re, sys
from compiler import ast, misc, pycodegen
class RPNError(Exception):
pass
_symbol_re = re.compile(r"\S+")
#_variable_re = re.compile(r"[A-Za-z_][A-Za-z0-9_]*$")
# Let's take anything!
_variable_re = re.compile(r".*$")
class Stack:
def __init__(self):
self.stack = []
def add_term(self, term, lineno):
term.lineno = lineno
self.stack.append(term)
def add_oper(self, klass, lineno, charpos):
if len(self.stack) < 2:
raise RPNError(
"Binary operator at line %d char %d missing terms" %
(lineno, charpos))
term = klass(self.stack[-2:])
term.lineno = lineno
self.stack[-2:] = [term]
def is_float(s):
try:
float(s)
return 1
except ValueError:
return 0
_id_gen = iter(xrange(sys.maxint))
_oper_table = {
"+": ast.Add,
"-": ast.Sub,
"*": ast.Mul,
"/": ast.Div,
"**": ast.Power,
}
def compile(s):
param_names = []
stack = Stack()
for lineno, line in enumerate(s.split("\n")):
for match in _symbol_re.finditer(line):
word = match.group(0)
charpos = match.start(0) + 1
if is_float(word):
stack.add_term(ast.Const(float(word)), lineno)
elif word in _oper_table:
stack.add_oper(_oper_table[word],
lineno, charpos)
elif _variable_re.match(word):
stack.add_term(ast.Name(word), lineno)
if word not in param_names:
param_names.append(word)
else:
# Hmm, wonder what it is.
raise RPNError(
"Unknown token %r at line %d, character %d" %
(word, lineno, charpos))
stack = stack.stack
if len(stack) != 1:
raise RPNError("stack ends with size %d" %
len(stack))
# go through an ugly bit of shenanigans
# (I don't like the compiler API ):
fctn_name = 'RPN' + str(_id_gen.next())
fctn = ast.Function(fctn_name, param_names, [],
0, None,
ast.Stmt([ast.Return(stack[0])]))
mod = ast.Module(None, ast.Stmt([fctn]))
misc.set_filename("<RPN string " + fctn_name + ">", mod)
code = pycodegen.ModuleCodeGenerator(mod).getCode()
d = {"__builtins__": {}}
exec code in d
return d[fctn_name]
def main():
assert compile("2 3 **")() == 8
assert compile("a 2 3 + -")(a=6) == 1
assert compile("a 2 3 + -")(7) == 2
assert compile("a b *")(2, 3) == 6
assert compile("a b -")(b=2, a=3) == 1
assert compile("1 2 3 4 + + +")() == 10
f = compile("(#) ') try + -")
print f(**{"(#)": 5, "')": 7, "try": 3})
print "All tests passed"
if __name__ == "__main__":
main()
More information about the Python-list
mailing list