| 1 | #!/usr/bin/env python2
|
| 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 _devbuild.gen.types_asdl import lex_mode_e
|
| 15 | from core import error
|
| 16 | from core import test_lib
|
| 17 | from core import ui
|
| 18 | from osh import sh_expr_eval
|
| 19 | from osh import split
|
| 20 | from osh import word_eval
|
| 21 | from core import state
|
| 22 |
|
| 23 | #from osh import arith_parse
|
| 24 |
|
| 25 |
|
| 26 | def ParseAndEval(code_str):
|
| 27 | arena = test_lib.MakeArena('<arith_parse_test.py>')
|
| 28 | parse_ctx = test_lib.InitParseContext(arena=arena)
|
| 29 | w_parser = test_lib.InitWordParser(code_str, arena=arena)
|
| 30 |
|
| 31 | # This is weird but works
|
| 32 | w_parser._SetNext(lex_mode_e.Arith) # Calling private method
|
| 33 | anode = w_parser.a_parser.Parse()
|
| 34 |
|
| 35 | print('node:', anode)
|
| 36 |
|
| 37 | mem = state.Mem('', [], arena, [])
|
| 38 | parse_opts, exec_opts, mutable_opts = state.MakeOpts(mem, None)
|
| 39 | mem.exec_opts = exec_opts
|
| 40 | state.InitMem(mem, {}, '0.1')
|
| 41 |
|
| 42 | splitter = split.SplitContext(mem)
|
| 43 | errfmt = ui.ErrorFormatter()
|
| 44 |
|
| 45 | tilde_ev = word_eval.TildeEvaluator(mem, exec_opts)
|
| 46 | word_ev = word_eval.CompletionWordEvaluator(mem, exec_opts, mutable_opts,
|
| 47 | tilde_ev, splitter, errfmt)
|
| 48 |
|
| 49 | arith_ev = sh_expr_eval.ArithEvaluator(mem, exec_opts, mutable_opts,
|
| 50 | parse_ctx, arena)
|
| 51 |
|
| 52 | arith_ev.word_ev = word_ev
|
| 53 | return arith_ev.EvalToInt(anode)
|
| 54 |
|
| 55 |
|
| 56 | def testEvalExpr(e, expected):
|
| 57 | print('expression:', e)
|
| 58 | actual = ParseAndEval(e)
|
| 59 | if actual != expected:
|
| 60 | raise AssertionError('%s => %r, expected %r' % (e, actual, expected))
|
| 61 |
|
| 62 |
|
| 63 | def testSyntaxError(expr):
|
| 64 | try:
|
| 65 | actual = ParseAndEval(expr)
|
| 66 | except error.Parse as e:
|
| 67 | print(expr, '\t\t', e)
|
| 68 | else:
|
| 69 | raise AssertionError('Expected parse error: %r, got %r' %
|
| 70 | (expr, actual))
|
| 71 |
|
| 72 |
|
| 73 | class ArithTest(unittest.TestCase):
|
| 74 |
|
| 75 | def testEval(self):
|
| 76 | testEvalExpr('(7)', 7)
|
| 77 |
|
| 78 | # Doesn't matter if you eval 2-3 first here
|
| 79 | testEvalExpr('1 + 2 - 3', 0)
|
| 80 |
|
| 81 | testEvalExpr('1 + 2 * 3', 7)
|
| 82 |
|
| 83 | testEvalExpr('7 - 9 * (2 - 3)', 16)
|
| 84 | testEvalExpr('2 * 3 * 4', 24)
|
| 85 |
|
| 86 | testEvalExpr('2 ** 3 ** 4', 2**(3**4))
|
| 87 |
|
| 88 | testEvalExpr('(2 ** 3) ** 4', 4096)
|
| 89 |
|
| 90 | testEvalExpr('5', 5)
|
| 91 | testEvalExpr('4 + 2', 6)
|
| 92 | testEvalExpr('9 - 8 - 7', -6)
|
| 93 | testEvalExpr('9 - (8 - 7)', 8)
|
| 94 | testEvalExpr('(9 - 8) - 7', -6)
|
| 95 | testEvalExpr('2 + 3 ** 2 * 3 + 4', 33)
|
| 96 |
|
| 97 | testEvalExpr('4 * 3 / 2', 6)
|
| 98 | testEvalExpr('3 * 2 % 4', 2)
|
| 99 | testEvalExpr('+ 1', 1)
|
| 100 | testEvalExpr('- 5', -5)
|
| 101 | testEvalExpr('-2-3', -5)
|
| 102 |
|
| 103 | # Comma has lower precedence
|
| 104 | testEvalExpr('1 ? 2 : 3, 4 ? 5 : 6', 5)
|
| 105 |
|
| 106 | # Two commas
|
| 107 | testEvalExpr('1 , 2, 3', 3)
|
| 108 |
|
| 109 | # TODO: fix the rest
|
| 110 | return
|
| 111 |
|
| 112 | testEvalExpr('0 = 2 > 3', 0) # => 0 > 3 => 0
|
| 113 |
|
| 114 | # string becomes integer"
|
| 115 | #testEvalExpr(['ab21xx', ':', '[^0-9]*([0-9]*)', '+', '3'], 24)
|
| 116 |
|
| 117 | #testEvalExpr(['ab21xx', ':', '[^0-9]*([0-9]*)'], '21')
|
| 118 |
|
| 119 | #testEvalExpr(['ab21xx', ':', '(.)'], 'a')
|
| 120 |
|
| 121 | # integer becomes string. -3 as a string is less than '-3-'
|
| 122 | #testEvalExpr(['2', '-', '5', '<', '-3-'], True)
|
| 123 | #testEvalExpr(['2', '-', '5', '<', '-2-'], False)
|
| 124 |
|
| 125 | # Arithmetic compare
|
| 126 | testEvalExpr(['-3', '<', '-2'], True)
|
| 127 | # String compare
|
| 128 | #testEvalExpr(['-3', '<', '-2-'], False)
|
| 129 |
|
| 130 | #testEvalExpr(['3a', ':', '(.)', '=', '1', '+', '2'], True)
|
| 131 |
|
| 132 | # More stuff:
|
| 133 | # expr / => / -- it's just a plain string
|
| 134 | # expr / : . => 1
|
| 135 | # expr \( / \) => / -- this is dumb
|
| 136 | # expr + match => match -- + is escaping, wtf
|
| 137 | #
|
| 138 | #testEvalExpr(['ab21xx', ':', '[^0-9]*([0-9]*)', '+', '3'], 24)
|
| 139 |
|
| 140 | def testEvalConstants(self):
|
| 141 | # Octal constant
|
| 142 | testEvalExpr('011', 9)
|
| 143 |
|
| 144 | # Hex constant
|
| 145 | testEvalExpr('0xA', 10)
|
| 146 |
|
| 147 | # Arbitrary base constant
|
| 148 | testEvalExpr('64#z', 35)
|
| 149 | testEvalExpr('64#Z', 61)
|
| 150 | testEvalExpr('64#@', 62)
|
| 151 | testEvalExpr('64#_', 63)
|
| 152 |
|
| 153 | def testErrors(self):
|
| 154 | # Now try some bad ones
|
| 155 |
|
| 156 | testSyntaxError('')
|
| 157 | testSyntaxError(')')
|
| 158 | testSyntaxError('(')
|
| 159 |
|
| 160 | # If ( is an error, I think this should be too.
|
| 161 | # For now left it out. Don't want to look up opinfo.
|
| 162 | #
|
| 163 | # In GNU expr, this is an error because + is escaping char!
|
| 164 | #testSyntaxError('+')
|
| 165 | #testSyntaxError('/')
|
| 166 |
|
| 167 | testSyntaxError('()') # Not valid, expr also fails.
|
| 168 | testSyntaxError('( 1')
|
| 169 | testSyntaxError('(1 + (3 * 4)')
|
| 170 | testSyntaxError('(1 + (3 * 4) 5') # Not valid, expr also fails.
|
| 171 |
|
| 172 | testSyntaxError(';')
|
| 173 | testSyntaxError('- ;')
|
| 174 |
|
| 175 | #testSyntaxError('1 1')
|
| 176 | #testSyntaxError('( 1 ) ( 2 )')
|
| 177 |
|
| 178 | # NOTE: @ implicitly ends it now
|
| 179 | #testSyntaxError('1 @ 2')
|
| 180 |
|
| 181 |
|
| 182 | if __name__ == '__main__':
|
| 183 | unittest.main()
|