1
0
mirror of https://github.com/go-task/task.git synced 2024-12-16 10:59:23 +02:00
task/vendor/mvdan.cc/sh/syntax/lexer.go

1077 lines
18 KiB
Go
Raw Normal View History

2017-04-24 14:47:10 +02:00
// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information
package syntax
import (
"bytes"
"io"
"unicode/utf8"
)
// bytes that form or start a token
func regOps(r rune) bool {
switch r {
case ';', '"', '\'', '(', ')', '$', '|', '&', '>', '<', '`':
return true
}
return false
}
// tokenize these inside parameter expansions
func paramOps(r rune) bool {
switch r {
case '}', '#', '!', ':', '-', '+', '=', '?', '%', '[', ']', '/', '^',
2018-01-21 13:39:15 +02:00
',', '@', '*':
2017-04-24 14:47:10 +02:00
return true
}
return false
}
2017-06-04 21:06:04 +02:00
// these start a parameter expansion name
func paramNameOp(r rune) bool {
switch r {
2018-01-03 19:12:40 +02:00
case '}', ':', '+', '=', '%', '[', ']', '/', '^', ',':
2017-06-04 21:06:04 +02:00
return false
}
return true
}
2017-04-24 14:47:10 +02:00
// tokenize these inside arithmetic expansions
func arithmOps(r rune) bool {
switch r {
case '+', '-', '!', '*', '/', '%', '(', ')', '^', '<', '>', ':', '=',
2017-05-27 16:17:49 +02:00
',', '?', '|', '&', '[', ']', '#':
2017-04-24 14:47:10 +02:00
return true
}
return false
}
2018-03-04 00:28:08 +02:00
func bquoteEscaped(b byte) bool {
switch b {
case '$', '`', '\\':
return true
}
return false
}
2017-05-17 19:49:27 +02:00
func (p *Parser) rune() rune {
2017-07-31 00:11:34 +02:00
if p.r == '\n' {
// p.r instead of b so that newline
// character positions don't have col 0.
p.npos.line++
p.npos.col = 1
} else {
p.npos.col += p.w
}
2018-02-12 02:02:22 +02:00
bquotes := 0
2017-04-24 14:47:10 +02:00
retry:
2017-07-06 01:46:05 +02:00
if p.bsp < len(p.bs) {
if b := p.bs[p.bsp]; b < utf8.RuneSelf {
p.bsp++
2018-02-12 02:02:22 +02:00
if b == '\\' && p.openBquotes > 0 {
// don't do it for newlines, as we want
// the newlines to be eaten in p.next
2018-04-28 20:35:15 +02:00
if bquotes < p.openBquotes && p.bsp < len(p.bs) &&
bquoteEscaped(p.bs[p.bsp]) {
2018-02-12 02:02:22 +02:00
bquotes++
goto retry
}
2018-04-28 20:35:15 +02:00
}
if b == '`' {
p.lastBquoteEsc = bquotes
2018-02-12 02:02:22 +02:00
}
2017-04-24 14:47:10 +02:00
if p.litBs != nil {
p.litBs = append(p.litBs, b)
}
2017-11-02 14:41:46 +02:00
p.w, p.r = 1, rune(b)
return p.r
2017-04-24 14:47:10 +02:00
}
2017-07-06 01:46:05 +02:00
if p.bsp+utf8.UTFMax >= len(p.bs) {
2017-04-24 14:47:10 +02:00
// we might need up to 4 bytes to read a full
// non-ascii rune
p.fill()
}
var w int
2017-07-06 01:46:05 +02:00
p.r, w = utf8.DecodeRune(p.bs[p.bsp:])
2017-04-24 14:47:10 +02:00
if p.litBs != nil {
2017-07-06 01:46:05 +02:00
p.litBs = append(p.litBs, p.bs[p.bsp:p.bsp+w]...)
2017-04-24 14:47:10 +02:00
}
2017-07-06 01:46:05 +02:00
p.bsp += w
2017-04-24 14:47:10 +02:00
if p.r == utf8.RuneError && w == 1 {
2017-07-06 01:46:05 +02:00
p.posErr(p.npos, "invalid UTF-8 encoding")
2017-04-24 14:47:10 +02:00
}
2017-07-31 00:11:34 +02:00
p.w = uint16(w)
2017-04-24 14:47:10 +02:00
} else {
if p.r == utf8.RuneSelf {
} else if p.fill(); p.bs == nil {
2017-07-06 01:46:05 +02:00
p.bsp++
2017-04-24 14:47:10 +02:00
p.r = utf8.RuneSelf
} else {
goto retry
}
}
return p.r
}
// fill reads more bytes from the input src into readBuf. Any bytes that
// had not yet been used at the end of the buffer are slid into the
// beginning of the buffer.
2017-05-17 19:49:27 +02:00
func (p *Parser) fill() {
2017-07-06 01:46:05 +02:00
p.offs += p.bsp
left := len(p.bs) - p.bsp
copy(p.readBuf[:left], p.readBuf[p.bsp:])
2018-02-12 02:02:22 +02:00
n, err := 0, p.readErr
if err == nil {
2017-04-24 14:47:10 +02:00
n, err = p.src.Read(p.readBuf[left:])
p.readErr = err
}
if n == 0 {
// don't use p.errPass as we don't want to overwrite p.tok
if err != nil && err != io.EOF {
p.err = err
}
if left > 0 {
p.bs = p.readBuf[:left]
} else {
p.bs = nil
}
} else {
p.bs = p.readBuf[:left+n]
}
2017-07-06 01:46:05 +02:00
p.bsp = 0
2017-04-24 14:47:10 +02:00
}
2017-05-17 19:49:27 +02:00
func (p *Parser) nextKeepSpaces() {
2017-04-24 14:47:10 +02:00
r := p.r
2017-07-31 00:11:34 +02:00
p.pos = p.getPos()
2017-04-24 14:47:10 +02:00
switch p.quote {
case paramExpRepl:
switch r {
case '}', '/':
p.tok = p.paramToken(r)
case '`', '"', '$':
2018-02-12 02:02:22 +02:00
p.tok = p.regToken(r)
2017-04-24 14:47:10 +02:00
default:
p.advanceLitOther(r)
}
case dblQuotes:
switch r {
case '`', '"', '$':
p.tok = p.dqToken(r)
default:
p.advanceLitDquote(r)
}
case hdocBody, hdocBodyTabs:
2018-06-24 15:40:44 +02:00
switch {
case r == '`' || r == '$':
2017-04-24 14:47:10 +02:00
p.tok = p.dqToken(r)
2018-06-24 15:40:44 +02:00
case p.hdocStop == nil:
p.tok = _Newl
2018-06-24 15:40:44 +02:00
default:
2017-04-24 14:47:10 +02:00
p.advanceLitHdoc(r)
}
2018-02-12 02:02:22 +02:00
default: // paramExpExp:
2017-04-24 14:47:10 +02:00
switch r {
case '}':
2018-02-12 02:02:22 +02:00
p.tok = p.paramToken(r)
case '`', '"', '$', '\'':
p.tok = p.regToken(r)
2017-04-24 14:47:10 +02:00
default:
p.advanceLitOther(r)
}
2018-02-12 02:02:22 +02:00
}
if p.err != nil && p.tok != _EOF {
p.tok = _EOF
2017-04-24 14:47:10 +02:00
}
}
2017-05-17 19:49:27 +02:00
func (p *Parser) next() {
2017-04-24 14:47:10 +02:00
if p.r == utf8.RuneSelf {
p.tok = _EOF
return
}
p.spaced = false
2017-04-24 14:47:10 +02:00
if p.quote&allKeepSpaces != 0 {
p.nextKeepSpaces()
return
}
r := p.r
skipSpace:
for {
switch r {
case utf8.RuneSelf:
p.tok = _EOF
return
case ' ', '\t', '\r':
p.spaced = true
r = p.rune()
case '\n':
if p.tok == _Newl {
2018-02-12 02:02:22 +02:00
// merge consecutive newline tokens
r = p.rune()
continue
2017-04-24 14:47:10 +02:00
}
p.spaced = true
p.tok = _Newl
if p.quote != hdocWord && len(p.heredocs) > p.buriedHdocs {
p.doHeredocs()
2017-04-24 14:47:10 +02:00
}
return
2017-04-24 14:47:10 +02:00
case '\\':
if !p.peekByte('\n') {
break skipSpace
}
p.rune()
r = p.rune()
default:
break skipSpace
}
}
2018-01-21 13:39:15 +02:00
if p.stopAt != nil && (p.spaced || p.tok == illegalTok || stopToken(p.tok)) {
w := utf8.RuneLen(r)
if bytes.HasPrefix(p.bs[p.bsp-w:], p.stopAt) {
p.r = utf8.RuneSelf
p.tok = _EOF
return
}
}
2017-07-31 00:11:34 +02:00
p.pos = p.getPos()
2017-04-24 14:47:10 +02:00
switch {
case p.quote&allRegTokens != 0:
switch r {
case ';', '"', '\'', '(', ')', '$', '|', '&', '>', '<', '`':
p.tok = p.regToken(r)
case '#':
r = p.rune()
p.newLit(r)
for r != utf8.RuneSelf && r != '\n' {
r = p.rune()
}
2017-05-17 19:49:27 +02:00
if p.keepComments {
2017-07-06 01:46:05 +02:00
*p.curComs = append(*p.curComs, Comment{
2017-04-24 14:47:10 +02:00
Hash: p.pos,
Text: p.endLit(),
})
} else {
p.litBs = nil
}
p.next()
2018-01-21 13:39:15 +02:00
case '[', '=':
2017-05-27 16:17:49 +02:00
if p.quote == arrayElems {
2018-01-21 13:39:15 +02:00
p.tok = p.paramToken(r)
2017-05-27 16:17:49 +02:00
} else {
p.advanceLitNone(r)
}
2017-04-24 14:47:10 +02:00
case '?', '*', '+', '@', '!':
if p.peekByte('(') {
switch r {
case '?':
p.tok = globQuest
case '*':
p.tok = globStar
case '+':
p.tok = globPlus
case '@':
p.tok = globAt
default: // '!'
p.tok = globExcl
}
p.rune()
p.rune()
} else {
p.advanceLitNone(r)
}
default:
p.advanceLitNone(r)
}
case p.quote&allArithmExpr != 0 && arithmOps(r):
p.tok = p.arithmToken(r)
case p.quote&allParamExp != 0 && paramOps(r):
p.tok = p.paramToken(r)
case p.quote == testRegexp:
2018-02-12 02:02:22 +02:00
switch r {
case ';', '"', '\'', '$', '&', '>', '<', '`':
2017-04-24 14:47:10 +02:00
p.tok = p.regToken(r)
2018-02-12 02:02:22 +02:00
case ')':
if p.reOpenParens > 0 {
// continuation of open paren
p.advanceLitRe(r)
} else {
p.tok = rightParen
}
default: // including '(', '|'
2017-04-24 14:47:10 +02:00
p.advanceLitRe(r)
}
case regOps(r):
p.tok = p.regToken(r)
default:
p.advanceLitOther(r)
}
if p.err != nil && p.tok != _EOF {
p.tok = _EOF
}
}
2017-05-17 19:49:27 +02:00
func (p *Parser) peekByte(b byte) bool {
2018-02-12 02:02:22 +02:00
if p.bsp == len(p.bs) {
2017-04-24 14:47:10 +02:00
p.fill()
}
2017-07-06 01:46:05 +02:00
return p.bsp < len(p.bs) && p.bs[p.bsp] == b
2017-04-24 14:47:10 +02:00
}
2017-05-17 19:49:27 +02:00
func (p *Parser) regToken(r rune) token {
2017-04-24 14:47:10 +02:00
switch r {
case '\'':
2018-03-04 00:28:08 +02:00
if p.openBquotes > 0 {
// bury openBquotes
p.buriedBquotes = p.openBquotes
p.openBquotes = 0
}
2017-04-24 14:47:10 +02:00
p.rune()
return sglQuote
case '"':
p.rune()
return dblQuote
case '`':
p.rune()
return bckQuote
case '&':
switch p.rune() {
case '&':
p.rune()
return andAnd
case '>':
2017-05-27 16:17:49 +02:00
if p.lang == LangPOSIX {
2017-04-24 14:47:10 +02:00
break
}
if p.rune() == '>' {
p.rune()
return appAll
}
return rdrAll
}
return and
case '|':
switch p.rune() {
case '|':
p.rune()
return orOr
case '&':
2017-05-27 16:17:49 +02:00
if p.lang == LangPOSIX {
2017-04-24 14:47:10 +02:00
break
}
p.rune()
2017-05-27 16:17:49 +02:00
return orAnd
2017-04-24 14:47:10 +02:00
}
return or
case '$':
switch p.rune() {
case '\'':
2017-05-27 16:17:49 +02:00
if p.lang == LangPOSIX {
2017-04-24 14:47:10 +02:00
break
}
p.rune()
return dollSglQuote
case '"':
2017-05-27 16:17:49 +02:00
if p.lang == LangPOSIX {
2017-04-24 14:47:10 +02:00
break
}
p.rune()
return dollDblQuote
case '{':
p.rune()
return dollBrace
case '[':
2018-01-21 13:39:15 +02:00
if p.lang != LangBash || p.quote == paramExpName {
// latter to not tokenise ${$[@]} as $[
2017-04-24 14:47:10 +02:00
break
}
p.rune()
return dollBrack
case '(':
if p.rune() == '(' {
p.rune()
return dollDblParen
}
return dollParen
}
return dollar
case '(':
2017-05-27 16:17:49 +02:00
if p.rune() == '(' && p.lang != LangPOSIX {
2017-04-24 14:47:10 +02:00
p.rune()
return dblLeftParen
}
return leftParen
case ')':
p.rune()
return rightParen
case ';':
switch p.rune() {
case ';':
2017-05-27 16:17:49 +02:00
if p.rune() == '&' && p.lang == LangBash {
2017-04-24 14:47:10 +02:00
p.rune()
2017-05-27 16:17:49 +02:00
return dblSemiAnd
2017-04-24 14:47:10 +02:00
}
return dblSemicolon
case '&':
2017-05-27 16:17:49 +02:00
if p.lang == LangPOSIX {
break
}
p.rune()
return semiAnd
case '|':
if p.lang != LangMirBSDKorn {
2017-04-24 14:47:10 +02:00
break
}
p.rune()
2017-05-27 16:17:49 +02:00
return semiOr
2017-04-24 14:47:10 +02:00
}
return semicolon
case '<':
switch p.rune() {
case '<':
if r = p.rune(); r == '-' {
p.rune()
return dashHdoc
2017-05-27 16:17:49 +02:00
} else if r == '<' && p.lang != LangPOSIX {
2017-04-24 14:47:10 +02:00
p.rune()
return wordHdoc
}
return hdoc
case '>':
p.rune()
return rdrInOut
case '&':
p.rune()
return dplIn
case '(':
2017-05-27 16:17:49 +02:00
if p.lang != LangBash {
2017-04-24 14:47:10 +02:00
break
}
p.rune()
return cmdIn
}
return rdrIn
default: // '>'
switch p.rune() {
case '>':
p.rune()
return appOut
case '&':
p.rune()
return dplOut
case '|':
p.rune()
return clbOut
case '(':
2017-05-27 16:17:49 +02:00
if p.lang != LangBash {
2017-04-24 14:47:10 +02:00
break
}
p.rune()
return cmdOut
}
return rdrOut
}
}
2017-05-17 19:49:27 +02:00
func (p *Parser) dqToken(r rune) token {
2017-04-24 14:47:10 +02:00
switch r {
case '"':
p.rune()
return dblQuote
case '`':
p.rune()
return bckQuote
default: // '$'
switch p.rune() {
case '{':
p.rune()
return dollBrace
case '[':
2017-05-27 16:17:49 +02:00
if p.lang != LangBash {
2017-04-24 14:47:10 +02:00
break
}
p.rune()
return dollBrack
case '(':
if p.rune() == '(' {
p.rune()
return dollDblParen
}
return dollParen
}
return dollar
}
}
2017-05-17 19:49:27 +02:00
func (p *Parser) paramToken(r rune) token {
2017-04-24 14:47:10 +02:00
switch r {
case '}':
p.rune()
return rightBrace
case ':':
switch p.rune() {
case '+':
p.rune()
return colPlus
case '-':
p.rune()
return colMinus
case '?':
p.rune()
return colQuest
case '=':
p.rune()
return colAssgn
}
return colon
case '+':
p.rune()
return plus
case '-':
p.rune()
return minus
case '?':
p.rune()
return quest
case '=':
p.rune()
return assgn
case '%':
if p.rune() == '%' {
p.rune()
return dblPerc
}
return perc
case '#':
if p.rune() == '#' {
p.rune()
return dblHash
}
return hash
case '!':
p.rune()
return exclMark
case '[':
p.rune()
return leftBrack
2018-01-21 13:39:15 +02:00
case ']':
p.rune()
return rightBrack
2017-04-24 14:47:10 +02:00
case '/':
2017-06-04 21:06:04 +02:00
if p.rune() == '/' && p.quote != paramExpRepl {
2017-04-24 14:47:10 +02:00
p.rune()
return dblSlash
}
return slash
case '^':
if p.rune() == '^' {
p.rune()
return dblCaret
}
return caret
case ',':
if p.rune() == ',' {
p.rune()
return dblComma
}
return comma
2018-01-21 13:39:15 +02:00
case '@':
2017-04-24 14:47:10 +02:00
p.rune()
return at
2018-01-21 13:39:15 +02:00
default: // '*'
p.rune()
return star
2017-04-24 14:47:10 +02:00
}
}
2017-05-17 19:49:27 +02:00
func (p *Parser) arithmToken(r rune) token {
2017-04-24 14:47:10 +02:00
switch r {
case '!':
if p.rune() == '=' {
p.rune()
return nequal
}
return exclMark
case '=':
if p.rune() == '=' {
p.rune()
return equal
}
return assgn
case '(':
p.rune()
return leftParen
case ')':
p.rune()
return rightParen
case '&':
switch p.rune() {
case '&':
p.rune()
return andAnd
case '=':
p.rune()
return andAssgn
}
return and
case '|':
switch p.rune() {
case '|':
p.rune()
return orOr
case '=':
p.rune()
return orAssgn
}
return or
case '<':
switch p.rune() {
case '<':
if p.rune() == '=' {
p.rune()
return shlAssgn
}
return hdoc
case '=':
p.rune()
return lequal
}
return rdrIn
case '>':
switch p.rune() {
case '>':
if p.rune() == '=' {
p.rune()
return shrAssgn
}
return appOut
case '=':
p.rune()
return gequal
}
return rdrOut
case '+':
switch p.rune() {
case '+':
p.rune()
return addAdd
case '=':
p.rune()
return addAssgn
}
return plus
case '-':
switch p.rune() {
case '-':
p.rune()
return subSub
case '=':
p.rune()
return subAssgn
}
return minus
case '%':
if p.rune() == '=' {
p.rune()
return remAssgn
}
return perc
case '*':
switch p.rune() {
case '*':
p.rune()
return power
case '=':
p.rune()
return mulAssgn
}
return star
case '/':
if p.rune() == '=' {
p.rune()
return quoAssgn
}
return slash
case '^':
if p.rune() == '=' {
p.rune()
return xorAssgn
}
return caret
2017-05-27 16:17:49 +02:00
case '[':
p.rune()
return leftBrack
2017-04-24 14:47:10 +02:00
case ']':
p.rune()
return rightBrack
case ',':
p.rune()
return comma
case '?':
p.rune()
return quest
2017-05-27 16:17:49 +02:00
case ':':
2017-04-24 14:47:10 +02:00
p.rune()
return colon
2017-05-27 16:17:49 +02:00
default: // '#'
p.rune()
return hash
2017-04-24 14:47:10 +02:00
}
}
2017-05-17 19:49:27 +02:00
func (p *Parser) newLit(r rune) {
2018-06-24 15:40:44 +02:00
switch {
case r < utf8.RuneSelf:
2017-04-24 14:47:10 +02:00
p.litBs = p.litBuf[:1]
p.litBs[0] = byte(r)
2018-06-24 15:40:44 +02:00
case r > utf8.RuneSelf:
2017-04-24 14:47:10 +02:00
w := utf8.RuneLen(r)
2017-07-06 01:46:05 +02:00
p.litBs = append(p.litBuf[:0], p.bs[p.bsp-w:p.bsp]...)
2018-06-24 15:40:44 +02:00
default:
// don't let r == utf8.RuneSelf go to the second case as RuneLen
// would return -1
p.litBs = p.litBuf[:0]
2017-04-24 14:47:10 +02:00
}
}
2017-05-17 19:49:27 +02:00
func (p *Parser) discardLit(n int) { p.litBs = p.litBs[:len(p.litBs)-n] }
2017-04-24 14:47:10 +02:00
2017-05-17 19:49:27 +02:00
func (p *Parser) endLit() (s string) {
2017-04-24 14:47:10 +02:00
if p.r == utf8.RuneSelf {
s = string(p.litBs)
2018-01-03 19:12:40 +02:00
} else {
s = string(p.litBs[:len(p.litBs)-int(p.w)])
2017-04-24 14:47:10 +02:00
}
p.litBs = nil
return
}
2018-02-12 02:02:22 +02:00
func (p *Parser) numLit() bool {
for _, b := range p.litBs {
switch b {
case '>', '<':
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
default:
return false
}
}
return true
}
func (p *Parser) advanceNameCont(r rune) {
// we know that r is a letter or underscore
loop:
for p.newLit(r); r != utf8.RuneSelf; r = p.rune() {
switch {
case r == '\\':
2018-01-03 19:12:40 +02:00
if p.peekByte('\n') {
p.rune()
p.discardLit(2)
2018-01-03 19:12:40 +02:00
} else {
break loop
}
case 'a' <= r && r <= 'z':
case 'A' <= r && r <= 'Z':
case r == '_':
case '0' <= r && r <= '9':
default:
break loop
}
}
p.tok, p.val = _LitWord, p.endLit()
}
2017-05-17 19:49:27 +02:00
func (p *Parser) advanceLitOther(r rune) {
2017-04-24 14:47:10 +02:00
tok := _LitWord
loop:
for p.newLit(r); r != utf8.RuneSelf; r = p.rune() {
switch r {
case '\\': // escaped byte follows
if r = p.rune(); r == '\n' {
p.discardLit(2)
}
case '"', '`', '$':
2018-02-12 02:02:22 +02:00
tok = _Lit
break loop
2017-04-24 14:47:10 +02:00
case '}':
if p.quote&allParamExp != 0 {
break loop
}
case '/':
if p.quote&allParamExp != 0 && p.quote != paramExpExp {
break loop
}
2018-01-21 13:39:15 +02:00
case ':', '=', '%', '^', ',', '?', '!', '*':
if p.quote&allArithmExpr != 0 || p.quote == paramExpName {
2017-05-01 00:50:22 +02:00
break loop
}
2018-04-28 20:35:15 +02:00
case '[', ']':
if p.lang != LangPOSIX && p.quote&allArithmExpr != 0 {
2017-04-24 14:47:10 +02:00
break loop
}
2018-04-28 20:35:15 +02:00
fallthrough
case '#', '@':
if p.quote&allParamReg != 0 {
2017-05-17 19:49:27 +02:00
break loop
}
2018-02-12 02:02:22 +02:00
case '\'', '+', '-', ' ', '\t', ';', '&', '>', '<', '|', '(', ')', '\n', '\r':
if p.quote&allKeepSpaces == 0 {
2017-04-24 14:47:10 +02:00
break loop
}
}
}
p.tok, p.val = tok, p.endLit()
}
2017-05-17 19:49:27 +02:00
func (p *Parser) advanceLitNone(r rune) {
2017-07-31 00:11:34 +02:00
p.eqlOffs = 0
2017-04-24 14:47:10 +02:00
tok := _LitWord
loop:
for p.newLit(r); r != utf8.RuneSelf; r = p.rune() {
switch r {
case ' ', '\t', '\n', '\r', '&', '|', ';', '(', ')':
break loop
case '\\': // escaped byte follows
2018-02-12 02:02:22 +02:00
if r = p.rune(); r == '\n' {
2017-04-24 14:47:10 +02:00
p.discardLit(2)
}
case '>', '<':
2018-02-12 02:02:22 +02:00
if p.peekByte('(') || !p.numLit() {
2017-04-24 14:47:10 +02:00
tok = _Lit
} else {
tok = _LitRedir
}
break loop
case '`':
if p.quote != subCmdBckquo {
tok = _Lit
}
break loop
case '"', '\'', '$':
tok = _Lit
break loop
case '?', '*', '+', '@', '!':
if p.peekByte('(') {
tok = _Lit
break loop
}
case '=':
2018-01-03 19:12:40 +02:00
if p.eqlOffs == 0 {
p.eqlOffs = len(p.litBs) - 1
}
2017-05-17 19:49:27 +02:00
case '[':
2017-05-27 16:17:49 +02:00
if p.lang != LangPOSIX && len(p.litBs) > 1 && p.litBs[0] != '[' {
2017-05-17 19:49:27 +02:00
tok = _Lit
break loop
}
2017-04-24 14:47:10 +02:00
}
}
p.tok, p.val = tok, p.endLit()
}
2017-05-17 19:49:27 +02:00
func (p *Parser) advanceLitDquote(r rune) {
2017-04-24 14:47:10 +02:00
tok := _LitWord
loop:
for p.newLit(r); r != utf8.RuneSelf; r = p.rune() {
switch r {
case '"':
break loop
case '\\': // escaped byte follows
p.rune()
case '`', '$':
tok = _Lit
break loop
}
}
p.tok, p.val = tok, p.endLit()
}
2017-05-17 19:49:27 +02:00
func (p *Parser) advanceLitHdoc(r rune) {
2017-04-24 14:47:10 +02:00
p.tok = _Lit
p.newLit(r)
if p.quote == hdocBodyTabs {
for r == '\t' {
2018-04-28 20:35:15 +02:00
p.discardLit(1)
2017-04-24 14:47:10 +02:00
r = p.rune()
}
}
lStart := len(p.litBs) - 1
2018-06-24 15:40:44 +02:00
if lStart < 0 {
return
}
2017-05-27 16:17:49 +02:00
for ; ; r = p.rune() {
2017-04-24 14:47:10 +02:00
switch r {
case '`', '$':
2017-05-27 16:17:49 +02:00
p.val = p.endLit()
return
2017-04-24 14:47:10 +02:00
case '\\': // escaped byte follows
p.rune()
2017-05-27 16:17:49 +02:00
case '\n', utf8.RuneSelf:
if bytes.HasPrefix(p.litBs[lStart:], p.hdocStop) {
2017-04-24 14:47:10 +02:00
p.val = p.endLit()[:lStart]
2017-05-27 16:17:49 +02:00
if p.val == "" {
p.tok = _Newl
2017-05-27 16:17:49 +02:00
}
2017-04-24 14:47:10 +02:00
p.hdocStop = nil
return
}
2017-05-27 16:17:49 +02:00
if r == utf8.RuneSelf {
return
}
2017-04-24 14:47:10 +02:00
if p.quote == hdocBodyTabs {
for p.peekByte('\t') {
p.rune()
2018-04-28 20:35:15 +02:00
p.discardLit(1)
2017-04-24 14:47:10 +02:00
}
}
lStart = len(p.litBs)
}
}
}
2017-05-17 19:49:27 +02:00
func (p *Parser) hdocLitWord() *Word {
2017-04-24 14:47:10 +02:00
r := p.r
p.newLit(r)
2017-05-27 16:17:49 +02:00
pos := p.getPos()
for ; ; r = p.rune() {
2017-04-24 14:47:10 +02:00
if r == utf8.RuneSelf {
2017-05-27 16:17:49 +02:00
return nil
2017-04-24 14:47:10 +02:00
}
if p.quote == hdocBodyTabs {
for r == '\t' {
2018-04-28 20:35:15 +02:00
p.discardLit(1)
2017-04-24 14:47:10 +02:00
r = p.rune()
}
}
lStart := len(p.litBs) - 1
2018-06-24 15:40:44 +02:00
if lStart < 0 {
return nil
}
2017-04-24 14:47:10 +02:00
for r != utf8.RuneSelf && r != '\n' {
r = p.rune()
}
2017-05-27 16:17:49 +02:00
if bytes.HasPrefix(p.litBs[lStart:], p.hdocStop) {
p.hdocStop = nil
val := p.endLit()[:lStart]
if val == "" {
return nil
}
return p.word(p.wps(p.lit(pos, val)))
2017-04-24 14:47:10 +02:00
}
}
}
2017-05-17 19:49:27 +02:00
func (p *Parser) advanceLitRe(r rune) {
2018-02-12 02:02:22 +02:00
for p.newLit(r); ; r = p.rune() {
2017-04-24 14:47:10 +02:00
switch r {
case '\\':
p.rune()
case '(':
2018-02-12 02:02:22 +02:00
p.reOpenParens++
2017-04-24 14:47:10 +02:00
case ')':
2018-02-12 02:02:22 +02:00
if p.reOpenParens--; p.reOpenParens < 0 {
p.tok, p.val = _LitWord, p.endLit()
return
}
case ' ', '\t', '\r', '\n':
if p.reOpenParens <= 0 {
p.tok, p.val = _LitWord, p.endLit()
return
2017-04-24 14:47:10 +02:00
}
2018-02-12 02:02:22 +02:00
case utf8.RuneSelf, ';', '"', '\'', '$', '&', '>', '<', '`':
p.tok, p.val = _LitWord, p.endLit()
return
2017-04-24 14:47:10 +02:00
}
}
}
2017-06-04 21:06:04 +02:00
func testUnaryOp(val string) UnTestOperator {
2017-04-24 14:47:10 +02:00
switch val {
case "!":
2017-06-04 21:06:04 +02:00
return TsNot
2017-04-24 14:47:10 +02:00
case "-e", "-a":
2017-06-04 21:06:04 +02:00
return TsExists
2017-04-24 14:47:10 +02:00
case "-f":
2017-06-04 21:06:04 +02:00
return TsRegFile
2017-04-24 14:47:10 +02:00
case "-d":
2017-06-04 21:06:04 +02:00
return TsDirect
2017-04-24 14:47:10 +02:00
case "-c":
2017-06-04 21:06:04 +02:00
return TsCharSp
2017-04-24 14:47:10 +02:00
case "-b":
2017-06-04 21:06:04 +02:00
return TsBlckSp
2017-04-24 14:47:10 +02:00
case "-p":
2017-06-04 21:06:04 +02:00
return TsNmPipe
2017-04-24 14:47:10 +02:00
case "-S":
2017-06-04 21:06:04 +02:00
return TsSocket
2017-04-24 14:47:10 +02:00
case "-L", "-h":
2017-06-04 21:06:04 +02:00
return TsSmbLink
2017-04-24 14:47:10 +02:00
case "-k":
2017-06-04 21:06:04 +02:00
return TsSticky
2017-04-24 14:47:10 +02:00
case "-g":
2017-06-04 21:06:04 +02:00
return TsGIDSet
2017-04-24 14:47:10 +02:00
case "-u":
2017-06-04 21:06:04 +02:00
return TsUIDSet
2017-04-24 14:47:10 +02:00
case "-G":
2017-06-04 21:06:04 +02:00
return TsGrpOwn
2017-04-24 14:47:10 +02:00
case "-O":
2017-06-04 21:06:04 +02:00
return TsUsrOwn
2017-04-24 14:47:10 +02:00
case "-N":
2017-06-04 21:06:04 +02:00
return TsModif
2017-04-24 14:47:10 +02:00
case "-r":
2017-06-04 21:06:04 +02:00
return TsRead
2017-04-24 14:47:10 +02:00
case "-w":
2017-06-04 21:06:04 +02:00
return TsWrite
2017-04-24 14:47:10 +02:00
case "-x":
2017-06-04 21:06:04 +02:00
return TsExec
2017-04-24 14:47:10 +02:00
case "-s":
2017-06-04 21:06:04 +02:00
return TsNoEmpty
2017-04-24 14:47:10 +02:00
case "-t":
2017-06-04 21:06:04 +02:00
return TsFdTerm
2017-04-24 14:47:10 +02:00
case "-z":
2017-06-04 21:06:04 +02:00
return TsEmpStr
2017-04-24 14:47:10 +02:00
case "-n":
2017-06-04 21:06:04 +02:00
return TsNempStr
2017-04-24 14:47:10 +02:00
case "-o":
2017-06-04 21:06:04 +02:00
return TsOptSet
2017-04-24 14:47:10 +02:00
case "-v":
2017-06-04 21:06:04 +02:00
return TsVarSet
2017-04-24 14:47:10 +02:00
case "-R":
2017-06-04 21:06:04 +02:00
return TsRefVar
2017-04-24 14:47:10 +02:00
default:
2017-06-04 21:06:04 +02:00
return 0
2017-04-24 14:47:10 +02:00
}
}
2017-06-04 21:06:04 +02:00
func testBinaryOp(val string) BinTestOperator {
2017-04-24 14:47:10 +02:00
switch val {
case "==", "=":
2017-06-04 21:06:04 +02:00
return TsMatch
2017-04-24 14:47:10 +02:00
case "!=":
2017-06-04 21:06:04 +02:00
return TsNoMatch
2017-04-24 14:47:10 +02:00
case "=~":
2017-06-04 21:06:04 +02:00
return TsReMatch
2017-04-24 14:47:10 +02:00
case "-nt":
2017-06-04 21:06:04 +02:00
return TsNewer
2017-04-24 14:47:10 +02:00
case "-ot":
2017-06-04 21:06:04 +02:00
return TsOlder
2017-04-24 14:47:10 +02:00
case "-ef":
2017-06-04 21:06:04 +02:00
return TsDevIno
2017-04-24 14:47:10 +02:00
case "-eq":
2017-06-04 21:06:04 +02:00
return TsEql
2017-04-24 14:47:10 +02:00
case "-ne":
2017-06-04 21:06:04 +02:00
return TsNeq
2017-04-24 14:47:10 +02:00
case "-le":
2017-06-04 21:06:04 +02:00
return TsLeq
2017-04-24 14:47:10 +02:00
case "-ge":
2017-06-04 21:06:04 +02:00
return TsGeq
2017-04-24 14:47:10 +02:00
case "-lt":
2017-06-04 21:06:04 +02:00
return TsLss
2017-04-24 14:47:10 +02:00
case "-gt":
2017-06-04 21:06:04 +02:00
return TsGtr
2017-04-24 14:47:10 +02:00
default:
2017-06-04 21:06:04 +02:00
return 0
2017-04-24 14:47:10 +02:00
}
}