prism 1.4.0 → 1.5.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.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -1
  3. data/Makefile +2 -1
  4. data/README.md +1 -0
  5. data/config.yml +264 -37
  6. data/docs/parser_translation.md +8 -23
  7. data/docs/ripper_translation.md +1 -1
  8. data/ext/prism/api_node.c +2 -0
  9. data/ext/prism/extension.c +14 -1
  10. data/ext/prism/extension.h +1 -1
  11. data/include/prism/ast.h +275 -49
  12. data/include/prism/diagnostic.h +4 -0
  13. data/include/prism/options.h +43 -3
  14. data/include/prism/regexp.h +2 -2
  15. data/include/prism/util/pm_buffer.h +8 -0
  16. data/include/prism/util/pm_integer.h +4 -0
  17. data/include/prism/util/pm_list.h +6 -0
  18. data/include/prism/util/pm_string.h +12 -2
  19. data/include/prism/version.h +2 -2
  20. data/include/prism.h +39 -14
  21. data/lib/prism/compiler.rb +456 -151
  22. data/lib/prism/desugar_compiler.rb +1 -0
  23. data/lib/prism/dispatcher.rb +16 -0
  24. data/lib/prism/dot_visitor.rb +5 -1
  25. data/lib/prism/dsl.rb +3 -0
  26. data/lib/prism/ffi.rb +17 -7
  27. data/lib/prism/inspect_visitor.rb +3 -0
  28. data/lib/prism/lex_compat.rb +1 -0
  29. data/lib/prism/mutation_compiler.rb +3 -0
  30. data/lib/prism/node.rb +506 -335
  31. data/lib/prism/node_ext.rb +4 -1
  32. data/lib/prism/pack.rb +2 -0
  33. data/lib/prism/parse_result/comments.rb +1 -0
  34. data/lib/prism/parse_result/errors.rb +1 -0
  35. data/lib/prism/parse_result/newlines.rb +1 -0
  36. data/lib/prism/parse_result.rb +1 -0
  37. data/lib/prism/pattern.rb +1 -0
  38. data/lib/prism/polyfill/scan_byte.rb +14 -0
  39. data/lib/prism/polyfill/warn.rb +42 -0
  40. data/lib/prism/reflection.rb +3 -0
  41. data/lib/prism/relocation.rb +1 -0
  42. data/lib/prism/serialize.rb +24 -19
  43. data/lib/prism/string_query.rb +1 -0
  44. data/lib/prism/translation/parser/builder.rb +1 -0
  45. data/lib/prism/translation/parser/compiler.rb +47 -25
  46. data/lib/prism/translation/parser/lexer.rb +29 -21
  47. data/lib/prism/translation/parser.rb +13 -1
  48. data/lib/prism/translation/parser33.rb +1 -0
  49. data/lib/prism/translation/parser34.rb +1 -0
  50. data/lib/prism/translation/parser35.rb +1 -0
  51. data/lib/prism/translation/parser_current.rb +24 -0
  52. data/lib/prism/translation/ripper/sexp.rb +1 -0
  53. data/lib/prism/translation/ripper.rb +17 -1
  54. data/lib/prism/translation/ruby_parser.rb +286 -3
  55. data/lib/prism/translation.rb +2 -0
  56. data/lib/prism/visitor.rb +457 -152
  57. data/lib/prism.rb +2 -0
  58. data/prism.gemspec +5 -1
  59. data/rbi/prism/dsl.rbi +3 -3
  60. data/rbi/prism/node.rbi +21 -9
  61. data/sig/prism/dispatcher.rbs +3 -0
  62. data/sig/prism/dsl.rbs +3 -3
  63. data/sig/prism/node.rbs +444 -30
  64. data/sig/prism/node_ext.rbs +84 -17
  65. data/sig/prism/parse_result/comments.rbs +38 -0
  66. data/sig/prism/parse_result.rbs +4 -0
  67. data/sig/prism/reflection.rbs +1 -1
  68. data/src/diagnostic.c +7 -1
  69. data/src/node.c +2 -0
  70. data/src/options.c +2 -2
  71. data/src/prettyprint.c +2 -0
  72. data/src/prism.c +252 -130
  73. data/src/serialize.c +2 -0
  74. data/src/token_type.c +36 -34
  75. metadata +6 -2
