缘起

最近复习设计模式
拜读谭勇德的<<设计模式就该这样学>>
本系列笔记拟采用golang练习之

解释器模式

  1. 解释器模式(Interpreter Pattern)指给定一门语言,
  2. 定义它的文法的一种表示,
  3. 并定义一个解释器
  4. ,该解释器使用该表示来解释语言中的句子。
  5. 解释器模式是一种按照规定的文法(语法)进行解析的模式,
  6. 属于行为型设计模式。
  7. (摘自 谭勇德 <<设计模式就该这样学>>)

场景

  • 某业务系统, 随数据量增加, 数据库访问压力日渐增大
  • 业务team希望平台team提供透明的缓存解决方案以缓解数据库压力
  • 平台team经反复讨论研究, 决定采用解释器模式, 直接拦截/解析/执行SQL(子集)语句, 提供透明化缓存服务

SQL(子集)文法

  1. SQL:
  2. select + FIELD_LIST + from TABLE_NAME + (where BOOL_EXPRESSION)?
  3. FIELD_LIST:
  4. *
  5. | COLUMN_LIST
  6. COLUMN_LIST:
  7. COLUMN_NAME + (,COLUMN_NAME)*
  8. COLUMN_NAME:
  9. IDENTIFIER
  10. IDENTIFIER:
  11. [_a-zA-Z] + [_a-zA-Z0-9]*
  12. TABLE_NAME:
  13. IDENTIFIER
  14. BOOL_EXPRESSION:
  15. STRING_FIELD = STRING_LITERAL
  16. | STRING_FIELD <> STRING_LITERAL
  17. | STRING_FIELD like STRING_LITERAL
  18. | STRING_FIELD not like STRING_LITERAL
  19. | INT_FIELD = INT_LITERAL
  20. | INT_FIELD <> INT_LITERAL
  21. | INT_FIELD > INT_LITERAL
  22. | INT_FIELD >= INT_LITERAL
  23. | INT_FIELD < INT_LITERAL
  24. | INT_FIELD <= INT_LITERAL
  25. | ( + BOOL_EXPRESSION + )
  26. | BOOL_EXPRESSION and BOOL_EXPRESSION
  27. | BOOL_EXPRESSION or BOOL_EXPRESSION
  28. STRING_FIELD:
  29. IDENTIFIER
  30. INT_FIELD:
  31. IDENTIFIER
  32. STRING_LITERAL:
  33. \" + [^"]* + \"
  34. INT_LIETRAL:
  35. [1-9] + [0-9]*

设计

  • IDatabase: 数据库接口
  • IDataTable: 数据表接口
  • IDataRow: 数据行接口
  • IDataField: 数据字段接口
  • IRowFilter: 数据行过滤器接口
  • tEmptyRowFilter: 静态为true/false的行过滤器
  • tExpressionRowFilter: 基于布尔表达式的行过滤器
  • Tokens: SQL(子集)记号枚举
  • Nodes: SQL(子集)语法树节点枚举
  • Chars: 词法分析辅助类
  • ISQLParser: SQL(子集)词法分析器接口
  • Lexer: 词法分析实现
  • Parser: 语法分析实现, SQL(子集)解释器的核心
  • tTokenQueue: 词法节点队列
  • tArrayStack: 基于数组实现的LIFO堆栈
  • IBoolExpression: 布尔表达式接口
  • tFieldExpression: 基于字段计算的布尔表达式
  • tLogicExpression: 基于关系(AND/OR)计算的布尔表达式
  • SaleOrder: 销售订单实体类
  • ISaleOrderService: 销售订单服务接口, 继承自IDataTable接口
  • tMockDatabase: 虚拟的数据库服务, 实现IDatabase接口
  • tMockSaleOrderService: 虚拟的销售订单服务, 实现ISaleOrderService接口

单元测试

interpreter_pattern_test.go, 模拟销售订单的保存与SQL查询

  1. package behavioral_patterns
  2. import (
  3. "learning/gooop/behavioral_patterns/interpreter"
  4. "testing"
  5. )
  6. func Test_Interpreter(t *testing.T) {
  7. service := interpreter.MockSaleOrderService
  8. service.Save(interpreter.NewSaleOrder(1, "张三", "广州", "电视", 10))
  9. service.Save(interpreter.NewSaleOrder(11, "张三三", "广州", "电视", 11))
  10. service.Save(interpreter.NewSaleOrder(2, "李四", "深圳", "冰箱", 20))
  11. service.Save(interpreter.NewSaleOrder(22, "李四四", "深圳", "冰箱", 21))
  12. service.Save(interpreter.NewSaleOrder(3, "王五", "东莞", "空调", 30))
  13. service.Save(interpreter.NewSaleOrder(33, "王五五", "东莞", "空调", 31))
  14. db := interpreter.MockDatabase
  15. e,rows := db.Query("select * from sale_order where (city='广州' or city='深圳') and quantity>10")
  16. if e != nil {
  17. t.Error(e)
  18. } else {
  19. for _,it := range rows {
  20. t.Logf("%s", it)
  21. }
  22. }
  23. }

