rbgccxml 0.1.1 → 0.8

Sign up to get free protection for your applications and to get access to all the features.
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
  #