Пример #1
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)
Пример #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 _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)
Пример #4
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)
Пример #5
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)
Пример #6
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)
Пример #7
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
Пример #8
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
Пример #9
0
    def ParseFactor(self):
        # type: () -> bool_expr_t
        """
    Factor  : WORD
            | UNARY_OP WORD
            | WORD BINARY_OP WORD
            | '(' Expr ')'
    """
        if self.b_kind == Kind.BoolUnary:
            # Just save the type and not the token itself?
            op = self.op_id
            self._Next()
            w = self.cur_word
            # e.g. [[ -f < ]].  But [[ -f '<' ]] is OK

            tag = w.tag_()
            if tag != word_e.Compound and tag != word_e.String:
                p_die('Invalid argument to unary operator', word=w)
            self._Next()
            node = bool_expr.Unary(op, w)  # type: bool_expr_t
            return node

        if self.b_kind == Kind.Word:
            # Peek ahead another token.
            t2 = self._LookAhead()
            t2_op_id = word_.BoolId(t2)
            t2_b_kind = consts.GetKind(t2_op_id)

            #log('t2 %s / t2_op_id %s / t2_b_kind %s', t2, t2_op_id, t2_b_kind)
            # Op for < and >, -a and -o pun
            if t2_b_kind == Kind.BoolBinary or t2_op_id in (Id.Op_Less,
                                                            Id.Op_Great):
                left = self.cur_word

                self._Next()
                op = self.op_id

                # TODO: Need to change to lex_mode_e.BashRegex.
                # _Next(lex_mode) then?
                is_regex = t2_op_id == Id.BoolBinary_EqualTilde
                if is_regex:
                    self._Next(lex_mode=lex_mode_e.BashRegex)
                else:
                    self._Next()

                right = self.cur_word
                if is_regex:
                    # NOTE: StaticEval for checking regex syntax isn't enough.  We could
                    # need to pass do_ere so that the quoted parts get escaped.
                    #ok, s, unused_quoted = word_.StaticEval(right)
                    pass

                self._Next()
                return bool_expr.Binary(op, left, right)
            else:
                # [[ foo ]]
                w = self.cur_word
                self._Next()
                return bool_expr.WordTest(w)

        if self.op_id == Id.Op_LParen:
            self._Next()
            node = self.ParseExpr()
            if self.op_id != Id.Op_RParen:
                p_die('Expected ), got %s',
                      word_.Pretty(self.cur_word),
                      word=self.cur_word)
            self._Next()
            return node

        # It's not WORD, UNARY_OP, or '('
        p_die('Unexpected token in boolean expression', word=self.cur_word)
Пример #10
0
def _StringWordTest(s):
    # TODO: Could be Word_String
    return bool_expr.WordTest(word.StringWord(Id.Word_Compound, s))