prism 0.20.0 → 0.22.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.
@@ -91,7 +91,8 @@ module Prism
91
91
  class Location
92
92
  # A Source object that is used to determine more information from the given
93
93
  # offset and length.
94
- protected attr_reader :source
94
+ attr_reader :source
95
+ protected :source
95
96
 
96
97
  # The byte offset from the beginning of the source where this location
97
98
  # starts.
@@ -97,6 +97,8 @@ module Prism
97
97
  result.errors.each do |error|
98
98
  on_parse_error(error.message)
99
99
  end
100
+
101
+ nil
100
102
  else
101
103
  result.value.accept(self)
102
104
  end
@@ -106,41 +108,91 @@ module Prism
106
108
  # Visitor methods
107
109
  ############################################################################
108
110
 
111
+ # Visit an ArrayNode node.
112
+ def visit_array_node(node)
113
+ elements = visit_elements(node.elements) unless node.elements.empty?
114
+ bounds(node.location)
115
+ on_array(elements)
116
+ end
117
+
109
118
  # Visit a CallNode node.
110
119
  def visit_call_node(node)
111
- if !node.message.match?(/^[[:alpha:]_]/) && node.opening_loc.nil? && node.arguments&.arguments&.length == 1
112
- left = visit(node.receiver)
113
- right = visit(node.arguments.arguments.first)
120
+ if node.variable_call?
121
+ if node.message.match?(/^[[:alpha:]_]/)
122
+ bounds(node.message_loc)
123
+ return on_vcall(on_ident(node.message))
124
+ end
114
125
 
115
- bounds(node.location)
116
- on_binary(left, node.name, right)
126
+ raise NotImplementedError, "Non-alpha variable call"
127
+ end
128
+
129
+ if node.opening_loc.nil?
130
+ left = visit(node.receiver)
131
+ if node.arguments&.arguments&.length == 1
132
+ right = visit(node.arguments.arguments.first)
133
+
134
+ on_binary(left, node.name, right)
135
+ elsif !node.arguments || node.arguments.empty?
136
+ on_unary(node.name, left)
137
+ else
138
+ raise NotImplementedError, "More than two arguments for operator"
139
+ end
117
140
  else
118
- raise NotImplementedError
141
+ raise NotImplementedError, "Non-nil opening_loc"
119
142
  end
120
143
  end
121
144
 
122
145
  # Visit a FloatNode node.
123
146
  def visit_float_node(node)
124
- bounds(node.location)
125
- on_float(node.slice)
147
+ visit_number(node) { |text| on_float(text) }
126
148
  end
127
149
 
128
150
  # Visit a ImaginaryNode node.
129
151
  def visit_imaginary_node(node)
130
- bounds(node.location)
131
- on_imaginary(node.slice)
152
+ visit_number(node) { |text| on_imaginary(text) }
132
153
  end
133
154
 
134
155
  # Visit an IntegerNode node.
135
156
  def visit_integer_node(node)
157
+ visit_number(node) { |text| on_int(text) }
158
+ end
159
+
160
+ # Visit a ParenthesesNode node.
161
+ def visit_parentheses_node(node)
162
+ body =
163
+ if node.body.nil?
164
+ on_stmts_add(on_stmts_new, on_void_stmt)
165
+ else
166
+ visit(node.body)
167
+ end
168
+
169
+ bounds(node.location)
170
+ on_paren(body)
171
+ end
172
+
173
+ # Visit a ProgramNode node.
174
+ def visit_program_node(node)
175
+ statements = visit(node.statements)
176
+ bounds(node.location)
177
+ on_program(statements)
178
+ end
179
+
180
+ # Visit a RangeNode node.
181
+ def visit_range_node(node)
182
+ left = visit(node.left)
183
+ right = visit(node.right)
184
+
136
185
  bounds(node.location)
137
- on_int(node.slice)
186
+ if node.exclude_end?
187
+ on_dot3(left, right)
188
+ else
189
+ on_dot2(left, right)
190
+ end
138
191
  end
139
192
 
140
193
  # Visit a RationalNode node.
141
194
  def visit_rational_node(node)
142
- bounds(node.location)
143
- on_rational(node.slice)
195
+ visit_number(node) { |text| on_rational(text) }
144
196
  end
145
197
 
146
198
  # Visit a StatementsNode node.
