1 | #!/usr/bin/env python
|
2 | # Copyright 2016 Andy Chu. All rights reserved.
|
3 | # Licensed under the Apache License, Version 2.0 (the "License");
|
4 | # you may not use this file except in compliance with the License.
|
5 | # You may obtain a copy of the License at
|
6 | #
|
7 | # http://www.apache.org/licenses/LICENSE-2.0
|
8 | """
|
9 | arith_parse_test.py: Tests for arith_parse.py
|
10 | """
|
11 |
|
12 | import unittest
|
13 |
|
14 | from core import expr_eval
|
15 | from core import legacy
|
16 | from core import word_eval
|
17 | from core import state
|
18 | from core import test_lib
|
19 |
|
20 | from osh import parse_lib
|
21 | #from osh import arith_parse
|
22 |
|
23 |
|
24 | class ExprSyntaxError(Exception):
|
25 | pass
|
26 |
|
27 |
|
28 | def ParseAndEval(code_str):
|
29 | arena = test_lib.MakeArena('<arith_parse_test.py>')
|
30 | w_parser, _ = parse_lib.MakeParserForCompletion(code_str, arena)
|
31 | anode = w_parser._ReadArithExpr() # need the right lex state?
|
32 |
|
33 | if not anode:
|
34 | raise ExprSyntaxError("failed %s" % w_parser.Error())
|
35 |
|
36 | print('node:', anode)
|
37 |
|
38 | mem = state.Mem('', [], {}, None)
|
39 | exec_opts = state.ExecOpts(mem)
|
40 | splitter = legacy.SplitContext(mem)
|
41 | ev = word_eval.CompletionWordEvaluator(mem, exec_opts, splitter)
|
42 |
|
43 | arith_ev = expr_eval.ArithEvaluator(mem, exec_opts, ev)
|
44 | value = arith_ev.Eval(anode)
|
45 | return value
|
46 |
|
47 |
|
48 | def testEvalExpr(e, expected):
|
49 | print('expression:', e)
|
50 | actual = ParseAndEval(e)
|
51 | if actual != expected:
|
52 | raise AssertionError('%s => %r, expected %r' % (e, actual, expected))
|
53 |
|
54 |
|
55 | def testSyntaxError(ex):
|
56 | try:
|
57 | actual = ParseAndEval(ex)
|
58 | except ExprSyntaxError as e:
|
59 | print(ex, '\t\t', e)
|
60 | else:
|
61 | raise AssertionError('Expected parse error: %r, got %r' % (ex, actual))
|
62 |
|
63 |
|
64 | class ArithTest(unittest.TestCase):
|
65 |
|
66 | def testEval(self):
|
67 | testEvalExpr('(7)', 7)
|
68 |
|
69 | # Doesn't matter if you eval 2-3 first here
|
70 | testEvalExpr('1 + 2 - 3', 0)
|
71 |
|
72 | testEvalExpr('1 + 2 * 3', 7)
|
73 |
|
74 | testEvalExpr('7 - 9 * (2 - 3)', 16)
|
75 | testEvalExpr('2 * 3 * 4', 24)
|
76 |
|
77 | testEvalExpr('2 ** 3 ** 4', 2 ** (3 ** 4))
|
78 |
|
79 | testEvalExpr('(2 ** 3) ** 4', 4096)
|
80 |
|
81 | testEvalExpr('5', 5)
|
82 | testEvalExpr('4 + 2', 6)
|
83 | testEvalExpr('9 - 8 - 7', -6)
|
84 | testEvalExpr('9 - (8 - 7)', 8)
|
85 | testEvalExpr('(9 - 8) - 7', -6)
|
86 | testEvalExpr('2 + 3 ** 2 * 3 + 4', 33)
|
87 |
|
88 | testEvalExpr('4 * 3 / 2', 6)
|
89 | testEvalExpr('3 * 2 % 4', 2)
|
90 | testEvalExpr('+ 1', 1)
|
91 | testEvalExpr('- 5', -5)
|
92 | testEvalExpr('-2-3', -5)
|
93 |
|
94 | # Comma has lower precedence
|
95 | testEvalExpr('1 ? 2 : 3, 4 ? 5 : 6', 5)
|
96 |
|
97 | # Two commas
|
98 | testEvalExpr('1 , 2, 3', 3)
|
99 |
|
100 | # TODO: fix the rest
|
101 | return
|
102 |
|
103 | testEvalExpr('0 = 2 > 3', 0) # => 0 > 3 => 0
|
104 |
|
105 | # string becomes integer"
|
106 | #testEvalExpr(['ab21xx', ':', '[^0-9]*([0-9]*)', '+', '3'], 24)
|
107 |
|
108 | #testEvalExpr(['ab21xx', ':', '[^0-9]*([0-9]*)'], '21')
|
109 |
|
110 | #testEvalExpr(['ab21xx', ':', '(.)'], 'a')
|
111 |
|
112 | # integer becomes string. -3 as a string is less than '-3-'
|
113 | #testEvalExpr(['2', '-', '5', '<', '-3-'], True)
|
114 | #testEvalExpr(['2', '-', '5', '<', '-2-'], False)
|
115 |
|
116 | # Arithmetic compare
|
117 | testEvalExpr(['-3', '<', '-2'], True)
|
118 | # String compare
|
119 | #testEvalExpr(['-3', '<', '-2-'], False)
|
120 |
|
121 | #testEvalExpr(['3a', ':', '(.)', '=', '1', '+', '2'], True)
|
122 |
|
123 | # More stuff:
|
124 | # expr / => / -- it's just a plain string
|
125 | # expr / : . => 1
|
126 | # expr \( / \) => / -- this is dumb
|
127 | # expr + match => match -- + is escaping, wtf
|
128 | #
|
129 | #testEvalExpr(['ab21xx', ':', '[^0-9]*([0-9]*)', '+', '3'], 24)
|
130 |
|
131 | def testEvalConstants(self):
|
132 | # Octal constant
|
133 | testEvalExpr('011', 9)
|
134 |
|
135 | # Hex constant
|
136 | testEvalExpr('0xA', 10)
|
137 |
|
138 | # Arbitrary base constant
|
139 | testEvalExpr('64#z', 35)
|
140 | testEvalExpr('64#Z', 61)
|
141 | testEvalExpr('64#@', 62)
|
142 | testEvalExpr('64#_', 63)
|
143 |
|
144 | def testErrors(self):
|
145 | # Now try some bad ones
|
146 |
|
147 | testSyntaxError('')
|
148 | testSyntaxError(')')
|
149 | testSyntaxError('(')
|
150 |
|
151 | # If ( is an error, I think this should be too.
|
152 | # For now left it out. Don't want to look up opinfo.
|
153 | #
|
154 | # In GNU expr, this is an error because + is escaping char!
|
155 | #testSyntaxError('+')
|
156 | #testSyntaxError('/')
|
157 |
|
158 | testSyntaxError('()') # Not valid, expr also fails.
|
159 | testSyntaxError('( 1')
|
160 | testSyntaxError('(1 + (3 * 4)')
|
161 | testSyntaxError('(1 + (3 * 4) 5') # Not valid, expr also fails.
|
162 | #testSyntaxError('1 1')
|
163 | #testSyntaxError('( 1 ) ( 2 )')
|
164 |
|
165 | # NOTE: @ implicitly ends it now
|
166 | #testSyntaxError('1 @ 2')
|
167 |
|
168 |
|
169 | if __name__ == '__main__':
|
170 | unittest.main()
|