1package parser
2
3import (
4 "bytes"
5
6 "github.com/gomarkdown/markdown/ast"
7)
8
9// citation parses a citation. In its most simple form [@ref], we allow multiple
10// being separated by semicolons and a sub reference inside ala pandoc: [@ref, p. 23].
11// Each citation can have a modifier: !, ? or - wich mean:
12//
13// ! - normative
14// ? - formative
15// - - suppressed
16//
17// The suffix starts after a comma, we strip any whitespace before and after. If the output
18// allows for it, this can be rendered.
19func citation(p *Parser, data []byte, offset int) (int, ast.Node) {
20 // look for the matching closing bracket
21 i := offset + 1
22 for level := 1; level > 0 && i < len(data); i++ {
23 switch {
24 case data[i] == '\n':
25 // no newlines allowed.
26 return 0, nil
27
28 case data[i-1] == '\\':
29 continue
30
31 case data[i] == '[':
32 level++
33
34 case data[i] == ']':
35 level--
36 if level <= 0 {
37 i-- // compensate for extra i++ in for loop
38 }
39 }
40 }
41
42 if i >= len(data) {
43 return 0, nil
44 }
45
46 node := &ast.Citation{}
47
48 citations := bytes.Split(data[1:i], []byte(";"))
49 for _, citation := range citations {
50 var suffix []byte
51 citation = bytes.TrimSpace(citation)
52 j := 0
53 if citation[j] != '@' {
54 // not a citation, drop out entirely.
55 return 0, nil
56 }
57 if c := bytes.Index(citation, []byte(",")); c > 0 {
58 part := citation[:c]
59 suff := citation[c+1:]
60 part = bytes.TrimSpace(part)
61 suff = bytes.TrimSpace(suff)
62
63 citation = part
64 suffix = suff
65 }
66
67 citeType := ast.CitationTypeInformative
68 j = 1
69 switch citation[j] {
70 case '!':
71 citeType = ast.CitationTypeNormative
72 j++
73 case '?':
74 citeType = ast.CitationTypeInformative
75 j++
76 case '-':
77 citeType = ast.CitationTypeSuppressed
78 j++
79 }
80 node.Destination = append(node.Destination, citation[j:])
81 node.Type = append(node.Type, citeType)
82 node.Suffix = append(node.Suffix, suffix)
83 }
84
85 return i + 1, node
86}