@@ -151,13 +203,6 @@ module Prism
151
203
  end
152
204
  end
153
205
 
154
- # Visit a ProgramNode node.
155
- def visit_program_node(node)
156
- statements = visit(node.statements)
157
- bounds(node.location)
158
- on_program(statements)
159
- end
160
-
161
206
  ############################################################################
162
207
  # Entrypoints for subclasses
163
208
  ############################################################################
@@ -174,6 +219,32 @@ module Prism
174
219
 
175
220
  private
176
221
 
222
+ # Visit a list of elements, like the elements of an array or arguments.
223
+ def visit_elements(elements)
224
+ bounds(elements.first.location)
225
+ elements.inject(on_args_new) do |args, element|
226
+ on_args_add(args, visit(element))
227
+ end
228
+ end
229
+
230
+ # Visit a node that represents a number. We need to explicitly handle the
231
+ # unary - operator.
232
+ def visit_number(node)
233
+ slice = node.slice
234
+ location = node.location
235
+
236
+ if slice[0] == "-"
237
+ bounds_values(location.start_line, location.start_column + 1)
238
+ value = yield slice[1..-1]
239
+
240
+ bounds(node.location)
241
+ on_unary(RUBY_ENGINE == "jruby" ? :- : :-@, value)
242
+ else
243
+ bounds(location)
244
+ yield slice
245
+ end
246
+ end
247
+
177
248
  # This method is responsible for updating lineno and column information
178
249
  # to reflect the current node.
179
250
  #
@@ -184,6 +255,13 @@ module Prism
184
255
  @column = location.start_column
185
256
  end
186
257
 
258
+ # If we need to do something unusual, we can directly update the line number
259
+ # and column to reflect the current node.
260
+ def bounds_values(lineno, column)
261
+ @lineno = lineno
262
+ @column = column
263
+ end
264
+
187
265
  # Lazily initialize the parse result.
188
266
  def result
189
267
  @result ||= Prism.parse(source)
@@ -27,7 +27,7 @@ module Prism
27
27
 
28
28
  # The minor version of prism that we are expecting to find in the serialized
29
29
  # strings.
30
- MINOR_VERSION = 20
30
+ MINOR_VERSION = 22
31
31
 
32
32
  # The patch version of prism that we are expecting to find in the serialized
33
33
  # strings.
@@ -244,6 +244,8 @@ module Prism
244
244
  case level
245
245
  when 0
246
246
  :fatal
247
+ when 1
248
+ :argument
247
249
  else
248
250
  raise "Unknown level: #{level}"
249
251
  end
@@ -105,14 +105,18 @@ module Prism
105
105
  # { a: 1 }
106
106
  # ^^^^
107
107
  def visit_assoc_node(node)
108
- if node.value.is_a?(ImplicitNode)
109
- builder.pair_label([node.key.slice.chomp(":"), srange(node.key.location)])
110
- elsif in_pattern && node.value.nil?
111
- if node.key.is_a?(SymbolNode)
112
- builder.match_hash_var([node.key.unescaped, srange(node.key.location)])
108
+ if in_pattern
109
+ if node.value.is_a?(ImplicitNode)
110
+ if node.key.is_a?(SymbolNode)
111
+ builder.match_hash_var([node.key.unescaped, srange(node.key.location)])
112
+ else
113
+ builder.match_hash_var_from_str(token(node.key.opening_loc), visit_all(node.key.parts), token(node.key.closing_loc))
114
+ end
113
115
  else
114
- builder.match_hash_var_from_str(token(node.key.opening_loc), visit_all(node.key.parts), token(node.key.closing_loc))
116
+ builder.pair_keyword([node.key.unescaped, srange(node.key.location)], visit(node.value))
115
117
  end
118
+ elsif node.value.is_a?(ImplicitNode)
119
+ builder.pair_label([node.key.unescaped, srange(node.key.location)])
116
120
  elsif node.operator_loc
117
121
  builder.pair(visit(node.key), token(node.operator_loc), visit(node.value))
118
122
  elsif node.key.is_a?(SymbolNode) && node.key.opening_loc.nil?
@@ -241,53 +245,51 @@ module Prism
241
245
  block = nil
242
246
  end
243
247
 
