ruby-bindgen 1.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE +25 -0
- data/README.md +68 -0
- data/Rakefile +8 -0
- data/bin/ruby-bindgen +133 -0
- data/docs/architecture.md +238 -0
- data/docs/c/c_bindings.md +65 -0
- data/docs/c/constants.md +21 -0
- data/docs/c/customizing.md +19 -0
- data/docs/c/filtering.md +24 -0
- data/docs/c/getting_started.md +56 -0
- data/docs/c/library_loading.md +53 -0
- data/docs/c/output.md +96 -0
- data/docs/c/types.md +61 -0
- data/docs/c/version_guards.md +105 -0
- data/docs/cmake/cmake_bindings.md +19 -0
- data/docs/cmake/filtering.md +26 -0
- data/docs/cmake/getting_started.md +52 -0
- data/docs/cmake/output.md +110 -0
- data/docs/configuration.md +351 -0
- data/docs/contributing.md +68 -0
- data/docs/cpp/buffers.md +24 -0
- data/docs/cpp/classes.md +139 -0
- data/docs/cpp/cpp_bindings.md +29 -0
- data/docs/cpp/customizing.md +124 -0
- data/docs/cpp/enums.md +42 -0
- data/docs/cpp/filtering.md +35 -0
- data/docs/cpp/getting_started.md +80 -0
- data/docs/cpp/iterators.md +94 -0
- data/docs/cpp/operators.md +170 -0
- data/docs/cpp/output.md +125 -0
- data/docs/cpp/templates.md +114 -0
- data/docs/examples.md +133 -0
- data/docs/index.md +133 -0
- data/docs/prior_art.md +37 -0
- data/docs/troubleshooting.md +243 -0
- data/docs/type_spelling.md +200 -0
- data/docs/updating_bindings.md +55 -0
- data/docs/version_guards.md +69 -0
- data/lib/ruby-bindgen/config.rb +63 -0
- data/lib/ruby-bindgen/generators/cmake/cmake.rb +171 -0
- data/lib/ruby-bindgen/generators/cmake/directory.erb +29 -0
- data/lib/ruby-bindgen/generators/cmake/guard.rb +55 -0
- data/lib/ruby-bindgen/generators/cmake/presets.erb +232 -0
- data/lib/ruby-bindgen/generators/cmake/project.erb +89 -0
- data/lib/ruby-bindgen/generators/ffi/callback.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/constant.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/enum_constant_decl.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/enum_decl.erb +4 -0
- data/lib/ruby-bindgen/generators/ffi/enum_decl_anonymous.erb +4 -0
- data/lib/ruby-bindgen/generators/ffi/ffi.rb +687 -0
- data/lib/ruby-bindgen/generators/ffi/field_decl.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/function.erb +5 -0
- data/lib/ruby-bindgen/generators/ffi/library.erb +39 -0
- data/lib/ruby-bindgen/generators/ffi/project.erb +18 -0
- data/lib/ruby-bindgen/generators/ffi/struct.erb +6 -0
- data/lib/ruby-bindgen/generators/ffi/translation_unit.erb +9 -0
- data/lib/ruby-bindgen/generators/ffi/typedef_decl.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/union.erb +6 -0
- data/lib/ruby-bindgen/generators/ffi/variable.erb +1 -0
- data/lib/ruby-bindgen/generators/ffi/version.erb +9 -0
- data/lib/ruby-bindgen/generators/ffi/version_method.erb +5 -0
- data/lib/ruby-bindgen/generators/generator.rb +52 -0
- data/lib/ruby-bindgen/generators/rice/auto_generated_base_class.erb +5 -0
- data/lib/ruby-bindgen/generators/rice/class.erb +31 -0
- data/lib/ruby-bindgen/generators/rice/class_template.erb +9 -0
- data/lib/ruby-bindgen/generators/rice/class_template_specialization.erb +10 -0
- data/lib/ruby-bindgen/generators/rice/constant.erb +9 -0
- data/lib/ruby-bindgen/generators/rice/constructor.erb +1 -0
- data/lib/ruby-bindgen/generators/rice/conversion_function.erb +4 -0
- data/lib/ruby-bindgen/generators/rice/cxx_iterator_method.erb +1 -0
- data/lib/ruby-bindgen/generators/rice/cxx_method.erb +6 -0
- data/lib/ruby-bindgen/generators/rice/enum_constant_decl.erb +7 -0
- data/lib/ruby-bindgen/generators/rice/enum_decl.erb +6 -0
- data/lib/ruby-bindgen/generators/rice/field_decl.erb +8 -0
- data/lib/ruby-bindgen/generators/rice/function.erb +6 -0
- data/lib/ruby-bindgen/generators/rice/function_pointer.rb +68 -0
- data/lib/ruby-bindgen/generators/rice/incomplete_class.erb +3 -0
- data/lib/ruby-bindgen/generators/rice/iterator_alias.erb +1 -0
- data/lib/ruby-bindgen/generators/rice/iterator_collector.rb +159 -0
- data/lib/ruby-bindgen/generators/rice/namespace.erb +5 -0
- data/lib/ruby-bindgen/generators/rice/non_member_operator_binary.erb +4 -0
- data/lib/ruby-bindgen/generators/rice/non_member_operator_inspect.erb +6 -0
- data/lib/ruby-bindgen/generators/rice/non_member_operator_unary.erb +4 -0
- data/lib/ruby-bindgen/generators/rice/operator[].erb +4 -0
- data/lib/ruby-bindgen/generators/rice/project.cpp.erb +18 -0
- data/lib/ruby-bindgen/generators/rice/project.hpp.erb +13 -0
- data/lib/ruby-bindgen/generators/rice/reference_qualifier.rb +495 -0
- data/lib/ruby-bindgen/generators/rice/rice.rb +1724 -0
- data/lib/ruby-bindgen/generators/rice/rice_include.hpp.erb +7 -0
- data/lib/ruby-bindgen/generators/rice/signature_builder.rb +230 -0
- data/lib/ruby-bindgen/generators/rice/template_resolver.rb +585 -0
- data/lib/ruby-bindgen/generators/rice/translation_unit.cpp.erb +40 -0
- data/lib/ruby-bindgen/generators/rice/translation_unit.hpp.erb +7 -0
- data/lib/ruby-bindgen/generators/rice/translation_unit.ipp.erb +3 -0
- data/lib/ruby-bindgen/generators/rice/type_index.rb +117 -0
- data/lib/ruby-bindgen/generators/rice/type_speller.rb +509 -0
- data/lib/ruby-bindgen/generators/rice/union.erb +5 -0
- data/lib/ruby-bindgen/generators/rice/variable.erb +5 -0
- data/lib/ruby-bindgen/inputter.rb +54 -0
- data/lib/ruby-bindgen/name_mapper.rb +65 -0
- data/lib/ruby-bindgen/namer.rb +138 -0
- data/lib/ruby-bindgen/outputter.rb +40 -0
- data/lib/ruby-bindgen/parser.rb +82 -0
- data/lib/ruby-bindgen/refinements/cursor.rb +57 -0
- data/lib/ruby-bindgen/refinements/string.rb +41 -0
- data/lib/ruby-bindgen/symbol_candidates.rb +282 -0
- data/lib/ruby-bindgen/symbol_entry.rb +21 -0
- data/lib/ruby-bindgen/symbols.rb +107 -0
- data/lib/ruby-bindgen/type_pointer_formatter.rb +35 -0
- data/lib/ruby-bindgen/version.rb +3 -0
- data/lib/ruby-bindgen.rb +19 -0
- data/ruby-bindgen.gemspec +52 -0
- metadata +260 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module RubyBindgen
|
|
2
|
+
class NameMapper
|
|
3
|
+
attr_reader :exact, :regex
|
|
4
|
+
protected :exact, :regex
|
|
5
|
+
|
|
6
|
+
def initialize(mappings = [])
|
|
7
|
+
@exact = {}
|
|
8
|
+
@regex = []
|
|
9
|
+
mappings.each do |pattern, replacement|
|
|
10
|
+
if pattern.is_a?(Regexp)
|
|
11
|
+
@regex << [pattern, replacement]
|
|
12
|
+
else
|
|
13
|
+
@exact[pattern] = replacement
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Factory: parses YAML config array of {from:, to:} entries
|
|
19
|
+
def self.from_config(mappings)
|
|
20
|
+
parsed = mappings.filter_map do |entry|
|
|
21
|
+
key = entry[:from] || entry["from"]
|
|
22
|
+
replacement = entry[:to] || entry["to"]
|
|
23
|
+
next if key.nil?
|
|
24
|
+
if key.start_with?('/') && key.end_with?('/') && key.length > 2
|
|
25
|
+
[Regexp.new(key[1..-2]), replacement]
|
|
26
|
+
else
|
|
27
|
+
[key, replacement]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
new(parsed)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Look up a name, trying each candidate in order.
|
|
34
|
+
# Returns the replacement value or nil.
|
|
35
|
+
def lookup(*candidates)
|
|
36
|
+
# O(1) exact match
|
|
37
|
+
candidates.each do |name|
|
|
38
|
+
result = @exact[name]
|
|
39
|
+
return result if result
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Regex fallback
|
|
43
|
+
@regex.each do |pattern, replacement|
|
|
44
|
+
candidates.each do |name|
|
|
45
|
+
if (m = pattern.match(name))
|
|
46
|
+
if replacement.is_a?(String)
|
|
47
|
+
return replacement.gsub(/\\(\d+)/) { m[$1.to_i] }
|
|
48
|
+
else
|
|
49
|
+
return replacement
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
nil
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Merge two tables. Other's entries override self's.
|
|
58
|
+
def merge(other)
|
|
59
|
+
exact_mappings = @exact.merge(other.exact).map { |k, v| [k, v] }
|
|
60
|
+
regex_mappings = other.regex + @regex
|
|
61
|
+
self.class.new(exact_mappings + regex_mappings)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
module RubyBindgen
|
|
2
|
+
class Namer
|
|
3
|
+
def initialize(rename_types = NameMapper.new, rename_methods = NameMapper.new,
|
|
4
|
+
conversion_mappings = NameMapper.new)
|
|
5
|
+
@rename_types = rename_types
|
|
6
|
+
@rename_methods = rename_methods
|
|
7
|
+
@conversion_mappings = conversion_mappings
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def ruby(cursor)
|
|
11
|
+
if cursor.anonymous? && cursor.kind == :cursor_namespace
|
|
12
|
+
"Anonymous"
|
|
13
|
+
elsif cursor.anonymous?
|
|
14
|
+
definer = cursor.anonymous_definer
|
|
15
|
+
definer ? definer.spelling.camelize : nil
|
|
16
|
+
else
|
|
17
|
+
case cursor.kind
|
|
18
|
+
when :cursor_translation_unit
|
|
19
|
+
basename = File.basename(cursor.spelling, File.extname(cursor.spelling))
|
|
20
|
+
basename.camelize
|
|
21
|
+
when :cursor_conversion_function
|
|
22
|
+
ruby_conversion_function(cursor)
|
|
23
|
+
when :cursor_function, :cursor_cxx_method
|
|
24
|
+
ruby_operator_or_method(cursor)
|
|
25
|
+
when :cursor_struct, :cursor_union, :cursor_enum_decl, :cursor_class_decl, :cursor_namespace
|
|
26
|
+
@rename_types.lookup(cursor.spelling) || cursor.spelling.camelize
|
|
27
|
+
when :cursor_field_decl
|
|
28
|
+
cursor.spelling.underscore
|
|
29
|
+
when :cursor_typedef_decl
|
|
30
|
+
cursor.underlying_type.declaration.invalid? ?
|
|
31
|
+
cursor.spelling.underscore :
|
|
32
|
+
ruby(cursor.underlying_type.declaration)
|
|
33
|
+
when :cursor_variable
|
|
34
|
+
cursor.spelling.camelize
|
|
35
|
+
else
|
|
36
|
+
cursor.spelling.underscore
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def cruby(cursor)
|
|
42
|
+
case cursor.kind
|
|
43
|
+
when :cursor_class_decl
|
|
44
|
+
"rb_c#{cursor.type.spelling.sub("(anonymous namespace)", "Anonymous").camelize}"
|
|
45
|
+
when :cursor_struct
|
|
46
|
+
"rb_c#{cursor.type.spelling.camelize}"
|
|
47
|
+
when :cursor_enum_decl
|
|
48
|
+
"rb_c#{cursor.type.spelling.sub("(anonymous namespace)", "Anonymous").camelize}"
|
|
49
|
+
when :cursor_namespace
|
|
50
|
+
if cursor.anonymous?
|
|
51
|
+
# qualified_name is nil for translation units
|
|
52
|
+
"rb_m#{cursor.semantic_parent.qualified_name&.camelize}Anonymous"
|
|
53
|
+
else
|
|
54
|
+
"rb_m#{cursor.qualified_name.camelize}"
|
|
55
|
+
end
|
|
56
|
+
when :cursor_typedef_decl
|
|
57
|
+
"rb_c#{cursor.spelling.sub("(anonymous namespace)", "Anonymous").camelize}"
|
|
58
|
+
when :cursor_translation_unit
|
|
59
|
+
"Class(rb_cObject)"
|
|
60
|
+
else
|
|
61
|
+
cursor.spelling.underscore
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Apply rename_types to a generated Ruby class name.
|
|
66
|
+
# Accepts one or more candidate names to try (e.g., raw C++ name, camelized name).
|
|
67
|
+
# Returns the first match, or the last candidate as fallback.
|
|
68
|
+
def apply_rename_types(*names)
|
|
69
|
+
@rename_types.lookup(*names) || names.last
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# Handle conversion functions like operator int(), operator float()
|
|
75
|
+
def ruby_conversion_function(cursor)
|
|
76
|
+
# Use result_type.spelling to get the original typedef name (e.g., "size_t")
|
|
77
|
+
# rather than the resolved type from cursor.spelling (e.g., "unsigned long")
|
|
78
|
+
type_name = cursor.type.result_type.spelling
|
|
79
|
+
|
|
80
|
+
# Look up Ruby convention for this type
|
|
81
|
+
suffix = @conversion_mappings.lookup(type_name)
|
|
82
|
+
if suffix
|
|
83
|
+
"to_#{suffix}"
|
|
84
|
+
else
|
|
85
|
+
# Handle std::basic_string variants (std::string is a typedef)
|
|
86
|
+
if type_name.include?('basic_string')
|
|
87
|
+
return "to_s"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Clean up the type name for Ruby method naming:
|
|
91
|
+
# - Remove reference/pointer markers
|
|
92
|
+
# - Use only the final type name (after last ::)
|
|
93
|
+
# - Convert to underscore style
|
|
94
|
+
clean_name = type_name.gsub(/[&*]/, '').strip
|
|
95
|
+
clean_name = clean_name.split('::').last || clean_name
|
|
96
|
+
# Remove template parameters for cleaner method names
|
|
97
|
+
clean_name = clean_name.sub(/<.*>$/, '')
|
|
98
|
+
"to_#{clean_name.underscore}"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Handle operators and regular methods
|
|
103
|
+
def ruby_operator_or_method(cursor)
|
|
104
|
+
# Check rename_methods first (includes operator mappings merged by generator).
|
|
105
|
+
# SymbolCandidates yields the cursor spelling, qualified-name forms (with
|
|
106
|
+
# the inline-namespace-collapsed parent fallback), template display
|
|
107
|
+
# variants, and parameter-list forms — the same set Symbols uses, so a
|
|
108
|
+
# rename rule expressed in any form Symbols would accept also matches here.
|
|
109
|
+
symbol_candidates = SymbolCandidates.new(cursor)
|
|
110
|
+
result = @rename_methods.lookup(*symbol_candidates)
|
|
111
|
+
|
|
112
|
+
case result
|
|
113
|
+
when String then return result
|
|
114
|
+
when Proc then return result.call(cursor)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# No mapping — apply heuristics for non-operators only
|
|
118
|
+
spelling = cursor.spelling
|
|
119
|
+
unless spelling.start_with?('operator')
|
|
120
|
+
is_bool = cursor.type.result_type.spelling == "bool"
|
|
121
|
+
# Methods starting with "is" prefix (isFoo or is_foo) are predicates
|
|
122
|
+
is_prefixed = spelling.match?(/^is[A-Z_]/)
|
|
123
|
+
|
|
124
|
+
# Add ? suffix for predicate methods:
|
|
125
|
+
# 1. bool return with no parameters, OR
|
|
126
|
+
# 2. bool return with "is" prefix (regardless of parameters)
|
|
127
|
+
if is_bool && (cursor.type.args_size == 0 || is_prefixed)
|
|
128
|
+
return "#{spelling.underscore.sub(/^is_/, "")}?"
|
|
129
|
+
else
|
|
130
|
+
return spelling.underscore
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Unknown operator with no mapping
|
|
135
|
+
raise "Unknown operator: #{spelling}"
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'pathname'
|
|
5
|
+
|
|
6
|
+
module RubyBindgen
|
|
7
|
+
class Outputter
|
|
8
|
+
attr_reader :base_path, :output_paths
|
|
9
|
+
|
|
10
|
+
def initialize(base_path)
|
|
11
|
+
@base_path = base_path
|
|
12
|
+
@output_paths = {}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def output_path(relative_path)
|
|
16
|
+
File.expand_path(File.join(self.base_path, relative_path))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def write(relative_path, content)
|
|
20
|
+
path = self.output_path(relative_path)
|
|
21
|
+
cleaned = cleanup_whitespace(content)
|
|
22
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
23
|
+
File.open(path, "wb") do |file|
|
|
24
|
+
file << cleaned
|
|
25
|
+
end
|
|
26
|
+
@output_paths[path] = cleaned
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
# Clean up whitespace issues in generated content:
|
|
32
|
+
# - Collapse multiple consecutive blank lines to single blank line
|
|
33
|
+
# - Remove blank lines before closing braces
|
|
34
|
+
def cleanup_whitespace(content)
|
|
35
|
+
content = content.gsub(/\n{3,}/, "\n\n")
|
|
36
|
+
content = content.gsub(/\n\n(\s*\})/, "\n\\1")
|
|
37
|
+
content
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'pathname'
|
|
4
|
+
|
|
5
|
+
module RubyBindgen
|
|
6
|
+
class Parser
|
|
7
|
+
class ParseError < RuntimeError
|
|
8
|
+
attr_reader :path, :details
|
|
9
|
+
|
|
10
|
+
def initialize(path, details: [])
|
|
11
|
+
@path = path
|
|
12
|
+
@details = details
|
|
13
|
+
super(build_message(path, details))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def build_message(path, details)
|
|
19
|
+
return "Failed to parse: #{path}" if details.empty?
|
|
20
|
+
|
|
21
|
+
formatted_details = details.map { |detail| " #{detail}" }.join("\n")
|
|
22
|
+
"Parse errors in #{path}:\n#{formatted_details}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_reader :inputter, :clang_args
|
|
27
|
+
|
|
28
|
+
def initialize(inputter, clang_args, libclang: nil)
|
|
29
|
+
@inputter = inputter
|
|
30
|
+
@clang_args = clang_args
|
|
31
|
+
|
|
32
|
+
# Set libclang path before loading ffi-clang (it reads ENV on load)
|
|
33
|
+
ENV['LIBCLANG'] = libclang if libclang
|
|
34
|
+
|
|
35
|
+
# Lazy-load ffi-clang and its refinements so CMake format doesn't need libclang
|
|
36
|
+
require 'ffi/clang'
|
|
37
|
+
require 'ruby-bindgen/refinements/cursor'
|
|
38
|
+
|
|
39
|
+
@index = FFI::Clang::Index.new(exclude_declarations_from_pch: false, display_diagnostics: true)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def generate(visitor)
|
|
43
|
+
visitor.visit_start
|
|
44
|
+
|
|
45
|
+
STDOUT << "\n" << "Processing:" << "\n"
|
|
46
|
+
self.inputter.each do |path, relative_path|
|
|
47
|
+
STDOUT << " " << path << "\n"
|
|
48
|
+
begin
|
|
49
|
+
translation_unit = parse_translation_unit(path)
|
|
50
|
+
rescue ParseError => error
|
|
51
|
+
raise unless visitor.respond_to?(:visit_parse_error)
|
|
52
|
+
|
|
53
|
+
visitor.visit_parse_error(path, relative_path, error)
|
|
54
|
+
next
|
|
55
|
+
end
|
|
56
|
+
visitor.visit_translation_unit(translation_unit, path, relative_path)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
visitor.visit_end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def parse_translation_unit(path)
|
|
65
|
+
translation_unit = @index.parse_translation_unit(path, self.clang_args, [],
|
|
66
|
+
[:detailed_preprocessing_record, :skip_function_bodies])
|
|
67
|
+
|
|
68
|
+
raise ParseError.new(path) if translation_unit.nil?
|
|
69
|
+
|
|
70
|
+
check_diagnostics(translation_unit, path)
|
|
71
|
+
translation_unit
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def check_diagnostics(translation_unit, path)
|
|
75
|
+
errors = translation_unit.diagnostics.select { |d| d.severity == :fatal || d.severity == :error }
|
|
76
|
+
return if errors.empty?
|
|
77
|
+
|
|
78
|
+
details = errors.map { |d| "#{d.severity}: #{d.spelling}" }
|
|
79
|
+
raise ParseError.new(path, details: details)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module FFI
|
|
2
|
+
module Clang
|
|
3
|
+
class Cursor
|
|
4
|
+
def self.namer
|
|
5
|
+
@namer || raise("Namer not set — generator must call Cursor.namer= before processing")
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.namer=(value)
|
|
9
|
+
@namer = value
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def ruby_name
|
|
13
|
+
self.class.namer.ruby(self)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def cruby_name
|
|
17
|
+
self.class.namer.cruby(self)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def anonymous_definer
|
|
21
|
+
return nil unless self.anonymous?
|
|
22
|
+
|
|
23
|
+
if self.kind == :cursor_namespace
|
|
24
|
+
return self
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# This could be a typedef of a field declaration in union or struct
|
|
28
|
+
#
|
|
29
|
+
# typedef struct {
|
|
30
|
+
# union {
|
|
31
|
+
# char *sdata;
|
|
32
|
+
# int idata;
|
|
33
|
+
# } u;
|
|
34
|
+
# } F_TextItemT;
|
|
35
|
+
_, result = self.translation_unit.cursor.find do |child, parent|
|
|
36
|
+
self.eql?(child) && (parent.kind == :cursor_field_decl ||
|
|
37
|
+
parent.kind == :cursor_typedef_decl)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Or this could be a variable declaration
|
|
41
|
+
#
|
|
42
|
+
# struct {
|
|
43
|
+
# int Value;
|
|
44
|
+
# uint8_t String[4];
|
|
45
|
+
# } MyArray_t;
|
|
46
|
+
unless result
|
|
47
|
+
variables = self.translation_unit.cursor.find_by_kind(true, :cursor_variable)
|
|
48
|
+
result = variables.find do |variable|
|
|
49
|
+
self.eql?(variable.type.declaration)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
result
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
class String
|
|
2
|
+
# Taken from ActiveSupport with modifications
|
|
3
|
+
def upcase_first
|
|
4
|
+
if self.length > 0
|
|
5
|
+
self[0].upcase.concat(self[1..-1])
|
|
6
|
+
else
|
|
7
|
+
self
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Taken from ActiveSupport with modifications
|
|
12
|
+
def camelize()
|
|
13
|
+
if self.match?(/\A[a-z\d]*\z/)
|
|
14
|
+
return self.capitalize
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
return self if self.match?(/\A[A-Z]+\z/)
|
|
18
|
+
|
|
19
|
+
input = self.match?(/\A[A-Z_0-9]*\z/) ? self.downcase : self
|
|
20
|
+
string = input.sub(/^[a-z\d]*/) { |match| match.capitalize! || match }
|
|
21
|
+
string.gsub!(/\/, ::/)
|
|
22
|
+
string.gsub!(/(?:_|-|\.|::|,| |\<|\>|(\/))([a-z\d]*)/i) do
|
|
23
|
+
word = $2
|
|
24
|
+
word[0] = word[0].capitalize || word[0] unless word.empty?
|
|
25
|
+
$1 ? "::#{word}" : word
|
|
26
|
+
end
|
|
27
|
+
string
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Taken from ActiveSupport with modifications
|
|
31
|
+
def underscore
|
|
32
|
+
return self unless /[A-Z-]|::/.match?(self)
|
|
33
|
+
word = self.gsub("::".freeze, "/".freeze)
|
|
34
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2'.freeze)
|
|
35
|
+
word.gsub!(/([a-z])([A-Z])/, '\1_\2'.freeze)
|
|
36
|
+
word.gsub!(/([a-z])(\d+[A-Z])/, '\1_\2'.freeze)
|
|
37
|
+
word.tr!("-".freeze, "_".freeze)
|
|
38
|
+
word.downcase!
|
|
39
|
+
word
|
|
40
|
+
end
|
|
41
|
+
end
|