mirror of
https://github.com/go-task/task.git
synced 2024-12-21 01:49:06 +02:00
179 lines
3.4 KiB
Go
179 lines
3.4 KiB
Go
// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
|
|
// See LICENSE for licensing information
|
|
|
|
package interp
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
|
|
"mvdan.cc/sh/syntax"
|
|
)
|
|
|
|
func (r *Runner) arithm(expr syntax.ArithmExpr) int {
|
|
switch x := expr.(type) {
|
|
case *syntax.Word:
|
|
str := r.loneWord(x)
|
|
// recursively fetch vars
|
|
for str != "" {
|
|
val := r.getVar(str)
|
|
if val == "" {
|
|
break
|
|
}
|
|
str = val
|
|
}
|
|
// default to 0
|
|
return atoi(str)
|
|
case *syntax.ParenArithm:
|
|
return r.arithm(x.X)
|
|
case *syntax.UnaryArithm:
|
|
switch x.Op {
|
|
case syntax.Inc, syntax.Dec:
|
|
name := x.X.(*syntax.Word).Parts[0].(*syntax.Lit).Value
|
|
old := atoi(r.getVar(name))
|
|
val := old
|
|
if x.Op == syntax.Inc {
|
|
val++
|
|
} else {
|
|
val--
|
|
}
|
|
r.setVarString(name, strconv.Itoa(val))
|
|
if x.Post {
|
|
return old
|
|
}
|
|
return val
|
|
}
|
|
val := r.arithm(x.X)
|
|
switch x.Op {
|
|
case syntax.Not:
|
|
return oneIf(val == 0)
|
|
case syntax.Plus:
|
|
return val
|
|
default: // syntax.Minus
|
|
return -val
|
|
}
|
|
case *syntax.BinaryArithm:
|
|
switch x.Op {
|
|
case syntax.Assgn, syntax.AddAssgn, syntax.SubAssgn,
|
|
syntax.MulAssgn, syntax.QuoAssgn, syntax.RemAssgn,
|
|
syntax.AndAssgn, syntax.OrAssgn, syntax.XorAssgn,
|
|
syntax.ShlAssgn, syntax.ShrAssgn:
|
|
return r.assgnArit(x)
|
|
case syntax.Quest: // Colon can't happen here
|
|
cond := r.arithm(x.X)
|
|
b2 := x.Y.(*syntax.BinaryArithm) // must have Op==Colon
|
|
if cond == 1 {
|
|
return r.arithm(b2.X)
|
|
}
|
|
return r.arithm(b2.Y)
|
|
}
|
|
return binArit(x.Op, r.arithm(x.X), r.arithm(x.Y))
|
|
default:
|
|
panic(fmt.Sprintf("unexpected arithm expr: %T", x))
|
|
}
|
|
}
|
|
|
|
func oneIf(b bool) int {
|
|
if b {
|
|
return 1
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// atoi is just a shorthand for strconv.Atoi that ignores the error,
|
|
// just like shells do.
|
|
func atoi(s string) int {
|
|
n, _ := strconv.Atoi(s)
|
|
return n
|
|
}
|
|
|
|
func (r *Runner) assgnArit(b *syntax.BinaryArithm) int {
|
|
name := b.X.(*syntax.Word).Parts[0].(*syntax.Lit).Value
|
|
val := atoi(r.getVar(name))
|
|
arg := r.arithm(b.Y)
|
|
switch b.Op {
|
|
case syntax.Assgn:
|
|
val = arg
|
|
case syntax.AddAssgn:
|
|
val += arg
|
|
case syntax.SubAssgn:
|
|
val -= arg
|
|
case syntax.MulAssgn:
|
|
val *= arg
|
|
case syntax.QuoAssgn:
|
|
val /= arg
|
|
case syntax.RemAssgn:
|
|
val %= arg
|
|
case syntax.AndAssgn:
|
|
val &= arg
|
|
case syntax.OrAssgn:
|
|
val |= arg
|
|
case syntax.XorAssgn:
|
|
val ^= arg
|
|
case syntax.ShlAssgn:
|
|
val <<= uint(arg)
|
|
case syntax.ShrAssgn:
|
|
val >>= uint(arg)
|
|
}
|
|
r.setVarString(name, strconv.Itoa(val))
|
|
return val
|
|
}
|
|
|
|
func intPow(a, b int) int {
|
|
p := 1
|
|
for b > 0 {
|
|
if b&1 != 0 {
|
|
p *= a
|
|
}
|
|
b >>= 1
|
|
a *= a
|
|
}
|
|
return p
|
|
}
|
|
|
|
func binArit(op syntax.BinAritOperator, x, y int) int {
|
|
switch op {
|
|
case syntax.Add:
|
|
return x + y
|
|
case syntax.Sub:
|
|
return x - y
|
|
case syntax.Mul:
|
|
return x * y
|
|
case syntax.Quo:
|
|
return x / y
|
|
case syntax.Rem:
|
|
return x % y
|
|
case syntax.Pow:
|
|
return intPow(x, y)
|
|
case syntax.Eql:
|
|
return oneIf(x == y)
|
|
case syntax.Gtr:
|
|
return oneIf(x > y)
|
|
case syntax.Lss:
|
|
return oneIf(x < y)
|
|
case syntax.Neq:
|
|
return oneIf(x != y)
|
|
case syntax.Leq:
|
|
return oneIf(x <= y)
|
|
case syntax.Geq:
|
|
return oneIf(x >= y)
|
|
case syntax.And:
|
|
return x & y
|
|
case syntax.Or:
|
|
return x | y
|
|
case syntax.Xor:
|
|
return x ^ y
|
|
case syntax.Shr:
|
|
return x >> uint(y)
|
|
case syntax.Shl:
|
|
return x << uint(y)
|
|
case syntax.AndArit:
|
|
return oneIf(x != 0 && y != 0)
|
|
case syntax.OrArit:
|
|
return oneIf(x != 0 || y != 0)
|
|
default: // syntax.Comma
|
|
// x is executed but its result discarded
|
|
return y
|
|
}
|
|
}
|