prettyprinter.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * Public Domain Software
  3. *
  4. * I (Matthias Ladkau) am the author of the source code in this file.
  5. * I have placed the source code in this file in the public domain.
  6. *
  7. * For further information see: http://creativecommons.org/publicdomain/zero/1.0/
  8. */
  9. package parser
  10. import (
  11. "bytes"
  12. "fmt"
  13. "regexp"
  14. "strconv"
  15. "strings"
  16. "text/template"
  17. "unicode"
  18. "devt.de/krotik/common/errorutil"
  19. "devt.de/krotik/common/stringutil"
  20. )
  21. /*
  22. IndentationLevel is the level of indentation which the pretty printer should use
  23. */
  24. const IndentationLevel = 2
  25. /*
  26. Map of pretty printer templates for AST nodes
  27. There is special treatment for NodeVALUE.
  28. */
  29. var prettyPrinterMap = map[string]*template.Template{
  30. NodeArgument + "_2": template.Must(template.New(NodeArgument).Parse("{{.c1}}: {{.c2}}")),
  31. NodeOperationDefinition + "_1": template.Must(template.New(NodeArgument).Parse("{{.c1}}")),
  32. NodeOperationDefinition + "_2": template.Must(template.New(NodeArgument).Parse("{{.c1}} {{.c2}}")),
  33. NodeOperationDefinition + "_3": template.Must(template.New(NodeArgument).Parse("{{.c1}} {{.c2}} {{.c3}}")),
  34. NodeOperationDefinition + "_4": template.Must(template.New(NodeArgument).Parse("{{.c1}} {{.c2}} {{.c3}} {{.c4}}")),
  35. NodeOperationDefinition + "_5": template.Must(template.New(NodeArgument).Parse("{{.c1}} {{.c2}} {{.c3}} {{.c4}} {{.c5}}")),
  36. NodeFragmentDefinition + "_3": template.Must(template.New(NodeArgument).Parse("fragment {{.c1}} {{.c2}} {{.c3}}")),
  37. NodeFragmentDefinition + "_4": template.Must(template.New(NodeArgument).Parse("fragment {{.c1}} {{.c2}} {{.c3}} {{.c4}}")),
  38. NodeInlineFragment + "_1": template.Must(template.New(NodeArgument).Parse("... {{.c1}}\n")),
  39. NodeInlineFragment + "_2": template.Must(template.New(NodeArgument).Parse("... {{.c1}} {{.c2}}\n")),
  40. NodeInlineFragment + "_3": template.Must(template.New(NodeArgument).Parse("... {{.c1}} {{.c2}} {{.c3}}\n")),
  41. NodeExecutableDefinition + "_1": template.Must(template.New(NodeArgument).Parse("{{.c1}}")),
  42. NodeVariableDefinition + "_2": template.Must(template.New(NodeArgument).Parse("{{.c1}}: {{.c2}}")),
  43. NodeVariableDefinition + "_3": template.Must(template.New(NodeArgument).Parse("{{.c1}}: {{.c2}}{{.c3}}")),
  44. NodeDirective + "_1": template.Must(template.New(NodeArgument).Parse("@{{.c1}}")),
  45. NodeDirective + "_2": template.Must(template.New(NodeArgument).Parse("@{{.c1}}{{.c2}}")),
  46. }
  47. /*
  48. PrettyPrint produces a pretty printed EQL query from a given AST.
  49. */
  50. func PrettyPrint(ast *ASTNode) (string, error) {
  51. var visit func(ast *ASTNode, path []*ASTNode) (string, error)
  52. quoteValue := func(val string, allowNonQuotation bool) string {
  53. if val == "" {
  54. return `""`
  55. }
  56. isNumber, _ := regexp.MatchString("^[0-9][0-9\\.e-+]*$", val)
  57. isInlineString, _ := regexp.MatchString("^[a-zA-Z0-9_:.]*$", val)
  58. if allowNonQuotation && (isNumber || isInlineString) {
  59. return val
  60. } else if strings.ContainsRune(val, '"') {
  61. val = strings.Replace(val, "\"", "\\\"", -1)
  62. }
  63. if strings.Contains(val, "\n") {
  64. return fmt.Sprintf("\"\"\"%v\"\"\"", val)
  65. }
  66. return fmt.Sprintf("\"%v\"", val)
  67. }
  68. visit = func(ast *ASTNode, path []*ASTNode) (string, error) {
  69. // Handle special cases which don't have children but values
  70. if ast.Name == NodeValue {
  71. v := ast.Token.Val
  72. _, err := strconv.ParseFloat(v, 32)
  73. isNum := err == nil
  74. isConst := stringutil.IndexOf(v, []string{
  75. "true", "false", "null",
  76. }) != -1
  77. return quoteValue(ast.Token.Val, isConst || isNum), nil
  78. } else if ast.Name == NodeVariable {
  79. return fmt.Sprintf("$%v", ast.Token.Val), nil
  80. } else if ast.Name == NodeAlias {
  81. return fmt.Sprintf("%v :", ast.Token.Val), nil
  82. } else if ast.Name == NodeFragmentSpread {
  83. return ppPostProcessing(ast, path, fmt.Sprintf("...%v\n", ast.Token.Val)), nil
  84. } else if ast.Name == NodeTypeCondition {
  85. return fmt.Sprintf("on %v", ast.Token.Val), nil
  86. } else if ast.Name == NodeDefaultValue {
  87. return fmt.Sprintf("=%v", ast.Token.Val), nil
  88. }
  89. var children map[string]string
  90. var tempKey = ast.Name
  91. var buf bytes.Buffer
  92. // First pretty print children
  93. if len(ast.Children) > 0 {
  94. children = make(map[string]string)
  95. for i, child := range ast.Children {
  96. res, err := visit(child, append(path, child))
  97. if err != nil {
  98. return "", err
  99. }
  100. children[fmt.Sprint("c", i+1)] = res
  101. }
  102. tempKey += fmt.Sprint("_", len(children))
  103. }
  104. // Handle special cases requiring children
  105. if ast.Name == NodeDocument {
  106. if children != nil {
  107. i := 1
  108. for ; i < len(children); i++ {
  109. buf.WriteString(children[fmt.Sprint("c", i)])
  110. if ast.Children[i].Name != NodeArguments {
  111. buf.WriteString("\n\n")
  112. }
  113. }
  114. buf.WriteString(children[fmt.Sprint("c", i)])
  115. }
  116. return ppPostProcessing(ast, path, buf.String()), nil
  117. } else if ast.Name == NodeOperationType || ast.Name == NodeName ||
  118. ast.Name == NodeFragmentName || ast.Name == NodeType || ast.Name == NodeEnumValue {
  119. return ast.Token.Val, nil
  120. } else if ast.Name == NodeArguments {
  121. buf.WriteString("(")
  122. if children != nil {
  123. i := 1
  124. for ; i < len(children); i++ {
  125. buf.WriteString(children[fmt.Sprint("c", i)])
  126. buf.WriteString(", ")
  127. }
  128. buf.WriteString(children[fmt.Sprint("c", i)])
  129. }
  130. buf.WriteString(")")
  131. return ppPostProcessing(ast, path, buf.String()), nil
  132. } else if ast.Name == NodeListValue {
  133. buf.WriteString("[")
  134. if children != nil {
  135. i := 1
  136. for ; i < len(children); i++ {
  137. buf.WriteString(children[fmt.Sprint("c", i)])
  138. buf.WriteString(", ")
  139. }
  140. buf.WriteString(children[fmt.Sprint("c", i)])
  141. }
  142. buf.WriteString("]")
  143. return ppPostProcessing(ast, path, buf.String()), nil
  144. } else if ast.Name == NodeVariableDefinitions {
  145. buf.WriteString("(")
  146. if children != nil {
  147. i := 1
  148. for ; i < len(children); i++ {
  149. buf.WriteString(children[fmt.Sprint("c", i)])
  150. buf.WriteString(", ")
  151. }
  152. buf.WriteString(children[fmt.Sprint("c", i)])
  153. }
  154. buf.WriteString(")")
  155. return ppPostProcessing(ast, path, buf.String()), nil
  156. } else if ast.Name == NodeSelectionSet {
  157. buf.WriteString("{\n")
  158. if children != nil {
  159. i := 1
  160. for ; i < len(children); i++ {
  161. buf.WriteString(children[fmt.Sprint("c", i)])
  162. }
  163. buf.WriteString(children[fmt.Sprint("c", i)])
  164. }
  165. buf.WriteString("}")
  166. return ppPostProcessing(ast, path, buf.String()), nil
  167. } else if ast.Name == NodeObjectValue {
  168. buf.WriteString("{")
  169. if children != nil {
  170. i := 1
  171. for ; i < len(children); i++ {
  172. buf.WriteString(children[fmt.Sprint("c", i)])
  173. buf.WriteString(", ")
  174. }
  175. buf.WriteString(children[fmt.Sprint("c", i)])
  176. }
  177. buf.WriteString("}")
  178. return ppPostProcessing(ast, path, buf.String()), nil
  179. } else if ast.Name == NodeObjectField {
  180. buf.WriteString(ast.Token.Val)
  181. buf.WriteString(" : ")
  182. buf.WriteString(children["c1"])
  183. return buf.String(), nil
  184. } else if ast.Name == NodeField {
  185. if children != nil {
  186. i := 1
  187. for ; i < len(children); i++ {
  188. buf.WriteString(children[fmt.Sprint("c", i)])
  189. if ast.Children[i].Name != NodeArguments {
  190. buf.WriteString(" ")
  191. }
  192. }
  193. buf.WriteString(children[fmt.Sprint("c", i)])
  194. buf.WriteString("\n")
  195. }
  196. return ppPostProcessing(ast, path, buf.String()), nil
  197. } else if ast.Name == NodeDirectives {
  198. if children != nil {
  199. i := 1
  200. for ; i < len(children); i++ {
  201. buf.WriteString(children[fmt.Sprint("c", i)])
  202. if ast.Children[i].Name != NodeArguments {
  203. buf.WriteString(" ")
  204. }
  205. }
  206. buf.WriteString(children[fmt.Sprint("c", i)])
  207. }
  208. return ppPostProcessing(ast, path, buf.String()), nil
  209. }
  210. // Retrieve the template
  211. temp, ok := prettyPrinterMap[tempKey]
  212. if !ok {
  213. return "", fmt.Errorf("Could not find template for %v (tempkey: %v)",
  214. ast.Name, tempKey)
  215. }
  216. // Use the children as parameters for template
  217. errorutil.AssertOk(temp.Execute(&buf, children))
  218. return ppPostProcessing(ast, path, buf.String()), nil
  219. }
  220. res, err := visit(ast, []*ASTNode{ast})
  221. return strings.TrimSpace(res), err
  222. }
  223. /*
  224. ppPostProcessing applies post processing rules.
  225. */
  226. func ppPostProcessing(ast *ASTNode, path []*ASTNode, ppString string) string {
  227. ret := ppString
  228. // Apply indentation
  229. if len(path) > 1 {
  230. if stringutil.IndexOf(ast.Name, []string{
  231. NodeField,
  232. NodeFragmentSpread,
  233. NodeInlineFragment,
  234. }) != -1 {
  235. parent := path[len(path)-3]
  236. indentSpaces := stringutil.GenerateRollingString(" ", IndentationLevel)
  237. ret = strings.ReplaceAll(ret, "\n", "\n"+indentSpaces)
  238. ret = fmt.Sprintf("%v%v", indentSpaces, ret)
  239. // Remove indentation from last line unless we have a special case
  240. if stringutil.IndexOf(parent.Name, []string{
  241. NodeField,
  242. NodeOperationDefinition,
  243. }) == -1 {
  244. if idx := strings.LastIndex(ret, "\n"); idx != -1 {
  245. ret = ret[:idx+1] + ret[idx+IndentationLevel+1:]
  246. }
  247. }
  248. }
  249. }
  250. // Remove all trailing spaces
  251. newlineSplit := strings.Split(ret, "\n")
  252. for i, s := range newlineSplit {
  253. newlineSplit[i] = strings.TrimRightFunc(s, unicode.IsSpace)
  254. }
  255. return strings.Join(newlineSplit, "\n")
  256. }