package subex import ( "main/walk" "errors" "strconv" ) func sumValues(values walk.ValueList) (walk.ValueList, error) { allBools := true var sum float64 = 0 var any bool = false for _, value := range values { switch v := value.(type) { case walk.NullScalar: allBools = false case walk.BoolScalar: if v { sum += 1 any = true } case walk.NumberScalar: allBools = false sum += float64(v) case walk.StringStructure: allBools = false num, err := strconv.ParseFloat(string(v), 64) if err == nil { sum += num } else { return nil, errors.New("Tried to sum non-castable string") } default: return nil, errors.New("Tried to sum non-number") } } if allBools { return walk.ValueList{walk.BoolScalar(any)}, nil } else { return walk.ValueList{walk.NumberScalar(sum)}, nil } } // Compounds atoms into values, if all values are booleans, does AND, if not, tries to cast to numbers and multiply func multiplyValues(values walk.ValueList) (walk.ValueList, error) { allBools := true var product float64 = 1 var all bool = false for _, value := range values { switch v := value.(type) { case walk.NullScalar: allBools = false product *= 0 case walk.BoolScalar: if !v { product *= 0 all = false } case walk.NumberScalar: allBools = false product *= float64(v) case walk.StringStructure: allBools = false num, err := strconv.ParseFloat(string(v), 64) if err == nil { product *= num } else { return nil, errors.New("Tried to multiply non-castable string") } default: return nil, errors.New("Tried to multiply non-number") } } if allBools { return walk.ValueList{walk.BoolScalar(all)}, nil } else { return walk.ValueList{walk.NumberScalar(product)}, nil } } // Does tries to cast all to numbers and negates them func negateValues(values walk.ValueList) (walk.ValueList, error) { var negatedNumbers walk.ValueList for _, value := range values { switch v := value.(type) { case walk.NullScalar: negatedNumbers = append(negatedNumbers, walk.NumberScalar(0)) case walk.BoolScalar: if v { negatedNumbers = append(negatedNumbers, walk.NumberScalar(-1)) } else { negatedNumbers = append(negatedNumbers, walk.NumberScalar(0)) } case walk.NumberScalar: negatedNumbers = append(negatedNumbers, walk.NumberScalar(-float64(v))) case walk.StringStructure: num, err := strconv.ParseFloat(string(v), 64) if err == nil { negatedNumbers = append(negatedNumbers, walk.NumberScalar(-num)) } else { return nil, errors.New("Tried to negate non-castable string") } default: return nil, errors.New("Tried to negate non-number") } } return negatedNumbers, nil } // If all are castable to numbers, takes reciprocals of all and returns them // Else errors func reciprocalValues(values walk.ValueList) (walk.ValueList, error) { var reciprocals walk.ValueList for _, value := range values { switch v := value.(type) { case walk.NullScalar: return nil, errors.New("Tried to take reciprocal of null") case walk.BoolScalar: if v { reciprocals = append(reciprocals, walk.NumberScalar(1)) } else { return nil, errors.New("Tried to take reciprocal of false") } case walk.NumberScalar: reciprocals = append(reciprocals, walk.NumberScalar(1 / float64(v))) case walk.StringStructure: num, err := strconv.ParseFloat(string(v), 64) if err == nil { reciprocals = append(reciprocals, walk.NumberScalar(1 / num)) } else { return nil, errors.New("Tried to take reciprocal of non-castable string") } default: return nil, errors.New("Tried to take reciprocal of non-number") } } return reciprocals, nil } // If all are castable to booleans, NOTs all and returns them // Else errors func notValues(values walk.ValueList) (notted walk.ValueList, err error) { for _, value := range values { switch v := value.(type) { case walk.NullScalar: notted = append(notted, walk.BoolScalar(true)) case walk.BoolScalar: notted = append(notted, walk.BoolScalar(!bool(v))) case walk.NumberScalar: notted = append(notted, walk.BoolScalar(v == 0)) case walk.StringStructure: notted = append(notted, walk.BoolScalar(len(v) == 0)) default: return nil, errors.New("Tried to NOT non-boolean") } } return notted, nil } // Returns true if all values are equal, false if not func equalValues(values walk.ValueList) (walk.ValueList, error) { if len(values) == 0 { return walk.ValueList{walk.BoolScalar(true)}, nil } first := values[0] for _, value := range values[1:] { // TODO: Refine the equality check if value != first { return walk.ValueList{walk.BoolScalar(false)}, nil } } return walk.ValueList{walk.BoolScalar(true)}, nil }