<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/subex/arithmetic.go
blob: ff30a5846a44ed152a099cff7f7e71e75103959b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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
}