Пример #1
0
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)
Пример #2
0
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)
Пример #3
0
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)
Пример #4
0
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)
Пример #5
0
 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()
Пример #6
0
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)
Пример #7
0
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)
Пример #8
0
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)
Пример #9
0
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)
Пример #10
0
    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
Пример #11
0
    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
Пример #12
0
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