github.com/hattya/go.sh@v0.0.0-20240328132134-f53276d95cc6/interp/arith.go.y (about) 1 %{ 2 // 3 // go.sh/interp :: arith.go 4 // 5 // Copyright (c) 2021 Akinori Hattori <hattya@gmail.com> 6 // 7 // SPDX-License-Identifier: MIT 8 // 9 10 package interp 11 12 import ( 13 "fmt" 14 "strconv" 15 "strings" 16 ) 17 %} 18 19 %union { 20 op string 21 expr expr 22 } 23 24 %token<expr> NUMBER IDENT 25 %token<op> '(' ')' 26 %token<op> INC DEC '+' '-' '~' '!' 27 %token<op> '*' '/' '%' LSH RSH '<' '>' LE GE EQ NE '&' '^' '|' LAND LOR 28 %token<op> '?' ':' 29 %token<op> '=' MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN SUB_ASSIGN LSH_ASSIGN RSH_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN 30 31 %type<expr> primary_expr 32 %type<expr> postfix_expr unary_expr 33 %type<op> unary_op 34 %type<expr> mul_expr add_expr shift_expr rel_expr eq_expr and_expr xor_expr or_expr land_expr lor_expr 35 %type<expr> cond_expr 36 %type<expr> expr 37 %type<op> assign_op 38 39 %right '=' MUL_ASSIGN DIV_ASSIGN MOD_ASSIGN ADD_ASSIGN SUB_ASSIGN LSH_ASSIGN RSH_ASSIGN AND_ASSIGN XOR_ASSIGN OR_ASSIGN 40 %right '?' 41 %left LOR 42 %left LAND 43 %left '|' 44 %left '^' 45 %left '&' 46 %left EQ NE 47 %left '<' '>' LE GE 48 %left LSH RSH 49 %left '+' '-' 50 %left '*' '/' '%' 51 %right INC DEC 52 53 %% 54 55 arith: 56 expr 57 { 58 if n, ok := expand(yylex, $1); ok { 59 yylex.(*lexer).n = n 60 } 61 } 62 63 primary_expr: 64 NUMBER 65 { 66 $$.s = "" 67 if n, err := strconv.ParseInt($1.s, 0, 0); err != nil { 68 yylex.Error(fmt.Sprintf("invalid number %q", $1.s)) 69 } else { 70 $$.n = int(n) 71 } 72 } 73 | IDENT 74 | '(' expr ')' 75 { 76 $$ = $2 77 } 78 79 postfix_expr: 80 primary_expr 81 | postfix_expr INC 82 { 83 $$.s = "" 84 if $1.s == "" { 85 yylex.Error(errLValue($2)) 86 } else if n, ok := expand(yylex, $1); ok { 87 $$.n = n 88 yylex.(*lexer).env.Set($1.s, strconv.Itoa($$.n + 1)) 89 } 90 } 91 | postfix_expr DEC 92 { 93 $$.s = "" 94 if $1.s == "" { 95 yylex.Error(errLValue($2)) 96 } else if n, ok := expand(yylex, $1); ok { 97 $$.n = n 98 yylex.(*lexer).env.Set($1.s, strconv.Itoa($$.n - 1)) 99 } 100 } 101 102 unary_expr: 103 postfix_expr 104 | INC unary_expr 105 { 106 $$.s = "" 107 if $2.s == "" { 108 yylex.Error(errLValue($1)) 109 } else if n, ok := expand(yylex, $2); ok { 110 $$.n = n + 1 111 yylex.(*lexer).env.Set($2.s, strconv.Itoa($$.n)) 112 } 113 } 114 | DEC unary_expr 115 { 116 $$.s = "" 117 if $2.s == "" { 118 yylex.Error(errLValue($1)) 119 } else if n, ok := expand(yylex, $2); ok { 120 $$.n = n - 1 121 yylex.(*lexer).env.Set($2.s, strconv.Itoa($$.n)) 122 } 123 } 124 | unary_op unary_expr 125 { 126 $$.s = "" 127 if n, ok := expand(yylex, $2); ok { 128 switch $1 { 129 case "+": 130 $$.n = +n 131 case "-": 132 $$.n = -n 133 case "~": 134 $$.n = ^n 135 case "!": 136 if n == 0 { 137 $$.n = 1 138 } else { 139 $$.n = 0 140 } 141 } 142 } 143 } 144 145 unary_op: 146 '+' 147 | '-' 148 | '~' 149 | '!' 150 151 mul_expr: 152 unary_expr 153 | mul_expr '*' unary_expr 154 { 155 $$, _ = calculate(yylex, $1, $2, $3) 156 } 157 | mul_expr '/' unary_expr 158 { 159 $$, _ = calculate(yylex, $1, $2, $3) 160 } 161 | mul_expr '%' unary_expr 162 { 163 $$, _ = calculate(yylex, $1, $2, $3) 164 } 165 166 add_expr: 167 mul_expr 168 | add_expr '+' mul_expr 169 { 170 $$, _ = calculate(yylex, $1, $2, $3) 171 } 172 | add_expr '-' mul_expr 173 { 174 $$, _ = calculate(yylex, $1, $2, $3) 175 } 176 177 shift_expr: 178 add_expr 179 | shift_expr LSH add_expr 180 { 181 $$, _ = calculate(yylex, $1, $2, $3) 182 } 183 | shift_expr RSH add_expr 184 { 185 $$, _ = calculate(yylex, $1, $2, $3) 186 } 187 188 rel_expr: 189 shift_expr 190 | rel_expr '<' shift_expr 191 { 192 $$ = compare(yylex, $1, $2, $3) 193 } 194 | rel_expr '>' shift_expr 195 { 196 $$ = compare(yylex, $1, $2, $3) 197 } 198 | rel_expr LE shift_expr 199 { 200 $$ = compare(yylex, $1, $2, $3) 201 } 202 | rel_expr GE shift_expr 203 { 204 $$ = compare(yylex, $1, $2, $3) 205 } 206 207 eq_expr: 208 rel_expr 209 | eq_expr EQ rel_expr 210 { 211 $$ = compare(yylex, $1, $2, $3) 212 } 213 | eq_expr NE rel_expr 214 { 215 $$ = compare(yylex, $1, $2, $3) 216 } 217 218 and_expr: 219 eq_expr 220 | and_expr '&' eq_expr 221 { 222 $$, _ = calculate(yylex, $1, $2, $3) 223 } 224 225 xor_expr: 226 and_expr 227 | xor_expr '^' and_expr 228 { 229 $$, _ = calculate(yylex, $1, $2, $3) 230 } 231 232 or_expr: 233 xor_expr 234 | or_expr '|' xor_expr 235 { 236 $$, _ = calculate(yylex, $1, $2, $3) 237 } 238 239 land_expr: 240 or_expr 241 | land_expr LAND or_expr 242 { 243 $$.n = 0 244 $$.s = "" 245 if l, ok := expand(yylex, $1); ok && l != 0 { 246 if r, ok := expand(yylex, $3); ok && r != 0 { 247 $$.n = 1 248 } 249 } 250 } 251 252 lor_expr: 253 land_expr 254 | lor_expr LOR land_expr 255 { 256 $$.n = 0 257 $$.s = "" 258 if l, ok := expand(yylex, $1); ok && l != 0 { 259 $$.n = 1 260 } else if r, ok := expand(yylex, $3); ok && r != 0 { 261 $$.n = 1 262 } 263 } 264 265 cond_expr: 266 lor_expr 267 | lor_expr '?' expr ':' cond_expr 268 { 269 $$.s = "" 270 if l, ok := expand(yylex, $1); ok { 271 if l != 0 { 272 $$.n, _ = expand(yylex, $3) 273 } else { 274 $$.n, _ = expand(yylex, $5) 275 } 276 } 277 } 278 279 expr: 280 cond_expr 281 | unary_expr assign_op expr 282 { 283 $$.s = "" 284 if $1.s == "" { 285 yylex.Error(errLValue($2)) 286 } else { 287 var ok bool 288 if $2 == "=" { 289 $$.n, ok = expand(yylex, $3) 290 } else { 291 $$, ok = calculate(yylex, $1, $2[:len($2)-1], $3) 292 } 293 if ok { 294 yylex.(*lexer).env.Set($1.s, strconv.Itoa($$.n)) 295 } 296 } 297 } 298 299 assign_op: 300 '=' 301 | MUL_ASSIGN 302 | DIV_ASSIGN 303 | MOD_ASSIGN 304 | ADD_ASSIGN 305 | SUB_ASSIGN 306 | LSH_ASSIGN 307 | RSH_ASSIGN 308 | AND_ASSIGN 309 | XOR_ASSIGN 310 | OR_ASSIGN 311 312 %% 313 314 func init() { 315 yyErrorVerbose = true 316 317 for i, s := range yyToknames { 318 switch s { 319 case "$end": 320 s = "EOF" 321 case "INC": 322 s = "'++'" 323 case "DEC": 324 s = "'--'" 325 case "LSH": 326 s = "'<<'" 327 case "RSH": 328 s = "'>>'" 329 case "LE": 330 s = "'<='" 331 case "GE": 332 s = "'>='" 333 case "EQ": 334 s = "'=='" 335 case "NE": 336 s = "'!='" 337 case "LAND": 338 s = "'&&'" 339 case "LOR": 340 s = "'||'" 341 case "MUL_ASSIGN": 342 s = "'*='" 343 case "DIV_ASSIGN": 344 s = "'/='" 345 case "MOD_ASSIGN": 346 s = "'%='" 347 case "ADD_ASSIGN": 348 s = "'+='" 349 case "SUB_ASSIGN": 350 s = "'-='" 351 case "LSH_ASSIGN": 352 s = "'<<='" 353 case "RSH_ASSIGN": 354 s = "'>>='" 355 case "AND_ASSIGN": 356 s = "'&='" 357 case "XOR_ASSIGN": 358 s = "'^='" 359 case "OR_ASSIGN": 360 s = "'|='" 361 } 362 yyToknames[i] = s 363 } 364 } 365 366 type expr struct { 367 n int 368 s string 369 } 370 371 func errLValue(op string) string { 372 return fmt.Sprintf("'%v' requires lvalue", op) 373 } 374 375 func expand(yylex yyLexer, x expr) (int, bool) { 376 if x.s == "" { 377 return x.n, true 378 } else if v, set := yylex.(*lexer).env.Get(x.s); !set || v.Value == "" { 379 return 0, true 380 } else if n, err := strconv.ParseInt(v.Value, 0, 0); err != nil { 381 yylex.Error(fmt.Sprintf("invalid number %q", v.Value)) 382 return 0, false 383 } else { 384 return int(n), true 385 } 386 } 387 388 func calculate(yylex yyLexer, l expr, op string, r expr) (x expr, ok bool) { 389 if l, ok1 := expand(yylex, l); ok1 { 390 if r, ok2 := expand(yylex, r); ok2 { 391 ok = true 392 switch op { 393 case "*": 394 x.n = l * r 395 case "/": 396 x.n = l / r 397 case "%": 398 x.n = l % r 399 case "+": 400 x.n = l + r 401 case "-": 402 x.n = l - r 403 case "<<": 404 x.n = l << r 405 case ">>": 406 x.n = l >> r 407 case "&": 408 x.n = l & r 409 case "^": 410 x.n = l ^ r 411 case "|": 412 x.n = l | r 413 } 414 } 415 } 416 return 417 } 418 419 func compare(yylex yyLexer, l expr, op string, r expr) (x expr) { 420 if l, ok := expand(yylex, l); ok { 421 if r, ok := expand(yylex, r); ok { 422 var b bool 423 switch op { 424 case "<": 425 b = l < r 426 case ">": 427 b = l > r 428 case "<=": 429 b = l <= r 430 case ">=": 431 b = l >= r 432 case "==": 433 b = l == r 434 case "!=": 435 b = l != r 436 } 437 if b { 438 x.n = 1 439 } 440 } 441 } 442 return 443 } 444 445 // Eval evaluates an arithmetic expression. 446 func (env *ExecEnv) Eval(expr string) (n int, err error) { 447 l := newLexer(env, strings.NewReader(expr)) 448 defer func() { 449 if e := recover(); e != nil { 450 l.Error(e.(error).Error()) 451 err = l.err 452 } 453 }() 454 455 yyParse(l) 456 return l.n, l.err 457 }