You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
387 lines
8.3 KiB
387 lines
8.3 KiB
package expression |
|
|
|
import ( |
|
"errors" |
|
"strconv" |
|
"strings" |
|
"unicode" |
|
) |
|
|
|
// Deprecated: 待优化, 简单表达式解析与求值器,支持变量、比较运算符和逻辑运算符 |
|
type Parser struct { |
|
expr string |
|
pos int |
|
} |
|
|
|
type Node interface { |
|
Eval(vars map[string]interface{}) (bool, error) |
|
} |
|
|
|
type numberNode struct{ val float64 } |
|
type boolNode struct{ val bool } |
|
type varNode struct{ name string } |
|
type binaryNode struct { |
|
op string |
|
left, right Node |
|
} |
|
type compareNode struct { |
|
op string |
|
left, right Node |
|
} |
|
type logicNode struct { |
|
op string |
|
left, right Node |
|
} |
|
type unaryNode struct { |
|
op string |
|
operand Node |
|
} |
|
|
|
// NewParser 创建解析器 |
|
func Parse(expr string) (*Parser, error) { |
|
expr = strings.ReplaceAll(expr, " ", "") // 去除空格 |
|
if expr == "" { |
|
return nil, errors.New("表达式为空") |
|
} |
|
return &Parser{expr: expr}, nil |
|
} |
|
|
|
// 解析并返回 AST 根节点 |
|
func (p *Parser) Parse() (Node, error) { |
|
node, err := p.parseLogicOr() |
|
if err != nil { |
|
return nil, err |
|
} |
|
if p.pos != len(p.expr) { |
|
return nil, errors.New("表达式末尾有多余字符") |
|
} |
|
return node, nil |
|
} |
|
|
|
// ====================== 解析层级 ====================== |
|
|
|
func (p *Parser) parseLogicOr() (Node, error) { |
|
left, err := p.parseLogicAnd() |
|
if err != nil { |
|
return nil, err |
|
} |
|
for p.pos < len(p.expr) && p.substr(p.pos, 2) == "||" { |
|
p.pos += 2 |
|
right, err := p.parseLogicAnd() |
|
if err != nil { |
|
return nil, err |
|
} |
|
left = &logicNode{op: "||", left: left, right: right} |
|
} |
|
return left, nil |
|
} |
|
|
|
func (p *Parser) parseLogicAnd() (Node, error) { |
|
left, err := p.parseComparison() |
|
if err != nil { |
|
return nil, err |
|
} |
|
for p.pos < len(p.expr) && p.substr(p.pos, 2) == "&&" { |
|
p.pos += 2 |
|
right, err := p.parseComparison() |
|
if err != nil { |
|
return nil, err |
|
} |
|
left = &logicNode{op: "&&", left: left, right: right} |
|
} |
|
return left, nil |
|
} |
|
|
|
func (p *Parser) parseComparison() (Node, error) { |
|
left, err := p.parseExpression() |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
ops := []string{">=", "<=", "==", "!=", ">", "<"} |
|
var op string |
|
for _, candidate := range ops { |
|
if p.substr(p.pos, len(candidate)) == candidate { |
|
op = candidate |
|
p.pos += len(candidate) |
|
break |
|
} |
|
} |
|
if op != "" { |
|
right, err := p.parseExpression() |
|
if err != nil { |
|
return nil, err |
|
} |
|
return &compareNode{op: op, left: left, right: right}, nil |
|
} |
|
return left, nil |
|
} |
|
|
|
func (p *Parser) parseExpression() (Node, error) { |
|
left, err := p.parseTerm() |
|
if err != nil { |
|
return nil, err |
|
} |
|
for p.pos < len(p.expr) && (p.current() == '+' || p.current() == '-') { |
|
op := string(p.current()) |
|
p.pos++ |
|
right, err := p.parseTerm() |
|
if err != nil { |
|
return nil, err |
|
} |
|
left = &binaryNode{op: op, left: left, right: right} |
|
} |
|
return left, nil |
|
} |
|
|
|
func (p *Parser) parseTerm() (Node, error) { |
|
left, err := p.parseUnary() |
|
if err != nil { |
|
return nil, err |
|
} |
|
for p.pos < len(p.expr) && (p.current() == '*' || p.current() == '/') { |
|
op := string(p.current()) |
|
p.pos++ |
|
right, err := p.parseUnary() |
|
if err != nil { |
|
return nil, err |
|
} |
|
left = &binaryNode{op: op, left: left, right: right} |
|
} |
|
return left, nil |
|
} |
|
|
|
func (p *Parser) parseUnary() (Node, error) { |
|
if p.current() == '!' { |
|
p.pos++ |
|
operand, err := p.parseUnary() |
|
if err != nil { |
|
return nil, err |
|
} |
|
return &unaryNode{op: "!", operand: operand}, nil |
|
} |
|
return p.parseAtom() |
|
} |
|
|
|
func (p *Parser) parseAtom() (Node, error) { |
|
ch := p.current() |
|
|
|
if ch == '(' { |
|
p.pos++ |
|
node, err := p.parseLogicOr() |
|
if err != nil { |
|
return nil, err |
|
} |
|
if p.current() != ')' { |
|
return nil, errors.New("缺少右括号") |
|
} |
|
p.pos++ |
|
return node, nil |
|
} |
|
|
|
if unicode.IsDigit(rune(ch)) || ch == '.' || ch == '-' && p.pos+1 < len(p.expr) && (unicode.IsDigit(rune(p.expr[p.pos+1])) || p.expr[p.pos+1] == '.') { |
|
return p.parseNumber() |
|
} |
|
|
|
if unicode.IsLetter(rune(ch)) || ch == '_' { |
|
return p.parseIdentifierOrBool() |
|
} |
|
|
|
return nil, errors.New("无效字符: " + string(ch)) |
|
} |
|
|
|
func (p *Parser) parseNumber() (Node, error) { |
|
start := p.pos |
|
if p.current() == '-' { |
|
p.pos++ |
|
} |
|
for p.pos < len(p.expr) && (unicode.IsDigit(rune(p.expr[p.pos])) || p.expr[p.pos] == '.') { |
|
p.pos++ |
|
} |
|
val, err := strconv.ParseFloat(p.expr[start:p.pos], 64) |
|
if err != nil { |
|
return nil, err |
|
} |
|
return &numberNode{val: val}, nil |
|
} |
|
|
|
func (p *Parser) parseIdentifierOrBool() (Node, error) { |
|
start := p.pos |
|
for p.pos < len(p.expr) && (unicode.IsLetter(rune(p.expr[p.pos])) || unicode.IsDigit(rune(p.expr[p.pos])) || p.expr[p.pos] == '_') { |
|
p.pos++ |
|
} |
|
name := p.expr[start:p.pos] |
|
|
|
if name == "true" { |
|
return &boolNode{val: true}, nil |
|
} |
|
if name == "false" { |
|
return &boolNode{val: false}, nil |
|
} |
|
return &varNode{name: name}, nil |
|
} |
|
|
|
// ====================== 辅助函数 ====================== |
|
|
|
func (p *Parser) current() byte { |
|
if p.pos >= len(p.expr) { |
|
return 0 |
|
} |
|
return p.expr[p.pos] |
|
} |
|
|
|
func (p *Parser) substr(start, length int) string { |
|
if start+length > len(p.expr) { |
|
return "" |
|
} |
|
return p.expr[start : start+length] |
|
} |
|
|
|
// ====================== 求值 ====================== |
|
|
|
func (n *numberNode) Eval(_ map[string]interface{}) (bool, error) { |
|
return false, errors.New("数字节点不能直接作为布尔表达式") |
|
} |
|
|
|
func (n *boolNode) Eval(_ map[string]interface{}) (bool, error) { |
|
return n.val, nil |
|
} |
|
|
|
func (n *varNode) Eval(vars map[string]interface{}) (bool, error) { |
|
val, exists := vars[n.name] |
|
if !exists { |
|
return false, errors.New("未定义的变量: " + n.name) |
|
} |
|
switch v := val.(type) { |
|
case float64: |
|
return v != 0, nil // 数字非零视为 true(可选逻辑) |
|
case bool: |
|
return v, nil |
|
default: |
|
return false, errors.New("变量必须是 float64 或 bool") |
|
} |
|
} |
|
|
|
func (n *binaryNode) Eval(vars map[string]interface{}) (bool, error) { |
|
_, err := evalToFloat(n.left, vars) |
|
if err != nil { |
|
return false, err |
|
} |
|
r, err := evalToFloat(n.right, vars) |
|
if err != nil { |
|
return false, err |
|
} |
|
switch n.op { |
|
case "+": |
|
return false, errors.New("二元运算不能直接返回 bool") |
|
case "-": |
|
return false, errors.New("二元运算不能直接返回 bool") |
|
case "*": |
|
return false, errors.New("二元运算不能直接返回 bool") |
|
case "/": |
|
if r == 0 { |
|
return false, errors.New("除以零") |
|
} |
|
return false, errors.New("二元运算不能直接返回 bool") |
|
} |
|
return false, errors.New("未知运算符") |
|
} |
|
|
|
func (n *compareNode) Eval(vars map[string]interface{}) (bool, error) { |
|
l, err := evalToFloat(n.left, vars) |
|
if err != nil { |
|
return false, err |
|
} |
|
r, err := evalToFloat(n.right, vars) |
|
if err != nil { |
|
return false, err |
|
} |
|
switch n.op { |
|
case ">": |
|
return l > r, nil |
|
case "<": |
|
return l < r, nil |
|
case "==": |
|
return l == r, nil |
|
case ">=": |
|
return l >= r, nil |
|
case "<=": |
|
return l <= r, nil |
|
case "!=": |
|
return l != r, nil |
|
} |
|
return false, errors.New("未知比较符") |
|
} |
|
|
|
func (n *logicNode) Eval(vars map[string]interface{}) (bool, error) { |
|
l, err := evalToBool(n.left, vars) |
|
if err != nil { |
|
return false, err |
|
} |
|
r, err := evalToBool(n.right, vars) |
|
if err != nil { |
|
return false, err |
|
} |
|
if n.op == "&&" { |
|
return l && r, nil |
|
} |
|
return l || r, nil |
|
} |
|
|
|
func (n *unaryNode) Eval(vars map[string]interface{}) (bool, error) { |
|
val, err := evalToBool(n.operand, vars) |
|
if err != nil { |
|
return false, err |
|
} |
|
return !val, nil |
|
} |
|
|
|
// 辅助求值函数 |
|
func evalToFloat(node Node, vars map[string]interface{}) (float64, error) { |
|
// 简化实现:这里假设算术表达式最终求值后用于比较 |
|
// 实际项目中可扩展返回 interface{} |
|
switch n := node.(type) { |
|
case *numberNode: |
|
return n.val, nil |
|
case *varNode: |
|
if v, ok := vars[n.name].(float64); ok { |
|
return v, nil |
|
} |
|
return 0, errors.New("变量不是数字") |
|
case *binaryNode: |
|
l, _ := evalToFloat(n.left, vars) |
|
r, _ := evalToFloat(n.right, vars) |
|
switch n.op { |
|
case "+": |
|
return l + r, nil |
|
case "-": |
|
return l - r, nil |
|
case "*": |
|
return l * r, nil |
|
case "/": |
|
if r == 0 { |
|
return 0, errors.New("除以零") |
|
} |
|
return l / r, nil |
|
} |
|
} |
|
return 0, errors.New("无法求值为数字") |
|
} |
|
|
|
func evalToBool(node Node, vars map[string]interface{}) (bool, error) { |
|
return node.Eval(vars) |
|
} |
|
|
|
// ====================== 使用示例 ====================== |
|
|
|
func Evaluate(expr string, vars map[string]interface{}) (bool, error) { |
|
p, err := Parse(expr) |
|
if err != nil { |
|
return false, err |
|
} |
|
ast, err := p.Parse() |
|
if err != nil { |
|
return false, err |
|
} |
|
return ast.Eval(vars) |
|
}
|
|
|