natalie_parser 2.1.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/src/parser.cpp CHANGED
@@ -46,7 +46,7 @@ enum class Parser::Precedence {
46
46
  REF, // foo[1] / foo[1] = 2
47
47
  };
48
48
 
49
- bool Parser::higher_precedence(Token &token, SharedPtr<Node> left, Precedence current_precedence) {
49
+ bool Parser::higher_precedence(Token &token, SharedPtr<Node> left, Precedence current_precedence, IterAllow iter_allow) {
50
50
  auto next_precedence = get_precedence(token, left);
51
51
 
52
52
  // printf("token %d, left %d, current_precedence %d, next_precedence %d\n", (int)token.type(), (int)left->type(), (int)current_precedence, (int)next_precedence);
@@ -83,11 +83,11 @@ bool Parser::higher_precedence(Token &token, SharedPtr<Node> left, Precedence cu
83
83
  // NOTE: `m_call_depth` should probably be called
84
84
  // `m_call_that_can_accept_a_block_depth`, but that's a bit long.
85
85
  //
86
- return m_call_depth.last() == 0;
86
+ return iter_allow == IterAllow::CURLY_AND_BLOCK && m_call_depth.last() == 0;
87
87
  }
88
88
 
89
89
  if (next_precedence == Precedence::ITER_CURLY)
90
- return left->is_callable();
90
+ return iter_allow >= IterAllow::CURLY_ONLY && left->is_callable();
91
91
 
92
92
  return next_precedence > current_precedence;
93
93
  }
@@ -198,7 +198,7 @@ Parser::Precedence Parser::get_precedence(Token &token, SharedPtr<Node> left) {
198
198
  return Precedence::LOWEST;
199
199
  }
200
200
 
201
- SharedPtr<Node> Parser::parse_expression(Parser::Precedence precedence, LocalsHashmap &locals) {
201
+ SharedPtr<Node> Parser::parse_expression(Parser::Precedence precedence, LocalsHashmap &locals, IterAllow iter_allow) {
202
202
  skip_newlines();
203
203
 
204
204
  m_precedence_stack.push(precedence);
@@ -211,7 +211,7 @@ SharedPtr<Node> Parser::parse_expression(Parser::Precedence precedence, LocalsHa
211
211
 
212
212
  while (current_token().is_valid()) {
213
213
  auto token = current_token();
214
- if (!higher_precedence(token, left, precedence))
214
+ if (!higher_precedence(token, left, precedence, iter_allow))
215
215
  break;
216
216
  auto left_fn = left_denotation(token, left, precedence);
217
217
  if (!left_fn)
@@ -278,44 +278,47 @@ SharedPtr<BlockNode> Parser::parse_def_body(LocalsHashmap &locals) {
278
278
  return parse_body(locals, Precedence::LOWEST, Token::Type::EndKeyword, true);
279
279
  }
280
280
 
281
+ void Parser::reinsert_collapsed_newline() {
282
+ auto token = previous_token();
283
+ if (token.can_precede_collapsible_newline()) {
284
+ // Some operators at the end of a line cause the newlines to be collapsed:
285
+ //
286
+ // foo <<
287
+ // bar
288
+ //
289
+ // ...but in this case (an alias), collapsing the newline was a mistake:
290
+ //
291
+ // alias foo <<
292
+ // def bar; end
293
+ //
294
+ // So, we'll put the newline back.
295
+ m_tokens->insert(m_index, Token { Token::Type::Newline, token.file(), token.line(), token.column(), token.whitespace_precedes() });
296
+ }
297
+ }
298
+
281
299
  SharedPtr<Node> Parser::parse_alias(LocalsHashmap &locals) {
282
300
  auto token = current_token();
283
301
  advance();
284
- SharedPtr<SymbolNode> new_name = parse_alias_arg(locals, "alias new name (first argument)", false);
285
- auto existing_name = parse_alias_arg(locals, "alias existing name (second argument)", true);
302
+ auto new_name = parse_alias_arg(locals, "alias new name (first argument)");
303
+ auto existing_name = parse_alias_arg(locals, "alias existing name (second argument)");
304
+ reinsert_collapsed_newline();
286
305
  return new AliasNode { token, new_name, existing_name };
287
306
  }
288
307
 
289
- SharedPtr<SymbolNode> Parser::parse_alias_arg(LocalsHashmap &locals, const char *expected_message, bool reinsert_collapsed_newline) {
308
+ SharedPtr<SymbolNode> Parser::parse_alias_arg(LocalsHashmap &locals, const char *expected_message) {
290
309
  auto token = current_token();
291
310
  switch (token.type()) {
292
- // TODO: handle Constant too
293
- case Token::Type::BareName: {
294
- advance();
295
- return new SymbolNode { token, token.literal_string() };
296
- }
311
+ case Token::Type::BareName:
312
+ case Token::Type::Constant:
313
+ case Token::Type::OperatorName:
314
+ return new SymbolNode { token, parse_method_name(locals) };
297
315
  case Token::Type::Symbol:
298
316
  return parse_symbol(locals).static_cast_as<SymbolNode>();
299
317
  case Token::Type::InterpolatedSymbolBegin:
300
318
  return parse_interpolated_symbol(locals).static_cast_as<SymbolNode>();
301
319
  default:
302
320
  if (token.is_operator() || token.is_keyword()) {
303
- advance();
304
- if (token.can_precede_collapsible_newline() && reinsert_collapsed_newline) {
305
- // Some operators at the end of a line cause the newlines to be collapsed:
306
- //
307
- // foo <<
308
- // bar
309
- //
310
- // ...but in this case (an alias), collapsing the newline was a mistake:
311
- //
312
- // alias foo <<
313
- // def bar; end
314
- //
315
- // So, we'll put the newline back.
316
- m_tokens->insert(m_index, Token { Token::Type::Newline, token.file(), token.line(), token.column(), token.whitespace_precedes() });
317
- }
318
- return new SymbolNode { token, new String(token.type_value()) };
321
+ return new SymbolNode { token, parse_method_name(locals) };
319
322
  } else {
320
323
  throw_unexpected(expected_message);
321
324
  }
@@ -431,16 +434,8 @@ void Parser::parse_rest_of_begin(BeginNode &begin_node, LocalsHashmap &locals) {
431
434
  }
432
435
  if (current_token().is_hash_rocket()) {
433
436
  advance();
434
- SharedPtr<IdentifierNode> name;
435
- switch (current_token().type()) {
436
- case Token::Type::BareName:
437
- name = new IdentifierNode { current_token(), current_token().literal_string() };
438
- advance();
439
- break;
440
- default:
441
- throw_unexpected("exception name");
442
- }
443
- name->add_to_locals(locals);
437
+ auto name = parse_expression(Precedence::ASSIGNMENT_LHS, locals);
438
+ add_assignment_locals(name, locals);
444
439
  rescue_node->set_exception_name(name);
445
440
  }
446
441
  next_expression();
@@ -501,7 +496,7 @@ SharedPtr<Node> Parser::parse_beginless_range(LocalsHashmap &locals) {
501
496
  SharedPtr<Node> Parser::parse_block_pass(LocalsHashmap &locals) {
502
497
  auto token = current_token();
503
498
  advance();
504
- auto value = parse_expression(Precedence::UNARY_PLUS, locals);
499
+ auto value = parse_expression(Precedence::LOWEST, locals);
505
500
  return new BlockPassNode { token, value };
506
501
  }
507
502
 
@@ -865,15 +860,19 @@ SharedPtr<BlockNode> Parser::parse_case_when_body(LocalsHashmap &locals) {
865
860
  }
866
861
 
867
862
  SharedPtr<Node> Parser::parse_class_or_module_name(LocalsHashmap &locals) {
868
- Token name_token;
869
- if (current_token().type() == Token::Type::ConstantResolution) {
870
- name_token = peek_token();
871
- } else {
872
- name_token = current_token();
873
- }
874
- if (name_token.type() != Token::Type::Constant)
863
+ auto name_token = current_token();
864
+ auto exp = parse_expression(Precedence::LESS_GREATER, locals);
865
+ switch (exp->type()) {
866
+ case Node::Type::Colon2:
867
+ case Node::Type::Colon3:
868
+ return exp;
869
+ case Node::Type::Identifier:
870
+ if (name_token.type() == Token::Type::Constant)
871
+ return exp;
872
+ [[fallthrough]];
873
+ default:
875
874
  throw SyntaxError { "class/module name must be CONSTANT" };
876
- return parse_expression(Precedence::LESS_GREATER, locals);
875
+ }
877
876
  }
878
877
 
879
878
  SharedPtr<Node> Parser::parse_class(LocalsHashmap &locals) {
@@ -903,7 +902,7 @@ SharedPtr<Node> Parser::parse_multiple_assignment_expression(SharedPtr<Node> lef
903
902
  list->add_node(left);
904
903
  while (current_token().is_comma()) {
905
904
  advance();
906
- if (current_token().is_rparen() || current_token().is_equal()) {
905
+ if (current_token().is_rparen() || current_token().is_equal() || current_token().type() == Token::Type::InKeyword) {
907
906
  // trailing comma with no additional identifier
908
907
  break;
909
908
  }
@@ -991,21 +990,24 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
991
990
  auto token = current_token();
992
991
  switch (token.type()) {
993
992
  case Token::Type::BareName:
994
- if (peek_token().type() == Token::Type::Dot) {
993
+ if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
995
994
  self_node = parse_identifier(locals);
996
995
  advance(); // dot
997
996
  }
998
997
  name = parse_method_name(locals);
999
998
  break;
1000
999
  case Token::Type::Constant:
1001
- if (peek_token().type() == Token::Type::Dot) {
1000
+ if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
1002
1001
  self_node = parse_constant(locals);
1003
1002
  advance(); // dot
1004
1003
  }
1005
1004
  name = parse_method_name(locals);
1006
1005
  break;
1006
+ case Token::Type::OperatorName:
1007
+ name = parse_method_name(locals);
1008
+ break;
1007
1009
  case Token::Type::SelfKeyword:
1008
- if (peek_token().type() == Token::Type::Dot) {
1010
+ if (peek_token().is_dot() || peek_token().is_constant_resolution()) {
1009
1011
  self_node = new SelfNode { current_token() };
1010
1012
  advance(); // self
1011
1013
  advance(); // dot
@@ -1023,10 +1025,6 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
1023
1025
  }
1024
1026
  }
1025
1027
  }
1026
- if (current_token().is_equal() && !current_token().whitespace_precedes()) {
1027
- advance();
1028
- name->append_char('=');
1029
- }
1030
1028
  auto args = Vector<SharedPtr<Node>> {};
1031
1029
  if (current_token().is_lparen()) {
1032
1030
  advance();
@@ -1037,7 +1035,7 @@ SharedPtr<Node> Parser::parse_def(LocalsHashmap &locals) {
1037
1035
  expect(Token::Type::RParen, "args closing paren");
1038
1036
  advance();
1039
1037
  }
1040
- } else if (current_token().is_bare_name() || current_token().is_splat() || current_token().is_symbol_key()) {
1038
+ } else if (current_token().can_be_first_arg_of_def()) {
1041
1039
  parse_def_args(args, our_locals);
1042
1040
  }
1043
1041
  SharedPtr<BlockNode> body;
@@ -1085,7 +1083,21 @@ void Parser::parse_def_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals
1085
1083
  }
1086
1084
  }
1087
1085
 
1088
- void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, ArgsContext context) {
1086
+ SharedPtr<Node> Parser::parse_arg_default_value(LocalsHashmap &locals, IterAllow iter_allow) {
1087
+ auto token = current_token();
1088
+ if (token.is_bare_name() && peek_token().is_equal()) {
1089
+ SharedPtr<ArgNode> arg = new ArgNode { token, token.literal_string() };
1090
+ advance();
1091
+ advance(); // =
1092
+ arg->add_to_locals(locals);
1093
+ arg->set_value(parse_arg_default_value(locals, iter_allow));
1094
+ return arg.static_cast_as<Node>();
1095
+ } else {
1096
+ return parse_expression(Precedence::DEF_ARG, locals, iter_allow);
1097
+ }
1098
+ }
1099
+
1100
+ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, ArgsContext context, IterAllow iter_allow) {
1089
1101
  auto args_have_any_splat = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::Arg && args.last().static_cast_as<ArgNode>()->splat_or_kwsplat(); };
1090
1102
  auto args_have_keyword = [&]() { return !args.is_empty() && args.last()->type() == Node::Type::KeywordArg; };
1091
1103
 
@@ -1105,7 +1117,7 @@ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &
1105
1117
  if (args_have_any_splat())
1106
1118
  throw_error(token, "default value after splat");
1107
1119
  advance(); // =
1108
- arg->set_value(parse_expression(Precedence::DEF_ARG, locals));
1120
+ arg->set_value(parse_arg_default_value(locals, iter_allow));
1109
1121
  }
1110
1122
  args.push(arg.static_cast_as<Node>());
1111
1123
  return;
@@ -1176,8 +1188,12 @@ void Parser::parse_def_single_arg(Vector<SharedPtr<Node>> &args, LocalsHashmap &
1176
1188
  case Token::Type::RParen:
1177
1189
  case Token::Type::Semicolon:
1178
1190
  break;
1191
+ case Token::Type::LCurlyBrace:
1192
+ if (iter_allow < IterAllow::CURLY_ONLY)
1193
+ break;
1194
+ [[fallthrough]];
1179
1195
  default:
1180
- arg->set_value(parse_expression(Precedence::DEF_ARG, locals));
1196
+ arg->set_value(parse_expression(Precedence::DEF_ARG, locals, iter_allow));
1181
1197
  }
1182
1198
  arg->add_to_locals(locals);
1183
1199
  args.push(arg.static_cast_as<Node>());
@@ -1263,16 +1279,28 @@ SharedPtr<Node> Parser::parse_file_constant(LocalsHashmap &) {
1263
1279
  return new StringNode { token, token.file() };
1264
1280
  }
1265
1281
 
1282
+ SharedPtr<Node> Parser::parse_line_constant(LocalsHashmap &) {
1283
+ auto token = current_token();
1284
+ advance();
1285
+ return new FixnumNode { token, static_cast<long long>(token.line() + 1) };
1286
+ }
1287
+
1266
1288
  SharedPtr<Node> Parser::parse_for(LocalsHashmap &locals) {
1267
1289
  auto token = current_token();
1268
1290
  advance();
1269
- auto vars = parse_assignment_identifier(false, locals);
1270
- if (current_token().type() == Token::Type::Comma) {
1291
+ auto vars = parse_assignment_identifier(true, locals);
1292
+ if (current_token().is_comma() || vars->type() == Node::Type::Splat) {
1271
1293
  vars = parse_multiple_assignment_expression(vars, locals);
1272
1294
  }
1295
+ add_assignment_locals(vars, locals);
1273
1296
  expect(Token::Type::InKeyword, "for in");
1274
1297
  advance();
1275
- auto expr = parse_expression(Precedence::LOWEST, locals);
1298
+ m_call_depth.last()++;
1299
+ auto expr = parse_expression(Precedence::BARE_CALL_ARG, locals, IterAllow::CURLY_ONLY);
1300
+ m_call_depth.last()--;
1301
+ if (current_token().type() == Token::Type::DoKeyword) {
1302
+ advance();
1303
+ }
1276
1304
  auto body = parse_body(locals, Precedence::LOWEST);
1277
1305
  expect(Token::Type::EndKeyword, "for end");
1278
1306
  advance();
@@ -1690,13 +1718,15 @@ SharedPtr<String> Parser::parse_method_name(LocalsHashmap &) {
1690
1718
  switch (token.type()) {
1691
1719
  case Token::Type::BareName:
1692
1720
  case Token::Type::Constant:
1721
+ case Token::Type::OperatorName:
1693
1722
  name = current_token().literal_string();
1694
1723
  break;
1695
1724
  default:
1696
- if (token.is_operator() || token.is_keyword())
1725
+ if (token.is_operator() || token.is_keyword()) {
1697
1726
  name = new String(current_token().type_value());
1698
- else
1727
+ } else {
1699
1728
  throw_unexpected("method name");
1729
+ }
1700
1730
  }
1701
1731
  advance();
1702
1732
  return name;
@@ -1766,15 +1796,15 @@ SharedPtr<Node> Parser::parse_nth_ref(LocalsHashmap &) {
1766
1796
  return new NthRefNode { token, token.get_fixnum() };
1767
1797
  }
1768
1798
 
1769
- void Parser::parse_proc_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals) {
1799
+ void Parser::parse_proc_args(Vector<SharedPtr<Node>> &args, LocalsHashmap &locals, IterAllow iter_allow) {
1770
1800
  if (current_token().is_semicolon()) {
1771
1801
  parse_shadow_variables_in_args(args, locals);
1772
1802
  return;
1773
1803
  }
1774
- parse_def_single_arg(args, locals, ArgsContext::Proc);
1804
+ parse_def_single_arg(args, locals, ArgsContext::Proc, iter_allow);
1775
1805
  while (current_token().is_comma()) {
1776
1806
  advance();
1777
- parse_def_single_arg(args, locals, ArgsContext::Proc);
1807
+ parse_def_single_arg(args, locals, ArgsContext::Proc, iter_allow);
1778
1808
  }
1779
1809
  if (current_token().is_semicolon()) {
1780
1810
  parse_shadow_variables_in_args(args, locals);
@@ -1887,13 +1917,13 @@ SharedPtr<Node> Parser::parse_stabby_proc(LocalsHashmap &locals) {
1887
1917
  if (current_token().is_rparen()) {
1888
1918
  advance(); // )
1889
1919
  } else {
1890
- parse_proc_args(args, locals);
1920
+ parse_proc_args(args, locals, IterAllow::CURLY_AND_BLOCK);
1891
1921
  expect(Token::Type::RParen, "proc args closing paren");
1892
1922
  advance(); // )
1893
1923
  }
1894
- } else if (current_token().is_bare_name() || current_token().type() == Token::Type::Star) {
1924
+ } else if (current_token().can_be_first_arg_of_def()) {
1895
1925
  has_args = true;
1896
- parse_proc_args(args, locals);
1926
+ parse_proc_args(args, locals, IterAllow::NONE);
1897
1927
  }
1898
1928
  if (current_token().type() != Token::Type::DoKeyword && current_token().type() != Token::Type::LCurlyBrace)
1899
1929
  throw_unexpected("block");
@@ -2065,36 +2095,23 @@ SharedPtr<Node> Parser::parse_unary_operator(LocalsHashmap &locals) {
2065
2095
  SharedPtr<Node> Parser::parse_undef(LocalsHashmap &locals) {
2066
2096
  auto undef_token = current_token();
2067
2097
  advance();
2068
- auto symbol_from_token = [&](Token &token) -> SharedPtr<Node> {
2069
- switch (token.type()) {
2070
- case Token::Type::BareName:
2071
- case Token::Type::Constant:
2072
- advance();
2073
- return new SymbolNode { token, token.literal_string() };
2074
- case Token::Type::Symbol:
2075
- return parse_symbol(locals);
2076
- case Token::Type::InterpolatedSymbolBegin: {
2077
- return parse_interpolated_symbol(locals);
2078
- }
2079
- default:
2080
- throw_unexpected("method name for undef");
2081
- }
2082
- };
2083
2098
  SharedPtr<UndefNode> undef_node = new UndefNode { undef_token };
2084
- auto token = current_token();
2085
- undef_node->add_arg(symbol_from_token(token));
2099
+ auto arg = parse_alias_arg(locals, "method name for undef");
2100
+ undef_node->add_arg(arg.static_cast_as<Node>());
2086
2101
  if (current_token().is_comma()) {
2087
2102
  SharedPtr<BlockNode> block = new BlockNode { undef_token };
2088
2103
  block->add_node(undef_node.static_cast_as<Node>());
2089
2104
  while (current_token().is_comma()) {
2090
2105
  advance();
2091
- token = current_token();
2092
2106
  SharedPtr<UndefNode> undef_node = new UndefNode { undef_token };
2093
- undef_node->add_arg(symbol_from_token(token));
2107
+ auto arg = parse_alias_arg(locals, "method name for undef");
2108
+ undef_node->add_arg(arg.static_cast_as<Node>());
2094
2109
  block->add_node(undef_node.static_cast_as<Node>());
2095
2110
  }
2111
+ reinsert_collapsed_newline();
2096
2112
  return block.static_cast_as<Node>();
2097
2113
  }
2114
+ reinsert_collapsed_newline();
2098
2115
  return undef_node.static_cast_as<Node>();
2099
2116
  };
2100
2117
 
@@ -2155,32 +2172,34 @@ SharedPtr<Node> Parser::parse_assignment_expression(SharedPtr<Node> left, Locals
2155
2172
  if (left->type() == Node::Type::Splat) {
2156
2173
  return parse_multiple_assignment_expression(left, locals);
2157
2174
  }
2175
+ add_assignment_locals(left, locals);
2176
+ advance();
2177
+ bool to_array = left->type() == Node::Type::MultipleAssignment;
2178
+ auto value = parse_assignment_expression_value(to_array, locals, allow_multiple);
2179
+ return new AssignmentNode { token, left, value };
2180
+ }
2181
+
2182
+ void Parser::add_assignment_locals(SharedPtr<Node> left, LocalsHashmap &locals) {
2158
2183
  switch (left->type()) {
2159
2184
  case Node::Type::Identifier: {
2160
2185
  auto left_identifier = left.static_cast_as<IdentifierNode>();
2161
2186
  left_identifier->add_to_locals(locals);
2162
- advance();
2163
- auto value = parse_assignment_expression_value(false, locals, allow_multiple);
2164
- return new AssignmentNode { token, left, value };
2187
+ break;
2165
2188
  }
2166
2189
  case Node::Type::Call:
2167
2190
  case Node::Type::Colon2:
2168
2191
  case Node::Type::Colon3:
2169
2192
  case Node::Type::SafeCall: {
2170
- advance();
2171
- auto value = parse_assignment_expression_value(false, locals, allow_multiple);
2172
- return new AssignmentNode { token, left, value };
2193
+ break;
2173
2194
  }
2174
2195
  case Node::Type::MultipleAssignment: {
2175
2196
  left.static_cast_as<MultipleAssignmentNode>()->add_locals(locals);
2176
- advance();
2177
- auto value = parse_assignment_expression_value(true, locals, allow_multiple);
2178
- return new AssignmentNode { token, left, value };
2197
+ break;
2179
2198
  }
2180
2199
  default:
2181
- throw_unexpected(left->token(), "left side of assignment");
2200
+ throw_unexpected(left->token(), "assignment identifier");
2182
2201
  }
2183
- };
2202
+ }
2184
2203
 
2185
2204
  SharedPtr<Node> Parser::parse_assignment_expression_value(bool to_array, LocalsHashmap &locals, bool allow_multiple) {
2186
2205
  auto token = current_token();
@@ -2416,6 +2435,7 @@ SharedPtr<Node> Parser::parse_constant_resolution_expression(SharedPtr<Node> lef
2416
2435
  SharedPtr<Node> node;
2417
2436
  switch (name_token.type()) {
2418
2437
  case Token::Type::BareName:
2438
+ case Token::Type::OperatorName:
2419
2439
  advance();
2420
2440
  node = new CallNode { name_token, left, name_token.literal_string() };
2421
2441
  break;
@@ -2434,7 +2454,12 @@ SharedPtr<Node> Parser::parse_constant_resolution_expression(SharedPtr<Node> lef
2434
2454
  break;
2435
2455
  }
2436
2456
  default:
2437
- throw_unexpected(name_token, ":: identifier name");
2457
+ if (name_token.is_operator() || name_token.is_keyword()) {
2458
+ advance();
2459
+ node = new CallNode { name_token, left, new String(name_token.type_value()) };
2460
+ } else {
2461
+ throw_unexpected(name_token, ":: identifier name");
2462
+ }
2438
2463
  }
2439
2464
  return node;
2440
2465
  }
@@ -2677,6 +2702,7 @@ SharedPtr<Node> Parser::parse_safe_send_expression(SharedPtr<Node> left, LocalsH
2677
2702
  break;
2678
2703
  case Token::Type::BareName:
2679
2704
  case Token::Type::Constant:
2705
+ case Token::Type::OperatorName:
2680
2706
  name = name_token.literal_string();
2681
2707
  advance();
2682
2708
  break;
@@ -2703,6 +2729,7 @@ SharedPtr<Node> Parser::parse_send_expression(SharedPtr<Node> left, LocalsHashma
2703
2729
  switch (name_token.type()) {
2704
2730
  case Token::Type::BareName:
2705
2731
  case Token::Type::Constant:
2732
+ case Token::Type::OperatorName:
2706
2733
  name = name_token.literal_string();
2707
2734
  advance();
2708
2735
  break;
@@ -2745,7 +2772,11 @@ SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
2745
2772
  if (condition->type() == Node::Type::Regexp) {
2746
2773
  condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
2747
2774
  }
2748
- next_expression();
2775
+ if (current_token().type() == Token::Type::ThenKeyword) {
2776
+ advance(); // then
2777
+ } else {
2778
+ next_expression();
2779
+ }
2749
2780
  SharedPtr<Node> false_expr = parse_if_body(locals);
2750
2781
  SharedPtr<Node> true_expr;
2751
2782
  if (current_token().is_else_keyword()) {
@@ -2762,11 +2793,15 @@ SharedPtr<Node> Parser::parse_unless(LocalsHashmap &locals) {
2762
2793
  SharedPtr<Node> Parser::parse_while(LocalsHashmap &locals) {
2763
2794
  auto token = current_token();
2764
2795
  advance();
2765
- SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals);
2796
+ SharedPtr<Node> condition = parse_expression(Precedence::LOWEST, locals, IterAllow::CURLY_ONLY);
2766
2797
  if (condition->type() == Node::Type::Regexp) {
2767
2798
  condition = new MatchNode { condition->token(), condition.static_cast_as<RegexpNode>() };
2768
2799
  }
2769
- next_expression();
2800
+ if (current_token().type() == Token::Type::DoKeyword) {
2801
+ advance();
2802
+ } else {
2803
+ next_expression();
2804
+ }
2770
2805
  SharedPtr<BlockNode> body = parse_body(locals, Precedence::LOWEST);
2771
2806
  expect(Token::Type::EndKeyword, "while end");
2772
2807
  advance();
@@ -2824,6 +2859,8 @@ Parser::parse_null_fn Parser::null_denotation(Token::Type type) {
2824
2859
  return &Parser::parse_group;
2825
2860
  case Type::LCurlyBrace:
2826
2861
  return &Parser::parse_hash;
2862
+ case Type::LINEKeyword:
2863
+ return &Parser::parse_line_constant;
2827
2864
  case Type::BareName:
2828
2865
  case Type::ClassVariable:
2829
2866
  case Type::Constant:
@@ -2907,7 +2944,7 @@ Parser::parse_left_fn Parser::left_denotation(Token &token, SharedPtr<Node> left
2907
2944
  using Type = Token::Type;
2908
2945
  switch (token.type()) {
2909
2946
  case Type::Equal:
2910
- if (precedence == Precedence::ARRAY || precedence == Precedence::BARE_CALL_ARG || precedence == Precedence::CALL_ARG)
2947
+ if (precedence == Precedence::ARRAY || precedence == Precedence::HASH || precedence == Precedence::BARE_CALL_ARG || precedence == Precedence::CALL_ARG)
2911
2948
  return &Parser::parse_assignment_expression_without_multiple_values;
2912
2949
  else
2913
2950
  return &Parser::parse_assignment_expression;
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: natalie_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Morgan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-12 00:00:00.000000000 Z
11
+ date: 2022-12-31 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: NatalieParser is a zero-dependency, from-scratch, hand-written recursive
14
14
  descent parser for the Ruby Programming Language.
@@ -151,6 +151,7 @@ files:
151
151
  - src/node/begin_node.cpp
152
152
  - src/node/begin_rescue_node.cpp
153
153
  - src/node/class_node.cpp
154
+ - src/node/for_node.cpp
154
155
  - src/node/interpolated_regexp_node.cpp
155
156
  - src/node/interpolated_shell_node.cpp
156
157
  - src/node/interpolated_string_node.cpp