github.com/hattya/go.sh@v0.0.0-20240328132134-f53276d95cc6/interp/arith_test.go (about)

     1  //
     2  // go.sh/interp :: arith_test.go
     3  //
     4  //   Copyright (c) 2021-2024 Akinori Hattori <hattya@gmail.com>
     5  //
     6  //   SPDX-License-Identifier: MIT
     7  //
     8  
     9  package interp_test
    10  
    11  import (
    12  	"testing"
    13  
    14  	"github.com/hattya/go.sh/interp"
    15  )
    16  
    17  var evalTests = []struct {
    18  	expr string
    19  	n    int
    20  }{
    21  	{" - 1 ", -1},
    22  	{"   0 ", 0},
    23  	{" + 1 ", 1},
    24  	// dec
    25  	{"9", 9},
    26  	{"+9", 9},
    27  	{"-9", -9},
    28  	{"~0", -1},
    29  	{"!0", 1},
    30  	{"!9", 0},
    31  	{"!+9", 0},
    32  	{"!-9", 0},
    33  	{"(9)", 9},
    34  	{"((9))", 9},
    35  	{"+(-(+9))", -9},
    36  	{"-(+(-9))", 9},
    37  	// oct
    38  	{"07", 7},
    39  	{"+07", 7},
    40  	{"-07", -7},
    41  	// hex
    42  	{"0xf", 15},
    43  	{"0Xf", 15},
    44  	{"+0xf", 15},
    45  	{"+0Xf", 15},
    46  	{"-0xf", -15},
    47  	{"-0Xf", -15},
    48  	// ident
    49  	{"_", 0},
    50  	{"E", 0},
    51  	{"D", 4},
    52  	{"O", 3},
    53  	{"X", 7},
    54  	{"+X", 7},
    55  	{"-X", -7},
    56  	{"~X", -8},
    57  	{"!X", 0},
    58  	{"((X))", 7},
    59  	{"+(-(+X))", -7},
    60  	{"-(+(-X))", 7},
    61  	// inc
    62  	{"X++", 7},
    63  	{"+X++", 7},
    64  	{"-X++", -7},
    65  	{"++X", 8},
    66  	{"+(++X)", 8},
    67  	{"-(++X)", -8},
    68  	// dec
    69  	{"X--", 7},
    70  	{"+X--", 7},
    71  	{"-X--", -7},
    72  	{"--X", 6},
    73  	{"+(--X)", 6},
    74  	{"-(--X)", -6},
    75  	// mul
    76  	{" 0 *  0 *  0 ", 0},
    77  	{" 2 *  4 *  8 ", 64},
    78  	{"+2 *  4 *  8 ", 64},
    79  	{" 2 * -4 *  8 ", -64},
    80  	{" 2 *  4 * +8 ", 64},
    81  	{"-2 * +4 * -8 ", 64},
    82  	{"-2 * (4 *  8)", -64},
    83  	// div
    84  	{" 64 /  8 /  4 ", 2},
    85  	{"+64 /  8 /  4 ", 2},
    86  	{" 64 / -8 /  4 ", -2},
    87  	{" 64 /  8 / +4 ", 2},
    88  	{"-64 / +8 / -4 ", 2},
    89  	{"-64 / (8 /  4)", -32},
    90  	// mod
    91  	{" 140 %  71 %  37 ", 32},
    92  	{"+140 %  71 %  37 ", 32},
    93  	{" 140 % -71 %  37 ", 32},
    94  	{" 140 %  71 % +37 ", 32},
    95  	{"-140 % +71 % -37 ", -32},
    96  	{"-140 % (71 %  37)", -4},
    97  	// add
    98  	{" 0 +  0  + 0 ", 0},
    99  	{" 8 +  4  + 2 ", 14},
   100  	{" 8 *  4  + 2 ", 34},
   101  	{" 8 * (4  + 2)", 48},
   102  	{" 8 +  4  / 2 ", 10},
   103  	{"(8 +  4) / 2 ", 6},
   104  	{" 8 %  4  + 2 ", 2},
   105  	{" 8 % (4  + 2)", 2},
   106  	// sub
   107  	{" 0 -  0  - 0", 0},
   108  	{" 8 -  4  - 2 ", 2},
   109  	{" 8 *  4  - 2 ", 30},
   110  	{" 8 * (4  - 2)", 16},
   111  	{" 8 -  4  / 2 ", 6},
   112  	{"(8 -  4) / 2 ", 2},
   113  	{" 8 %  4  - 2 ", -2},
   114  	{" 8 % (4  - 2)", 0},
   115  	// lsh
   116  	{" 4 <<  2  << 1 ", 32},
   117  	{" 4 +   2  << 1 ", 12},
   118  	{" 4 +  (2  << 1)", 8},
   119  	{" 4 <<  2  -  1 ", 8},
   120  	{"(4 <<  2) -  1 ", 15},
   121  	// rsh
   122  	{" 8 >>  2  >> 1 ", 1},
   123  	{" 8 +   2  >> 1 ", 5},
   124  	{" 8 +  (2  >> 1)", 9},
   125  	{" 8 >>  2  -  1 ", 4},
   126  	{"(8 >>  2) -  1 ", 1},
   127  	// lt
   128  	{" 1 <   2  ", 1},
   129  	{" 2 <   2  ", 0},
   130  	{" 2 <   1  ", 0},
   131  	{" 1 <<  2  <  4 ", 0},
   132  	{" 1 << (2  <  4)", 2},
   133  	{" 1 <   4  >> 1 ", 1},
   134  	{"(1 <   4) >> 1 ", 0},
   135  	// le
   136  	{" 1 <=  2  ", 1},
   137  	{" 2 <=  2  ", 1},
   138  	{" 2 <=  1  ", 0},
   139  	{" 1 <<  2  <= 4 ", 1},
   140  	{" 1 << (2  <= 4)", 2},
   141  	{" 1 <=  4  >> 1 ", 1},
   142  	{"(1 <=  4) >> 1 ", 0},
   143  	// gt
   144  	{" 1 >   2  ", 0},
   145  	{" 2 >   2  ", 0},
   146  	{" 2 >   1  ", 1},
   147  	{" 4 <<  2  >  2 ", 1},
   148  	{" 4 << (2  >  2)", 4},
   149  	{" 4 >   2  >> 1 ", 1},
   150  	{"(4 >   2) >> 1 ", 0},
   151  	// ge
   152  	{" 1 >=  2  ", 0},
   153  	{" 2 >=  2  ", 1},
   154  	{" 2 >=  1  ", 1},
   155  	{" 4 <<  2  >= 2 ", 1},
   156  	{" 4 << (2  >= 2)", 8},
   157  	{" 4 >=  2  >> 1 ", 1},
   158  	{"(4 >=  2) >> 1 ", 0},
   159  	// eq
   160  	{" 1 ==  1  ", 1},
   161  	{" 1 ==  0  ", 0},
   162  	{" 1 <   1  == 0 ", 1},
   163  	{" 1 <  (1  == 0)", 0},
   164  	{" 0 ==  1  <= 1 ", 0},
   165  	{"(0 ==  1) <= 1 ", 1},
   166  	{" 0 >   1  == 0 ", 1},
   167  	{" 0 >  (1  == 0)", 0},
   168  	{" 0 ==  1  >= 0 ", 0},
   169  	{"(0 ==  1) >= 0 ", 1},
   170  	// ne
   171  	{" 1 !=  1  ", 0},
   172  	{" 1 !=  2  ", 1},
   173  	{" 1 <   0  != 1 ", 1},
   174  	{" 1 <  (0  != 1)", 0},
   175  	{" 1 !=  0  <= 1 ", 0},
   176  	{"(1 !=  0) <= 1 ", 1},
   177  	{" 0 >   1  != 1 ", 1},
   178  	{" 0 >  (1  != 1)", 0},
   179  	{" 1 !=  1  >= 0 ", 0},
   180  	{"(1 !=  1) >= 0 ", 1},
   181  	// and
   182  	{" 0 &   0  ", 0},
   183  	{" 0 &   1  ", 0},
   184  	{" 1 &   0  ", 0},
   185  	{" 1 &   1  ", 1},
   186  	{" 0 ==  1  &  0 ", 0},
   187  	{" 0 == (1  &  0)", 1},
   188  	{" 0 &   1  != 1 ", 0},
   189  	{"(0 &   1) != 1 ", 1},
   190  	// xor
   191  	{" 0 ^  0 ", 0},
   192  	{" 0 ^  1 ", 1},
   193  	{" 1 ^  0 ", 1},
   194  	{" 1 ^  1 ", 0},
   195  	{" 0 &  0  ^ 1 ", 1},
   196  	{" 0 & (0  ^ 1)", 0},
   197  	{" 1 ^  0  & 0 ", 1},
   198  	{"(1 ^  0) & 0 ", 0},
   199  	// or
   200  	{" 0 |  0 ", 0},
   201  	{" 0 |  1 ", 1},
   202  	{" 1 |  0 ", 1},
   203  	{" 1 |  1 ", 1},
   204  	{" 1 ^  0  | 1 ", 1},
   205  	{" 1 ^ (0  | 1)", 0},
   206  	{" 1 |  0  ^ 1 ", 1},
   207  	{"(1 |  0) ^ 1 ", 0},
   208  	// logical and
   209  	{" 0 &&  0  && 0 ", 0},
   210  	{" 0 &&  0  && 1 ", 0},
   211  	{" 0 &&  1  && 0 ", 0},
   212  	{" 1 &&  0  && 0 ", 0},
   213  	{" 1 &&  1  && 1 ", 1},
   214  	{" 1 |   1  && 0 ", 0},
   215  	{" 1 |  (1  && 0)", 1},
   216  	{" 0 &&  1  |  1 ", 0},
   217  	{"(0 &&  1) |  1 ", 1},
   218  	// logical or
   219  	{" 0 ||  0  || 0 ", 0},
   220  	{" 0 ||  0  || 1 ", 1},
   221  	{" 0 ||  1  || 0 ", 1},
   222  	{" 1 ||  0  || 0 ", 1},
   223  	{" 1 ||  1  || 1 ", 1},
   224  	{" 0 &&  0  || 1 ", 1},
   225  	{" 0 && (0  || 1)", 0},
   226  	{" 1 ||  0  && 0 ", 1},
   227  	{"(1 ||  0) && 0 ", 0},
   228  	// conditional
   229  	{"-1 == -1 ? -1 : 0 == 0 ? 0 : 1", -1},
   230  	{" 0 == -1 ? -1 : 0 == 0 ? 0 : 1", 0},
   231  	{" 1 == -1 ? -1 : 1 == 0 ? 0 : 1", 1},
   232  	// assignment
   233  	{"X   = 2", 2},
   234  	{"X  *= 2", 14},
   235  	{"X  /= 2", 3},
   236  	{"X  %= 2", 1},
   237  	{"X  += 2", 9},
   238  	{"X  -= 2", 5},
   239  	{"X <<= 2", 28},
   240  	{"X >>= 2", 1},
   241  	{"X  &= 2", 2},
   242  	{"X  ^= 2", 5},
   243  	{"X  |= 2", 7},
   244  }
   245  
   246  func TestEval(t *testing.T) {
   247  	env := interp.NewExecEnv(name)
   248  	env.Unset("_")
   249  	for _, tt := range evalTests {
   250  		env.Set("E", "")
   251  		env.Set("D", "4")
   252  		env.Set("O", "03")
   253  		env.Set("X", "0x7")
   254  		switch g, err := env.Eval(tt.expr); {
   255  		case err != nil:
   256  			t.Error("unexpected error:", err)
   257  		case g != tt.n:
   258  			t.Errorf("expected %v, got %v", tt.n, g)
   259  		}
   260  	}
   261  }
   262  
   263  var evalErrorTests = []struct {
   264  	expr string
   265  	err  string
   266  }{
   267  	// empty
   268  	{"", "unexpected EOF"},
   269  	// number
   270  	{"09", `invalid number "09"`},
   271  	{"0xz", `invalid number "0xz"`},
   272  	{"0 1 2", "unexpected NUMBER"},
   273  	// ident
   274  	{"A", `invalid number "alpha"`},
   275  	{"Z", `invalid number "0z777"`},
   276  	{"M N", "unexpected IDENT"},
   277  	// parenthesis
   278  	{"(", "unexpected EOF"},
   279  	{")", "unexpected ')'"},
   280  	// op
   281  	{"$", "unexpected '$'"},
   282  	{"++_--", "'++' requires lvalue"},
   283  	{"+++_", "'++' requires lvalue"},
   284  	{"++0--", "'++' requires lvalue"},
   285  	{"0++", "'++' requires lvalue"},
   286  	{"++0", "'++' requires lvalue"},
   287  	{"+++0", "'++' requires lvalue"},
   288  	{"--_++", "'--' requires lvalue"},
   289  	{"---_", "'--' requires lvalue"},
   290  	{"--0++", "'--' requires lvalue"},
   291  	{"0--", "'--' requires lvalue"},
   292  	{"--0", "'--' requires lvalue"},
   293  	{"---0", "'--' requires lvalue"},
   294  	{"<<", "unexpected '<<'"},
   295  	{">>", "unexpected '>>'"},
   296  	{"<", "unexpected '<'"},
   297  	{">", "unexpected '>'"},
   298  	{"<=", "unexpected '<='"},
   299  	{">=", "unexpected '>='"},
   300  	{"==", "unexpected '=='"},
   301  	{"!=", "unexpected '!='"},
   302  	{"&&", "unexpected '&&'"},
   303  	{"||", "unexpected '||'"},
   304  	{"=", "unexpected '='"},
   305  	{"*=", "unexpected '*='"},
   306  	{"/=", "unexpected '/='"},
   307  	{"%=", "unexpected '%='"},
   308  	{"+=", "unexpected '+='"},
   309  	{"-=", "unexpected '-='"},
   310  	{"<<=", "unexpected '<<='"},
   311  	{">>=", "unexpected '>>='"},
   312  	{"&=", "unexpected '&='"},
   313  	{"^=", "unexpected '^='"},
   314  	{"|=", "unexpected '|='"},
   315  	{"0   = 1", "'=' requires lvalue"},
   316  	{"0  *= 1", "'*=' requires lvalue"},
   317  	{"0  /= 1", "'/=' requires lvalue"},
   318  	{"0  %= 1", "'%=' requires lvalue"},
   319  	{"0  += 1", "'+=' requires lvalue"},
   320  	{"0  -= 1", "'-=' requires lvalue"},
   321  	{"0 <<= 1", "'<<=' requires lvalue"},
   322  	{"0 >>= 1", "'>>=' requires lvalue"},
   323  	{"0  &= 1", "'&=' requires lvalue"},
   324  	{"0  ^= 1", "'^=' requires lvalue"},
   325  	{"0  |= 1", "'|=' requires lvalue"},
   326  	// divide by zero
   327  	{"0 /  0", "integer divide by zero"},
   328  	{"0 %  0", "integer divide by zero"},
   329  	{"M /= 0", "integer divide by zero"},
   330  	{"M %= 0", "integer divide by zero"},
   331  	// negative shift
   332  	{"1 <<  -1", "negative shift amount"},
   333  	{"1 >>  -1", "negative shift amount"},
   334  	{"N <<= -1", "negative shift amount"},
   335  	{"N >>= -1", "negative shift amount"},
   336  }
   337  
   338  func TestEvalError(t *testing.T) {
   339  	env := interp.NewExecEnv(name)
   340  	env.Set("A", "alpha")
   341  	env.Set("M", "0")
   342  	env.Set("N", "1")
   343  	env.Set("Z", "0z777")
   344  	env.Unset("_")
   345  	for _, tt := range evalErrorTests {
   346  		switch _, err := env.Eval(tt.expr); {
   347  		case err == nil:
   348  			t.Error("expected error")
   349  		case err.Error() != tt.err:
   350  			t.Error("unexpected error:", err)
   351  		}
   352  	}
   353  }