rbs 1.6.2 → 1.7.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +0 -4
  3. data/.gitignore +1 -0
  4. data/CHANGELOG.md +6 -0
  5. data/Gemfile +1 -0
  6. data/Rakefile +7 -22
  7. data/core/kernel.rbs +4 -4
  8. data/core/trace_point.rbs +1 -1
  9. data/ext/rbs/extension/constants.c +140 -0
  10. data/ext/rbs/extension/constants.h +72 -0
  11. data/ext/rbs/extension/extconf.rb +3 -0
  12. data/ext/rbs/extension/lexer.c +1070 -0
  13. data/ext/rbs/extension/lexer.h +145 -0
  14. data/ext/rbs/extension/location.c +295 -0
  15. data/ext/rbs/extension/location.h +59 -0
  16. data/ext/rbs/extension/main.c +9 -0
  17. data/ext/rbs/extension/parser.c +2418 -0
  18. data/ext/rbs/extension/parser.h +23 -0
  19. data/ext/rbs/extension/parserstate.c +313 -0
  20. data/ext/rbs/extension/parserstate.h +141 -0
  21. data/ext/rbs/extension/rbs_extension.h +40 -0
  22. data/ext/rbs/extension/ruby_objs.c +585 -0
  23. data/ext/rbs/extension/ruby_objs.h +46 -0
  24. data/ext/rbs/extension/unescape.c +65 -0
  25. data/goodcheck.yml +1 -1
  26. data/lib/rbs/ast/comment.rb +0 -12
  27. data/lib/rbs/buffer.rb +4 -0
  28. data/lib/rbs/cli.rb +5 -8
  29. data/lib/rbs/collection/sources/git.rb +18 -3
  30. data/lib/rbs/errors.rb +14 -1
  31. data/lib/rbs/location.rb +221 -217
  32. data/lib/rbs/location_aux.rb +108 -0
  33. data/lib/rbs/locator.rb +10 -7
  34. data/lib/rbs/parser_aux.rb +24 -0
  35. data/lib/rbs/types.rb +2 -3
  36. data/lib/rbs/version.rb +1 -1
  37. data/lib/rbs/writer.rb +4 -2
  38. data/lib/rbs.rb +3 -7
  39. data/rbs.gemspec +2 -1
  40. data/sig/ancestor_builder.rbs +2 -2
  41. data/sig/annotation.rbs +2 -2
  42. data/sig/comment.rbs +7 -7
  43. data/sig/constant_table.rbs +1 -1
  44. data/sig/declarations.rbs +9 -9
  45. data/sig/definition.rbs +1 -1
  46. data/sig/definition_builder.rbs +2 -2
  47. data/sig/errors.rbs +30 -25
  48. data/sig/location.rbs +42 -79
  49. data/sig/locator.rbs +2 -2
  50. data/sig/members.rbs +7 -7
  51. data/sig/method_types.rbs +3 -3
  52. data/sig/parser.rbs +11 -21
  53. data/sig/types.rbs +45 -27
  54. data/sig/writer.rbs +1 -1
  55. data/stdlib/json/0/json.rbs +3 -3
  56. metadata +24 -6
  57. data/lib/rbs/parser.rb +0 -3614
