Showing
11 changed files
with
311 additions
and
46 deletions
... | @@ -16,3 +16,6 @@ class ParseError (ZINCError) : | ... | @@ -16,3 +16,6 @@ class ParseError (ZINCError) : |
16 | if loc : | 16 | if loc : |
17 | msg = "[%s] %s" % (loc, msg) | 17 | msg = "[%s] %s" % (loc, msg) |
18 | ZINCError.__init__(self, msg) | 18 | ZINCError.__init__(self, msg) |
19 | + | ||
20 | +class CompileError (ParseError) : | ||
21 | + pass | ... | ... |
... | @@ -40,10 +40,20 @@ _errre = re.compile(r"^.*?error at line ([0-9]+), col ([0-9]+):[ \t]*" | ... | @@ -40,10 +40,20 @@ _errre = re.compile(r"^.*?error at line ([0-9]+), col ([0-9]+):[ \t]*" |
40 | "((.|\n)*)$", re.I|re.A) | 40 | "((.|\n)*)$", re.I|re.A) |
41 | 41 | ||
42 | class BaseParser (object) : | 42 | class BaseParser (object) : |
43 | + @classmethod | ||
44 | + def make_parser (cls) : | ||
45 | + def parse (source) : | ||
46 | + if isinstance(source, str) : | ||
47 | + return cls().parse(source, "<string>") | ||
48 | + else : | ||
49 | + return cls().parse(source.read(), getattr(source, "name", "<string>")) | ||
50 | + return parse | ||
43 | def init (self, parser) : | 51 | def init (self, parser) : |
44 | pass | 52 | pass |
45 | def parse (self, source, path) : | 53 | def parse (self, source, path) : |
46 | - parser = self.__parser__(indedent(source)) | 54 | + self.source = source |
55 | + self.path = path | ||
56 | + self.parser = parser = self.__parser__(indedent(source)) | ||
47 | self.init(parser) | 57 | self.init(parser) |
48 | try : | 58 | try : |
49 | do_parse = getattr(parser, parser.__default__, "INPUT") | 59 | do_parse = getattr(parser, parser.__default__, "INPUT") | ... | ... |
... | @@ -10,8 +10,4 @@ class Parser (BaseParser) : | ... | @@ -10,8 +10,4 @@ class Parser (BaseParser) : |
10 | ["negate"], | 10 | ["negate"], |
11 | ["task"]] | 11 | ["task"]] |
12 | 12 | ||
13 | -def parse (source) : | 13 | +parse = Parser.make_parser() |
14 | - if isinstance(source, str) : | ||
15 | - return Parser().parse(source, "<string>") | ||
16 | - else : | ||
17 | - return Parser().parse(source.read(), getattr(source, "name", "<string>")) | ... | ... |
zinc/io/imp.py
0 → 100644
1 | +import pathlib, logging, inspect | ||
2 | +from .. import CompileError, nets | ||
3 | +from . import metaparse, BaseParser | ||
4 | +from .meta import Compiler as BaseCompiler, node as decl | ||
5 | + | ||
6 | +class Parser (BaseParser) : | ||
7 | + class __parser__ (metaparse.MetaParser) : | ||
8 | + __compound__ = [["if", "*elif", "?else"], | ||
9 | + ["for", "?else"], | ||
10 | + ["while", "?else"], | ||
11 | + ["try", "except"], | ||
12 | + ["def"], | ||
13 | + ["task"]] | ||
14 | + | ||
15 | +parse = Parser.make_parser() | ||
16 | + | ||
17 | +class Context (dict) : | ||
18 | + def __call__ (self, **args) : | ||
19 | + d = self.copy() | ||
20 | + d.update(args) | ||
21 | + return d | ||
22 | + def __getattr__ (self, key) : | ||
23 | + return self[key] | ||
24 | + def __setattr__ (self, key, val) : | ||
25 | + self[key] = val | ||
26 | + | ||
27 | +class Compiler (BaseCompiler) : | ||
28 | + def __init__ (self, module=nets) : | ||
29 | + self.n = module | ||
30 | + def __call__ (self, source) : | ||
31 | + self.p = Parser() | ||
32 | + tree = self.parser.parse(source) | ||
33 | + self._tasks = {} | ||
34 | + return self.visit(tree, Context(_path=pathlib.Path("/"))) | ||
35 | + def _get_pos (self, node) : | ||
36 | + if "_pos" in node : | ||
37 | + return (self.p.parser._p_get_line(node._pos), | ||
38 | + self.p.parser._p_get_col(node._pos) - 1) | ||
39 | + else : | ||
40 | + return None, None | ||
41 | + def warn (self, node, message) : | ||
42 | + lno, cno = self._get_pos(node) | ||
43 | + if lno is not None : | ||
44 | + message = "[%s] %s" % (":".join([self.p.path, str(lno)]), message) | ||
45 | + logging.warn(message) | ||
46 | + def _check (self, node, cond, error) : | ||
47 | + lno, cno = self._get_pos(node) | ||
48 | + raise CompileError(error, lno=lno, cno=cno, path=self.p.path) | ||
49 | + def build_seq (self, seq, ctx) : | ||
50 | + net = self.visit(seq[0], ctx) | ||
51 | + for other in seq[1:] : | ||
52 | + n = self.visit(other, ctx) | ||
53 | + if n is not None : | ||
54 | + net &= n | ||
55 | + return net | ||
56 | + def visit_model (self, node, ctx) : | ||
57 | + return self.visit_seq(node.body, ctx) | ||
58 | + def visit_task (self, node, ctx) : | ||
59 | + pass | ||
60 | + def visit_def (self, node, ctx) : | ||
61 | + self._check(node, node.deco is None, "decorators are not supported") | ||
62 | + self._check(node, node.largs, "missing parameters NAME") | ||
63 | + self._check(node, node.largs[0].type == "name", | ||
64 | + "invalid function name %r (%s)" % (node.largs[0].value, | ||
65 | + node.largs[0].type)) | ||
66 | + for a in node.largs[1:] : | ||
67 | + self._check(node, a.kind == "name", "invalid parameter %s (%s)" | ||
68 | + % (a.value, a.kind)) | ||
69 | + for a, v in node.kargs.items() : | ||
70 | + self._check(node, v.kind in ("int", "code", "str"), | ||
71 | + "invalid defaut value for parameter %s: %r (%s)" | ||
72 | + % (a, v.value, v.kind)) | ||
73 | + name = node.largs[0].value | ||
74 | + if name in ctx : | ||
75 | + self.warn(node, "previous declaration of %r is masked" % name) | ||
76 | + path = ctx._path / name | ||
77 | + flag = inspect.Parameter.POSITIONAL_OR_KEYWORD | ||
78 | + sig = inspect.Signature([inspect.Parameter(a.value, flag) | ||
79 | + for a in node.largs[1:]] | ||
80 | + + [inspect.Parameter(a, flag, default=v.value) | ||
81 | + for a, v in node.kargs.items()]) | ||
82 | + ctx[name] = decl(kind="function", args=sig) | ||
83 | + body = self.build_seq(node.body, ctx(_path=path, | ||
84 | + **{a : decl(kind="variable") | ||
85 | + for a in sig.parameters})) | ||
86 | + # TODO: add initial transition that gets all the arguments and store them into | ||
87 | + # buffer places + terminal transition that flush these places and return | ||
88 | + # the value | ||
89 | + call = ret = None | ||
90 | + net = (call & body & ret) / tuple(sig.parameters) | ||
91 | + self._tasks[path] = net.task(name) | ||
92 | + def visit_assign (self, node, ctx) : | ||
93 | + tgt = node.target | ||
94 | + val = node.value | ||
95 | + self._check(node, tgt not in ctx or ctx[tgt].kind == "variable", | ||
96 | + "cannot assign to previously declared %s %r" | ||
97 | + % (ctx[tgt].kind, tgt)) | ||
98 | + if val.tag == "call" : | ||
99 | + net = self.visit(val) | ||
100 | + if tgt not in net : | ||
101 | + net.add_place(self.n.Place(tgt, status=self.n.buffer(tgt))) | ||
102 | + if tgt in ctx : | ||
103 | + net.add_input(tgt, "_w", self.n.Variable("_")) | ||
104 | + net.add_output(tgt, "_w", self.n.Variable("_r")) | ||
105 | + else : | ||
106 | + net = self.n.PetriNet("[%s:%s] %s" % (self._get_pos(node) + (node._src,))) | ||
107 | + net.add_transition(self.n.Transition("_t")) | ||
108 | + net.add_place(self.n.Place(tgt, status=self.n.buffer(tgt))) | ||
109 | + net.add_place(self.n.Place("_e", status=self.n.entry)) | ||
110 | + net.add_place(self.n.Place("_x", status=self.n.exit)) | ||
111 | + net.add_input("_e", "_t", self.n.Value(self.n.dot)) | ||
112 | + net.add_output("_x", "_t", self.n.Value(self.n.dot)) | ||
113 | + if tgt in ctx : | ||
114 | + net.add_input(tgt, "_t", self.n.Variable("_")) | ||
115 | + if val.tag == "name" : | ||
116 | + net.add_place(self.n.Place(tgt.value)) | ||
117 | + net.add_input(tgt.value, "_t", self.n.Test(self.n.Variable("_v"))) | ||
118 | + net.add_output(tgt, "_t", self.n.Variable("_v")) | ||
119 | + elif val.tag in ("int", "str") : | ||
120 | + net.add_output(tgt, "_t", self.n.Value(repr(val.value))) | ||
121 | + elif val.tag == "code" : | ||
122 | + net.add_output(tgt, "_t", self.n.Expression(val.value)) | ||
123 | + else : | ||
124 | + self._check(node, False, "unsupported assignment %r" % node._src) | ||
125 | + ctx[tgt] = decl(kind="variable") | ||
126 | + return net | ||
127 | + def visit_call (self, node, ctx) : | ||
128 | + self._check(node, node.name in ctx, "undeclared function %r" % node.name) | ||
129 | + self._check(node, ctx[node.name].kind == "function", | ||
130 | + "%s %s is not a function" % (ctx[node.name].kind, node.name)) | ||
131 | + try : | ||
132 | + argmap = ctx[node.name].args.bind(*node.largs, **node.kwargs) | ||
133 | + argmap.apply_defaults() | ||
134 | + except Exception as err : | ||
135 | + self._check(node, False, str(err)) | ||
136 | + net = self.n.PetriNet("[%s:%s] %s" % (self._get_pos(node) + (node._src,))) | ||
137 | + net.add_place(self.n.Place("_e", status=self.n.entry)) | ||
138 | + net.add_place(self.n.Place("_i", status=self.n.internal)) | ||
139 | + net.add_place(self.n.Place("_x", status=self.n.exit)) | ||
140 | + net.add_transition(self.n.Transition("_c")) | ||
141 | + net.add_transition(self.n.Transition("_w")) | ||
142 | + net.add_input("_e", "_t", self.n.Value(self.n.dot)) | ||
143 | + net.add_output("_i", "_t", self.n.Value(self.n.dot)) | ||
144 | + net.add_input("_i", "_w", self.n.Value(self.n.dot)) | ||
145 | + net.add_output("_x", "_w", self.n.Variable("_s")) | ||
146 | + label = [] | ||
147 | + for arg, val in argmap.arguments : | ||
148 | + if val.kind == "name" : | ||
149 | + if not net.has_place(val.value) : | ||
150 | + net.add_place(self.n.Place(val.value, status=self.n.entry)) | ||
151 | + net.add_input(val.value, "_c", self.n.Test(self.n.Variable(val.value))) | ||
152 | + label.append(self.n.Variable(val.value)) | ||
153 | + elif val.kind in ("int", "str") : | ||
154 | + label.append(self.n.Value(repr(val.value))) | ||
155 | + elif val.kind == "code" : | ||
156 | + label.append(self.n.Expression(val.value)) | ||
157 | + net.add_place(self.n.Place("call_" + node.name)) | ||
158 | + net.add_output("call_" + node.name, "_c", self.n.Tuple(self.n.dot, | ||
159 | + self.n.Tuple(*label))) | ||
160 | + net.add_place(self.n.Place("ret_" + node.name)) | ||
161 | + net.add_input("ret_" + node.name, "_w", self.n.Tuple(self.n.Variable("_s"), | ||
162 | + self.n.Variable("_r"))) | ||
163 | + return net | ||
164 | + def visit_if (self, node, ctx) : | ||
165 | + pass | ||
166 | + def visit_for (self, node, ctx) : | ||
167 | + pass | ||
168 | + def visit_while (self, node, ctx) : | ||
169 | + pass | ||
170 | + def visit_try (self, node, ctx) : | ||
171 | + pass |
... | @@ -7,10 +7,6 @@ class Parser (BaseParser) : | ... | @@ -7,10 +7,6 @@ class Parser (BaseParser) : |
7 | __compound__ = spec | 7 | __compound__ = spec |
8 | self.__parser__ = MP | 8 | self.__parser__ = MP |
9 | 9 | ||
10 | -def parse (source, spec) : | 10 | +parse = Parser.make_parser() |
11 | - if isinstance(source, str) : | ||
12 | - return Parser(spec).parse(source, "<string>") | ||
13 | - else : | ||
14 | - return Parser(spec).parse(source.read(), getattr(source, "name", "<string>")) | ||
15 | - | ||
16 | Compiler = metaparse.Compiler | 11 | Compiler = metaparse.Compiler |
12 | +node = metaparse.node | ... | ... |
1 | # coding: utf-8 | 1 | # coding: utf-8 |
2 | 2 | ||
3 | -import ast, sys | 3 | +import ast, sys, collections |
4 | import fastidious | 4 | import fastidious |
5 | 5 | ||
6 | class node (object) : | 6 | class node (object) : |
... | @@ -114,26 +114,26 @@ class Nest (object) : | ... | @@ -114,26 +114,26 @@ class Nest (object) : |
114 | return n | 114 | return n |
115 | 115 | ||
116 | class Compiler (object) : | 116 | class Compiler (object) : |
117 | - def visit (self, tree) : | 117 | + def visit (self, tree, *l, **k) : |
118 | if isinstance(tree, node) : | 118 | if isinstance(tree, node) : |
119 | method = getattr(self, "visit_" + tree.tag, self.generic_visit) | 119 | method = getattr(self, "visit_" + tree.tag, self.generic_visit) |
120 | - return method(tree) | 120 | + return method(tree, *l, **k) |
121 | else : | 121 | else : |
122 | return tree | 122 | return tree |
123 | - def generic_visit (self, tree) : | 123 | + def generic_visit (self, tree, *l, **k) : |
124 | sub = {} | 124 | sub = {} |
125 | ret = {tree.tag : sub} | 125 | ret = {tree.tag : sub} |
126 | for name, child in tree : | 126 | for name, child in tree : |
127 | if isinstance(child, list) : | 127 | if isinstance(child, list) : |
128 | sub[name] = [] | 128 | sub[name] = [] |
129 | for c in child : | 129 | for c in child : |
130 | - sub[name].append(self.visit(c)) | 130 | + sub[name].append(self.visit(c, *l, **k)) |
131 | elif isinstance(child, dict) : | 131 | elif isinstance(child, dict) : |
132 | sub[name] = {} | 132 | sub[name] = {} |
133 | for k, v in child.items() : | 133 | for k, v in child.items() : |
134 | - sub[name][k] = self.visit(v) | 134 | + sub[name][k] = self.visit(v, *l, **k) |
135 | else : | 135 | else : |
136 | - sub[name] = self.visit(child) | 136 | + sub[name] = self.visit(child, *l, **k) |
137 | return ret | 137 | return ret |
138 | 138 | ||
139 | class MetaParser (fastidious.Parser) : | 139 | class MetaParser (fastidious.Parser) : |
... | @@ -157,7 +157,6 @@ class MetaParser (fastidious.Parser) : | ... | @@ -157,7 +157,6 @@ class MetaParser (fastidious.Parser) : |
157 | DEDENT <- _ "↤" NL? _ {_drop} | 157 | DEDENT <- _ "↤" NL? _ {_drop} |
158 | NUMBER <- _ ~"[+-]?[0-9]+" {_number} | 158 | NUMBER <- _ ~"[+-]?[0-9]+" {_number} |
159 | NAME <- _ ~"[a-z][a-z0-9_]*"i _ {p_flatten} | 159 | NAME <- _ ~"[a-z][a-z0-9_]*"i _ {p_flatten} |
160 | - atom <- :name / num:NUMBER / :code / :string {_first} | ||
161 | name <- name:NAME {_name} | 160 | name <- name:NAME {_name} |
162 | code <- codec / codeb {_code} | 161 | code <- codec / codeb {_code} |
163 | codec <- LCB (~"([^{}\\\\]|[{}])+" / codec)* RCB {p_flatten} | 162 | codec <- LCB (~"([^{}\\\\]|[{}])+" / codec)* RCB {p_flatten} |
... | @@ -170,14 +169,17 @@ class MetaParser (fastidious.Parser) : | ... | @@ -170,14 +169,17 @@ class MetaParser (fastidious.Parser) : |
170 | def _drop (self, match) : | 169 | def _drop (self, match) : |
171 | return "" | 170 | return "" |
172 | def _number (self, match) : | 171 | def _number (self, match) : |
173 | - return node("const", value=int(self.p_flatten(match)), type="int") | 172 | + return node("const", value=int(self.p_flatten(match)), type="int", |
173 | + _pos=self.pos, _src=self.p_flatten(match)) | ||
174 | def _name (self, match, name) : | 174 | def _name (self, match, name) : |
175 | - return node("const", value=name, type="name") | 175 | + return node("const", value=name, type="name", |
176 | + _pos=self.pos, _src=self.p_flatten(match)) | ||
176 | def _code (self, match) : | 177 | def _code (self, match) : |
177 | - return node("const", value=self.p_flatten(match).strip()[1:-1], type="code") | 178 | + return node("const", value=self.p_flatten(match).strip()[1:-1], type="code", |
179 | + _pos=self.pos, _src=self.p_flatten(match)) | ||
178 | def _string (self, match) : | 180 | def _string (self, match) : |
179 | return node("const", value=ast.literal_eval(self.p_flatten(match).strip()), | 181 | return node("const", value=ast.literal_eval(self.p_flatten(match).strip()), |
180 | - type="str") | 182 | + type="str", _pos=self.pos, _src=self.p_flatten(match)) |
181 | def _tuple (self, match) : | 183 | def _tuple (self, match) : |
182 | lst = [] | 184 | lst = [] |
183 | for m in match : | 185 | for m in match : |
... | @@ -192,11 +194,14 @@ class MetaParser (fastidious.Parser) : | ... | @@ -192,11 +194,14 @@ class MetaParser (fastidious.Parser) : |
192 | if isinstance(v, node) : | 194 | if isinstance(v, node) : |
193 | return v | 195 | return v |
194 | else : | 196 | else : |
195 | - return node(k, value=v) | 197 | + return node(k, value=v, _pos=self.pos, _src=self.p_flatten(match)) |
196 | return self.NoMatch | 198 | return self.NoMatch |
197 | __grammar__ += r""" | 199 | __grammar__ += r""" |
198 | model <- stmt:stmt+ | 200 | model <- stmt:stmt+ |
199 | - stmt <- :call / :block {_first} | 201 | + stmt <- :assign / :call / :block {_first} |
202 | + assign <- name:NAME EQ :expr | ||
203 | + expr <- :atom / :call {_first} | ||
204 | + atom <- :name / num:NUMBER / :code / :string {_first} | ||
200 | call <- name:NAME :args NL | 205 | call <- name:NAME :args NL |
201 | args <- LP ( :arglist )? RP | 206 | args <- LP ( :arglist )? RP |
202 | arglist <- arg (COMMA arg)* {_tuple} | 207 | arglist <- arg (COMMA arg)* {_tuple} |
... | @@ -241,13 +246,17 @@ class MetaParser (fastidious.Parser) : | ... | @@ -241,13 +246,17 @@ class MetaParser (fastidious.Parser) : |
241 | return s | 246 | return s |
242 | def on_model (self, match, stmt) : | 247 | def on_model (self, match, stmt) : |
243 | return node("model", body=self._glue(stmt), _pos=self.pos) | 248 | return node("model", body=self._glue(stmt), _pos=self.pos) |
249 | + def on_assign (self, match, name, expr) : | ||
250 | + return node("assign", target=name, value=expr, | ||
251 | + _pos=self.pos, _src=self.p_flatten(match)) | ||
244 | def on_call (self, match, name, args) : | 252 | def on_call (self, match, name, args) : |
245 | - return node("call", name=name, largs=args.l, kargs=args.k, _pos=self.pos) | 253 | + return node("call", name=name, largs=args.l, kargs=args.k, |
254 | + _pos=self.pos, _src=self.p_flatten(match)) | ||
246 | def on_args (self, match, arglist=None) : | 255 | def on_args (self, match, arglist=None) : |
247 | if arglist is self.NoMatch or not arglist : | 256 | if arglist is self.NoMatch or not arglist : |
248 | arglist = [] | 257 | arglist = [] |
249 | l = [] | 258 | l = [] |
250 | - k = {} | 259 | + k = collections.OrderedDict() |
251 | pos = True | 260 | pos = True |
252 | for arg in arglist : | 261 | for arg in arglist : |
253 | if arg.kw : | 262 | if arg.kw : |
... | @@ -258,15 +267,17 @@ class MetaParser (fastidious.Parser) : | ... | @@ -258,15 +267,17 @@ class MetaParser (fastidious.Parser) : |
258 | else : | 267 | else : |
259 | self.p_parse_error("forbidden positional arg after a keyword arg", | 268 | self.p_parse_error("forbidden positional arg after a keyword arg", |
260 | arg._pos) | 269 | arg._pos) |
261 | - return node("args", l=l, k=k) | 270 | + return node("args", l=l, k=k, _pos=self.pos, _src=self.p_flatten(match)) |
262 | def on_arg (self, match, left, right=None) : | 271 | def on_arg (self, match, left, right=None) : |
263 | if right is None : | 272 | if right is None : |
264 | - return node("arg", kw=False, value=left, _pos=self.pos) | 273 | + return node("arg", kw=False, value=left, |
274 | + _pos=self.pos, _src=self.p_flatten(match)) | ||
265 | elif left.type != "name" : | 275 | elif left.type != "name" : |
266 | self.p_parse_error("invalid parameter %r (of type %s)" | 276 | self.p_parse_error("invalid parameter %r (of type %s)" |
267 | % (left.value, left.type)) | 277 | % (left.value, left.type)) |
268 | else : | 278 | else : |
269 | - return node("arg", kw=True, name=left.value, value=right, _pos=self.pos) | 279 | + return node("arg", kw=True, name=left.value, value=right, |
280 | + _pos=self.pos, _src=self.p_flatten(match)) | ||
270 | def on_block (self, match, name, stmt, args=None, deco=None) : | 281 | def on_block (self, match, name, stmt, args=None, deco=None) : |
271 | if args is self.NoMatch or not args : | 282 | if args is self.NoMatch or not args : |
272 | args = None | 283 | args = None |
... | @@ -275,6 +286,7 @@ class MetaParser (fastidious.Parser) : | ... | @@ -275,6 +286,7 @@ class MetaParser (fastidious.Parser) : |
275 | return self.nest(name, body=self._glue(stmt), args=args, deco=deco, | 286 | return self.nest(name, body=self._glue(stmt), args=args, deco=deco, |
276 | _pos=self.pos) | 287 | _pos=self.pos) |
277 | def on_deco (self, match, name, args=[]) : | 288 | def on_deco (self, match, name, args=[]) : |
278 | - return node("deco", name=name, args=args) | 289 | + return node("deco", name=name, args=args, |
290 | + _pos=self.pos, _src=self.p_flatten(match)) | ||
279 | def __INIT__ (self) : | 291 | def __INIT__ (self) : |
280 | self.nest = Nest(self.__compound__) | 292 | self.nest = Nest(self.__compound__) | ... | ... |
This diff could not be displayed because it is too large.
... | @@ -57,6 +57,12 @@ class PetriNet (object) : | ... | @@ -57,6 +57,12 @@ class PetriNet (object) : |
57 | return self._place[name] | 57 | return self._place[name] |
58 | else : | 58 | else : |
59 | raise ConstraintError("place %r not found" % name) | 59 | raise ConstraintError("place %r not found" % name) |
60 | + def has_place (self, name) : | ||
61 | + return name in self._place | ||
62 | + def has_transition (self, name) : | ||
63 | + return name in self._trans | ||
64 | + def __contains__ (self, name) : | ||
65 | + return name in self._node | ||
60 | def transition (self, name=None) : | 66 | def transition (self, name=None) : |
61 | if name is None : | 67 | if name is None : |
62 | return self._trans.values() | 68 | return self._trans.values() | ... | ... |
zinc/plugins/__init__.py
0 → 100644
1 | +import importlib, types, sys, inspect | ||
2 | + | ||
3 | +from .. import ZINCError | ||
4 | + | ||
5 | +class PluginError (ZINCError) : | ||
6 | + pass | ||
7 | + | ||
8 | +class Plugin (object) : | ||
9 | + modname = "zn" | ||
10 | + plugins = [] | ||
11 | + modules = [] | ||
12 | + def __init__ (self, *extends, conflicts=[], depends=[]) : | ||
13 | + # check conflicts | ||
14 | + for p in conflicts : | ||
15 | + if p in self.plugins : | ||
16 | + raise PluginError("plugin conflicts with %r" % p) | ||
17 | + # load dependencies | ||
18 | + for p in depends : | ||
19 | + try : | ||
20 | + return importlib.import_module(p) | ||
21 | + except : | ||
22 | + try : | ||
23 | + return importlib.import_module("zinc.plugins." + p) | ||
24 | + except : | ||
25 | + raise PluginError("could not load %r from '.' or 'zinc.plugins'" % p) | ||
26 | + # create result module | ||
27 | + if self.modname not in sys.modules : | ||
28 | + self._mod = sys.modules[self.modname] = types.ModuleType(self.modname) | ||
29 | + # load extended modules | ||
30 | + for base in extends : | ||
31 | + if base not in self.modules : | ||
32 | + mod = importlib.import_module(base) | ||
33 | + self._mod.__dict__.update(mod.__dict__) | ||
34 | + self.modules.append(base) | ||
35 | + # record loaded plugin | ||
36 | + stack = inspect.stack() | ||
37 | + caller = inspect.getmodule(stack[1][0]) | ||
38 | + self.plugins.append(caller.__name__) | ||
39 | + def __getattr__ (self, name) : | ||
40 | + return getattr(self._mod, name) | ||
41 | + def __call__ (self, obj) : | ||
42 | + setattr(self._mod, obj.__name__, obj) | ||
43 | + return obj | ||
44 | + @classmethod | ||
45 | + def reset_plugins (cls, name="zn") : | ||
46 | + for p in cls.plugins + cls.modules + [cls.modname] : | ||
47 | + del sys.modules[p] | ||
48 | + cls.modname = name | ||
49 | + cls.plugins = [] | ||
50 | + cls.modules = [] |
zinc/plugins/hello.py
0 → 100644
1 | +"""An example plugin that allows instances of `PetriNet` to say hello. | ||
2 | + | ||
3 | +The source code can be used as a starting example: | ||
4 | +1. Import `zinc.plugins.Plugin` | ||
5 | +2. Create an instance of it, passing: | ||
6 | + * the modules that are extended | ||
7 | + * the conflicts (if any) | ||
8 | + * the dependencies (if any) | ||
9 | +3. This instance will serve as: | ||
10 | + * a decorator for each class or function that should be included | ||
11 | + in the extended module | ||
12 | + * classes or functions to extend are retreived as its attributes | ||
13 | +""" | ||
14 | + | ||
15 | +from . import Plugin | ||
16 | +plug = Plugin("zinc.nets") | ||
17 | + | ||
18 | +@plug | ||
19 | +class PetriNet (plug.PetriNet) : | ||
20 | + def hello (self, name="world") : | ||
21 | + print("hello %s from %s" % (name, self.name)) |
1 | +import enum | ||
1 | from zinc.data import hashable, mset | 2 | from zinc.data import hashable, mset |
2 | 3 | ||
3 | class Token (object) : | 4 | class Token (object) : |
5 | + pass | ||
6 | + | ||
7 | +class CodeToken (Token) : | ||
4 | def __init__ (self, code) : | 8 | def __init__ (self, code) : |
5 | self.code = code | 9 | self.code = code |
6 | def __ast__ (self, place, ctx) : | 10 | def __ast__ (self, place, ctx) : |
... | @@ -37,21 +41,17 @@ class Token (object) : | ... | @@ -37,21 +41,17 @@ class Token (object) : |
37 | def __lt__ (self, other) : | 41 | def __lt__ (self, other) : |
38 | return not self.__ge__(other) | 42 | return not self.__ge__(other) |
39 | 43 | ||
40 | -class BlackToken (Token) : | 44 | +class BlackToken (Token, enum.IntEnum) : |
41 | - def __init__ (self) : | 45 | + DOT = enum.auto() |
42 | - pass | 46 | + |
43 | - def __str__ (self) : | 47 | +dot = BlackToken.DOT |
44 | - return "dot" | 48 | + |
45 | - def __repr__ (self) : | 49 | +class BlackWhiteToken (Token, enum.IntEnum) : |
46 | - return "dot" | 50 | + BLACK = enum.auto() |
47 | - def __new__ (cls) : | 51 | + WHITE = enum.auto() |
48 | - if not hasattr(cls, "dot") : | ||
49 | - cls.dot = object.__new__(cls) | ||
50 | - return cls.dot | ||
51 | - def __hash__ (self) : | ||
52 | - return hash(self.__class__.__name__) | ||
53 | 52 | ||
54 | -dot = BlackToken() | 53 | +black = BlackWhiteToken.BLACK |
54 | +white = BlackWhiteToken.WHITE | ||
55 | 55 | ||
56 | @hashable | 56 | @hashable |
57 | class Marking (dict) : | 57 | class Marking (dict) : | ... | ... |
-
Please register or login to post a comment