yarp 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/yarp.rb CHANGED
@@ -20,6 +20,10 @@ module YARP
20
20
  offsets.bsearch_index { |offset| offset > value } || offsets.length
21
21
  end
22
22
 
23
+ def line_offset(value)
24
+ offsets[line(value) - 1]
25
+ end
26
+
23
27
  def column(value)
24
28
  value - offsets[line(value) - 1]
25
29
  end
@@ -46,10 +50,14 @@ module YARP
46
50
  # The length of this location in bytes.
47
51
  attr_reader :length
48
52
 
53
+ # The list of comments attached to this location
54
+ attr_reader :comments
55
+
49
56
  def initialize(source, start_offset, length)
50
57
  @source = source
51
58
  @start_offset = start_offset
52
59
  @length = length
60
+ @comments = []
53
61
  end
54
62
 
55
63
  # Create a new location object with the given options.
@@ -81,6 +89,12 @@ module YARP
81
89
  source.line(start_offset)
82
90
  end
83
91
 
92
+ # The content of the line where this location starts before this location.
93
+ def start_line_slice
94
+ offset = source.line_offset(start_offset)
95
+ source.slice(offset, start_offset - offset)
96
+ end
97
+
84
98
  # The line number where this location ends.
85
99
  def end_line
86
100
  source.line(end_offset - 1)
@@ -95,7 +109,7 @@ module YARP
95
109
  # The column number in bytes where this location ends from the start of the
96
110
  # line.
97
111
  def end_column
98
- source.column(end_offset - 1)
112
+ source.column(end_offset)
99
113
  end
100
114
 
101
115
  def deconstruct_keys(keys)
@@ -141,6 +155,15 @@ module YARP
141
155
  def deconstruct_keys(keys)
142
156
  { type: type, location: location }
143
157
  end
158
+
159
+ # Returns true if the comment happens on the same line as other code and false if the comment is by itself
160
+ def trailing?
161
+ type == :inline && !location.start_line_slice.strip.empty?
162
+ end
163
+
164
+ def inspect
165
+ "#<YARP::Comment @type=#{@type.inspect} @location=#{@location.inspect}>"
166
+ end
144
167
  end
145
168
 
146
169
  # This represents an error that was encountered during parsing.
@@ -155,6 +178,10 @@ module YARP
155
178
  def deconstruct_keys(keys)
156
179
  { message: message, location: location }
157
180
  end
181
+
182
+ def inspect
183
+ "#<YARP::ParseError @message=#{@message.inspect} @location=#{@location.inspect}>"
184
+ end
158
185
  end
159
186
 
160
187
  # This represents a warning that was encountered during parsing.
@@ -169,6 +196,10 @@ module YARP
169
196
  def deconstruct_keys(keys)
170
197
  { message: message, location: location }
171
198
  end
199
+
200
+ def inspect
201
+ "#<YARP::ParseWarning @message=#{@message.inspect} @location=#{@location.inspect}>"
202
+ end
172
203
  end
173
204
 
174
205
  # A class that knows how to walk down the tree. None of the individual visit
@@ -217,45 +248,6 @@ module YARP
217
248
  def failure?
218
249
  !success?
219
250
  end
220
-
221
- # Keep in sync with Java MarkNewlinesVisitor
222
- class MarkNewlinesVisitor < YARP::Visitor
223
- def initialize(newline_marked)
224
- @newline_marked = newline_marked
225
- end
226
-
227
- def visit_block_node(node)
228
- old_newline_marked = @newline_marked
229
- @newline_marked = Array.new(old_newline_marked.size, false)
230
- begin
231
- super(node)
232
- ensure
233
- @newline_marked = old_newline_marked
234
- end
235
- end
236
- alias_method :visit_lambda_node, :visit_block_node
237
-
238
- def visit_if_node(node)
239
- node.set_newline_flag(@newline_marked)
240
- super(node)
241
- end
242
- alias_method :visit_unless_node, :visit_if_node
243
-
244
- def visit_statements_node(node)
245
- node.body.each do |child|
246
- child.set_newline_flag(@newline_marked)
247
- end
248
- super(node)
249
- end
250
- end
251
- private_constant :MarkNewlinesVisitor
252
-
253
- def mark_newlines
254
- newline_marked = Array.new(1 + @source.offsets.size, false)
255
- visitor = MarkNewlinesVisitor.new(newline_marked)
256
- value.accept(visitor)
257
- value
258
- end
259
251
  end
260
252
 
261
253
  # This represents a token from the Ruby source.
