rbgccxml 0.9.1 → 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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