Skip to content

Commit

Permalink
remove solver order (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
pedroegsilva authored Jan 26, 2022
1 parent 5fc1a53 commit ef6e4fd
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 288 deletions.
25 changes: 0 additions & 25 deletions benchmarks/benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,6 @@ func BenchmarkSolverPartialMap100(b *testing.B) {
BMSolver(exp100, sortedMatchesByKeywordPart100, b)
}

func BenchmarkSolverIterCompleteMap100(b *testing.B) {
BMSolverIter(exp100, sortedMatchesByKeywordComp100, b)
}

func BenchmarkSolverIterPartialMap100(b *testing.B) {
BMSolverIter(exp100, sortedMatchesByKeywordPart100, b)
}

func BenchmarkAhocorasickCloudFlareBuild100(b *testing.B) {
BMCloudFlareBuild(exp100, b)
}
Expand Down Expand Up @@ -155,14 +147,6 @@ func BenchmarkSolverPartialMap10000(b *testing.B) {
BMSolver(exp10000, sortedMatchesByKeywordPart10000, b)
}

func BenchmarkSolverIterCompleteMap10000(b *testing.B) {
BMSolverIter(exp10000, sortedMatchesByKeywordComp10000, b)
}

func BenchmarkSolverIterPartialMap10000(b *testing.B) {
BMSolverIter(exp10000, sortedMatchesByKeywordPart10000, b)
}

func BenchmarkAhocorasickCloudFlareBuild10000(b *testing.B) {
BMCloudFlareBuild(exp10000, b)
}
Expand Down Expand Up @@ -331,15 +315,6 @@ func BMSolver(exp string, sortedMatchesByKeyword map[string][]int, b *testing.B)
}
}

func BMSolverIter(exp string, sortedMatchesByKeyword map[string][]int, b *testing.B) {
p := dsl.NewParser(strings.NewReader(exp), true)
e, _ := p.Parse()
so := e.CreateSolverOrder()
for i := 0; i < b.N; i++ {
so.Solve(sortedMatchesByKeyword)
}
}

