mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-03-22 01:54:12 -05:00
193 lines
4.1 KiB
Go
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 ""
|
|
}
|