Beispiel #1
0
def ToLValue(node):
    # type: (arith_expr_t) -> sh_lhs_expr_t
    """Determine if a node is a valid L-value by whitelisting tags.

  Valid:
    x = y
    a[1] = y
  Invalid:
    a[0][0] = y
  """
    UP_node = node
    with tagswitch(node) as case:
        if case(arith_expr_e.VarRef):
            node = cast(arith_expr__VarRef, UP_node)
            # For consistency with osh/cmd_parse.py, append a span_id.
            # TODO: (( a[ x ] = 1 )) and a[x]=1 should use different LST nodes.
            n = sh_lhs_expr.Name(node.token.val)
            n.spids.append(node.token.span_id)
            return n

        elif case(arith_expr_e.Binary):
            node = cast(arith_expr__Binary, UP_node)
            if (node.op_id == Id.Arith_LBracket
                    and node.left.tag_() == arith_expr_e.VarRef):
                left = cast(arith_expr__VarRef, node.left)
                return sh_lhs_expr.IndexedName(left.token.val, node.right)
            # But a[0][0] = 1 is NOT valid.

    return None
Beispiel #2
0
    def Eval(self, node):
        # type: (command_t) -> None

        UP_node = node
        with tagswitch(node) as case:
            if case(command_e.Simple):
                node = cast(command__Simple, UP_node)

                # Need splitter for this.
                if 0:
                    cmd_val = self.word_ev.EvalWordSequence2(node.words,
                                                             allow_assign=True)
                    for arg in cmd_val.argv:
                        log('arg %s', arg)
                for w in node.words:
                    val = self.word_ev.EvalWordToString(w)
                    # TODO: how to print repr() in C++?
                    log('arg %d', val.tag_())

            elif case(command_e.DParen):
                node = cast(command__DParen, UP_node)

                a = self.arith_ev.Eval(node.child)
                # TODO: how to print repr() in C++?
                log('arith val %d', a.tag_())

            else:
                log('Unhandled node %s', NewStr(command_str(node.tag_())))
Beispiel #3
0
Datei: word_.py Projekt: o11c/oil
def CommandId(w):
    # type: (word_t) -> Id_t
    UP_w = w
    with tagswitch(w) as case:
        if case(word_e.Token):
            tok = cast(Token, UP_w)
            return tok.id

        elif case(word_e.Compound):
            w = cast(compound_word, UP_w)

            # Has to be a single literal part
            if len(w.parts) != 1:
                return Id.Word_Compound

            token_type = _LiteralId(w.parts[0])
            if token_type == Id.Undefined_Tok:
                return Id.Word_Compound

            elif token_type in (Id.Lit_LBrace, Id.Lit_RBrace, Id.Lit_Equals,
                                Id.ControlFlow_Return):
                # OSH and Oil recognize:  { }
                # Oil recognizes:         = return
                return token_type

            token_kind = consts.GetKind(token_type)
            if token_kind == Kind.KW:
                return token_type

            return Id.Word_Compound

        else:
            raise AssertionError(w.tag_())
Beispiel #4
0
  def Push(self, redirects, waiter):
    # type: (List[redirect_t], Waiter) -> bool
    #log('> fd_state.Push %s', redirects)
    new_frame = _FdFrame()
    self.stack.append(new_frame)
    self.cur_frame = new_frame

    for r in redirects:
      # TODO: Could we use inheritance to make this cheaper?
      UP_r = r
      with tagswitch(r) as case:
        if case(redirect_e.Path):
          r = cast(redirect__Path, UP_r)
          op_spid = r.op_spid
        elif case(redirect_e.FileDesc):
          r = cast(redirect__FileDesc, UP_r)
          op_spid = r.op_spid
        elif case(redirect_e.HereDoc):
          r = cast(redirect__HereDoc, UP_r)
          op_spid = r.op_spid
        else:
          raise AssertionError()

      #log('apply %s', r)
      self.errfmt.PushLocation(op_spid)
      try:
        if not self._ApplyRedirect(r, waiter):
          return False  # for bad descriptor
      finally:
        self.errfmt.PopLocation()
    #log('done applying %d redirects', len(redirects))
    return True
Beispiel #5
0
 def GetJoinChar(self):
     # type: () -> str
     """
 For decaying arrays by joining, eg. "$@" -> $@.
 array
 """
     # https://www.gnu.org/software/bash/manual/bashref.html#Special-Parameters
     # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02
     # "When the expansion occurs within a double-quoted string (see
     # Double-Quotes), it shall expand to a single field with the value of
     # each parameter separated by the first character of the IFS variable, or
     # by a <space> if IFS is unset. If IFS is set to a null string, this is
     # not equivalent to unsetting it; its first character does not exist, so
     # the parameter values are concatenated."
     val = self.mem.GetValue('IFS')  # type: value_t
     UP_val = val
     with tagswitch(val) as case:
         if case(value_e.Undef):
             return ' '
         elif case(value_e.Str):
             val = cast(value__Str, UP_val)
             if len(val.s):
                 return val.s[0]
             else:
                 return ''
         else:
             # TODO: Raise proper error
             raise AssertionError("IFS shouldn't be an array")
