djot 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,807 +0,0 @@
1
- local inline = require("djot.inline")
2
- local attributes = require("djot.attributes")
3
- local match = require("djot.match")
4
- local make_match, unpack_match = match.make_match, match.unpack_match
5
- local unpack = unpack or table.unpack
6
- local find, sub, byte = string.find, string.sub, string.byte
7
-
8
- local Container = {}
9
-
10
- function Container:new(spec, data)
11
- local contents = {}
12
- setmetatable(contents, spec)
13
- spec.__index = spec
14
- if data then
15
- for k,v in pairs(data) do
16
- contents[k] = v
17
- end
18
- end
19
- return contents
20
- end
21
-
22
- local function get_list_style(marker)
23
- if marker == "+" or marker == "-" or marker == "*" or marker == ":" then
24
- return marker
25
- elseif find(marker, "^[+*-] %[[Xx ]%]") then
26
- return "X" -- task list
27
- elseif find(marker, "^%[[Xx ]%]") then
28
- return "X"
29
- elseif find(marker, "^[(]?%d+[).]") then
30
- return (marker:gsub("%d+","1"))
31
- -- in ambiguous cases we return two values
32
- elseif find(marker, "^[(]?[ivxlcdm][).]") then
33
- return (marker:gsub("%a+", "a")), (marker:gsub("%a+", "i"))
34
- elseif find(marker, "^[(]?[IVXLCDM][).]") then
35
- return (marker:gsub("%a+", "A")), (marker:gsub("%a+", "I"))
36
- elseif find(marker, "^[(]?%l[).]") then
37
- return (marker:gsub("%l", "a"))
38
- elseif find(marker, "^[(]?%u[).]") then
39
- return (marker:gsub("%u", "A"))
40
- elseif find(marker, "^[(]?[ivxlcdm]+[).]") then
41
- return (marker:gsub("%a+", "i"))
42
- elseif find(marker, "^[(]?[IVXLCDM]+[).]") then
43
- return (marker:gsub("%a+", "I"))
44
- else
45
- assert(false, "Could not identify list style for " .. marker)
46
- end
47
- end
48
-
49
- local Parser = {}
50
-
51
- function Parser:new(subject, opts)
52
- -- ensure the subject ends with a newline character
53
- if not subject:find("[\r\n]$") then
54
- subject = subject .. "\n"
55
- end
56
- local state = {
57
- subject = subject,
58
- indent = 0,
59
- startline = nil,
60
- starteol = nil,
61
- endeol = nil,
62
- matches = {},
63
- containers = {},
64
- pos = 1,
65
- last_matched_container = 0,
66
- timer = {},
67
- warnings = {},
68
- opts = opts or {},
69
- finished_line = false }
70
- setmetatable(state, self)
71
- self.__index = self
72
- return state
73
- end
74
-
75
- -- parameters are start and end position
76
- function Parser:parse_table_row(sp, ep)
77
- local orig_matches = #self.matches -- so we can rewind
78
- local startpos = self.pos
79
- self:add_match(sp, sp, "+row")
80
- -- skip | and any initial space in the cell:
81
- self.pos = find(self.subject, "%S", sp + 1)
82
- -- check to see if we have a separator line
83
- local seps = {}
84
- local p = self.pos
85
- local sepfound = false
86
- while not sepfound do
87
- local sepsp, sepep, left, right, trailing =
88
- find(self.subject, "^(%:?)%-%-*(%:?)([ \t]*%|[ \t]*)", p)
89
- if sepep then
90
- local st = "separator_default"
91
- if #left > 0 and #right > 0 then
92
- st = "separator_center"
93
- elseif #right > 0 then
94
- st = "separator_right"
95
- elseif #left > 0 then
96
- st = "separator_left"
97
- end
98
- seps[#seps + 1] = {sepsp, sepep - #trailing, st}
99
- p = sepep + 1
100
- if p == self.starteol then
101
- sepfound = true
102
- break
103
- end
104
- else
105
- break
106
- end
107
- end
108
- if sepfound then
109
- for i=1,#seps do
110
- self:add_match(unpack(seps[i]))
111
- end
112
- self:add_match(self.starteol - 1, self.starteol - 1, "-row")
113
- self.pos = self.starteol
114
- self.finished_line = true
115
- return true
116
- end
117
- local inline_parser = inline.Parser:new(self.subject, self.opts)
118
- self:add_match(sp, sp, "+cell")
119
- while self.pos < ep do
120
- -- parse a chunk as inline content
121
- local _,nextbar = self:find("^[^|\r\n]*|")
122
- inline_parser:feed(self.pos, nextbar - 1)
123
- if inline_parser:in_verbatim() then
124
- -- read the next | as part of verbatim
125
- inline_parser:feed(nextbar, nextbar)
126
- self.pos = nextbar + 1
127
- else
128
- self.pos = nextbar + 1 -- skip past the next |
129
- -- add a table cell
130
- local cell_matches = inline_parser:get_matches()
131
- for i=1,#cell_matches do
132
- local s,e,ann = unpack_match(cell_matches[i])
133
- if i == #cell_matches and ann == "str" then
134
- -- strip trailing space
135
- while byte(self.subject, e) == 32 and e >= s do
136
- e = e - 1
137
- end
138
- end
139
- self:add_match(s,e,ann)
140
- end
141
- self:add_match(nextbar, nextbar, "-cell")
142
- if nextbar < ep then
143
- -- reset inline parser state
144
- inline_parser = inline.Parser:new(self.subject, self.opts)
145
- self:add_match(nextbar, nextbar, "+cell")
146
- self.pos = find(self.subject, "%S", self.pos)
147
- end
148
- end
149
- end
150
- if inline_parser:in_verbatim() then
151
- -- rewind, this is not a valid table row
152
- self.pos = startpos
153
- for i = orig_matches,#self.matches do
154
- self.matches[i] = nil
155
- end
156
- return false
157
- else
158
- self:add_match(self.pos, self.pos, "-row")
159
- self.pos = self.starteol
160
- self.finished_line = true
161
- return true
162
- end
163
- end
164
-
165
- function Parser:specs()
166
- return {
167
- { name = "para",
168
- is_para = true,
169
- content = "inline",
170
- continue = function()
171
- return self:find("^%S")
172
- end,
173
- open = function(spec)
174
- self:add_container(Container:new(spec,
175
- { inline_parser = inline.Parser:new(self.subject, self.opts) }))
176
- self:add_match(self.pos, self.pos, "+para")
177
- return true
178
- end,
179
- close = function()
180
- self:get_inline_matches()
181
- self:add_match(self.pos - 1, self.pos - 1, "-para")
182
- self.containers[#self.containers] = nil
183
- end
184
- },
185
-
186
- { name = "caption",
187
- is_para = false,
188
- content = "inline",
189
- continue = function()
190
- return self:find("^%S")
191
- end,
192
- open = function(spec)
193
- local _, ep = self:find("^%^[ \t]+")
194
- if ep then
195
- self.pos = ep + 1
196
- self:add_container(Container:new(spec,
197
- { inline_parser = inline.Parser:new(self.subject, self.opts) }))
198
- self:add_match(self.pos, self.pos, "+caption")
199
- return true
200
- end
201
- end,
202
- close = function()
203
- self:get_inline_matches()
204
- self:add_match(self.pos - 1, self.pos - 1, "-caption")
205
- self.containers[#self.containers] = nil
206
- end
207
- },
208
-
209
- { name = "blockquote",
210
- content = "block",
211
- continue = function()
212
- if self:find("^%>%s") then
213
- self.pos = self.pos + 1
214
- return true
215
- else
216
- return false
217
- end
218
- end,
219
- open = function(spec)
220
- if self:find("^%>%s") then
221
- self:add_container(Container:new(spec))
222
- self:add_match(self.pos, self.pos, "+blockquote")
223
- self.pos = self.pos + 1
224
- return true
225
- end
226
- end,
227
- close = function()
228
- self:add_match(self.pos, self.pos, "-blockquote")
229
- self.containers[#self.containers] = nil
230
- end
231
- },
232
-
233
- -- should go before reference definitions
234
- { name = "footnote",
235
- content = "block",
236
- continue = function(container)
237
- if self.indent > container.indent or self:find("^[\r\n]") then
238
- return true
239
- else
240
- return false
241
- end
242
- end,
243
- open = function(spec)
244
- local sp, ep, label = self:find("^%[%^([^]]+)%]:%s")
245
- if not sp then
246
- return nil
247
- end
248
- -- adding container will close others
249
- self:add_container(Container:new(spec, {note_label = label,
250
- indent = self.indent}))
251
- self:add_match(sp, sp, "+footnote")
252
- self:add_match(sp + 2, ep - 3, "note_label")
253
- self.pos = ep
254
- return true
255
- end,
256
- close = function(_container)
257
- self:add_match(self.pos, self.pos, "-footnote")
258
- self.containers[#self.containers] = nil
259
- end
260
- },
261
-
262
- -- should go before list_item_spec
263
- { name = "thematic_break",
264
- content = nil,
265
- continue = function()
266
- return false
267
- end,
268
- open = function(spec)
269
- local sp, ep = self:find("^[-*][ \t]*[-*][ \t]*[-*][-* \t]*[\r\n]")
270
- if ep then
271
- self:add_container(Container:new(spec))
272
- self:add_match(sp, ep, "thematic_break")
273
- self.pos = ep
274
- return true
275
- end
276
- end,
277
- close = function(_container)
278
- self.containers[#self.containers] = nil
279
- end
280
- },
281
-
282
- { name = "list_item",
283
- content = "block",
284
- continue = function(container)
285
- if self.indent > container.indent or self:find("^[\r\n]") then
286
- return true
287
- else
288
- return false
289
- end
290
- end,
291
- open = function(spec)
292
- local sp, ep = self:find("^[-*+:]%s")
293
- if not sp then
294
- sp, ep = self:find("^%d+[.)]%s")
295
- end
296
- if not sp then
297
- sp, ep = self:find("^%(%d+%)%s")
298
- end
299
- if not sp then
300
- sp, ep = self:find("^[ivxlcdmIVXLCDM]+[.)]%s")
301
- end
302
- if not sp then
303
- sp, ep = self:find("^%([ivxlcdmIVXLCDM]+%)%s")
304
- end
305
- if not sp then
306
- sp, ep = self:find("^%a[.)]%s")
307
- end
308
- if not sp then
309
- sp, ep = self:find("^%(%a%)%s")
310
- end
311
- if not sp then
312
- return nil
313
- end
314
- local marker = sub(self.subject, sp, ep - 1)
315
- local checkbox = nil
316
- if self:find("^[*+-] %[[Xx ]%]%s", sp + 1) then -- task list
317
- marker = sub(self.subject, sp, sp + 4)
318
- checkbox = sub(self.subject, sp + 3, sp + 3)
319
- end
320
- -- some items have ambiguous style
321
- local styles = {get_list_style(marker)}
322
- local data = { styles = styles,
323
- indent = self.indent }
324
- -- adding container will close others
325
- self:add_container(Container:new(spec, data))
326
- local annot = "+list_item"
327
- for i=1,#styles do
328
- annot = annot .. "[" .. styles[i] .. "]"
329
- end
330
- self:add_match(sp, ep - 1, annot)
331
- self.pos = ep
332
- if checkbox then
333
- if checkbox == " " then
334
- self:add_match(sp + 2, sp + 4, "checkbox_unchecked")
335
- else
336
- self:add_match(sp + 2, sp + 4, "checkbox_checked")
337
- end
338
- self.pos = sp + 5
339
- end
340
- return true
341
- end,
342
- close = function(_container)
343
- self:add_match(self.pos, self.pos, "-list_item")
344
- self.containers[#self.containers] = nil
345
- end
346
- },
347
-
348
- { name = "reference_definition",
349
- content = nil,
350
- continue = function(container)
351
- if container.indent >= self.indent then
352
- return false
353
- end
354
- local _, ep, rest = self:find("^(%S+)")
355
- if ep then
356
- self:add_match(ep - #rest + 1, ep, "reference_value")
357
- container.value = rest
358
- self.pos = ep + 1
359
- end
360
- return true
361
- end,
362
- open = function(spec)
363
- local sp, ep, label, rest = self:find("^%[([^]\r\n]*)%]:[ \t]*(%S*)")
364
- if sp then
365
- self:add_container(Container:new(spec,
366
- { key = label,
367
- value = rest,
368
- indent = self.indent }))
369
- self:add_match(sp, sp, "+reference_definition")
370
- self:add_match(sp, sp + #label + 1, "reference_key")
371
- if #rest > 0 then
372
- self:add_match(ep - #rest + 1, ep, "reference_value")
373
- end
374
- self.pos = ep + 1
375
- return true
376
- end
377
- end,
378
- close = function(_container)
379
- self:add_match(self.pos, self.pos, "-reference_definition")
380
- self.containers[#self.containers] = nil
381
- end
382
- },
383
-
384
- { name = "heading",
385
- content = "inline",
386
- continue = function(_container)
387
- return false
388
- end,
389
- open = function(spec)
390
- local sp, ep = self:find("^#+")
391
- if ep and find(self.subject, "^%s", ep + 1) then
392
- local level = ep - sp + 1
393
- self:add_container(Container:new(spec, {level = level,
394
- inline_parser = inline.Parser:new(self.subject, self.opts) }))
395
- self:add_match(sp, ep, "+heading")
396
- self.pos = ep + 1
397
- return true
398
- end
399
- end,
400
- close = function(_container)
401
- self:get_inline_matches()
402
- local last = self.matches[#self.matches] or self.pos - 1
403
- local sp, ep, annot = unpack_match(last)
404
- -- handle final ###
405
- local endheadingpos = ep
406
- local endheadingendpos = ep
407
- if annot == "str" then
408
- local endheadingstart, _, hashes =
409
- find(sub(self.subject, sp, ep), "%s+(#+)$")
410
- if hashes then
411
- endheadingpos = endheadingpos - #hashes
412
- if endheadingstart == 1 then
413
- -- remove final str match
414
- self.matches[#self.matches] = nil
415
- else
416
- self.matches[#self.matches] =
417
- make_match(sp, sp + (endheadingstart - 2), "str")
418
- end
419
- end
420
- end
421
- self:add_match(endheadingpos, endheadingendpos, "-heading")
422
- self.containers[#self.containers] = nil
423
- end
424
- },
425
-
426
- { name = "code_block",
427
- content = "text",
428
- continue = function(container)
429
- local char = sub(container.border, 1, 1)
430
- local sp, ep, border = self:find("^(" .. container.border ..
431
- char .. "*)[ \t]*[\r\n]")
432
- if ep then
433
- container.end_fence_sp = sp
434
- container.end_fence_ep = sp + #border - 1
435
- self.pos = ep -- before newline
436
- self.finished_line = true
437
- return false
438
- else
439
- return true
440
- end
441
- end,
442
- open = function(spec)
443
- local sp, ep, border, ws, lang =
444
- self:find("^(~~~~*)([ \t]*)(%S*)[ \t]*[\r\n]")
445
- if not ep then
446
- sp, ep, border, ws, lang =
447
- self:find("^(````*)([ \t]*)([^%s`]*)[ \t]*[\r\n]")
448
- end
449
- if border then
450
- local is_raw = find(lang, "^=") and true or false
451
- self:add_container(Container:new(spec, {border = border,
452
- indent = self.indent }))
453
- self:add_match(sp, sp + #border - 1, "+code_block")
454
- if #lang > 0 then
455
- local langstart = sp + #border + #ws
456
- if is_raw then
457
- self:add_match(langstart, langstart + #lang - 1, "raw_format")
458
- else
459
- self:add_match(langstart, langstart + #lang - 1, "code_language")
460
- end
461
- end
462
- self.pos = ep -- before newline
463
- self.finished_line = true
464
- return true
465
- end
466
- end,
467
- close = function(container)
468
- local sp = container.end_fence_sp or self.pos
469
- local ep = container.end_fence_ep or self.pos
470
- self:add_match(sp, ep, "-code_block")
471
- if sp == ep then
472
- self.warnings[#self.warnings + 1] = {self.pos, "Unclosed code block"}
473
- end
474
- self.containers[#self.containers] = nil
475
- end
476
- },
477
-
478
- { name = "fenced_div",
479
- content = "block",
480
- continue = function(container)
481
- local sp, ep, equals = self:find("^(::::*)[ \t]*[r\n]")
482
- if ep and #equals >= container.equals then
483
- container.end_fence_sp = sp
484
- container.end_fence_ep = sp + #equals - 1
485
- self.pos = ep -- before newline
486
- return false
487
- else
488
- return true
489
- end
490
- end,
491
- open = function(spec)
492
- local sp, ep1, equals = self:find("^(::::*)[ \t]*")
493
- if not ep1 then
494
- return false
495
- end
496
- local clsp, ep = find(self.subject, "^%w*", ep1 + 1)
497
- local _, eol = find(self.subject, "^[ \t]*[\r\n]", ep + 1)
498
- if eol then
499
- self:add_container(Container:new(spec, {equals = #equals}))
500
- self:add_match(sp, ep, "+div")
501
- if ep > clsp then
502
- self:add_match(clsp, ep, "class")
503
- end
504
- self.pos = eol + 1
505
- self.finished_line = true
506
- return true
507
- end
508
- end,
509
- close = function(container)
510
- local sp = container.end_fence_sp or self.pos
511
- local ep = container.end_fence_ep or self.pos
512
- -- check to make sure the match is in order
513
- self:add_match(sp, ep, "-div")
514
- if sp == ep then
515
- self.warnings[#self.warnings + 1] = {self.pos, "Unclosed div"}
516
- end
517
- self.containers[#self.containers] = nil
518
- end
519
- },
520
-
521
- { name = "table",
522
- content = "cells",
523
- continue = function(_container)
524
- local sp, ep = self:find("^|[^\r\n]*|")
525
- local eolsp = " *[\r\n]" -- make sure at end of line
526
- if sp and eolsp then
527
- return self:parse_table_row(sp, ep)
528
- end
529
- end,
530
- open = function(spec)
531
- local sp, ep = self:find("^|[^\r\n]*|")
532
- local eolsp = " *[\r\n]" -- make sure at end of line
533
- if sp and eolsp then
534
- self:add_container(Container:new(spec, { columns = 0 }))
535
- self:add_match(sp, sp, "+table")
536
- if self:parse_table_row(sp, ep) then
537
- return true
538
- else
539
- self.containers[#self.containers] = nil
540
- self.matches[#self.matches] = nil -- remove +table match
541
- return false
542
- end
543
- end
544
- end,
545
- close = function(_container)
546
- self:add_match(self.pos, self.pos, "-table")
547
- self.containers[#self.containers] = nil
548
- end
549
- },
550
-
551
- { name = "attributes",
552
- content = "attributes",
553
- continue = function(container)
554
- if self.indent > container.indent then
555
- container.slices[#container.slices + 1] =
556
- {self.pos, self.endeol}
557
- self.pos = self.starteol
558
- return true
559
- else
560
- return false
561
- end
562
- end,
563
- open = function(spec)
564
- if self:find("^%{") then
565
- self:add_container(Container:new(spec,
566
- { slices = {{self.pos, self.endeol}},
567
- indent = self.indent }))
568
- self.pos = self.starteol
569
- return true
570
- end
571
- end,
572
- close = function(container)
573
- local attribute_parser = attributes.AttributeParser:new(self.subject)
574
- local slices = container.slices
575
- local status, finalpos
576
- for i=1,#slices do
577
- status, finalpos = attribute_parser:feed(unpack(slices[i]))
578
- if status ~= 'continue' then
579
- break
580
- end
581
- end
582
- -- make sure there's no extra content after the }
583
- if status == 'done' and find(self.subject, "^[ \t]*[\r\n]", finalpos + 1) then
584
- local attr_matches = attribute_parser:get_matches()
585
- self:add_match(slices[1][1], slices[1][1], "+block_attributes")
586
- for i=1,#attr_matches do
587
- self:add_match(unpack_match(attr_matches[i]))
588
- end
589
- self:add_match(slices[#slices][2], slices[#slices][2], "-block_attributes")
590
- else -- If not, parse it as inlines and add paragraph match
591
- container.inline_parser = inline.Parser:new(self.subject, self.opts)
592
- self:add_match(slices[1][1], slices[1][1], "+para")
593
- for i=1,#slices do
594
- container.inline_parser:feed(unpack(slices[i]))
595
- end
596
- self:get_inline_matches()
597
- self:add_match(slices[#slices][2], slices[#slices][2], "-para")
598
- end
599
- self.containers[#self.containers] = nil
600
- end
601
- }
602
- }
603
- end
604
-
605
- function Parser:get_inline_matches()
606
- local matches, warnings =
607
- self.containers[#self.containers].inline_parser:get_matches()
608
- for i=1,#matches do
609
- self.matches[#self.matches + 1] = matches[i]
610
- end
611
- for i=1,#warnings do
612
- self.warnings[#self.warnings + 1] = warnings[i]
613
- end
614
- end
615
-
616
- function Parser:find(patt)
617
- return find(self.subject, patt, self.pos)
618
- end
619
-
620
- function Parser:add_match(startpos, endpos, annotation)
621
- self.matches[#self.matches + 1] = make_match(startpos, endpos, annotation)
622
- end
623
-
624
- function Parser:add_container(container)
625
- local last_matched = self.last_matched_container
626
- while #self.containers > last_matched or
627
- (#self.containers > 0 and
628
- self.containers[#self.containers].content ~= "block") do
629
- self.containers[#self.containers]:close()
630
- end
631
- self.containers[#self.containers + 1] = container
632
- end
633
-
634
- function Parser:skip_space()
635
- local newpos, _ = find(self.subject, "[^ \t]", self.pos)
636
- if newpos then
637
- self.indent = newpos - self.startline
638
- self.pos = newpos
639
- end
640
- end
641
-
642
- function Parser:get_eol()
643
- local starteol, endeol = find(self.subject, "[\r]?[\n]", self.pos)
644
- if not endeol then
645
- starteol, endeol = #self.subject, #self.subject
646
- end
647
- self.starteol = starteol
648
- self.endeol = endeol
649
- end
650
-
651
- function Parser:parse()
652
- local specs = self:specs()
653
- local para_spec = specs[1]
654
- local subjectlen = #self.subject
655
- while self.pos <= subjectlen do
656
-
657
- self.indent = 0
658
- self.startline = self.pos
659
- self.finished_line = false
660
- self:get_eol()
661
-
662
- -- check open containers for continuation
663
- self.last_matched_container = 0
664
- local idx = 0
665
- while idx < #self.containers do
666
- idx = idx + 1
667
- local container = self.containers[idx]
668
- -- skip any indentation
669
- self:skip_space()
670
- if container:continue() then
671
- self.last_matched_container = idx
672
- else
673
- break
674
- end
675
- end
676
-
677
- -- if we hit a close fence, we can move to next line
678
- if self.finished_line then
679
- while #self.containers > self.last_matched_container do
680
- self.containers[#self.containers]:close()
681
- end
682
- end
683
-
684
- if not self.finished_line then
685
- -- check for new containers
686
- self:skip_space()
687
- local is_blank = (self.pos == self.starteol)
688
-
689
- local new_starts = false
690
- local last_match = self.containers[self.last_matched_container]
691
- local check_starts = not is_blank and
692
- (not last_match or last_match.content == "block") and
693
- not self:find("^%a+%s") -- optimization
694
- while check_starts do
695
- check_starts = false
696
- for i=1,#specs do
697
- local spec = specs[i]
698
- if not spec.is_para then
699
- if spec:open() then
700
- self.last_matched_container = #self.containers
701
- if self.finished_line then
702
- check_starts = false
703
- else
704
- self:skip_space()
705
- new_starts = true
706
- check_starts = spec.content ~= "text"
707
- end
708
- break
709
- end
710
- end
711
- end
712
- end
713
-
714
- if not self.finished_line then
715
- -- handle remaining content
716
- self:skip_space()
717
-
718
- is_blank = (self.pos == self.starteol)
719
-
720
- local is_lazy = not is_blank and
721
- not new_starts and
722
- self.last_matched_container < #self.containers and
723
- self.containers[#self.containers].content == 'inline'
724
-
725
- if not is_lazy and
726
- self.last_matched_container < #self.containers then
727
- while #self.containers > self.last_matched_container do
728
- self.containers[#self.containers]:close()
729
- end
730
- end
731
-
732
- local tip = self.containers[#self.containers]
733
-
734
- -- add para by default if there's text
735
- if not tip or tip.content == 'block' then
736
- if is_blank then
737
- if not new_starts then
738
- -- need to track these for tight/loose lists
739
- self:add_match(self.pos, self.endeol, "blankline")
740
- end
741
- else
742
- para_spec:open()
743
- end
744
- tip = self.containers[#self.containers]
745
- end
746
-
747
- if tip then
748
- if tip.content == "text" then
749
- local startpos = self.pos
750
- if tip.indent and self.indent > tip.indent then
751
- -- get back the leading spaces we gobbled
752
- startpos = startpos - (self.indent - tip.indent)
753
- end
754
- self:add_match(startpos, self.endeol, "str")
755
- elseif tip.content == "inline" then
756
- if not is_blank then
757
- tip.inline_parser:feed(self.pos, self.endeol)
758
- end
759
- end
760
- end
761
- end
762
- end
763
-
764
- self.pos = self.endeol + 1
765
- end
766
- self:finish()
767
-
768
- end
769
-
770
- function Parser:finish()
771
- -- close unmatched containers
772
- while #self.containers > 0 do
773
- self.containers[#self.containers]:close()
774
- end
775
- end
776
-
777
- function Parser:get_matches()
778
- return self.matches
779
- end
780
-
781
- return { Parser = Parser,
782
- Container = Container }
783
-
784
-
785
- --[[
786
- Copyright (C) 2022 John MacFarlane
787
-
788
- Permission is hereby granted, free of charge, to any person obtaining
789
- a copy of this software and associated documentation files (the
790
- "Software"), to deal in the Software without restriction, including
791
- without limitation the rights to use, copy, modify, merge, publish,
792
- distribute, sublicense, and/or sell copies of the Software, and to
793
- permit persons to whom the Software is furnished to do so, subject to
794
- the following conditions:
795
-
796
- The above copyright notice and this permission notice shall be included
797
- in all copies or substantial portions of the Software.
798
-
799
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
800
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
801
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
802
- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
803
- CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
804
- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
805
- SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
806
-
807
- ]]