Class: YARD::Parser::Ruby::Legacy::StatementList
Relationships & Source Files | |
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
self,
::Array
|
|
Instance Chain:
|
|
Inherits: |
Array
|
Defined in: | lib/yard/parser/ruby/legacy/statement_list.rb |
Constant Summary
-
OPEN_BLOCK_TOKENS =
The following list of tokens will require a block to be opened if used at the beginning of a statement.
[TkCLASS, TkDEF, TkMODULE, TkUNTIL, TkIF, TkELSIF, TkUNLESS, TkWHILE, TkFOR, TkCASE]
RubyToken
- Included
EXPR_ARG, EXPR_BEG, EXPR_CLASS, EXPR_DOT, EXPR_END, EXPR_FNAME, EXPR_MID, NEWLINE_TOKEN, TkReading2Token, TkSymbol2Token, TokenDefinitions
Class Method Summary
-
.new(content) ⇒ StatementList
constructor
Creates a new statement list.
Instance Attribute Summary
- #encoding_line rw
- #shebang_line rw
Instance Method Summary
-
#balances?(tk) ⇒ Boolean
private
Handles the balancing of parentheses and blocks.
-
#next_statement ⇒ Statement
private
Returns the next statement in the token stream.
- #parse_statements private
-
#peek_no_space ⇒ RubyToken::Token
private
Returns the next token in the stream that's not a space.
-
#process_block_token(tk)
private
Processes a token in a block.
-
#process_complex_block_opener(tk)
private
Processes a complex block-opening token; that is, a block opener such as
while
orfor
that is followed by an expression. -
#process_initial_comment(tk) ⇒ Boolean
private
Processes a comment token that comes before a statement.
-
#process_simple_block_opener(tk)
private
Processes a simple block-opening token; that is, a block opener such as
begin
ordo
that isn't followed by an expression. -
#process_statement_end(tk)
private
Processes a token that closes a statement.
-
#process_token(tk)
private
Processes a single token.
-
#push_token(tk)
private
Adds a token to the current statement, unless it's a newline, semicolon, or comment.
- #sanitize_block private
- #sanitize_statement_end private
RubyToken
- Included
::Array
- Inherited
#place | Places values before or after another object (by value) in an array. |
Constructor Details
.new(content) ⇒ StatementList
Creates a new statement list
# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 17
def initialize(content) @shebang_line = nil @encoding_line = nil @comments_last_line = nil if content.is_a? TokenList @tokens = content.dup elsif content.is_a? String @tokens = TokenList.new(content.delete("\r")) else raise ArgumentError, "Invalid content for StatementList: #{content.inspect}:#{content.class}" end parse_statements end
Instance Attribute Details
#encoding_line (rw)
[ GitHub ]# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 7
attr_accessor :shebang_line, :encoding_line
#shebang_line (rw)
[ GitHub ]# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 7
attr_accessor :shebang_line, :encoding_line
Instance Method Details
#balances?(tk) ⇒ Boolean
(private)
Handles the balancing of parentheses and blocks
# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 362
def balances?(tk) unless [TkALIAS, TkDEF].include?(@last_ns_tk.class) || @before_last_ns_tk.class == TkALIAS if [TkLPAREN, TkLBRACK, TkLBRACE, TkDO, TkBEGIN].include?(tk.class) @level += 1 elsif OPEN_BLOCK_TOKENS.include?(tk.class) @level += 1 unless tk.class == TkELSIF elsif [TkRPAREN, TkRBRACK, TkRBRACE, TkEND].include?(tk.class) && @level > 0 @level -= 1 end end @level == 0 end
#next_statement ⇒ Statement (private)
Returns the next statement in the token stream
# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 45
def next_statement @state = :first_statement @statement_stack = [] @level = 0 @block_num = 0 @done = false @current_block = nil @comments_line = nil @comments_hash_flag = nil @statement = TokenList.new @block = nil @comments = nil @last_tk = nil @last_ns_tk = nil @before_last_tk = nil @before_last_ns_tk = nil @first_line = nil until @done tk = @tokens.shift break if tk.nil? process_token(tk) @before_last_tk = @last_tk @last_tk = tk # Save last token unless [TkSPACE, TkNL, TkEND_OF_SCRIPT].include? tk.class @before_last_ns_tk = @last_ns_tk @last_ns_tk = tk end end # Return the code block with starting token and initial comments # If there is no code in the block, return nil @comments = @comments.compact if @comments if @block || !@statement.empty? sanitize_statement_end sanitize_block @statement.pop if [TkNL, TkSPACE, TkSEMICOLON].include?(@statement.last.class) stmt = Statement.new(@statement, @block, @comments) if @comments && @comments_line stmt.comments_range = (@comments_line..(@comments_line + @comments.size - 1)) stmt.comments_hash_flag = @comments_hash_flag end stmt elsif @comments @statement << TkCOMMENT.new(@comments_line, 0) @statement.first.set_text("# " + @comments.join("\n# ")) Statement.new(@statement, nil, @comments) end end
#parse_statements (private)
[ GitHub ]# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 34
def parse_statements loop do stmt = next_statement break if stmt.nil? self << stmt end end
#peek_no_space ⇒ RubyToken::Token (private)
Returns the next token in the stream that's not a space
# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 388
def peek_no_space return @tokens.first unless @tokens.first.class == TkSPACE @tokens[1] end
#process_block_token(tk) (private)
Processes a token in a block
# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 194
def process_block_token(tk) if balances?(tk) @statement << tk @state = :first_statement process_statement_end(tk) elsif @block_num > 1 || (@block.empty? && [TkSPACE, TkNL].include?(tk.class)) @statement << tk else if @block.empty? @statement << TkBlockContents.new(tk.line_no, tk.char_no) end @block << tk end end
#process_complex_block_opener(tk) (private)
Processes a complex block-opening token;
that is, a block opener such as while
or for
that is followed by an expression
# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 293
def process_complex_block_opener(tk) return unless OPEN_BLOCK_TOKENS.include?(tk.class) @current_block = tk.class @state = :block_statement true end
#process_initial_comment(tk) ⇒ Boolean
(private)
Processes a comment token that comes before a statement
# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 213
def process_initial_comment(tk) if @statement.empty? && (@comments_last_line || 0) < tk.line_no - 2 @comments = nil end return unless tk.class == TkCOMMENT case tk.text when Parser::SourceParser::SHEBANG_LINE if !@last_ns_tk && !@encoding_line @shebang_line = tk.text return end when Parser::SourceParser::ENCODING_LINE if (@last_ns_tk.class == TkCOMMENT && @last_ns_tk.text == @shebang_line) || !@last_ns_tk @encoding_line = tk.text return end end return if !@statement.empty? && @comments return if @first_line && tk.line_no > @first_line if @comments_last_line && @comments_last_line < tk.line_no - 1 if @comments && @statement.empty? @tokens.unshift(tk) return @done = true end @comments = nil end @comments_line = tk.line_no unless @comments # Remove the "#" and up to 1 space before the text # Since, of course, the convention is to have "# text" # and not "#text", which I deem ugly (you heard it here first) @comments ||= [] if tk.text.start_with?('=begin') lines = tk.text.count("\n") @comments += tk.text.gsub(/\A=begin.*\r?\n|\r?\n=end.*\r?\n?\Z/, '').split(/\r?\n/) @comments_last_line = tk.line_no + lines else @comments << tk.text.gsub(/^(#+)\s{0,1}/, '') @comments_hash_flag = $1 == '##' if @comments_hash_flag.nil? @comments_last_line = tk.line_no end @comments.pop if @comments.size == 1 && @comments.first =~ /^\s*$/ true end
#process_simple_block_opener(tk) (private)
Processes a simple block-opening token;
that is, a block opener such as begin
or do
that isn't followed by an expression
# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 268
def process_simple_block_opener(tk) return unless [TkLBRACE, TkDO, TkBEGIN, TkELSE].include?(tk.class) && # Make sure hashes are parsed as hashes, not as blocks (@last_ns_tk.nil? || @last_ns_tk.lex_state != EXPR_BEG) @level += 1 @state = :block @block_num += 1 if @block.nil? @block = TokenList.new tokens = [tk, TkStatementEnd.new(tk.line_no, tk.char_no)] tokens = tokens.reverse if TkBEGIN === tk.class @statement.concat(tokens) else @statement << tk end true end
#process_statement_end(tk) (private)
Processes a token that closes a statement
# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 305
def process_statement_end(tk) # Whitespace means that we keep the same value of @new_statement as last token return if tk.class == TkSPACE return unless # We might be coming after a statement-ending token... (@last_tk && [TkSEMICOLON, TkNL, TkEND_OF_SCRIPT].include?(tk.class)) || # Or we might be at the beginning of an argument list (@current_block == TkDEF && tk.class == TkRPAREN) # Continue line ending on . or :: return if @last_tk && [EXPR_DOT].include?(@last_tk.lex_state) # Continue a possible existing new statement unless we just finished an expression... return unless (@last_tk && [EXPR_END, EXPR_ARG].include?(@last_tk.lex_state)) || # Or we've opened a block and are ready to move into the body (@current_block && [TkNL, TkSEMICOLON].include?(tk.class) && # Handle the case where the block statement's expression is on the next line # # while # foo # end @last_ns_tk.class != @current_block && # And the case where part of the expression is on the next line # # while foo || # bar # end @last_tk.lex_state != EXPR_BEG) # Continue with the statement if we've hit a comma in a def return if @current_block == TkDEF && peek_no_space.class == TkCOMMA if [TkEND_OF_SCRIPT, TkNL, TkSEMICOLON].include?(tk.class) && @state == :block_statement && [TkRBRACE, TkEND].include?(@last_ns_tk.class) && @level == 0 @current_block = nil end unless @current_block @done = true return end @state = :pre_block @level += 1 @block_num += 1 unless @block @block = TokenList.new @statement << TkStatementEnd.new(tk.line_no, tk.char_no) end end
#process_token(tk) (private)
Processes a single token
# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 130
def process_token(tk) # p tk.class, tk.text, @state, @level, @current_block, "<br/>" case @state when :first_statement return if process_initial_comment(tk) return if @statement.empty? && [TkSPACE, TkNL, TkCOMMENT].include?(tk.class) @comments_last_line = nil if @statement.empty? && tk.class == TkALIAS @state = :alias_statement @alias_values = [] push_token(tk) return end return if process_simple_block_opener(tk) push_token(tk) return if process_complex_block_opener(tk) if balances?(tk) process_statement_end(tk) else @state = :balance end when :alias_statement push_token(tk) @alias_values << tk unless [TkSPACE, TkNL, TkCOMMENT].include?(tk.class) if @alias_values.size == 2 @state = :first_statement if [NilClass, TkNL, TkEND_OF_SCRIPT, TkSEMICOLON].include?(peek_no_space.class) @done = true end end when :balance @statement << tk return unless balances?(tk) @state = :first_statement process_statement_end(tk) when :block_statement push_token(tk) return unless balances?(tk) process_statement_end(tk) when :pre_block @current_block = nil process_block_token(tk) unless tk.class == TkSEMICOLON @state = :block when :block process_block_token(tk) when :post_block if tk.class == TkSPACE @statement << tk return end process_statement_end(tk) @state = :block end if @first_line == tk.line_no && !@statement.empty? && TkCOMMENT === tk process_initial_comment(tk) end end
#push_token(tk) (private)
Adds a token to the current statement, unless it's a newline, semicolon, or comment
# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 380
def push_token(tk) @first_line = tk.line_no if @statement.empty? @statement << tk unless @level == 0 && [TkCOMMENT].include?(tk.class) end
#sanitize_block (private)
[ GitHub ]# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 111
def sanitize_block return unless @block extra = [] while [TkSPACE, TkNL, TkSEMICOLON].include?(@block.last.class) next(@block.pop) if TkSEMICOLON === @block.last extra.unshift(@block.pop) end @statement.each_with_index do |token, index| if TkBlockContents === token @statement[index, 1] = [token, *extra] break end end end
#sanitize_statement_end (private)
[ GitHub ]# File 'lib/yard/parser/ruby/legacy/statement_list.rb', line 96
def sanitize_statement_end extra = [] (@statement.size - 1).downto(0) do |index| token = @statement[index] next unless TkStatementEnd === token while [TkNL, TkSPACE, TkSEMICOLON].include?(@statement[index - 1].class) extra.unshift(@statement.delete_at(index - 1)) index -= 1 end @statement.insert(index + 1, *extra) break end end