解释器模式

解释器模式

定义:为某个语言定义它的语法表示,并定义一个解释器来处理这个语法

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Package interpreter 解释器模式
// 采用原课程的示例, 并且做了一下简化
// 假设我们现在有一个监控系统
// 现在需要实现一个告警模块,可以根据输入的告警规则来决定是否触发告警
// 告警规则支持 &&、>、< 3种运算符
// 其中 >、< 优先级比 && 更高
package interpreter

import (
"fmt"
"regexp"
"strconv"
"strings"
)

// AlertRule 告警规则
type AlertRule struct {
expression IExpression
}

// NewAlertRule NewAlertRule
func NewAlertRule(rule string) (*AlertRule, error) {
exp, err := NewAndExpression(rule)
return &AlertRule{expression: exp}, err
}

// Interpret 判断告警是否触发
func (r AlertRule) Interpret(stats map[string]float64) bool {
return r.expression.Interpret(stats)
}

// IExpression 表达式接口
type IExpression interface {
Interpret(stats map[string]float64) bool
}

// GreaterExpression >
type GreaterExpression struct {
key string
value float64
}

// Interpret Interpret
func (g GreaterExpression) Interpret(stats map[string]float64) bool {
v, ok := stats[g.key]
if !ok {
return false
}
return v > g.value
}

// NewGreaterExpression NewGreaterExpression
func NewGreaterExpression(exp string) (*GreaterExpression, error) {
data := regexp.MustCompile(`\s+`).Split(strings.TrimSpace(exp), -1)
if len(data) != 3 || data[1] != ">" {
return nil, fmt.Errorf("exp is invalid: %s", exp)
}

val, err := strconv.ParseFloat(data[2], 10)
if err != nil {
return nil, fmt.Errorf("exp is invalid: %s", exp)
}

return &GreaterExpression{
key: data[0],
value: val,
}, nil
}

// LessExpression <
type LessExpression struct {
key string
value float64
}

// Interpret Interpret
func (g LessExpression) Interpret(stats map[string]float64) bool {
v, ok := stats[g.key]
if !ok {
return false
}
return v < g.value
}

// NewLessExpression NewLessExpression
func NewLessExpression(exp string) (*LessExpression, error) {
data := regexp.MustCompile(`\s+`).Split(strings.TrimSpace(exp), -1)
if len(data) != 3 || data[1] != "<" {
return nil, fmt.Errorf("exp is invalid: %s", exp)
}

val, err := strconv.ParseFloat(data[2], 10)
if err != nil {
return nil, fmt.Errorf("exp is invalid: %s", exp)
}

return &LessExpression{
key: data[0],
value: val,
}, nil
}

// AndExpression &&
type AndExpression struct {
expressions []IExpression
}

// Interpret Interpret
func (e AndExpression) Interpret(stats map[string]float64) bool {
for _, expression := range e.expressions {
if !expression.Interpret(stats) {
return false
}
}
return true
}

// NewAndExpression NewAndExpression
func NewAndExpression(exp string) (*AndExpression, error) {
exps := strings.Split(exp, "&&")
expressions := make([]IExpression, len(exps))

for i, e := range exps {
var expression IExpression
var err error

switch {
case strings.Contains(e, ">"):
expression, err = NewGreaterExpression(e)
case strings.Contains(e, "<"):
expression, err = NewLessExpression(e)
default:
err = fmt.Errorf("exp is invalid: %s", exp)
}

if err != nil {
return nil, err
}

expressions[i] = expression
}

return &AndExpression{expressions: expressions}, nil
}

单元测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package interpreter

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestAlertRule_Interpret(t *testing.T) {
stats := map[string]float64{
"a": 1,
"b": 2,
"c": 3,
}
tests := []struct {
name string
stats map[string]float64
rule string
want bool
}{
{
name: "case1",
stats: stats,
rule: "a > 1 && b > 10 && c < 5",
want: false,
},
{
name: "case2",
stats: stats,
rule: "a < 2 && b > 10 && c < 5",
want: false,
},
{
name: "case3",
stats: stats,
rule: "a < 5 && b > 1 && c < 10",
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r, err := NewAlertRule(tt.rule)
require.NoError(t, err)
assert.Equal(t, tt.want, r.Interpret(tt.stats))
})
}
}