Franck Pommereau

moved let to a plugin with extended capabilities

...@@ -1040,43 +1040,6 @@ class Expression (ArcAnnotation) : ...@@ -1040,43 +1040,6 @@ class Expression (ArcAnnotation) :
1040 def __hash__ (self) : 1040 def __hash__ (self) :
1041 return hash(self._str.strip()) 1041 return hash(self._str.strip())
1042 1042
1043 -def let (**args) :
1044 - """A dirty trick to allow side effects in expressions
1045 -
1046 - Assignement takes place when method `bind` of the expression is
1047 - called. Assigned variables are then stored in the `Substitution`
1048 - passed to `bind`. This is useful in some cases when you want to
1049 - repeat a computation in several output arcs: you do it once in the
1050 - guard and then use the bounded variable on the output arcs.
1051 -
1052 - >>> n = PetriNet('n')
1053 - >>> n.globals['let'] = let
1054 - >>> t = Transition('t', Expression('x is not None and let(foo=42)'))
1055 - >>> n.add_transition(t)
1056 - >>> n.add_place(Place('p', [dot]))
1057 - >>> n.add_input('p', 't', Variable('x'))
1058 - >>> t.modes() == [Substitution(x=dot, foo=42)]
1059 - True
1060 -
1061 - Note in the example above that `let` is not known by default, you
1062 - have to import it explicitly in the global environment of your
1063 - `PetriNet` (line 2). This is intended so it is clear that you
1064 - known what you do.
1065 -
1066 - @param args: a list of `name=value` to assign
1067 - @return: `True` if assignment could be made, `False` otherwise
1068 - @rtype: `bool`
1069 - @warning: use with care, sides effects are nasty tricks, you may
1070 - get unexpected results while playing with `let`
1071 - """
1072 - try :
1073 - __binding__ = inspect.stack()[1][0].f_locals["__binding__"]
1074 - for name, value in args.items() :
1075 - __binding__[name] = value
1076 - except :
1077 - return False
1078 - return True
1079 -
1080 class MultiArc (ArcAnnotation) : 1043 class MultiArc (ArcAnnotation) :
1081 """A collection of other annotations, allowing to consume or produce 1044 """A collection of other annotations, allowing to consume or produce
1082 several tokens at once. 1045 several tokens at once.
......
1 +"""A dirty trick to allow side effects in expressions
2 +
3 +Assignement takes place when method `bind` of the expression is
4 +called. Assigned variables are then stored in the `Substitution`
5 +passed to `bind`. This is useful in some cases when you want to
6 +repeat a computation in several output arcs: you do it once in the
7 +guard and then use the bounded variable on the output arcs.
8 +
9 +>>> import snakes.plugins
10 +>>> snakes.plugins.load('let', 'snakes.nets', 'snk')
11 +<module ...>
12 +>>> from snk import *
13 +>>> n = PetriNet('n')
14 +>>> t = Transition('t', Expression('x is not None and let("egg, spam = iter(str(foo))", foo=42, __raise__=True)'))
15 +>>> n.add_transition(t)
16 +>>> n.add_place(Place('p', [dot]))
17 +>>> n.add_input('p', 't', Variable('x'))
18 +>>> t.modes() == [Substitution(x=dot, foo=42, egg='4', spam='2')]
19 +True
20 +
21 +@param args: a list of `name=value` to assign
22 +@return: `True` if assignment could be made, `False` otherwise
23 +@rtype: `bool`
24 +@warning: use with care, sides effects are nasty tricks, you may
25 + get unexpected results while playing with `let`
26 +"""
27 +
28 +import inspect
29 +import snakes.plugins
30 +
31 +from snakes.lang.python.parser import ast, parse
32 +from snakes.lang import unparse
33 +
34 +class DropLet (ast.NodeTransformer) :
35 + def __init__ (self, names) :
36 + ast.NodeTransformer.__init__(self)
37 + self.names = set(names)
38 + self.calls = []
39 + def visit_Call (self, node) :
40 + func = node.func
41 + if func.__class__.__name__ == "Name" and func.id in self.names :
42 + self.calls.append((func.id,
43 + node.args and node.args[0].s or None,
44 + [(k.arg, unparse(k.value))
45 + for k in node.keywords]))
46 + return ast.Name(id="True")
47 + return node
48 +
49 +class DropTrue (ast.NodeTransformer) :
50 + def visit_BoolOp (self, node) :
51 + if node.op.__class__.__name__ == "And" :
52 + values = [self.visit(v) for v in node.values
53 + if v.__class__.__name__ != "Name" or v.id != "True"]
54 + if values :
55 + node.values[:] = values
56 + return node
57 + else :
58 + return ast.Name(id="True")
59 + else :
60 + return self.visit(node)
61 +
62 +def unlet (expr, *names) :
63 + if not names :
64 + names = ["let"]
65 + drop = DropLet(names)
66 + new = DropTrue().visit(drop.visit(parse(expr)))
67 + return unparse(new), drop.calls
68 +
69 +class MakeLet (object) :
70 + def __init__ (self, globals) :
71 + self.globals = globals
72 + def match (self, match, binding) :
73 + env = binding.dict()
74 + env.update(iter(self.globals))
75 + exec("", env)
76 + old = set(env)
77 + exec(match, env)
78 + for name in set(env) - old :
79 + binding[name] = env[name]
80 + def __call__ (self, __match__=None, __raise__=False, **args) :
81 + try :
82 + __binding__ = inspect.stack()[1][0].f_locals["__binding__"]
83 + for name, value in args.items() :
84 + __binding__[name] = value
85 + if __match__ :
86 + self.match(__match__, __binding__)
87 + except :
88 + if __raise__ :
89 + raise
90 + return False
91 + return True
92 +
93 +@snakes.plugins.plugin("snakes.nets")
94 +def extend (module) :
95 + class PetriNet (module.PetriNet) :
96 + def __init__ (self, name, **args) :
97 + module.PetriNet.__init__(self, name, **args)
98 + self.globals["let"] = MakeLet(self.globals)
99 + return PetriNet, unlet