248
+ if node.call_operator_loc.nil?
249
+ case name
250
+ when :!
251
+ return visit_block(builder.not_op(token(node.message_loc), token(node.opening_loc), visit(node.receiver), token(node.closing_loc)), block)
252
+ when :[]
253
+ return visit_block(builder.index(visit(node.receiver), token(node.opening_loc), visit_all(arguments), token(node.closing_loc)), block)
254
+ when :[]=
255
+ if node.message != "[]=" && node.arguments && block.nil? && !node.safe_navigation?
256
+ return visit_block(
257
+ builder.assign(
258
+ builder.index_asgn(
259
+ visit(node.receiver),
260
+ token(node.opening_loc),
261
+ visit_all(node.arguments.arguments[...-1]),
262
+ token(node.closing_loc),
263
+ ),
264
+ srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset, ["="]),
265
+ visit(node.arguments.arguments.last)
266
+ ),
267
+ block
268
+ )
269
+ end
270
+ end
271
+ end
272
+
273
+ message_loc = node.message_loc
274
+ call_operator_loc = node.call_operator_loc
275
+ call_operator = [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)] if call_operator_loc
276
+
244
277
  visit_block(
245
- if name == :!
246
- builder.not_op(
247
- token(node.message_loc),
248
- token(node.opening_loc),
249
- visit(node.receiver),
250
- token(node.closing_loc)
278
+ if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil?
279
+ builder.assign(
280
+ builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)),
281
+ srange_find(message_loc.end_offset, node.arguments.location.start_offset, ["="]),
282
+ visit(node.arguments.arguments.last)
251
283
  )
252
- elsif name == :[]
253
- builder.index(
284
+ else
285
+ builder.call_method(
254
286
  visit(node.receiver),
287
+ call_operator,
288
+ message_loc ? [node.name, srange(message_loc)] : nil,
255
289
  token(node.opening_loc),
256
290
  visit_all(arguments),
257
291
  token(node.closing_loc)
258
292
  )
259
- elsif name == :[]= && node.message != "[]=" && node.arguments && block.nil?
260
- builder.assign(
261
- builder.index_asgn(
262
- visit(node.receiver),
263
- token(node.opening_loc),
264
- visit_all(node.arguments.arguments[...-1]),
265
- token(node.closing_loc),
266
- ),
267
- srange_find(node.message_loc.end_offset, node.arguments.arguments.last.location.start_offset, ["="]),
268
- visit(node.arguments.arguments.last)
269
- )
270
- else
271
- message_loc = node.message_loc
272
- call_operator_loc = node.call_operator_loc
273
- call_operator = [{ "." => :dot, "&." => :anddot, "::" => "::" }.fetch(call_operator_loc.slice), srange(call_operator_loc)] if call_operator_loc
274
-
275
- if name.end_with?("=") && !message_loc.slice.end_with?("=") && node.arguments && block.nil?
276
- builder.assign(
277
- builder.attr_asgn(visit(node.receiver), call_operator, token(message_loc)),
278
- srange_find(message_loc.end_offset, node.arguments.location.start_offset, ["="]),
279
- visit(node.arguments.arguments.last)
280
- )
281
- else
282
- builder.call_method(
283
- visit(node.receiver),
284
- call_operator,
285
- message_loc ? [node.name, srange(message_loc)] : nil,
286
- token(node.opening_loc),
287
- visit_all(arguments),
288
- token(node.closing_loc)
289
- )
290
- end
291
293
  end,
292
294
  block
293
295
  )
@@ -519,8 +521,6 @@ module Prism
519
521
  # def self.foo; end
520
522
  # ^^^^^^^^^^^^^^^^^
521
523
  def visit_def_node(node)
522
- forwarding = find_forwarding(node.parameters)
523
-
524
524
  if node.equal_loc
525
525
  if node.receiver
526
526
  builder.def_endless_singleton(
@@ -530,7 +530,7 @@ module Prism
530
530
  token(node.name_loc),
531
531
  builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
532
532
  token(node.equal_loc),
533
- node.body&.accept(copy_compiler(forwarding: forwarding))
533
+ node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters)))
534
534
  )
535
535
  else
536
536
  builder.def_endless_method(
@@ -538,7 +538,7 @@ module Prism
538
538
  token(node.name_loc),
539
539
  builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
540
540
  token(node.equal_loc),
541
- node.body&.accept(copy_compiler(forwarding: forwarding))
541
+ node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters)))
542
542
  )
