Franck Pommereau

moved let to a plugin with extended capabilities

......@@ -1040,43 +1040,6 @@ class Expression (ArcAnnotation) :
def __hash__ (self) :
return hash(self._str.strip())
def let (**args) :
"""A dirty trick to allow side effects in expressions
Assignement takes place when method `bind` of the expression is
called. Assigned variables are then stored in the `Substitution`
passed to `bind`. This is useful in some cases when you want to
repeat a computation in several output arcs: you do it once in the
guard and then use the bounded variable on the output arcs.
>>> n = PetriNet('n')
>>> n.globals['let'] = let
>>> t = Transition('t', Expression('x is not None and let(foo=42)'))
>>> n.add_transition(t)
>>> n.add_place(Place('p', [dot]))
>>> n.add_input('p', 't', Variable('x'))
>>> t.modes() == [Substitution(x=dot, foo=42)]
True
Note in the example above that `let` is not known by default, you
have to import it explicitly in the global environment of your
`PetriNet` (line 2). This is intended so it is clear that you
known what you do.
@param args: a list of `name=value` to assign
@return: `True` if assignment could be made, `False` otherwise
@rtype: `bool`
@warning: use with care, sides effects are nasty tricks, you may
get unexpected results while playing with `let`
"""
try :
__binding__ = inspect.stack()[1][0].f_locals["__binding__"]
for name, value in args.items() :
__binding__[name] = value
except :
return False
return True
class MultiArc (ArcAnnotation) :
"""A collection of other annotations, allowing to consume or produce
several tokens at once.
......
"""A dirty trick to allow side effects in expressions
Assignement takes place when method `bind` of the expression is
called. Assigned variables are then stored in the `Substitution`
passed to `bind`. This is useful in some cases when you want to
repeat a computation in several output arcs: you do it once in the
guard and then use the bounded variable on the output arcs.
>>> import snakes.plugins
>>> snakes.plugins.load('let', 'snakes.nets', 'snk')
<module ...>
>>> from snk import *
>>> n = PetriNet('n')
>>> t = Transition('t', Expression('x is not None and let("egg, spam = iter(str(foo))", foo=42, __raise__=True)'))
>>> n.add_transition(t)
>>> n.add_place(Place('p', [dot]))
>>> n.add_input('p', 't', Variable('x'))
>>> t.modes() == [Substitution(x=dot, foo=42, egg='4', spam='2')]
True
@param args: a list of `name=value` to assign
@return: `True` if assignment could be made, `False` otherwise
@rtype: `bool`
@warning: use with care, sides effects are nasty tricks, you may
get unexpected results while playing with `let`
"""
import inspect
import snakes.plugins
from snakes.lang.python.parser import ast, parse
from snakes.lang import unparse
class DropLet (ast.NodeTransformer) :
def __init__ (self, names) :
ast.NodeTransformer.__init__(self)
self.names = set(names)
self.calls = []
def visit_Call (self, node) :
func = node.func
if func.__class__.__name__ == "Name" and func.id in self.names :
self.calls.append((func.id,
node.args and node.args[0].s or None,
[(k.arg, unparse(k.value))
for k in node.keywords]))
return ast.Name(id="True")
return node
class DropTrue (ast.NodeTransformer) :
def visit_BoolOp (self, node) :
if node.op.__class__.__name__ == "And" :
values = [self.visit(v) for v in node.values
if v.__class__.__name__ != "Name" or v.id != "True"]
if values :
node.values[:] = values
return node
else :
return ast.Name(id="True")
else :
return self.visit(node)
def unlet (expr, *names) :
if not names :
names = ["let"]
drop = DropLet(names)
new = DropTrue().visit(drop.visit(parse(expr)))
return unparse(new), drop.calls
class MakeLet (object) :
def __init__ (self, globals) :
self.globals = globals
def match (self, match, binding) :
env = binding.dict()
env.update(iter(self.globals))
exec("", env)
old = set(env)
exec(match, env)
for name in set(env) - old :
binding[name] = env[name]
def __call__ (self, __match__=None, __raise__=False, **args) :
try :
__binding__ = inspect.stack()[1][0].f_locals["__binding__"]
for name, value in args.items() :
__binding__[name] = value
if __match__ :
self.match(__match__, __binding__)
except :
if __raise__ :
raise
return False
return True
@snakes.plugins.plugin("snakes.nets")
def extend (module) :
class PetriNet (module.PetriNet) :
def __init__ (self, name, **args) :
module.PetriNet.__init__(self, name, **args)
self.globals["let"] = MakeLet(self.globals)
return PetriNet, unlet