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  }