filter_tmpl.nim 6.6 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, strutils, ast, msgs, options,
  12. filters, lineinfos, pathutils
  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 output will be parsed by parser
  23. subsChar, nimDirective: char
  24. emit, conc, toStr: string
  25. curly, bracket, par: int
  26. pendingExprLine: bool
  27. config: ConfigRef
  28. const
  29. PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
  30. proc newLine(p: var TTmplParser) =
  31. llStreamWrite(p.outp, repeat(')', p.emitPar))
  32. p.emitPar = 0
  33. if p.info.line > uint16(1): llStreamWrite(p.outp, "\n")
  34. if p.pendingExprLine:
  35. llStreamWrite(p.outp, spaces(2))
  36. p.pendingExprLine = false
  37. proc scanPar(p: var TTmplParser, d: int) =
  38. var i = d
  39. while i < p.x.len:
  40. case p.x[i]
  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. let len = p.x.len
  57. while j < len and p.x[j] == ' ': inc(j)
  58. if len >= 2 and p.x[0] == p.nimDirective and p.x[1] == '?':
  59. newLine(p)
  60. elif j < len and p.x[j] == p.nimDirective:
  61. newLine(p)
  62. inc(j)
  63. while j < len and p.x[j] == ' ': inc(j)
  64. let d = j
  65. var keyw = ""
  66. while j < len and p.x[j] in PatternChars:
  67. keyw.add(p.x[j])
  68. inc(j)
  69. scanPar(p, j)
  70. p.pendingExprLine = withInExpr(p) or p.x.endsWith(LineContinuationOprs)
  71. case keyw
  72. of "end":
  73. if p.indent >= 2:
  74. dec(p.indent, 2)
  75. else:
  76. p.info.col = int16(j)
  77. localError(p.config, p.info, "'end' does not close a control flow construct")
  78. llStreamWrite(p.outp, spaces(p.indent))
  79. llStreamWrite(p.outp, "#end")
  80. of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator",
  81. "converter", "macro", "template", "method", "func":
  82. llStreamWrite(p.outp, spaces(p.indent))
  83. llStreamWrite(p.outp, substr(p.x, d))
  84. inc(p.indent, 2)
  85. of "elif", "of", "else", "except", "finally":
  86. llStreamWrite(p.outp, spaces(p.indent - 2))
  87. llStreamWrite(p.outp, substr(p.x, d))
  88. of "let", "var", "const", "type":
  89. llStreamWrite(p.outp, spaces(p.indent))
  90. llStreamWrite(p.outp, substr(p.x, d))
  91. if not p.x.contains({':', '='}):
  92. # no inline element --> treat as block:
  93. inc(p.indent, 2)
  94. else:
  95. llStreamWrite(p.outp, spaces(p.indent))
  96. llStreamWrite(p.outp, substr(p.x, d))
  97. p.state = psDirective
  98. else:
  99. # data line
  100. # reset counters
  101. p.par = 0
  102. p.curly = 0
  103. p.bracket = 0
  104. j = 0
  105. case p.state
  106. of psTempl:
  107. # next line of string literal:
  108. llStreamWrite(p.outp, p.conc)
  109. llStreamWrite(p.outp, "\n")
  110. llStreamWrite(p.outp, spaces(p.indent + 2))
  111. llStreamWrite(p.outp, "\"")
  112. of psDirective:
  113. newLine(p)
  114. llStreamWrite(p.outp, spaces(p.indent))
  115. llStreamWrite(p.outp, p.emit)
  116. llStreamWrite(p.outp, "(\"")
  117. inc(p.emitPar)
  118. p.state = psTempl
  119. while j < len:
  120. case p.x[j]
  121. of '\x01'..'\x1F', '\x80'..'\xFF':
  122. llStreamWrite(p.outp, "\\x")
  123. llStreamWrite(p.outp, toHex(ord(p.x[j]), 2))
  124. inc(j)
  125. of '\\':
  126. llStreamWrite(p.outp, "\\\\")
  127. inc(j)
  128. of '\'':
  129. llStreamWrite(p.outp, "\\\'")
  130. inc(j)
  131. of '\"':
  132. llStreamWrite(p.outp, "\\\"")
  133. inc(j)
  134. else:
  135. if p.x[j] == p.subsChar:
  136. # parse Nim expression:
  137. inc(j)
  138. case p.x[j]
  139. of '{':
  140. p.info.col = int16(j)
  141. llStreamWrite(p.outp, '\"')
  142. llStreamWrite(p.outp, p.conc)
  143. llStreamWrite(p.outp, p.toStr)
  144. llStreamWrite(p.outp, '(')
  145. inc(j)
  146. var curly = 0
  147. while j < len:
  148. case p.x[j]
  149. of '{':
  150. inc(j)
  151. inc(curly)
  152. llStreamWrite(p.outp, '{')
  153. of '}':
  154. inc(j)
  155. if curly == 0: break
  156. if curly > 0: dec(curly)
  157. llStreamWrite(p.outp, '}')
  158. else:
  159. llStreamWrite(p.outp, p.x[j])
  160. inc(j)
  161. if curly > 0:
  162. localError(p.config, p.info, "expected closing '}'")
  163. break
  164. llStreamWrite(p.outp, ')')
  165. llStreamWrite(p.outp, p.conc)
  166. llStreamWrite(p.outp, '\"')
  167. of 'a'..'z', 'A'..'Z', '\x80'..'\xFF':
  168. llStreamWrite(p.outp, '\"')
  169. llStreamWrite(p.outp, p.conc)
  170. llStreamWrite(p.outp, p.toStr)
  171. llStreamWrite(p.outp, '(')
  172. while j < len and p.x[j] in PatternChars:
  173. llStreamWrite(p.outp, p.x[j])
  174. inc(j)
  175. llStreamWrite(p.outp, ')')
  176. llStreamWrite(p.outp, p.conc)
  177. llStreamWrite(p.outp, '\"')
  178. else:
  179. if p.x[j] == p.subsChar:
  180. llStreamWrite(p.outp, p.subsChar)
  181. inc(j)
  182. else:
  183. p.info.col = int16(j)
  184. localError(p.config, p.info, "invalid expression")
  185. else:
  186. llStreamWrite(p.outp, p.x[j])
  187. inc(j)
  188. llStreamWrite(p.outp, "\\n\"")
  189. proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile,
  190. call: PNode): PLLStream =
  191. var p = TTmplParser(config: conf, info: newLineInfo(conf, filename, 0, 0),
  192. outp: llStreamOpen(""), inp: stdin,
  193. subsChar: charArg(conf, call, "subschar", 1, '$'),
  194. nimDirective: charArg(conf, call, "metachar", 2, '#'),
  195. emit: strArg(conf, call, "emit", 3, "result.add"),
  196. conc: strArg(conf, call, "conc", 4, " & "),
  197. toStr: strArg(conf, call, "tostring", 5, "$"),
  198. x: newStringOfCap(120)
  199. )
  200. # do not process the first line which contains the directive:
  201. if llStreamReadLine(p.inp, p.x):
  202. inc p.info.line
  203. while llStreamReadLine(p.inp, p.x):
  204. inc p.info.line
  205. parseLine(p)
  206. newLine(p)
  207. result = p.outp
  208. llStreamClose(p.inp)