Golang通过栈实现表达式运算(中缀表达式转后缀表达式解析语法)

Golang通过栈实现表达式运算(中缀表达式转后缀表达式解析语法)

需求背景:将string表达式数组 [title == AUSU && ( header == Wecome || brand != AC68U )] 解析并使用ES查询得到运算结果。

分析:带有括号的表达式,需要根据优先级解析,可将中缀表达式转换为后缀表达式,去除括号

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
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值