1
0
mirror of https://github.com/go-task/task.git synced 2025-03-03 14:52:13 +02:00

Update vendor of github.com/mvdan/sh

This commit is contained in:
Andrey Nering 2017-04-30 19:50:22 -03:00
parent bb84b067c5
commit 86e0496555
13 changed files with 290 additions and 257 deletions

View File

@ -11,8 +11,8 @@ import (
func (r *Runner) arithm(expr syntax.ArithmExpr) int {
switch x := expr.(type) {
case *syntax.Word:
str := r.loneWord(x)
case *syntax.Lit:
str := x.Value
// recursively fetch vars
for {
val := r.getVar(str)
@ -23,12 +23,14 @@ func (r *Runner) arithm(expr syntax.ArithmExpr) int {
}
// default to 0
return atoi(str)
case *syntax.ParamExp:
return atoi(r.paramExp(x))
case *syntax.ParenArithm:
return r.arithm(x.X)
case *syntax.UnaryArithm:
switch x.Op {
case syntax.Inc, syntax.Dec:
name := x.X.(*syntax.Word).Parts[0].(*syntax.Lit).Value
name := x.X.(*syntax.Lit).Value
old := atoi(r.getVar(name))
val := old
if x.Op == syntax.Inc {
@ -81,7 +83,7 @@ func atoi(s string) int {
}
func (r *Runner) assgnArit(b *syntax.BinaryArithm) int {
name := b.X.(*syntax.Word).Parts[0].(*syntax.Lit).Value
name := b.X.(*syntax.Lit).Value
val := atoi(r.getVar(name))
arg := r.arithm(b.Y)
switch b.Op {
@ -105,7 +107,7 @@ func (r *Runner) assgnArit(b *syntax.BinaryArithm) int {
val ^= arg
case syntax.ShlAssgn:
val <<= uint(arg)
default: // syntax.ShrAssgn
case syntax.ShrAssgn:
val >>= uint(arg)
}
r.setVar(name, strconv.Itoa(val))

View File

@ -5,13 +5,26 @@ package interp
import (
"os"
"os/exec"
"path/filepath"
"strconv"
"github.com/mvdan/sh/syntax"
)
func (r *Runner) builtin(pos syntax.Pos, name string, args []string) bool {
func isBuiltin(name string) bool {
switch name {
case "true", ":", "false", "exit", "set", "shift", "unset",
"echo", "printf", "break", "continue", "pwd", "cd",
"wait", "builtin", "trap", "type", "source", "command",
"pushd", "popd", "umask", "alias", "unalias", "fg", "bg",
"getopts":
return true
}
return false
}
func (r *Runner) builtin(pos syntax.Pos, name string, args []string) {
exit := 0
switch name {
case "true", ":":
@ -46,6 +59,7 @@ func (r *Runner) builtin(pos syntax.Pos, name string, args []string) bool {
default:
r.errf("usage: shift [n]\n")
exit = 2
break
}
if len(r.args) < n {
n = len(r.args)
@ -160,20 +174,28 @@ func (r *Runner) builtin(pos syntax.Pos, name string, args []string) bool {
if len(args) < 1 {
break
}
// TODO: pos
if !r.builtin(0, args[0], args[1:]) {
if !isBuiltin(args[0]) {
exit = 1
break
}
case "trap", "type", "source", "command", "pushd", "popd",
// TODO: pos
r.builtin(0, args[0], args[1:])
case "type":
for _, arg := range args {
if isBuiltin(arg) {
r.outf("%s is a shell builtin\n", arg)
continue
}
if path, err := exec.LookPath(arg); err == nil {
r.outf("%s is %s\n", arg, path)
continue
}
exit = 1
r.errf("type: %s: not found\n", arg)
}
case "trap", "source", "command", "pushd", "popd",
"umask", "alias", "unalias", "fg", "bg", "getopts":
r.errf("unhandled builtin: %s", name)
// TODO(mvdan): we rely on the binary versions of these, we
// should eventually implement them as builtins like Bash for
// portability
// case "[", "test":
default:
return false
}
r.exit = exit
return true
}

View File

@ -98,12 +98,10 @@ func (r *Runner) varInd(v varValue, e syntax.ArithmExpr) string {
}
case []string:
// TODO: @ between double quotes
if w, ok := e.(*syntax.Word); ok {
if lit, ok := w.Parts[0].(*syntax.Lit); ok {
switch lit.Value {
case "@", "*":
return strings.Join(x, " ")
}
if lit, ok := e.(*syntax.Lit); ok {
switch lit.Value {
case "@", "*":
return strings.Join(x, " ")
}
}
i := r.arithm(e)
@ -373,6 +371,7 @@ func (r *Runner) cmd(cm syntax.Command) {
r.stmts(x.ThenStmts)
return
}
r.exit = 0
for _, el := range x.Elifs {
r.stmts(el.CondStmts)
if r.exit == 0 {
@ -381,9 +380,6 @@ func (r *Runner) cmd(cm syntax.Command) {
}
}
r.stmts(x.ElseStmts)
if len(x.Elifs)+len(x.ElseStmts) == 0 {
r.exit = 0
}
case *syntax.WhileClause:
for r.err == nil {
r.stmts(x.CondStmts)
@ -559,10 +555,16 @@ func (r *Runner) wordParts(wps []syntax.WordPart, quoted bool) []string {
curBuf.WriteString(field)
}
}
for _, wp := range wps {
for i, wp := range wps {
switch x := wp.(type) {
case *syntax.Lit:
curBuf.WriteString(x.Value)
s := x.Value
if i > 0 || len(s) == 0 || s[0] != '~' {
} else if len(s) < 2 || s[1] == '/' {
// TODO: ~someuser
s = r.getVar("HOME") + s[1:]
}
curBuf.WriteString(s)
case *syntax.SglQuoted:
curBuf.WriteString(x.Value)
case *syntax.DblQuoted:
@ -619,7 +621,8 @@ func (r *Runner) call(pos syntax.Pos, name string, args []string) {
r.args = oldArgs
return
}
if r.builtin(pos, name, args) {
if isBuiltin(name) {
r.builtin(pos, name, args)
return
}
cmd := exec.CommandContext(r.Context, name, args...)

View File

@ -40,7 +40,7 @@ func (r *Runner) paramExp(pe *syntax.ParamExp) string {
switch {
case pe.Length:
str = strconv.Itoa(utf8.RuneCountInString(str))
case pe.Excl:
case pe.Indirect:
val, set = r.lookupVar(str)
str = varStr(val)
}
@ -141,7 +141,7 @@ func (r *Runner) paramExp(pe *syntax.ParamExp) string {
str = string(rs)
case syntax.LowerAll:
str = strings.ToLower(str)
default: // syntax.OtherParamOps
case syntax.OtherParamOps:
switch arg {
case "Q":
str = strconv.Quote(str)

View File

@ -74,10 +74,10 @@ func (r *Runner) binTest(op syntax.BinTestOperator, x, y string) bool {
return x != "" && y != ""
case syntax.OrTest:
return x != "" || y != ""
case syntax.TsEqual:
case syntax.TsMatch:
m, _ := path.Match(y, x)
return m
case syntax.TsNequal:
case syntax.TsNoMatch:
m, _ := path.Match(y, x)
return !m
case syntax.TsBefore:

View File

@ -25,9 +25,9 @@ case $foo in
esac
foo | bar
foo \
&& $(bar) \
&& (more)
foo &&
$(bar) &&
(more)
foo 2>&1
foo <<EOF

View File

@ -732,15 +732,37 @@ loop:
if p.quote&allArithmExpr != 0 {
break loop
}
case ':', '=', '%', '?', '^', ',':
if p.quote == paramName && p.peekByte('(') {
tok = _Lit
break loop
}
case '?':
if p.quote == paramName && p.peekByte('(') {
tok = _Lit
break loop
}
fallthrough
case ':', '=', '%', '^', ',':
if p.quote&allArithmExpr != 0 || p.quote&allParamReg != 0 {
break loop
}
case '#', '[', '@':
case '@':
if p.quote == paramName && p.peekByte('(') {
tok = _Lit
break loop
}
fallthrough
case '#', '[':
if p.quote&allParamReg != 0 {
break loop
}
case '+', '-':
case '+':
if p.quote == paramName && p.peekByte('(') {
tok = _Lit
break loop
}
fallthrough
case '-':
switch p.quote {
case paramExpInd, paramExpLen, paramExpOff,
paramExpExp, paramExpRepl, sglQuotes:
@ -778,7 +800,7 @@ loop:
p.discardLit(1)
if r = p.rune(); r == '\\' {
p.discardLit(1)
r = p.rune()
p.rune()
}
}
}
@ -893,7 +915,7 @@ func (p *parser) hdocLitWord() *Word {
r = p.rune()
}
l := p.lit(pos, val)
return p.word(p.singleWps(l))
return p.word(p.wps(l))
}
func (p *parser) advanceLitRe(r rune) {

View File

@ -144,6 +144,10 @@ func (s *Stmt) End() Pos {
// Command represents all nodes that are simple commands, which are
// directly placed in a Stmt.
//
// These are *CallExpr, *IfClause, *WhileClause, *UntilClause,
// *ForClause, *CaseClause, *Block, *Subshell, *BinaryCmd, *FuncDecl,
// *ArithmCmd, *TestClause, *DeclClause, *LetClause, and *CoprocClause.
type Command interface {
Node
commandNode()
@ -162,7 +166,6 @@ func (*FuncDecl) commandNode() {}
func (*ArithmCmd) commandNode() {}
func (*TestClause) commandNode() {}
func (*DeclClause) commandNode() {}
func (*EvalClause) commandNode() {}
func (*LetClause) commandNode() {}
func (*CoprocClause) commandNode() {}
@ -280,7 +283,7 @@ type ForClause struct {
func (f *ForClause) Pos() Pos { return f.For }
func (f *ForClause) End() Pos { return f.Done + 4 }
// Loop represents all nodes that can be loops in a for clause.
// Loop holds either *WordIter or *CStyleLoop.
type Loop interface {
Node
loopNode()
@ -342,6 +345,9 @@ func (w *Word) Pos() Pos { return w.Parts[0].Pos() }
func (w *Word) End() Pos { return w.Parts[len(w.Parts)-1].End() }
// WordPart represents all nodes that can form a word.
//
// These are *Lit, *SglQuoted, *DblQuoted, *ParamExp, *CmdSubst,
// *ArithmExp, *ProcSubst, *ArrayExpr, and *ExtGlob.
type WordPart interface {
Node
wordPartNode()
@ -414,7 +420,8 @@ func (c *CmdSubst) End() Pos { return c.Right + 1 }
type ParamExp struct {
Dollar, Rbrace Pos
Short bool
Length, Excl bool // TODO(mvdan): rename Excl in 2.0 (Indirect, etc)
Indirect bool
Length bool
Param *Lit
Ind *Index
Slice *Slice
@ -484,6 +491,9 @@ func (a *ArithmCmd) Pos() Pos { return a.Left }
func (a *ArithmCmd) End() Pos { return a.Right + 2 }
// ArithmExpr represents all nodes that form arithmetic expressions.
//
// These are *BinaryArithm, *UnaryArithm, *ParenArithm, *Lit, and
// *ParamExp.
type ArithmExpr interface {
Node
arithmExprNode()
@ -492,20 +502,18 @@ type ArithmExpr interface {
func (*BinaryArithm) arithmExprNode() {}
func (*UnaryArithm) arithmExprNode() {}
func (*ParenArithm) arithmExprNode() {}
func (*Word) arithmExprNode() {}
func (*Lit) arithmExprNode() {}
func (*ParamExp) arithmExprNode() {}
// BinaryArithm represents a binary expression between two arithmetic
// expression.
//
// If Op is any assign operator, X will be a *Word with a single *Lit
// whose value is a valid name.
// If Op is any assign operator, X will be a *Lit whose value is a valid
// name.
//
// Ternary operators like "a ? b : c" are fit into this structure. Thus,
// if Op == Quest, Y will be a *BinaryArithm with Op == Colon. Op can
// only be Colon in that scenario.
//
// TODO(mvdan): we probably want to split up assigns in 2.0 (X would be
// a *Lit) to simplify the rules here. Perhaps reuse the Assign type?
type BinaryArithm struct {
OpPos Pos
Op BinAritOperator
@ -518,11 +526,7 @@ func (b *BinaryArithm) End() Pos { return b.Y.End() }
// UnaryArithm represents an unary expression over a node, either before
// or after it.
//
// If Op is Inc or Dec, X will be a *Word with a single *Lit whose value
// is a valid name.
//
// TODO(mvdan): consider splitting up Inc/Dec like the assigns above in
// 2.0.
// If Op is Inc or Dec, X will be a *Lit whose value is a valid name.
type UnaryArithm struct {
OpPos Pos
Op UnAritOperator
@ -584,6 +588,8 @@ func (t *TestClause) Pos() Pos { return t.Left }
func (t *TestClause) End() Pos { return t.Right + 2 }
// TestExpr represents all nodes that form arithmetic expressions.
//
// These are *BinaryTest, *UnaryTest, *ParenTest, and *Word.
type TestExpr interface {
Node
testExprNode()
@ -681,25 +687,6 @@ type ProcSubst struct {
func (s *ProcSubst) Pos() Pos { return s.OpPos }
func (s *ProcSubst) End() Pos { return s.Rparen + 1 }
// EvalClause represents a Bash eval clause.
//
// This node will never appear when in PosixConformant mode.
//
// TODO(mvdan): EvalClause is actually pointless, as any non-trivial use
// of eval will involve parsing the program at run-time. Remove in 2.0.
type EvalClause struct {
Eval Pos
Stmt *Stmt
}
func (e *EvalClause) Pos() Pos { return e.Eval }
func (e *EvalClause) End() Pos {
if e.Stmt != nil {
return e.Stmt.End()
}
return e.Eval + 4
}
// CoprocClause represents a Bash coproc clause.
//
// This node will never appear when in PosixConformant mode.

View File

@ -132,7 +132,7 @@ func (p *parser) word(parts []WordPart) *Word {
return w
}
func (p *parser) singleWps(wp WordPart) []WordPart {
func (p *parser) wps(wp WordPart) []WordPart {
if len(p.wpsBatch) == 0 {
p.wpsBatch = make([]WordPart, 64)
}
@ -142,15 +142,6 @@ func (p *parser) singleWps(wp WordPart) []WordPart {
return wps
}
func (p *parser) wps() []WordPart {
if len(p.wpsBatch) < 4 {
p.wpsBatch = make([]WordPart, 64)
}
wps := p.wpsBatch[:0:4]
p.wpsBatch = p.wpsBatch[4:]
return wps
}
func (p *parser) stmt(pos Pos) *Stmt {
if len(p.stmtBatch) == 0 {
p.stmtBatch = make([]Stmt, 16)
@ -204,6 +195,7 @@ const (
arithmExprBrack
testRegexp
switchCase
paramName
paramExpName
paramExpInd
paramExpOff
@ -218,7 +210,7 @@ const (
arithmExprBrack | allParamArith
allRbrack = arithmExprBrack | paramExpInd
allParamArith = paramExpInd | paramExpOff | paramExpLen
allParamReg = paramExpName | allParamArith
allParamReg = paramName | paramExpName | allParamArith
allParamExp = allParamReg | paramExpRepl | paramExpExp
)
@ -498,11 +490,6 @@ func (p *parser) invalidStmtStart() {
}
func (p *parser) getWord() *Word {
if p.tok == _LitWord {
w := p.word(p.singleWps(p.lit(p.pos, p.val)))
p.next()
return w
}
if parts := p.wordParts(); len(parts) > 0 {
return p.word(parts)
}
@ -514,7 +501,7 @@ func (p *parser) getWordOrEmpty() *Word {
if len(parts) == 0 {
l := p.lit(p.pos, "")
l.ValueEnd = l.ValuePos // force Lit.Pos() == Lit.End()
return p.word(p.singleWps(l))
return p.word(p.wps(l))
}
return p.word(parts)
}
@ -536,9 +523,10 @@ func (p *parser) wordParts() (wps []WordPart) {
return
}
if wps == nil {
wps = p.wps()
wps = p.wps(n)
} else {
wps = append(wps, n)
}
wps = append(wps, n)
if p.spaced {
return
}
@ -607,11 +595,10 @@ func (p *parser) wordPart() WordPart {
p.rune()
p.tok, p.val = _LitWord, string(r)
default:
if p.quote&allRegTokens != 0 {
p.advanceLitNone(r)
} else {
p.advanceLitOther(r)
}
old := p.quote
p.quote = paramName
p.advanceLitOther(r)
p.quote = old
}
pe.Param = p.getLit()
return pe
@ -665,12 +652,7 @@ func (p *parser) wordPart() WordPart {
old := p.quote
p.quote = dblQuotes
p.next()
if p.tok == _LitWord {
q.Parts = p.singleWps(p.lit(p.pos, p.val))
p.next()
} else {
q.Parts = p.wordParts()
}
q.Parts = p.wordParts()
p.quote = old
if !p.got(dblQuote) {
p.quoteErr(q.Pos(), dblQuote)
@ -803,7 +785,7 @@ func (p *parser) arithmExpr(ftok token, fpos Pos, level int, compact, tern bool)
}
case AddAssgn, SubAssgn, MulAssgn, QuoAssgn, RemAssgn, AndAssgn,
OrAssgn, XorAssgn, ShlAssgn, ShrAssgn, Assgn:
if w, ok := b.X.(*Word); !ok || !p.wordIdent(w) {
if l, ok := b.X.(*Lit); !ok || !validIdent(l.Value, p.bash()) {
p.posErr(b.OpPos, "%s must follow a name", b.Op.String())
}
}
@ -822,14 +804,6 @@ func (p *parser) arithmExpr(ftok token, fpos Pos, level int, compact, tern bool)
return b
}
func (p *parser) wordIdent(w *Word) bool {
if len(w.Parts) != 1 {
return false
}
lit, ok := w.Parts[0].(*Lit)
return ok && validIdent(lit.Value, p.bash())
}
func (p *parser) arithmExprBase(compact bool) ArithmExpr {
var x ArithmExpr
switch p.tok {
@ -843,7 +817,11 @@ func (p *parser) arithmExprBase(compact bool) ArithmExpr {
case addAdd, subSub:
ue := &UnaryArithm{OpPos: p.pos, Op: UnAritOperator(p.tok)}
p.next()
ue.X = p.followWordTok(token(ue.Op), ue.OpPos)
if lit := p.getLit(); lit == nil {
p.followErr(ue.OpPos, token(ue.Op).String(), "a literal")
} else {
ue.X = lit
}
return ue
case leftParen:
pe := &ParenArithm{Lparen: p.pos}
@ -864,23 +842,27 @@ func (p *parser) arithmExprBase(compact bool) ArithmExpr {
p.followErrExp(ue.OpPos, ue.Op.String())
}
x = ue
case illegalTok, rightBrack, rightBrace, rightParen:
case _LitWord:
x = p.getLit()
case dollar, dollBrace:
x = p.wordPart().(*ParamExp)
case bckQuote:
if p.quote == arithmExprLet {
return nil
}
fallthrough
default:
if w := p.getWord(); w != nil {
// we want real nil, not (*Word)(nil) as that
// sets the type to non-nil and then x != nil
x = w
if arithmOpLevel(BinAritOperator(p.tok)) >= 0 {
break
}
p.curErr("arithmetic expressions must consist of names and numbers")
}
if compact && p.spaced {
return x
}
if p.tok == addAdd || p.tok == subSub {
if w, ok := x.(*Word); !ok || !p.wordIdent(w) {
if l, ok := x.(*Lit); !ok || !validIdent(l.Value, p.bash()) {
p.curErr("%s must follow a name", p.tok.String())
}
u := &UnaryArithm{
@ -914,7 +896,7 @@ func (p *parser) paramExp() *ParamExp {
}
case exclMark:
if p.r != '}' {
pe.Excl = true
pe.Indirect = true
p.next()
}
}
@ -1089,7 +1071,7 @@ func (p *parser) getAssign() *Assign {
start := p.lit(p.pos+1, p.val[p.asPos+1:])
if start.Value != "" {
start.ValuePos += Pos(p.asPos)
as.Value = p.word(p.singleWps(start))
as.Value = p.word(p.wps(start))
}
if p.next(); p.spaced {
return as
@ -1108,7 +1090,7 @@ func (p *parser) getAssign() *Assign {
}
}
ae.Rparen = p.matched(ae.Lparen, leftParen, rightParen)
as.Value = p.word(p.singleWps(ae))
as.Value = p.word(p.wps(ae))
} else if !p.newLine && !stopToken(p.tok) {
if w := p.getWord(); w != nil {
if as.Value == nil {
@ -1227,55 +1209,66 @@ preLoop:
return
}
func bashDeclareWord(s string) bool {
switch s {
case "declare", "local", "export", "readonly", "typeset", "nameref":
return true
}
return false
}
func (p *parser) gotStmtPipe(s *Stmt) *Stmt {
switch p.tok {
case leftParen:
s.Cmd = p.subshell()
case dblLeftParen:
s.Cmd = p.arithmExpCmd()
case _LitWord:
switch {
case p.val == "}":
p.curErr("%s can only be used to close a block", p.val)
case p.val == "{":
switch p.val {
case "{":
s.Cmd = p.block()
case p.val == "if":
case "if":
s.Cmd = p.ifClause()
case p.val == "while":
case "while":
s.Cmd = p.whileClause()
case p.val == "until":
case "until":
s.Cmd = p.untilClause()
case p.val == "for":
case "for":
s.Cmd = p.forClause()
case p.val == "case":
case "case":
s.Cmd = p.caseClause()
case p.bash() && p.val == "[[":
s.Cmd = p.testClause()
case p.bash() && bashDeclareWord(p.val):
s.Cmd = p.declClause()
case p.bash() && p.val == "eval":
s.Cmd = p.evalClause()
case p.bash() && p.val == "coproc":
s.Cmd = p.coprocClause()
case p.bash() && p.val == "let":
s.Cmd = p.letClause()
case p.bash() && p.val == "function":
s.Cmd = p.bashFuncDecl()
case "}":
p.curErr(`%s can only be used to close a block`, p.val)
case "]]":
if !p.bash() {
break
}
p.curErr(`%s can only be used to close a test`, p.val)
case "then":
p.curErr(`%q can only be used in an if`, p.val)
case "elif":
p.curErr(`%q can only be used in an if`, p.val)
case "fi":
p.curErr(`%q can only be used to end an if`, p.val)
case "do":
p.curErr(`%q can only be used in a loop`, p.val)
case "done":
p.curErr(`%q can only be used to end a loop`, p.val)
case "esac":
p.curErr(`%q can only be used to end a case`, p.val)
default:
if !p.bash() {
break
}
switch p.val {
case "[[":
s.Cmd = p.testClause()
case "declare", "local", "export", "readonly",
"typeset", "nameref":
s.Cmd = p.declClause()
case "coproc":
s.Cmd = p.coprocClause()
case "let":
s.Cmd = p.letClause()
case "function":
s.Cmd = p.bashFuncDecl()
}
}
if s.Cmd == nil {
name := p.lit(p.pos, p.val)
if p.next(); p.gotSameLine(leftParen) {
p.follow(name.ValuePos, "foo(", rightParen)
s.Cmd = p.funcDecl(name, name.ValuePos)
} else {
s.Cmd = p.callExpr(s, p.word(p.singleWps(name)))
s.Cmd = p.callExpr(s, p.word(p.wps(name)))
}
}
case bckQuote:
@ -1291,6 +1284,10 @@ func (p *parser) gotStmtPipe(s *Stmt) *Stmt {
p.posErr(w.Pos(), "invalid func name")
}
s.Cmd = p.callExpr(s, w)
case leftParen:
s.Cmd = p.subshell()
case dblLeftParen:
s.Cmd = p.arithmExpCmd()
}
for !p.newLine && p.peekRedir() {
p.doRedirect(s)
@ -1625,24 +1622,15 @@ func (p *parser) declClause() *DeclClause {
return ds
}
func (p *parser) evalClause() *EvalClause {
ec := &EvalClause{Eval: p.pos}
p.next()
ec.Stmt, _ = p.getStmt(false, false)
return ec
}
func isBashCompoundCommand(tok token, val string) bool {
switch tok {
case leftParen, dblLeftParen:
return true
case _LitWord:
switch val {
case "{", "if", "while", "until", "for", "case", "[[", "eval",
"coproc", "let", "function":
return true
}
if bashDeclareWord(val) {
case "{", "if", "while", "until", "for", "case", "[[",
"coproc", "let", "function", "declare", "local",
"export", "readonly", "typeset", "nameref":
return true
}
}
@ -1668,12 +1656,12 @@ func (p *parser) coprocClause() *CoprocClause {
}
// name was in fact the stmt
cc.Stmt = p.stmt(cc.Name.ValuePos)
cc.Stmt.Cmd = p.call(p.word(p.singleWps(cc.Name)))
cc.Stmt.Cmd = p.call(p.word(p.wps(cc.Name)))
cc.Name = nil
} else if cc.Name != nil {
if call, ok := cc.Stmt.Cmd.(*CallExpr); ok {
// name was in fact the start of a call
call.Args = append([]*Word{p.word(p.singleWps(cc.Name))},
call.Args = append([]*Word{p.word(p.wps(cc.Name))},
call.Args...)
cc.Name = nil
}
@ -1726,7 +1714,7 @@ func (p *parser) callExpr(s *Stmt, w *Word) *CallExpr {
return ce
case _LitWord:
ce.Args = append(ce.Args, p.word(
p.singleWps(p.lit(p.pos, p.val)),
p.wps(p.lit(p.pos, p.val)),
))
p.next()
case bckQuote:

View File

@ -11,7 +11,14 @@ import (
// PrintConfig controls how the printing of an AST node will behave.
type PrintConfig struct {
Spaces int // 0 (default) for tabs, >0 for number of spaces
// Spaces dictates the indentation style. The default value of 0
// uses tabs, and any positive value uses that number of spaces.
Spaces int
// BinaryNextLine makes binary operators (such as &&, || and |)
// be at the start of a line if the statement that follows them
// is on a separate line. This means that the operator will come
// after an escaped newline.
BinaryNextLine bool
}
var printerFree = sync.Pool{
@ -310,52 +317,7 @@ func (p *printer) wordPart(wp WordPart) {
p.nestedStmts(x.Stmts, x.Right)
p.sepTok(")", x.Right)
case *ParamExp:
if x.Short {
p.WriteByte('$')
p.WriteString(x.Param.Value)
break
}
p.WriteString("${")
switch {
case x.Length:
p.WriteByte('#')
case x.Excl:
p.WriteByte('!')
}
if x.Param != nil {
p.WriteString(x.Param.Value)
}
if x.Ind != nil {
p.WriteByte('[')
p.arithmExpr(x.Ind.Expr, false)
p.WriteByte(']')
}
if x.Slice != nil {
p.WriteByte(':')
if un, ok := x.Slice.Offset.(*UnaryArithm); ok {
if un.Op == Plus || un.Op == Minus {
// to avoid :+ and :-
p.WriteByte(' ')
}
}
p.arithmExpr(x.Slice.Offset, true)
if x.Slice.Length != nil {
p.WriteByte(':')
p.arithmExpr(x.Slice.Length, true)
}
} else if x.Repl != nil {
if x.Repl.All {
p.WriteByte('/')
}
p.WriteByte('/')
p.word(x.Repl.Orig)
p.WriteByte('/')
p.word(x.Repl.With)
} else if x.Exp != nil {
p.WriteString(x.Exp.Op.String())
p.word(x.Exp.Word)
}
p.WriteByte('}')
p.paramExp(x)
case *ArithmExp:
p.WriteString("$((")
p.arithmExpr(x.X, false)
@ -381,6 +343,55 @@ func (p *printer) wordPart(wp WordPart) {
}
}
func (p *printer) paramExp(pe *ParamExp) {
if pe.Short {
p.WriteByte('$')
p.WriteString(pe.Param.Value)
return
}
p.WriteString("${")
switch {
case pe.Length:
p.WriteByte('#')
case pe.Indirect:
p.WriteByte('!')
}
if pe.Param != nil {
p.WriteString(pe.Param.Value)
}
if pe.Ind != nil {
p.WriteByte('[')
p.arithmExpr(pe.Ind.Expr, false)
p.WriteByte(']')
}
if pe.Slice != nil {
p.WriteByte(':')
if un, ok := pe.Slice.Offset.(*UnaryArithm); ok {
if un.Op == Plus || un.Op == Minus {
// to avoid :+ and :-
p.WriteByte(' ')
}
}
p.arithmExpr(pe.Slice.Offset, true)
if pe.Slice.Length != nil {
p.WriteByte(':')
p.arithmExpr(pe.Slice.Length, true)
}
} else if pe.Repl != nil {
if pe.Repl.All {
p.WriteByte('/')
}
p.WriteByte('/')
p.word(pe.Repl.Orig)
p.WriteByte('/')
p.word(pe.Repl.With)
} else if pe.Exp != nil {
p.WriteString(pe.Exp.Op.String())
p.word(pe.Exp.Word)
}
p.WriteByte('}')
}
func (p *printer) loop(loop Loop) {
switch x := loop.(type) {
case *WordIter:
@ -405,8 +416,10 @@ func (p *printer) loop(loop Loop) {
func (p *printer) arithmExpr(expr ArithmExpr, compact bool) {
switch x := expr.(type) {
case *Word:
p.word(x)
case *Lit:
p.WriteString(x.Value)
case *ParamExp:
p.paramExp(x)
case *BinaryArithm:
if compact {
p.arithmExpr(x.X, compact)
@ -636,19 +649,28 @@ func (p *printer) command(cmd Command, redirs []*Redirect) (startRedirs int) {
p.incLevel()
}
_, p.nestedBinary = x.Y.Cmd.(*BinaryCmd)
if len(p.pendingHdocs) == 0 && x.Y.Pos() > p.nline {
p.bslashNewl()
p.indent()
}
p.spacedString(x.Op.String())
if p.anyCommentsBefore(x.Y.Pos()) {
p.wantSpace = false
p.WriteByte('\n')
p.indent()
p.incLines(p.comments[0].Pos())
p.commentsUpTo(x.Y.Pos())
p.WriteByte('\n')
p.indent()
if p.BinaryNextLine {
if len(p.pendingHdocs) == 0 && x.Y.Pos() > p.nline {
p.bslashNewl()
p.indent()
}
p.spacedString(x.Op.String())
if p.anyCommentsBefore(x.Y.Pos()) {
p.wantSpace = false
p.WriteByte('\n')
p.indent()
p.incLines(p.comments[0].Pos())
p.commentsUpTo(x.Y.Pos())
p.WriteByte('\n')
p.indent()
}
} else {
p.spacedString(x.Op.String())
if x.Y.Pos() > p.nline {
p.commentsUpTo(x.Y.Pos())
p.newline(0)
p.indent()
}
}
p.incLines(x.Y.Pos())
p.stmt(x.Y)
@ -723,11 +745,6 @@ func (p *printer) command(cmd Command, redirs []*Redirect) (startRedirs int) {
p.word(w)
}
p.assigns(x.Assigns)
case *EvalClause:
p.spacedString("eval")
if x.Stmt != nil {
p.stmt(x.Stmt)
}
case *CoprocClause:
p.spacedString("coproc")
if x.Name != nil {

View File

@ -316,16 +316,12 @@ const (
TsGeq
TsLss
TsGtr
AndTest = BinTestOperator(andAnd)
OrTest = BinTestOperator(orOr)
// TODO(mvdan): == and != are pattern matches; use more
// appropriate names like TsMatch and TsNoMatch in 2.0
TsEqual = BinTestOperator(equal)
TsNequal = BinTestOperator(nequal)
TsBefore = BinTestOperator(rdrIn)
TsAfter = BinTestOperator(rdrOut)
// Deprecated: now parses as TsEqual
TsAssgn = BinTestOperator(assgn) // TODO(mvdan): remove in 2.0
AndTest = BinTestOperator(andAnd)
OrTest = BinTestOperator(orOr)
TsMatch = BinTestOperator(equal)
TsNoMatch = BinTestOperator(nequal)
TsBefore = BinTestOperator(rdrIn)
TsAfter = BinTestOperator(rdrOut)
)
func (o RedirOperator) String() string { return token(o).String() }

View File

@ -163,10 +163,6 @@ func Walk(node Node, f func(Node) bool) {
Walk(x.Pattern, f)
case *ProcSubst:
walkStmts(x.Stmts, f)
case *EvalClause:
if x.Stmt != nil {
Walk(x.Stmt, f)
}
case *CoprocClause:
if x.Name != nil {
Walk(x.Name, f)

12
vendor/vendor.json vendored
View File

@ -51,16 +51,16 @@
"revisionTime": "2017-01-24T11:57:57Z"
},
{
"checksumSHA1": "1ZLAvHVYAS3kxaYI8OQiTBllqNU=",
"checksumSHA1": "ZjfvXVu+OyeRBysQ8uowAkPD/6o=",
"path": "github.com/mvdan/sh/interp",
"revision": "17e267b541e30baece16b7ddeae50822cc6a795f",
"revisionTime": "2017-04-24T11:31:08Z"
"revision": "faf782d3a498f50cfc6aa9071d04e6f1e82e8035",
"revisionTime": "2017-04-30T14:10:52Z"
},
{
"checksumSHA1": "4/7joITdf4wl+uoV8zDXgYqy2aw=",
"checksumSHA1": "RIR7FOsCR78SmOJOUsclJe9lvxo=",
"path": "github.com/mvdan/sh/syntax",
"revision": "17e267b541e30baece16b7ddeae50822cc6a795f",
"revisionTime": "2017-04-24T11:31:08Z"
"revision": "faf782d3a498f50cfc6aa9071d04e6f1e82e8035",
"revisionTime": "2017-04-30T14:10:52Z"
},
{
"checksumSHA1": "HUXE+Nrcau8FSaVEvPYHMvDjxOE=",