rst.nim 54 KB

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