测试输出

  1. $ go test -v interpreter_pattern_test.go
  2. === RUN Test_Interpreter
  3. interpreter_pattern_test.go:24: id=2, customer=李四, city=深圳, product=冰箱, quantity=20
  4. interpreter_pattern_test.go:24: id=22, customer=李四四, city=深圳, product=冰箱, quantity=21
  5. interpreter_pattern_test.go:24: id=11, customer=张三三, city=广州, product=电视, quantity=11
  6. --- PASS: Test_Interpreter (0.00s)
  7. PASS
  8. ok command-line-arguments 0.002s

IDatabase.go

数据库接口

  1. package interpreter
  2. type IDatabase interface {
  3. Register(table IDataTable)
  4. Query(sql string) (error, []IDataRow)
  5. }

IDataTable.go

数据表接口

  1. package interpreter
  2. type IDataTable interface {
  3. Name() string
  4. Filter(filter IRowFilter) []IDataRow
  5. }

IDataRow.go

数据行接口

  1. package interpreter
  2. type IDataRow interface {
  3. GetField(field string) (error,IDataField)
  4. }

IDataField.go

数据字段接口

  1. package interpreter
  2. import (
  3. "errors"
  4. )
  5. type IDataField interface {
  6. Name() string
  7. DataType() DataTypes
  8. GetString() (error,string)
  9. GetInt() (error, int)
  10. }
  11. type DataTypes int
  12. const StringDataType DataTypes = 1
  13. const IntDataType DataTypes = 2
  14. type tIntField struct {
  15. name string
  16. value int
  17. }
  18. func newIntField(name string, value int) *tIntField {
  19. return &tIntField{
  20. name,value,
  21. }
  22. }
  23. func (me *tIntField) Name() string {
  24. return me.name
  25. }
  26. func (me *tIntField) DataType() DataTypes {
  27. return IntDataType
  28. }
  29. func (me *tIntField) GetString() (error,string) {
  30. return errors.New("not implemented"), ""
  31. }
  32. func (me *tIntField) GetInt() (error,int) {
  33. return nil, me.value
  34. }
  35. type tStringField struct {
  36. name string
  37. value string
  38. }
  39. func newStringField(name string, value string) *tStringField {
  40. return &tStringField{
  41. name,value,
  42. }
  43. }
  44. func (me *tStringField) Name() string {
  45. return me.name
  46. }
  47. func (me *tStringField) DataType() DataTypes {
  48. return StringDataType
  49. }
  50. func (me *tStringField) GetString() (error,string) {
  51. return nil, me.value
  52. }
  53. func (me *tStringField) GetInt() (error,int) {
  54. return errors.New("not implemented"), 0
  55. }

IRowFilter.go

数据行过滤器接口

  1. package interpreter
  2. type IRowFilter interface {
  3. Filter(row IDataRow) bool
  4. }

tEmptyRowFilter.go

静态为true/false的行过滤器

  1. package interpreter
  2. type tEmptyRowFilter struct {
  3. accept bool
  4. }
  5. func newEmptyRowFilter(accept bool) IRowFilter {
  6. return &tEmptyRowFilter{
  7. accept: accept,
  8. }
  9. }
  10. func (me *tEmptyRowFilter) Filter(row IDataRow) bool {
  11. return me.accept
  12. }

tExpressionRowFilter.go

基于布尔表达式的行过滤器

  1. package interpreter
  2. type tExpressionRowFilter struct {
  3. expression IBoolExpression
  4. }
  5. func newExpressionRowFilter(e IBoolExpression) IRowFilter {
  6. return &tExpressionRowFilter{
  7. expression: e,
  8. }
  9. }
  10. func (me *tExpressionRowFilter) Filter(row IDataRow) bool {
  11. return me.expression.Eval(row)
  12. }

Tokens.go

SQL记号枚举

  1. package tokens
  2. type Tokens string
  3. const Select Tokens = "select"
  4. const Star Tokens = "*"
  5. const Comma Tokens = ","
  6. const From Tokens = "from"
  7. const Where Tokens = "where"
  8. const Identifier Tokens = "identifier"
  9. const LB Tokens = "("
  10. const RB Tokens = ")"
  11. const And Tokens = "and"
  12. const OR Tokens = "or"
  13. const Equal Tokens = "="
  14. const NotEqual Tokens = "<>"
  15. const Greater Tokens = ">"
  16. const GreaterEqual Tokens = ">="
  17. const Less Tokens = "<"
  18. const LessEqual Tokens = "<="
  19. const Like Tokens = "like"
  20. const NotLike Tokens = "not like"
  21. const StringLiteral Tokens = "string_literal"
  22. const IntLiteral Tokens = "int_literal"

Nodes.go

SQL语法树节点枚举

  1. package nodes
  2. type Nodes int
  3. const TokenNode Nodes = 1
  4. const ExpressionNode Nodes = 2

Chars.go

