ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/aya/vendor/github.com/eknkc/amber/parser/parser.go
Revision: 1.1
Committed: Mon Sep 30 00:42:06 2024 UTC (6 weeks, 4 days ago) by yakumo_izuru
Branch: MAIN
CVS Tags: HEAD
Error occurred while calculating annotation data.
Log Message:
Mirrored from https://git.chaotic.ninja/git/yakumo_izuru/aya

File Contents

# Content
1 package parser
2
3 import (
4 "bytes"
5 "fmt"
6 "io"
7 "io/ioutil"
8 "net/http"
9 "path/filepath"
10 "strings"
11 )
12
13 type Parser struct {
14 scanner *scanner
15 filename string
16 fs http.FileSystem
17 currenttoken *token
18 namedBlocks map[string]*NamedBlock
19 parent *Parser
20 result *Block
21 }
22
23 func newParser(rdr io.Reader) *Parser {
24 p := new(Parser)
25 p.scanner = newScanner(rdr)
26 p.namedBlocks = make(map[string]*NamedBlock)
27 return p
28 }
29
30 func StringParser(input string) (*Parser, error) {
31 return newParser(bytes.NewReader([]byte(input))), nil
32 }
33
34 func ByteParser(input []byte) (*Parser, error) {
35 return newParser(bytes.NewReader(input)), nil
36 }
37
38 func (p *Parser) SetFilename(filename string) {
39 p.filename = filename
40 }
41
42 func (p *Parser) SetVirtualFilesystem(fs http.FileSystem) {
43 p.fs = fs
44 }
45
46 func FileParser(filename string) (*Parser, error) {
47 data, err := ioutil.ReadFile(filename)
48
49 if err != nil {
50 return nil, err
51 }
52
53 parser := newParser(bytes.NewReader(data))
54 parser.filename = filename
55 return parser, nil
56 }
57
58 func VirtualFileParser(filename string, fs http.FileSystem) (*Parser, error) {
59 file, err := fs.Open(filename)
60 if err != nil {
61 return nil, err
62 }
63
64 data, err := ioutil.ReadAll(file)
65 if err != nil {
66 return nil, err
67 }
68
69 parser := newParser(bytes.NewReader(data))
70 parser.filename = filename
71 parser.fs = fs
72 return parser, nil
73 }
74
75 func (p *Parser) Parse() *Block {
76 if p.result != nil {
77 return p.result
78 }
79
80 defer func() {
81 if r := recover(); r != nil {
82 if rs, ok := r.(string); ok && rs[:len("Amber Error")] == "Amber Error" {
83 panic(r)
84 }
85
86 pos := p.pos()
87
88 if len(pos.Filename) > 0 {
89 panic(fmt.Sprintf("Amber Error in <%s>: %v - Line: %d, Column: %d, Length: %d", pos.Filename, r, pos.LineNum, pos.ColNum, pos.TokenLength))
90 } else {
91 panic(fmt.Sprintf("Amber Error: %v - Line: %d, Column: %d, Length: %d", r, pos.LineNum, pos.ColNum, pos.TokenLength))
92 }
93 }
94 }()
95
96 block := newBlock()
97 p.advance()
98
99 for {
100 if p.currenttoken == nil || p.currenttoken.Kind == tokEOF {
101 break
102 }
103
104 if p.currenttoken.Kind == tokBlank {
105 p.advance()
106 continue
107 }
108
109 block.push(p.parse())
110 }
111
112 if p.parent != nil {
113 p.parent.Parse()
114
115 for _, prev := range p.parent.namedBlocks {
116 ours := p.namedBlocks[prev.Name]
117
118 if ours == nil {
119 // Put a copy of the named block into current context, so that sub-templates can use the block
120 p.namedBlocks[prev.Name] = prev
121 continue
122 }
123
124 top := findTopmostParentWithNamedBlock(p, prev.Name)
125 nb := top.namedBlocks[prev.Name]
126 switch ours.Modifier {
127 case NamedBlockAppend:
128 for i := 0; i < len(ours.Children); i++ {
129 nb.push(ours.Children[i])
130 }
131 case NamedBlockPrepend:
132 for i := len(ours.Children) - 1; i >= 0; i-- {
133 nb.pushFront(ours.Children[i])
134 }
135 default:
136 nb.Children = ours.Children
137 }
138 }
139
140 block = p.parent.result
141 }
142
143 p.result = block
144 return block
145 }
146
147 func (p *Parser) pos() SourcePosition {
148 pos := p.scanner.Pos()
149 pos.Filename = p.filename
150 return pos
151 }
152
153 func (p *Parser) parseRelativeFile(filename string) *Parser {
154 if len(p.filename) == 0 {
155 panic("Unable to import or extend " + filename + " in a non filesystem based parser.")
156 }
157
158 filename = filepath.Join(filepath.Dir(p.filename), filename)
159
160 if strings.IndexRune(filepath.Base(filename), '.') < 0 {
161 filename = filename + ".amber"
162 }
163
164 parser, err := FileParser(filename)
165 if err != nil && p.fs != nil {
166 parser, err = VirtualFileParser(filename, p.fs)
167 }
168 if err != nil {
169 panic("Unable to read " + filename + ", Error: " + string(err.Error()))
170 }
171
172 return parser
173 }
174
175 func (p *Parser) parse() Node {
176 switch p.currenttoken.Kind {
177 case tokDoctype:
178 return p.parseDoctype()
179 case tokComment:
180 return p.parseComment()
181 case tokText:
182 return p.parseText()
183 case tokIf:
184 return p.parseIf()
185 case tokEach:
186 return p.parseEach()
187 case tokImport:
188 return p.parseImport()
189 case tokTag:
190 return p.parseTag()
191 case tokAssignment:
192 return p.parseAssignment()
193 case tokNamedBlock:
194 return p.parseNamedBlock()
195 case tokExtends:
196 return p.parseExtends()
197 case tokIndent:
198 return p.parseBlock(nil)
199 case tokMixin:
200 return p.parseMixin()
201 case tokMixinCall:
202 return p.parseMixinCall()
203 }
204
205 panic(fmt.Sprintf("Unexpected token: %d", p.currenttoken.Kind))
206 }
207
208 func (p *Parser) expect(typ rune) *token {
209 if p.currenttoken.Kind != typ {
210 panic("Unexpected token!")
211 }
212 curtok := p.currenttoken
213 p.advance()
214 return curtok
215 }
216
217 func (p *Parser) advance() {
218 p.currenttoken = p.scanner.Next()
219 }
220
221 func (p *Parser) parseExtends() *Block {
222 if p.parent != nil {
223 panic("Unable to extend multiple parent templates.")
224 }
225
226 tok := p.expect(tokExtends)
227 parser := p.parseRelativeFile(tok.Value)
228 parser.Parse()
229 p.parent = parser
230 return newBlock()
231 }
232
233 func (p *Parser) parseBlock(parent Node) *Block {
234 p.expect(tokIndent)
235 block := newBlock()
236 block.SourcePosition = p.pos()
237
238 for {
239 if p.currenttoken == nil || p.currenttoken.Kind == tokEOF || p.currenttoken.Kind == tokOutdent {
240 break
241 }
242
243 if p.currenttoken.Kind == tokBlank {
244 p.advance()
245 continue
246 }
247
248 if p.currenttoken.Kind == tokId ||
249 p.currenttoken.Kind == tokClassName ||
250 p.currenttoken.Kind == tokAttribute {
251
252 if tag, ok := parent.(*Tag); ok {
253 attr := p.expect(p.currenttoken.Kind)
254 cond := attr.Data["Condition"]
255
256 switch attr.Kind {
257 case tokId:
258 tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "id", attr.Value, true, cond})
259 case tokClassName:
260 tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "class", attr.Value, true, cond})
261 case tokAttribute:
262 tag.Attributes = append(tag.Attributes, Attribute{p.pos(), attr.Value, attr.Data["Content"], attr.Data["Mode"] == "raw", cond})
263 }
264
265 continue
266 } else {
267 panic("Conditional attributes must be placed immediately within a parent tag.")
268 }
269 }
270
271 block.push(p.parse())
272 }
273
274 p.expect(tokOutdent)
275
276 return block
277 }
278
279 func (p *Parser) parseIf() *Condition {
280 tok := p.expect(tokIf)
281 cnd := newCondition(tok.Value)
282 cnd.SourcePosition = p.pos()
283
284 readmore:
285 switch p.currenttoken.Kind {
286 case tokIndent:
287 cnd.Positive = p.parseBlock(cnd)
288 goto readmore
289 case tokElse:
290 p.expect(tokElse)
291 if p.currenttoken.Kind == tokIf {
292 cnd.Negative = newBlock()
293 cnd.Negative.push(p.parseIf())
294 } else if p.currenttoken.Kind == tokIndent {
295 cnd.Negative = p.parseBlock(cnd)
296 } else {
297 panic("Unexpected token!")
298 }
299 goto readmore
300 }
301
302 return cnd
303 }
304
305 func (p *Parser) parseEach() *Each {
306 tok := p.expect(tokEach)
307 ech := newEach(tok.Value)
308 ech.SourcePosition = p.pos()
309 ech.X = tok.Data["X"]
310 ech.Y = tok.Data["Y"]
311
312 if p.currenttoken.Kind == tokIndent {
313 ech.Block = p.parseBlock(ech)
314 }
315
316 return ech
317 }
318
319 func (p *Parser) parseImport() *Block {
320 tok := p.expect(tokImport)
321 node := p.parseRelativeFile(tok.Value).Parse()
322 node.SourcePosition = p.pos()
323 return node
324 }
325
326 func (p *Parser) parseNamedBlock() *Block {
327 tok := p.expect(tokNamedBlock)
328
329 if p.namedBlocks[tok.Value] != nil {
330 panic("Multiple definitions of named blocks are not permitted. Block " + tok.Value + " has been re defined.")
331 }
332
333 block := newNamedBlock(tok.Value)
334 block.SourcePosition = p.pos()
335
336 if tok.Data["Modifier"] == "append" {
337 block.Modifier = NamedBlockAppend
338 } else if tok.Data["Modifier"] == "prepend" {
339 block.Modifier = NamedBlockPrepend
340 }
341
342 if p.currenttoken.Kind == tokIndent {
343 block.Block = *(p.parseBlock(nil))
344 }
345
346 p.namedBlocks[block.Name] = block
347
348 if block.Modifier == NamedBlockDefault {
349 return &block.Block
350 }
351
352 return newBlock()
353 }
354
355 func (p *Parser) parseDoctype() *Doctype {
356 tok := p.expect(tokDoctype)
357 node := newDoctype(tok.Value)
358 node.SourcePosition = p.pos()
359 return node
360 }
361
362 func (p *Parser) parseComment() *Comment {
363 tok := p.expect(tokComment)
364 cmnt := newComment(tok.Value)
365 cmnt.SourcePosition = p.pos()
366 cmnt.Silent = tok.Data["Mode"] == "silent"
367
368 if p.currenttoken.Kind == tokIndent {
369 cmnt.Block = p.parseBlock(cmnt)
370 }
371
372 return cmnt
373 }
374
375 func (p *Parser) parseText() *Text {
376 tok := p.expect(tokText)
377 node := newText(tok.Value, tok.Data["Mode"] == "raw")
378 node.SourcePosition = p.pos()
379 return node
380 }
381
382 func (p *Parser) parseAssignment() *Assignment {
383 tok := p.expect(tokAssignment)
384 node := newAssignment(tok.Data["X"], tok.Value)
385 node.SourcePosition = p.pos()
386 return node
387 }
388
389 func (p *Parser) parseTag() *Tag {
390 tok := p.expect(tokTag)
391 tag := newTag(tok.Value)
392 tag.SourcePosition = p.pos()
393
394 ensureBlock := func() {
395 if tag.Block == nil {
396 tag.Block = newBlock()
397 }
398 }
399
400 readmore:
401 switch p.currenttoken.Kind {
402 case tokIndent:
403 if tag.IsRawText() {
404 p.scanner.readRaw = true
405 }
406
407 block := p.parseBlock(tag)
408 if tag.Block == nil {
409 tag.Block = block
410 } else {
411 for _, c := range block.Children {
412 tag.Block.push(c)
413 }
414 }
415 case tokId:
416 id := p.expect(tokId)
417 if len(id.Data["Condition"]) > 0 {
418 panic("Conditional attributes must be placed in a block within a tag.")
419 }
420 tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "id", id.Value, true, ""})
421 goto readmore
422 case tokClassName:
423 cls := p.expect(tokClassName)
424 if len(cls.Data["Condition"]) > 0 {
425 panic("Conditional attributes must be placed in a block within a tag.")
426 }
427 tag.Attributes = append(tag.Attributes, Attribute{p.pos(), "class", cls.Value, true, ""})
428 goto readmore
429 case tokAttribute:
430 attr := p.expect(tokAttribute)
431 if len(attr.Data["Condition"]) > 0 {
432 panic("Conditional attributes must be placed in a block within a tag.")
433 }
434 tag.Attributes = append(tag.Attributes, Attribute{p.pos(), attr.Value, attr.Data["Content"], attr.Data["Mode"] == "raw", ""})
435 goto readmore
436 case tokText:
437 if p.currenttoken.Data["Mode"] != "piped" {
438 ensureBlock()
439 tag.Block.pushFront(p.parseText())
440 goto readmore
441 }
442 }
443
444 return tag
445 }
446
447 func (p *Parser) parseMixin() *Mixin {
448 tok := p.expect(tokMixin)
449 mixin := newMixin(tok.Value, tok.Data["Args"])
450 mixin.SourcePosition = p.pos()
451
452 if p.currenttoken.Kind == tokIndent {
453 mixin.Block = p.parseBlock(mixin)
454 }
455
456 return mixin
457 }
458
459 func (p *Parser) parseMixinCall() *MixinCall {
460 tok := p.expect(tokMixinCall)
461 mixinCall := newMixinCall(tok.Value, tok.Data["Args"])
462 mixinCall.SourcePosition = p.pos()
463 return mixinCall
464 }
465
466 func findTopmostParentWithNamedBlock(p *Parser, name string) *Parser {
467 top := p
468
469 for {
470 if top.namedBlocks[name] == nil {
471 return nil
472 }
473 if top.parent == nil {
474 return top
475 }
476 if top.parent.namedBlocks[name] != nil {
477 top = top.parent
478 } else {
479 return top
480 }
481 }
482 }