def testVarOps(self): ev = InitEvaluator() # initializes x=xxx and y=yyy unset_sub = braced_var_sub(Tok(Id.VSub_Name, 'unset')) part_vals = [] ev._EvalWordPart(unset_sub, part_vals) print(part_vals) set_sub = braced_var_sub(Tok(Id.VSub_Name, 'x')) part_vals = [] ev._EvalWordPart(set_sub, part_vals) print(part_vals) # Now add some ops part = Tok(Id.Lit_Chars, 'default') arg_word = compound_word([part]) test_op = suffix_op.Unary(Id.VTest_ColonHyphen, arg_word) unset_sub.suffix_op = test_op set_sub.suffix_op = test_op part_vals = [] ev._EvalWordPart(unset_sub, part_vals) print(part_vals) part_vals = [] ev._EvalWordPart(set_sub, part_vals) print(part_vals)
def _ParseVarExpr(self, arg_lex_mode, allow_query=False): # type: (lex_mode_t, bool) -> braced_var_sub """ Start parsing at the op -- we already skipped past the name. """ part = self._ParseVarOf() self._Peek() if self.token_type == Id.Right_DollarBrace: return part # no ops op_kind = self.token_kind if op_kind == Kind.VTest: op_id = self.token_type arg_word = self._ReadVarOpArg(arg_lex_mode) if self.token_type != Id.Right_DollarBrace: p_die('Expected } to close ${', token=self.cur_token) part.suffix_op = suffix_op.Unary(op_id, arg_word) elif op_kind == Kind.VOp0: part.suffix_op = self.cur_token # Nullary self._Next(lex_mode_e.VSub_2) # Expecting } self._Peek() elif op_kind == Kind.VOp1: # % %% # ## etc. op_id = self.token_type # Weird exception that all shells have: these operators take a glob # pattern, so they're lexed as VSub_ArgUnquoted, not VSub_ArgDQ arg_word = self._ReadVarOpArg(lex_mode_e.VSub_ArgUnquoted) if self.token_type != Id.Right_DollarBrace: p_die('Expected } to close ${', token=self.cur_token) part.suffix_op = suffix_op.Unary(op_id, arg_word) elif op_kind == Kind.VOp2: # / : [ ] if self.token_type == Id.VOp2_Slash: op_spid = self.cur_token.span_id # for attributing error to / patsub_op = self._ReadPatSubVarOp() patsub_op.spids.append(op_spid) # awkwardness for mycpp; could fix temp = cast(suffix_op_t, patsub_op) part.suffix_op = temp # Checked by the method above assert self.token_type == Id.Right_DollarBrace, self.cur_token elif self.token_type == Id.VOp2_Colon: part.suffix_op = self._ReadSliceVarOp() # NOTE: } in arithmetic mode. if self.token_type != Id.Arith_RBrace: # Token seems off; doesn't point to X in # ${a:1:2 X p_die('Expected } to close ${', token=self.cur_token) else: # TODO: Does this ever happen? p_die('Unexpected token in ${} (%s)', 'VOp2', token=self.cur_token) elif op_kind == Kind.VOp3: # ${prefix@} etc. if allow_query: part.suffix_op = self.cur_token # Nullary self._Next(lex_mode_e.VSub_2) # Expecting } self._Peek() else: p_die("Unexpected token in ${} (%s)", 'VOp3', token=self.cur_token) # NOTE: Arith_RBrace is for slicing, because it reads } in arithmetic # mode. It's redundantly checked above. if self.token_type not in (Id.Right_DollarBrace, Id.Arith_RBrace): # ${a.} or ${!a.} p_die('Expected } to close ${', token=self.cur_token) # Now look for ops return part
def _ParseVarExpr(self, arg_lex_mode): # type: (lex_mode_t) -> braced_var_sub """ Start parsing at the op -- we already skipped past the name. """ part = self._ParseVarOf() self._Peek() if self.token_type == Id.Right_DollarBrace: return part # no ops op_kind = self.token_kind if op_kind == Kind.VTest: op_id = self.token_type arg_word = self._ReadVarOpArg(arg_lex_mode) if self.token_type != Id.Right_DollarBrace: p_die('Unexpected token (after VTest): %r', self.cur_token.val, token=self.cur_token) part.suffix_op = suffix_op.Unary(op_id, arg_word) elif op_kind == Kind.VOp0: op_id = self.token_type part.suffix_op = suffix_op.Nullary(op_id) self._Next(lex_mode_e.VSub_2) # Expecting } self._Peek() elif op_kind == Kind.VOp1: op_id = self.token_type arg_word = self._ReadVarOpArg(arg_lex_mode) if self.token_type != Id.Right_DollarBrace: p_die('Unexpected token (after VOp1): %r', self.cur_token.val, token=self.cur_token) part.suffix_op = suffix_op.Unary(op_id, arg_word) elif op_kind == Kind.VOp2: if self.token_type == Id.VOp2_Slash: op_spid = self.cur_token.span_id # for attributing error to / # TODO: op_temp is only necessary for MyPy. It can be removed when # 'spids' are put on the base class suffix_op_t. op_temp = self._ReadPatSubVarOp(arg_lex_mode) op_temp.spids.append(op_spid) op = cast(suffix_op_t, op_temp) # for MyPy # Checked by the method above assert self.token_type == Id.Right_DollarBrace, self.cur_token elif self.token_type == Id.VOp2_Colon: op = self._ReadSliceVarOp() # NOTE: } in arithmetic mode. if self.token_type != Id.Arith_RBrace: # Token seems off; doesn't point to X in # ${a:1:2 X p_die('Unexpected token after slice: %r', self.cur_token.val, token=self.cur_token) else: p_die('Unexpected token %r', self.cur_token.val, token=self.cur_token) part.suffix_op = op # NOTE: Arith_RBrace is for slicing, because it reads } in arithmetic # mode. It's redundantly checked above. if self.token_type not in (Id.Right_DollarBrace, Id.Arith_RBrace): # ${a.} or ${!a.} p_die('Expected } after var sub, got %r', self.cur_token.val, token=self.cur_token) # Now look for ops return part