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  }