1 package statements
2
3 import (
4 "fmt"
5
6 "github.com/pkg/errors"
7
8 "github.com/noirbizarre/gonja/exec"
9 "github.com/noirbizarre/gonja/nodes"
10 "github.com/noirbizarre/gonja/parser"
11 "github.com/noirbizarre/gonja/tokens"
12 )
13
14 type ImportStmt struct {
15 Location *tokens.Token
16 Filename string
17 FilenameExpr nodes.Expression
18 As string
19 WithContext bool
20 Template *nodes.Template
21 }
22
23 func (stmt *ImportStmt) Position() *tokens.Token { return stmt.Location }
24 func (stmt *ImportStmt) String() string {
25 t := stmt.Position()
26 return fmt.Sprintf("ImportStmt(Line=%d Col=%d)", t.Line, t.Col)
27 }
28 func (stmt *ImportStmt) Execute(r *exec.Renderer, tag *nodes.StatementBlock) error {
29 var imported map[string]*nodes.Macro
30 macros := map[string]exec.Macro{}
31
32 if stmt.FilenameExpr != nil {
33 filenameValue := r.Eval(stmt.FilenameExpr)
34 if filenameValue.IsError() {
35 return errors.Wrap(filenameValue, `Unable to evaluate filename`)
36 }
37
38 filename := filenameValue.String()
39 tpl, err := r.Loader.GetTemplate(filename)
40 if err != nil {
41 return errors.Wrapf(err, `Unable to load template '%s'`, filename)
42 }
43 imported = tpl.Root.Macros
44
45 } else {
46 imported = stmt.Template.Macros
47 }
48
49 for name, macro := range imported {
50 fn, err := exec.MacroNodeToFunc(macro, r)
51 if err != nil {
52 return errors.Wrapf(err, `Unable to import macro '%s'`, name)
53 }
54 macros[name] = fn
55 }
56
57 r.Ctx.Set(stmt.As, macros)
58 return nil
59 }
60
61 type FromImportStmt struct {
62 Location *tokens.Token
63 Filename string
64 FilenameExpr nodes.Expression
65 WithContext bool
66 Template *nodes.Template
67 As map[string]string
68 Macros map[string]*nodes.Macro
69 }
70
71 func (stmt *FromImportStmt) Position() *tokens.Token { return stmt.Location }
72 func (stmt *FromImportStmt) String() string {
73 t := stmt.Position()
74 return fmt.Sprintf("FromImportStmt(Line=%d Col=%d)", t.Line, t.Col)
75 }
76 func (stmt *FromImportStmt) Execute(r *exec.Renderer, tag *nodes.StatementBlock) error {
77 var imported map[string]*nodes.Macro
78
79 if stmt.FilenameExpr != nil {
80 filenameValue := r.Eval(stmt.FilenameExpr)
81 if filenameValue.IsError() {
82 return errors.Wrap(filenameValue, `Unable to evaluate filename`)
83 }
84
85 filename := filenameValue.String()
86 tpl, err := r.Loader.GetTemplate(filename)
87 if err != nil {
88 return errors.Wrapf(err, `Unable to load template '%s'`, filename)
89 }
90 imported = tpl.Root.Macros
91
92 } else {
93 imported = stmt.Template.Macros
94 }
95
96 for alias, name := range stmt.As {
97 node := imported[name]
98 fn, err := exec.MacroNodeToFunc(node, r)
99 if err != nil {
100 return errors.Wrapf(err, `Unable to import macro '%s'`, name)
101 }
102 r.Ctx.Set(alias, fn)
103 }
104 return nil
105 }
106
107 func importParser(p *parser.Parser, args *parser.Parser) (nodes.Statement, error) {
108 stmt := &ImportStmt{
109 Location: p.Current(),
110
111 }
112
113 if args.End() {
114 return nil, args.Error("You must at least specify one macro to import.", nil)
115 }
116
117 if tok := args.Match(tokens.String); tok != nil {
118 stmt.Filename = tok.Val
119 } else {
120 expr, err := args.ParseExpression()
121 if err != nil {
122 return nil, err
123 }
124 stmt.FilenameExpr = expr
125 }
126 if args.MatchName("as") == nil {
127 return nil, args.Error(`Expected "as" keyword`, args.Current())
128 }
129
130 alias := args.Match(tokens.Name)
131 if alias == nil {
132 return nil, args.Error("Expected macro alias name (identifier)", args.Current())
133 }
134 stmt.As = alias.Val
135
136 if tok := args.MatchName("with", "without"); tok != nil {
137 if args.MatchName("context") != nil {
138 stmt.WithContext = tok.Val == "with"
139 } else {
140 args.Stream.Backup()
141 }
142 }
143
144
145 if stmt.Filename != "" {
146 tpl, err := p.TemplateParser(stmt.Filename)
147 if err != nil {
148 return nil, errors.Wrapf(err, `Unable to parse imported template '%s'`, stmt.Filename)
149 } else {
150 stmt.Template = tpl
151 }
152 }
153
154 return stmt, nil
155 }
156
157 func fromParser(p *parser.Parser, args *parser.Parser) (nodes.Statement, error) {
158 stmt := &FromImportStmt{
159 Location: p.Current(),
160 As: map[string]string{},
161
162 }
163
164 if args.End() {
165 return nil, args.Error("You must at least specify one macro to import.", nil)
166 }
167
168 if tok := args.Match(tokens.String); tok != nil {
169 stmt.Filename = tok.Val
170 } else {
171 filename, err := args.ParseExpression()
172 if err != nil {
173 return nil, err
174 }
175 stmt.FilenameExpr = filename
176 }
177
178 if args.MatchName("import") == nil {
179 return nil, args.Error("Expected import keyword", args.Current())
180 }
181
182 for !args.End() {
183 name := args.Match(tokens.Name)
184 if name == nil {
185 return nil, args.Error("Expected macro name (identifier).", args.Current())
186 }
187
188
189 if args.MatchName("as") != nil {
190 alias := args.Match(tokens.Name)
191 if alias == nil {
192 return nil, args.Error("Expected macro alias name (identifier).", nil)
193 }
194
195 stmt.As[alias.Val] = name.Val
196 } else {
197 stmt.As[name.Val] = name.Val
198 }
199
200
201
202
203
204
205
206
207 if tok := args.MatchName("with", "without"); tok != nil {
208 if args.MatchName("context") != nil {
209 stmt.WithContext = tok.Val == "with"
210 break
211 } else {
212 args.Stream.Backup()
213 }
214 }
215
216 if args.End() {
217 break
218 }
219
220 if args.Match(tokens.Comma) == nil {
221 return nil, args.Error("Expected ','.", nil)
222 }
223 }
224
225
226 if stmt.Filename != "" {
227 tpl, err := p.TemplateParser(stmt.Filename)
228 if err != nil {
229 return nil, errors.Wrapf(err, `Unable to parse imported template '%s'`, stmt.Filename)
230 } else {
231 stmt.Template = tpl
232 }
233 }
234
235 return stmt, nil
236 }
237
238 func init() {
239 All.Register("import", importParser)
240 All.Register("from", fromParser)
241 }
242
View as plain text