github.com/cilki/sh@v2.6.4+incompatible/interp/test_classic.go (about)

     1  // Copyright (c) 2017, Daniel Martí <mvdan@mvdan.cc>
     2  // See LICENSE for licensing information
     3  
     4  package interp
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"mvdan.cc/sh/syntax"
    10  )
    11  
    12  const illegalTok = 0
    13  
    14  type testParser struct {
    15  	eof bool
    16  	val string
    17  	rem []string
    18  
    19  	err func(err error)
    20  }
    21  
    22  func (p *testParser) errf(format string, a ...interface{}) {
    23  	p.err(fmt.Errorf(format, a...))
    24  }
    25  
    26  func (p *testParser) next() {
    27  	if p.eof || len(p.rem) == 0 {
    28  		p.eof = true
    29  		p.val = ""
    30  		return
    31  	}
    32  	p.val = p.rem[0]
    33  	p.rem = p.rem[1:]
    34  }
    35  
    36  func (p *testParser) followWord(fval string) *syntax.Word {
    37  	if p.eof {
    38  		p.errf("%s must be followed by a word", fval)
    39  	}
    40  	w := &syntax.Word{Parts: []syntax.WordPart{
    41  		&syntax.Lit{Value: p.val},
    42  	}}
    43  	p.next()
    44  	return w
    45  }
    46  
    47  func (p *testParser) classicTest(fval string, pastAndOr bool) syntax.TestExpr {
    48  	var left syntax.TestExpr
    49  	if pastAndOr {
    50  		left = p.testExprBase(fval)
    51  	} else {
    52  		left = p.classicTest(fval, true)
    53  	}
    54  	if left == nil || p.eof {
    55  		return left
    56  	}
    57  	opStr := p.val
    58  	op := testBinaryOp(p.val)
    59  	if op == illegalTok {
    60  		p.errf("not a valid test operator: %s", p.val)
    61  	}
    62  	b := &syntax.BinaryTest{
    63  		Op: op,
    64  		X:  left,
    65  	}
    66  	p.next()
    67  	switch b.Op {
    68  	case syntax.AndTest, syntax.OrTest:
    69  		if b.Y = p.classicTest(opStr, false); b.Y == nil {
    70  			p.errf("%s must be followed by an expression", opStr)
    71  		}
    72  	default:
    73  		b.Y = p.followWord(opStr)
    74  	}
    75  	return b
    76  }
    77  
    78  func (p *testParser) testExprBase(fval string) syntax.TestExpr {
    79  	if p.eof {
    80  		return nil
    81  	}
    82  	op := testUnaryOp(p.val)
    83  	switch op {
    84  	case syntax.TsNot:
    85  		u := &syntax.UnaryTest{Op: op}
    86  		p.next()
    87  		u.X = p.classicTest(op.String(), false)
    88  		return u
    89  	case illegalTok:
    90  		return p.followWord(fval)
    91  	default:
    92  		u := &syntax.UnaryTest{Op: op}
    93  		p.next()
    94  		if p.eof {
    95  			// make [ -e ] fall back to [ -n -e ], i.e. use
    96  			// the operator as an argument
    97  			return &syntax.Word{Parts: []syntax.WordPart{
    98  				&syntax.Lit{Value: op.String()},
    99  			}}
   100  		}
   101  		u.X = p.followWord(op.String())
   102  		return u
   103  	}
   104  }
   105  
   106  // testUnaryOp is an exact copy of syntax's.
   107  func testUnaryOp(val string) syntax.UnTestOperator {
   108  	switch val {
   109  	case "!":
   110  		return syntax.TsNot
   111  	case "-e", "-a":
   112  		return syntax.TsExists
   113  	case "-f":
   114  		return syntax.TsRegFile
   115  	case "-d":
   116  		return syntax.TsDirect
   117  	case "-c":
   118  		return syntax.TsCharSp
   119  	case "-b":
   120  		return syntax.TsBlckSp
   121  	case "-p":
   122  		return syntax.TsNmPipe
   123  	case "-S":
   124  		return syntax.TsSocket
   125  	case "-L", "-h":
   126  		return syntax.TsSmbLink
   127  	case "-k":
   128  		return syntax.TsSticky
   129  	case "-g":
   130  		return syntax.TsGIDSet
   131  	case "-u":
   132  		return syntax.TsUIDSet
   133  	case "-G":
   134  		return syntax.TsGrpOwn
   135  	case "-O":
   136  		return syntax.TsUsrOwn
   137  	case "-N":
   138  		return syntax.TsModif
   139  	case "-r":
   140  		return syntax.TsRead
   141  	case "-w":
   142  		return syntax.TsWrite
   143  	case "-x":
   144  		return syntax.TsExec
   145  	case "-s":
   146  		return syntax.TsNoEmpty
   147  	case "-t":
   148  		return syntax.TsFdTerm
   149  	case "-z":
   150  		return syntax.TsEmpStr
   151  	case "-n":
   152  		return syntax.TsNempStr
   153  	case "-o":
   154  		return syntax.TsOptSet
   155  	case "-v":
   156  		return syntax.TsVarSet
   157  	case "-R":
   158  		return syntax.TsRefVar
   159  	default:
   160  		return illegalTok
   161  	}
   162  }
   163  
   164  // testBinaryOp is like syntax's, but with -a and -o, and without =~.
   165  func testBinaryOp(val string) syntax.BinTestOperator {
   166  	switch val {
   167  	case "-a":
   168  		return syntax.AndTest
   169  	case "-o":
   170  		return syntax.OrTest
   171  	case "==", "=":
   172  		return syntax.TsMatch
   173  	case "!=":
   174  		return syntax.TsNoMatch
   175  	case "-nt":
   176  		return syntax.TsNewer
   177  	case "-ot":
   178  		return syntax.TsOlder
   179  	case "-ef":
   180  		return syntax.TsDevIno
   181  	case "-eq":
   182  		return syntax.TsEql
   183  	case "-ne":
   184  		return syntax.TsNeq
   185  	case "-le":
   186  		return syntax.TsLeq
   187  	case "-ge":
   188  		return syntax.TsGeq
   189  	case "-lt":
   190  		return syntax.TsLss
   191  	case "-gt":
   192  		return syntax.TsGtr
   193  	default:
   194  		return illegalTok
   195  	}
   196  }