filter_tmpl.nim 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2012 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # This module implements Nim's standard template filter.
  10. import
  11. llstream, os, wordrecg, idents, strutils, ast, astalgo, msgs, options,
  12. renderer, filters
  13. type
  14. TParseState = enum
  15. psDirective, psTempl
  16. TTmplParser = object
  17. inp: PLLStream
  18. state: TParseState
  19. info: TLineInfo
  20. indent, emitPar: int
  21. x: string # the current input line
  22. outp: PLLStream # the ouput will be parsed by pnimsyn
  23. subsChar, nimDirective: char
  24. emit, conc, toStr: string
  25. curly, bracket, par: int
  26. pendingExprLine: bool
  27. const
  28. PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
  29. proc newLine(p: var TTmplParser) =
  30. llStreamWrite(p.outp, repeat(')', p.emitPar))
  31. p.emitPar = 0
  32. if p.info.line > int16(1): llStreamWrite(p.outp, "\n")
  33. if p.pendingExprLine:
  34. llStreamWrite(p.outp, spaces(2))
  35. p.pendingExprLine = false
  36. proc scanPar(p: var TTmplParser, d: int) =
  37. var i = d
  38. while true:
  39. case p.x[i]
  40. of '\0': break
  41. of '(': inc(p.par)
  42. of ')': dec(p.par)
  43. of '[': inc(p.bracket)
  44. of ']': dec(p.bracket)
  45. of '{': inc(p.curly)
  46. of '}': dec(p.curly)
  47. else: discard
  48. inc(i)
  49. proc withInExpr(p: TTmplParser): bool {.inline.} =
  50. result = p.par > 0 or p.bracket > 0 or p.curly > 0
  51. const
  52. LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '^',
  53. '|', '%', '&', '$', '@', '~', ','}
  54. proc parseLine(p: var TTmplParser) =
  55. var j = 0
  56. while p.x[j] == ' ': inc(j)
  57. if p.x[0] == p.nimDirective and p.x[1] == '?':
  58. newLine(p)
  59. elif p.x[j] == p.nimDirective:
  60. newLine(p)
  61. inc(j)
  62. while p.x[j] == ' ': inc(j)
  63. let d = j
  64. var keyw = ""
  65. while p.x[j] in PatternChars:
  66. add(keyw, p.x[j])
  67. inc(j)
  68. scanPar(p, j)
  69. p.pendingExprLine = withInExpr(p) or p.x.endsWith(LineContinuationOprs)
  70. case keyw
  71. of "end":
  72. if p.indent >= 2:
  73. dec(p.indent, 2)
  74. else:
  75. p.info.col = int16(j)
  76. localError(p.info, errXNotAllowedHere, "end")
  77. llStreamWrite(p.outp, spaces(p.indent))
  78. llStreamWrite(p.outp, "#end")
  79. of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator",
  80. "converter", "macro", "template", "method", "func":
  81. llStreamWrite(p.outp, spaces(p.indent))
  82. llStreamWrite(p.outp, substr(p.x, d))
  83. inc(p.indent, 2)
  84. of "elif", "of", "else", "except", "finally":
  85. llStreamWrite(p.outp, spaces(p.indent - 2))
  86. llStreamWrite(p.outp, substr(p.x, d))
  87. of "let", "var", "const", "type":
  88. llStreamWrite(p.outp, spaces(p.indent))
  89. llStreamWrite(p.outp, substr(p.x, d))
  90. if not p.x.contains({':', '='}):
  91. # no inline element --> treat as block:
  92. inc(p.indent, 2)
  93. else:
  94. llStreamWrite(p.outp, spaces(p.indent))
  95. llStreamWrite(p.outp, substr(p.x, d))
  96. p.state = psDirective
  97. else:
  98. # data line
  99. # reset counters
  100. p.par = 0
  101. p.curly = 0
  102. p.bracket = 0
  103. j = 0
  104. case p.state
  105. of psTempl:
  106. # next line of string literal:
  107. llStreamWrite(p.outp, p.conc)
  108. llStreamWrite(p.outp, "\n")
  109. llStreamWrite(p.outp, spaces(p.indent + 2))
  110. llStreamWrite(p.outp, "\"")
  111. of psDirective:
  112. newLine(p)
  113. llStreamWrite(p.outp, spaces(p.indent))
  114. llStreamWrite(p.outp, p.emit)
  115. llStreamWrite(p.outp, "(\"")
  116. inc(p.emitPar)
  117. p.state = psTempl
  118. while true:
  119. case p.x[j]
  120. of '\0':
  121. break
  122. of '\x01'..'\x1F', '\x80'..'\xFF':
  123. llStreamWrite(p.outp, "\\x")
  124. llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
  125. inc(j)
  126. of '\\':
  127. llStreamWrite(p.outp, "\\\\")
  128. inc(j)
  129. of '\'':
  130. llStreamWrite(p.outp, "\\\'")
  131. inc(j)
  132. of '\"':
  133. llStreamWrite(p.outp, "\\\"")
  134. inc(j)
  135. else:
  136. if p.x[j] == p.subsChar:
  137. # parse Nim expression:
  138. inc(j)
  139. case p.x[j]
  140. of '{':
  141. p.info.col = int16(j)
  142. llStreamWrite(p.outp, '\"')
  143. llStreamWrite(p.outp, p.conc)
  144. llStreamWrite(p.outp, p.toStr)
  145. llStreamWrite(p.outp, '(')
  146. inc(j)
  147. var curly = 0
  148. while true:
  149. case p.x[j]
  150. of '\0':
  151. localError(p.info, errXExpected, "}")
  152. break
  153. of '{':
  154. inc(j)
  155. inc(curly)
  156. llStreamWrite(p.outp, '{')
  157. of '}':
  158. inc(j)
  159. if curly == 0: break
  160. if curly > 0: dec(curly)
  161. llStreamWrite(p.outp, '}')
  162. else:
  163. llStreamWrite(p.outp, p.x[j])
  164. inc(j)
  165. llStreamWrite(p.outp, ')')
  166. llStreamWrite(p.outp, p.conc)
  167. llStreamWrite(p.outp, '\"')
  168. of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
  169. llStreamWrite(p.outp, '\"')
  170. llStreamWrite(p.outp, p.conc)
  171. llStreamWrite(p.outp, p.toStr)
  172. llStreamWrite(p.outp, '(')
  173. while p.x[j] in PatternChars:
  174. llStreamWrite(p.outp, p.x[j])
  175. inc(j)
  176. llStreamWrite(p.outp, ')')
  177. llStreamWrite(p.outp, p.conc)
  178. llStreamWrite(p.outp, '\"')
  179. else:
  180. if p.x[j] == p.subsChar:
  181. llStreamWrite(p.outp, p.subsChar)
  182. inc(j)
  183. else:
  184. p.info.col = int16(j)
  185. localError(p.info, errInvalidExpression, "$")
  186. else:
  187. llStreamWrite(p.outp, p.x[j])
  188. inc(j)
  189. llStreamWrite(p.outp, "\\n\"")
  190. proc filterTmpl*(stdin: PLLStream, filename: string, call: PNode): PLLStream =
  191. var p: TTmplParser
  192. p.info = newLineInfo(filename, 0, 0)
  193. p.outp = llStreamOpen("")
  194. p.inp = stdin
  195. p.subsChar = charArg(call, "subschar", 1, '$')
  196. p.nimDirective = charArg(call, "metachar", 2, '#')
  197. p.emit = strArg(call, "emit", 3, "result.add")
  198. p.conc = strArg(call, "conc", 4, " & ")
  199. p.toStr = strArg(call, "tostring", 5, "$")
  200. p.x = newStringOfCap(120)
  201. # do not process the first line which contains the directive:
  202. if llStreamReadLine(p.inp, p.x):
  203. p.info.line = p.info.line + int16(1)
  204. while llStreamReadLine(p.inp, p.x):
  205. p.info.line = p.info.line + int16(1)
  206. parseLine(p)
  207. newLine(p)
  208. result = p.outp
  209. llStreamClose(p.inp)