rbgccxml 0.1.1 → 0.8

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
@@ -4,7 +4,7 @@ require 'rake/contrib/sshpublisher'
4
4
  require 'rake/gempackagetask'
5
5
 
6
6
  PROJECT_NAME = "rbgccxml"
7
- RBGCCXML_VERSION = "0.1.1"
7
+ RBGCCXML_VERSION = "0.8"
8
8
 
9
9
  task :default => :test
10
10
 
@@ -25,15 +25,25 @@ end
25
25
  RUBYFORGE_USERNAME = "jameskilton"
26
26
  PROJECT_WEB_PATH = "/var/www/gforge-projects/rbplusplus/rbgccxml"
27
27
 
28
- # As part of the rbplusplus project, this just goes in a subfolder
29
- desc "Update the website"
30
- task :upload_web => :rdoc do |t|
31
- unless File.directory?("publish")
32
- mkdir "publish"
28
+ namespace :web do
29
+ desc "Put the website together"
30
+ task :build => :rdoc do
31
+ unless File.directory?("publish")
32
+ mkdir "publish"
33
+ end
34
+ sh "cp -r html/* publish/"
35
+ end
36
+
37
+ # As part of the rbplusplus project, this just goes in a subfolder
38
+ desc "Update the website"
39
+ task :upload => "web:build" do |t|
40
+ Rake::SshDirPublisher.new("#{RUBYFORGE_USERNAME}@rubyforge.org", PROJECT_WEB_PATH, "publish").upload
41
+ end
42
+
43
+ desc "Clean up generated web files"
44
+ task :clean do
45
+ rm_rf "publish"
33
46
  end
34
- sh "cp -r html/* publish/"
35
- Rake::SshDirPublisher.new("#{RUBYFORGE_USERNAME}@rubyforge.org", PROJECT_WEB_PATH, "publish").upload
36
- rm_rf "publish"
37
47
  end
38
48
 
39
49
  spec = Gem::Specification.new do |s|
data/lib/rbgccxml/node.rb CHANGED
@@ -8,7 +8,7 @@ module RbGCCXML
8
8
  #
9
9
  # Any Node further down the heirarchy chain can and should define which
10
10
  # finder methods are and are not avaiable at that level. For example, the
11
- # Class Node cannot search for other Namespaces within that class.
11
+ # Class node cannot search for other Namespaces within that class.
12
12
  class Node
13
13
  attr_reader :node
14
14
 
@@ -17,88 +17,117 @@ module RbGCCXML
17
17
  def initialize(node)
18
18
  @node = node
19
19
  end
20
-
20
+ protected :initialize
21
+
21
22
  # Get the C++ name of this node
22
23
  def name
23
24
  @node.attributes['name']
24
25
  end
25
-
26
+
26
27
  # Get the fully qualified (demangled) C++ name of this node.
27
- # The 'demangled' attribute of the node for methods / functions is the
28
- # full signature, so cut that out.
29
28
  def qualified_name
30
29
  if @node.attributes["demangled"]
