123456789_123456789_123456789_123456789_123456789_

Class: YARD::Parser::C::CParser

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
Instance Chain:
Inherits: YARD::Parser::Base
Defined in: lib/yard/parser/c/c_parser.rb

Class Method Summary

::YARD::Parser::Base - Inherited

.new

This default constructor does nothing.

.parse

Convenience method to create a new parser and #parse

Instance Method Summary

::YARD::Parser::Base - Inherited

#enumerator

This method should be implemented to return a list of semantic tokens representing the source code to be post-processed.

#parse

This method should be implemented to parse the source and return itself.

#tokenize

This method should be implemented to tokenize given source.

Constructor Details

.new(source, file = '(stdin)') ⇒ CParser

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 6

def initialize(source, file = '(stdin)')
  @file = file
  @namespaces = {}
  @content = source
  @index = 0
  @line = 1
  @state = nil
  @newline = true
  @statements = []
  @last_comment = nil
  @last_statement = nil
end

Instance Method Details

#advance(num = 1) (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 213

def advance(num = 1) @index += num end

#advance_loop (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 216

def advance_loop
  yield while @index <= @content.size
end

#attach_comment(statement) (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 195

def attach_comment(statement)
  if Comment === statement
    if @last_statement && @last_statement.line == statement.line
      @last_statement.comments = statement
      statement.statement = @last_statement
    end
    @last_comment = statement
    @last_statement = nil
  else
    if @last_comment
      statement.comments = @last_comment
      @last_comment.statement = statement
    end
    @last_statement = statement
    @last_comment = nil
  end
end

#back(num = 1) (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 214

def back(num = 1) @index -= num end

#char(num = 1) (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 225

def char(num = 1) @content[@index, num] end

#consume_body_statements (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 96

def consume_body_statements
  stmts = []
  brace_level = 1
  loop do
    strip_non_statement_data
    start = @index
    line = @line
    consume_until(/[{};]/)
    brace_level += 1 if prevchar == '{'
    brace_level -= 1 if prevchar == '}'

    break if prevchar.empty? || (brace_level <= 0 && prevchar == '}')
    src = @content[start...@index]
    next unless src && src !~ /\A\s*\Z|\A\}\Z/

    stmt = BodyStatement.new(src, @file, line)
    attach_comment(stmt)
    stmts << stmt
  end
  stmts
end

#consume_comment(add_comment = true) (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 136

def consume_comment(add_comment = true)
  return(advance) unless nextchar == '*' || nextchar == '/'
  line = @line
  type = nextchar == '*' ? :multi : :line
  advance(2)
  comment = String.new("")
  advance_loop do
    comment << char
    if type == :multi
      nextline if char == "\n"
      if char(2) == '*/'
        if add_comment
          comment << '/'
          stmt = Comment.new(comment, @file, line)
          stmt.type = type
          attach_comment(stmt)
          @statements << stmt
        end
        return advance(2)
      end
    elsif char == "\n"
      if add_comment
        stmt = Comment.new(comment[0...-1], @file, line)
        stmt.type = type
        attach_comment(stmt)
        @statements << stmt
      end
      return
    end
    advance
  end
end

#consume_directive (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 59

def consume_directive
  return(advance) unless @newline
  @last_comment = nil
  @last_statement = nil
  advance_loop do
    if char == '\\' && nextchar =~ /[\r\n]/
      advance_loop { advance; break(nextline) if char == "\n" }
    elsif char == "\n"
      return
    end
    advance
  end
end

#consume_quote(type = '"') (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 47

def consume_quote(type = '"')
  advance
  advance_loop do
    case char
    when "\n"; advance; nextline
    when '\\'; advance(2)
    when type; advance; return
    else advance
    end
  end
end

#consume_toplevel_statement (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 73

def consume_toplevel_statement
  @newline = false
  start = @index
  line = @line
  decl = consume_until(/[{;]/)
  return nil if decl =~ /\A\s*\Z/
  # Skip C++ namespace - treat content as top level statement.
  return nil if decl =~ /\A(namespace)/
  statement = ToplevelStatement.new(nil, @file, line)
  @statements << statement
  attach_comment(statement)
  stmts = nil
  if prevchar == '{'
    stmts = consume_body_statements
    if decl =~ /\A(typedef|enum|class|#{struct}|union)/
      consume_until(';')
    end
  end
  statement.source = @content[start..@index]
  statement.block = stmts
  statement.declaration = decl # rubocop:disable Lint/UselessSetterCall
end

#consume_until(end_char, bracket_level = 0, brace_level = 0, add_comment = true) (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 169

def consume_until(end_char, bracket_level = 0, brace_level = 0, add_comment = true)
  end_char = /#{end_char}/ if end_char.is_a?(String)
  start = @index
  advance_loop do
    chr = char
    case chr
    when /\s/; consume_whitespace
    when /['"]/; consume_quote(char)
    when '#'; consume_directive
    when '/'; consume_comment(add_comment)
    when '{'; advance; brace_level += 1
    when '}'; advance; brace_level -= 1
    when '('; advance; bracket_level += 1
    when ')'; advance; bracket_level -= 1
    else advance
    end
    @newline = false if chr !~ /\s/

    if chr =~ end_char
      break if chr == '{' || chr == '('
      break if bracket_level <= 0 && brace_level <= 0
    end
  end
  @content[start...@index]
end

#consume_whitespace (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 132

def consume_whitespace
  advance_loop { nextline if char == "\n"; break if char =~ /\S/; advance }
end

#enumerator

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 24

def enumerator
  @statements
end

#nextchar(num = 1) (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 227

def nextchar(num = 1) @content[@index + 1, num] end

#nextline (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 220

def nextline
  @line += 1
  @newline = true
end

#parse

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 19

def parse
  parse_toplevel
  enumerator
end

#parse_toplevel (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 34

def parse_toplevel
  advance_loop do
    case char
    when /['"]/; consume_quote(char)
    when '#'; consume_directive
    when '/'; consume_comment
    when /\s/; consume_whitespace
    when '}'; advance # Skip possible C++ namespace closing brackets.
    else consume_toplevel_statement
    end
  end
end

#prevchar(num = 1) (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 226

def prevchar(num = 1) @content[@index - 1, num] end

#strip_non_statement_data (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 118

def strip_non_statement_data
  start = @index
  loop do
    start = @index
    case char
    when /\s/; consume_whitespace
    when '#';  consume_directive
    when '/';  consume_comment
    end

    break if start == @index
  end
end

#struct (private)

[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 229

def struct
  /struct\s[a-zA-Z0-9]+\s\{/
end

#tokenize

Raises:

  • (NotImplementedError)
[ GitHub ]

  
# File 'lib/yard/parser/c/c_parser.rb', line 28

def tokenize
  raise NotImplementedError, "no tokenization support for C/C++ files"
end