543
543
  end
544
544
  elsif node.receiver
@@ -548,7 +548,7 @@ module Prism
548
548
  token(node.operator_loc),
549
549
  token(node.name_loc),
550
550
  builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
551
- node.body&.accept(copy_compiler(forwarding: forwarding)),
551
+ node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))),
552
552
  token(node.end_keyword_loc)
553
553
  )
554
554
  else
@@ -556,7 +556,7 @@ module Prism
556
556
  token(node.def_keyword_loc),
557
557
  token(node.name_loc),
558
558
  builder.args(token(node.lparen_loc), visit(node.parameters) || [], token(node.rparen_loc), false),
559
- node.body&.accept(copy_compiler(forwarding: forwarding)),
559
+ node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters))),
560
560
  token(node.end_keyword_loc)
561
561
  )
562
562
  end
@@ -614,9 +614,7 @@ module Prism
614
614
  # foo => [*, bar, *]
615
615
  # ^^^^^^^^^^^
616
616
  def visit_find_pattern_node(node)
617
- elements = [*node.requireds]
618
- elements << node.rest if !node.rest.nil? && !node.rest.is_a?(ImplicitRestNode)
619
- elements.concat(node.posts)
617
+ elements = [node.left, *node.requireds, node.right]
620
618
 
621
619
  if node.constant
622
620
  builder.const_pattern(visit(node.constant), token(node.opening_loc), builder.find_pattern(nil, visit_all(elements), nil), token(node.closing_loc))
@@ -993,24 +991,24 @@ module Prism
993
991
 
994
992
  # -> {}
995
993
  def visit_lambda_node(node)
994
+ parameters = node.parameters
995
+
996
996
  builder.block(
997
997
  builder.call_lambda(token(node.operator_loc)),
998
998
  [node.opening, srange(node.opening_loc)],
999
- if node.parameters
1000
- if node.parameters.is_a?(NumberedParametersNode)
1001
- visit(node.parameters)
1002
- else
1003
- builder.args(
1004
- token(node.parameters.opening_loc),
1005
- visit(node.parameters),
1006
- token(node.parameters.closing_loc),
1007
- false
1008
- )
1009
- end
1010
- else
999
+ if parameters.nil?
1011
1000
  builder.args(nil, [], nil, false)
1001
+ elsif node.parameters.is_a?(NumberedParametersNode)
1002
+ visit(node.parameters)
1003
+ else
1004
+ builder.args(
1005
+ token(node.parameters.opening_loc),
1006
+ visit(node.parameters),
1007
+ token(node.parameters.closing_loc),
1008
+ false
1009
+ )
1012
1010
  end,
1013
- node.body&.accept(copy_compiler(forwarding: find_forwarding(node.parameters&.parameters))),
1011
+ node.body&.accept(copy_compiler(forwarding: parameters.is_a?(NumberedParametersNode) ? [] : find_forwarding(parameters&.parameters))),
1014
1012
  [node.closing, srange(node.closing_loc)]
1015
1013
  )
1016
1014
  end
@@ -1064,12 +1062,22 @@ module Prism
1064
1062
 
1065
1063
  # foo in bar
1066
1064
  # ^^^^^^^^^^
1067
- def visit_match_predicate_node(node)
1068
- builder.match_pattern_p(
1069
- visit(node.value),
1070
- token(node.operator_loc),
1071
- within_pattern { |compiler| node.pattern.accept(compiler) }
1072
- )
1065
+ if RUBY_VERSION >= "3.0"
1066
+ def visit_match_predicate_node(node)
1067
+ builder.match_pattern_p(
1068
+ visit(node.value),
1069
+ token(node.operator_loc),
1070
+ within_pattern { |compiler| node.pattern.accept(compiler) }
1071
+ )
1072
+ end
1073
+ else
1074
+ def visit_match_predicate_node(node)
1075
+ builder.match_pattern(
1076
+ visit(node.value),
1077
+ token(node.operator_loc),
1078
+ within_pattern { |compiler| node.pattern.accept(compiler) }
1079
+ )
1080
+ end
1073
1081
  end
1074
1082
 
1075
1083
  # foo => bar
@@ -1096,7 +1104,7 @@ module Prism
1096
1104
  # case of a syntax error. The parser gem doesn't have such a concept, so
1097
1105
  # we invent our own here.
