-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathif_tag.go
125 lines (110 loc) · 2.57 KB
/
if_tag.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package salix
import "go.elara.ws/salix/ast"
// ifTag represents a #if tag within a Salix template
type ifTag struct{}
func (it ifTag) Run(tc *TagContext, block, args []ast.Node) error {
if len(args) != 1 {
return tc.PosError(tc.Tag, "expected one argument, got %d", len(args))
}
inner, err := it.findInner(tc, block)
if err != nil {
return err
}
val, err := tc.GetValue(args[0], nil)
if err != nil {
return err
}
cond, ok := val.(bool)
if !ok {
return tc.PosError(args[0], "expected boolean argument, got %T", val)
}
if cond {
return tc.Execute(block[:inner.endRoot], nil)
} else {
if len(inner.elifTags) > 0 {
for i, elifTag := range inner.elifTags {
val, err := tc.GetValue(elifTag.value, nil)
if err != nil {
return err
}
cond, ok := val.(bool)
if !ok {
return tc.PosError(elifTag.value, "expected boolean argument, got %T", val)
}
nextIndex := len(block)
if i < len(inner.elifTags)-1 {
nextIndex = inner.elifTags[i+1].index
} else if inner.elseIndex != 0 {
nextIndex = inner.elseIndex
}
if cond {
return tc.Execute(block[elifTag.index+1:nextIndex], nil)
}
}
}
if inner.elseIndex != 0 {
return tc.Execute(block[inner.elseIndex+1:], nil)
}
}
return nil
}
type innerTags struct {
endRoot int
elifTags []elif
elseIndex int
}
type elif struct {
index int
value ast.Node
}
// findInner finds the inner elif and else tags in a block
// passed to the if tag.
func (it ifTag) findInner(tc *TagContext, block []ast.Node) (innerTags, error) {
// Depth keeps track of nested if statements. We only want to look for
// else/elif tags within the current if tag, not any nested ones.
depth := 0
var out innerTags
for i, node := range block {
switch tag := node.(type) {
case ast.Tag:
switch tag.Name.Value {
case "if":
depth++
case "elif":
if depth != 0 {
continue
}
if out.endRoot == 0 {
out.endRoot = i
}
if len(tag.Params) > 1 {
return innerTags{}, tc.PosError(tag.Params[1], "expected one argument, got %d", len(tag.Params))
}
out.elifTags = append(out.elifTags, elif{
index: i,
value: tag.Params[0],
})
case "else":
if depth != 0 {
continue
}
if out.elseIndex != 0 {
return innerTags{}, tc.PosError(tag, "cannot have more than one else tag in an if tag")
}
if out.endRoot == 0 {
out.endRoot = i
}
out.elseIndex = i
break
}
case ast.EndTag:
if tag.Name.Value == "if" {
depth--
}
}
}
if out.endRoot == 0 {
out.endRoot = len(block)
}
return out, nil
}