rbgccxml 0.9.1 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,17 +1,18 @@
1
- require 'rake/testtask'
2
1
  require 'rake/rdoctask'
3
2
  require 'rake/contrib/sshpublisher'
4
3
  require 'rake/gempackagetask'
5
4
 
5
+ require 'rspec/core/rake_task'
6
+
6
7
  PROJECT_NAME = "rbgccxml"
7
- RBGCCXML_VERSION = "0.9.1"
8
+ RBGCCXML_VERSION = "1.0"
8
9
 
9
- task :default => :test
10
+ task :default => :spec
10
11
 
11
- Rake::TestTask.new do |t|
12
- t.libs = ["lib", "test"]
13
- t.test_files = FileList["test/*_test.rb"]
14
- t.verbose = true
12
+ desc "Run all specs"
13
+ RSpec::Core::RakeTask.new do |t|
14
+ t.ruby_opts = ["-Itest"]
15
+ t.pattern = "test/**/*.rb"
15
16
  end
16
17
 
17
18
  Rake::RDocTask.new do |rd|
@@ -58,9 +59,6 @@ spec = Gem::Specification.new do |s|
58
59
  s.add_dependency "nokogiri", "~> 1.4.0"
59
60
  s.add_dependency "gccxml_gem", "~> 0.9"
60
61
 
61
- s.add_development_dependency "test-unit", "1.2.3"
62
- s.add_development_dependency "mocha", "~> 0.9"
63
-
64
62
  s.description = <<-END
