Co-routines
Duncan Booth
duncan at NOSPAMrcp.co.uk
Fri Jul 18 10:57:35 EDT 2003
<thewrights at ozemail.com.au> wrote in
news:g7xRa.305$fF.9914 at nnrp1.ozemail.com.au:
> I have an application in which I want the users to be able to create
> python functions:
>
> def f1():
> print "1-1"
> print "1-2"
> print "1-3"
>
> def f2():
> print "2-1"
> print "2-2"
> print "3-3"
>
> and when my application runs, I want to execute these functions in
> "lock-step", so that the output looks like:
>
> 1-1
> 2-2
> 1-2
> 2-2
> 1-3
> 2-3
Does this do what you want?
Given an input string:
SOURCE = """\
def f1():
print "1-1"
for ch in ('a', 'b', 'c'):
print "1-2",ch
print "1-3"
def f2():
print "2-1"
print "2-2"
print "2-3"
"""
it produces:
1-1
2-1
1-2 a
2-2
1-2 b
2-3
1-2 c
1-3
N.B. It executes all top-level functions in the input, interleaving statements.
If a function calls another function, the called function will be executed
at full speed. A function could call another function 'line at a time' by
wrapping it's generator in for loop, but it would have to be explicit.
----- begin steplock.py -----
# Execute functions in steplock
import symbol, token
from symbol import *
from token import *
import parser
import compiler
import types
class Atom:
def __init__(self, value):
self.value = value
def __repr__(self):
return str(self.value)
def pretty(astList):
res = []
el = astList[0]
if el in symbol.sym_name:
res.append(Atom(symbol.sym_name[el]))
elif el in token.tok_name:
res.append(Atom(token.tok_name[el]))
else:
res.append(el)
for el in astList[1:]:
if isinstance(el, (list, tuple)):
res.append(pretty(el))
else:
res.append(el)
return type(astList)(res)
def printAst(ast, depth=2):
if not isinstance(ast, (list, tuple)):
ast = ast.tolist()
import pprint
pp = pprint.PrettyPrinter(depth=depth)
pp.pprint(pretty(ast))
class TreeWalker:
def __init__(self):
pass
def dovisit(self, node):
t = node[0]
nodetype = "default"
if t in symbol.sym_name:
nodetype = sym_name[t]
elif t in token.tok_name:
nodetype = tok_name[t]
fname = "visit_" + nodetype
if hasattr(self, fname):
return getattr(self, fname)(node)
return self.visit_default(node)
def visit_default(self, node):
# Visit an otherwise boring node.
res = [ node[0] ]
for child in node[1:]:
if isinstance(child, (list, tuple)):
res.append(self.dovisit(child))
else:
res.append(child)
return tuple(res)
class TopWalker(TreeWalker):
def __init__(self):
self.generators = []
def visit_file_input(self, node):
newnodes = self.visit_default(node)
assert newnodes[-1][0] == 0 and newnodes[-2][0] == 4
newnodes = newnodes[:-2] + tuple(self.generators) + newnodes[-2:]
return newnodes
#def visit_stmt(self, node):
# printAst(node, depth=255)
# return self.visit_default(node)
def visit_funcdef(self, node):
self.generators.extend(MakeGenerator(node))
return node
YIELD_STMT = (stmt, (simple_stmt, (small_stmt, (flow_stmt, (yield_stmt, (NAME, 'yield'),
(testlist, (test, (and_test, (not_test, (comparison,
(expr, (xor_expr, (and_expr, (shift_expr, (arith_expr, (term, (factor,
(power, (atom, (NAME, 'None')))))))))))))))))), (NEWLINE, '')
))
def MakeGenerator(funcdefnode):
funcname = funcdefnode[2][1]
newfuncname = funcname + '_generator'
suite = funcdefnode[-1]
walker = FunctionWalker()
newsuite = walker.dovisit(suite)
NewFunc = (funcdef, (NAME, 'def'), (NAME, newfuncname)) + funcdefnode[3:-1] + (newsuite,)
NewFunc_stmt = (stmt, (compound_stmt, NewFunc))
#return (NewFunc_stmt, )
ASSIGN_stmt = (stmt, (simple_stmt, (small_stmt, (expr_stmt, (testlist,
(symbol.test, (and_test, (not_test, (comparison, (expr, (xor_expr,
(and_expr, (shift_expr, (arith_expr,(term, (factor, (power,
(atom, (NAME, funcname)),
(trailer, (DOT, '.'), (NAME, 'generator'))))))))))))))),
(EQUAL, '='),
(testlist, (symbol.test, (and_test, (not_test, (comparison, (expr, (xor_expr,
(and_expr, (shift_expr, (arith_expr,(term,
(factor, (power, (atom, (NAME, newfuncname))))))))))))))))),
(NEWLINE, '')))
DEL_stmt = (stmt, (simple_stmt, (small_stmt,
(del_stmt, (NAME, 'del'),
(exprlist, (expr, (xor_expr, (and_expr, (shift_expr,
(arith_expr, (term, (factor, (power, (atom, (NAME, newfuncname))))))))))))),
(NEWLINE, '')))
return (NewFunc_stmt, ASSIGN_stmt, DEL_stmt)
class FunctionWalker(TreeWalker):
def visit_funcdef(self, node):
return node # Ignore nested functions!
def visit_suite(self, node):
#print "Visiting suite of %d children" % (len(node)-1)
res = [ node[0] ]
for el in node[1:]:
if el[0] == stmt:
if el[1][0] == compound_stmt:
res.append(self.dovisit(el))
else:
res.append(el)
res.append(YIELD_STMT)
else:
res.append(el)
return tuple(res)
def test():
SOURCE = """\
def f1():
print "1-1"
for ch in ('a', 'b', 'c'):
print "1-2",ch
print "1-3"
def f2():
print "2-1"
print "2-2"
print "2-3"
"""
ast = parser.suite(SOURCE)
astList = ast.totuple(1)
walker = TopWalker()
newList = walker.dovisit(astList)
code = parser.sequence2ast(newList)
namespace = { '__builtins__': __builtins__ }
exec code.compile('dummy.py') in namespace
functions = [ namespace[key].generator()
for key in namespace
if isinstance(namespace[key], types.FunctionType) ]
while functions:
for f in functions[:]:
try:
f.next()
except StopIteration:
functions.remove(f)
if __name__=='__main__':
test()
----- end steplock.py -----
--
Duncan Booth duncan at rcp.co.uk
int month(char *p){return(124864/((p[0]+p[1]-p[2]&0x1f)+1)%12)["\5\x8\3"
"\6\7\xb\1\x9\xa\2\0\4"];} // Who said my code was obscure?
More information about the Python-list
mailing list