class Lemon::SourceParser

Attributes

options[RW]
parser[RW]
scopes[RW]

Public Class Methods

new(options = {}) click to toggle source

Each instance of SourceParser accumulates scopes with each parse, making it easy to parse an entire project in chunks but more difficult to parse disparate files in one go. Create separate instances for separate global scopes.

Returns an instance of SourceParser.

# File lib/lemon/coverage/source_parser.rb, line 40
def initialize(options = {})
  @options = {}
  @scopes  = {}
  #@parser  = RubyParser.new
end
parse(text) click to toggle source

Converts Ruby code into a data structure.

text - A String of Ruby code.

Returns a Hash with each key a namespace and each value another Hash or Scope.

# File lib/lemon/coverage/source_parser.rb, line 13
def self.parse(text)
  new.parse(text)
end
parse_units(text) click to toggle source

Converts Ruby code into a data structure.

text - A String of Ruby code.

Returns an Array of Snapshot::Unit objects.

# File lib/lemon/coverage/source_parser.rb, line 22
def self.parse_units(text)
  sp = new
  sp.parse(text)
  sp.units
end

Public Instance Methods

args_for_node(node) click to toggle source

Given a method sexp, returns an array of the args.

# File lib/lemon/coverage/source_parser.rb, line 161
def args_for_node(node)
  Array(node)[1..-1].select{ |arg| arg.is_a? Symbol }
end
parse(text) click to toggle source

Converts Ruby code into a data structure. Note that at the instance level scopes accumulate, which makes it easy to parse multiple files in a single project but harder to parse files that have no connection.

text - A String of Ruby code.

Examples

@parser = SourceParser.new
files.each do |file|
  @parser.parse(File.read(file))
end
pp @parser.scopes

Returns a Hash with each key a namespace and each value another Hash or Scope.

# File lib/lemon/coverage/source_parser.rb, line 68
def parse(text)
  process(tokenize(sexp(text)))
  @scopes
end
process(ast, scope=nil) click to toggle source

Converts a tokenized Array of classes, modules, and methods into Scopes and Methods, adding them to the @scopes instance variable as it works.

ast - Tokenized Array produced by calling `tokenize`. scope - An optional Scope object for nested classes or modules.

Returns nothing.

# File lib/lemon/coverage/source_parser.rb, line 91
def process(ast, scope=nil)
  case Array(ast)[0]
  when :module, :class
    name = ast[1]
    new_scope = Scope.new(name, ast[2])

    if scope
      new_scope.parent = scope
      scope.scopes[name] = new_scope
    elsif @scopes[name]
      new_scope = @scopes[name]
    else
      @scopes[name] = new_scope
    end

    process(ast[3], new_scope)
  when :imethod
    ast.shift
    scope.instance_methods << Method.new(*ast)
  when :cmethod
    ast.shift
    scope.class_methods << Method.new(*ast)
  when Array
    ast.map { |a| process(a, scope) }
  end
end
reset() click to toggle source

Resets the state of the parser to a pristine one. Maintains options.

Returns nothing.

# File lib/lemon/coverage/source_parser.rb, line 49
def reset
  initialize(@options)
end
sexp(text) click to toggle source

Converts Ruby sourcecode into an AST.

text - A String of Ruby source.

Returns a Sexp representing the AST.

# File lib/lemon/coverage/source_parser.rb, line 78
def sexp(text)
  Ripper.sexp(text)
  #@parser.parse(text)
end
tokenize(node) click to toggle source

Converts a Ruby AST-style Sexp into an Array of more useful tokens.

node - A Ruby AST Sexp or Array

Examples

[:module, :Math, "",
  [:class, :Multiplexer, "# Class Comment",
    [:cmethod,
      :multiplex, "# Class Method Comment", [:text]],
    [:imethod,
      :multiplex, "# Instance Method Comment", [:text, :count]]]]

# In others words:
# [ :type, :name, :comment, other ]

Returns an Array in the above format.

# File lib/lemon/coverage/source_parser.rb, line 135
def tokenize(node)
  case Array(node)[0]
  when :module
    name = node[1][1][1]
    [ :module, name, '', tokenize(node[2]) ]
  when :class
    name = node[1][1][1]
    [ :class, name, '', tokenize(node[3]) ]
  when :def
    name = node[1][1]
    args = args_for_node(node[2])
    [ :imethod, name, '', args ]
  when :defs
    name = node[3][1]
    args = args_for_node(node[4])
    [ :cmethod, name, '', args ]
  when :block
    tokenize(node[1..-1])
  when :program, :bodystmt, :scope
    tokenize(node[1])
  when Array
    node.map { |n| tokenize(n) }.compact
  end
end
units() click to toggle source
# File lib/lemon/coverage/source_parser.rb, line 166
def units
  list = []
  @scopes.each do |name, scope|
    list.concat(scope.to_units)
  end
  list
end