]>
Commit | Line | Data |
---|---|---|
adb0401d ILT |
1 | // Copyright 2011 The Go Authors. All rights reserved. |
2 | // Use of this source code is governed by a BSD-style | |
3 | // license that can be found in the LICENSE file. | |
4 | ||
5 | // Parse nodes. | |
6 | ||
7 | package parse | |
8 | ||
9 | import ( | |
adb0401d | 10 | "fmt" |
adb0401d ILT |
11 | "strconv" |
12 | "strings" | |
13 | ) | |
14 | ||
f038dae6 ILT |
15 | var textFormat = "%s" // Changed to "%q" in tests for better error messages. |
16 | ||
4ccad563 ILT |
17 | // A Node is an element in the parse tree. The interface is trivial. |
18 | // The interface contains an unexported method so that only | |
19 | // types local to this package can satisfy it. | |
adb0401d ILT |
20 | type Node interface { |
21 | Type() NodeType | |
22 | String() string | |
cbb6491d ILT |
23 | // Copy does a deep copy of the Node and all its components. |
24 | // To avoid type assertions, some XxxNodes also have specialized | |
25 | // CopyXxx methods that return *XxxNode. | |
26 | Copy() Node | |
4ccad563 | 27 | Position() Pos // byte position of start of node in full original input string |
f8d9fa9e ILT |
28 | // tree returns the containing *Tree. |
29 | // It is unexported so all implementations of Node are in this package. | |
30 | tree() *Tree | |
5a8ea165 ILT |
31 | // writeTo writes the String output to the builder. |
32 | writeTo(*strings.Builder) | |
adb0401d ILT |
33 | } |
34 | ||
35 | // NodeType identifies the type of a parse tree node. | |
36 | type NodeType int | |
37 | ||
4ccad563 ILT |
38 | // Pos represents a byte position in the original input text from which |
39 | // this template was parsed. | |
40 | type Pos int | |
41 | ||
42 | func (p Pos) Position() Pos { | |
43 | return p | |
44 | } | |
45 | ||
adb0401d ILT |
46 | // Type returns itself and provides an easy default implementation |
47 | // for embedding in a Node. Embedded in all non-trivial Nodes. | |
48 | func (t NodeType) Type() NodeType { | |
49 | return t | |
50 | } | |
51 | ||
52 | const ( | |
53 | NodeText NodeType = iota // Plain text. | |
4ccad563 | 54 | NodeAction // A non-control action such as a field evaluation. |
adb0401d | 55 | NodeBool // A boolean constant. |
4ccad563 | 56 | NodeChain // A sequence of field accesses. |
adb0401d ILT |
57 | NodeCommand // An element of a pipeline. |
58 | NodeDot // The cursor, dot. | |
59 | nodeElse // An else action. Not added to tree. | |
60 | nodeEnd // An end action. Not added to tree. | |
61 | NodeField // A field or method name. | |
62 | NodeIdentifier // An identifier; always a function name. | |
63 | NodeIf // An if action. | |
64 | NodeList // A list of Nodes. | |
4ccad563 | 65 | NodeNil // An untyped nil constant. |
adb0401d ILT |
66 | NodeNumber // A numerical constant. |
67 | NodePipe // A pipeline of commands. | |
68 | NodeRange // A range action. | |
69 | NodeString // A string constant. | |
70 | NodeTemplate // A template invocation action. | |
71 | NodeVariable // A $ variable. | |
72 | NodeWith // A with action. | |
73 | ) | |
74 | ||
75 | // Nodes. | |
76 | ||
77 | // ListNode holds a sequence of nodes. | |
78 | type ListNode struct { | |
79 | NodeType | |
4ccad563 | 80 | Pos |
f8d9fa9e | 81 | tr *Tree |
adb0401d ILT |
82 | Nodes []Node // The element nodes in lexical order. |
83 | } | |
84 | ||
f8d9fa9e ILT |
85 | func (t *Tree) newList(pos Pos) *ListNode { |
86 | return &ListNode{tr: t, NodeType: NodeList, Pos: pos} | |
adb0401d ILT |
87 | } |
88 | ||
89 | func (l *ListNode) append(n Node) { | |
90 | l.Nodes = append(l.Nodes, n) | |
91 | } | |
92 | ||
f8d9fa9e ILT |
93 | func (l *ListNode) tree() *Tree { |
94 | return l.tr | |
95 | } | |
96 | ||
adb0401d | 97 | func (l *ListNode) String() string { |
5a8ea165 ILT |
98 | var sb strings.Builder |
99 | l.writeTo(&sb) | |
100 | return sb.String() | |
101 | } | |
102 | ||
103 | func (l *ListNode) writeTo(sb *strings.Builder) { | |
adb0401d | 104 | for _, n := range l.Nodes { |
5a8ea165 | 105 | n.writeTo(sb) |
adb0401d | 106 | } |
adb0401d ILT |
107 | } |
108 | ||
cbb6491d ILT |
109 | func (l *ListNode) CopyList() *ListNode { |
110 | if l == nil { | |
111 | return l | |
112 | } | |
f8d9fa9e | 113 | n := l.tr.newList(l.Pos) |
cbb6491d ILT |
114 | for _, elem := range l.Nodes { |
115 | n.append(elem.Copy()) | |
116 | } | |
117 | return n | |
118 | } | |
119 | ||
120 | func (l *ListNode) Copy() Node { | |
121 | return l.CopyList() | |
122 | } | |
123 | ||
adb0401d ILT |
124 | // TextNode holds plain text. |
125 | type TextNode struct { | |
126 | NodeType | |
4ccad563 | 127 | Pos |
f8d9fa9e | 128 | tr *Tree |
adb0401d ILT |
129 | Text []byte // The text; may span newlines. |
130 | } | |
131 | ||
f8d9fa9e ILT |
132 | func (t *Tree) newText(pos Pos, text string) *TextNode { |
133 | return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)} | |
adb0401d ILT |
134 | } |
135 | ||
136 | func (t *TextNode) String() string { | |
f038dae6 | 137 | return fmt.Sprintf(textFormat, t.Text) |
adb0401d ILT |
138 | } |
139 | ||
5a8ea165 ILT |
140 | func (t *TextNode) writeTo(sb *strings.Builder) { |
141 | sb.WriteString(t.String()) | |
142 | } | |
143 | ||
f8d9fa9e ILT |
144 | func (t *TextNode) tree() *Tree { |
145 | return t.tr | |
146 | } | |
147 | ||
cbb6491d | 148 | func (t *TextNode) Copy() Node { |
f8d9fa9e | 149 | return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)} |
cbb6491d ILT |
150 | } |
151 | ||
adb0401d ILT |
152 | // PipeNode holds a pipeline with optional declaration |
153 | type PipeNode struct { | |
154 | NodeType | |
4ccad563 | 155 | Pos |
dd931d9b ILT |
156 | tr *Tree |
157 | Line int // The line number in the input. Deprecated: Kept for compatibility. | |
158 | IsAssign bool // The variables are being assigned, not declared. | |
159 | Decl []*VariableNode // Variables in lexical order. | |
160 | Cmds []*CommandNode // The commands in lexical order. | |
adb0401d ILT |
161 | } |
162 | ||
dd931d9b ILT |
163 | func (t *Tree) newPipeline(pos Pos, line int, vars []*VariableNode) *PipeNode { |
164 | return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: vars} | |
adb0401d ILT |
165 | } |
166 | ||
167 | func (p *PipeNode) append(command *CommandNode) { | |
168 | p.Cmds = append(p.Cmds, command) | |
169 | } | |
170 | ||
171 | func (p *PipeNode) String() string { | |
5a8ea165 ILT |
172 | var sb strings.Builder |
173 | p.writeTo(&sb) | |
174 | return sb.String() | |
175 | } | |
176 | ||
177 | func (p *PipeNode) writeTo(sb *strings.Builder) { | |
af92e385 ILT |
178 | if len(p.Decl) > 0 { |
179 | for i, v := range p.Decl { | |
180 | if i > 0 { | |
5a8ea165 | 181 | sb.WriteString(", ") |
af92e385 | 182 | } |
5a8ea165 | 183 | v.writeTo(sb) |
af92e385 | 184 | } |
5a8ea165 | 185 | sb.WriteString(" := ") |
af92e385 ILT |
186 | } |
187 | for i, c := range p.Cmds { | |
188 | if i > 0 { | |
5a8ea165 | 189 | sb.WriteString(" | ") |
af92e385 | 190 | } |
5a8ea165 | 191 | c.writeTo(sb) |
adb0401d | 192 | } |
adb0401d ILT |
193 | } |
194 | ||
f8d9fa9e ILT |
195 | func (p *PipeNode) tree() *Tree { |
196 | return p.tr | |
197 | } | |
198 | ||
cbb6491d ILT |
199 | func (p *PipeNode) CopyPipe() *PipeNode { |
200 | if p == nil { | |
201 | return p | |
202 | } | |
5a8ea165 ILT |
203 | vars := make([]*VariableNode, len(p.Decl)) |
204 | for i, d := range p.Decl { | |
205 | vars[i] = d.Copy().(*VariableNode) | |
cbb6491d | 206 | } |
dd931d9b ILT |
207 | n := p.tr.newPipeline(p.Pos, p.Line, vars) |
208 | n.IsAssign = p.IsAssign | |
cbb6491d ILT |
209 | for _, c := range p.Cmds { |
210 | n.append(c.Copy().(*CommandNode)) | |
211 | } | |
212 | return n | |
213 | } | |
214 | ||
215 | func (p *PipeNode) Copy() Node { | |
216 | return p.CopyPipe() | |
217 | } | |
218 | ||
adb0401d ILT |
219 | // ActionNode holds an action (something bounded by delimiters). |
220 | // Control actions have their own nodes; ActionNode represents simple | |
4ccad563 | 221 | // ones such as field evaluations and parenthesized pipelines. |
adb0401d ILT |
222 | type ActionNode struct { |
223 | NodeType | |
4ccad563 | 224 | Pos |
f8d9fa9e | 225 | tr *Tree |
af146490 | 226 | Line int // The line number in the input. Deprecated: Kept for compatibility. |
adb0401d ILT |
227 | Pipe *PipeNode // The pipeline in the action. |
228 | } | |
229 | ||
f8d9fa9e ILT |
230 | func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode { |
231 | return &ActionNode{tr: t, NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe} | |
adb0401d ILT |
232 | } |
233 | ||
234 | func (a *ActionNode) String() string { | |
5a8ea165 ILT |
235 | var sb strings.Builder |
236 | a.writeTo(&sb) | |
237 | return sb.String() | |
238 | } | |
af92e385 | 239 | |
5a8ea165 ILT |
240 | func (a *ActionNode) writeTo(sb *strings.Builder) { |
241 | sb.WriteString("{{") | |
242 | a.Pipe.writeTo(sb) | |
243 | sb.WriteString("}}") | |
adb0401d ILT |
244 | } |
245 | ||
f8d9fa9e ILT |
246 | func (a *ActionNode) tree() *Tree { |
247 | return a.tr | |
248 | } | |
249 | ||
cbb6491d | 250 | func (a *ActionNode) Copy() Node { |
f8d9fa9e | 251 | return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe()) |
cbb6491d ILT |
252 | |
253 | } | |
254 | ||
adb0401d ILT |
255 | // CommandNode holds a command (a pipeline inside an evaluating action). |
256 | type CommandNode struct { | |
257 | NodeType | |
4ccad563 | 258 | Pos |
f8d9fa9e | 259 | tr *Tree |
adb0401d ILT |
260 | Args []Node // Arguments in lexical order: Identifier, field, or constant. |
261 | } | |
262 | ||
f8d9fa9e ILT |
263 | func (t *Tree) newCommand(pos Pos) *CommandNode { |
264 | return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos} | |
adb0401d ILT |
265 | } |
266 | ||
267 | func (c *CommandNode) append(arg Node) { | |
268 | c.Args = append(c.Args, arg) | |
269 | } | |
270 | ||
271 | func (c *CommandNode) String() string { | |
5a8ea165 ILT |
272 | var sb strings.Builder |
273 | c.writeTo(&sb) | |
274 | return sb.String() | |
275 | } | |
276 | ||
277 | func (c *CommandNode) writeTo(sb *strings.Builder) { | |
af92e385 ILT |
278 | for i, arg := range c.Args { |
279 | if i > 0 { | |
5a8ea165 | 280 | sb.WriteByte(' ') |
af92e385 | 281 | } |
4ccad563 | 282 | if arg, ok := arg.(*PipeNode); ok { |
5a8ea165 ILT |
283 | sb.WriteByte('(') |
284 | arg.writeTo(sb) | |
285 | sb.WriteByte(')') | |
4ccad563 ILT |
286 | continue |
287 | } | |
5a8ea165 | 288 | arg.writeTo(sb) |
af92e385 | 289 | } |
adb0401d ILT |
290 | } |
291 | ||
f8d9fa9e ILT |
292 | func (c *CommandNode) tree() *Tree { |
293 | return c.tr | |
294 | } | |
295 | ||
cbb6491d ILT |
296 | func (c *CommandNode) Copy() Node { |
297 | if c == nil { | |
298 | return c | |
299 | } | |
f8d9fa9e | 300 | n := c.tr.newCommand(c.Pos) |
cbb6491d ILT |
301 | for _, c := range c.Args { |
302 | n.append(c.Copy()) | |
303 | } | |
304 | return n | |
305 | } | |
306 | ||
adb0401d ILT |
307 | // IdentifierNode holds an identifier. |
308 | type IdentifierNode struct { | |
309 | NodeType | |
4ccad563 | 310 | Pos |
f8d9fa9e | 311 | tr *Tree |
adb0401d ILT |
312 | Ident string // The identifier's name. |
313 | } | |
314 | ||
315 | // NewIdentifier returns a new IdentifierNode with the given identifier name. | |
316 | func NewIdentifier(ident string) *IdentifierNode { | |
317 | return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident} | |
318 | } | |
319 | ||
4ccad563 ILT |
320 | // SetPos sets the position. NewIdentifier is a public method so we can't modify its signature. |
321 | // Chained for convenience. | |
322 | // TODO: fix one day? | |
323 | func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode { | |
324 | i.Pos = pos | |
325 | return i | |
326 | } | |
327 | ||
f8d9fa9e ILT |
328 | // SetTree sets the parent tree for the node. NewIdentifier is a public method so we can't modify its signature. |
329 | // Chained for convenience. | |
330 | // TODO: fix one day? | |
331 | func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode { | |
332 | i.tr = t | |
333 | return i | |
334 | } | |
335 | ||
adb0401d | 336 | func (i *IdentifierNode) String() string { |
af92e385 | 337 | return i.Ident |
adb0401d ILT |
338 | } |
339 | ||
5a8ea165 ILT |
340 | func (i *IdentifierNode) writeTo(sb *strings.Builder) { |
341 | sb.WriteString(i.String()) | |
342 | } | |
343 | ||
f8d9fa9e ILT |
344 | func (i *IdentifierNode) tree() *Tree { |
345 | return i.tr | |
346 | } | |
347 | ||
cbb6491d | 348 | func (i *IdentifierNode) Copy() Node { |
f8d9fa9e | 349 | return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos) |
cbb6491d ILT |
350 | } |
351 | ||
dd931d9b | 352 | // AssignNode holds a list of variable names, possibly with chained field |
4ccad563 | 353 | // accesses. The dollar sign is part of the (first) name. |
adb0401d ILT |
354 | type VariableNode struct { |
355 | NodeType | |
4ccad563 | 356 | Pos |
f8d9fa9e | 357 | tr *Tree |
4ccad563 | 358 | Ident []string // Variable name and fields in lexical order. |
adb0401d ILT |
359 | } |
360 | ||
f8d9fa9e ILT |
361 | func (t *Tree) newVariable(pos Pos, ident string) *VariableNode { |
362 | return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")} | |
adb0401d ILT |
363 | } |
364 | ||
365 | func (v *VariableNode) String() string { | |
5a8ea165 ILT |
366 | var sb strings.Builder |
367 | v.writeTo(&sb) | |
368 | return sb.String() | |
369 | } | |
370 | ||
371 | func (v *VariableNode) writeTo(sb *strings.Builder) { | |
af92e385 ILT |
372 | for i, id := range v.Ident { |
373 | if i > 0 { | |
5a8ea165 | 374 | sb.WriteByte('.') |
af92e385 | 375 | } |
5a8ea165 | 376 | sb.WriteString(id) |
af92e385 | 377 | } |
adb0401d ILT |
378 | } |
379 | ||
f8d9fa9e ILT |
380 | func (v *VariableNode) tree() *Tree { |
381 | return v.tr | |
382 | } | |
383 | ||
cbb6491d | 384 | func (v *VariableNode) Copy() Node { |
f8d9fa9e | 385 | return &VariableNode{tr: v.tr, NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)} |
cbb6491d ILT |
386 | } |
387 | ||
4ccad563 ILT |
388 | // DotNode holds the special identifier '.'. |
389 | type DotNode struct { | |
f8d9fa9e | 390 | NodeType |
4ccad563 | 391 | Pos |
f8d9fa9e | 392 | tr *Tree |
4ccad563 | 393 | } |
adb0401d | 394 | |
f8d9fa9e ILT |
395 | func (t *Tree) newDot(pos Pos) *DotNode { |
396 | return &DotNode{tr: t, NodeType: NodeDot, Pos: pos} | |
adb0401d ILT |
397 | } |
398 | ||
399 | func (d *DotNode) Type() NodeType { | |
f8d9fa9e ILT |
400 | // Override method on embedded NodeType for API compatibility. |
401 | // TODO: Not really a problem; could change API without effect but | |
402 | // api tool complains. | |
adb0401d ILT |
403 | return NodeDot |
404 | } | |
405 | ||
406 | func (d *DotNode) String() string { | |
af92e385 | 407 | return "." |
adb0401d ILT |
408 | } |
409 | ||
5a8ea165 ILT |
410 | func (d *DotNode) writeTo(sb *strings.Builder) { |
411 | sb.WriteString(d.String()) | |
412 | } | |
413 | ||
f8d9fa9e ILT |
414 | func (d *DotNode) tree() *Tree { |
415 | return d.tr | |
416 | } | |
417 | ||
cbb6491d | 418 | func (d *DotNode) Copy() Node { |
f8d9fa9e | 419 | return d.tr.newDot(d.Pos) |
4ccad563 ILT |
420 | } |
421 | ||
422 | // NilNode holds the special identifier 'nil' representing an untyped nil constant. | |
423 | type NilNode struct { | |
f8d9fa9e | 424 | NodeType |
4ccad563 | 425 | Pos |
f8d9fa9e | 426 | tr *Tree |
4ccad563 ILT |
427 | } |
428 | ||
f8d9fa9e ILT |
429 | func (t *Tree) newNil(pos Pos) *NilNode { |
430 | return &NilNode{tr: t, NodeType: NodeNil, Pos: pos} | |
4ccad563 ILT |
431 | } |
432 | ||
433 | func (n *NilNode) Type() NodeType { | |
f8d9fa9e ILT |
434 | // Override method on embedded NodeType for API compatibility. |
435 | // TODO: Not really a problem; could change API without effect but | |
436 | // api tool complains. | |
4ccad563 ILT |
437 | return NodeNil |
438 | } | |
439 | ||
440 | func (n *NilNode) String() string { | |
441 | return "nil" | |
442 | } | |
443 | ||
5a8ea165 ILT |
444 | func (n *NilNode) writeTo(sb *strings.Builder) { |
445 | sb.WriteString(n.String()) | |
446 | } | |
447 | ||
f8d9fa9e ILT |
448 | func (n *NilNode) tree() *Tree { |
449 | return n.tr | |
450 | } | |
451 | ||
4ccad563 | 452 | func (n *NilNode) Copy() Node { |
f8d9fa9e | 453 | return n.tr.newNil(n.Pos) |
cbb6491d ILT |
454 | } |
455 | ||
adb0401d ILT |
456 | // FieldNode holds a field (identifier starting with '.'). |
457 | // The names may be chained ('.x.y'). | |
458 | // The period is dropped from each ident. | |
459 | type FieldNode struct { | |
460 | NodeType | |
4ccad563 | 461 | Pos |
f8d9fa9e | 462 | tr *Tree |
adb0401d ILT |
463 | Ident []string // The identifiers in lexical order. |
464 | } | |
465 | ||
f8d9fa9e ILT |
466 | func (t *Tree) newField(pos Pos, ident string) *FieldNode { |
467 | return &FieldNode{tr: t, NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} // [1:] to drop leading period | |
adb0401d ILT |
468 | } |
469 | ||
470 | func (f *FieldNode) String() string { | |
5a8ea165 ILT |
471 | var sb strings.Builder |
472 | f.writeTo(&sb) | |
473 | return sb.String() | |
474 | } | |
475 | ||
476 | func (f *FieldNode) writeTo(sb *strings.Builder) { | |
af92e385 | 477 | for _, id := range f.Ident { |
5a8ea165 ILT |
478 | sb.WriteByte('.') |
479 | sb.WriteString(id) | |
af92e385 | 480 | } |
adb0401d ILT |
481 | } |
482 | ||
f8d9fa9e ILT |
483 | func (f *FieldNode) tree() *Tree { |
484 | return f.tr | |
485 | } | |
486 | ||
cbb6491d | 487 | func (f *FieldNode) Copy() Node { |
f8d9fa9e | 488 | return &FieldNode{tr: f.tr, NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)} |
4ccad563 ILT |
489 | } |
490 | ||
491 | // ChainNode holds a term followed by a chain of field accesses (identifier starting with '.'). | |
492 | // The names may be chained ('.x.y'). | |
493 | // The periods are dropped from each ident. | |
494 | type ChainNode struct { | |
495 | NodeType | |
496 | Pos | |
f8d9fa9e | 497 | tr *Tree |
4ccad563 ILT |
498 | Node Node |
499 | Field []string // The identifiers in lexical order. | |
500 | } | |
501 | ||
f8d9fa9e ILT |
502 | func (t *Tree) newChain(pos Pos, node Node) *ChainNode { |
503 | return &ChainNode{tr: t, NodeType: NodeChain, Pos: pos, Node: node} | |
4ccad563 ILT |
504 | } |
505 | ||
506 | // Add adds the named field (which should start with a period) to the end of the chain. | |
507 | func (c *ChainNode) Add(field string) { | |
508 | if len(field) == 0 || field[0] != '.' { | |
509 | panic("no dot in field") | |
510 | } | |
511 | field = field[1:] // Remove leading dot. | |
512 | if field == "" { | |
513 | panic("empty field") | |
514 | } | |
515 | c.Field = append(c.Field, field) | |
516 | } | |
517 | ||
518 | func (c *ChainNode) String() string { | |
5a8ea165 ILT |
519 | var sb strings.Builder |
520 | c.writeTo(&sb) | |
521 | return sb.String() | |
522 | } | |
523 | ||
524 | func (c *ChainNode) writeTo(sb *strings.Builder) { | |
4ccad563 | 525 | if _, ok := c.Node.(*PipeNode); ok { |
5a8ea165 ILT |
526 | sb.WriteByte('(') |
527 | c.Node.writeTo(sb) | |
528 | sb.WriteByte(')') | |
529 | } else { | |
530 | c.Node.writeTo(sb) | |
4ccad563 ILT |
531 | } |
532 | for _, field := range c.Field { | |
5a8ea165 ILT |
533 | sb.WriteByte('.') |
534 | sb.WriteString(field) | |
4ccad563 | 535 | } |
4ccad563 ILT |
536 | } |
537 | ||
f8d9fa9e ILT |
538 | func (c *ChainNode) tree() *Tree { |
539 | return c.tr | |
540 | } | |
541 | ||
4ccad563 | 542 | func (c *ChainNode) Copy() Node { |
f8d9fa9e | 543 | return &ChainNode{tr: c.tr, NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)} |
cbb6491d ILT |
544 | } |
545 | ||
adb0401d ILT |
546 | // BoolNode holds a boolean constant. |
547 | type BoolNode struct { | |
548 | NodeType | |
4ccad563 | 549 | Pos |
f8d9fa9e | 550 | tr *Tree |
adb0401d ILT |
551 | True bool // The value of the boolean constant. |
552 | } | |
553 | ||
f8d9fa9e ILT |
554 | func (t *Tree) newBool(pos Pos, true bool) *BoolNode { |
555 | return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true} | |
adb0401d ILT |
556 | } |
557 | ||
558 | func (b *BoolNode) String() string { | |
af92e385 ILT |
559 | if b.True { |
560 | return "true" | |
561 | } | |
562 | return "false" | |
adb0401d ILT |
563 | } |
564 | ||
5a8ea165 ILT |
565 | func (b *BoolNode) writeTo(sb *strings.Builder) { |
566 | sb.WriteString(b.String()) | |
567 | } | |
568 | ||
f8d9fa9e ILT |
569 | func (b *BoolNode) tree() *Tree { |
570 | return b.tr | |
571 | } | |
572 | ||
cbb6491d | 573 | func (b *BoolNode) Copy() Node { |
f8d9fa9e | 574 | return b.tr.newBool(b.Pos, b.True) |
cbb6491d ILT |
575 | } |
576 | ||
adb0401d ILT |
577 | // NumberNode holds a number: signed or unsigned integer, float, or complex. |
578 | // The value is parsed and stored under all the types that can represent the value. | |
579 | // This simulates in a small amount of code the behavior of Go's ideal constants. | |
580 | type NumberNode struct { | |
581 | NodeType | |
4ccad563 | 582 | Pos |
f8d9fa9e | 583 | tr *Tree |
adb0401d ILT |
584 | IsInt bool // Number has an integral value. |
585 | IsUint bool // Number has an unsigned integral value. | |
586 | IsFloat bool // Number has a floating-point value. | |
587 | IsComplex bool // Number is complex. | |
588 | Int64 int64 // The signed integer value. | |
589 | Uint64 uint64 // The unsigned integer value. | |
590 | Float64 float64 // The floating-point value. | |
591 | Complex128 complex128 // The complex value. | |
592 | Text string // The original textual representation from the input. | |
593 | } | |
594 | ||
f8d9fa9e ILT |
595 | func (t *Tree) newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) { |
596 | n := &NumberNode{tr: t, NodeType: NodeNumber, Pos: pos, Text: text} | |
adb0401d ILT |
597 | switch typ { |
598 | case itemCharConstant: | |
599 | rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0]) | |
600 | if err != nil { | |
601 | return nil, err | |
602 | } | |
603 | if tail != "'" { | |
604 | return nil, fmt.Errorf("malformed character constant: %s", text) | |
605 | } | |
606 | n.Int64 = int64(rune) | |
607 | n.IsInt = true | |
608 | n.Uint64 = uint64(rune) | |
609 | n.IsUint = true | |
610 | n.Float64 = float64(rune) // odd but those are the rules. | |
611 | n.IsFloat = true | |
612 | return n, nil | |
613 | case itemComplex: | |
614 | // fmt.Sscan can parse the pair, so let it do the work. | |
615 | if _, err := fmt.Sscan(text, &n.Complex128); err != nil { | |
616 | return nil, err | |
617 | } | |
618 | n.IsComplex = true | |
619 | n.simplifyComplex() | |
620 | return n, nil | |
621 | } | |
622 | // Imaginary constants can only be complex unless they are zero. | |
623 | if len(text) > 0 && text[len(text)-1] == 'i' { | |
d5363590 | 624 | f, err := strconv.ParseFloat(text[:len(text)-1], 64) |
adb0401d ILT |
625 | if err == nil { |
626 | n.IsComplex = true | |
627 | n.Complex128 = complex(0, f) | |
628 | n.simplifyComplex() | |
629 | return n, nil | |
630 | } | |
631 | } | |
632 | // Do integer test first so we get 0x123 etc. | |
d5363590 | 633 | u, err := strconv.ParseUint(text, 0, 64) // will fail for -0; fixed below. |
adb0401d ILT |
634 | if err == nil { |
635 | n.IsUint = true | |
636 | n.Uint64 = u | |
637 | } | |
d5363590 | 638 | i, err := strconv.ParseInt(text, 0, 64) |
adb0401d ILT |
639 | if err == nil { |
640 | n.IsInt = true | |
641 | n.Int64 = i | |
642 | if i == 0 { | |
643 | n.IsUint = true // in case of -0. | |
644 | n.Uint64 = u | |
645 | } | |
646 | } | |
647 | // If an integer extraction succeeded, promote the float. | |
648 | if n.IsInt { | |
649 | n.IsFloat = true | |
650 | n.Float64 = float64(n.Int64) | |
651 | } else if n.IsUint { | |
652 | n.IsFloat = true | |
653 | n.Float64 = float64(n.Uint64) | |
654 | } else { | |
d5363590 | 655 | f, err := strconv.ParseFloat(text, 64) |
adb0401d | 656 | if err == nil { |
af146490 ILT |
657 | // If we parsed it as a float but it looks like an integer, |
658 | // it's a huge number too large to fit in an int. Reject it. | |
aa8901e9 | 659 | if !strings.ContainsAny(text, ".eEpP") { |
af146490 ILT |
660 | return nil, fmt.Errorf("integer overflow: %q", text) |
661 | } | |
adb0401d ILT |
662 | n.IsFloat = true |
663 | n.Float64 = f | |
664 | // If a floating-point extraction succeeded, extract the int if needed. | |
665 | if !n.IsInt && float64(int64(f)) == f { | |
666 | n.IsInt = true | |
667 | n.Int64 = int64(f) | |
668 | } | |
669 | if !n.IsUint && float64(uint64(f)) == f { | |
670 | n.IsUint = true | |
671 | n.Uint64 = uint64(f) | |
672 | } | |
673 | } | |
674 | } | |
675 | if !n.IsInt && !n.IsUint && !n.IsFloat { | |
676 | return nil, fmt.Errorf("illegal number syntax: %q", text) | |
677 | } | |
678 | return n, nil | |
679 | } | |
680 | ||
681 | // simplifyComplex pulls out any other types that are represented by the complex number. | |
682 | // These all require that the imaginary part be zero. | |
683 | func (n *NumberNode) simplifyComplex() { | |
684 | n.IsFloat = imag(n.Complex128) == 0 | |
685 | if n.IsFloat { | |
686 | n.Float64 = real(n.Complex128) | |
687 | n.IsInt = float64(int64(n.Float64)) == n.Float64 | |
688 | if n.IsInt { | |
689 | n.Int64 = int64(n.Float64) | |
690 | } | |
691 | n.IsUint = float64(uint64(n.Float64)) == n.Float64 | |
692 | if n.IsUint { | |
693 | n.Uint64 = uint64(n.Float64) | |
694 | } | |
695 | } | |
696 | } | |
697 | ||
698 | func (n *NumberNode) String() string { | |
af92e385 | 699 | return n.Text |
adb0401d ILT |
700 | } |
701 | ||
5a8ea165 ILT |
702 | func (n *NumberNode) writeTo(sb *strings.Builder) { |
703 | sb.WriteString(n.String()) | |
704 | } | |
705 | ||
f8d9fa9e ILT |
706 | func (n *NumberNode) tree() *Tree { |
707 | return n.tr | |
708 | } | |
709 | ||
cbb6491d ILT |
710 | func (n *NumberNode) Copy() Node { |
711 | nn := new(NumberNode) | |
712 | *nn = *n // Easy, fast, correct. | |
713 | return nn | |
714 | } | |
715 | ||
adb0401d ILT |
716 | // StringNode holds a string constant. The value has been "unquoted". |
717 | type StringNode struct { | |
718 | NodeType | |
4ccad563 | 719 | Pos |
f8d9fa9e | 720 | tr *Tree |
adb0401d ILT |
721 | Quoted string // The original text of the string, with quotes. |
722 | Text string // The string, after quote processing. | |
723 | } | |
724 | ||
f8d9fa9e ILT |
725 | func (t *Tree) newString(pos Pos, orig, text string) *StringNode { |
726 | return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text} | |
adb0401d ILT |
727 | } |
728 | ||
729 | func (s *StringNode) String() string { | |
af92e385 | 730 | return s.Quoted |
adb0401d ILT |
731 | } |
732 | ||
5a8ea165 ILT |
733 | func (s *StringNode) writeTo(sb *strings.Builder) { |
734 | sb.WriteString(s.String()) | |
735 | } | |
736 | ||
f8d9fa9e ILT |
737 | func (s *StringNode) tree() *Tree { |
738 | return s.tr | |
739 | } | |
740 | ||
cbb6491d | 741 | func (s *StringNode) Copy() Node { |
f8d9fa9e | 742 | return s.tr.newString(s.Pos, s.Quoted, s.Text) |
cbb6491d ILT |
743 | } |
744 | ||
4ccad563 | 745 | // endNode represents an {{end}} action. |
adb0401d | 746 | // It does not appear in the final parse tree. |
4ccad563 | 747 | type endNode struct { |
f8d9fa9e | 748 | NodeType |
4ccad563 | 749 | Pos |
f8d9fa9e | 750 | tr *Tree |
4ccad563 | 751 | } |
adb0401d | 752 | |
f8d9fa9e ILT |
753 | func (t *Tree) newEnd(pos Pos) *endNode { |
754 | return &endNode{tr: t, NodeType: nodeEnd, Pos: pos} | |
adb0401d ILT |
755 | } |
756 | ||
757 | func (e *endNode) String() string { | |
758 | return "{{end}}" | |
759 | } | |
760 | ||
5a8ea165 ILT |
761 | func (e *endNode) writeTo(sb *strings.Builder) { |
762 | sb.WriteString(e.String()) | |
763 | } | |
764 | ||
f8d9fa9e ILT |
765 | func (e *endNode) tree() *Tree { |
766 | return e.tr | |
767 | } | |
768 | ||
cbb6491d | 769 | func (e *endNode) Copy() Node { |
f8d9fa9e | 770 | return e.tr.newEnd(e.Pos) |
cbb6491d ILT |
771 | } |
772 | ||
adb0401d ILT |
773 | // elseNode represents an {{else}} action. Does not appear in the final tree. |
774 | type elseNode struct { | |
775 | NodeType | |
4ccad563 | 776 | Pos |
f8d9fa9e | 777 | tr *Tree |
af146490 | 778 | Line int // The line number in the input. Deprecated: Kept for compatibility. |
adb0401d ILT |
779 | } |
780 | ||
f8d9fa9e ILT |
781 | func (t *Tree) newElse(pos Pos, line int) *elseNode { |
782 | return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line} | |
adb0401d ILT |
783 | } |
784 | ||
785 | func (e *elseNode) Type() NodeType { | |
786 | return nodeElse | |
787 | } | |
788 | ||
789 | func (e *elseNode) String() string { | |
790 | return "{{else}}" | |
791 | } | |
792 | ||
5a8ea165 ILT |
793 | func (e *elseNode) writeTo(sb *strings.Builder) { |
794 | sb.WriteString(e.String()) | |
795 | } | |
796 | ||
f8d9fa9e ILT |
797 | func (e *elseNode) tree() *Tree { |
798 | return e.tr | |
799 | } | |
800 | ||
cbb6491d | 801 | func (e *elseNode) Copy() Node { |
f8d9fa9e | 802 | return e.tr.newElse(e.Pos, e.Line) |
cbb6491d ILT |
803 | } |
804 | ||
d8f41257 ILT |
805 | // BranchNode is the common representation of if, range, and with. |
806 | type BranchNode struct { | |
adb0401d | 807 | NodeType |
4ccad563 | 808 | Pos |
f8d9fa9e | 809 | tr *Tree |
af146490 | 810 | Line int // The line number in the input. Deprecated: Kept for compatibility. |
adb0401d ILT |
811 | Pipe *PipeNode // The pipeline to be evaluated. |
812 | List *ListNode // What to execute if the value is non-empty. | |
813 | ElseList *ListNode // What to execute if the value is empty (nil if absent). | |
814 | } | |
815 | ||
d8f41257 | 816 | func (b *BranchNode) String() string { |
5a8ea165 ILT |
817 | var sb strings.Builder |
818 | b.writeTo(&sb) | |
819 | return sb.String() | |
820 | } | |
821 | ||
822 | func (b *BranchNode) writeTo(sb *strings.Builder) { | |
d8f41257 ILT |
823 | name := "" |
824 | switch b.NodeType { | |
825 | case NodeIf: | |
826 | name = "if" | |
827 | case NodeRange: | |
828 | name = "range" | |
829 | case NodeWith: | |
830 | name = "with" | |
831 | default: | |
832 | panic("unknown branch type") | |
833 | } | |
5a8ea165 ILT |
834 | sb.WriteString("{{") |
835 | sb.WriteString(name) | |
836 | sb.WriteByte(' ') | |
837 | b.Pipe.writeTo(sb) | |
838 | sb.WriteString("}}") | |
839 | b.List.writeTo(sb) | |
d8f41257 | 840 | if b.ElseList != nil { |
5a8ea165 ILT |
841 | sb.WriteString("{{else}}") |
842 | b.ElseList.writeTo(sb) | |
d8f41257 | 843 | } |
5a8ea165 | 844 | sb.WriteString("{{end}}") |
adb0401d ILT |
845 | } |
846 | ||
f8d9fa9e ILT |
847 | func (b *BranchNode) tree() *Tree { |
848 | return b.tr | |
849 | } | |
850 | ||
851 | func (b *BranchNode) Copy() Node { | |
852 | switch b.NodeType { | |
853 | case NodeIf: | |
854 | return b.tr.newIf(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) | |
855 | case NodeRange: | |
856 | return b.tr.newRange(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) | |
857 | case NodeWith: | |
858 | return b.tr.newWith(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) | |
859 | default: | |
860 | panic("unknown branch type") | |
861 | } | |
862 | } | |
863 | ||
d8f41257 ILT |
864 | // IfNode represents an {{if}} action and its commands. |
865 | type IfNode struct { | |
866 | BranchNode | |
867 | } | |
868 | ||
f8d9fa9e ILT |
869 | func (t *Tree) newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode { |
870 | return &IfNode{BranchNode{tr: t, NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} | |
adb0401d ILT |
871 | } |
872 | ||
cbb6491d | 873 | func (i *IfNode) Copy() Node { |
f8d9fa9e | 874 | return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList()) |
cbb6491d ILT |
875 | } |
876 | ||
adb0401d ILT |
877 | // RangeNode represents a {{range}} action and its commands. |
878 | type RangeNode struct { | |
d8f41257 | 879 | BranchNode |
adb0401d ILT |
880 | } |
881 | ||
f8d9fa9e ILT |
882 | func (t *Tree) newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode { |
883 | return &RangeNode{BranchNode{tr: t, NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} | |
adb0401d ILT |
884 | } |
885 | ||
cbb6491d | 886 | func (r *RangeNode) Copy() Node { |
f8d9fa9e | 887 | return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList()) |
cbb6491d ILT |
888 | } |
889 | ||
d8f41257 ILT |
890 | // WithNode represents a {{with}} action and its commands. |
891 | type WithNode struct { | |
892 | BranchNode | |
893 | } | |
894 | ||
f8d9fa9e ILT |
895 | func (t *Tree) newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode { |
896 | return &WithNode{BranchNode{tr: t, NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} | |
adb0401d ILT |
897 | } |
898 | ||
cbb6491d | 899 | func (w *WithNode) Copy() Node { |
f8d9fa9e | 900 | return w.tr.newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList()) |
cbb6491d ILT |
901 | } |
902 | ||
adb0401d ILT |
903 | // TemplateNode represents a {{template}} action. |
904 | type TemplateNode struct { | |
905 | NodeType | |
4ccad563 | 906 | Pos |
f8d9fa9e | 907 | tr *Tree |
af146490 | 908 | Line int // The line number in the input. Deprecated: Kept for compatibility. |
adb0401d ILT |
909 | Name string // The name of the template (unquoted). |
910 | Pipe *PipeNode // The command to evaluate as dot for the template. | |
911 | } | |
912 | ||
f8d9fa9e ILT |
913 | func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode { |
914 | return &TemplateNode{tr: t, NodeType: NodeTemplate, Pos: pos, Line: line, Name: name, Pipe: pipe} | |
adb0401d ILT |
915 | } |
916 | ||
917 | func (t *TemplateNode) String() string { | |
5a8ea165 ILT |
918 | var sb strings.Builder |
919 | t.writeTo(&sb) | |
920 | return sb.String() | |
921 | } | |
922 | ||
923 | func (t *TemplateNode) writeTo(sb *strings.Builder) { | |
924 | sb.WriteString("{{template ") | |
925 | sb.WriteString(strconv.Quote(t.Name)) | |
926 | if t.Pipe != nil { | |
927 | sb.WriteByte(' ') | |
928 | t.Pipe.writeTo(sb) | |
adb0401d | 929 | } |
5a8ea165 | 930 | sb.WriteString("}}") |
adb0401d | 931 | } |
cbb6491d | 932 | |
f8d9fa9e ILT |
933 | func (t *TemplateNode) tree() *Tree { |
934 | return t.tr | |
935 | } | |
936 | ||
cbb6491d | 937 | func (t *TemplateNode) Copy() Node { |
f8d9fa9e | 938 | return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe()) |
cbb6491d | 939 | } |