Example #1
0
File: state.py Project: jedahan/oil
    def _BindNewAssocArrayWithEntry(self, namespace, lval, value, new_flags):
        """Fill 'namespace' with a new indexed array entry."""
        d = {lval.index: value.s}  # TODO: RHS has to be string?
        new_value = runtime.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.cell(new_value, False, readonly, False)
Example #2
0
File: state.py Project: jedahan/oil
    def _BindNewArrayWithEntry(self, namespace, lval, value, new_flags):
        """Fill 'namespace' with a new indexed array entry."""
        items = [None] * lval.index
        items.append(value.s)
        new_value = runtime.StrArray(items)

        # arrays can't be exported; can't have AssocArray flag
        readonly = var_flags_e.ReadOnly in new_flags
        namespace[lval.name] = runtime.cell(new_value, False, readonly, False)
Example #3
0
    def SetVar(self, lval, value, 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?

        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', value)
                if lval.spids:
                    span_id = lval.spids[0]
                    line_span = self.arena.GetLineSpan(span_id)
                    line_id = line_span.line_id
                    #line = arena.GetLine(line_id)
                    path, line_num = self.arena.GetDebugInfo(line_id)
                    col = line_span.col
                    #length = line_span.length
                    util.log('--- spid %s: %s, line %d, col %d', span_id, path,
                             line_num + 1, col)

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

            # Maybe this should return one of (cell, scope).  existing cell, or the
            # scope to put it in?
            # _FindCellOrScope

            cell, namespace = self._FindCellAndNamespace(
                lval.name, lookup_mode)
            if cell:
                if value is not None:
                    if cell.readonly:
                        # TODO: error context
                        e_die("Can't assign to readonly value %r", lval.name)
                    cell.val = value
                if var_flags_e.Exported in new_flags:
                    cell.exported = True
                if var_flags_e.ReadOnly in new_flags:
                    cell.readonly = True
            else:
                if value is None:
                    value = runtime.Undef()  # export foo, readonly foo
                cell = runtime.cell(value, var_flags_e.Exported in new_flags,
                                    var_flags_e.ReadOnly 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:
            # a[1]=(1 2 3)
            if value.tag == value_e.StrArray:
                e_die("Can't assign array to array member"
                      )  # TODO: error context

            cell, namespace = self._FindCellAndNamespace(
                lval.name, lookup_mode)
            if cell:
                if cell.val.tag != value_e.StrArray:
                    # s=x
                    # s[1]=y
                    e_die("Can't index non-array")  # TODO: error context

                if cell.readonly:
                    e_die("Can't assign to readonly value")

                strs = cell.val.strs
                try:
                    strs[lval.index] = value.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] = value.s
            else:
                # When the array doesn't exist yet, it is created filled with None.
                # Access to the array needs to explicitly filter those sentinel values.
                # It also wastes memory. But indexed access is fast.

                # What should be optimized for? Bash uses a linked list. Random access
                # takes linear time, but iteration skips unset entries automatically.

                # - Maybe represent as hash table?  Then it's not an ASDL type?

                # representations:
                # - array_item.Str array_item.Undef
                # - parallel array: val.strs, val.undefs
                # - or change ASDL type checking
                #   - ASDL language does not allow: StrArray(string?* strs)
                # - or add dict to ASDL?  Didn't it support obj?
                #   - finding the max index is linear time?
                #     - also you have to sort the indices
                #
                # array ops:
                # a=(1 2)
                # a[1]=x
                # a+=(1 2)
                # ${a[@]}  - get all
                # ${#a[@]} - length
                # ${!a[@]} - keys
                # That seems pretty minimal.

                items = [None] * lval.index
                items.append(value.s)
                new_value = runtime.StrArray(items)
                # arrays can't be exported
                cell = runtime.cell(new_value, False, var_flags_e.ReadOnly
                                    in new_flags)
                namespace[lval.name] = cell

        else:
            raise AssertionError
Example #4
0
File: state.py Project: jedahan/oil
    def SetVar(self, lval, value, new_flags, lookup_mode, strict_array=False):
        """
    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?

        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', value)
                if lval.spids:
                    span_id = lval.spids[0]
                    line_span = self.arena.GetLineSpan(span_id)
                    line_id = line_span.line_id
                    #line = arena.GetLine(line_id)
                    path, line_num = self.arena.GetDebugInfo(line_id)
                    col = line_span.col
                    #length = line_span.length
                    util.log('--- spid %s: %s, line %d, col %d', span_id, path,
                             line_num + 1, col)

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

            # Maybe this should return one of (cell, scope).  existing cell, or the
            # scope to put it in?
            # _FindCellOrScope

            cell, namespace = self._FindCellAndNamespace(
                lval.name, lookup_mode)
            if cell:
                if value is not None:
                    if cell.readonly:
                        # TODO: error context
                        e_die("Can't assign to readonly value %r", lval.name)
                    cell.val = value
                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 value is None:
                    # set -o nounset; local foo; echo $foo  # It's still undefined!
                    value = runtime.Undef()  # export foo, readonly foo
                cell = runtime.cell(value, 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 value.tag == value_e.StrArray:
                e_die("Can't assign array to array member", span_id=left_spid)

            cell, namespace = self._FindCellAndNamespace(
                lval.name, lookup_mode)
            if not cell:
                self._BindNewArrayWithEntry(namespace, lval, value, new_flags)
                return

            # bash/mksh have annoying behavior of letting you do LHS assignment to
            # Undef, which then turns into an array.  (Undef means that set -o
            # nounset fails.)
            cell_tag = cell.val.tag
            if (cell_tag == value_e.Str
                    or (cell_tag == value_e.Undef and strict_array)):
                # 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)

            if cell_tag == value_e.Undef:
                if cell.is_assoc_array:
                    self._BindNewAssocArrayWithEntry(namespace, lval, value,
                                                     new_flags)
                else:
                    self._BindNewArrayWithEntry(namespace, lval, value,
                                                new_flags)
                return

            if cell_tag == value_e.StrArray:
                strs = cell.val.strs
                try:
                    strs[lval.index] = value.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] = value.s
                return

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

        else:
            raise AssertionError(lval.__class__.__name__)