prism 0.16.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -1
  3. data/Makefile +6 -0
  4. data/README.md +1 -1
  5. data/config.yml +50 -35
  6. data/docs/fuzzing.md +1 -1
  7. data/docs/serialization.md +28 -29
  8. data/ext/prism/api_node.c +802 -770
  9. data/ext/prism/api_pack.c +20 -9
  10. data/ext/prism/extension.c +464 -162
  11. data/ext/prism/extension.h +1 -1
  12. data/include/prism/ast.h +3173 -763
  13. data/include/prism/defines.h +32 -9
  14. data/include/prism/diagnostic.h +36 -3
  15. data/include/prism/enc/pm_encoding.h +118 -28
  16. data/include/prism/node.h +38 -13
  17. data/include/prism/options.h +204 -0
  18. data/include/prism/pack.h +44 -33
  19. data/include/prism/parser.h +445 -200
  20. data/include/prism/prettyprint.h +12 -1
  21. data/include/prism/regexp.h +16 -2
  22. data/include/prism/util/pm_buffer.h +94 -16
  23. data/include/prism/util/pm_char.h +162 -48
  24. data/include/prism/util/pm_constant_pool.h +126 -32
  25. data/include/prism/util/pm_list.h +68 -38
  26. data/include/prism/util/pm_memchr.h +18 -3
  27. data/include/prism/util/pm_newline_list.h +70 -27
  28. data/include/prism/util/pm_state_stack.h +25 -7
  29. data/include/prism/util/pm_string.h +115 -27
  30. data/include/prism/util/pm_string_list.h +25 -6
  31. data/include/prism/util/pm_strncasecmp.h +32 -0
  32. data/include/prism/util/pm_strpbrk.h +31 -17
  33. data/include/prism/version.h +27 -2
  34. data/include/prism.h +224 -31
  35. data/lib/prism/compiler.rb +6 -3
  36. data/lib/prism/debug.rb +23 -7
  37. data/lib/prism/dispatcher.rb +33 -18
  38. data/lib/prism/dsl.rb +10 -5
  39. data/lib/prism/ffi.rb +132 -80
  40. data/lib/prism/lex_compat.rb +25 -15
  41. data/lib/prism/mutation_compiler.rb +10 -5
  42. data/lib/prism/node.rb +370 -135
  43. data/lib/prism/node_ext.rb +1 -1
  44. data/lib/prism/node_inspector.rb +1 -1
  45. data/lib/prism/pack.rb +79 -40
  46. data/lib/prism/parse_result/comments.rb +7 -2
  47. data/lib/prism/parse_result/newlines.rb +4 -0
  48. data/lib/prism/parse_result.rb +150 -30
  49. data/lib/prism/pattern.rb +11 -0
  50. data/lib/prism/ripper_compat.rb +28 -10
  51. data/lib/prism/serialize.rb +86 -54
  52. data/lib/prism/visitor.rb +10 -3
  53. data/lib/prism.rb +20 -2
  54. data/prism.gemspec +4 -2
  55. data/rbi/prism.rbi +104 -60
  56. data/rbi/prism_static.rbi +16 -2
  57. data/sig/prism.rbs +72 -43
  58. data/sig/prism_static.rbs +14 -1
  59. data/src/diagnostic.c +56 -53
  60. data/src/enc/pm_big5.c +1 -0
  61. data/src/enc/pm_euc_jp.c +1 -0
  62. data/src/enc/pm_gbk.c +1 -0
  63. data/src/enc/pm_shift_jis.c +1 -0
  64. data/src/enc/pm_tables.c +316 -80
  65. data/src/enc/pm_unicode.c +53 -8
  66. data/src/enc/pm_windows_31j.c +1 -0
  67. data/src/node.c +334 -321
  68. data/src/options.c +170 -0
  69. data/src/prettyprint.c +74 -47
  70. data/src/prism.c +1642 -856
  71. data/src/regexp.c +151 -95
  72. data/src/serialize.c +44 -20
  73. data/src/token_type.c +3 -1
  74. data/src/util/pm_buffer.c +45 -15
  75. data/src/util/pm_char.c +103 -57
  76. data/src/util/pm_constant_pool.c +51 -21
  77. data/src/util/pm_list.c +12 -4
  78. data/src/util/pm_memchr.c +5 -3
  79. data/src/util/pm_newline_list.c +20 -12
  80. data/src/util/pm_state_stack.c +9 -3
  81. data/src/util/pm_string.c +95 -85
  82. data/src/util/pm_string_list.c +14 -15
  83. data/src/util/pm_strncasecmp.c +10 -3
  84. data/src/util/pm_strpbrk.c +25 -19
  85. metadata +5 -3
  86. data/docs/prism.png +0 -0