@@ -0,0 +1,46 @@
1
+ #ifndef RBS__RUBY_OBJS_H
2
+ #define RBS__RUBY_OBJS_H
3
+
4
+ #include "ruby.h"
5
+
6
+ VALUE rbs_alias(VALUE typename, VALUE location);
7
+ VALUE rbs_ast_annotation(VALUE string, VALUE location);
8
+ VALUE rbs_ast_comment(VALUE string, VALUE location);
9
+ VALUE rbs_ast_decl_alias(VALUE name, VALUE type, VALUE annotations, VALUE location, VALUE comment);
10
+ VALUE rbs_ast_decl_class_super(VALUE name, VALUE args, VALUE location);
11
+ VALUE rbs_ast_decl_class(VALUE name, VALUE type_params, VALUE super_class, VALUE members, VALUE annotations, VALUE location, VALUE comment);
12
+ VALUE rbs_ast_decl_constant(VALUE name, VALUE type, VALUE location, VALUE comment);
13
+ VALUE rbs_ast_decl_global(VALUE name, VALUE type, VALUE location, VALUE comment);
14
+ VALUE rbs_ast_decl_interface(VALUE name, VALUE type_params, VALUE members, VALUE annotations, VALUE location, VALUE comment);
15
+ VALUE rbs_ast_decl_module_self(VALUE name, VALUE args, VALUE location);
16
+ VALUE rbs_ast_decl_module_type_params_param(VALUE name, VALUE variance, VALUE skip_validation, VALUE location);
17
+ VALUE rbs_ast_decl_module_type_params();
18
+ VALUE rbs_ast_decl_module(VALUE name, VALUE type_params, VALUE self_types, VALUE members, VALUE annotations, VALUE location, VALUE comment);
19
+ VALUE rbs_ast_members_alias(VALUE new_name, VALUE old_name, VALUE kind, VALUE annotations, VALUE location, VALUE comment);
20
+ VALUE rbs_ast_members_attribute(VALUE klass, VALUE name, VALUE type, VALUE ivar_name, VALUE kind, VALUE annotations, VALUE location, VALUE comment);
21
+ VALUE rbs_ast_members_method_definition(VALUE name, VALUE kind, VALUE types, VALUE annotations, VALUE location, VALUE comment, VALUE overload);
22
+ VALUE rbs_ast_members_mixin(VALUE klass, VALUE name, VALUE args, VALUE annotations, VALUE location, VALUE comment);
23
+ VALUE rbs_ast_members_variable(VALUE klass, VALUE name, VALUE type, VALUE location, VALUE comment);
24
+ VALUE rbs_ast_members_visibility(VALUE klass, VALUE location);
25
+ VALUE rbs_base_type(VALUE klass, VALUE location);
26
+ VALUE rbs_block(VALUE type, VALUE required);
27
+ VALUE rbs_class_instance(VALUE typename, VALUE type_args, VALUE location);
28
+ VALUE rbs_class_singleton(VALUE typename, VALUE location);
29
+ VALUE rbs_function_param(VALUE type, VALUE name, VALUE location);
30
+ VALUE rbs_function(VALUE required_positional_params, VALUE optional_positional_params, VALUE rest_positional_params, VALUE trailing_positional_params, VALUE required_keywords, VALUE optional_keywords, VALUE rest_keywords, VALUE return_type);
31
+ VALUE rbs_interface(VALUE typename, VALUE type_args, VALUE location);
32
+ VALUE rbs_intersection(VALUE types, VALUE location);
33
+ VALUE rbs_literal(VALUE literal, VALUE location);
34
+ VALUE rbs_method_type(VALUE type_params, VALUE type, VALUE block, VALUE location);
35
+ VALUE rbs_namespace(VALUE path, VALUE absolute);
36
+ VALUE rbs_optional(VALUE type, VALUE location);
37
+ VALUE rbs_proc(VALUE function, VALUE block, VALUE location);
38
+ VALUE rbs_record(VALUE fields, VALUE location);
39
+ VALUE rbs_tuple(VALUE types, VALUE location);
40
+ VALUE rbs_type_name(VALUE namespace, VALUE name);
41
+ VALUE rbs_union(VALUE types, VALUE location);
42
+ VALUE rbs_variable(VALUE name, VALUE location);
43
+
44
+ void pp(VALUE object);
45
+
46
+ #endif
@@ -0,0 +1,65 @@
1
+ #include "rbs_extension.h"
2
+
3
+ static VALUE REGEXP = 0;
4
+ static VALUE HASH = 0;
5
+
6
+ static const char *regexp_str = "\\\\[abefnrstv\"]";
7
+
8
+ static ID gsub = 0;
9
+
10
+ void rbs_unescape_string(VALUE string) {
11
+ if (!REGEXP) {
12
+ REGEXP = rb_reg_new(regexp_str, strlen(regexp_str), 0);
13
+ rb_global_variable(&REGEXP);
14
+ }
15
+
16
+ if (!gsub) {
17
+ gsub = rb_intern("gsub!");
18
+ }
19
+
20
+ if (!HASH) {
21
+ HASH = rb_hash_new();
22
+ rb_hash_aset(HASH, rb_str_new_literal("\\a"), rb_str_new_literal("\a"));
23
+ rb_hash_aset(HASH, rb_str_new_literal("\\b"), rb_str_new_literal("\b"));
24
+ rb_hash_aset(HASH, rb_str_new_literal("\\e"), rb_str_new_literal("\e"));
25
+ rb_hash_aset(HASH, rb_str_new_literal("\\f"), rb_str_new_literal("\f"));
26
+ rb_hash_aset(HASH, rb_str_new_literal("\\n"), rb_str_new_literal("\n"));
27
+ rb_hash_aset(HASH, rb_str_new_literal("\\r"), rb_str_new_literal("\r"));
28
+ rb_hash_aset(HASH, rb_str_new_literal("\\s"), rb_str_new_literal(" "));
29
+ rb_hash_aset(HASH, rb_str_new_literal("\\t"), rb_str_new_literal("\t"));
30
+ rb_hash_aset(HASH, rb_str_new_literal("\\v"), rb_str_new_literal("\v"));
31
+ rb_hash_aset(HASH, rb_str_new_literal("\\\""), rb_str_new_literal("\""));
32
+ rb_global_variable(&HASH);
33
+ }
34
+
35
+ rb_funcall(string, gsub, 2, REGEXP, HASH);
36
+ }
37
+
38
+ VALUE rbs_unquote_string(parserstate *state, range rg, int offset_bytes) {
39
+ VALUE string = state->lexstate->string;
40
+ rb_encoding *enc = rb_enc_get(string);
41
+
42
+ unsigned int first_char = rb_enc_mbc_to_codepoint(
43
+ RSTRING_PTR(string) + rg.start.byte_pos + offset_bytes,
44
+ RSTRING_END(string),
45
+ enc
46
+ );
47
+
48
+ int byte_length = rg.end.byte_pos - rg.start.byte_pos - offset_bytes;
49
+
50
+ if (first_char == '"' || first_char == '\'' || first_char == '`') {
51
+ int bs = rb_enc_codelen(first_char, enc);
52
+ offset_bytes += bs;
53
+ byte_length -= 2 * bs;
54
+ }
55
+
56
+ char *buffer = RSTRING_PTR(state->lexstate->string) + rg.start.byte_pos + offset_bytes;
57
+ VALUE str = rb_enc_str_new(buffer, byte_length, enc);
58
+
59
+ if (first_char == '\"') {
60
+ rbs_unescape_string(str);
61
+ }
62
+
63
+ return str;
64
+ }
65
+
data/goodcheck.yml CHANGED
@@ -40,7 +40,7 @@ rules:
40
40
  glob:
