natalie_parser 2.0.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/src/lexer.cpp CHANGED
@@ -80,7 +80,47 @@ Token Lexer::next_token() {
80
80
  m_whitespace_precedes = skip_whitespace();
81
81
  m_token_line = m_cursor_line;
82
82
  m_token_column = m_cursor_column;
83
- return build_next_token();
83
+ Token token = build_next_token();
84
+ switch (token.type()) {
85
+ case Token::Type::AliasKeyword:
86
+ m_remaining_method_names = 2;
87
+ break;
88
+ case Token::Type::ConstantResolution:
89
+ case Token::Type::DefKeyword:
90
+ m_remaining_method_names = 1;
91
+ m_allow_assignment_method = true;
92
+ break;
93
+ case Token::Type::Dot:
94
+ m_remaining_method_names = 1;
95
+ break;
96
+ case Token::Type::UndefKeyword:
97
+ m_remaining_method_names = std::numeric_limits<size_t>::max();
98
+ m_method_name_separator = Token::Type::Comma;
99
+ break;
100
+ default:
101
+ if (m_method_name_separator != Token::Type::Invalid) {
102
+ if (m_last_method_name) {
103
+ m_last_method_name = {};
104
+ if (token.type() != m_method_name_separator) {
105
+ m_remaining_method_names = 0;
106
+ m_method_name_separator = Token::Type::Invalid;
107
+ }
108
+ } else {
109
+ m_last_method_name = token;
110
+ }
111
+ } else if (m_remaining_method_names > 0) {
112
+ m_remaining_method_names--;
113
+ } else {
114
+ m_allow_assignment_method = false;
115
+ }
116
+ break;
117
+ }
118
+ return token;
119
+ }
120
+
121
+ bool is_name_start_char(char c) {
122
+ if (!c) return false;
123
+ return (c >= 'a' && c <= 'z') || c == '_' || (unsigned int)c >= 128;
84
124
  }
85
125
 
