• R/O
  • HTTP
  • SSH
  • HTTPS

Commit

Tags
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Nix flake for RPython interpreters


Commit MetaInfo

Revisão7ec0ccb963f6940059592ce24d7c6bc667fd2718 (tree)
Hora2024-04-30 15:44:34
AutorCorbin <cds@corb...>
CommiterCorbin

Mensagem de Log

regiux: Split parser.

Mudança Sumário

Diff

--- a/regiux/main.py
+++ b/regiux/main.py
@@ -1,360 +1,6 @@
11 import sys
22
3-from rpython.rlib.objectmodel import we_are_translated
4-from rpython.rlib.rsre import rsre_core
5-from rpython.rlib.rsre.rpy import get_code
6-
7-import rply
8-from rply.token import BaseBox
9-
10-lg = rply.LexerGenerator()
11-
12-KEYWORDS = "IF THEN ELSE ASSERT WITH LET REC INHERIT OR IN".split()
13-for kw in KEYWORDS: lg.add(kw, kw.lower())
14-
15-lg.add("ELLIPSIS", "\.\.\.")
16-lg.add("EQ", "\=\=")
17-lg.add("NEQ", "\!\=")
18-lg.add("LEQ", "\<\=")
19-lg.add("LE", "\<")
20-lg.add("GEQ", "\>\=")
21-lg.add("GE", "\>")
22-lg.add("AND", "\&\&")
23-lg.add("OR_OP", "\|\|")
24-lg.add("IMPL", "\-\>")
25-lg.add("UPDATE", "\/\/")
26-lg.add("CONCAT", "\+\+")
27-lg.add("PLUS", "\+")
28-lg.add("MINUS", "-")
29-lg.add("MUL", "\*")
30-lg.add("DIV", "\/")
31-lg.add("NOT", "!")
32-lg.add("HAS", "\?")
33-
34-lg.add("COLON", ":")
35-lg.add("SEMI", ";")
36-lg.add("OPEN_BRACE", "\{")
37-lg.add("CLOSE_BRACE", "\}")
38-lg.add("OPEN_BRACK", "\[")
39-lg.add("CLOSE_BRACK", "\]")
40-lg.add("OPEN_PAREN", "\(")
41-lg.add("CLOSE_PAREN", "\)")
42-lg.add("DOT", "\.")
43-lg.add("COMMA", ",")
44-lg.add("AT", "@")
45-lg.add("EQUALS", "\=")
46-
47-PATH_CHAR = "[a-zA-Z0-9\.\_\-\+]"
48-lg.add("URI", "[a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+")
49-lg.add("ID", "[a-zA-Z\_][a-zA-Z0-9\_\'\-]*")
50-lg.add("INT", "[0-9]+")
51-lg.add("FLOAT", "(([1-9][0-9]*\.[0-9]*)|(0?\.[0-9]+))([Ee][+-]?[0-9]+)?")
52-lg.add("PATH_CHAR", PATH_CHAR)
53-lg.add("PATH", "{0}*(\/{0}+)+\/?".format(PATH_CHAR))
54-lg.add("PATH_SEG", "{0}*\/".format(PATH_CHAR))
55-lg.add("HPATH", "\~(\/{0}+)+\/?".format(PATH_CHAR))
56-lg.add("HPATH_START", "\~\/")
57-lg.add("SPATH", "\<{0}+(\/{0}+)*\>".format(PATH_CHAR))
58-
59-lg.ignore("[ \t\r\n]+")
60-lg.ignore("#[^\r\n]*")
61-lg.ignore("\/\*([^*]|\*+[^*/])*\*+\/")
62-
63-lexer = lg.build()
64-
65-# Syntactic classes:
66-# * exprs
67-# * formal params
68-# * attrs
69-
70-class FormalBox(BaseBox):
71- def __init__(self, name, default):
72- self.name = name
73- self.default = default
74- def pretty(self):
75- if self.default is None:
76- return self.name
77- else:
78- return "%s ? %s" % (self.name, self.default.pretty())
79-
80-class BindsBox(BaseBox):
81- def __init__(self, binds): self.binds = binds
82- def pretty(self):
83- binds = [bind.pretty() for bind in self.binds]
84- return "{ %s }" % " ".join(binds)
85- def getbinds(self): return self.binds
86-
87-class BindExprBox(BaseBox):
88- def __init__(self, path, expr):
89- self.path = path
90- self.expr = expr
91- def pretty(self): return "%s = %s;" % (self.path.pretty(), self.expr.pretty())
92-
93-class BindInheritBox(BaseBox):
94- def __init__(self, attrs, scope):
95- self.attrs = attrs
96- self.scope = scope
97- def pretty(self):
98- if self.scope:
99- return "inherit (%s) %s;" % (self.scope.pretty(), self.attrs.pretty())
100- else: return "inherit %s;" % self.attrs.pretty()
101-
102-class FormalsBox(BaseBox):
103- def __init__(self, params, hasEllipsis):
104- self.params = params
105- self.hasEllipsis = hasEllipsis
106- def pretty(self):
107- params = [param.pretty() for param in self.params]
108- if self.hasEllipsis: params.append("...")
109- return "{ %s }" % ", ".join(params)
110- def getparams(self): return self.params
111- def getellipsis(self): return self.hasEllipsis
112-
113-class VarBox(BaseBox):
114- def __init__(self, name): self.name = name
115- def pretty(self): return self.name
116-
117-class AttrPathBox(BaseBox):
118- def __init__(self, attrs): self.attrs = attrs
119- def pretty(self): return ".".join([attr.pretty() for attr in self.attrs])
120- def getattrs(self): return self.attrs
121-
122-class AttrsBox(BaseBox):
123- def __init__(self, attrs): self.attrs = attrs
124- def pretty(self): return " ".join([attr.pretty() for attr in self.attrs])
125- def getattrs(self): return self.attrs
126-
127-class ListBox(BaseBox):
128- def __init__(self, exprs): self.exprs = exprs
129- def pretty(self):
130- return "[ %s ]" % " ".join([expr.pretty() for expr in self.exprs])
131- def getexprs(self): return self.exprs
132-
133-class IntBox(BaseBox):
134- def __init__(self, value): self.value = value
135- def pretty(self): return str(self.value)
136-
137-class StrBox(BaseBox):
138- def __init__(self, value): self.value = value
139- def pretty(self): return '"%s"' % self.value
140-
141-class ExprUnaryBox(BaseBox):
142- def __init__(self, expr, op):
143- self.expr = expr
144- self.op = op.getstr()
145- def pretty(self):
146- return "(%s%s)" % (self.op, self.expr.pretty())
147-
148-class SelectBox(BaseBox):
149- def __init__(self, expr, path, alt):
150- self.expr = expr
151- self.path = path
152- self.alt = alt
153- def pretty(self):
154- if self.alt is None:
155- return "%s.%s" % (self.expr.pretty(), self.path.pretty())
156- else:
157- return "%s.%s or %s" % (self.expr.pretty(), self.path.pretty(), self.alt.pretty())
158-
159-class ExprBinaryBox(BaseBox):
160- def __init__(self, left, right, op):
161- self.left = left
162- self.right = right
163- self.op = op.getstr()
164- def pretty(self):
165- return "(%s %s %s)" % (self.left.pretty(), self.op, self.right.pretty())
166-
167-class HasBox(BaseBox):
168- def __init__(self, value, path):
169- self.value = value
170- self.path = path
171- def pretty(self):
172- return "(%s ? %s)" % (self.value.pretty(), self.path.pretty())
173-
174-class AssertBox(BaseBox):
175- def __init__(self, cond, expr):
176- self.cond = cond
177- self.expr = expr
178- def pretty(self): return "assert %s; %s" % (self.cond.pretty(), self.expr.pretty())
179-
180-class WithBox(BaseBox):
181- def __init__(self, scope, expr):
182- self.scope = scope
183- self.expr = expr
184- def pretty(self): return "with %s; %s" % (self.scope.pretty(), self.expr.pretty())
185-
186-class LambdaBox(BaseBox):
187- def __init__(self, binding, params, body):
188- self.binding = binding
189- self.params = params
190- self.body = body
191- def pretty(self):
192- body = self.body.pretty()
193- if self.binding and self.params:
194- return "%s@%s: %s" % (self.binding.pretty(), self.params.pretty(), body)
195- elif self.binding: return "%s: %s" % (self.binding.pretty(), body)
196- elif self.params: return "%s: %s" % (self.params.pretty(), body)
197- else: return "_: " + body
198-
199-class AppBox(BaseBox):
200- def __init__(self, func, arg):
201- self.func = func
202- self.arg = arg
203- def pretty(self): return "(%s) (%s)" % (self.func.pretty(), self.arg.pretty())
204-
205-class IfBox(BaseBox):
206- def __init__(self, cond, seq, alt):
207- self.cond = cond
208- self.seq = seq
209- self.alt = alt
210- def pretty(self):
211- return "if %s then %s else %s" % (
212- self.cond.pretty(), self.seq.pretty(), self.alt.pretty())
213-
214-pg = rply.ParserGenerator(KEYWORDS + [
215- "ID", "INT", "SPATH", "URI",
216- "AND", "IMPL", "OR_OP",
217- "EQ", "NEQ", "LE", "GE", "LEQ", "GEQ", "HAS",
218- "CONCAT", "UPDATE",
219- "DIV", "MINUS", "MUL", "PLUS",
220- "NEGATE", "NOT",
221- "COLON", "SEMI", "DOT", "COMMA", "AT", "EQUALS", "ELLIPSIS",
222- "OPEN_BRACE", "CLOSE_BRACE", "OPEN_BRACK", "CLOSE_BRACK", "OPEN_PAREN", "CLOSE_PAREN",
223-], precedence=[
224- ("right", ["IMPL"]),
225- ("left", ["OR_OP"]),
226- ("left", ["AND"]),
227- ("nonassoc", ["EQ", "NEQ"]),
228- ("nonassoc", ["LE", "GE", "LEQ", "GEQ"]),
229- ("right", ["UPDATE"]),
230- ("left", ["NOT"]),
231- ("left", ["PLUS", "MINUS"]),
232- ("left", ["MUL", "DIV"]),
233- ("right", ["CONCAT"]),
234- ("nonassoc", ["HAS"]),
235- ("nonassoc", ["NEGATE"]),
236-])
237-
238-class ParseError(Exception):
239- def __init__(self, token): self.token = token
240-
241-@pg.error
242-def parseError(token): raise ParseError(token)
243-
244-def constRule(rule, pb): pg.production(rule)(lambda _: pb)
245-def enclosedRule(rule, i): pg.production(rule)(lambda p: p[i])
246-def precRule(sup, sub): enclosedRule("expr%s : expr%s" % (sup, sub), 0)
247-SPINE = "", "_function", "_if", "_op", "_app", "_select", "_simple"
248-for sup, sub in zip(SPINE, SPINE[1:]): precRule(sup, sub)
249-
250-@pg.production("expr_function : ID COLON expr_function")
251-def exprLambda(p): return LambdaBox(VarBox(p[0].getstr()), None, p[2])
252-
253-@pg.production("expr_function : OPEN_BRACE formals CLOSE_BRACE COLON expr_function")
254-def exprLambda(p): return LambdaBox(None, p[1], p[4])
255-
256-@pg.production("expr_function : OPEN_BRACE formals CLOSE_BRACE AT ID COLON expr_function")
257-def exprLambda(p): return LambdaBox(VarBox(p[4].getstr()), p[1], p[6])
258-
259-@pg.production("expr_function : ID AT OPEN_BRACE formals CLOSE_BRACE COLON expr_function")
260-def exprLambda(p): return LambdaBox(VarBox(p[0].getstr()), p[3], p[6])
261-
262-@pg.production("expr_function : ASSERT expr SEMI expr_function")
263-def exprAssert(p): return AssertBox(p[1], p[3])
264-
265-@pg.production("expr_function : WITH expr SEMI expr_function")
266-def exprWith(p): return WithBox(p[1], p[3])
267-
268-@pg.production("expr_if : IF expr THEN expr ELSE expr")
269-def exprIf(p): return IfBox(p[1], p[3], p[5])
270-
271-@pg.production("expr_op : NEGATE expr_op | NOT expr_op")
272-def exprUnary(p): return ExprUnaryBox(p[1], p[0])
273-
274-@pg.production("expr_op : expr_op AND expr_op")
275-@pg.production("expr_op : expr_op CONCAT expr_op")
276-@pg.production("expr_op : expr_op DIV expr_op")
277-@pg.production("expr_op : expr_op EQ expr_op")
278-@pg.production("expr_op : expr_op GE expr_op")
279-@pg.production("expr_op : expr_op GEQ expr_op")
280-@pg.production("expr_op : expr_op IMPL expr_op")
281-@pg.production("expr_op : expr_op LE expr_op")
282-@pg.production("expr_op : expr_op LEQ expr_op")
283-@pg.production("expr_op : expr_op MINUS expr_op")
284-@pg.production("expr_op : expr_op MUL expr_op")
285-@pg.production("expr_op : expr_op NEQ expr_op")
286-@pg.production("expr_op : expr_op OR_OP expr_op")
287-@pg.production("expr_op : expr_op PLUS expr_op")
288-@pg.production("expr_op : expr_op UPDATE expr_op")
289-def exprBinary(p): return ExprBinaryBox(p[0], p[2], p[1])
290-
291-@pg.production("expr_op : expr_op HAS attrpath")
292-def exprHas(p): return HasBox(p[0], p[2])
293-
294-@pg.production("expr_app : expr_app expr_select")
295-def exprApp(p): return AppBox(p[0], p[1])
296-
297-@pg.production("expr_select : expr_simple DOT attrpath | expr_simple DOT attrpath OR expr_select")
298-def exprSelect(p): return SelectBox(p[0], p[2], p[4] if len(p) == 5 else None)
299-
300-@pg.production("expr_simple : ID")
301-def exprSimpleId(p): return VarBox(p[0].getstr())
302-
303-@pg.production("expr_simple : INT")
304-def exprSimpleInt(p): return IntBox(int(p[0].getstr()))
305-
306-@pg.production("expr_simple : URI")
307-def exprURI(p): return StrBox(p[0].getstr())
308-
309-enclosedRule("expr_simple : OPEN_BRACE binds CLOSE_BRACE", 1)
310-enclosedRule("expr_simple : OPEN_PAREN expr CLOSE_PAREN", 1)
311-enclosedRule("expr_simple : OPEN_BRACK expr_list CLOSE_BRACK", 1)
312-constRule("expr_list :", ListBox([]))
313-
314-@pg.production("expr_list : expr_list expr_select")
315-def exprListCons(p): return ListBox(p[0].getexprs() + [p[1]])
316-
317-@pg.production("binds : binds attrpath EQUALS expr SEMI")
318-def bindsExpr(p): return BindsBox(p[0].getbinds() + [BindExprBox(p[1], p[3])])
319-
320-@pg.production("binds : binds INHERIT attrs SEMI")
321-def bindsInherit(p):
322- return BindsBox(p[0].getbinds() + [BindInheritBox(p[2], None)])
323-
324-@pg.production("binds : binds INHERIT OPEN_PAREN expr CLOSE_PAREN attrs SEMI")
325-def bindsInherit(p):
326- return BindsBox(p[0].getbinds() + [BindInheritBox(p[5], p[3])])
327-
328-constRule("binds :", BindsBox([]))
329-
330-@pg.production("formals : formal COMMA formals")
331-def formalsComma(p):
332- return FormalsBox([p[0]] + p[2].getparams(), p[2].getellipsis())
333-
334-@pg.production("formals : formal")
335-def formalsFormal(p): return FormalsBox([p[0]], False)
336-
337-constRule("formals :", FormalsBox([], False))
338-constRule("formals : ELLIPSIS", FormalsBox([], True))
339-
340-@pg.production("formal : ID | ID HAS expr")
341-def formalId(p): return FormalBox(p[0].getstr(), p[2] if len(p) > 1 else None)
342-
343-@pg.production("attrs : attrs attr")
344-def attrsAttr(p): return AttrsBox(p[0].getattrs() + [p[1]])
345-
346-constRule("attrs :", AttrsBox([]))
347-
348-@pg.production("attrpath : attrpath DOT attr")
349-def attrpathNil(p): return AttrPathBox(p[0].getattrs() + [p[2]])
350-
351-@pg.production("attrpath : attr")
352-def attrpathCons(p): return AttrPathBox(p)
353-
354-@pg.production("attr : ID")
355-def attrId(p): return StrBox(p[0].getstr())
356-
357-parser = pg.build()
3+from parser import parse, ParseError
3584
3595 def entryPoint(argv):
3606 if len(argv) < 2:
@@ -363,8 +9,7 @@ def entryPoint(argv):
3639 path = argv[1]
36410 with open(path, "rb") as handle: expr = handle.read()
36511 try:
366- ast = parser.parse(lexer.lex(expr))
367- print "Success"
12+ ast = parse(expr)
36813 print "AST:", ast
36914 print ast.pretty()
37015 return 0
@@ -380,5 +25,3 @@ def entryPoint(argv):
38025 def target(*args): return entryPoint, None
38126
38227 if __name__ == "__main__": entryPoint(sys.argv)
383-
384-# 456 + 293 = 749
--- /dev/null
+++ b/regiux/parser.py
@@ -0,0 +1,358 @@
1+import rply
2+from rply.token import BaseBox
3+
4+# A basic Nix parser ported from CppNix.
5+# LoC target:
6+# 456 (parser.y) + 293 (lexer.l) = 749
7+
8+lg = rply.LexerGenerator()
9+
10+KEYWORDS = "IF THEN ELSE ASSERT WITH LET REC INHERIT OR IN".split()
11+for kw in KEYWORDS: lg.add(kw, kw.lower())
12+
13+lg.add("ELLIPSIS", "\.\.\.")
14+lg.add("EQ", "\=\=")
15+lg.add("NEQ", "\!\=")
16+lg.add("LEQ", "\<\=")
17+lg.add("LE", "\<")
18+lg.add("GEQ", "\>\=")
19+lg.add("GE", "\>")
20+lg.add("AND", "\&\&")
21+lg.add("OR_OP", "\|\|")
22+lg.add("IMPL", "\-\>")
23+lg.add("UPDATE", "\/\/")
24+lg.add("CONCAT", "\+\+")
25+lg.add("PLUS", "\+")
26+lg.add("MINUS", "-")
27+lg.add("MUL", "\*")
28+lg.add("DIV", "\/")
29+lg.add("NOT", "!")
30+lg.add("HAS", "\?")
31+
32+lg.add("COLON", ":")
33+lg.add("SEMI", ";")
34+lg.add("OPEN_BRACE", "\{")
35+lg.add("CLOSE_BRACE", "\}")
36+lg.add("OPEN_BRACK", "\[")
37+lg.add("CLOSE_BRACK", "\]")
38+lg.add("OPEN_PAREN", "\(")
39+lg.add("CLOSE_PAREN", "\)")
40+lg.add("DOT", "\.")
41+lg.add("COMMA", ",")
42+lg.add("AT", "@")
43+lg.add("EQUALS", "\=")
44+
45+PATH_CHAR = "[a-zA-Z0-9\.\_\-\+]"
46+lg.add("URI", "[a-zA-Z][a-zA-Z0-9\+\-\.]*\:[a-zA-Z0-9\%\/\?\:\@\&\=\+\$\,\-\_\.\!\~\*\']+")
47+lg.add("ID", "[a-zA-Z\_][a-zA-Z0-9\_\'\-]*")
48+lg.add("INT", "[0-9]+")
49+lg.add("FLOAT", "(([1-9][0-9]*\.[0-9]*)|(0?\.[0-9]+))([Ee][+-]?[0-9]+)?")
50+lg.add("PATH_CHAR", PATH_CHAR)
51+lg.add("PATH", "{0}*(\/{0}+)+\/?".format(PATH_CHAR))
52+lg.add("PATH_SEG", "{0}*\/".format(PATH_CHAR))
53+lg.add("HPATH", "\~(\/{0}+)+\/?".format(PATH_CHAR))
54+lg.add("HPATH_START", "\~\/")
55+lg.add("SPATH", "\<{0}+(\/{0}+)*\>".format(PATH_CHAR))
56+
57+lg.ignore("[ \t\r\n]+")
58+lg.ignore("#[^\r\n]*")
59+lg.ignore("\/\*([^*]|\*+[^*/])*\*+\/")
60+
61+lexer = lg.build()
62+
63+# Syntactic classes:
64+# * exprs
65+# * formal params
66+# * attrs
67+
68+class FormalBox(BaseBox):
69+ def __init__(self, name, default):
70+ self.name = name
71+ self.default = default
72+ def pretty(self):
73+ if self.default is None:
74+ return self.name
75+ else:
76+ return "%s ? %s" % (self.name, self.default.pretty())
77+
78+class BindsBox(BaseBox):
79+ def __init__(self, binds): self.binds = binds
80+ def pretty(self):
81+ binds = [bind.pretty() for bind in self.binds]
82+ return "{ %s }" % " ".join(binds)
83+ def getbinds(self): return self.binds
84+
85+class BindExprBox(BaseBox):
86+ def __init__(self, path, expr):
87+ self.path = path
88+ self.expr = expr
89+ def pretty(self): return "%s = %s;" % (self.path.pretty(), self.expr.pretty())
90+
91+class BindInheritBox(BaseBox):
92+ def __init__(self, attrs, scope):
93+ self.attrs = attrs
94+ self.scope = scope
95+ def pretty(self):
96+ if self.scope:
97+ return "inherit (%s) %s;" % (self.scope.pretty(), self.attrs.pretty())
98+ else: return "inherit %s;" % self.attrs.pretty()
99+
100+class FormalsBox(BaseBox):
101+ def __init__(self, params, hasEllipsis):
102+ self.params = params
103+ self.hasEllipsis = hasEllipsis
104+ def pretty(self):
105+ params = [param.pretty() for param in self.params]
106+ if self.hasEllipsis: params.append("...")
107+ return "{ %s }" % ", ".join(params)
108+ def getparams(self): return self.params
109+ def getellipsis(self): return self.hasEllipsis
110+
111+class VarBox(BaseBox):
112+ def __init__(self, name): self.name = name
113+ def pretty(self): return self.name
114+
115+class AttrPathBox(BaseBox):
116+ def __init__(self, attrs): self.attrs = attrs
117+ def pretty(self): return ".".join([attr.pretty() for attr in self.attrs])
118+ def getattrs(self): return self.attrs
119+
120+class AttrsBox(BaseBox):
121+ def __init__(self, attrs): self.attrs = attrs
122+ def pretty(self): return " ".join([attr.pretty() for attr in self.attrs])
123+ def getattrs(self): return self.attrs
124+
125+class ListBox(BaseBox):
126+ def __init__(self, exprs): self.exprs = exprs
127+ def pretty(self):
128+ return "[ %s ]" % " ".join([expr.pretty() for expr in self.exprs])
129+ def getexprs(self): return self.exprs
130+
131+class IntBox(BaseBox):
132+ def __init__(self, value): self.value = value
133+ def pretty(self): return str(self.value)
134+
135+class StrBox(BaseBox):
136+ def __init__(self, value): self.value = value
137+ def pretty(self): return '"%s"' % self.value
138+
139+class ExprUnaryBox(BaseBox):
140+ def __init__(self, expr, op):
141+ self.expr = expr
142+ self.op = op.getstr()
143+ def pretty(self):
144+ return "(%s%s)" % (self.op, self.expr.pretty())
145+
146+class SelectBox(BaseBox):
147+ def __init__(self, expr, path, alt):
148+ self.expr = expr
149+ self.path = path
150+ self.alt = alt
151+ def pretty(self):
152+ if self.alt is None:
153+ return "%s.%s" % (self.expr.pretty(), self.path.pretty())
154+ else:
155+ return "%s.%s or %s" % (self.expr.pretty(), self.path.pretty(), self.alt.pretty())
156+
157+class ExprBinaryBox(BaseBox):
158+ def __init__(self, left, right, op):
159+ self.left = left
160+ self.right = right
161+ self.op = op.getstr()
162+ def pretty(self):
163+ return "(%s %s %s)" % (self.left.pretty(), self.op, self.right.pretty())
164+
165+class HasBox(BaseBox):
166+ def __init__(self, value, path):
167+ self.value = value
168+ self.path = path
169+ def pretty(self):
170+ return "(%s ? %s)" % (self.value.pretty(), self.path.pretty())
171+
172+class AssertBox(BaseBox):
173+ def __init__(self, cond, expr):
174+ self.cond = cond
175+ self.expr = expr
176+ def pretty(self): return "assert %s; %s" % (self.cond.pretty(), self.expr.pretty())
177+
178+class WithBox(BaseBox):
179+ def __init__(self, scope, expr):
180+ self.scope = scope
181+ self.expr = expr
182+ def pretty(self): return "with %s; %s" % (self.scope.pretty(), self.expr.pretty())
183+
184+class LambdaBox(BaseBox):
185+ def __init__(self, binding, params, body):
186+ self.binding = binding
187+ self.params = params
188+ self.body = body
189+ def pretty(self):
190+ body = self.body.pretty()
191+ if self.binding and self.params:
192+ return "%s@%s: %s" % (self.binding.pretty(), self.params.pretty(), body)
193+ elif self.binding: return "%s: %s" % (self.binding.pretty(), body)
194+ elif self.params: return "%s: %s" % (self.params.pretty(), body)
195+ else: return "_: " + body
196+
197+class AppBox(BaseBox):
198+ def __init__(self, func, arg):
199+ self.func = func
200+ self.arg = arg
201+ def pretty(self): return "(%s) (%s)" % (self.func.pretty(), self.arg.pretty())
202+
203+class IfBox(BaseBox):
204+ def __init__(self, cond, seq, alt):
205+ self.cond = cond
206+ self.seq = seq
207+ self.alt = alt
208+ def pretty(self):
209+ return "if %s then %s else %s" % (
210+ self.cond.pretty(), self.seq.pretty(), self.alt.pretty())
211+
212+pg = rply.ParserGenerator(KEYWORDS + [
213+ "ID", "INT", "SPATH", "URI",
214+ "AND", "IMPL", "OR_OP",
215+ "EQ", "NEQ", "LE", "GE", "LEQ", "GEQ", "HAS",
216+ "CONCAT", "UPDATE",
217+ "DIV", "MINUS", "MUL", "PLUS",
218+ "NEGATE", "NOT",
219+ "COLON", "SEMI", "DOT", "COMMA", "AT", "EQUALS", "ELLIPSIS",
220+ "OPEN_BRACE", "CLOSE_BRACE", "OPEN_BRACK", "CLOSE_BRACK", "OPEN_PAREN", "CLOSE_PAREN",
221+], precedence=[
222+ ("right", ["IMPL"]),
223+ ("left", ["OR_OP"]),
224+ ("left", ["AND"]),
225+ ("nonassoc", ["EQ", "NEQ"]),
226+ ("nonassoc", ["LE", "GE", "LEQ", "GEQ"]),
227+ ("right", ["UPDATE"]),
228+ ("left", ["NOT"]),
229+ ("left", ["PLUS", "MINUS"]),
230+ ("left", ["MUL", "DIV"]),
231+ ("right", ["CONCAT"]),
232+ ("nonassoc", ["HAS"]),
233+ ("nonassoc", ["NEGATE"]),
234+])
235+
236+class ParseError(Exception):
237+ def __init__(self, token): self.token = token
238+
239+@pg.error
240+def parseError(token): raise ParseError(token)
241+
242+def constRule(rule, pb): pg.production(rule)(lambda _: pb)
243+def enclosedRule(rule, i): pg.production(rule)(lambda p: p[i])
244+def precRule(sup, sub): enclosedRule("expr%s : expr%s" % (sup, sub), 0)
245+SPINE = "", "_function", "_if", "_op", "_app", "_select", "_simple"
246+for sup, sub in zip(SPINE, SPINE[1:]): precRule(sup, sub)
247+
248+@pg.production("expr_function : ID COLON expr_function")
249+def exprLambda(p): return LambdaBox(VarBox(p[0].getstr()), None, p[2])
250+
251+@pg.production("expr_function : OPEN_BRACE formals CLOSE_BRACE COLON expr_function")
252+def exprLambda(p): return LambdaBox(None, p[1], p[4])
253+
254+@pg.production("expr_function : OPEN_BRACE formals CLOSE_BRACE AT ID COLON expr_function")
255+def exprLambda(p): return LambdaBox(VarBox(p[4].getstr()), p[1], p[6])
256+
257+@pg.production("expr_function : ID AT OPEN_BRACE formals CLOSE_BRACE COLON expr_function")
258+def exprLambda(p): return LambdaBox(VarBox(p[0].getstr()), p[3], p[6])
259+
260+@pg.production("expr_function : ASSERT expr SEMI expr_function")
261+def exprAssert(p): return AssertBox(p[1], p[3])
262+
263+@pg.production("expr_function : WITH expr SEMI expr_function")
264+def exprWith(p): return WithBox(p[1], p[3])
265+
266+@pg.production("expr_if : IF expr THEN expr ELSE expr")
267+def exprIf(p): return IfBox(p[1], p[3], p[5])
268+
269+@pg.production("expr_op : NEGATE expr_op | NOT expr_op")
270+def exprUnary(p): return ExprUnaryBox(p[1], p[0])
271+
272+@pg.production("expr_op : expr_op AND expr_op")
273+@pg.production("expr_op : expr_op CONCAT expr_op")
274+@pg.production("expr_op : expr_op DIV expr_op")
275+@pg.production("expr_op : expr_op EQ expr_op")
276+@pg.production("expr_op : expr_op GE expr_op")
277+@pg.production("expr_op : expr_op GEQ expr_op")
278+@pg.production("expr_op : expr_op IMPL expr_op")
279+@pg.production("expr_op : expr_op LE expr_op")
280+@pg.production("expr_op : expr_op LEQ expr_op")
281+@pg.production("expr_op : expr_op MINUS expr_op")
282+@pg.production("expr_op : expr_op MUL expr_op")
283+@pg.production("expr_op : expr_op NEQ expr_op")
284+@pg.production("expr_op : expr_op OR_OP expr_op")
285+@pg.production("expr_op : expr_op PLUS expr_op")
286+@pg.production("expr_op : expr_op UPDATE expr_op")
287+def exprBinary(p): return ExprBinaryBox(p[0], p[2], p[1])
288+
289+@pg.production("expr_op : expr_op HAS attrpath")
290+def exprHas(p): return HasBox(p[0], p[2])
291+
292+@pg.production("expr_app : expr_app expr_select")
293+def exprApp(p): return AppBox(p[0], p[1])
294+
295+@pg.production("expr_select : expr_simple DOT attrpath | expr_simple DOT attrpath OR expr_select")
296+def exprSelect(p): return SelectBox(p[0], p[2], p[4] if len(p) == 5 else None)
297+
298+@pg.production("expr_simple : ID")
299+def exprSimpleId(p): return VarBox(p[0].getstr())
300+
301+@pg.production("expr_simple : INT")
302+def exprSimpleInt(p): return IntBox(int(p[0].getstr()))
303+
304+@pg.production("expr_simple : URI")
305+def exprURI(p): return StrBox(p[0].getstr())
306+
307+enclosedRule("expr_simple : OPEN_BRACE binds CLOSE_BRACE", 1)
308+enclosedRule("expr_simple : OPEN_PAREN expr CLOSE_PAREN", 1)
309+enclosedRule("expr_simple : OPEN_BRACK expr_list CLOSE_BRACK", 1)
310+constRule("expr_list :", ListBox([]))
311+
312+@pg.production("expr_list : expr_list expr_select")
313+def exprListCons(p): return ListBox(p[0].getexprs() + [p[1]])
314+
315+@pg.production("binds : binds attrpath EQUALS expr SEMI")
316+def bindsExpr(p): return BindsBox(p[0].getbinds() + [BindExprBox(p[1], p[3])])
317+
318+@pg.production("binds : binds INHERIT attrs SEMI")
319+def bindsInherit(p):
320+ return BindsBox(p[0].getbinds() + [BindInheritBox(p[2], None)])
321+
322+@pg.production("binds : binds INHERIT OPEN_PAREN expr CLOSE_PAREN attrs SEMI")
323+def bindsInherit(p):
324+ return BindsBox(p[0].getbinds() + [BindInheritBox(p[5], p[3])])
325+
326+constRule("binds :", BindsBox([]))
327+
328+@pg.production("formals : formal COMMA formals")
329+def formalsComma(p):
330+ return FormalsBox([p[0]] + p[2].getparams(), p[2].getellipsis())
331+
332+@pg.production("formals : formal")
333+def formalsFormal(p): return FormalsBox([p[0]], False)
334+
335+constRule("formals :", FormalsBox([], False))
336+constRule("formals : ELLIPSIS", FormalsBox([], True))
337+
338+@pg.production("formal : ID | ID HAS expr")
339+def formalId(p): return FormalBox(p[0].getstr(), p[2] if len(p) > 1 else None)
340+
341+@pg.production("attrs : attrs attr")
342+def attrsAttr(p): return AttrsBox(p[0].getattrs() + [p[1]])
343+
344+constRule("attrs :", AttrsBox([]))
345+
346+@pg.production("attrpath : attrpath DOT attr")
347+def attrpathNil(p): return AttrPathBox(p[0].getattrs() + [p[2]])
348+
349+@pg.production("attrpath : attr")
350+def attrpathCons(p): return AttrPathBox(p)
351+
352+@pg.production("attr : ID")
353+def attrId(p): return StrBox(p[0].getstr())
354+
355+parser = pg.build()
356+
357+# Top-level interface: Lex and parse UTF-8 text.
358+def parse(expr): return parser.parse(lexer.lex(expr))