Skip to content

Commit 0f262a8

Browse files
authored
Merge pull request #14 from sue445/feature/config_file
Make `Data` configurable
2 parents 448015a + 7ea2d54 commit 0f262a8

File tree

12 files changed

+176
-33
lines changed

12 files changed

+176
-33
lines changed

.yardopts

+1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
--hide-void-return
44
-
55
CHANGELOG.md
6+
CONFIG.md
67
LICENSE.txt

CONFIG.md

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Configuration file specification
2+
3+
## Example
4+
```yaml
5+
function:
6+
include_name:
7+
- !ruby/regexp /^rb_/i
8+
- !ruby/regexp /^rstring_/i
9+
10+
exclude_name:
11+
# deprecated functions
12+
- !ruby/regexp /^rb_check_safe_str$/i
13+
- !ruby/regexp /^rb_clear_constant_cache$/i
14+
- !ruby/regexp /^rb_clone_setup$/i
15+
16+
pointer_hint:
17+
RSTRING_PTR:
18+
self: raw
19+
rb_data_object_make:
20+
4: sref
21+
22+
struct:
23+
include_name:
24+
- !ruby/regexp /^rb_/i
25+
- re_registers
26+
27+
exclude_name:
28+
- rb_data_type_struct
29+
30+
type:
31+
include_name:
32+
- !ruby/regexp /^rb_/i
33+
- !ruby/regexp /^st_/i
34+
- ID
35+
- VALUE
36+
37+
exclude_name: []
38+
39+
enum:
40+
include_name:
41+
- ruby_value_type
42+
- rb_io_wait_readwrite
43+
44+
exclude_name: []
45+
```
46+
47+
## Full configuration file
48+
[config/default.yml](config/default.yml) is used by https://github.com/sue445/go-gem-wrapper to generate bindings for Go.
49+
50+
## `function.include_name`, `struct.include_name`, `type.include_name`, `enum.include_name`
51+
Return functions and structures that match the condition with a [RubyHeaderParser::Parser](lib/ruby_header_parser/parser.rb)
52+
53+
e.g.
54+
55+
```yaml
56+
struct:
57+
include_name:
58+
- !ruby/regexp /^rb_/i
59+
- re_registers
60+
```
61+
62+
Elements in the array accept the following
63+
64+
* `String`: Exact match
65+
* `Regexp`(`!ruby/regexp`): Regular expression match
66+
67+
## `function.exclude_name`, `struct.exclude_name`, `type.exclude_name`, `enum.exclude_name`
68+
Doesn't return functions and structures that match the condition with a [RubyHeaderParser::Parser](lib/ruby_header_parser/parser.rb)
69+
70+
e.g.
71+
72+
```yaml
73+
function:
74+
exclude_name:
75+
- !ruby/regexp /^rb_check_safe_str$/i
76+
- rb_scan_args_bad_format
77+
```
78+
79+
`exclude_name` is preferred over `include_name`
80+
81+
Other specifications are the same as `include_name`
82+
83+
## `function.pointer_hint`
84+
Provide a hint if the function argument type is a pointer
85+
86+
e.g.
87+
88+
```yaml
89+
function:
90+
pointer_hint:
91+
RSTRING_PTR: # function name (Exact match)
92+
self: raw
93+
rb_data_object_make:
94+
4: sref
95+
```
96+
97+
### Function arguments (`1`, `2`, `3`, ...)
98+
* `ref` (default)
99+
* normal pointer
100+
* e.g. 1st argument of `VALUE rb_const_list(void*)`
101+
* `in_ref`
102+
* input only pointer
103+
* e.g. 2nd argument of `void rb_define_variable(const char *name, VALUE *var)`
104+
* `sref`
105+
* special one for multiple pointer
106+
* e.g. 3rd argument of `rb_data_typed_object_make(VALUE klass, const rb_data_type_t *type, void **datap, size_t size)`
107+
* `array`
108+
* array
109+
* e.g. 4th argument of `VALUE rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv)`
110+
* `ref_array`
111+
* array of pointer
112+
* e.g. 10th argument of `rb_scan_args_set(int kw_flag, int argc, const VALUE *argv, int n_lead, int n_opt, int n_trail, bool f_var, bool f_hash, bool f_block, VALUE *vars[], RB_UNUSED_VAR(const char *fmt), RB_UNUSED_VAR(int varc))`
113+
* `str_array`
114+
* array of string
115+
* e.g. 2nd argument of `int rb_find_file_ext(VALUE *feature, const char *const *exts)`
116+
* `function`
117+
* function pointer
118+
* e.g. 4th argument of `void rb_define_method(VALUE klass, const char *mid, VALUE (*func)(), int arity)`
119+
120+
### Function return value (`self`)
121+
* `ref` (default)
122+
* normal pointer
123+
* e.g. Return value of `int *rb_errno_ptr(void)`
124+
* `raw`
125+
* In many cases `char*` can be interpreted as a string. But if `raw` is specified, it suppresses interpretation as a string
126+
* e.g. Return value of `char* RSTRING_PTR(VALUE str)`

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ See below for details.
5050

