Showing
3 changed files
with
188 additions
and
0 deletions
snakes/utils/ctlstar/__init__.py
0 → 100644
File mode changed
snakes/utils/ctlstar/build.py
0 → 100644
1 | +from snakes.lang.ctlstar.parser import parse | ||
2 | +from snakes.lang.ctlstar import asdl as ast | ||
3 | +from snakes.lang import getvars, unparse | ||
4 | +import _ast | ||
5 | + | ||
6 | +class SpecError (Exception) : | ||
7 | + def __init__ (self, node, reason) : | ||
8 | + Exception.__init__(self, "[line %s] %s" % (node.lineno, reason)) | ||
9 | + | ||
10 | +def astcopy (node) : | ||
11 | + if not isinstance(node, _ast.AST) : | ||
12 | + return node | ||
13 | + attr = {} | ||
14 | + for name in node._fields + node._attributes : | ||
15 | + value = getattr(node, name) | ||
16 | + if isinstance(value, list) : | ||
17 | + attr[name] = [astcopy(child) for child in value] | ||
18 | + else : | ||
19 | + attr[name] = astcopy(value) | ||
20 | + return node.__class__(**attr) | ||
21 | + | ||
22 | +class Binder (ast.NodeTransformer) : | ||
23 | + def __init__ (self, bind) : | ||
24 | + ast.NodeTransformer.__init__(self) | ||
25 | + self.bind = [bind] | ||
26 | + def visit (self, node) : | ||
27 | + return ast.NodeTransformer.visit(self, astcopy(node)) | ||
28 | + def visit_ListComp (self, node) : | ||
29 | + """ | ||
30 | + >>> tree = ast.parse('x+y+[x+y+z for x, y in l]') | ||
31 | + >>> unparse(Binder({'x':ast.Name('hello')}).visit(tree)) | ||
32 | + '((hello + y) + [((x + y) + z) for (x, y) in l])' | ||
33 | + >>> unparse(Binder({'y':ast.Name('hello')}).visit(tree)) | ||
34 | + '((x + hello) + [((x + y) + z) for (x, y) in l])' | ||
35 | + >>> unparse(Binder({'z':ast.Name('hello')}).visit(tree)) | ||
36 | + '((x + y) + [((x + y) + hello) for (x, y) in l])' | ||
37 | + """ | ||
38 | + bind = self.bind[-1].copy() | ||
39 | + for comp in node.generators : | ||
40 | + for name in getvars(comp.target) : | ||
41 | + if name in bind : | ||
42 | + del bind[name] | ||
43 | + self.bind.append(bind) | ||
44 | + node.elt = self.visit(node.elt) | ||
45 | + self.bind.pop(-1) | ||
46 | + return node | ||
47 | + def visit_Name (self, node) : | ||
48 | + if node.id in self.bind[-1] : | ||
49 | + return astcopy(self.bind[-1][node.id]) | ||
50 | + else : | ||
51 | + return astcopy(node) | ||
52 | + | ||
53 | +def bind (node, ctx) : | ||
54 | + return Binder(ctx).visit(node) | ||
55 | + | ||
56 | +class Builder (object) : | ||
57 | + def __init__ (self, spec) : | ||
58 | + self.spec = spec | ||
59 | + self.decl = {} | ||
60 | + for node in spec.atoms + spec.properties : | ||
61 | + if node.name in self.decl : | ||
62 | + raise SpecError(node, "%r already declare line %s" | ||
63 | + % (name.name, self.decl[name.name].lineno)) | ||
64 | + self.decl[node.name] = node | ||
65 | + self.main = spec.main | ||
66 | + def build (self, node) : | ||
67 | + node = astcopy(node) | ||
68 | + return self._build(node, {}) | ||
69 | + def _build (self, node, ctx) : | ||
70 | + if isinstance(node, ast.atom) : | ||
71 | + try : | ||
72 | + builder = getattr(self, "_build_%s" % node.__class__.__name__) | ||
73 | + except AttributeError : | ||
74 | + node.atomic = True | ||
75 | + else : | ||
76 | + node = builder(node, ctx) | ||
77 | + node.atomic = True | ||
78 | + elif isinstance(node, ast.CtlBinary) : | ||
79 | + node.left = self._build(node.left, ctx) | ||
80 | + node.right = self._build(node.right, ctx) | ||
81 | + node.atomic = (isinstance(node.op, (ast.boolop, ast.Imply, | ||
82 | + ast.Iff)) | ||
83 | + and node.left.atomic | ||
84 | + and node.right.atomic) | ||
85 | + elif isinstance(node, ast.CtlUnary) : | ||
86 | + node.child = self._build(node.child, ctx) | ||
87 | + node.atomic = (isinstance(node.op, ast.Not) | ||
88 | + and node.child.atomic) | ||
89 | + else : | ||
90 | + assert False, "how did we get there?" | ||
91 | + return node | ||
92 | + def _build_place (self, param, ctx) : | ||
93 | + if isinstance(param, ast.Parameter) : | ||
94 | + if param.name not in ctx : | ||
95 | + raise SpecError(param, "place %r should be instantiated" | ||
96 | + % param.name) | ||
97 | + return ctx[param.name] | ||
98 | + else : | ||
99 | + return param | ||
100 | + def _build_InPlace (self, node, ctx) : | ||
101 | + node.data = [bind(child, ctx) for child in node.data] | ||
102 | + node.place = self._build_place(node.place, ctx) | ||
103 | + return node | ||
104 | + def _build_NotInPlace (self, node, ctx) : | ||
105 | + return self._build_InPlace(node, ctx) | ||
106 | + def _build_EmptyPlace (self, node, ctx) : | ||
107 | + node.place = self._build_place(node.place, ctx) | ||
108 | + return node | ||
109 | + def _build_MarkedPlace (self, node, ctx) : | ||
110 | + return self._build_EmptyPlace(node, ctx) | ||
111 | + # skip Deadlock and Boolean: nothing to do | ||
112 | + def _build_Quantifier (self, node, ctx) : | ||
113 | + node.place = self._build_place(node.place, ctx) | ||
114 | + ctx = ctx.copy() | ||
115 | + for name in node.vars : | ||
116 | + ctx[name] = ast.Token(name, node.place.place) | ||
117 | + node.child = self._build(node.child, ctx) | ||
118 | + return node | ||
119 | + def _build_Instance (self, node, ctx) : | ||
120 | + if node.name not in self.decl : | ||
121 | + raise SpecError(node, "undeclared object %r" % node.name) | ||
122 | + ctx = ctx.copy() | ||
123 | + decl = self.decl[node.name] | ||
124 | + for arg in decl.args : | ||
125 | + ctx[arg.name] = arg | ||
126 | + if isinstance(decl, ast.Property) : | ||
127 | + return self._build_Instance_Property(node, decl, ctx) | ||
128 | + else : | ||
129 | + return self._build_Instance_Atom(node, decl, ctx) | ||
130 | + def _build_Instance_Property (self, node, prop, ctx) : | ||
131 | + bound = set(a.name for a in prop.args) | ||
132 | + args = dict((a.arg, a.annotation) for a in node.args) | ||
133 | + for param in prop.params : | ||
134 | + if param.name in bound : | ||
135 | + raise SpecError(node, "argument %r already bound" | ||
136 | + % param.name) | ||
137 | + elif param.name in args : | ||
138 | + arg = args.pop(param.name) | ||
139 | + bound.add(param.name) | ||
140 | + else : | ||
141 | + raise SpecError(node, "missing argument %r" % param.name) | ||
142 | + if param.type == "place" : | ||
143 | + if not isinstance(arg, ast.Place) : | ||
144 | + raise SpecError(node, "expected place for %r" | ||
145 | + % param.name) | ||
146 | + arg.name = param.name | ||
147 | + ctx[param.name] = arg | ||
148 | + if args : | ||
149 | + raise SpecError(node, "too many arguments (%s)" | ||
150 | + % ", ".join(repr(a) for a in args)) | ||
151 | + return self._build(astcopy(prop.body), ctx) | ||
152 | + def _build_Instance_Atom (self, node, atom, ctx) : | ||
153 | + bound = set(a.name for a in atom.args) | ||
154 | + args = dict((a.arg, a.annotation) for a in node.args) | ||
155 | + new = astcopy(atom) | ||
156 | + for param in atom.params : | ||
157 | + if param.name in bound : | ||
158 | + raise SpecError(node, "argument %r already bound" | ||
159 | + % param.name) | ||
160 | + elif param.name in args : | ||
161 | + arg = args.pop(param.name) | ||
162 | + bound.add(param.name) | ||
163 | + else : | ||
164 | + raise SpecError(node, "missing argument %r" % param.name) | ||
165 | + if param.type == "place" : | ||
166 | + if not isinstance(arg, ast.Place) : | ||
167 | + raise SpecError(node, "expected place for %r" | ||
168 | + % param.name) | ||
169 | + arg.name = param.name | ||
170 | + else : | ||
171 | + arg = ast.Argument(name=param.name, | ||
172 | + value=arg, | ||
173 | + type=param.type) | ||
174 | + new.args.append(arg) | ||
175 | + if args : | ||
176 | + raise SpecError(node, "too many arguments (%s)" | ||
177 | + % ", ".join(repr(a) for a in args)) | ||
178 | + del new.params[:] | ||
179 | + return new | ||
180 | + | ||
181 | +def build (spec, main=None) : | ||
182 | + b = Builder(spec) | ||
183 | + if main is None : | ||
184 | + return b.build(spec.main) | ||
185 | + else : | ||
186 | + return b.build(main) |
-
Please register or login to post a comment