stred-go

Stred: Streaming Tree Editor. Like sed but for JSON. This is the go implementation
git clone https://shtanton.xyz/git/stred-go.git
Log | Files | Refs | README

arithmetic.go (4866B)


      1 package subex
      2 
      3 import (
      4 	"main/walk"
      5 	"errors"
      6 	"strconv"
      7 )
      8 
      9 func sumValues(values walk.ValueList) (walk.ValueList, error) {
     10 	allBools := true
     11 	var sum float64 = 0
     12 	var any bool = false
     13 	for _, value := range values {
     14 		switch v := value.(type) {
     15 			case walk.NullScalar:
     16 				allBools = false
     17 			case walk.BoolScalar:
     18 				if v {
     19 					sum += 1
     20 					any = true
     21 				}
     22 			case walk.NumberScalar:
     23 				allBools = false
     24 				sum += float64(v)
     25 			case walk.StringStructure:
     26 				allBools = false
     27 				num, err := strconv.ParseFloat(string(v), 64)
     28 				if err == nil {
     29 					sum += num
     30 				} else {
     31 					return nil, errors.New("Tried to sum non-castable string")
     32 				}
     33 			default:
     34 				return nil, errors.New("Tried to sum non-number")
     35 		}
     36 	}
     37 	if allBools {
     38 		return walk.ValueList{walk.BoolScalar(any)}, nil
     39 	} else {
     40 		return walk.ValueList{walk.NumberScalar(sum)}, nil
     41 	}
     42 }
     43 
     44 // Compounds atoms into values, if all values are booleans, does AND, if not, tries to cast to numbers and multiply
     45 func multiplyValues(values walk.ValueList) (walk.ValueList, error) {
     46 	allBools := true
     47 	var product float64 = 1
     48 	var all bool = false
     49 	for _, value := range values {
     50 		switch v := value.(type) {
     51 			case walk.NullScalar:
     52 				allBools = false
     53 				product *= 0
     54 			case walk.BoolScalar:
     55 				if !v {
     56 					product *= 0
     57 					all = false
     58 				}
     59 			case walk.NumberScalar:
     60 				allBools = false
     61 				product *= float64(v)
     62 			case walk.StringStructure:
     63 				allBools = false
     64 				num, err := strconv.ParseFloat(string(v), 64)
     65 				if err == nil {
     66 					product *= num
     67 				} else {
     68 					return nil, errors.New("Tried to multiply non-castable string")
     69 				}
     70 			default:
     71 				return nil, errors.New("Tried to multiply non-number")
     72 		}
     73 	}
     74 	if allBools {
     75 		return walk.ValueList{walk.BoolScalar(all)}, nil
     76 	} else {
     77 		return walk.ValueList{walk.NumberScalar(product)}, nil
     78 	}
     79 }
     80 
     81 // Does tries to cast all to numbers and negates them
     82 func negateValues(values walk.ValueList) (walk.ValueList, error) {
     83 	var negatedNumbers walk.ValueList
     84 	for _, value := range values {
     85 		switch v := value.(type) {
     86 			case walk.NullScalar:
     87 				negatedNumbers = append(negatedNumbers, walk.NumberScalar(0))
     88 			case walk.BoolScalar:
     89 				if v {
     90 					negatedNumbers = append(negatedNumbers, walk.NumberScalar(-1))
     91 				} else {
     92 					negatedNumbers = append(negatedNumbers, walk.NumberScalar(0))
     93 				}
     94 			case walk.NumberScalar:
     95 				negatedNumbers = append(negatedNumbers, walk.NumberScalar(-float64(v)))
     96 			case walk.StringStructure:
     97 				num, err := strconv.ParseFloat(string(v), 64)
     98 				if err == nil {
     99 					negatedNumbers = append(negatedNumbers, walk.NumberScalar(-num))
    100 				} else {
    101 					return nil, errors.New("Tried to negate non-castable string")
    102 				}
    103 			default:
    104 				return nil, errors.New("Tried to negate non-number")
    105 		}
    106 	}
    107 	return negatedNumbers, nil
    108 }
    109 
    110 // If all are castable to numbers, takes reciprocals of all and returns them
    111 // Else errors
    112 func reciprocalValues(values walk.ValueList) (walk.ValueList, error) {
    113 	var reciprocals walk.ValueList
    114 	for _, value := range values {
    115 		switch v := value.(type) {
    116 			case walk.NullScalar:
    117 				return nil, errors.New("Tried to take reciprocal of null")
    118 			case walk.BoolScalar:
    119 				if v {
    120 					reciprocals = append(reciprocals, walk.NumberScalar(1))
    121 				} else {
    122 					return nil, errors.New("Tried to take reciprocal of false")
    123 				}
    124 			case walk.NumberScalar:
    125 				reciprocals = append(reciprocals, walk.NumberScalar(1 / float64(v)))
    126 			case walk.StringStructure:
    127 				num, err := strconv.ParseFloat(string(v), 64)
    128 				if err == nil {
    129 					reciprocals = append(reciprocals, walk.NumberScalar(1 / num))
    130 				} else {
    131 					return nil, errors.New("Tried to take reciprocal of non-castable string")
    132 				}
    133 			default:
    134 				return nil, errors.New("Tried to take reciprocal of non-number")
    135 		}
    136 	}
    137 	return reciprocals, nil
    138 }
    139 
    140 // If all are castable to booleans, NOTs all and returns them
    141 // Else errors
    142 func notValues(values walk.ValueList) (notted walk.ValueList, err error) {
    143 	for _, value := range values {
    144 		switch v := value.(type) {
    145 			case walk.NullScalar:
    146 				notted = append(notted, walk.BoolScalar(true))
    147 			case walk.BoolScalar:
    148 				notted = append(notted, walk.BoolScalar(!bool(v)))
    149 			case walk.NumberScalar:
    150 				notted = append(notted, walk.BoolScalar(v == 0))
    151 			case walk.StringStructure:
    152 				notted = append(notted, walk.BoolScalar(len(v) == 0))
    153 			default:
    154 				return nil, errors.New("Tried to NOT non-boolean")
    155 		}
    156 	}
    157 	return notted, nil
    158 }
    159 
    160 // Returns true if all values are equal, false if not
    161 func equalValues(values walk.ValueList) (walk.ValueList, error) {
    162 	if len(values) == 0 {
    163 		return walk.ValueList{walk.BoolScalar(true)}, nil
    164 	}
    165 	first := values[0]
    166 	for _, value := range values[1:] {
    167 		// TODO: Refine the equality check
    168 		if value != first {
    169 			return walk.ValueList{walk.BoolScalar(false)}, nil
    170 		}
    171 	}
    172 	return walk.ValueList{walk.BoolScalar(true)}, nil
    173 }