-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
456 additions
and
443 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package eval | ||
|
||
const ( | ||
tokenTypeInt = 1 | ||
tokenTypeString = 2 | ||
tokenTypeFloat = 3 | ||
tokenTypeBoolean = 4 | ||
tokenTypeChar = 5 | ||
tokenTypeDate = 6 | ||
) | ||
|
||
type token struct { | ||
value []byte | ||
left *token | ||
right *token | ||
tokenType int | ||
} | ||
|
||
var ( | ||
//keywords = []string{"&&", "!&", "||", "!|", "==", "!="} | ||
strongSplit = []string{"&&", "!&", "||", "!|"} | ||
) | ||
|
||
func evaluate(f *token) *token { | ||
if f.left == nil && f.right == nil { | ||
return f | ||
} | ||
l := evaluate(f.left) | ||
m := f | ||
r := evaluate(f.right) | ||
switch string(m.value) { | ||
case "<": | ||
return less(l, r) | ||
case ">": | ||
return greater(l, r) | ||
case ">=": | ||
return greaterThan(l, r) | ||
case "<=": | ||
return lessThan(l, r) | ||
case "==": | ||
return equal(l, r) | ||
case "!=": | ||
return notEqual(l, r) | ||
case "&&": | ||
return and(l, r) | ||
case "||": | ||
return or(l, r) | ||
case "!&": | ||
return nand(l, r) | ||
case "!|": | ||
return nor(l, r) | ||
} | ||
panic("UNREACHABLE: NOT A VALID OPERATOR " + string(m.value)) | ||
return nil | ||
} | ||
|
||
func Eval(f string) bool { | ||
p := parse(f) | ||
t := toTree(p) | ||
if len(t) == 0 { | ||
panic("Error while eval") | ||
} | ||
e := evaluate(t[0]) | ||
if e == nil { | ||
panic("Eval returned nil") | ||
} | ||
if string(e.value) == "t" { | ||
return true | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package eval | ||
|
||
func less(l, r *token) *token { | ||
checkIfCompatible(l, r) | ||
if l.tokenType == tokenTypeDate && compareDates(string(l.value), string(r.value)) == 1 { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} else if convertBytesToFloat(l.value) < convertBytesToFloat(r.value) { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} | ||
return &token{value: []byte("f"), tokenType: tokenTypeBoolean} | ||
} | ||
|
||
func greater(l, r *token) *token { | ||
checkIfCompatible(l, r) | ||
if l.tokenType == tokenTypeDate && compareDates(string(l.value), string(r.value)) == -1 { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} else if convertBytesToFloat(l.value) > convertBytesToFloat(r.value) { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} | ||
return &token{value: []byte("f"), tokenType: tokenTypeBoolean} | ||
} | ||
|
||
func greaterThan(l, r *token) *token { | ||
checkIfCompatible(l, r) | ||
if l.tokenType == tokenTypeDate && compareDates(string(l.value), string(r.value)) <= 0 { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} else if convertBytesToFloat(l.value) >= convertBytesToFloat(r.value) { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} | ||
return &token{value: []byte("f"), tokenType: tokenTypeBoolean} | ||
} | ||
|
||
func lessThan(l, r *token) *token { | ||
checkIfCompatible(l, r) | ||
if l.tokenType == tokenTypeDate && compareDates(string(l.value), string(r.value)) >= 0 { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} else if convertBytesToFloat(l.value) <= convertBytesToFloat(r.value) { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} | ||
return &token{value: []byte("f"), tokenType: tokenTypeBoolean} | ||
} | ||
|
||
func equal(l, r *token) *token { | ||
checkIfCompatible(l, r) | ||
if l.tokenType == tokenTypeDate && compareDates(string(l.value), string(r.value)) == 0 { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} else if compareByteArray(l.value, r.value) { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} | ||
return &token{value: []byte("f"), tokenType: tokenTypeBoolean} | ||
} | ||
|
||
func notEqual(l, r *token) *token { | ||
checkIfCompatible(l, r) | ||
if l.tokenType == tokenTypeDate && compareDates(string(l.value), string(r.value)) != 0 { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} else if !compareByteArray(l.value, r.value) { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} | ||
return &token{value: []byte("f"), tokenType: tokenTypeBoolean} | ||
} | ||
|
||
func and(l, r *token) *token { | ||
if !(l.tokenType == tokenTypeBoolean && r.tokenType == tokenTypeBoolean) { | ||
panic("&& expects both sides to be a boolean") | ||
} | ||
if compareByteArray(l.value, []byte("t")) && compareByteArray(r.value, []byte("t")) { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} | ||
return &token{value: []byte("f"), tokenType: tokenTypeBoolean} | ||
} | ||
|
||
func or(l, r *token) *token { | ||
if !(l.tokenType == tokenTypeBoolean && r.tokenType == tokenTypeBoolean) { | ||
panic("&& expects both sides to be a boolean") | ||
} | ||
if compareByteArray(l.value, []byte("t")) || compareByteArray(r.value, []byte("t")) { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} | ||
return &token{value: []byte("f"), tokenType: tokenTypeBoolean} | ||
} | ||
|
||
func nand(l, r *token) *token { | ||
if !(l.tokenType == tokenTypeBoolean && r.tokenType == tokenTypeBoolean) { | ||
panic("&& expects both sides to be a boolean") | ||
} | ||
if !(compareByteArray(l.value, []byte("t")) && compareByteArray(r.value, []byte("t"))) { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} | ||
return &token{value: []byte("f"), tokenType: tokenTypeBoolean} | ||
} | ||
|
||
func nor(l, r *token) *token { | ||
if !(l.tokenType == tokenTypeBoolean && r.tokenType == tokenTypeBoolean) { | ||
panic("&& expects both sides to be a boolean") | ||
} | ||
if !(compareByteArray(l.value, []byte("t")) || compareByteArray(r.value, []byte("t"))) { | ||
return &token{value: []byte("t"), tokenType: tokenTypeBoolean} | ||
} | ||
return &token{value: []byte("f"), tokenType: tokenTypeBoolean} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
package eval | ||
|
||
import ( | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
func toTree(nodes []*token) []*token { | ||
var op string | ||
var query []*token | ||
var base []*token | ||
|
||
buildQuery := func() { | ||
nq := &token{ | ||
value: []byte(op), | ||
left: query[0], | ||
right: query[1], | ||
} | ||
query = []*token{} | ||
op = "" | ||
query = append(query, nq) | ||
} | ||
|
||
for i := 0; i < len(nodes); i++ { | ||
isOp := false | ||
for _, k := range strongSplit { | ||
if compareByteArray([]byte(k), nodes[i].value) { | ||
op = k | ||
isOp = true | ||
} | ||
} | ||
//handle braces | ||
if string(nodes[i].value) == "(" && !isOp { | ||
openCount := 1 | ||
var tmp []*token | ||
i += 1 | ||
for ; i < len(nodes); i++ { | ||
if string(nodes[i].value) == "(" { | ||
openCount++ | ||
} | ||
if string(nodes[i].value) == ")" { | ||
openCount-- | ||
if openCount == 0 { | ||
break | ||
} | ||
|
||
} | ||
tmp = append(tmp, nodes[i]) | ||
} | ||
if i == len(nodes) { | ||
panic("No ) found") | ||
} | ||
query = append(query, toTree(tmp)[0]) | ||
if len(query) == 2 { | ||
buildQuery() | ||
} | ||
continue | ||
} | ||
if !isOp { | ||
base = append(base, nodes[i]) | ||
} | ||
if len(base) == 3 { | ||
ne := &token{ | ||
value: base[1].value, | ||
left: &token{value: base[0].value, tokenType: base[0].tokenType}, | ||
right: &token{value: base[2].value, tokenType: base[2].tokenType}, | ||
} | ||
query = append(query, ne) | ||
if len(query) == 2 { | ||
buildQuery() | ||
} | ||
base = []*token{} | ||
} | ||
} | ||
return query | ||
} | ||
|
||
func traverseTree(t *token) { | ||
if t == nil { | ||
return | ||
} | ||
traverseTree(t.left) | ||
println(string(t.value)) | ||
traverseTree(t.right) | ||
} | ||
|
||
func parse(f string) []*token { | ||
var nodes []*token | ||
split := splitForStrings(f) | ||
if len(split) == 0 { | ||
split = strings.Split(f, " ") //no string in f. We can split normal | ||
} | ||
for _, v := range split { | ||
if strings.Contains(v, "'") { | ||
tokenType := tokenTypeString | ||
v = strings.ReplaceAll(v, "'", "") | ||
v = strings.TrimSpace(v) | ||
if len(v) == 0 { | ||
continue | ||
} | ||
//strings and chars both use ' '. The length decides if it's a char or a string | ||
if len(v) == 1 { | ||
tokenType = tokenTypeChar | ||
} | ||
nodes = append(nodes, &token{ | ||
value: []byte(v), | ||
left: nil, | ||
right: nil, | ||
tokenType: tokenType, | ||
}) | ||
continue | ||
} | ||
s := strings.Split(v, " ") | ||
for _, k := range s { | ||
var tokenType int | ||
k = strings.TrimSpace(k) | ||
if len(k) == 0 { | ||
continue | ||
} | ||
switch k { | ||
case "t", "f": | ||
tokenType = tokenTypeBoolean | ||
break | ||
default: | ||
if validateDate(k) { | ||
tokenType = tokenTypeDate | ||
} else if _, err := strconv.Atoi(k); err == nil { | ||
tokenType = tokenTypeInt | ||
} else if _, err = strconv.ParseFloat(k, 64); err == nil { | ||
tokenType = tokenTypeFloat | ||
} | ||
break | ||
} | ||
|
||
nodes = append(nodes, &token{ | ||
value: []byte(k), | ||
left: nil, | ||
right: nil, | ||
tokenType: tokenType, | ||
}) | ||
} | ||
} | ||
return nodes | ||
} |
Oops, something went wrong.