Skip to content

Commit

Permalink
clean up, avg and sum function
Browse files Browse the repository at this point in the history
  • Loading branch information
ax4w committed Dec 27, 2023
1 parent 97d0f2e commit 46088e5
Show file tree
Hide file tree
Showing 10 changed files with 456 additions and 443 deletions.
430 changes: 0 additions & 430 deletions eval.go

This file was deleted.

71 changes: 71 additions & 0 deletions eval/eval.go
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
}
12 changes: 6 additions & 6 deletions eval_test.go → eval/eval_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package Gorage
package eval

import (
"testing"
Expand All @@ -17,19 +17,19 @@ import (
//

func TestEval(t *testing.T) {
if !eval("( 'William' == 'William' && 2 == 2 ) || 85.5 >= 90.0") {
if !Eval("( 'William' == 'William' && 2 == 2 ) || 85.5 >= 90.0") {
t.Fatalf("Should return true")
}
if eval("1 != 1") {
if Eval("1 != 1") {
t.Fatalf("Should be false")
}
if !eval("( 'Hi' == 'hi' ) || ( 1 == 1 && ( 5 != 5 !& ( t == f ) ) ) && 1.0 < 1.1") {
if !Eval("( 'Hi' == 'hi' ) || ( 1 == 1 && ( 5 != 5 !& ( t == f ) ) ) && 1.0 < 1.1") {
t.Fatalf("Should be true")
}
if !eval("2023-11-19 == 2023-11-19") {
if !Eval("2023-11-19 == 2023-11-19") {
t.Fatalf("Should be true")
}
if !eval("2022-11-19 <= 2023-11-19") {
if !Eval("2022-11-19 <= 2023-11-19") {
t.Fatalf("Should be true")
}
}
101 changes: 101 additions & 0 deletions eval/operations.go
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}
}
144 changes: 144 additions & 0 deletions eval/parser.go
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
}
Loading

0 comments on commit 46088e5

Please sign in to comment.