prettyprinter.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  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. "strconv"
  14. "text/template"
  15. "devt.de/krotik/common/errorutil"
  16. "devt.de/krotik/common/stringutil"
  17. )
  18. /*
  19. Map of AST nodes corresponding to lexer tokens
  20. */
  21. var prettyPrinterMap map[string]*template.Template
  22. /*
  23. Map of nodes where the precedence might have changed because of parentheses
  24. */
  25. var bracketPrecedenceMap map[string]bool
  26. func init() {
  27. prettyPrinterMap = map[string]*template.Template{
  28. NodeSTRING: template.Must(template.New(NodeSTRING).Parse("{{.qval}}")),
  29. NodeNUMBER: template.Must(template.New(NodeNUMBER).Parse("{{.val}}")),
  30. // NodeIDENTIFIER - Special case (handled in code)
  31. // Constructed tokens
  32. // NodeSTATEMENTS - Special case (handled in code)
  33. // NodeFUNCCALL - Special case (handled in code)
  34. NodeCOMPACCESS + "_1": template.Must(template.New(NodeCOMPACCESS).Parse("[{{.c1}}]")),
  35. // TokenLIST - Special case (handled in code)
  36. // TokenMAP - Special case (handled in code)
  37. // TokenPARAMS - Special case (handled in code)
  38. NodeGUARD + "_1": template.Must(template.New(NodeGUARD).Parse("{{.c1}}")),
  39. // Condition operators
  40. NodeGEQ + "_2": template.Must(template.New(NodeGEQ).Parse("{{.c1}} >= {{.c2}}")),
  41. NodeLEQ + "_2": template.Must(template.New(NodeLEQ).Parse("{{.c1}} <= {{.c2}}")),
  42. NodeNEQ + "_2": template.Must(template.New(NodeNEQ).Parse("{{.c1}} != {{.c2}}")),
  43. NodeEQ + "_2": template.Must(template.New(NodeEQ).Parse("{{.c1}} == {{.c2}}")),
  44. NodeGT + "_2": template.Must(template.New(NodeGT).Parse("{{.c1}} > {{.c2}}")),
  45. NodeLT + "_2": template.Must(template.New(NodeLT).Parse("{{.c1}} < {{.c2}}")),
  46. // Separators
  47. NodeKVP + "_2": template.Must(template.New(NodeKVP).Parse("{{.c1}} : {{.c2}}")),
  48. NodePRESET + "_2": template.Must(template.New(NodePRESET).Parse("{{.c1}}={{.c2}}")),
  49. // Arithmetic operators
  50. NodePLUS + "_1": template.Must(template.New(NodePLUS).Parse("+{{.c1}}")),
  51. NodePLUS + "_2": template.Must(template.New(NodePLUS).Parse("{{.c1}} + {{.c2}}")),
  52. NodeMINUS + "_1": template.Must(template.New(NodeMINUS).Parse("-{{.c1}}")),
  53. NodeMINUS + "_2": template.Must(template.New(NodeMINUS).Parse("{{.c1}} - {{.c2}}")),
  54. NodeTIMES + "_2": template.Must(template.New(NodeTIMES).Parse("{{.c1}} * {{.c2}}")),
  55. NodeDIV + "_2": template.Must(template.New(NodeDIV).Parse("{{.c1}} / {{.c2}}")),
  56. NodeMODINT + "_2": template.Must(template.New(NodeMODINT).Parse("{{.c1}} % {{.c2}}")),
  57. NodeDIVINT + "_2": template.Must(template.New(NodeDIVINT).Parse("{{.c1}} // {{.c2}}")),
  58. // Assignment statement
  59. NodeASSIGN + "_2": template.Must(template.New(NodeASSIGN).Parse("{{.c1}} := {{.c2}}")),
  60. // Import statement
  61. NodeIMPORT + "_2": template.Must(template.New(NodeIMPORT).Parse("import {{.c1}} as {{.c2}}")),
  62. // Sink definition
  63. // NodeSINK - Special case (handled in code)
  64. NodeKINDMATCH + "_1": template.Must(template.New(NodeKINDMATCH).Parse("kindmatch {{.c1}}")),
  65. NodeSCOPEMATCH + "_1": template.Must(template.New(NodeSCOPEMATCH).Parse("scopematch {{.c1}}")),
  66. NodeSTATEMATCH + "_1": template.Must(template.New(NodeSTATEMATCH).Parse("statematch {{.c1}}")),
  67. NodePRIORITY + "_1": template.Must(template.New(NodePRIORITY).Parse("priority {{.c1}}")),
  68. NodeSUPPRESSES + "_1": template.Must(template.New(NodeSUPPRESSES).Parse("suppresses {{.c1}}")),
  69. // Function definition
  70. NodeFUNC + "_3": template.Must(template.New(NodeFUNC).Parse("func {{.c1}}{{.c2}} {\n{{.c3}}}")),
  71. NodeRETURN: template.Must(template.New(NodeRETURN).Parse("return")),
  72. NodeRETURN + "_1": template.Must(template.New(NodeRETURN).Parse("return {{.c1}}")),
  73. // Boolean operators
  74. NodeOR + "_2": template.Must(template.New(NodeOR).Parse("{{.c1}} or {{.c2}}")),
  75. NodeAND + "_2": template.Must(template.New(NodeAND).Parse("{{.c1}} and {{.c2}}")),
  76. NodeNOT + "_1": template.Must(template.New(NodeNOT).Parse("not {{.c1}}")),
  77. // Condition operators
  78. NodeLIKE + "_2": template.Must(template.New(NodeLIKE).Parse("{{.c1}} like {{.c2}}")),
  79. NodeIN + "_2": template.Must(template.New(NodeIN).Parse("{{.c1}} in {{.c2}}")),
  80. NodeHASPREFIX + "_2": template.Must(template.New(NodeHASPREFIX).Parse("{{.c1}} hasprefix {{.c2}}")),
  81. NodeHASSUFFIX + "_2": template.Must(template.New(NodeHASSUFFIX).Parse("{{.c1}} hassuffix {{.c2}}")),
  82. NodeNOTIN + "_2": template.Must(template.New(NodeNOTIN).Parse("{{.c1}} notin {{.c2}}")),
  83. // Constant terminals
  84. NodeTRUE: template.Must(template.New(NodeTRUE).Parse("true")),
  85. NodeFALSE: template.Must(template.New(NodeFALSE).Parse("false")),
  86. NodeNULL: template.Must(template.New(NodeNULL).Parse("null")),
  87. // Conditional statements
  88. // TokenIF - Special case (handled in code)
  89. // TokenELIF - Special case (handled in code)
  90. // TokenELSE - Special case (handled in code)
  91. // Loop statements
  92. NodeLOOP + "_2": template.Must(template.New(NodeLOOP).Parse("for {{.c1}} {\n{{.c2}}}\n")),
  93. NodeBREAK: template.Must(template.New(NodeBREAK).Parse("break")),
  94. NodeCONTINUE: template.Must(template.New(NodeCONTINUE).Parse("continue")),
  95. }
  96. bracketPrecedenceMap = map[string]bool{
  97. NodePLUS: true,
  98. NodeMINUS: true,
  99. NodeAND: true,
  100. NodeOR: true,
  101. }
  102. }
  103. /*
  104. PrettyPrint produces pretty printed code from a given AST.
  105. */
  106. func PrettyPrint(ast *ASTNode) (string, error) {
  107. var visit func(ast *ASTNode, level int) (string, error)
  108. ppMetaData := func(ast *ASTNode, ppString string) string {
  109. ret := ppString
  110. // Add meta data
  111. if len(ast.Meta) > 0 {
  112. for _, meta := range ast.Meta {
  113. if meta.Type() == MetaDataPreComment {
  114. ret = fmt.Sprintf("/*%v*/ %v", meta.Value(), ret)
  115. } else if meta.Type() == MetaDataPostComment {
  116. ret = fmt.Sprintf("%v #%v", ret, meta.Value())
  117. }
  118. }
  119. }
  120. return ret
  121. }
  122. visit = func(ast *ASTNode, level int) (string, error) {
  123. var buf bytes.Buffer
  124. var numChildren = len(ast.Children)
  125. tempKey := ast.Name
  126. tempParam := make(map[string]string)
  127. // First pretty print children
  128. if numChildren > 0 {
  129. for i, child := range ast.Children {
  130. res, err := visit(child, level+1)
  131. if err != nil {
  132. return "", err
  133. }
  134. if _, ok := bracketPrecedenceMap[child.Name]; ok && ast.binding > child.binding {
  135. // Put the expression in brackets iff (if and only if) the binding would
  136. // normally order things differently
  137. res = fmt.Sprintf("(%v)", res)
  138. }
  139. tempParam[fmt.Sprint("c", i+1)] = res
  140. }
  141. tempKey += fmt.Sprint("_", len(tempParam))
  142. }
  143. // Handle special cases - children in tempParam have been resolved
  144. if ast.Name == NodeSTATEMENTS {
  145. // For statements just concat all children
  146. for i := 0; i < numChildren; i++ {
  147. buf.WriteString(stringutil.GenerateRollingString(" ", level*4))
  148. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  149. buf.WriteString("\n")
  150. }
  151. return ppMetaData(ast, buf.String()), nil
  152. } else if ast.Name == NodeSINK {
  153. buf.WriteString("sink ")
  154. buf.WriteString(tempParam["c1"])
  155. buf.WriteString("\n")
  156. for i := 1; i < len(ast.Children)-1; i++ {
  157. buf.WriteString(" ")
  158. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  159. buf.WriteString("\n")
  160. }
  161. buf.WriteString("{\n")
  162. buf.WriteString(tempParam[fmt.Sprint("c", len(ast.Children))])
  163. buf.WriteString("}\n")
  164. return ppMetaData(ast, buf.String()), nil
  165. } else if ast.Name == NodeFUNCCALL {
  166. // For statements just concat all children
  167. for i := 0; i < numChildren; i++ {
  168. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  169. if i < numChildren-1 {
  170. buf.WriteString(", ")
  171. }
  172. }
  173. return ppMetaData(ast, buf.String()), nil
  174. } else if ast.Name == NodeIDENTIFIER {
  175. buf.WriteString(ast.Token.Val)
  176. for i := 0; i < numChildren; i++ {
  177. if ast.Children[i].Name == NodeIDENTIFIER {
  178. buf.WriteString(".")
  179. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  180. } else if ast.Children[i].Name == NodeFUNCCALL {
  181. buf.WriteString("(")
  182. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  183. buf.WriteString(")")
  184. } else if ast.Children[i].Name == NodeCOMPACCESS {
  185. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  186. }
  187. }
  188. return ppMetaData(ast, buf.String()), nil
  189. } else if ast.Name == NodeLIST {
  190. buf.WriteString("[")
  191. i := 1
  192. for ; i < numChildren; i++ {
  193. buf.WriteString(tempParam[fmt.Sprint("c", i)])
  194. buf.WriteString(", ")
  195. }
  196. buf.WriteString(tempParam[fmt.Sprint("c", i)])
  197. buf.WriteString("]")
  198. return ppMetaData(ast, buf.String()), nil
  199. } else if ast.Name == NodeMAP {
  200. buf.WriteString("{")
  201. i := 1
  202. for ; i < numChildren; i++ {
  203. buf.WriteString(tempParam[fmt.Sprint("c", i)])
  204. buf.WriteString(", ")
  205. }
  206. buf.WriteString(tempParam[fmt.Sprint("c", i)])
  207. buf.WriteString("}")
  208. return ppMetaData(ast, buf.String()), nil
  209. } else if ast.Name == NodePARAMS {
  210. buf.WriteString("(")
  211. i := 1
  212. for ; i < numChildren; i++ {
  213. buf.WriteString(tempParam[fmt.Sprint("c", i)])
  214. buf.WriteString(", ")
  215. }
  216. buf.WriteString(tempParam[fmt.Sprint("c", i)])
  217. buf.WriteString(")")
  218. return ppMetaData(ast, buf.String()), nil
  219. } else if ast.Name == NodeIF {
  220. writeGUARD := func(child int) {
  221. buf.WriteString(tempParam[fmt.Sprint("c", child)])
  222. buf.WriteString(" {\n")
  223. buf.WriteString(tempParam[fmt.Sprint("c", child+1)])
  224. buf.WriteString("}")
  225. }
  226. buf.WriteString("if ")
  227. writeGUARD(1)
  228. for i := 0; i < len(ast.Children); i += 2 {
  229. if i+2 == len(ast.Children) && ast.Children[i].Children[0].Name == NodeTRUE {
  230. buf.WriteString(" else {\n")
  231. buf.WriteString(tempParam[fmt.Sprint("c", i+2)])
  232. buf.WriteString("}")
  233. } else if i > 0 {
  234. buf.WriteString(" elif ")
  235. writeGUARD(i + 1)
  236. }
  237. }
  238. buf.WriteString("\n")
  239. return ppMetaData(ast, buf.String()), nil
  240. }
  241. if ast.Token != nil {
  242. // Adding node value to template parameters
  243. tempParam["val"] = ast.Token.Val
  244. tempParam["qval"] = strconv.Quote(ast.Token.Val)
  245. }
  246. // Retrieve the template
  247. temp, ok := prettyPrinterMap[tempKey]
  248. if !ok {
  249. return "", fmt.Errorf("Could not find template for %v (tempkey: %v)",
  250. ast.Name, tempKey)
  251. }
  252. // Use the children as parameters for template
  253. errorutil.AssertOk(temp.Execute(&buf, tempParam))
  254. return ppMetaData(ast, buf.String()), nil
  255. }
  256. return visit(ast, 0)
  257. }