Showing
7 changed files
with
274 additions
and
116 deletions
1 | # coding: utf-8 | 1 | # coding: utf-8 |
2 | 2 | ||
3 | import io, re | 3 | import io, re |
4 | +from .. import ParseError | ||
4 | 5 | ||
5 | ## | 6 | ## |
6 | ## insert INDENT/DEDENT | 7 | ## insert INDENT/DEDENT |
... | @@ -30,3 +31,35 @@ def indedent (text, indent="↦", dedent="↤") : | ... | @@ -30,3 +31,35 @@ def indedent (text, indent="↦", dedent="↤") : |
30 | out.write(dedent) | 31 | out.write(dedent) |
31 | stack.pop() | 32 | stack.pop() |
32 | return out.getvalue() | 33 | return out.getvalue() |
34 | + | ||
35 | +## | ||
36 | +## base parser | ||
37 | +## | ||
38 | + | ||
39 | +_errre = re.compile(r"^.*?error at line ([0-9]+), col ([0-9]+):[ \t]*" | ||
40 | + "((.|\n)*)$", re.I|re.A) | ||
41 | + | ||
42 | +class BaseParser (object) : | ||
43 | + def init (self, parser) : | ||
44 | + pass | ||
45 | + def parse (self, source, path) : | ||
46 | + parser = self.__parser__(indedent(source)) | ||
47 | + self.init(parser) | ||
48 | + try : | ||
49 | + do_parse = getattr(parser, parser.__default__, "INPUT") | ||
50 | + result = do_parse() | ||
51 | + if result is parser.NoMatch or parser.p_peek() is not None: | ||
52 | + parser.p_raise() | ||
53 | + return result | ||
54 | + except parser.ParserError as err : | ||
55 | + self._raise(err, path) | ||
56 | + def _raise (self, error, path=None) : | ||
57 | + message = str(error) | ||
58 | + match = _errre.match(message) | ||
59 | + if match : | ||
60 | + raise ParseError(match.group(3), | ||
61 | + lno=int(match.group(1)), | ||
62 | + cno=int(match.group(2)), | ||
63 | + path=path) | ||
64 | + else : | ||
65 | + raise ParseError(message, path=path) | ... | ... |
zinc/io/aspic.py
0 → 100644
1 | +from . import metaparse, BaseParser | ||
2 | + | ||
3 | +class Parser (BaseParser) : | ||
4 | + class __parser__ (metaparse.MetaParser) : | ||
5 | + __compound__ = [["if", "*elif", "?else"], | ||
6 | + ["choose", "+either"], | ||
7 | + ["parallel", "+besides"], | ||
8 | + ["race", "+against"], | ||
9 | + ["retry"], | ||
10 | + ["negate"], | ||
11 | + ["task"]] | ||
12 | + | ||
13 | +def parse (source) : | ||
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/meta.py
0 → 100644
1 | +from . import BaseParser | ||
2 | +from . import metaparse | ||
3 | + | ||
4 | +class Parser (BaseParser) : | ||
5 | + def __init__ (self, spec) : | ||
6 | + class MP (metaparse.MetaParser) : | ||
7 | + __compound__ = spec | ||
8 | + self.__parser__ = MP | ||
9 | + | ||
10 | +def parse (source, spec) : | ||
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>")) |
zinc/io/meta_peg.py
0 → 100644
1 | +# coding: utf-8 | ||
2 | + | ||
3 | +import ast | ||
4 | +import fastidious | ||
5 | + | ||
6 | +class node (object) : | ||
7 | + def __init__ (self, tag, **children) : | ||
8 | + self.__dict__.update(tag=tag, **children) | ||
9 | + self._fields = list(children) | ||
10 | + def __setitem__ (self, key, val) : | ||
11 | + setattr(self, key, val) | ||
12 | + if key not in self._fields : | ||
13 | + self._fields.append(key) | ||
14 | + def __getitem__ (self, key) : | ||
15 | + return getattr(self, key) | ||
16 | + def __iter__ (self) : | ||
17 | + for name in self._fields : | ||
18 | + yield name, self[name] | ||
19 | + def __repr__ (self) : | ||
20 | + return "\n".join(self._repr()) | ||
21 | + def _repr (self, indent=0) : | ||
22 | + TAB = " | " * indent | ||
23 | + if not self._fields : | ||
24 | + yield TAB + self.tag | ||
25 | + else : | ||
26 | + yield TAB + "%s :" % self.tag | ||
27 | + for name, child in self : | ||
28 | + if name.startswith("_") : | ||
29 | + pass | ||
30 | + elif isinstance(child, node) : | ||
31 | + for i, line in enumerate(child._repr(indent + 1)) : | ||
32 | + if i == 0 : | ||
33 | + yield TAB + " + %s = %s" % (name, line.lstrip(" |")) | ||
34 | + else : | ||
35 | + yield line | ||
36 | + elif isinstance(child, list) and child and isinstance(child[0], node) : | ||
37 | + for n, c in enumerate(child) : | ||
38 | + for i, line in enumerate(c._repr(1)) : | ||
39 | + if i == 0 : | ||
40 | + l = line.lstrip("| ") | ||
41 | + yield TAB + " + %s[%s] = %s" % (name, n, l) | ||
42 | + else : | ||
43 | + yield TAB + line | ||
44 | + else : | ||
45 | + yield TAB + " + %s = %r" % (name, child) | ||
46 | + | ||
47 | +class Nest (object) : | ||
48 | + def __init__ (self, blocks) : | ||
49 | + self.pred = {} | ||
50 | + self.succ = {} | ||
51 | + self.rep = {} | ||
52 | + self.opt = {} | ||
53 | + self.first = {} | ||
54 | + self.last = {} | ||
55 | + for spec in blocks : | ||
56 | + _spec = [] | ||
57 | + first = True | ||
58 | + for name in spec : | ||
59 | + _name = name.lstrip("*?") | ||
60 | + self.rep[_name] = name.startswith("*") or name.startswith("+") | ||
61 | + self.opt[_name] = name.startswith("?") or name.startswith("*") | ||
62 | + self.succ[_name] = set() | ||
63 | + self.pred[_name] = set() | ||
64 | + _spec.append(_name) | ||
65 | + self.first[_name] = first | ||
66 | + first = first and self.opt[_name] | ||
67 | + last = True | ||
68 | + for name in reversed(_spec) : | ||
69 | + self.last[name] = last | ||
70 | + last = last and self.opt[name] | ||
71 | + for one, two in zip(_spec, _spec[1:]) : | ||
72 | + if self.rep[two] : | ||
73 | + self.pred[two].update({one, two} | self.pred[one]) | ||
74 | + elif self.opt[two] : | ||
75 | + self.pred[two].update({one} | self.pred[one]) | ||
76 | + else : | ||
77 | + self.pred[two].add(one) | ||
78 | + for two, preds in self.pred.items() : | ||
79 | + for one in preds : | ||
80 | + self.succ[one].add(two) | ||
81 | + | ||
82 | +class MetaParser (fastidious.Parser) : | ||
83 | + __compound__ = [] | ||
84 | + __default__ = "INPUT" | ||
85 | + __grammar__ = r""" | ||
86 | + INPUT <- NL? :model {@model} | ||
87 | + NL <- ~"([\t ]*(#.*)?\n)+" {_drop} | ||
88 | + _ <- ~"[\t ]+"? {_drop} | ||
89 | + COLON <- _ ":" _ {_drop} | ||
90 | + COMMA <- _ "," _ {p_flatten} | ||
91 | + AT <- _ "@" _ {p_flatten} | ||
92 | + EQ <- _ "=" _ {p_flatten} | ||
93 | + LP <- _ "(" _ {p_flatten} | ||
94 | + RP <- _ ")" _ {p_flatten} | ||
95 | + LSB <- _ "[" _ {p_flatten} | ||
96 | + RSB <- _ "]" _ {p_flatten} | ||
97 | + LCB <- _ "{" _ {p_flatten} | ||
98 | + RCB <- _ "}" _ {p_flatten} | ||
99 | + INDENT <- _ "↦" NL? _ {_drop} | ||
100 | + DEDENT <- _ "↤" NL? _ {_drop} | ||
101 | + NUMBER <- _ ~"[+-]?[0-9]+" {_number} | ||
102 | + NAME <- _ ~"[a-z][a-z0-9_]*"i _ {p_flatten} | ||
103 | + atom <- :name / num:NUMBER / :code / :string {_first} | ||
104 | + name <- name:NAME {_name} | ||
105 | + code <- codec / codeb {_code} | ||
106 | + codec <- LCB (~"([^{}\\\\]|[{}])+" / codec)* RCB {p_flatten} | ||
107 | + codeb <- LSB (~"([^\\[\\]\\\\]|[\\[\\]])+" / codeb)* RSB {p_flatten} | ||
108 | + string <- _ ( ~"'{3}.*?'{3}"s | ||
109 | + / ~'"{3}.*?"{3}'s | ||
110 | + / ~"'([^'\\\\]|\\.)*'" | ||
111 | + / ~'"([^"\\\\]|\\.)*"' ) _ {_string} | ||
112 | + """ | ||
113 | + def _drop (self, match) : | ||
114 | + return "" | ||
115 | + def _number (self, match) : | ||
116 | + return node("const", value=int(self.p_flatten(match)), type="int") | ||
117 | + def _name (self, match, name) : | ||
118 | + return node("const", value=name, type="name") | ||
119 | + def _code (self, match) : | ||
120 | + return node("const", value=self.p_flatten(match).strip()[1:-1], type="code") | ||
121 | + def _string (self, match) : | ||
122 | + return node("const", value=ast.literal_eval(self.p_flatten(match).strip()), | ||
123 | + type="str") | ||
124 | + def _tuple (self, match) : | ||
125 | + lst = [] | ||
126 | + for m in match : | ||
127 | + if isinstance(m, list) : | ||
128 | + lst.extend(i[1] for i in m) | ||
129 | + else : | ||
130 | + lst.append(m) | ||
131 | + return tuple(lst) | ||
132 | + def _first (self, match, **args) : | ||
133 | + for k, v in args.items() : | ||
134 | + if v is not self.NoMatch : | ||
135 | + if isinstance(v, node) : | ||
136 | + return v | ||
137 | + else : | ||
138 | + return node(k, value=v) | ||
139 | + return self.NoMatch | ||
140 | + __grammar__ += r""" | ||
141 | + model <- stmt:stmt+ | ||
142 | + stmt <- :call / :block {_first} | ||
143 | + call <- name:NAME :args NL | ||
144 | + args <- LP ( pos:posargs | ||
145 | + / kw:kwargs | ||
146 | + / pos:posargs COMMA kw:kwargs )? RP | ||
147 | + posargs <- atom (COMMA atom)* {_tuple} | ||
148 | + kwargs <- kwa (COMMA kwa) {_tuple} | ||
149 | + kwa <- name:NAME "EQ" :atom | ||
150 | + block <- deco:deco? name:NAME args:args? COLON NL INDENT stmt:stmt+ DEDENT | ||
151 | + deco <- AT name:NAME args:args? NL | ||
152 | + """ | ||
153 | + def _glue (self, stmt) : | ||
154 | + n = self.nest | ||
155 | + s = [] | ||
156 | + l = None | ||
157 | + for child in stmt : | ||
158 | + if child.tag == "call" : | ||
159 | + s.append(child) | ||
160 | + l = None | ||
161 | + elif n.pred[child.tag] : | ||
162 | + if l in n.pred[child.tag] : | ||
163 | + s[-1][child.tag] = child | ||
164 | + elif l and n.last[l] and n.first[child.tag] : | ||
165 | + l = child.tag | ||
166 | + s.append(child) | ||
167 | + elif l : | ||
168 | + self.p_parse_error("invalid block %r after %r" % (child.tag, l), | ||
169 | + child._pos) | ||
170 | + else : | ||
171 | + self.p_parse_error("unexpected block %r" % child.tag, | ||
172 | + child._pos) | ||
173 | + else : | ||
174 | + if l and not n.last[l] : | ||
175 | + self.p_parse_error("invalid block %r after %r" % (child.tag, l), | ||
176 | + child._pos) | ||
177 | + if not n.first[child.tag] : | ||
178 | + self.p_parse_error("unexpected block %r" % child.tag, | ||
179 | + child._pos) | ||
180 | + l = child.tag | ||
181 | + s.append(child) | ||
182 | + return s | ||
183 | + def on_model (self, match, stmt) : | ||
184 | + return node("model", body=self._glue(stmt), _pos=self.pos) | ||
185 | + def on_call (self, match, name, args) : | ||
186 | + return node("call", name=name, largs=args.l, kargs=args.k, _pos=self.pos) | ||
187 | + def on_args (self, match, pos=None, kw=None) : | ||
188 | + if pos is self.NoMatch or not pos : | ||
189 | + pos = [] | ||
190 | + if kw is self.NoMatch or not kw : | ||
191 | + kw = {} | ||
192 | + return node("args", l=list(pos), k=dict(kw)) | ||
193 | + def on_kwa (self, name, atom) : | ||
194 | + return name, atom | ||
195 | + def on_block (self, match, name, stmt, args=None, deco=None) : | ||
196 | + if args is self.NoMatch or not args : | ||
197 | + args = None | ||
198 | + if deco is self.NoMatch or not deco : | ||
199 | + deco = None | ||
200 | + return node(name, body=self._glue(stmt), args=args, deco=deco, _pos=self.pos) | ||
201 | + def on_deco (self, match, name, args=[]) : | ||
202 | + return node("deco", name=name, args=args) | ||
203 | + def __INIT__ (self) : | ||
204 | + self.nest = Nest(self.__compound__) |
zinc/io/metaparse.py
0 → 100644
This diff could not be displayed because it is too large.
1 | -import ast, functools | 1 | +from .. import nets |
2 | +from . import znparse, BaseParser | ||
2 | 3 | ||
3 | -from .. import nets, ParseError, ZINCError | 4 | +class Parser (BaseParser) : |
4 | -from . import znparse, indedent | 5 | + __parser__ = znparse.ZnParser |
5 | -from ..data import flatten | ||
6 | - | ||
7 | -class _TupleParser (object) : | ||
8 | - def __init__ (self) : | ||
9 | - self.p = znparse.znParser() | ||
10 | - def parse (self, source) : | ||
11 | - return self.p.parse(source, "tailtuple", semantics=self) | ||
12 | - def tuple (self, st) : | ||
13 | - def _tuple (l) : | ||
14 | - if isinstance(l, list) : | ||
15 | - return tuple(_tuple(x) for x in l) | ||
16 | - else : | ||
17 | - return l | ||
18 | - return _tuple(st[1][::2]) | ||
19 | - def tailtuple (self, st) : | ||
20 | - return st | ||
21 | - def code (self, st) : | ||
22 | - return "".join(flatten(st))[1:-1].strip() | ||
23 | - def string (self, st) : | ||
24 | - return "".join(flatten(st)) | ||
25 | - def text (self, st) : | ||
26 | - if st.name : | ||
27 | - return st.name | ||
28 | - elif st.string : | ||
29 | - return ast.literal_eval(st.string) | ||
30 | - elif st.code : | ||
31 | - return st.code | ||
32 | - | ||
33 | -class _Parser (_TupleParser) : | ||
34 | - def spec (self, st) : | ||
35 | - try : | ||
36 | - net = self.n.PetriNet(st.net, st.lang) | ||
37 | - except ZINCError as err : | ||
38 | - raise ParseError(str(err), None, None, self._p) | ||
39 | - for lvl, decl in st.declare : | ||
40 | - net.declare(decl, lvl) | ||
41 | - for node, *rest in st.nodes : | ||
42 | - if isinstance(node, self.n.Place) : | ||
43 | - try : | ||
44 | - net.add_place(node) | ||
45 | - except ZINCError as err : | ||
46 | - raise ParseError(err, rest[0], None, self._p) | ||
47 | - else : | ||
48 | - lineno, inputs, outputs = rest | ||
49 | - try : | ||
50 | - net.add_transition(node) | ||
51 | - except ZINCError as err : | ||
52 | - raise ParseError(err, lineno, None, self._p) | ||
53 | - for place, label in inputs.items() : | ||
54 | - try : | ||
55 | - if len(label) == 1 : | ||
56 | - net.add_input(place, node.name, label[0]) | ||
57 | - else : | ||
58 | - net.add_input(place, node.name, self.n.MultiArc(*label)) | ||
59 | - except ZINCError as err : | ||
60 | - raise ParseError(err, self._l[place, node.name], None, self._p) | ||
61 | - for place, label in outputs.items() : | ||
62 | - try : | ||
63 | - if len(label) == 1 : | ||
64 | - net.add_output(place, node.name, label[0]) | ||
65 | - else : | ||
66 | - net.add_output(place, node.name, self.n.MultiArc(*label)) | ||
67 | - except ZINCError as err : | ||
68 | - raise ParseError(err, self._l[node.name, place], None, self._p) | ||
69 | - return net | ||
70 | - def _mkarc (self, st) : | ||
71 | - _arc = {"val" : self.n.Value, | ||
72 | - "var" : self.n.Variable, | ||
73 | - "expr" : self.n.Expression, | ||
74 | - "flush" : self.n.Flush, | ||
75 | - "flush+" : functools.partial(self.n.Flush, notempty=True), | ||
76 | - "fill" : self.n.Fill} | ||
77 | - def mktuple (kind, label) : | ||
78 | - if isinstance(kind, tuple) : | ||
79 | - if not (isinstance(label, tuple) and len(kind) == len(label)) : | ||
80 | - raise ParseError("unmatched tuples %r and %r" % (kind, label), | ||
81 | - st.parseinfo.line+1, None, self._p) | ||
82 | - return self.n.Tuple(*(mktuple(k, l) for k, l in zip(kind, label))) | ||
83 | - else : | ||
84 | - return _arc[kind](label) | ||
85 | - if isinstance(st.kind, tuple) : | ||
86 | - try : | ||
87 | - lbl = self.t.parse(st.label) | ||
88 | - except Exception as err : | ||
89 | - raise ParseError(str(err), st.parseinfo.line+1, None, self._p) | ||
90 | - label = mktuple(st.kind, lbl) | ||
91 | - elif st.kind in _arc : | ||
92 | - label = _arc[st.kind](st.label.strip()) | ||
93 | - if st.mod : | ||
94 | - if st.mod.kind == "?" : | ||
95 | - label = self.n.Test(label) | ||
96 | - elif st.mod.kind == "!" : | ||
97 | - label = self.n.Inhibitor(label, st.mod.guard) | ||
98 | - return st.way == "<", st.place, label, st.parseinfo.line+1 | ||
99 | - def arc (self, st) : | ||
100 | - try : | ||
101 | - return self._mkarc(st) | ||
102 | - except ZINCError as err : | ||
103 | - raise ParseError(str(err), st.parseinfo.line+1, None, self._p) | ||
104 | - | ||
105 | -class Parser (object) : | ||
106 | def __init__ (self, module=nets) : | 6 | def __init__ (self, module=nets) : |
107 | self.n = module | 7 | self.n = module |
108 | - def parse (self, source, path) : | 8 | + def init (self, parser) : |
109 | - parser = znparse.ZnParser(indedent(source)) | ||
110 | parser.n = self.n | 9 | parser.n = self.n |
111 | - try : | ||
112 | - result = parser.INPUT() | ||
113 | - if result is parser.NoMatch or parser.p_peek() is not None: | ||
114 | - parser.p_raise() | ||
115 | - return result | ||
116 | - except parser.ParserError as err : | ||
117 | - raise | ||
118 | - except : | ||
119 | - raise | ||
120 | 10 | ||
121 | def loads (source, module=nets) : | 11 | def loads (source, module=nets) : |
122 | return Parser(module).parse(source, "<string>") | 12 | return Parser(module).parse(source, "<string>") | ... | ... |
-
Please register or login to post a comment