data/src/prism.c CHANGED
@@ -1409,7 +1409,7 @@ pm_conditional_predicate_warn_write_literal_p(const pm_node_t *node) {
1409
1409
  static inline void
1410
1410
  pm_conditional_predicate_warn_write_literal(pm_parser_t *parser, const pm_node_t *node) {
1411
1411
  if (pm_conditional_predicate_warn_write_literal_p(node)) {
1412
- pm_parser_warn_node(parser, node, parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3 : PM_WARN_EQUAL_IN_CONDITIONAL);
1412
+ pm_parser_warn_node(parser, node, parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_WARN_EQUAL_IN_CONDITIONAL_3_3 : PM_WARN_EQUAL_IN_CONDITIONAL);
1413
1413
  }
1414
1414
  }
1415
1415
 
@@ -2976,7 +2976,7 @@ pm_call_and_write_node_create(pm_parser_t *parser, pm_call_node_t *target, const
2976
2976
  */
2977
2977
  static void
2978
2978
  pm_index_arguments_check(pm_parser_t *parser, const pm_arguments_node_t *arguments, const pm_node_t *block) {
2979
- if (parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) {
2979
+ if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) {
2980
2980
  if (arguments != NULL && PM_NODE_FLAG_P(arguments, PM_ARGUMENTS_NODE_FLAGS_CONTAINS_KEYWORDS)) {
2981
2981
  pm_node_t *node;
2982
2982
  PM_NODE_LIST_FOREACH(&arguments->arguments, index, node) {
@@ -3874,7 +3874,7 @@ pm_def_node_create(
3874
3874
  end = end_keyword->end;
3875
3875
  }
3876
3876
 
3877
- if ((receiver != NULL) && PM_NODE_TYPE_P(receiver, PM_PARENTHESES_NODE)) {
3877
+ if (receiver != NULL) {
3878
3878
  pm_def_node_receiver_check(parser, receiver);
3879
3879
  }
3880
3880
 
@@ -4253,7 +4253,7 @@ pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) {
4253
4253
  const uint8_t *point = memchr(start, '.', length);
4254
4254
  assert(point && "should have a decimal point");
4255
4255
 
4256
- uint8_t *digits = malloc(length);
4256
+ uint8_t *digits = xmalloc(length);
4257
4257
  if (digits == NULL) {
4258
4258
  fputs("[pm_float_node_rational_create] Failed to allocate memory", stderr);
4259
4259
  abort();
@@ -4266,7 +4266,7 @@ pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) {
4266
4266
  digits[0] = '1';
4267
4267
  if (end - point > 1) memset(digits + 1, '0', (size_t) (end - point - 1));
4268
4268
  pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + (end - point));
4269
- free(digits);
4269
+ xfree(digits);
4270
4270
 
4271
4271
  pm_integers_reduce(&node->numerator, &node->denominator);
4272
4272
  return node;
@@ -5279,6 +5279,10 @@ pm_interpolated_string_node_append(pm_interpolated_string_node_t *node, pm_node_
5279
5279
 
5280
5280
  switch (PM_NODE_TYPE(part)) {
5281
5281
  case PM_STRING_NODE:
5282
+ // If inner string is not frozen, clear flags for this string
5283
+ if (!PM_NODE_FLAG_P(part, PM_STRING_FLAGS_FROZEN)) {
5284
+ CLEAR_FLAGS(node);
5285
+ }
5282
5286
  part->flags = (pm_node_flags_t) ((part->flags | PM_NODE_FLAG_STATIC_LITERAL | PM_STRING_FLAGS_FROZEN) & ~PM_STRING_FLAGS_MUTABLE);
5283
5287
  break;
5284
5288
  case PM_INTERPOLATED_STRING_NODE:
@@ -8582,85 +8586,66 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
8582
8586
  /* Context manipulations */
8583
8587
  /******************************************************************************/
8584
8588
 
8585
- static bool
8586
- context_terminator(pm_context_t context, pm_token_t *token) {
8587
- switch (context) {
8588
- case PM_CONTEXT_MAIN:
8589
- case PM_CONTEXT_DEF_PARAMS:
8590
- case PM_CONTEXT_DEFINED:
8591
- case PM_CONTEXT_MULTI_TARGET:
8592
- case PM_CONTEXT_TERNARY:
8593
- case PM_CONTEXT_RESCUE_MODIFIER:
8594
- return token->type == PM_TOKEN_EOF;
8595
- case PM_CONTEXT_DEFAULT_PARAMS:
8596
- return token->type == PM_TOKEN_COMMA || token->type == PM_TOKEN_PARENTHESIS_RIGHT;
8597
- case PM_CONTEXT_PREEXE:
8598
- case PM_CONTEXT_POSTEXE:
8599
- return token->type == PM_TOKEN_BRACE_RIGHT;
8600
- case PM_CONTEXT_MODULE:
8601
- case PM_CONTEXT_CLASS:
8602
- case PM_CONTEXT_SCLASS:
8603
- case PM_CONTEXT_LAMBDA_DO_END:
8604
- case PM_CONTEXT_DEF:
8605
- case PM_CONTEXT_BLOCK_KEYWORDS:
8606
- return token->type == PM_TOKEN_KEYWORD_END || token->type == PM_TOKEN_KEYWORD_RESCUE || token->type == PM_TOKEN_KEYWORD_ENSURE;
8607
- case PM_CONTEXT_WHILE:
8608
- case PM_CONTEXT_UNTIL:
8609
- case PM_CONTEXT_ELSE:
8610
- case PM_CONTEXT_FOR:
8611
- case PM_CONTEXT_BEGIN_ENSURE:
8612
- case PM_CONTEXT_BLOCK_ENSURE:
8613
- case PM_CONTEXT_CLASS_ENSURE:
8614
- case PM_CONTEXT_DEF_ENSURE:
8615
- case PM_CONTEXT_LAMBDA_ENSURE:
8616
- case PM_CONTEXT_MODULE_ENSURE:
8617
- case PM_CONTEXT_SCLASS_ENSURE:
8618
- return token->type == PM_TOKEN_KEYWORD_END;
8619
- case PM_CONTEXT_LOOP_PREDICATE:
8620
- return token->type == PM_TOKEN_KEYWORD_DO || token->type == PM_TOKEN_KEYWORD_THEN;
8621
- case PM_CONTEXT_FOR_INDEX:
8622
- return token->type == PM_TOKEN_KEYWORD_IN;
8623
- case PM_CONTEXT_CASE_WHEN:
8624
- return token->type == PM_TOKEN_KEYWORD_WHEN || token->type == PM_TOKEN_KEYWORD_END || token->type == PM_TOKEN_KEYWORD_ELSE;
8625
- case PM_CONTEXT_CASE_IN:
8626
- return token->type == PM_TOKEN_KEYWORD_IN || token->type == PM_TOKEN_KEYWORD_END || token->type == PM_TOKEN_KEYWORD_ELSE;
8627
- case PM_CONTEXT_IF:
8628
- case PM_CONTEXT_ELSIF:
8629
- return token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_ELSIF || token->type == PM_TOKEN_KEYWORD_END;
8630
- case PM_CONTEXT_UNLESS:
8631
- return token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_END;
8632
- case PM_CONTEXT_EMBEXPR:
8633
- return token->type == PM_TOKEN_EMBEXPR_END;
8634
- case PM_CONTEXT_BLOCK_BRACES:
8635
- return token->type == PM_TOKEN_BRACE_RIGHT;
8636
- case PM_CONTEXT_PARENS:
8637
- return token->type == PM_TOKEN_PARENTHESIS_RIGHT;
8638
- case PM_CONTEXT_BEGIN:
8639
- case PM_CONTEXT_BEGIN_RESCUE:
8640
- case PM_CONTEXT_BLOCK_RESCUE:
8641
- case PM_CONTEXT_CLASS_RESCUE:
8642
- case PM_CONTEXT_DEF_RESCUE:
8643
- case PM_CONTEXT_LAMBDA_RESCUE:
8644
- case PM_CONTEXT_MODULE_RESCUE:
8645
- case PM_CONTEXT_SCLASS_RESCUE:
8646
- return token->type == PM_TOKEN_KEYWORD_ENSURE || token->type == PM_TOKEN_KEYWORD_RESCUE || token->type == PM_TOKEN_KEYWORD_ELSE || token->type == PM_TOKEN_KEYWORD_END;
8647
- case PM_CONTEXT_BEGIN_ELSE:
8648
- case PM_CONTEXT_BLOCK_ELSE:
8649
- case PM_CONTEXT_CLASS_ELSE:
8650
- case PM_CONTEXT_DEF_ELSE:
8651
- case PM_CONTEXT_LAMBDA_ELSE:
8652
- case PM_CONTEXT_MODULE_ELSE:
8653
- case PM_CONTEXT_SCLASS_ELSE:
8654
- return token->type == PM_TOKEN_KEYWORD_ENSURE || token->type == PM_TOKEN_KEYWORD_END;
8655
- case PM_CONTEXT_LAMBDA_BRACES:
8656
- return token->type == PM_TOKEN_BRACE_RIGHT;
8657
- case PM_CONTEXT_PREDICATE:
8658
- return token->type == PM_TOKEN_KEYWORD_THEN || token->type == PM_TOKEN_NEWLINE || token->type == PM_TOKEN_SEMICOLON;
8659
- case PM_CONTEXT_NONE:
8660
- return false;
8661
- }
8589
+ static const uint32_t context_terminators[] = {
8590
+ [PM_CONTEXT_NONE] = 0,
8591
+ [PM_CONTEXT_BEGIN] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8592
+ [PM_CONTEXT_BEGIN_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8593
+ [PM_CONTEXT_BEGIN_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8594
+ [PM_CONTEXT_BEGIN_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8595
+ [PM_CONTEXT_BLOCK_BRACES] = (1 << PM_TOKEN_BRACE_RIGHT),
8596
+ [PM_CONTEXT_BLOCK_KEYWORDS] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
8597
+ [PM_CONTEXT_BLOCK_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8598
+ [PM_CONTEXT_BLOCK_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8599
+ [PM_CONTEXT_BLOCK_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8600
+ [PM_CONTEXT_CASE_WHEN] = (1 << PM_TOKEN_KEYWORD_WHEN) | (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_ELSE),
8601
+ [PM_CONTEXT_CASE_IN] = (1 << PM_TOKEN_KEYWORD_IN) | (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_ELSE),
8602
+ [PM_CONTEXT_CLASS] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
8603
+ [PM_CONTEXT_CLASS_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8604
+ [PM_CONTEXT_CLASS_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8605
+ [PM_CONTEXT_CLASS_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8606
+ [PM_CONTEXT_DEF] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
8607
+ [PM_CONTEXT_DEF_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8608
+ [PM_CONTEXT_DEF_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8609
+ [PM_CONTEXT_DEF_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8610
+ [PM_CONTEXT_DEF_PARAMS] = (1 << PM_TOKEN_EOF),
8611
+ [PM_CONTEXT_DEFINED] = (1 << PM_TOKEN_EOF),
8612
+ [PM_CONTEXT_DEFAULT_PARAMS] = (1 << PM_TOKEN_COMMA) | (1 << PM_TOKEN_PARENTHESIS_RIGHT),
8613
+ [PM_CONTEXT_ELSE] = (1 << PM_TOKEN_KEYWORD_END),
8614
+ [PM_CONTEXT_ELSIF] = (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_ELSIF) | (1 << PM_TOKEN_KEYWORD_END),
8615
+ [PM_CONTEXT_EMBEXPR] = (1 << PM_TOKEN_EMBEXPR_END),
8616
+ [PM_CONTEXT_FOR] = (1 << PM_TOKEN_KEYWORD_END),
8617
+ [PM_CONTEXT_FOR_INDEX] = (1 << PM_TOKEN_KEYWORD_IN),
8618
+ [PM_CONTEXT_IF] = (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_ELSIF) | (1 << PM_TOKEN_KEYWORD_END),
8619
+ [PM_CONTEXT_LAMBDA_BRACES] = (1 << PM_TOKEN_BRACE_RIGHT),
8620
+ [PM_CONTEXT_LAMBDA_DO_END] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
8621
+ [PM_CONTEXT_LAMBDA_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8622
+ [PM_CONTEXT_LAMBDA_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8623
+ [PM_CONTEXT_LAMBDA_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8624
+ [PM_CONTEXT_LOOP_PREDICATE] = (1 << PM_TOKEN_KEYWORD_DO) | (1 << PM_TOKEN_KEYWORD_THEN),
8625
+ [PM_CONTEXT_MAIN] = (1 << PM_TOKEN_EOF),
8626
+ [PM_CONTEXT_MODULE] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
8627
+ [PM_CONTEXT_MODULE_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8628
+ [PM_CONTEXT_MODULE_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8629
+ [PM_CONTEXT_MODULE_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8630
+ [PM_CONTEXT_MULTI_TARGET] = (1 << PM_TOKEN_EOF),
8631
+ [PM_CONTEXT_PARENS] = (1 << PM_TOKEN_PARENTHESIS_RIGHT),
8632
+ [PM_CONTEXT_POSTEXE] = (1 << PM_TOKEN_BRACE_RIGHT),
8633
+ [PM_CONTEXT_PREDICATE] = (1 << PM_TOKEN_KEYWORD_THEN) | (1 << PM_TOKEN_NEWLINE) | (1 << PM_TOKEN_SEMICOLON),
8634
+ [PM_CONTEXT_PREEXE] = (1 << PM_TOKEN_BRACE_RIGHT),
8635
+ [PM_CONTEXT_RESCUE_MODIFIER] = (1 << PM_TOKEN_EOF),
8636
+ [PM_CONTEXT_SCLASS] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
8637
+ [PM_CONTEXT_SCLASS_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8638
+ [PM_CONTEXT_SCLASS_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8639
+ [PM_CONTEXT_SCLASS_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8640
+ [PM_CONTEXT_TERNARY] = (1 << PM_TOKEN_EOF),
8641
+ [PM_CONTEXT_UNLESS] = (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8642
+ [PM_CONTEXT_UNTIL] = (1 << PM_TOKEN_KEYWORD_END),
8643
+ [PM_CONTEXT_WHILE] = (1 << PM_TOKEN_KEYWORD_END),
8644
+ };
8662
8645
 
8663
- return false;
8646
+ static inline bool
8647
+ context_terminator(pm_context_t context, pm_token_t *token) {
8648
+ return token->type < 32 && (context_terminators[context] & (1 << token->type));
8664
8649
  }
8665
8650
 
8666
8651
  /**
@@ -9109,7 +9094,7 @@ lex_global_variable(pm_parser_t *parser) {
9109
9094
  } while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
9110
9095
 
9111
9096
  // $0 isn't allowed to be followed by anything.
9112
- pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL;
9097
+ pm_diagnostic_id_t diag_id = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL;
9113
9098
  PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, diag_id);
9114
9099
  }
9115
9100
 
@@ -9146,7 +9131,7 @@ lex_global_variable(pm_parser_t *parser) {
9146
9131
  } else {
9147
9132
  // If we get here, then we have a $ followed by something that
9148
9133
  // isn't recognized as a global variable.
9149
- pm_diagnostic_id_t diag_id = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL;
9134
+ pm_diagnostic_id_t diag_id = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? PM_ERR_INVALID_VARIABLE_GLOBAL_3_3 : PM_ERR_INVALID_VARIABLE_GLOBAL;
9150
9135
  const uint8_t *end = parser->current.end + parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9151
9136
  PM_PARSER_ERR_FORMAT(parser, parser->current.start, end, diag_id, (int) (end - parser->current.start), (const char *) parser->current.start);
9152
9137
  }
@@ -10173,7 +10158,7 @@ lex_at_variable(pm_parser_t *parser) {
10173
10158
  }
10174
10159
  } else if (parser->current.end < end && pm_char_is_decimal_digit(*parser->current.end)) {
10175
10160
  pm_diagnostic_id_t diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE;
10176
- if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) {
10161
+ if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) {
10177
10162
  diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3;
10178
10163
  }
10179
10164
 
@@ -10849,14 +10834,37 @@ parser_lex(pm_parser_t *parser) {
10849
10834
  following = next_newline(following, parser->end - following);
10850
10835
  }
10851
10836
 
10852
- // If the lex state was ignored, or we hit a '.' or a '&.',
10853
- // we will lex the ignored newline
10837
+ // If the lex state was ignored, we will lex the
10838
+ // ignored newline.
10839
+ if (lex_state_ignored_p(parser)) {
10840
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
10841
+ lexed_comment = false;
10842
+ goto lex_next_token;
10843
+ }
10844
+
10845
+ // If we hit a '.' or a '&.' we will lex the ignored
10846
+ // newline.
10847
+ if (following && (
10848
+ (peek_at(parser, following) == '.') ||
10849
+ (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
10850
+ )) {
10851
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
10852
+ lexed_comment = false;
10853
+ goto lex_next_token;
10854
+ }
10855
+
10856
+
10857
+ // If we are parsing as CRuby 3.5 or later and we
10858
+ // hit a '&&' or a '||' then we will lex the ignored
10859
+ // newline.
10854
10860
  if (
10855
- lex_state_ignored_p(parser) ||
10856
- (following && (
10857
- (peek_at(parser, following) == '.') ||
10858
- (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
10859
- ))
10861
+ (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) &&
10862
+ following && (
10863
+ (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') ||
10864
+ (peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') ||
10865
+ (peek_at(parser, following) == 'a' && peek_at(parser, following + 1) == 'n' && peek_at(parser, following + 2) == 'd' && !char_is_identifier(parser, following + 3, parser->end - (following + 3))) ||
10866
+ (peek_at(parser, following) == 'o' && peek_at(parser, following + 1) == 'r' && !char_is_identifier(parser, following + 2, parser->end - (following + 2)))
10867
+ )
10860
10868
  ) {
10861
10869
  if (!lexed_comment) parser_lex_ignored_newline(parser);
10862
10870
  lexed_comment = false;
@@ -10896,6 +10904,63 @@ parser_lex(pm_parser_t *parser) {
10896
10904
  parser->next_start = NULL;
10897
10905
  LEX(PM_TOKEN_AMPERSAND_DOT);
10898
10906
  }
10907
+
10908
+ if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) {
10909
+ // If we hit an && then we are in a logical chain
10910
+ // and we need to return the logical operator.
10911
+ if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '&') {
10912
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
10913
+ lex_state_set(parser, PM_LEX_STATE_BEG);
10914
+ parser->current.start = next_content;
10915
+ parser->current.end = next_content + 2;
10916
+ parser->next_start = NULL;
10917
+ LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10918
+ }
10919
+
10920
+ // If we hit a || then we are in a logical chain and
10921
+ // we need to return the logical operator.
10922
+ if (peek_at(parser, next_content) == '|' && peek_at(parser, next_content + 1) == '|') {
10923
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
10924
+ lex_state_set(parser, PM_LEX_STATE_BEG);
10925
+ parser->current.start = next_content;
10926
+ parser->current.end = next_content + 2;
10927
+ parser->next_start = NULL;
10928
+ LEX(PM_TOKEN_PIPE_PIPE);
10929
+ }
10930
+
10931
+ // If we hit an 'and' then we are in a logical chain
10932
+ // and we need to return the logical operator.
10933
+ if (
10934
+ peek_at(parser, next_content) == 'a' &&
10935
+ peek_at(parser, next_content + 1) == 'n' &&
10936
+ peek_at(parser, next_content + 2) == 'd' &&
10937
+ !char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3))
10938
+ ) {
10939
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
10940
+ lex_state_set(parser, PM_LEX_STATE_BEG);
10941
+ parser->current.start = next_content;
10942
+ parser->current.end = next_content + 3;
10943
+ parser->next_start = NULL;
10944
+ parser->command_start = true;
10945
+ LEX(PM_TOKEN_KEYWORD_AND);
10946
+ }
10947
+
10948
+ // If we hit a 'or' then we are in a logical chain
10949
+ // and we need to return the logical operator.
10950
+ if (
10951
+ peek_at(parser, next_content) == 'o' &&
10952
+ peek_at(parser, next_content + 1) == 'r' &&
10953
+ !char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2))
10954
+ ) {
10955
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
10956
+ lex_state_set(parser, PM_LEX_STATE_BEG);
10957
+ parser->current.start = next_content;
10958
+ parser->current.end = next_content + 2;
10959
+ parser->next_start = NULL;
10960
+ parser->command_start = true;
10961
+ LEX(PM_TOKEN_KEYWORD_OR);
10962
+ }
10963
+ }
10899
10964
  }
10900
10965
 
10901
10966
  // At this point we know this is a regular newline, and we can set the
@@ -13142,14 +13207,6 @@ match8(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2,
13142
13207
  return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6) || match1(parser, type7) || match1(parser, type8);
13143
13208
  }
13144
13209
 
13145
- /**
13146
- * Returns true if the current token is any of the nine given types.
13147
- */
13148
- static inline bool
13149
- match9(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2, pm_token_type_t type3, pm_token_type_t type4, pm_token_type_t type5, pm_token_type_t type6, pm_token_type_t type7, pm_token_type_t type8, pm_token_type_t type9) {
13150
- return match1(parser, type1) || match1(parser, type2) || match1(parser, type3) || match1(parser, type4) || match1(parser, type5) || match1(parser, type6) || match1(parser, type7) || match1(parser, type8) || match1(parser, type9);
13151
- }
13152
-
13153
13210
  /**
13154
13211
  * If the current token is of the specified type, lex forward by one token and
13155
13212
  * return true. Otherwise, return false. For example:
@@ -14671,7 +14728,7 @@ parse_parameters(
14671
14728
  parser_lex(parser);
14672
14729
 
14673
14730
  pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &name);
14674
- uint32_t reads = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14731
+ uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14675
14732
 
14676
14733
  if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14677
14734
  pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1));
@@ -14687,7 +14744,7 @@ parse_parameters(
14687
14744
  // If the value of the parameter increased the number of
14688
14745
  // reads of that parameter, then we need to warn that we
14689
14746
  // have a circular definition.
14690
- if ((parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14747
+ if ((parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14691
14748
  PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, name, PM_ERR_PARAMETER_CIRCULAR);
14692
14749
  }
14693
14750
 
@@ -14772,13 +14829,13 @@ parse_parameters(
14772
14829
 
14773
14830
  if (token_begins_expression_p(parser->current.type)) {
14774
14831
  pm_constant_id_t name_id = pm_parser_constant_id_token(parser, &local);
14775
- uint32_t reads = parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14832
+ uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14776
14833
 
14777
14834
  if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14778
14835
  pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT_KW, (uint16_t) (depth + 1));
14779
14836
  if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14780
14837
 
14781
- if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14838
+ if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14782
14839
  PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR);
14783
14840
  }
14784
14841
 
@@ -16482,7 +16539,7 @@ parse_variable(pm_parser_t *parser) {
16482
16539
  pm_node_list_append(&current_scope->implicit_parameters, node);
16483
16540
 
16484
16541
  return node;
16485
- } else if ((parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
16542
+ } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
16486
16543
  pm_node_t *node = (pm_node_t *) pm_it_local_variable_read_node_create(parser, &parser->previous);
16487
16544
  pm_node_list_append(&current_scope->implicit_parameters, node);
16488
16545
 
@@ -17412,6 +17469,14 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm
17412
17469
  // If we found a label, we need to immediately return to the caller.
17413
17470
  if (pm_symbol_node_label_p(node)) return node;
17414
17471
 
17472
+ // Call nodes (arithmetic operations) are not allowed in patterns
17473
+ if (PM_NODE_TYPE(node) == PM_CALL_NODE) {
17474
+ pm_parser_err_node(parser, node, diag_id);
17475
+ pm_missing_node_t *missing_node = pm_missing_node_create(parser, node->location.start, node->location.end);
17476
+ pm_node_destroy(parser, node);
17477
+ return (pm_node_t *) missing_node;
17478
+ }
17479
+
17415
17480
  // Now that we have a primitive, we need to check if it's part of a range.
17416
17481
  if (accept2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
17417
17482
  pm_token_t operator = parser->previous;
@@ -17694,7 +17759,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag
17694
17759
  // Gather up all of the patterns into the list.
17695
17760
  while (accept1(parser, PM_TOKEN_COMMA)) {
17696
17761
  // Break early here in case we have a trailing comma.
17697
- if (match9(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_SEMICOLON, PM_TOKEN_NEWLINE, PM_TOKEN_EOF,PM_TOKEN_KEYWORD_AND, PM_TOKEN_KEYWORD_OR)) {
17762
+ if (match7(parser, PM_TOKEN_KEYWORD_THEN, PM_TOKEN_BRACE_RIGHT, PM_TOKEN_BRACKET_RIGHT, PM_TOKEN_PARENTHESIS_RIGHT, PM_TOKEN_SEMICOLON, PM_TOKEN_KEYWORD_AND, PM_TOKEN_KEYWORD_OR)) {
17698
17763
  node = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous);
17699
17764
  pm_node_list_append(&nodes, node);
17700
17765
  trailing_rest = true;
@@ -18585,17 +18650,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18585
18650
  call->closing_loc = arguments.closing_loc;
18586
18651
  call->block = arguments.block;
18587
18652
 
18588
- if (arguments.block != NULL) {
18589
- call->base.location.end = arguments.block->location.end;
18590
- } else if (arguments.closing_loc.start == NULL) {
18591
- if (arguments.arguments != NULL) {
18592
- call->base.location.end = arguments.arguments->base.location.end;
18593
- } else {
18594
- call->base.location.end = call->message_loc.end;
18595
- }
18596
- } else {
18597
- call->base.location.end = arguments.closing_loc.end;
18653
+ const uint8_t *end = pm_arguments_end(&arguments);
18654
+ if (!end) {
18655
+ end = call->message_loc.end;
18598
18656
  }
18657
+ call->base.location.end = end;
18599
18658
  }
18600
18659
  } else {
18601
18660
  // Otherwise, we know the identifier is in the local table. This
@@ -19123,7 +19182,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
19123
19182
  pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
19124
19183
 
19125
19184
  if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
19185
+ pm_token_t next = parser->current;
19126
19186
  parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, (uint16_t) (depth + 1));
19187
+
19188
+ // Reject `foo && return bar`.
19189
+ if (!accepts_command_call && arguments.arguments != NULL) {
19190
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(next.type));
19191
+ }
19127
19192
  }
19128
19193
  }
19129
19194
 
@@ -19520,7 +19585,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
19520
19585
  pm_do_loop_stack_push(parser, false);
19521
19586
  statements = (pm_node_t *) pm_statements_node_create(parser);
19522
19587
 
19523
- pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, binding_power < PM_BINDING_POWER_COMPOSITION, false, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1));
19588
+ bool allow_command_call;
19589
+ if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) {
19590
+ allow_command_call = accepts_command_call;
19591
+ } else {
19592
+ // Allow `def foo = puts "Hello"` but not `private def foo = puts "Hello"`
19593
+ allow_command_call = binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION;
19594
+ }
19595
+
19596
+ pm_node_t *statement = parse_expression(parser, PM_BINDING_POWER_DEFINED + 1, allow_command_call, false, PM_ERR_DEF_ENDLESS, (uint16_t) (depth + 1));
19524
19597
 
19525
19598
  if (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
19526
19599
  context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
@@ -19607,18 +19680,27 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
19607
19680
  pm_token_t lparen;
19608
19681
  pm_token_t rparen;
19609
19682
  pm_node_t *expression;
19683
+
19610
19684
  context_push(parser, PM_CONTEXT_DEFINED);
19685
+ bool newline = accept1(parser, PM_TOKEN_NEWLINE);
19611
19686
 
19612
19687
  if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
19613
19688
  lparen = parser->previous;
19614
- expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
19615
19689
 
19616
- if (parser->recovering) {
19690
+ if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19691
+ expression = (pm_node_t *) pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0);
19692
+ lparen = not_provided(parser);
19617
19693
  rparen = not_provided(parser);
19618
19694
  } else {
19619
- accept1(parser, PM_TOKEN_NEWLINE);
19620
- expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19621
- rparen = parser->previous;
19695
+ expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
19696
+
19697
+ if (parser->recovering) {
19698
+ rparen = not_provided(parser);
19699
+ } else {
19700
+ accept1(parser, PM_TOKEN_NEWLINE);
19701
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19702
+ rparen = parser->previous;
19703
+ }
19622
19704
  }
19623
19705
  } else {
19624
19706
  lparen = not_provided(parser);
@@ -19766,6 +19848,20 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
19766
19848
  pm_arguments_t arguments = { 0 };
19767
19849
  pm_node_t *receiver = NULL;
19768
19850
 
19851
+ // If we do not accept a command call, then we also do not accept a
19852
+ // not without parentheses. In this case we need to reject this
19853
+ // syntax.
19854
+ if (!accepts_command_call && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
19855
+ if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) {
19856
+ pm_parser_err(parser, parser->previous.end, parser->previous.end + 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN);
19857
+ } else {
19858
+ accept1(parser, PM_TOKEN_NEWLINE);
19859
+ pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER);
19860
+ }
19861
+
19862
+ return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end);
19863
+ }
19864
+
19769
19865
  accept1(parser, PM_TOKEN_NEWLINE);
19770
19866
 
19771
19867
  if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
@@ -21167,6 +21263,13 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
21167
21263
  }
21168
21264
  PRISM_FALLTHROUGH
21169
21265
  case PM_CASE_WRITABLE: {
21266
+ // When we have `it = value`, we need to add `it` as a local
21267
+ // variable before parsing the value, in case the value
21268
+ // references the variable.
21269
+ if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
21270
+ pm_parser_local_add_location(parser, node->location.start, node->location.end, 0);
21271
+ }
21272
+
21170
21273
  parser_lex(parser);
21171
21274
  pm_node_t *value = parse_assignment_values(parser, previous_binding_power, PM_NODE_TYPE_P(node, PM_MULTI_TARGET_NODE) ? PM_BINDING_POWER_MULTI_ASSIGNMENT + 1 : binding_power, accepts_command_call, PM_ERR_EXPECT_EXPRESSION_AFTER_EQUAL, (uint16_t) (depth + 1));
21172
21275
 
@@ -22160,6 +22263,12 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
22160
22263
  ) {
22161
22264
  node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, accepts_command_call, (uint16_t) (depth + 1));
22162
22265
 
22266
+ if (context_terminator(parser->current_context->context, &parser->current)) {
22267
+ // If this token terminates the current context, then we need to
22268
+ // stop parsing the expression, as it has become a statement.
22269
+ return node;
22270
+ }
22271
+
22163
22272
  switch (PM_NODE_TYPE(node)) {
22164
22273
  case PM_MULTI_WRITE_NODE:
22165
22274
  // Multi-write nodes are statements, and cannot be followed by
@@ -22614,6 +22723,12 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm
22614
22723
  }
22615
22724
  }
22616
22725
 
22726
+ // Now that we have established the user-provided options, check if
22727
+ // a version was given and parse as the latest version otherwise.
22728
+ if (parser->version == PM_OPTIONS_VERSION_UNSET) {
22729
+ parser->version = PM_OPTIONS_VERSION_LATEST;
22730
+ }
22731
+
22617
22732
  pm_accepts_block_stack_push(parser, true);
22618
22733
 
22619
22734
  // Skip past the UTF-8 BOM if it exists.
@@ -22667,7 +22782,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm
22667
22782
  }
22668
22783
 
22669
22784
  search_shebang = false;
22670
- } else if (options->main_script && !parser->parsing_eval) {
22785
+ } else if (options != NULL && options->main_script && !parser->parsing_eval) {
22671
22786
  search_shebang = true;
22672
22787
  }
22673
22788
  }
@@ -22807,7 +22922,7 @@ pm_parse(pm_parser_t *parser) {
22807
22922
  * otherwise return true.
22808
22923
  */
22809
22924
  static bool
22810
- pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets) {
22925
+ pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof) {
22811
22926
  #define LINE_SIZE 4096
22812
22927
  char line[LINE_SIZE];
22813
22928
 
@@ -22843,6 +22958,12 @@ pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t
22843
22958
  if (strncmp(line, "__END__\r\n", 9) == 0) return false;
22844
22959
  break;
22845
22960
  }
22961
+
22962
+ // All data should be read via gets. If the string returned by gets
22963
+ // _doesn't_ end with a newline, then we assume we hit EOF condition.
22964
+ if (stream_feof(stream)) {
22965
+ break;
22966
+ }
22846
22967
  }
22847
22968
 
22848
22969
  return true;
@@ -22878,16 +22999,17 @@ pm_parse_stream_unterminated_heredoc_p(pm_parser_t *parser) {
22878
22999
  * can stream stdin in to Ruby so we need to support a streaming API.
22879
23000
  */
22880
23001
  PRISM_EXPORTED_FUNCTION pm_node_t *
22881
- pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const pm_options_t *options) {
23002
+ pm_parse_stream(pm_parser_t *parser, pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const pm_options_t *options) {
22882
23003
  pm_buffer_init(buffer);
22883
23004
 
22884
- bool eof = pm_parse_stream_read(buffer, stream, stream_fgets);
23005
+ bool eof = pm_parse_stream_read(buffer, stream, stream_fgets, stream_feof);
23006
+
22885
23007
  pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options);
22886
23008
  pm_node_t *node = pm_parse(parser);
22887
23009
 
22888
23010
  while (!eof && parser->error_list.size > 0 && (parser->lex_modes.index > 0 || pm_parse_stream_unterminated_heredoc_p(parser))) {
22889
23011
  pm_node_destroy(parser, node);
22890
- eof = pm_parse_stream_read(buffer, stream, stream_fgets);
23012
+ eof = pm_parse_stream_read(buffer, stream, stream_fgets, stream_feof);
22891
23013
 
22892
23014
  pm_parser_free(parser);
22893
23015
  pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options);
@@ -22979,13 +23101,13 @@ pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, cons
22979
23101
  * given stream into to the given buffer.
22980
23102
  */
22981
23103
  PRISM_EXPORTED_FUNCTION void
22982
- pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, const char *data) {
23104
+ pm_serialize_parse_stream(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets, pm_parse_stream_feof_t *stream_feof, const char *data) {
22983
23105
  pm_parser_t parser;
22984
23106
  pm_options_t options = { 0 };
22985
23107
  pm_options_read(&options, data);
22986
23108
 
22987
23109
  pm_buffer_t parser_buffer;
22988
- pm_node_t *node = pm_parse_stream(&parser, &parser_buffer, stream, stream_fgets, &options);
23110
+ pm_node_t *node = pm_parse_stream(&parser, &parser_buffer, stream, stream_fgets, stream_feof, &options);
22989
23111
  pm_serialize_header(buffer);
22990
23112
  pm_serialize_content(&parser, node, buffer);
22991
23113
  pm_buffer_append_byte(buffer, '\0');
data/src/serialize.c CHANGED
@@ -1,3 +1,5 @@
1
+ /* :markup: markdown */
2
+
1
3
  /*----------------------------------------------------------------------------*/
2
4
  /* This file is generated by the templates/template.rb script and should not */
3
5
  /* be modified manually. See */