<- Back to shtanton's homepage
aboutsummaryrefslogtreecommitdiff
path: root/subex/arithmetic.go
blob: a7dc73a92b1ecd78e5cfac3b8c91e53e46498638 (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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
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.Compound(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.Compound(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.Compound(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.Compound(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
}

// If all are castable to booleans, NOTs all and returns them
// Else errors
func notValues(atoms []walk.Atom) (notted []walk.Atom, err error) {
	values, err := walk.Compound(atoms)
	if err != nil {
		return nil, err
	}
	for _, value := range values {
		switch v := value.(type) {
			case walk.ValueNull:
				notted = append(notted, walk.ValueBool(true))
			case walk.ValueBool:
				notted = append(notted, walk.ValueBool(!v))
			case walk.ValueNumber:
				notted = append(notted, walk.ValueBool(v == 0))
			case walk.ValueString:
				notted = append(notted, walk.ValueBool(len(v) == 0))
			default:
				return nil, errors.New("Tried to NOT non-boolean")
		}
	}
	return notted, nil
}