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