Beispiel #6
0
Datei: dev.py Projekt: ilyash/oil
    def OnShAssignment(self, lval, op, val, flags, which_scopes):
        # type: (lvalue_t, assign_op_t, value_t, int, scope_t) -> None
        buf = self._ShTraceBegin()
        if not buf:
            return

        left = '?'
        UP_lval = lval
        with tagswitch(lval) as case:
            if case(lvalue_e.Named):
                lval = cast(lvalue__Named, UP_lval)
                left = lval.name
            elif case(lvalue_e.Indexed):
                lval = cast(lvalue__Indexed, UP_lval)
                left = '%s[%d]' % (lval.name, lval.index)
            elif case(lvalue_e.Keyed):
                lval = cast(lvalue__Keyed, UP_lval)
                left = '%s[%s]' % (lval.name, qsn.maybe_shell_encode(lval.key))
        buf.write(left)

        # Only two possibilities here
        buf.write('+=' if op == assign_op_e.PlusEqual else '=')

        _PrintShValue(val, buf)

        buf.write('\n')
        self.f.write(buf.getvalue())
Beispiel #7
0
def _EvalLhsArith(node, mem, arith_ev):
    # type: (sh_lhs_expr_t, Mem, ArithEvaluator) -> lvalue_t
    """sh_lhs_expr -> lvalue.
  
  Very similar to EvalLhs above in core/cmd_exec.
  """
    assert isinstance(node, sh_lhs_expr_t), node

    UP_node = node
    with tagswitch(node) as case:
        if case(sh_lhs_expr_e.Name):  # (( i = 42 ))
            node = cast(sh_lhs_expr__Name, UP_node)

            lval = lvalue.Named(node.name)  # type: lvalue_t
            # TODO: location info.  Use the = token?
            #lval.spids.append(spid)

        elif case(sh_lhs_expr_e.IndexedName):  # (( a[42] = 42 ))
            node = cast(sh_lhs_expr__IndexedName, UP_node)

            # The index of MaybeStrArray needs to be coerced to int, but not the
            # index of an AssocArray.
            if mem.IsAssocArray(node.name, scope_e.Dynamic):
                key = arith_ev.EvalWordToString(node.index)
                lval = lvalue.Keyed(node.name, key)
            else:
                index = arith_ev.EvalToInt(node.index)
                lval = lvalue.Indexed(node.name, index)
                # TODO: location info.  Use the = token?
                #lval.spids.append(node.spids[0])

        else:
            raise AssertionError(node.tag_())

    return lval
Beispiel #8
0
Datei: dev.py Projekt: ilyash/oil
def _PrintShValue(val, buf):
    # type: (value_t, mylib.BufWriter) -> None
    """Using maybe_shell_encode() for legacy xtrace_details."""

    # NOTE: This is a bit like _PrintVariables for declare -p
    result = '?'
    UP_val = val
    with tagswitch(val) as case:
        if case(value_e.Str):
            val = cast(value__Str, UP_val)
            result = qsn.maybe_shell_encode(val.s)

        elif case(value_e.MaybeStrArray):
            val = cast(value__MaybeStrArray, UP_val)
            parts = ['(']
            for s in val.strs:
                parts.append(qsn.maybe_shell_encode(s))
            parts.append(')')
            result = ' '.join(parts)

        elif case(value_e.AssocArray):
            val = cast(value__AssocArray, UP_val)
            parts = ['(']
            for k, v in iteritems(val.d):
                parts.append(
                    '[%s]=%s' %
                    (qsn.maybe_shell_encode(k), qsn.maybe_shell_encode(v)))
            parts.append(')')
            result = ' '.join(parts)

    buf.write(result)
Beispiel #9
0
Datei: word_.py Projekt: o11c/oil
def BoolId(w):
    # type: (word_t) -> Id_t
    UP_w = w
    with tagswitch(w) as case:
        if case(word_e.String):  # for test/[
            w = cast(word__String, UP_w)
            return w.id

        elif case(word_e.Token):
            tok = cast(Token, UP_w)
            return tok.id

        elif case(word_e.Compound):
            w = cast(compound_word, UP_w)

            if len(w.parts) != 1:
                return Id.Word_Compound

            token_type = _LiteralId(w.parts[0])
            if token_type == Id.Undefined_Tok:
                return Id.Word_Compound  # It's a regular word

            # This is outside the BoolUnary/BoolBinary namespace, but works the same.
            if token_type in (Id.KW_Bang, Id.Lit_DRightBracket):
                return token_type  # special boolean "tokens"

            token_kind = consts.GetKind(token_type)
            if token_kind in (Kind.BoolUnary, Kind.BoolBinary):
                return token_type  # boolean operators

            return Id.Word_Compound

        else:
            # I think Empty never happens in this context?
            raise AssertionError(w.tag_())
