Beispiel #1
0
  def _BindNewAssocArrayWithEntry(self, namespace, lval, val, new_flags):
    """Fill 'namespace' with a new indexed array entry."""
    d = {lval.index: val.s}  # TODO: RHS has to be string?
    new_value = value.AssocArray(d)

    # associative arrays can't be exported; don't need AssocArray flag
    readonly = var_flags_e.ReadOnly in new_flags
    namespace[lval.name] = runtime_asdl.cell(new_value, False, readonly, False)
Beispiel #2
0
  def _BindNewArrayWithEntry(self, namespace, lval, val, new_flags):
    """Fill 'namespace' with a new indexed array entry."""
    items = [None] * lval.index
    items.append(val.s)
    new_value = value.StrArray(items)

    # arrays can't be exported; can't have AssocArray flag
    readonly = var_flags_e.ReadOnly in new_flags
    namespace[lval.name] = runtime_asdl.cell(new_value, False, readonly, False)
Beispiel #3
0
  def SetVar(self, lval, val, new_flags, lookup_mode):
    """
    Args:
      lval: lvalue
      val: value, or None if only changing flags
      new_flags: tuple of flags to set: ReadOnly | Exported
        () means no flags to start with
        None means unchanged?
      scope:
        Local | Global | Dynamic - for builtins, PWD, etc.

      NOTE: in bash, PWD=/ changes the directory.  But not in dash.
    """
    # STRICTNESS / SANENESS:
    #
    # 1) Don't create arrays automatically, e.g. a[1000]=x
    # 2) Never change types?  yeah I think that's a good idea, at least for oil
    # (not sh, for compatibility).  set -o strict-types or something.  That
    # means arrays have to be initialized with let arr = [], which is fine.
    # This helps with stuff like IFS.  It starts off as a string, and assigning
    # it to a list is en error.  I guess you will have to turn this no for
    # bash?
    #
    # TODO:
    # - COMPUTED_VARS can't be set
    # - What about PWD / OLDPWD / UID / EUID ?  You can simply make them
    # readonly.
    # - $PS1 and $PS4 should be PARSED when they are set, to avoid the error on use
    # - Other validity: $HOME could be checked for existence

    assert new_flags is not None

    if lval.tag == lvalue_e.LhsName:
      #if lval.name == 'ldflags':
      # TODO: Turn this into a tracing feature.  Like osh --tracevar ldflags
      # --tracevar foo.  Has to respect environment variables too.
      if 0:
        util.log('--- SETTING ldflags to %s', val)
        if lval.spids:
          span_id = lval.spids[0]
          line_span = self.arena.GetLineSpan(span_id)
          line_id = line_span.line_id
          source_str = self.arena.GetLineSourceString(line_id)
          line_num = self.arena.GetLineNumber(line_id)
          #length = line_span.length
          util.log('--- spid %s: %s, line %d, col %d', span_id, source_str,
                   line_num+1, line_span.col)

          # TODO: Need the arena to look it up the line spid and line number.

      cell, namespace = self._FindCellAndNamespace(lval.name, lookup_mode)
      if cell:
        if val is not None:
          if cell.readonly:
            # TODO: error context
            e_die("Can't assign to readonly value %r", lval.name)
          cell.val = val
        if var_flags_e.Exported in new_flags:
          cell.exported = True
        if var_flags_e.ReadOnly in new_flags:
          cell.readonly = True
        if var_flags_e.AssocArray in new_flags:
          cell.is_assoc_array = True
      else:
        if val is None:
          # set -o nounset; local foo; echo $foo  # It's still undefined!
          val = value.Undef()  # export foo, readonly foo
        cell = runtime_asdl.cell(val,
                                 var_flags_e.Exported in new_flags,
                                 var_flags_e.ReadOnly in new_flags,
                                 var_flags_e.AssocArray in new_flags)
        namespace[lval.name] = cell

      if (cell.val is not None and cell.val.tag == value_e.StrArray and
          cell.exported):
        e_die("Can't export array")  # TODO: error context

    elif lval.tag == lvalue_e.LhsIndexedName:
      # TODO: All paths should have this?  We can get here by a[x]=1 or
      # (( a[ x ] = 1 )).  Maybe we should make them different?
      if lval.spids:
        left_spid = lval.spids[0]
      else:
        left_spid = const.NO_INTEGER

      # TODO: This is a parse error!
      # a[1]=(1 2 3)
      if val.tag == value_e.StrArray:
        e_die("Can't assign array to array member", span_id=left_spid)

      # bash/mksh have annoying behavior of letting you do LHS assignment to
      # Undef, which then turns into an INDEXED array.  (Undef means that set
      # -o nounset fails.)
      cell, namespace = self._FindCellAndNamespace(lval.name, lookup_mode)
      if not cell:
        self._BindNewArrayWithEntry(namespace, lval, val, new_flags)
        return

      cell_tag = cell.val.tag
      if cell_tag == value_e.Str:
        # s=x
        # s[1]=y  # invalid
        e_die("Entries in value of type %s can't be assigned to",
              cell.val.__class__.__name__, span_id=left_spid)

      if cell.readonly:
        e_die("Can't assign to readonly value", span_id=left_spid)

      # This is for the case where we did declare -a foo or declare -A foo.
      # There IS a cell, but it's still undefined.
      if cell_tag == value_e.Undef:
        if cell.is_assoc_array:
          self._BindNewAssocArrayWithEntry(namespace, lval, val, new_flags)
        else:
          self._BindNewArrayWithEntry(namespace, lval, val, new_flags)
        return

      if cell_tag == value_e.StrArray:
        strs = cell.val.strs
        try:
          strs[lval.index] = val.s
        except IndexError:
          # Fill it in with None.  It could look like this:
          # ['1', 2, 3, None, None, '4', None]
          # Then ${#a[@]} counts the entries that are not None.
          #
          # TODO: strict-array for Oil arrays won't auto-fill.
          n = lval.index - len(strs) + 1
          strs.extend([None] * n)
          strs[lval.index] = val.s
        return

      if cell_tag == value_e.AssocArray:
        cell.val.d[lval.index] = val.s
        return

    else:
      raise AssertionError(lval.__class__.__name__)
