manual: CI check with nitunit
[nit.git] / lib / markdown2 / markdown_ast.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Markdown AST representation
16 module markdown_ast
17
18 # An abstract node
19 abstract class MdNode
20
21 # Node location in original markdown
22 var location: MdLocation
23
24 # Node parent
25 var parent: nullable MdNode = null is writable
26
27 # First child
28 var first_child: nullable MdNode = null is writable
29
30 # Last child
31 var last_child: nullable MdNode = null is writable
32
33 # Previous node
34 var prev: nullable MdNode = null is writable
35
36 # Next node
37 var next: nullable MdNode = null is writable
38
39 # Children nodes of `self`
40 fun children: Array[MdNode] do
41 var nodes = new Array[MdNode]
42
43 var node = first_child
44 while node != null do
45 nodes.add node
46 node = node.next
47 end
48
49 return nodes
50 end
51
52 # Append a child to `self`
53 fun append_child(child: MdNode) do
54 child.unlink
55 child.parent = self
56 if last_child != null then
57 last_child.as(not null).next = child
58 child.prev = last_child
59 last_child = child
60 else
61 first_child = child
62 last_child = child
63 end
64 end
65
66 # Prepend a child to `self`
67 fun prepend_child(child: MdNode) do
68 child.unlink
69 child.parent = self
70 if first_child != null then
71 first_child.as(not null).prev = child
72 child.next = first_child
73 first_child = child
74 else
75 first_child = child
76 last_child = child
77 end
78 end
79
80 # Unlink `self` from its `parent`
81 fun unlink do
82 if prev != null then
83 prev.as(not null).next = next
84 else if parent != null then
85 parent.as(not null).first_child = next
86 end
87 if next != null then
88 next.as(not null).prev = prev
89 else if parent != null then
90 parent.as(not null).last_child = prev
91 end
92 parent = null
93 next = null
94 prev = null
95 end
96
97 # Insert `sibling` after `self`.
98 fun insert_after(sibling: MdNode) do
99 sibling.unlink
100 sibling.next = next
101 if sibling.next != null then
102 sibling.next.as(not null).prev = sibling
103 end
104 sibling.prev = self
105 next = sibling
106 sibling.parent = parent
107 if sibling.next == null then
108 sibling.parent.as(not null).last_child = sibling
109 end
110 end
111
112 # Insert `sibling` before `self`.
113 fun insert_before(sibling: MdNode) do
114 sibling.unlink
115 sibling.prev = prev
116 if sibling.prev != null then
117 sibling.prev.as(not null).next = sibling
118 end
119 sibling.next = self
120 prev = sibling
121 sibling.parent = parent
122 if sibling.prev == null then
123 sibling.parent.as(not null).first_child = sibling
124 end
125 end
126
127 # Visit all children or `self`
128 fun visit_all(v: MdVisitor) do
129 var node = first_child
130 while node != null do
131 var next = node.next
132 v.visit(node)
133 node = next
134 end
135 end
136
137 redef fun to_s do return "{super}\{{to_s_attrs}\}"
138
139 # Returns `self` attributes as a String
140 #
141 # Mainly used for debug purposes.
142 fun to_s_attrs: String do return "loc: {location}"
143
144 # Print `self` AST
145 fun debug do
146 var v = new MdASTPrinter
147 v.enter_visit(self)
148 end
149 end
150
151 # A visitor for Markdown AST
152 interface MdVisitor
153
154 # Start visiting `node`
155 fun enter_visit(node: MdNode) do visit(node)
156
157 # Visit `node`
158 #
159 # Method to define in specific visitor.
160 # It should not be called directly but used by `enter_visit`.
161 protected fun visit(node: MdNode) is abstract
162 end
163
164 # Print the AST content
165 class MdASTPrinter
166 super MdVisitor
167
168 # Current indent level
169 var indent = 0
170
171 # Visit `self` to print the AST content
172 fun print_ast(node: MdNode) do
173 print "{" " * indent}{node}"
174 indent += 1
175 node.visit_all(self)
176 indent -= 1
177 end
178
179 redef fun visit(node) do print_ast(node)
180 end
181
182 # Blocks
183
184 # An abstract markdown block
185 abstract class MdBlock
186 super MdNode
187
188 redef fun parent do return super
189
190 # Can this block contain other blocks?
191 var is_container = false
192
193 # Can this block contain `block`?
194 fun can_contain(block: MdBlock): Bool do return false
195
196 # Parents of blocks can only be blocks
197 redef fun parent=(node) do
198 assert parent == null or parent isa MdBlock else
199 print "Parent of block must also be block."
200 end
201 super(node)
202 end
203 end
204
205 # A Markdown document
206 class MdDocument
207 super MdBlock
208
209 redef var is_container = true
210
211 redef fun can_contain(block) do return true
212 end
213
214 # A block quote
215 class MdBlockQuote
216 super MdBlock
217
218 redef var is_container = true
219
220 redef fun can_contain(block) do return true
221 end
222
223 # A block of code (indented or fenced)
224 abstract class MdCodeBlock
225 super MdBlock
226
227 # Literal content
228 var literal: nullable String = null is writable
229
230 # Fence info / meta
231 var info: nullable String = null is writable
232
233 redef fun to_s_attrs do return "{super}, info={info or else "null"}, literal={literal or else "null"}"
234 end
235
236 # A block code that starts with an indent
237 class MdIndentedCodeBlock
238 super MdCodeBlock
239
240 # Does this block use tabs instead of spaces?
241 var use_tabs: Bool
242
243 redef fun to_s_attrs do return "{super}, use_tabs={use_tabs}"
244 end
245
246 # A code block that starts with a fence
247 class MdFencedCodeBlock
248 super MdCodeBlock
249
250 # Fence character
251 var fence_char: Char
252
253 # Fence length
254 var fence_length: Int
255
256 # Fence indentation level
257 var fence_indent: Int
258
259 redef fun to_s_attrs do return "{super}, fence_char={fence_char}, fence_length={fence_length}, fence_indent={fence_indent}"
260 end
261
262 # A heading
263 class MdHeading
264 super MdBlock
265
266 # Heading level (from 1 to 6)
267 var level: Int
268
269 # Is this heading in the setext format in the original source?
270 var is_setext = false is optional
271
272 # Does this heading has an atx trailing in the original source?
273 var has_atx_trailing = false is optional
274
275 redef fun to_s_attrs do return "{super}, level={level}"
276 end
277
278 # An html block
279 class MdHtmlBlock
280 super MdBlock
281
282 # Literal content
283 var literal: nullable String = null is writable
284
285 redef fun to_s_attrs do return "{super}, literal={literal or else "null"}"
286 end
287
288 # An ordered or unordered list block
289 abstract class MdListBlock
290 super MdBlock
291
292 # Does this list contains line breaks?
293 var is_tight: Bool = false is writable
294
295 redef var is_container = true
296
297 redef fun can_contain(block) do return block isa MdListItem
298
299 redef fun to_s_attrs do return "{super}, is_tight={is_tight}"
300 end
301
302 # An ordered or unordered list item block
303 class MdListItem
304 super MdBlock
305
306 redef var is_container = true
307
308 redef fun can_contain(block) do return true
309 end
310
311 # An ordered list block
312 class MdOrderedList
313 super MdListBlock
314
315 # List starting number
316 var start_number: Int
317
318 # List number delimiter
319 var delimiter: Char
320
321 redef fun to_s_attrs do return "{super}, start_number={start_number}, delimiter={delimiter}"
322 end
323
324 # An unordered list
325 class MdUnorderedList
326 super MdListBlock
327
328 # List bullet marker
329 var bullet_marker: Char
330
331 redef fun to_s_attrs do return "{super}, bullet_marker={bullet_marker}"
332 end
333
334 # A paragraph block
335 class MdParagraph
336 super MdBlock
337
338 # Is this paragraph in a list?
339 fun is_in_list: Bool do
340 var parent = self.parent
341 return parent != null and parent.parent isa MdListBlock
342 end
343
344 # Is this paragraph in a tight list?
345 fun is_in_tight_list: Bool do
346 var parent = self.parent
347 if parent == null then return false
348 var gramps = parent.parent
349 return gramps isa MdListBlock and gramps.is_tight
350 end
351 end
352
353 # A ruler
354 class MdThematicBreak
355 super MdBlock
356
357 # Thematic break pattern used in markdown source
358 var original_pattern: String
359 end
360
361 # Inline nodes
362
363 # A line break (soft or hard)
364 abstract class MdLineBreak
365 super MdNode
366 end
367
368 # A hardline break (`\\n` or ` \n`)
369 class MdHardLineBreak
370 super MdLineBreak
371
372 # Does this line break used a backslash in the original source?
373 var has_backslash: Bool
374 end
375
376 # A soft line breack (`\r` or `\n`)
377 class MdSoftLineBreak
378 super MdLineBreak
379 end
380
381 # An inline code string
382 class MdCode
383 super MdNode
384
385 # Emphasis delimiter
386 var delimiter: String
387
388 # Literal content
389 var literal: String is writable
390
391 redef fun to_s_attrs do return "{super}, literal={literal}"
392 end
393
394 # A node that users delimiters in the Markdown form
395 #
396 # For example the emphasis: `*bold*`.
397 abstract class MdDelimited
398 super MdNode
399
400 # Emphasis delimiter
401 var delimiter: String
402
403 # Opening delimiter
404 fun opening_delimiter: String do return delimiter
405
406 # Closing delimiter
407 fun closing_delimiter: String do return delimiter
408
409 redef fun to_s_attrs do return "{super}, delimiter={delimiter}"
410 end
411
412 # An emphasis
413 class MdEmphasis
414 super MdDelimited
415 end
416
417 # A strong emphasis token
418 class MdStrongEmphasis
419 super MdDelimited
420 end
421
422 # An inlined html string
423 class MdHtmlInline
424 super MdNode
425
426 # Literal content
427 var literal: String is writable
428
429 redef fun to_s_attrs do return "{super}, literal={literal}"
430 end
431
432 # A link or image
433 abstract class MdLinkOrImage
434 super MdNode
435
436 # Link destination
437 var destination: String is writable
438
439 # Link title
440 var title: nullable String is writable
441
442 # Is the `destination` between pointy brackets `<dest>`
443 var has_brackets = false is writable
444
445 redef fun to_s_attrs do return "{super}, destination={destination}, title={title or else "null"}"
446 end
447
448 # An image
449 class MdImage
450 super MdLinkOrImage
451 end
452
453 # A link
454 class MdLink
455 super MdLinkOrImage
456
457 # Is this link an autolink?
458 var is_autolink = false is optional, writable
459 end
460
461 # A raw text token
462 class MdText
463 super MdNode
464
465 # Literal content
466 var literal: String is writable
467
468 redef fun to_s_attrs do return "{super}, literal={literal}"
469 end
470
471 # MdNode location in the Markdown input
472 class MdLocation
473
474 # Starting line number (starting from 1).
475 var line_start: Int is writable
476
477 # Starting column number (starting from 1).
478 var column_start: Int is writable
479
480 # Stopping line number (starting from 1).
481 var line_end: Int is writable
482
483 # Stopping column number (starting from 1).
484 var column_end: Int is writable
485
486 redef fun to_s do return "{line_start},{column_start}--{line_end},{column_end}"
487 end