-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#12 Refactor factory with the operator precedence. Reverse Polish Not…
…ation in progress
- Loading branch information
1 parent
8ff4ff8
commit 9e97dff
Showing
2 changed files
with
48 additions
and
161 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 |
---|---|---|
@@ -1,180 +1,72 @@ | ||
package rule | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"reflect" | ||
|
||
"github.com/emirpasic/gods/stacks/arraystack" | ||
) | ||
import "github.com/emirpasic/gods/stacks/arraystack" | ||
|
||
var ( | ||
matches = []match{} | ||
|
||
adjustableNodes = []reflect.Type{ | ||
reflect.TypeOf(gt{}), | ||
} | ||
|
||
higherOrderNodes = []reflect.Type{ | ||
reflect.TypeOf(and{}), | ||
reflect.TypeOf(or{}), | ||
// operatorPrecedence is a slice of operators in the precedence order. | ||
// The most important ones are on the top, the lest important ones are on the bottom. | ||
// Also if there are two operators: A and B, and if those are in the same row, | ||
// those are equal in terms of precedence. | ||
operatorPrecedence = [][]string{ | ||
// Postfix | ||
{ | ||
tokenLParen, | ||
tokenRParen, | ||
tokenDot, | ||
}, | ||
// Relational | ||
{ | ||
tokenMoreThan, | ||
tokenLessThan, | ||
}, | ||
// Equality | ||
{ | ||
tokenEqual, | ||
tokenNotEqual, | ||
}, | ||
// AND | ||
{ | ||
tokenDoubleAmpersand, | ||
}, | ||
// OR | ||
{ | ||
tokenOr, | ||
}, | ||
// Dot | ||
{ | ||
tokenDot, | ||
}, | ||
} | ||
) | ||
|
||
type NodeAdjuster interface { | ||
AdjustNode(nd node, tokensMatch []Token) (node, error) | ||
} | ||
|
||
type expressionTreeFactory struct { | ||
nodeAdjuster NodeAdjuster | ||
} | ||
|
||
var _ ExpressionTreeFactory = (*expressionTreeFactory)(nil) | ||
|
||
func (e *expressionTreeFactory) CreateExpressionTree(tokens []Token) (expressionTree, error) { | ||
var ( | ||
nodes []node | ||
tokenMatch []Token | ||
) | ||
|
||
for _, token := range tokens { | ||
tokenMatch = append(tokenMatch, token) | ||
|
||
for _, m := range matches { | ||
if m.isMatching(tokenMatch) { | ||
nd, err := e.adaptNode(m.node, tokenMatch) | ||
if err != nil { | ||
return nil, fmt.Errorf("adapt node: %w", err) | ||
} | ||
|
||
nodes = append(nodes, nd) | ||
|
||
tokenMatch = []Token{} | ||
} | ||
} | ||
} | ||
|
||
eTree, err := buildExpressionTree(nodes) | ||
if err != nil { | ||
return nil, fmt.Errorf("build expression tree: %w", err) | ||
} | ||
|
||
return eTree, nil | ||
} | ||
|
||
func (e *expressionTreeFactory) adaptNode(base node, tokenMatch []Token) (node, error) { | ||
if !isAdjustableNode(base) { | ||
return base, nil | ||
} | ||
|
||
nd, err := e.nodeAdjuster.AdjustNode(base, tokenMatch) | ||
if err != nil { | ||
return nil, fmt.Errorf("adjust node %s: %w", reflect.TypeOf(base).Name(), err) | ||
} | ||
|
||
return nd, nil | ||
} | ||
|
||
type match struct { | ||
tokens []Token | ||
node node | ||
} | ||
func reversePolishNotationSort(tokens []Token) []Token { | ||
var ( | ||
operatorStack arraystack.Stack | ||
outputTokens []Token | ||
) | ||
|
||
func (m *match) isMatching(tokens []Token) bool { | ||
if len(m.tokens) != len(tokens) { | ||
return false | ||
} | ||
for _, token := range tokens { | ||
if isOperator(token) { | ||
|
||
for i, token := range m.tokens { | ||
if tokens[i].Name != token.Name { | ||
return false | ||
} | ||
} | ||
|
||
return true | ||
} | ||
|
||
func isAdjustableNode(nd node) bool { | ||
nodeType := reflect.TypeOf(nd) | ||
|
||
for _, adjustableNode := range adjustableNodes { | ||
if nodeType.Name() == adjustableNode.Name() { | ||
func isOperator(tkn Token) bool { | ||
for _, tknOperator := range tokensOperators { | ||
if tkn.Name == tknOperator { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
func buildExpressionTree(nodes []node) (expressionTree, error) { | ||
stack := arraystack.Stack{} | ||
|
||
// todo: we need to add additional check if less than 2 then we need to validate if NEXT node is not a higherOrderNode | ||
// and then we need to build based on that | ||
for i := 0; i < len(nodes); i++ { | ||
switch nodes[i].(type) { | ||
case and: | ||
// after nth iteration it could be an and, we want then continue | ||
// in order to add additional elements to the stack | ||
if stack.Size() < 2 { | ||
continue | ||
} | ||
|
||
if stack.Size() > 2 { | ||
return nil, errors.New("expression is invalid, and can only evaluate 2 predicates") | ||
} | ||
|
||
nd := and{} | ||
|
||
var childrenNodes []node | ||
|
||
for j := 0; j < 2; j++ { | ||
v, _ := stack.Pop() | ||
vt, _ := v.(node) | ||
childrenNodes = append(childrenNodes, vt) | ||
} | ||
|
||
// nodes are popped from the stack, therefore we need to change order of the children | ||
nd.SetChild(childrenNodes[1], childrenNodes[0]) | ||
|
||
stack.Clear() | ||
|
||
stack.Push(nd) | ||
case or: | ||
if stack.Size() < 2 { | ||
continue | ||
} | ||
|
||
if stack.Size() > 2 { | ||
return nil, errors.New("expression is invalid, and can only evaluate 2 predicates") | ||
} | ||
|
||
nd := or{} | ||
|
||
var childrenNodes []node | ||
|
||
for j := 0; j < 2; j++ { | ||
v, _ := stack.Pop() | ||
vt, _ := v.(node) | ||
childrenNodes = append(childrenNodes, vt) | ||
} | ||
|
||
nd.SetChild(childrenNodes[1], childrenNodes[0]) | ||
|
||
stack.Clear() | ||
|
||
stack.Push(nd) | ||
default: | ||
stack.Push(nodes[i]) | ||
} | ||
} | ||
|
||
v, ok := stack.Pop() | ||
if !ok { | ||
return nil, fmt.Errorf("empty expression stack") | ||
} | ||
|
||
vt, _ := v.(node) | ||
|
||
return expressionTree(vt), nil | ||
} |
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