词法分析辅助类

  1. package interpreter
  2. type tChars struct {
  3. }
  4. func newCharsLib() *tChars {
  5. return &tChars{}
  6. }
  7. func (me *tChars) IsSpace(it rune) bool {
  8. switch it {
  9. case ' ':
  10. return true
  11. case '\t':
  12. return true
  13. case '\r':
  14. return true
  15. case '\n':
  16. return true
  17. }
  18. return false
  19. }
  20. func (me *tChars) Is09(it rune) bool {
  21. return it >= '0' && it <= '9'
  22. }
  23. func (me *tChars) Is19(it rune) bool {
  24. return it >= '1' && it <= '9'
  25. }
  26. func (me *tChars) IsLetter(it rune) bool {
  27. return (it >= 'a' && it <= 'z') || (it >= 'A' && it <= 'Z')
  28. }
  29. func (me *tChars) IsUnderscore(it rune) bool {
  30. return it == '_'
  31. }
  32. func (me *tChars) IsLB(it rune) bool {
  33. return it == '('
  34. }
  35. func (me *tChars) IsRB(it rune) bool {
  36. return it == ')'
  37. }
  38. func (me *tChars) IsChar(it rune, args... rune) bool {
  39. for _,v := range args {
  40. if v == it {
  41. return true
  42. }
  43. }
  44. return false
  45. }
  46. var chars = newCharsLib()

ISQLParser.go

SQL词法分析器接口

  1. package interpreter
  2. type ISQLParser interface {
  3. Parse(sql string) (error,*ParseResult)
  4. }
  5. type ParseResult struct {
  6. Fields []string
  7. Table string
  8. RowFilter IRowFilter
  9. }
  10. func newParseResult() *ParseResult {
  11. return &ParseResult{
  12. make([]string, 0),
  13. "",
  14. nil,
  15. }
  16. }

Lexer.go

SQL(子集)词法分析实现

  1. package interpreter
  2. import (
  3. "errors"
  4. "fmt"
  5. "learning/gooop/behavioral_patterns/interpreter/tokens"
  6. )
  7. type tLexer struct {
  8. chars []rune
  9. count int
  10. pos int
  11. tokens []*tTokenNode
  12. }
  13. func newLexer(sql string) *tLexer {
  14. chars := []rune(sql)
  15. return &tLexer{
  16. chars: chars,
  17. count: len(chars),
  18. pos: 0,
  19. tokens: make([]*tTokenNode, 0),
  20. }
  21. }
  22. func (me *tLexer) push(it *tTokenNode) {
  23. me.tokens = append(me.tokens, it)
  24. }
  25. func (me *tLexer) Parse() (error, []*tTokenNode) {
  26. fnMatchingWord := func(t tokens.Tokens) bool {
  27. pattern := string(t)
  28. from := me.pos
  29. if me.MatchingConst(pattern) && me.MatchingSpace() {
  30. me.push(newTokenNode(t, string(t), from, me.pos - 1))
  31. return true
  32. }
  33. return false
  34. }
  35. fnMatchingOP := func(t tokens.Tokens) bool {
  36. pattern := string(t)
  37. from := me.pos
  38. if me.MatchingConst(pattern) {
  39. me.push(newTokenNode(t, string(t), from, me.pos - 1))
  40. return true
  41. }
  42. return false
  43. }
  44. for {
  45. // eof
  46. if me.IsEof() {
  47. return nil, me.tokens
  48. }
  49. me.SkipSpace()
  50. from := me.pos
  51. // select, from, where
  52. if fnMatchingWord(tokens.Select) || fnMatchingWord(tokens.From) || fnMatchingWord(tokens.Where) {
  53. continue
  54. }
  55. // start,comma
  56. if fnMatchingWord(tokens.Star) || fnMatchingWord(tokens.Comma) {
  57. continue
  58. }
  59. // and, or
  60. if fnMatchingWord(tokens.And) {
  61. continue
  62. } else if fnMatchingWord(tokens.OR) {
  63. continue
  64. }
  65. // like, not like
  66. if fnMatchingWord(tokens.Like) {
  67. continue
  68. } else if fnMatchingWord(tokens.NotLike) {
  69. continue
  70. }
  71. // (, )
  72. if fnMatchingOP(tokens.LB) {
  73. continue
  74. } else if fnMatchingOP(tokens.RB) {
  75. continue
  76. }
  77. // =,<>,>,>=,<,<=
  78. if fnMatchingOP(tokens.Equal) {
  79. continue
  80. } else if fnMatchingOP(tokens.NotEqual) {
  81. continue
  82. } else if fnMatchingOP(tokens.GreaterEqual) {
  83. continue
  84. } else if fnMatchingOP(tokens.Greater) {
  85. continue
  86. } else if fnMatchingOP(tokens.LessEqual) {
  87. continue
  88. } else if fnMatchingOP(tokens.Less) {
  89. continue
  90. }
  91. // identifier
  92. b,v := me.MatchingIdentifier()
  93. if b {
  94. me.push(newTokenNode(tokens.Identifier, v, from, from + len(v)))
  95. continue
  96. }
  97. // string literal
  98. b,v = me.MatchingString()
  99. if b {
  100. me.push(newTokenNode(tokens.StringLiteral, v, from, from + len(v)))
  101. continue
  102. }
  103. // int literal
  104. b,v = me.MatchingInt()
  105. if b {
  106. me.push(newTokenNode(tokens.IntLiteral, v, from, from + len(v)))
  107. continue
  108. }
  109. // unknown
  110. return errors.New(fmt.Sprintf("unknown token at %v", from)), nil
  111. }
  112. }
  113. func (me *tLexer) IsEof() bool {
  114. return me.pos >= me.count
  115. }
  116. func (me *tLexer) SkipSpace() {
  117. for {
  118. if me.pos >= me.count {
  119. break
  120. }
  121. if chars.IsSpace(me.Char()) {
  122. me.pos++
  123. } else {
  124. break
  125. }
  126. }
  127. }
  128. func (me *tLexer) MatchingConst(s string) bool {
  129. pattern := []rune(s)
  130. for i,it := range pattern {
  131. n := me.pos + i
  132. if n >= me.count {
  133. return false
  134. }
  135. if me.chars[n] != it {
  136. return false
  137. }
  138. }
  139. me.pos += len(pattern)
  140. return true
  141. }
  142. func (me *tLexer) Char() rune {
  143. if me.pos >= me.count {
  144. return 0
  145. }
  146. return me.chars[me.pos]
  147. }
  148. func (me *tLexer) MatchingSpace() bool {
  149. if chars.IsSpace(me.Char()) {
  150. me.pos++
  151. return true
  152. }
  153. return false
  154. }
  155. func (me *tLexer) MatchingString() (bool,string) {
  156. mark := me.pos
  157. if me.Char() != '\'' {
  158. return false, ""
  159. }
  160. i := mark
  161. for {
  162. i++
  163. switch me.chars[i] {
  164. case '\'':
  165. me.pos = i + 1
  166. return true, string(me.chars[mark:me.pos])
  167. default:
  168. if i >= me.count {
  169. return false, ""
  170. }
  171. }
  172. }
  173. }
  174. func (me *tLexer) MatchingIdentifier() (bool, string) {
  175. mark := me.pos
  176. c := me.Char()
  177. if !(chars.IsUnderscore(c) || chars.IsLetter(c)) {
  178. return false, ""
  179. }
  180. i := mark
  181. for {
  182. i++
  183. if i > mark + 30 {
  184. return false,""
  185. }
  186. it := me.chars[i]
  187. if chars.IsLetter(it) || chars.Is09(it) || chars.IsUnderscore(it) {
  188. continue
  189. } else {
  190. me.pos = i
  191. return true, string(me.chars[mark:i])
  192. }
  193. }
  194. }
  195. func (me *tLexer) MatchingInt() (bool, string) {
  196. if me.Char() == '0' {
  197. me.pos++
  198. return true, "0"
  199. }
  200. mark := me.pos
  201. if !chars.Is19(me.Char()) {
  202. return false, ""
  203. }
  204. i := mark
  205. for {
  206. i++
  207. if i >= me.count {
  208. me.pos = me.count
  209. return true, string(me.chars[mark:])
  210. }
  211. if i > mark + 10 {
  212. return false, ""
  213. }
  214. it := me.chars[i]
  215. if chars.Is09(it) {
  216. continue
  217. }
  218. if chars.IsSpace(it) {
  219. me.pos = i - 1
  220. return true, string(me.chars[mark:i])
  221. } else {
  222. return false, ""
  223. }
  224. }
  225. }