5151
https://sue445.github.io/ruby_header_parser/RubyHeaderParser/Parser.html
5252

53+
See [CONFIG.md](CONFIG.md) for config file details
54+
5355
## Development
5456

5557
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
File renamed without changes.

lib/ruby_header_parser.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module RubyHeaderParser
1010
class Error < StandardError; end
1111

1212
autoload :ArgumentDefinition, "ruby_header_parser/argument_definition"
13-
autoload :Data, "ruby_header_parser/data"
13+
autoload :Config, "ruby_header_parser/config"
1414
autoload :EnumDefinition, "ruby_header_parser/enum_definition"
1515
autoload :FunctionDefinition, "ruby_header_parser/function_definition"
1616
autoload :Parser, "ruby_header_parser/parser"

lib/ruby_header_parser/data.rb renamed to lib/ruby_header_parser/config.rb

+7-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
# frozen_string_literal: true
22

33
module RubyHeaderParser
4-
# Manager for `data.yml`
5-
class Data
4+
# Manager for config file
5+
class Config
66
# @!attribute [r] data
77
# @return [Hash]
88
attr_reader :data
99

10-
def initialize
11-
yaml = File.read(File.join(__dir__.to_s, "data.yml"))
10+
# @param config_file [String]
11+
#
12+
# @note See [CONFIG.md](../file.CONFIG.html) for config file details
13+
def initialize(config_file)
14+
yaml = File.read(config_file)
1215
@data = YAML.safe_load(yaml, aliases: true, permitted_classes: [Regexp])
1316
end
1417

lib/ruby_header_parser/parser.rb

+16-11
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ class Parser # rubocop:disable Metrics/ClassLength
1616
attr_reader :dist_preprocessed_header_file
1717

1818
# @!attribute [r] data
19-
# @return [RubyHeaderParser::Data]
20-
attr_reader :data
19+
# @return [RubyHeaderParser::Config]
20+
attr_reader :config
2121

2222
DEFAULT_HEADER_FILE = "#{RbConfig::CONFIG["rubyhdrdir"]}/ruby.h".freeze
2323

@@ -30,15 +30,20 @@ class Parser # rubocop:disable Metrics/ClassLength
3030
# @param include_paths [Array<String>]
3131
# @param dist_preprocessed_header_file [String,nil] Destination path to the output of preprocessed ruby.h.
3232
# (default: `"#{Dir.tmpdir}/ruby_preprocessed.h"`)
33+
# @param config_file [String,nil] Path to config file (default: `config/default.yml`)
3334
#
3435
# @note `dist_preprocessed_header_file` is used as the output destination for temporary files when the parser
3536
# is executed
37+
#
38+
# @note See [CONFIG.md](../file.CONFIG.html) for config file details
3639
def initialize(dist_preprocessed_header_file: nil, header_file: DEFAULT_HEADER_FILE,
37-
include_paths: DEFAULT_INCLUDE_PATHS)
40+
include_paths: DEFAULT_INCLUDE_PATHS, config_file: nil)
3841
@header_file = header_file
3942
@include_paths = include_paths
4043
@dist_preprocessed_header_file = dist_preprocessed_header_file || File.join(Dir.tmpdir, "ruby_preprocessed.h")
41-
@data = Data.new
44+
45+
config_file ||= File.expand_path("../../config/default.yml", __dir__.to_s)
46+
@config = Config.new(config_file)
4247
end
4348

