prettyprinter.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /*
  2. * EliasDB
  3. *
  4. * Copyright 2016 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the Mozilla Public
  7. * License, v. 2.0. If a copy of the MPL was not distributed with this
  8. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  9. */
  10. package parser
  11. import (
  12. "bytes"
  13. "fmt"
  14. "regexp"
  15. "strings"
  16. "text/template"
  17. "devt.de/krotik/common/errorutil"
  18. "devt.de/krotik/common/stringutil"
  19. )
  20. /*
  21. Map of pretty printer templates for AST nodes
  22. There is special treatment for NodeVALUE, NodeGET, NodeLOOKUP, NodeTRAVERSE,
  23. NodeFUNC, NodeSHOW, NodeSHOWTERM, NodeORDERING, NodeFILTERING, NodeWITH,
  24. NodeLPAREN, NodeRPAREN, NodeLBRACK and NodeRBRACK.
  25. */
  26. var prettyPrinterMap = map[string]*template.Template{
  27. NodeTRUE: template.Must(template.New(NodeTRUE).Parse("true")),
  28. NodeFALSE: template.Must(template.New(NodeFALSE).Parse("false")),
  29. NodeNULL: template.Must(template.New(NodeNULL).Parse("null")),
  30. NodeNULLTRAVERSAL + "_1": template.Must(template.New(NodeNULLTRAVERSAL).Parse("nulltraversal({{.c1}})")),
  31. // Special tokens - always handled in a denotation function
  32. NodeGROUP + "_1": template.Must(template.New(NodeGROUP).Parse("group {{.c1}}")),
  33. NodeEND: template.Must(template.New(NodeEND).Parse("end")),
  34. NodeAS + "_1": template.Must(template.New(NodeAS).Parse("as {{.c1}}")),
  35. NodeFORMAT + "_1": template.Must(template.New(NodeFORMAT).Parse("format {{.c1}}")),
  36. // Keywords
  37. NodeFROM + "_1": template.Must(template.New(NodeFROM).Parse("from {{.c1}}")),
  38. NodeWHERE + "_1": template.Must(template.New(NodeWHERE).Parse("where {{.c1}}")),
  39. NodeUNIQUE + "_1": template.Must(template.New(NodeUNIQUE).Parse("unique {{.c1}}")),
  40. NodeUNIQUECOUNT + "_1": template.Must(template.New(NodeUNIQUECOUNT).Parse("uniquecount {{.c1}}")),
  41. NodeISNOTNULL + "_1": template.Must(template.New(NodeISNOTNULL).Parse("isnotnull {{.c1}}")),
  42. NodeASCENDING + "_1": template.Must(template.New(NodeASCENDING).Parse("ascending {{.c1}}")),
  43. NodeDESCENDING + "_1": template.Must(template.New(NodeDESCENDING).Parse("descending {{.c1}}")),
  44. NodePRIMARY + "_1": template.Must(template.New(NodePRIMARY).Parse("primary {{.c1}}")),
  45. NodeLIST: template.Must(template.New(NodeLIST).Parse("list")),
  46. // Boolean operations
  47. NodeNOT + "_1": template.Must(template.New(NodeNOT).Parse("not {{.c1}}")),
  48. NodeGEQ + "_2": template.Must(template.New(NodeGEQ).Parse("{{.c1}} >= {{.c2}}")),
  49. NodeLEQ + "_2": template.Must(template.New(NodeLEQ).Parse("{{.c1}} <= {{.c2}}")),
  50. NodeNEQ + "_2": template.Must(template.New(NodeNEQ).Parse("{{.c1}} != {{.c2}}")),
  51. NodeEQ + "_2": template.Must(template.New(NodeEQ).Parse("{{.c1}} = {{.c2}}")),
  52. NodeGT + "_2": template.Must(template.New(NodeGT).Parse("{{.c1}} > {{.c2}}")),
  53. NodeLT + "_2": template.Must(template.New(NodeLT).Parse("{{.c1}} < {{.c2}}")),
  54. // List operations
  55. NodeIN + "_2": template.Must(template.New(NodeIN).Parse("{{.c1}} in {{.c2}}")),
  56. NodeNOTIN + "_2": template.Must(template.New(NodeNOTIN).Parse("{{.c1}} notin {{.c2}}")),
  57. // String operations
  58. NodeLIKE + "_2": template.Must(template.New(NodeLIKE).Parse("{{.c1}} like {{.c2}}")),
  59. NodeCONTAINS + "_2": template.Must(template.New(NodeCONTAINS).Parse("{{.c1}} contains {{.c2}}")),
  60. NodeBEGINSWITH + "_2": template.Must(template.New(NodeBEGINSWITH).Parse("{{.c1}} beginswith {{.c2}}")),
  61. NodeENDSWITH + "_2": template.Must(template.New(NodeENDSWITH).Parse("{{.c1}} endswith {{.c2}}")),
  62. NodeCONTAINSNOT + "_2": template.Must(template.New(NodeCONTAINSNOT).Parse("{{.c1}} containsnot {{.c2}}")),
  63. // Simple arithmetic expressions
  64. NodePLUS + "_2": template.Must(template.New(NodePLUS).Parse("{{.c1}} + {{.c2}}")),
  65. NodeMINUS + "_1": template.Must(template.New(NodeMINUS).Parse("-{{.c1}}")),
  66. NodeMINUS + "_2": template.Must(template.New(NodeMINUS).Parse("{{.c1}} - {{.c2}}")),
  67. NodeTIMES + "_2": template.Must(template.New(NodeTIMES).Parse("{{.c1}} * {{.c2}}")),
  68. NodeDIV + "_2": template.Must(template.New(NodeDIV).Parse("{{.c1}} / {{.c2}}")),
  69. NodeMODINT + "_2": template.Must(template.New(NodeMODINT).Parse("{{.c1}} % {{.c2}}")),
  70. NodeDIVINT + "_2": template.Must(template.New(NodeDIVINT).Parse("{{.c1}} // {{.c2}}")),
  71. }
  72. /*
  73. Map of nodes where the precedence might have changed because of parentheses
  74. */
  75. var bracketPrecedenceMap = map[string]bool{
  76. NodePLUS: true,
  77. NodeMINUS: true,
  78. NodeAND: true,
  79. NodeOR: true,
  80. }
  81. /*
  82. PrettyPrint produces a pretty printed EQL query from a given AST.
  83. */
  84. func PrettyPrint(ast *ASTNode) (string, error) {
  85. var visit func(ast *ASTNode, level int) (string, error)
  86. quoteValue := func(val string, allowNonQuotation bool) string {
  87. if val == "" {
  88. return `""`
  89. }
  90. isNumber, _ := regexp.MatchString("^[0-9][0-9\\.e-+]*$", val)
  91. isInlineString, _ := regexp.MatchString("^[a-zA-Z0-9_:.]*$", val)
  92. if allowNonQuotation && (isNumber || isInlineString) {
  93. return val
  94. } else if strings.ContainsRune(val, '"') {
  95. if strings.ContainsRune(val, '\'') {
  96. val = strings.Replace(val, "\"", "\\\"", -1)
  97. } else {
  98. return fmt.Sprintf("'%v'", val)
  99. }
  100. }
  101. return fmt.Sprintf("\"%v\"", val)
  102. }
  103. visit = func(ast *ASTNode, level int) (string, error) {
  104. // Handle special cases which don't have children
  105. if ast.Name == NodeVALUE || (ast.Name == NodeSHOWTERM && len(ast.Children) == 0) {
  106. return quoteValue(ast.Token.Val, true), nil
  107. }
  108. var children map[string]string
  109. var tempKey = ast.Name
  110. var buf bytes.Buffer
  111. // First pretty print children
  112. if len(ast.Children) > 0 {
  113. children = make(map[string]string)
  114. for i, child := range ast.Children {
  115. res, err := visit(child, level+1)
  116. if err != nil {
  117. return "", err
  118. }
  119. if _, ok := bracketPrecedenceMap[child.Name]; ok && ast.binding > child.binding {
  120. res = fmt.Sprintf("(%v)", res)
  121. }
  122. children[fmt.Sprint("c", i+1)] = res
  123. }
  124. tempKey += fmt.Sprint("_", len(children))
  125. }
  126. // Handle special cases requiring children
  127. if ast.Name == NodeLIST {
  128. buf.WriteString("[")
  129. if children != nil {
  130. i := 1
  131. for ; i < len(children); i++ {
  132. buf.WriteString(children[fmt.Sprint("c", i)])
  133. buf.WriteString(", ")
  134. }
  135. buf.WriteString(children[fmt.Sprint("c", i)])
  136. }
  137. buf.WriteString("]")
  138. return buf.String(), nil
  139. } else if ast.Name == NodeLOOKUP {
  140. buf.WriteString("lookup ")
  141. buf.WriteString(children["c1"])
  142. if 1 < len(children) {
  143. buf.WriteString(" ")
  144. }
  145. i := 1
  146. for ; i < len(children) && ast.Children[i].Name == NodeVALUE; i++ {
  147. buf.WriteString(quoteValue(ast.Children[i].Token.Val, false))
  148. if i < len(children)-1 && ast.Children[i+1].Name == NodeVALUE {
  149. buf.WriteString(", ")
  150. }
  151. }
  152. if i < len(children) {
  153. buf.WriteString(" ")
  154. }
  155. for ; i < len(children); i++ {
  156. buf.WriteString(children[fmt.Sprint("c", i+1)])
  157. if i < len(children)-1 && ast.Children[i+1].Name != NodeSHOW {
  158. buf.WriteString(" ")
  159. }
  160. }
  161. return buf.String(), nil
  162. } else if ast.Name == NodeGET {
  163. buf.WriteString("get ")
  164. buf.WriteString(children["c1"])
  165. if 1 < len(children) {
  166. buf.WriteString(" ")
  167. }
  168. for i := 1; i < len(children); i++ {
  169. buf.WriteString(children[fmt.Sprint("c", i+1)])
  170. if i < len(children)-1 && ast.Children[i+1].Name != NodeSHOW {
  171. buf.WriteString(" ")
  172. }
  173. }
  174. return buf.String(), nil
  175. } else if ast.Name == NodeTRAVERSE {
  176. buf.WriteString("\n")
  177. buf.WriteString(stringutil.GenerateRollingString(" ", level*2))
  178. buf.WriteString("traverse ")
  179. for i := 0; i < len(children); i++ {
  180. buf.WriteString(children[fmt.Sprint("c", i+1)])
  181. if i < len(children)-1 {
  182. buf.WriteString(" ")
  183. }
  184. }
  185. buf.WriteString("\n")
  186. buf.WriteString(stringutil.GenerateRollingString(" ", level*2))
  187. buf.WriteString("end")
  188. return buf.String(), nil
  189. } else if ast.Name == NodeFUNC {
  190. buf.WriteString("@")
  191. buf.WriteString(children["c1"])
  192. buf.WriteString("(")
  193. for i := 1; i < len(children); i++ {
  194. buf.WriteString(children[fmt.Sprint("c", i+1)])
  195. if i < len(children)-1 {
  196. buf.WriteString(", ")
  197. }
  198. }
  199. buf.WriteString(")")
  200. return buf.String(), nil
  201. } else if ast.Name == NodeSHOW {
  202. buf.WriteString("\nshow\n ")
  203. for i := 0; i < len(children); i++ {
  204. buf.WriteString(children[fmt.Sprint("c", i+1)])
  205. if i < len(children)-1 {
  206. buf.WriteString(",\n ")
  207. }
  208. }
  209. return buf.String(), nil
  210. } else if ast.Name == NodeSHOWTERM {
  211. if ast.Token.Val != "" && ast.Token.Val != "@" {
  212. buf.WriteString(quoteValue(ast.Token.Val, true))
  213. buf.WriteString(" ")
  214. }
  215. for i := 0; i < len(children); i++ {
  216. buf.WriteString(children[fmt.Sprint("c", i+1)])
  217. if i < len(children)-1 {
  218. buf.WriteString(" ")
  219. }
  220. }
  221. return buf.String(), nil
  222. } else if ast.Name == NodeORDERING {
  223. buf.WriteString("ordering")
  224. buf.WriteString("(")
  225. for i := 0; i < len(children); i++ {
  226. buf.WriteString(children[fmt.Sprint("c", i+1)])
  227. if i < len(children)-1 {
  228. buf.WriteString(", ")
  229. }
  230. }
  231. buf.WriteString(")")
  232. return buf.String(), nil
  233. } else if ast.Name == NodeFILTERING {
  234. buf.WriteString("filtering")
  235. buf.WriteString("(")
  236. for i := 0; i < len(children); i++ {
  237. buf.WriteString(children[fmt.Sprint("c", i+1)])
  238. if i < len(children)-1 {
  239. buf.WriteString(", ")
  240. }
  241. }
  242. buf.WriteString(")")
  243. return buf.String(), nil
  244. } else if ast.Name == NodeWITH {
  245. buf.WriteString("\nwith\n")
  246. for i := 0; i < len(children); i++ {
  247. buf.WriteString(" ")
  248. buf.WriteString(children[fmt.Sprint("c", i+1)])
  249. if i < len(children)-1 {
  250. buf.WriteString(",\n")
  251. }
  252. }
  253. return buf.String(), nil
  254. } else if ast.Name == NodeAND || ast.Name == NodeOR {
  255. for i := 0; i < len(children); i++ {
  256. buf.WriteString(children[fmt.Sprint("c", i+1)])
  257. if i < len(children)-1 {
  258. buf.WriteString(" ")
  259. buf.WriteString(strings.ToLower(ast.Token.Val))
  260. buf.WriteString(" ")
  261. }
  262. }
  263. return buf.String(), nil
  264. }
  265. // Retrieve the template
  266. temp, ok := prettyPrinterMap[tempKey]
  267. if !ok {
  268. return "", fmt.Errorf("Could not find template for %v (tempkey: %v)",
  269. ast.Name, tempKey)
  270. }
  271. // Use the children as parameters for template
  272. errorutil.AssertOk(temp.Execute(&buf, children))
  273. return buf.String(), nil
  274. }
  275. return visit(ast, 0)
  276. }