Parser.go

语法分析实现

  1. package interpreter
  2. import (
  3. "errors"
  4. "fmt"
  5. "learning/gooop/behavioral_patterns/interpreter/tokens"
  6. "strconv"
  7. )
  8. type tSQLParser struct {
  9. result *ParseResult
  10. }
  11. func newSQLParser() ISQLParser {
  12. return &tSQLParser{
  13. newParseResult(),
  14. }
  15. }
  16. func (me *tSQLParser) Parse(sql string) (error,*ParseResult) {
  17. lexer := newLexer(sql)
  18. e, tks := lexer.Parse()
  19. if e != nil {
  20. return e, nil
  21. }
  22. queue := newTokenQueue(tks)
  23. // select (* | field-list)
  24. e = me.ParseSelectPart(queue)
  25. if e != nil {
  26. return e, nil
  27. }
  28. // from
  29. e = me.ParseFromPart(queue)
  30. if e != nil {
  31. return e, nil
  32. }
  33. // where + bool_expression
  34. e = me.ParseWherePart(queue)
  35. if e != nil {
  36. return e, nil
  37. }
  38. // eof
  39. if !queue.IsEmpty() {
  40. _,t := queue.Poll()
  41. return errors.New(fmt.Sprintf("expecting EOF at %v", t.from)), nil
  42. }
  43. return nil, me.result
  44. }
  45. func (me *tSQLParser) ParseSelectPart(queue *tTokenQueue) error {
  46. b,v := queue.Poll()
  47. if !b {
  48. return errors.New("expecting SELECT keyword")
  49. }
  50. if v.token != tokens.Select {
  51. return errors.New(fmt.Sprintf("expecting SELECT keyword but found '%s'", v.value))
  52. }
  53. fields := make([]string, 0)
  54. b,v = queue.Peek()
  55. if !b {
  56. return errors.New("unexpected EOF")
  57. }
  58. switch v.token {
  59. case tokens.Star:
  60. queue.Poll()
  61. fields = append(fields, v.value)
  62. break
  63. case tokens.Identifier:
  64. queue.Poll()
  65. fields = append(fields, v.value)
  66. break
  67. default:
  68. return errors.New(fmt.Sprintf("expecting column name but found '%s'", v.value))
  69. }
  70. for {
  71. b,v := queue.Peek()
  72. if !b {
  73. break
  74. }
  75. if v.token != tokens.Comma {
  76. break
  77. }
  78. queue.Poll()
  79. b,v = queue.Peek()
  80. if !b || v.token != tokens.Identifier {
  81. return errors.New(fmt.Sprintf("expecting column name but found '%s'", v.value))
  82. }
  83. queue.Poll()
  84. fields = append(fields, v.value)
  85. }
  86. if len(fields) > 0 {
  87. me.result.Fields = fields
  88. return nil
  89. }
  90. return errors.New("expecting column names")
  91. }
  92. func (me *tSQLParser) ParseFromPart(queue *tTokenQueue) error {
  93. b,v1 := queue.Poll()
  94. if !b {
  95. return errors.New("expecting 'from', but eof")
  96. }
  97. if v1.token != tokens.From {
  98. return errors.New(fmt.Sprintf("expecting 'from' at %v", v1.from))
  99. }
  100. b,v2 := queue.Poll()
  101. if !b {
  102. return errors.New("expecting table name, but eof")
  103. }
  104. if v2.token == tokens.Identifier {
  105. me.result.Table = v2.value
  106. return nil
  107. }
  108. return errors.New(fmt.Sprintf("expecting table name at %v", v2.from))
  109. }
  110. func (me *tSQLParser) ParseWherePart(queue *tTokenQueue) error {
  111. if queue.IsEmpty() {
  112. // no where clause
  113. me.result.RowFilter = newEmptyRowFilter(true)
  114. return nil
  115. }
  116. _,v1 := queue.Poll()
  117. if v1.token != tokens.Where {
  118. return errors.New(fmt.Sprintf("expecting 'where' keyword at %v", v1.from))
  119. }
  120. stack := newArrayStack()
  121. e, expression := me.ParseWhereExpression(queue, stack)
  122. if e != nil {
  123. return e
  124. }
  125. me.result.RowFilter = newExpressionRowFilter(expression)
  126. return nil
  127. }
  128. func (me *tSQLParser) ParseWhereExpression(queue *tTokenQueue, stack *tArrayStack) (error, IBoolExpression) {
  129. for {
  130. if queue.IsEmpty() {
  131. break
  132. }
  133. _,t := queue.Poll()
  134. switch t.token {
  135. case tokens.LB:
  136. stack.Push(t)
  137. break
  138. case tokens.Identifier:
  139. stack.Push(t)
  140. break
  141. case tokens.Equal:
  142. stack.Push(t)
  143. break
  144. case tokens.NotEqual:
  145. stack.Push(t)
  146. break
  147. case tokens.Like:
  148. stack.Push(t)
  149. break
  150. case tokens.NotLike:
  151. stack.Push(t)
  152. break
  153. case tokens.Greater:
  154. stack.Push(t)
  155. break
  156. case tokens.GreaterEqual:
  157. stack.Push(t)
  158. break
  159. case tokens.Less:
  160. stack.Push(t)
  161. break
  162. case tokens.LessEqual:
  163. stack.Push(t)
  164. break
  165. case tokens.And:
  166. stack.Push(t)
  167. break
  168. case tokens.OR:
  169. stack.Push(t)
  170. break
  171. case tokens.StringLiteral:
  172. // field op string
  173. b, v := stack.Pop()
  174. if !b || !v.IsToken() {
  175. return errors.New(fmt.Sprintf("expecting operator before %s", t.value)), nil
  176. }
  177. op := v.(*tTokenNode)
  178. if !me.TokenIn(op.token, tokens.Equal, tokens.NotEqual, tokens.Like, tokens.NotLike) {
  179. return errors.New(fmt.Sprintf("expecting string operator before %s", t.value)), nil
  180. }
  181. b,v = stack.Pop()
  182. if !b || !v.IsToken() {
  183. return errors.New(fmt.Sprintf("expecting column name before %s", op.from)), nil
  184. }
  185. field := v.(*tTokenNode)
  186. if field.token != tokens.Identifier {
  187. return errors.New(fmt.Sprintf("expecting column name at %v", field.from)), nil
  188. }
  189. exp := newStringFieldExpression(op.token, field.value, t.value)
  190. e := me.PushExpression(exp, stack)
  191. if e != nil {
  192. return e, nil
  193. }
  194. break
  195. case tokens.IntLiteral:
  196. // field op int
  197. b, v := stack.Pop()
  198. if !b || !v.IsToken() {
  199. return errors.New(fmt.Sprintf("expecting operator before %s", t.value)), nil
  200. }
  201. op := v.(*tTokenNode)
  202. if !me.TokenIn(op.token, tokens.Equal, tokens.NotEqual, tokens.Greater, tokens.GreaterEqual, tokens.Less, tokens.LessEqual) {
  203. return errors.New(fmt.Sprintf("expecting int operator before %s", t.value)), nil
  204. }
  205. b,v = stack.Pop()
  206. if !b || !v.IsToken() {
  207. return errors.New(fmt.Sprintf("expecting column name before %v", op.from)), nil
  208. }
  209. field := v.(*tTokenNode)
  210. if field.token != tokens.Identifier {
  211. return errors.New(fmt.Sprintf("expecting column name at %v", field.from)), nil
  212. }
  213. i,_ := strconv.Atoi(t.value)
  214. exp := newIntFieldExpression(op.token, field.value, i)
  215. e := me.PushExpression(exp, stack)
  216. if e != nil {
  217. return e, nil
  218. }
  219. break
  220. case tokens.RB:
  221. // )
  222. b,v := stack.Pop()
  223. if !b || !v.IsExpression() {
  224. return errors.New(fmt.Sprintf("expecting expression before %v", t.from)), nil
  225. }
  226. expression := v.(*tExpressionNode).Expression
  227. b,v = stack.Pop()
  228. if !b || !v.IsToken() {
  229. return errors.New(fmt.Sprintf("expected ( not found at %v", t.from)), nil
  230. }
  231. lb := v.(*tTokenNode)
  232. if lb.token != tokens.LB {
  233. return errors.New(fmt.Sprintf("expected ( not found at %v", t.from)), nil
  234. }
  235. e := me.PushExpression(expression, stack)
  236. if e != nil {
  237. return e, nil
  238. }
  239. break
  240. }
  241. }
  242. if stack.Size() != 1{
  243. return errors.New("invalid expression"), nil
  244. }
  245. ok, node := stack.Peek()
  246. if !ok || !node.IsExpression() {
  247. return errors.New("invalid expression"), nil
  248. }
  249. return nil,node.(*tExpressionNode).Expression
  250. }
  251. func (me *tSQLParser) TokenIn(t tokens.Tokens, args... tokens.Tokens) bool {
  252. for _,it := range args {
  253. if it == t {
  254. return true
  255. }
  256. }
  257. return false
  258. }
  259. func (me *tSQLParser) PushExpression(exp IBoolExpression, stack *tArrayStack) error {
  260. b,n := stack.Peek()
  261. if !b {
  262. stack.Push(newExpressionNode(exp))
  263. return nil
  264. }
  265. if !n.IsToken() {
  266. return errors.New("expecting and/or/(")
  267. }
  268. t := n.(*tTokenNode)
  269. if !me.TokenIn(t.token, tokens.And, tokens.OR, tokens.LB) {
  270. return errors.New("expecting and/or/(")
  271. }
  272. if me.TokenIn(t.token, tokens.And, tokens.OR) {
  273. stack.Pop()
  274. b,n = stack.Pop()
  275. if !b || !n.IsExpression(){
  276. return errors.New("expecting bool expression")
  277. }
  278. e := n.(*tExpressionNode)
  279. return me.PushExpression(newLogicNode(t.token, e.Expression, exp), stack)
  280. } else {
  281. stack.Push(newExpressionNode(exp))
  282. return nil
  283. }
  284. }

