| 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()
 |