mirror of
https://github.com/go-task/task.git
synced 2025-01-12 04:34:11 +02:00
Vendor github.com/mvdan/sh
This commit is contained in:
parent
6bc27baa96
commit
25134279f4
27
vendor/github.com/mvdan/sh/LICENSE
generated
vendored
Normal file
27
vendor/github.com/mvdan/sh/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2016, Daniel Martí. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
171
vendor/github.com/mvdan/sh/interp/arith.go
generated
vendored
Normal file
171
vendor/github.com/mvdan/sh/interp/arith.go
generated
vendored
Normal file
@ -0,0 +1,171 @@
|
||||
// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
|
||||
// See LICENSE for licensing information
|
||||
|
||||
package interp
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/mvdan/sh/syntax"
|
||||
)
|
||||
|
||||
func (r *Runner) arithm(expr syntax.ArithmExpr) int {
|
||||
switch x := expr.(type) {
|
||||
case *syntax.Word:
|
||||
str := r.loneWord(x)
|
||||
// recursively fetch vars
|
||||
for {
|
||||
val := r.getVar(str)
|
||||
if val == "" {
|
||||
break
|
||||
}
|
||||
str = val
|
||||
}
|
||||
// default to 0
|
||||
return atoi(str)
|
||||
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
|
||||
old := atoi(r.getVar(name))
|
||||
val := old
|
||||
if x.Op == syntax.Inc {
|
||||
val++
|
||||
} else {
|
||||
val--
|
||||
}
|
||||
r.setVar(name, strconv.Itoa(val))
|
||||
if x.Post {
|
||||
return old
|
||||
}
|
||||
return val
|
||||
}
|
||||
val := r.arithm(x.X)
|
||||
switch x.Op {
|
||||
case syntax.Not:
|
||||
return oneIf(val == 0)
|
||||
case syntax.Plus:
|
||||
return val
|
||||
default: // syntax.Minus
|
||||
return -val
|
||||
}
|
||||
case *syntax.BinaryArithm:
|
||||
switch x.Op {
|
||||
case syntax.Assgn, syntax.AddAssgn, syntax.SubAssgn,
|
||||
syntax.MulAssgn, syntax.QuoAssgn, syntax.RemAssgn,
|
||||
syntax.AndAssgn, syntax.OrAssgn, syntax.XorAssgn,
|
||||
syntax.ShlAssgn, syntax.ShrAssgn:
|
||||
return r.assgnArit(x)
|
||||
case syntax.Quest: // Colon can't happen here
|
||||
cond := r.arithm(x.X)
|
||||
b2 := x.Y.(*syntax.BinaryArithm) // must have Op==Colon
|
||||
if cond == 1 {
|
||||
return r.arithm(b2.X)
|
||||
}
|
||||
return r.arithm(b2.Y)
|
||||
}
|
||||
return binArit(x.Op, r.arithm(x.X), r.arithm(x.Y))
|
||||
default:
|
||||
r.errf("unexpected arithm expr: %T", x)
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// atoi is just a shorthand for strconv.Atoi that ignores the error,
|
||||
// just like shells do.
|
||||
func atoi(s string) int {
|
||||
n, _ := strconv.Atoi(s)
|
||||
return n
|
||||
}
|
||||
|
||||
func (r *Runner) assgnArit(b *syntax.BinaryArithm) int {
|
||||
name := b.X.(*syntax.Word).Parts[0].(*syntax.Lit).Value
|
||||
val := atoi(r.getVar(name))
|
||||
arg := r.arithm(b.Y)
|
||||
switch b.Op {
|
||||
case syntax.Assgn:
|
||||
val = arg
|
||||
case syntax.AddAssgn:
|
||||
val += arg
|
||||
case syntax.SubAssgn:
|
||||
val -= arg
|
||||
case syntax.MulAssgn:
|
||||
val *= arg
|
||||
case syntax.QuoAssgn:
|
||||
val /= arg
|
||||
case syntax.RemAssgn:
|
||||
val %= arg
|
||||
case syntax.AndAssgn:
|
||||
val &= arg
|
||||
case syntax.OrAssgn:
|
||||
val |= arg
|
||||
case syntax.XorAssgn:
|
||||
val ^= arg
|
||||
case syntax.ShlAssgn:
|
||||
val <<= uint(arg)
|
||||
default: // syntax.ShrAssgn
|
||||
val >>= uint(arg)
|
||||
}
|
||||
r.setVar(name, strconv.Itoa(val))
|
||||
return val
|
||||
}
|
||||
|
||||
func intPow(a, b int) int {
|
||||
p := 1
|
||||
for b > 0 {
|
||||
if b&1 != 0 {
|
||||
p *= a
|
||||
}
|
||||
b >>= 1
|
||||
a *= a
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func binArit(op syntax.BinAritOperator, x, y int) int {
|
||||
switch op {
|
||||
case syntax.Add:
|
||||
return x + y
|
||||
case syntax.Sub:
|
||||
return x - y
|
||||
case syntax.Mul:
|
||||
return x * y
|
||||
case syntax.Quo:
|
||||
return x / y
|
||||
case syntax.Rem:
|
||||
return x % y
|
||||
case syntax.Pow:
|
||||
return intPow(x, y)
|
||||
case syntax.Eql:
|
||||
return oneIf(x == y)
|
||||
case syntax.Gtr:
|
||||
return oneIf(x > y)
|
||||
case syntax.Lss:
|
||||
return oneIf(x < y)
|
||||
case syntax.Neq:
|
||||
return oneIf(x != y)
|
||||
case syntax.Leq:
|
||||
return oneIf(x <= y)
|
||||
case syntax.Geq:
|
||||
return oneIf(x >= y)
|
||||
case syntax.And:
|
||||
return x & y
|
||||
case syntax.Or:
|
||||
return x | y
|
||||
case syntax.Xor:
|
||||
return x ^ y
|
||||
case syntax.Shr:
|
||||
return x >> uint(y)
|
||||
case syntax.Shl:
|
||||
return x << uint(y)
|
||||
case syntax.AndArit:
|
||||
return oneIf(x != 0 && y != 0)
|
||||
case syntax.OrArit:
|
||||
return oneIf(x != 0 || y != 0)
|
||||
default: // syntax.Comma
|
||||
// x is executed but its result discarded
|
||||
return y
|
||||
}
|
||||
}
|
179
vendor/github.com/mvdan/sh/interp/builtin.go
generated
vendored
Normal file
179
vendor/github.com/mvdan/sh/interp/builtin.go
generated
vendored
Normal file
@ -0,0 +1,179 @@
|
||||
// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
|
||||
// See LICENSE for licensing information
|
||||
|
||||
package interp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"github.com/mvdan/sh/syntax"
|
||||
)
|
||||
|
||||
func (r *Runner) builtin(pos syntax.Pos, name string, args []string) bool {
|
||||
exit := 0
|
||||
switch name {
|
||||
case "true", ":":
|
||||
case "false":
|
||||
exit = 1
|
||||
case "exit":
|
||||
switch len(args) {
|
||||
case 0:
|
||||
r.lastExit()
|
||||
case 1:
|
||||
if n, err := strconv.Atoi(args[0]); err != nil {
|
||||
r.runErr(pos, "invalid exit code: %q", args[0])
|
||||
} else {
|
||||
exit = n
|
||||
r.err = ExitCode(n)
|
||||
}
|
||||
default:
|
||||
r.runErr(pos, "exit cannot take multiple arguments")
|
||||
}
|
||||
case "set":
|
||||
r.args = args
|
||||
case "shift":
|
||||
n := 1
|
||||
switch len(args) {
|
||||
case 0:
|
||||
case 1:
|
||||
if n2, err := strconv.Atoi(args[0]); err == nil {
|
||||
n = n2
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
r.errf("usage: shift [n]\n")
|
||||
exit = 2
|
||||
}
|
||||
if len(r.args) < n {
|
||||
n = len(r.args)
|
||||
}
|
||||
r.args = r.args[n:]
|
||||
case "unset":
|
||||
for _, arg := range args {
|
||||
r.delVar(arg)
|
||||
}
|
||||
case "echo":
|
||||
newline := true
|
||||
opts:
|
||||
for len(args) > 0 {
|
||||
switch args[0] {
|
||||
case "-n":
|
||||
newline = false
|
||||
case "-e", "-E":
|
||||
// TODO: what should be our default?
|
||||
// exactly what is the difference in
|
||||
// what we write?
|
||||
default:
|
||||
break opts
|
||||
}
|
||||
args = args[1:]
|
||||
}
|
||||
for i, arg := range args {
|
||||
if i > 0 {
|
||||
r.outf(" ")
|
||||
}
|
||||
r.outf("%s", arg)
|
||||
}
|
||||
if newline {
|
||||
r.outf("\n")
|
||||
}
|
||||
case "printf":
|
||||
if len(args) == 0 {
|
||||
r.errf("usage: printf format [arguments]\n")
|
||||
exit = 2
|
||||
break
|
||||
}
|
||||
var a []interface{}
|
||||
for _, arg := range args[1:] {
|
||||
a = append(a, arg)
|
||||
}
|
||||
r.outf(args[0], a...)
|
||||
case "break":
|
||||
if !r.inLoop {
|
||||
r.errf("break is only useful in a loop")
|
||||
break
|
||||
}
|
||||
switch len(args) {
|
||||
case 0:
|
||||
r.breakEnclosing = 1
|
||||
case 1:
|
||||
if n, err := strconv.Atoi(args[0]); err == nil {
|
||||
r.breakEnclosing = n
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
r.errf("usage: break [n]\n")
|
||||
exit = 2
|
||||
}
|
||||
case "continue":
|
||||
if !r.inLoop {
|
||||
r.errf("continue is only useful in a loop")
|
||||
break
|
||||
}
|
||||
switch len(args) {
|
||||
case 0:
|
||||
r.contnEnclosing = 1
|
||||
case 1:
|
||||
if n, err := strconv.Atoi(args[0]); err == nil {
|
||||
r.contnEnclosing = n
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
r.errf("usage: continue [n]\n")
|
||||
exit = 2
|
||||
}
|
||||
case "pwd":
|
||||
r.outf("%s\n", r.getVar("PWD"))
|
||||
case "cd":
|
||||
if len(args) > 1 {
|
||||
r.errf("usage: cd [dir]\n")
|
||||
exit = 2
|
||||
break
|
||||
}
|
||||
var dir string
|
||||
if len(args) == 0 {
|
||||
dir = r.getVar("HOME")
|
||||
} else {
|
||||
dir = args[0]
|
||||
}
|
||||
if !filepath.IsAbs(dir) {
|
||||
dir = filepath.Join(r.Dir, dir)
|
||||
}
|
||||
_, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
exit = 1
|
||||
break
|
||||
}
|
||||
r.Dir = dir
|
||||
case "wait":
|
||||
if len(args) > 0 {
|
||||
r.errf("wait with args not handled yet")
|
||||
break
|
||||
}
|
||||
r.bgShells.Wait()
|
||||
case "builtin":
|
||||
if len(args) < 1 {
|
||||
break
|
||||
}
|
||||
// TODO: pos
|
||||
if !r.builtin(0, args[0], args[1:]) {
|
||||
exit = 1
|
||||
}
|
||||
case "trap", "type", "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
|
||||
}
|
9
vendor/github.com/mvdan/sh/interp/doc.go
generated
vendored
Normal file
9
vendor/github.com/mvdan/sh/interp/doc.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
|
||||
// See LICENSE for licensing information
|
||||
|
||||
// Package interp implements an interpreter that executes shell
|
||||
// programs. It aims to support POSIX.
|
||||
//
|
||||
// This package is a work in progress and EXPERIMENTAL; its API is not
|
||||
// subject to the 1.x backwards compatibility guarantee.
|
||||
package interp
|
653
vendor/github.com/mvdan/sh/interp/interp.go
generated
vendored
Normal file
653
vendor/github.com/mvdan/sh/interp/interp.go
generated
vendored
Normal file
@ -0,0 +1,653 @@
|
||||
// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
|
||||
// See LICENSE for licensing information
|
||||
|
||||
package interp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/mvdan/sh/syntax"
|
||||
)
|
||||
|
||||
// A Runner interprets shell programs. It cannot be reused once a
|
||||
// program has been interpreted.
|
||||
//
|
||||
// Note that writes to Stdout and Stderr may not be sequential. If
|
||||
// you plan on using an io.Writer implementation that isn't safe for
|
||||
// concurrent use, consider a workaround like hiding writes behind a
|
||||
// mutex.
|
||||
type Runner struct {
|
||||
// TODO: syntax.Node instead of *syntax.File?
|
||||
File *syntax.File
|
||||
|
||||
// Env specifies the environment of the interpreter.
|
||||
// If Env is nil, Run uses the current process's environment.
|
||||
Env []string
|
||||
|
||||
// envMap is just Env as a map, to simplify and speed up its use
|
||||
envMap map[string]string
|
||||
|
||||
// Dir specifies the working directory of the command. If Dir is
|
||||
// the empty string, Run runs the command in the calling
|
||||
// process's current directory.
|
||||
Dir string
|
||||
|
||||
// Separate maps, note that bash allows a name to be both a var
|
||||
// and a func simultaneously
|
||||
vars map[string]varValue
|
||||
funcs map[string]*syntax.Stmt
|
||||
|
||||
// like vars, but local to a cmd i.e. "foo=bar prog args..."
|
||||
cmdVars map[string]varValue
|
||||
|
||||
// Current arguments, if executing a function
|
||||
args []string
|
||||
|
||||
// >0 to break or continue out of N enclosing loops
|
||||
breakEnclosing, contnEnclosing int
|
||||
|
||||
inLoop bool
|
||||
|
||||
err error // current fatal error
|
||||
exit int // current (last) exit code
|
||||
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
|
||||
bgShells sync.WaitGroup
|
||||
|
||||
// Context can be used to cancel the interpreter before it finishes
|
||||
Context context.Context
|
||||
}
|
||||
|
||||
// varValue can hold a string, an indexed array ([]string) or an
|
||||
// associative array (map[string]string)
|
||||
// TODO: implement associative arrays
|
||||
type varValue interface{}
|
||||
|
||||
func varStr(v varValue) string {
|
||||
switch x := v.(type) {
|
||||
case string:
|
||||
return x
|
||||
case []string:
|
||||
if len(x) > 0 {
|
||||
return x[0]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *Runner) varInd(v varValue, e syntax.ArithmExpr) string {
|
||||
switch x := v.(type) {
|
||||
case string:
|
||||
i := r.arithm(e)
|
||||
if i == 0 {
|
||||
return x
|
||||
}
|
||||
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, " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
i := r.arithm(e)
|
||||
if len(x) > 0 {
|
||||
return x[i]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type ExitCode uint8
|
||||
|
||||
func (e ExitCode) Error() string { return fmt.Sprintf("exit status %d", e) }
|
||||
|
||||
type RunError struct {
|
||||
syntax.Position
|
||||
Text string
|
||||
}
|
||||
|
||||
func (e RunError) Error() string {
|
||||
return fmt.Sprintf("%s: %s", e.Position.String(), e.Text)
|
||||
}
|
||||
|
||||
func (r *Runner) runErr(pos syntax.Pos, format string, a ...interface{}) {
|
||||
if r.err == nil {
|
||||
r.err = RunError{
|
||||
Position: r.File.Position(pos),
|
||||
Text: fmt.Sprintf(format, a...),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runner) lastExit() {
|
||||
if r.err == nil {
|
||||
r.err = ExitCode(r.exit)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runner) setVar(name string, val varValue) {
|
||||
if r.vars == nil {
|
||||
r.vars = make(map[string]varValue, 4)
|
||||
}
|
||||
r.vars[name] = val
|
||||
}
|
||||
|
||||
func (r *Runner) lookupVar(name string) (varValue, bool) {
|
||||
switch name {
|
||||
case "PWD":
|
||||
return r.Dir, true
|
||||
case "HOME":
|
||||
u, _ := user.Current()
|
||||
return u.HomeDir, true
|
||||
}
|
||||
if val, e := r.cmdVars[name]; e {
|
||||
return val, true
|
||||
}
|
||||
if val, e := r.vars[name]; e {
|
||||
return val, true
|
||||
}
|
||||
str, e := r.envMap[name]
|
||||
return str, e
|
||||
}
|
||||
|
||||
func (r *Runner) getVar(name string) string {
|
||||
val, _ := r.lookupVar(name)
|
||||
return varStr(val)
|
||||
}
|
||||
|
||||
func (r *Runner) delVar(name string) {
|
||||
delete(r.vars, name)
|
||||
delete(r.envMap, name)
|
||||
}
|
||||
|
||||
func (r *Runner) setFunc(name string, body *syntax.Stmt) {
|
||||
if r.funcs == nil {
|
||||
r.funcs = make(map[string]*syntax.Stmt, 4)
|
||||
}
|
||||
r.funcs[name] = body
|
||||
}
|
||||
|
||||
// Run starts the interpreter and returns any error.
|
||||
func (r *Runner) Run() error {
|
||||
if r.Context == nil {
|
||||
r.Context = context.Background()
|
||||
}
|
||||
if r.Env == nil {
|
||||
r.Env = os.Environ()
|
||||
}
|
||||
r.envMap = make(map[string]string, len(r.Env))
|
||||
for _, kv := range r.Env {
|
||||
i := strings.IndexByte(kv, '=')
|
||||
if i < 0 {
|
||||
return fmt.Errorf("env not in the form key=value: %q", kv)
|
||||
}
|
||||
name, val := kv[:i], kv[i+1:]
|
||||
r.envMap[name] = val
|
||||
}
|
||||
if r.Dir == "" {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not get current dir: %v", err)
|
||||
}
|
||||
r.Dir = dir
|
||||
}
|
||||
r.stmts(r.File.Stmts)
|
||||
r.lastExit()
|
||||
if r.err == ExitCode(0) {
|
||||
r.err = nil
|
||||
}
|
||||
return r.err
|
||||
}
|
||||
|
||||
func (r *Runner) outf(format string, a ...interface{}) {
|
||||
fmt.Fprintf(r.Stdout, format, a...)
|
||||
}
|
||||
|
||||
func (r *Runner) errf(format string, a ...interface{}) {
|
||||
fmt.Fprintf(r.Stderr, format, a...)
|
||||
}
|
||||
|
||||
func (r *Runner) fields(words []*syntax.Word) []string {
|
||||
fields := make([]string, 0, len(words))
|
||||
for _, word := range words {
|
||||
fields = append(fields, r.wordParts(word.Parts, false)...)
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
func (r *Runner) loneWord(word *syntax.Word) string {
|
||||
return strings.Join(r.wordParts(word.Parts, false), "")
|
||||
}
|
||||
|
||||
func (r *Runner) stop() bool {
|
||||
if r.err != nil {
|
||||
return true
|
||||
}
|
||||
if err := r.Context.Err(); err != nil {
|
||||
r.err = err
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *Runner) stmt(st *syntax.Stmt) {
|
||||
if r.stop() {
|
||||
return
|
||||
}
|
||||
if st.Background {
|
||||
r.bgShells.Add(1)
|
||||
r2 := *r
|
||||
r2.bgShells = sync.WaitGroup{}
|
||||
go func() {
|
||||
r2.stmtSync(st)
|
||||
r.bgShells.Done()
|
||||
}()
|
||||
} else {
|
||||
r.stmtSync(st)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runner) assignValue(word *syntax.Word) varValue {
|
||||
if word == nil {
|
||||
return nil
|
||||
}
|
||||
ae, ok := word.Parts[0].(*syntax.ArrayExpr)
|
||||
if !ok {
|
||||
return r.loneWord(word)
|
||||
}
|
||||
strs := make([]string, len(ae.List))
|
||||
for i, w := range ae.List {
|
||||
strs[i] = r.loneWord(w)
|
||||
}
|
||||
return strs
|
||||
}
|
||||
|
||||
func (r *Runner) stmtSync(st *syntax.Stmt) {
|
||||
oldVars := r.cmdVars
|
||||
for _, as := range st.Assigns {
|
||||
name := as.Name.Value
|
||||
val := r.assignValue(as.Value)
|
||||
if st.Cmd == nil {
|
||||
r.setVar(name, val)
|
||||
continue
|
||||
}
|
||||
if r.cmdVars == nil {
|
||||
r.cmdVars = make(map[string]varValue, len(st.Assigns))
|
||||
}
|
||||
r.cmdVars[name] = val
|
||||
}
|
||||
oldIn, oldOut, oldErr := r.Stdin, r.Stdout, r.Stderr
|
||||
for _, rd := range st.Redirs {
|
||||
cls, err := r.redir(rd)
|
||||
if err != nil {
|
||||
r.exit = 1
|
||||
return
|
||||
}
|
||||
if cls != nil {
|
||||
defer cls.Close()
|
||||
}
|
||||
}
|
||||
if st.Cmd == nil {
|
||||
r.exit = 0
|
||||
} else {
|
||||
r.cmd(st.Cmd)
|
||||
}
|
||||
if st.Negated {
|
||||
r.exit = oneIf(r.exit == 0)
|
||||
}
|
||||
r.cmdVars = oldVars
|
||||
r.Stdin, r.Stdout, r.Stderr = oldIn, oldOut, oldErr
|
||||
}
|
||||
|
||||
func oneIf(b bool) int {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (r *Runner) cmd(cm syntax.Command) {
|
||||
if r.stop() {
|
||||
return
|
||||
}
|
||||
switch x := cm.(type) {
|
||||
case *syntax.Block:
|
||||
r.stmts(x.Stmts)
|
||||
case *syntax.Subshell:
|
||||
r2 := *r
|
||||
r2.stmts(x.Stmts)
|
||||
r.exit = r2.exit
|
||||
case *syntax.CallExpr:
|
||||
fields := r.fields(x.Args)
|
||||
r.call(x.Args[0].Pos(), fields[0], fields[1:])
|
||||
case *syntax.BinaryCmd:
|
||||
switch x.Op {
|
||||
case syntax.AndStmt:
|
||||
r.stmt(x.X)
|
||||
if r.exit == 0 {
|
||||
r.stmt(x.Y)
|
||||
}
|
||||
case syntax.OrStmt:
|
||||
r.stmt(x.X)
|
||||
if r.exit != 0 {
|
||||
r.stmt(x.Y)
|
||||
}
|
||||
case syntax.Pipe, syntax.PipeAll:
|
||||
pr, pw := io.Pipe()
|
||||
r2 := *r
|
||||
r2.Stdin = r.Stdin
|
||||
r2.Stdout = pw
|
||||
if x.Op == syntax.PipeAll {
|
||||
r2.Stderr = pw
|
||||
} else {
|
||||
r2.Stderr = r.Stderr
|
||||
}
|
||||
r.Stdin = pr
|
||||
go func() {
|
||||
r2.stmt(x.X)
|
||||
pw.Close()
|
||||
}()
|
||||
r.stmt(x.Y)
|
||||
pr.Close()
|
||||
}
|
||||
case *syntax.IfClause:
|
||||
r.stmts(x.CondStmts)
|
||||
if r.exit == 0 {
|
||||
r.stmts(x.ThenStmts)
|
||||
return
|
||||
}
|
||||
for _, el := range x.Elifs {
|
||||
r.stmts(el.CondStmts)
|
||||
if r.exit == 0 {
|
||||
r.stmts(el.ThenStmts)
|
||||
return
|
||||
}
|
||||
}
|
||||
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)
|
||||
if r.exit != 0 {
|
||||
r.exit = 0
|
||||
break
|
||||
}
|
||||
if r.loopStmtsBroken(x.DoStmts) {
|
||||
break
|
||||
}
|
||||
}
|
||||
case *syntax.UntilClause:
|
||||
for r.err == nil {
|
||||
r.stmts(x.CondStmts)
|
||||
if r.exit == 0 {
|
||||
break
|
||||
}
|
||||
r.exit = 0
|
||||
if r.loopStmtsBroken(x.DoStmts) {
|
||||
break
|
||||
}
|
||||
}
|
||||
case *syntax.ForClause:
|
||||
switch y := x.Loop.(type) {
|
||||
case *syntax.WordIter:
|
||||
name := y.Name.Value
|
||||
for _, field := range r.fields(y.List) {
|
||||
r.setVar(name, field)
|
||||
if r.loopStmtsBroken(x.DoStmts) {
|
||||
break
|
||||
}
|
||||
}
|
||||
case *syntax.CStyleLoop:
|
||||
r.arithm(y.Init)
|
||||
for r.arithm(y.Cond) != 0 {
|
||||
if r.loopStmtsBroken(x.DoStmts) {
|
||||
break
|
||||
}
|
||||
r.arithm(y.Post)
|
||||
}
|
||||
}
|
||||
case *syntax.FuncDecl:
|
||||
r.setFunc(x.Name.Value, x.Body)
|
||||
case *syntax.ArithmCmd:
|
||||
if r.arithm(x.X) == 0 {
|
||||
r.exit = 1
|
||||
}
|
||||
case *syntax.LetClause:
|
||||
var val int
|
||||
for _, expr := range x.Exprs {
|
||||
val = r.arithm(expr)
|
||||
}
|
||||
if val == 0 {
|
||||
r.exit = 1
|
||||
}
|
||||
case *syntax.CaseClause:
|
||||
str := r.loneWord(x.Word)
|
||||
for _, pl := range x.List {
|
||||
for _, word := range pl.Patterns {
|
||||
pat := r.loneWord(word)
|
||||
// TODO: error?
|
||||
matched, _ := path.Match(pat, str)
|
||||
if matched {
|
||||
r.stmts(pl.Stmts)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
case *syntax.TestClause:
|
||||
if r.bashTest(x.X) == "" && r.exit == 0 {
|
||||
r.exit = 1
|
||||
}
|
||||
default:
|
||||
r.errf("unhandled command node: %T", x)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runner) stmts(stmts []*syntax.Stmt) {
|
||||
for _, stmt := range stmts {
|
||||
r.stmt(stmt)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runner) redir(rd *syntax.Redirect) (io.Closer, error) {
|
||||
if rd.Hdoc != nil {
|
||||
hdoc := r.loneWord(rd.Hdoc)
|
||||
r.Stdin = strings.NewReader(hdoc)
|
||||
return nil, nil
|
||||
}
|
||||
orig := &r.Stdout
|
||||
if rd.N != nil {
|
||||
switch rd.N.Value {
|
||||
case "1":
|
||||
case "2":
|
||||
orig = &r.Stderr
|
||||
}
|
||||
}
|
||||
arg := r.loneWord(rd.Word)
|
||||
switch rd.Op {
|
||||
case syntax.WordHdoc:
|
||||
r.Stdin = strings.NewReader(arg + "\n")
|
||||
return nil, nil
|
||||
case syntax.DplOut:
|
||||
switch arg {
|
||||
case "1":
|
||||
*orig = r.Stdout
|
||||
case "2":
|
||||
*orig = r.Stderr
|
||||
}
|
||||
return nil, nil
|
||||
case syntax.DplIn:
|
||||
r.errf("unhandled redirect op: %v", rd.Op)
|
||||
}
|
||||
mode := os.O_RDONLY
|
||||
switch rd.Op {
|
||||
case syntax.AppOut, syntax.AppAll:
|
||||
mode = os.O_RDWR | os.O_CREATE | os.O_APPEND
|
||||
case syntax.RdrOut, syntax.RdrAll:
|
||||
mode = os.O_RDWR | os.O_CREATE | os.O_TRUNC
|
||||
}
|
||||
f, err := os.OpenFile(arg, mode, 0644)
|
||||
if err != nil {
|
||||
// TODO: print to stderr?
|
||||
return nil, err
|
||||
}
|
||||
switch rd.Op {
|
||||
case syntax.RdrIn:
|
||||
r.Stdin = f
|
||||
case syntax.RdrOut, syntax.AppOut:
|
||||
*orig = f
|
||||
case syntax.RdrAll, syntax.AppAll:
|
||||
r.Stdout = f
|
||||
r.Stderr = f
|
||||
default:
|
||||
r.errf("unhandled redirect op: %v", rd.Op)
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (r *Runner) loopStmtsBroken(stmts []*syntax.Stmt) bool {
|
||||
r.inLoop = true
|
||||
defer func() { r.inLoop = false }()
|
||||
for _, stmt := range stmts {
|
||||
r.stmt(stmt)
|
||||
if r.contnEnclosing > 0 {
|
||||
r.contnEnclosing--
|
||||
return r.contnEnclosing > 0
|
||||
}
|
||||
if r.breakEnclosing > 0 {
|
||||
r.breakEnclosing--
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (r *Runner) wordParts(wps []syntax.WordPart, quoted bool) []string {
|
||||
var parts []string
|
||||
var curBuf bytes.Buffer
|
||||
flush := func() {
|
||||
if curBuf.Len() == 0 {
|
||||
return
|
||||
}
|
||||
parts = append(parts, curBuf.String())
|
||||
curBuf.Reset()
|
||||
}
|
||||
splitAdd := func(val string) {
|
||||
// TODO: use IFS
|
||||
for i, field := range strings.Fields(val) {
|
||||
if i > 0 {
|
||||
flush()
|
||||
}
|
||||
curBuf.WriteString(field)
|
||||
}
|
||||
}
|
||||
for _, wp := range wps {
|
||||
switch x := wp.(type) {
|
||||
case *syntax.Lit:
|
||||
curBuf.WriteString(x.Value)
|
||||
case *syntax.SglQuoted:
|
||||
curBuf.WriteString(x.Value)
|
||||
case *syntax.DblQuoted:
|
||||
// TODO: @ between double quotes but not alone
|
||||
if len(x.Parts) == 1 {
|
||||
pe, ok := x.Parts[0].(*syntax.ParamExp)
|
||||
if ok && pe.Param.Value == "@" {
|
||||
for i, arg := range r.args {
|
||||
if i > 0 {
|
||||
flush()
|
||||
}
|
||||
curBuf.WriteString(arg)
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, str := range r.wordParts(x.Parts, true) {
|
||||
curBuf.WriteString(str)
|
||||
}
|
||||
case *syntax.ParamExp:
|
||||
val := r.paramExp(x)
|
||||
if quoted {
|
||||
curBuf.WriteString(val)
|
||||
} else {
|
||||
splitAdd(val)
|
||||
}
|
||||
case *syntax.CmdSubst:
|
||||
r2 := *r
|
||||
var outBuf bytes.Buffer
|
||||
r2.Stdout = &outBuf
|
||||
r2.stmts(x.Stmts)
|
||||
val := strings.TrimRight(outBuf.String(), "\n")
|
||||
if quoted {
|
||||
curBuf.WriteString(val)
|
||||
} else {
|
||||
splitAdd(val)
|
||||
}
|
||||
case *syntax.ArithmExp:
|
||||
curBuf.WriteString(strconv.Itoa(r.arithm(x.X)))
|
||||
default:
|
||||
r.errf("unhandled word part: %T", x)
|
||||
}
|
||||
}
|
||||
flush()
|
||||
return parts
|
||||
}
|
||||
|
||||
func (r *Runner) call(pos syntax.Pos, name string, args []string) {
|
||||
if body := r.funcs[name]; body != nil {
|
||||
// stack them to support nested func calls
|
||||
oldArgs := r.args
|
||||
r.args = args
|
||||
r.stmt(body)
|
||||
r.args = oldArgs
|
||||
return
|
||||
}
|
||||
if r.builtin(pos, name, args) {
|
||||
return
|
||||
}
|
||||
cmd := exec.CommandContext(r.Context, name, args...)
|
||||
cmd.Env = r.Env
|
||||
for name, val := range r.cmdVars {
|
||||
cmd.Env = append(cmd.Env, name+"="+varStr(val))
|
||||
}
|
||||
cmd.Dir = r.Dir
|
||||
cmd.Stdin = r.Stdin
|
||||
cmd.Stdout = r.Stdout
|
||||
cmd.Stderr = r.Stderr
|
||||
err := cmd.Run()
|
||||
switch x := err.(type) {
|
||||
case *exec.ExitError:
|
||||
// started, but errored - default to 1 if OS
|
||||
// doesn't have exit statuses
|
||||
if status, ok := x.Sys().(syscall.WaitStatus); ok {
|
||||
r.exit = status.ExitStatus()
|
||||
} else {
|
||||
r.exit = 1
|
||||
}
|
||||
case *exec.Error:
|
||||
// did not start
|
||||
// TODO: can this be anything other than
|
||||
// "command not found"?
|
||||
r.exit = 127
|
||||
// TODO: print something?
|
||||
default:
|
||||
r.exit = 0
|
||||
}
|
||||
}
|
198
vendor/github.com/mvdan/sh/interp/param.go
generated
vendored
Normal file
198
vendor/github.com/mvdan/sh/interp/param.go
generated
vendored
Normal file
@ -0,0 +1,198 @@
|
||||
// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
|
||||
// See LICENSE for licensing information
|
||||
|
||||
package interp
|
||||
|
||||
import (
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/mvdan/sh/syntax"
|
||||
)
|
||||
|
||||
func (r *Runner) paramExp(pe *syntax.ParamExp) string {
|
||||
name := pe.Param.Value
|
||||
var val varValue
|
||||
set := false
|
||||
switch name {
|
||||
case "#":
|
||||
val = strconv.Itoa(len(r.args))
|
||||
case "*", "@":
|
||||
val = strings.Join(r.args, " ")
|
||||
case "?":
|
||||
val = strconv.Itoa(r.exit)
|
||||
default:
|
||||
if n, err := strconv.Atoi(name); err == nil {
|
||||
if i := n - 1; i < len(r.args) {
|
||||
val, set = r.args[i], true
|
||||
}
|
||||
} else {
|
||||
val, set = r.lookupVar(name)
|
||||
}
|
||||
}
|
||||
str := varStr(val)
|
||||
if pe.Ind != nil {
|
||||
str = r.varInd(val, pe.Ind.Expr)
|
||||
}
|
||||
switch {
|
||||
case pe.Length:
|
||||
str = strconv.Itoa(utf8.RuneCountInString(str))
|
||||
case pe.Excl:
|
||||
val, set = r.lookupVar(str)
|
||||
str = varStr(val)
|
||||
}
|
||||
slicePos := func(expr syntax.ArithmExpr) int {
|
||||
p := r.arithm(expr)
|
||||
if p < 0 {
|
||||
p = len(str) + p
|
||||
if p < 0 {
|
||||
p = len(str)
|
||||
}
|
||||
} else if p > len(str) {
|
||||
p = len(str)
|
||||
}
|
||||
return p
|
||||
}
|
||||
if pe.Slice != nil {
|
||||
if pe.Slice.Offset != nil {
|
||||
offset := slicePos(pe.Slice.Offset)
|
||||
str = str[offset:]
|
||||
}
|
||||
if pe.Slice.Length != nil {
|
||||
length := slicePos(pe.Slice.Length)
|
||||
str = str[:length]
|
||||
}
|
||||
}
|
||||
if pe.Repl != nil {
|
||||
orig := r.loneWord(pe.Repl.Orig)
|
||||
with := r.loneWord(pe.Repl.With)
|
||||
n := 1
|
||||
if pe.Repl.All {
|
||||
n = -1
|
||||
}
|
||||
str = strings.Replace(str, orig, with, n)
|
||||
}
|
||||
if pe.Exp != nil {
|
||||
arg := r.loneWord(pe.Exp.Word)
|
||||
switch pe.Exp.Op {
|
||||
case syntax.SubstColPlus:
|
||||
if str == "" {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case syntax.SubstPlus:
|
||||
if set {
|
||||
str = arg
|
||||
}
|
||||
case syntax.SubstMinus:
|
||||
if set {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case syntax.SubstColMinus:
|
||||
if str == "" {
|
||||
str = arg
|
||||
}
|
||||
case syntax.SubstQuest:
|
||||
if set {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case syntax.SubstColQuest:
|
||||
if str == "" {
|
||||
r.errf("%s", arg)
|
||||
r.exit = 1
|
||||
r.lastExit()
|
||||
}
|
||||
case syntax.SubstAssgn:
|
||||
if set {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case syntax.SubstColAssgn:
|
||||
if str == "" {
|
||||
r.setVar(name, arg)
|
||||
str = arg
|
||||
}
|
||||
case syntax.RemSmallPrefix:
|
||||
str = removePattern(str, arg, false, false)
|
||||
case syntax.RemLargePrefix:
|
||||
str = removePattern(str, arg, false, true)
|
||||
case syntax.RemSmallSuffix:
|
||||
str = removePattern(str, arg, true, false)
|
||||
case syntax.RemLargeSuffix:
|
||||
str = removePattern(str, arg, true, true)
|
||||
case syntax.UpperFirst:
|
||||
rs := []rune(str)
|
||||
if len(rs) > 0 {
|
||||
rs[0] = unicode.ToUpper(rs[0])
|
||||
}
|
||||
str = string(rs)
|
||||
case syntax.UpperAll:
|
||||
str = strings.ToUpper(str)
|
||||
case syntax.LowerFirst:
|
||||
rs := []rune(str)
|
||||
if len(rs) > 0 {
|
||||
rs[0] = unicode.ToLower(rs[0])
|
||||
}
|
||||
str = string(rs)
|
||||
case syntax.LowerAll:
|
||||
str = strings.ToLower(str)
|
||||
default: // syntax.OtherParamOps
|
||||
switch arg {
|
||||
case "Q":
|
||||
str = strconv.Quote(str)
|
||||
case "E":
|
||||
tail := str
|
||||
var rns []rune
|
||||
for tail != "" {
|
||||
var rn rune
|
||||
rn, _, tail, _ = strconv.UnquoteChar(tail, 0)
|
||||
rns = append(rns, rn)
|
||||
}
|
||||
str = string(rns)
|
||||
case "P", "A", "a":
|
||||
r.errf("unhandled @%s param expansion", arg)
|
||||
default:
|
||||
r.errf("unexpected @%s param expansion", arg)
|
||||
}
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func removePattern(str, pattern string, fromEnd, longest bool) string {
|
||||
// TODO: really slow to not re-implement path.Match.
|
||||
last := str
|
||||
s := str
|
||||
i := len(str)
|
||||
if fromEnd {
|
||||
i = 0
|
||||
}
|
||||
for {
|
||||
if m, _ := path.Match(pattern, s); m {
|
||||
last = str[i:]
|
||||
if fromEnd {
|
||||
last = str[:i]
|
||||
}
|
||||
if longest {
|
||||
return last
|
||||
}
|
||||
}
|
||||
if fromEnd {
|
||||
if i++; i >= len(str) {
|
||||
break
|
||||
}
|
||||
s = str[i:]
|
||||
} else {
|
||||
if i--; i < 1 {
|
||||
break
|
||||
}
|
||||
s = str[:i]
|
||||
}
|
||||
}
|
||||
return last
|
||||
}
|
160
vendor/github.com/mvdan/sh/interp/test.go
generated
vendored
Normal file
160
vendor/github.com/mvdan/sh/interp/test.go
generated
vendored
Normal file
@ -0,0 +1,160 @@
|
||||
// Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
|
||||
// See LICENSE for licensing information
|
||||
|
||||
package interp
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
"github.com/mvdan/sh/syntax"
|
||||
)
|
||||
|
||||
// non-empty string is true, empty string is false
|
||||
func (r *Runner) bashTest(expr syntax.TestExpr) string {
|
||||
switch x := expr.(type) {
|
||||
case *syntax.Word:
|
||||
return r.loneWord(x)
|
||||
case *syntax.ParenTest:
|
||||
return r.bashTest(x.X)
|
||||
case *syntax.BinaryTest:
|
||||
if r.binTest(x.Op, r.bashTest(x.X), r.bashTest(x.Y)) {
|
||||
return "1"
|
||||
}
|
||||
return ""
|
||||
case *syntax.UnaryTest:
|
||||
if r.unTest(x.Op, r.bashTest(x.X)) {
|
||||
return "1"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (r *Runner) binTest(op syntax.BinTestOperator, x, y string) bool {
|
||||
switch op {
|
||||
case syntax.TsReMatch:
|
||||
re, err := regexp.Compile(y)
|
||||
if err != nil {
|
||||
r.exit = 2
|
||||
return false
|
||||
}
|
||||
return re.MatchString(x)
|
||||
case syntax.TsNewer:
|
||||
i1, i2 := stat(x), stat(y)
|
||||
if i1 == nil || i2 == nil {
|
||||
return false
|
||||
}
|
||||
return i1.ModTime().After(i2.ModTime())
|
||||
case syntax.TsOlder:
|
||||
i1, i2 := stat(x), stat(y)
|
||||
if i1 == nil || i2 == nil {
|
||||
return false
|
||||
}
|
||||
return i1.ModTime().Before(i2.ModTime())
|
||||
case syntax.TsDevIno:
|
||||
i1, i2 := stat(x), stat(y)
|
||||
return os.SameFile(i1, i2)
|
||||
case syntax.TsEql:
|
||||
return atoi(x) == atoi(y)
|
||||
case syntax.TsNeq:
|
||||
return atoi(x) != atoi(y)
|
||||
case syntax.TsLeq:
|
||||
return atoi(x) <= atoi(y)
|
||||
case syntax.TsGeq:
|
||||
return atoi(x) >= atoi(y)
|
||||
case syntax.TsLss:
|
||||
return atoi(x) < atoi(y)
|
||||
case syntax.TsGtr:
|
||||
return atoi(x) > atoi(y)
|
||||
case syntax.AndTest:
|
||||
return x != "" && y != ""
|
||||
case syntax.OrTest:
|
||||
return x != "" || y != ""
|
||||
case syntax.TsEqual:
|
||||
m, _ := path.Match(y, x)
|
||||
return m
|
||||
case syntax.TsNequal:
|
||||
m, _ := path.Match(y, x)
|
||||
return !m
|
||||
case syntax.TsBefore:
|
||||
return x < y
|
||||
default: // syntax.TsAfter
|
||||
return x > y
|
||||
}
|
||||
}
|
||||
|
||||
func stat(name string) os.FileInfo {
|
||||
info, _ := os.Stat(name)
|
||||
return info
|
||||
}
|
||||
|
||||
func statMode(name string, mode os.FileMode) bool {
|
||||
info := stat(name)
|
||||
return info != nil && info.Mode()&mode != 0
|
||||
}
|
||||
|
||||
func (r *Runner) unTest(op syntax.UnTestOperator, x string) bool {
|
||||
switch op {
|
||||
case syntax.TsExists:
|
||||
return stat(x) != nil
|
||||
case syntax.TsRegFile:
|
||||
info := stat(x)
|
||||
return info != nil && info.Mode().IsRegular()
|
||||
case syntax.TsDirect:
|
||||
return statMode(x, os.ModeDir)
|
||||
//case syntax.TsCharSp:
|
||||
//case syntax.TsBlckSp:
|
||||
case syntax.TsNmPipe:
|
||||
return statMode(x, os.ModeNamedPipe)
|
||||
case syntax.TsSocket:
|
||||
return statMode(x, os.ModeSocket)
|
||||
case syntax.TsSmbLink:
|
||||
info, _ := os.Lstat(x)
|
||||
return info != nil && info.Mode()&os.ModeSymlink != 0
|
||||
case syntax.TsSticky:
|
||||
return statMode(x, os.ModeSticky)
|
||||
case syntax.TsUIDSet:
|
||||
return statMode(x, os.ModeSetuid)
|
||||
case syntax.TsGIDSet:
|
||||
return statMode(x, os.ModeSetgid)
|
||||
//case syntax.TsGrpOwn:
|
||||
//case syntax.TsUsrOwn:
|
||||
//case syntax.TsModif:
|
||||
case syntax.TsRead:
|
||||
f, err := os.OpenFile(x, os.O_RDONLY, 0)
|
||||
if err == nil {
|
||||
f.Close()
|
||||
}
|
||||
return err == nil
|
||||
case syntax.TsWrite:
|
||||
f, err := os.OpenFile(x, os.O_WRONLY, 0)
|
||||
if err == nil {
|
||||
f.Close()
|
||||
}
|
||||
return err == nil
|
||||
case syntax.TsExec:
|
||||
// use an absolute path to not use $PATH
|
||||
_, err := exec.LookPath(filepath.Join(r.Dir, x))
|
||||
return err == nil
|
||||
case syntax.TsNoEmpty:
|
||||
info := stat(x)
|
||||
return info != nil && info.Size() > 0
|
||||
//case syntax.TsFdTerm:
|
||||
case syntax.TsEmpStr:
|
||||
return x == ""
|
||||
case syntax.TsNempStr:
|
||||
return x != ""
|
||||
//case syntax.TsOptSet:
|
||||
//case syntax.TsVarSet:
|
||||
//case syntax.TsRefVar:
|
||||
case syntax.TsNot:
|
||||
return x == ""
|
||||
default:
|
||||
r.errf("unhandled unary test op: %v", op)
|
||||
return false
|
||||
}
|
||||
}
|
37
vendor/github.com/mvdan/sh/syntax/canonical.sh
generated
vendored
Normal file
37
vendor/github.com/mvdan/sh/syntax/canonical.sh
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
#!/bin/bash
|
||||
|
||||
# separate comment
|
||||
|
||||
! foo bar >a &
|
||||
|
||||
foo() { bar; }
|
||||
|
||||
{
|
||||
var1="some long value" # var1 comment
|
||||
var2=short # var2 comment
|
||||
}
|
||||
|
||||
if foo; then bar; fi
|
||||
|
||||
for foo in a b c; do
|
||||
bar
|
||||
done
|
||||
|
||||
case $foo in
|
||||
a) A ;;
|
||||
b)
|
||||
B
|
||||
;;
|
||||
esac
|
||||
|
||||
foo | bar
|
||||
foo \
|
||||
&& $(bar) \
|
||||
&& (more)
|
||||
|
||||
foo 2>&1
|
||||
foo <<EOF
|
||||
bar
|
||||
EOF
|
||||
|
||||
$((3 + 4))
|
6
vendor/github.com/mvdan/sh/syntax/doc.go
generated
vendored
Normal file
6
vendor/github.com/mvdan/sh/syntax/doc.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
|
||||
// See LICENSE for licensing information
|
||||
|
||||
// Package syntax implements parsing and formatting of shell programs.
|
||||
// It supports both POSIX Shell and Bash.
|
||||
package syntax
|
1005
vendor/github.com/mvdan/sh/syntax/lexer.go
generated
vendored
Normal file
1005
vendor/github.com/mvdan/sh/syntax/lexer.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
731
vendor/github.com/mvdan/sh/syntax/nodes.go
generated
vendored
Normal file
731
vendor/github.com/mvdan/sh/syntax/nodes.go
generated
vendored
Normal file
@ -0,0 +1,731 @@
|
||||
// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
|
||||
// See LICENSE for licensing information
|
||||
|
||||
package syntax
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Node represents an AST node.
|
||||
type Node interface {
|
||||
// Pos returns the first character of the node
|
||||
Pos() Pos
|
||||
// End returns the character immediately after the node
|
||||
End() Pos
|
||||
}
|
||||
|
||||
// File is a shell program.
|
||||
type File struct {
|
||||
Name string
|
||||
|
||||
Stmts []*Stmt
|
||||
Comments []*Comment
|
||||
|
||||
lines []Pos
|
||||
}
|
||||
|
||||
// Pos is the internal representation of a position within a source
|
||||
// file.
|
||||
type Pos uint32
|
||||
|
||||
// IsValid reports whether the position is valid. All positions in nodes
|
||||
// returned by Parse are valid.
|
||||
func (p Pos) IsValid() bool { return p > 0 }
|
||||
|
||||
const maxPos = Pos(^uint32(0))
|
||||
|
||||
// Position describes a position within a source file including the line
|
||||
// and column location. A Position is valid if the line number is > 0.
|
||||
type Position struct {
|
||||
Filename string // if any
|
||||
Offset int // byte offset, starting at 0
|
||||
Line int // line number, starting at 1
|
||||
Column int // column number, starting at 1 (in bytes)
|
||||
}
|
||||
|
||||
// IsValid reports whether the position is valid. All positions in nodes
|
||||
// returned by Parse are valid.
|
||||
func (p Position) IsValid() bool { return p.Line > 0 }
|
||||
|
||||
// String returns the position in the "file:line:column" form, or
|
||||
// "line:column" if there is no filename available.
|
||||
func (p Position) String() string {
|
||||
prefix := ""
|
||||
if p.Filename != "" {
|
||||
prefix = p.Filename + ":"
|
||||
}
|
||||
return fmt.Sprintf("%s%d:%d", prefix, p.Line, p.Column)
|
||||
}
|
||||
|
||||
func (f *File) Pos() Pos {
|
||||
if len(f.Stmts) == 0 {
|
||||
return 0
|
||||
}
|
||||
return f.Stmts[0].Pos()
|
||||
}
|
||||
|
||||
func (f *File) End() Pos {
|
||||
if len(f.Stmts) == 0 {
|
||||
return 0
|
||||
}
|
||||
return f.Stmts[len(f.Stmts)-1].End()
|
||||
}
|
||||
|
||||
func (f *File) Position(p Pos) (pos Position) {
|
||||
pos.Filename = f.Name
|
||||
pos.Offset = int(p) - 1
|
||||
if i := searchPos(f.lines, p); i >= 0 {
|
||||
pos.Line, pos.Column = i+1, int(p-f.lines[i])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func searchPos(a []Pos, x Pos) int {
|
||||
i, j := 0, len(a)
|
||||
for i < j {
|
||||
h := i + (j-i)/2
|
||||
if a[h] <= x {
|
||||
i = h + 1
|
||||
} else {
|
||||
j = h
|
||||
}
|
||||
}
|
||||
return i - 1
|
||||
}
|
||||
|
||||
func posMax(p1, p2 Pos) Pos {
|
||||
if p2 > p1 {
|
||||
return p2
|
||||
}
|
||||
return p1
|
||||
}
|
||||
|
||||
// Comment represents a single comment on a single line.
|
||||
type Comment struct {
|
||||
Hash Pos
|
||||
Text string
|
||||
}
|
||||
|
||||
func (c *Comment) Pos() Pos { return c.Hash }
|
||||
func (c *Comment) End() Pos { return c.Hash + Pos(len(c.Text)) }
|
||||
|
||||
// Stmt represents a statement, otherwise known as a compound command.
|
||||
// It is compromised of a command and other components that may come
|
||||
// before or after it.
|
||||
type Stmt struct {
|
||||
Cmd Command
|
||||
Position Pos
|
||||
Semicolon Pos
|
||||
Negated bool
|
||||
Background bool
|
||||
Assigns []*Assign
|
||||
Redirs []*Redirect
|
||||
}
|
||||
|
||||
func (s *Stmt) Pos() Pos { return s.Position }
|
||||
func (s *Stmt) End() Pos {
|
||||
if s.Semicolon.IsValid() {
|
||||
return s.Semicolon + 1
|
||||
}
|
||||
end := s.Position
|
||||
if s.Negated {
|
||||
end++
|
||||
}
|
||||
if s.Cmd != nil {
|
||||
end = s.Cmd.End()
|
||||
}
|
||||
if len(s.Assigns) > 0 {
|
||||
end = posMax(end, s.Assigns[len(s.Assigns)-1].End())
|
||||
}
|
||||
if len(s.Redirs) > 0 {
|
||||
end = posMax(end, s.Redirs[len(s.Redirs)-1].End())
|
||||
}
|
||||
return end
|
||||
}
|
||||
|
||||
// Command represents all nodes that are simple commands, which are
|
||||
// directly placed in a Stmt.
|
||||
type Command interface {
|
||||
Node
|
||||
commandNode()
|
||||
}
|
||||
|
||||
func (*CallExpr) commandNode() {}
|
||||
func (*IfClause) commandNode() {}
|
||||
func (*WhileClause) commandNode() {}
|
||||
func (*UntilClause) commandNode() {}
|
||||
func (*ForClause) commandNode() {}
|
||||
func (*CaseClause) commandNode() {}
|
||||
func (*Block) commandNode() {}
|
||||
func (*Subshell) commandNode() {}
|
||||
func (*BinaryCmd) commandNode() {}
|
||||
func (*FuncDecl) commandNode() {}
|
||||
func (*ArithmCmd) commandNode() {}
|
||||
func (*TestClause) commandNode() {}
|
||||
func (*DeclClause) commandNode() {}
|
||||
func (*EvalClause) commandNode() {}
|
||||
func (*LetClause) commandNode() {}
|
||||
func (*CoprocClause) commandNode() {}
|
||||
|
||||
// Assign represents an assignment to a variable.
|
||||
type Assign struct {
|
||||
Append bool
|
||||
Name *Lit
|
||||
Value *Word
|
||||
}
|
||||
|
||||
func (a *Assign) Pos() Pos {
|
||||
if a.Name != nil {
|
||||
return a.Name.Pos()
|
||||
}
|
||||
return a.Value.Pos()
|
||||
}
|
||||
|
||||
func (a *Assign) End() Pos {
|
||||
if a.Value != nil {
|
||||
return a.Value.End()
|
||||
}
|
||||
return a.Name.End() + 1
|
||||
}
|
||||
|
||||
// Redirect represents an input/output redirection.
|
||||
type Redirect struct {
|
||||
OpPos Pos
|
||||
Op RedirOperator
|
||||
N *Lit
|
||||
Word, Hdoc *Word
|
||||
}
|
||||
|
||||
func (r *Redirect) Pos() Pos {
|
||||
if r.N != nil {
|
||||
return r.N.Pos()
|
||||
}
|
||||
return r.OpPos
|
||||
}
|
||||
func (r *Redirect) End() Pos { return r.Word.End() }
|
||||
|
||||
// CallExpr represents a command execution or function call.
|
||||
type CallExpr struct {
|
||||
Args []*Word
|
||||
}
|
||||
|
||||
func (c *CallExpr) Pos() Pos { return c.Args[0].Pos() }
|
||||
func (c *CallExpr) End() Pos { return c.Args[len(c.Args)-1].End() }
|
||||
|
||||
// Subshell represents a series of commands that should be executed in a
|
||||
// nested shell environment.
|
||||
type Subshell struct {
|
||||
Lparen, Rparen Pos
|
||||
Stmts []*Stmt
|
||||
}
|
||||
|
||||
func (s *Subshell) Pos() Pos { return s.Lparen }
|
||||
func (s *Subshell) End() Pos { return s.Rparen + 1 }
|
||||
|
||||
// Block represents a series of commands that should be executed in a
|
||||
// nested scope.
|
||||
type Block struct {
|
||||
Lbrace, Rbrace Pos
|
||||
Stmts []*Stmt
|
||||
}
|
||||
|
||||
func (b *Block) Pos() Pos { return b.Rbrace }
|
||||
func (b *Block) End() Pos { return b.Rbrace + 1 }
|
||||
|
||||
// IfClause represents an if statement.
|
||||
type IfClause struct {
|
||||
If, Then, Else, Fi Pos
|
||||
CondStmts []*Stmt
|
||||
ThenStmts []*Stmt
|
||||
Elifs []*Elif
|
||||
ElseStmts []*Stmt
|
||||
}
|
||||
|
||||
func (c *IfClause) Pos() Pos { return c.If }
|
||||
func (c *IfClause) End() Pos { return c.Fi + 2 }
|
||||
|
||||
// Elif represents an "else if" case in an if clause.
|
||||
type Elif struct {
|
||||
Elif, Then Pos
|
||||
CondStmts []*Stmt
|
||||
ThenStmts []*Stmt
|
||||
}
|
||||
|
||||
// WhileClause represents a while clause.
|
||||
type WhileClause struct {
|
||||
While, Do, Done Pos
|
||||
CondStmts []*Stmt
|
||||
DoStmts []*Stmt
|
||||
}
|
||||
|
||||
func (w *WhileClause) Pos() Pos { return w.While }
|
||||
func (w *WhileClause) End() Pos { return w.Done + 4 }
|
||||
|
||||
// UntilClause represents an until clause.
|
||||
type UntilClause struct {
|
||||
Until, Do, Done Pos
|
||||
CondStmts []*Stmt
|
||||
DoStmts []*Stmt
|
||||
}
|
||||
|
||||
func (u *UntilClause) Pos() Pos { return u.Until }
|
||||
func (u *UntilClause) End() Pos { return u.Done + 4 }
|
||||
|
||||
// ForClause represents a for clause.
|
||||
type ForClause struct {
|
||||
For, Do, Done Pos
|
||||
Loop Loop
|
||||
DoStmts []*Stmt
|
||||
}
|
||||
|
||||
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.
|
||||
type Loop interface {
|
||||
Node
|
||||
loopNode()
|
||||
}
|
||||
|
||||
func (*WordIter) loopNode() {}
|
||||
func (*CStyleLoop) loopNode() {}
|
||||
|
||||
// WordIter represents the iteration of a variable over a series of
|
||||
// words in a for clause.
|
||||
type WordIter struct {
|
||||
Name *Lit
|
||||
List []*Word
|
||||
}
|
||||
|
||||
func (w *WordIter) Pos() Pos { return w.Name.Pos() }
|
||||
func (w *WordIter) End() Pos { return posMax(w.Name.End(), wordLastEnd(w.List)) }
|
||||
|
||||
// CStyleLoop represents the behaviour of a for clause similar to the C
|
||||
// language.
|
||||
//
|
||||
// This node will never appear when in PosixConformant mode.
|
||||
type CStyleLoop struct {
|
||||
Lparen, Rparen Pos
|
||||
Init, Cond, Post ArithmExpr
|
||||
}
|
||||
|
||||
func (c *CStyleLoop) Pos() Pos { return c.Lparen }
|
||||
func (c *CStyleLoop) End() Pos { return c.Rparen + 2 }
|
||||
|
||||
// BinaryCmd represents a binary expression between two statements.
|
||||
type BinaryCmd struct {
|
||||
OpPos Pos
|
||||
Op BinCmdOperator
|
||||
X, Y *Stmt
|
||||
}
|
||||
|
||||
func (b *BinaryCmd) Pos() Pos { return b.X.Pos() }
|
||||
func (b *BinaryCmd) End() Pos { return b.Y.End() }
|
||||
|
||||
// FuncDecl represents the declaration of a function.
|
||||
type FuncDecl struct {
|
||||
Position Pos
|
||||
BashStyle bool
|
||||
Name *Lit
|
||||
Body *Stmt
|
||||
}
|
||||
|
||||
func (f *FuncDecl) Pos() Pos { return f.Position }
|
||||
func (f *FuncDecl) End() Pos { return f.Body.End() }
|
||||
|
||||
// Word represents a non-empty list of nodes that are contiguous to each
|
||||
// other. The word is delimeted by word boundaries.
|
||||
type Word struct {
|
||||
Parts []WordPart
|
||||
}
|
||||
|
||||
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.
|
||||
type WordPart interface {
|
||||
Node
|
||||
wordPartNode()
|
||||
}
|
||||
|
||||
func (*Lit) wordPartNode() {}
|
||||
func (*SglQuoted) wordPartNode() {}
|
||||
func (*DblQuoted) wordPartNode() {}
|
||||
func (*ParamExp) wordPartNode() {}
|
||||
func (*CmdSubst) wordPartNode() {}
|
||||
func (*ArithmExp) wordPartNode() {}
|
||||
func (*ProcSubst) wordPartNode() {}
|
||||
func (*ArrayExpr) wordPartNode() {}
|
||||
func (*ExtGlob) wordPartNode() {}
|
||||
|
||||
// Lit represents an unquoted string consisting of characters that were
|
||||
// not tokenized.
|
||||
type Lit struct {
|
||||
ValuePos, ValueEnd Pos
|
||||
Value string
|
||||
}
|
||||
|
||||
func (l *Lit) Pos() Pos { return l.ValuePos }
|
||||
func (l *Lit) End() Pos { return l.ValueEnd }
|
||||
|
||||
// SglQuoted represents a string within single quotes.
|
||||
type SglQuoted struct {
|
||||
Position Pos
|
||||
Dollar bool
|
||||
Value string
|
||||
}
|
||||
|
||||
func (q *SglQuoted) Pos() Pos { return q.Position }
|
||||
func (q *SglQuoted) End() Pos {
|
||||
end := q.Position + 2 + Pos(len(q.Value))
|
||||
if q.Dollar {
|
||||
end++
|
||||
}
|
||||
return end
|
||||
}
|
||||
|
||||
// DblQuoted represents a list of nodes within double quotes.
|
||||
type DblQuoted struct {
|
||||
Position Pos
|
||||
Dollar bool
|
||||
Parts []WordPart
|
||||
}
|
||||
|
||||
func (q *DblQuoted) Pos() Pos { return q.Position }
|
||||
func (q *DblQuoted) End() Pos {
|
||||
if len(q.Parts) == 0 {
|
||||
if q.Dollar {
|
||||
return q.Position + 3
|
||||
}
|
||||
return q.Position + 2
|
||||
}
|
||||
return q.Parts[len(q.Parts)-1].End() + 1
|
||||
}
|
||||
|
||||
// CmdSubst represents a command substitution.
|
||||
type CmdSubst struct {
|
||||
Left, Right Pos
|
||||
Stmts []*Stmt
|
||||
}
|
||||
|
||||
func (c *CmdSubst) Pos() Pos { return c.Left }
|
||||
func (c *CmdSubst) End() Pos { return c.Right + 1 }
|
||||
|
||||
// ParamExp represents a parameter expansion.
|
||||
type ParamExp struct {
|
||||
Dollar, Rbrace Pos
|
||||
Short bool
|
||||
Length, Excl bool // TODO(mvdan): rename Excl in 2.0 (Indirect, etc)
|
||||
Param *Lit
|
||||
Ind *Index
|
||||
Slice *Slice
|
||||
Repl *Replace
|
||||
Exp *Expansion
|
||||
}
|
||||
|
||||
func (p *ParamExp) Pos() Pos { return p.Dollar }
|
||||
func (p *ParamExp) End() Pos {
|
||||
if !p.Short {
|
||||
return p.Rbrace + 1
|
||||
}
|
||||
return p.Param.End()
|
||||
}
|
||||
|
||||
// Index represents access to an array via an index inside a ParamExp.
|
||||
//
|
||||
// This node will never appear when in PosixConformant mode.
|
||||
type Index struct {
|
||||
Expr ArithmExpr
|
||||
}
|
||||
|
||||
// Slice represents character slicing inside a ParamExp.
|
||||
//
|
||||
// This node will never appear when in PosixConformant mode.
|
||||
type Slice struct {
|
||||
Offset, Length ArithmExpr
|
||||
}
|
||||
|
||||
// Replace represents a search and replace inside a ParamExp.
|
||||
type Replace struct {
|
||||
All bool
|
||||
Orig, With *Word
|
||||
}
|
||||
|
||||
// Expansion represents string manipulation in a ParamExp other than
|
||||
// those covered by Replace.
|
||||
type Expansion struct {
|
||||
Op ParExpOperator
|
||||
Word *Word
|
||||
}
|
||||
|
||||
// ArithmExp represents an arithmetic expansion.
|
||||
type ArithmExp struct {
|
||||
Left, Right Pos
|
||||
Bracket bool
|
||||
X ArithmExpr
|
||||
}
|
||||
|
||||
func (a *ArithmExp) Pos() Pos { return a.Left }
|
||||
func (a *ArithmExp) End() Pos {
|
||||
if a.Bracket {
|
||||
return a.Right + 1
|
||||
}
|
||||
return a.Right + 2
|
||||
}
|
||||
|
||||
// ArithmCmd represents an arithmetic command.
|
||||
//
|
||||
// This node will never appear when in PosixConformant mode.
|
||||
type ArithmCmd struct {
|
||||
Left, Right Pos
|
||||
X ArithmExpr
|
||||
}
|
||||
|
||||
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.
|
||||
type ArithmExpr interface {
|
||||
Node
|
||||
arithmExprNode()
|
||||
}
|
||||
|
||||
func (*BinaryArithm) arithmExprNode() {}
|
||||
func (*UnaryArithm) arithmExprNode() {}
|
||||
func (*ParenArithm) arithmExprNode() {}
|
||||
func (*Word) 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.
|
||||
//
|
||||
// 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
|
||||
X, Y ArithmExpr
|
||||
}
|
||||
|
||||
func (b *BinaryArithm) Pos() Pos { return b.X.Pos() }
|
||||
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.
|
||||
type UnaryArithm struct {
|
||||
OpPos Pos
|
||||
Op UnAritOperator
|
||||
Post bool
|
||||
X ArithmExpr
|
||||
}
|
||||
|
||||
func (u *UnaryArithm) Pos() Pos {
|
||||
if u.Post {
|
||||
return u.X.Pos()
|
||||
}
|
||||
return u.OpPos
|
||||
}
|
||||
|
||||
func (u *UnaryArithm) End() Pos {
|
||||
if u.Post {
|
||||
return u.OpPos + 2
|
||||
}
|
||||
return u.X.End()
|
||||
}
|
||||
|
||||
// ParenArithm represents an expression within parentheses inside an
|
||||
// ArithmExp.
|
||||
type ParenArithm struct {
|
||||
Lparen, Rparen Pos
|
||||
X ArithmExpr
|
||||
}
|
||||
|
||||
func (p *ParenArithm) Pos() Pos { return p.Lparen }
|
||||
func (p *ParenArithm) End() Pos { return p.Rparen + 1 }
|
||||
|
||||
// CaseClause represents a case (switch) clause.
|
||||
type CaseClause struct {
|
||||
Case, Esac Pos
|
||||
Word *Word
|
||||
List []*PatternList
|
||||
}
|
||||
|
||||
func (c *CaseClause) Pos() Pos { return c.Case }
|
||||
func (c *CaseClause) End() Pos { return c.Esac + 4 }
|
||||
|
||||
// PatternList represents a pattern list (case) within a CaseClause.
|
||||
type PatternList struct {
|
||||
Op CaseOperator
|
||||
OpPos Pos
|
||||
Patterns []*Word
|
||||
Stmts []*Stmt
|
||||
}
|
||||
|
||||
// TestClause represents a Bash extended test clause.
|
||||
//
|
||||
// This node will never appear when in PosixConformant mode.
|
||||
type TestClause struct {
|
||||
Left, Right Pos
|
||||
X TestExpr
|
||||
}
|
||||
|
||||
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.
|
||||
type TestExpr interface {
|
||||
Node
|
||||
testExprNode()
|
||||
}
|
||||
|
||||
func (*BinaryTest) testExprNode() {}
|
||||
func (*UnaryTest) testExprNode() {}
|
||||
func (*ParenTest) testExprNode() {}
|
||||
func (*Word) testExprNode() {}
|
||||
|
||||
// BinaryTest represents a binary expression between two arithmetic
|
||||
// expression.
|
||||
type BinaryTest struct {
|
||||
OpPos Pos
|
||||
Op BinTestOperator
|
||||
X, Y TestExpr
|
||||
}
|
||||
|
||||
func (b *BinaryTest) Pos() Pos { return b.X.Pos() }
|
||||
func (b *BinaryTest) End() Pos { return b.Y.End() }
|
||||
|
||||
// UnaryTest represents an unary expression over a node, either before
|
||||
// or after it.
|
||||
type UnaryTest struct {
|
||||
OpPos Pos
|
||||
Op UnTestOperator
|
||||
X TestExpr
|
||||
}
|
||||
|
||||
func (u *UnaryTest) Pos() Pos { return u.OpPos }
|
||||
func (u *UnaryTest) End() Pos { return u.X.End() }
|
||||
|
||||
// ParenTest represents an expression within parentheses inside an
|
||||
// TestExp.
|
||||
type ParenTest struct {
|
||||
Lparen, Rparen Pos
|
||||
X TestExpr
|
||||
}
|
||||
|
||||
func (p *ParenTest) Pos() Pos { return p.Lparen }
|
||||
func (p *ParenTest) End() Pos { return p.Rparen + 1 }
|
||||
|
||||
// DeclClause represents a Bash declare clause.
|
||||
//
|
||||
// This node will never appear when in PosixConformant mode.
|
||||
type DeclClause struct {
|
||||
Position Pos
|
||||
Variant string
|
||||
Opts []*Word
|
||||
Assigns []*Assign
|
||||
}
|
||||
|
||||
func (d *DeclClause) Pos() Pos { return d.Position }
|
||||
func (d *DeclClause) End() Pos {
|
||||
if len(d.Assigns) > 0 {
|
||||
return d.Assigns[len(d.Assigns)-1].End()
|
||||
}
|
||||
return wordLastEnd(d.Opts)
|
||||
}
|
||||
|
||||
// ArrayExpr represents a Bash array expression.
|
||||
//
|
||||
// This node will never appear when in PosixConformant mode.
|
||||
type ArrayExpr struct {
|
||||
Lparen, Rparen Pos
|
||||
List []*Word
|
||||
}
|
||||
|
||||
func (a *ArrayExpr) Pos() Pos { return a.Lparen }
|
||||
func (a *ArrayExpr) End() Pos { return a.Rparen + 1 }
|
||||
|
||||
// ExtGlob represents a Bash extended globbing expression. Note that
|
||||
// these are parsed independently of whether shopt has been called or
|
||||
// not.
|
||||
//
|
||||
// This node will never appear when in PosixConformant mode.
|
||||
type ExtGlob struct {
|
||||
OpPos Pos
|
||||
Op GlobOperator
|
||||
Pattern *Lit
|
||||
}
|
||||
|
||||
func (e *ExtGlob) Pos() Pos { return e.OpPos }
|
||||
func (e *ExtGlob) End() Pos { return e.Pattern.End() + 1 }
|
||||
|
||||
// ProcSubst represents a Bash process substitution.
|
||||
//
|
||||
// This node will never appear when in PosixConformant mode.
|
||||
type ProcSubst struct {
|
||||
OpPos, Rparen Pos
|
||||
Op ProcOperator
|
||||
Stmts []*Stmt
|
||||
}
|
||||
|
||||
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.
|
||||
type CoprocClause struct {
|
||||
Coproc Pos
|
||||
Name *Lit
|
||||
Stmt *Stmt
|
||||
}
|
||||
|
||||
func (c *CoprocClause) Pos() Pos { return c.Coproc }
|
||||
func (c *CoprocClause) End() Pos { return c.Stmt.End() }
|
||||
|
||||
// LetClause represents a Bash let clause.
|
||||
//
|
||||
// This node will never appear when in PosixConformant mode.
|
||||
type LetClause struct {
|
||||
Let Pos
|
||||
Exprs []ArithmExpr
|
||||
}
|
||||
|
||||
func (l *LetClause) Pos() Pos { return l.Let }
|
||||
func (l *LetClause) End() Pos { return l.Exprs[len(l.Exprs)-1].End() }
|
||||
|
||||
func wordLastEnd(ws []*Word) Pos {
|
||||
if len(ws) == 0 {
|
||||
return 0
|
||||
}
|
||||
return ws[len(ws)-1].End()
|
||||
}
|
1768
vendor/github.com/mvdan/sh/syntax/parser.go
generated
vendored
Normal file
1768
vendor/github.com/mvdan/sh/syntax/parser.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
904
vendor/github.com/mvdan/sh/syntax/printer.go
generated
vendored
Normal file
904
vendor/github.com/mvdan/sh/syntax/printer.go
generated
vendored
Normal file
@ -0,0 +1,904 @@
|
||||
// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
|
||||
// See LICENSE for licensing information
|
||||
|
||||
package syntax
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
var printerFree = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &printer{
|
||||
bufWriter: bufio.NewWriter(nil),
|
||||
lenPrinter: new(printer),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Fprint "pretty-prints" the given AST file to the given writer.
|
||||
func (c PrintConfig) Fprint(w io.Writer, f *File) error {
|
||||
p := printerFree.Get().(*printer)
|
||||
p.reset()
|
||||
p.PrintConfig = c
|
||||
p.lines, p.comments = f.lines, f.Comments
|
||||
p.bufWriter.Reset(w)
|
||||
p.stmts(f.Stmts)
|
||||
p.commentsUpTo(0)
|
||||
p.newline(0)
|
||||
var err error
|
||||
if flusher, ok := p.bufWriter.(interface {
|
||||
Flush() error
|
||||
}); ok {
|
||||
err = flusher.Flush()
|
||||
}
|
||||
printerFree.Put(p)
|
||||
return err
|
||||
}
|
||||
|
||||
// Fprint "pretty-prints" the given AST file to the given writer. It
|
||||
// calls PrintConfig.Fprint with its default settings.
|
||||
func Fprint(w io.Writer, f *File) error {
|
||||
return PrintConfig{}.Fprint(w, f)
|
||||
}
|
||||
|
||||
type bufWriter interface {
|
||||
WriteByte(byte) error
|
||||
WriteString(string) (int, error)
|
||||
Reset(io.Writer)
|
||||
}
|
||||
|
||||
type printer struct {
|
||||
bufWriter
|
||||
|
||||
PrintConfig
|
||||
lines []Pos
|
||||
|
||||
wantSpace bool
|
||||
wantNewline bool
|
||||
wroteSemi bool
|
||||
|
||||
commentPadding int
|
||||
|
||||
// nline is the position of the next newline
|
||||
nline Pos
|
||||
nlineIndex int
|
||||
|
||||
// lastLevel is the last level of indentation that was used.
|
||||
lastLevel int
|
||||
// level is the current level of indentation.
|
||||
level int
|
||||
// levelIncs records which indentation level increments actually
|
||||
// took place, to revert them once their section ends.
|
||||
levelIncs []bool
|
||||
|
||||
nestedBinary bool
|
||||
|
||||
// comments is the list of pending comments to write.
|
||||
comments []*Comment
|
||||
|
||||
// pendingHdocs is the list of pending heredocs to write.
|
||||
pendingHdocs []*Redirect
|
||||
|
||||
// used in stmtLen to align comments
|
||||
lenPrinter *printer
|
||||
lenCounter byteCounter
|
||||
}
|
||||
|
||||
func (p *printer) reset() {
|
||||
p.wantSpace, p.wantNewline = false, false
|
||||
p.commentPadding = 0
|
||||
p.nline, p.nlineIndex = 0, 0
|
||||
p.lastLevel, p.level = 0, 0
|
||||
p.levelIncs = p.levelIncs[:0]
|
||||
p.nestedBinary = false
|
||||
p.pendingHdocs = p.pendingHdocs[:0]
|
||||
}
|
||||
|
||||
func (p *printer) incLine() {
|
||||
if p.nlineIndex++; p.nlineIndex >= len(p.lines) {
|
||||
p.nline = maxPos
|
||||
} else {
|
||||
p.nline = p.lines[p.nlineIndex]
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) incLines(pos Pos) {
|
||||
for p.nline < pos {
|
||||
p.incLine()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) spaces(n int) {
|
||||
for i := 0; i < n; i++ {
|
||||
p.WriteByte(' ')
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) bslashNewl() {
|
||||
if p.wantSpace {
|
||||
p.WriteByte(' ')
|
||||
}
|
||||
p.WriteString("\\\n")
|
||||
p.wantSpace = false
|
||||
p.incLine()
|
||||
}
|
||||
|
||||
func (p *printer) spacedString(s string) {
|
||||
if p.wantSpace {
|
||||
p.WriteByte(' ')
|
||||
}
|
||||
p.WriteString(s)
|
||||
p.wantSpace = true
|
||||
}
|
||||
|
||||
func (p *printer) semiOrNewl(s string, pos Pos) {
|
||||
if p.wantNewline {
|
||||
p.newline(pos)
|
||||
p.indent()
|
||||
} else {
|
||||
if !p.wroteSemi {
|
||||
p.WriteByte(';')
|
||||
}
|
||||
p.WriteByte(' ')
|
||||
p.incLines(pos)
|
||||
}
|
||||
p.WriteString(s)
|
||||
p.wantSpace = true
|
||||
}
|
||||
|
||||
func (p *printer) incLevel() {
|
||||
inc := false
|
||||
if p.level <= p.lastLevel || len(p.levelIncs) == 0 {
|
||||
p.level++
|
||||
inc = true
|
||||
} else if last := &p.levelIncs[len(p.levelIncs)-1]; *last {
|
||||
*last = false
|
||||
inc = true
|
||||
}
|
||||
p.levelIncs = append(p.levelIncs, inc)
|
||||
}
|
||||
|
||||
func (p *printer) decLevel() {
|
||||
if p.levelIncs[len(p.levelIncs)-1] {
|
||||
p.level--
|
||||
}
|
||||
p.levelIncs = p.levelIncs[:len(p.levelIncs)-1]
|
||||
}
|
||||
|
||||
func (p *printer) indent() {
|
||||
p.lastLevel = p.level
|
||||
switch {
|
||||
case p.level == 0:
|
||||
case p.Spaces == 0:
|
||||
for i := 0; i < p.level; i++ {
|
||||
p.WriteByte('\t')
|
||||
}
|
||||
case p.Spaces > 0:
|
||||
p.spaces(p.Spaces * p.level)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) newline(pos Pos) {
|
||||
p.wantNewline, p.wantSpace = false, false
|
||||
p.WriteByte('\n')
|
||||
if pos > p.nline {
|
||||
p.incLine()
|
||||
}
|
||||
hdocs := p.pendingHdocs
|
||||
p.pendingHdocs = p.pendingHdocs[:0]
|
||||
for _, r := range hdocs {
|
||||
p.word(r.Hdoc)
|
||||
p.incLines(r.Hdoc.End())
|
||||
p.unquotedWord(r.Word)
|
||||
p.WriteByte('\n')
|
||||
p.incLine()
|
||||
p.wantSpace = false
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) newlines(pos Pos) {
|
||||
p.newline(pos)
|
||||
if pos > p.nline {
|
||||
// preserve single empty lines
|
||||
p.WriteByte('\n')
|
||||
p.incLine()
|
||||
}
|
||||
p.indent()
|
||||
}
|
||||
|
||||
func (p *printer) commentsAndSeparate(pos Pos) {
|
||||
p.commentsUpTo(pos)
|
||||
if p.wantNewline || pos > p.nline {
|
||||
p.newlines(pos)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) sepTok(s string, pos Pos) {
|
||||
p.level++
|
||||
p.commentsUpTo(pos)
|
||||
p.level--
|
||||
if p.wantNewline || pos > p.nline {
|
||||
p.newlines(pos)
|
||||
}
|
||||
p.WriteString(s)
|
||||
p.wantSpace = true
|
||||
}
|
||||
|
||||
func (p *printer) semiRsrv(s string, pos Pos, fallback bool) {
|
||||
p.level++
|
||||
p.commentsUpTo(pos)
|
||||
p.level--
|
||||
if p.wantNewline || pos > p.nline {
|
||||
p.newlines(pos)
|
||||
} else if fallback {
|
||||
if !p.wroteSemi {
|
||||
p.WriteByte(';')
|
||||
}
|
||||
p.WriteByte(' ')
|
||||
} else if p.wantSpace {
|
||||
p.WriteByte(' ')
|
||||
}
|
||||
p.WriteString(s)
|
||||
p.wantSpace = true
|
||||
}
|
||||
|
||||
func (p *printer) anyCommentsBefore(pos Pos) bool {
|
||||
if !pos.IsValid() || len(p.comments) < 1 {
|
||||
return false
|
||||
}
|
||||
return p.comments[0].Hash < pos
|
||||
}
|
||||
|
||||
func (p *printer) commentsUpTo(pos Pos) {
|
||||
if len(p.comments) < 1 {
|
||||
return
|
||||
}
|
||||
c := p.comments[0]
|
||||
if pos.IsValid() && c.Hash >= pos {
|
||||
return
|
||||
}
|
||||
p.comments = p.comments[1:]
|
||||
switch {
|
||||
case p.nlineIndex == 0:
|
||||
case c.Hash > p.nline:
|
||||
p.newlines(c.Hash)
|
||||
case p.wantSpace:
|
||||
p.spaces(p.commentPadding + 1)
|
||||
}
|
||||
p.incLines(c.Hash)
|
||||
p.WriteByte('#')
|
||||
p.WriteString(c.Text)
|
||||
p.commentsUpTo(pos)
|
||||
}
|
||||
|
||||
func (p *printer) wordPart(wp WordPart) {
|
||||
switch x := wp.(type) {
|
||||
case *Lit:
|
||||
p.WriteString(x.Value)
|
||||
case *SglQuoted:
|
||||
if x.Dollar {
|
||||
p.WriteByte('$')
|
||||
}
|
||||
p.WriteByte('\'')
|
||||
p.WriteString(x.Value)
|
||||
p.WriteByte('\'')
|
||||
p.incLines(x.End())
|
||||
case *DblQuoted:
|
||||
if x.Dollar {
|
||||
p.WriteByte('$')
|
||||
}
|
||||
p.WriteByte('"')
|
||||
for i, n := range x.Parts {
|
||||
p.wordPart(n)
|
||||
if i == len(x.Parts)-1 {
|
||||
p.incLines(n.End())
|
||||
}
|
||||
}
|
||||
p.WriteByte('"')
|
||||
case *CmdSubst:
|
||||
p.incLines(x.Pos())
|
||||
p.WriteString("$(")
|
||||
p.wantSpace = len(x.Stmts) > 0 && startsWithLparen(x.Stmts[0])
|
||||
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('}')
|
||||
case *ArithmExp:
|
||||
p.WriteString("$((")
|
||||
p.arithmExpr(x.X, false)
|
||||
p.WriteString("))")
|
||||
case *ArrayExpr:
|
||||
p.wantSpace = false
|
||||
p.WriteByte('(')
|
||||
p.wordJoin(x.List, false)
|
||||
p.sepTok(")", x.Rparen)
|
||||
case *ExtGlob:
|
||||
p.WriteString(x.Op.String())
|
||||
p.WriteString(x.Pattern.Value)
|
||||
p.WriteByte(')')
|
||||
case *ProcSubst:
|
||||
// avoid conflict with << and others
|
||||
if p.wantSpace {
|
||||
p.WriteByte(' ')
|
||||
p.wantSpace = false
|
||||
}
|
||||
p.WriteString(x.Op.String())
|
||||
p.nestedStmts(x.Stmts, 0)
|
||||
p.WriteByte(')')
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) loop(loop Loop) {
|
||||
switch x := loop.(type) {
|
||||
case *WordIter:
|
||||
p.WriteString(x.Name.Value)
|
||||
if len(x.List) > 0 {
|
||||
p.spacedString(" in")
|
||||
p.wordJoin(x.List, true)
|
||||
}
|
||||
case *CStyleLoop:
|
||||
p.WriteString("((")
|
||||
if x.Init == nil {
|
||||
p.WriteByte(' ')
|
||||
}
|
||||
p.arithmExpr(x.Init, false)
|
||||
p.WriteString("; ")
|
||||
p.arithmExpr(x.Cond, false)
|
||||
p.WriteString("; ")
|
||||
p.arithmExpr(x.Post, false)
|
||||
p.WriteString("))")
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) arithmExpr(expr ArithmExpr, compact bool) {
|
||||
switch x := expr.(type) {
|
||||
case *Word:
|
||||
p.word(x)
|
||||
case *BinaryArithm:
|
||||
if compact {
|
||||
p.arithmExpr(x.X, compact)
|
||||
p.WriteString(x.Op.String())
|
||||
p.arithmExpr(x.Y, compact)
|
||||
} else {
|
||||
p.arithmExpr(x.X, compact)
|
||||
if x.Op != Comma {
|
||||
p.WriteByte(' ')
|
||||
}
|
||||
p.WriteString(x.Op.String())
|
||||
p.WriteByte(' ')
|
||||
p.arithmExpr(x.Y, compact)
|
||||
}
|
||||
case *UnaryArithm:
|
||||
if x.Post {
|
||||
p.arithmExpr(x.X, compact)
|
||||
p.WriteString(x.Op.String())
|
||||
} else {
|
||||
p.WriteString(x.Op.String())
|
||||
p.arithmExpr(x.X, compact)
|
||||
}
|
||||
case *ParenArithm:
|
||||
p.WriteByte('(')
|
||||
p.arithmExpr(x.X, false)
|
||||
p.WriteByte(')')
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) testExpr(expr TestExpr) {
|
||||
switch x := expr.(type) {
|
||||
case *Word:
|
||||
p.word(x)
|
||||
case *BinaryTest:
|
||||
p.testExpr(x.X)
|
||||
p.WriteByte(' ')
|
||||
p.WriteString(x.Op.String())
|
||||
p.WriteByte(' ')
|
||||
p.testExpr(x.Y)
|
||||
case *UnaryTest:
|
||||
p.WriteString(x.Op.String())
|
||||
p.WriteByte(' ')
|
||||
p.testExpr(x.X)
|
||||
case *ParenTest:
|
||||
p.WriteByte('(')
|
||||
p.testExpr(x.X)
|
||||
p.WriteByte(')')
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) word(w *Word) {
|
||||
for _, n := range w.Parts {
|
||||
p.wordPart(n)
|
||||
}
|
||||
p.wantSpace = true
|
||||
}
|
||||
|
||||
func (p *printer) unquotedWord(w *Word) {
|
||||
for _, wp := range w.Parts {
|
||||
switch x := wp.(type) {
|
||||
case *SglQuoted:
|
||||
p.WriteString(x.Value)
|
||||
case *DblQuoted:
|
||||
for _, qp := range x.Parts {
|
||||
p.wordPart(qp)
|
||||
}
|
||||
case *Lit:
|
||||
for i := 0; i < len(x.Value); i++ {
|
||||
if b := x.Value[i]; b == '\\' {
|
||||
if i++; i < len(x.Value) {
|
||||
p.WriteByte(x.Value[i])
|
||||
}
|
||||
} else {
|
||||
p.WriteByte(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) wordJoin(ws []*Word, backslash bool) {
|
||||
anyNewline := false
|
||||
for _, w := range ws {
|
||||
if pos := w.Pos(); pos > p.nline {
|
||||
p.commentsUpTo(pos)
|
||||
if backslash {
|
||||
p.bslashNewl()
|
||||
} else {
|
||||
p.WriteByte('\n')
|
||||
p.incLine()
|
||||
}
|
||||
if !anyNewline {
|
||||
p.incLevel()
|
||||
anyNewline = true
|
||||
}
|
||||
p.indent()
|
||||
} else if p.wantSpace {
|
||||
p.WriteByte(' ')
|
||||
p.wantSpace = false
|
||||
}
|
||||
p.word(w)
|
||||
}
|
||||
if anyNewline {
|
||||
p.decLevel()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) stmt(s *Stmt) {
|
||||
if s.Negated {
|
||||
p.spacedString("!")
|
||||
}
|
||||
p.assigns(s.Assigns)
|
||||
var startRedirs int
|
||||
if s.Cmd != nil {
|
||||
startRedirs = p.command(s.Cmd, s.Redirs)
|
||||
}
|
||||
anyNewline := false
|
||||
for _, r := range s.Redirs[startRedirs:] {
|
||||
if r.OpPos > p.nline {
|
||||
p.bslashNewl()
|
||||
if !anyNewline {
|
||||
p.incLevel()
|
||||
anyNewline = true
|
||||
}
|
||||
p.indent()
|
||||
}
|
||||
p.commentsAndSeparate(r.OpPos)
|
||||
if p.wantSpace {
|
||||
p.WriteByte(' ')
|
||||
}
|
||||
if r.N != nil {
|
||||
p.WriteString(r.N.Value)
|
||||
}
|
||||
p.WriteString(r.Op.String())
|
||||
p.word(r.Word)
|
||||
if r.Op == Hdoc || r.Op == DashHdoc {
|
||||
p.pendingHdocs = append(p.pendingHdocs, r)
|
||||
}
|
||||
}
|
||||
p.wroteSemi = false
|
||||
if s.Semicolon.IsValid() && s.Semicolon > p.nline {
|
||||
p.incLevel()
|
||||
p.bslashNewl()
|
||||
p.indent()
|
||||
p.decLevel()
|
||||
p.WriteByte(';')
|
||||
p.wroteSemi = true
|
||||
} else if s.Background {
|
||||
p.WriteString(" &")
|
||||
}
|
||||
if anyNewline {
|
||||
p.decLevel()
|
||||
}
|
||||
}
|
||||
|
||||
func (p *printer) command(cmd Command, redirs []*Redirect) (startRedirs int) {
|
||||
if p.wantSpace {
|
||||
p.WriteByte(' ')
|
||||
p.wantSpace = false
|
||||
}
|
||||
switch x := cmd.(type) {
|
||||
case *CallExpr:
|
||||
if len(x.Args) <= 1 {
|
||||
p.wordJoin(x.Args, true)
|
||||
return 0
|
||||
}
|
||||
p.wordJoin(x.Args[:1], true)
|
||||
for _, r := range redirs {
|
||||
if r.Pos() > x.Args[1].Pos() || r.Op == Hdoc || r.Op == DashHdoc {
|
||||
break
|
||||
}
|
||||
if p.wantSpace {
|
||||
p.WriteByte(' ')
|
||||
}
|
||||
if r.N != nil {
|
||||
p.WriteString(r.N.Value)
|
||||
}
|
||||
p.WriteString(r.Op.String())
|
||||
p.word(r.Word)
|
||||
startRedirs++
|
||||
}
|
||||
p.wordJoin(x.Args[1:], true)
|
||||
case *Block:
|
||||
p.WriteByte('{')
|
||||
p.wantSpace = true
|
||||
p.nestedStmts(x.Stmts, x.Rbrace)
|
||||
p.semiRsrv("}", x.Rbrace, true)
|
||||
case *IfClause:
|
||||
p.spacedString("if")
|
||||
p.nestedStmts(x.CondStmts, 0)
|
||||
p.semiOrNewl("then", x.Then)
|
||||
p.nestedStmts(x.ThenStmts, 0)
|
||||
for _, el := range x.Elifs {
|
||||
p.semiRsrv("elif", el.Elif, true)
|
||||
p.nestedStmts(el.CondStmts, 0)
|
||||
p.semiOrNewl("then", el.Then)
|
||||
p.nestedStmts(el.ThenStmts, 0)
|
||||
}
|
||||
if len(x.ElseStmts) > 0 {
|
||||
p.semiRsrv("else", x.Else, true)
|
||||
p.nestedStmts(x.ElseStmts, 0)
|
||||
} else if x.Else.IsValid() {
|
||||
p.incLines(x.Else)
|
||||
}
|
||||
p.semiRsrv("fi", x.Fi, true)
|
||||
case *Subshell:
|
||||
p.WriteByte('(')
|
||||
p.wantSpace = len(x.Stmts) > 0 && startsWithLparen(x.Stmts[0])
|
||||
p.nestedStmts(x.Stmts, x.Rparen)
|
||||
p.sepTok(")", x.Rparen)
|
||||
case *WhileClause:
|
||||
p.spacedString("while")
|
||||
p.nestedStmts(x.CondStmts, 0)
|
||||
p.semiOrNewl("do", x.Do)
|
||||
p.nestedStmts(x.DoStmts, 0)
|
||||
p.semiRsrv("done", x.Done, true)
|
||||
case *ForClause:
|
||||
p.WriteString("for ")
|
||||
p.loop(x.Loop)
|
||||
p.semiOrNewl("do", x.Do)
|
||||
p.nestedStmts(x.DoStmts, 0)
|
||||
p.semiRsrv("done", x.Done, true)
|
||||
case *BinaryCmd:
|
||||
p.stmt(x.X)
|
||||
indent := !p.nestedBinary
|
||||
if indent {
|
||||
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()
|
||||
}
|
||||
p.incLines(x.Y.Pos())
|
||||
p.stmt(x.Y)
|
||||
if indent {
|
||||
p.decLevel()
|
||||
}
|
||||
p.nestedBinary = false
|
||||
case *FuncDecl:
|
||||
if x.BashStyle {
|
||||
p.WriteString("function ")
|
||||
}
|
||||
p.WriteString(x.Name.Value)
|
||||
p.WriteString("() ")
|
||||
p.incLines(x.Body.Pos())
|
||||
p.stmt(x.Body)
|
||||
case *CaseClause:
|
||||
p.WriteString("case ")
|
||||
p.word(x.Word)
|
||||
p.WriteString(" in")
|
||||
p.incLevel()
|
||||
for _, pl := range x.List {
|
||||
p.commentsAndSeparate(pl.Patterns[0].Pos())
|
||||
for i, w := range pl.Patterns {
|
||||
if i > 0 {
|
||||
p.spacedString("|")
|
||||
}
|
||||
if p.wantSpace {
|
||||
p.WriteByte(' ')
|
||||
}
|
||||
p.word(w)
|
||||
}
|
||||
p.WriteByte(')')
|
||||
p.wantSpace = true
|
||||
sep := len(pl.Stmts) > 1 || (len(pl.Stmts) > 0 && pl.Stmts[0].Pos() > p.nline)
|
||||
p.nestedStmts(pl.Stmts, 0)
|
||||
p.level++
|
||||
if sep {
|
||||
p.commentsUpTo(pl.OpPos)
|
||||
p.newlines(pl.OpPos)
|
||||
}
|
||||
p.spacedString(pl.Op.String())
|
||||
p.incLines(pl.OpPos)
|
||||
p.level--
|
||||
if sep || pl.OpPos == x.Esac {
|
||||
p.wantNewline = true
|
||||
}
|
||||
}
|
||||
p.decLevel()
|
||||
p.semiRsrv("esac", x.Esac, len(x.List) == 0)
|
||||
case *UntilClause:
|
||||
p.spacedString("until")
|
||||
p.nestedStmts(x.CondStmts, 0)
|
||||
p.semiOrNewl("do", x.Do)
|
||||
p.nestedStmts(x.DoStmts, 0)
|
||||
p.semiRsrv("done", x.Done, true)
|
||||
case *ArithmCmd:
|
||||
p.WriteString("((")
|
||||
p.arithmExpr(x.X, false)
|
||||
p.WriteString("))")
|
||||
case *TestClause:
|
||||
p.WriteString("[[ ")
|
||||
p.testExpr(x.X)
|
||||
p.spacedString("]]")
|
||||
case *DeclClause:
|
||||
name := x.Variant
|
||||
if name == "" {
|
||||
name = "declare"
|
||||
}
|
||||
p.spacedString(name)
|
||||
for _, w := range x.Opts {
|
||||
p.WriteByte(' ')
|
||||
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 {
|
||||
p.WriteByte(' ')
|
||||
p.WriteString(x.Name.Value)
|
||||
}
|
||||
p.stmt(x.Stmt)
|
||||
case *LetClause:
|
||||
p.spacedString("let")
|
||||
for _, n := range x.Exprs {
|
||||
p.WriteByte(' ')
|
||||
p.arithmExpr(n, true)
|
||||
}
|
||||
}
|
||||
return startRedirs
|
||||
}
|
||||
|
||||
func startsWithLparen(s *Stmt) bool {
|
||||
switch x := s.Cmd.(type) {
|
||||
case *Subshell:
|
||||
return true
|
||||
case *BinaryCmd:
|
||||
return startsWithLparen(x.X)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *printer) hasInline(pos, npos, nline Pos) bool {
|
||||
for _, c := range p.comments {
|
||||
if c.Hash > nline {
|
||||
return false
|
||||
}
|
||||
if c.Hash > pos && (npos == 0 || c.Hash < npos) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *printer) stmts(stmts []*Stmt) {
|
||||
switch len(stmts) {
|
||||
case 0:
|
||||
return
|
||||
case 1:
|
||||
s := stmts[0]
|
||||
pos := s.Pos()
|
||||
p.commentsUpTo(pos)
|
||||
if pos <= p.nline {
|
||||
p.stmt(s)
|
||||
} else {
|
||||
if p.nlineIndex > 0 {
|
||||
p.newlines(pos)
|
||||
}
|
||||
p.incLines(pos)
|
||||
p.stmt(s)
|
||||
p.wantNewline = true
|
||||
}
|
||||
return
|
||||
}
|
||||
inlineIndent := 0
|
||||
for i, s := range stmts {
|
||||
pos := s.Pos()
|
||||
ind := p.nlineIndex
|
||||
p.commentsUpTo(pos)
|
||||
if p.nlineIndex > 0 {
|
||||
p.newlines(pos)
|
||||
}
|
||||
p.incLines(pos)
|
||||
p.stmt(s)
|
||||
var npos Pos
|
||||
if i+1 < len(stmts) {
|
||||
npos = stmts[i+1].Pos()
|
||||
}
|
||||
if !p.hasInline(pos, npos, p.nline) {
|
||||
inlineIndent = 0
|
||||
p.commentPadding = 0
|
||||
continue
|
||||
}
|
||||
if ind < len(p.lines)-1 && s.End() > p.lines[ind+1] {
|
||||
inlineIndent = 0
|
||||
}
|
||||
if inlineIndent == 0 {
|
||||
ind2 := p.nlineIndex
|
||||
nline2 := p.nline
|
||||
follow := stmts[i:]
|
||||
for j, s2 := range follow {
|
||||
pos2 := s2.Pos()
|
||||
var npos2 Pos
|
||||
if j+1 < len(follow) {
|
||||
npos2 = follow[j+1].Pos()
|
||||
}
|
||||
if pos2 > nline2 || !p.hasInline(pos2, npos2, nline2) {
|
||||
break
|
||||
}
|
||||
if l := p.stmtLen(s2); l > inlineIndent {
|
||||
inlineIndent = l
|
||||
}
|
||||
if ind2++; ind2 >= len(p.lines) {
|
||||
nline2 = maxPos
|
||||
} else {
|
||||
nline2 = p.lines[ind2]
|
||||
}
|
||||
}
|
||||
if ind2 == p.nlineIndex+1 {
|
||||
// no inline comments directly after this one
|
||||
continue
|
||||
}
|
||||
}
|
||||
if inlineIndent > 0 {
|
||||
p.commentPadding = inlineIndent - p.stmtLen(s)
|
||||
}
|
||||
}
|
||||
p.wantNewline = true
|
||||
}
|
||||
|
||||
type byteCounter int
|
||||
|
||||
func (c *byteCounter) WriteByte(b byte) error {
|
||||
*c++
|
||||
return nil
|
||||
}
|
||||
func (c *byteCounter) WriteString(s string) (int, error) {
|
||||
*c += byteCounter(len(s))
|
||||
return 0, nil
|
||||
}
|
||||
func (c *byteCounter) Reset(io.Writer) { *c = 0 }
|
||||
|
||||
func (p *printer) stmtLen(s *Stmt) int {
|
||||
*p.lenPrinter = printer{bufWriter: &p.lenCounter}
|
||||
p.lenPrinter.bufWriter.Reset(nil)
|
||||
p.lenPrinter.incLines(s.Pos())
|
||||
p.lenPrinter.stmt(s)
|
||||
return int(p.lenCounter)
|
||||
}
|
||||
|
||||
func (p *printer) nestedStmts(stmts []*Stmt, closing Pos) {
|
||||
p.incLevel()
|
||||
if len(stmts) == 1 && closing > p.nline && stmts[0].End() <= p.nline {
|
||||
p.newline(0)
|
||||
p.indent()
|
||||
}
|
||||
p.stmts(stmts)
|
||||
p.decLevel()
|
||||
}
|
||||
|
||||
func (p *printer) assigns(assigns []*Assign) {
|
||||
anyNewline := false
|
||||
for _, a := range assigns {
|
||||
if a.Pos() > p.nline {
|
||||
p.bslashNewl()
|
||||
if !anyNewline {
|
||||
p.incLevel()
|
||||
anyNewline = true
|
||||
}
|
||||
p.indent()
|
||||
} else if p.wantSpace {
|
||||
p.WriteByte(' ')
|
||||
}
|
||||
if a.Name != nil {
|
||||
p.WriteString(a.Name.Value)
|
||||
if a.Append {
|
||||
p.WriteByte('+')
|
||||
}
|
||||
p.WriteByte('=')
|
||||
}
|
||||
if a.Value != nil {
|
||||
p.word(a.Value)
|
||||
}
|
||||
p.wantSpace = true
|
||||
}
|
||||
if anyNewline {
|
||||
p.decLevel()
|
||||
}
|
||||
}
|
16
vendor/github.com/mvdan/sh/syntax/token_string.go
generated
vendored
Normal file
16
vendor/github.com/mvdan/sh/syntax/token_string.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// Code generated by "stringer -type token"; DO NOT EDIT
|
||||
|
||||
package syntax
|
||||
|
||||
import "fmt"
|
||||
|
||||
const _token_name = "illegalTokEOFLitLitWordLitRedir'\"`&&&||||&$$'$\"${$[$($(([(((}])));;;;&;;&!++--***==!=<=>=+=-=*=/=%=&=|=^=<<=>>=>>><<><&>&>|<<<<-<<<&>&>><(>(+:+-:-?:?=:=%%%###^^^,,,@///:-e-f-d-c-b-p-S-L-k-g-u-G-O-N-r-w-x-s-t-z-n-o-v-R=~-nt-ot-ef-eq-ne-le-ge-lt-gt?(*(+(@(!("
|
||||
|
||||
var _token_index = [...]uint16{0, 10, 13, 16, 23, 31, 32, 33, 34, 35, 37, 39, 40, 42, 43, 45, 47, 49, 51, 53, 56, 57, 58, 60, 61, 62, 63, 65, 66, 68, 70, 73, 74, 76, 78, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 108, 111, 112, 114, 115, 117, 119, 121, 123, 125, 128, 131, 133, 136, 138, 140, 141, 143, 144, 146, 147, 149, 150, 152, 153, 155, 156, 158, 159, 161, 162, 164, 165, 166, 168, 169, 171, 173, 175, 177, 179, 181, 183, 185, 187, 189, 191, 193, 195, 197, 199, 201, 203, 205, 207, 209, 211, 213, 215, 217, 219, 222, 225, 228, 231, 234, 237, 240, 243, 246, 248, 250, 252, 254, 256}
|
||||
|
||||
func (i token) String() string {
|
||||
if i >= token(len(_token_index)-1) {
|
||||
return fmt.Sprintf("token(%d)", i)
|
||||
}
|
||||
return _token_name[_token_index[i]:_token_index[i+1]]
|
||||
}
|
340
vendor/github.com/mvdan/sh/syntax/tokens.go
generated
vendored
Normal file
340
vendor/github.com/mvdan/sh/syntax/tokens.go
generated
vendored
Normal file
@ -0,0 +1,340 @@
|
||||
// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
|
||||
// See LICENSE for licensing information
|
||||
|
||||
package syntax
|
||||
|
||||
type token uint32
|
||||
|
||||
// Modified version of golang.org/x/tools/cmd/stringer that gets the
|
||||
// string value from the inline comment of each constant, if there is
|
||||
// one. Also removes leading '_'.
|
||||
//go:generate stringer -type token
|
||||
|
||||
// The list of all possible tokens.
|
||||
const (
|
||||
illegalTok token = iota
|
||||
_EOF
|
||||
_Lit
|
||||
_LitWord
|
||||
_LitRedir
|
||||
|
||||
sglQuote // '
|
||||
dblQuote // "
|
||||
bckQuote // `
|
||||
|
||||
and // &
|
||||
andAnd // &&
|
||||
orOr // ||
|
||||
or // |
|
||||
pipeAll // |&
|
||||
|
||||
dollar // $
|
||||
dollSglQuote // $'
|
||||
dollDblQuote // $"
|
||||
dollBrace // ${
|
||||
dollBrack // $[
|
||||
dollParen // $(
|
||||
dollDblParen // $((
|
||||
leftBrack // [
|
||||
leftParen // (
|
||||
dblLeftParen // ((
|
||||
|
||||
rightBrace // }
|
||||
rightBrack // ]
|
||||
rightParen // )
|
||||
dblRightParen // ))
|
||||
semicolon // ;
|
||||
|
||||
dblSemicolon // ;;
|
||||
semiFall // ;&
|
||||
dblSemiFall // ;;&
|
||||
|
||||
exclMark // !
|
||||
addAdd // ++
|
||||
subSub // --
|
||||
star // *
|
||||
power // **
|
||||
equal // ==
|
||||
nequal // !=
|
||||
lequal // <=
|
||||
gequal // >=
|
||||
|
||||
addAssgn // +=
|
||||
subAssgn // -=
|
||||
mulAssgn // *=
|
||||
quoAssgn // /=
|
||||
remAssgn // %=
|
||||
andAssgn // &=
|
||||
orAssgn // |=
|
||||
xorAssgn // ^=
|
||||
shlAssgn // <<=
|
||||
shrAssgn // >>=
|
||||
|
||||
rdrOut // >
|
||||
appOut // >>
|
||||
rdrIn // <
|
||||
rdrInOut // <>
|
||||
dplIn // <&
|
||||
dplOut // >&
|
||||
clbOut // >|
|
||||
hdoc // <<
|
||||
dashHdoc // <<-
|
||||
wordHdoc // <<<
|
||||
rdrAll // &>
|
||||
appAll // &>>
|
||||
|
||||
cmdIn // <(
|
||||
cmdOut // >(
|
||||
|
||||
plus // +
|
||||
colPlus // :+
|
||||
minus // -
|
||||
colMinus // :-
|
||||
quest // ?
|
||||
colQuest // :?
|
||||
assgn // =
|
||||
colAssgn // :=
|
||||
perc // %
|
||||
dblPerc // %%
|
||||
hash // #
|
||||
dblHash // ##
|
||||
caret // ^
|
||||
dblCaret // ^^
|
||||
comma // ,
|
||||
dblComma // ,,
|
||||
at // @
|
||||
slash // /
|
||||
dblSlash // //
|
||||
colon // :
|
||||
|
||||
tsExists // -e
|
||||
tsRegFile // -f
|
||||
tsDirect // -d
|
||||
tsCharSp // -c
|
||||
tsBlckSp // -b
|
||||
tsNmPipe // -p
|
||||
tsSocket // -S
|
||||
tsSmbLink // -L
|
||||
tsSticky // -k
|
||||
tsGIDSet // -g
|
||||
tsUIDSet // -u
|
||||
tsGrpOwn // -G
|
||||
tsUsrOwn // -O
|
||||
tsModif // -N
|
||||
tsRead // -r
|
||||
tsWrite // -w
|
||||
tsExec // -x
|
||||
tsNoEmpty // -s
|
||||
tsFdTerm // -t
|
||||
tsEmpStr // -z
|
||||
tsNempStr // -n
|
||||
tsOptSet // -o
|
||||
tsVarSet // -v
|
||||
tsRefVar // -R
|
||||
|
||||
tsReMatch // =~
|
||||
tsNewer // -nt
|
||||
tsOlder // -ot
|
||||
tsDevIno // -ef
|
||||
tsEql // -eq
|
||||
tsNeq // -ne
|
||||
tsLeq // -le
|
||||
tsGeq // -ge
|
||||
tsLss // -lt
|
||||
tsGtr // -gt
|
||||
|
||||
globQuest // ?(
|
||||
globStar // *(
|
||||
globPlus // +(
|
||||
globAt // @(
|
||||
globExcl // !(
|
||||
)
|
||||
|
||||
type RedirOperator token
|
||||
|
||||
const (
|
||||
RdrOut = RedirOperator(rdrOut) + iota
|
||||
AppOut
|
||||
RdrIn
|
||||
RdrInOut
|
||||
DplIn
|
||||
DplOut
|
||||
ClbOut
|
||||
Hdoc
|
||||
DashHdoc
|
||||
WordHdoc
|
||||
RdrAll
|
||||
AppAll
|
||||
)
|
||||
|
||||
type ProcOperator token
|
||||
|
||||
const (
|
||||
CmdIn = ProcOperator(cmdIn) + iota
|
||||
CmdOut
|
||||
)
|
||||
|
||||
type GlobOperator token
|
||||
|
||||
const (
|
||||
GlobQuest = GlobOperator(globQuest) + iota
|
||||
GlobStar
|
||||
GlobPlus
|
||||
GlobAt
|
||||
GlobExcl
|
||||
)
|
||||
|
||||
type BinCmdOperator token
|
||||
|
||||
const (
|
||||
AndStmt = BinCmdOperator(andAnd) + iota
|
||||
OrStmt
|
||||
Pipe
|
||||
PipeAll
|
||||
)
|
||||
|
||||
type CaseOperator token
|
||||
|
||||
const (
|
||||
DblSemicolon = CaseOperator(dblSemicolon) + iota
|
||||
SemiFall
|
||||
DblSemiFall
|
||||
)
|
||||
|
||||
type ParExpOperator token
|
||||
|
||||
const (
|
||||
SubstPlus = ParExpOperator(plus) + iota
|
||||
SubstColPlus
|
||||
SubstMinus
|
||||
SubstColMinus
|
||||
SubstQuest
|
||||
SubstColQuest
|
||||
SubstAssgn
|
||||
SubstColAssgn
|
||||
RemSmallSuffix
|
||||
RemLargeSuffix
|
||||
RemSmallPrefix
|
||||
RemLargePrefix
|
||||
UpperFirst
|
||||
UpperAll
|
||||
LowerFirst
|
||||
LowerAll
|
||||
OtherParamOps
|
||||
)
|
||||
|
||||
type UnAritOperator token
|
||||
|
||||
const (
|
||||
Not = UnAritOperator(exclMark) + iota
|
||||
Inc
|
||||
Dec
|
||||
Plus = UnAritOperator(plus)
|
||||
Minus = UnAritOperator(minus)
|
||||
)
|
||||
|
||||
type BinAritOperator token
|
||||
|
||||
const (
|
||||
Add = BinAritOperator(plus)
|
||||
Sub = BinAritOperator(minus)
|
||||
Mul = BinAritOperator(star)
|
||||
Quo = BinAritOperator(slash)
|
||||
Rem = BinAritOperator(perc)
|
||||
Pow = BinAritOperator(power)
|
||||
Eql = BinAritOperator(equal)
|
||||
Gtr = BinAritOperator(rdrOut)
|
||||
Lss = BinAritOperator(rdrIn)
|
||||
Neq = BinAritOperator(nequal)
|
||||
Leq = BinAritOperator(lequal)
|
||||
Geq = BinAritOperator(gequal)
|
||||
And = BinAritOperator(and)
|
||||
Or = BinAritOperator(or)
|
||||
Xor = BinAritOperator(caret)
|
||||
Shr = BinAritOperator(appOut)
|
||||
Shl = BinAritOperator(hdoc)
|
||||
|
||||
AndArit = BinAritOperator(andAnd)
|
||||
OrArit = BinAritOperator(orOr)
|
||||
Comma = BinAritOperator(comma)
|
||||
Quest = BinAritOperator(quest)
|
||||
Colon = BinAritOperator(colon)
|
||||
|
||||
Assgn = BinAritOperator(assgn)
|
||||
AddAssgn = BinAritOperator(addAssgn)
|
||||
SubAssgn = BinAritOperator(subAssgn)
|
||||
MulAssgn = BinAritOperator(mulAssgn)
|
||||
QuoAssgn = BinAritOperator(quoAssgn)
|
||||
RemAssgn = BinAritOperator(remAssgn)
|
||||
AndAssgn = BinAritOperator(andAssgn)
|
||||
OrAssgn = BinAritOperator(orAssgn)
|
||||
XorAssgn = BinAritOperator(xorAssgn)
|
||||
ShlAssgn = BinAritOperator(shlAssgn)
|
||||
ShrAssgn = BinAritOperator(shrAssgn)
|
||||
)
|
||||
|
||||
type UnTestOperator token
|
||||
|
||||
const (
|
||||
TsExists = UnTestOperator(tsExists) + iota
|
||||
TsRegFile
|
||||
TsDirect
|
||||
TsCharSp
|
||||
TsBlckSp
|
||||
TsNmPipe
|
||||
TsSocket
|
||||
TsSmbLink
|
||||
TsSticky
|
||||
TsGIDSet
|
||||
TsUIDSet
|
||||
TsGrpOwn
|
||||
TsUsrOwn
|
||||
TsModif
|
||||
TsRead
|
||||
TsWrite
|
||||
TsExec
|
||||
TsNoEmpty
|
||||
TsFdTerm
|
||||
TsEmpStr
|
||||
TsNempStr
|
||||
TsOptSet
|
||||
TsVarSet
|
||||
TsRefVar
|
||||
TsNot = UnTestOperator(exclMark)
|
||||
)
|
||||
|
||||
type BinTestOperator token
|
||||
|
||||
const (
|
||||
TsReMatch = BinTestOperator(tsReMatch) + iota
|
||||
TsNewer
|
||||
TsOlder
|
||||
TsDevIno
|
||||
TsEql
|
||||
TsNeq
|
||||
TsLeq
|
||||
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
|
||||
)
|
||||
|
||||
func (o RedirOperator) String() string { return token(o).String() }
|
||||
func (o ProcOperator) String() string { return token(o).String() }
|
||||
func (o GlobOperator) String() string { return token(o).String() }
|
||||
func (o BinCmdOperator) String() string { return token(o).String() }
|
||||
func (o CaseOperator) String() string { return token(o).String() }
|
||||
func (o ParExpOperator) String() string { return token(o).String() }
|
||||
func (o UnAritOperator) String() string { return token(o).String() }
|
||||
func (o BinAritOperator) String() string { return token(o).String() }
|
||||
func (o UnTestOperator) String() string { return token(o).String() }
|
||||
func (o BinTestOperator) String() string { return token(o).String() }
|
184
vendor/github.com/mvdan/sh/syntax/walk.go
generated
vendored
Normal file
184
vendor/github.com/mvdan/sh/syntax/walk.go
generated
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
|
||||
// See LICENSE for licensing information
|
||||
|
||||
package syntax
|
||||
|
||||
import "fmt"
|
||||
|
||||
func walkStmts(stmts []*Stmt, f func(Node) bool) {
|
||||
for _, s := range stmts {
|
||||
Walk(s, f)
|
||||
}
|
||||
}
|
||||
|
||||
func walkWords(words []*Word, f func(Node) bool) {
|
||||
for _, w := range words {
|
||||
Walk(w, f)
|
||||
}
|
||||
}
|
||||
|
||||
// Walk traverses an AST in depth-first order: It starts by calling
|
||||
// f(node); node must not be nil. If f returns true, Walk invokes f
|
||||
// recursively for each of the non-nil children of node, followed by
|
||||
// f(nil).
|
||||
func Walk(node Node, f func(Node) bool) {
|
||||
if !f(node) {
|
||||
return
|
||||
}
|
||||
|
||||
switch x := node.(type) {
|
||||
case *File:
|
||||
walkStmts(x.Stmts, f)
|
||||
case *Stmt:
|
||||
if x.Cmd != nil {
|
||||
Walk(x.Cmd, f)
|
||||
}
|
||||
for _, a := range x.Assigns {
|
||||
Walk(a, f)
|
||||
}
|
||||
for _, r := range x.Redirs {
|
||||
Walk(r, f)
|
||||
}
|
||||
case *Assign:
|
||||
if x.Name != nil {
|
||||
Walk(x.Name, f)
|
||||
}
|
||||
if x.Value != nil {
|
||||
Walk(x.Value, f)
|
||||
}
|
||||
case *Redirect:
|
||||
if x.N != nil {
|
||||
Walk(x.N, f)
|
||||
}
|
||||
Walk(x.Word, f)
|
||||
if x.Hdoc != nil {
|
||||
Walk(x.Hdoc, f)
|
||||
}
|
||||
case *CallExpr:
|
||||
walkWords(x.Args, f)
|
||||
case *Subshell:
|
||||
walkStmts(x.Stmts, f)
|
||||
case *Block:
|
||||
walkStmts(x.Stmts, f)
|
||||
case *IfClause:
|
||||
walkStmts(x.CondStmts, f)
|
||||
walkStmts(x.ThenStmts, f)
|
||||
for _, elif := range x.Elifs {
|
||||
walkStmts(elif.CondStmts, f)
|
||||
walkStmts(elif.ThenStmts, f)
|
||||
}
|
||||
walkStmts(x.ElseStmts, f)
|
||||
case *WhileClause:
|
||||
walkStmts(x.CondStmts, f)
|
||||
walkStmts(x.DoStmts, f)
|
||||
case *UntilClause:
|
||||
walkStmts(x.CondStmts, f)
|
||||
walkStmts(x.DoStmts, f)
|
||||
case *ForClause:
|
||||
Walk(x.Loop, f)
|
||||
walkStmts(x.DoStmts, f)
|
||||
case *WordIter:
|
||||
Walk(x.Name, f)
|
||||
walkWords(x.List, f)
|
||||
case *CStyleLoop:
|
||||
if x.Init != nil {
|
||||
Walk(x.Init, f)
|
||||
}
|
||||
if x.Cond != nil {
|
||||
Walk(x.Cond, f)
|
||||
}
|
||||
if x.Post != nil {
|
||||
Walk(x.Post, f)
|
||||
}
|
||||
case *BinaryCmd:
|
||||
Walk(x.X, f)
|
||||
Walk(x.Y, f)
|
||||
case *FuncDecl:
|
||||
Walk(x.Name, f)
|
||||
Walk(x.Body, f)
|
||||
case *Word:
|
||||
for _, wp := range x.Parts {
|
||||
Walk(wp, f)
|
||||
}
|
||||
case *Lit:
|
||||
case *SglQuoted:
|
||||
case *DblQuoted:
|
||||
for _, wp := range x.Parts {
|
||||
Walk(wp, f)
|
||||
}
|
||||
case *CmdSubst:
|
||||
walkStmts(x.Stmts, f)
|
||||
case *ParamExp:
|
||||
if x.Param != nil {
|
||||
Walk(x.Param, f)
|
||||
}
|
||||
if x.Ind != nil {
|
||||
Walk(x.Ind.Expr, f)
|
||||
}
|
||||
if x.Repl != nil {
|
||||
Walk(x.Repl.Orig, f)
|
||||
Walk(x.Repl.With, f)
|
||||
}
|
||||
if x.Exp != nil {
|
||||
Walk(x.Exp.Word, f)
|
||||
}
|
||||
case *ArithmExp:
|
||||
if x.X != nil {
|
||||
Walk(x.X, f)
|
||||
}
|
||||
case *ArithmCmd:
|
||||
if x.X != nil {
|
||||
Walk(x.X, f)
|
||||
}
|
||||
case *BinaryArithm:
|
||||
Walk(x.X, f)
|
||||
Walk(x.Y, f)
|
||||
case *BinaryTest:
|
||||
Walk(x.X, f)
|
||||
Walk(x.Y, f)
|
||||
case *UnaryArithm:
|
||||
Walk(x.X, f)
|
||||
case *UnaryTest:
|
||||
Walk(x.X, f)
|
||||
case *ParenArithm:
|
||||
Walk(x.X, f)
|
||||
case *ParenTest:
|
||||
Walk(x.X, f)
|
||||
case *CaseClause:
|
||||
Walk(x.Word, f)
|
||||
for _, pl := range x.List {
|
||||
walkWords(pl.Patterns, f)
|
||||
walkStmts(pl.Stmts, f)
|
||||
}
|
||||
case *TestClause:
|
||||
Walk(x.X, f)
|
||||
case *DeclClause:
|
||||
walkWords(x.Opts, f)
|
||||
for _, a := range x.Assigns {
|
||||
Walk(a, f)
|
||||
}
|
||||
case *ArrayExpr:
|
||||
walkWords(x.List, f)
|
||||
case *ExtGlob:
|
||||
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)
|
||||
}
|
||||
Walk(x.Stmt, f)
|
||||
case *LetClause:
|
||||
for _, expr := range x.Exprs {
|
||||
Walk(expr, f)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Sprintf("syntax.Walk: unexpected node type %T", x))
|
||||
}
|
||||
|
||||
f(nil)
|
||||
}
|
12
vendor/vendor.json
vendored
12
vendor/vendor.json
vendored
@ -50,6 +50,18 @@
|
||||
"revision": "95345c4e1c0ebc9d16a3284177f09360f4d20fab",
|
||||
"revisionTime": "2017-01-24T11:57:57Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "1ZLAvHVYAS3kxaYI8OQiTBllqNU=",
|
||||
"path": "github.com/mvdan/sh/interp",
|
||||
"revision": "17e267b541e30baece16b7ddeae50822cc6a795f",
|
||||
"revisionTime": "2017-04-24T11:31:08Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "4/7joITdf4wl+uoV8zDXgYqy2aw=",
|
||||
"path": "github.com/mvdan/sh/syntax",
|
||||
"revision": "17e267b541e30baece16b7ddeae50822cc6a795f",
|
||||
"revisionTime": "2017-04-24T11:31:08Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "HUXE+Nrcau8FSaVEvPYHMvDjxOE=",
|
||||
"path": "github.com/satori/go.uuid",
|
||||
|
Loading…
Reference in New Issue
Block a user