tTokenQueue.go

词法节点队列

  1. package interpreter
  2. type tTokenQueue struct {
  3. items []*tTokenNode
  4. size int
  5. next int
  6. }
  7. func newTokenQueue(nodes []*tTokenNode) *tTokenQueue {
  8. return &tTokenQueue{
  9. nodes,
  10. len(nodes),
  11. 0,
  12. }
  13. }
  14. func (me *tTokenQueue) Poll() (bool,*tTokenNode) {
  15. b,v := me.Peek()
  16. if b {
  17. me.next++
  18. }
  19. return b,v
  20. }
  21. func (me *tTokenQueue) Peek() (bool, *tTokenNode) {
  22. if me.next >= me.size {
  23. return false, nil
  24. }
  25. it := me.items[me.next]
  26. return true, it
  27. }
  28. func (me *tTokenQueue) IsEmpty() bool {
  29. return me.next >= me.size
  30. }

tArrayStack.go

基于数组实现的LIFO堆栈

  1. package interpreter
  2. import "learning/gooop/behavioral_patterns/interpreter/tokens"
  3. type tArrayStack struct {
  4. items []iStackNode
  5. }
  6. type iStackNode interface {
  7. IsToken() bool
  8. IsExpression() bool
  9. }
  10. func newArrayStack() *tArrayStack {
  11. return &tArrayStack{
  12. make([]iStackNode, 0),
  13. }
  14. }
  15. func (me *tArrayStack) Push(node iStackNode) {
  16. me.items = append(me.items, node)
  17. }
  18. func (me *tArrayStack) Size() int {
  19. return len(me.items)
  20. }
  21. func (me *tArrayStack) Peek() (bool,iStackNode) {
  22. if me.Size() <= 0 {
  23. return false, nil
  24. }
  25. return true, me.items[me.Size() - 1]
  26. }
  27. func (me *tArrayStack) Pop() (bool, iStackNode) {
  28. if me.Size() <= 0 {
  29. return false, nil
  30. }
  31. it := me.items[me.Size() - 1]
  32. me.items = me.items[:me.Size() - 1]
  33. return true, it
  34. }
  35. type tTokenNode struct {
  36. token tokens.Tokens
  37. value string
  38. from int
  39. to int
  40. }
  41. func newTokenNode(t tokens.Tokens, v string, from int, to int) *tTokenNode {
  42. return &tTokenNode{
  43. t,v, from, to,
  44. }
  45. }
  46. func (me *tTokenNode) IsToken() bool {
  47. return true
  48. }
  49. func (me *tTokenNode) IsExpression() bool {
  50. return false
  51. }
  52. type tExpressionNode struct {
  53. Expression IBoolExpression
  54. }
  55. func newExpressionNode(e IBoolExpression) *tExpressionNode {
  56. return &tExpressionNode{
  57. e,
  58. }
  59. }
  60. func (me *tExpressionNode) IsToken() bool {
  61. return false
  62. }
  63. func (me *tExpressionNode) IsExpression() bool {
  64. return true
  65. }

