Showing
2 changed files
with
99 additions
and
37 deletions
... | @@ -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. | ... | ... |
snakes/plugins/let.py
0 → 100644
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 |
-
Please register or login to post a comment