bbcode - tokeniser.go
1 package bbcode
2
3 import (
4 "vimagination.zapto.org/parser"
5 )
6
7 const (
8 tokenText parser.TokenType = iota
9 tokenOpenTag
10 tokenTagAttribute
11 tokenCloseTag
12 )
13
14 const (
15 phraseText parser.PhraseType = iota
16 phraseOpen
17 phraseClose
18 )
19
20 type tokeniser struct {
21 openTag, closeTag, closingTag, validTagName, attributeSep string
22 closeTagRune, closingTagRune, attributeSepRune rune
23 }
24
25 func getTokeniser(c Config) tokeniser {
26 return tokeniser{
27 openTag: string(c.TagOpen),
28 closeTag: string(c.TagClose),
29 closingTag: string(c.ClosingTag),
30 validTagName: c.ValidTagName,
31 attributeSep: string(c.AttributeSep),
32 closeTagRune: c.TagClose,
33 closingTagRune: c.ClosingTag,
34 attributeSepRune: c.AttributeSep,
35 }
36 }
37
38 func (tks *tokeniser) getParser(t parser.Tokeniser) parser.Parser {
39 p := parser.New(t)
40 p.TokeniserState(tks.text)
41 p.PhraserState(tks.phraser)
42 return p
43 }
44
45 func (tks *tokeniser) text(t *parser.Tokeniser) (parser.Token, parser.TokenFunc) {
46 t.ExceptRun(tks.openTag)
47 tk := parser.Token{
48 Type: tokenText,
49 Data: t.Get(),
50 }
51 if t.Peek() == -1 {
52 if tk.Data == "" {
53 return t.Done()
54 }
55 return tk, (*parser.Tokeniser).Done
56 }
57 if tk.Data == "" {
58 return tks.opening(t)
59 }
60 return tk, tks.opening
61 }
62
63 func (tks *tokeniser) opening(t *parser.Tokeniser) (parser.Token, parser.TokenFunc) {
64 t.Accept(tks.openTag)
65 if t.Peek() == tks.closingTagRune {
66 return tks.closing(t)
67 }
68 if !t.Accept(tks.validTagName) {
69 return tks.text(t)
70 }
71 t.AcceptRun(tks.validTagName)
72 var (
73 next parser.TokenFunc = tks.text
74 data string
75 )
76 switch t.Peek() {
77 case tks.closeTagRune:
78 data = t.Get()
79 t.Accept(tks.closeTag)
80 t.Get()
81 case tks.attributeSepRune:
82 data = t.Get()
83 t.Accept(tks.attributeSep)
84 if t.ExceptRun(tks.closeTag) != tks.closeTagRune {
85 return parser.Token{
86 Type: tokenText,
87 Data: data,
88 }, tks.text
89 }
90 next = tks.attribute
91 default:
92 return tks.text(t)
93 }
94 data = data[1:]
95 return parser.Token{
96 Type: tokenOpenTag,
97 Data: data,
98 }, next
99 }
100
101 func (tks *tokeniser) closing(t *parser.Tokeniser) (parser.Token, parser.TokenFunc) {
102 t.Accept(tks.closingTag)
103 if !t.Accept(tks.validTagName) {
104 return tks.text(t)
105 }
106 t.AcceptRun(tks.validTagName)
107 if t.Peek() == tks.closeTagRune {
108 data := t.Get()
109 t.Accept(tks.closeTag)
110 t.Get()
111 return parser.Token{
112 Type: tokenCloseTag,
113 Data: data[2:],
114 }, tks.text
115 }
116 return tks.text(t)
117 }
118
119 func (tks *tokeniser) attribute(t *parser.Tokeniser) (parser.Token, parser.TokenFunc) {
120 data := t.Get()
121 t.Accept(tks.closeTag)
122 t.Get()
123 return parser.Token{
124 Type: tokenTagAttribute,
125 Data: data[1:],
126 }, tks.text
127 }
128
129 func (tks *tokeniser) phraser(p *parser.Parser) (parser.Phrase, parser.PhraseFunc) {
130 var phraseType parser.PhraseType
131 if p.Accept(tokenText) {
132 p.AcceptRun(tokenText)
133 phraseType = phraseText
134 } else if p.Accept(tokenOpenTag) {
135 p.Accept(tokenTagAttribute)
136 phraseType = phraseOpen
137 } else if p.Accept(tokenCloseTag) {
138 phraseType = phraseClose
139 } else if p.Accept(parser.TokenDone) {
140 return p.Done()
141 } else if p.Accept(parser.TokenError) {
142 return p.Error()
143 }
144 return parser.Phrase{
145 Type: phraseType,
146 Data: p.Get(),
147 }, tks.phraser
148 }
149