def _EvalDoubleQuotedPart(self, part, part_vals): """DoubleQuotedPart -> part_value Args: part_vals: output param to append to. """ # Example of returning array: # $ a=(1 2); b=(3); $ c=(4 5) # $ argv "${a[@]}${b[@]}${c[@]}" # ['1', '234', '5'] # Example of multiple parts # $ argv "${a[@]}${undef[@]:-${c[@]}}" # ['1', '24', '5'] #log('DQ part %s', part) # Special case for "". The parser outputs (DoubleQuotedPart []), instead # of (DoubleQuotedPart [LiteralPart '']). This is better but it means we # have to check for it. if not part.parts: v = runtime.StringPartValue('', False) part_vals.append(v) return for p in part.parts: self._EvalWordPart(p, part_vals, quoted=True)
def _EvalCommandSub(self, node, quoted): stdout = self.ex.RunCommandSub(node) # Runtime errors test case: # $("echo foo > $@") # Why rstrip()? # https://unix.stackexchange.com/questions/17747/why-does-shell-command-substitution-gobble-up-a-trailing-newline-char return runtime.StringPartValue(stdout, not quoted, not quoted)
def _EvalDoubleQuotedPart(self, part): # Example of returning array: # $ a=(1 2); b=(3); $ c=(4 5) # $ argv "${a[@]}${b[@]}${c[@]}" # ['1', '234', '5'] # Example of multiple parts # $ argv "${a[@]}${undef[@]:-${c[@]}}" # ['1', '24', '5'] #log('DQ part %s', part) # Special case for "". The parser outputs (DoubleQuotedPart []), instead # of (DoubleQuotedPart [LiteralPart '']). This is better but it means we # have to check for it. if not part.parts: return runtime.StringPartValue('', False, False) frag_arrays = [[]] for p in part.parts: for part_val in self._EvalWordPart(p, quoted=True): assert isinstance(part_val, runtime.part_value), (p, part_val) if part_val.tag == part_value_e.StringPartValue: frag_arrays[-1].append(part_val.s) else: for i, s in enumerate(part_val.strs): if i == 0: frag_arrays[-1].append(s) else: frag_arrays.append([s]) #log('frag_arrays %s', frag_arrays) strs = [] for frag_array in frag_arrays: # "${empty[@]}" leads to [[]], should eval to [] and not [''] if frag_array: strs.append(''.join(frag_array)) # This should be able to evaluate to EMPTY ARRAY! #log('strs %s', strs) if len(strs) == 1: val = runtime.StringPartValue(strs[0], False, False) else: val = runtime.ArrayPartValue(strs) return val
def _ValueToPartValue(val, quoted): """Helper for VarSub evaluation.""" assert isinstance(val, runtime.value), val if val.tag == value_e.Undef: return runtime.UndefPartValue() elif val.tag == value_e.Str: return runtime.StringPartValue(val.s, not quoted, not quoted) elif val.tag == value_e.StrArray: return runtime.ArrayPartValue(val.strs) else: raise AssertionError
def _ValueToPartValue(val, quoted): """Helper for VarSub evaluation. Called by _EvalBracedVarSub and __EvalWordPart for SimpleVarSub. """ assert isinstance(val, runtime.value), val if val.tag == value_e.Str: return runtime.StringPartValue(val.s, not quoted) elif val.tag == value_e.StrArray: return runtime.ArrayPartValue(val.strs) else: # Undef should be caught by _EmptyStrOrError(). raise AssertionError
def _EvalCommandSub(self, node, quoted): p = self.ex._GetProcessForNode(node) # NOTE: We could do an optimization for pipelines. Pick the last # process element, and do pi.procs[-1].CaptureOutput() stdout = [] p.CaptureOutput(stdout) status = p.Run() # Runtime errors: # what if the command sub was "echo foo > $@". That is invalid. Then # Return false here. How do we get that value from the Process then? Do # we use a special return value? # I think $() does a strip basically? # argv $(echo ' hi')$(echo bye) -> hibye s = ''.join(stdout).strip() return runtime.StringPartValue(s, not quoted, not quoted)
def _EvalCommandSub(self, node, quoted): p = self.ex._GetProcessForNode(node) # NOTE: We could do an optimization for pipelines. Pick the last # process element, and do pi.procs[-1].CaptureOutput() stdout = [] p.CaptureOutput(stdout) status = p.Run() # TODO: Add context if self.ex.exec_opts.errexit and status != 0: e_die('Command sub exited with status %d (%r)', status, node.__class__.__name__) # Runtime errors: # what if the command sub was "echo foo > $@". That is invalid. Then # Return false here. How do we get that value from the Process then? Do # we use a special return value? # Why rstrip()? # https://unix.stackexchange.com/questions/17747/why-does-shell-command-substitution-gobble-up-a-trailing-newline-char s = ''.join(stdout).rstrip('\n') return runtime.StringPartValue(s, not quoted, not quoted)
def _EvalProcessSub(self, node, id_): return runtime.StringPartValue('__PROCESS_SUB_NOT_EXECUTED__', False)
def _EvalCommandSub(self, node, quoted): return runtime.StringPartValue('__COMMAND_SUB_NOT_EXECUTED__', not quoted)
def _EvalProcessSub(self, node, id_): dev_path = self.ex.RunProcessSub(node, id_) return runtime.StringPartValue(dev_path, False) # no split or glob
def _EvalCommandSub(self, node, quoted): stdout = self.ex.RunCommandSub(node) return runtime.StringPartValue(stdout, not quoted)
def _EvalWordPart(self, part, part_vals, quoted=False): """Evaluate a word part. Args: part_vals: Output parameter. Returns: None """ if part.tag == word_part_e.ArrayLiteralPart: raise AssertionError( 'Array literal should have been handled at word level') elif part.tag == word_part_e.LiteralPart: v = runtime.StringPartValue(part.token.val, not quoted) part_vals.append(v) elif part.tag == word_part_e.EscapedLiteralPart: val = part.token.val assert len(val) == 2, val # e.g. \* assert val[0] == '\\' s = val[1] v = runtime.StringPartValue(s, False) part_vals.append(v) elif part.tag == word_part_e.EmptyPart: part_vals.append(runtime.StringPartValue('', False)) elif part.tag == word_part_e.SingleQuotedPart: if part.left.id == Id.Left_SingleQuote: s = ''.join(t.val for t in part.tokens) elif part.left.id == Id.Left_DollarSingleQuote: # NOTE: This could be done at compile time s = ''.join(word_compile.EvalCStringToken(t.id, t.val) for t in part.tokens) else: raise AssertionError(part.left.id) v = runtime.StringPartValue(s, False) part_vals.append(v) elif part.tag == word_part_e.DoubleQuotedPart: self._EvalDoubleQuotedPart(part, part_vals) elif part.tag == word_part_e.CommandSubPart: id_ = part.left_token.id if id_ in (Id.Left_CommandSub, Id.Left_Backtick): v = self._EvalCommandSub(part.command_list, quoted) elif id_ in (Id.Left_ProcSubIn, Id.Left_ProcSubOut): v = self._EvalProcessSub(part.command_list, id_) else: raise AssertionError(id_) part_vals.append(v) elif part.tag == word_part_e.SimpleVarSub: decay_array = False # 1. Evaluate from (var_name, var_num, token) -> defined, value if part.token.id == Id.VSub_Name: var_name = part.token.val[1:] val = self.mem.GetVar(var_name) elif part.token.id == Id.VSub_Number: var_num = int(part.token.val[1:]) val = self._EvalVarNum(var_num) else: val, decay_array = self._EvalSpecialVar(part.token.id, quoted) #log('SIMPLE %s', part) val = self._EmptyStrOrError(val, token=part.token) if decay_array: val = self._DecayArray(val) v = _ValueToPartValue(val, quoted) part_vals.append(v) elif part.tag == word_part_e.BracedVarSub: self._EvalBracedVarSub(part, part_vals, quoted) elif part.tag == word_part_e.TildeSubPart: # We never parse a quoted string into a TildeSubPart. assert not quoted s = self._EvalTildeSub(part.prefix) v = runtime.StringPartValue(s, False) part_vals.append(v) elif part.tag == word_part_e.ArithSubPart: num = self.arith_ev.Eval(part.anode) v = runtime.StringPartValue(str(num), False) part_vals.append(v) else: raise AssertionError(part.__class__.__name__)
def _EvalWordPart(self, part, quoted=False): """Evaluate a word part. Returns: A LIST of part_value, rather than just a single part_value, because of the quirk where ${a:-'x'y} is a single WordPart, but yields two part_values. """ if part.tag == word_part_e.ArrayLiteralPart: raise AssertionError( 'Array literal should have been handled at word level') elif part.tag == word_part_e.LiteralPart: s = part.token.val do_split_elide = not quoted do_glob = True return [runtime.StringPartValue(s, do_split_elide, do_glob)] elif part.tag == word_part_e.EscapedLiteralPart: val = part.token.val assert len(val) == 2, val # e.g. \* assert val[0] == '\\' s = val[1] return [runtime.StringPartValue(s, False, False)] elif part.tag == word_part_e.SingleQuotedPart: s = ''.join(t.val for t in part.tokens) return [runtime.StringPartValue(s, False, False)] elif part.tag == word_part_e.DoubleQuotedPart: return [self._EvalDoubleQuotedPart(part)] elif part.tag == word_part_e.CommandSubPart: if part.left_token.id not in (Id.Left_CommandSub, Id.Left_Backtick): # TODO: If token is Id.Left_ProcSubIn or Id.Left_ProcSubOut, we have to # supply something like /dev/fd/63. raise NotImplementedError(part.left_token.id) return [self._EvalCommandSub(part.command_list, quoted)] elif part.tag == word_part_e.SimpleVarSub: decay_array = False # 1. Evaluate from (var_name, var_num, token) -> defined, value if part.token.id == Id.VSub_Name: var_name = part.token.val[1:] val = self.mem.GetVar(var_name) elif part.token.id == Id.VSub_Number: var_num = int(part.token.val[1:]) val = self._EvalVarNum(var_num) else: val, decay_array = self._EvalSpecialVar(part.token.id, quoted) #log('SIMPLE %s', part) val = self._EmptyStrOrError(val, token=part.token) if decay_array: val = self._DecayArray(val) part_val = _ValueToPartValue(val, quoted) return [part_val] elif part.tag == word_part_e.BracedVarSub: return self._EvalBracedVarSub(part, quoted) elif part.tag == word_part_e.TildeSubPart: # We never parse a quoted string into a TildeSubPart. assert not quoted s = self._EvalTildeSub(part.prefix) return [runtime.StringPartValue(s, False, False)] elif part.tag == word_part_e.ArithSubPart: num = self.arith_ev.Eval(part.anode) return [runtime.StringPartValue(str(num), True, True)] else: raise AssertionError(part.tag)