IBoolExpression.go

布尔表达式接口

  1. package interpreter
  2. type IBoolExpression interface {
  3. Eval(row IDataRow) bool
  4. }

tFieldExpression.go

基于字段计算的布尔表达式

  1. package interpreter
  2. import (
  3. "errors"
  4. "fmt"
  5. "learning/gooop/behavioral_patterns/interpreter/tokens"
  6. "strings"
  7. )
  8. type tFieldExpression struct {
  9. op tokens.Tokens
  10. field string
  11. stringLiteral string
  12. intLiteral int
  13. }
  14. func newStringFieldExpression(op tokens.Tokens, field string, s string) *tFieldExpression {
  15. return &tFieldExpression{
  16. op, field, strings.Trim(s, "'"), 0,
  17. }
  18. }
  19. func newIntFieldExpression(op tokens.Tokens, field string, value int) *tFieldExpression {
  20. return &tFieldExpression{
  21. op, field, "", value,
  22. }
  23. }
  24. func (me *tFieldExpression) Eval(row IDataRow) bool {
  25. e, fld := row.GetField(me.field)
  26. if e != nil {
  27. panic(e)
  28. }
  29. switch fld.DataType() {
  30. case StringDataType:
  31. e,v := fld.GetString()
  32. if e != nil {
  33. panic(e)
  34. }
  35. return me.EvalString(v)
  36. case IntDataType:
  37. e,v := fld.GetInt()
  38. if e != nil {
  39. panic(e)
  40. }
  41. return me.EvalInt(v)
  42. default:
  43. panic(errors.New("unknown data type"))
  44. }
  45. }
  46. func (me *tFieldExpression) EvalString(value string) bool {
  47. switch me.op {
  48. case tokens.Equal:
  49. return value == me.stringLiteral
  50. case tokens.NotEqual:
  51. return value != me.stringLiteral
  52. case tokens.Like:
  53. fallthrough
  54. case tokens.NotLike:
  55. like := false
  56. p := strings.HasPrefix(value, "%")
  57. s := strings.HasPrefix(value, "%")
  58. if p && s {
  59. like = strings.Contains(value, me.stringLiteral)
  60. } else if p {
  61. like = strings.HasSuffix(value, me.stringLiteral)
  62. } else if s {
  63. like = strings.HasPrefix(value, me.stringLiteral)
  64. } else {
  65. like = value == me.stringLiteral
  66. }
  67. if me.op == tokens.Like {
  68. return like
  69. } else {
  70. return !like
  71. }
  72. break
  73. default:
  74. panic(errors.New(fmt.Sprintf("unsupported string operation: %s", me.op)))
  75. }
  76. return false
  77. }
  78. func (me *tFieldExpression) EvalInt(value int) bool {
  79. switch me.op {
  80. case tokens.Equal:
  81. return value == me.intLiteral
  82. case tokens.NotEqual:
  83. return value != me.intLiteral
  84. case tokens.Greater:
  85. return value > me.intLiteral
  86. case tokens.GreaterEqual:
  87. return value >= me.intLiteral
  88. case tokens.Less:
  89. return value < me.intLiteral
  90. case tokens.LessEqual:
  91. return value <= me.intLiteral
  92. default:
  93. panic(errors.New(fmt.Sprintf("unsupported int operation: %s", me.op)))
  94. }
  95. }

