| 1 | #!/usr/bin/env python2
|
| 2 | from __future__ import print_function
|
| 3 |
|
| 4 | from _devbuild.gen.typed_arith_asdl import arith_expr_t
|
| 5 | from asdl.examples import tdop
|
| 6 | from asdl.examples import typed_arith_parse # module under test
|
| 7 |
|
| 8 | from typing import Callable, Optional, TYPE_CHECKING
|
| 9 |
|
| 10 |
|
| 11 | # Type alias
|
| 12 | if TYPE_CHECKING:
|
| 13 | ParseFunc = Callable[[str, Optional[str]], arith_expr_t]
|
| 14 |
|
| 15 |
|
| 16 | def _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 |
|
| 31 | def 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 |
|
| 70 | def 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 |
|
| 80 | def 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 |
|
| 99 | def 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 |
|
| 123 | def 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 |
|
| 131 | def TestComma(t_parse):
|
| 132 | # type: (ParseFunc) -> None
|
| 133 | t_parse('x=1,y=2,z=3', '(, (= x 1) (= y 2) (= z 3))')
|
| 134 |
|
| 135 |
|
| 136 | def 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 |
|
| 150 | def 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 |
|
| 178 | def 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 |
|
| 207 | def 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 |
|
| 226 | if __name__ == '__main__':
|
| 227 | main()
|