Beispiel #4
0
    def SetVar(self,
               lval,
               val,
               flags_to_set,
               lookup_mode,
               flags_to_clear=(),
               keyword_id=None):
        """
    Args:
      lval: lvalue
      val: value, or None if only changing flags
      flags_to_set: tuple of flags to set: ReadOnly | Exported
        () means no flags to start with
      scope:
        Local | Global | Dynamic - for builtins, PWD, etc.

      NOTE: in bash, PWD=/ changes the directory.  But not in dash.
    """
        # STRICTNESS / SANENESS:
        #
        # 1) Don't create arrays automatically, e.g. a[1000]=x
        # 2) Never change types?  yeah I think that's a good idea, at least for oil
        # (not sh, for compatibility).  set -o strict-types or something.  That
        # means arrays have to be initialized with let arr = [], which is fine.
        # This helps with stuff like IFS.  It starts off as a string, and assigning
        # it to a list is an error.  I guess you will have to turn this no for
        # bash?
        #
        # TODO:
        # - COMPUTED_VARS can't be set
        # - What about PWD / OLDPWD / UID / EUID ?  You can simply make them
        # readonly.
        # - $PS1 and $PS4 should be PARSED when they are set, to avoid the error on use
        # - Other validity: $HOME could be checked for existence

        assert flags_to_set is not None
        if lval.tag == lvalue_e.Named:
            cell, namespace = self._FindCellAndNamespace(
                lval.name, lookup_mode)
            self._CheckOilKeyword(keyword_id, lval, cell)
            if cell:
                # Clear before checking readonly bit.
                # NOTE: Could be cell.flags &= flag_clear_mask
                if var_flags_e.Exported in flags_to_clear:
                    cell.exported = False
                if var_flags_e.ReadOnly in flags_to_clear:
                    cell.readonly = False

                if val is not None:  # e.g. declare -rx existing
                    if cell.readonly:
                        # TODO: error context
                        e_die("Can't assign to readonly value %r", lval.name)
                    cell.val = val

                # NOTE: Could be cell.flags |= flag_set_mask
                if var_flags_e.Exported in flags_to_set:
                    cell.exported = True
                if var_flags_e.ReadOnly in flags_to_set:
                    cell.readonly = True

            else:
                if val is None:  # declare -rx nonexistent
                    # set -o nounset; local foo; echo $foo  # It's still undefined!
                    val = value.Undef()  # export foo, readonly foo

                cell = runtime_asdl.cell(val, var_flags_e.Exported
                                         in flags_to_set, var_flags_e.ReadOnly
                                         in flags_to_set)
                namespace[lval.name] = cell

            # Maintain invariant that only strings and undefined cells can be
            # exported.
            if (cell.val is not None
                    and cell.val.tag not in (value_e.Undef, value_e.Str)
                    and cell.exported):
                e_die("Can't export array")  # TODO: error context

        elif lval.tag == lvalue_e.Indexed:
            # There is no syntax 'declare a[x]'
            assert val is not None, val

            # TODO: All paths should have this?  We can get here by a[x]=1 or
            # (( a[ x ] = 1 )).  Maybe we should make them different?
            left_spid = lval.spids[0] if lval.spids else const.NO_INTEGER

            # bash/mksh have annoying behavior of letting you do LHS assignment to
            # Undef, which then turns into an INDEXED array.  (Undef means that set
            # -o nounset fails.)
            cell, namespace = self._FindCellAndNamespace(
                lval.name, lookup_mode)
            self._CheckOilKeyword(keyword_id, lval, cell)
            if not cell:
                self._BindNewArrayWithEntry(namespace, lval, val, flags_to_set)
                return

            if cell.readonly:
                e_die("Can't assign to readonly array", span_id=left_spid)

            cell_tag = cell.val.tag

            # undef[0]=y is allowed
            if cell_tag == value_e.Undef:
                self._BindNewArrayWithEntry(namespace, lval, val, flags_to_set)
                return

            if cell_tag == value_e.Str:
                # s=x
                # s[1]=y  # invalid
                e_die("Entries in value of type %s can't be assigned to",
                      cell.val.__class__.__name__,
                      span_id=left_spid)

            if cell_tag == value_e.MaybeStrArray:
                strs = cell.val.strs
                try:
                    strs[lval.index] = val.s
                except IndexError:
                    # Fill it in with None.  It could look like this:
                    # ['1', 2, 3, None, None, '4', None]
                    # Then ${#a[@]} counts the entries that are not None.
                    #
                    # TODO: strict-array for Oil arrays won't auto-fill.
                    n = lval.index - len(strs) + 1
                    strs.extend([None] * n)
                    strs[lval.index] = val.s
                return

            # AssocArray shouldn't happen because we query IsAssocArray before
            # evaluating lhs_expr.
            e_die("Object of this type can't be indexed: %s", cell.val)

        elif lval.tag == lvalue_e.Keyed:
            # There is no syntax 'declare A["x"]'
            assert val is not None, val

            left_spid = lval.spids[0] if lval.spids else const.NO_INTEGER

            cell, namespace = self._FindCellAndNamespace(
                lval.name, lookup_mode)
            self._CheckOilKeyword(keyword_id, lval, cell)
            # We already looked it up before making the lvalue
            assert cell.val.tag == value_e.AssocArray, cell
            if cell.readonly:
                e_die("Can't assign to readonly associative array",
                      span_id=left_spid)

            cell.val.d[lval.key] = val.s

        else:
            raise AssertionError(lval.__class__.__name__)