Beispiel #10
0
Datei: word_.py Projekt: o11c/oil
def LeftMostSpanForWord(w):
    # type: (word_t) -> int
    UP_w = w
    with tagswitch(w) as case:
        if case(word_e.Compound):
            w = cast(compound_word, UP_w)
            if len(w.parts):
                return LeftMostSpanForPart(w.parts[0])
            else:
                # This is possible for empty brace sub alternative {a,b,}
                return runtime.NO_SPID

        elif case(word_e.Token):
            tok = cast(Token, UP_w)
            return tok.span_id

        elif case(word_e.Empty):
            return runtime.NO_SPID

        elif case(word_e.BracedTree):
            w = cast(word__BracedTree, UP_w)
            # This should always have one part?
            return LeftMostSpanForPart(w.parts[0])

        elif case(word_e.String):
            w = cast(word__String, UP_w)
            return w.span_id  # See _StringWordEmitter in osh/builtin_bracket.py

        else:
            raise AssertionError(w.tag_())
Beispiel #11
0
def _SetToArg(action, suffix, arg_r, out):
    # type: (SetToArgAction, Optional[str], Reader, _Attributes) -> bool
    """
  Perform the action.
  """
    if suffix:  # for the ',' in -d,
        arg = suffix
    else:
        arg_r.Next()
        arg = arg_r.Peek()
        if arg is None:
            e_usage('expected argument to %r' % ('-' + action.name),
                    span_id=arg_r.SpanId())

    # e.g. spec.LongFlag('--format', ['text', 'html'])
    # Should change to arg.Enum([...])
    with tagswitch(action.flag_type) as case:
        if case(flag_type_e.Enum):
            alts = cast(flag_type__Enum, action.flag_type).alts
            if arg not in alts:
                e_usage('got invalid argument %r to %r, expected one of: %s' %
                        (arg, ('-' + action.name), ', '.join(alts)),
                        span_id=arg_r.SpanId())
            val = value.Str(arg)  # type: value_t

        elif case(flag_type_e.Str):
            val = value.Str(arg)

        elif case(flag_type_e.Int):
            try:
                i = int(arg)
            except ValueError:
                e_usage('expected integer after %s, got %r' %
                        ('-' + action.name, arg),
                        span_id=arg_r.SpanId())

            # So far all our int values are > 0, so use -1 as the 'unset' value
            if i < 0:
                e_usage('got invalid integer for %s: %s' %
                        ('-' + action.name, arg),
                        span_id=arg_r.SpanId())
            val = value.Int(i)

        elif case(flag_type_e.Float):
            try:
                val = value.Float(float(arg))
            except ValueError:
                e_usage('expected number after %r, got %r' %
                        ('-' + action.name, arg),
                        span_id=arg_r.SpanId())
        else:
            raise AssertionError()

    out.Set(action.name, val)
    return action.quit_parsing_flags
Beispiel #12
0
def _VarRefOrWord(node, dynamic_arith):
  # type: (arith_expr_t, bool) -> bool
  with tagswitch(node) as case:
    if case(arith_expr_e.VarRef):
      return True

    elif case(arith_expr_e.Word):
      if dynamic_arith:
        return True

  return False
Beispiel #13
0
Datei: word_.py Projekt: o11c/oil
def _RightMostSpanForPart(part):
    # type: (word_part_t) -> int
    UP_part = part
    with tagswitch(part) as case:
        if case(word_part_e.ShArrayLiteral):
            part = cast(sh_array_literal, UP_part)
            # TODO: Return )
            return LeftMostSpanForWord(part.words[0])  # Hm this is a=(1 2 3)

        elif case(word_part_e.Literal):
            # Just use the token
            tok = cast(Token, UP_part)
            return tok.span_id

        elif case(word_part_e.EscapedLiteral):
            part = cast(word_part__EscapedLiteral, UP_part)
            return part.token.span_id

        elif case(word_part_e.SingleQuoted):
            part = cast(single_quoted, UP_part)
            return part.spids[1]  # right '

        elif case(word_part_e.DoubleQuoted):
            part = cast(double_quoted, UP_part)
            return part.spids[1]  # right "

        elif case(word_part_e.SimpleVarSub):
            part = cast(simple_var_sub, UP_part)
            return part.token.span_id

        elif case(word_part_e.BracedVarSub):
            part = cast(braced_var_sub, UP_part)
            spid = part.spids[1]  # right }
            assert spid != runtime.NO_SPID
            return spid

        elif case(word_part_e.CommandSub):
            part = cast(command_sub, UP_part)
            return part.spids[1]

        elif case(word_part_e.TildeSub):
            return runtime.NO_SPID

        elif case(word_part_e.ArithSub):
            part = cast(word_part__ArithSub, UP_part)
            return part.spids[1]

        elif case(word_part_e.ExtGlob):
            part = cast(word_part__ExtGlob, UP_part)
            return part.spids[1]

        # TODO: Do Splice and FuncCall need it?
        else:
            raise AssertionError(part.tag_())