41
41
  - "**/*.rbs"
42
42
  justification:
43
- - When you strictly want `true | false`.
43
+ - When you strictly want `true | false`. Use `bool` in this case.
44
44
  pass:
45
45
  - "(arg: boolish)"
46
46
  - "{ () -> boolish }"
@@ -22,18 +22,6 @@ module RBS
22
22
  def to_json(state = _ = nil)
23
23
  { string: string, location: location }.to_json(state)
24
24
  end
25
-
26
- def concat(string:, location:)
27
- @string.concat string
28
-
29
- if loc = @location
30
- loc.concat location
31
- else
32
- @location = location
33
- end
34
-
35
- self
36
- end
37
25
  end
38
26
  end
39
27
  end
data/lib/rbs/buffer.rb CHANGED
@@ -46,5 +46,9 @@ module RBS
46
46
  def last_position
47
47
  content.size
48
48
  end
49
+
50
+ def inspect
51
+ "#<RBS::Buffer:#{__id__} @name=#{name}, @content=#{content.bytesize} bytes, @lines=#{lines.size} lines,>"
52
+ end
49
53
  end
50
54
  end
data/lib/rbs/cli.rb CHANGED
@@ -749,14 +749,11 @@ Examples:
749
749
  args.each do |path|
750
750
  path = Pathname(path)
