1// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
2// See LICENSE for licensing information
3
4package syntax
5
6import (
7	"fmt"
8	"strings"
9)
10
11// Node represents a syntax tree node.
12type Node interface {
13	// Pos returns the position of the first character of the node. Comments
14	// are ignored, except if the node is a *File.
15	Pos() Pos
16	// End returns the position of the character immediately after the node.
17	// If the character is a newline, the line number won't cross into the
18	// next line. Comments are ignored, except if the node is a *File.
19	End() Pos
20}
21
22// File represents a shell source file.
23type File struct {
24	Name string
25
26	Stmts []*Stmt
27	Last  []Comment
28}
29
30func (f *File) Pos() Pos { return stmtsPos(f.Stmts, f.Last) }
31func (f *File) End() Pos { return stmtsEnd(f.Stmts, f.Last) }
32
33func stmtsPos(stmts []*Stmt, last []Comment) Pos {
34	if len(stmts) > 0 {
35		s := stmts[0]
36		sPos := s.Pos()
37		if len(s.Comments) > 0 {
38			if cPos := s.Comments[0].Pos(); sPos.After(cPos) {
39				return cPos
40			}
41		}
42		return sPos
43	}
44	if len(last) > 0 {
45		return last[0].Pos()
46	}
47	return Pos{}
48}
49
50func stmtsEnd(stmts []*Stmt, last []Comment) Pos {
51	if len(last) > 0 {
52		return last[len(last)-1].End()
53	}
54	if len(stmts) > 0 {
55		s := stmts[len(stmts)-1]
56		sEnd := s.End()
57		if len(s.Comments) > 0 {
58			if cEnd := s.Comments[0].End(); cEnd.After(sEnd) {
59				return cEnd
60			}
61		}
62		return sEnd
63	}
64	return Pos{}
65}
66
67// Pos is a position within a shell source file.
68type Pos struct {
69	offs      uint32
70	line, col uint16
71}
72
73// Offset returns the byte offset of the position in the original source file.
74// Byte offsets start at 0.
75func (p Pos) Offset() uint { return uint(p.offs) }
76
77// Line returns the line number of the position, starting at 1.
78func (p Pos) Line() uint { return uint(p.line) }
79
80// Col returns the column number of the position, starting at 1. It counts in
81// bytes.
82func (p Pos) Col() uint { return uint(p.col) }
83
84func (p Pos) String() string {
85	return fmt.Sprintf("%d:%d", p.Line(), p.Col())
86}
87
88// IsValid reports whether the position is valid. All positions in nodes
89// returned by Parse are valid.
90func (p Pos) IsValid() bool { return p.line > 0 }
91
92// After reports whether the position p is after p2. It is a more expressive
93// version of p.Offset() > p2.Offset().
94func (p Pos) After(p2 Pos) bool { return p.offs > p2.offs }
95
96func posAddCol(p Pos, n int) Pos {
97	p.col += uint16(n)
98	p.offs += uint32(n)
99	return p
100}
101
102func posMax(p1, p2 Pos) Pos {
103	if p2.After(p1) {
104		return p2
105	}
106	return p1
107}
108
109// Comment represents a single comment on a single line.
110type Comment struct {
111	Hash Pos
112	Text string
113}
114
115func (c *Comment) Pos() Pos { return c.Hash }
116func (c *Comment) End() Pos { return posAddCol(c.Hash, 1+len(c.Text)) }
117
118// Stmt represents a statement, also known as a "complete command". It is
119// compromised of a command and other components that may come before or after
120// it.
121type Stmt struct {
122	Comments   []Comment
123	Cmd        Command
124	Position   Pos
125	Semicolon  Pos  // position of ';', '&', or '|&', if any
126	Negated    bool // ! stmt
127	Background bool // stmt &
128	Coprocess  bool // mksh's |&
129
130	Redirs []*Redirect // stmt >a <b
131}
132
133func (s *Stmt) Pos() Pos { return s.Position }
134func (s *Stmt) End() Pos {
135	if s.Semicolon.IsValid() {
136		end := posAddCol(s.Semicolon, 1) // ';' or '&'
137		if s.Coprocess {
138			end = posAddCol(end, 1) // '|&'
139		}
140		return end
141	}
142	end := s.Position
143	if s.Negated {
144		end = posAddCol(end, 1)
145	}
146	if s.Cmd != nil {
147		end = s.Cmd.End()
148	}
149	if len(s.Redirs) > 0 {
150		end = posMax(end, s.Redirs[len(s.Redirs)-1].End())
151	}
152	return end
153}
154
155// Command represents all nodes that are simple or compound commands, including
156// function declarations.
157//
158// These are *CallExpr, *IfClause, *WhileClause, *ForClause, *CaseClause,
159// *Block, *Subshell, *BinaryCmd, *FuncDecl, *ArithmCmd, *TestClause,
160// *DeclClause, *LetClause, *TimeClause, and *CoprocClause.
161type Command interface {
162	Node
163	commandNode()
164}
165
166func (*CallExpr) commandNode()     {}
167func (*IfClause) commandNode()     {}
168func (*WhileClause) commandNode()  {}
169func (*ForClause) commandNode()    {}
170func (*CaseClause) commandNode()   {}
171func (*Block) commandNode()        {}
172func (*Subshell) commandNode()     {}
173func (*BinaryCmd) commandNode()    {}
174func (*FuncDecl) commandNode()     {}
175func (*ArithmCmd) commandNode()    {}
176func (*TestClause) commandNode()   {}
177func (*DeclClause) commandNode()   {}
178func (*LetClause) commandNode()    {}
179func (*TimeClause) commandNode()   {}
180func (*CoprocClause) commandNode() {}
181
182// Assign represents an assignment to a variable.
183//
184// Here and elsewhere, Index can mean either an index expression into an indexed
185// array, or a string key into an associative array.
186//
187// If Index is non-nil, the value will be a word and not an array as nested
188// arrays are not allowed.
189//
190// If Naked is true and Name is nil, the assignment is part of a DeclClause and
191// the argument (in the Value field) will be evaluated at run-time. This
192// includes parameter expansions, which may expand to assignments or options.
193type Assign struct {
194	Append bool       // +=
195	Naked  bool       // without '='
196	Name   *Lit       // must be a valid name
197	Index  ArithmExpr // [i], ["k"]
198	Value  *Word      // =val
199	Array  *ArrayExpr // =(arr)
200}
201
202func (a *Assign) Pos() Pos {
203	if a.Name == nil {
204		return a.Value.Pos()
205	}
206	return a.Name.Pos()
207}
208
209func (a *Assign) End() Pos {
210	if a.Value != nil {
211		return a.Value.End()
212	}
213	if a.Array != nil {
214		return a.Array.End()
215	}
216	if a.Index != nil {
217		return posAddCol(a.Index.End(), 2)
218	}
219	if a.Naked {
220		return a.Name.End()
221	}
222	return posAddCol(a.Name.End(), 1)
223}
224
225// Redirect represents an input/output redirection.
226type Redirect struct {
227	OpPos Pos
228	Op    RedirOperator
229	N     *Lit  // fd>, or {varname}> in Bash
230	Word  *Word // >word
231	Hdoc  *Word // here-document body
232}
233
234func (r *Redirect) Pos() Pos {
235	if r.N != nil {
236		return r.N.Pos()
237	}
238	return r.OpPos
239}
240
241func (r *Redirect) End() Pos {
242	if r.Hdoc != nil {
243		return r.Hdoc.End()
244	}
245	return r.Word.End()
246}
247
248// CallExpr represents a command execution or function call, otherwise known as
249// a "simple command".
250//
251// If Args is empty, Assigns apply to the shell environment. Otherwise, they are
252// variables that cannot be arrays and which only apply to the call.
253type CallExpr struct {
254	Assigns []*Assign // a=x b=y args
255	Args    []*Word
256}
257
258func (c *CallExpr) Pos() Pos {
259	if len(c.Assigns) > 0 {
260		return c.Assigns[0].Pos()
261	}
262	return c.Args[0].Pos()
263}
264
265func (c *CallExpr) End() Pos {
266	if len(c.Args) == 0 {
267		return c.Assigns[len(c.Assigns)-1].End()
268	}
269	return c.Args[len(c.Args)-1].End()
270}
271
272// Subshell represents a series of commands that should be executed in a nested
273// shell environment.
274type Subshell struct {
275	Lparen, Rparen Pos
276
277	Stmts []*Stmt
278	Last  []Comment
279}
280
281func (s *Subshell) Pos() Pos { return s.Lparen }
282func (s *Subshell) End() Pos { return posAddCol(s.Rparen, 1) }
283
284// Block represents a series of commands that should be executed in a nested
285// scope. It is essentially a list of statements within curly braces.
286type Block struct {
287	Lbrace, Rbrace Pos
288
289	Stmts []*Stmt
290	Last  []Comment
291}
292
293func (b *Block) Pos() Pos { return b.Lbrace }
294func (b *Block) End() Pos { return posAddCol(b.Rbrace, 1) }
295
296// IfClause represents an if statement.
297type IfClause struct {
298	Position Pos // position of the starting "if", "elif", or "else" token
299	ThenPos  Pos // position of "then", empty if this is an "else"
300	FiPos    Pos // position of "fi", shared with .Else if non-nil
301
302	Cond     []*Stmt
303	CondLast []Comment
304	Then     []*Stmt
305	ThenLast []Comment
306
307	Else *IfClause // if non-nil, an "elif" or an "else"
308
309	Last []Comment // comments on the first "elif", "else", or "fi"
310}
311
312func (c *IfClause) Pos() Pos { return c.Position }
313func (c *IfClause) End() Pos { return posAddCol(c.FiPos, 2) }
314
315// WhileClause represents a while or an until clause.
316type WhileClause struct {
317	WhilePos, DoPos, DonePos Pos
318	Until                    bool
319
320	Cond     []*Stmt
321	CondLast []Comment
322	Do       []*Stmt
323	DoLast   []Comment
324}
325
326func (w *WhileClause) Pos() Pos { return w.WhilePos }
327func (w *WhileClause) End() Pos { return posAddCol(w.DonePos, 4) }
328
329// ForClause represents a for or a select clause. The latter is only present in
330// Bash.
331type ForClause struct {
332	ForPos, DoPos, DonePos Pos
333	Select                 bool
334	Braces                 bool // deprecated form with { } instead of do/done
335	Loop                   Loop
336
337	Do     []*Stmt
338	DoLast []Comment
339}
340
341func (f *ForClause) Pos() Pos { return f.ForPos }
342func (f *ForClause) End() Pos { return posAddCol(f.DonePos, 4) }
343
344// Loop holds either *WordIter or *CStyleLoop.
345type Loop interface {
346	Node
347	loopNode()
348}
349
350func (*WordIter) loopNode()   {}
351func (*CStyleLoop) loopNode() {}
352
353// WordIter represents the iteration of a variable over a series of words in a
354// for clause. If InPos is an invalid position, the "in" token was missing, so
355// the iteration is over the shell's positional parameters.
356type WordIter struct {
357	Name  *Lit
358	InPos Pos // position of "in"
359	Items []*Word
360}
361
362func (w *WordIter) Pos() Pos { return w.Name.Pos() }
363func (w *WordIter) End() Pos {
364	if len(w.Items) > 0 {
365		return wordLastEnd(w.Items)
366	}
367	return posMax(w.Name.End(), posAddCol(w.InPos, 2))
368}
369
370// CStyleLoop represents the behaviour of a for clause similar to the C
371// language.
372//
373// This node will only appear with LangBash.
374type CStyleLoop struct {
375	Lparen, Rparen   Pos
376	Init, Cond, Post ArithmExpr
377}
378
379func (c *CStyleLoop) Pos() Pos { return c.Lparen }
380func (c *CStyleLoop) End() Pos { return posAddCol(c.Rparen, 2) }
381
382// BinaryCmd represents a binary expression between two statements.
383type BinaryCmd struct {
384	OpPos Pos
385	Op    BinCmdOperator
386	X, Y  *Stmt
387}
388
389func (b *BinaryCmd) Pos() Pos { return b.X.Pos() }
390func (b *BinaryCmd) End() Pos { return b.Y.End() }
391
392// FuncDecl represents the declaration of a function.
393type FuncDecl struct {
394	Position Pos
395	RsrvWord bool // non-posix "function f()" style
396	Name     *Lit
397	Body     *Stmt
398}
399
400func (f *FuncDecl) Pos() Pos { return f.Position }
401func (f *FuncDecl) End() Pos { return f.Body.End() }
402
403// Word represents a shell word, containing one or more word parts contiguous to
404// each other. The word is delimeted by word boundaries, such as spaces,
405// newlines, semicolons, or parentheses.
406type Word struct {
407	Parts []WordPart
408}
409
410func (w *Word) Pos() Pos { return w.Parts[0].Pos() }
411func (w *Word) End() Pos { return w.Parts[len(w.Parts)-1].End() }
412
413// Lit returns the word as a literal value, if the word consists of *Lit nodes
414// only. An empty string is returned otherwise. Words with multiple literals,
415// which can appear in some edge cases, are handled properly.
416//
417// For example, the word "foo" will return "foo", but the word "foo${bar}" will
418// return "".
419func (w *Word) Lit() string {
420	// In the usual case, we'll have either a single part that's a literal,
421	// or one of the parts being a non-literal. Using strings.Join instead
422	// of a strings.Builder avoids extra work in these cases, since a single
423	// part is a shortcut, and many parts don't incur string copies.
424	lits := make([]string, 0, 1)
425	for _, part := range w.Parts {
426		lit, ok := part.(*Lit)
427		if !ok {
428			return ""
429		}
430		lits = append(lits, lit.Value)
431	}
432	return strings.Join(lits, "")
433}
434
435// WordPart represents all nodes that can form part of a word.
436//
437// These are *Lit, *SglQuoted, *DblQuoted, *ParamExp, *CmdSubst, *ArithmExp,
438// *ProcSubst, and *ExtGlob.
439type WordPart interface {
440	Node
441	wordPartNode()
442}
443
444func (*Lit) wordPartNode()       {}
445func (*SglQuoted) wordPartNode() {}
446func (*DblQuoted) wordPartNode() {}
447func (*ParamExp) wordPartNode()  {}
448func (*CmdSubst) wordPartNode()  {}
449func (*ArithmExp) wordPartNode() {}
450func (*ProcSubst) wordPartNode() {}
451func (*ExtGlob) wordPartNode()   {}
452func (*BraceExp) wordPartNode()  {}
453
454// Lit represents a string literal.
455//
456// Note that a parsed string literal may not appear as-is in the original source
457// code, as it is possible to split literals by escaping newlines. The splitting
458// is lost, but the end position is not.
459type Lit struct {
460	ValuePos, ValueEnd Pos
461	Value              string
462}
463
464func (l *Lit) Pos() Pos { return l.ValuePos }
465func (l *Lit) End() Pos { return l.ValueEnd }
466
467// SglQuoted represents a string within single quotes.
468type SglQuoted struct {
469	Left, Right Pos
470	Dollar      bool // $''
471	Value       string
472}
473
474func (q *SglQuoted) Pos() Pos { return q.Left }
475func (q *SglQuoted) End() Pos { return posAddCol(q.Right, 1) }
476
477// DblQuoted represents a list of nodes within double quotes.
478type DblQuoted struct {
479	Left, Right Pos
480	Dollar      bool // $""
481	Parts       []WordPart
482}
483
484func (q *DblQuoted) Pos() Pos { return q.Left }
485func (q *DblQuoted) End() Pos { return posAddCol(q.Right, 1) }
486
487// CmdSubst represents a command substitution.
488type CmdSubst struct {
489	Left, Right Pos
490
491	Stmts []*Stmt
492	Last  []Comment
493
494	Backquotes bool // deprecated `foo`
495	TempFile   bool // mksh's ${ foo;}
496	ReplyVar   bool // mksh's ${|foo;}
497}
498
499func (c *CmdSubst) Pos() Pos { return c.Left }
500func (c *CmdSubst) End() Pos { return posAddCol(c.Right, 1) }
501
502// ParamExp represents a parameter expansion.
503type ParamExp struct {
504	Dollar, Rbrace Pos
505
506	Short  bool // $a instead of ${a}
507	Excl   bool // ${!a}
508	Length bool // ${#a}
509	Width  bool // ${%a}
510	Param  *Lit
511	Index  ArithmExpr       // ${a[i]}, ${a["k"]}
512	Slice  *Slice           // ${a:x:y}
513	Repl   *Replace         // ${a/x/y}
514	Names  ParNamesOperator // ${!prefix*} or ${!prefix@}
515	Exp    *Expansion       // ${a:-b}, ${a#b}, etc
516}
517
518func (p *ParamExp) Pos() Pos { return p.Dollar }
519func (p *ParamExp) End() Pos {
520	if !p.Short {
521		return posAddCol(p.Rbrace, 1)
522	}
523	if p.Index != nil {
524		return posAddCol(p.Index.End(), 1)
525	}
526	return p.Param.End()
527}
528
529func (p *ParamExp) nakedIndex() bool {
530	return p.Short && p.Index != nil
531}
532
533// Slice represents a character slicing expression inside a ParamExp.
534//
535// This node will only appear in LangBash and LangMirBSDKorn.
536type Slice struct {
537	Offset, Length ArithmExpr
538}
539
540// Replace represents a search and replace expression inside a ParamExp.
541type Replace struct {
542	All        bool
543	Orig, With *Word
544}
545
546// Expansion represents string manipulation in a ParamExp other than those
547// covered by Replace.
548type Expansion struct {
549	Op   ParExpOperator
550	Word *Word
551}
552
553// ArithmExp represents an arithmetic expansion.
554type ArithmExp struct {
555	Left, Right Pos
556	Bracket     bool // deprecated $[expr] form
557	Unsigned    bool // mksh's $((# expr))
558
559	X ArithmExpr
560}
561
562func (a *ArithmExp) Pos() Pos { return a.Left }
563func (a *ArithmExp) End() Pos {
564	if a.Bracket {
565		return posAddCol(a.Right, 1)
566	}
567	return posAddCol(a.Right, 2)
568}
569
570// ArithmCmd represents an arithmetic command.
571//
572// This node will only appear in LangBash and LangMirBSDKorn.
573type ArithmCmd struct {
574	Left, Right Pos
575	Unsigned    bool // mksh's ((# expr))
576
577	X ArithmExpr
578}
579
580func (a *ArithmCmd) Pos() Pos { return a.Left }
581func (a *ArithmCmd) End() Pos { return posAddCol(a.Right, 2) }
582
583// ArithmExpr represents all nodes that form arithmetic expressions.
584//
585// These are *BinaryArithm, *UnaryArithm, *ParenArithm, and *Word.
586type ArithmExpr interface {
587	Node
588	arithmExprNode()
589}
590
591func (*BinaryArithm) arithmExprNode() {}
592func (*UnaryArithm) arithmExprNode()  {}
593func (*ParenArithm) arithmExprNode()  {}
594func (*Word) arithmExprNode()         {}
595
596// BinaryArithm represents a binary arithmetic expression.
597//
598// If Op is any assign operator, X will be a word with a single *Lit whose value
599// is a valid name.
600//
601// Ternary operators like "a ? b : c" are fit into this structure. Thus, if
602// Op==TernQuest, Y will be a *BinaryArithm with Op==TernColon. Op can only be
603// TernColon in that scenario.
604type BinaryArithm struct {
605	OpPos Pos
606	Op    BinAritOperator
607	X, Y  ArithmExpr
608}
609
610func (b *BinaryArithm) Pos() Pos { return b.X.Pos() }
611func (b *BinaryArithm) End() Pos { return b.Y.End() }
612
613// UnaryArithm represents an unary arithmetic expression. The unary opearator
614// may come before or after the sub-expression.
615//
616// If Op is Inc or Dec, X will be a word with a single *Lit whose value is a
617// valid name.
618type UnaryArithm struct {
619	OpPos Pos
620	Op    UnAritOperator
621	Post  bool
622	X     ArithmExpr
623}
624
625func (u *UnaryArithm) Pos() Pos {
626	if u.Post {
627		return u.X.Pos()
628	}
629	return u.OpPos
630}
631
632func (u *UnaryArithm) End() Pos {
633	if u.Post {
634		return posAddCol(u.OpPos, 2)
635	}
636	return u.X.End()
637}
638
639// ParenArithm represents an arithmetic expression within parentheses.
640type ParenArithm struct {
641	Lparen, Rparen Pos
642
643	X ArithmExpr
644}
645
646func (p *ParenArithm) Pos() Pos { return p.Lparen }
647func (p *ParenArithm) End() Pos { return posAddCol(p.Rparen, 1) }
648
649// CaseClause represents a case (switch) clause.
650type CaseClause struct {
651	Case, In, Esac Pos
652	Braces         bool // deprecated mksh form with braces instead of in/esac
653
654	Word  *Word
655	Items []*CaseItem
656	Last  []Comment
657}
658
659func (c *CaseClause) Pos() Pos { return c.Case }
660func (c *CaseClause) End() Pos { return posAddCol(c.Esac, 4) }
661
662// CaseItem represents a pattern list (case) within a CaseClause.
663type CaseItem struct {
664	Op       CaseOperator
665	OpPos    Pos // unset if it was finished by "esac"
666	Comments []Comment
667	Patterns []*Word
668
669	Stmts []*Stmt
670	Last  []Comment
671}
672
673func (c *CaseItem) Pos() Pos { return c.Patterns[0].Pos() }
674func (c *CaseItem) End() Pos {
675	if c.OpPos.IsValid() {
676		return posAddCol(c.OpPos, len(c.Op.String()))
677	}
678	return stmtsEnd(c.Stmts, c.Last)
679}
680
681// TestClause represents a Bash extended test clause.
682//
683// This node will only appear in LangBash and LangMirBSDKorn.
684type TestClause struct {
685	Left, Right Pos
686
687	X TestExpr
688}
689
690func (t *TestClause) Pos() Pos { return t.Left }
691func (t *TestClause) End() Pos { return posAddCol(t.Right, 2) }
692
693// TestExpr represents all nodes that form test expressions.
694//
695// These are *BinaryTest, *UnaryTest, *ParenTest, and *Word.
696type TestExpr interface {
697	Node
698	testExprNode()
699}
700
701func (*BinaryTest) testExprNode() {}
702func (*UnaryTest) testExprNode()  {}
703func (*ParenTest) testExprNode()  {}
704func (*Word) testExprNode()       {}
705
706// BinaryTest represents a binary test expression.
707type BinaryTest struct {
708	OpPos Pos
709	Op    BinTestOperator
710	X, Y  TestExpr
711}
712
713func (b *BinaryTest) Pos() Pos { return b.X.Pos() }
714func (b *BinaryTest) End() Pos { return b.Y.End() }
715
716// UnaryTest represents a unary test expression. The unary opearator may come
717// before or after the sub-expression.
718type UnaryTest struct {
719	OpPos Pos
720	Op    UnTestOperator
721	X     TestExpr
722}
723
724func (u *UnaryTest) Pos() Pos { return u.OpPos }
725func (u *UnaryTest) End() Pos { return u.X.End() }
726
727// ParenTest represents a test expression within parentheses.
728type ParenTest struct {
729	Lparen, Rparen Pos
730
731	X TestExpr
732}
733
734func (p *ParenTest) Pos() Pos { return p.Lparen }
735func (p *ParenTest) End() Pos { return posAddCol(p.Rparen, 1) }
736
737// DeclClause represents a Bash declare clause.
738//
739// Args can contain a mix of regular and naked assignments. The naked
740// assignments can represent either options or variable names.
741//
742// This node will only appear with LangBash.
743type DeclClause struct {
744	// Variant is one of "declare", "local", "export", "readonly",
745	// "typeset", or "nameref".
746	Variant *Lit
747	Args    []*Assign
748}
749
750func (d *DeclClause) Pos() Pos { return d.Variant.Pos() }
751func (d *DeclClause) End() Pos {
752	if len(d.Args) > 0 {
753		return d.Args[len(d.Args)-1].End()
754	}
755	return d.Variant.End()
756}
757
758// ArrayExpr represents a Bash array expression.
759//
760// This node will only appear with LangBash.
761type ArrayExpr struct {
762	Lparen, Rparen Pos
763
764	Elems []*ArrayElem
765	Last  []Comment
766}
767
768func (a *ArrayExpr) Pos() Pos { return a.Lparen }
769func (a *ArrayExpr) End() Pos { return posAddCol(a.Rparen, 1) }
770
771// ArrayElem represents a Bash array element.
772//
773// Index can be nil; for example, declare -a x=(value).
774// Value can be nil; for example, declare -A x=([index]=).
775// Finally, neither can be nil; for example, declare -A x=([index]=value)
776type ArrayElem struct {
777	Index    ArithmExpr
778	Value    *Word
779	Comments []Comment
780}
781
782func (a *ArrayElem) Pos() Pos {
783	if a.Index != nil {
784		return a.Index.Pos()
785	}
786	return a.Value.Pos()
787}
788
789func (a *ArrayElem) End() Pos {
790	if a.Value != nil {
791		return a.Value.End()
792	}
793	return posAddCol(a.Index.Pos(), 1)
794}
795
796// ExtGlob represents a Bash extended globbing expression. Note that these are
797// parsed independently of whether shopt has been called or not.
798//
799// This node will only appear in LangBash and LangMirBSDKorn.
800type ExtGlob struct {
801	OpPos   Pos
802	Op      GlobOperator
803	Pattern *Lit
804}
805
806func (e *ExtGlob) Pos() Pos { return e.OpPos }
807func (e *ExtGlob) End() Pos { return posAddCol(e.Pattern.End(), 1) }
808
809// ProcSubst represents a Bash process substitution.
810//
811// This node will only appear with LangBash.
812type ProcSubst struct {
813	OpPos, Rparen Pos
814	Op            ProcOperator
815
816	Stmts []*Stmt
817	Last  []Comment
818}
819
820func (s *ProcSubst) Pos() Pos { return s.OpPos }
821func (s *ProcSubst) End() Pos { return posAddCol(s.Rparen, 1) }
822
823// TimeClause represents a Bash time clause. PosixFormat corresponds to the -p
824// flag.
825//
826// This node will only appear in LangBash and LangMirBSDKorn.
827type TimeClause struct {
828	Time        Pos
829	PosixFormat bool
830	Stmt        *Stmt
831}
832
833func (c *TimeClause) Pos() Pos { return c.Time }
834func (c *TimeClause) End() Pos {
835	if c.Stmt == nil {
836		return posAddCol(c.Time, 4)
837	}
838	return c.Stmt.End()
839}
840
841// CoprocClause represents a Bash coproc clause.
842//
843// This node will only appear with LangBash.
844type CoprocClause struct {
845	Coproc Pos
846	Name   *Word
847	Stmt   *Stmt
848}
849
850func (c *CoprocClause) Pos() Pos { return c.Coproc }
851func (c *CoprocClause) End() Pos { return c.Stmt.End() }
852
853// LetClause represents a Bash let clause.
854//
855// This node will only appear in LangBash and LangMirBSDKorn.
856type LetClause struct {
857	Let   Pos
858	Exprs []ArithmExpr
859}
860
861func (l *LetClause) Pos() Pos { return l.Let }
862func (l *LetClause) End() Pos { return l.Exprs[len(l.Exprs)-1].End() }
863
864// BraceExp represents a Bash brace expression, such as "{a,f}" or "{1..10}".
865//
866// This node will only appear as a result of SplitBraces.
867type BraceExp struct {
868	Sequence bool // {x..y[..incr]} instead of {x,y[,...]}
869	Elems    []*Word
870}
871
872func (b *BraceExp) Pos() Pos {
873	return posAddCol(b.Elems[0].Pos(), -1)
874}
875
876func (b *BraceExp) End() Pos {
877	return posAddCol(wordLastEnd(b.Elems), 1)
878}
879
880func wordLastEnd(ws []*Word) Pos {
881	if len(ws) == 0 {
882		return Pos{}
883	}
884	return ws[len(ws)-1].End()
885}
886