OILS / asdl / examples / typed_arith_parse_test.py View on Github | oilshell.org

227 lines, 132 significant
1#!/usr/bin/env python2
2from __future__ import print_function
3
4from _devbuild.gen.typed_arith_asdl import arith_expr_t
5from asdl.examples import tdop
6from asdl.examples import typed_arith_parse # module under test
7
8from typing import Callable, Optional, TYPE_CHECKING
9
10
11# Type alias
12if TYPE_CHECKING:
13 ParseFunc = Callable[[str, Optional[str]], arith_expr_t]
14
15
16def _assertParseError(make_parser, s, error_substring=''):
17 # type: (Callable[[str], tdop.Parser], str, str) -> None
18 p = make_parser(s)
19 try:
20 node = p.Parse()
21 except tdop.ParseError as e:
22 err = str(e)
23 if error_substring in err:
24 print('got expected error for %s: %s' % (s, err))
25 else:
26 raise AssertionError('Expected %r to be in %r' % (error_substring, err))
27 else:
28 raise AssertionError('%r should have failed' % s)
29
30
31def TestArith(t_parse):
32 # type: (ParseFunc) -> None
33 t_parse('1+2+3', '(+ (+ 1 2) 3)')
34 t_parse('1+2*3', '(+ 1 (* 2 3))')
35 t_parse('4*(2+3)', '(* 4 (+ 2 3))')
36 t_parse('(2+3)*4', '(* (+ 2 3) 4)')
37 t_parse('1<2', '(< 1 2)')
38 t_parse('x=3', '(= x 3)')
39 t_parse('x = 2*3', '(= x (* 2 3))')
40 t_parse('x = y', '(= x y)')
41
42 t_parse('x*y - y*z', '(- (* x y) (* y z))')
43 t_parse('x/y - y%z', '(- (/ x y) (% y z))')
44
45 t_parse("x = y", "(= x y)")
46 t_parse('2 ** 3 ** 2', '(** 2 (** 3 2))')
47 t_parse('a = b = 10', '(= a (= b 10))')
48
49 t_parse('x = ((y*4)-2)', '(= x (- (* y 4) 2))')
50
51 t_parse('x - -y', '(- x (- y))')
52 t_parse("-1 * -2", "(* (- 1) (- 2))")
53 t_parse("-x * -y", "(* (- x) (- y))")
54 t_parse('x - -234', '(- x (- 234))')
55
56 # Python doesn't allow this
57 t_parse('x += y += 3', '(+= x (+= y 3))')
58
59 # This is sort of nonsensical, but bash allows it. The 1 is discarded as
60 # the first element of the comma operator.
61 t_parse('x[1,2]', '(get x (, 1 2))')
62
63 # Python doesn't have unary +
64 t_parse('+1 - +2', '(- (+ 1) (+ 2))')
65
66 # LHS
67 t_parse('f[x] += 1', '(+= (get f x) 1)')
68
69
70def TestBitwise(t_parse):
71 # type: (ParseFunc) -> None
72 t_parse("~1 | ~2", "(| (~ 1) (~ 2))")
73 t_parse("x & y | a & b", "(| (& x y) (& a b))")
74 t_parse("~x ^ y", "(^ (~ x) y)")
75 t_parse("x << y | y << z", "(| (<< x y) (<< y z))")
76
77 t_parse("a ^= b-1", "(^= a (- b 1))")
78
79
80def TestLogical(t_parse):
81 # type: (ParseFunc) -> None
82 t_parse("a && b || c && d", "(|| (&& a b) (&& c d))")
83 t_parse("!a && !b", "(&& (! a) (! b))")
84 t_parse("a != b && c == d", "(&& (!= a b) (== c d))")
85
86 t_parse("a > b ? 0 : 1", "(? (> a b) 0 1)")
87 t_parse("a > b ? x+1 : y+1", "(? (> a b) (+ x 1) (+ y 1))")
88
89 t_parse("1 ? true1 : 2 ? true2 : false", "(? 1 true1 (? 2 true2 false))")
90 t_parse("1 ? true1 : (2 ? true2 : false)", "(? 1 true1 (? 2 true2 false))")
91
92 t_parse("1 ? (2 ? true : false1) : false2", "(? 1 (? 2 true false1) false2)")
93 t_parse("1 ? 2 ? true : false1 : false2", "(? 1 (? 2 true false1) false2)")
94
95 # Should have higher precedence than comma
96 t_parse("x ? 1 : 2, y ? 3 : 4", "(, (? x 1 2) (? y 3 4))")
97
98
99def TestUnary(t_parse):
100 # type: (ParseFunc) -> None
101 t_parse("!x", "(! x)")
102 t_parse("x--", "(post-- x)")
103 t_parse("x[1]--", "(post-- (get x 1))")
104
105 t_parse("--x", "(-- x)")
106 t_parse("++x[1]", "(++ (get x 1))")
107
108 t_parse("!x--", "(! (post-- x))")
109 t_parse("~x++", "(~ (post++ x))")
110
111 t_parse("x++ - y++", "(- (post++ x) (post++ y))")
112
113 t_parse("++x - ++y", "(- (++ x) (++ y))")
114
115 #
116 # 1. x++ f() x[] left associative
117 # f(x)[1]++ means
118 # (++ (get (call f x) 1))
119 # 2. ++x + - ! ~ right associative
120 # -++x means (- (++ x))
121
122
123def TestArrays(t_parse):
124 # type: (ParseFunc) -> None
125 """Shared between shell, oil, and Python."""
126 t_parse('x[1]', '(get x 1)')
127 t_parse('x[a+b]', '(get x (+ a b))')
128 t_parse('x[1:2]', 'SLICE')
129
130
131def TestComma(t_parse):
132 # type: (ParseFunc) -> None
133 t_parse('x=1,y=2,z=3', '(, (= x 1) (= y 2) (= z 3))')
134
135
136def TestFuncCalls(t_parse):
137 # type: (ParseFunc) -> None
138 t_parse('x = y(2)*3 + y(4)*5', '(= x (+ (* (call y 2) 3) (* (call y 4) 5)))')
139
140 t_parse('x(1,2)+y(3,4)', '(+ (call x 1 2) (call y 3 4))')
141 t_parse('x(a,b,c[d])', '(call x a b (get c d))')
142 t_parse('x(1,2)*j+y(3,4)*k+z(5,6)*l',
143 '(+ (+ (* (call x 1 2) j) (* (call y 3 4) k)) (* (call z 5 6) l))')
144 t_parse('print(test(2,3))', '(call print (call test 2 3))')
145 t_parse('print("x")', '(call print x)')
146 t_parse('min(255,n*2)', '(call min 255 (* n 2))')
147 t_parse('c = pal[i*8]', '(= c (get pal (* i 8)))')
148
149
150def TestErrors(p):
151 # type: (Callable[[str], tdop.Parser]) -> None
152 _assertParseError(p, '}')
153 _assertParseError(p, ']')
154
155 _assertParseError(p, '{') # depends on language
156
157 _assertParseError(p, "x+1 = y", "Can't assign")
158 _assertParseError(p, "(x+1)++", "Can't assign")
159
160 # Should be an EOF error
161 _assertParseError(p, 'foo ? 1 :', 'Unexpected end')
162
163 _assertParseError(p, 'foo ? 1 ', 'expected :')
164 _assertParseError(p, '%', "can't be used in prefix position")
165
166 error_str = "can't be used in prefix"
167 _assertParseError(p, '}')
168 _assertParseError(p, '{')
169 _assertParseError(p, ']', error_str)
170
171 _assertParseError(p, '1 ( 2', "can't be called")
172 _assertParseError(p, '(x+1) ( 2 )', "can't be called")
173 #_assertParseError(p, '1 ) 2')
174
175 _assertParseError(p, '1 [ 2 ]', "can't be indexed")
176
177
178def t_parse(s, # type: str
179 expected=None, # type: Optional[str]
180 ):
181 # type: (...) -> arith_expr_t
182 p = typed_arith_parse.MakeParser(s)
183 tree = p.Parse()
184
185 print()
186 print('---')
187 print()
188
189 print(s)
190 print()
191 print(tree)
192
193 from asdl import format as fmt
194 from mycpp import mylib
195
196 ast_f = fmt.DetectConsoleOutput(mylib.Stdout())
197 p_node = tree.AbbreviatedTree()
198 fmt.PrintTree(p_node, ast_f)
199
200 #v = PrettyPrinter()
201 #v.Visit(tree)
202
203 #print('%-40s %s' % (s, sexpr))
204 return tree
205
206
207def main():
208 # type: () -> None
209
210 # TODO: Enable once we switch to MyPy
211 if 0:
212 return
213
214 p = typed_arith_parse.MakeParser
215
216 TestArith(t_parse)
217 TestBitwise(t_parse)
218 TestLogical(t_parse)
219 TestUnary(t_parse)
220 TestArrays(t_parse)
221 TestFuncCalls(t_parse)
222 TestComma(t_parse)
223 TestErrors(p)
224
225
226if __name__ == '__main__':
227 main()