prettyprinter.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. /*
  2. * ECAL
  3. *
  4. * Copyright 2020 Matthias Ladkau. All rights reserved.
  5. *
  6. * This Source Code Form is subject to the terms of the MIT
  7. * License, If a copy of the MIT License was not distributed with this
  8. * file, You can obtain one at https://opensource.org/licenses/MIT.
  9. */
  10. package parser
  11. import (
  12. "bufio"
  13. "bytes"
  14. "fmt"
  15. "strconv"
  16. "strings"
  17. "text/template"
  18. "unicode"
  19. "devt.de/krotik/common/errorutil"
  20. "devt.de/krotik/common/stringutil"
  21. )
  22. /*
  23. IndentationLevel is the level of indentation which the pretty printer should use
  24. */
  25. const IndentationLevel = 4
  26. /*
  27. Map of AST nodes corresponding to lexer tokens
  28. */
  29. var prettyPrinterMap map[string]*template.Template
  30. /*
  31. Map of nodes where the precedence might have changed because of parentheses
  32. */
  33. var bracketPrecedenceMap map[string]bool
  34. func init() {
  35. prettyPrinterMap = map[string]*template.Template{
  36. NodeSTRING: template.Must(template.New(NodeSTRING).Parse("{{.qval}}")),
  37. NodeNUMBER: template.Must(template.New(NodeNUMBER).Parse("{{.val}}")),
  38. // NodeIDENTIFIER - Special case (handled in code)
  39. // Constructed tokens
  40. // NodeSTATEMENTS - Special case (handled in code)
  41. // NodeFUNCCALL - Special case (handled in code)
  42. NodeCOMPACCESS + "_1": template.Must(template.New(NodeCOMPACCESS).Parse("[{{.c1}}]")),
  43. // TokenLIST - Special case (handled in code)
  44. // TokenMAP - Special case (handled in code)
  45. // TokenPARAMS - Special case (handled in code)
  46. NodeGUARD + "_1": template.Must(template.New(NodeGUARD).Parse("{{.c1}}")),
  47. // Condition operators
  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. // Separators
  55. NodeKVP + "_2": template.Must(template.New(NodeKVP).Parse("{{.c1}} : {{.c2}}")),
  56. NodePRESET + "_2": template.Must(template.New(NodePRESET).Parse("{{.c1}}={{.c2}}")),
  57. // Arithmetic operators
  58. NodePLUS + "_1": template.Must(template.New(NodePLUS).Parse("+{{.c1}}")),
  59. NodePLUS + "_2": template.Must(template.New(NodePLUS).Parse("{{.c1}} + {{.c2}}")),
  60. NodeMINUS + "_1": template.Must(template.New(NodeMINUS).Parse("-{{.c1}}")),
  61. NodeMINUS + "_2": template.Must(template.New(NodeMINUS).Parse("{{.c1}} - {{.c2}}")),
  62. NodeTIMES + "_2": template.Must(template.New(NodeTIMES).Parse("{{.c1}} * {{.c2}}")),
  63. NodeDIV + "_2": template.Must(template.New(NodeDIV).Parse("{{.c1}} / {{.c2}}")),
  64. NodeMODINT + "_2": template.Must(template.New(NodeMODINT).Parse("{{.c1}} % {{.c2}}")),
  65. NodeDIVINT + "_2": template.Must(template.New(NodeDIVINT).Parse("{{.c1}} // {{.c2}}")),
  66. // Assignment statement
  67. NodeASSIGN + "_2": template.Must(template.New(NodeASSIGN).Parse("{{.c1}} := {{.c2}}")),
  68. NodeLET + "_1": template.Must(template.New(NodeASSIGN).Parse("let {{.c1}}")),
  69. // Import statement
  70. NodeIMPORT + "_2": template.Must(template.New(NodeIMPORT).Parse("import {{.c1}} as {{.c2}}")),
  71. NodeAS + "_1": template.Must(template.New(NodeRETURN).Parse("as {{.c1}}")),
  72. // Sink definition
  73. // NodeSINK - Special case (handled in code)
  74. NodeKINDMATCH + "_1": template.Must(template.New(NodeKINDMATCH).Parse("kindmatch {{.c1}}")),
  75. NodeSCOPEMATCH + "_1": template.Must(template.New(NodeSCOPEMATCH).Parse("scopematch {{.c1}}")),
  76. NodeSTATEMATCH + "_1": template.Must(template.New(NodeSTATEMATCH).Parse("statematch {{.c1}}")),
  77. NodePRIORITY + "_1": template.Must(template.New(NodePRIORITY).Parse("priority {{.c1}}")),
  78. NodeSUPPRESSES + "_1": template.Must(template.New(NodeSUPPRESSES).Parse("suppresses {{.c1}}")),
  79. // Function definition
  80. NodeFUNC + "_2": template.Must(template.New(NodeFUNC).Parse("func {{.c1}} {\n{{.c2}}}")),
  81. NodeFUNC + "_3": template.Must(template.New(NodeFUNC).Parse("func {{.c1}}{{.c2}} {\n{{.c3}}}")),
  82. NodeRETURN: template.Must(template.New(NodeRETURN).Parse("return")),
  83. NodeRETURN + "_1": template.Must(template.New(NodeRETURN).Parse("return {{.c1}}")),
  84. // Boolean operators
  85. NodeOR + "_2": template.Must(template.New(NodeOR).Parse("{{.c1}} or {{.c2}}")),
  86. NodeAND + "_2": template.Must(template.New(NodeAND).Parse("{{.c1}} and {{.c2}}")),
  87. NodeNOT + "_1": template.Must(template.New(NodeNOT).Parse("not {{.c1}}")),
  88. // Condition operators
  89. NodeLIKE + "_2": template.Must(template.New(NodeLIKE).Parse("{{.c1}} like {{.c2}}")),
  90. NodeIN + "_2": template.Must(template.New(NodeIN).Parse("{{.c1}} in {{.c2}}")),
  91. NodeHASPREFIX + "_2": template.Must(template.New(NodeHASPREFIX).Parse("{{.c1}} hasprefix {{.c2}}")),
  92. NodeHASSUFFIX + "_2": template.Must(template.New(NodeHASSUFFIX).Parse("{{.c1}} hassuffix {{.c2}}")),
  93. NodeNOTIN + "_2": template.Must(template.New(NodeNOTIN).Parse("{{.c1}} notin {{.c2}}")),
  94. // Constant terminals
  95. NodeTRUE: template.Must(template.New(NodeTRUE).Parse("true")),
  96. NodeFALSE: template.Must(template.New(NodeFALSE).Parse("false")),
  97. NodeNULL: template.Must(template.New(NodeNULL).Parse("null")),
  98. // Conditional statements
  99. // TokenIF - Special case (handled in code)
  100. // TokenELIF - Special case (handled in code)
  101. // TokenELSE - Special case (handled in code)
  102. // Loop statement
  103. NodeLOOP + "_2": template.Must(template.New(NodeLOOP).Parse("for {{.c1}} {\n{{.c2}}}")),
  104. NodeBREAK: template.Must(template.New(NodeBREAK).Parse("break")),
  105. NodeCONTINUE: template.Must(template.New(NodeCONTINUE).Parse("continue")),
  106. // Try statement
  107. // TokenTRY - Special case (handled in code)
  108. // TokenEXCEPT - Special case (handled in code)
  109. NodeOTHERWISE + "_1": template.Must(template.New(NodeOTHERWISE).Parse(" otherwise {\n{{.c1}}}")),
  110. NodeFINALLY + "_1": template.Must(template.New(NodeFINALLY).Parse(" finally {\n{{.c1}}}")),
  111. // Mutex block
  112. NodeMUTEX + "_2": template.Must(template.New(NodeLOOP).Parse("mutex {{.c1}} {\n{{.c2}}}\n")),
  113. }
  114. bracketPrecedenceMap = map[string]bool{
  115. NodePLUS: true,
  116. NodeMINUS: true,
  117. NodeAND: true,
  118. NodeOR: true,
  119. }
  120. }
  121. /*
  122. PrettyPrint produces pretty printed code from a given AST.
  123. */
  124. func PrettyPrint(ast *ASTNode) (string, error) {
  125. var visit func(ast *ASTNode, path []*ASTNode) (string, error)
  126. visit = func(ast *ASTNode, path []*ASTNode) (string, error) {
  127. var buf bytes.Buffer
  128. if ast == nil {
  129. return "", fmt.Errorf("Nil pointer in AST")
  130. }
  131. numChildren := len(ast.Children)
  132. tempKey := ast.Name
  133. tempParam := make(map[string]string)
  134. // First pretty print children
  135. if numChildren > 0 {
  136. for i, child := range ast.Children {
  137. res, err := visit(child, append(path, child))
  138. if err != nil {
  139. return "", err
  140. }
  141. if _, ok := bracketPrecedenceMap[child.Name]; ok && ast.binding > child.binding {
  142. // Put the expression in brackets iff (if and only if) the binding would
  143. // normally order things differently
  144. res = fmt.Sprintf("(%v)", res)
  145. }
  146. tempParam[fmt.Sprint("c", i+1)] = res
  147. }
  148. tempKey += fmt.Sprint("_", len(tempParam))
  149. }
  150. if res, ok := ppSpecialDefs(ast, path, tempParam, &buf); ok {
  151. return res, nil
  152. } else if res, ok := ppSpecialBlocks(ast, path, tempParam, &buf); ok {
  153. return res, nil
  154. } else if res, ok := ppContainerBlocks(ast, path, tempParam, &buf); ok {
  155. return res, nil
  156. } else if res, ok := ppSpecialStatements(ast, path, tempParam, &buf); ok {
  157. return res, nil
  158. }
  159. if ast.Token != nil {
  160. // Adding node value to template parameters
  161. tempParam["val"] = ast.Token.Val
  162. tempParam["qval"] = strconv.Quote(ast.Token.Val)
  163. }
  164. // Retrieve the template
  165. temp, ok := prettyPrinterMap[tempKey]
  166. errorutil.AssertTrue(ok,
  167. fmt.Sprintf("Could not find template for %v (tempkey: %v)",
  168. ast.Name, tempKey))
  169. // Use the children as parameters for template
  170. errorutil.AssertOk(temp.Execute(&buf, tempParam))
  171. return ppPostProcessing(ast, path, buf.String()), nil
  172. }
  173. res, err := visit(ast, []*ASTNode{ast})
  174. return strings.TrimSpace(res), err
  175. }
  176. /*
  177. ppPostProcessing applies post processing rules.
  178. */
  179. func ppPostProcessing(ast *ASTNode, path []*ASTNode, ppString string) string {
  180. // Add meta data
  181. ret := ppMetaData(ast, path, ppString)
  182. // Apply indentation
  183. if len(path) > 1 {
  184. if stringutil.IndexOf(ast.Name, []string{
  185. NodeSTATEMENTS,
  186. NodeMAP,
  187. NodeLIST,
  188. NodeKINDMATCH,
  189. NodeSTATEMATCH,
  190. NodeSCOPEMATCH,
  191. NodePRIORITY,
  192. NodeSUPPRESSES,
  193. }) != -1 {
  194. parent := path[len(path)-2]
  195. indentSpaces := stringutil.GenerateRollingString(" ", IndentationLevel)
  196. ret = strings.ReplaceAll(ret, "\n", "\n"+indentSpaces)
  197. // Add initial indent only if we are inside a block statement
  198. if stringutil.IndexOf(parent.Name, []string{
  199. NodeRETURN,
  200. NodeIN,
  201. NodeASSIGN,
  202. NodePRESET,
  203. NodeKVP,
  204. NodeLIST,
  205. NodeFUNCCALL,
  206. NodeKINDMATCH,
  207. NodeSTATEMATCH,
  208. NodeSCOPEMATCH,
  209. NodePRIORITY,
  210. NodeSUPPRESSES,
  211. }) == -1 {
  212. ret = fmt.Sprintf("%v%v", indentSpaces, ret)
  213. }
  214. // Remove indentation from last line unless we have a special case
  215. if stringutil.IndexOf(parent.Name, []string{
  216. NodeSINK,
  217. }) == -1 || ast.Name == NodeSTATEMENTS {
  218. if idx := strings.LastIndex(ret, "\n"); idx != -1 {
  219. ret = ret[:idx+1] + ret[idx+IndentationLevel+1:]
  220. }
  221. }
  222. }
  223. }
  224. if ast.Token != nil {
  225. // Calculate number of extra newlines which should be prefixed the default
  226. // pretty printer assumes a single one which produces very compact code
  227. // the previous formatting might give a hint where to add extra newlines.
  228. // The pretty printer only ever adds a maximum of 1 additional line per
  229. // statement
  230. if ast.Token.PrefixNewlines-1 > 0 {
  231. ret = fmt.Sprintf("\n%v", ret)
  232. }
  233. }
  234. // Remove all trailing spaces
  235. newlineSplit := strings.Split(ret, "\n")
  236. for i, s := range newlineSplit {
  237. newlineSplit[i] = strings.TrimRightFunc(s, unicode.IsSpace)
  238. }
  239. return strings.Join(newlineSplit, "\n")
  240. }
  241. /*
  242. ppMetaData pretty prints comments.
  243. */
  244. func ppMetaData(ast *ASTNode, path []*ASTNode, ppString string) string {
  245. ret := ppString
  246. if len(ast.Meta) > 0 {
  247. for _, meta := range ast.Meta {
  248. metaValue := meta.Value()
  249. if meta.Type() == MetaDataPreComment {
  250. var buf bytes.Buffer
  251. scanner := bufio.NewScanner(strings.NewReader(metaValue))
  252. for scanner.Scan() {
  253. buf.WriteString(fmt.Sprintf(" %v\n", strings.TrimSpace(scanner.Text())))
  254. }
  255. if ast.Token.Lpos != 1 || strings.Index(metaValue, "\n") == -1 {
  256. // Remove the last newline if we are not on the root level
  257. // or we didn't have any newlines in the original comment
  258. buf.Truncate(buf.Len() - 1)
  259. }
  260. if strings.Index(buf.String(), "\n") == -1 {
  261. // If the pretty printed comment does not have any newlines
  262. // Add at least a space at the end
  263. buf.WriteString(" ")
  264. }
  265. ret = fmt.Sprintf("/*%v*/\n%v", buf.String(), ret)
  266. if ast.Token.Lline > 1 {
  267. ret = fmt.Sprintf("\n%v", ret)
  268. }
  269. } else if meta.Type() == MetaDataPostComment {
  270. metaValue = strings.TrimSpace(strings.ReplaceAll(metaValue, "\n", ""))
  271. ret = fmt.Sprintf("%v # %v", ret, metaValue)
  272. }
  273. }
  274. }
  275. return ret
  276. }
  277. /*
  278. ppSpecialDefs pretty prints special cases.
  279. */
  280. func ppSpecialDefs(ast *ASTNode, path []*ASTNode, tempParam map[string]string, buf *bytes.Buffer) (string, bool) {
  281. numChildren := len(ast.Children)
  282. if ast.Name == NodeFUNCCALL {
  283. for i := 0; i < numChildren; i++ {
  284. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  285. if i < numChildren-1 {
  286. buf.WriteString(", ")
  287. }
  288. }
  289. return ppPostProcessing(ast, path, buf.String()), true
  290. } else if ast.Name == NodeSINK {
  291. buf.WriteString("sink ")
  292. buf.WriteString(tempParam["c1"])
  293. buf.WriteString("\n")
  294. for i := 1; i < len(ast.Children)-1; i++ {
  295. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  296. buf.WriteString("\n")
  297. }
  298. buf.WriteString("{\n")
  299. buf.WriteString(tempParam[fmt.Sprint("c", len(ast.Children))])
  300. buf.WriteString("}\n")
  301. return ppPostProcessing(ast, path, buf.String()), true
  302. }
  303. return "", false
  304. }
  305. /*
  306. ppContainerBlocks pretty prints container structures.
  307. */
  308. func ppContainerBlocks(ast *ASTNode, path []*ASTNode, tempParam map[string]string, buf *bytes.Buffer) (string, bool) {
  309. numChildren := len(ast.Children)
  310. if ast.Name == NodeLIST {
  311. multilineThreshold := 4
  312. buf.WriteString("[")
  313. if numChildren > multilineThreshold {
  314. buf.WriteString("\n")
  315. }
  316. for i := 0; i < numChildren; i++ {
  317. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  318. if i < numChildren-1 {
  319. if numChildren > multilineThreshold {
  320. buf.WriteString(",")
  321. } else {
  322. buf.WriteString(", ")
  323. }
  324. }
  325. if numChildren > multilineThreshold {
  326. buf.WriteString("\n")
  327. }
  328. }
  329. buf.WriteString("]")
  330. return ppPostProcessing(ast, path, buf.String()), true
  331. } else if ast.Name == NodeMAP {
  332. multilineThreshold := 2
  333. buf.WriteString("{")
  334. if numChildren > multilineThreshold {
  335. buf.WriteString("\n")
  336. }
  337. for i := 0; i < numChildren; i++ {
  338. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  339. if i < numChildren-1 {
  340. if numChildren > multilineThreshold {
  341. buf.WriteString(",")
  342. } else {
  343. buf.WriteString(", ")
  344. }
  345. }
  346. if numChildren > multilineThreshold {
  347. buf.WriteString("\n")
  348. }
  349. }
  350. buf.WriteString("}")
  351. return ppPostProcessing(ast, path, buf.String()), true
  352. }
  353. return "", false
  354. }
  355. /*
  356. ppSpecialBlocks pretty prints special cases.
  357. */
  358. func ppSpecialBlocks(ast *ASTNode, path []*ASTNode, tempParam map[string]string, buf *bytes.Buffer) (string, bool) {
  359. numChildren := len(ast.Children)
  360. // Handle special cases - children in tempParam have been resolved
  361. if ast.Name == NodeSTATEMENTS {
  362. // For statements just concat all children
  363. for i := 0; i < numChildren; i++ {
  364. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  365. buf.WriteString("\n")
  366. }
  367. return ppPostProcessing(ast, path, buf.String()), true
  368. } else if ast.Name == NodeTRY {
  369. buf.WriteString("try {\n")
  370. buf.WriteString(tempParam[fmt.Sprint("c1")])
  371. buf.WriteString("}")
  372. for i := 1; i < len(ast.Children); i++ {
  373. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  374. }
  375. return ppPostProcessing(ast, path, buf.String()), true
  376. } else if ast.Name == NodeEXCEPT {
  377. buf.WriteString(" except ")
  378. for i := 0; i < len(ast.Children)-1; i++ {
  379. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  380. if ast.Children[i+1].Name != NodeAS && i < len(ast.Children)-2 {
  381. buf.WriteString(",")
  382. }
  383. buf.WriteString(" ")
  384. }
  385. buf.WriteString("{\n")
  386. buf.WriteString(tempParam[fmt.Sprint("c", len(ast.Children))])
  387. buf.WriteString("}")
  388. return ppPostProcessing(ast, path, buf.String()), true
  389. }
  390. return "", false
  391. }
  392. /*
  393. ppSpecialStatements pretty prints special cases.
  394. */
  395. func ppSpecialStatements(ast *ASTNode, path []*ASTNode, tempParam map[string]string, buf *bytes.Buffer) (string, bool) {
  396. numChildren := len(ast.Children)
  397. if ast.Name == NodeIDENTIFIER {
  398. buf.WriteString(ast.Token.Val)
  399. for i := 0; i < numChildren; i++ {
  400. if ast.Children[i].Name == NodeIDENTIFIER {
  401. buf.WriteString(".")
  402. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  403. } else if ast.Children[i].Name == NodeFUNCCALL {
  404. buf.WriteString("(")
  405. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  406. buf.WriteString(")")
  407. } else if ast.Children[i].Name == NodeCOMPACCESS {
  408. buf.WriteString(tempParam[fmt.Sprint("c", i+1)])
  409. }
  410. }
  411. return ppPostProcessing(ast, path, buf.String()), true
  412. } else if ast.Name == NodePARAMS {
  413. buf.WriteString("(")
  414. i := 1
  415. for ; i < numChildren; i++ {
  416. buf.WriteString(tempParam[fmt.Sprint("c", i)])
  417. buf.WriteString(", ")
  418. }
  419. buf.WriteString(tempParam[fmt.Sprint("c", i)])
  420. buf.WriteString(")")
  421. return ppPostProcessing(ast, path, buf.String()), true
  422. } else if ast.Name == NodeIF {
  423. writeGUARD := func(child int) {
  424. buf.WriteString(tempParam[fmt.Sprint("c", child)])
  425. buf.WriteString(" {\n")
  426. buf.WriteString(tempParam[fmt.Sprint("c", child+1)])
  427. buf.WriteString("}")
  428. }
  429. buf.WriteString("if ")
  430. writeGUARD(1)
  431. for i := 0; i < len(ast.Children); i += 2 {
  432. if i+2 == len(ast.Children) && ast.Children[i].Children[0].Name == NodeTRUE {
  433. buf.WriteString(" else {\n")
  434. buf.WriteString(tempParam[fmt.Sprint("c", i+2)])
  435. buf.WriteString("}")
  436. } else if i > 0 {
  437. buf.WriteString(" elif ")
  438. writeGUARD(i + 1)
  439. }
  440. }
  441. return ppPostProcessing(ast, path, buf.String()), true
  442. }
  443. return "", false
  444. }