wfc-server/serverbrowser/filter/parse.go
2024-02-06 17:51:52 -05:00

193 lines
4.1 KiB
Go

// Modified from github.com/zdebeer99/goexpression
package filter
import (
"errors"
"fmt"
"strings"
"unicode"
)
type stateFn func(*parser) stateFn
type parser struct {
scan *Scanner
root *TreeNode
curr *TreeNode
err error
state stateFn
}
func Parse(input string) (root *TreeNode, err error) {
defer func() {
if str := recover(); str != nil {
root = nil
err = errors.New(str.(string))
}
}()
root = NewTreeNode(NewEmptyToken())
parse := &parser{NewScanner(input), root, root, nil, nil}
parse.parse()
err = parse.err
return root, err
}
func (this *parser) getCurr() Token {
if this.curr != nil {
return this.curr.Value
}
return nil
}
func (this *parser) parse() {
this.pumpExpression()
}
func (this *parser) add(token Token) *TreeNode {
return this.curr.Add(token)
}
func (this *parser) push(token Token) *TreeNode {
return this.curr.Push(token)
}
func (this *parser) lastNode() *TreeNode {
return this.curr.LastElement()
}
func (this *parser) parentNode() *TreeNode {
return this.curr.Parent()
}
func (this *parser) commit() string {
return this.scan.Commit()
}
// parseOpenBracket
func (this *parser) parseOpenBracket() bool {
this.curr = this.add(NewGroupToken("()"))
this.commit()
return true
}
// parseCloseBracket
func (this *parser) parseCloseBracket() stateFn {
for {
v1, ok := this.curr.Value.(*GroupToken)
if ok && v1.GroupType == "()" {
this.commit()
this.curr = this.curr.Parent()
return branchExpressionOperatorPart
}
if ok && v1.GroupType == "" {
//must be a bracket part of a parent loop, exit this sub loop.
this.scan.Backup()
return nil
}
if this.curr.Parent() == nil {
panic("brackets not closed")
}
this.curr = this.curr.Parent()
}
panic("unreachable code")
}
func (this *parser) AcceptOperator() bool {
scan := this.scan
state := scan.SaveState()
for _, op := range operatorList {
if scan.Prefix(op) || scan.Prefix(strings.ToUpper(op)) {
p := scan.Peek()
if unicode.IsLetter(rune(op[0])) && (unicode.IsLetter(p) || unicode.IsNumber(p) || strings.IndexRune(charValidString, p) >= 0) {
// this is a prefix of a longer word
scan.LoadState(state)
continue
}
return true
}
}
return false
}
// parseOperator
func (this *parser) parseOperator() bool {
operator := this.commit()
lastnode := this.lastNode()
onode, ok := this.getCurr().(*OperatorToken)
//push excisting operator up in tree structure
if ok {
//operator is the same current operator ignore
if onode.Operator == operator {
return true
}
//change order for */ presedence
//fmt.Println(onode, operator, onode.Precedence(operator))
if onode.Precedence(operator) > 0 {
if lastnode != nil {
this.curr = lastnode.Push(NewOperatorToken(operator))
return true
}
}
//after */ presedence fallback and continue pushing +- operators from the bottom.
if onode.Precedence(operator) < 0 {
for {
v1, ok := this.curr.Parent().Value.(*OperatorToken)
//if ok && strings.Index("+-", v1.Name) >= 0 {
if ok && operators.Level(v1.Operator) >= 0 {
this.curr = this.curr.Parent()
} else {
break
}
}
}
//standard operator push
this.curr = this.push(NewOperatorToken(operator))
return true
}
//set previous found value as argument of the operator
if lastnode != nil {
this.curr = lastnode.Push(NewOperatorToken(operator))
} else {
this.state = nil
panic(fmt.Sprintf("expecting a value before operator %q", operator))
}
return true
}
// parseLRFunc
func (this *parser) parseLRFunc() bool {
lrfunc := this.commit()
lastnode := this.lastNode()
if lastnode != nil {
this.curr = lastnode.Push(NewLRFuncToken(lrfunc))
} else {
this.state = nil
panic(fmt.Sprintf("expecting a value before operator %q", lrfunc))
}
return false
}
func (this *parser) ParseText() string {
scan := this.scan
r := scan.Next()
if r == '"' || r == '\'' {
scan.Ignore()
endqoute := r
for {
r = scan.Next()
if r == endqoute {
scan.Backup()
txt := scan.Commit()
scan.Next()
scan.Ignore()
return txt
}
if scan.IsEOF() {
panic("missing quote and end of text")
}
}
}
return ""
}