@@ -323,7 +315,6 @@ module YARP
323
315
  q.nest(2) do
324
316
  deconstructed = deconstruct_keys([])
325
317
  deconstructed.delete(:location)
326
-
327
318
  q.breakable("")
328
319
  q.seplist(deconstructed, lambda { q.comma_breakable }, :each_value) { |value| q.pp(value) }
329
320
  end
@@ -333,6 +324,71 @@ module YARP
333
324
  end
334
325
  end
335
326
 
327
+ # This object is responsible for generating the output for the inspect method
328
+ # implementations of child nodes.
329
+ class NodeInspector
330
+ attr_reader :prefix, :output
331
+
332
+ def initialize(prefix = "")
333
+ @prefix = prefix
334
+ @output = +""
335
+ end
336
+
337
+ # Appends a line to the output with the current prefix.
338
+ def <<(line)
339
+ output << "#{prefix}#{line}"
340
+ end
341
+
342
+ # This generates a string that is used as the header of the inspect output
343
+ # for any given node.
344
+ def header(node)
345
+ output = +"@ #{node.class.name.split("::").last} ("
346
+ output << "location: (#{node.location.start_offset}...#{node.location.end_offset})"
347
+ output << ", newline: true" if node.newline?
348
+ output << ")\n"
349
+ output
350
+ end
351
+
352
+ # Generates a string that represents a list of nodes. It handles properly
353
+ # using the box drawing characters to make the output look nice.
354
+ def list(prefix, nodes)
355
+ output = +"(length: #{nodes.length})\n"
356
+ last_index = nodes.length - 1
357
+
358
+ nodes.each_with_index do |node, index|
359
+ pointer, preadd = (index == last_index) ? ["└── ", " "] : ["├── ", "│ "]
360
+ node_prefix = "#{prefix}#{preadd}"
361
+ output << node.inspect(NodeInspector.new(node_prefix)).sub(node_prefix, "#{prefix}#{pointer}")
362
+ end
363
+
364
+ output
365
+ end
366
+
367
+ # Generates a string that represents a location field on a node.
368
+ def location(value)
369
+ if value
370
+ "(#{value.start_offset}...#{value.end_offset}) = #{value.slice.inspect}"
371
+ else
372
+ "∅"
373
+ end
374
+ end
375
+
376
+ # Generates a string that represents a child node.
377
+ def child_node(node, append)
378
+ node.inspect(child_inspector(append)).delete_prefix(prefix)
379
+ end
380
+
381
+ # Returns a new inspector that can be used to inspect a child node.
382
+ def child_inspector(append)
383
+ NodeInspector.new("#{prefix}#{append}")
384
+ end
385
+
386
+ # Returns the output as a string.
387
+ def to_str
388
+ output
389
+ end
390
+ end
391
+
336
392
  class FloatNode < Node
337
393
  def value
338
394
  Float(slice)
@@ -464,10 +520,10 @@ module YARP
464
520
  sorted = [
465
521
  *params.requireds.grep(RequiredParameterNode).map(&:name),
466
522
  *params.optionals.map(&:name),
467
- *((params.rest.name ? params.rest.name.to_sym : :*) if params.rest && params.rest.operator != ","),
523
+ *((params.rest.name || :*) if params.rest && params.rest.operator != ","),
468
524
  *params.posts.grep(RequiredParameterNode).map(&:name),
469
- *params.keywords.reject(&:value).map { |param| param.name.chomp(":").to_sym },
470
- *params.keywords.select(&:value).map { |param| param.name.chomp(":").to_sym }
525
+ *params.keywords.reject(&:value).map(&:name),
526
+ *params.keywords.select(&:value).map(&:name)
471
527
  ]
472
528
 
473
529
  # TODO: When we get a ... parameter, we should be pushing * and &
@@ -533,6 +589,10 @@ require_relative "yarp/node"
533
589
  require_relative "yarp/ripper_compat"
534
590
  require_relative "yarp/serialize"
535
591
  require_relative "yarp/pack"
592
+ require_relative "yarp/pattern"
593
+
594
+ require_relative "yarp/parse_result/comments"
595
+ require_relative "yarp/parse_result/newlines"
536
596
 
537
597
  if RUBY_ENGINE == "ruby" and !ENV["YARP_FFI_BACKEND"]
538
598
  require "yarp/yarp"
data/src/diagnostic.c CHANGED
@@ -1,12 +1,264 @@
1
1
  #include "yarp/diagnostic.h"
2
2
 
