summary refs log tree commit diff stats
path: root/lib/packages/docutils/rstast.nim
blob: bb0b61889382f53f8ade310eef8b49b03e1eea99 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
14
#
#
#            Nimrod's Runtime Library
#        (c) Copyright 2012 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

## This module implements an AST for the `reStructuredText`:idx: parser.

import strutils

type
  TRstNodeKind* = enum        ## the possible node kinds of an PRstNode
    rnInner,                  # an inner node or a root
    rnHeadline,               # a headline
    rnOverline,               # an over- and underlined headline
    rnTransition,             # a transition (the ------------- <hr> thingie)
    rnParagraph,              # a paragraph
    rnBulletList,             # a bullet list
    rnBulletItem,             # a bullet item
    rnEnumList,               # an enumerated list
    rnEnumItem,               # an enumerated item
    rnDefList,                # a definition list
    rnDefItem,                # an item of a definition list consisting of ...
    rnDefName,                # ... a name part ...
    rnDefBody,                # ... and a body part ...
    rnFieldList,              # a field list
    rnField,                  # a field item
    rnFieldName,              # consisting of a field name ...
    rnFieldBody,              # ... and a field body
    rnOptionList, rnOptionListItem, rnOptionGroup, rnOption, rnOptionString, 
    rnOptionArgument, rnDescription, rnLiteralBlock, rnQuotedLiteralBlock,
    rnLineBlock,              # the | thingie
    rnLineBlockItem,          # sons of the | thing
    rnBlockQuote,             # text just indented
    rnTable, rnGridTable, rnTableRow, rnTableHeaderCell, rnTableDataCell,
    rnLabel,                  # used for footnotes and other things
    rnFootnote,               # a footnote
    rnCitation,               # similar to footnote
    rnStandaloneHyperlink, rnHyperlink, rnRef, rnDirective, # a directive
    rnDirArg, rnRaw, rnTitle, rnContents, rnImage, rnFigure, rnCodeBlock,
    rnRawHtml, rnRawLatex,
    rnContainer,              # ``container`` directive
    rnIndex,                  # index directve:
                              # .. index::
                              #   key
                              #     * `file#id <file#id>`_
                              #     * `file#id <file#id>'_
    rnSubstitutionDef,        # a definition of a substitution
    rnGeneralRole,            # Inline markup:
    rnSub, rnSup, rnIdx, 
    rnEmphasis,               # "*"
    rnStrongEmphasis,         # "**"
    rnTripleEmphasis,         # "***"
    rnInterpretedText,        # "`"
    rnInlineLiteral,          # "``"
    rnSubstitutionReferences, # "|"
    rnSmiley,                 # some smiley
    rnLeaf                    # a leaf; the node's text field contains the
                              # leaf val


  PRSTNode* = ref TRSTNode    ## an RST node
  TRstNodeSeq* = seq[PRSTNode]
  TRSTNode* {.acyclic, final.} = object ## an RST node's description
    kind*: TRstNodeKind       ## the node's kind
    text*: string             ## valid for leafs in the AST; and the title of
                              ## the document or the section
    level*: int               ## valid for some node kinds
    sons*: TRstNodeSeq        ## the node's sons

proc len*(n: PRSTNode): int = 
  result = len(n.sons)

proc newRstNode*(kind: TRstNodeKind): PRSTNode = 
  new(result)
  result.sons = @[]
  result.kind = kind

proc newRstNode*(kind: TRstNodeKind, s: string): PRSTNode = 
  result = newRstNode(kind)
  result.text = s

proc lastSon*(n: PRSTNode): PRSTNode = 
  result = n.sons[len(n.sons)-1]

proc add*(father, son: PRSTNode) =
  add(father.sons, son)

proc addIfNotNil*(father, son: PRSTNode) = 
  if son != nil: add(father, son)


type
  TRenderContext {.pure.} = object
    indent: int
    verbatim: int

proc renderRstToRst(d: var TRenderContext, n: PRSTNode, result: var string)

proc renderRstSons(d: var TRenderContext, n: PRSTNode, result: var string) = 
  for i in countup(0, len(n) - 1): 
    renderRstToRst(d, n.sons[i], result)
  