def SpanForArithExpr(node):
    # type: (arith_expr_t) -> int
    UP_node = node
    with tagswitch(node) as case:
        if case(arith_expr_e.VarRef):
            token = cast(Token, UP_node)
            return token.span_id
        elif case(arith_expr_e.Word):
            w = cast(compound_word, UP_node)
            return word_.LeftMostSpanForWord(w)

    return runtime.NO_SPID
Beispiel #15
0
def _ExpandPart(
        parts,  # type: List[word_part_t]
        first_alt_index,  # type: int
        suffixes,  # type: List[List[word_part_t]]
):
    # type: (...) -> List[List[word_part_t]]
    """Mutually recursive with _BraceExpand.

  Args:
    parts: input parts
    first_alt_index: index of the first BracedTuple
    suffixes: List of suffixes to append.
  """
    out = []  # type: List[List[word_part_t]]

    prefix = parts[:first_alt_index]
    expand_part = parts[first_alt_index]

    UP_part = expand_part
    with tagswitch(expand_part) as case:
        if case(word_part_e.BracedTuple):
            expand_part = cast(word_part__BracedTuple, UP_part)
            # Call _BraceExpand on each of the inner words too!
            expanded_alts = []  # type: List[List[word_part_t]]
            for w in expand_part.words:
                expanded_alts.extend(_BraceExpand(w.parts))

            for alt_parts in expanded_alts:
                for suffix in suffixes:
                    out_parts = []  # type: List[word_part_t]
                    out_parts.extend(prefix)
                    out_parts.extend(alt_parts)
                    out_parts.extend(suffix)
                    out.append(out_parts)

        elif case(word_part_e.BracedRange):
            expand_part = cast(word_part__BracedRange, UP_part)
            # Not mutually recursive with _BraceExpand
            strs = _RangeStrings(expand_part)
            for s in strs:
                for suffix in suffixes:
                    out_parts_ = []  # type: List[word_part_t]
                    out_parts_.extend(prefix)
                    # Preserve span_id from the original
                    t = Token(Id.Lit_Chars, expand_part.spids[0], s)
                    out_parts_.append(t)
                    out_parts_.extend(suffix)
                    out.append(out_parts_)

        else:
            raise AssertionError()

    return out
Beispiel #16
0
def SpanForArithExpr(node):
    # type: (arith_expr_t) -> int
    UP_node = node
    with tagswitch(node) as case:
        if case(arith_expr_e.VarRef):
            # TODO: VarRef should store a token instead of a string!
            return runtime.NO_SPID
        elif case(arith_expr_e.ArithWord):
            node = cast(arith_expr__ArithWord, UP_node)
            return word_.LeftMostSpanForWord(node.w)

    return runtime.NO_SPID
    def OnMatch(self, prefix, suffix, arg_r, out):
        # type: (Optional[str], Optional[str], Reader, _Attributes) -> bool
        """Called when the flag matches."""

        if suffix:  # for the ',' in -d,
            arg = suffix
        else:
            arg_r.Next()
            arg = arg_r.Peek()
            if arg is None:
                e_usage('expected argument to %r' % ('-' + self.name),
                        span_id=arg_r.SpanId())

        # e.g. spec.LongFlag('--format', ['text', 'html'])
        # Should change to arg.Enum([...])
        with tagswitch(self.flag_type) as case:
            if case(flag_type_e.Enum):
                alts = cast(flag_type__Enum, self.flag_type).alts
                if arg not in alts:
                    e_usage(
                        'got invalid argument %r to %r, expected one of: %s' %
                        (arg, ('-' + self.name), ', '.join(alts)),
                        span_id=arg_r.SpanId())
                val = value.Str(arg)  # type: value_t

            elif case(flag_type_e.Str):
                val = value.Str(arg)

            elif case(flag_type_e.Int):
                try:
                    val = value.Int(int(arg))
                except ValueError:
                    e_usage('expected integer after %r, got %r' %
                            ('-' + self.name, arg),
                            span_id=arg_r.SpanId())

            elif case(flag_type_e.Float):
                try:
                    val = value.Float(float(arg))
                except ValueError:
                    e_usage('expected number after %r, got %r' %
                            ('-' + self.name, arg),
                            span_id=arg_r.SpanId())
            else:
                raise AssertionError()

        out.Set(self.name, val)
        return self.quit_parsing_flags
