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 = 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)
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 = 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)
def SetVar(self, lval, val, 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', val) 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 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) cell, namespace = self._FindCellAndNamespace( lval.name, lookup_mode) if not cell: self._BindNewArrayWithEntry(namespace, lval, val, 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, 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__)