proc renderRstToRst(d: var TRenderContext, n: PRstNode, result: var string) =
  # this is needed for the index generation; it may also be useful for
  # debugging, but most code is already debugged...
  const 
    lvlToChar: array[0..8, char] = ['!', '=', '-', '~', '`', '<', '*', '|', '+']
  if n == nil: return
  var ind = repeatChar(d.indent)
  case n.kind
  of rnInner: 
    renderRstSons(d, n, result)
  of rnHeadline:
    result.add("\n")
    result.add(ind)
    
    let oldLen = result.len
    renderRstSons(d, n, result)
    let headlineLen = result.len - oldLen

    result.add("\n")
    result.add(ind)
    result.add repeatChar(headlineLen, lvlToChar[n.level])
  of rnOverline:
    result.add("\n")
    result.add(ind)

    var headline = ""
    renderRstSons(d, n, headline)
    
    let lvl = repeatChar(headline.len - d.indent, lvlToChar[n.level])
    result.add(lvl)
    result.add("\n")
    result.add(headline)
    
    result.add("\n")
    result.add(ind)
    result.add(lvl)
  of rnTransition: 
    result.add("\n\n")
    result.add(ind)
    result.add repeatChar(78-d.indent, '-')
    result.add("\n\n")
  of rnParagraph:
    result.add("\n\n")
    result.add(ind)
    renderRstSons(d, n, result)
  of rnBulletItem: 
    inc(d.indent, 2)
    var tmp = ""
    renderRstSons(d, n, tmp)
    if tmp.len > 0: 
      result.add("\n")
      result.add(ind)
      result.add("* ")
      result.add(tmp)
    dec(d.indent, 2)
  of rnEnumItem:
    inc(d.indent, 4)
    var tmp = ""
    renderRstSons(d, n, tmp)
    if tmp.len > 0: 
      result.add("\n")
      result.add(ind)
      result.add("(#) ")
      result.add(tmp)
    dec(d.indent, 4)
  of rnOptionList, rnFieldList, rnDefList, rnDefItem, rnLineBlock, rnFieldName, 
     rnFieldBody, rnStandaloneHyperlink, rnBulletList, rnEnumList: 
    renderRstSons(d, n, result)
  of rnDefName: 
    result.add("\n\n")
    result.add(ind)
    renderRstSons(d, n, result)
  of rnDefBody:
    inc(d.indent, 2)
    if n.sons[0].kind != rnBulletList: 
      result.add("\n")
      result.add(ind)
      result.add("  ")
    renderRstSons(d, n, result)
    dec(d.indent, 2)
  of rnField:
    var tmp = ""
    renderRstToRst(d, n.sons[0], tmp)
    
    var L = max(tmp.len + 3, 30)
    inc(d.indent, L)
    
    result.add "\n"
    result.add ind
    result.add ':'
    result.add tmp
    result.add ':'
    result.add repeatChar(L - tmp.len - 2)
    renderRstToRst(d, n.sons[1], result)
    
    dec(d.indent, L)
  of rnLineBlockItem: 
    result.add("\n")
    result.add(ind)
    result.add("| ")
    renderRstSons(d, n, result)
  of rnBlockQuote:
    inc(d.indent, 2)
    renderRstSons(d, n, result)
    dec(d.indent, 2)
  of rnRef: 
    result.add("`")
    renderRstSons(d, n, result)
    result.add("`_")
  of rnHyperlink: 
    result.add('`')
    renderRstToRst(d, n.sons[0], result)
    result.add(" <")
    renderRstToRst(d, n.sons[1], result)
    result.add(">`_")
  of rnGeneralRole:
    result.add('`')
    renderRstToRst(d, n.sons[0],result)
    result.add("`:")
    renderRstToRst(d, n.sons[1],result)
    result.add(':')
  of rnSub: 
    result.add('`')
    renderRstSons(d, n, result)
    result.add("`:sub:")
  of rnSup: 
    result.add('`')
    renderRstSons(d, n, result)
    result.add("`:sup:")
  of rnIdx: 
    result.add('`')
    renderRstSons(d, n, result)
    result.add("`:idx:")
  of rnEmphasis: 
    result.add("*")
    renderRstSons(d, n, result)
    result.add("*")
  of rnStrongEmphasis: 
    result.add("**")
    renderRstSons(d, n, result)
    result.add("**")
  of rnTripleEmphasis:
    result.add("***")
    renderRstSons(d, n, result)
    result.add("***")
  of rnInterpretedText: 
    result.add('`')
    renderRstSons(d, n, result)
    result.add('`')
  of rnInlineLiteral: 
    inc(d.verbatim)
    result.add("``")
    renderRstSons(d, n, result)
    result.add("``")
    dec(d.verbatim)
  of rnSmiley:
    result.add(n.text)
  of rnLeaf:
    if d.verbatim == 0 and n.text == "\\":
      result.add("\\\\") # XXX: escape more special characters!
    else:
      result.add(n.text)
  of rnIndex: 
    result.add("\n\n")
    result.add(ind)
    result.add(".. index::\n")
    
    inc(d.indent, 3)
    if n.sons[2] != nil: renderRstSons(d, n.sons[2], result)
    dec(d.indent, 3)
  of rnContents:
    result.add("\n\n")
    result.add(ind)
    result.add(".. contents::")
  else:
    result.add("Error: cannot render: " & $n.kind)
  
proc renderRstToRst*(n: PRSTNode, result: var string) =
  ## renders `n` into its string representation and appends to `result`.
  var d: TRenderContext
  renderRstToRst(d, n, result)