Beispiel #18
0
def find_dynamic_token(part):
    # type: (word_part_t) -> token|bool
    """Recursive search for dynamic token.

    This is patterned on word_._EvalWordPart, but with
    ~inverted boolean logic: it returns the token object
    if one is found, otherwise False.
    """
    UP_part = part
    with tagswitch(part) as case:
        if case(word_part_e.ShArrayLiteral):
            return cast(Token, UP_part).token

        elif case(word_part_e.AssocArrayLiteral):
            return cast(Token, UP_part).token

        elif case(word_part_e.Literal):
            return False

        elif case(word_part_e.EscapedLiteral):
            return False

        elif case(word_part_e.SingleQuoted):
            return False

        elif case(word_part_e.DoubleQuoted):
            part = cast(double_quoted, UP_part)
            for p in part.parts:
                tok = find_dynamic_token(p)
                if tok:
                    return tok

            return False

        elif case(
                word_part_e.CommandSub,
                word_part_e.SimpleVarSub,
                word_part_e.BracedVarSub,
                word_part_e.TildeSub,
                word_part_e.ArithSub,
                word_part_e.ExtGlob,
                word_part_e.Splice,
        ):
            return cast(Token, UP_part).token
        else:
            raise AssertionError(part.tag_())
Beispiel #19
0
    def _ValToIntOrError(self, val, span_id=runtime.NO_SPID):
        # type: (value_t, int) -> int
        try:
            UP_val = val
            with tagswitch(val) as case:
                if case(value_e.Undef
                        ):  # 'nounset' already handled before got here
                    # Happens upon a[undefined]=42, which unfortunately turns into a[0]=42.
                    #log('blame_word %s   arena %s', blame_word, self.arena)
                    e_die('Undefined value in arithmetic context',
                          span_id=span_id)

                elif case(value_e.Int):
                    val = cast(value__Int, UP_val)
                    return val.i

                elif case(value_e.Str):
                    val = cast(value__Str, UP_val)
                    return _StringToInteger(val.s,
                                            span_id=span_id)  # calls e_die

                elif case(value_e.Obj):
                    # Note: this handles var x = 42; echo $(( x > 2 )).
                    if mylib.PYTHON:
                        val = cast(value__Obj, UP_val)
                        if isinstance(val.obj, int):
                            return val.obj
                    raise AssertionError()  # not in C++

        except error.FatalRuntime as e:
            if self.exec_opts.strict_arith():
                raise
            else:
                span_id = word_.SpanIdFromError(e)
                self.errfmt.PrettyPrintError(e, prefix='warning: ')
                return 0

        # Arrays and associative arrays always fail -- not controlled by strict_arith.
        # In bash, (( a )) is like (( a[0] )), but I don't want that.
        # And returning '0' gives different results.
        e_die("Expected a value convertible to integer, got %s",
              ui.ValType(val),
              span_id=span_id)
Beispiel #20
0
  def _VarRefOrWord(self, anode):
    # type: (arith_expr_t) -> Tuple[Optional[str], int]
    """
    Returns (var_name, span_id) if the arith node can be interpreted that way
    """
    UP_anode = anode
    with tagswitch(anode) as case:
      if case(arith_expr_e.VarRef):
        tok = cast(Token, UP_anode)
        return (tok.val, tok.span_id)

      elif case(arith_expr_e.Word):
        w = cast(compound_word, UP_anode)
        var_name = self.EvalWordToString(w)
        span_id = word_.LeftMostSpanForWord(w)
        return (var_name, span_id)

    no_str = None  # type: str
    return (no_str, runtime.NO_SPID)
Beispiel #21
0
def BraceExpandWords(words):
    # type: (List[word_t]) -> List[compound_word]
    out = []  # type: List[compound_word]
    for w in words:
        UP_w = w
        with tagswitch(w) as case:
            if case(word_e.BracedTree):
                w = cast(word__BracedTree, UP_w)
                parts_list = _BraceExpand(w.parts)
                tmp = [compound_word(p) for p in parts_list]
                out.extend(tmp)

            elif case(word_e.Compound):
                w = cast(compound_word, UP_w)
                out.append(w)

            else:
                raise AssertionError(w.tag_())

    return out
Beispiel #22
0
    def _GetSplitter(self, ifs=None):
        # type: (str) -> IfsSplitter
        """Based on the current stack frame, get the splitter."""
        if ifs is None:
            val = self.mem.GetVar('IFS')

            UP_val = val
            with tagswitch(val) as case:
                if case(value_e.Undef):
                    ifs = DEFAULT_IFS
                elif case(value_e.Str):
                    val = cast(value__Str, UP_val)
                    ifs = val.s
                else:
                    # TODO: Raise proper error
                    raise AssertionError("IFS shouldn't be an array")

        try:
            sp = self.splitters[ifs]
        except KeyError:
            # Figure out what kind of splitter we should instantiate.

            ifs_whitespace = mylib.BufWriter()
            ifs_other = mylib.BufWriter()
            for c in ifs:
                if c in ' \t\n':  # Happens to be the same as DEFAULT_IFS
                    ifs_whitespace.write(c)
                else:
                    # TODO: \ not supported
                    ifs_other.write(c)

            sp = IfsSplitter(ifs_whitespace.getvalue(), ifs_other.getvalue())

            # NOTE: Technically, we could make the key more precise.  IFS=$' \t' is
            # the same as IFS=$'\t '.  But most programs probably don't do that, and
            # everything should work in any case.
            self.splitters[ifs] = sp

        return sp
