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