github.com/cilki/sh@v2.6.4+incompatible/syntax/lexer.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 "bytes" 8 "io" 9 "unicode/utf8" 10 ) 11 12 // bytes that form or start a token 13 func regOps(r rune) bool { 14 switch r { 15 case ';', '"', '\'', '(', ')', '$', '|', '&', '>', '<', '`': 16 return true 17 } 18 return false 19 } 20 21 // tokenize these inside parameter expansions 22 func paramOps(r rune) bool { 23 switch r { 24 case '}', '#', '!', ':', '-', '+', '=', '?', '%', '[', ']', '/', '^', 25 ',', '@', '*': 26 return true 27 } 28 return false 29 } 30 31 // these start a parameter expansion name 32 func paramNameOp(r rune) bool { 33 switch r { 34 case '}', ':', '+', '=', '%', '[', ']', '/', '^', ',': 35 return false 36 } 37 return true 38 } 39 40 // tokenize these inside arithmetic expansions 41 func arithmOps(r rune) bool { 42 switch r { 43 case '+', '-', '!', '*', '/', '%', '(', ')', '^', '<', '>', ':', '=', 44 ',', '?', '|', '&', '[', ']', '#': 45 return true 46 } 47 return false 48 } 49 50 func bquoteEscaped(b byte) bool { 51 switch b { 52 case '$', '`', '\\': 53 return true 54 } 55 return false 56 } 57 58 func (p *Parser) rune() rune { 59 if p.r == '\n' { 60 // p.r instead of b so that newline 61 // character positions don't have col 0. 62 p.npos.line++ 63 p.npos.col = 0 64 } 65 p.npos.col += p.w 66 bquotes := 0 67 retry: 68 if p.bsp < len(p.bs) { 69 if b := p.bs[p.bsp]; b < utf8.RuneSelf { 70 p.bsp++ 71 if b == '\\' && p.openBquotes > 0 { 72 // don't do it for newlines, as we want 73 // the newlines to be eaten in p.next 74 if bquotes < p.openBquotes && p.bsp < len(p.bs) && 75 bquoteEscaped(p.bs[p.bsp]) { 76 bquotes++ 77 goto retry 78 } 79 } 80 if b == '`' { 81 p.lastBquoteEsc = bquotes 82 } 83 if p.litBs != nil { 84 p.litBs = append(p.litBs, b) 85 } 86 p.w, p.r = 1, rune(b) 87 return p.r 88 } 89 if !utf8.FullRune(p.bs[p.bsp:]) { 90 // we need more bytes to read a full non-ascii rune 91 p.fill() 92 } 93 var w int 94 p.r, w = utf8.DecodeRune(p.bs[p.bsp:]) 95 if p.litBs != nil { 96 p.litBs = append(p.litBs, p.bs[p.bsp:p.bsp+w]...) 97 } 98 p.bsp += w 99 if p.r == utf8.RuneError && w == 1 { 100 p.posErr(p.npos, "invalid UTF-8 encoding") 101 } 102 p.w = uint16(w) 103 } else { 104 if p.r == utf8.RuneSelf { 105 } else if p.fill(); p.bs == nil { 106 p.bsp++ 107 p.r = utf8.RuneSelf 108 p.w = 1 109 } else { 110 goto retry 111 } 112 } 113 return p.r 114 } 115 116 // fill reads more bytes from the input src into readBuf. Any bytes that 117 // had not yet been used at the end of the buffer are slid into the 118 // beginning of the buffer. 119 func (p *Parser) fill() { 120 p.offs += p.bsp 121 left := len(p.bs) - p.bsp 122 copy(p.readBuf[:left], p.readBuf[p.bsp:]) 123 readAgain: 124 n, err := 0, p.readErr 125 if err == nil { 126 n, err = p.src.Read(p.readBuf[left:]) 127 p.readErr = err 128 } 129 if n == 0 { 130 if err == nil { 131 goto readAgain 132 } 133 // don't use p.errPass as we don't want to overwrite p.tok 134 if err != io.EOF { 135 p.err = err 136 } 137 if left > 0 { 138 p.bs = p.readBuf[:left] 139 } else { 140 p.bs = nil 141 } 142 } else { 143 p.bs = p.readBuf[:left+n] 144 } 145 p.bsp = 0 146 } 147 148 func (p *Parser) nextKeepSpaces() { 149 r := p.r 150 p.pos = p.getPos() 151 switch p.quote { 152 case paramExpRepl: 153 switch r { 154 case '}', '/': 155 p.tok = p.paramToken(r) 156 case '`', '"', '$': 157 p.tok = p.regToken(r) 158 default: 159 p.advanceLitOther(r) 160 } 161 case dblQuotes: 162 switch r { 163 case '`', '"', '$': 164 p.tok = p.dqToken(r) 165 default: 166 p.advanceLitDquote(r) 167 } 168 case hdocBody, hdocBodyTabs: 169 switch { 170 case r == '`' || r == '$': 171 p.tok = p.dqToken(r) 172 case p.hdocStop == nil: 173 p.tok = _Newl 174 default: 175 p.advanceLitHdoc(r) 176 } 177 default: // paramExpExp: 178 switch r { 179 case '}': 180 p.tok = p.paramToken(r) 181 case '`', '"', '$', '\'': 182 p.tok = p.regToken(r) 183 default: 184 p.advanceLitOther(r) 185 } 186 } 187 if p.err != nil && p.tok != _EOF { 188 p.tok = _EOF 189 } 190 } 191 192 func (p *Parser) next() { 193 if p.r == utf8.RuneSelf { 194 p.tok = _EOF 195 return 196 } 197 p.spaced = false 198 if p.quote&allKeepSpaces != 0 { 199 p.nextKeepSpaces() 200 return 201 } 202 r := p.r 203 skipSpace: 204 for { 205 switch r { 206 case utf8.RuneSelf: 207 p.tok = _EOF 208 return 209 case ' ', '\t', '\r': 210 p.spaced = true 211 r = p.rune() 212 case '\n': 213 if p.tok == _Newl { 214 // merge consecutive newline tokens 215 r = p.rune() 216 continue 217 } 218 p.spaced = true 219 p.tok = _Newl 220 if p.quote != hdocWord && len(p.heredocs) > p.buriedHdocs { 221 p.doHeredocs() 222 } 223 return 224 case '\\': 225 if !p.peekByte('\n') { 226 break skipSpace 227 } 228 p.rune() 229 r = p.rune() 230 default: 231 break skipSpace 232 } 233 } 234 if p.stopAt != nil && (p.spaced || p.tok == illegalTok || stopToken(p.tok)) { 235 w := utf8.RuneLen(r) 236 if bytes.HasPrefix(p.bs[p.bsp-w:], p.stopAt) { 237 p.r = utf8.RuneSelf 238 p.w = 1 239 p.tok = _EOF 240 return 241 } 242 } 243 changedState: 244 p.pos = p.getPos() 245 switch { 246 case p.quote&allRegTokens != 0: 247 switch r { 248 case ';', '"', '\'', '(', ')', '$', '|', '&', '>', '<', '`': 249 p.tok = p.regToken(r) 250 case '#': 251 r = p.rune() 252 p.newLit(r) 253 for r != utf8.RuneSelf && r != '\n' { 254 r = p.rune() 255 } 256 if p.keepComments { 257 *p.curComs = append(*p.curComs, Comment{ 258 Hash: p.pos, 259 Text: p.endLit(), 260 }) 261 } else { 262 p.litBs = nil 263 } 264 p.next() 265 case '[', '=': 266 if p.quote == arrayElems { 267 p.tok = p.paramToken(r) 268 } else { 269 p.advanceLitNone(r) 270 } 271 case '?', '*', '+', '@', '!': 272 if p.peekByte('(') { 273 switch r { 274 case '?': 275 p.tok = globQuest 276 case '*': 277 p.tok = globStar 278 case '+': 279 p.tok = globPlus 280 case '@': 281 p.tok = globAt 282 default: // '!' 283 p.tok = globExcl 284 } 285 p.rune() 286 p.rune() 287 } else { 288 p.advanceLitNone(r) 289 } 290 default: 291 p.advanceLitNone(r) 292 } 293 case p.quote&allArithmExpr != 0 && arithmOps(r): 294 p.tok = p.arithmToken(r) 295 case p.quote&allParamExp != 0 && paramOps(r): 296 p.tok = p.paramToken(r) 297 case p.quote == testRegexp: 298 if !p.rxFirstPart && p.spaced { 299 p.quote = noState 300 goto changedState 301 } 302 p.rxFirstPart = false 303 switch r { 304 case ';', '"', '\'', '$', '&', '>', '<', '`': 305 p.tok = p.regToken(r) 306 case ')': 307 if p.rxOpenParens > 0 { 308 // continuation of open paren 309 p.advanceLitRe(r) 310 } else { 311 p.tok = rightParen 312 p.quote = noState 313 } 314 default: // including '(', '|' 315 p.advanceLitRe(r) 316 } 317 case regOps(r): 318 p.tok = p.regToken(r) 319 default: 320 p.advanceLitOther(r) 321 } 322 if p.err != nil && p.tok != _EOF { 323 p.tok = _EOF 324 } 325 } 326 327 func (p *Parser) peekByte(b byte) bool { 328 if p.bsp == len(p.bs) { 329 p.fill() 330 } 331 return p.bsp < len(p.bs) && p.bs[p.bsp] == b 332 } 333 334 func (p *Parser) regToken(r rune) token { 335 switch r { 336 case '\'': 337 if p.openBquotes > 0 { 338 // bury openBquotes 339 p.buriedBquotes = p.openBquotes 340 p.openBquotes = 0 341 } 342 p.rune() 343 return sglQuote 344 case '"': 345 p.rune() 346 return dblQuote 347 case '`': 348 // Don't call p.rune, as we need to work out p.openBquotes to 349 // properly handle backslashes in the lexer. 350 return bckQuote 351 case '&': 352 switch p.rune() { 353 case '&': 354 p.rune() 355 return andAnd 356 case '>': 357 if p.lang == LangPOSIX { 358 break 359 } 360 if p.rune() == '>' { 361 p.rune() 362 return appAll 363 } 364 return rdrAll 365 } 366 return and 367 case '|': 368 switch p.rune() { 369 case '|': 370 p.rune() 371 return orOr 372 case '&': 373 if p.lang == LangPOSIX { 374 break 375 } 376 p.rune() 377 return orAnd 378 } 379 return or 380 case '$': 381 switch p.rune() { 382 case '\'': 383 if p.lang == LangPOSIX { 384 break 385 } 386 p.rune() 387 return dollSglQuote 388 case '"': 389 if p.lang == LangPOSIX { 390 break 391 } 392 p.rune() 393 return dollDblQuote 394 case '{': 395 p.rune() 396 return dollBrace 397 case '[': 398 if p.lang != LangBash || p.quote == paramExpName { 399 // latter to not tokenise ${$[@]} as $[ 400 break 401 } 402 p.rune() 403 return dollBrack 404 case '(': 405 if p.rune() == '(' { 406 p.rune() 407 return dollDblParen 408 } 409 return dollParen 410 } 411 return dollar 412 case '(': 413 if p.rune() == '(' && p.lang != LangPOSIX { 414 p.rune() 415 return dblLeftParen 416 } 417 return leftParen 418 case ')': 419 p.rune() 420 return rightParen 421 case ';': 422 switch p.rune() { 423 case ';': 424 if p.rune() == '&' && p.lang == LangBash { 425 p.rune() 426 return dblSemiAnd 427 } 428 return dblSemicolon 429 case '&': 430 if p.lang == LangPOSIX { 431 break 432 } 433 p.rune() 434 return semiAnd 435 case '|': 436 if p.lang != LangMirBSDKorn { 437 break 438 } 439 p.rune() 440 return semiOr 441 } 442 return semicolon 443 case '<': 444 switch p.rune() { 445 case '<': 446 if r = p.rune(); r == '-' { 447 p.rune() 448 return dashHdoc 449 } else if r == '<' && p.lang != LangPOSIX { 450 p.rune() 451 return wordHdoc 452 } 453 return hdoc 454 case '>': 455 p.rune() 456 return rdrInOut 457 case '&': 458 p.rune() 459 return dplIn 460 case '(': 461 if p.lang != LangBash { 462 break 463 } 464 p.rune() 465 return cmdIn 466 } 467 return rdrIn 468 default: // '>' 469 switch p.rune() { 470 case '>': 471 p.rune() 472 return appOut 473 case '&': 474 p.rune() 475 return dplOut 476 case '|': 477 p.rune() 478 return clbOut 479 case '(': 480 if p.lang != LangBash { 481 break 482 } 483 p.rune() 484 return cmdOut 485 } 486 return rdrOut 487 } 488 } 489 490 func (p *Parser) dqToken(r rune) token { 491 switch r { 492 case '"': 493 p.rune() 494 return dblQuote 495 case '`': 496 // Don't call p.rune, as we need to work out p.openBquotes to 497 // properly handle backslashes in the lexer. 498 return bckQuote 499 default: // '$' 500 switch p.rune() { 501 case '{': 502 p.rune() 503 return dollBrace 504 case '[': 505 if p.lang != LangBash { 506 break 507 } 508 p.rune() 509 return dollBrack 510 case '(': 511 if p.rune() == '(' { 512 p.rune() 513 return dollDblParen 514 } 515 return dollParen 516 } 517 return dollar 518 } 519 } 520 521 func (p *Parser) paramToken(r rune) token { 522 switch r { 523 case '}': 524 p.rune() 525 return rightBrace 526 case ':': 527 switch p.rune() { 528 case '+': 529 p.rune() 530 return colPlus 531 case '-': 532 p.rune() 533 return colMinus 534 case '?': 535 p.rune() 536 return colQuest 537 case '=': 538 p.rune() 539 return colAssgn 540 } 541 return colon 542 case '+': 543 p.rune() 544 return plus 545 case '-': 546 p.rune() 547 return minus 548 case '?': 549 p.rune() 550 return quest 551 case '=': 552 p.rune() 553 return assgn 554 case '%': 555 if p.rune() == '%' { 556 p.rune() 557 return dblPerc 558 } 559 return perc 560 case '#': 561 if p.rune() == '#' { 562 p.rune() 563 return dblHash 564 } 565 return hash 566 case '!': 567 p.rune() 568 return exclMark 569 case '[': 570 p.rune() 571 return leftBrack 572 case ']': 573 p.rune() 574 return rightBrack 575 case '/': 576 if p.rune() == '/' && p.quote != paramExpRepl { 577 p.rune() 578 return dblSlash 579 } 580 return slash 581 case '^': 582 if p.rune() == '^' { 583 p.rune() 584 return dblCaret 585 } 586 return caret 587 case ',': 588 if p.rune() == ',' { 589 p.rune() 590 return dblComma 591 } 592 return comma 593 case '@': 594 p.rune() 595 return at 596 default: // '*' 597 p.rune() 598 return star 599 } 600 } 601 602 func (p *Parser) arithmToken(r rune) token { 603 switch r { 604 case '!': 605 if p.rune() == '=' { 606 p.rune() 607 return nequal 608 } 609 return exclMark 610 case '=': 611 if p.rune() == '=' { 612 p.rune() 613 return equal 614 } 615 return assgn 616 case '(': 617 p.rune() 618 return leftParen 619 case ')': 620 p.rune() 621 return rightParen 622 case '&': 623 switch p.rune() { 624 case '&': 625 p.rune() 626 return andAnd 627 case '=': 628 p.rune() 629 return andAssgn 630 } 631 return and 632 case '|': 633 switch p.rune() { 634 case '|': 635 p.rune() 636 return orOr 637 case '=': 638 p.rune() 639 return orAssgn 640 } 641 return or 642 case '<': 643 switch p.rune() { 644 case '<': 645 if p.rune() == '=' { 646 p.rune() 647 return shlAssgn 648 } 649 return hdoc 650 case '=': 651 p.rune() 652 return lequal 653 } 654 return rdrIn 655 case '>': 656 switch p.rune() { 657 case '>': 658 if p.rune() == '=' { 659 p.rune() 660 return shrAssgn 661 } 662 return appOut 663 case '=': 664 p.rune() 665 return gequal 666 } 667 return rdrOut 668 case '+': 669 switch p.rune() { 670 case '+': 671 p.rune() 672 return addAdd 673 case '=': 674 p.rune() 675 return addAssgn 676 } 677 return plus 678 case '-': 679 switch p.rune() { 680 case '-': 681 p.rune() 682 return subSub 683 case '=': 684 p.rune() 685 return subAssgn 686 } 687 return minus 688 case '%': 689 if p.rune() == '=' { 690 p.rune() 691 return remAssgn 692 } 693 return perc 694 case '*': 695 switch p.rune() { 696 case '*': 697 p.rune() 698 return power 699 case '=': 700 p.rune() 701 return mulAssgn 702 } 703 return star 704 case '/': 705 if p.rune() == '=' { 706 p.rune() 707 return quoAssgn 708 } 709 return slash 710 case '^': 711 if p.rune() == '=' { 712 p.rune() 713 return xorAssgn 714 } 715 return caret 716 case '[': 717 p.rune() 718 return leftBrack 719 case ']': 720 p.rune() 721 return rightBrack 722 case ',': 723 p.rune() 724 return comma 725 case '?': 726 p.rune() 727 return quest 728 case ':': 729 p.rune() 730 return colon 731 default: // '#' 732 p.rune() 733 return hash 734 } 735 } 736 737 func (p *Parser) newLit(r rune) { 738 switch { 739 case r < utf8.RuneSelf: 740 p.litBs = p.litBuf[:1] 741 p.litBs[0] = byte(r) 742 case r > utf8.RuneSelf: 743 w := utf8.RuneLen(r) 744 p.litBs = append(p.litBuf[:0], p.bs[p.bsp-w:p.bsp]...) 745 default: 746 // don't let r == utf8.RuneSelf go to the second case as RuneLen 747 // would return -1 748 p.litBs = p.litBuf[:0] 749 } 750 } 751 752 func (p *Parser) discardLit(n int) { p.litBs = p.litBs[:len(p.litBs)-n] } 753 754 func (p *Parser) endLit() (s string) { 755 if p.r == utf8.RuneSelf { 756 s = string(p.litBs) 757 } else { 758 s = string(p.litBs[:len(p.litBs)-int(p.w)]) 759 } 760 p.litBs = nil 761 return 762 } 763 764 func (p *Parser) isLitRedir() bool { 765 lit := p.litBs[:len(p.litBs)-1] 766 if lit[0] == '{' && lit[len(lit)-1] == '}' { 767 return ValidName(string(lit[1 : len(lit)-1])) 768 } 769 for _, b := range lit { 770 switch b { 771 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9': 772 default: 773 return false 774 } 775 } 776 return true 777 } 778 779 func (p *Parser) advanceNameCont(r rune) { 780 // we know that r is a letter or underscore 781 loop: 782 for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { 783 switch { 784 case r == '\\': 785 if p.peekByte('\n') { 786 p.rune() 787 p.discardLit(2) 788 } else { 789 break loop 790 } 791 case 'a' <= r && r <= 'z': 792 case 'A' <= r && r <= 'Z': 793 case r == '_': 794 case '0' <= r && r <= '9': 795 default: 796 break loop 797 } 798 } 799 p.tok, p.val = _LitWord, p.endLit() 800 } 801 802 func (p *Parser) advanceLitOther(r rune) { 803 tok := _LitWord 804 loop: 805 for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { 806 switch r { 807 case '\\': // escaped byte follows 808 if r = p.rune(); r == '\n' { 809 p.discardLit(2) 810 } 811 case '"', '`', '$': 812 tok = _Lit 813 break loop 814 case '}': 815 if p.quote&allParamExp != 0 { 816 break loop 817 } 818 case '/': 819 if p.quote != paramExpExp { 820 break loop 821 } 822 case ':', '=', '%', '^', ',', '?', '!', '*': 823 if p.quote&allArithmExpr != 0 || p.quote == paramExpName { 824 break loop 825 } 826 case '[', ']': 827 if p.lang != LangPOSIX && p.quote&allArithmExpr != 0 { 828 break loop 829 } 830 fallthrough 831 case '#', '@': 832 if p.quote&allParamReg != 0 { 833 break loop 834 } 835 case '\'', '+', '-', ' ', '\t', ';', '&', '>', '<', '|', '(', ')', '\n', '\r': 836 if p.quote&allKeepSpaces == 0 { 837 break loop 838 } 839 } 840 } 841 p.tok, p.val = tok, p.endLit() 842 } 843 844 func (p *Parser) advanceLitNone(r rune) { 845 p.eqlOffs = 0 846 tok := _LitWord 847 loop: 848 for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { 849 switch r { 850 case ' ', '\t', '\n', '\r', '&', '|', ';', '(', ')': 851 break loop 852 case '\\': // escaped byte follows 853 if r = p.rune(); r == '\n' { 854 p.discardLit(2) 855 } 856 case '>', '<': 857 if p.peekByte('(') || !p.isLitRedir() { 858 tok = _Lit 859 } else { 860 tok = _LitRedir 861 } 862 break loop 863 case '`': 864 if p.quote != subCmdBckquo { 865 tok = _Lit 866 } 867 break loop 868 case '"', '\'', '$': 869 tok = _Lit 870 break loop 871 case '?', '*', '+', '@', '!': 872 if p.peekByte('(') { 873 tok = _Lit 874 break loop 875 } 876 case '=': 877 if p.eqlOffs == 0 { 878 p.eqlOffs = len(p.litBs) - 1 879 } 880 case '[': 881 if p.lang != LangPOSIX && len(p.litBs) > 1 && p.litBs[0] != '[' { 882 tok = _Lit 883 break loop 884 } 885 } 886 } 887 p.tok, p.val = tok, p.endLit() 888 } 889 890 func (p *Parser) advanceLitDquote(r rune) { 891 tok := _LitWord 892 loop: 893 for p.newLit(r); r != utf8.RuneSelf; r = p.rune() { 894 switch r { 895 case '"': 896 break loop 897 case '\\': // escaped byte follows 898 p.rune() 899 case '`', '$': 900 tok = _Lit 901 break loop 902 } 903 } 904 p.tok, p.val = tok, p.endLit() 905 } 906 907 func (p *Parser) advanceLitHdoc(r rune) { 908 p.tok = _Lit 909 p.newLit(r) 910 if p.quote == hdocBodyTabs { 911 for r == '\t' { 912 r = p.rune() 913 } 914 } 915 lStart := len(p.litBs) - 1 916 if lStart < 0 { 917 return 918 } 919 for ; ; r = p.rune() { 920 switch r { 921 case '`', '$': 922 p.val = p.endLit() 923 return 924 case '\\': // escaped byte follows 925 p.rune() 926 case '\n', utf8.RuneSelf: 927 if p.parsingDoc { 928 if r == utf8.RuneSelf { 929 p.val = p.endLit() 930 return 931 } 932 } else if bytes.HasPrefix(p.litBs[lStart:], p.hdocStop) { 933 p.val = p.endLit()[:lStart] 934 if p.val == "" { 935 p.tok = _Newl 936 } 937 p.hdocStop = nil 938 return 939 } 940 if r == utf8.RuneSelf { 941 return 942 } 943 if p.quote == hdocBodyTabs { 944 for p.peekByte('\t') { 945 p.rune() 946 } 947 } 948 lStart = len(p.litBs) 949 } 950 } 951 } 952 953 func (p *Parser) quotedHdocWord() *Word { 954 r := p.r 955 p.newLit(r) 956 pos := p.getPos() 957 for ; ; r = p.rune() { 958 if r == utf8.RuneSelf { 959 return nil 960 } 961 if p.quote == hdocBodyTabs { 962 for r == '\t' { 963 r = p.rune() 964 } 965 } 966 lStart := len(p.litBs) - 1 967 if lStart < 0 { 968 return nil 969 } 970 for r != utf8.RuneSelf && r != '\n' { 971 r = p.rune() 972 } 973 if bytes.HasPrefix(p.litBs[lStart:], p.hdocStop) { 974 p.hdocStop = nil 975 val := p.endLit()[:lStart] 976 if val == "" { 977 return nil 978 } 979 return p.word(p.wps(p.lit(pos, val))) 980 } 981 } 982 } 983 984 func (p *Parser) advanceLitRe(r rune) { 985 for p.newLit(r); ; r = p.rune() { 986 switch r { 987 case '\\': 988 p.rune() 989 case '(': 990 p.rxOpenParens++ 991 case ')': 992 if p.rxOpenParens--; p.rxOpenParens < 0 { 993 p.tok, p.val = _LitWord, p.endLit() 994 p.quote = noState 995 return 996 } 997 case ' ', '\t', '\r', '\n': 998 if p.rxOpenParens <= 0 { 999 p.tok, p.val = _LitWord, p.endLit() 1000 p.quote = noState 1001 return 1002 } 1003 case '"', '\'', '$', '`': 1004 p.tok, p.val = _Lit, p.endLit() 1005 return 1006 case utf8.RuneSelf, ';', '&', '>', '<': 1007 p.tok, p.val = _LitWord, p.endLit() 1008 p.quote = noState 1009 return 1010 } 1011 } 1012 } 1013 1014 func testUnaryOp(val string) UnTestOperator { 1015 switch val { 1016 case "!": 1017 return TsNot 1018 case "-e", "-a": 1019 return TsExists 1020 case "-f": 1021 return TsRegFile 1022 case "-d": 1023 return TsDirect 1024 case "-c": 1025 return TsCharSp 1026 case "-b": 1027 return TsBlckSp 1028 case "-p": 1029 return TsNmPipe 1030 case "-S": 1031 return TsSocket 1032 case "-L", "-h": 1033 return TsSmbLink 1034 case "-k": 1035 return TsSticky 1036 case "-g": 1037 return TsGIDSet 1038 case "-u": 1039 return TsUIDSet 1040 case "-G": 1041 return TsGrpOwn 1042 case "-O": 1043 return TsUsrOwn 1044 case "-N": 1045 return TsModif 1046 case "-r": 1047 return TsRead 1048 case "-w": 1049 return TsWrite 1050 case "-x": 1051 return TsExec 1052 case "-s": 1053 return TsNoEmpty 1054 case "-t": 1055 return TsFdTerm 1056 case "-z": 1057 return TsEmpStr 1058 case "-n": 1059 return TsNempStr 1060 case "-o": 1061 return TsOptSet 1062 case "-v": 1063 return TsVarSet 1064 case "-R": 1065 return TsRefVar 1066 default: 1067 return 0 1068 } 1069 } 1070 1071 func testBinaryOp(val string) BinTestOperator { 1072 switch val { 1073 case "==", "=": 1074 return TsMatch 1075 case "!=": 1076 return TsNoMatch 1077 case "=~": 1078 return TsReMatch 1079 case "-nt": 1080 return TsNewer 1081 case "-ot": 1082 return TsOlder 1083 case "-ef": 1084 return TsDevIno 1085 case "-eq": 1086 return TsEql 1087 case "-ne": 1088 return TsNeq 1089 case "-le": 1090 return TsLeq 1091 case "-ge": 1092 return TsGeq 1093 case "-lt": 1094 return TsLss 1095 case "-gt": 1096 return TsGtr 1097 default: 1098 return 0 1099 } 1100 }