prism 1.4.0 → 1.5.1

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 +30 -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 +3 -3
  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 +25 -20
  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 +248 -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;
@@ -8582,85 +8582,66 @@ parser_lex_magic_comment(pm_parser_t *parser, bool semantic_token_seen) {
8582
8582
  /* Context manipulations */
8583
8583
  /******************************************************************************/
8584
8584
 
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
- }
8585
+ static const uint32_t context_terminators[] = {
8586
+ [PM_CONTEXT_NONE] = 0,
8587
+ [PM_CONTEXT_BEGIN] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8588
+ [PM_CONTEXT_BEGIN_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8589
+ [PM_CONTEXT_BEGIN_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8590
+ [PM_CONTEXT_BEGIN_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8591
+ [PM_CONTEXT_BLOCK_BRACES] = (1 << PM_TOKEN_BRACE_RIGHT),
8592
+ [PM_CONTEXT_BLOCK_KEYWORDS] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
8593
+ [PM_CONTEXT_BLOCK_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8594
+ [PM_CONTEXT_BLOCK_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8595
+ [PM_CONTEXT_BLOCK_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8596
+ [PM_CONTEXT_CASE_WHEN] = (1 << PM_TOKEN_KEYWORD_WHEN) | (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_ELSE),
8597
+ [PM_CONTEXT_CASE_IN] = (1 << PM_TOKEN_KEYWORD_IN) | (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_ELSE),
8598
+ [PM_CONTEXT_CLASS] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
8599
+ [PM_CONTEXT_CLASS_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8600
+ [PM_CONTEXT_CLASS_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8601
+ [PM_CONTEXT_CLASS_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8602
+ [PM_CONTEXT_DEF] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
8603
+ [PM_CONTEXT_DEF_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8604
+ [PM_CONTEXT_DEF_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8605
+ [PM_CONTEXT_DEF_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_PARAMS] = (1 << PM_TOKEN_EOF),
8607
+ [PM_CONTEXT_DEFINED] = (1 << PM_TOKEN_EOF),
8608
+ [PM_CONTEXT_DEFAULT_PARAMS] = (1 << PM_TOKEN_COMMA) | (1 << PM_TOKEN_PARENTHESIS_RIGHT),
8609
+ [PM_CONTEXT_ELSE] = (1 << PM_TOKEN_KEYWORD_END),
8610
+ [PM_CONTEXT_ELSIF] = (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_ELSIF) | (1 << PM_TOKEN_KEYWORD_END),
8611
+ [PM_CONTEXT_EMBEXPR] = (1 << PM_TOKEN_EMBEXPR_END),
8612
+ [PM_CONTEXT_FOR] = (1 << PM_TOKEN_KEYWORD_END),
8613
+ [PM_CONTEXT_FOR_INDEX] = (1 << PM_TOKEN_KEYWORD_IN),
8614
+ [PM_CONTEXT_IF] = (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_ELSIF) | (1 << PM_TOKEN_KEYWORD_END),
8615
+ [PM_CONTEXT_LAMBDA_BRACES] = (1 << PM_TOKEN_BRACE_RIGHT),
8616
+ [PM_CONTEXT_LAMBDA_DO_END] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
8617
+ [PM_CONTEXT_LAMBDA_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8618
+ [PM_CONTEXT_LAMBDA_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8619
+ [PM_CONTEXT_LAMBDA_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8620
+ [PM_CONTEXT_LOOP_PREDICATE] = (1 << PM_TOKEN_KEYWORD_DO) | (1 << PM_TOKEN_KEYWORD_THEN),
8621
+ [PM_CONTEXT_MAIN] = (1 << PM_TOKEN_EOF),
8622
+ [PM_CONTEXT_MODULE] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
8623
+ [PM_CONTEXT_MODULE_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8624
+ [PM_CONTEXT_MODULE_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8625
+ [PM_CONTEXT_MODULE_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8626
+ [PM_CONTEXT_MULTI_TARGET] = (1 << PM_TOKEN_EOF),
8627
+ [PM_CONTEXT_PARENS] = (1 << PM_TOKEN_PARENTHESIS_RIGHT),
8628
+ [PM_CONTEXT_POSTEXE] = (1 << PM_TOKEN_BRACE_RIGHT),
8629
+ [PM_CONTEXT_PREDICATE] = (1 << PM_TOKEN_KEYWORD_THEN) | (1 << PM_TOKEN_NEWLINE) | (1 << PM_TOKEN_SEMICOLON),
8630
+ [PM_CONTEXT_PREEXE] = (1 << PM_TOKEN_BRACE_RIGHT),
8631
+ [PM_CONTEXT_RESCUE_MODIFIER] = (1 << PM_TOKEN_EOF),
8632
+ [PM_CONTEXT_SCLASS] = (1 << PM_TOKEN_KEYWORD_END) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ENSURE),
8633
+ [PM_CONTEXT_SCLASS_ENSURE] = (1 << PM_TOKEN_KEYWORD_END),
8634
+ [PM_CONTEXT_SCLASS_ELSE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_END),
8635
+ [PM_CONTEXT_SCLASS_RESCUE] = (1 << PM_TOKEN_KEYWORD_ENSURE) | (1 << PM_TOKEN_KEYWORD_RESCUE) | (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8636
+ [PM_CONTEXT_TERNARY] = (1 << PM_TOKEN_EOF),
8637
+ [PM_CONTEXT_UNLESS] = (1 << PM_TOKEN_KEYWORD_ELSE) | (1 << PM_TOKEN_KEYWORD_END),
8638
+ [PM_CONTEXT_UNTIL] = (1 << PM_TOKEN_KEYWORD_END),
8639
+ [PM_CONTEXT_WHILE] = (1 << PM_TOKEN_KEYWORD_END),
8640
+ };
8662
8641
 
8663
- return false;
8642
+ static inline bool
8643
+ context_terminator(pm_context_t context, pm_token_t *token) {
8644
+ return token->type < 32 && (context_terminators[context] & (1 << token->type));
8664
8645
  }
8665
8646
 
8666
8647
  /**
@@ -9109,7 +9090,7 @@ lex_global_variable(pm_parser_t *parser) {
9109
9090
  } while ((width = char_is_identifier(parser, parser->current.end, parser->end - parser->current.end)) > 0);
9110
9091
 
9111
9092
  // $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;
9093
+ 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
9094
  PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, parser->current, diag_id);
9114
9095
  }
9115
9096
 
@@ -9146,7 +9127,7 @@ lex_global_variable(pm_parser_t *parser) {
9146
9127
  } else {
9147
9128
  // If we get here, then we have a $ followed by something that
9148
9129
  // 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;
9130
+ 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
9131
  const uint8_t *end = parser->current.end + parser->encoding->char_width(parser->current.end, parser->end - parser->current.end);
9151
9132
  PM_PARSER_ERR_FORMAT(parser, parser->current.start, end, diag_id, (int) (end - parser->current.start), (const char *) parser->current.start);
9152
9133
  }
@@ -10173,7 +10154,7 @@ lex_at_variable(pm_parser_t *parser) {
10173
10154
  }
10174
10155
  } else if (parser->current.end < end && pm_char_is_decimal_digit(*parser->current.end)) {
10175
10156
  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) {
10157
+ if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) {
10177
10158
  diag_id = (type == PM_TOKEN_CLASS_VARIABLE) ? PM_ERR_INCOMPLETE_VARIABLE_CLASS_3_3 : PM_ERR_INCOMPLETE_VARIABLE_INSTANCE_3_3;
10178
10159
  }
10179
10160
 
@@ -10849,14 +10830,37 @@ parser_lex(pm_parser_t *parser) {
10849
10830
  following = next_newline(following, parser->end - following);
10850
10831
  }
10851
10832
 
10852
- // If the lex state was ignored, or we hit a '.' or a '&.',
10853
- // we will lex the ignored newline
10833
+ // If the lex state was ignored, we will lex the
10834
+ // ignored newline.
10835
+ if (lex_state_ignored_p(parser)) {
10836
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
10837
+ lexed_comment = false;
10838
+ goto lex_next_token;
10839
+ }
10840
+
10841
+ // If we hit a '.' or a '&.' we will lex the ignored
10842
+ // newline.
10843
+ if (following && (
10844
+ (peek_at(parser, following) == '.') ||
10845
+ (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
10846
+ )) {
10847
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
10848
+ lexed_comment = false;
10849
+ goto lex_next_token;
10850
+ }
10851
+
10852
+
10853
+ // If we are parsing as CRuby 3.5 or later and we
10854
+ // hit a '&&' or a '||' then we will lex the ignored
10855
+ // newline.
10854
10856
  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
- ))
10857
+ (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) &&
10858
+ following && (
10859
+ (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '&') ||
10860
+ (peek_at(parser, following) == '|' && peek_at(parser, following + 1) == '|') ||
10861
+ (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))) ||
10862
+ (peek_at(parser, following) == 'o' && peek_at(parser, following + 1) == 'r' && !char_is_identifier(parser, following + 2, parser->end - (following + 2)))
10863
+ )
10860
10864
  ) {
10861
10865
  if (!lexed_comment) parser_lex_ignored_newline(parser);
10862
10866
  lexed_comment = false;
@@ -10896,6 +10900,63 @@ parser_lex(pm_parser_t *parser) {
10896
10900
  parser->next_start = NULL;
10897
10901
  LEX(PM_TOKEN_AMPERSAND_DOT);
10898
10902
  }
10903
+
10904
+ if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) {
10905
+ // If we hit an && then we are in a logical chain
10906
+ // and we need to return the logical operator.
10907
+ if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '&') {
10908
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
10909
+ lex_state_set(parser, PM_LEX_STATE_BEG);
10910
+ parser->current.start = next_content;
10911
+ parser->current.end = next_content + 2;
10912
+ parser->next_start = NULL;
10913
+ LEX(PM_TOKEN_AMPERSAND_AMPERSAND);
10914
+ }
10915
+
10916
+ // If we hit a || then we are in a logical chain and
10917
+ // we need to return the logical operator.
10918
+ if (peek_at(parser, next_content) == '|' && peek_at(parser, next_content + 1) == '|') {
10919
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
10920
+ lex_state_set(parser, PM_LEX_STATE_BEG);
10921
+ parser->current.start = next_content;
10922
+ parser->current.end = next_content + 2;
10923
+ parser->next_start = NULL;
10924
+ LEX(PM_TOKEN_PIPE_PIPE);
10925
+ }
10926
+
10927
+ // If we hit an 'and' then we are in a logical chain
10928
+ // and we need to return the logical operator.
10929
+ if (
10930
+ peek_at(parser, next_content) == 'a' &&
10931
+ peek_at(parser, next_content + 1) == 'n' &&
10932
+ peek_at(parser, next_content + 2) == 'd' &&
10933
+ !char_is_identifier(parser, next_content + 3, parser->end - (next_content + 3))
10934
+ ) {
10935
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
10936
+ lex_state_set(parser, PM_LEX_STATE_BEG);
10937
+ parser->current.start = next_content;
10938
+ parser->current.end = next_content + 3;
10939
+ parser->next_start = NULL;
10940
+ parser->command_start = true;
10941
+ LEX(PM_TOKEN_KEYWORD_AND);
10942
+ }
10943
+
10944
+ // If we hit a 'or' then we are in a logical chain
10945
+ // and we need to return the logical operator.
10946
+ if (
10947
+ peek_at(parser, next_content) == 'o' &&
10948
+ peek_at(parser, next_content + 1) == 'r' &&
10949
+ !char_is_identifier(parser, next_content + 2, parser->end - (next_content + 2))
10950
+ ) {
10951
+ if (!lexed_comment) parser_lex_ignored_newline(parser);
10952
+ lex_state_set(parser, PM_LEX_STATE_BEG);
10953
+ parser->current.start = next_content;
10954
+ parser->current.end = next_content + 2;
10955
+ parser->next_start = NULL;
10956
+ parser->command_start = true;
10957
+ LEX(PM_TOKEN_KEYWORD_OR);
10958
+ }
10959
+ }
10899
10960
  }
10900
10961
 
10901
10962
  // At this point we know this is a regular newline, and we can set the
@@ -13142,14 +13203,6 @@ match8(const pm_parser_t *parser, pm_token_type_t type1, pm_token_type_t type2,
13142
13203
  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
13204
  }
13144
13205
 
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
13206
  /**
13154
13207
  * If the current token is of the specified type, lex forward by one token and
13155
13208
  * return true. Otherwise, return false. For example:
@@ -14671,7 +14724,7 @@ parse_parameters(
14671
14724
  parser_lex(parser);
14672
14725
 
14673
14726
  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;
14727
+ uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14675
14728
 
14676
14729
  if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14677
14730
  pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT, (uint16_t) (depth + 1));
@@ -14687,7 +14740,7 @@ parse_parameters(
14687
14740
  // If the value of the parameter increased the number of
14688
14741
  // reads of that parameter, then we need to warn that we
14689
14742
  // have a circular definition.
14690
- if ((parser->version == PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14743
+ if ((parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3) && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14691
14744
  PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, name, PM_ERR_PARAMETER_CIRCULAR);
14692
14745
  }
14693
14746
 
@@ -14772,13 +14825,13 @@ parse_parameters(
14772
14825
 
14773
14826
  if (token_begins_expression_p(parser->current.type)) {
14774
14827
  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;
14828
+ uint32_t reads = parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 ? pm_locals_reads(&parser->current_scope->locals, name_id) : 0;
14776
14829
 
14777
14830
  if (accepts_blocks_in_defaults) pm_accepts_block_stack_push(parser, true);
14778
14831
  pm_node_t *value = parse_value_expression(parser, binding_power, false, false, PM_ERR_PARAMETER_NO_DEFAULT_KW, (uint16_t) (depth + 1));
14779
14832
  if (accepts_blocks_in_defaults) pm_accepts_block_stack_pop(parser);
14780
14833
 
14781
- if (parser->version == PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14834
+ if (parser->version <= PM_OPTIONS_VERSION_CRUBY_3_3 && (pm_locals_reads(&parser->current_scope->locals, name_id) != reads)) {
14782
14835
  PM_PARSER_ERR_TOKEN_FORMAT_CONTENT(parser, local, PM_ERR_PARAMETER_CIRCULAR);
14783
14836
  }
14784
14837
 
@@ -16482,7 +16535,7 @@ parse_variable(pm_parser_t *parser) {
16482
16535
  pm_node_list_append(&current_scope->implicit_parameters, node);
16483
16536
 
16484
16537
  return node;
16485
- } else if ((parser->version != PM_OPTIONS_VERSION_CRUBY_3_3) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
16538
+ } else if ((parser->version >= PM_OPTIONS_VERSION_CRUBY_3_4) && pm_token_is_it(parser->previous.start, parser->previous.end)) {
16486
16539
  pm_node_t *node = (pm_node_t *) pm_it_local_variable_read_node_create(parser, &parser->previous);
16487
16540
  pm_node_list_append(&current_scope->implicit_parameters, node);
16488
16541
 
@@ -17412,6 +17465,14 @@ parse_pattern_primitive(pm_parser_t *parser, pm_constant_id_list_t *captures, pm
17412
17465
  // If we found a label, we need to immediately return to the caller.
17413
17466
  if (pm_symbol_node_label_p(node)) return node;
17414
17467
 
17468
+ // Call nodes (arithmetic operations) are not allowed in patterns
17469
+ if (PM_NODE_TYPE(node) == PM_CALL_NODE) {
17470
+ pm_parser_err_node(parser, node, diag_id);
17471
+ pm_missing_node_t *missing_node = pm_missing_node_create(parser, node->location.start, node->location.end);
17472
+ pm_node_destroy(parser, node);
17473
+ return (pm_node_t *) missing_node;
17474
+ }
17475
+
17415
17476
  // Now that we have a primitive, we need to check if it's part of a range.
17416
17477
  if (accept2(parser, PM_TOKEN_DOT_DOT, PM_TOKEN_DOT_DOT_DOT)) {
17417
17478
  pm_token_t operator = parser->previous;
@@ -17694,7 +17755,7 @@ parse_pattern(pm_parser_t *parser, pm_constant_id_list_t *captures, uint8_t flag
17694
17755
  // Gather up all of the patterns into the list.
17695
17756
  while (accept1(parser, PM_TOKEN_COMMA)) {
17696
17757
  // 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)) {
17758
+ 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
17759
  node = (pm_node_t *) pm_implicit_rest_node_create(parser, &parser->previous);
17699
17760
  pm_node_list_append(&nodes, node);
17700
17761
  trailing_rest = true;
@@ -18585,17 +18646,11 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
18585
18646
  call->closing_loc = arguments.closing_loc;
18586
18647
  call->block = arguments.block;
18587
18648
 
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;
18649
+ const uint8_t *end = pm_arguments_end(&arguments);
18650
+ if (!end) {
18651
+ end = call->message_loc.end;
18598
18652
  }
18653
+ call->base.location.end = end;
18599
18654
  }
18600
18655
  } else {
18601
18656
  // Otherwise, we know the identifier is in the local table. This
@@ -19123,7 +19178,13 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
19123
19178
  pm_binding_power_t binding_power = pm_binding_powers[parser->current.type].left;
19124
19179
 
19125
19180
  if (binding_power == PM_BINDING_POWER_UNSET || binding_power >= PM_BINDING_POWER_RANGE) {
19181
+ pm_token_t next = parser->current;
19126
19182
  parse_arguments(parser, &arguments, false, PM_TOKEN_EOF, (uint16_t) (depth + 1));
19183
+
19184
+ // Reject `foo && return bar`.
19185
+ if (!accepts_command_call && arguments.arguments != NULL) {
19186
+ PM_PARSER_ERR_TOKEN_FORMAT(parser, next, PM_ERR_EXPECT_EOL_AFTER_STATEMENT, pm_token_type_human(next.type));
19187
+ }
19127
19188
  }
19128
19189
  }
19129
19190
 
@@ -19520,7 +19581,15 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
19520
19581
  pm_do_loop_stack_push(parser, false);
19521
19582
  statements = (pm_node_t *) pm_statements_node_create(parser);
19522
19583
 
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));
19584
+ bool allow_command_call;
19585
+ if (parser->version >= PM_OPTIONS_VERSION_CRUBY_3_5) {
19586
+ allow_command_call = accepts_command_call;
19587
+ } else {
19588
+ // Allow `def foo = puts "Hello"` but not `private def foo = puts "Hello"`
19589
+ allow_command_call = binding_power == PM_BINDING_POWER_ASSIGNMENT || binding_power < PM_BINDING_POWER_COMPOSITION;
19590
+ }
19591
+
19592
+ 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
19593
 
19525
19594
  if (accept1(parser, PM_TOKEN_KEYWORD_RESCUE_MODIFIER)) {
19526
19595
  context_push(parser, PM_CONTEXT_RESCUE_MODIFIER);
@@ -19607,18 +19676,27 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
19607
19676
  pm_token_t lparen;
19608
19677
  pm_token_t rparen;
19609
19678
  pm_node_t *expression;
19679
+
19610
19680
  context_push(parser, PM_CONTEXT_DEFINED);
19681
+ bool newline = accept1(parser, PM_TOKEN_NEWLINE);
19611
19682
 
19612
19683
  if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
19613
19684
  lparen = parser->previous;
19614
- expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
19615
19685
 
19616
- if (parser->recovering) {
19686
+ if (newline && accept1(parser, PM_TOKEN_PARENTHESIS_RIGHT)) {
19687
+ expression = (pm_node_t *) pm_parentheses_node_create(parser, &lparen, NULL, &parser->previous, 0);
19688
+ lparen = not_provided(parser);
19617
19689
  rparen = not_provided(parser);
19618
19690
  } else {
19619
- accept1(parser, PM_TOKEN_NEWLINE);
19620
- expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19621
- rparen = parser->previous;
19691
+ expression = parse_expression(parser, PM_BINDING_POWER_COMPOSITION, true, false, PM_ERR_DEFINED_EXPRESSION, (uint16_t) (depth + 1));
19692
+
19693
+ if (parser->recovering) {
19694
+ rparen = not_provided(parser);
19695
+ } else {
19696
+ accept1(parser, PM_TOKEN_NEWLINE);
19697
+ expect1(parser, PM_TOKEN_PARENTHESIS_RIGHT, PM_ERR_EXPECT_RPAREN);
19698
+ rparen = parser->previous;
19699
+ }
19622
19700
  }
19623
19701
  } else {
19624
19702
  lparen = not_provided(parser);
@@ -19766,6 +19844,20 @@ parse_expression_prefix(pm_parser_t *parser, pm_binding_power_t binding_power, b
19766
19844
  pm_arguments_t arguments = { 0 };
19767
19845
  pm_node_t *receiver = NULL;
19768
19846
 
19847
+ // If we do not accept a command call, then we also do not accept a
19848
+ // not without parentheses. In this case we need to reject this
19849
+ // syntax.
19850
+ if (!accepts_command_call && !match1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
19851
+ if (match1(parser, PM_TOKEN_PARENTHESIS_LEFT_PARENTHESES)) {
19852
+ pm_parser_err(parser, parser->previous.end, parser->previous.end + 1, PM_ERR_EXPECT_LPAREN_AFTER_NOT_LPAREN);
19853
+ } else {
19854
+ accept1(parser, PM_TOKEN_NEWLINE);
19855
+ pm_parser_err_current(parser, PM_ERR_EXPECT_LPAREN_AFTER_NOT_OTHER);
19856
+ }
19857
+
19858
+ return (pm_node_t *) pm_missing_node_create(parser, parser->current.start, parser->current.end);
19859
+ }
19860
+
19769
19861
  accept1(parser, PM_TOKEN_NEWLINE);
19770
19862
 
19771
19863
  if (accept1(parser, PM_TOKEN_PARENTHESIS_LEFT)) {
@@ -21167,6 +21259,13 @@ parse_expression_infix(pm_parser_t *parser, pm_node_t *node, pm_binding_power_t
21167
21259
  }
21168
21260
  PRISM_FALLTHROUGH
21169
21261
  case PM_CASE_WRITABLE: {
21262
+ // When we have `it = value`, we need to add `it` as a local
21263
+ // variable before parsing the value, in case the value
21264
+ // references the variable.
21265
+ if (PM_NODE_TYPE_P(node, PM_IT_LOCAL_VARIABLE_READ_NODE)) {
21266
+ pm_parser_local_add_location(parser, node->location.start, node->location.end, 0);
21267
+ }
21268
+
21170
21269
  parser_lex(parser);
21171
21270
  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
21271
 
@@ -22160,6 +22259,12 @@ parse_expression(pm_parser_t *parser, pm_binding_power_t binding_power, bool acc
22160
22259
  ) {
22161
22260
  node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right, accepts_command_call, (uint16_t) (depth + 1));
22162
22261
 
22262
+ if (context_terminator(parser->current_context->context, &parser->current)) {
22263
+ // If this token terminates the current context, then we need to
22264
+ // stop parsing the expression, as it has become a statement.
22265
+ return node;
22266
+ }
22267
+
22163
22268
  switch (PM_NODE_TYPE(node)) {
22164
22269
  case PM_MULTI_WRITE_NODE:
22165
22270
  // Multi-write nodes are statements, and cannot be followed by
@@ -22614,6 +22719,12 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm
22614
22719
  }
22615
22720
  }
22616
22721
 
22722
+ // Now that we have established the user-provided options, check if
22723
+ // a version was given and parse as the latest version otherwise.
22724
+ if (parser->version == PM_OPTIONS_VERSION_UNSET) {
22725
+ parser->version = PM_OPTIONS_VERSION_LATEST;
22726
+ }
22727
+
22617
22728
  pm_accepts_block_stack_push(parser, true);
22618
22729
 
22619
22730
  // Skip past the UTF-8 BOM if it exists.
@@ -22667,7 +22778,7 @@ pm_parser_init(pm_parser_t *parser, const uint8_t *source, size_t size, const pm
22667
22778
  }
22668
22779
 
22669
22780
  search_shebang = false;
22670
- } else if (options->main_script && !parser->parsing_eval) {
22781
+ } else if (options != NULL && options->main_script && !parser->parsing_eval) {
22671
22782
  search_shebang = true;
22672
22783
  }
22673
22784
  }
@@ -22807,7 +22918,7 @@ pm_parse(pm_parser_t *parser) {
22807
22918
  * otherwise return true.
22808
22919
  */
22809
22920
  static bool
22810
- pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t *stream_fgets) {
22921
+ 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
22922
  #define LINE_SIZE 4096
22812
22923
  char line[LINE_SIZE];
22813
22924
 
@@ -22843,6 +22954,12 @@ pm_parse_stream_read(pm_buffer_t *buffer, void *stream, pm_parse_stream_fgets_t
22843
22954
  if (strncmp(line, "__END__\r\n", 9) == 0) return false;
22844
22955
  break;
22845
22956
  }
22957
+
22958
+ // All data should be read via gets. If the string returned by gets
22959
+ // _doesn't_ end with a newline, then we assume we hit EOF condition.
22960
+ if (stream_feof(stream)) {
22961
+ break;
22962
+ }
22846
22963
  }
22847
22964
 
22848
22965
  return true;
@@ -22878,16 +22995,17 @@ pm_parse_stream_unterminated_heredoc_p(pm_parser_t *parser) {
22878
22995
  * can stream stdin in to Ruby so we need to support a streaming API.
22879
22996
  */
22880
22997
  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) {
22998
+ 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
22999
  pm_buffer_init(buffer);
22883
23000
 
22884
- bool eof = pm_parse_stream_read(buffer, stream, stream_fgets);
23001
+ bool eof = pm_parse_stream_read(buffer, stream, stream_fgets, stream_feof);
23002
+
22885
23003
  pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options);
22886
23004
  pm_node_t *node = pm_parse(parser);
22887
23005
 
22888
23006
  while (!eof && parser->error_list.size > 0 && (parser->lex_modes.index > 0 || pm_parse_stream_unterminated_heredoc_p(parser))) {
22889
23007
  pm_node_destroy(parser, node);
22890
- eof = pm_parse_stream_read(buffer, stream, stream_fgets);
23008
+ eof = pm_parse_stream_read(buffer, stream, stream_fgets, stream_feof);
22891
23009
 
22892
23010
  pm_parser_free(parser);
22893
23011
  pm_parser_init(parser, (const uint8_t *) pm_buffer_value(buffer), pm_buffer_length(buffer), options);
@@ -22979,13 +23097,13 @@ pm_serialize_parse(pm_buffer_t *buffer, const uint8_t *source, size_t size, cons
22979
23097
  * given stream into to the given buffer.
22980
23098
  */
22981
23099
  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) {
23100
+ 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
23101
  pm_parser_t parser;
22984
23102
  pm_options_t options = { 0 };
22985
23103
  pm_options_read(&options, data);
22986
23104
 
22987
23105
  pm_buffer_t parser_buffer;
22988
- pm_node_t *node = pm_parse_stream(&parser, &parser_buffer, stream, stream_fgets, &options);
23106
+ pm_node_t *node = pm_parse_stream(&parser, &parser_buffer, stream, stream_fgets, stream_feof, &options);
22989
23107
  pm_serialize_header(buffer);
22990
23108
  pm_serialize_content(&parser, node, buffer);
22991
23109
  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 */