1098
1106
  def visit_missing_node(node)
1099
- raise CompilationError, "Cannot compile missing nodes"
1107
+ ::AST::Node.new(:missing, [], location: ::Parser::Source::Map.new(srange(node.location)))
1100
1108
  end
1101
1109
 
1102
1110
  # module Foo; end
@@ -1727,29 +1735,29 @@ module Prism
1727
1735
  # Visit a block node on a call.
1728
1736
  def visit_block(call, block)
1729
1737
  if block
1738
+ parameters = block.parameters
1739
+
1730
1740
  builder.block(
1731
1741
  call,
1732
1742
  token(block.opening_loc),
1733
- if (parameters = block.parameters)
1734
- if parameters.is_a?(NumberedParametersNode)
1735
- visit(parameters)
1736
- else
1737
- builder.args(
1738
- token(parameters.opening_loc),
1739
- if procarg0?(parameters.parameters)
1740
- parameter = parameters.parameters.requireds.first
1741
- [builder.procarg0(visit(parameter))].concat(visit_all(parameters.locals))
1742
- else
1743
- visit(parameters)
1744
- end,
1745
- token(parameters.closing_loc),
1746
- false
1747
- )
1748
- end
1749
- else
1743
+ if parameters.nil?
1750
1744
  builder.args(nil, [], nil, false)
1745
+ elsif parameters.is_a?(NumberedParametersNode)
1746
+ visit(parameters)
1747
+ else
1748
+ builder.args(
1749
+ token(parameters.opening_loc),
1750
+ if procarg0?(parameters.parameters)
1751
+ parameter = parameters.parameters.requireds.first
1752
+ [builder.procarg0(visit(parameter))].concat(visit_all(parameters.locals))
1753
+ else
1754
+ visit(parameters)
1755
+ end,
1756
+ token(parameters.closing_loc),
1757
+ false
1758
+ )
1751
1759
  end,
1752
- block.body&.accept(copy_compiler(forwarding: find_forwarding(block.parameters&.parameters))),
1760
+ block.body&.accept(copy_compiler(forwarding: parameters.is_a?(NumberedParametersNode) ? [] : find_forwarding(parameters&.parameters))),
1753
1761
  token(block.closing_loc)
1754
1762
  )
1755
1763
  else
@@ -1762,9 +1770,9 @@ module Prism
1762
1770
  children = []
1763
1771
  node.parts.each do |part|
1764
1772
  pushing =
1765
- if part.is_a?(StringNode) && part.unescaped.count("\n") > 1
1766
- unescaped = part.unescaped.split("\n")
1767
- escaped = part.content.split("\n")
1773
+ if part.is_a?(StringNode) && part.unescaped.include?("\n")
1774
+ unescaped = part.unescaped.lines(chomp: true)
1775
+ escaped = part.content.lines(chomp: true)
1768
1776
 
1769
1777
  escaped_lengths =
1770
1778
  if node.opening.end_with?("'")
@@ -1779,7 +1787,6 @@ module Prism
1779
1787
  unescaped.zip(escaped_lengths).map do |unescaped_line, escaped_length|
1780
1788
  end_offset = start_offset + (escaped_length || 0)
1781
1789
  inner_part = builder.string_internal(["#{unescaped_line}\n", srange_offsets(start_offset, end_offset)])
1782
-
1783
1790
  start_offset = end_offset
1784
1791
  inner_part
1785
1792
  end
@@ -26,7 +26,7 @@ module Prism
26
26
  Racc_debug_parser = false # :nodoc:
27
27
 
28
28
  def version # :nodoc:
29
- 33
29
+ 34
30
30
  end
31
31
 
32
32
  # The default encoding for Ruby files is UTF-8.
@@ -42,9 +42,10 @@ module Prism
42
42
  @source_buffer = source_buffer
43
43
  source = source_buffer.source
44
44
 
45
- result = unwrap(Prism.parse(source, filepath: source_buffer.name))
45
+ offset_cache = build_offset_cache(source)
46
+ result = unwrap(Prism.parse(source, filepath: source_buffer.name), offset_cache)
46
47
 
47
- build_ast(result.value, build_offset_cache(source))
48
+ build_ast(result.value, offset_cache)
48
49
  ensure
49
50
  @source_buffer = nil
50
51
  end