3
+ /*
4
+ ## Message composition
5
+
6
+ When composing an error message, use sentence fragments.
7
+
8
+ Try describing the property of the code that caused the error, rather than the rule that is being
9
+ violated. It may help to use a fragment that completes a sentence beginning, "The parser
10
+ encountered (a) ...". If appropriate, add a description of the rule violation (or other helpful
11
+ context) after a semicolon.
12
+
13
+ For example:, instead of "Control escape sequence cannot be doubled", prefer:
14
+
15
+ > "Invalid control escape sequence; control cannot be repeated"
16
+
17
+ In some cases, where the failure is more general or syntax expectations are violated, it may make
18
+ more sense to use a fragment that completes a sentence beginning, "The parser ...".
19
+
20
+ For example:
21
+
22
+ > "Expected an expression after `(`"
23
+ > "Cannot parse the expression"
24
+
25
+
26
+ ## Message style guide
27
+
28
+ - Use articles like "a", "an", and "the" when appropriate.
29
+ - e.g., prefer "Cannot parse the expression" to "Cannot parse expression".
30
+ - Use the common name for tokens and nodes.
31
+ - e.g., prefer "keyword splat" to "assoc splat"
32
+ - e.g., prefer "embedded document" to "embdoc"
33
+ - Capitalize the initial word of the message.
34
+ - Use back ticks around token literals
35
+ - e.g., "Expected a `=>` between the hash key and value"
36
+ - Do not use `.` or other punctuation at the end of the message.
37
+ - Do not use contractions like "can't". Prefer "cannot" to "can not".
38
+ - For tokens that can have multiple meanings, reference the token and its meaning.
39
+ - e.g., "`*` splat argument" is clearer and more complete than "splat argument" or "`*` argument"
40
+
41
+
42
+ ## Error names (YP_ERR_*)
43
+
44
+ - When appropriate, prefer node name to token name.
45
+ - e.g., prefer "SPLAT" to "STAR" in the context of argument parsing.
46
+ - Prefer token name to common name.
47
+ - e.g., prefer "STAR" to "ASTERISK".
48
+ - Try to order the words in the name from more general to more specific,
49
+ - e.g., "INVALID_NUMBER_DECIMAL" is better than "DECIMAL_INVALID_NUMBER".
50
+ - When in doubt, look for similar patterns and name them so that they are grouped when lexically
51
+ sorted. See YP_ERR_ARGUMENT_NO_FORWARDING_* for an example.
52
+ */
53
+
54
+ static const char* const diagnostic_messages[YP_DIAGNOSTIC_ID_LEN] = {
55
+ [YP_ERR_ALIAS_ARGUMENT] = "Invalid argument being passed to `alias`; expected a bare word, symbol, constant, or global variable",
56
+ [YP_ERR_AMPAMPEQ_MULTI_ASSIGN] = "Unexpected `&&=` in a multiple assignment",
57
+ [YP_ERR_ARGUMENT_AFTER_BLOCK] = "Unexpected argument after a block argument",
58
+ [YP_ERR_ARGUMENT_BARE_HASH] = "Unexpected bare hash argument",
59
+ [YP_ERR_ARGUMENT_BLOCK_MULTI] = "Multiple block arguments; only one block is allowed",
60
+ [YP_ERR_ARGUMENT_FORMAL_CLASS] = "Invalid formal argument; formal argument cannot be a class variable",
61
+ [YP_ERR_ARGUMENT_FORMAL_CONSTANT] = "Invalid formal argument; formal argument cannot be a constant",
62
+ [YP_ERR_ARGUMENT_FORMAL_GLOBAL] = "Invalid formal argument; formal argument cannot be a global variable",
63
+ [YP_ERR_ARGUMENT_FORMAL_IVAR] = "Invalid formal argument; formal argument cannot be an instance variable",
64
+ [YP_ERR_ARGUMENT_NO_FORWARDING_AMP] = "Unexpected `&` when the parent method is not forwarding",
65
+ [YP_ERR_ARGUMENT_NO_FORWARDING_ELLIPSES] = "Unexpected `...` when the parent method is not forwarding",
66
+ [YP_ERR_ARGUMENT_NO_FORWARDING_STAR] = "Unexpected `*` when the parent method is not forwarding",
67
+ [YP_ERR_ARGUMENT_SPLAT_AFTER_ASSOC_SPLAT] = "Unexpected `*` splat argument after a `**` keyword splat argument",
68
+ [YP_ERR_ARGUMENT_SPLAT_AFTER_SPLAT] = "Unexpected `*` splat argument after a `*` splat argument",
69
+ [YP_ERR_ARGUMENT_TERM_PAREN] = "Expected a `)` to close the arguments",
70
+ [YP_ERR_ARRAY_ELEMENT] = "Expected an element for the array",
71
+ [YP_ERR_ARRAY_EXPRESSION] = "Expected an expression for the array element",
72
+ [YP_ERR_ARRAY_EXPRESSION_AFTER_STAR] = "Expected an expression after `*` in the array",
73
+ [YP_ERR_ARRAY_SEPARATOR] = "Expected a `,` separator for the array elements",
74
+ [YP_ERR_ARRAY_TERM] = "Expected a `]` to close the array",
75
+ [YP_ERR_BEGIN_LONELY_ELSE] = "Unexpected `else` in `begin` block; a `rescue` clause must precede `else`",
76
+ [YP_ERR_BEGIN_TERM] = "Expected an `end` to close the `begin` statement",
77
+ [YP_ERR_BEGIN_UPCASE_BRACE] = "Expected a `{` after `BEGIN`",
78
+ [YP_ERR_BEGIN_UPCASE_TERM] = "Expected a `}` to close the `BEGIN` statement",
79
+ [YP_ERR_BLOCK_PARAM_LOCAL_VARIABLE] = "Expected a local variable name in the block parameters",
80
+ [YP_ERR_BLOCK_PARAM_PIPE_TERM] = "Expected the block parameters to end with `|`",
81
+ [YP_ERR_BLOCK_TERM_BRACE] = "Expected a block beginning with `{` to end with `}`",
82
+ [YP_ERR_BLOCK_TERM_END] = "Expected a block beginning with `do` to end with `end`",
83
+ [YP_ERR_CANNOT_PARSE_EXPRESSION] = "Cannot parse the expression",
84
+ [YP_ERR_CANNOT_PARSE_STRING_PART] = "Cannot parse the string part",
85
+ [YP_ERR_CASE_EXPRESSION_AFTER_CASE] = "Expected an expression after `case`",
86
+ [YP_ERR_CASE_EXPRESSION_AFTER_WHEN] = "Expected an expression after `when`",
87
+ [YP_ERR_CASE_LONELY_ELSE] = "Unexpected `else` in `case` statement; a `when` clause must precede `else`",
88
+ [YP_ERR_CASE_TERM] = "Expected an `end` to close the `case` statement",
89
+ [YP_ERR_CLASS_IN_METHOD] = "Unexpected class definition in a method body",
90
+ [YP_ERR_CLASS_NAME] = "Expected a constant name after `class`",
91
+ [YP_ERR_CLASS_SUPERCLASS] = "Expected a superclass after `<`",
92
+ [YP_ERR_CLASS_TERM] = "Expected an `end` to close the `class` statement",
93
+ [YP_ERR_CONDITIONAL_ELSIF_PREDICATE] = "Expected a predicate expression for the `elsif` statement",
94
+ [YP_ERR_CONDITIONAL_IF_PREDICATE] = "Expected a predicate expression for the `if` statement",
95
+ [YP_ERR_CONDITIONAL_TERM] = "Expected an `end` to close the conditional clause",
96
+ [YP_ERR_CONDITIONAL_TERM_ELSE] = "Expected an `end` to close the `else` clause",
97
+ [YP_ERR_CONDITIONAL_UNLESS_PREDICATE] = "Expected a predicate expression for the `unless` statement",
98
+ [YP_ERR_CONDITIONAL_UNTIL_PREDICATE] = "Expected a predicate expression for the `until` statement",
99
+ [YP_ERR_CONDITIONAL_WHILE_PREDICATE] = "Expected a predicate expression for the `while` statement",
100
+ [YP_ERR_CONSTANT_PATH_COLON_COLON_CONSTANT] = "Expected a constant after the `::` operator",
101
+ [YP_ERR_DEF_ENDLESS] = "Could not parse the endless method body",
102
+ [YP_ERR_DEF_ENDLESS_SETTER] = "Invalid method name; a setter method cannot be defined in an endless method definition",
103
+ [YP_ERR_DEF_NAME] = "Expected a method name",
104
+ [YP_ERR_DEF_NAME_AFTER_RECEIVER] = "Expected a method name after the receiver",
105
+ [YP_ERR_DEF_PARAMS_TERM] = "Expected a delimiter to close the parameters",
106
+ [YP_ERR_DEF_PARAMS_TERM_PAREN] = "Expected a `)` to close the parameters",
107
+ [YP_ERR_DEF_RECEIVER] = "Expected a receiver for the method definition",
108
+ [YP_ERR_DEF_RECEIVER_TERM] = "Expected a `.` or `::` after the receiver in a method definition",
109
+ [YP_ERR_DEF_TERM] = "Expected an `end` to close the `def` statement",
110
+ [YP_ERR_DEFINED_EXPRESSION] = "Expected an expression after `defined?`",
111
+ [YP_ERR_EMBDOC_TERM] = "Could not find a terminator for the embedded document",
112
+ [YP_ERR_EMBEXPR_END] = "Expected a `}` to close the embedded expression",
113
+ [YP_ERR_EMBVAR_INVALID] = "Invalid embedded variable",
114
+ [YP_ERR_END_UPCASE_BRACE] = "Expected a `{` after `END`",
115
+ [YP_ERR_END_UPCASE_TERM] = "Expected a `}` to close the `END` statement",
116
+ [YP_ERR_ESCAPE_INVALID_CONTROL] = "Invalid control escape sequence",
117
+ [YP_ERR_ESCAPE_INVALID_CONTROL_REPEAT] = "Invalid control escape sequence; control cannot be repeated",
118
+ [YP_ERR_ESCAPE_INVALID_HEXADECIMAL] = "Invalid hexadecimal escape sequence",
119
+ [YP_ERR_ESCAPE_INVALID_META] = "Invalid meta escape sequence",
120
+ [YP_ERR_ESCAPE_INVALID_META_REPEAT] = "Invalid meta escape sequence; meta cannot be repeated",
121
+ [YP_ERR_ESCAPE_INVALID_UNICODE] = "Invalid Unicode escape sequence",
122
+ [YP_ERR_ESCAPE_INVALID_UNICODE_CM_FLAGS] = "Invalid Unicode escape sequence; Unicode cannot be combined with control or meta flags",
123
+ [YP_ERR_ESCAPE_INVALID_UNICODE_LITERAL] = "Invalid Unicode escape sequence; multiple codepoints are not allowed in a character literal",
124
+ [YP_ERR_ESCAPE_INVALID_UNICODE_LONG] = "Invalid Unicode escape sequence; maximum length is 6 digits",
125
+ [YP_ERR_ESCAPE_INVALID_UNICODE_TERM] = "Invalid Unicode escape sequence; needs closing `}`",
126
+ [YP_ERR_EXPECT_ARGUMENT] = "Expected an argument",
127
+ [YP_ERR_EXPECT_EOL_AFTER_STATEMENT] = "Expected a newline or semicolon after the statement",
128
+ [YP_ERR_EXPECT_EXPRESSION_AFTER_AMPAMPEQ] = "Expected an expression after `&&=`",
129
+ [YP_ERR_EXPECT_EXPRESSION_AFTER_PIPEPIPEEQ] = "Expected an expression after `||=`",
130
+ [YP_ERR_EXPECT_EXPRESSION_AFTER_COMMA] = "Expected an expression after `,`",
131
+ [YP_ERR_EXPECT_EXPRESSION_AFTER_EQUAL] = "Expected an expression after `=`",
132
+ [YP_ERR_EXPECT_EXPRESSION_AFTER_LESS_LESS] = "Expected an expression after `<<`",
133
+ [YP_ERR_EXPECT_EXPRESSION_AFTER_LPAREN] = "Expected an expression after `(`",
134
+ [YP_ERR_EXPECT_EXPRESSION_AFTER_OPERATOR] = "Expected an expression after the operator",
135
+ [YP_ERR_EXPECT_EXPRESSION_AFTER_SPLAT] = "Expected an expression after `*` splat in an argument",
136
+ [YP_ERR_EXPECT_EXPRESSION_AFTER_SPLAT_HASH] = "Expected an expression after `**` in a hash",
137
+ [YP_ERR_EXPECT_EXPRESSION_AFTER_STAR] = "Expected an expression after `*`",
138
+ [YP_ERR_EXPECT_IDENT_REQ_PARAMETER] = "Expected an identifier for the required parameter",
139
+ [YP_ERR_EXPECT_LPAREN_REQ_PARAMETER] = "Expected a `(` to start a required parameter",
140
+ [YP_ERR_EXPECT_RBRACKET] = "Expected a matching `]`",
141
+ [YP_ERR_EXPECT_RPAREN] = "Expected a matching `)`",
142
+ [YP_ERR_EXPECT_RPAREN_AFTER_MULTI] = "Expected a `)` after multiple assignment",
143
+ [YP_ERR_EXPECT_RPAREN_REQ_PARAMETER] = "Expected a `)` to end a required parameter",
144
+ [YP_ERR_EXPECT_STRING_CONTENT] = "Expected string content after opening string delimiter",
145
+ [YP_ERR_EXPECT_WHEN_DELIMITER] = "Expected a delimiter after the predicates of a `when` clause",
146
+ [YP_ERR_EXPRESSION_BARE_HASH] = "Unexpected bare hash in expression",
147
+ [YP_ERR_FOR_COLLECTION] = "Expected a collection after the `in` in a `for` statement",
148
+ [YP_ERR_FOR_INDEX] = "Expected an index after `for`",
149
+ [YP_ERR_FOR_IN] = "Expected an `in` after the index in a `for` statement",
150
+ [YP_ERR_FOR_TERM] = "Expected an `end` to close the `for` loop",
151
+ [YP_ERR_HASH_EXPRESSION_AFTER_LABEL] = "Expected an expression after the label in a hash",
152
+ [YP_ERR_HASH_KEY] = "Expected a key in the hash literal",
153
+ [YP_ERR_HASH_ROCKET] = "Expected a `=>` between the hash key and value",
154
+ [YP_ERR_HASH_TERM] = "Expected a `}` to close the hash literal",
155
+ [YP_ERR_HASH_VALUE] = "Expected a value in the hash literal",
156
+ [YP_ERR_HEREDOC_TERM] = "Could not find a terminator for the heredoc",
157
+ [YP_ERR_INCOMPLETE_QUESTION_MARK] = "Incomplete expression at `?`",
158
+ [YP_ERR_INCOMPLETE_VARIABLE_CLASS] = "Incomplete class variable",
159
+ [YP_ERR_INCOMPLETE_VARIABLE_INSTANCE] = "Incomplete instance variable",
160
+ [YP_ERR_INVALID_ENCODING_MAGIC_COMMENT] = "Unknown or invalid encoding in the magic comment",
161
+ [YP_ERR_INVALID_FLOAT_EXPONENT] = "Invalid exponent",
162
+ [YP_ERR_INVALID_NUMBER_BINARY] = "Invalid binary number",
163
+ [YP_ERR_INVALID_NUMBER_DECIMAL] = "Invalid decimal number",
164
+ [YP_ERR_INVALID_NUMBER_HEXADECIMAL] = "Invalid hexadecimal number",
165
+ [YP_ERR_INVALID_NUMBER_OCTAL] = "Invalid octal number",
166
+ [YP_ERR_INVALID_PERCENT] = "Invalid `%` token", // TODO WHAT?
167
+ [YP_ERR_INVALID_TOKEN] = "Invalid token", // TODO WHAT?
168
+ [YP_ERR_INVALID_VARIABLE_GLOBAL] = "Invalid global variable",
169
+ [YP_ERR_LAMBDA_OPEN] = "Expected a `do` keyword or a `{` to open the lambda block",
170
+ [YP_ERR_LAMBDA_TERM_BRACE] = "Expected a lambda block beginning with `{` to end with `}`",
171
+ [YP_ERR_LAMBDA_TERM_END] = "Expected a lambda block beginning with `do` to end with `end`",
172
+ [YP_ERR_LIST_I_LOWER_ELEMENT] = "Expected a symbol in a `%i` list",
173
+ [YP_ERR_LIST_I_LOWER_TERM] = "Expected a closing delimiter for the `%i` list",
174
+ [YP_ERR_LIST_I_UPPER_ELEMENT] = "Expected a symbol in a `%I` list",
175
+ [YP_ERR_LIST_I_UPPER_TERM] = "Expected a closing delimiter for the `%I` list",
176
+ [YP_ERR_LIST_W_LOWER_ELEMENT] = "Expected a string in a `%w` list",
177
+ [YP_ERR_LIST_W_LOWER_TERM] = "Expected a closing delimiter for the `%w` list",
178
+ [YP_ERR_LIST_W_UPPER_ELEMENT] = "Expected a string in a `%W` list",
179
+ [YP_ERR_LIST_W_UPPER_TERM] = "Expected a closing delimiter for the `%W` list",
180
+ [YP_ERR_MALLOC_FAILED] = "Failed to allocate memory",
181
+ [YP_ERR_MODULE_IN_METHOD] = "Unexpected module definition in a method body",
182
+ [YP_ERR_MODULE_NAME] = "Expected a constant name after `module`",
183
+ [YP_ERR_MODULE_TERM] = "Expected an `end` to close the `module` statement",
184
+ [YP_ERR_MULTI_ASSIGN_MULTI_SPLATS] = "Multiple splats in multiple assignment",
185
+ [YP_ERR_NOT_EXPRESSION] = "Expected an expression after `not`",
186
+ [YP_ERR_NUMBER_LITERAL_UNDERSCORE] = "Number literal ending with a `_`",
187
+ [YP_ERR_OPERATOR_MULTI_ASSIGN] = "Unexpected operator for a multiple assignment",
188
+ [YP_ERR_PARAMETER_ASSOC_SPLAT_MULTI] = "Unexpected multiple `**` splat parameters",
189
+ [YP_ERR_PARAMETER_BLOCK_MULTI] = "Multiple block parameters; only one block is allowed",
190
+ [YP_ERR_PARAMETER_NAME_REPEAT] = "Repeated parameter name",
191
+ [YP_ERR_PARAMETER_NO_DEFAULT] = "Expected a default value for the parameter",
192
+ [YP_ERR_PARAMETER_NO_DEFAULT_KW] = "Expected a default value for the keyword parameter",
193
+ [YP_ERR_PARAMETER_NUMBERED_RESERVED] = "Token reserved for a numbered parameter",
194
+ [YP_ERR_PARAMETER_ORDER] = "Unexpected parameter order",
195
+ [YP_ERR_PARAMETER_SPLAT_MULTI] = "Unexpected multiple `*` splat parameters",
196
+ [YP_ERR_PARAMETER_STAR] = "Unexpected parameter `*`",
197
+ [YP_ERR_PARAMETER_WILD_LOOSE_COMMA] = "Unexpected `,` in parameters",
198
+ [YP_ERR_PATTERN_EXPRESSION_AFTER_BRACKET] = "Expected a pattern expression after the `[` operator",
199
+ [YP_ERR_PATTERN_EXPRESSION_AFTER_COMMA] = "Expected a pattern expression after `,`",
200
+ [YP_ERR_PATTERN_EXPRESSION_AFTER_HROCKET] = "Expected a pattern expression after `=>`",
201
+ [YP_ERR_PATTERN_EXPRESSION_AFTER_IN] = "Expected a pattern expression after the `in` keyword",
202
+ [YP_ERR_PATTERN_EXPRESSION_AFTER_KEY] = "Expected a pattern expression after the key",
203
+ [YP_ERR_PATTERN_EXPRESSION_AFTER_PAREN] = "Expected a pattern expression after the `(` operator",
204
+ [YP_ERR_PATTERN_EXPRESSION_AFTER_PIN] = "Expected a pattern expression after the `^` pin operator",
205
+ [YP_ERR_PATTERN_EXPRESSION_AFTER_PIPE] = "Expected a pattern expression after the `|` operator",
206
+ [YP_ERR_PATTERN_EXPRESSION_AFTER_RANGE] = "Expected a pattern expression after the range operator",
207
+ [YP_ERR_PATTERN_HASH_KEY] = "Expected a key in the hash pattern",
208
+ [YP_ERR_PATTERN_HASH_KEY_LABEL] = "Expected a label as the key in the hash pattern", // TODO // THIS // AND // ABOVE // IS WEIRD
209
+ [YP_ERR_PATTERN_IDENT_AFTER_HROCKET] = "Expected an identifier after the `=>` operator",
210
+ [YP_ERR_PATTERN_LABEL_AFTER_COMMA] = "Expected a label after the `,` in the hash pattern",
211
+ [YP_ERR_PATTERN_REST] = "Unexpected rest pattern",
212
+ [YP_ERR_PATTERN_TERM_BRACE] = "Expected a `}` to close the pattern expression",
213
+ [YP_ERR_PATTERN_TERM_BRACKET] = "Expected a `]` to close the pattern expression",
214
+ [YP_ERR_PATTERN_TERM_PAREN] = "Expected a `)` to close the pattern expression",
215
+ [YP_ERR_PIPEPIPEEQ_MULTI_ASSIGN] = "Unexpected `||=` in a multiple assignment",
216
+ [YP_ERR_REGEXP_TERM] = "Expected a closing delimiter for the regular expression",
217
+ [YP_ERR_RESCUE_EXPRESSION] = "Expected a rescued expression",
218
+ [YP_ERR_RESCUE_MODIFIER_VALUE] = "Expected a value after the `rescue` modifier",
219
+ [YP_ERR_RESCUE_TERM] = "Expected a closing delimiter for the `rescue` clause",
220
+ [YP_ERR_RESCUE_VARIABLE] = "Expected an exception variable after `=>` in a rescue statement",
221
+ [YP_ERR_RETURN_INVALID] = "Invalid `return` in a class or module body",
222
+ [YP_ERR_STRING_CONCATENATION] = "Expected a string for concatenation",
223
+ [YP_ERR_STRING_INTERPOLATED_TERM] = "Expected a closing delimiter for the interpolated string",
224
+ [YP_ERR_STRING_LITERAL_TERM] = "Expected a closing delimiter for the string literal",
225
+ [YP_ERR_SYMBOL_INVALID] = "Invalid symbol", // TODO expected symbol? yarp.c ~9719
226
+ [YP_ERR_SYMBOL_TERM_DYNAMIC] = "Expected a closing delimiter for the dynamic symbol",
227
+ [YP_ERR_SYMBOL_TERM_INTERPOLATED] = "Expected a closing delimiter for the interpolated symbol",
228
+ [YP_ERR_TERNARY_COLON] = "Expected a `:` after the true expression of a ternary operator",
229
+ [YP_ERR_TERNARY_EXPRESSION_FALSE] = "Expected an expression after `:` in the ternary operator",
230
+ [YP_ERR_TERNARY_EXPRESSION_TRUE] = "Expected an expression after `?` in the ternary operator",
231
+ [YP_ERR_UNDEF_ARGUMENT] = "Invalid argument being passed to `undef`; expected a bare word, constant, or symbol argument",
232
+ [YP_ERR_UNARY_RECEIVER_BANG] = "Expected a receiver for unary `!`",
233
+ [YP_ERR_UNARY_RECEIVER_MINUS] = "Expected a receiver for unary `-`",
234
+ [YP_ERR_UNARY_RECEIVER_PLUS] = "Expected a receiver for unary `+`",
235
+ [YP_ERR_UNARY_RECEIVER_TILDE] = "Expected a receiver for unary `~`",
236
+ [YP_ERR_UNTIL_TERM] = "Expected an `end` to close the `until` statement",
237
+ [YP_ERR_WHILE_TERM] = "Expected an `end` to close the `while` statement",
238
+ [YP_ERR_WRITE_TARGET_READONLY] = "Immutable variable as a write target",
239
+ [YP_ERR_WRITE_TARGET_UNEXPECTED] = "Unexpected write target",
240
+ [YP_ERR_XSTRING_TERM] = "Expected a closing delimiter for the `%x` or backtick string",
241
+ [YP_WARN_AMBIGUOUS_FIRST_ARGUMENT_MINUS] = "Ambiguous first argument; put parentheses or a space even after `-` operator",
242
+ [YP_WARN_AMBIGUOUS_FIRST_ARGUMENT_PLUS] = "Ambiguous first argument; put parentheses or a space even after `+` operator",
243
+ [YP_WARN_AMBIGUOUS_PREFIX_STAR] = "Ambiguous `*` has been interpreted as an argument prefix",
244
+ [YP_WARN_AMBIGUOUS_SLASH] = "Ambiguous `/`; wrap regexp in parentheses or add a space after `/` operator",
245
+ };
246
+
247
+ static const char*
248
+ yp_diagnostic_message(yp_diagnostic_id_t diag_id) {
249
+ assert(diag_id < YP_DIAGNOSTIC_ID_LEN);
250
+ const char *message = diagnostic_messages[diag_id];
251
+ assert(message);
252
+ return message;
253
+ }
254
+
3
255
  // Append an error to the given list of diagnostic.
4
256
  bool
5
- yp_diagnostic_list_append(yp_list_t *list, const uint8_t *start, const uint8_t *end, const char *message) {
257
+ yp_diagnostic_list_append(yp_list_t *list, const uint8_t *start, const uint8_t *end, yp_diagnostic_id_t diag_id) {
6
258
  yp_diagnostic_t *diagnostic = (yp_diagnostic_t *) malloc(sizeof(yp_diagnostic_t));
7
259
  if (diagnostic == NULL) return false;
8
260
 
9
- *diagnostic = (yp_diagnostic_t) { .start = start, .end = end, .message = message };
261
+ *diagnostic = (yp_diagnostic_t) { .start = start, .end = end, .message = yp_diagnostic_message(diag_id) };
10
262
  yp_list_append(list, (yp_list_node_t *) diagnostic);
11
263
  return true;
12
264
  }