65
63
  Rbgccxml is a library that parses out GCCXML (http://www.gccxml.org) output
66
64
  and provides a simple but very powerful querying API for finding exactly
data/lib/rbgccxml/node.rb CHANGED
@@ -13,61 +13,79 @@ module RbGCCXML
13
13
  # attempt to will throw a NotQueryableException.
14
14
  class Node
15
15
 
16
- # The underlying xml node for this Node.
17
- attr_reader :node
16
+ # GCC_XML id of this node
17
+ attr_accessor :id
18
18
 
19
- # Initialize this node according to the XML element passed in
19
+ # The C++ scoping parent of this node
20
+ attr_accessor :parent
21
+
22
+ # Any children this node has
23
+ attr_accessor :children
24
+
25
+ # The base name of this node
26
+ attr_accessor :name
27
+
28
+ # Hash of all the attributes
29
+ attr_accessor :attributes
30
+
31
+ # HACK A linking ivar that lets types figure out where they sit,
32
+ # for example an argument type can find the <Argument> it's
33
+ # a part of.
34
+ attr_accessor :container
35
+
36
+ # Initialize this node according to the attributes passed in
20
37
  # Only to be used internally. Use query methods on the object
21
38
  # returned by RbGCCXML::parse
22
- def initialize(node)
23
- @node = node
24
- end
25
- protected :initialize
39
+ def initialize(attributes)
40
+ @id = attributes["id"]
41
+ @name = attributes["name"]
42
+ @demangled = attributes["demangled"]
26
43
 
27
- # Get the C++ name of this node
28
- def name
29
- @node['name']
44
+ @attributes = attributes
45
+
46
+ @children = []
30
47
  end
31
48
 
32
49
  # Get the fully qualified (demangled) C++ name of this node.
33
50
  def qualified_name
34
- if @node["demangled"]
51
+ if @demangled
35
52
  # The 'demangled' attribute of the node for methods / functions is the
36
53
  # full signature, so cut that part out.
37
- @node["demangled"].split(/\(/)[0]
54
+ @demangled.split(/\(/)[0]
38
55
  else
39
- parent ? "#{parent.qualified_name}::#{name}" : name
56
+ @parent ? "#{@parent.qualified_name}::#{@name}" : @name
40
57
  end
41
58
  end
59
+ once :qualified_name
42
60
 
43
61
  # Is this node const qualified?
44
62
  def const?
45
- @node["const"] ? @node["const"] == "1" : false
63
+ @attributes["const"] ? @attributes["const"] == "1" : false
46
64
  end
47
65
 
48
66
  # Does this node have public access?
49
67
  def public?
50
- @node["access"] ? @node["access"] == "public" : true
68
+ @attributes["access"] ? @attributes["access"] == "public" : true
51
69
  end
52
70
 
53
71
  # Does this node have protected access?
54
72
  def protected?
55
- @node["access"] ? @node["access"] == "protected" : false
73
+ @attributes["access"] ? @attributes["access"] == "protected" : false
56
74
  end
57
75
 
58
76
  # Does this node have private access?
59
77
  def private?
60
- @node["access"] ? @node["access"] == "private" : false
78
+ @attributes["access"] ? @attributes["access"] == "private" : false
61
79
  end
62
80
 
63
- # Access to the underlying xml node's attributes.
64
- def attributes
65
- @node
81
+ # Is this node automatically generated by gcc?
82
+ def artificial?
83
+ @attributes["artificial"] ? @attributes["artificial"] == "1" : false
66
84
  end
67
85
 
68
86
  # Access indivitual attributes directly
69
87
  def [](val)
70
- @node[val]
88
+ @attributes[val]
71
89
  end
72
90
 
73
91
  # Some C++ nodes are actually wrappers around other nodes. For example,
@@ -83,90 +101,68 @@ module RbGCCXML
83
101
  # Returns the full path to the file this node is found in.
84
102
  # Returns nil if no File node is found for this node
85
103
  def file
86
- file_id = @node["file"]
87
- file_node = XMLParsing.find(:node_type => "File", :id => file_id) if file_id
88
- file_node ? file_node.attributes["name"] : nil
89
- end
90
-
91
- # Returns the parent node of this node. e.g. function.parent will get the class
92
- # the function is contained in.
93
- def parent
94
- return nil if @node["context"].nil? || @node["context"] == "_1"
95
- XMLParsing.find(:id => @node["context"])
104
+ file_id = @attributes["file"]
105
+ file_node = NodeCache.find(file_id)
106
+ file_node ? file_node.name : nil
96
107
  end
108
+ once :file
97
109
 
98
110
  # This is a unified search routine for finding nested nodes. It
99
111
  # simplifies the search routines below significantly.
100
- def find_nested_nodes_of_type(type, matcher = nil, &block)
101
- res = XMLParsing.find_nested_nodes_of_type(@node, type)
102
-
103
- case matcher
104
- when String
105
- res = res.find(:name => matcher)
106
- when Regexp
107
- res = res.find_all { |t| t.name =~ matcher }
108
- when nil
109
- # Do nothing, since not specifying a matcher is okay.
110
- else
111
- message = "Can't handle a match condition of type #{matcher.class}."
112
- raise UnsupportedMatcherException.new(message)
113
- end
114
-
115
- res = res.find_all(&block) if block
116
-
117
- res
112
+ def find_children_of_type(type, matcher = nil)
113
+ NodeCache.find_children_of_type(self, type, matcher)
118
114
  end
119
- private :find_nested_nodes_of_type
115
+ private :find_children_of_type
120
116
 
121
117
  # Find all namespaces. There are two ways of calling this method:
122
118
  # #namespaces => Get all namespaces in this scope
123
119
  # #namespaces(name) => Shortcut for namespaces.find(:name => name)
124
120
  #
125
121
  # Returns a QueryResult unless only one node was found
126
- def namespaces(name = nil, &block)
127
- find_nested_nodes_of_type("Namespace", name, &block)
122
+ def namespaces(name = nil)
123
+ find_children_of_type("Namespace", name)
128
124
  end
129
125
 
130
126
  # Find all classes in this scope.
131
127
  #
132
128
  # See Node.namespaces
133
- def classes(name = nil, &block)
134
- find_nested_nodes_of_type("Class", name, &block)
129
+ def classes(name = nil)
130
+ find_children_of_type("Class", name)
135
131
  end
136
132
 
137
133
  # Find all structs in this scope.
138
134
  #
139
135
  # See Node.namespaces
140
- def structs(name = nil, &block)
141
- find_nested_nodes_of_type("Struct", name, &block)
136
+ def structs(name = nil)
137
+ find_children_of_type("Struct", name)
142
138
  end
143
139
 
144
140
  # Find all functions in this scope.
145
141
  #
146
142
  # See Node.namespaces
147
- def functions(name = nil, &block)
148
- find_nested_nodes_of_type("Function", name, &block)
143
+ def functions(name = nil)
144
+ find_children_of_type("Function", name)
149
145
  end
150
146
 
151
147
  # Find all enumerations in this scope.
152
148
  #
153
149
  # See Node.namespaces
154
- def enumerations(name = nil, &block)
155
- find_nested_nodes_of_type("Enumeration", name, &block)
150
+ def enumerations(name = nil)
151
+ find_children_of_type("Enumeration", name)
156
152
  end
157
153
 
158
154
  # Find all variables in this scope
159
155
  #
160
156
  # See Node.namespaces
161
- def variables(name = nil, &block)
162
- find_nested_nodes_of_type("Variable", name, &block)
157
+ def variables(name = nil)
158
+ find_children_of_type("Variable", name)
163
159
  end
164
160
 
165
161
  # Find all typedefs in this scope
166
162
  #
167
163
  # See Node.namespaces
168
- def typedefs(name = nil, &block)
169
- find_nested_nodes_of_type("Typedef", name, &block)
164
+ def typedefs(name = nil)
165
+ find_children_of_type("Typedef", name)
170
166
  end
171
167
 
172
168
  # Print out the full C++ valid code for this node.
@@ -175,6 +171,11 @@ module RbGCCXML
175
171
  def to_cpp(qualified = true)
176
172
  qualified ? self.qualified_name : self.name
177
173
  end
174
+
175
+ def to_s
176
+ "#<#{self.class.name} @attributes=#{self.attributes.inspect}>"
177
+ end
178
+ alias :inspect :to_s
178
179
  end
179
180
 
180
181
  end
@@ -0,0 +1,93 @@
1
+ module RbGCCXML
2
+
3
+ # Manager of the tree of Nodes we build from GCC-XML.
4
+ #
5
+ # This is a static class that keeps around references to the
6
+ # entire generated Node tree as well as other structures
7
+ # designed for quick and easy searching of Nodes
8
+ class NodeCache
9
+ class << self
10
+
11
+ # Hash of id => node for all nodes
12
+ attr_reader :index_list
13
+
14
+ # Hash of Type => [node list] for easy
15
+ # searching of all nodes of a given type
16
+ attr_reader :types_list
17
+
18
+ # Add a new node to the index list
19
+ def <<(node)
20
+ @index_list ||= {}
21
+ @types_list ||= {}
22
+
23
+ @index_list[node.id] = node
24
+
25
+ class_name = node.class.name.split("::")[-1]
26
+ @types_list[class_name] ||= []
27
+ @types_list[class_name] << node
28
+ end
29
+
30
+ def clear
31
+ @index_list = {}
32
+ @types_list = {}
33
+ end
34
+
35
+ # Get the root node of the parse
36
+ def root
37
+ # This is simply a way to hide GCC_XML specifics,
38
+ # the :: Namespace node is always id _1
39
+ @index_list["_1"]
40
+ end
41
+
42
+ # Given an id, find the node
43
+ def find(id)
44
+ @index_list[id]
45
+ end
46
+
47
+ # Get the list of all nodes of a given type
48
+ def all(type)
49
+ @types_list[type] || []
50
+ end
51
+
52
+ # Given an array of ids return an array of nodes that match
53
+ def find_by_ids(ids)
54
+ QueryResult.new(ids.map {|id| @index_list[id] })
55
+ end
56
+
57
+ # Look through the DOM under +node+ for +type+ nodes.
58
+ # +type+ must be the string name of an existing Node subclass.
59
+ #
60
+ # Returns a QueryResult with the findings.
61
+ def find_children_of_type(node, type, matcher = nil)
62
+ results = QueryResult.new(self.select_nodes_of_type(node.children, type))
63
+ results = results.find(:name => matcher) if matcher
64
+ results
65
+ end
66
+
67
+ # Once all parsing is done and all nodes are in memory
68
+ # we need to actually build up the parent / child relationships
69
+ # of the nodes. We can't do this during parsing because there
70
+ # is no guarentee the nodes will come in the right order
71
+ def process_tree
72
+ @index_list.each do |id, node|
73
+
74
+ # If this node has a context, link to that context as a parent
75
+ # and then put ourselves as that parent's child
76
+ if node["context"]
77
+ node.parent = @index_list[node["context"]]
78
+ node.parent.children << node
79
+ end
80
+
81
+ end
82
+ end
83
+
84
+ protected
85
+
86
+ def select_nodes_of_type(nodes, type)
87
+ nodes.select {|node| node.class.to_s == "RbGCCXML::#{type}" }
88
+ end
89
+
90
+ end
91
+ end
92
+
93
+ end
@@ -5,7 +5,9 @@ module RbGCCXML
5
5
 
6
6
  # Get the Node for this argument's type
7
7
  def cpp_type
8
- XMLParsing.find_type_of(node, "type")
8
+ type = NodeCache.find(attributes["type"])
9
+ type.container = self
10
+ type
9
11
  end
10
12
 
11
13
  # Get any default value for this argument
@@ -17,6 +19,7 @@ module RbGCCXML
17
19
  def to_cpp(qualified = true)
18
20
  "#{self.cpp_type.to_cpp(qualified)} #{self.name}"
19
21
  end
22
+ once :to_cpp
20
23
 
21
24
  end
22
25
  end
@@ -7,7 +7,7 @@ module RbGCCXML
7
7
 
8
8
  # Get the Class node representing the type of this Base
9
9
  def cpp_type
10
- XMLParsing.find_type_of(node, "type")
10
+ NodeCache.find(attributes["type"])
11
11
  end
12
12
 
13
13
  end
@@ -1,7 +1,7 @@
1
1
  module RbGCCXML
2
2
  # Represents a <Class> node.
3
3
  class Class < Node
4
-
4
+
5
5
  # Disabled: Classes cannot have nested namespaces
6
6
  def namespaces(name = nil)
7
7
  raise NotQueryableException.new("Cannot query for Namespaces while in a Class")
@@ -9,30 +9,32 @@ module RbGCCXML
9
9
 
10
10
  # Is this class pure virtual?
11
11
  def pure_virtual?
12
- @node["abstract"] ? @node["abstract"] == "1" : false
12
+ attributes["abstract"] ? attributes["abstract"] == "1" : false
13
13
  end
14
14
 
15
- # Find all the constructors for this class.
15
+ # Find all the constructors for this class.
16
16
  def constructors
17
- XMLParsing::find_nested_nodes_of_type(@node, "Constructor")
17
+ NodeCache.find_children_of_type(self, "Constructor")
18
18
  end
19
19
 
20
20
  # Find the destructor for this class.
21
21
  # To tell if a destructor is gcc-generated or not, check the
22
22
  # 'artificial' attribute:
23
23
  #
24
- # class_node.destructor.attributes[:artificial]
24
+ # class_node.destructor.artificial?
25
25
  #
26
26
  def destructor
27
- XMLParsing::find_nested_nodes_of_type(@node, "Destructor")[0]
27
+ NodeCache.find_children_of_type(self, "Destructor")[0]
28
28
  end
29
29
 
30
- # Find the superclass for this class.
30
+ # Find the superclass for this class.
31
31
  # By default, this will find the superclass of any access type, pass in
32
- # the type of access you're looking for if you want specific types (e.g. public, private, protected).
32
+ # the type of access you're looking for if you want specific types
33
+ # (e.g. public, private, protected).
33
34
  #
34
- # If there is more than one superclass to this class, this method will only return the first superclass.
35
- # If you know or expect there to be multiple superclasses, use #superclasses instead
35
+ # If there is more than one superclass to this class, this method will only return
36
+ # the first superclass. # If you know or expect there to be multiple superclasses,
37
+ # use #superclasses instead
36
38
  def superclass(access_type = nil)
37
39
  found = superclasses(access_type)
38
40
  found.empty? ? nil : found[0]
@@ -41,22 +43,27 @@ module RbGCCXML
41
43
  # Like #superclass above, this will find all superclasses for this class.
42
44
  # Functions the same as #superclass except this method always returns a QueryResult
43
45
  def superclasses(access_type = nil)
44
- XMLParsing::find_bases_for(@node, access_type)
46
+ [
47
+ NodeCache.find_children_of_type(self, "Base").select do |base|
48
+ access_type.nil? ? true : base.send("#{access_type}?")
49
+ end
50
+ ].flatten.map {|base| base.cpp_type }
45
51
  end
46
52
 
47
53
  # Find all methods for this class. See Node#namespaces
48
- def methods(name = nil, &block)
49
- find_nested_nodes_of_type("Method", name, &block)
54
+ def methods(name = nil)
55
+ NodeCache.find_children_of_type(self, "Method", name)
50
56
  end
51
57
 
52
58
  # Find all instance variables for this class. See Node#namespaces
53
59
  def variables(name = nil, &block)
54
- find_nested_nodes_of_type("Field", name, &block)
60
+ NodeCache.find_children_of_type(self, "Field", name)
55
61
  end
56
62
 
57
63
  # Find all constants under this class. See Node#namespaces
58
64
  def constants(name = nil, &block)
59
- find_nested_nodes_of_type("Variable", name, &block)
65
+ NodeCache.find_children_of_type(self, "Variable", name)
60
66
  end
67
+
61
68
  end
62
69
  end
@@ -3,12 +3,9 @@ module RbGCCXML
3
3
  # Represents an <EnumValue> node, which is an entry in an <Enumeration>
4
4
  class EnumValue < Node
5
5
 
6
- # Link to the Enumeration that holds this EnumValue
7
- attr_accessor :parent
8
-
9
6
  # Get the defined value of this EnumValue
10
7
  def value
11
- node["init"].to_i
8
+ attributes["init"].to_i
12
9
  end
13
10
 
14
11
  # The qualified name of an EnumValue doesn't
@@ -16,6 +13,7 @@ module RbGCCXML
16
13
  def qualified_name #:nodoc:
17
14
  "#{self.parent.parent.qualified_name}::#{self.name}"
18
15
  end
16
+ once :qualified_name
19
17
 
20
18
  end
21
19
 
@@ -6,7 +6,7 @@ module RbGCCXML
6
6
 
7
7
  # Get the list of EnumValues for this enumeration
8
8
  def values
9
- XMLParsing.get_values_of(self).each {|v| v.parent = self }
9
+ children
10
10
  end
11
11
 
12
12
  # Is this enumeration anonymous? As in, does it have a name or is
@@ -5,13 +5,15 @@ module RbGCCXML
5
5
 
6
6
  # Get the Node representing this field's type
7
7
  def cpp_type
8
- XMLParsing.find_type_of(node, "type")
8
+ NodeCache.find(attributes["type"])
9
9
  end
10
+ once :cpp_type
10
11
 
11
12
  # See Node#to_cpp
12
13
  def to_cpp(qualified = false)
13
14
  "#{self.cpp_type.to_cpp(qualified)} #{self.name}"
14
15
  end
16
+ once :to_cpp
15
17
 
16
18
  end
17
19
 
@@ -13,12 +13,12 @@ module RbGCCXML
13
13
 
14
14
  # Get the list of Arguments for this Function.
15
15
  def arguments
16
- XMLParsing.find_arguments_for(node)
16
+ children
17
17
  end
18
18
 
19
19
  # Get the Node representing this Function's return type
20
20
  def return_type
21
- XMLParsing.find_type_of(node, "returns")
21
+ NodeCache.find(attributes["returns"])
22
22
  end
23
23
  end
24
24
 
@@ -5,17 +5,17 @@ module RbGCCXML
5
5
 
6
6
  # Is this method static?
7
7
  def static?
8
- @node["static"] == "1"
8
+ attributes["static"] == "1"
9
9
  end
10
10
 
11
11
  # Is this a virtual method?
12
12
  def virtual?
13
- @node["virtual"] == "1"
13
+ attributes["virtual"] == "1"
14
14
  end
15
15
 
16
16
  # Is this a pure virtual method? A purely virtual method has no body.
17
17
  def purely_virtual?
18
- @node["pure_virtual"] == "1"
18
+ attributes["pure_virtual"] == "1"
19
19
  end
20
20
 
21
21
  end
@@ -2,5 +2,19 @@ module RbGCCXML
2
2
  # Represents the <Namespace> node. Namespaces can have in it
3
3
  # more namespaces, methods, classes, structs, attributes, ... everything.
4
4
  class Namespace < Node
5
+
6
+ # Special case for the top-level namespace, ignore :: or
7
+ # we get elements that print out as ::::type
8
+ def qualified_name #:nodoc:
9
+ if @name == "::"
10
+ ""
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def to_cpp
17
+ self.qualified_name
18
+ end
5
19
  end
6
20
  end
@@ -12,7 +12,7 @@ module RbGCCXML
12
12
  def check_sub_type_without(val, delim)
13
13
  return false unless val =~ delim
14
14
  new_val = val.gsub(delim, "").strip
15
- XMLParsing.find_type_of(self.node, "type") == new_val
15
+ NodeCache.find(attributes["type"]) == new_valu
16
16
  end
17
17
 
18
18
  # Get the base type without any qualifiers. E.g, if you've
@@ -21,13 +21,14 @@ module RbGCCXML
21
21
  #
22
22
  # returns: Node related to the base C++ construct of this type
23
23
  def base_type
24
- n = XMLParsing.find_type_of(self.node, "type")
24
+ n = NodeCache.find(attributes["type"])
25
25
  n.is_a?(Type) ? n.base_type : n
26
26
  end
27
+ once :base_type
27
28
 
28
29
  # Is this type const qualified?
29
30
  def const?
30
- found = XMLParsing.find_type_of(self.node, "type")
31
+ found = NodeCache.find(attributes["type"])
31
32
  found ? found.const? : false
32
33
  end
33
34
 
@@ -20,9 +20,10 @@ module RbGCCXML
20
20
 
21
21
  # See Node#to_cpp
22
22
  def to_cpp(qualified = true)
23
- type = XMLParsing.find_type_of(self.node, "type")
24
- "#{type.to_cpp(qualified)}[#{self.node["max"].gsub(/[^\d]/, '').to_i + 1}]"
23
+ type = NodeCache.find(attributes["type"])
24
+ "#{type.to_cpp(qualified)}[#{attributes["max"].gsub(/[^\d]/, '').to_i + 1}]"
25
25
  end
26
+ once :to_cpp
26
27
 
27
28
  end
28
29
 
@@ -3,20 +3,26 @@ module RbGCCXML
3
3
  # Represents a <CvQualifiedType> node. This node keeps track of
4
4
  # the const nature of a Node.
5
5
  class CvQualifiedType < Type
6
-
6
+
7
7
  def ==(val)
8
8
  check_sub_type_without(val, /const/)
9
9
  end
10
10
 
11
11
  # See Node#to_cpp
12
12
  def to_cpp(qualified = true)
13
- type = XMLParsing.find_type_of(self.node, "type")
14
- "const #{type.to_cpp(qualified)}"
13
+ type = NodeCache.find(attributes["type"])
14
+
15
+ post_const = container ? " const" : ""
16
+ pre_const = container ? "" : "const "
17
+
18
+ "#{pre_const}#{type.to_cpp(qualified)}#{post_const}"
15
19
  end
20
+ once :to_cpp
16
21
 
17
- # Is this node const?
18
- def const?
19
- self.node["const"].to_i == 1
22
+ # Have to redefine this as Type#const? looks up the
23
+ # tree for const, and in our case we are the const knower
24
+ def const? #:nodoc:
25
+ attributes["const"] == "1"
20
26
  end
21
27
 
22
28
  end
@@ -9,9 +9,10 @@ module RbGCCXML
9
9
 
10
10
  # See Node#to_cpp
11
11
  def to_cpp(qualified = true)
12
- type = XMLParsing.find_type_of(self.node, "type")
12
+ type = NodeCache.find(attributes["type"])
13
13
  "#{type.to_cpp(qualified)}*"
14
14
  end
15
+ once :to_cpp
15
16
 
16
17
  end
17
18
 
@@ -8,9 +8,10 @@ module RbGCCXML
8
8
  end
9
9
 
10
10
  def to_cpp(qualified = true)
11
- type = XMLParsing.find_type_of(self.node, "type")
11
+ type = NodeCache.find(attributes["type"])
12
12
  "#{type.to_cpp(qualified)}&"
13
13
  end
14
+ once :to_cpp
14
15
 
15
16
  end
16
17