def _Trailer(self, base, p_trailer): # type: (expr_t, PNode) -> expr_t """ trailer: ( '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME | '->' NAME | '::' NAME ) """ children = p_trailer.children op_tok = children[0].tok # TODO: Need to process ALL the trailers, e.g. f(x, y)[1, 2](x, y) if op_tok.id == Id.Op_LParen: arglist = arg_list() if len(children) == 2: # () return expr.FuncCall(base, arglist) p = children[1] # the X in ( X ) assert p.typ == grammar_nt.arglist # f(x, y) self._Arglist(p.children, arglist) return expr.FuncCall(base, arglist) if op_tok.id == Id.Op_LBracket: p_args = children[1] assert p_args.typ == grammar_nt.subscriptlist indices = [] # type: List[expr_t] n = len(p_args.children) for i in xrange(0, n, 2): # was children[::2] a = p_args.children[i] indices.append(self._Subscript(a.children)) return subscript(base, indices) if op_tok.id in (Id.Expr_Dot, Id.Expr_RArrow, Id.Expr_DColon): attr = children[1].tok # will be Id.Expr_Name return attribute(base, op_tok, attr, expr_context_e.Store) raise AssertionError(Id_str(op_tok.id))
def _ReadCompoundWord3(self, lex_mode, eof_type, empty_ok): # type: (lex_mode_t, Id_t, bool) -> compound_word """ Precondition: Looking at the first token of the first word part Postcondition: Looking at the token after, e.g. space or operator NOTE: eof_type is necessary because / is a literal, i.e. Lit_Slash, but it could be an operator delimiting a compound word. Can we change lexer modes and remove this special case? """ w = compound_word() num_parts = 0 brace_count = 0 done = False while not done: self._Peek() allow_done = empty_ok or num_parts != 0 if allow_done and self.token_type == eof_type: done = True # e.g. for ${foo//pat/replace} # Keywords like "for" are treated like literals elif self.token_kind in ( Kind.Lit, Kind.History, Kind.KW, Kind.ControlFlow, Kind.BoolUnary, Kind.BoolBinary): if self.token_type == Id.Lit_EscapedChar: part = word_part.EscapedLiteral(self.cur_token) # type: word_part_t else: part = self.cur_token if self.token_type == Id.Lit_VarLike and num_parts == 0: # foo= w.parts.append(part) # Unfortunately it's awkward to pull the check for a=(1 2) up to # _ReadWord. next_id = self.lexer.LookAhead(lex_mode) if next_id == Id.Op_LParen: self.lexer.PushHint(Id.Op_RParen, Id.Right_ShArrayLiteral) part2 = self._ReadArrayLiteral() w.parts.append(part2) # Array literal must be the last part of the word. self._Next(lex_mode) self._Peek() # EOF, whitespace, newline, Right_Subshell if self.token_kind not in KINDS_THAT_END_WORDS: p_die('Unexpected token after array literal', token=self.cur_token) done = True elif (self.parse_opts.parse_at() and self.token_type == Id.Lit_Splice and num_parts == 0): splice_token = self.cur_token next_id = self.lexer.LookAhead(lex_mode) if next_id == Id.Op_LParen: # @arrayfunc(x) arglist = arg_list() self._ParseCallArguments(arglist) part = word_part.FuncCall(splice_token, arglist) else: part = word_part.Splice(splice_token) w.parts.append(part) # @words or @arrayfunc() must be the last part of the word self._Next(lex_mode) self._Peek() # EOF, whitespace, newline, Right_Subshell if self.token_kind not in KINDS_THAT_END_WORDS: p_die('Unexpected token after array splice', token=self.cur_token) done = True else: # Syntax error for { and } if self.token_type == Id.Lit_LBrace: brace_count += 1 elif self.token_type == Id.Lit_RBrace: brace_count -= 1 # not a literal with lookahead; append it w.parts.append(part) elif self.token_kind == Kind.VSub: vsub_token = self.cur_token part = simple_var_sub(vsub_token) if self.token_type == Id.VSub_DollarName: # Look ahead for $strfunc(x) # $f(x) or --name=$f(x) is allowed # but "--name=$f(x)" not allowed? This would BREAK EXISTING CODE. # It would need a parse option. next_id = self.lexer.LookAhead(lex_mode) if next_id == Id.Op_LParen: arglist = arg_list() self._ParseCallArguments(arglist) part = word_part.FuncCall(vsub_token, arglist) # Unlike @arrayfunc(x), it makes sense to allow $f(1)$f(2) # var a = f(1); var b = f(2); echo $a$b # It's consistent with other uses of $. w.parts.append(part) elif self.token_kind == Kind.ExtGlob: part = self._ReadExtGlob() w.parts.append(part) elif self.token_kind == Kind.Left: part = self._ReadLeftParts() w.parts.append(part) # NOT done yet, will advance below elif self.token_kind == Kind.Right: # Still part of the word; will be done on the next iter. if self.token_type == Id.Right_DoubleQuote: pass # Never happens, no PushHint for this case. #elif self.token_type == Id.Right_DollarParen: # pass elif self.token_type == Id.Right_Subshell: # LEXER HACK for (case x in x) ;; esac ) # Rewind before it's used assert self.next_lex_mode == lex_mode_e.Undefined if self.lexer.MaybeUnreadOne(): self.lexer.PushHint(Id.Op_RParen, Id.Right_Subshell) self._Next(lex_mode) done = True else: done = True elif self.token_kind == Kind.Ignored: done = True else: # LEXER HACK for unbalanced case clause. 'case foo in esac' is valid, # so to test for ESAC, we can read ) before getting a chance to # PushHint(Id.Op_RParen, Id.Right_CasePat). So here we unread one # token and do it again. # We get Id.Op_RParen at top level: case x in x) ;; esac # We get Id.Eof_RParen inside ComSub: $(case x in x) ;; esac ) if self.token_type in (Id.Op_RParen, Id.Eof_RParen): # Rewind before it's used assert self.next_lex_mode == lex_mode_e.Undefined if self.lexer.MaybeUnreadOne(): if self.token_type == Id.Eof_RParen: # Redo translation self.lexer.PushHint(Id.Op_RParen, Id.Eof_RParen) self._Next(lex_mode) done = True # anything we don't recognize means we're done if not done: self._Next(lex_mode) num_parts += 1 if self.parse_opts.parse_brace() and num_parts > 1 and brace_count != 0: # accept { and }, but not foo{ p_die( 'Word has unbalanced { }. Maybe add a space or quote it like \{', word=w) return w