30
+ # The 'demangled' attribute of the node for methods / functions is the
31
+ # full signature, so cut that part out.
31
32
  @node.attributes["demangled"].split(/\(/)[0]
32
33
  else
33
- self.name
34
+ parent ? "#{parent.qualified_name}::#{name}" : name
34
35
  end
35
36
  end
36
37
 
37
- # Any unknown methods get sent to the XML node
38
- def method_missing(name, *args)
39
- if @node.respond_to?(name)
40
- @node.send(name, *args)
41
- else
42
- # Make sure we still throw NoMethodErrors
43
- super
44
- end
38
+ # Is this node const qualified?
39
+ def const?
40
+ @node.attributes["const"] ? @node.attributes["const"] == "1" : false
41
+ end
42
+
43
+ # Does this node have public access?
44
+ def public?
45
+ @node.attributes["access"] ? @node.attributes["access"] == "public" : true
46
+ end
47
+
48
+ # Does this node have protected access?
49
+ def protected?
50
+ @node.attributes["access"] ? @node.attributes["access"] == "protected" : false
51
+ end
52
+
53
+ # Does this node have private access?
54
+ def private?
55
+ @node.attributes["access"] ? @node.attributes["access"] == "private" : false
56
+ end
57
+
58
+ # Forward up attribute array for easy access to the
59
+ # underlying XML node
60
+ def attributes
61
+ @node.attributes
45
62
  end
46
63
 
47
- # Get the file name of the file this node is found in.
64
+ # Returns the full file name of the file this node is found in.
48
65
  def file_name(basename = true)
49
66
  file_id = @node.attributes["file"]
50
- file_node = XMLParsing::find(:type => "File", :id => file_id)
67
+ file_node = XMLParsing.find(:type => "File", :id => file_id)
51
68
  name = file_node.attributes["name"]
52
69
  basename ? ::File.basename(name) : name
53
70
  end
54
71
 
55
- # Get the parent node of this node. e.g. function.parent will get the class
72
+ # Returns the parent node of this node. e.g. function.parent will get the class
56
73
  # the function is contained in.
57
74
  def parent
58
- return nil if @node.attributes["context"] == "_1"
59
- XMLParsing::find(:id => @node.attributes["context"])
75
+ return nil if @node.attributes["context"].nil? || @node.attributes["context"] == "_1"
76
+ XMLParsing.find(:id => @node.attributes["context"])
60
77
  end
61
78
 
62
79
  # Find all namespaces. There are two ways of calling this method:
63
80
  # #namespaces => Get all namespaces in this scope
64
81
  # #namespaces(name) => Shortcut for namespaces.find(:name => name)
82
+ #
83
+ # Returns a QueryResult unless only one node was found
65
84
  def namespaces(name = nil)
66
85
  if name
67
86
  namespaces.find(:name => name)
68
87
  else
69
- XMLParsing::find_nested_nodes_of_type(@node, "Namespace")
88
+ XMLParsing.find_nested_nodes_of_type(@node, "Namespace")
70
89
  end
71
90
  end
72
91
 
73
- # Find all classes in this scope. Like #namespaces, there are
74
- # two ways of calling this method.
92
+ # Find all classes in this scope.
93
+ # See Node.namespaces
75
94
  def classes(name = nil)
76
95
  if name
77
96
  classes.find(:name => name)
78
97
  else
79
- XMLParsing::find_nested_nodes_of_type(@node, "Class")
98
+ XMLParsing.find_nested_nodes_of_type(@node, "Class")
80
99
  end
81
100
  end
82
101
 
83
- # Find all structs in this scope. Like #namespaces, there are
84
- # two ways of calling this method.
102
+ # Find all structs in this scope.
103
+ # See Node.namespaces
85
104
  def structs(name = nil)
86
105
  if name
87
106
  structs.find(:name => name)
88
107
  else
89
- XMLParsing::find_nested_nodes_of_type(@node, "Struct")
108
+ XMLParsing.find_nested_nodes_of_type(@node, "Struct")
90
109
  end
91
110
  end
92
111
 
93
112
  # Find all functions in this scope. Functions are free non-class
94
113
  # functions. To search for class methods, use #methods.
95
- #
96
- # Like #namespaces, there are two ways of calling this method.
114
+ #
115
+ # See Node.namespaces
97
116
  def functions(name = nil)
98
117
  if name
99
118
  functions.find(:name => name)
100
119
  else
101
- XMLParsing::find_nested_nodes_of_type(@node, "Function")
120
+ XMLParsing.find_nested_nodes_of_type(@node, "Function")
121
+ end
122
+ end
123
+
124
+ # Find all enumerations in this scope.
125
+ # See Node.namespaces
126
+ def enumerations(name = nil)
127
+ if name
128
+ enumerations.find(:name => name)
129
+ else
130
+ XMLParsing.find_nested_nodes_of_type(@node, "Enumeration")
102
131
  end
103
132
  end
104
133
 
@@ -111,12 +140,34 @@ module RbGCCXML
111
140
  def ==(val)
112
141
  if val.is_a?(String)
113
142
  return true if self.name == val
114
- return true if self.qualified_name =~ /#{val.gsub("*", "\\*")}/
143
+ # Need to take care of '*' which is a regex character, and any leading ::,
144
+ # which are redundant in our case.
145
+ if val =~ /::/
146
+ return true if self.qualified_name =~ /#{val.gsub("*", "\\*").gsub(/^::/, "")}/
147
+ end
148
+
115
149
  false
150
+ elsif val.is_a?(Regexp)
151
+ self =~ val
116
152
  else
117
153
  super
118
154
  end
119
155
  end
156
+
157
+ # Regexp comparison operator for consistency. See Node.==
158
+ def =~(val)
159
+ if val.is_a?(Regexp)
160
+ self.name =~ val || self.qualified_name =~ val
161
+ else
162
+ super
163
+ end
164
+ end
165
+
166
+ # Print out the name of this node. If passed in <tt>true</tt> then will
167
+ # print out the fully qualified name of this node.
168
+ def to_s(full = false)
169
+ full ? self.qualified_name : self.name
170
+ end
120
171
  end
121
172
 
122
173
  end
@@ -1,5 +1,5 @@
1
1
  module RbGCCXML
2
- # Class representing a single <Argument ...> node.
2
+ # Class representing a single <Argument> node.
3
3
  class Argument < Node
4
4
 
5
5
  # Get the C++ type of this argument.
@@ -1,22 +1,18 @@
1
1
  module RbGCCXML
2
- # Node type represending <Class ...> nodes.
2
+ # Node type represending <Class> nodes.
3
3
  class Class < Node
4
4
 
5
- # Classes cannot have nested namespaces
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")
8
8
  end
9
9
 
10
- # Find all the constructors for this class
10
+ # Find all the constructors for this class.
11
11
  def constructors
12
12
  XMLParsing::find_nested_nodes_of_type(@node, "Constructor")
13
13
  end
14
14
 
15
- # Find all methods for this class. The typical two-ways apply:
16
- #
17
- # <tt>methods</tt>:: Get all methods in this scope
18
- # <tt>methods(name)</tt>:: Shortcut for methods.find(:name => name)
19
- #
15
+ # Find all methods for this class. See Node.namespaces
20
16
  def methods(name = nil)
21
17
  if name
22
18
  methods.find(:name => name)
@@ -1,10 +1,7 @@
1
1
  module RbGCCXML
2
- # Class representing <Constructor ...> nodes.
3
- # Has arguments
2
+ # Class representing <Constructor> nodes.
4
3
  class Constructor < Function
5
-
6
- # Constructors do not have a return_type, this raises an
7
- # exception.
4
+ # Disabled: Constructors do not have a return_type.
8
5
  def return_type
9
6
  raise "There is no return_type of a constructor"
10
7
  end
@@ -0,0 +1,20 @@
1
+ module RbGCCXML
2
+ # A specific value entry of an enumeration <EnumValue>
3
+ class EnumValue < Node
4
+
5
+ # Link to the Enumeration Node that holds this EnumValue
6
+ attr_accessor :parent
7
+
8
+ # Get the C++ value of the EnumValue
9
+ def value
10
+ node.attributes["init"].to_i
11
+ end
12
+
13
+ # The qualified name of an Enum Value doesn't
14
+ # include the name of the enum itself
15
+ def qualified_name #:nodoc:
16
+ "#{self.parent.parent.qualified_name}::#{self.name}"
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ module RbGCCXML
2
+ # Representation of the <Enumeration> node
3
+ class Enumeration < Node
4
+ # Get the list of Values for this enumeration
5
+ def values
6
+ XMLParsing.get_values_of(self).each {|v| v.parent = self }
7
+ end
8
+
9
+ end
10
+ end
@@ -1,6 +1,5 @@
1
1
  module RbGCCXML
2
-
3
- # Handles the <File...> node.
2
+ # Handles the <File> node.
4
3
  class File < Node
5
4
  end
6
5
  end
@@ -1,5 +1,5 @@
1
1
  module RbGCCXML
2
- # Function query node. Functions are the end point of the tree.
2
+ # Represents the <Function> node. Functions are the end point of the tree.
3
3
  # They have arguments and return types.
4
4
  class Function < Node
5
5
 
@@ -11,7 +11,6 @@ module RbGCCXML
11
11
  end
12
12
 
13
13
  # Get the list of arguments for this Function.
14
- # Returns an array of Argument nodes
15
14
  def arguments
16
15
  XMLParsing.find_arguments_for(node)
17
16
  end
@@ -1,5 +1,5 @@
1
1
  module RbGCCXML
2
- # Node type represending <Method ...> nodes, which are representation
2
+ # Node type representing <Method> nodes, which are representation
3
3
  # of class methods.
4
4
  class Method < Function
5
5
 
@@ -7,5 +7,15 @@ module RbGCCXML
7
7
  def static?
8
8
  @node.attributes["static"] == "1"
9
9
  end
10
+
11
+ # Is this a virtual method?
12
+ def virtual?
13
+ @node.attributes["virtual"] == "1"
14
+ end
15
+
16
+ # Is this a pure virtual method? A purely virtual method has no body.
17
+ def purely_virtual?
18
+ @node.attributes["pure_virtual"] == "1"
19
+ end
10
20
  end
11
21
  end
@@ -1,5 +1,5 @@
1
1
  module RbGCCXML
2
- # Namespace query node. Namespaces can have in it
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
5
  end
@@ -1,6 +1,6 @@
1
1
  module RbGCCXML
2
- # Node type represending <Struct ...> nodes.
3
- # It's really just a class
2
+ # Node type represending <Struct> nodes.
3
+ # It's really just a class with default of public instead of private
4
4
  class Struct < ::RbGCCXML::Class
5
5
  end
6
6
  end
@@ -1,7 +1,34 @@
1
1
  module RbGCCXML
2
-
3
2
  # The base class for all type management classes.
3
+ # RbGCCXML has a pretty extensive type querying sub-system that
4
+ # allows type matching by names, types, base types, etc
4
5
  class Type < Node
6
+
7
+ # For types like pointers or references, we recursively track down
8
+ # the base type when doing comparisons.
9
+ #
10
+ # delim needs to be a regex
11
+ def check_sub_type_without(val, delim)
12
+ return false unless val =~ delim
13
+ new_val = val.gsub(delim, "").strip
14
+ XMLParsing.find_type_of(self.node, "type") == new_val
15
+ end
16
+
17
+ # Get the base type without any qualifiers. E.g, if you've
18
+ # got the CVQualified type "const my_space::MyClass&, this
19
+ # will return the node for "my_space::MyClass"
20
+ #
21
+ # returns: Node related to the base C++ construct of this type
22
+ def base_type
23
+ n = XMLParsing.find_type_of(self.node, "type")
24
+ n.is_a?(Type) ? n.base_type : n
25
+ end
26
+
27
+ # Is this type a const?
28
+ def const?
29
+ found = XMLParsing.find_type_of(self.node, "type")
30
+ found ? found.const? : false
31
+ end
5
32
  end
6
33
 
7
34
  end
@@ -0,0 +1,20 @@
1
+ module RbGCCXML
2
+ # Node that represents <CvQualifiedType>. This gccxml node handles
3
+ # both const and volitile flags, but for our case we only use the
4
+ # const flag.
5
+ class CvQualifiedType < Type
6
+
7
+ def ==(val)
8
+ check_sub_type_without(val, /const/)
9
+ end
10
+
11
+ def to_s(full = false)
12
+ type = XMLParsing.find_type_of(self.node, "type")
13
+ "const #{type.to_s(full)}"
14
+ end
15
+
16
+ def const?
17
+ self.node.attributes["const"].to_i == 1
18
+ end
19
+ end
20
+ end
@@ -1,8 +1,14 @@
1
1
  module RbGCCXML
2
-
3
- # The FundamentalType. This represents all the built-in types
2
+ # The <FundamentalType>. This represents all the built-in types
4
3
  # of C++ like int, double, char, etc.
5
4
  class FundamentalType < Type
5
+
6
+ # We are base types. There's nothing
7
+ # more to find once we've gotten to this type.
8
+ def base_type
9
+ self
10
+ end
11
+
6
12
  end
7
13
 
8
14
  end
@@ -1,16 +1,14 @@
1
1
  module RbGCCXML
2
-
3
2
  # This represents a Pointer of any other kind of type
4
3
  class PointerType < Type
5
4
 
6
- # Does this type match the given name string?
7
5
  def ==(val)
8
- # Look for the '*' denoting a pointer type.
9
- # Assuming we find one, drop it and look for the
10
- # base type.
11
- return false unless val =~ /\*/
12
- new_val = val.gsub("*", "").strip
13
- XMLParsing.find_type_of(self.node, "type") == new_val
6
+ check_sub_type_without(val, /\*/)
7
+ end
8
+
9
+ def to_s(full = false)
10
+ type = XMLParsing.find_type_of(self.node, "type")
11
+ "#{type.to_s(full)}*"
14
12
  end
15
13
  end
16
14
 
@@ -0,0 +1,14 @@
1
+ module RbGCCXML
2
+ # This deals with C++ Reference nodes (&)
3
+ class ReferenceType < Type
4
+
5
+ def ==(val)
6
+ check_sub_type_without(val, /\&/)
7
+ end
8
+
9
+ def to_s(full = false)
10
+ type = XMLParsing.find_type_of(self.node, "type")
11
+ "#{type.to_s(full)}&"
12
+ end
13
+ end
14
+ end
@@ -1,5 +1,4 @@
1
1
  module RbGCCXML
2
-
3
2
  # This represents a Typedef, basically the renaming of one type to
4
3
  # another.
5
4
  class Typedef < Type
@@ -2,7 +2,7 @@ require 'tempfile'
2
2
 
3
3
  module RbGCCXML
4
4
 
5
- # This class starts the whole process.
5
+ # This class manages the parsing of the C++ code.
6
6
  # Please use RbGCCXML.parse and not this class directly
7
7
  class Parser
8
8
 
@@ -1,20 +1,35 @@
1
1
  module RbGCCXML
2
- # Any node query will return an instance of this class, QueryResult. This
3
- # class is an Array with slightly different handling of #find. Array#find
4
- # expects a block to find elements, we want #find to take an options
5
- # hash.
2
+ # All queries return either an instance of this class, or in the case of
3
+ # a single result, the node found. Use this class to further define query
4
+ # parameters.
6
5
  class QueryResult < Array
7
6
 
7
+ # To facilitate the management of what could be many nodes found by a single query,
8
+ # we forward off any unknown methods to each node in the results query.
9
+ # We assume that if one node accepts the method, then all of them will.
10
+ def method_missing(name, *args)
11
+ if self[0].respond_to?(name)
12
+ self.each do |node|
13
+ node.send(name, *args)
14
+ end
15
+ else
16
+ super
17
+ end
18
+ end
19
+
20
+ EXPECTED_OPTIONS = [:name, :returns, :arguments, :access] unless defined?(EXPECTED_OPTIONS)
21
+
8
22
  # Find within this result set any nodes that match the given options
9
23
  # Options can be any or all of the following, based on the type of node:
10
24
  #
11
- # All nodes:
12
25
  # <tt>:name</tt>:: The unmangled name of the node. Can be a string or Regexp. Works on all nodes.
13
26
  # <tt>:arguments</tt>:: Search according to argument types.
14
27
  # This needs to be an array of strings or symbols. nil can be
15
28
  # used as a "any" flag. Only works on Functions, Methods, and Constructors
16
29
  # <tt>:returns</tt>:: Search according to the return type. Can be a string or symbol.
17
30
  # Only works on Functions and Methods
31
+ # <tt>:access</tt>:: Search according to access properties. Can be :public, :protected, or :private.
32
+ # Only works on Classes and Methods
18
33
  #
19
34
  # All arguments added to the options are processed in an AND format. If you
20
35
  # are looking for 3 random arguments with a return type of int:
@@ -38,33 +53,44 @@ module RbGCCXML
38
53
  #
39
54
  # find(:returns => "MyClass*")
40
55
  #
56
+ # There will be cases where you'll want to search *all* of a given type no matter what scope
57
+ # or nesting exists. To put a finder into this mode, you simply send :all as the first parameter:
58
+ #
59
+ # find(:all, [arguments as defined above])
60
+ #
61
+ # will find all nodes that fit the normal arguments for a given type (the node type of the first
62
+ # in the initial result set. e.g. if you run <tt>classes.find(:all)</tt> then all Class nodes)
63
+ #
41
64
  # Returns: A new QueryResult containing the results, allowing for nested +finds+.
42
65
  # However, If there is only one result, returns that single Node instead.
43
- def find(options = {})
66
+ def find(*options)
44
67
  result = QueryResult.new
68
+ query_set = self
45
69
 
46
- # Handler hash for doing AND intersection checking
47
- found = {}
70
+ if options[0] == :all
71
+ node_type = self[0].class.to_s.split(/::/)[-1]
72
+ query_set = XMLParsing.find_all(:type => node_type)
73
+ options = options[1]
74
+ else
75
+ options = options[0]
76
+ end
48
77
 
49
78
  name = options.delete(:name)
50
79
  returns = options.delete(:returns)
51
80
  arguments = options.delete(:arguments)
81
+ access = options.delete(:access)
52
82
 
53
83
  raise ":arguments must be an array" if arguments && !arguments.is_a?(Array)
54
- raise "Unknown keys #{option.keys.join(", ")}. " +
55
- "Expected are: :name, :arguments, and :returns" unless options.empty?
84
+ raise "Unknown keys #{options.keys.join(", ")}. " +
85
+ "Expected are: #{EXPECTED_OPTIONS.join(",")}" unless options.empty?
86
+
87
+ found = {}
56
88
 
57
- self.each do |node|
89
+ query_set.each do |node|
58
90
  # C++ name
59
91
  if name
60
92
  found[:name] ||= []
61
- if name.is_a?(Regexp)
62
- found_name = (node.attributes["name"] =~ name)
63
- else
64
- found_name = (node.attributes["name"] == name)
65
- end
66
-
67
- found[:name] << node if found_name
93
+ found[:name] << node if node == name
68
94
  end
69
95
 
70
96
  # Return type
@@ -92,12 +118,18 @@ module RbGCCXML
92
118
 
93
119
  found[:arguments] << node if keep
94
120
  end
121
+
122
+ # Access type
123
+ if access
124
+ found[:access] ||= []
125
+ found[:access] << node if node.attributes["access"] == access.to_s
126
+ end
95
127
  end
96
128
 
97
129
  # Now we do an intersection of all the found nodes,
98
130
  # which ensures that we AND together all the parts
99
131
  # the user is looking for
100
- tmp = self
132
+ tmp = query_set
101
133
  found.each_value do |value|
102
134
  tmp = (tmp & value)
103
135
  end
@@ -3,7 +3,7 @@ module RbGCCXML
3
3
 
4
4
  class << self
5
5
 
6
- # This is where it all happens. This method must be after any calls
6
+ # This is where it all happens. This method must be called after any calls
7
7
  # to RbGCCXML.gccxml_path= or RbGCCXML.add_include_paths.
8
8
  # Files can be one of many formats (and should always be full directory paths):
9
9
  #