Golang通过栈实现表达式运算(中缀表达式转后缀表达式解析语法)
需求背景:将string表达式数组 [title == AUSU && ( header == Wecome || brand != AC68U )] 解析并使用ES查询得到运算结果。文章来源:https://www.toymoban.com/news/detail-634966.html
分析:带有括号的表达式,需要根据优先级解析,可将中缀表达式转换为后缀表达式,去除括号文章来源地址https://www.toymoban.com/news/detail-634966.html
1. 定义栈结构
package scanner_utils
import "errors"
type Stack []interface{}
func (stack Stack) Len() int {
return len(stack)
}
func (stack Stack) IsEmpty() bool {
return len(stack) == 0
}
func (stack Stack) Cap() int {
return cap(stack)
}
func (stack *Stack) Push(value interface{}) {
*stack = append(*stack, value)
}
func (stack Stack) Top() (interface{}, error) {
if len(stack) == 0 {
return nil, errors.New("Stack out of index, len is 0")
}
return stack[len(stack)-1], nil
}
func (stack *Stack) Pop() (interface{}, error) {
theStack := *stack
if len(theStack) == 0 {
return nil, errors.New("Stack out of index, len is 0")
}
value := theStack[len(theStack)-1]
*stack = theStack[:len(theStack)-1]
return value, nil
}
func (stack *Stack) Peek() interface{} {
theStack := *stack
value := theStack[len(theStack)-1]
return value
}
2. 中缀表达式转后缀表达式
// [title==AUSU && ( header==Wecome || brand!=AC68U )] => [title==AUSU header==Wecome brand!=AC68U || &&]
func InfixToSuffixExpression(infixList []string) []string {
var suffixList []string
// 符号栈
stack := Stack{}
for _, item := range infixList {
if item != "(" && item != ")" && item != "&&" && item != "||" { // 不是符号,直接添加到数组
suffixList = append(suffixList, item)
} else {
if item == "(" { // 左括号,直接入符号栈
stack.Push(item)
} else if item == ")" { // 右括号,将符号栈的运算符弹出,直到遇到左括号
for {
if stack.IsEmpty() {
// 语法错误
break
}
if "(" == stack.Peek().(string) { // 遇到左括号,将左括号弹出,结束循环
stack.Pop()
break
}
oper, _ := stack.Pop()
suffixList = append(suffixList, oper.(string))
}
} else {
// item的优先级小于等于符号栈顶元素优先级时,将符号栈顶元素弹出并添加到数组
// item的优先级大于符号栈顶元素优先级时,将item入栈
for {
if stack.IsEmpty() || getPriority(stack.Peek().(string)) < getPriority(item) {
stack.Push(item)
break
}
oper, _ := stack.Pop()
suffixList = append(suffixList, oper.(string))
}
}
}
}
// 将符号栈剩余的元素依次弹出
for {
if stack.IsEmpty() {
break
}
oper, _ := stack.Pop()
suffixList = append(suffixList, oper.(string))
}
return suffixList
}
// 定义优先级
func getPriority(operator string) int {
OR := 1
AND := 2
var resp int
if operator == "&&" {
resp = AND
}
if operator == "||" {
resp = OR
}
return resp
}
3. 遍历后缀表达式进行运算,并拼接ES查询语法
// 输入参数:[title==AUSU header==Wecome brand!=AC68U || &&]
func jointSearchDsl(suffixExpreList []string) (*elastic.BoolQuery, error) {
expreStack := scanner_utils.Stack{}
queryMap := make(map[string]*elastic.BoolQuery)
index := 1
var err error
for _, item := range suffixExpreList {
if item != "&&" && item != "||" { // 不是运算符,直接入栈
expreStack.Push(item)
} else { // 是运算符,弹出栈中两个元素进行计算
expre1, _ := expreStack.Pop()
expreStr1 := expre1.(string)
expre2, _ := expreStack.Pop()
expreStr2 := expre2.(string)
// 解析两个表达式的K-V
kv1BoolQuery := elastic.NewBoolQuery()
if !strings.HasPrefix(expreStr1, "doneExpre") { // 需要处理
kv1BoolQuery, err = getExpresDsl(expreStr1)
if err != nil {
return nil, err
}
} else {
kv1BoolQuery = queryMap[expreStr1]
}
kv2BoolQuery := elastic.NewBoolQuery()
if !strings.HasPrefix(expreStr2, "doneExpre") { // 需要处理
kv2BoolQuery, err = getExpresDsl(expreStr2)
if err != nil {
return nil, err
}
} else {
kv2BoolQuery = queryMap[expreStr2]
}
// 根据运算符使用must或者should拼接
boolQueryOper := elastic.NewBoolQuery()
if item == "&&" {
boolQueryOper.Must(kv1BoolQuery, kv2BoolQuery)
}
if item == "||" {
boolQueryOper.Should(kv1BoolQuery, kv2BoolQuery)
}
index++
// 写一个标识到栈,当作已处理的表达式
doneExpre := "doneExpre" + strconv.Itoa(index)
expreStack.Push(doneExpre)
queryMap[doneExpre] = boolQueryOper
}
}
// 循环结束,取出最后的一个元素,获取对应的boolQuery
expreN, _ := expreStack.Pop()
boolQuery := elastic.NewBoolQuery()
if !strings.HasPrefix(expreN.(string), "doneExpre") {
boolQuery, err = getExpresDsl(expreN.(string))
if err != nil {
return nil, err
}
} else {
boolQuery = queryMap[expreN.(string)]
}
return boolQuery, nil
}
// 输入参数:header==Wecome
// 将每一个表达式解析,拼接ES query dsl
func getExpresDsl(expre string) (*elastic.BoolQuery, error) {
boolQuery := elastic.NewBoolQuery()
// TODO...
return boolQuery, nil
}
到了这里,关于Golang通过栈实现表达式运算(中缀表达式转后缀表达式解析语法)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!