package subex import ( "main/walk" "errors" "strconv" ) func sumValues(atoms []walk.Atom) ([]walk.Atom, error) { allBools := true var sum float64 = 0 var any bool = false values, err := walk.MemoryCompound(atoms) if err != nil { return nil, err } for _, value := range values { switch v := value.(type) { case walk.ValueNull: allBools = false case walk.ValueBool: if bool(v) { sum += 1 any = true } case walk.ValueNumber: allBools = false sum += float64(v) case walk.ValueString: 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.Atom{walk.ValueBool(any)}, nil } else { return []walk.Atom{walk.ValueNumber(sum)}, nil } } // Compounds atoms into values, if all values are booleans, does AND, if not, tries to cast to numbers and multiply func multiplyValues(atoms []walk.Atom) ([]walk.Atom, error) { allBools := true var product float64 = 1 var all bool = false values, err := walk.MemoryCompound(atoms) if err != nil { return nil, err } for _, value := range values { switch v := value.(type) { case walk.ValueNull: allBools = false product *= 0 case walk.ValueBool: if !bool(v) { product *= 0 all = false } case walk.ValueNumber: allBools = false product *= float64(v) case walk.ValueString: 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.Atom{walk.ValueBool(all)}, nil } else { return []walk.Atom{walk.ValueNumber(product)}, nil } } // Does tries to cast all to numbers and negates them func negateValues(atoms []walk.Atom) ([]walk.Atom, error) { var negatedNumbers []walk.Atom values, err := walk.MemoryCompound(atoms) if err != nil { return nil, err } for _, value := range values { switch v := value.(type) { case walk.ValueNull: negatedNumbers = append(negatedNumbers, walk.ValueNumber(0)) case walk.ValueBool: if bool(v) { negatedNumbers = append(negatedNumbers, walk.ValueNumber(-1)) } else { negatedNumbers = append(negatedNumbers, walk.ValueNumber(0)) } case walk.ValueNumber: negatedNumbers = append(negatedNumbers, walk.ValueNumber(-v)) case walk.ValueString: num, err := strconv.ParseFloat(string(v), 64) if err == nil { negatedNumbers = append(negatedNumbers, walk.ValueNumber(-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(atoms []walk.Atom) ([]walk.Atom, error) { var reciprocals []walk.Atom values, err := walk.MemoryCompound(atoms) if err != nil { return nil, err } for _, value := range values { switch v := value.(type) { case walk.ValueNull: return nil, errors.New("Tried to take reciprocal of null") case walk.ValueBool: if bool(v) { reciprocals = append(reciprocals, walk.ValueNumber(1)) } else { return nil, errors.New("Tried to take reciprocal of false") } case walk.ValueNumber: reciprocals = append(reciprocals, walk.ValueNumber(1 / v)) case walk.ValueString: num, err := strconv.ParseFloat(string(v), 64) if err == nil { reciprocals = append(reciprocals, walk.ValueNumber(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 }