def _TwoArgs(w_parser): # type: (_StringWordEmitter) -> bool_expr_t """Returns an expression tree to be evaluated.""" w0 = w_parser.Read() w1 = w_parser.Read() s0 = w0.s if s0 == '!': return bool_expr.LogicalNot(bool_expr.WordTest(w1)) unary_id = Id.Undefined_Tok # Oil's preferred long flags if w0.s.startswith('--'): if s0 == '--dir': unary_id = Id.BoolUnary_d elif s0 == '--exists': unary_id = Id.BoolUnary_e elif s0 == '--file': unary_id = Id.BoolUnary_f elif s0 == '--symlink': unary_id = Id.BoolUnary_L if unary_id == Id.Undefined_Tok: unary_id = match.BracketUnary(w0.s) if unary_id == Id.Undefined_Tok: p_die('Expected unary operator, got %r (2 args)', w0.s, word=w0) return bool_expr.Unary(unary_id, w1)
def _ThreeArgs(w_parser): # type: (_StringWordEmitter) -> bool_expr_t """Returns an expression tree to be evaluated.""" w0 = w_parser.Read() w1 = w_parser.Read() w2 = w_parser.Read() # NOTE: Order is important here. binary_id = match.BracketBinary(w1.s) if binary_id != Id.Undefined_Tok: return bool_expr.Binary(binary_id, w0, w2) if w1.s == '-a': return bool_expr.LogicalAnd(bool_expr.WordTest(w0), bool_expr.WordTest(w2)) if w1.s == '-o': return bool_expr.LogicalOr(bool_expr.WordTest(w0), bool_expr.WordTest(w2)) if w0.s == '!': w_parser.Rewind(2) child = _TwoArgs(w_parser) return bool_expr.LogicalNot(child) if w0.s == '(' and w2.s == ')': return bool_expr.WordTest(w1) p_die('Expected binary operator, got %r (3 args)', w1.s, word=w1)
def _ThreeArgs(argv): """Returns an expression tree to be evaluated.""" a0, a1, a2 = argv # NOTE: Order is important here. binary_id = _BINARY_LOOKUP.get(a1) if binary_id is not None: left = word.StringWord(Id.Word_Compound, a0) right = word.StringWord(Id.Word_Compound, a2) return bool_expr.BoolBinary(IdInstance(binary_id), left, right) if a1 == '-a': left = _StringWordTest(a0) right = _StringWordTest(a2) return bool_expr.LogicalAnd(left, right) if a1 == '-o': left = _StringWordTest(a0) right = _StringWordTest(a2) return bool_expr.LogicalOr(left, right) if a0 == '!': child = _TwoArgs(argv[1:]) return bool_expr.LogicalNot(child) if a0 == '(' and a2 == ')': return _StringWordTest(a1) p_die('Syntax error: binary operator expected, got %r (3 args)', a1)
def _ThreeArgs(w_parser): """Returns an expression tree to be evaluated.""" w0 = w_parser.Read() w1 = w_parser.Read() w2 = w_parser.Read() # NOTE: Order is important here. binary_id = _BINARY_LOOKUP.get(w1.s) if binary_id is not None: return bool_expr.BoolBinary(IdInstance(binary_id), w0, w2) if w1.s == '-a': return bool_expr.LogicalAnd(bool_expr.WordTest(w0), bool_expr.WordTest(w2)) if w1.s == '-o': return bool_expr.LogicalOr(bool_expr.WordTest(w0), bool_expr.WordTest(w2)) if w0.s == '!': w_parser.Rewind(2) child = _TwoArgs(w_parser) return bool_expr.LogicalNot(child) if w0.s == '(' and w2.s == ')': return bool_expr.WordTest(w1) p_die('Expected binary operator, got %r (3 args)', w1.s, word=w1)
def ParseNegatedFactor(self): # type: () -> bool_expr_t """ Negated : '!'? Factor """ if self.op_id == Id.KW_Bang: self._Next() child = self.ParseFactor() return bool_expr.LogicalNot(child) else: return self.ParseFactor()
def _TwoArgs(w_parser): """Returns an expression tree to be evaluated.""" w0 = w_parser.Read() w1 = w_parser.Read() if w0.s == '!': return bool_expr.LogicalNot(bool_expr.WordTest(w1)) unary_id = _UNARY_LOOKUP.get(w0.s) if unary_id is None: # TODO: # - separate lookup by unary p_die('Expected unary operator, got %r (2 args)', w0.s, word=w0) return bool_expr.BoolUnary(IdInstance(unary_id), w1)
def _TwoArgs(w_parser): # type: (_StringWordEmitter) -> bool_expr_t """Returns an expression tree to be evaluated.""" w0 = w_parser.Read() w1 = w_parser.Read() if w0.s == '!': return bool_expr.LogicalNot(bool_expr.WordTest(w1)) unary_id = match.BracketUnary(w0.s) if unary_id == -1: # TODO: # - separate lookup by unary p_die('Expected unary operator, got %r (2 args)', w0.s, word=w0) return bool_expr.Unary(unary_id, w1)
def _TwoArgs(argv): """Returns an expression tree to be evaluated.""" a0, a1 = argv if a0 == '!': return bool_expr.LogicalNot(_StringWordTest(a1)) unary_id = _UNARY_LOOKUP.get(a0) if unary_id is None: # TODO: # - syntax error # - separate lookup by unary p_die('Expected unary operator, got %r (2 args)', a0) child = word.StringWord(Id.Word_Compound, a1) return bool_expr.BoolUnary(IdInstance(unary_id), child)
def _TwoArgs(w_parser): # type: (_StringWordEmitter) -> bool_expr_t """Returns an expression tree to be evaluated.""" w0 = w_parser.Read() # TODO: Implement --dir, --file, --exists here # --symlink, maybe --executable w1 = w_parser.Read() if w0.s == '!': return bool_expr.LogicalNot(bool_expr.WordTest(w1)) unary_id = match.BracketUnary(w0.s) if unary_id == Id.Undefined_Tok: # TODO: # - separate lookup by unary p_die('Expected unary operator, got %r (2 args)', w0.s, word=w0) return bool_expr.Unary(unary_id, w1)
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int """The test/[ builtin. The only difference between test and [ is that [ needs a matching ]. """ if self.need_right_bracket: # Preprocess right bracket if self.exec_opts.simple_test_builtin(): e_usage("should be invoked as 'test' (simple_test_builtin)") strs = cmd_val.argv if not strs or strs[-1] != ']': self.errfmt.Print_('missing closing ]', span_id=cmd_val.arg_spids[0]) return 2 # Remove the right bracket cmd_val.argv.pop() cmd_val.arg_spids.pop() w_parser = _StringWordEmitter(cmd_val) w_parser.Read() # dummy: advance past argv[0] b_parser = bool_parse.BoolParser(w_parser) # There is a fundamental ambiguity due to poor language design, in cases like: # [ -z ] # [ -z -a ] # [ -z -a ] ] # # See posixtest() in bash's test.c: # "This is an implementation of a Posix.2 proposal by David Korn." # It dispatches on expressions of length 0, 1, 2, 3, 4, and N args. We do # the same here. # # Another ambiguity: # -a is both a unary prefix operator and an infix operator. How to fix this # ambiguity? bool_node = None # type: bool_expr_t n = len(cmd_val.argv) - 1 if self.exec_opts.simple_test_builtin() and n > 3: e_usage( "should only have 3 arguments or fewer (simple_test_builtin)") try: if n == 0: return 1 # [ ] is False elif n == 1: w = w_parser.Read() bool_node = bool_expr.WordTest(w) elif n == 2: bool_node = _TwoArgs(w_parser) elif n == 3: bool_node = _ThreeArgs(w_parser) if n == 4: a0 = w_parser.Peek(0) if a0 == '!': w_parser.Read() # skip ! child = _ThreeArgs(w_parser) bool_node = bool_expr.LogicalNot(child) elif a0 == '(' and w_parser.Peek(3) == ')': w_parser.Read() # skip ')' bool_node = _TwoArgs(w_parser) else: pass # fallthrough if bool_node is None: bool_node = b_parser.ParseForBuiltin() except error.Parse as e: self.errfmt.PrettyPrintError(e, prefix='(test) ') return 2 # We technically don't need mem because we don't support BASH_REMATCH here. word_ev = _WordEvaluator() bool_ev = sh_expr_eval.BoolEvaluator(self.mem, self.exec_opts, None, self.errfmt) # We want [ a -eq a ] to always be an error, unlike [[ a -eq a ]]. This is a # weird case of [[ being less strict. bool_ev.Init_AlwaysStrict() bool_ev.word_ev = word_ev bool_ev.CheckCircularDeps() try: b = bool_ev.EvalB(bool_node) except error._ErrorWithLocation as e: # We want to catch e_die() and e_strict(). Those are both FatalRuntime # errors now, but it might not make sense later. # NOTE: This doesn't seem to happen. We have location info for all # errors that arise out of [. #if not e.HasLocation(): # raise self.errfmt.PrettyPrintError(e, prefix='(test) ') return 2 # 1 means 'false', and this usage error is like a parse error. status = 0 if b else 1 return status
def __call__(self, cmd_val): # type: (cmd_value__Argv) -> int """The test/[ builtin. The only difference between test and [ is that [ needs a matching ]. """ if self.need_right_bracket: # Preprocess right bracket strs = cmd_val.argv if not strs or strs[-1] != ']': self.errfmt.Print('missing closing ]', span_id=cmd_val.arg_spids[0]) return 2 # Remove the right bracket cmd_val.argv.pop() cmd_val.arg_spids.pop() w_parser = _StringWordEmitter(cmd_val) w_parser.Read() # dummy: advance past argv[0] b_parser = bool_parse.BoolParser(w_parser) # There is a fundamental ambiguity due to poor language design, in cases like: # [ -z ] # [ -z -a ] # [ -z -a ] ] # # See posixtest() in bash's test.c: # "This is an implementation of a Posix.2 proposal by David Korn." # It dispatches on expressions of length 0, 1, 2, 3, 4, and N args. We do # the same here. # # Another ambiguity: # -a is both a unary prefix operator and an infix operator. How to fix this # ambiguity? bool_node = None # type: bool_expr_t n = len(cmd_val.argv) - 1 try: if n == 0: return 1 # [ ] is False elif n == 1: w = w_parser.Read() bool_node = bool_expr.WordTest(w) elif n == 2: bool_node = _TwoArgs(w_parser) elif n == 3: bool_node = _ThreeArgs(w_parser) if n == 4: a0 = w_parser.Peek(0) if a0 == '!': w_parser.Read() # skip ! child = _ThreeArgs(w_parser) bool_node = bool_expr.LogicalNot(child) elif a0 == '(' and w_parser.Peek(3) == ')': w_parser.Read() # skip ')' bool_node = _TwoArgs(w_parser) else: pass # fallthrough if bool_node is None: bool_node = b_parser.ParseForBuiltin() except error.Parse as e: self.errfmt.PrettyPrintError(e, prefix='(test) ') return 2 # mem: Don't need it for BASH_REMATCH? Or I guess you could support it mem = None # Not necessary word_ev = _WordEvaluator() arena = None # We want [ a -eq a ] to always be an error, unlike [[ a -eq a ]]. This is a # weird case of [[ being less strict. class _DummyExecOpts(): def __init__(self): # type: () -> None self.strict_arith = True exec_opts = _DummyExecOpts() bool_ev = expr_eval.BoolEvaluator(mem, exec_opts, word_ev, arena) try: b = bool_ev.Eval(bool_node) # type = bool except error.FatalRuntime as e: # Hack: we don't get the (test) prefix but we get location info. We # don't have access to mem.CurrentSpanId() here. if not e.HasLocation(): raise self.errfmt.PrettyPrintError(e, prefix='(test) ') return 2 # 1 means 'false', and this usage error is like a parse error. status = 0 if b else 1 return status
def Test(argv, need_right_bracket): """The test/[ builtin. The only difference between test and [ is that [ needs a matching ]. """ if need_right_bracket: if not argv or argv[-1] != ']': util.error('[: missing closing ]') return 2 del argv[-1] w_parser = _StringWordEmitter(argv) b_parser = bool_parse.BoolParser(w_parser) # There is a fundamental ambiguity due to poor language design, in cases like: # [ -z ] # [ -z -a ] # [ -z -a ] ] # # See posixtest() in bash's test.c: # "This is an implementation of a Posix.2 proposal by David Korn." # It dispatches on expressions of length 0, 1, 2, 3, 4, and N args. We do # the same here. # # Another ambiguity: # -a is both a unary prefix operator and an infix operator. How to fix this # ambiguity? bool_node = None n = len(argv) try: if n == 0: return 1 # [ ] is False elif n == 1: bool_node = _StringWordTest(argv[0]) elif n == 2: bool_node = _TwoArgs(argv) elif n == 3: bool_node = _ThreeArgs(argv) if n == 4: a0 = argv[0] if a0 == '!': child = _ThreeArgs(argv[1:]) bool_node = bool_expr.LogicalNot(child) elif a0 == '(' and argv[3] == ')': bool_node = _TwoArgs(argv[1:3]) else: pass # fallthrough if bool_node is None: bool_node = b_parser.ParseForBuiltin() except util.ParseError as e: # TODO: There should be a nice method to print argv. And some way to point # to the error. log("Error parsing %s", argv) util.error("test: %s", e.UserErrorString()) return 2 # parse error is 2 # mem: Don't need it for BASH_REMATCH? Or I guess you could support it # exec_opts: don't need it, but might need it later mem = None # Not necessary word_ev = _WordEvaluator() arena = None # We want [ a -eq a ] to always be an error, unlike [[ a -eq a ]]. This is a # weird case of [[ being less strict. class _DummyExecOpts(): def __init__(self): self.strict_arith = True exec_opts = _DummyExecOpts() bool_ev = expr_eval.BoolEvaluator(mem, exec_opts, word_ev, arena) try: b = bool_ev.Eval(bool_node) except util.FatalRuntimeError as e: # e.g. [ -t xxx ] # TODO: Printing the location would be nice. util.error('test: %s', e.UserErrorString()) return 2 # because this is more like a parser error. status = 0 if b else 1 return status