github.com/cilki/sh@v2.6.4+incompatible/syntax/nodes.go (about)

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