tLogicExpression.go

基于关系(AND/OR)计算的布尔表达式

  1. package interpreter
  2. import (
  3. "fmt"
  4. "learning/gooop/behavioral_patterns/interpreter/tokens"
  5. )
  6. type tLogicExpression struct {
  7. op tokens.Tokens
  8. left IBoolExpression
  9. right IBoolExpression
  10. }
  11. func newLogicNode(op tokens.Tokens, left IBoolExpression, right IBoolExpression) *tLogicExpression {
  12. return &tLogicExpression{
  13. op, left, right,
  14. }
  15. }
  16. func (me *tLogicExpression) Eval(row IDataRow) bool {
  17. switch me.op {
  18. case tokens.And:
  19. return me.left.Eval(row) && me.right.Eval(row)
  20. case tokens.OR:
  21. return me.left.Eval(row) || me.right.Eval(row)
  22. default:
  23. panic(fmt.Sprintf("unsupported bool operation: %s", me.op))
  24. }
  25. }

SaleOrder.go

销售订单实体类

  1. package interpreter
  2. import (
  3. "errors"
  4. "fmt"
  5. "strings"
  6. )
  7. type SaleOrder struct {
  8. id *tIntField
  9. customer *tStringField
  10. city *tStringField
  11. product *tStringField
  12. quantity *tIntField
  13. }
  14. func NewSaleOrder(id int, customer string, city string, product string, quantity int) *SaleOrder {
  15. return &SaleOrder{
  16. id: newIntField("id", id),
  17. customer: newStringField("customer", customer),
  18. city: newStringField("city", city),
  19. product: newStringField("product", product),
  20. quantity: newIntField("quantity", quantity),
  21. }
  22. }
  23. func (me *SaleOrder) ID() int {
  24. return me.id.value
  25. }
  26. func (me *SaleOrder) Customer() string {
  27. return me.customer.value
  28. }
  29. func (me *SaleOrder) City() string {
  30. return me.city.value
  31. }
  32. func (me *SaleOrder) Product() string {
  33. return me.product.value
  34. }
  35. func (me *SaleOrder) Quantity() int {
  36. return me.quantity.value
  37. }
  38. func (me *SaleOrder) GetField(field string) (error,IDataField) {
  39. if strings.EqualFold(field, "ID") {
  40. return nil, me.id
  41. }
  42. if strings.EqualFold(field, "Customer") {
  43. return nil, me.customer
  44. }
  45. if strings.EqualFold(field, "City") {
  46. return nil, me.city
  47. }
  48. if strings.EqualFold(field, "Product") {
  49. return nil, me.product
  50. }
  51. if strings.EqualFold(field, "Quantity") {
  52. return nil, me.quantity
  53. }
  54. return errors.New("no such field"), nil
  55. }
  56. func (me *SaleOrder) String() string {
  57. return fmt.Sprintf("id=%v, customer=%v, city=%v, product=%v, quantity=%v", me.ID(), me.Customer(), me.City(), me.Product(), me.Quantity())
  58. }