@@ -3,7 +3,7 @@
3
3
  # Here we are reopening the prism module to provide methods on nodes that aren't
4
4
  # templated and are meant as convenience methods.
5
5
  module Prism
6
- module RegularExpressionOptions
6
+ module RegularExpressionOptions # :nodoc:
7
7
  # Returns a numeric value that represents the flags that were used to create
8
8
  # the regular expression.
9
9
  def options
@@ -3,7 +3,7 @@
3
3
  module Prism
4
4
  # This object is responsible for generating the output for the inspect method
5
5
  # implementations of child nodes.
6
- class NodeInspector
6
+ class NodeInspector # :nodoc:
7
7
  attr_reader :prefix, :output
8
8
 
9
9
  def initialize(prefix = "")
data/lib/prism/pack.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Prism
4
+ # A parser for the pack template language.
4
5
  module Pack
5
6
  %i[
6
7
  SPACE
@@ -54,9 +55,36 @@ module Prism
54
55
  const_set(const, const)
55
56
  end
56
57
 
58
+ # A directive in the pack template language.
57
59
  class Directive
58
- attr_reader :version, :variant, :source, :type, :signed, :endian, :size, :length_type, :length
60
+ # A symbol representing the version of Ruby.
61
+ attr_reader :version
59
62
 
63
+ # A symbol representing whether or not we are packing or unpacking.
64
+ attr_reader :variant
65
+
66
+ # A byteslice of the source string that this directive represents.
67
+ attr_reader :source
68
+
69
+ # The type of the directive.
70
+ attr_reader :type
71
+
72
+ # The type of signedness of the directive.
73
+ attr_reader :signed
74
+
75
+ # The type of endianness of the directive.
76
+ attr_reader :endian
77
+
78
+ # The size of the directive.
79
+ attr_reader :size
80
+
81
+ # The length type of this directive (used for integers).
82
+ attr_reader :length_type
83
+
84
+ # The length of this directive (used for integers).
85
+ attr_reader :length
86
+
87
+ # Initialize a new directive with the given values.
60
88
  def initialize(version, variant, source, type, signed, endian, size, length_type, length)
61
89
  @version = version
62
90
  @variant = variant
@@ -69,38 +97,42 @@ module Prism
69
97
  @length = length
70
98
  end
71
99
 
100
+ # The descriptions of the various types of endianness.
72
101
  ENDIAN_DESCRIPTIONS = {
73
- AGNOSTIC_ENDIAN: 'agnostic',
74
- LITTLE_ENDIAN: 'little-endian (VAX)',
75
- BIG_ENDIAN: 'big-endian (network)',
76
- NATIVE_ENDIAN: 'native-endian',
77
- ENDIAN_NA: 'n/a'
102
+ AGNOSTIC_ENDIAN: "agnostic",
103
+ LITTLE_ENDIAN: "little-endian (VAX)",
104
+ BIG_ENDIAN: "big-endian (network)",
105
+ NATIVE_ENDIAN: "native-endian",
106
+ ENDIAN_NA: "n/a"
78
107
  }
79
108
 
109
+ # The descriptions of the various types of signedness.
80
110
  SIGNED_DESCRIPTIONS = {
81
- UNSIGNED: 'unsigned',
82
- SIGNED: 'signed',
83
- SIGNED_NA: 'n/a'
111
+ UNSIGNED: "unsigned",
112
+ SIGNED: "signed",
113
+ SIGNED_NA: "n/a"
84
114
  }
85
115
 
116
+ # The descriptions of the various types of sizes.
86
117
  SIZE_DESCRIPTIONS = {
87
- SIZE_SHORT: 'short',
88
- SIZE_INT: 'int-width',
89
- SIZE_LONG: 'long',
90
- SIZE_LONG_LONG: 'long long',
91
- SIZE_8: '8-bit',
92
- SIZE_16: '16-bit',
93
- SIZE_32: '32-bit',
94
- SIZE_64: '64-bit',
95
- SIZE_P: 'pointer-width'
118
+ SIZE_SHORT: "short",
119
+ SIZE_INT: "int-width",
120
+ SIZE_LONG: "long",
121
+ SIZE_LONG_LONG: "long long",
122
+ SIZE_8: "8-bit",
123
+ SIZE_16: "16-bit",
124
+ SIZE_32: "32-bit",
125
+ SIZE_64: "64-bit",
126
+ SIZE_P: "pointer-width"
96
127
  }
97
128
 
129
+ # Provide a human-readable description of the directive.
98
130
  def describe
99
131
  case type
100
132
  when SPACE
101
- 'whitespace'
133
+ "whitespace"
102
134
  when COMMENT
103
- 'comment'
135
+ "comment"
104
136
  when INTEGER
105
137
  if size == SIZE_8
106
138
  base = "#{SIGNED_DESCRIPTIONS[signed]} #{SIZE_DESCRIPTIONS[size]} integer"
@@ -115,58 +147,65 @@ module Prism
115
147
  base
116
148
  end
117
149
  when LENGTH_MAX
118
- base + ', as many as possible'
150
+ base + ", as many as possible"
119
151
  end
120
152
  when UTF8
121
- 'UTF-8 character'
153
+ "UTF-8 character"
122
154
  when BER
123
- 'BER-compressed integer'
155
+ "BER-compressed integer"
124
156
  when FLOAT
125
157
  "#{SIZE_DESCRIPTIONS[size]} #{ENDIAN_DESCRIPTIONS[endian]} float"
126
158
  when STRING_SPACE_PADDED
127
- 'arbitrary binary string (space padded)'
159
+ "arbitrary binary string (space padded)"
128
160
  when STRING_NULL_PADDED
129
- 'arbitrary binary string (null padded, count is width)'
161
+ "arbitrary binary string (null padded, count is width)"
130
162
  when STRING_NULL_TERMINATED
131
- 'arbitrary binary string (null padded, count is width), except that null is added with *'
163
+ "arbitrary binary string (null padded, count is width), except that null is added with *"
132
164
  when STRING_MSB
133
- 'bit string (MSB first)'
165
+ "bit string (MSB first)"
134
166
  when STRING_LSB
135
- 'bit string (LSB first)'
167
+ "bit string (LSB first)"
136
168
  when STRING_HEX_HIGH
137
- 'hex string (high nibble first)'
169
+ "hex string (high nibble first)"
138
170
  when STRING_HEX_LOW
139
- 'hex string (low nibble first)'
171
+ "hex string (low nibble first)"
140
172
  when STRING_UU
141
- 'UU-encoded string'
173
+ "UU-encoded string"
142
174
  when STRING_MIME
143
- 'quoted printable, MIME encoding'
175
+ "quoted printable, MIME encoding"
144
176
  when STRING_BASE64
145
- 'base64 encoded string'
177
+ "base64 encoded string"
146
178
  when STRING_FIXED
147
- 'pointer to a structure (fixed-length string)'
179
+ "pointer to a structure (fixed-length string)"
148
180
  when STRING_POINTER
149
- 'pointer to a null-terminated string'
181
+ "pointer to a null-terminated string"
150
182
  when MOVE
151
- 'move to absolute position'
183
+ "move to absolute position"
152
184
  when BACK
153
- 'back up a byte'
185
+ "back up a byte"
154
186
  when NULL
155
- 'null byte'
187
+ "null byte"
156
188
  else
157
189
  raise
158
190
  end
159
191
  end
160
192
  end
161
193
 
194
+ # The result of parsing a pack template.
162
195
  class Format
163
- attr_reader :directives, :encoding
196
+ # A list of the directives in the template.
197
+ attr_reader :directives
198
+
199
+ # The encoding of the template.
200
+ attr_reader :encoding
164
201
 
202
+ # Create a new Format with the given directives and encoding.
165
203
  def initialize(directives, encoding)
166
204
  @directives = directives
167
205
  @encoding = encoding
168
206
  end
169
207
 
208
+ # Provide a human-readable description of the format.
170
209
  def describe
171
210
  source_width = directives.map { |d| d.source.inspect.length }.max
172
211
  directive_lines = directives.map do |directive|
@@ -178,7 +217,7 @@ module Prism
178
217
  " #{source.ljust(source_width)} #{directive.describe}"
179
218
  end
180
219
 
181
- (['Directives:'] + directive_lines + ['Encoding:', " #{encoding}"]).join("\n")
220
+ (["Directives:"] + directive_lines + ["Encoding:", " #{encoding}"]).join("\n")
182
221
  end
183
222
  end
184
223
  end
@@ -19,7 +19,7 @@ module Prism
19
19
  class Comments
20
20
  # A target for attaching comments that is based on a specific node's
21
21
  # location.
22
- class NodeTarget
22
+ class NodeTarget # :nodoc:
23
23
  attr_reader :node
24
24
 
25
25
  def initialize(node)
@@ -46,7 +46,7 @@ module Prism
46
46
 
47
47
  # A target for attaching comments that is based on a location field on a
48
48
  # node. For example, the `end` token of a ClassNode.
49
- class LocationTarget
49
+ class LocationTarget # :nodoc:
50
50
  attr_reader :location
51
51
 
52
52
  def initialize(location)
@@ -70,12 +70,17 @@ module Prism
70
70
  end
71
71
  end
72
72
 
73
+ # The parse result that we are attaching comments to.
73
74
  attr_reader :parse_result
74
75
 
76
+ # Create a new Comments object that will attach comments to the given
77
+ # parse result.
75
78
  def initialize(parse_result)
76
79
  @parse_result = parse_result
77
80
  end
78
81
 
82
+ # Attach the comments to their respective locations in the tree by
83
+ # mutating the parse result.
79
84
  def attach!
80
85
  parse_result.comments.each do |comment|
81
86
  preceding, enclosing, following = nearest_targets(parse_result.value, comment)
@@ -18,10 +18,12 @@ module Prism
18
18
  # MarkNewlinesVisitor, since that visitor is responsible for marking the
19
19
  # newlines for JRuby/TruffleRuby.
20
20
  class Newlines < Visitor
21
+ # Create a new Newlines visitor with the given newline offsets.
21
22
  def initialize(newline_marked)
22
23
  @newline_marked = newline_marked
23
24
  end
24
25
 
26
+ # Permit block/lambda nodes to mark newlines within themselves.
25
27
  def visit_block_node(node)
26
28
  old_newline_marked = @newline_marked
27
29
  @newline_marked = Array.new(old_newline_marked.size, false)
@@ -35,6 +37,7 @@ module Prism
35
37
 
36
38
  alias_method :visit_lambda_node, :visit_block_node
37
39
 
40
+ # Mark if/unless nodes as newlines.
38
41
  def visit_if_node(node)
39
42
  node.set_newline_flag(@newline_marked)
40
43
  super(node)
@@ -42,6 +45,7 @@ module Prism
42
45
 
43
46
  alias_method :visit_unless_node, :visit_if_node
44
47
 
48
+ # Permit statements lists to mark newlines within themselves.
45
49
  def visit_statements_node(node)
46
50
  node.body.each do |child|
47
51
  child.set_newline_flag(@newline_marked)
@@ -5,20 +5,52 @@ module Prism
5
5
  # conjunction with locations to allow them to resolve line numbers and source
6
6
  # ranges.
7
7
  class Source
8
- attr_reader :source, :offsets
8
+ # The source code that this source object represents.
9
+ attr_reader :source
9
10
 
10
- def initialize(source, offsets = compute_offsets(source))
11
+ # The line number where this source starts.
12
+ attr_accessor :start_line
13
+
14
+ # The list of newline byte offsets in the source code.
15
+ attr_reader :offsets
16
+
17
+ # Create a new source object with the given source code and newline byte
18
+ # offsets. If no newline byte offsets are given, they will be computed from
19
+ # the source code.
20
+ def initialize(source, start_line = 1, offsets = compute_offsets(source))
11
21
  @source = source
22
+ @start_line = start_line
12
23
  @offsets = offsets
13
24
  end
14
25
 
26
+ # Perform a byteslice on the source code using the given byte offset and
27
+ # byte length.
15
28
  def slice(offset, length)
16
29
  source.byteslice(offset, length)
17
30
  end
18
31
 
19
32
  # Binary search through the offsets to find the line number for the given
20
- # offset.
33
+ # byte offset.
21
34
  def line(value)
35
+ start_line + find_line(value)
36
+ end
37
+
38
+ # Return the byte offset of the start of the line corresponding to the given
39
+ # byte offset.
40
+ def line_offset(value)
41
+ offsets[find_line(value)]
42
+ end
43
+
44
+ # Return the column number for the given byte offset.
45
+ def column(value)
46
+ value - offsets[find_line(value)]
47
+ end
48
+
49
+ private
50
+
51
+ # Binary search through the offsets to find the line number for the given
52
+ # byte offset.
53
+ def find_line(value)
22
54
  left = 0
23
55
  right = offsets.length - 1
24
56
 
@@ -36,16 +68,8 @@ module Prism
36
68
  left - 1
37
69
  end
38
70
 
39
- def line_offset(value)
40
- offsets[line(value)]
41
- end
42
-
43
- def column(value)
44
- value - offsets[line(value)]
45
- end
46
-
47
- private
48
-
71
+ # Find all of the newlines in the source code and return their byte offsets
72
+ # from the start of the string an array.
49
73
  def compute_offsets(code)
50
74
  offsets = [0]
51
75
  code.b.scan("\n") { offsets << $~.end(0) }
@@ -69,6 +93,8 @@ module Prism
69
93
  # The list of comments attached to this location
70
94
  attr_reader :comments
71
95
 
96
+ # Create a new location object with the given source, start byte offset, and
97
+ # byte length.
72
98
  def initialize(source, start_offset, length)
73
99
  @source = source
74
100
  @start_offset = start_offset
@@ -102,7 +128,7 @@ module Prism
102
128
 
103
129
  # The line number where this location starts.
104
130
  def start_line
105
- source.line(start_offset) + 1
131
+ source.line(start_offset)
106
132
  end
107
133
 
108
134
  # The content of the line where this location starts before this location.
@@ -113,7 +139,7 @@ module Prism
113
139
 
114
140
  # The line number where this location ends.
115
141
  def end_line
116
- source.line(end_offset) + 1
142
+ source.line(end_offset)
117
143
  end
118
144
 
119
145
  # The column number in bytes where this location starts from the start of
@@ -128,14 +154,17 @@ module Prism
128
154
  source.column(end_offset)
129
155
  end
130
156
 
157
+ # Implement the hash pattern matching interface for Location.
131
158
  def deconstruct_keys(keys)
132
159
  { start_offset: start_offset, end_offset: end_offset }
133
160
  end
134
161
 
162
+ # Implement the pretty print interface for Location.
135
163
  def pretty_print(q)
136
164
  q.text("(#{start_line},#{start_column})-(#{end_line},#{end_column})")
137
165
  end
138
166
 
167
+ # Returns true if the given other location is equal to this location.
139
168
  def ==(other)
140
169
  other.is_a?(Location) &&
141
170
  other.start_offset == start_offset &&
@@ -152,57 +181,99 @@ module Prism
152
181
  Location.new(source, start_offset, other.end_offset - start_offset)
153
182
  end
154
183
 
184
+ # Returns a null location that does not correspond to a source and points to
185
+ # the beginning of the file. Useful for when you want a location object but
186
+ # do not care where it points.
155
187
  def self.null
156
188
  new(nil, 0, 0)
157
189
  end
158
190
  end
159
191
 
160
- # This represents a comment that was encountered during parsing.
192
+ # This represents a comment that was encountered during parsing. It is the
193
+ # base class for all comment types.
161
194
  class Comment
162
- TYPES = [:inline, :embdoc, :__END__]
195
+ # The location of this comment in the source.
196
+ attr_reader :location
163
197
 
164
- attr_reader :type, :location
165
-
166
- def initialize(type, location)
167
- @type = type
198
+ # Create a new comment object with the given location.
199
+ def initialize(location)
168
200
  @location = location
169
201
  end
170
202
 
203
+ # Implement the hash pattern matching interface for Comment.
171
204
  def deconstruct_keys(keys)
172
- { type: type, location: location }
205
+ { location: location }
173
206
  end
174
207
 
175
- # Returns true if the comment happens on the same line as other code and false if the comment is by itself
208
+ # This can only be true for inline comments.
176
209
  def trailing?
177
- type == :inline && !location.start_line_slice.strip.empty?
210
+ false
178
211
  end
212
+ end
213
+
214
+ # InlineComment objects are the most common. They correspond to comments in
215
+ # the source file like this one that start with #.
216
+ class InlineComment < Comment
217
+ # Returns true if this comment happens on the same line as other code and
218
+ # false if the comment is by itself.
219
+ def trailing?
220
+ !location.start_line_slice.strip.empty?
221
+ end
222
+
223
+ # Returns a string representation of this comment.
224
+ def inspect
225
+ "#<Prism::InlineComment @location=#{location.inspect}>"
226
+ end
227
+ end
228
+
229
+ # EmbDocComment objects correspond to comments that are surrounded by =begin
230
+ # and =end.
231
+ class EmbDocComment < Comment
232
+ # Returns a string representation of this comment.
233
+ def inspect
234
+ "#<Prism::EmbDocComment @location=#{location.inspect}>"
235
+ end
236
+ end
179
237
 
238
+ # DATAComment objects correspond to comments that are after the __END__
239
+ # keyword in a source file.
240
+ class DATAComment < Comment
241
+ # Returns a string representation of this comment.
180
242
  def inspect
181
- "#<Prism::Comment @type=#{@type.inspect} @location=#{@location.inspect}>"
243
+ "#<Prism::DATAComment @location=#{location.inspect}>"
182
244
  end
183
245
  end
184
246
 
185
247
  # This represents a magic comment that was encountered during parsing.
186
248
  class MagicComment
187
- attr_reader :key_loc, :value_loc
249
+ # A Location object representing the location of the key in the source.
250
+ attr_reader :key_loc
188
251
 
252
+ # A Location object representing the location of the value in the source.
253
+ attr_reader :value_loc
254
+
255
+ # Create a new magic comment object with the given key and value locations.
189
256
  def initialize(key_loc, value_loc)
190
257
  @key_loc = key_loc
191
258
  @value_loc = value_loc
192
259
  end
193
260
 
261
+ # Returns the key of the magic comment by slicing it from the source code.
194
262
  def key
195
263
  key_loc.slice
196
264
  end
197
265
 
266
+ # Returns the value of the magic comment by slicing it from the source code.
198
267
  def value
199
268
  value_loc.slice
200
269
  end
201
270
 
271
+ # Implement the hash pattern matching interface for MagicComment.
202
272
  def deconstruct_keys(keys)
203
273
  { key_loc: key_loc, value_loc: value_loc }
204
274
  end
205
275
 
276
+ # Returns a string representation of this magic comment.
206
277
  def inspect
207
278
  "#<Prism::MagicComment @key=#{key.inspect} @value=#{value.inspect}>"
208
279
  end
@@ -210,17 +281,24 @@ module Prism
210
281
 
211
282
  # This represents an error that was encountered during parsing.
212
283
  class ParseError
213
- attr_reader :message, :location
284
+ # The message associated with this error.
285
+ attr_reader :message
286
+
287
+ # A Location object representing the location of this error in the source.
288
+ attr_reader :location
214
289
 
290
+ # Create a new error object with the given message and location.
215
291
  def initialize(message, location)
216
292
  @message = message
217
293
  @location = location
218
294
  end
219
295
 
296
+ # Implement the hash pattern matching interface for ParseError.
220
297
  def deconstruct_keys(keys)
221
298
  { message: message, location: location }
222
299
  end
223
300
 
301
+ # Returns a string representation of this error.
224
302
  def inspect
225
303
  "#<Prism::ParseError @message=#{@message.inspect} @location=#{@location.inspect}>"
226
304
  end
@@ -228,17 +306,24 @@ module Prism
228
306
 
229
307
  # This represents a warning that was encountered during parsing.
230
308
  class ParseWarning
231
- attr_reader :message, :location
309
+ # The message associated with this warning.
310
+ attr_reader :message
311
+
312
+ # A Location object representing the location of this warning in the source.
313
+ attr_reader :location
232
314
 
315
+ # Create a new warning object with the given message and location.
233
316
  def initialize(message, location)
234
317
  @message = message
235
318
  @location = location
236
319
  end
237
320
 
321
+ # Implement the hash pattern matching interface for ParseWarning.
238
322
  def deconstruct_keys(keys)
239
323
  { message: message, location: location }
240
324
  end
241
325
 
326
+ # Returns a string representation of this warning.
242
327
  def inspect
243
328
  "#<Prism::ParseWarning @message=#{@message.inspect} @location=#{@location.inspect}>"
244
329
  end
@@ -248,8 +333,27 @@ module Prism
248
333
  # the AST, any comments that were encounters, and any errors that were
249
334
  # encountered.
250
335
  class ParseResult
251
- attr_reader :value, :comments, :magic_comments, :errors, :warnings, :source
336
+ # The value that was generated by parsing. Normally this holds the AST, but
337
+ # it can sometimes how a list of tokens or other results passed back from
338
+ # the parser.
339
+ attr_reader :value
340
+
341
+ # The list of comments that were encountered during parsing.
342
+ attr_reader :comments
343
+
344
+ # The list of magic comments that were encountered during parsing.
345
+ attr_reader :magic_comments
252
346
 
347
+ # The list of errors that were generated during parsing.
348
+ attr_reader :errors
349
+
350
+ # The list of warnings that were generated during parsing.
351
+ attr_reader :warnings
352
+
353
+ # A Source instance that represents the source code that was parsed.
354
+ attr_reader :source
355
+
356
+ # Create a new parse result object with the given values.
253
357
  def initialize(value, comments, magic_comments, errors, warnings, source)
254
358
  @value = value
255
359
  @comments = comments
@@ -259,14 +363,19 @@ module Prism
259
363
  @source = source
260
364
  end
261
365
 
366
+ # Implement the hash pattern matching interface for ParseResult.
262
367
  def deconstruct_keys(keys)
263
368
  { value: value, comments: comments, magic_comments: magic_comments, errors: errors, warnings: warnings }
264
369
  end
265
370
 
371
+ # Returns true if there were no errors during parsing and false if there
372
+ # were.
266
373
  def success?
267
374
  errors.empty?
268
375
  end
269
376
 
377
+ # Returns true if there were errors during parsing and false if there were
378
+ # not.
270
379
  def failure?
271
380
  !success?
272
381
  end
@@ -274,18 +383,28 @@ module Prism
274
383
 
275
384
  # This represents a token from the Ruby source.
276
385
  class Token
277
- attr_reader :type, :value, :location
386
+ # The type of token that this token is.
387
+ attr_reader :type
388
+
389
+ # A byteslice of the source that this token represents.
390
+ attr_reader :value
391
+
392
+ # A Location object representing the location of this token in the source.
393
+ attr_reader :location
278
394
 
395
+ # Create a new token object with the given type, value, and location.
279
396
  def initialize(type, value, location)
280
397
  @type = type
281
398
  @value = value
282
399
  @location = location
283
400
  end
284
401
 
402
+ # Implement the hash pattern matching interface for Token.
285
403
  def deconstruct_keys(keys)
286
404
  { type: type, value: value, location: location }
287
405
  end
288
406
 
407
+ # Implement the pretty print interface for Token.
289
408
  def pretty_print(q)
290
409
  q.group do
291
410
  q.text(type.to_s)
@@ -300,6 +419,7 @@ module Prism
300
419
  end
301
420
  end
302
421
 
422
+ # Returns true if the given other token is equal to this token.
303
423
  def ==(other)
304
424
  other.is_a?(Token) &&
305
425
  other.type == type &&
data/lib/prism/pattern.rb CHANGED
@@ -38,6 +38,8 @@ module Prism
38
38
  # Raised when the query given to a pattern is either invalid Ruby syntax or
39
39
  # is using syntax that we don't yet support.
40
40
  class CompilationError < StandardError
41
+ # Create a new CompilationError with the given representation of the node
42
+ # that caused the error.
41
43
  def initialize(repr)
42
44
  super(<<~ERROR)
43
45
  prism was unable to compile the pattern you provided into a usable
@@ -53,18 +55,27 @@ module Prism
53
55
  end
54
56
  end
55
57
 
58
+ # The query that this pattern was initialized with.
56
59
  attr_reader :query
57
60
 
61
+ # Create a new pattern with the given query. The query should be a string
62
+ # containing a Ruby pattern matching expression.
58
63
  def initialize(query)
59
64
  @query = query
60
65
  @compiled = nil
61
66
  end
62
67
 
68
+ # Compile the query into a callable object that can be used to match against
69
+ # nodes.
63
70
  def compile
64
71
  result = Prism.parse("case nil\nin #{query}\nend")
65
72
  compile_node(result.value.statements.body.last.conditions.last.pattern)
66
73
  end
67
74
 
75
+ # Scan the given node and all of its children for nodes that match the
76
+ # pattern. If a block is given, it will be called with each node that
77
+ # matches the pattern. If no block is given, an enumerator will be returned
78
+ # that will yield each node that matches the pattern.
68
79
  def scan(root)
69
80
  return to_enum(__method__, root) unless block_given?
70
81