1+ """
2+ A parser for a simple logical expression language using Lark. The syntax is inspired by Lean's
3+
4+ Unicode symbols are not required, but if you like them, adding the Unicode LaTeX extension for VSCode
5+ https://marketplace.visualstudio.com/items?itemName=oijaz.unicode-latex
6+ Goto the `File > Preferences > Settings` of the plugin and add `python` to the enabled extensions.
7+ Reloading your window will enable access via backslash autocomplete commands.
8+ For example \\ alpha will tab autocomplete to α.
9+ """
10+
111import lark
212import kdrag .smt as smt
313from lark import Tree
414
515grammar = r"""
616start: expr
717
8- ?expr: quantifier
18+ ?expr: ite
19+ ?ite: quantifier | "if" expr "then" expr "else" expr -> if
920?quantifier: implication | ("forall" | "∀") binders "," expr -> forall_ | \
10- ("exists" | "∃") binders "," expr -> exists_ | ("fun" | "λ") binders "=>" expr -> fun_
21+ ("exists" | "∃") binders "," expr -> exists_ | ("fun" | "λ") binders ( "=>" | "↦") expr -> fun_ | set
1122?implication: disjunction | disjunction ("->" | "→") implication -> implies
12- ?disjunction: conjunction | disjunction ("\\/" | "∨" | "||") conjunction -> or_
13- ?conjunction: comparison | conjunction ("/\\" | "∧" | "&&") comparison -> and_
23+ ?disjunction: conjunction | disjunction ("\\/" | "∨" | "||" | "∪" ) conjunction -> or_
24+ ?conjunction: comparison | conjunction ("/\\" | "∧" | "&&" | "∩" ) comparison -> and_
1425?comparison: additive
1526 | comparison ("=" | "==") additive -> eq
1627 | comparison "!=" additive -> neq
17- | comparison "<" additive -> lt
18- | comparison ">" additive -> gt
19- | comparison "<=" additive -> le
20- | comparison ">=" additive -> ge
28+ | comparison ( "<" | "⊂") additive -> lt
29+ | comparison ( ">" | "⊃") additive -> gt
30+ | comparison ( "<=" | "≤" | "⊆") additive -> le
31+ | comparison ( ">=" | "≥" | "⊇") additive -> ge
2132?additive: multiplicative
2233 | additive "+" multiplicative -> add | additive "-" multiplicative -> sub
2334?multiplicative: application
2435 | multiplicative "*" application -> mul | multiplicative "/" application -> div
2536?application: atom atom* -> app
2637?atom: const | num | bool_ | "(" expr ")" | seq
2738
28- binders: binder+
39+ binders: binder+ | NAME ":" sort -> sing_binder
2940?binder: "(" NAME+ ":" sort ")" -> annot_binder | NAME -> infer_binder
3041?sort: arrow
3142?arrow: sortatom | sortatom "->" arrow -> array
3243?sortatom : NAME -> sortlit | "BitVec" NUMBER -> bitvecsort | "(" sort ")" | "'" NAME -> typevar
3344
34- const: NAME
45+ const: NAME ("." NAME)*
3546num: NUMBER
3647bool_: "true" -> true | "false" -> false
3748seq : "[" expr ("," expr)* "]"
3849
50+ set : "{" binders "|" expr "}"
51+
3952NAME: /[a-zA-Z_][a-zA-Z0-9_']*/
4053NUMBER: /-?\d+/
4154%import common.WS
@@ -79,6 +92,13 @@ def parse(s: str, globals=None) -> smt.ExprRef:
7992 True
8093 >>> parse("[true, false]")
8194 Concat(Unit(True), Unit(False))
95+ >>> q = smt.Const("x", smt.TupleSort("pair", [smt.IntSort(), smt.BoolSort()])[0])
96+ >>> parse("q.project1", {"q": q})
97+ project1(x)
98+ >>> parse("{x : Int | x > 0}")
99+ Lambda(x, x > 0)
100+ >>> parse("if true && false then 1 + 1 else 0")
101+ If(And(True, False), 2, 0)
82102 """
83103 env = {}
84104
@@ -139,10 +159,14 @@ def binders(tree) -> list[smt.ExprRef]:
139159 match tree :
140160 case Tree ("binders" , bs ):
141161 return [v for b in bs for v in binder (b )]
162+ case Tree ("sing_binder" , [name , sort_tree ]):
163+ s = sort (sort_tree )
164+ return [smt .Const (str (name ), s )]
142165 case _:
143166 raise ValueError ("Unknown binders tree" , tree )
144167
145168 def quant (vs , body_tree , q ) -> smt .QuantifierRef :
169+ # TODO: doofy. Should make a env stack
146170 nonlocal env
147171 old_env = env .copy ()
148172 vs = binders (vs )
@@ -157,8 +181,10 @@ def expr(tree) -> smt.ExprRef:
157181 # TODO: obviously this is not well typed.
158182 case Tree ("num" , [n ]):
159183 return int (n ) # type: ignore
160- case Tree ("const" , [name ]):
184+ case Tree ("const" , [name , * attrs ]):
161185 res = lookup (name ) # type: ignore
186+ for attr in attrs :
187+ res = getattr (res , str (attr )) # type: ignore
162188 return res # type: ignore
163189 case Tree ("true" , []):
164190 return smt .BoolVal (True )
@@ -213,6 +239,13 @@ def expr(tree) -> smt.ExprRef:
213239 return quant (vs , body , smt .Exists )
214240 case Tree ("fun_" , [vs , body ]):
215241 return quant (vs , body , smt .Lambda )
242+ case Tree ("set" , [vs , body ]):
243+ t = quant (vs , body , smt .Lambda )
244+ if t .sort ().range () != smt .BoolSort ():
245+ raise ValueError ("Set comprehension must return Bool" , t )
246+ return t
247+ case Tree ("if" , [cond , then_ , else_ ]):
248+ return smt .If (expr (cond ), expr (then_ ), expr (else_ ))
216249 case Tree ("implies" , [left , right ]):
217250 return smt .Implies (expr (left ), expr (right ))
218251 case _:
0 commit comments