ISaleOrderService.go

销售订单服务接口, 继承自IDataTable接口

  1. package interpreter
  2. type ISaleOrderService interface {
  3. IDataTable
  4. Save(order *SaleOrder)
  5. }

tMockDatabase.go

虚拟的数据库服务, 实现IDatabase接口

  1. package interpreter
  2. import "errors"
  3. type tMockDatabase struct {
  4. tables map[string]IDataTable
  5. }
  6. func newMockDatabase() IDatabase {
  7. return &tMockDatabase{
  8. tables: make(map[string]IDataTable, 0),
  9. }
  10. }
  11. func (me *tMockDatabase) Register(table IDataTable) {
  12. me.tables[table.Name()] = table
  13. }
  14. func (me *tMockDatabase) Query(sql string) (error, []IDataRow) {
  15. parser := newSQLParser()
  16. e, result := parser.Parse(sql)
  17. if e != nil {
  18. return e, nil
  19. }
  20. table, ok := me.tables[result.Table]
  21. if !ok {
  22. return errors.New("table not found"), nil
  23. }
  24. return nil, table.Filter(result.RowFilter)
  25. }
  26. var MockDatabase = newMockDatabase()

tMockSaleOrderService.go

虚拟的销售订单服务, 实现ISaleOrderService接口

  1. package interpreter
  2. type tMockSaleOrderService struct {
  3. items map[int]*SaleOrder
  4. }
  5. func (me *tMockSaleOrderService) Save(it *SaleOrder) {
  6. me.items[it.ID()] = it
  7. }
  8. func (me *tMockSaleOrderService) Name() string {
  9. return "sale_order"
  10. }
  11. func (me *tMockSaleOrderService) Filter(filter IRowFilter) []IDataRow {
  12. rows := make([]IDataRow, 0)
  13. for _,it := range me.items {
  14. if filter.Filter(it) {
  15. rows = append(rows, it)
  16. }
  17. }
  18. return rows
  19. }
  20. func newMockSaleOrderService() ISaleOrderService {
  21. it := &tMockSaleOrderService{
  22. items: make(map[int]*SaleOrder, 0),
  23. }
  24. MockDatabase.Register(it)
  25. return it
  26. }
  27. var MockSaleOrderService = newMockSaleOrderService()

解释器模式小结

  1. 解释器模式的优点
  2. 1)在解释器模式中,由于语法是由很多类表示的,当语法规则更改时,
  3. 只需修改相应的非终结符表达式即可;
  4. 当扩展语法时,只需添加相应的非终结符类即可。
  5. 2)增加了新的解释表达式的方式。
  6. 3)解释器模式对应的文法应当是比较简单且易于实现的,过于复杂的语法并不适合使用解释器模式。
  7. 解释器模式的缺点
  8. 1)解释器模式的每个语法都要产生一个非终结符表达式,当语法规则比较复杂时,
  9. 就会产生大量解释类,引起类膨胀,增加系统维护的难度。
  10. 2)解释器模式采用递归调用方法,
  11. 每个非终结符表达式都只关心与自己有关的表达式,
  12. 每个表达式都需要知道最终的结果,
  13. 因此完整表达式的最终结果是通过从后往前递归调用的方式获取的。
  14. 当完整表达式层级较深时,解释效率下降,
  15. 且出错时调试困难,因为递归迭代的层级太深。
  16. (摘自 谭勇德 <<设计模式就该这样学>>)

(end)