rstast.nim 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. #
  2. #
  3. # Nim's Runtime Library
  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 an AST for the `reStructuredText`:idx: parser.
  10. import strutils, json
  11. type
  12. RstNodeKind* = enum ## the possible node kinds of an PRstNode
  13. rnInner, # an inner node or a root
  14. rnHeadline, # a headline
  15. rnOverline, # an over- and underlined headline
  16. rnTransition, # a transition (the ------------- <hr> thingie)
  17. rnParagraph, # a paragraph
  18. rnBulletList, # a bullet list
  19. rnBulletItem, # a bullet item
  20. rnEnumList, # an enumerated list
  21. rnEnumItem, # an enumerated item
  22. rnDefList, # a definition list
  23. rnDefItem, # an item of a definition list consisting of ...
  24. rnDefName, # ... a name part ...
  25. rnDefBody, # ... and a body part ...
  26. rnFieldList, # a field list
  27. rnField, # a field item
  28. rnFieldName, # consisting of a field name ...
  29. rnFieldBody, # ... and a field body
  30. rnOptionList, rnOptionListItem, rnOptionGroup, rnOption, rnOptionString,
  31. rnOptionArgument, rnDescription, rnLiteralBlock, rnQuotedLiteralBlock,
  32. rnLineBlock, # the | thingie
  33. rnLineBlockItem, # sons of the | thing
  34. rnBlockQuote, # text just indented
  35. rnTable, rnGridTable, rnTableRow, rnTableHeaderCell, rnTableDataCell,
  36. rnLabel, # used for footnotes and other things
  37. rnFootnote, # a footnote
  38. rnCitation, # similar to footnote
  39. rnStandaloneHyperlink, rnHyperlink, rnRef, rnDirective, # a directive
  40. rnDirArg, rnRaw, rnTitle, rnContents, rnImage, rnFigure, rnCodeBlock,
  41. rnRawHtml, rnRawLatex,
  42. rnContainer, # ``container`` directive
  43. rnIndex, # index directve:
  44. # .. index::
  45. # key
  46. # * `file#id <file#id>`_
  47. # * `file#id <file#id>'_
  48. rnSubstitutionDef, # a definition of a substitution
  49. rnGeneralRole, # Inline markup:
  50. rnSub, rnSup, rnIdx,
  51. rnEmphasis, # "*"
  52. rnStrongEmphasis, # "**"
  53. rnTripleEmphasis, # "***"
  54. rnInterpretedText, # "`"
  55. rnInlineLiteral, # "``"
  56. rnSubstitutionReferences, # "|"
  57. rnSmiley, # some smiley
  58. rnLeaf # a leaf; the node's text field contains the
  59. # leaf val
  60. PRstNode* = ref RstNode ## an RST node
  61. RstNodeSeq* = seq[PRstNode]
  62. RstNode* {.acyclic, final.} = object ## an RST node's description
  63. kind*: RstNodeKind ## the node's kind
  64. text*: string ## valid for leafs in the AST; and the title of
  65. ## the document or the section
  66. level*: int ## valid for some node kinds
  67. sons*: RstNodeSeq ## the node's sons
  68. proc len*(n: PRstNode): int =
  69. result = len(n.sons)
  70. proc newRstNode*(kind: RstNodeKind): PRstNode =
  71. new(result)
  72. result.sons = @[]
  73. result.kind = kind
  74. proc newRstNode*(kind: RstNodeKind, s: string): PRstNode =
  75. result = newRstNode(kind)
  76. result.text = s
  77. proc lastSon*(n: PRstNode): PRstNode =
  78. result = n.sons[len(n.sons)-1]
  79. proc add*(father, son: PRstNode) =
  80. add(father.sons, son)
  81. proc addIfNotNil*(father, son: PRstNode) =
  82. if son != nil: add(father, son)
  83. type
  84. RenderContext {.pure.} = object
  85. indent: int
  86. verbatim: int
  87. proc renderRstToRst(d: var RenderContext, n: PRstNode,
  88. result: var string) {.gcsafe.}
  89. proc renderRstSons(d: var RenderContext, n: PRstNode, result: var string) =
  90. for i in countup(0, len(n) - 1):
  91. renderRstToRst(d, n.sons[i], result)
  92. proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) =
  93. # this is needed for the index generation; it may also be useful for
  94. # debugging, but most code is already debugged...
  95. const
  96. lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+']
  97. if n == nil: return
  98. var ind = spaces(d.indent)
  99. case n.kind
  100. of rnInner:
  101. renderRstSons(d, n, result)
  102. of rnHeadline:
  103. result.add("\n")
  104. result.add(ind)
  105. let oldLen = result.len
  106. renderRstSons(d, n, result)
  107. let headlineLen = result.len - oldLen
  108. result.add("\n")
  109. result.add(ind)
  110. result.add repeat(lvlToChar[n.level], headlineLen)
  111. of rnOverline:
  112. result.add("\n")
  113. result.add(ind)
  114. var headline = ""
  115. renderRstSons(d, n, headline)
  116. let lvl = repeat(lvlToChar[n.level], headline.len - d.indent)
  117. result.add(lvl)
  118. result.add("\n")
  119. result.add(headline)
  120. result.add("\n")
  121. result.add(ind)
  122. result.add(lvl)
  123. of rnTransition:
  124. result.add("\n\n")
  125. result.add(ind)
  126. result.add repeat('-', 78-d.indent)
  127. result.add("\n\n")
  128. of rnParagraph:
  129. result.add("\n\n")
  130. result.add(ind)
  131. renderRstSons(d, n, result)
  132. of rnBulletItem:
  133. inc(d.indent, 2)
  134. var tmp = ""
  135. renderRstSons(d, n, tmp)
  136. if tmp.len > 0:
  137. result.add("\n")
  138. result.add(ind)
  139. result.add("* ")
  140. result.add(tmp)
  141. dec(d.indent, 2)
  142. of rnEnumItem:
  143. inc(d.indent, 4)
  144. var tmp = ""
  145. renderRstSons(d, n, tmp)
  146. if tmp.len > 0:
  147. result.add("\n")
  148. result.add(ind)
  149. result.add("(#) ")
  150. result.add(tmp)
  151. dec(d.indent, 4)
  152. of rnOptionList, rnFieldList, rnDefList, rnDefItem, rnLineBlock, rnFieldName,
  153. rnFieldBody, rnStandaloneHyperlink, rnBulletList, rnEnumList:
  154. renderRstSons(d, n, result)
  155. of rnDefName:
  156. result.add("\n\n")
  157. result.add(ind)
  158. renderRstSons(d, n, result)
  159. of rnDefBody:
  160. inc(d.indent, 2)
  161. if n.sons[0].kind != rnBulletList:
  162. result.add("\n")
  163. result.add(ind)
  164. result.add(" ")
  165. renderRstSons(d, n, result)
  166. dec(d.indent, 2)
  167. of rnField:
  168. var tmp = ""
  169. renderRstToRst(d, n.sons[0], tmp)
  170. var L = max(tmp.len + 3, 30)
  171. inc(d.indent, L)
  172. result.add "\n"
  173. result.add ind
  174. result.add ':'
  175. result.add tmp
  176. result.add ':'
  177. result.add spaces(L - tmp.len - 2)
  178. renderRstToRst(d, n.sons[1], result)
  179. dec(d.indent, L)
  180. of rnLineBlockItem:
  181. result.add("\n")
  182. result.add(ind)
  183. result.add("| ")
  184. renderRstSons(d, n, result)
  185. of rnBlockQuote:
  186. inc(d.indent, 2)
  187. renderRstSons(d, n, result)
  188. dec(d.indent, 2)
  189. of rnRef:
  190. result.add("`")
  191. renderRstSons(d, n, result)
  192. result.add("`_")
  193. of rnHyperlink:
  194. result.add('`')
  195. renderRstToRst(d, n.sons[0], result)
  196. result.add(" <")
  197. renderRstToRst(d, n.sons[1], result)
  198. result.add(">`_")
  199. of rnGeneralRole:
  200. result.add('`')
  201. renderRstToRst(d, n.sons[0],result)
  202. result.add("`:")
  203. renderRstToRst(d, n.sons[1],result)
  204. result.add(':')
  205. of rnSub:
  206. result.add('`')
  207. renderRstSons(d, n, result)
  208. result.add("`:sub:")
  209. of rnSup:
  210. result.add('`')
  211. renderRstSons(d, n, result)
  212. result.add("`:sup:")
  213. of rnIdx:
  214. result.add('`')
  215. renderRstSons(d, n, result)
  216. result.add("`:idx:")
  217. of rnEmphasis:
  218. result.add("*")
  219. renderRstSons(d, n, result)
  220. result.add("*")
  221. of rnStrongEmphasis:
  222. result.add("**")
  223. renderRstSons(d, n, result)
  224. result.add("**")
  225. of rnTripleEmphasis:
  226. result.add("***")
  227. renderRstSons(d, n, result)
  228. result.add("***")
  229. of rnInterpretedText:
  230. result.add('`')
  231. renderRstSons(d, n, result)
  232. result.add('`')
  233. of rnInlineLiteral:
  234. inc(d.verbatim)
  235. result.add("``")
  236. renderRstSons(d, n, result)
  237. result.add("``")
  238. dec(d.verbatim)
  239. of rnSmiley:
  240. result.add(n.text)
  241. of rnLeaf:
  242. if d.verbatim == 0 and n.text == "\\":
  243. result.add("\\\\") # XXX: escape more special characters!
  244. else:
  245. result.add(n.text)
  246. of rnIndex:
  247. result.add("\n\n")
  248. result.add(ind)
  249. result.add(".. index::\n")
  250. inc(d.indent, 3)
  251. if n.sons[2] != nil: renderRstSons(d, n.sons[2], result)
  252. dec(d.indent, 3)
  253. of rnContents:
  254. result.add("\n\n")
  255. result.add(ind)
  256. result.add(".. contents::")
  257. else:
  258. result.add("Error: cannot render: " & $n.kind)
  259. proc renderRstToRst*(n: PRstNode, result: var string) =
  260. ## renders `n` into its string representation and appends to `result`.
  261. var d: RenderContext
  262. renderRstToRst(d, n, result)
  263. proc renderRstToJsonNode(node: PRstNode): JsonNode =
  264. result =
  265. %[
  266. (key: "kind", val: %($node.kind)),
  267. (key: "level", val: %BiggestInt(node.level))
  268. ]
  269. if node.text.len > 0:
  270. result.add("text", %node.text)
  271. if len(node.sons) > 0:
  272. var accm = newSeq[JsonNode](len(node.sons))
  273. for i, son in node.sons:
  274. accm[i] = renderRstToJsonNode(son)
  275. result.add("sons", %accm)
  276. proc renderRstToJson*(node: PRstNode): string =
  277. ## Writes the given RST node as JSON that is in the form
  278. ## ::
  279. ## {
  280. ## "kind":string node.kind,
  281. ## "text":optional string node.text,
  282. ## "level":optional int node.level,
  283. ## "sons":optional node array
  284. ## }
  285. renderRstToJsonNode(node).pretty