751
751
  loader.each_file(path, skip_hidden: false, immediate: true) do |file_path|
752
- Parser.parse_signature(file_path.read)
753
- rescue RBS::Parser::SyntaxError => ex
754
- loc = ex.error_value.location
755
- stdout.puts "#{file_path}:#{loc.start_line}:#{loc.start_column}: parse error on value: (#{ex.token_str})"
756
- syntax_error = true
757
- rescue RBS::Parser::SemanticsError => ex
758
- loc = ex.location
759
- stdout.puts "#{file_path}:#{loc.start_line}:#{loc.start_column}: #{ex.original_message}"
752
+ RBS.logger.info "Parsing #{file_path}..."
753
+ buffer = Buffer.new(content: file_path.read, name: file_path)
754
+ Parser.parse_signature(buffer)
755
+ rescue RBS::ParsingError => ex
756
+ stdout.puts ex.message
760
757
  syntax_error = true
761
758
  end
762
759
  end
@@ -1,5 +1,6 @@
1
1
  require 'digest/sha2'
2
2
  require 'open3'
3
+ require 'find'
3
4
 
4
5
  module RBS
5
6
  module Collection
@@ -52,12 +53,26 @@ module RBS
52
53
  private def _install(dest:, config_entry:)
53
54
  gem_name = config_entry['name']
54
55
  version = config_entry['version'] or raise
55
- dest = dest.join(gem_name)
56
+ dest = dest.join(gem_name, version)
56
57
  dest.mkpath
57
58
  src = gem_repo_dir.join(gem_name, version)
58
59
 
59
- FileUtils.cp_r(src, dest)
60
- dest.join(version, METADATA_FILENAME).write(YAML.dump(config_entry))
60
+ cp_r(src, dest)
61
+ dest.join(METADATA_FILENAME).write(YAML.dump(config_entry))
62
+ end
63
+
64
+ private def cp_r(src, dest)
65
+ Find.find(src) do |file_src|
66
+ file_src = Pathname(file_src)
67
+
68
+ # Skip file if it starts with _, such as _test/
69
+ Find.prune if file_src.basename.to_s.start_with?('_')
70
+
71
+ file_src_relative = file_src.relative_path_from(src)
72
+ file_dest = dest.join(file_src_relative)
73
+ file_dest.dirname.mkpath
74
+ FileUtils.copy_entry(file_src, file_dest, false, true) unless file_src.directory?
75
+ end
61
76
  end
62
77
 
63
78
  def to_lockfile
data/lib/rbs/errors.rb CHANGED
@@ -15,10 +15,23 @@ module RBS
15
15
  end
16
16
 
17
17
  class ErrorBase < StandardError; end
18
- class ParsingError < ErrorBase; end
19
18
  class LoadingError < ErrorBase; end
20
19
  class DefinitionError < ErrorBase; end
21
20
 
21
+ class ParsingError < ErrorBase
22
+ attr_reader :location
23
+ attr_reader :error_message
24
+ attr_reader :token_type
25
+
26
+ def initialize(location, error_message, token_type)
27
+ @location = location
28
+ @error_message = error_message
29
+ @token_type = token_type
30
+
31
+ super "#{Location.to_string location}: Syntax error: #{error_message}, token=`#{location.source}` (#{token_type})"
32
+ end
33
+ end
34
+
22
35
  class InvalidTypeApplicationError < DefinitionError
23
36
  attr_reader :type_name
24
37
  attr_reader :args