Beispiel #23
0
def EvalLhs(node, arith_ev, mem, spid, lookup_mode):
    # type: (sh_lhs_expr_t, ArithEvaluator, Mem, int, scope_t) -> lvalue_t
    """sh_lhs_expr -> lvalue.

  Used for a=b and a[x]=b
  """
    assert isinstance(node, sh_lhs_expr_t), node

    UP_node = node
    lval = None  # type: lvalue_t
    with tagswitch(node) as case:
        if case(sh_lhs_expr_e.Name):  # a=x
            node = cast(sh_lhs_expr__Name, UP_node)

            # Note: C++ constructor doesn't take spids directly.  Should we add that?
            lval1 = lvalue.Named(node.name)
            lval1.spids.append(spid)
            lval = lval1

        elif case(sh_lhs_expr_e.IndexedName):  # a[1+2]=x
            node = cast(sh_lhs_expr__IndexedName, UP_node)

            if mem.IsAssocArray(node.name, lookup_mode):
                key = arith_ev.EvalWordToString(node.index)
                # copy left-mode spid
                lval2 = lvalue.Keyed(node.name, key)
                lval2.spids.append(node.spids[0])
                lval = lval2
            else:
                index = arith_ev.EvalToInt(node.index)
                # copy left-mode spid
                lval3 = lvalue.Indexed(node.name, index)
                lval3.spids.append(node.spids[0])
                lval = lval3

        else:
            raise AssertionError(node.tag_())

    return lval
Beispiel #24
0
Datei: word_.py Projekt: o11c/oil
def SpanForLhsExpr(node):
    # type: (sh_lhs_expr_t) -> int

    # This switch is annoying but we don't have inheritance from the sum type
    # (because of diamond issue).  We might change the schema later, which maeks
    # it moot.  See the comment in frontend/syntax.asdl.
    UP_node = node
    with tagswitch(node) as case:
        if case(sh_lhs_expr_e.Name):
            node = cast(sh_lhs_expr__Name, UP_node)
            spids = node.spids
        elif case(sh_lhs_expr_e.IndexedName):
            node = cast(sh_lhs_expr__IndexedName, UP_node)
            spids = node.spids
        else:
            # Should not see UnparsedIndex
            raise AssertionError()

    if len(spids):
        return spids[0]
    else:
        return runtime.NO_SPID
Beispiel #25
0
Datei: word_.py Projekt: o11c/oil
def RightMostSpanForWord(w):
    # type: (word_t) -> int
    """Needed for here doc delimiters."""
    UP_w = w
    with tagswitch(w) as case:
        if case(word_e.Compound):
            w = cast(compound_word, UP_w)
            if len(w.parts) == 0:
                # TODO: Use Empty instead
                raise AssertionError("Compound shouldn't be empty")
            else:
                end = w.parts[-1]
                return _RightMostSpanForPart(end)

        elif case(word_e.Empty):
            return runtime.NO_SPID

        elif case(word_e.Token):
            tok = cast(Token, UP_w)
            return tok.span_id

        else:
            raise AssertionError(w.tag_())
    def Set(self, name, val):
        # type: (str, value_t) -> None

        self.attrs[name] = val

        if mylib.PYTHON:
            # Backward compatibility!
            with tagswitch(val) as case:
                if case(value_e.Undef):
                    py_val = None  # type: Any
                elif case(value_e.Bool):
                    py_val = cast(value__Bool, val).b
                elif case(value_e.Int):
                    py_val = cast(value__Int, val).i
                elif case(value_e.Float):
                    py_val = cast(value__Float, val).f
                elif case(value_e.Str):
                    py_val = cast(value__Str, val).s
                else:
                    raise AssertionError(val)

            # debug-completion -> debug_completion
            setattr(self, name.replace('-', '_'), py_val)
