rst.nim 57 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830
  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 a `reStructuredText`:idx: parser. A large
  10. ## subset is implemented. Some features of the `markdown`:idx: wiki syntax are
  11. ## also supported.
  12. ##
  13. ## **Note:** Import ``packages/docutils/rst`` to use this module
  14. import
  15. os, strutils, rstast
  16. type
  17. RstParseOption* = enum ## options for the RST parser
  18. roSkipPounds, ## skip ``#`` at line beginning (documentation
  19. ## embedded in Nim comments)
  20. roSupportSmilies, ## make the RST parser support smilies like ``:)``
  21. roSupportRawDirective, ## support the ``raw`` directive (don't support
  22. ## it for sandboxing)
  23. roSupportMarkdown ## support additional features of markdown
  24. RstParseOptions* = set[RstParseOption]
  25. MsgClass* = enum
  26. mcHint = "Hint",
  27. mcWarning = "Warning",
  28. mcError = "Error"
  29. MsgKind* = enum ## the possible messages
  30. meCannotOpenFile,
  31. meExpected,
  32. meGridTableNotImplemented,
  33. meNewSectionExpected,
  34. meGeneralParseError,
  35. meInvalidDirective,
  36. mwRedefinitionOfLabel,
  37. mwUnknownSubstitution,
  38. mwUnsupportedLanguage,
  39. mwUnsupportedField
  40. MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind,
  41. arg: string) {.closure, gcsafe.} ## what to do in case of an error
  42. FindFileHandler* = proc (filename: string): string {.closure, gcsafe.}
  43. const
  44. messages: array[MsgKind, string] = [
  45. meCannotOpenFile: "cannot open '$1'",
  46. meExpected: "'$1' expected",
  47. meGridTableNotImplemented: "grid table is not implemented",
  48. meNewSectionExpected: "new section expected",
  49. meGeneralParseError: "general parse error",
  50. meInvalidDirective: "invalid directive: '$1'",
  51. mwRedefinitionOfLabel: "redefinition of label '$1'",
  52. mwUnknownSubstitution: "unknown substitution '$1'",
  53. mwUnsupportedLanguage: "language '$1' not supported",
  54. mwUnsupportedField: "field '$1' not supported"
  55. ]
  56. proc rstnodeToRefname*(n: PRstNode): string
  57. proc addNodes*(n: PRstNode): string
  58. proc getFieldValue*(n: PRstNode, fieldname: string): string
  59. proc getArgument*(n: PRstNode): string
  60. # ----------------------------- scanner part --------------------------------
  61. const
  62. SymChars: set[char] = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF'}
  63. SmileyStartChars: set[char] = {':', ';', '8'}
  64. Smilies = {
  65. ":D": "icon_e_biggrin",
  66. ":-D": "icon_e_biggrin",
  67. ":)": "icon_e_smile",
  68. ":-)": "icon_e_smile",
  69. ";)": "icon_e_wink",
  70. ";-)": "icon_e_wink",
  71. ":(": "icon_e_sad",
  72. ":-(": "icon_e_sad",
  73. ":o": "icon_e_surprised",
  74. ":-o": "icon_e_surprised",
  75. ":shock:": "icon_eek",
  76. ":?": "icon_e_confused",
  77. ":-?": "icon_e_confused",
  78. ":-/": "icon_e_confused",
  79. "8-)": "icon_cool",
  80. ":lol:": "icon_lol",
  81. ":x": "icon_mad",
  82. ":-x": "icon_mad",
  83. ":P": "icon_razz",
  84. ":-P": "icon_razz",
  85. ":oops:": "icon_redface",
  86. ":cry:": "icon_cry",
  87. ":evil:": "icon_evil",
  88. ":twisted:": "icon_twisted",
  89. ":roll:": "icon_rolleyes",
  90. ":!:": "icon_exclaim",
  91. ":?:": "icon_question",
  92. ":idea:": "icon_idea",
  93. ":arrow:": "icon_arrow",
  94. ":|": "icon_neutral",
  95. ":-|": "icon_neutral",
  96. ":mrgreen:": "icon_mrgreen",
  97. ":geek:": "icon_e_geek",
  98. ":ugeek:": "icon_e_ugeek"
  99. }
  100. type
  101. TokType = enum
  102. tkEof, tkIndent, tkWhite, tkWord, tkAdornment, tkPunct, tkOther
  103. Token = object # a RST token
  104. kind*: TokType # the type of the token
  105. ival*: int # the indentation or parsed integer value
  106. symbol*: string # the parsed symbol as string
  107. line*, col*: int # line and column of the token
  108. TokenSeq = seq[Token]
  109. Lexer = object of RootObj
  110. buf*: cstring
  111. bufpos*: int
  112. line*, col*, baseIndent*: int
  113. skipPounds*: bool
  114. proc getThing(L: var Lexer, tok: var Token, s: set[char]) =
  115. tok.kind = tkWord
  116. tok.line = L.line
  117. tok.col = L.col
  118. var pos = L.bufpos
  119. while true:
  120. add(tok.symbol, L.buf[pos])
  121. inc(pos)
  122. if L.buf[pos] notin s: break
  123. inc(L.col, pos - L.bufpos)
  124. L.bufpos = pos
  125. proc getAdornment(L: var Lexer, tok: var Token) =
  126. tok.kind = tkAdornment
  127. tok.line = L.line
  128. tok.col = L.col
  129. var pos = L.bufpos
  130. var c = L.buf[pos]
  131. while true:
  132. add(tok.symbol, L.buf[pos])
  133. inc(pos)
  134. if L.buf[pos] != c: break
  135. inc(L.col, pos - L.bufpos)
  136. L.bufpos = pos
  137. proc getBracket(L: var Lexer, tok: var Token) =
  138. tok.kind = tkPunct
  139. tok.line = L.line
  140. tok.col = L.col
  141. add(tok.symbol, L.buf[L.bufpos])
  142. inc L.col
  143. inc L.bufpos
  144. proc getIndentAux(L: var Lexer, start: int): int =
  145. var pos = start
  146. # skip the newline (but include it in the token!)
  147. if L.buf[pos] == '\x0D':
  148. if L.buf[pos + 1] == '\x0A': inc(pos, 2)
  149. else: inc(pos)
  150. elif L.buf[pos] == '\x0A':
  151. inc(pos)
  152. if L.skipPounds:
  153. if L.buf[pos] == '#': inc(pos)
  154. if L.buf[pos] == '#': inc(pos)
  155. while true:
  156. case L.buf[pos]
  157. of ' ', '\x0B', '\x0C':
  158. inc(pos)
  159. inc(result)
  160. of '\x09':
  161. inc(pos)
  162. result = result - (result mod 8) + 8
  163. else:
  164. break # EndOfFile also leaves the loop
  165. if L.buf[pos] == '\0':
  166. result = 0
  167. elif (L.buf[pos] == '\x0A') or (L.buf[pos] == '\x0D'):
  168. # look at the next line for proper indentation:
  169. result = getIndentAux(L, pos)
  170. L.bufpos = pos # no need to set back buf
  171. proc getIndent(L: var Lexer, tok: var Token) =
  172. tok.col = 0
  173. tok.kind = tkIndent # skip the newline (but include it in the token!)
  174. tok.ival = getIndentAux(L, L.bufpos)
  175. inc L.line
  176. tok.line = L.line
  177. L.col = tok.ival
  178. tok.ival = max(tok.ival - L.baseIndent, 0)
  179. tok.symbol = "\n" & spaces(tok.ival)
  180. proc rawGetTok(L: var Lexer, tok: var Token) =
  181. tok.symbol = ""
  182. tok.ival = 0
  183. var c = L.buf[L.bufpos]
  184. case c
  185. of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '0'..'9':
  186. getThing(L, tok, SymChars)
  187. of ' ', '\x09', '\x0B', '\x0C':
  188. getThing(L, tok, {' ', '\x09'})
  189. tok.kind = tkWhite
  190. if L.buf[L.bufpos] in {'\x0D', '\x0A'}:
  191. rawGetTok(L, tok) # ignore spaces before \n
  192. of '\x0D', '\x0A':
  193. getIndent(L, tok)
  194. of '!', '\"', '#', '$', '%', '&', '\'', '*', '+', ',', '-', '.',
  195. '/', ':', ';', '<', '=', '>', '?', '@', '\\', '^', '_', '`',
  196. '|', '~':
  197. getAdornment(L, tok)
  198. if len(tok.symbol) <= 3: tok.kind = tkPunct
  199. of '(', ')', '[', ']', '{', '}':
  200. getBracket(L, tok)
  201. else:
  202. tok.line = L.line
  203. tok.col = L.col
  204. if c == '\0':
  205. tok.kind = tkEof
  206. else:
  207. tok.kind = tkOther
  208. add(tok.symbol, c)
  209. inc(L.bufpos)
  210. inc(L.col)
  211. tok.col = max(tok.col - L.baseIndent, 0)
  212. proc getTokens(buffer: string, skipPounds: bool, tokens: var TokenSeq): int =
  213. var L: Lexer
  214. var length = len(tokens)
  215. L.buf = cstring(buffer)
  216. L.line = 0 # skip UTF-8 BOM
  217. if (L.buf[0] == '\xEF') and (L.buf[1] == '\xBB') and (L.buf[2] == '\xBF'):
  218. inc(L.bufpos, 3)
  219. L.skipPounds = skipPounds
  220. if skipPounds:
  221. if L.buf[L.bufpos] == '#':
  222. inc(L.bufpos)
  223. inc(result)
  224. if L.buf[L.bufpos] == '#':
  225. inc(L.bufpos)
  226. inc(result)
  227. L.baseIndent = 0
  228. while L.buf[L.bufpos] == ' ':
  229. inc(L.bufpos)
  230. inc(L.baseIndent)
  231. inc(result)
  232. while true:
  233. inc(length)
  234. setLen(tokens, length)
  235. rawGetTok(L, tokens[length - 1])
  236. if tokens[length - 1].kind == tkEof: break
  237. if tokens[0].kind == tkWhite:
  238. # BUGFIX
  239. tokens[0].ival = len(tokens[0].symbol)
  240. tokens[0].kind = tkIndent
  241. type
  242. LevelMap = array[char, int]
  243. Substitution = object
  244. key*: string
  245. value*: PRstNode
  246. SharedState = object
  247. options: RstParseOptions # parsing options
  248. uLevel, oLevel: int # counters for the section levels
  249. subs: seq[Substitution] # substitutions
  250. refs: seq[Substitution] # references
  251. underlineToLevel: LevelMap # Saves for each possible title adornment
  252. # character its level in the
  253. # current document.
  254. # This is for single underline adornments.
  255. overlineToLevel: LevelMap # Saves for each possible title adornment
  256. # character its level in the current
  257. # document.
  258. # This is for over-underline adornments.
  259. msgHandler: MsgHandler # How to handle errors.
  260. findFile: FindFileHandler # How to find files.
  261. PSharedState = ref SharedState
  262. RstParser = object of RootObj
  263. idx*: int
  264. tok*: TokenSeq
  265. s*: PSharedState
  266. indentStack*: seq[int]
  267. filename*: string
  268. line*, col*: int
  269. hasToc*: bool
  270. EParseError* = object of ValueError
  271. proc whichMsgClass*(k: MsgKind): MsgClass =
  272. ## returns which message class `k` belongs to.
  273. case ($k)[1]
  274. of 'e', 'E': result = mcError
  275. of 'w', 'W': result = mcWarning
  276. of 'h', 'H': result = mcHint
  277. else: assert false, "msgkind does not fit naming scheme"
  278. proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind,
  279. arg: string) =
  280. let mc = msgkind.whichMsgClass
  281. let a = messages[msgkind] % arg
  282. let message = "$1($2, $3) $4: $5" % [filename, $line, $col, $mc, a]
  283. if mc == mcError: raise newException(EParseError, message)
  284. else: writeLine(stdout, message)
  285. proc defaultFindFile*(filename: string): string =
  286. if fileExists(filename): result = filename
  287. else: result = ""
  288. proc newSharedState(options: RstParseOptions,
  289. findFile: FindFileHandler,
  290. msgHandler: MsgHandler): PSharedState =
  291. new(result)
  292. result.subs = @[]
  293. result.refs = @[]
  294. result.options = options
  295. result.msgHandler = if not isNil(msgHandler): msgHandler else: defaultMsgHandler
  296. result.findFile = if not isNil(findFile): findFile else: defaultFindFile
  297. proc findRelativeFile(p: RstParser; filename: string): string =
  298. result = p.filename.splitFile.dir / filename
  299. if not fileExists(result):
  300. result = p.s.findFile(filename)
  301. proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) =
  302. p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line,
  303. p.col + p.tok[p.idx].col, msgKind, arg)
  304. proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string, line, col: int) =
  305. p.s.msgHandler(p.filename, p.line + line,
  306. p.col + col, msgKind, arg)
  307. proc rstMessage(p: RstParser, msgKind: MsgKind) =
  308. p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line,
  309. p.col + p.tok[p.idx].col, msgKind,
  310. p.tok[p.idx].symbol)
  311. proc currInd(p: RstParser): int =
  312. result = p.indentStack[high(p.indentStack)]
  313. proc pushInd(p: var RstParser, ind: int) =
  314. add(p.indentStack, ind)
  315. proc popInd(p: var RstParser) =
  316. if len(p.indentStack) > 1: setLen(p.indentStack, len(p.indentStack) - 1)
  317. proc initParser(p: var RstParser, sharedState: PSharedState) =
  318. p.indentStack = @[0]
  319. p.tok = @[]
  320. p.idx = 0
  321. p.filename = ""
  322. p.hasToc = false
  323. p.col = 0
  324. p.line = 1
  325. p.s = sharedState
  326. proc addNodesAux(n: PRstNode, result: var string) =
  327. if n.kind == rnLeaf:
  328. add(result, n.text)
  329. else:
  330. for i in countup(0, len(n) - 1): addNodesAux(n.sons[i], result)
  331. proc addNodes(n: PRstNode): string =
  332. result = ""
  333. addNodesAux(n, result)
  334. proc rstnodeToRefnameAux(n: PRstNode, r: var string, b: var bool) =
  335. template special(s) =
  336. if b:
  337. add(r, '-')
  338. b = false
  339. add(r, s)
  340. if n == nil: return
  341. if n.kind == rnLeaf:
  342. for i in countup(0, len(n.text) - 1):
  343. case n.text[i]
  344. of '0'..'9':
  345. if b:
  346. add(r, '-')
  347. b = false
  348. if len(r) == 0: add(r, 'Z')
  349. add(r, n.text[i])
  350. of 'a'..'z', '\128'..'\255':
  351. if b:
  352. add(r, '-')
  353. b = false
  354. add(r, n.text[i])
  355. of 'A'..'Z':
  356. if b:
  357. add(r, '-')
  358. b = false
  359. add(r, chr(ord(n.text[i]) - ord('A') + ord('a')))
  360. of '$': special "dollar"
  361. of '%': special "percent"
  362. of '&': special "amp"
  363. of '^': special "roof"
  364. of '!': special "emark"
  365. of '?': special "qmark"
  366. of '*': special "star"
  367. of '+': special "plus"
  368. of '-': special "minus"
  369. of '/': special "slash"
  370. of '\\': special "backslash"
  371. of '=': special "eq"
  372. of '<': special "lt"
  373. of '>': special "gt"
  374. of '~': special "tilde"
  375. of ':': special "colon"
  376. of '.': special "dot"
  377. of '@': special "at"
  378. of '|': special "bar"
  379. else:
  380. if len(r) > 0: b = true
  381. else:
  382. for i in countup(0, len(n) - 1): rstnodeToRefnameAux(n.sons[i], r, b)
  383. proc rstnodeToRefname(n: PRstNode): string =
  384. result = ""
  385. var b = false
  386. rstnodeToRefnameAux(n, result, b)
  387. proc findSub(p: var RstParser, n: PRstNode): int =
  388. var key = addNodes(n)
  389. # the spec says: if no exact match, try one without case distinction:
  390. for i in countup(0, high(p.s.subs)):
  391. if key == p.s.subs[i].key:
  392. return i
  393. for i in countup(0, high(p.s.subs)):
  394. if cmpIgnoreStyle(key, p.s.subs[i].key) == 0:
  395. return i
  396. result = -1
  397. proc setSub(p: var RstParser, key: string, value: PRstNode) =
  398. var length = len(p.s.subs)
  399. for i in countup(0, length - 1):
  400. if key == p.s.subs[i].key:
  401. p.s.subs[i].value = value
  402. return
  403. setLen(p.s.subs, length + 1)
  404. p.s.subs[length].key = key
  405. p.s.subs[length].value = value
  406. proc setRef(p: var RstParser, key: string, value: PRstNode) =
  407. var length = len(p.s.refs)
  408. for i in countup(0, length - 1):
  409. if key == p.s.refs[i].key:
  410. if p.s.refs[i].value.addNodes != value.addNodes:
  411. rstMessage(p, mwRedefinitionOfLabel, key)
  412. p.s.refs[i].value = value
  413. return
  414. setLen(p.s.refs, length + 1)
  415. p.s.refs[length].key = key
  416. p.s.refs[length].value = value
  417. proc findRef(p: var RstParser, key: string): PRstNode =
  418. for i in countup(0, high(p.s.refs)):
  419. if key == p.s.refs[i].key:
  420. return p.s.refs[i].value
  421. proc newLeaf(p: var RstParser): PRstNode =
  422. result = newRstNode(rnLeaf, p.tok[p.idx].symbol)
  423. proc getReferenceName(p: var RstParser, endStr: string): PRstNode =
  424. var res = newRstNode(rnInner)
  425. while true:
  426. case p.tok[p.idx].kind
  427. of tkWord, tkOther, tkWhite:
  428. add(res, newLeaf(p))
  429. of tkPunct:
  430. if p.tok[p.idx].symbol == endStr:
  431. inc(p.idx)
  432. break
  433. else:
  434. add(res, newLeaf(p))
  435. else:
  436. rstMessage(p, meExpected, endStr)
  437. break
  438. inc(p.idx)
  439. result = res
  440. proc untilEol(p: var RstParser): PRstNode =
  441. result = newRstNode(rnInner)
  442. while not (p.tok[p.idx].kind in {tkIndent, tkEof}):
  443. add(result, newLeaf(p))
  444. inc(p.idx)
  445. proc expect(p: var RstParser, tok: string) =
  446. if p.tok[p.idx].symbol == tok: inc(p.idx)
  447. else: rstMessage(p, meExpected, tok)
  448. proc isInlineMarkupEnd(p: RstParser, markup: string): bool =
  449. result = p.tok[p.idx].symbol == markup
  450. if not result:
  451. return # Rule 3:
  452. result = not (p.tok[p.idx - 1].kind in {tkIndent, tkWhite})
  453. if not result:
  454. return # Rule 4:
  455. result = (p.tok[p.idx + 1].kind in {tkIndent, tkWhite, tkEof}) or
  456. (markup in ["``", "`"] and p.tok[p.idx + 1].kind in {tkIndent, tkWhite, tkWord, tkEof}) or
  457. (p.tok[p.idx + 1].symbol[0] in
  458. {'\'', '\"', ')', ']', '}', '>', '-', '/', '\\', ':', '.', ',', ';', '!',
  459. '?', '_'})
  460. if not result:
  461. return # Rule 7:
  462. if p.idx > 0:
  463. if (markup != "``") and (p.tok[p.idx - 1].symbol == "\\"):
  464. result = false
  465. proc isInlineMarkupStart(p: RstParser, markup: string): bool =
  466. var d: char
  467. result = p.tok[p.idx].symbol == markup
  468. if not result:
  469. return # Rule 1:
  470. result = (p.idx == 0) or (p.tok[p.idx - 1].kind in {tkIndent, tkWhite}) or
  471. (markup in ["``", "`"] and p.tok[p.idx - 1].kind in {tkIndent, tkWhite, tkWord}) or
  472. (p.tok[p.idx - 1].symbol[0] in
  473. {'\'', '\"', '(', '[', '{', '<', '-', '/', ':', '_'})
  474. if not result:
  475. return # Rule 2:
  476. result = not (p.tok[p.idx + 1].kind in {tkIndent, tkWhite, tkEof})
  477. if not result:
  478. return # Rule 5 & 7:
  479. if p.idx > 0:
  480. if p.tok[p.idx - 1].symbol == "\\":
  481. result = false
  482. else:
  483. var c = p.tok[p.idx - 1].symbol[0]
  484. case c
  485. of '\'', '\"': d = c
  486. of '(': d = ')'
  487. of '[': d = ']'
  488. of '{': d = '}'
  489. of '<': d = '>'
  490. else: d = '\0'
  491. if d != '\0': result = p.tok[p.idx + 1].symbol[0] != d
  492. proc match(p: RstParser, start: int, expr: string): bool =
  493. # regular expressions are:
  494. # special char exact match
  495. # 'w' tkWord
  496. # ' ' tkWhite
  497. # 'a' tkAdornment
  498. # 'i' tkIndent
  499. # 'p' tkPunct
  500. # 'T' always true
  501. # 'E' whitespace, indent or eof
  502. # 'e' tkWord or '#' (for enumeration lists)
  503. var i = 0
  504. var j = start
  505. var last = len(expr) - 1
  506. while i <= last:
  507. case expr[i]
  508. of 'w': result = p.tok[j].kind == tkWord
  509. of ' ': result = p.tok[j].kind == tkWhite
  510. of 'i': result = p.tok[j].kind == tkIndent
  511. of 'p': result = p.tok[j].kind == tkPunct
  512. of 'a': result = p.tok[j].kind == tkAdornment
  513. of 'o': result = p.tok[j].kind == tkOther
  514. of 'T': result = true
  515. of 'E': result = p.tok[j].kind in {tkEof, tkWhite, tkIndent}
  516. of 'e':
  517. result = (p.tok[j].kind == tkWord) or (p.tok[j].symbol == "#")
  518. if result:
  519. case p.tok[j].symbol[0]
  520. of 'a'..'z', 'A'..'Z', '#': result = len(p.tok[j].symbol) == 1
  521. of '0'..'9': result = allCharsInSet(p.tok[j].symbol, {'0'..'9'})
  522. else: result = false
  523. else:
  524. var c = expr[i]
  525. var length = 0
  526. while (i <= last) and (expr[i] == c):
  527. inc(i)
  528. inc(length)
  529. dec(i)
  530. result = (p.tok[j].kind in {tkPunct, tkAdornment}) and
  531. (len(p.tok[j].symbol) == length) and (p.tok[j].symbol[0] == c)
  532. if not result: return
  533. inc(j)
  534. inc(i)
  535. result = true
  536. proc fixupEmbeddedRef(n, a, b: PRstNode) =
  537. var sep = - 1
  538. for i in countdown(len(n) - 2, 0):
  539. if n.sons[i].text == "<":
  540. sep = i
  541. break
  542. var incr = if (sep > 0) and (n.sons[sep - 1].text[0] == ' '): 2 else: 1
  543. for i in countup(0, sep - incr): add(a, n.sons[i])
  544. for i in countup(sep + 1, len(n) - 2): add(b, n.sons[i])
  545. proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode =
  546. result = n
  547. if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"):
  548. inc(p.idx)
  549. if p.tok[p.idx-2].symbol == "`" and p.tok[p.idx-3].symbol == ">":
  550. var a = newRstNode(rnInner)
  551. var b = newRstNode(rnInner)
  552. fixupEmbeddedRef(n, a, b)
  553. if len(a) == 0:
  554. result = newRstNode(rnStandaloneHyperlink)
  555. add(result, b)
  556. else:
  557. result = newRstNode(rnHyperlink)
  558. add(result, a)
  559. add(result, b)
  560. setRef(p, rstnodeToRefname(a), b)
  561. elif n.kind == rnInterpretedText:
  562. n.kind = rnRef
  563. else:
  564. result = newRstNode(rnRef)
  565. add(result, n)
  566. elif match(p, p.idx, ":w:"):
  567. # a role:
  568. if p.tok[p.idx + 1].symbol == "idx":
  569. n.kind = rnIdx
  570. elif p.tok[p.idx + 1].symbol == "literal":
  571. n.kind = rnInlineLiteral
  572. elif p.tok[p.idx + 1].symbol == "strong":
  573. n.kind = rnStrongEmphasis
  574. elif p.tok[p.idx + 1].symbol == "emphasis":
  575. n.kind = rnEmphasis
  576. elif (p.tok[p.idx + 1].symbol == "sub") or
  577. (p.tok[p.idx + 1].symbol == "subscript"):
  578. n.kind = rnSub
  579. elif (p.tok[p.idx + 1].symbol == "sup") or
  580. (p.tok[p.idx + 1].symbol == "supscript"):
  581. n.kind = rnSup
  582. else:
  583. result = newRstNode(rnGeneralRole)
  584. n.kind = rnInner
  585. add(result, n)
  586. add(result, newRstNode(rnLeaf, p.tok[p.idx + 1].symbol))
  587. inc(p.idx, 3)
  588. proc matchVerbatim(p: RstParser, start: int, expr: string): int =
  589. result = start
  590. var j = 0
  591. while j < expr.len and result < p.tok.len and
  592. continuesWith(expr, p.tok[result].symbol, j):
  593. inc j, p.tok[result].symbol.len
  594. inc result
  595. if j < expr.len: result = 0
  596. proc parseSmiley(p: var RstParser): PRstNode =
  597. if p.tok[p.idx].symbol[0] notin SmileyStartChars: return
  598. for key, val in items(Smilies):
  599. let m = matchVerbatim(p, p.idx, key)
  600. if m > 0:
  601. p.idx = m
  602. result = newRstNode(rnSmiley)
  603. result.text = val
  604. return
  605. when false:
  606. const
  607. urlChars = {'A'..'Z', 'a'..'z', '0'..'9', ':', '#', '@', '%', '/', ';',
  608. '$', '(', ')', '~', '_', '?', '+', '-', '=', '\\', '.', '&',
  609. '\128'..'\255'}
  610. proc isUrl(p: RstParser, i: int): bool =
  611. result = (p.tok[i+1].symbol == ":") and (p.tok[i+2].symbol == "//") and
  612. (p.tok[i+3].kind == tkWord) and
  613. (p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"])
  614. proc parseUrl(p: var RstParser, father: PRstNode) =
  615. #if p.tok[p.idx].symbol[strStart] == '<':
  616. if isUrl(p, p.idx):
  617. var n = newRstNode(rnStandaloneHyperlink)
  618. while true:
  619. case p.tok[p.idx].kind
  620. of tkWord, tkAdornment, tkOther: discard
  621. of tkPunct:
  622. if p.tok[p.idx+1].kind notin {tkWord, tkAdornment, tkOther, tkPunct}:
  623. break
  624. else: break
  625. add(n, newLeaf(p))
  626. inc(p.idx)
  627. add(father, n)
  628. else:
  629. var n = newLeaf(p)
  630. inc(p.idx)
  631. if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
  632. add(father, n)
  633. proc parseBackslash(p: var RstParser, father: PRstNode) =
  634. assert(p.tok[p.idx].kind == tkPunct)
  635. if p.tok[p.idx].symbol == "\\\\":
  636. add(father, newRstNode(rnLeaf, "\\"))
  637. inc(p.idx)
  638. elif p.tok[p.idx].symbol == "\\":
  639. # XXX: Unicode?
  640. inc(p.idx)
  641. if p.tok[p.idx].kind != tkWhite: add(father, newLeaf(p))
  642. if p.tok[p.idx].kind != tkEof: inc(p.idx)
  643. else:
  644. add(father, newLeaf(p))
  645. inc(p.idx)
  646. when false:
  647. proc parseAdhoc(p: var RstParser, father: PRstNode, verbatim: bool) =
  648. if not verbatim and isURL(p, p.idx):
  649. var n = newRstNode(rnStandaloneHyperlink)
  650. while true:
  651. case p.tok[p.idx].kind
  652. of tkWord, tkAdornment, tkOther: nil
  653. of tkPunct:
  654. if p.tok[p.idx+1].kind notin {tkWord, tkAdornment, tkOther, tkPunct}:
  655. break
  656. else: break
  657. add(n, newLeaf(p))
  658. inc(p.idx)
  659. add(father, n)
  660. elif not verbatim and roSupportSmilies in p.sharedState.options:
  661. let n = parseSmiley(p)
  662. if s != nil:
  663. add(father, n)
  664. else:
  665. var n = newLeaf(p)
  666. inc(p.idx)
  667. if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n)
  668. add(father, n)
  669. proc parseUntil(p: var RstParser, father: PRstNode, postfix: string,
  670. interpretBackslash: bool) =
  671. let
  672. line = p.tok[p.idx].line
  673. col = p.tok[p.idx].col
  674. inc p.idx
  675. while true:
  676. case p.tok[p.idx].kind
  677. of tkPunct:
  678. if isInlineMarkupEnd(p, postfix):
  679. inc(p.idx)
  680. break
  681. elif interpretBackslash:
  682. parseBackslash(p, father)
  683. else:
  684. add(father, newLeaf(p))
  685. inc(p.idx)
  686. of tkAdornment, tkWord, tkOther:
  687. add(father, newLeaf(p))
  688. inc(p.idx)
  689. of tkIndent:
  690. add(father, newRstNode(rnLeaf, " "))
  691. inc(p.idx)
  692. if p.tok[p.idx].kind == tkIndent:
  693. rstMessage(p, meExpected, postfix, line, col)
  694. break
  695. of tkWhite:
  696. add(father, newRstNode(rnLeaf, " "))
  697. inc(p.idx)
  698. else: rstMessage(p, meExpected, postfix, line, col)
  699. proc parseMarkdownCodeblock(p: var RstParser): PRstNode =
  700. var args = newRstNode(rnDirArg)
  701. if p.tok[p.idx].kind == tkWord:
  702. add(args, newLeaf(p))
  703. inc(p.idx)
  704. else:
  705. args = nil
  706. var n = newRstNode(rnLeaf, "")
  707. while true:
  708. case p.tok[p.idx].kind
  709. of tkEof:
  710. rstMessage(p, meExpected, "```")
  711. break
  712. of tkPunct:
  713. if p.tok[p.idx].symbol == "```":
  714. inc(p.idx)
  715. break
  716. else:
  717. add(n.text, p.tok[p.idx].symbol)
  718. inc(p.idx)
  719. else:
  720. add(n.text, p.tok[p.idx].symbol)
  721. inc(p.idx)
  722. var lb = newRstNode(rnLiteralBlock)
  723. add(lb, n)
  724. result = newRstNode(rnCodeBlock)
  725. add(result, args)
  726. add(result, PRstNode(nil))
  727. add(result, lb)
  728. proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool =
  729. result = true
  730. var desc, link = ""
  731. var i = p.idx
  732. template parse(endToken, dest) =
  733. inc i # skip begin token
  734. while true:
  735. if p.tok[i].kind in {tkEof, tkIndent}: return false
  736. if p.tok[i].symbol == endToken: break
  737. dest.add p.tok[i].symbol
  738. inc i
  739. inc i # skip end token
  740. parse("]", desc)
  741. if p.tok[i].symbol != "(": return false
  742. parse(")", link)
  743. let child = newRstNode(rnHyperlink)
  744. child.add desc
  745. child.add link
  746. # only commit if we detected no syntax error:
  747. father.add child
  748. p.idx = i
  749. result = true
  750. proc parseInline(p: var RstParser, father: PRstNode) =
  751. case p.tok[p.idx].kind
  752. of tkPunct:
  753. if isInlineMarkupStart(p, "***"):
  754. var n = newRstNode(rnTripleEmphasis)
  755. parseUntil(p, n, "***", true)
  756. add(father, n)
  757. elif isInlineMarkupStart(p, "**"):
  758. var n = newRstNode(rnStrongEmphasis)
  759. parseUntil(p, n, "**", true)
  760. add(father, n)
  761. elif isInlineMarkupStart(p, "*"):
  762. var n = newRstNode(rnEmphasis)
  763. parseUntil(p, n, "*", true)
  764. add(father, n)
  765. elif roSupportMarkdown in p.s.options and p.tok[p.idx].symbol == "```":
  766. inc(p.idx)
  767. add(father, parseMarkdownCodeblock(p))
  768. elif isInlineMarkupStart(p, "``"):
  769. var n = newRstNode(rnInlineLiteral)
  770. parseUntil(p, n, "``", false)
  771. add(father, n)
  772. elif isInlineMarkupStart(p, "`"):
  773. var n = newRstNode(rnInterpretedText)
  774. parseUntil(p, n, "`", true)
  775. n = parsePostfix(p, n)
  776. add(father, n)
  777. elif isInlineMarkupStart(p, "|"):
  778. var n = newRstNode(rnSubstitutionReferences)
  779. parseUntil(p, n, "|", false)
  780. add(father, n)
  781. elif roSupportMarkdown in p.s.options and
  782. p.tok[p.idx].symbol == "[" and p.tok[p.idx+1].symbol != "[" and
  783. parseMarkdownLink(p, father):
  784. discard "parseMarkdownLink already processed it"
  785. else:
  786. if roSupportSmilies in p.s.options:
  787. let n = parseSmiley(p)
  788. if n != nil:
  789. add(father, n)
  790. return
  791. parseBackslash(p, father)
  792. of tkWord:
  793. if roSupportSmilies in p.s.options:
  794. let n = parseSmiley(p)
  795. if n != nil:
  796. add(father, n)
  797. return
  798. parseUrl(p, father)
  799. of tkAdornment, tkOther, tkWhite:
  800. if roSupportSmilies in p.s.options:
  801. let n = parseSmiley(p)
  802. if n != nil:
  803. add(father, n)
  804. return
  805. add(father, newLeaf(p))
  806. inc(p.idx)
  807. else: discard
  808. proc getDirective(p: var RstParser): string =
  809. if p.tok[p.idx].kind == tkWhite and p.tok[p.idx+1].kind == tkWord:
  810. var j = p.idx
  811. inc(p.idx)
  812. result = p.tok[p.idx].symbol
  813. inc(p.idx)
  814. while p.tok[p.idx].kind in {tkWord, tkPunct, tkAdornment, tkOther}:
  815. if p.tok[p.idx].symbol == "::": break
  816. add(result, p.tok[p.idx].symbol)
  817. inc(p.idx)
  818. if p.tok[p.idx].kind == tkWhite: inc(p.idx)
  819. if p.tok[p.idx].symbol == "::":
  820. inc(p.idx)
  821. if (p.tok[p.idx].kind == tkWhite): inc(p.idx)
  822. else:
  823. p.idx = j # set back
  824. result = "" # error
  825. else:
  826. result = ""
  827. proc parseComment(p: var RstParser): PRstNode =
  828. case p.tok[p.idx].kind
  829. of tkIndent, tkEof:
  830. if p.tok[p.idx].kind != tkEof and p.tok[p.idx + 1].kind == tkIndent:
  831. inc(p.idx) # empty comment
  832. else:
  833. var indent = p.tok[p.idx].ival
  834. while true:
  835. case p.tok[p.idx].kind
  836. of tkEof:
  837. break
  838. of tkIndent:
  839. if (p.tok[p.idx].ival < indent): break
  840. else:
  841. discard
  842. inc(p.idx)
  843. else:
  844. while p.tok[p.idx].kind notin {tkIndent, tkEof}: inc(p.idx)
  845. result = nil
  846. type
  847. DirKind = enum # must be ordered alphabetically!
  848. dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents,
  849. dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle
  850. const
  851. DirIds: array[0..12, string] = ["", "author", "authors", "code",
  852. "code-block", "container", "contents", "figure", "image", "include",
  853. "index", "raw", "title"]
  854. proc getDirKind(s: string): DirKind =
  855. let i = find(DirIds, s)
  856. if i >= 0: result = DirKind(i)
  857. else: result = dkNone
  858. proc parseLine(p: var RstParser, father: PRstNode) =
  859. while true:
  860. case p.tok[p.idx].kind
  861. of tkWhite, tkWord, tkOther, tkPunct: parseInline(p, father)
  862. else: break
  863. proc parseUntilNewline(p: var RstParser, father: PRstNode) =
  864. while true:
  865. case p.tok[p.idx].kind
  866. of tkWhite, tkWord, tkAdornment, tkOther, tkPunct: parseInline(p, father)
  867. of tkEof, tkIndent: break
  868. proc parseSection(p: var RstParser, result: PRstNode) {.gcsafe.}
  869. proc parseField(p: var RstParser): PRstNode =
  870. ## Returns a parsed rnField node.
  871. ##
  872. ## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody.
  873. result = newRstNode(rnField)
  874. var col = p.tok[p.idx].col
  875. var fieldname = newRstNode(rnFieldName)
  876. parseUntil(p, fieldname, ":", false)
  877. var fieldbody = newRstNode(rnFieldBody)
  878. if p.tok[p.idx].kind != tkIndent: parseLine(p, fieldbody)
  879. if p.tok[p.idx].kind == tkIndent:
  880. var indent = p.tok[p.idx].ival
  881. if indent > col:
  882. pushInd(p, indent)
  883. parseSection(p, fieldbody)
  884. popInd(p)
  885. add(result, fieldname)
  886. add(result, fieldbody)
  887. proc parseFields(p: var RstParser): PRstNode =
  888. ## Parses fields for a section or directive block.
  889. ##
  890. ## This proc may return nil if the parsing doesn't find anything of value,
  891. ## otherwise it will return a node of rnFieldList type with children.
  892. result = nil
  893. var atStart = p.idx == 0 and p.tok[0].symbol == ":"
  894. if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx + 1].symbol == ":") or
  895. atStart:
  896. var col = if atStart: p.tok[p.idx].col else: p.tok[p.idx].ival
  897. result = newRstNode(rnFieldList)
  898. if not atStart: inc(p.idx)
  899. while true:
  900. add(result, parseField(p))
  901. if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and
  902. (p.tok[p.idx + 1].symbol == ":"):
  903. inc(p.idx)
  904. else:
  905. break
  906. proc getFieldValue*(n: PRstNode): string =
  907. ## Returns the value of a specific ``rnField`` node.
  908. ##
  909. ## This proc will assert if the node is not of the expected type. The empty
  910. ## string will be returned as a minimum. Any value in the rst will be
  911. ## stripped form leading/trailing whitespace.
  912. assert n.kind == rnField
  913. assert n.len == 2
  914. assert n.sons[0].kind == rnFieldName
  915. assert n.sons[1].kind == rnFieldBody
  916. result = addNodes(n.sons[1]).strip
  917. proc getFieldValue(n: PRstNode, fieldname: string): string =
  918. result = ""
  919. if n.sons[1] == nil: return
  920. if (n.sons[1].kind != rnFieldList):
  921. #InternalError("getFieldValue (2): " & $n.sons[1].kind)
  922. # We don't like internal errors here anymore as that would break the forum!
  923. return
  924. for i in countup(0, len(n.sons[1]) - 1):
  925. var f = n.sons[1].sons[i]
  926. if cmpIgnoreStyle(addNodes(f.sons[0]), fieldname) == 0:
  927. result = addNodes(f.sons[1])
  928. if result == "": result = "\x01\x01" # indicates that the field exists
  929. return
  930. proc getArgument(n: PRstNode): string =
  931. if n.sons[0] == nil: result = ""
  932. else: result = addNodes(n.sons[0])
  933. proc parseDotDot(p: var RstParser): PRstNode {.gcsafe.}
  934. proc parseLiteralBlock(p: var RstParser): PRstNode =
  935. result = newRstNode(rnLiteralBlock)
  936. var n = newRstNode(rnLeaf, "")
  937. if p.tok[p.idx].kind == tkIndent:
  938. var indent = p.tok[p.idx].ival
  939. inc(p.idx)
  940. while true:
  941. case p.tok[p.idx].kind
  942. of tkEof:
  943. break
  944. of tkIndent:
  945. if (p.tok[p.idx].ival < indent):
  946. break
  947. else:
  948. add(n.text, "\n")
  949. add(n.text, spaces(p.tok[p.idx].ival - indent))
  950. inc(p.idx)
  951. else:
  952. add(n.text, p.tok[p.idx].symbol)
  953. inc(p.idx)
  954. else:
  955. while not (p.tok[p.idx].kind in {tkIndent, tkEof}):
  956. add(n.text, p.tok[p.idx].symbol)
  957. inc(p.idx)
  958. add(result, n)
  959. proc getLevel(map: var LevelMap, lvl: var int, c: char): int =
  960. if map[c] == 0:
  961. inc(lvl)
  962. map[c] = lvl
  963. result = map[c]
  964. proc tokenAfterNewline(p: RstParser): int =
  965. result = p.idx
  966. while true:
  967. case p.tok[result].kind
  968. of tkEof:
  969. break
  970. of tkIndent:
  971. inc(result)
  972. break
  973. else: inc(result)
  974. proc isLineBlock(p: RstParser): bool =
  975. var j = tokenAfterNewline(p)
  976. result = (p.tok[p.idx].col == p.tok[j].col) and (p.tok[j].symbol == "|") or
  977. (p.tok[j].col > p.tok[p.idx].col)
  978. proc predNL(p: RstParser): bool =
  979. result = true
  980. if p.idx > 0:
  981. result = p.tok[p.idx-1].kind == tkIndent and
  982. p.tok[p.idx-1].ival == currInd(p)
  983. proc isDefList(p: RstParser): bool =
  984. var j = tokenAfterNewline(p)
  985. result = (p.tok[p.idx].col < p.tok[j].col) and
  986. (p.tok[j].kind in {tkWord, tkOther, tkPunct}) and
  987. (p.tok[j - 2].symbol != "::")
  988. proc isOptionList(p: RstParser): bool =
  989. result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or
  990. match(p, p.idx, "/w") or match(p, p.idx, "//w")
  991. proc isMarkdownHeadlinePattern(s: string): bool =
  992. if s.len >= 1 and s.len <= 6:
  993. for c in s:
  994. if c != '#': return false
  995. result = true
  996. proc isMarkdownHeadline(p: RstParser): bool =
  997. if roSupportMarkdown in p.s.options:
  998. if isMarkdownHeadlinePattern(p.tok[p.idx].symbol) and p.tok[p.idx+1].kind == tkWhite:
  999. if p.tok[p.idx+2].kind in {tkWord, tkOther, tkPunct}:
  1000. result = true
  1001. proc whichSection(p: RstParser): RstNodeKind =
  1002. case p.tok[p.idx].kind
  1003. of tkAdornment:
  1004. if match(p, p.idx + 1, "ii"): result = rnTransition
  1005. elif match(p, p.idx + 1, " a"): result = rnTable
  1006. elif match(p, p.idx + 1, "i"): result = rnOverline
  1007. elif isMarkdownHeadline(p):
  1008. result = rnHeadline
  1009. else:
  1010. result = rnLeaf
  1011. of tkPunct:
  1012. if isMarkdownHeadline(p):
  1013. result = rnHeadline
  1014. elif p.tok[p.idx].symbol == "```":
  1015. result = rnCodeBlock
  1016. elif match(p, tokenAfterNewline(p), "ai"):
  1017. result = rnHeadline
  1018. elif p.tok[p.idx].symbol == "::":
  1019. result = rnLiteralBlock
  1020. elif predNL(p) and
  1021. ((p.tok[p.idx].symbol == "+") or (p.tok[p.idx].symbol == "*") or
  1022. (p.tok[p.idx].symbol == "-")) and (p.tok[p.idx + 1].kind == tkWhite):
  1023. result = rnBulletList
  1024. elif (p.tok[p.idx].symbol == "|") and isLineBlock(p):
  1025. result = rnLineBlock
  1026. elif (p.tok[p.idx].symbol == "..") and predNL(p):
  1027. result = rnDirective
  1028. elif match(p, p.idx, ":w:") and predNL(p):
  1029. # (p.tok[p.idx].symbol == ":")
  1030. result = rnFieldList
  1031. elif match(p, p.idx, "(e) ") or match(p, p.idx, "e. "):
  1032. result = rnEnumList
  1033. elif match(p, p.idx, "+a+"):
  1034. result = rnGridTable
  1035. rstMessage(p, meGridTableNotImplemented)
  1036. elif isDefList(p):
  1037. result = rnDefList
  1038. elif isOptionList(p):
  1039. result = rnOptionList
  1040. else:
  1041. result = rnParagraph
  1042. of tkWord, tkOther, tkWhite:
  1043. if match(p, tokenAfterNewline(p), "ai"): result = rnHeadline
  1044. elif match(p, p.idx, "e) ") or match(p, p.idx, "e. "): result = rnEnumList
  1045. elif isDefList(p): result = rnDefList
  1046. else: result = rnParagraph
  1047. else: result = rnLeaf
  1048. proc parseLineBlock(p: var RstParser): PRstNode =
  1049. result = nil
  1050. if p.tok[p.idx + 1].kind == tkWhite:
  1051. var col = p.tok[p.idx].col
  1052. result = newRstNode(rnLineBlock)
  1053. pushInd(p, p.tok[p.idx + 2].col)
  1054. inc(p.idx, 2)
  1055. while true:
  1056. var item = newRstNode(rnLineBlockItem)
  1057. parseSection(p, item)
  1058. add(result, item)
  1059. if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and
  1060. (p.tok[p.idx + 1].symbol == "|") and
  1061. (p.tok[p.idx + 2].kind == tkWhite):
  1062. inc(p.idx, 3)
  1063. else:
  1064. break
  1065. popInd(p)
  1066. proc parseParagraph(p: var RstParser, result: PRstNode) =
  1067. while true:
  1068. case p.tok[p.idx].kind
  1069. of tkIndent:
  1070. if p.tok[p.idx + 1].kind == tkIndent:
  1071. inc(p.idx)
  1072. break
  1073. elif (p.tok[p.idx].ival == currInd(p)):
  1074. inc(p.idx)
  1075. case whichSection(p)
  1076. of rnParagraph, rnLeaf, rnHeadline, rnOverline, rnDirective:
  1077. add(result, newRstNode(rnLeaf, " "))
  1078. of rnLineBlock:
  1079. addIfNotNil(result, parseLineBlock(p))
  1080. else: break
  1081. else:
  1082. break
  1083. of tkPunct:
  1084. if (p.tok[p.idx].symbol == "::") and
  1085. (p.tok[p.idx + 1].kind == tkIndent) and
  1086. (currInd(p) < p.tok[p.idx + 1].ival):
  1087. add(result, newRstNode(rnLeaf, ":"))
  1088. inc(p.idx) # skip '::'
  1089. add(result, parseLiteralBlock(p))
  1090. break
  1091. else:
  1092. parseInline(p, result)
  1093. of tkWhite, tkWord, tkAdornment, tkOther:
  1094. parseInline(p, result)
  1095. else: break
  1096. proc parseHeadline(p: var RstParser): PRstNode =
  1097. result = newRstNode(rnHeadline)
  1098. if isMarkdownHeadline(p):
  1099. result.level = p.tok[p.idx].symbol.len
  1100. assert(p.tok[p.idx+1].kind == tkWhite)
  1101. inc p.idx, 2
  1102. parseUntilNewline(p, result)
  1103. else:
  1104. parseUntilNewline(p, result)
  1105. assert(p.tok[p.idx].kind == tkIndent)
  1106. assert(p.tok[p.idx + 1].kind == tkAdornment)
  1107. var c = p.tok[p.idx + 1].symbol[0]
  1108. inc(p.idx, 2)
  1109. result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c)
  1110. type
  1111. IntSeq = seq[int]
  1112. proc tokEnd(p: RstParser): int =
  1113. result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1
  1114. proc getColumns(p: var RstParser, cols: var IntSeq) =
  1115. var L = 0
  1116. while true:
  1117. inc(L)
  1118. setLen(cols, L)
  1119. cols[L - 1] = tokEnd(p)
  1120. assert(p.tok[p.idx].kind == tkAdornment)
  1121. inc(p.idx)
  1122. if p.tok[p.idx].kind != tkWhite: break
  1123. inc(p.idx)
  1124. if p.tok[p.idx].kind != tkAdornment: break
  1125. if p.tok[p.idx].kind == tkIndent: inc(p.idx)
  1126. # last column has no limit:
  1127. cols[L - 1] = 32000
  1128. proc parseDoc(p: var RstParser): PRstNode {.gcsafe.}
  1129. proc parseSimpleTable(p: var RstParser): PRstNode =
  1130. var
  1131. cols: IntSeq
  1132. row: seq[string]
  1133. i, last, line: int
  1134. c: char
  1135. q: RstParser
  1136. a, b: PRstNode
  1137. result = newRstNode(rnTable)
  1138. cols = @[]
  1139. row = @[]
  1140. a = nil
  1141. c = p.tok[p.idx].symbol[0]
  1142. while true:
  1143. if p.tok[p.idx].kind == tkAdornment:
  1144. last = tokenAfterNewline(p)
  1145. if p.tok[last].kind in {tkEof, tkIndent}:
  1146. # skip last adornment line:
  1147. p.idx = last
  1148. break
  1149. getColumns(p, cols)
  1150. setLen(row, len(cols))
  1151. if a != nil:
  1152. for j in 0..len(a)-1: a.sons[j].kind = rnTableHeaderCell
  1153. if p.tok[p.idx].kind == tkEof: break
  1154. for j in countup(0, high(row)): row[j] = ""
  1155. # the following while loop iterates over the lines a single cell may span:
  1156. line = p.tok[p.idx].line
  1157. while true:
  1158. i = 0
  1159. while not (p.tok[p.idx].kind in {tkIndent, tkEof}):
  1160. if (tokEnd(p) <= cols[i]):
  1161. add(row[i], p.tok[p.idx].symbol)
  1162. inc(p.idx)
  1163. else:
  1164. if p.tok[p.idx].kind == tkWhite: inc(p.idx)
  1165. inc(i)
  1166. if p.tok[p.idx].kind == tkIndent: inc(p.idx)
  1167. if tokEnd(p) <= cols[0]: break
  1168. if p.tok[p.idx].kind in {tkEof, tkAdornment}: break
  1169. for j in countup(1, high(row)): add(row[j], '\x0A')
  1170. a = newRstNode(rnTableRow)
  1171. for j in countup(0, high(row)):
  1172. initParser(q, p.s)
  1173. q.col = cols[j]
  1174. q.line = line - 1
  1175. q.filename = p.filename
  1176. q.col += getTokens(row[j], false, q.tok)
  1177. b = newRstNode(rnTableDataCell)
  1178. add(b, parseDoc(q))
  1179. add(a, b)
  1180. add(result, a)
  1181. proc parseTransition(p: var RstParser): PRstNode =
  1182. result = newRstNode(rnTransition)
  1183. inc(p.idx)
  1184. if p.tok[p.idx].kind == tkIndent: inc(p.idx)
  1185. if p.tok[p.idx].kind == tkIndent: inc(p.idx)
  1186. proc parseOverline(p: var RstParser): PRstNode =
  1187. var c = p.tok[p.idx].symbol[0]
  1188. inc(p.idx, 2)
  1189. result = newRstNode(rnOverline)
  1190. while true:
  1191. parseUntilNewline(p, result)
  1192. if p.tok[p.idx].kind == tkIndent:
  1193. inc(p.idx)
  1194. if p.tok[p.idx - 1].ival > currInd(p):
  1195. add(result, newRstNode(rnLeaf, " "))
  1196. else:
  1197. break
  1198. else:
  1199. break
  1200. result.level = getLevel(p.s.overlineToLevel, p.s.oLevel, c)
  1201. if p.tok[p.idx].kind == tkAdornment:
  1202. inc(p.idx) # XXX: check?
  1203. if p.tok[p.idx].kind == tkIndent: inc(p.idx)
  1204. proc parseBulletList(p: var RstParser): PRstNode =
  1205. result = nil
  1206. if p.tok[p.idx + 1].kind == tkWhite:
  1207. var bullet = p.tok[p.idx].symbol
  1208. var col = p.tok[p.idx].col
  1209. result = newRstNode(rnBulletList)
  1210. pushInd(p, p.tok[p.idx + 2].col)
  1211. inc(p.idx, 2)
  1212. while true:
  1213. var item = newRstNode(rnBulletItem)
  1214. parseSection(p, item)
  1215. add(result, item)
  1216. if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and
  1217. (p.tok[p.idx + 1].symbol == bullet) and
  1218. (p.tok[p.idx + 2].kind == tkWhite):
  1219. inc(p.idx, 3)
  1220. else:
  1221. break
  1222. popInd(p)
  1223. proc parseOptionList(p: var RstParser): PRstNode =
  1224. result = newRstNode(rnOptionList)
  1225. while true:
  1226. if isOptionList(p):
  1227. var a = newRstNode(rnOptionGroup)
  1228. var b = newRstNode(rnDescription)
  1229. var c = newRstNode(rnOptionListItem)
  1230. if match(p, p.idx, "//w"): inc(p.idx)
  1231. while not (p.tok[p.idx].kind in {tkIndent, tkEof}):
  1232. if (p.tok[p.idx].kind == tkWhite) and (len(p.tok[p.idx].symbol) > 1):
  1233. inc(p.idx)
  1234. break
  1235. add(a, newLeaf(p))
  1236. inc(p.idx)
  1237. var j = tokenAfterNewline(p)
  1238. if (j > 0) and (p.tok[j - 1].kind == tkIndent) and
  1239. (p.tok[j - 1].ival > currInd(p)):
  1240. pushInd(p, p.tok[j - 1].ival)
  1241. parseSection(p, b)
  1242. popInd(p)
  1243. else:
  1244. parseLine(p, b)
  1245. if (p.tok[p.idx].kind == tkIndent): inc(p.idx)
  1246. add(c, a)
  1247. add(c, b)
  1248. add(result, c)
  1249. else:
  1250. break
  1251. proc parseDefinitionList(p: var RstParser): PRstNode =
  1252. result = nil
  1253. var j = tokenAfterNewline(p) - 1
  1254. if (j >= 1) and (p.tok[j].kind == tkIndent) and
  1255. (p.tok[j].ival > currInd(p)) and (p.tok[j - 1].symbol != "::"):
  1256. var col = p.tok[p.idx].col
  1257. result = newRstNode(rnDefList)
  1258. while true:
  1259. j = p.idx
  1260. var a = newRstNode(rnDefName)
  1261. parseLine(p, a)
  1262. if (p.tok[p.idx].kind == tkIndent) and
  1263. (p.tok[p.idx].ival > currInd(p)) and
  1264. (p.tok[p.idx + 1].symbol != "::") and
  1265. not (p.tok[p.idx + 1].kind in {tkIndent, tkEof}):
  1266. pushInd(p, p.tok[p.idx].ival)
  1267. var b = newRstNode(rnDefBody)
  1268. parseSection(p, b)
  1269. var c = newRstNode(rnDefItem)
  1270. add(c, a)
  1271. add(c, b)
  1272. add(result, c)
  1273. popInd(p)
  1274. else:
  1275. p.idx = j
  1276. break
  1277. if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col):
  1278. inc(p.idx)
  1279. j = tokenAfterNewline(p) - 1
  1280. if j >= 1 and p.tok[j].kind == tkIndent and p.tok[j].ival > col and
  1281. p.tok[j-1].symbol != "::" and p.tok[j+1].kind != tkIndent:
  1282. discard
  1283. else:
  1284. break
  1285. if len(result) == 0: result = nil
  1286. proc parseEnumList(p: var RstParser): PRstNode =
  1287. const
  1288. wildcards: array[0..2, string] = ["(e) ", "e) ", "e. "]
  1289. wildpos: array[0..2, int] = [1, 0, 0]
  1290. result = nil
  1291. var w = 0
  1292. while w <= 2:
  1293. if match(p, p.idx, wildcards[w]): break
  1294. inc(w)
  1295. if w <= 2:
  1296. var col = p.tok[p.idx].col
  1297. result = newRstNode(rnEnumList)
  1298. inc(p.idx, wildpos[w] + 3)
  1299. var j = tokenAfterNewline(p)
  1300. if (p.tok[j].col == p.tok[p.idx].col) or match(p, j, wildcards[w]):
  1301. pushInd(p, p.tok[p.idx].col)
  1302. while true:
  1303. var item = newRstNode(rnEnumItem)
  1304. parseSection(p, item)
  1305. add(result, item)
  1306. if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and
  1307. match(p, p.idx + 1, wildcards[w]):
  1308. inc(p.idx, wildpos[w] + 4)
  1309. else:
  1310. break
  1311. popInd(p)
  1312. else:
  1313. dec(p.idx, wildpos[w] + 3)
  1314. result = nil
  1315. proc sonKind(father: PRstNode, i: int): RstNodeKind =
  1316. result = rnLeaf
  1317. if i < len(father): result = father.sons[i].kind
  1318. proc parseSection(p: var RstParser, result: PRstNode) =
  1319. while true:
  1320. var leave = false
  1321. assert(p.idx >= 0)
  1322. while p.tok[p.idx].kind == tkIndent:
  1323. if currInd(p) == p.tok[p.idx].ival:
  1324. inc(p.idx)
  1325. elif p.tok[p.idx].ival > currInd(p):
  1326. pushInd(p, p.tok[p.idx].ival)
  1327. var a = newRstNode(rnBlockQuote)
  1328. parseSection(p, a)
  1329. add(result, a)
  1330. popInd(p)
  1331. else:
  1332. leave = true
  1333. break
  1334. if leave or p.tok[p.idx].kind == tkEof: break
  1335. var a: PRstNode = nil
  1336. var k = whichSection(p)
  1337. case k
  1338. of rnLiteralBlock:
  1339. inc(p.idx) # skip '::'
  1340. a = parseLiteralBlock(p)
  1341. of rnBulletList: a = parseBulletList(p)
  1342. of rnLineBlock: a = parseLineBlock(p)
  1343. of rnDirective: a = parseDotDot(p)
  1344. of rnEnumList: a = parseEnumList(p)
  1345. of rnLeaf: rstMessage(p, meNewSectionExpected)
  1346. of rnParagraph: discard
  1347. of rnDefList: a = parseDefinitionList(p)
  1348. of rnFieldList:
  1349. if p.idx > 0: dec(p.idx)
  1350. a = parseFields(p)
  1351. of rnTransition: a = parseTransition(p)
  1352. of rnHeadline: a = parseHeadline(p)
  1353. of rnOverline: a = parseOverline(p)
  1354. of rnTable: a = parseSimpleTable(p)
  1355. of rnOptionList: a = parseOptionList(p)
  1356. else:
  1357. #InternalError("rst.parseSection()")
  1358. discard
  1359. if a == nil and k != rnDirective:
  1360. a = newRstNode(rnParagraph)
  1361. parseParagraph(p, a)
  1362. addIfNotNil(result, a)
  1363. if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph:
  1364. result.sons[0].kind = rnInner
  1365. proc parseSectionWrapper(p: var RstParser): PRstNode =
  1366. result = newRstNode(rnInner)
  1367. parseSection(p, result)
  1368. while (result.kind == rnInner) and (len(result) == 1):
  1369. result = result.sons[0]
  1370. proc `$`(t: Token): string =
  1371. result = $t.kind & ' ' & t.symbol
  1372. proc parseDoc(p: var RstParser): PRstNode =
  1373. result = parseSectionWrapper(p)
  1374. if p.tok[p.idx].kind != tkEof:
  1375. when false:
  1376. assert isAllocatedPtr(cast[pointer](p.tok))
  1377. for i in 0 .. high(p.tok):
  1378. assert isNil(p.tok[i].symbol) or
  1379. isAllocatedPtr(cast[pointer](p.tok[i].symbol))
  1380. echo "index: ", p.idx, " length: ", high(p.tok), "##",
  1381. p.tok[p.idx-1], p.tok[p.idx], p.tok[p.idx+1]
  1382. #assert isAllocatedPtr(cast[pointer](p.indentStack))
  1383. rstMessage(p, meGeneralParseError)
  1384. type
  1385. DirFlag = enum
  1386. hasArg, hasOptions, argIsFile, argIsWord
  1387. DirFlags = set[DirFlag]
  1388. SectionParser = proc (p: var RstParser): PRstNode {.nimcall.}
  1389. proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode =
  1390. ## Parses arguments and options for a directive block.
  1391. ##
  1392. ## A directive block will always have three sons: the arguments for the
  1393. ## directive (rnDirArg), the options (rnFieldList) and the block
  1394. ## (rnLineBlock). This proc parses the two first nodes, the block is left to
  1395. ## the outer `parseDirective` call.
  1396. ##
  1397. ## Both rnDirArg and rnFieldList children nodes might be nil, so you need to
  1398. ## check them before accessing.
  1399. result = newRstNode(rnDirective)
  1400. var args: PRstNode = nil
  1401. var options: PRstNode = nil
  1402. if hasArg in flags:
  1403. args = newRstNode(rnDirArg)
  1404. if argIsFile in flags:
  1405. while true:
  1406. case p.tok[p.idx].kind
  1407. of tkWord, tkOther, tkPunct, tkAdornment:
  1408. add(args, newLeaf(p))
  1409. inc(p.idx)
  1410. else: break
  1411. elif argIsWord in flags:
  1412. while p.tok[p.idx].kind == tkWhite: inc(p.idx)
  1413. if p.tok[p.idx].kind == tkWord:
  1414. add(args, newLeaf(p))
  1415. inc(p.idx)
  1416. else:
  1417. args = nil
  1418. else:
  1419. parseLine(p, args)
  1420. add(result, args)
  1421. if hasOptions in flags:
  1422. if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival >= 3) and
  1423. (p.tok[p.idx + 1].symbol == ":"):
  1424. options = parseFields(p)
  1425. add(result, options)
  1426. proc indFollows(p: RstParser): bool =
  1427. result = p.tok[p.idx].kind == tkIndent and p.tok[p.idx].ival > currInd(p)
  1428. proc parseDirective(p: var RstParser, flags: DirFlags,
  1429. contentParser: SectionParser): PRstNode =
  1430. ## Returns a generic rnDirective tree.
  1431. ##
  1432. ## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil.
  1433. result = parseDirective(p, flags)
  1434. if not isNil(contentParser) and indFollows(p):
  1435. pushInd(p, p.tok[p.idx].ival)
  1436. var content = contentParser(p)
  1437. popInd(p)
  1438. add(result, content)
  1439. else:
  1440. add(result, PRstNode(nil))
  1441. proc parseDirBody(p: var RstParser, contentParser: SectionParser): PRstNode =
  1442. if indFollows(p):
  1443. pushInd(p, p.tok[p.idx].ival)
  1444. result = contentParser(p)
  1445. popInd(p)
  1446. proc dirInclude(p: var RstParser): PRstNode =
  1447. ##
  1448. ## The following options are recognized:
  1449. ##
  1450. ## :start-after: text to find in the external data file
  1451. ##
  1452. ## Only the content after the first occurrence of the specified
  1453. ## text will be included. If text is not found inclusion will
  1454. ## start from beginning of the file
  1455. ##
  1456. ## :end-before: text to find in the external data file
  1457. ##
  1458. ## Only the content before the first occurrence of the specified
  1459. ## text (but after any after text) will be included. If text is
  1460. ## not found inclusion will happen until the end of the file.
  1461. #literal : flag (empty)
  1462. # The entire included text is inserted into the document as a single
  1463. # literal block (useful for program listings).
  1464. #encoding : name of text encoding
  1465. # The text encoding of the external data file. Defaults to the document's
  1466. # encoding (if specified).
  1467. #
  1468. result = nil
  1469. var n = parseDirective(p, {hasArg, argIsFile, hasOptions}, nil)
  1470. var filename = strip(addNodes(n.sons[0]))
  1471. var path = p.findRelativeFile(filename)
  1472. if path == "":
  1473. rstMessage(p, meCannotOpenFile, filename)
  1474. else:
  1475. # XXX: error handling; recursive file inclusion!
  1476. if getFieldValue(n, "literal") != "":
  1477. result = newRstNode(rnLiteralBlock)
  1478. add(result, newRstNode(rnLeaf, readFile(path)))
  1479. else:
  1480. let inputString = readFile(path).string()
  1481. let startPosition =
  1482. block:
  1483. let searchFor = n.getFieldValue("start-after").strip()
  1484. if searchFor != "":
  1485. let pos = inputString.find(searchFor)
  1486. if pos != -1: pos + searchFor.len()
  1487. else: 0
  1488. else:
  1489. 0
  1490. let endPosition =
  1491. block:
  1492. let searchFor = n.getFieldValue("end-before").strip()
  1493. if searchFor != "":
  1494. let pos = inputString.find(searchFor, start = startPosition)
  1495. if pos != -1: pos - 1
  1496. else: 0
  1497. else:
  1498. inputString.len - 1
  1499. var q: RstParser
  1500. initParser(q, p.s)
  1501. q.filename = path
  1502. q.col += getTokens(
  1503. inputString[startPosition..endPosition].strip(),
  1504. false,
  1505. q.tok)
  1506. # workaround a GCC bug; more like the interior pointer bug?
  1507. #if find(q.tok[high(q.tok)].symbol, "\0\x01\x02") > 0:
  1508. # InternalError("Too many binary zeros in include file")
  1509. result = parseDoc(q)
  1510. proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode =
  1511. ## Parses a code block.
  1512. ##
  1513. ## Code blocks are rnDirective trees with a `kind` of rnCodeBlock. See the
  1514. ## description of ``parseDirective`` for further structure information.
  1515. ##
  1516. ## Code blocks can come in two forms, the standard `code directive
  1517. ## <http://docutils.sourceforge.net/docs/ref/rst/directives.html#code>`_ and
  1518. ## the nim extension ``.. code-block::``. If the block is an extension, we
  1519. ## want the default language syntax highlighting to be Nim, so we create a
  1520. ## fake internal field to communicate with the generator. The field is named
  1521. ## ``default-language``, which is unlikely to collide with a field specified
  1522. ## by any random rst input file.
  1523. ##
  1524. ## As an extension this proc will process the ``file`` extension field and if
  1525. ## present will replace the code block with the contents of the referenced
  1526. ## file.
  1527. result = parseDirective(p, {hasArg, hasOptions}, parseLiteralBlock)
  1528. var filename = strip(getFieldValue(result, "file"))
  1529. if filename != "":
  1530. var path = p.findRelativeFile(filename)
  1531. if path == "": rstMessage(p, meCannotOpenFile, filename)
  1532. var n = newRstNode(rnLiteralBlock)
  1533. add(n, newRstNode(rnLeaf, readFile(path)))
  1534. result.sons[2] = n
  1535. # Extend the field block if we are using our custom extension.
  1536. if nimExtension:
  1537. # Create a field block if the input block didn't have any.
  1538. if result.sons[1].isNil: result.sons[1] = newRstNode(rnFieldList)
  1539. assert result.sons[1].kind == rnFieldList
  1540. # Hook the extra field and specify the Nim language as value.
  1541. var extraNode = newRstNode(rnField)
  1542. extraNode.add(newRstNode(rnFieldName))
  1543. extraNode.add(newRstNode(rnFieldBody))
  1544. extraNode.sons[0].add(newRstNode(rnLeaf, "default-language"))
  1545. extraNode.sons[1].add(newRstNode(rnLeaf, "Nim"))
  1546. result.sons[1].add(extraNode)
  1547. result.kind = rnCodeBlock
  1548. proc dirContainer(p: var RstParser): PRstNode =
  1549. result = parseDirective(p, {hasArg}, parseSectionWrapper)
  1550. assert(result.kind == rnDirective)
  1551. assert(len(result) == 3)
  1552. result.kind = rnContainer
  1553. proc dirImage(p: var RstParser): PRstNode =
  1554. result = parseDirective(p, {hasOptions, hasArg, argIsFile}, nil)
  1555. result.kind = rnImage
  1556. proc dirFigure(p: var RstParser): PRstNode =
  1557. result = parseDirective(p, {hasOptions, hasArg, argIsFile},
  1558. parseSectionWrapper)
  1559. result.kind = rnFigure
  1560. proc dirTitle(p: var RstParser): PRstNode =
  1561. result = parseDirective(p, {hasArg}, nil)
  1562. result.kind = rnTitle
  1563. proc dirContents(p: var RstParser): PRstNode =
  1564. result = parseDirective(p, {hasArg}, nil)
  1565. result.kind = rnContents
  1566. proc dirIndex(p: var RstParser): PRstNode =
  1567. result = parseDirective(p, {}, parseSectionWrapper)
  1568. result.kind = rnIndex
  1569. proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind,
  1570. contentParser: SectionParser) =
  1571. var filename = getFieldValue(result, "file")
  1572. if filename.len > 0:
  1573. var path = p.findRelativeFile(filename)
  1574. if path.len == 0:
  1575. rstMessage(p, meCannotOpenFile, filename)
  1576. else:
  1577. var f = readFile(path)
  1578. result = newRstNode(kind)
  1579. add(result, newRstNode(rnLeaf, f))
  1580. else:
  1581. result.kind = kind
  1582. add(result, parseDirBody(p, contentParser))
  1583. proc dirRaw(p: var RstParser): PRstNode =
  1584. #
  1585. #The following options are recognized:
  1586. #
  1587. #file : string (newlines removed)
  1588. # The local filesystem path of a raw data file to be included.
  1589. #
  1590. # html
  1591. # latex
  1592. result = parseDirective(p, {hasOptions, hasArg, argIsWord})
  1593. if result.sons[0] != nil:
  1594. if cmpIgnoreCase(result.sons[0].sons[0].text, "html") == 0:
  1595. dirRawAux(p, result, rnRawHtml, parseLiteralBlock)
  1596. elif cmpIgnoreCase(result.sons[0].sons[0].text, "latex") == 0:
  1597. dirRawAux(p, result, rnRawLatex, parseLiteralBlock)
  1598. else:
  1599. rstMessage(p, meInvalidDirective, result.sons[0].sons[0].text)
  1600. else:
  1601. dirRawAux(p, result, rnRaw, parseSectionWrapper)
  1602. proc parseDotDot(p: var RstParser): PRstNode =
  1603. result = nil
  1604. var col = p.tok[p.idx].col
  1605. inc(p.idx)
  1606. var d = getDirective(p)
  1607. if d != "":
  1608. pushInd(p, col)
  1609. case getDirKind(d)
  1610. of dkInclude: result = dirInclude(p)
  1611. of dkImage: result = dirImage(p)
  1612. of dkFigure: result = dirFigure(p)
  1613. of dkTitle: result = dirTitle(p)
  1614. of dkContainer: result = dirContainer(p)
  1615. of dkContents: result = dirContents(p)
  1616. of dkRaw:
  1617. if roSupportRawDirective in p.s.options:
  1618. result = dirRaw(p)
  1619. else:
  1620. rstMessage(p, meInvalidDirective, d)
  1621. of dkCode: result = dirCodeBlock(p)
  1622. of dkCodeBlock: result = dirCodeBlock(p, nimExtension = true)
  1623. of dkIndex: result = dirIndex(p)
  1624. else: rstMessage(p, meInvalidDirective, d)
  1625. popInd(p)
  1626. elif match(p, p.idx, " _"):
  1627. # hyperlink target:
  1628. inc(p.idx, 2)
  1629. var a = getReferenceName(p, ":")
  1630. if p.tok[p.idx].kind == tkWhite: inc(p.idx)
  1631. var b = untilEol(p)
  1632. setRef(p, rstnodeToRefname(a), b)
  1633. elif match(p, p.idx, " |"):
  1634. # substitution definitions:
  1635. inc(p.idx, 2)
  1636. var a = getReferenceName(p, "|")
  1637. var b: PRstNode
  1638. if p.tok[p.idx].kind == tkWhite: inc(p.idx)
  1639. if cmpIgnoreStyle(p.tok[p.idx].symbol, "replace") == 0:
  1640. inc(p.idx)
  1641. expect(p, "::")
  1642. b = untilEol(p)
  1643. elif cmpIgnoreStyle(p.tok[p.idx].symbol, "image") == 0:
  1644. inc(p.idx)
  1645. b = dirImage(p)
  1646. else:
  1647. rstMessage(p, meInvalidDirective, p.tok[p.idx].symbol)
  1648. setSub(p, addNodes(a), b)
  1649. elif match(p, p.idx, " ["):
  1650. # footnotes, citations
  1651. inc(p.idx, 2)
  1652. var a = getReferenceName(p, "]")
  1653. if p.tok[p.idx].kind == tkWhite: inc(p.idx)
  1654. var b = untilEol(p)
  1655. setRef(p, rstnodeToRefname(a), b)
  1656. else:
  1657. result = parseComment(p)
  1658. proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode =
  1659. result = n
  1660. if n == nil: return
  1661. case n.kind
  1662. of rnSubstitutionReferences:
  1663. var x = findSub(p, n)
  1664. if x >= 0:
  1665. result = p.s.subs[x].value
  1666. else:
  1667. var key = addNodes(n)
  1668. var e = getEnv(key)
  1669. if e != "": result = newRstNode(rnLeaf, e)
  1670. else: rstMessage(p, mwUnknownSubstitution, key)
  1671. of rnRef:
  1672. var y = findRef(p, rstnodeToRefname(n))
  1673. if y != nil:
  1674. result = newRstNode(rnHyperlink)
  1675. n.kind = rnInner
  1676. add(result, n)
  1677. add(result, y)
  1678. of rnLeaf:
  1679. discard
  1680. of rnContents:
  1681. p.hasToc = true
  1682. else:
  1683. for i in countup(0, len(n) - 1): n.sons[i] = resolveSubs(p, n.sons[i])
  1684. proc rstParse*(text, filename: string,
  1685. line, column: int, hasToc: var bool,
  1686. options: RstParseOptions,
  1687. findFile: FindFileHandler = nil,
  1688. msgHandler: MsgHandler = nil): PRstNode =
  1689. var p: RstParser
  1690. initParser(p, newSharedState(options, findFile, msgHandler))
  1691. p.filename = filename
  1692. p.line = line
  1693. p.col = column + getTokens(text, roSkipPounds in options, p.tok)
  1694. result = resolveSubs(p, parseDoc(p))
  1695. hasToc = p.hasToc