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 }