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