@@ -55,7 +56,7 @@ module Prism
55
56
  source = source_buffer.source
56
57
 
57
58
  offset_cache = build_offset_cache(source)
58
- result = unwrap(Prism.parse(source, filepath: source_buffer.name))
59
+ result = unwrap(Prism.parse(source, filepath: source_buffer.name), offset_cache)
59
60
 
60
61
  [
61
62
  build_ast(result.value, offset_cache),
@@ -67,17 +68,23 @@ module Prism
67
68
 
68
69
  # Parses a source buffer and returns the AST, the source code comments,
69
70
  # and the tokens emitted by the lexer.
70
- def tokenize(source_buffer, _recover = false)
71
+ def tokenize(source_buffer, recover = false)
71
72
  @source_buffer = source_buffer
72
73
  source = source_buffer.source
73
74
 
74
75
  offset_cache = build_offset_cache(source)
75
- result = unwrap(Prism.parse_lex(source, filepath: source_buffer.name))
76
+ result =
77
+ begin
78
+ unwrap(Prism.parse_lex(source, filepath: source_buffer.name), offset_cache)
79
+ rescue ::Parser::SyntaxError
80
+ raise if !recover
81
+ end
76
82
 
77
83
  program, tokens = result.value
84
+ ast = build_ast(program, offset_cache) if result.success?
78
85
 
79
86
  [
80
- build_ast(program, offset_cache),
87
+ ast,
81
88
  build_comments(result.comments, offset_cache),
82
89
  build_tokens(tokens, offset_cache)
83
90
  ]
@@ -93,16 +100,23 @@ module Prism
93
100
 
94
101
  private
95
102
 
103
+ # This is a hook to allow consumers to disable some errors if they don't
104
+ # want them to block creating the syntax tree.
105
+ def valid_error?(error)
106
+ true
107
+ end
108
+
96
109
  # If there was a error generated during the parse, then raise an
97
110
  # appropriate syntax error. Otherwise return the result.
98
- def unwrap(result)
99
- return result if result.success?
111
+ def unwrap(result, offset_cache)
112
+ result.errors.each do |error|
113
+ next unless valid_error?(error)
100
114
 
101
- error = result.errors.first
102
- offset_cache = build_offset_cache(source_buffer.source)
115
+ location = build_range(error.location, offset_cache)
116
+ diagnostics.process(Diagnostic.new(error.message, location))
117
+ end
103
118
 
104
- diagnostic = Diagnostic.new(error.message, build_range(error.location, offset_cache))
105
- raise ::Parser::SyntaxError, diagnostic
119
+ result
106
120
  end
107
121
 
108
122
  # Prism deals with offsets in bytes, while the parser gem deals with
data/prism.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "prism"
5
- spec.version = "0.20.0"
5
+ spec.version = "0.22.0"
6
6
  spec.authors = ["Shopify"]
7
7
  spec.email = ["ruby@shopify.com"]
8
8
 
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.homepage = "https://github.com/ruby/prism"
11
11
  spec.license = "MIT"
12
12
 
13
- spec.required_ruby_version = ">= 3.0.0"
13
+ spec.required_ruby_version = ">= 2.7.0"
14
14
 
15
15
  spec.require_paths = ["lib"]
16
16
  spec.files = [
data/src/diagnostic.c CHANGED
@@ -63,7 +63,8 @@ typedef struct {
63
63
  *
64
64
  * For errors, they are:
65
65
  *
66
- * * `PM_ERROR_LEVEL_FATAL` - The level for all errors.
66
+ * * `PM_ERROR_LEVEL_FATAL` - The default level for errors.
67
+ * * `PM_ERROR_LEVEL_ARGUMENT` - Errors that should raise ArgumentError.
67
68
  *
68
69
  * For warnings, they are:
69
70
  *
@@ -71,9 +72,13 @@ typedef struct {
71
72
  * * `PM_WARNING_LEVEL_VERBOSE` - Warnings that appear with `-w`, as in `ruby -w -c -e 'code'`.
72
73
  */
73
74
  static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
75
+ // Special error that can be replaced
74
76
  [PM_ERR_CANNOT_PARSE_EXPRESSION] = { "cannot parse the expression", PM_ERROR_LEVEL_FATAL },
75
77
 
76
- // Errors
78
+ // Errors that should raise argument errors
79
+ [PM_ERR_INVALID_ENCODING_MAGIC_COMMENT] = { "unknown or invalid encoding in the magic comment", PM_ERROR_LEVEL_ARGUMENT },
80
+
81
+ // Errors that should raise syntax errors
77
82
  [PM_ERR_ALIAS_ARGUMENT] = { "invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable", PM_ERROR_LEVEL_FATAL },
78
83
  [PM_ERR_AMPAMPEQ_MULTI_ASSIGN] = { "unexpected `&&=` in a multiple assignment", PM_ERROR_LEVEL_FATAL },
79
84
  [PM_ERR_ARGUMENT_AFTER_BLOCK] = { "unexpected argument after a block argument", PM_ERROR_LEVEL_FATAL },
@@ -187,15 +192,16 @@ static const pm_diagnostic_data_t diagnostic_messages[PM_DIAGNOSTIC_ID_LEN] = {
187
192
  [PM_ERR_INCOMPLETE_QUESTION_MARK] = { "incomplete expression at `?`", PM_ERROR_LEVEL_FATAL },
188
193
  [PM_ERR_INCOMPLETE_VARIABLE_CLASS] = { "incomplete class variable", PM_ERROR_LEVEL_FATAL },
189
194
  [PM_ERR_INCOMPLETE_VARIABLE_INSTANCE] = { "incomplete instance variable", PM_ERROR_LEVEL_FATAL },
190
- [PM_ERR_INVALID_ENCODING_MAGIC_COMMENT] = { "unknown or invalid encoding in the magic comment", PM_ERROR_LEVEL_FATAL },
191
195
  [PM_ERR_INVALID_FLOAT_EXPONENT] = { "invalid exponent", PM_ERROR_LEVEL_FATAL },
192
196
  [PM_ERR_INVALID_NUMBER_BINARY] = { "invalid binary number", PM_ERROR_LEVEL_FATAL },
193
197
  [PM_ERR_INVALID_NUMBER_DECIMAL] = { "invalid decimal number", PM_ERROR_LEVEL_FATAL },
194
198
  [PM_ERR_INVALID_NUMBER_HEXADECIMAL] = { "invalid hexadecimal number", PM_ERROR_LEVEL_FATAL },
195
199
  [PM_ERR_INVALID_NUMBER_OCTAL] = { "invalid octal number", PM_ERROR_LEVEL_FATAL },
196
200
  [PM_ERR_INVALID_NUMBER_UNDERSCORE] = { "invalid underscore placement in number", PM_ERROR_LEVEL_FATAL },
201
+ [PM_ERR_INVALID_CHARACTER] = { "invalid character 0x%X", PM_ERROR_LEVEL_FATAL },
202
+ [PM_ERR_INVALID_MULTIBYTE_CHARACTER] = { "invalid multibyte character 0x%X", PM_ERROR_LEVEL_FATAL },
203
+ [PM_ERR_INVALID_PRINTABLE_CHARACTER] = { "invalid character `%c`", PM_ERROR_LEVEL_FATAL },
197
204
  [PM_ERR_INVALID_PERCENT] = { "invalid `%` token", PM_ERROR_LEVEL_FATAL }, // TODO WHAT?
198
- [PM_ERR_INVALID_TOKEN] = { "invalid token", PM_ERROR_LEVEL_FATAL }, // TODO WHAT?
199
205
  [PM_ERR_INVALID_VARIABLE_GLOBAL] = { "invalid global variable", PM_ERROR_LEVEL_FATAL },
200
206
  [PM_ERR_IT_NOT_ALLOWED] = { "`it` is not allowed when an ordinary parameter is defined", PM_ERROR_LEVEL_FATAL },
201
207
  [PM_ERR_LAMBDA_OPEN] = { "expected a `do` keyword or a `{` to open the lambda block", PM_ERROR_LEVEL_FATAL },
data/src/encoding.c CHANGED
@@ -2252,7 +2252,7 @@ static const uint8_t pm_utf_8_dfa[] = {
2252
2252
  */
2253
2253
  static pm_unicode_codepoint_t
2254
2254
  pm_utf_8_codepoint(const uint8_t *b, ptrdiff_t n, size_t *width) {
2255
- assert(n >= 1);
2255
+ assert(n >= 0);
2256
2256
  size_t maximum = (size_t) n;
2257
2257
 
2258
2258
  uint32_t codepoint;