86
126
  bool is_identifier_char(char c) {
@@ -204,10 +244,10 @@ Token Lexer::build_next_token() {
204
244
  advance();
205
245
  return Token { Token::Type::PlusEqual, m_file, m_token_line, m_token_column, m_whitespace_precedes };
206
246
  case '@':
207
- if (m_last_token.is_def_keyword() || m_last_token.is_dot()) {
247
+ if (m_remaining_method_names > 0) {
208
248
  advance();
209
249
  SharedPtr<String> lit = new String("+@");
210
- return Token { Token::Type::BareName, lit, m_file, m_token_line, m_token_column, m_whitespace_precedes };
250
+ return Token { Token::Type::OperatorName, lit, m_file, m_token_line, m_token_column, m_whitespace_precedes };
211
251
  } else {
212
252
  return Token { Token::Type::Plus, m_file, m_token_line, m_token_column, m_whitespace_precedes };
213
253
  }
@@ -224,10 +264,10 @@ Token Lexer::build_next_token() {
224
264
  advance();
225
265
  return Token { Token::Type::MinusEqual, m_file, m_token_line, m_token_column, m_whitespace_precedes };
226
266
  case '@':
227
- if (m_last_token.is_def_keyword() || m_last_token.is_dot()) {
267
+ if (m_remaining_method_names > 0) {
228
268
  advance();
229
269
  SharedPtr<String> lit = new String("-@");
230
- return Token { Token::Type::BareName, lit, m_file, m_token_line, m_token_column, m_whitespace_precedes };
270
+ return Token { Token::Type::OperatorName, lit, m_file, m_token_line, m_token_column, m_whitespace_precedes };
231
271
  } else {
232
272
  return Token { Token::Type::Minus, m_file, m_token_line, m_token_column, m_whitespace_precedes };
233
273
  }
@@ -256,21 +296,28 @@ Token Lexer::build_next_token() {
256
296
  advance();
257
297
  if (!m_last_token)
258
298
  return consume_regexp('/', '/');
299
+ if (m_remaining_method_names > 0)
300
+ return Token { Token::Type::Slash, m_file, m_token_line, m_token_column, m_whitespace_precedes };
259
301
  switch (m_last_token.type()) {
260
302
  case Token::Type::Comma:
261
303
  case Token::Type::Doc:
304
+ case Token::Type::Equal:
262
305
  case Token::Type::LBracket:
263
306
  case Token::Type::LCurlyBrace:
264
307
  case Token::Type::LParen:
265
308
  case Token::Type::Match:
266
309
  case Token::Type::Newline:
310
+ case Token::Type::Not:
311
+ case Token::Type::Pipe:
267
312
  return consume_regexp('/', '/');
268
- case Token::Type::DefKeyword:
269
- return Token { Token::Type::Slash, m_file, m_token_line, m_token_column, m_whitespace_precedes };
270
313
  default: {
271
314
  switch (current_char()) {
272
315
  case ' ':
273
- return Token { Token::Type::Slash, m_file, m_token_line, m_token_column, m_whitespace_precedes };
316
+ if (m_last_token.is_keyword() && m_last_token.can_precede_regexp_literal()) {
317
+ return consume_regexp('/', '/');
318
+ } else {
319
+ return Token { Token::Type::Slash, m_file, m_token_line, m_token_column, m_whitespace_precedes };
320
+ }
274
321
  case '=':
275
322
  advance();
276
323
  return Token { Token::Type::SlashEqual, m_file, m_token_line, m_token_column, m_whitespace_precedes };
@@ -291,216 +338,26 @@ Token Lexer::build_next_token() {
291
338
  advance();
292
339
  return Token { Token::Type::PercentEqual, m_file, m_token_line, m_token_column, m_whitespace_precedes };
293
340
  case 'q':
294
- switch (peek()) {
295
- case '[':
296
- advance(2);
297
- return consume_single_quoted_string('[', ']');
298
- case '{':
299
- advance(2);
300
- return consume_single_quoted_string('{', '}');
301
- case '<':
302
- advance(2);
303
- return consume_single_quoted_string('<', '>');
304
- case '(':
305
- advance(2);
306
- return consume_single_quoted_string('(', ')');
307
- default: {
308
- char c = peek();
309
- if (char_can_be_string_or_regexp_delimiter(c)) {
310
- advance(2);
311
- return consume_single_quoted_string(c, c);
312
- } else {
313
- return Token { Token::Type::Percent, m_file, m_token_line, m_token_column, m_whitespace_precedes };
314
- }
315
- }
316
- }
341
+ return consume_percent_string(&Lexer::consume_single_quoted_string);
317
342
  case 'Q':
318
- switch (peek()) {
319
- case '[':
320
- advance(2);
321
- return consume_double_quoted_string('[', ']');
322
- case '{':
323
- advance(2);
324
- return consume_double_quoted_string('{', '}');
325
- case '<':
326
- advance(2);
327
- return consume_double_quoted_string('<', '>');
328
- case '(':
329
- advance(2);
330
- return consume_double_quoted_string('(', ')');
331
- default: {
332
- char c = peek();
333
- if (char_can_be_string_or_regexp_delimiter(c)) {
334
- advance(2);
335
- return consume_double_quoted_string(c, c);
336
- } else {
337
- return Token { Token::Type::Percent, m_file, m_token_line, m_token_column, m_whitespace_precedes };
338
- }
339
- }
340
- }
343
+ return consume_percent_string(&Lexer::consume_interpolated_string);
341
344
  case 'r':
342
- switch (peek()) {
343
- case '[':
344
- advance(2);
345
- return consume_regexp('[', ']');
346
- case '{':
347
- advance(2);
348
- return consume_regexp('{', '}');
349
- case '(':
350
- advance(2);
351
- return consume_regexp('(', ')');
352
- case '<':
353
- advance(2);
354
- return consume_regexp('<', '>');
355
- default: {
356
- char c = peek();
357
- if (char_can_be_string_or_regexp_delimiter(c)) {
358
- advance(2);
359
- return consume_regexp(c, c);
360
- } else {
361
- return Token { Token::Type::Percent, m_file, m_token_line, m_token_column, m_whitespace_precedes };
362
- }
363
- }
364
- }
345
+ return consume_percent_string(&Lexer::consume_regexp);
346
+ case 's':
347
+ return consume_percent_string(&Lexer::consume_percent_symbol);
365
348
  case 'x':
366
- switch (peek()) {
367
- case '/': {
368
- advance(2);
369
- return consume_double_quoted_string('/', '/', Token::Type::InterpolatedShellBegin, Token::Type::InterpolatedShellEnd);
370
- }
371
- case '[': {
372
- advance(2);
373
- return consume_double_quoted_string('[', ']', Token::Type::InterpolatedShellBegin, Token::Type::InterpolatedShellEnd);
374
- }
375
- case '{': {
376
- advance(2);
377
- return consume_double_quoted_string('{', '}', Token::Type::InterpolatedShellBegin, Token::Type::InterpolatedShellEnd);
378
- }
379
- case '(': {
380
- advance(2);
381
- return consume_double_quoted_string('(', ')', Token::Type::InterpolatedShellBegin, Token::Type::InterpolatedShellEnd);
382
- }
383
- default:
384
- return Token { Token::Type::Percent, m_file, m_token_line, m_token_column, m_whitespace_precedes };
385
- }
349
+ return consume_percent_string(&Lexer::consume_interpolated_shell);
386
350
  case 'w':
387
- switch (peek()) {
388
- case '/':
389
- case '|': {
390
- char c = next();
391
- advance();
392
- return consume_quoted_array_without_interpolation(c, c, Token::Type::PercentLowerW);
393
- }
394
- case '[':
395
- advance(2);
396
- return consume_quoted_array_without_interpolation('[', ']', Token::Type::PercentLowerW);
397
- case '{':
398
- advance(2);
399
- return consume_quoted_array_without_interpolation('{', '}', Token::Type::PercentLowerW);
400
- case '<':
401
- advance(2);
402
- return consume_quoted_array_without_interpolation('<', '>', Token::Type::PercentLowerW);
403
- case '(':
404
- advance(2);
405
- return consume_quoted_array_without_interpolation('(', ')', Token::Type::PercentLowerW);
406
- default:
407
- return Token { Token::Type::Percent, m_file, m_token_line, m_token_column, m_whitespace_precedes };
408
- }
351
+ return consume_percent_string(&Lexer::consume_percent_lower_w);
409
352
  case 'W':
410
- switch (peek()) {
411
- case '/':
412
- case '|': {
413
- char c = next();
414
- advance();
415
- return consume_quoted_array_with_interpolation(0, c, Token::Type::PercentUpperW);
416
- }
417
- case '[':
418
- advance(2);
419
- return consume_quoted_array_with_interpolation('[', ']', Token::Type::PercentUpperW);
420
- case '{':
421
- advance(2);
422
- return consume_quoted_array_with_interpolation('{', '}', Token::Type::PercentUpperW);
423
- case '<':
424
- advance(2);
425
- return consume_quoted_array_with_interpolation('<', '>', Token::Type::PercentUpperW);
426
- case '(':
427
- advance(2);
428
- return consume_quoted_array_with_interpolation('(', ')', Token::Type::PercentUpperW);
429
- default:
430
- return Token { Token::Type::Percent, m_file, m_token_line, m_token_column, m_whitespace_precedes };
431
- }
353
+ return consume_percent_string(&Lexer::consume_percent_upper_w);
432
354
  case 'i':
433
- switch (peek()) {
434
- case '|':
435
- case '/': {
436
- char c = next();
437
- advance();
438
- return consume_quoted_array_without_interpolation(c, c, Token::Type::PercentLowerI);
439
- }
440
- case '[':
441
- advance(2);
442
- return consume_quoted_array_without_interpolation('[', ']', Token::Type::PercentLowerI);
443
- case '{':
444
- advance(2);
445
- return consume_quoted_array_without_interpolation('{', '}', Token::Type::PercentLowerI);
446
- case '<':
447
- advance(2);
448
- return consume_quoted_array_without_interpolation('<', '>', Token::Type::PercentLowerI);
449
- case '(':
450
- advance(2);
451
- return consume_quoted_array_without_interpolation('(', ')', Token::Type::PercentLowerI);
452
- default:
453
- return Token { Token::Type::Percent, m_file, m_token_line, m_token_column, m_whitespace_precedes };
454
- }
355
+ return consume_percent_string(&Lexer::consume_percent_lower_i);
455
356
  case 'I':
456
- switch (peek()) {
457
- case '|':
458
- case '/': {
459
- char c = next();
460
- advance();
461
- return consume_quoted_array_with_interpolation(0, c, Token::Type::PercentUpperI);
462
- }
463
- case '[':
464
- advance(2);
465
- return consume_quoted_array_with_interpolation('[', ']', Token::Type::PercentUpperI);
466
- case '{':
467
- advance(2);
468
- return consume_quoted_array_with_interpolation('{', '}', Token::Type::PercentUpperI);
469
- case '<':
470
- advance(2);
471
- return consume_quoted_array_with_interpolation('<', '>', Token::Type::PercentUpperI);
472
- case '(':
473
- advance(2);
474
- return consume_quoted_array_with_interpolation('(', ')', Token::Type::PercentUpperI);
475
- default:
476
- return Token { Token::Type::Percent, m_file, m_token_line, m_token_column, m_whitespace_precedes };
477
- }
478
- case '[':
479
- advance();
480
- return consume_double_quoted_string('[', ']');
481
- case '{':
482
- advance();
483
- return consume_double_quoted_string('{', '}');
484
- case '<':
485
- advance();
486
- return consume_double_quoted_string('<', '>');
487
- case '(':
488
- if (m_last_token.type() == Token::Type::DefKeyword || m_last_token.type() == Token::Type::Dot) {
489
- // It's a trap! This looks like a %(string) but it's a method def/call!
490
- break;
491
- }
492
- advance();
493
- return consume_double_quoted_string('(', ')');
494
- default: {
495
- auto c = current_char();
496
- if (char_can_be_string_or_regexp_delimiter(c)) {
497
- advance();
498
- return consume_double_quoted_string(c, c);
499
- }
500
- break;
501
- }
357
+ return consume_percent_string(&Lexer::consume_percent_upper_i);
358
+ default:
359
+ return consume_percent_string(&Lexer::consume_interpolated_string, false);
502
360
  }
503
- return Token { Token::Type::Percent, m_file, m_token_line, m_token_column, m_whitespace_precedes };
504
361
  case '!':
505
362
  advance();
506
363
  switch (current_char()) {
@@ -511,10 +368,10 @@ Token Lexer::build_next_token() {
511
368
  advance();
512
369
  return Token { Token::Type::NotMatch, m_file, m_token_line, m_token_column, m_whitespace_precedes };
513
370
  case '@':
514
- if (m_last_token.is_def_keyword() || m_last_token.is_dot()) {
371
+ if (m_remaining_method_names > 0) {
515
372
  advance();
516
373
  SharedPtr<String> lit = new String("!@");
517
- return Token { Token::Type::BareName, lit, m_file, m_token_line, m_token_column, m_whitespace_precedes };
374
+ return Token { Token::Type::OperatorName, lit, m_file, m_token_line, m_token_column, m_whitespace_precedes };
518
375
  } else {
519
376
  return Token { Token::Type::Not, m_file, m_token_line, m_token_column, m_whitespace_precedes };
520
377
  }
@@ -649,10 +506,10 @@ Token Lexer::build_next_token() {
649
506
  advance();
650
507
  switch (current_char()) {
651
508
  case '@':
652
- if (m_last_token.is_def_keyword() || m_last_token.is_dot()) {
509
+ if (m_remaining_method_names > 0) {
653
510
  advance();
654
511
  SharedPtr<String> lit = new String("~@");
655
- return Token { Token::Type::BareName, lit, m_file, m_token_line, m_token_column, m_whitespace_precedes };
512
+ return Token { Token::Type::OperatorName, lit, m_file, m_token_line, m_token_column, m_whitespace_precedes };
656
513
  } else {
657
514
  return Token { Token::Type::Tilde, m_file, m_token_line, m_token_column, m_whitespace_precedes };
658
515
  }
@@ -661,7 +518,7 @@ Token Lexer::build_next_token() {
661
518
  }
662
519
  case '?': {
663
520
  auto c = next();
664
- if (isspace(c)) {
521
+ if (isspace(c) || c == 0) {
665
522
  m_open_ternary = true;
666
523
  return Token { Token::Type::TernaryQuestion, m_file, m_token_line, m_token_column, m_whitespace_precedes };
667
524
  } else {
@@ -691,7 +548,7 @@ Token Lexer::build_next_token() {
691
548
  advance();
692
549
  auto string = consume_single_quoted_string('\'', '\'');
693
550
  return Token { Token::Type::Symbol, string.literal(), m_file, m_token_line, m_token_column, m_whitespace_precedes };
694
- } else if (isspace(c)) {
551
+ } else if (isspace(c) || c == 0) {
695
552
  m_open_ternary = false;
696
553
  auto token = Token { Token::Type::TernaryColon, m_file, m_token_line, m_token_column, m_whitespace_precedes };
697
554
  return token;
@@ -789,13 +646,18 @@ Token Lexer::build_next_token() {
789
646
  return Token { Token::Type::Comma, m_file, m_token_line, m_token_column, m_whitespace_precedes };
790
647
  case '"':
791
648
  advance();
792
- return consume_double_quoted_string('"', '"');
649
+ return consume_interpolated_string('"', '"');
793
650
  case '\'':
794
651
  advance();
795
652
  return consume_single_quoted_string('\'', '\'');
796
653
  case '`': {
797
654
  advance();
798
- return consume_double_quoted_string('`', '`', Token::Type::InterpolatedShellBegin, Token::Type::InterpolatedShellEnd);
655
+ if (m_remaining_method_names > 0) {
656
+ SharedPtr<String> lit = new String("`");
657
+ return Token { Token::Type::OperatorName, lit, m_file, m_token_line, m_token_column, m_whitespace_precedes };
658
+ } else {
659
+ return consume_interpolated_shell('`', '`');
660
+ }
799
661
  }
800
662
  case '#':
801
663
  if (token_is_first_on_line()) {
@@ -858,14 +720,14 @@ Token Lexer::build_next_token() {
858
720
 
859
721
  Token keyword_token;
860
722
 
861
- if (!m_last_token.is_dot() && match(4, "self")) {
862
- if (current_char() == '.')
723
+ if (!m_last_token.is_dot() && !m_last_token.is_constant_resolution() && match(4, "self")) {
724
+ if (current_char() == '.' || (current_char() == ':' && peek() == ':'))
863
725
  keyword_token = { Token::Type::SelfKeyword, m_file, m_token_line, m_token_column, m_whitespace_precedes };
864
726
  else
865
727
  rewind(4);
866
728
  }
867
729
 
868
- if (!m_last_token.is_dot() && !m_last_token.is_def_keyword()) {
730
+ if (m_remaining_method_names == 0) {
869
731
  if (match(12, "__ENCODING__"))
870
732
  keyword_token = { Token::Type::ENCODINGKeyword, m_file, m_token_line, m_token_column, m_whitespace_precedes };
871
733
  else if (match(8, "__LINE__"))
@@ -960,10 +822,10 @@ Token Lexer::build_next_token() {
960
822
  }
961
823
 
962
824
  auto c = current_char();
963
- if ((c >= 'a' && c <= 'z') || c == '_') {
964
- return consume_bare_name();
825
+ if (is_name_start_char(c)) {
826
+ return consume_bare_name_or_constant(Token::Type::BareName);
965
827
  } else if (c >= 'A' && c <= 'Z') {
966
- return consume_constant();
828
+ return consume_bare_name_or_constant(Token::Type::Constant);
967
829
  } else {
968
830
  auto buf = consume_non_whitespace();
969
831
  auto token = Token { Token::Type::Invalid, buf, m_file, m_token_line, m_token_column, m_whitespace_precedes };
@@ -1093,45 +955,47 @@ Token Lexer::consume_symbol() {
1093
955
  return Token { Token::Type::Symbol, buf, m_file, m_token_line, m_token_column, m_whitespace_precedes };
1094
956
  }
1095
957
 
1096
- Token Lexer::consume_word(Token::Type type) {
958
+ SharedPtr<String> Lexer::consume_word() {
1097
959
  char c = current_char();
1098
960
  SharedPtr<String> buf = new String("");
1099
961
  do {
1100
962
  buf->append_char(c);
1101
963
  c = next();
1102
964
  } while (is_identifier_char(c));
965
+ return buf;
966
+ }
967
+
968
+ Token Lexer::consume_word(Token::Type type) {
969
+ return Token { type, consume_word(), m_file, m_token_line, m_token_column, m_whitespace_precedes };
970
+ }
971
+
972
+ Token Lexer::consume_bare_name_or_constant(Token::Type type) {
973
+ auto buf = consume_word();
974
+ auto c = current_char();
1103
975
  switch (c) {
1104
976
  case '?':
1105
977
  case '!':
1106
978
  advance();
1107
979
  buf->append_char(c);
1108
980
  break;
981
+ case '=':
982
+ if (m_allow_assignment_method || (!m_last_token.is_dot() && m_remaining_method_names > 0)) {
983
+ advance();
984
+ buf->append_char(c);
985
+ }
986
+ break;
987
+ case ':':
988
+ if (peek() != ':' && m_last_token.can_precede_symbol_key()) {
989
+ advance();
990
+ type = Token::Type::SymbolKey;
991
+ }
992
+ break;
1109
993
  default:
1110
994
  break;
1111
995
  }
1112
996
  return Token { type, buf, m_file, m_token_line, m_token_column, m_whitespace_precedes };
1113
997
  }
1114
998
 
1115
- Token Lexer::consume_bare_name() {
1116
- auto token = consume_word(Token::Type::BareName);
1117
- auto c = current_char();
1118
- if (c == ':' && peek() != ':' && m_last_token.can_precede_symbol_key()) {
1119
- advance();
1120
- token.set_type(Token::Type::SymbolKey);
1121
- }
1122
- return token;
1123
- }
1124
-
1125
- Token Lexer::consume_constant() {
1126
- auto token = consume_word(Token::Type::Constant);
1127
- auto c = current_char();
1128
- if (c == ':' && peek() != ':' && m_last_token.can_precede_symbol_key()) {
1129
- advance();
1130
- token.set_type(Token::Type::SymbolKey);
1131
- }
1132
- return token;
1133
- }
1134
-
1135
999
  Token Lexer::consume_global_variable() {
1136
1000
  switch (peek()) {
1137
1001
  case '?':
@@ -1153,7 +1017,6 @@ Token Lexer::consume_global_variable() {
1153
1017
  case '.':
1154
1018
  case ',':
1155
1019
  case ':':
1156
- case '_':
1157
1020
  case '~': {
1158
1021
  advance();
1159
1022
  SharedPtr<String> buf = new String("$");
@@ -1277,7 +1140,7 @@ Token Lexer::consume_heredoc() {
1277
1140
  }
1278
1141
  advance();
1279
1142
  } else {
1280
- heredoc_name = String(consume_word(Token::Type::BareName).literal());
1143
+ heredoc_name = *consume_word();
1281
1144
  }
1282
1145
 
1283
1146
  SharedPtr<String> doc = new String("");
@@ -1673,7 +1536,7 @@ Token Lexer::consume_single_quoted_string(char start_char, char stop_char) {
1673
1536
  SharedPtr<String> buf = new String("");
1674
1537
  char c = current_char();
1675
1538
  while (c) {
1676
- if (c == '\\') {
1539
+ if (c == '\\' && stop_char != '\\') {
1677
1540
  c = next();
1678
1541
  if (c == stop_char || c == '\\') {
1679
1542
  buf->append_char(c);
@@ -1720,6 +1583,65 @@ Token Lexer::consume_regexp(char start_char, char stop_char) {
1720
1583
  return Token { Token::Type::InterpolatedRegexpBegin, start_char, m_file, m_token_line, m_token_column, m_whitespace_precedes };
1721
1584
  }
1722
1585
 
1586
+ Token Lexer::consume_percent_symbol(char start_char, char stop_char) {
1587
+ Token token = consume_single_quoted_string(start_char, stop_char);
1588
+ token.set_type(Token::Type::Symbol);
1589
+ return token;
1590
+ }
1591
+
1592
+ Token Lexer::consume_interpolated_string(char start_char, char stop_char) {
1593
+ return consume_double_quoted_string(start_char, stop_char, Token::Type::InterpolatedStringBegin, Token::Type::InterpolatedStringEnd);
1594
+ }
1595
+
1596
+ Token Lexer::consume_interpolated_shell(char start_char, char stop_char) {
1597
+ return consume_double_quoted_string(start_char, stop_char, Token::Type::InterpolatedShellBegin, Token::Type::InterpolatedShellEnd);
1598
+ }
1599
+
1600
+ Token Lexer::consume_percent_lower_w(char start_char, char stop_char) {
1601
+ return consume_quoted_array_without_interpolation(start_char, stop_char, Token::Type::PercentLowerW);
1602
+ }
1603
+
1604
+ Token Lexer::consume_percent_upper_w(char start_char, char stop_char) {
1605
+ return consume_quoted_array_with_interpolation(start_char, stop_char, Token::Type::PercentUpperW);
1606
+ }
1607
+
1608
+ Token Lexer::consume_percent_lower_i(char start_char, char stop_char) {
1609
+ return consume_quoted_array_without_interpolation(start_char, stop_char, Token::Type::PercentLowerI);
1610
+ }
1611
+
1612
+ Token Lexer::consume_percent_upper_i(char start_char, char stop_char) {
1613
+ return consume_quoted_array_with_interpolation(start_char, stop_char, Token::Type::PercentUpperI);
1614
+ }
1615
+
1616
+ Token Lexer::consume_percent_string(Token (Lexer::*consumer)(char start_char, char stop_char), bool is_lettered) {
1617
+ if (m_remaining_method_names > 0) {
1618
+ return Token { Token::Type::Percent, m_file, m_token_line, m_token_column, m_whitespace_precedes };
1619
+ }
1620
+ char c = is_lettered ? peek() : current_char();
1621
+ size_t bytes = is_lettered ? 2 : 1;
1622
+ switch (c) {
1623
+ case '[':
1624
+ advance(bytes);
1625
+ return (this->*consumer)('[', ']');
1626
+ case '{':
1627
+ advance(bytes);
1628
+ return (this->*consumer)('{', '}');
1629
+ case '<':
1630
+ advance(bytes);
1631
+ return (this->*consumer)('<', '>');
1632
+ case '(':
1633
+ advance(bytes);
1634
+ return (this->*consumer)('(', ')');
1635
+ default:
1636
+ if (char_can_be_string_or_regexp_delimiter(c)) {
1637
+ advance(bytes);
1638
+ return (this->*consumer)(c, c);
1639
+ } else {
1640
+ return Token { Token::Type::Percent, m_file, m_token_line, m_token_column, m_whitespace_precedes };
1641
+ }
1642
+ }
1643
+ }
1644
+
1723
1645
  SharedPtr<String> Lexer::consume_non_whitespace() {
1724
1646
  char c = current_char();
1725
1647
  SharedPtr<String> buf = new String("");
@@ -3,6 +3,11 @@
3
3
  namespace NatalieParser {
4
4
 
5
5
  void MatchNode::transform(Creator *creator) const {
6
+ if (!m_arg) {
7
+ creator->set_type("match");
8
+ creator->append(m_regexp.ref());
9
+ return;
10
+ }
6
11
  if (m_regexp_on_left)
7
12
  creator->set_type("match2");
8
13
  else