mirror of
https://github.com/WiiLink24/wfc-server.git
synced 2026-08-27 19:14:02 -05:00
ServerBrowser: Add LIKE operator
This commit is contained in:
@@ -3,6 +3,7 @@ package filter
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
@@ -55,7 +56,7 @@ func (this *expression) switchOther(node *TreeNode) {
|
||||
|
||||
func (this *expression) switchFunction(node *TreeNode) int64 {
|
||||
val1 := node.Value.(*OperatorToken)
|
||||
switch val1.Operator {
|
||||
switch strings.ToLower(val1.Operator) {
|
||||
case "=":
|
||||
return this.evalEquals(node.Items())
|
||||
case "==":
|
||||
@@ -85,6 +86,9 @@ func (this *expression) switchFunction(node *TreeNode) int64 {
|
||||
case "||":
|
||||
return this.evalOr(node.Items())
|
||||
|
||||
case "like":
|
||||
return this.evalLike(node.Items())
|
||||
|
||||
default:
|
||||
panic("function not supported: " + val1.Operator)
|
||||
}
|
||||
@@ -288,6 +292,68 @@ func (this *expression) evalMathLessOrEqual(val1, val2 int64) int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (this *expression) evalLike(args []*TreeNode) int64 {
|
||||
cnt := len(args)
|
||||
switch {
|
||||
case cnt < 2:
|
||||
panic("operator missing arguments")
|
||||
case cnt == 2:
|
||||
return this.evalLikeSingle(args[0], args[1])
|
||||
default:
|
||||
panic("operator like does not support multiple arguments")
|
||||
}
|
||||
}
|
||||
|
||||
func (this *expression) evalLikeSingle(arg1, arg2 *TreeNode) int64 {
|
||||
val1 := this.getString(arg1)
|
||||
val2 := this.getString(arg2)
|
||||
|
||||
allowedCharacters := `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_%\`
|
||||
|
||||
regexString := "^"
|
||||
|
||||
// Convert SQL like pattern to regex
|
||||
for i, c := range val2 {
|
||||
if strings.IndexRune(allowedCharacters, c) < 0 {
|
||||
panic("invalid character in like pattern: " + string(c))
|
||||
}
|
||||
|
||||
if i != 0 && val2[i-1] == '\\' {
|
||||
if c == '\\' {
|
||||
regexString += "\\\\"
|
||||
continue
|
||||
}
|
||||
|
||||
regexString += string(c)
|
||||
continue
|
||||
}
|
||||
|
||||
switch c {
|
||||
case '%':
|
||||
regexString += ".*"
|
||||
|
||||
case '_':
|
||||
regexString += "."
|
||||
|
||||
case '\\':
|
||||
// Do nothing
|
||||
|
||||
default:
|
||||
regexString += string(c)
|
||||
}
|
||||
}
|
||||
|
||||
regexString += "$"
|
||||
|
||||
if matched, err := regexp.MatchString(regexString, val1); err != nil {
|
||||
panic(err)
|
||||
} else if matched {
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// Get a value from the context.
|
||||
func (this *expression) getValue(token *IdentityToken) string {
|
||||
return this.context[token.Name]
|
||||
|
||||
@@ -4,6 +4,8 @@ package filter
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type stateFn func(*parser) stateFn
|
||||
@@ -93,8 +95,15 @@ func (this *parser) parseCloseBracket() stateFn {
|
||||
|
||||
func (this *parser) AcceptOperator() bool {
|
||||
scan := this.scan
|
||||
state := scan.SaveState()
|
||||
for _, op := range operatorList {
|
||||
if scan.Prefix(op) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ func (p Pos) Position() Pos {
|
||||
|
||||
const eof = -1
|
||||
|
||||
//Scanner, Iterates through a string.
|
||||
// Scanner, Iterates through a string.
|
||||
type Scanner struct {
|
||||
input string
|
||||
start Pos
|
||||
@@ -52,7 +52,7 @@ func (this *Scanner) Commit() string {
|
||||
return r1
|
||||
}
|
||||
|
||||
//IsEOF check if the end of the current string has been reached.
|
||||
// IsEOF check if the end of the current string has been reached.
|
||||
func (this *Scanner) IsEOF() bool {
|
||||
return int(this.pos) >= len(this.input)
|
||||
}
|
||||
@@ -65,7 +65,7 @@ func (this *Scanner) MoveStart(pos int) {
|
||||
this.start = this.start + Pos(pos)
|
||||
}
|
||||
|
||||
//Next returns the next rune in the input.
|
||||
// Next returns the next rune in the input.
|
||||
func (this *Scanner) Next() rune {
|
||||
this.safebackup = true
|
||||
if this.IsEOF() {
|
||||
|
||||
@@ -153,7 +153,7 @@ type OperatorPrecedence [][]string
|
||||
func (this OperatorPrecedence) Level(operator string) int {
|
||||
for level, operators := range this {
|
||||
for _, op := range operators {
|
||||
if op == operator {
|
||||
if op == strings.ToLower(operator) {
|
||||
return 5 - level
|
||||
}
|
||||
}
|
||||
@@ -177,7 +177,7 @@ var operators = OperatorPrecedence{
|
||||
{"+", "-"},
|
||||
{"==", "=", "!=", ">=", "<=", ">", "<"},
|
||||
{"&&", "and"},
|
||||
{"||", "or"},
|
||||
{"||", "or", "like"},
|
||||
}
|
||||
|
||||
var operatorList = operators.All()
|
||||
|
||||
Reference in New Issue
Block a user