func BMCloudFlareBuild(exp string, b *testing.B) {
p := dsl.NewParser(strings.NewReader(exp), true)
p.Parse()
Expand Down
106 changes: 0 additions & 106 deletions dsl/expression.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,112 +170,6 @@ func (exp *Expression) prettyFormat(lvl int) (pprint string) {
return
}

// SolverOrder store the expressions Preorder
type SolverOrder []*Expression

type valAndPos struct {
Val bool
Pos []int
}

// Solve solves the expresion iteratively. It has the option to use a complete map of
// PatternResult or a incomplete map. If the complete map option is used the map must have
// all the terms needed to solve de expression or it will return an error.
// If the incomplete map is used, missing keys will be considered as a no match on the
// document.
func (so SolverOrder) Solve(sortedMatchesByKeyword map[string][]int) (bool, error) {
values := make(map[*Expression]valAndPos)
for i := len(so) - 1; i >= 0; i-- {
exp := so[i]
if exp == nil {
return false, fmt.Errorf("malformed solver order - solver order should not have nil values")
}
switch exp.Type {
case UNIT_EXPR:
if sortedMatches, ok := sortedMatchesByKeyword[exp.Literal]; ok {
values[exp] = valAndPos{
Val: true,
Pos: sortedMatches,
}
} else {
values[exp] = valAndPos{Val: false}
}

case AND_EXPR:
l, lOk := values[exp.LExpr]
r, rOk := values[exp.RExpr]
if !lOk || !rOk {
return false, fmt.Errorf("AND statement do not have right or left expression: %v", exp)
}
vap := valAndPos{Val: l.Val && r.Val}

if exp.Inord {
lpos := l.Pos
rpos := r.Pos
if exp.Inord && len(lpos) > 0 && len(rpos) > 0 {
idx := getLowestIdxGTVal(rpos, lpos[0])
if idx >= 0 {
vap.Pos = rpos[idx:]
}
}
}
values[exp] = vap

case OR_EXPR:
l, lOk := values[exp.LExpr]
r, rOk := values[exp.RExpr]
if !lOk || !rOk {
return false, fmt.Errorf("OR statement do not have right or left expression: %v", exp)
}
vap := valAndPos{Val: l.Val || r.Val}
if exp.Inord {
vap.Pos = mergeArraysSorted(l.Pos, r.Pos)
}
values[exp] = vap

case NOT_EXPR:
r, rOk := values[exp.RExpr]
if !rOk {
return false, fmt.Errorf("NOT statement do not have expression: %v", exp)
}
values[exp] = valAndPos{Val: !r.Val}

case INORD_EXPR:
r, rOk := values[exp.RExpr]
if !rOk {
return false, fmt.Errorf("INORD statement do not have expression: %v", exp)
}
values[exp] = valAndPos{Val: r.Val && len(r.Pos) > 0}
default:
return false, fmt.Errorf("unable to process expression type %d", exp.Type)
}
}
return values[so[0]].Val, nil
}

// CreateSolverOrder traverses the expression tree in Preorder and
// stores the expressions on SolverOrder
func (exp *Expression) CreateSolverOrder() *SolverOrder {
solverOrder := new(SolverOrder)
cpExp := exp
createSolverOrder(cpExp, solverOrder)
return solverOrder
}

// createSolverOrder recursion that traverses the expression
// tree in Preorder
func createSolverOrder(exp *Expression, arr *SolverOrder) {
(*arr) = append((*arr), exp)

if exp.LExpr != nil {
createSolverOrder(exp.LExpr, arr)
}

if exp.RExpr != nil {
createSolverOrder(exp.RExpr, arr)
}
}

// getLowestIdxGTVal uses binary search to find the
// index of the lowest element that is greater than 'value'
func getLowestIdxGTVal(positions []int, value int) int {
Expand Down
78 changes: 0 additions & 78 deletions dsl/expression_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,84 +18,6 @@ func TestSolver(t *testing.T) {
}
}

func TestSolverInter(t *testing.T) {
assert := assert.New(t)
for _, tc := range solverTestCases {
exp, err := NewParser(strings.NewReader(tc.expStr), true).Parse()
arr := exp.CreateSolverOrder()
assert.Nil(err, tc.message+" iter")
respInt, err := arr.Solve(tc.sortedMatchesByKeyword)
assert.Nil(err, tc.message+" iter")
assert.Equal(tc.expectedResp, respInt, tc.message+" iter")
}
}

// TODO(pedro.silva) create unit test for CreateSolverOrder
func TestCreateSolverOrder(t *testing.T) {
assert := assert.New(t)

unit1 := &Expression{
Type: UNIT_EXPR,
Literal: "sharpest",
}
unit2 := &Expression{
Type: UNIT_EXPR,
Literal: "words",
}
unit3 := &Expression{
Type: UNIT_EXPR,
Literal: "no one",
}

expr1 := &Expression{
Type: OR_EXPR,
LExpr: unit2,
RExpr: unit3,
}

//"sharpest" and "words" or "no one"
exp1 := &Expression{
Type: AND_EXPR,
LExpr: unit1,
RExpr: expr1,
}

notExp := &Expression{
Type: NOT_EXPR,
RExpr: unit1,
}
// Not "sharpest" and "words" or "no one"
exp2 := &Expression{
Type: AND_EXPR,
LExpr: notExp,
RExpr: expr1,
}

tests := []struct {
exp *Expression
expectedArr *SolverOrder
message string
}{
{
exp: exp1,
expectedArr: &SolverOrder{exp1, unit1, expr1, unit2, unit3},
message: `"sharpest" and "words" or "no one"`,
},
{
exp: exp2,
expectedArr: &SolverOrder{exp2, notExp, unit1, expr1, unit2, unit3},
message: `Not "sharpest" and "words" or "no one"`,
},
}
for _, tc := range tests {
firstExp := tc.exp
arr := tc.exp.CreateSolverOrder()
assert.Equal(tc.expectedArr, arr, tc.message)
// Asserts that the expression was not changed by the createSolver order
assert.Equal(firstExp, tc.exp, tc.message)
}
}

var solverTestCases = []struct {
expStr string
sortedMatchesByKeyword map[string][]int
Expand Down
7 changes: 0 additions & 7 deletions examples/dsl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,6 @@ func main() {
}
fmt.Println("recursive eval ", responseRecursive)

solverArr := expression.CreateSolverOrder()
responseIter, err := solverArr.Solve(matches)
if err != nil {
log.Fatal(err)
}
fmt.Println("iterative eval ", responseIter)

// should return an error
_, err = expression.Solve(matches)
if err != nil {
Expand Down
6 changes: 3 additions & 3 deletions finder/finder.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ type Match struct {
// the SolverOrder to later solve the expression.
type exprWrapper struct {
exprString string
solverOrd *dsl.SolverOrder
expression *dsl.Expression
tag string
}

Expand Down Expand Up @@ -100,7 +100,7 @@ func (finder *Finder) AddExpressionWithTag(expression string, tag string) error
return err
}

finder.expressions = append(finder.expressions, exprWrapper{expression, exp.CreateSolverOrder(), tag})
finder.expressions = append(finder.expressions, exprWrapper{expression, exp, tag})
for key := range p.GetKeywords() {
finder.keywords[key] = struct{}{}
finder.updatedSubMachine = false
Expand Down Expand Up @@ -180,7 +180,7 @@ func (finder *Finder) addMatchesToSolverMap(matches []*Match, sortedMatchesByKey
func (finder *Finder) solveExpressions(sortedMatchesByKeyword map[string][]int) (expRes []ExpressionResult, err error) {
expRes = make([]ExpressionResult, 0)
for i, exp := range finder.expressions {
res, err := exp.solverOrd.Solve(sortedMatchesByKeyword)
res, err := exp.expression.Solve(sortedMatchesByKeyword)
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit ef6e4fd

Please sign in to comment.