Class: Lemon::SourceParser

Inherits:
Object
  • Object
show all
Defined in:
lib/lemon/coverage/source_parser.rb

Defined Under Namespace

Classes: Method, Scope

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ SourceParser

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.



40
41
42
43
44
# File 'lib/lemon/coverage/source_parser.rb', line 40

def initialize(options = {})
  @options = {}
  @scopes  = {}
  #@parser  = RubyParser.new
end

Instance Attribute Details

#optionsObject

Returns the value of attribute options.



32
33
34
# File 'lib/lemon/coverage/source_parser.rb', line 32

def options
  @options
end

#parserObject

Returns the value of attribute parser.



28
29
30
# File 'lib/lemon/coverage/source_parser.rb', line 28

def parser
  @parser
end

#scopesObject

Returns the value of attribute scopes.



30
31
32
# File 'lib/lemon/coverage/source_parser.rb', line 30

def scopes
  @scopes
end

Class Method Details

.parse(text) ⇒ Object

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.



13
14
15
# File 'lib/lemon/coverage/source_parser.rb', line 13

def self.parse(text)
  new.parse(text)
end

.parse_units(text) ⇒ Object

Converts Ruby code into a data structure.

text - A String of Ruby code.

Returns an Array of Snapshot::Unit objects.



22
23
24
25
26
# File 'lib/lemon/coverage/source_parser.rb', line 22

def self.parse_units(text)
  sp = new
  sp.parse(text)
  sp.units
end

Instance Method Details

#args_for_node(node) ⇒ Object

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



161
162
163
# 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) ⇒ Object

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.



68
69
70
71
# File 'lib/lemon/coverage/source_parser.rb', line 68

def parse(text)
  process(tokenize(sexp(text)))
  @scopes
end

#process(ast, scope = nil) ⇒ Object

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.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# 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

#resetObject

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

Returns nothing.



49
50
51
# File 'lib/lemon/coverage/source_parser.rb', line 49

def reset
  initialize(@options)
end

#sexp(text) ⇒ Object

Converts Ruby sourcecode into an AST.

text - A String of Ruby source.

Returns a Sexp representing the AST.



78
79
80
81
# File 'lib/lemon/coverage/source_parser.rb', line 78

def sexp(text)
  Ripper.sexp(text)
  #@parser.parse(text)
end

#tokenize(node) ⇒ Object

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.



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# 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

#unitsObject



166
167
168
169
170
171
172
# 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