4449
# @return [Array<RubyHeaderParser::FunctionDefinition>]
@@ -59,7 +64,7 @@ def extract_struct_definitions
5964
parts = line.split("\t")
6065

6166
struct_name = parts[0]
62-
next unless data.should_generate_struct?(struct_name)
67+
next unless config.should_generate_struct?(struct_name)
6368

6469
definitions << StructDefinition.new(
6570
name: struct_name,
@@ -76,7 +81,7 @@ def extract_type_definitions
7681

7782
type_name = parts[0]
7883

79-
next unless data.should_generate_type?(type_name)
84+
next unless config.should_generate_type?(type_name)
8085

8186
definitions << TypeDefinition.new(
8287
name: type_name,
@@ -97,7 +102,7 @@ def extract_enum_definitions
97102

98103
value = parts[0]
99104

100-
next unless data.should_generate_enum?(enum_name)
105+
next unless config.should_generate_enum?(enum_name)
101106

102107
hash[enum_name] ||= EnumDefinition.new(name: enum_name)
103108
hash[enum_name].values << value
@@ -131,7 +136,7 @@ def generate_function_definition_from_line(line:, kind:, is_parse_multiline_defi
131136
function_name = parts[0]
132137
filepath = parts[1]
133138

134-
return nil unless data.should_generate_function?(function_name)
139+
return nil unless config.should_generate_function?(function_name)
135140

136141
return nil unless parts[3] == kind
137142

@@ -228,7 +233,7 @@ def create_typeref(definition:, function_name:, typeref_field:, filepath:, line_
228233
typeref_pointer = nil
229234
if typeref_type.match?(/\*+$/)
230235
typeref_type = typeref_type.gsub(/\*+$/, "").strip
231-
typeref_pointer = data.function_self_pointer_hint(function_name)
236+
typeref_pointer = config.function_self_pointer_hint(function_name)
232237
end
233238

234239
TyperefDefinition.new(type: typeref_type, pointer: typeref_pointer)
@@ -319,12 +324,12 @@ def analyze_argument_type(function_name:, arg_pos:, parts:)
319324
case original_type
320325
when /\*+$/
321326
type = original_type.gsub(/\*+$/, "").strip
322-
pointer = data.function_arg_pointer_hint(function_name:, pos: arg_pos)
327+
pointer = config.function_arg_pointer_hint(function_name:, pos: arg_pos)
323328

324329
when /^void\s*/, /\(.*\)/
325330
# function pointer (e.g. void *(*func)(void *)) is treated as `void*`
326331
type = "void"
327-
pointer = data.function_arg_pointer_hint(function_name:, pos: arg_pos)
332+
pointer = config.function_arg_pointer_hint(function_name:, pos: arg_pos)
328333

329334
else
330335
type = original_type

sig/ruby_header_parser/data.rbs renamed to sig/ruby_header_parser/config.rbs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
module RubyHeaderParser
2-
class Data
2+
class Config
33
attr_reader data: Hash[untyped, untyped]
44

5-
def initialize: () -> void
5+
def initialize: (String config_file) -> void
66

77
def function_arg_pointer_hint: (function_name: String, pos: Integer) -> (:ref | :array | :ref_array | :function | :sref | :str_array | :in_ref)
88

sig/ruby_header_parser/parser.rbs

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,16 @@ module RubyHeaderParser
33
attr_reader header_file: String
44
attr_reader include_paths: Array[String]
55
attr_reader dist_preprocessed_header_file: String
6-
attr_reader data: Data
6+
attr_reader config: Config
77

88
DEFAULT_HEADER_FILE: String
99
DEFAULT_INCLUDE_PATHS: Array[String]
1010

1111
def initialize: (
1212
?dist_preprocessed_header_file: String?,
1313
?header_file: String,
14-
?include_paths: Array[String]
14+
?include_paths: Array[String],
15+
?config_file: String?
1516
) -> void
1617

1718
def extract_function_definitions: () -> Array[FunctionDefinition]

spec/ruby_header_parser/data_spec.rb renamed to spec/ruby_header_parser/config_spec.rb

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
# frozen_string_literal: true
22

3-
RSpec.describe RubyHeaderParser::Data do
4-
let(:data) { RubyHeaderParser::Data.new }
3+
RSpec.describe RubyHeaderParser::Config do
4+
let(:config) { RubyHeaderParser::Config.new(default_config_file) }
55

66
describe "#function_arg_pointer_hint" do
7-
subject { data.function_arg_pointer_hint(function_name:, pos:) }
7+
subject { config.function_arg_pointer_hint(function_name:, pos:) }
88

9-
context "found in data.yml" do
9+
context "found in config/default.yml" do
1010
let(:function_name) { "rb_funcallv" }
1111
let(:pos) { 4 }
1212

1313
it { should eq :array }
1414
end
1515

16-
context "not found in data.yml" do
16+
context "not found in config/default.yml" do
1717
let(:function_name) { "rb_funcallv" }
1818
let(:pos) { 5 }
1919

@@ -22,23 +22,23 @@
2222
end
2323

2424
describe "#function_self_pointer_hint" do
25-
subject { data.function_self_pointer_hint(function_name) }
25+
subject { config.function_self_pointer_hint(function_name) }
2626

27-
context "found in data.yml" do
27+
context "found in default.yml" do
2828
let(:function_name) { "RSTRING_PTR" }
2929

3030
it { should eq :raw }
3131
end
3232

33-
context "not found in data.yml" do
33+
context "not found in config/default.yml" do
3434
let(:function_name) { "rb_class2name" }
3535

3636
it { should eq :ref }
3737
end
3838
end
3939

4040
describe "#should_generate_function?" do
41-
subject { data.should_generate_function?(function_name) }
41+
subject { config.should_generate_function?(function_name) }
4242

4343
context "rb function (denied)" do
4444
let(:function_name) { "rb_check_safe_str" }
@@ -60,7 +60,7 @@
6060
end
6161

6262
describe "#should_generate_struct?" do
63-
subject { data.should_generate_struct?(struct_name) }
63+
subject { config.should_generate_struct?(struct_name) }
6464

6565
context "rb struct" do
6666
let(:struct_name) { "rb_random_struct" }
@@ -76,7 +76,7 @@
7676
end
7777

7878
describe "#should_generate_type?" do
79-
subject { data.should_generate_type?(type_name) }
79+
subject { config.should_generate_type?(type_name) }
8080

8181
context "rb type" do
8282
let(:type_name) { "rb_data_type_t" }
@@ -98,7 +98,7 @@
9898
end
9999

100100
describe "#should_generate_enum?" do
101-
subject { data.should_generate_enum?(enum_name) }
101+
subject { config.should_generate_enum?(enum_name) }
102102

103103
context "ruby_value_type" do
104104
let(:enum_name) { "ruby_value_type" }

spec/ruby_header_parser/parser_spec.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
RSpec.describe RubyHeaderParser::Parser do
44
include_context "uses temp dir"
55

6-
let(:parser) { RubyHeaderParser::Parser.new(dist_preprocessed_header_file:) }
6+
let(:parser) { RubyHeaderParser::Parser.new(dist_preprocessed_header_file:, config_file: default_config_file) }
77
let(:dist_preprocessed_header_file) { File.join(temp_dir, "ruby_preprocessed.h") }
88

99
describe "#extract_function_definitions" do

spec/spec_helper.rb

+5
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,8 @@
2626
meta[:aggregate_failures] = true
2727
end
2828
end
29+
30+
# @return [String]
31+
def default_config_file
32+
File.expand_path("../config/default.yml", __dir__)
33+
end

0 commit comments

Comments
 (0)