package subex import ( "main/walk" "errors" "strconv" ) func sumValues(values []walk.Value) ([]walk.Value, error) { allBools := true var sum float64 = 0 var any bool = false for _, value := range values { switch v := value.(type) { case walk.NullValue: allBools = false case walk.BoolValue: if v { sum += 1 any = true } case walk.NumberValue: allBools = false sum += float64(v) case walk.StringValue: 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.Value{walk.BoolValue(any)}, nil } else { return []walk.Value{walk.NumberValue(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.Value) ([]walk.Value, error) { allBools := true var product float64 = 1 var all bool = false for _, value := range values { switch v := value.(type) { case walk.NullValue: allBools = false product *= 0 case walk.BoolValue: if !v { product *= 0 all = false } case walk.NumberValue: allBools = false product *= float64(v) case walk.StringValue: 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.Value{walk.BoolValue(all)}, nil } else { return []walk.Value{walk.NumberValue(product)}, nil } } // Does tries to cast all to numbers and negates them func negateValues(values []walk.Value) ([]walk.Value, error) { var negatedNumbers []walk.Value for _, value := range values { switch v := value.(type) { case walk.NullValue: negatedNumbers = append(negatedNumbers, walk.NumberValue(0)) case walk.BoolValue: if v { negatedNumbers = append(negatedNumbers, walk.NumberValue(-1)) } else { negatedNumbers = append(negatedNumbers, walk.NumberValue(0)) } case walk.NumberValue: negatedNumbers = append(negatedNumbers, walk.NumberValue(-float64(v))) case walk.StringValue: num, err := strconv.ParseFloat(string(v), 64) if err == nil { negatedNumbers = append(negatedNumbers, walk.NumberValue(-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.Value) ([]walk.Value, error) { var reciprocals []walk.Value for _, value := range values { switch v := value.(type) { case walk.NullValue: return nil, errors.New("Tried to take reciprocal of null") case walk.BoolValue: if v { reciprocals = append(reciprocals, walk.NumberValue(1)) } else { return nil, errors.New("Tried to take reciprocal of false") } case walk.NumberValue: reciprocals = append(reciprocals, walk.NumberValue(1 / float64(v))) case walk.StringValue: num, err := strconv.ParseFloat(string(v), 64) if err == nil { reciprocals = append(reciprocals, walk.NumberValue(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.Value) (notted []walk.Value, err error) { for _, value := range values { switch v := value.(type) { case walk.NullValue: notted = append(notted, walk.BoolValue(true)) case walk.BoolValue: notted = append(notted, walk.BoolValue(!bool(v))) case walk.NumberValue: notted = append(notted, walk.BoolValue(v == 0)) case walk.StringValue: notted = append(notted, walk.BoolValue(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.Value) ([]walk.Value, error) { if len(values) == 0 { return []walk.Value{walk.BoolValue(true)}, nil } first := values[0] for _, value := range values[1:] { // TODO: Refine the equality check if value != first { return []walk.Value{walk.BoolValue(false)}, nil } } return []walk.Value{walk.BoolValue(true)}, nil }