prism 0.16.0 → 0.17.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 +4 -4
- data/CHANGELOG.md +16 -1
- data/Makefile +6 -0
- data/README.md +1 -1
- data/config.yml +50 -35
- data/docs/fuzzing.md +1 -1
- data/docs/serialization.md +28 -29
- data/ext/prism/api_node.c +802 -770
- data/ext/prism/api_pack.c +20 -9
- data/ext/prism/extension.c +464 -162
- data/ext/prism/extension.h +1 -1
- data/include/prism/ast.h +3173 -763
- data/include/prism/defines.h +32 -9
- data/include/prism/diagnostic.h +36 -3
- data/include/prism/enc/pm_encoding.h +118 -28
- data/include/prism/node.h +38 -13
- data/include/prism/options.h +204 -0
- data/include/prism/pack.h +44 -33
- data/include/prism/parser.h +445 -200
- data/include/prism/prettyprint.h +12 -1
- data/include/prism/regexp.h +16 -2
- data/include/prism/util/pm_buffer.h +94 -16
- data/include/prism/util/pm_char.h +162 -48
- data/include/prism/util/pm_constant_pool.h +126 -32
- data/include/prism/util/pm_list.h +68 -38
- data/include/prism/util/pm_memchr.h +18 -3
- data/include/prism/util/pm_newline_list.h +70 -27
- data/include/prism/util/pm_state_stack.h +25 -7
- data/include/prism/util/pm_string.h +115 -27
- data/include/prism/util/pm_string_list.h +25 -6
- data/include/prism/util/pm_strncasecmp.h +32 -0
- data/include/prism/util/pm_strpbrk.h +31 -17
- data/include/prism/version.h +27 -2
- data/include/prism.h +224 -31
- data/lib/prism/compiler.rb +6 -3
- data/lib/prism/debug.rb +23 -7
- data/lib/prism/dispatcher.rb +33 -18
- data/lib/prism/dsl.rb +10 -5
- data/lib/prism/ffi.rb +132 -80
- data/lib/prism/lex_compat.rb +25 -15
- data/lib/prism/mutation_compiler.rb +10 -5
- data/lib/prism/node.rb +370 -135
- data/lib/prism/node_ext.rb +1 -1
- data/lib/prism/node_inspector.rb +1 -1
- data/lib/prism/pack.rb +79 -40
- data/lib/prism/parse_result/comments.rb +7 -2
- data/lib/prism/parse_result/newlines.rb +4 -0
- data/lib/prism/parse_result.rb +150 -30
- data/lib/prism/pattern.rb +11 -0
- data/lib/prism/ripper_compat.rb +28 -10
- data/lib/prism/serialize.rb +86 -54
- data/lib/prism/visitor.rb +10 -3
- data/lib/prism.rb +20 -2
- data/prism.gemspec +4 -2
- data/rbi/prism.rbi +104 -60
- data/rbi/prism_static.rbi +16 -2
- data/sig/prism.rbs +72 -43
- data/sig/prism_static.rbs +14 -1
- data/src/diagnostic.c +56 -53
- data/src/enc/pm_big5.c +1 -0
- data/src/enc/pm_euc_jp.c +1 -0
- data/src/enc/pm_gbk.c +1 -0
- data/src/enc/pm_shift_jis.c +1 -0
- data/src/enc/pm_tables.c +316 -80
- data/src/enc/pm_unicode.c +53 -8
- data/src/enc/pm_windows_31j.c +1 -0
- data/src/node.c +334 -321
- data/src/options.c +170 -0
- data/src/prettyprint.c +74 -47
- data/src/prism.c +1642 -856
- data/src/regexp.c +151 -95
- data/src/serialize.c +44 -20
- data/src/token_type.c +3 -1
- data/src/util/pm_buffer.c +45 -15
- data/src/util/pm_char.c +103 -57
- data/src/util/pm_constant_pool.c +51 -21
- data/src/util/pm_list.c +12 -4
- data/src/util/pm_memchr.c +5 -3
- data/src/util/pm_newline_list.c +20 -12
- data/src/util/pm_state_stack.c +9 -3
- data/src/util/pm_string.c +95 -85
- data/src/util/pm_string_list.c +14 -15
- data/src/util/pm_strncasecmp.c +10 -3
- data/src/util/pm_strpbrk.c +25 -19
- metadata +5 -3
- data/docs/prism.png +0 -0
data/lib/prism/node_ext.rb
CHANGED
@@ -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
|
data/lib/prism/node_inspector.rb
CHANGED
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
|
-
|
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:
|
74
|
-
LITTLE_ENDIAN:
|
75
|
-
BIG_ENDIAN:
|
76
|
-
NATIVE_ENDIAN:
|
77
|
-
ENDIAN_NA:
|
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:
|
82
|
-
SIGNED:
|
83
|
-
SIGNED_NA:
|
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:
|
88
|
-
SIZE_INT:
|
89
|
-
SIZE_LONG:
|
90
|
-
SIZE_LONG_LONG:
|
91
|
-
SIZE_8:
|
92
|
-
SIZE_16:
|
93
|
-
SIZE_32:
|
94
|
-
SIZE_64:
|
95
|
-
SIZE_P:
|
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
|
-
|
133
|
+
"whitespace"
|
102
134
|
when COMMENT
|
103
|
-
|
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 +
|
150
|
+
base + ", as many as possible"
|
119
151
|
end
|
120
152
|
when UTF8
|
121
|
-
|
153
|
+
"UTF-8 character"
|
122
154
|
when BER
|
123
|
-
|
155
|
+
"BER-compressed integer"
|
124
156
|
when FLOAT
|
125
157
|
"#{SIZE_DESCRIPTIONS[size]} #{ENDIAN_DESCRIPTIONS[endian]} float"
|
126
158
|
when STRING_SPACE_PADDED
|
127
|
-
|
159
|
+
"arbitrary binary string (space padded)"
|
128
160
|
when STRING_NULL_PADDED
|
129
|
-
|
161
|
+
"arbitrary binary string (null padded, count is width)"
|
130
162
|
when STRING_NULL_TERMINATED
|
131
|
-
|
163
|
+
"arbitrary binary string (null padded, count is width), except that null is added with *"
|
132
164
|
when STRING_MSB
|
133
|
-
|
165
|
+
"bit string (MSB first)"
|
134
166
|
when STRING_LSB
|
135
|
-
|
167
|
+
"bit string (LSB first)"
|
136
168
|
when STRING_HEX_HIGH
|
137
|
-
|
169
|
+
"hex string (high nibble first)"
|
138
170
|
when STRING_HEX_LOW
|
139
|
-
|
171
|
+
"hex string (low nibble first)"
|
140
172
|
when STRING_UU
|
141
|
-
|
173
|
+
"UU-encoded string"
|
142
174
|
when STRING_MIME
|
143
|
-
|
175
|
+
"quoted printable, MIME encoding"
|
144
176
|
when STRING_BASE64
|
145
|
-
|
177
|
+
"base64 encoded string"
|
146
178
|
when STRING_FIXED
|
147
|
-
|
179
|
+
"pointer to a structure (fixed-length string)"
|
148
180
|
when STRING_POINTER
|
149
|
-
|
181
|
+
"pointer to a null-terminated string"
|
150
182
|
when MOVE
|
151
|
-
|
183
|
+
"move to absolute position"
|
152
184
|
when BACK
|
153
|
-
|
185
|
+
"back up a byte"
|
154
186
|
when NULL
|
155
|
-
|
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
|
-
|
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
|
-
([
|
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)
|
data/lib/prism/parse_result.rb
CHANGED
@@ -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
|
-
|
8
|
+
# The source code that this source object represents.
|
9
|
+
attr_reader :source
|
9
10
|
|
10
|
-
|
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
|
-
|
40
|
-
|
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)
|
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)
|
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
|
-
|
195
|
+
# The location of this comment in the source.
|
196
|
+
attr_reader :location
|
163
197
|
|
164
|
-
|
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
|
-
{
|
205
|
+
{ location: location }
|
173
206
|
end
|
174
207
|
|
175
|
-
#
|
208
|
+
# This can only be true for inline comments.
|
176
209
|
def trailing?
|
177
|
-
|
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::
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
|