Beispiel #27
0
  def EvalShellLhs(self, node, spid, lookup_mode):
    # type: (sh_lhs_expr_t, int, scope_t) -> lvalue_t
    """Evaluate a shell LHS expression, i.e. place expression.

    For  a=b  and  a[x]=b  etc.
    """
    assert isinstance(node, sh_lhs_expr_t), node

    UP_node = node
    lval = None  # type: lvalue_t
    with tagswitch(node) as case:
      if case(sh_lhs_expr_e.Name):  # a=x
        node = cast(sh_lhs_expr__Name, UP_node)

        # Note: C++ constructor doesn't take spids directly.  Should we add that?
        lval1 = lvalue.Named(node.name)
        lval1.spids.append(spid)
        lval = lval1

      elif case(sh_lhs_expr_e.IndexedName):  # a[1+2]=x
        node = cast(sh_lhs_expr__IndexedName, UP_node)

        if self.mem.IsAssocArray(node.name, lookup_mode):
          key = self.EvalWordToString(node.index)
          lval2 = lvalue.Keyed(node.name, key)
          lval2.spids.append(node.spids[0])
          lval = lval2
        else:
          index = self.EvalToInt(node.index)
          lval3 = lvalue.Indexed(node.name, index)
          lval3.spids.append(node.spids[0])
          lval = lval3

      else:
        raise AssertionError(node.tag_())

    return lval
Beispiel #28
0
Datei: dev.py Projekt: ilyash/oil
    def OnProcessStart(self, pid, why):
        # type: (int, trace_t) -> None
        buf = self._RichTraceBegin('|')
        if not buf:
            return

        # TODO: ProcessSub and PipelinePart are commonly command.Simple, and also
        # Fork/ForkWait through the BraceGroup.  We could print those argv arrays.

        UP_why = why
        with tagswitch(why) as case:
            # Synchronous cases
            if case(trace_e.External):
                why = cast(trace__External, UP_why)
                buf.write('command %d:' % pid)
                _PrintArgv(why.argv, buf)

            # Everything below is the same.  Could use string literals?
            elif case(trace_e.ForkWait):
                buf.write('forkwait %d\n' % pid)
            elif case(trace_e.CommandSub):
                buf.write('command sub %d\n' % pid)

            # Async cases
            elif case(trace_e.ProcessSub):
                buf.write('proc sub %d\n' % pid)
            elif case(trace_e.HereDoc):
                buf.write('here doc %d\n' % pid)
            elif case(trace_e.Fork):
                buf.write('fork %d\n' % pid)
            elif case(trace_e.PipelinePart):
                buf.write('part %d\n' % pid)

            else:
                raise AssertionError()

        self.f.write(buf.getvalue())
Beispiel #29
0
    def _ApplyRedirect(self, r, waiter):
        # type: (redirect, Waiter) -> None
        arg = r.arg
        UP_arg = arg
        with tagswitch(arg) as case:

            if case(redirect_arg_e.Path):
                arg = cast(redirect_arg__Path, UP_arg)

                if r.op_id in (Id.Redir_Great, Id.Redir_AndGreat):  # >   &>
                    # NOTE: This is different than >| because it respects noclobber, but
                    # that option is almost never used.  See test/wild.sh.
                    mode = posix.O_CREAT | posix.O_WRONLY | posix.O_TRUNC
                elif r.op_id == Id.Redir_Clobber:  # >|
                    mode = posix.O_CREAT | posix.O_WRONLY | posix.O_TRUNC
                elif r.op_id in (Id.Redir_DGreat,
                                 Id.Redir_AndDGreat):  # >>   &>>
                    mode = posix.O_CREAT | posix.O_WRONLY | posix.O_APPEND
                elif r.op_id == Id.Redir_Less:  # <
                    mode = posix.O_RDONLY
                elif r.op_id == Id.Redir_LessGreat:  # <>
                    mode = posix.O_CREAT | posix.O_RDWR
                else:
                    raise NotImplementedError(r.op_id)

                # NOTE: 0666 is affected by umask, all shells use it.
                try:
                    open_fd = posix.open(arg.filename, mode, 0o666)
                except OSError as e:
                    self.errfmt.Print_("Can't open %r: %s" %
                                       (arg.filename, pyutil.strerror(e)),
                                       span_id=r.op_spid)
                    raise  # redirect failed

                new_fd = self._PushDup(open_fd, r.loc)
                if new_fd != NO_FD:
                    posix.close(open_fd)

                # Now handle &> and &>> and their variants.  These pairs are the same:
                #
                #   stdout_stderr.py &> out-err.txt
                #   stdout_stderr.py > out-err.txt 2>&1
                #
                #   stdout_stderr.py 3&> out-err.txt
                #   stdout_stderr.py 3> out-err.txt 2>&3
                #
                # Ditto for {fd}> and {fd}&>

                if r.op_id in (Id.Redir_AndGreat, Id.Redir_AndDGreat):
                    self._PushDup(new_fd, redir_loc.Fd(2))

            elif case(redirect_arg_e.CopyFd):  # e.g. echo hi 1>&2
                arg = cast(redirect_arg__CopyFd, UP_arg)

                if r.op_id == Id.Redir_GreatAnd:  # 1>&2
                    self._PushDup(arg.target_fd, r.loc)

                elif r.op_id == Id.Redir_LessAnd:  # 0<&5
                    # The only difference between >& and <& is the default file
                    # descriptor argument.
                    self._PushDup(arg.target_fd, r.loc)

                else:
                    raise NotImplementedError()

            elif case(redirect_arg_e.MoveFd):  # e.g. echo hi 5>&6-
                arg = cast(redirect_arg__MoveFd, UP_arg)
                new_fd = self._PushDup(arg.target_fd, r.loc)
                if new_fd != NO_FD:
                    posix.close(arg.target_fd)

                    UP_loc = r.loc
                    if r.loc.tag_() == redir_loc_e.Fd:
                        fd = cast(redir_loc__Fd, UP_loc).fd
                    else:
                        fd = NO_FD

                    self.cur_frame.saved.append(_RedirFrame(new_fd, fd, False))

            elif case(redirect_arg_e.CloseFd):  # e.g. echo hi 5>&-
                self._PushCloseFd(r.loc)

            elif case(redirect_arg_e.HereDoc):
                arg = cast(redirect_arg__HereDoc, UP_arg)

                # NOTE: Do these descriptors have to be moved out of the range 0-9?
                read_fd, write_fd = posix.pipe()

                self._PushDup(read_fd, r.loc)  # stdin is now the pipe

                # We can't close like we do in the filename case above?  The writer can
                # get a "broken pipe".
                self._PushClose(read_fd)

                thunk = _HereDocWriterThunk(write_fd, arg.body)

                # TODO: Use PIPE_SIZE to save a process in the case of small here docs,
                # which are the common case.  (dash does this.)
                start_process = True
                #start_process = False

                if start_process:
                    here_proc = Process(thunk, self.job_state)

                    # NOTE: we could close the read pipe here, but it doesn't really
                    # matter because we control the code.
                    _ = here_proc.Start()
                    #log('Started %s as %d', here_proc, pid)
                    self._PushWait(here_proc, waiter)

                    # Now that we've started the child, close it in the parent.
                    posix.close(write_fd)

                else:
                    posix.write(write_fd, arg.body)
                    posix.close(write_fd)