data/lib/rbs/location.rb CHANGED
@@ -1,217 +1,221 @@
1
- module RBS
2
- class Location
3
- attr_reader :buffer
4
- attr_reader :start_pos
5
- attr_reader :end_pos
6
-
7
- def initialize(buffer:, start_pos:, end_pos:)
8
- @buffer = buffer
9
- @start_pos = start_pos
10
- @end_pos = end_pos
11
- end
12
-
13
- def inspect
14
- "#<#{self.class}:#{self.__id__} @buffer=#{buffer.name}, @pos=#{start_pos}...#{end_pos}, source='#{source.lines.first}', start_line=#{start_line}, start_column=#{start_column}>"
15
- end
16
-
17
- def name
18
- buffer.name
19
- end
20
-
21
- def start_line
22
- start_loc[0]
23
- end
24
-
25
- def start_column
26
- start_loc[1]
27
- end
28
-
29
- def end_line
30
- end_loc[0]
31
- end
32
-
33
- def end_column
34
- end_loc[1]
35
- end
36
-
37
- def start_loc
38
- @start_loc ||= buffer.pos_to_loc(start_pos)
39
- end
40
-
41
- def end_loc
42
- @end_loc ||= buffer.pos_to_loc(end_pos)
43
- end
44
-
45
- def range
46
- start_pos...end_pos
47
- end
48
-
49
- def source
50
- @source ||= buffer.content[start_pos...end_pos] or raise
51
- end
52
-
53
- def to_s
54
- "#{name || "-"}:#{start_line}:#{start_column}...#{end_line}:#{end_column}"
55
- end
56
-
57
- def self.to_string(location, default: "*:*:*...*:*")
58
- location&.to_s || default
59
- end
60
-
61
- def ==(other)
62
- other.is_a?(Location) &&
63
- other.buffer == buffer &&
64
- other.start_pos == start_pos &&
65
- other.end_pos == end_pos
66
- end
67
-
68
- def +(other)
69
- if other
70
- raise "Invalid concat: buffer=#{buffer.name}, other.buffer=#{other.buffer.name}" unless other.buffer == buffer
71
-
72
- self.class.new(buffer: buffer,
73
- start_pos: start_pos,
74
- end_pos: other.end_pos)
75
- else
76
- self
77
- end
78
- end
79
-
80
- def concat(*others)
81
- others.each { |other| self << other }
82
- self
83
- end
84
-
85
- def <<(other)
86
- if other
87
- raise "Invalid concat: buffer=#{buffer.name}, other.buffer=#{other.buffer.name}" unless other.buffer == buffer
88
- @end_pos = other.end_pos
89
- @source = nil
90
- @end_loc = nil
91
- end
92
- self
93
- end
94
-
95
- def pred?(loc)
96
- loc.is_a?(Location) &&
97
- loc.name == name &&
98
- loc.start_pos == end_pos
99
- end
100
-
101
- def to_json(state = _ = nil)
102
- {
103
- start: {
104
- line: start_line,
105
- column: start_column
106
- },
107
- end: {
108
- line: end_line,
109
- column: end_column
110
- },
111
- buffer: {
112
- name: name&.to_s
113
- }
114
- }.to_json(state)
115
- end
116
-
117
- def with_children(required: {}, optional: {})
118
- # @type var required: Hash[Symbol, Range[Integer] | Location]
119
- # @type var optional: Hash[Symbol, Range[Integer] | Location | nil]
120
-
121
- this = WithChildren.new(buffer: buffer, start_pos: start_pos, end_pos: end_pos)
122
-
123
- req = required.transform_values do |value|
124
- case value
125
- when Location
126
- value.range
127
- else
128
- value
129
- end
130
- end
131
-
132
- opt = optional.transform_values do |value|
133
- case value
134
- when Location
135
- value.range
136
- else
137
- value
138
- end
139
- end
140
-
141
- this.required_children.merge!(req)
142
- this.optional_children.merge!(opt)
143
-
144
- this
145
- end
146
-
147
- class WithChildren < Location
148
- attr_reader :required_children, :optional_children
149
-
150
- def initialize(buffer:, start_pos:, end_pos:)
151
- super(buffer: buffer, start_pos: start_pos, end_pos: end_pos)
152
-
153
- @optional_children = {}
154
- @required_children = {}
155
- end
156
-
157
- def initialize_copy(from)
158
- required_children.merge!(from.required_children)
159
- optional_children.merge!(from.optional_children)
160
- self
161
- end
162
-
163
- def [](key)
164
- case
165
- when required_children.key?(_ = key)
166
- range = required_children[_ = key]
167
- Location.new(buffer: buffer, start_pos: range.begin, end_pos: range.end)
168
- when optional_children.key?(_ = key)
169
- range = required_children[_ = key] || optional_children[_ = key]
170
- if range
171
- Location.new(buffer: buffer, start_pos: range.begin, end_pos: range.end)
172
- end
173
- else
174
- raise "Unknown key given: `#{key}`"
175
- end
176
- end
177
-
178
- def merge_required(hash)
179
- this = dup
180
-
181
- h = hash.transform_values do |value|
182
- case value
183
- when Range
184
- value
185
- when Location
186
- value.range
187
- else
188
- raise
189
- end
190
- end
191
-
192
- this.required_children.merge!(h)
193
-
194
- this
195
- end
196
-
197
- def merge_optional(hash)
198
- this = dup
199
-
200
- h = hash.transform_values do |value|
201
- case value
202
- when Range
203
- value
204
- when Location
205
- value.range
206
- else
207
- nil
208
- end
209
- end
210
-
211
- this.optional_children.merge!(h)
212
-
213
- this
214
- end
215
- end
216
- end
217
- end
1
+ # module RBS
2
+ # class Location
3
+ # attr_reader :buffer
4
+ # attr_reader :start_pos
5
+ # attr_reader :end_pos
6
+
7
+ # def initialize(buffer, start_pos, end_pos)
8
+ # @buffer = buffer
9
+ # @start_pos = start_pos
10
+ # @end_pos = end_pos
11
+ # end
12
+
13
+ # # def self.new(buffer_ = nil, start_pos_ = nil, end_pos_ = nil)
14
+ # # __skip__ = super(buffer_, start_pos_, end_pos_)
15
+ # # end
16
+
17
+ # # def inspect
18
+ # # "#<#{self.class}:#{self.__id__} @buffer=#{buffer.name}, @pos=#{start_pos}...#{end_pos}, source='#{source.lines.first}', start_line=#{start_line}, start_column=#{start_column}>"
19
+ # # end
20
+
21
+ # def name
22
+ # buffer.name
23
+ # end
24
+
25
+ # def start_line
26
+ # start_loc[0]
27
+ # end
28
+
29
+ # def start_column
30
+ # start_loc[1]
31
+ # end
32
+
33
+ # def end_line
34
+ # end_loc[0]
35
+ # end
36
+
37
+ # def end_column
38
+ # end_loc[1]
39
+ # end
40
+
41
+ # def start_loc
42
+ # @start_loc ||= buffer.pos_to_loc(start_pos)
43
+ # end
44
+
45
+ # def end_loc
46
+ # @end_loc ||= buffer.pos_to_loc(end_pos)
47
+ # end
48
+
49
+ # def range
50
+ # start_pos...end_pos
51
+ # end
52
+
53
+ # def source
54
+ # @source ||= buffer.content[start_pos...end_pos] or raise
55
+ # end
56
+
57
+ # def to_s
58
+ # "#{name || "-"}:#{start_line}:#{start_column}...#{end_line}:#{end_column}"
59
+ # end
60
+
61
+ # def self.to_string(location, default: "*:*:*...*:*")
62
+ # location&.to_s || default
63
+ # end
64
+
65
+ # def ==(other)
66
+ # other.is_a?(Location) &&
67
+ # other.buffer == buffer &&
68
+ # other.start_pos == start_pos &&
69
+ # other.end_pos == end_pos
70
+ # end
71
+
72
+ # def +(other)
73
+ # if other
74
+ # raise "Invalid concat: buffer=#{buffer.name}, other.buffer=#{other.buffer.name}" unless other.buffer == buffer
75
+
76
+ # self.class.new(buffer: buffer,
77
+ # start_pos: start_pos,
78
+ # end_pos: other.end_pos)
79
+ # else
80
+ # self
81
+ # end
82
+ # end
83
+
84
+ # def concat(*others)
85
+ # others.each { |other| self << other }
86
+ # self
87
+ # end
88
+
89
+ # def <<(other)
90
+ # if other
91
+ # raise "Invalid concat: buffer=#{buffer.name}, other.buffer=#{other.buffer.name}" unless other.buffer == buffer
92
+ # @end_pos = other.end_pos
93
+ # @source = nil
94
+ # @end_loc = nil
95
+ # end
96
+ # self
97
+ # end
98
+
99
+ # def pred?(loc)
100
+ # loc.is_a?(Location) &&
101
+ # loc.name == name &&
102
+ # loc.start_pos == end_pos
103
+ # end
104
+
105
+ # def to_json(state = _ = nil)
106
+ # {
107
+ # start: {
108
+ # line: start_line,
109
+ # column: start_column
110
+ # },
111
+ # end: {
112
+ # line: end_line,
113
+ # column: end_column
114
+ # },
115
+ # buffer: {
116
+ # name: name&.to_s
117
+ # }
118
+ # }.to_json(state)
119
+ # end
120
+
121
+ # def with_children(required: {}, optional: {})
122
+ # # @type var required: Hash[Symbol, Range[Integer] | Location]
123
+ # # @type var optional: Hash[Symbol, Range[Integer] | Location | nil]
124
+
125
+ # this = WithChildren.new(buffer: buffer, start_pos: start_pos, end_pos: end_pos)
126
+
127
+ # req = required.transform_values do |value|
128
+ # case value
129
+ # when Location
130
+ # value.range
131
+ # else
132
+ # value
133
+ # end
134
+ # end
135
+
136
+ # opt = optional.transform_values do |value|
137
+ # case value
138
+ # when Location
139
+ # value.range
140
+ # else
141
+ # value
142
+ # end
143
+ # end
144
+
145
+ # this.required_children.merge!(req)
146
+ # this.optional_children.merge!(opt)
147
+
148
+ # this
149
+ # end
150
+
151
+ # class WithChildren < Location
152
+ # attr_reader :required_children, :optional_children
153
+
154
+ # def initialize(buffer, start_pos, end_pos)
155
+ # super(buffer, start_pos, end_pos)
156
+
157
+ # @optional_children = {}
158
+ # @required_children = {}
159
+ # end
160
+
161
+ # def initialize_copy(from)
162
+ # required_children.merge!(from.required_children)
163
+ # optional_children.merge!(from.optional_children)
164
+ # self
165
+ # end
166
+
167
+ # def [](key)
168
+ # case
169
+ # when required_children.key?(_ = key)
170
+ # range = required_children[_ = key]
171
+ # Location.new(buffer: buffer, start_pos: range.begin, end_pos: range.end)
172
+ # when optional_children.key?(_ = key)
173
+ # range = required_children[_ = key] || optional_children[_ = key]
174
+ # if range
175
+ # Location.new(buffer: buffer, start_pos: range.begin, end_pos: range.end)
176
+ # end
177
+ # else
178
+ # raise "Unknown key given: `#{key}`"
179
+ # end
180
+ # end
181
+
182
+ # def merge_required(hash)
183
+ # this = dup
184
+
185
+ # h = hash.transform_values do |value|
186
+ # case value
187
+ # when Range
188
+ # value
189
+ # when Location
190
+ # value.range
191
+ # else
192
+ # raise
193
+ # end
194
+ # end
195
+
196
+ # this.required_children.merge!(h)
197
+
198
+ # this
199
+ # end
200
+
201
+ # def merge_optional(hash)
202
+ # this = dup
203
+
204
+ # h = hash.transform_values do |value|
205
+ # case value
206
+ # when Range
207
+ # value
208
+ # when Location
209
+ # value.range
210
+ # else
211
+ # nil
212
+ # end
213
+ # end
214
+
215
+ # this.optional_children.merge!(h)
216
+
217
+ # this
218
+ # end
219
+ # end
220
+ # end
221
+ # end