filter_tmpl.nim 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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, ast, msgs, options,
  12. filters, lineinfos, pathutils
  13. import std/strutils
  14. type
  15. TParseState = enum
  16. psDirective, psTempl
  17. TTmplParser = object
  18. inp: PLLStream
  19. state: TParseState
  20. info: TLineInfo
  21. indent, emitPar: int
  22. x: string # the current input line
  23. outp: PLLStream # the output will be parsed by parser
  24. subsChar, nimDirective: char
  25. emit, conc, toStr: string
  26. curly, bracket, par: int
  27. pendingExprLine: bool
  28. config: ConfigRef
  29. const
  30. PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '.', '_'}
  31. proc newLine(p: var TTmplParser) =
  32. llStreamWrite(p.outp, repeat(')', p.emitPar))
  33. p.emitPar = 0
  34. if p.info.line > uint16(1): llStreamWrite(p.outp, "\n")
  35. if p.pendingExprLine:
  36. llStreamWrite(p.outp, spaces(2))
  37. p.pendingExprLine = false
  38. proc scanPar(p: var TTmplParser, d: int) =
  39. var i = d
  40. while i < p.x.len:
  41. case p.x[i]
  42. of '(': inc(p.par)
  43. of ')': dec(p.par)
  44. of '[': inc(p.bracket)
  45. of ']': dec(p.bracket)
  46. of '{': inc(p.curly)
  47. of '}': dec(p.curly)
  48. else: discard
  49. inc(i)
  50. proc withInExpr(p: TTmplParser): bool {.inline.} =
  51. result = p.par > 0 or p.bracket > 0 or p.curly > 0
  52. const
  53. LineContinuationOprs = {'+', '-', '*', '/', '\\', '<', '>', '^',
  54. '|', '%', '&', '$', '@', '~', ','}
  55. proc parseLine(p: var TTmplParser) =
  56. var j = 0
  57. let len = p.x.len
  58. while j < len and p.x[j] == ' ': inc(j)
  59. if len >= 2 and p.x[0] == p.nimDirective and p.x[1] == '?':
  60. newLine(p)
  61. elif j < len and p.x[j] == p.nimDirective:
  62. newLine(p)
  63. inc(j)
  64. while j < len and p.x[j] == ' ': inc(j)
  65. let d = j
  66. var keyw = ""
  67. while j < len and p.x[j] in PatternChars:
  68. keyw.add(p.x[j])
  69. inc(j)
  70. scanPar(p, j)
  71. p.pendingExprLine = withInExpr(p) or p.x.endsWith(LineContinuationOprs)
  72. case keyw
  73. of "end":
  74. if p.indent >= 2:
  75. dec(p.indent, 2)
  76. else:
  77. p.info.col = int16(j)
  78. localError(p.config, p.info, "'end' does not close a control flow construct")
  79. llStreamWrite(p.outp, spaces(p.indent))
  80. llStreamWrite(p.outp, "#end")
  81. of "if", "when", "try", "while", "for", "block", "case", "proc", "iterator",
  82. "converter", "macro", "template", "method", "func":
  83. llStreamWrite(p.outp, spaces(p.indent))
  84. llStreamWrite(p.outp, substr(p.x, d))
  85. inc(p.indent, 2)
  86. of "elif", "of", "else", "except", "finally":
  87. llStreamWrite(p.outp, spaces(p.indent - 2))
  88. llStreamWrite(p.outp, substr(p.x, d))
  89. of "let", "var", "const", "type":
  90. llStreamWrite(p.outp, spaces(p.indent))
  91. llStreamWrite(p.outp, substr(p.x, d))
  92. if not p.x.contains({':', '='}):
  93. # no inline element --> treat as block:
  94. inc(p.indent, 2)
  95. else:
  96. llStreamWrite(p.outp, spaces(p.indent))
  97. llStreamWrite(p.outp, substr(p.x, d))
  98. p.state = psDirective
  99. else:
  100. # data line
  101. # reset counters
  102. p.par = 0
  103. p.curly = 0
  104. p.bracket = 0
  105. j = 0
  106. case p.state
  107. of psTempl:
  108. # next line of string literal:
  109. llStreamWrite(p.outp, p.conc)
  110. llStreamWrite(p.outp, "\n")
  111. llStreamWrite(p.outp, spaces(p.indent + 2))
  112. llStreamWrite(p.outp, "\"")
  113. of psDirective:
  114. newLine(p)
  115. llStreamWrite(p.outp, spaces(p.indent))
  116. llStreamWrite(p.outp, p.emit)
  117. llStreamWrite(p.outp, "(\"")
  118. inc(p.emitPar)
  119. p.state = psTempl
  120. while j < len:
  121. case p.x[j]
  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 j < len:
  149. case p.x[j]
  150. of '{':
  151. inc(j)
  152. inc(curly)
  153. llStreamWrite(p.outp, '{')
  154. of '}':
  155. inc(j)
  156. if curly == 0: break
  157. if curly > 0: dec(curly)
  158. llStreamWrite(p.outp, '}')
  159. else:
  160. llStreamWrite(p.outp, p.x[j])
  161. inc(j)
  162. if curly > 0:
  163. localError(p.config, p.info, "expected closing '}'")
  164. break
  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 j < len and 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.config, p.info, "invalid expression")
  186. else:
  187. llStreamWrite(p.outp, p.x[j])
  188. inc(j)
  189. llStreamWrite(p.outp, "\\n\"")
  190. proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile,
  191. call: PNode): PLLStream =
  192. var p = TTmplParser(config: conf, info: newLineInfo(conf, filename, 0, 0),
  193. outp: llStreamOpen(""), inp: stdin,
  194. subsChar: charArg(conf, call, "subschar", 1, '$'),
  195. nimDirective: charArg(conf, call, "metachar", 2, '#'),
  196. emit: strArg(conf, call, "emit", 3, "result.add"),
  197. conc: strArg(conf, call, "conc", 4, " & "),
  198. toStr: strArg(conf, call, "tostring", 5, "$"),
  199. x: newStringOfCap(120)
  200. )
  201. # do not process the first line which contains the directive:
  202. if llStreamReadLine(p.inp, p.x):
  203. inc p.info.line
  204. while llStreamReadLine(p.inp, p.x):
  205. inc p.info.line
  206. parseLine(p)
  207. newLine(p)
  208. result = p.outp
  209. llStreamClose(p.inp)