Beispiel #30
0
    def Run(self, cmd_val):
        arg_r = args.Reader(cmd_val.argv, spids=cmd_val.arg_spids)
        arg_r.Next()  # skip 'json'

        action, action_spid = arg_r.Peek2()
        if action is None:
            raise error.Usage(_JSON_ACTION_ERROR)
        arg_r.Next()

        if action == 'write':
            arg, _ = JSON_WRITE_SPEC.Parse(arg_r)

            # GetVar() of each name and print it.

            for var_name in arg_r.Rest():
                if var_name.startswith(':'):
                    var_name = var_name[1:]

                val = self.mem.GetVar(var_name)
                with tagswitch(val) as case:
                    if case(value_e.Undef):
                        # TODO: blame the right span_id
                        self.errfmt.Print("no variable named %r is defined",
                                          var_name)
                        return 1
                    elif case(value_e.Str):
                        obj = val.s
                    elif case(value_e.MaybeStrArray):
                        obj = val.strs
                    elif case(value_e.AssocArray):
                        obj = val.d
                    elif case(value_e.Obj):
                        obj = val.obj
                    else:
                        raise AssertionError(val)

                if arg.pretty:
                    indent = arg.indent
                    extra_newline = False
                else:
                    # How yajl works: if indent is -1, then everything is on one line.
                    indent = -1
                    extra_newline = True

                j = yajl.dump(obj, sys.stdout, indent=indent)
                if extra_newline:
                    sys.stdout.write('\n')

            # TODO: Accept a block.  They aren't hooked up yet.
            if cmd_val.block:
                # TODO: flatten value.{Str,Obj} into a flat dict?
                namespace = self.cmd_ev.EvalBlock(cmd_val.block)

                print(yajl.dump(namespace))

        elif action == 'read':
            arg, _ = JSON_READ_SPEC.Parse(arg_r)
            # TODO:
            # Respect -validate=F

            var_name, name_spid = arg_r.ReadRequired2("expected variable name")
            if var_name.startswith(':'):
                var_name = var_name[1:]

            if not match.IsValidVarName(var_name):
                raise error.Usage('got invalid variable name %r' % var_name,
                                  span_id=name_spid)

            try:
                # Use a global _STDIN, because we get EBADF on a redirect if we use a
                # local.  A Py_DECREF closes the file, which we don't want, because the
                # redirect is responsible for freeing it.
                #
                # https://github.com/oilshell/oil/issues/675
                #
                # TODO: write a better binding like yajl.readfd()
                #
                # It should use streaming like here:
                # https://lloyd.github.io/yajl/

                obj = yajl.load(_STDIN)
            except ValueError as e:
                self.errfmt.Print('json read: %s', e, span_id=action_spid)
                return 1

            self.mem.SetVar(sh_lhs_expr.Name(var_name), value.Obj(obj),
                            scope_e.LocalOnly)

        else:
            raise error.Usage(_JSON_ACTION_ERROR, span_id=action_spid)

        return 0