yarp 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/src/yarp.c CHANGED
@@ -1,5 +1,4 @@
1
1
  #include "yarp.h"
2
- #include "yarp/version.h"
3
2
 
4
3
  // The YARP version and the serialization format.
5
4
  const char *
@@ -362,7 +361,7 @@ lex_state_ignored_p(yp_parser_t *parser) {
362
361
 
363
362
  if (ignored) {
364
363
  return YP_IGNORED_NEWLINE_ALL;
365
- } else if (parser->lex_state == (YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED)) {
364
+ } else if ((parser->lex_state & ~((unsigned int) YP_LEX_STATE_LABEL)) == (YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED)) {
366
365
  return YP_IGNORED_NEWLINE_PATTERN;
367
366
  } else {
368
367
  return YP_IGNORED_NEWLINE_NONE;
@@ -536,6 +535,73 @@ yp_arguments_validate(yp_parser_t *parser, yp_arguments_t *arguments) {
536
535
  }
537
536
  }
538
537
 
538
+ /******************************************************************************/
539
+ /* Scope node functions */
540
+ /******************************************************************************/
541
+
542
+ // Generate a scope node from the given node.
543
+ void
544
+ yp_scope_node_init(yp_node_t *node, yp_scope_node_t *scope) {
545
+ scope->base.type = YP_NODE_SCOPE_NODE;
546
+ scope->base.location.start = node->location.start;
547
+ scope->base.location.end = node->location.end;
548
+
549
+ scope->parameters = NULL;
550
+ scope->body = NULL;
551
+ yp_constant_id_list_init(&scope->locals);
552
+
553
+ switch (YP_NODE_TYPE(node)) {
554
+ case YP_NODE_BLOCK_NODE: {
555
+ yp_block_node_t *cast = (yp_block_node_t *) node;
556
+ if (cast->parameters) scope->parameters = cast->parameters->parameters;
557
+ scope->body = cast->body;
558
+ scope->locals = cast->locals;
559
+ break;
560
+ }
561
+ case YP_NODE_CLASS_NODE: {
562
+ yp_class_node_t *cast = (yp_class_node_t *) node;
563
+ scope->body = cast->body;
564
+ scope->locals = cast->locals;
565
+ break;
566
+ }
567
+ case YP_NODE_DEF_NODE: {
568
+ yp_def_node_t *cast = (yp_def_node_t *) node;
569
+ scope->parameters = cast->parameters;
570
+ scope->body = cast->body;
571
+ scope->locals = cast->locals;
572
+ break;
573
+ }
574
+ case YP_NODE_LAMBDA_NODE: {
575
+ yp_lambda_node_t *cast = (yp_lambda_node_t *) node;
576
+ if (cast->parameters) scope->parameters = cast->parameters->parameters;
577
+ scope->body = cast->body;
578
+ scope->locals = cast->locals;
579
+ break;
580
+ }
581
+ case YP_NODE_MODULE_NODE: {
582
+ yp_module_node_t *cast = (yp_module_node_t *) node;
583
+ scope->body = cast->body;
584
+ scope->locals = cast->locals;
585
+ break;
586
+ }
587
+ case YP_NODE_PROGRAM_NODE: {
588
+ yp_program_node_t *cast = (yp_program_node_t *) node;
589
+ scope->body = (yp_node_t *) cast->statements;
590
+ scope->locals = cast->locals;
591
+ break;
592
+ }
593
+ case YP_NODE_SINGLETON_CLASS_NODE: {
594
+ yp_singleton_class_node_t *cast = (yp_singleton_class_node_t *) node;
595
+ scope->body = cast->body;
596
+ scope->locals = cast->locals;
597
+ break;
598
+ }
599
+ default:
600
+ assert(false && "unreachable");
601
+ break;
602
+ }
603
+ }
604
+
539
605
  /******************************************************************************/
540
606
  /* Node creation functions */
541
607
  /******************************************************************************/
@@ -658,27 +724,6 @@ yp_and_node_create(yp_parser_t *parser, yp_node_t *left, const yp_token_t *opera
658
724
  return node;
659
725
  }
660
726
 
661
- // Allocate and initialize a new AndWriteNode.
662
- static yp_and_write_node_t *
663
- yp_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
664
- yp_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_and_write_node_t);
665
-
666
- *node = (yp_and_write_node_t) {
667
- {
668
- .type = YP_NODE_AND_WRITE_NODE,
669
- .location = {
670
- .start = target->location.start,
671
- .end = value->location.end
672
- },
673
- },
674
- .target = target,
675
- .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
676
- .value = value
677
- };
678
-
679
- return node;
680
- }
681
-
682
727
  // Allocate an initialize a new arguments node.
683
728
  static yp_arguments_node_t *
684
729
  yp_arguments_node_create(yp_parser_t *parser) {
@@ -1151,7 +1196,7 @@ yp_call_node_create(yp_parser_t *parser) {
1151
1196
  },
1152
1197
  .receiver = NULL,
1153
1198
  .operator_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
1154
- .message_loc = YP_LOCATION_NULL_VALUE(parser),
1199
+ .message_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
1155
1200
  .opening_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
1156
1201
  .arguments = NULL,
1157
1202
  .closing_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
@@ -1486,7 +1531,7 @@ yp_case_node_end_keyword_loc_set(yp_case_node_t *node, const yp_token_t *end_key
1486
1531
 
1487
1532
  // Allocate a new ClassNode node.
1488
1533
  static yp_class_node_t *
1489
- yp_class_node_create(yp_parser_t *parser, yp_constant_id_list_t *locals, const yp_token_t *class_keyword, yp_node_t *constant_path, const yp_token_t *inheritance_operator, yp_node_t *superclass, yp_node_t *body, const yp_token_t *end_keyword) {
1534
+ yp_class_node_create(yp_parser_t *parser, yp_constant_id_list_t *locals, const yp_token_t *class_keyword, yp_node_t *constant_path, const yp_token_t *name, const yp_token_t *inheritance_operator, yp_node_t *superclass, yp_node_t *body, const yp_token_t *end_keyword) {
1490
1535
  yp_class_node_t *node = YP_ALLOC_NODE(parser, yp_class_node_t);
1491
1536
 
1492
1537
  *node = (yp_class_node_t) {
@@ -1500,7 +1545,77 @@ yp_class_node_create(yp_parser_t *parser, yp_constant_id_list_t *locals, const y
1500
1545
  .inheritance_operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(inheritance_operator),
1501
1546
  .superclass = superclass,
1502
1547
  .body = body,
1503
- .end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword)
1548
+ .end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword),
1549
+ .name = YP_EMPTY_STRING
1550
+ };
1551
+
1552
+ yp_string_shared_init(&node->name, name->start, name->end);
1553
+ return node;
1554
+ }
1555
+
1556
+ // Allocate and initialize a new ClassVariableAndWriteNode node.
1557
+ static yp_class_variable_and_write_node_t *
1558
+ yp_class_variable_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
1559
+ assert(YP_NODE_TYPE_P(target, YP_NODE_CLASS_VARIABLE_READ_NODE));
1560
+ assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
1561
+ yp_class_variable_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_class_variable_and_write_node_t);
1562
+
1563
+ *node = (yp_class_variable_and_write_node_t) {
1564
+ {
1565
+ .type = YP_NODE_CLASS_VARIABLE_AND_WRITE_NODE,
1566
+ .location = {
1567
+ .start = target->location.start,
1568
+ .end = value->location.end
1569
+ }
1570
+ },
1571
+ .name_loc = target->location,
1572
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
1573
+ .value = value
1574
+ };
1575
+
1576
+ return node;
1577
+ }
1578
+
1579
+ // Allocate and initialize a new ClassVariableOperatorWriteNode node.
1580
+ static yp_class_variable_operator_write_node_t *
1581
+ yp_class_variable_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
1582
+ yp_class_variable_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_class_variable_operator_write_node_t);
1583
+
1584
+ *node = (yp_class_variable_operator_write_node_t) {
1585
+ {
1586
+ .type = YP_NODE_CLASS_VARIABLE_OPERATOR_WRITE_NODE,
1587
+ .location = {
1588
+ .start = target->location.start,
1589
+ .end = value->location.end
1590
+ }
1591
+ },
1592
+ .name_loc = target->location,
1593
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
1594
+ .value = value,
1595
+ .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1)
1596
+ };
1597
+
1598
+ return node;
1599
+ }
1600
+
1601
+ // Allocate and initialize a new ClassVariableOrWriteNode node.
1602
+ static yp_class_variable_or_write_node_t *
1603
+ yp_class_variable_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
1604
+ assert(YP_NODE_TYPE_P(target, YP_NODE_CLASS_VARIABLE_READ_NODE));
1605
+ assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
1606
+ yp_class_variable_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_class_variable_or_write_node_t);
1607
+
1608
+ *node = (yp_class_variable_or_write_node_t) {
1609
+ {
1610
+ .type = YP_NODE_CLASS_VARIABLE_OR_WRITE_NODE,
1611
+ .location = {
1612
+ .start = target->location.start,
1613
+ .end = value->location.end
1614
+ }
1615
+ },
1616
+ .name_loc = target->location,
1617
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
1618
+ .value = value
1504
1619
  };
1505
1620
 
1506
1621
  return node;
@@ -1525,10 +1640,10 @@ yp_class_variable_read_node_to_class_variable_write_node(yp_parser_t *parser, yp
1525
1640
  .type = YP_NODE_CLASS_VARIABLE_WRITE_NODE,
1526
1641
  .location = {
1527
1642
  .start = read_node->base.location.start,
1528
- .end = value != NULL ? value->location.end : read_node->base.location.end
1643
+ .end = value->location.end
1529
1644
  },
1530
1645
  },
1531
- .name_loc = YP_LOCATION_NODE_VALUE((yp_node_t *)read_node),
1646
+ .name_loc = YP_LOCATION_NODE_VALUE((yp_node_t *) read_node),
1532
1647
  .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
1533
1648
  .value = value
1534
1649
  };
@@ -1536,6 +1651,72 @@ yp_class_variable_read_node_to_class_variable_write_node(yp_parser_t *parser, yp
1536
1651
  return node;
1537
1652
  }
1538
1653
 
1654
+ // Allocate and initialize a new ConstantPathAndWriteNode node.
1655
+ static yp_constant_path_and_write_node_t *
1656
+ yp_constant_path_and_write_node_create(yp_parser_t *parser, yp_constant_path_node_t *target, const yp_token_t *operator, yp_node_t *value) {
1657
+ assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
1658
+ yp_constant_path_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_path_and_write_node_t);
1659
+
1660
+ *node = (yp_constant_path_and_write_node_t) {
1661
+ {
1662
+ .type = YP_NODE_CONSTANT_PATH_AND_WRITE_NODE,
1663
+ .location = {
1664
+ .start = target->base.location.start,
1665
+ .end = value->location.end
1666
+ }
1667
+ },
1668
+ .target = target,
1669
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
1670
+ .value = value
1671
+ };
1672
+
1673
+ return node;
1674
+ }
1675
+
1676
+ // Allocate and initialize a new ConstantPathOperatorWriteNode node.
1677
+ static yp_constant_path_operator_write_node_t *
1678
+ yp_constant_path_operator_write_node_create(yp_parser_t *parser, yp_constant_path_node_t *target, const yp_token_t *operator, yp_node_t *value) {
1679
+ yp_constant_path_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_path_operator_write_node_t);
1680
+
1681
+ *node = (yp_constant_path_operator_write_node_t) {
1682
+ {
1683
+ .type = YP_NODE_CONSTANT_PATH_OPERATOR_WRITE_NODE,
1684
+ .location = {
1685
+ .start = target->base.location.start,
1686
+ .end = value->location.end
1687
+ }
1688
+ },
1689
+ .target = target,
1690
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
1691
+ .value = value,
1692
+ .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1)
1693
+ };
1694
+
1695
+ return node;
1696
+ }
1697
+
1698
+ // Allocate and initialize a new ConstantPathOrWriteNode node.
1699
+ static yp_constant_path_or_write_node_t *
1700
+ yp_constant_path_or_write_node_create(yp_parser_t *parser, yp_constant_path_node_t *target, const yp_token_t *operator, yp_node_t *value) {
1701
+ assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
1702
+ yp_constant_path_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_path_or_write_node_t);
1703
+
1704
+ *node = (yp_constant_path_or_write_node_t) {
1705
+ {
1706
+ .type = YP_NODE_CONSTANT_PATH_OR_WRITE_NODE,
1707
+ .location = {
1708
+ .start = target->base.location.start,
1709
+ .end = value->location.end
1710
+ }
1711
+ },
1712
+ .target = target,
1713
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
1714
+ .value = value
1715
+ };
1716
+
1717
+ return node;
1718
+ }
1719
+
1539
1720
  // Allocate and initialize a new ConstantPathNode node.
1540
1721
  static yp_constant_path_node_t *
1541
1722
  yp_constant_path_node_create(yp_parser_t *parser, yp_node_t *parent, const yp_token_t *delimiter, yp_node_t *child) {
@@ -1567,7 +1748,7 @@ yp_constant_path_write_node_create(yp_parser_t *parser, yp_constant_path_node_t
1567
1748
  .type = YP_NODE_CONSTANT_PATH_WRITE_NODE,
1568
1749
  .location = {
1569
1750
  .start = target->base.location.start,
1570
- .end = (value == NULL ? target->base.location.end : value->location.end)
1751
+ .end = value->location.end
1571
1752
  },
1572
1753
  },
1573
1754
  .target = target,
@@ -1578,6 +1759,74 @@ yp_constant_path_write_node_create(yp_parser_t *parser, yp_constant_path_node_t
1578
1759
  return node;
1579
1760
  }
1580
1761
 
1762
+ // Allocate and initialize a new ConstantAndWriteNode node.
1763
+ static yp_constant_and_write_node_t *
1764
+ yp_constant_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
1765
+ assert(YP_NODE_TYPE_P(target, YP_NODE_CONSTANT_READ_NODE));
1766
+ assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
1767
+ yp_constant_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_and_write_node_t);
1768
+
1769
+ *node = (yp_constant_and_write_node_t) {
1770
+ {
1771
+ .type = YP_NODE_CONSTANT_AND_WRITE_NODE,
1772
+ .location = {
1773
+ .start = target->location.start,
1774
+ .end = value->location.end
1775
+ }
1776
+ },
1777
+ .name_loc = target->location,
1778
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
1779
+ .value = value
1780
+ };
1781
+
1782
+ return node;
1783
+ }
1784
+
1785
+ // Allocate and initialize a new ConstantOperatorWriteNode node.
1786
+ static yp_constant_operator_write_node_t *
1787
+ yp_constant_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
1788
+ yp_constant_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_operator_write_node_t);
1789
+
1790
+ *node = (yp_constant_operator_write_node_t) {
1791
+ {
1792
+ .type = YP_NODE_CONSTANT_OPERATOR_WRITE_NODE,
1793
+ .location = {
1794
+ .start = target->location.start,
1795
+ .end = value->location.end
1796
+ }
1797
+ },
1798
+ .name_loc = target->location,
1799
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
1800
+ .value = value,
1801
+ .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1)
1802
+ };
1803
+
1804
+ return node;
1805
+ }
1806
+
1807
+ // Allocate and initialize a new ConstantOrWriteNode node.
1808
+ static yp_constant_or_write_node_t *
1809
+ yp_constant_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
1810
+ assert(YP_NODE_TYPE_P(target, YP_NODE_CONSTANT_READ_NODE));
1811
+ assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
1812
+ yp_constant_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_or_write_node_t);
1813
+
1814
+ *node = (yp_constant_or_write_node_t) {
1815
+ {
1816
+ .type = YP_NODE_CONSTANT_OR_WRITE_NODE,
1817
+ .location = {
1818
+ .start = target->location.start,
1819
+ .end = value->location.end
1820
+ }
1821
+ },
1822
+ .name_loc = target->location,
1823
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
1824
+ .value = value
1825
+ };
1826
+
1827
+ return node;
1828
+ }
1829
+
1581
1830
  // Allocate and initialize a new ConstantReadNode node.
1582
1831
  static yp_constant_read_node_t *
1583
1832
  yp_constant_read_node_create(yp_parser_t *parser, const yp_token_t *name) {
@@ -1598,7 +1847,7 @@ yp_constant_write_node_create(yp_parser_t *parser, yp_location_t *name_loc, cons
1598
1847
  .type = YP_NODE_CONSTANT_WRITE_NODE,
1599
1848
  .location = {
1600
1849
  .start = name_loc->start,
1601
- .end = value != NULL ? value->location.end : name_loc->end
1850
+ .end = value->location.end
1602
1851
  },
1603
1852
  },
1604
1853
  .name_loc = *name_loc,
@@ -2012,6 +2261,74 @@ yp_hash_pattern_node_node_list_create(yp_parser_t *parser, yp_node_list_t *assoc
2012
2261
  return node;
2013
2262
  }
2014
2263
 
2264
+ // Allocate and initialize a new GlobalVariableAndWriteNode node.
2265
+ static yp_global_variable_and_write_node_t *
2266
+ yp_global_variable_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
2267
+ assert(YP_NODE_TYPE_P(target, YP_NODE_GLOBAL_VARIABLE_READ_NODE) || YP_NODE_TYPE_P(target, YP_NODE_BACK_REFERENCE_READ_NODE) || YP_NODE_TYPE_P(target, YP_NODE_NUMBERED_REFERENCE_READ_NODE));
2268
+ assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
2269
+ yp_global_variable_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_global_variable_and_write_node_t);
2270
+
2271
+ *node = (yp_global_variable_and_write_node_t) {
2272
+ {
2273
+ .type = YP_NODE_GLOBAL_VARIABLE_AND_WRITE_NODE,
2274
+ .location = {
2275
+ .start = target->location.start,
2276
+ .end = value->location.end
2277
+ }
2278
+ },
2279
+ .name_loc = target->location,
2280
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
2281
+ .value = value
2282
+ };
2283
+
2284
+ return node;
2285
+ }
2286
+
2287
+ // Allocate and initialize a new GlobalVariableOperatorWriteNode node.
2288
+ static yp_global_variable_operator_write_node_t *
2289
+ yp_global_variable_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
2290
+ yp_global_variable_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_global_variable_operator_write_node_t);
2291
+
2292
+ *node = (yp_global_variable_operator_write_node_t) {
2293
+ {
2294
+ .type = YP_NODE_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE,
2295
+ .location = {
2296
+ .start = target->location.start,
2297
+ .end = value->location.end
2298
+ }
2299
+ },
2300
+ .name_loc = target->location,
2301
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
2302
+ .value = value,
2303
+ .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1)
2304
+ };
2305
+
2306
+ return node;
2307
+ }
2308
+
2309
+ // Allocate and initialize a new GlobalVariableOrWriteNode node.
2310
+ static yp_global_variable_or_write_node_t *
2311
+ yp_global_variable_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
2312
+ assert(YP_NODE_TYPE_P(target, YP_NODE_GLOBAL_VARIABLE_READ_NODE) || YP_NODE_TYPE_P(target, YP_NODE_BACK_REFERENCE_READ_NODE) || YP_NODE_TYPE_P(target, YP_NODE_NUMBERED_REFERENCE_READ_NODE));
2313
+ assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
2314
+ yp_global_variable_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_global_variable_or_write_node_t);
2315
+
2316
+ *node = (yp_global_variable_or_write_node_t) {
2317
+ {
2318
+ .type = YP_NODE_GLOBAL_VARIABLE_OR_WRITE_NODE,
2319
+ .location = {
2320
+ .start = target->location.start,
2321
+ .end = value->location.end
2322
+ }
2323
+ },
2324
+ .name_loc = target->location,
2325
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
2326
+ .value = value
2327
+ };
2328
+
2329
+ return node;
2330
+ }
2331
+
2015
2332
  // Allocate a new GlobalVariableReadNode node.
2016
2333
  static yp_global_variable_read_node_t *
2017
2334
  yp_global_variable_read_node_create(yp_parser_t *parser, const yp_token_t *name) {
@@ -2037,7 +2354,7 @@ yp_global_variable_write_node_create(yp_parser_t *parser, const yp_location_t *n
2037
2354
  .type = YP_NODE_GLOBAL_VARIABLE_WRITE_NODE,
2038
2355
  .location = {
2039
2356
  .start = name_loc->start,
2040
- .end = (value == NULL ? name_loc->end : value->location.end)
2357
+ .end = value->location.end
2041
2358
  },
2042
2359
  },
2043
2360
  .name_loc = *name_loc,
@@ -2302,6 +2619,74 @@ yp_in_node_create(yp_parser_t *parser, yp_node_t *pattern, yp_statements_node_t
2302
2619
  return node;
2303
2620
  }
2304
2621
 
2622
+ // Allocate and initialize a new InstanceVariableAndWriteNode node.
2623
+ static yp_instance_variable_and_write_node_t *
2624
+ yp_instance_variable_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
2625
+ assert(YP_NODE_TYPE_P(target, YP_NODE_INSTANCE_VARIABLE_READ_NODE));
2626
+ assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
2627
+ yp_instance_variable_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_instance_variable_and_write_node_t);
2628
+
2629
+ *node = (yp_instance_variable_and_write_node_t) {
2630
+ {
2631
+ .type = YP_NODE_INSTANCE_VARIABLE_AND_WRITE_NODE,
2632
+ .location = {
2633
+ .start = target->location.start,
2634
+ .end = value->location.end
2635
+ }
2636
+ },
2637
+ .name_loc = target->location,
2638
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
2639
+ .value = value
2640
+ };
2641
+
2642
+ return node;
2643
+ }
2644
+
2645
+ // Allocate and initialize a new InstanceVariableOperatorWriteNode node.
2646
+ static yp_instance_variable_operator_write_node_t *
2647
+ yp_instance_variable_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
2648
+ yp_instance_variable_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_instance_variable_operator_write_node_t);
2649
+
2650
+ *node = (yp_instance_variable_operator_write_node_t) {
2651
+ {
2652
+ .type = YP_NODE_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE,
2653
+ .location = {
2654
+ .start = target->location.start,
2655
+ .end = value->location.end
2656
+ }
2657
+ },
2658
+ .name_loc = target->location,
2659
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
2660
+ .value = value,
2661
+ .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1)
2662
+ };
2663
+
2664
+ return node;
2665
+ }
2666
+
2667
+ // Allocate and initialize a new InstanceVariableOrWriteNode node.
2668
+ static yp_instance_variable_or_write_node_t *
2669
+ yp_instance_variable_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
2670
+ assert(YP_NODE_TYPE_P(target, YP_NODE_INSTANCE_VARIABLE_READ_NODE));
2671
+ assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
2672
+ yp_instance_variable_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_instance_variable_or_write_node_t);
2673
+
2674
+ *node = (yp_instance_variable_or_write_node_t) {
2675
+ {
2676
+ .type = YP_NODE_INSTANCE_VARIABLE_OR_WRITE_NODE,
2677
+ .location = {
2678
+ .start = target->location.start,
2679
+ .end = value->location.end
2680
+ }
2681
+ },
2682
+ .name_loc = target->location,
2683
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
2684
+ .value = value
2685
+ };
2686
+
2687
+ return node;
2688
+ }
2689
+
2305
2690
  // Allocate and initialize a new InstanceVariableReadNode node.
2306
2691
  static yp_instance_variable_read_node_t *
2307
2692
  yp_instance_variable_read_node_create(yp_parser_t *parser, const yp_token_t *token) {
@@ -2324,7 +2709,7 @@ yp_instance_variable_write_node_create(yp_parser_t *parser, yp_instance_variable
2324
2709
  .type = YP_NODE_INSTANCE_VARIABLE_WRITE_NODE,
2325
2710
  .location = {
2326
2711
  .start = read_node->base.location.start,
2327
- .end = value == NULL ? read_node->base.location.end : value->location.end
2712
+ .end = value->location.end
2328
2713
  }
2329
2714
  },
2330
2715
  .name_loc = YP_LOCATION_NODE_BASE_VALUE(read_node),
@@ -2438,12 +2823,6 @@ yp_interpolated_symbol_node_append(yp_interpolated_symbol_node_t *node, yp_node_
2438
2823
  node->base.location.end = part->location.end;
2439
2824
  }
2440
2825
 
2441
- static inline void
2442
- yp_interpolated_symbol_node_closing_set(yp_interpolated_symbol_node_t *node, const yp_token_t *closing) {
2443
- node->closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(closing);
2444
- node->base.location.end = closing->end;
2445
- }
2446
-
2447
2826
  // Allocate a new InterpolatedXStringNode node.
2448
2827
  static yp_interpolated_x_string_node_t *
2449
2828
  yp_interpolated_xstring_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *closing) {
@@ -2551,25 +2930,102 @@ static yp_lambda_node_t *
2551
2930
  yp_lambda_node_create(
2552
2931
  yp_parser_t *parser,
2553
2932
  yp_constant_id_list_t *locals,
2933
+ const yp_token_t *operator,
2554
2934
  const yp_token_t *opening,
2935
+ const yp_token_t *closing,
2555
2936
  yp_block_parameters_node_t *parameters,
2556
- yp_node_t *body,
2557
- const yp_token_t *closing
2937
+ yp_node_t *body
2558
2938
  ) {
2559
2939
  yp_lambda_node_t *node = YP_ALLOC_NODE(parser, yp_lambda_node_t);
2560
2940
 
2561
- *node = (yp_lambda_node_t) {
2941
+ *node = (yp_lambda_node_t) {
2942
+ {
2943
+ .type = YP_NODE_LAMBDA_NODE,
2944
+ .location = {
2945
+ .start = operator->start,
2946
+ .end = closing->end
2947
+ },
2948
+ },
2949
+ .locals = *locals,
2950
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
2951
+ .opening_loc = YP_LOCATION_TOKEN_VALUE(opening),
2952
+ .closing_loc = YP_LOCATION_TOKEN_VALUE(closing),
2953
+ .parameters = parameters,
2954
+ .body = body
2955
+ };
2956
+
2957
+ return node;
2958
+ }
2959
+
2960
+ // Allocate and initialize a new LocalVariableAndWriteNode node.
2961
+ static yp_local_variable_and_write_node_t *
2962
+ yp_local_variable_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value, yp_constant_id_t constant_id, uint32_t depth) {
2963
+ assert(YP_NODE_TYPE_P(target, YP_NODE_LOCAL_VARIABLE_READ_NODE) || YP_NODE_TYPE_P(target, YP_NODE_CALL_NODE));
2964
+ assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL);
2965
+ yp_local_variable_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_and_write_node_t);
2966
+
2967
+ *node = (yp_local_variable_and_write_node_t) {
2968
+ {
2969
+ .type = YP_NODE_LOCAL_VARIABLE_AND_WRITE_NODE,
2970
+ .location = {
2971
+ .start = target->location.start,
2972
+ .end = value->location.end
2973
+ }
2974
+ },
2975
+ .name_loc = target->location,
2976
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
2977
+ .value = value,
2978
+ .constant_id = constant_id,
2979
+ .depth = depth
2980
+ };
2981
+
2982
+ return node;
2983
+ }
2984
+
2985
+ // Allocate and initialize a new LocalVariableOperatorWriteNode node.
2986
+ static yp_local_variable_operator_write_node_t *
2987
+ yp_local_variable_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value, yp_constant_id_t constant_id, uint32_t depth) {
2988
+ yp_local_variable_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_operator_write_node_t);
2989
+
2990
+ *node = (yp_local_variable_operator_write_node_t) {
2991
+ {
2992
+ .type = YP_NODE_LOCAL_VARIABLE_OPERATOR_WRITE_NODE,
2993
+ .location = {
2994
+ .start = target->location.start,
2995
+ .end = value->location.end
2996
+ }
2997
+ },
2998
+ .name_loc = target->location,
2999
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
3000
+ .value = value,
3001
+ .constant_id = constant_id,
3002
+ .operator_id = yp_parser_constant_id_location(parser, operator->start, operator->end - 1),
3003
+ .depth = depth
3004
+ };
3005
+
3006
+ return node;
3007
+ }
3008
+
3009
+ // Allocate and initialize a new LocalVariableOrWriteNode node.
3010
+ static yp_local_variable_or_write_node_t *
3011
+ yp_local_variable_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value, yp_constant_id_t constant_id, uint32_t depth) {
3012
+ assert(YP_NODE_TYPE_P(target, YP_NODE_LOCAL_VARIABLE_READ_NODE) || YP_NODE_TYPE_P(target, YP_NODE_CALL_NODE));
3013
+ assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL);
3014
+ yp_local_variable_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_or_write_node_t);
3015
+
3016
+ *node = (yp_local_variable_or_write_node_t) {
2562
3017
  {
2563
- .type = YP_NODE_LAMBDA_NODE,
3018
+ .type = YP_NODE_LOCAL_VARIABLE_OR_WRITE_NODE,
2564
3019
  .location = {
2565
- .start = opening->start,
2566
- .end = closing->end
2567
- },
3020
+ .start = target->location.start,
3021
+ .end = value->location.end
3022
+ }
2568
3023
  },
2569
- .locals = *locals,
2570
- .opening_loc = YP_LOCATION_TOKEN_VALUE(opening),
2571
- .parameters = parameters,
2572
- .body = body
3024
+ .name_loc = target->location,
3025
+ .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
3026
+ .value = value,
3027
+ .constant_id = constant_id,
3028
+ .depth = depth
2573
3029
  };
2574
3030
 
2575
3031
  return node;
@@ -2602,7 +3058,7 @@ yp_local_variable_write_node_create(yp_parser_t *parser, yp_constant_id_t consta
2602
3058
  .type = YP_NODE_LOCAL_VARIABLE_WRITE_NODE,
2603
3059
  .location = {
2604
3060
  .start = name_loc->start,
2605
- .end = value == NULL ? name_loc->end : value->location.end
3061
+ .end = value->location.end
2606
3062
  }
2607
3063
  },
2608
3064
  .constant_id = constant_id,
@@ -2615,21 +3071,18 @@ yp_local_variable_write_node_create(yp_parser_t *parser, yp_constant_id_t consta
2615
3071
  return node;
2616
3072
  }
2617
3073
 
2618
- // Allocate and initialize a new LocalVariableWriteNode node without an operator or target.
2619
- static yp_local_variable_write_node_t *
3074
+ // Allocate and initialize a new LocalVariableTargetNode node.
3075
+ static yp_local_variable_target_node_t *
2620
3076
  yp_local_variable_target_node_create(yp_parser_t *parser, const yp_token_t *name) {
2621
- yp_local_variable_write_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_write_node_t);
3077
+ yp_local_variable_target_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_target_node_t);
2622
3078
 
2623
- *node = (yp_local_variable_write_node_t) {
3079
+ *node = (yp_local_variable_target_node_t) {
2624
3080
  {
2625
- .type = YP_NODE_LOCAL_VARIABLE_WRITE_NODE,
3081
+ .type = YP_NODE_LOCAL_VARIABLE_TARGET_NODE,
2626
3082
  .location = YP_LOCATION_TOKEN_VALUE(name)
2627
3083
  },
2628
3084
  .constant_id = yp_parser_constant_id_token(parser, name),
2629
- .depth = 0,
2630
- .value = NULL,
2631
- .name_loc = YP_LOCATION_TOKEN_VALUE(name),
2632
- .operator_loc = { .start = NULL, .end = NULL }
3085
+ .depth = 0
2633
3086
  };
2634
3087
 
2635
3088
  return node;
@@ -2679,7 +3132,7 @@ yp_match_required_node_create(yp_parser_t *parser, yp_node_t *value, yp_node_t *
2679
3132
 
2680
3133
  // Allocate a new ModuleNode node.
2681
3134
  static yp_module_node_t *
2682
- yp_module_node_create(yp_parser_t *parser, yp_constant_id_list_t *locals, const yp_token_t *module_keyword, yp_node_t *constant_path, yp_node_t *body, const yp_token_t *end_keyword) {
3135
+ yp_module_node_create(yp_parser_t *parser, yp_constant_id_list_t *locals, const yp_token_t *module_keyword, yp_node_t *constant_path, const yp_token_t *name, yp_node_t *body, const yp_token_t *end_keyword) {
2683
3136
  yp_module_node_t *node = YP_ALLOC_NODE(parser, yp_module_node_t);
2684
3137
 
2685
3138
  *node = (yp_module_node_t) {
@@ -2694,9 +3147,11 @@ yp_module_node_create(yp_parser_t *parser, yp_constant_id_list_t *locals, const
2694
3147
  .module_keyword_loc = YP_LOCATION_TOKEN_VALUE(module_keyword),
2695
3148
  .constant_path = constant_path,
2696
3149
  .body = body,
2697
- .end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword)
3150
+ .end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword),
3151
+ .name = YP_EMPTY_STRING
2698
3152
  };
2699
3153
 
3154
+ yp_string_shared_init(&node->name, name->start, name->end);
2700
3155
  return node;
2701
3156
  }
2702
3157
 
@@ -2708,7 +3163,10 @@ yp_multi_write_node_create(yp_parser_t *parser, const yp_token_t *operator, yp_n
2708
3163
  *node = (yp_multi_write_node_t) {
2709
3164
  {
2710
3165
  .type = YP_NODE_MULTI_WRITE_NODE,
2711
- .location = { .start = NULL, .end = NULL },
3166
+ .location = {
3167
+ .start = lparen_loc->start,
3168
+ .end = value == NULL ? rparen_loc->end : value->location.end
3169
+ },
2712
3170
  },
2713
3171
  .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator),
2714
3172
  .value = value,
@@ -2808,28 +3266,6 @@ yp_numbered_reference_read_node_create(yp_parser_t *parser, const yp_token_t *na
2808
3266
  return node;
2809
3267
  }
2810
3268
 
2811
- // Allocate and initialize a new OperatorWriteNode.
2812
- static yp_operator_write_node_t *
2813
- yp_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
2814
- yp_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_operator_write_node_t);
2815
-
2816
- *node = (yp_operator_write_node_t) {
2817
- {
2818
- .type = YP_NODE_OPERATOR_WRITE_NODE,
2819
- .location = {
2820
- .start = target->location.start,
2821
- .end = value->location.end
2822
- },
2823
- },
2824
- .target = target,
2825
- .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
2826
- .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1),
2827
- .value = value
2828
- };
2829
-
2830
- return node;
2831
- }
2832
-
2833
3269
  // Allocate a new OptionalParameterNode node.
2834
3270
  static yp_optional_parameter_node_t *
2835
3271
  yp_optional_parameter_node_create(yp_parser_t *parser, const yp_token_t *name, const yp_token_t *operator, yp_node_t *value) {
@@ -2873,27 +3309,6 @@ yp_or_node_create(yp_parser_t *parser, yp_node_t *left, const yp_token_t *operat
2873
3309
  return node;
2874
3310
  }
2875
3311
 
2876
- // Allocate and initialize a new OrWriteNode.
2877
- static yp_or_write_node_t *
2878
- yp_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) {
2879
- yp_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_or_write_node_t);
2880
-
2881
- *node = (yp_or_write_node_t) {
2882
- {
2883
- .type = YP_NODE_OR_WRITE_NODE,
2884
- .location = {
2885
- .start = target->location.start,
2886
- .end = value->location.end
2887
- },
2888
- },
2889
- .target = target,
2890
- .operator_loc = YP_LOCATION_TOKEN_VALUE(operator),
2891
- .value = value
2892
- };
2893
-
2894
- return node;
2895
- }
2896
-
2897
3312
  // Allocate and initialize a new ParametersNode node.
2898
3313
  static yp_parameters_node_t *
2899
3314
  yp_parameters_node_create(yp_parser_t *parser) {
@@ -3600,7 +4015,7 @@ yp_symbol_node_label_create(yp_parser_t *parser, const yp_token_t *token) {
3600
4015
  assert((label.end - label.start) >= 0);
3601
4016
  yp_string_shared_init(&node->unescaped, label.start, label.end);
3602
4017
 
3603
- yp_unescape_manipulate_string(parser, &node->unescaped, YP_UNESCAPE_ALL, &parser->error_list);
4018
+ yp_unescape_manipulate_string(parser, &node->unescaped, YP_UNESCAPE_ALL);
3604
4019
  break;
3605
4020
  }
3606
4021
  case YP_TOKEN_MISSING: {
@@ -3641,20 +4056,20 @@ yp_symbol_node_label_p(yp_node_t *node) {
3641
4056
 
3642
4057
  // Convert the given StringNode node to a SymbolNode node.
3643
4058
  static yp_symbol_node_t *
3644
- yp_string_node_to_symbol_node(yp_parser_t *parser, yp_string_node_t *node) {
4059
+ yp_string_node_to_symbol_node(yp_parser_t *parser, yp_string_node_t *node, const yp_token_t *opening, const yp_token_t *closing) {
3645
4060
  yp_symbol_node_t *new_node = YP_ALLOC_NODE(parser, yp_symbol_node_t);
3646
4061
 
3647
4062
  *new_node = (yp_symbol_node_t) {
3648
4063
  {
3649
4064
  .type = YP_NODE_SYMBOL_NODE,
3650
4065
  .location = {
3651
- .start = node->base.location.start - 2,
3652
- .end = node->base.location.end + 1
4066
+ .start = opening->start,
4067
+ .end = closing->end
3653
4068
  }
3654
4069
  },
3655
- .opening_loc = node->opening_loc,
4070
+ .opening_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(opening),
3656
4071
  .value_loc = node->content_loc,
3657
- .closing_loc = node->closing_loc,
4072
+ .closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
3658
4073
  .unescaped = node->unescaped
3659
4074
  };
3660
4075
 
@@ -3793,34 +4208,43 @@ yp_unless_node_end_keyword_loc_set(yp_unless_node_t *node, const yp_token_t *end
3793
4208
 
3794
4209
  // Allocate a new UntilNode node.
3795
4210
  static yp_until_node_t *
3796
- yp_until_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *predicate, yp_statements_node_t *statements, yp_node_flags_t flags) {
4211
+ yp_until_node_create(yp_parser_t *parser, const yp_token_t *keyword, const yp_token_t *closing, yp_node_t *predicate, yp_statements_node_t *statements, yp_node_flags_t flags) {
3797
4212
  yp_until_node_t *node = YP_ALLOC_NODE(parser, yp_until_node_t);
3798
- bool has_statements = (statements != NULL) && (statements->body.size != 0);
3799
4213
 
3800
- const char *start = NULL;
3801
- if (has_statements && (keyword->start > statements->base.location.start)) {
3802
- start = statements->base.location.start;
3803
- } else {
3804
- start = keyword->start;
3805
- }
4214
+ *node = (yp_until_node_t) {
4215
+ {
4216
+ .type = YP_NODE_UNTIL_NODE,
4217
+ .flags = flags,
4218
+ .location = {
4219
+ .start = keyword->start,
4220
+ .end = closing->end,
4221
+ },
4222
+ },
4223
+ .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword),
4224
+ .closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
4225
+ .predicate = predicate,
4226
+ .statements = statements
4227
+ };
3806
4228
 
3807
- const char *end = NULL;
3808
- if (has_statements && (predicate->location.end < statements->base.location.end)) {
3809
- end = statements->base.location.end;
3810
- } else {
3811
- end = predicate->location.end;
3812
- }
4229
+ return node;
4230
+ }
4231
+
4232
+ // Allocate a new UntilNode node.
4233
+ static yp_until_node_t *
4234
+ yp_until_node_modifier_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *predicate, yp_statements_node_t *statements, yp_node_flags_t flags) {
4235
+ yp_until_node_t *node = YP_ALLOC_NODE(parser, yp_until_node_t);
3813
4236
 
3814
4237
  *node = (yp_until_node_t) {
3815
4238
  {
3816
4239
  .type = YP_NODE_UNTIL_NODE,
3817
4240
  .flags = flags,
3818
4241
  .location = {
3819
- .start = start,
3820
- .end = end,
4242
+ .start = statements->base.location.start,
4243
+ .end = predicate->location.end,
3821
4244
  },
3822
4245
  },
3823
4246
  .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword),
4247
+ .closing_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
3824
4248
  .predicate = predicate,
3825
4249
  .statements = statements
3826
4250
  };
@@ -3868,34 +4292,43 @@ yp_when_node_statements_set(yp_when_node_t *node, yp_statements_node_t *statemen
3868
4292
 
3869
4293
  // Allocate a new WhileNode node.
3870
4294
  static yp_while_node_t *
3871
- yp_while_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *predicate, yp_statements_node_t *statements, yp_node_flags_t flags) {
4295
+ yp_while_node_create(yp_parser_t *parser, const yp_token_t *keyword, const yp_token_t *closing, yp_node_t *predicate, yp_statements_node_t *statements, yp_node_flags_t flags) {
3872
4296
  yp_while_node_t *node = YP_ALLOC_NODE(parser, yp_while_node_t);
3873
4297
 
3874
- const char *start = NULL;
3875
- bool has_statements = (statements != NULL) && (statements->body.size != 0);
3876
- if (has_statements && (keyword->start > statements->base.location.start)) {
3877
- start = statements->base.location.start;
3878
- } else {
3879
- start = keyword->start;
3880
- }
4298
+ *node = (yp_while_node_t) {
4299
+ {
4300
+ .type = YP_NODE_WHILE_NODE,
4301
+ .flags = flags,
4302
+ .location = {
4303
+ .start = keyword->start,
4304
+ .end = closing->end
4305
+ },
4306
+ },
4307
+ .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword),
4308
+ .closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(closing),
4309
+ .predicate = predicate,
4310
+ .statements = statements
4311
+ };
3881
4312
 
3882
- const char *end = NULL;
3883
- if (has_statements && (predicate->location.end < statements->base.location.end)) {
3884
- end = statements->base.location.end;
3885
- } else {
3886
- end = predicate->location.end;
3887
- }
4313
+ return node;
4314
+ }
4315
+
4316
+ // Allocate a new WhileNode node.
4317
+ static yp_while_node_t *
4318
+ yp_while_node_modifier_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *predicate, yp_statements_node_t *statements, yp_node_flags_t flags) {
4319
+ yp_while_node_t *node = YP_ALLOC_NODE(parser, yp_while_node_t);
3888
4320
 
3889
4321
  *node = (yp_while_node_t) {
3890
4322
  {
3891
4323
  .type = YP_NODE_WHILE_NODE,
3892
4324
  .flags = flags,
3893
4325
  .location = {
3894
- .start = start,
3895
- .end = end,
4326
+ .start = statements->base.location.start,
4327
+ .end = predicate->location.end
3896
4328
  },
3897
4329
  },
3898
4330
  .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword),
4331
+ .closing_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE,
3899
4332
  .predicate = predicate,
3900
4333
  .statements = statements
3901
4334
  };
@@ -4003,13 +4436,15 @@ yp_parser_local_depth(yp_parser_t *parser, yp_token_t *token) {
4003
4436
  }
4004
4437
 
4005
4438
  // Add a local variable from a location to the current scope.
4006
- static void
4439
+ static yp_constant_id_t
4007
4440
  yp_parser_local_add_location(yp_parser_t *parser, const char *start, const char *end) {
4008
4441
  yp_constant_id_t constant_id = yp_parser_constant_id_location(parser, start, end);
4009
4442
 
4010
4443
  if (!yp_constant_id_list_includes(&parser->current_scope->locals, constant_id)) {
4011
4444
  yp_constant_id_list_append(&parser->current_scope->locals, constant_id);
4012
4445
  }
4446
+
4447
+ return constant_id;
4013
4448
  }
4014
4449
 
4015
4450
  // Add a local variable from a token to the current scope.
@@ -4157,27 +4592,30 @@ yp_do_loop_stack_p(yp_parser_t *parser) {
4157
4592
  /* Lexer check helpers */
4158
4593
  /******************************************************************************/
4159
4594
 
4160
- // Get the next character in the source starting from parser->current.end and
4161
- // adding the given offset. If that position is beyond the end of the source
4162
- // then return '\0'.
4595
+ // Get the next character in the source starting from +cursor+. If that position
4596
+ // is beyond the end of the source then return '\0'.
4163
4597
  static inline char
4164
- peek_at(yp_parser_t *parser, size_t offset) {
4165
- if (parser->current.end + offset < parser->end) {
4166
- return parser->current.end[offset];
4598
+ peek_at(yp_parser_t *parser, const char *cursor) {
4599
+ if (cursor < parser->end) {
4600
+ return *cursor;
4167
4601
  } else {
4168
4602
  return '\0';
4169
4603
  }
4170
4604
  }
4171
4605
 
4606
+ // Get the next character in the source starting from parser->current.end and
4607
+ // adding the given offset. If that position is beyond the end of the source
4608
+ // then return '\0'.
4609
+ static inline char
4610
+ peek_offset(yp_parser_t *parser, ptrdiff_t offset) {
4611
+ return peek_at(parser, parser->current.end + offset);
4612
+ }
4613
+
4172
4614
  // Get the next character in the source starting from parser->current.end. If
4173
4615
  // that position is beyond the end of the source then return '\0'.
4174
4616
  static inline char
4175
4617
  peek(yp_parser_t *parser) {
4176
- if (parser->current.end < parser->end) {
4177
- return *parser->current.end;
4178
- } else {
4179
- return '\0';
4180
- }
4618
+ return peek_at(parser, parser->current.end);
4181
4619
  }
4182
4620
 
4183
4621
  // Get the next string of length len in the source starting from parser->current.end.
@@ -4202,6 +4640,35 @@ match(yp_parser_t *parser, char value) {
4202
4640
  return false;
4203
4641
  }
4204
4642
 
4643
+ // Return the length of the line ending string starting at +cursor+, or 0 if it
4644
+ // is not a line ending. This function is intended to be CRLF/LF agnostic.
4645
+ static inline size_t
4646
+ match_eol_at(yp_parser_t *parser, const char *cursor) {
4647
+ if (peek_at(parser, cursor) == '\n') {
4648
+ return 1;
4649
+ }
4650
+ if (peek_at(parser, cursor) == '\r' && peek_at(parser, cursor + 1) == '\n') {
4651
+ return 2;
4652
+ }
4653
+ return 0;
4654
+ }
4655
+
4656
+ // Return the length of the line ending string starting at
4657
+ // parser->current.end + offset, or 0 if it is not a line ending. This function
4658
+ // is intended to be CRLF/LF agnostic.
4659
+ static inline size_t
4660
+ match_eol_offset(yp_parser_t *parser, ptrdiff_t offset) {
4661
+ return match_eol_at(parser, parser->current.end + offset);
4662
+ }
4663
+
4664
+ // Return the length of the line ending string starting at parser->current.end,
4665
+ // or 0 if it is not a line ending. This function is intended to be CRLF/LF
4666
+ // agnostic.
4667
+ static inline size_t
4668
+ match_eol(yp_parser_t *parser) {
4669
+ return match_eol_at(parser, parser->current.end);
4670
+ }
4671
+
4205
4672
  // Skip to the next newline character or NUL byte.
4206
4673
  static inline const char *
4207
4674
  next_newline(const char *cursor, ptrdiff_t length) {
@@ -4225,11 +4692,13 @@ parser_lex_encoding_comment_start(yp_parser_t *parser, const char *cursor, ptrdi
4225
4692
 
4226
4693
  const char *cursor_limit = cursor + length - key_length + 1;
4227
4694
  while ((cursor = yp_memchr(cursor, 'c', (size_t) (cursor_limit - cursor), parser->encoding_changed, &parser->encoding)) != NULL) {
4228
- if (
4229
- (strncmp(cursor, "coding", key_length - 1) == 0) &&
4230
- (cursor[key_length - 1] == ':' || cursor[key_length - 1] == '=')
4231
- ) {
4232
- return cursor + key_length;
4695
+ if (strncmp(cursor, "coding", key_length - 1) == 0) {
4696
+ size_t whitespace_after_coding = yp_strspn_inline_whitespace(cursor + key_length - 1, parser->end - (cursor + key_length - 1));
4697
+ size_t cur_pos = key_length + whitespace_after_coding;
4698
+
4699
+ if (cursor[cur_pos - 1] == ':' || cursor[cur_pos - 1] == '=') {
4700
+ return cursor + cur_pos;
4701
+ }
4233
4702
  }
4234
4703
 
4235
4704
  cursor++;
@@ -4485,7 +4954,7 @@ lex_optional_float_suffix(yp_parser_t *parser) {
4485
4954
  // Here we're going to attempt to parse the optional decimal portion of a
4486
4955
  // float. If it's not there, then it's okay and we'll just continue on.
4487
4956
  if (peek(parser) == '.') {
4488
- if (yp_char_is_decimal_digit(peek_at(parser, 1))) {
4957
+ if (yp_char_is_decimal_digit(peek_offset(parser, 1))) {
4489
4958
  parser->current.end += 2;
4490
4959
  parser->current.end += yp_strspn_decimal_number(parser->current.end, parser->end - parser->current.end);
4491
4960
  type = YP_TOKEN_FLOAT;
@@ -4518,7 +4987,7 @@ static yp_token_type_t
4518
4987
  lex_numeric_prefix(yp_parser_t *parser) {
4519
4988
  yp_token_type_t type = YP_TOKEN_INTEGER;
4520
4989
 
4521
- if (parser->current.end[-1] == '0') {
4990
+ if (peek_offset(parser, -1) == '0') {
4522
4991
  switch (*parser->current.end) {
4523
4992
  // 0d1111 is a decimal number
4524
4993
  case 'd':
@@ -4601,7 +5070,7 @@ lex_numeric_prefix(yp_parser_t *parser) {
4601
5070
 
4602
5071
  // If the last character that we consumed was an underscore, then this is
4603
5072
  // actually an invalid integer value, and we should return an invalid token.
4604
- if (parser->current.end[-1] == '_') {
5073
+ if (peek_offset(parser, -1) == '_') {
4605
5074
  yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Number literal cannot end with a `_`.");
4606
5075
  }
4607
5076
 
@@ -4782,7 +5251,7 @@ lex_identifier(yp_parser_t *parser, bool previous_command_start) {
4782
5251
 
4783
5252
  if (
4784
5253
  ((lex_state_p(parser, YP_LEX_STATE_LABEL | YP_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
4785
- (peek(parser) == ':') && (peek_at(parser, 1) != ':')
5254
+ (peek(parser) == ':') && (peek_offset(parser, 1) != ':')
4786
5255
  ) {
4787
5256
  // If we're in a position where we can accept a : at the end of an
4788
5257
  // identifier, then we'll optionally accept it.
@@ -4798,7 +5267,7 @@ lex_identifier(yp_parser_t *parser, bool previous_command_start) {
4798
5267
  }
4799
5268
 
4800
5269
  return YP_TOKEN_IDENTIFIER;
4801
- } else if (lex_state_p(parser, YP_LEX_STATE_FNAME) && peek_at(parser, 1) != '~' && peek_at(parser, 1) != '>' && (peek_at(parser, 1) != '=' || peek_at(parser, 2) == '>') && match(parser, '=')) {
5270
+ } else if (lex_state_p(parser, YP_LEX_STATE_FNAME) && peek_offset(parser, 1) != '~' && peek_offset(parser, 1) != '>' && (peek_offset(parser, 1) != '=' || peek_offset(parser, 2) == '>') && match(parser, '=')) {
4802
5271
  // If we're in a position where we can accept a = at the end of an
4803
5272
  // identifier, then we'll optionally accept it.
4804
5273
  return YP_TOKEN_IDENTIFIER;
@@ -4806,7 +5275,7 @@ lex_identifier(yp_parser_t *parser, bool previous_command_start) {
4806
5275
 
4807
5276
  if (
4808
5277
  ((lex_state_p(parser, YP_LEX_STATE_LABEL | YP_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) &&
4809
- peek(parser) == ':' && peek_at(parser, 1) != ':'
5278
+ peek(parser) == ':' && peek_offset(parser, 1) != ':'
4810
5279
  ) {
4811
5280
  // If we're in a position where we can accept a : at the end of an
4812
5281
  // identifier, then we'll optionally accept it.
@@ -5074,7 +5543,7 @@ lex_question_mark(yp_parser_t *parser) {
5074
5543
 
5075
5544
  if (parser->current.start[1] == '\\') {
5076
5545
  lex_state_set(parser, YP_LEX_STATE_END);
5077
- parser->current.end += yp_unescape_calculate_difference(parser->current.start + 1, parser->end, YP_UNESCAPE_ALL, true, &parser->error_list);
5546
+ parser->current.end += yp_unescape_calculate_difference(parser, parser->current.start + 1, YP_UNESCAPE_ALL, true);
5078
5547
  return YP_TOKEN_CHARACTER_LITERAL;
5079
5548
  } else {
5080
5549
  size_t encoding_width = parser->encoding.char_width(parser->current.end, parser->end - parser->current.end);
@@ -5083,7 +5552,7 @@ lex_question_mark(yp_parser_t *parser) {
5083
5552
  // an underscore. We check for this case
5084
5553
  if (
5085
5554
  !(parser->encoding.alnum_char(parser->current.end, parser->end - parser->current.end) ||
5086
- *parser->current.end == '_') ||
5555
+ peek(parser) == '_') ||
5087
5556
  (
5088
5557
  (parser->current.end + encoding_width >= parser->end) ||
5089
5558
  !char_is_identifier(parser, parser->current.end + encoding_width)
@@ -5299,30 +5768,22 @@ parser_lex(yp_parser_t *parser) {
5299
5768
  space_seen = true;
5300
5769
  break;
5301
5770
  case '\r':
5302
- if (peek_at(parser, 1) == '\n') {
5771
+ if (match_eol_offset(parser, 1)) {
5303
5772
  chomping = false;
5304
5773
  } else {
5305
5774
  parser->current.end++;
5306
5775
  space_seen = true;
5307
5776
  }
5308
5777
  break;
5309
- case '\\':
5310
- if (peek_at(parser, 1) == '\n') {
5311
- if (parser->heredoc_end) {
5312
- parser->current.end = parser->heredoc_end;
5313
- parser->heredoc_end = NULL;
5314
- } else {
5315
- yp_newline_list_append(&parser->newline_list, parser->current.end + 1);
5316
- parser->current.end += 2;
5317
- space_seen = true;
5318
- }
5319
- } else if (peek_at(parser, 1) == '\r' && peek_at(parser, 2) == '\n') {
5778
+ case '\\': {
5779
+ size_t eol_length = match_eol_offset(parser, 1);
5780
+ if (eol_length) {
5320
5781
  if (parser->heredoc_end) {
5321
5782
  parser->current.end = parser->heredoc_end;
5322
5783
  parser->heredoc_end = NULL;
5323
5784
  } else {
5324
- yp_newline_list_append(&parser->newline_list, parser->current.end + 2);
5325
- parser->current.end += 3;
5785
+ parser->current.end += eol_length + 1;
5786
+ yp_newline_list_append(&parser->newline_list, parser->current.end - 1);
5326
5787
  space_seen = true;
5327
5788
  }
5328
5789
  } else if (yp_char_is_inline_whitespace(*parser->current.end)) {
@@ -5330,7 +5791,9 @@ parser_lex(yp_parser_t *parser) {
5330
5791
  } else {
5331
5792
  chomping = false;
5332
5793
  }
5794
+
5333
5795
  break;
5796
+ }
5334
5797
  default:
5335
5798
  chomping = false;
5336
5799
  break;
@@ -5340,13 +5803,14 @@ parser_lex(yp_parser_t *parser) {
5340
5803
  // Next, we'll set to start of this token to be the current end.
5341
5804
  parser->current.start = parser->current.end;
5342
5805
 
5343
- // We'll check if we're at the end of the file. If we are, then we need to
5344
- // return the EOF token.
5806
+ // We'll check if we're at the end of the file. If we are, then we
5807
+ // need to return the EOF token.
5345
5808
  if (parser->current.end >= parser->end) {
5346
5809
  LEX(YP_TOKEN_EOF);
5347
5810
  }
5348
5811
 
5349
- // Finally, we'll check the current character to determine the next token.
5812
+ // Finally, we'll check the current character to determine the next
5813
+ // token.
5350
5814
  switch (*parser->current.end++) {
5351
5815
  case '\0': // NUL or end of script
5352
5816
  case '\004': // ^D
@@ -5356,16 +5820,14 @@ parser_lex(yp_parser_t *parser) {
5356
5820
 
5357
5821
  case '#': { // comments
5358
5822
  const char *ending = next_newline(parser->current.end, parser->end - parser->current.end);
5359
- while (ending && ending < parser->end && *ending != '\n') {
5360
- ending = next_newline(ending + 1, parser->end - ending);
5361
- }
5362
5823
 
5363
5824
  parser->current.end = ending == NULL ? parser->end : ending + 1;
5364
5825
  parser->current.type = YP_TOKEN_COMMENT;
5365
5826
  parser_lex_callback(parser);
5366
5827
 
5367
- // If we found a comment while lexing, then we're going to add it to the
5368
- // list of comments in the file and keep lexing.
5828
+ // If we found a comment while lexing, then we're going to
5829
+ // add it to the list of comments in the file and keep
5830
+ // lexing.
5369
5831
  yp_comment_t *comment = parser_comment(parser, YP_COMMENT_INLINE);
5370
5832
  yp_list_append(&parser->comment_list, (yp_list_node_t *) comment);
5371
5833
 
@@ -5376,21 +5838,29 @@ parser_lex(yp_parser_t *parser) {
5376
5838
  lexed_comment = true;
5377
5839
  }
5378
5840
  /* fallthrough */
5379
- case '\r': {
5380
- // The only way you can have carriage returns in this particular loop
5381
- // is if you have a carriage return followed by a newline. In that
5382
- // case we'll just skip over the carriage return and continue lexing,
5383
- // in order to make it so that the newline token encapsulates both the
5384
- // carriage return and the newline. Note that we need to check that
5385
- // we haven't already lexed a comment here because that falls through
5386
- // into here as well.
5387
- if (!lexed_comment) parser->current.end++;
5388
- }
5389
- /* fallthrough */
5841
+ case '\r':
5390
5842
  case '\n': {
5391
- if (parser->heredoc_end == NULL) {
5392
- yp_newline_list_append(&parser->newline_list, parser->current.end - 1);
5393
- } else {
5843
+ size_t eol_length = match_eol_at(parser, parser->current.end - 1);
5844
+ if (eol_length) {
5845
+ // The only way you can have carriage returns in this
5846
+ // particular loop is if you have a carriage return
5847
+ // followed by a newline. In that case we'll just skip
5848
+ // over the carriage return and continue lexing, in
5849
+ // order to make it so that the newline token
5850
+ // encapsulates both the carriage return and the
5851
+ // newline. Note that we need to check that we haven't
5852
+ // already lexed a comment here because that falls
5853
+ // through into here as well.
5854
+ if (!lexed_comment) {
5855
+ parser->current.end += eol_length - 1; // skip CR
5856
+ }
5857
+
5858
+ if (parser->heredoc_end == NULL) {
5859
+ yp_newline_list_append(&parser->newline_list, parser->current.end - 1);
5860
+ }
5861
+ }
5862
+
5863
+ if (parser->heredoc_end) {
5394
5864
  parser_flush_heredoc_end(parser);
5395
5865
  }
5396
5866
 
@@ -5446,7 +5916,13 @@ parser_lex(yp_parser_t *parser) {
5446
5916
 
5447
5917
  // If the lex state was ignored, or we hit a '.' or a '&.',
5448
5918
  // we will lex the ignored newline
5449
- if (lex_state_ignored_p(parser) || (following && ((following[0] == '.') || (following + 1 < parser->end && following[0] == '&' && following[1] == '.')))) {
5919
+ if (
5920
+ lex_state_ignored_p(parser) ||
5921
+ (following && (
5922
+ (peek_at(parser, following) == '.') ||
5923
+ (peek_at(parser, following) == '&' && peek_at(parser, following + 1) == '.')
5924
+ ))
5925
+ ) {
5450
5926
  if (!lexed_comment) parser_lex_ignored_newline(parser);
5451
5927
  lexed_comment = false;
5452
5928
  goto lex_next_token;
@@ -5459,7 +5935,7 @@ parser_lex(yp_parser_t *parser) {
5459
5935
  // To match ripper, we need to emit an ignored newline even though
5460
5936
  // its a real newline in the case that we have a beginless range
5461
5937
  // on a subsequent line.
5462
- if ((next_content + 1 < parser->end) && (next_content[1] == '.')) {
5938
+ if (peek_at(parser, next_content + 1) == '.') {
5463
5939
  if (!lexed_comment) parser_lex_ignored_newline(parser);
5464
5940
  lex_state_set(parser, YP_LEX_STATE_BEG);
5465
5941
  parser->command_start = true;
@@ -5477,7 +5953,7 @@ parser_lex(yp_parser_t *parser) {
5477
5953
 
5478
5954
  // If we hit a &. after a newline, then we're in a call chain and
5479
5955
  // we need to return the call operator.
5480
- if (next_content + 1 < parser->end && next_content[0] == '&' && next_content[1] == '.') {
5956
+ if (peek_at(parser, next_content) == '&' && peek_at(parser, next_content + 1) == '.') {
5481
5957
  if (!lexed_comment) parser_lex_ignored_newline(parser);
5482
5958
  lex_state_set(parser, YP_LEX_STATE_DOT);
5483
5959
  parser->current.start = next_content;
@@ -5674,7 +6150,7 @@ parser_lex(yp_parser_t *parser) {
5674
6150
 
5675
6151
  // = => =~ == === =begin
5676
6152
  case '=':
5677
- if (current_token_starts_line(parser) && strncmp(peek_string(parser, 5), "begin", 5) == 0 && yp_char_is_whitespace(peek_at(parser, 5))) {
6153
+ if (current_token_starts_line(parser) && strncmp(peek_string(parser, 5), "begin", 5) == 0 && yp_char_is_whitespace(peek_offset(parser, 5))) {
5678
6154
  yp_token_type_t type = lex_embdoc(parser);
5679
6155
 
5680
6156
  if (type == YP_TOKEN_EOF) {
@@ -6098,13 +6574,13 @@ parser_lex(yp_parser_t *parser) {
6098
6574
  LEX(YP_TOKEN_COLON_COLON);
6099
6575
  }
6100
6576
 
6101
- if (lex_state_end_p(parser) || yp_char_is_whitespace(*parser->current.end) || (*parser->current.end == '#')) {
6577
+ if (lex_state_end_p(parser) || yp_char_is_whitespace(*parser->current.end) || peek(parser) == '#') {
6102
6578
  lex_state_set(parser, YP_LEX_STATE_BEG);
6103
6579
  LEX(YP_TOKEN_COLON);
6104
6580
  }
6105
6581
 
6106
- if ((*parser->current.end == '"') || (*parser->current.end == '\'')) {
6107
- lex_mode_push_string(parser, *parser->current.end == '"', false, '\0', *parser->current.end);
6582
+ if (peek(parser) == '"' || peek(parser) == '\'') {
6583
+ lex_mode_push_string(parser, peek(parser) == '"', false, '\0', *parser->current.end);
6108
6584
  parser->current.end++;
6109
6585
  }
6110
6586
 
@@ -6173,25 +6649,26 @@ parser_lex(yp_parser_t *parser) {
6173
6649
  }
6174
6650
  else if(
6175
6651
  lex_state_beg_p(parser) ||
6176
- (lex_state_p(parser, YP_LEX_STATE_FITEM) && (*parser->current.end == 's')) ||
6652
+ (lex_state_p(parser, YP_LEX_STATE_FITEM) && (peek(parser) == 's')) ||
6177
6653
  lex_state_spcarg_p(parser, space_seen)
6178
6654
  ) {
6179
6655
  if (!parser->encoding.alnum_char(parser->current.end, parser->end - parser->current.end)) {
6180
6656
  lex_mode_push_string(parser, true, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end));
6181
6657
 
6182
- if (*parser->current.end == '\r') {
6658
+ size_t eol_length = match_eol(parser);
6659
+ if (eol_length) {
6660
+ parser->current.end += eol_length;
6661
+ yp_newline_list_append(&parser->newline_list, parser->current.end - 1);
6662
+ } else {
6183
6663
  parser->current.end++;
6184
6664
  }
6185
6665
 
6186
- if (*parser->current.end == '\n') {
6187
- yp_newline_list_append(&parser->newline_list, parser->current.end);
6666
+ if (parser->current.end < parser->end) {
6667
+ LEX(YP_TOKEN_STRING_BEGIN);
6188
6668
  }
6189
-
6190
- parser->current.end++;
6191
- LEX(YP_TOKEN_STRING_BEGIN);
6192
6669
  }
6193
6670
 
6194
- switch (*parser->current.end) {
6671
+ switch (peek(parser)) {
6195
6672
  case 'i': {
6196
6673
  parser->current.end++;
6197
6674
 
@@ -6215,6 +6692,7 @@ parser_lex(yp_parser_t *parser) {
6215
6692
 
6216
6693
  if (parser->current.end < parser->end) {
6217
6694
  lex_mode_push_regexp(parser, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end));
6695
+ yp_newline_list_check_append(&parser->newline_list, parser->current.end);
6218
6696
  parser->current.end++;
6219
6697
  }
6220
6698
 
@@ -6225,6 +6703,7 @@ parser_lex(yp_parser_t *parser) {
6225
6703
 
6226
6704
  if (parser->current.end < parser->end) {
6227
6705
  lex_mode_push_string(parser, false, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end));
6706
+ yp_newline_list_check_append(&parser->newline_list, parser->current.end);
6228
6707
  parser->current.end++;
6229
6708
  }
6230
6709
 
@@ -6235,6 +6714,7 @@ parser_lex(yp_parser_t *parser) {
6235
6714
 
6236
6715
  if (parser->current.end < parser->end) {
6237
6716
  lex_mode_push_string(parser, true, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end));
6717
+ yp_newline_list_check_append(&parser->newline_list, parser->current.end);
6238
6718
  parser->current.end++;
6239
6719
  }
6240
6720
 
@@ -6284,7 +6764,7 @@ parser_lex(yp_parser_t *parser) {
6284
6764
  // unparseable. In this case we'll just drop it from the parser
6285
6765
  // and skip past it and hope that the next token is something
6286
6766
  // that we can parse.
6287
- yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "invalid %% token");
6767
+ yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid %% token");
6288
6768
  goto lex_next_token;
6289
6769
  }
6290
6770
  }
@@ -6336,8 +6816,9 @@ parser_lex(yp_parser_t *parser) {
6336
6816
  ((parser->current.end - parser->current.start) == 7) &&
6337
6817
  current_token_starts_line(parser) &&
6338
6818
  (strncmp(parser->current.start, "__END__", 7) == 0) &&
6339
- (parser->current.end == parser->end || *parser->current.end == '\n' || (*parser->current.end == '\r' && parser->current.end[1] == '\n'))
6340
- ) {
6819
+ (parser->current.end == parser->end || match_eol(parser))
6820
+ )
6821
+ {
6341
6822
  parser->current.end = parser->end;
6342
6823
  parser->current.type = YP_TOKEN___END__;
6343
6824
  parser_lex_callback(parser);
@@ -6394,7 +6875,7 @@ parser_lex(yp_parser_t *parser) {
6394
6875
 
6395
6876
  if ((whitespace = yp_strspn_whitespace_newlines(parser->current.end, parser->end - parser->current.end, &parser->newline_list, should_stop)) > 0) {
6396
6877
  parser->current.end += whitespace;
6397
- if (parser->current.end[-1] == '\n') {
6878
+ if (peek_offset(parser, -1) == '\n') {
6398
6879
  // mutates next_start
6399
6880
  parser_flush_heredoc_end(parser);
6400
6881
  }
@@ -6458,13 +6939,11 @@ parser_lex(yp_parser_t *parser) {
6458
6939
  // and find the next breakpoint.
6459
6940
  if (*breakpoint == '\\') {
6460
6941
  yp_unescape_type_t unescape_type = lex_mode->as.list.interpolation ? YP_UNESCAPE_ALL : YP_UNESCAPE_MINIMAL;
6461
- size_t difference = yp_unescape_calculate_difference(breakpoint, parser->end, unescape_type, false, &parser->error_list);
6942
+ size_t difference = yp_unescape_calculate_difference(parser, breakpoint, unescape_type, false);
6462
6943
 
6463
6944
  // If the result is an escaped newline, then we need to
6464
6945
  // track that newline.
6465
- if (breakpoint[difference - 1] == '\n') {
6466
- yp_newline_list_append(&parser->newline_list, breakpoint + difference - 1);
6467
- }
6946
+ yp_newline_list_check_append(&parser->newline_list, breakpoint + difference - 1);
6468
6947
 
6469
6948
  breakpoint = yp_strpbrk(parser, breakpoint + difference, breakpoints, parser->end - (breakpoint + difference));
6470
6949
  continue;
@@ -6499,7 +6978,13 @@ parser_lex(yp_parser_t *parser) {
6499
6978
 
6500
6979
  case YP_LEX_REGEXP: {
6501
6980
  // First, we'll set to start of this token to be the current end.
6502
- parser->current.start = parser->current.end;
6981
+ if (parser->next_start == NULL) {
6982
+ parser->current.start = parser->current.end;
6983
+ } else {
6984
+ parser->current.start = parser->next_start;
6985
+ parser->current.end = parser->next_start;
6986
+ parser->next_start = NULL;
6987
+ }
6503
6988
 
6504
6989
  // We'll check if we're at the end of the file. If we are, then we need to
6505
6990
  // return the EOF token.
@@ -6526,7 +7011,16 @@ parser_lex(yp_parser_t *parser) {
6526
7011
  // If we've hit a newline, then we need to track that in the
6527
7012
  // list of newlines.
6528
7013
  if (*breakpoint == '\n') {
6529
- yp_newline_list_append(&parser->newline_list, breakpoint);
7014
+ // For the special case of a newline-terminated regular expression, we will pass
7015
+ // through this branch twice -- once with YP_TOKEN_REGEXP_BEGIN and then again
7016
+ // with YP_TOKEN_STRING_CONTENT. Let's avoid tracking the newline twice, by
7017
+ // tracking it only in the REGEXP_BEGIN case.
7018
+ if (
7019
+ !(lex_mode->as.regexp.terminator == '\n' && parser->current.type != YP_TOKEN_REGEXP_BEGIN)
7020
+ && parser->heredoc_end == NULL
7021
+ ) {
7022
+ yp_newline_list_append(&parser->newline_list, breakpoint);
7023
+ }
6530
7024
 
6531
7025
  if (lex_mode->as.regexp.terminator != '\n') {
6532
7026
  // If the terminator is not a newline, then we can set
@@ -6567,12 +7061,20 @@ parser_lex(yp_parser_t *parser) {
6567
7061
  // literally. In this case we'll skip past the next character
6568
7062
  // and find the next breakpoint.
6569
7063
  if (*breakpoint == '\\') {
6570
- size_t difference = yp_unescape_calculate_difference(breakpoint, parser->end, YP_UNESCAPE_ALL, false, &parser->error_list);
6571
-
6572
- // If the result is an escaped newline, then we need to
6573
- // track that newline.
6574
- if (breakpoint[difference - 1] == '\n') {
6575
- yp_newline_list_append(&parser->newline_list, breakpoint + difference - 1);
7064
+ size_t difference = yp_unescape_calculate_difference(parser, breakpoint, YP_UNESCAPE_ALL, false);
7065
+
7066
+ // If the result is an escaped newline ...
7067
+ if (*(breakpoint + difference - 1) == '\n') {
7068
+ if (parser->heredoc_end) {
7069
+ // ... if we are on the same line as a heredoc, flush the heredoc and
7070
+ // continue parsing after heredoc_end.
7071
+ parser->current.end = breakpoint + difference;
7072
+ parser_flush_heredoc_end(parser);
7073
+ LEX(YP_TOKEN_STRING_CONTENT);
7074
+ } else {
7075
+ // ... else track the newline.
7076
+ yp_newline_list_append(&parser->newline_list, breakpoint + difference - 1);
7077
+ }
6576
7078
  }
6577
7079
 
6578
7080
  breakpoint = yp_strpbrk(parser, breakpoint + difference, breakpoints, parser->end - (breakpoint + difference));
@@ -6660,21 +7162,18 @@ parser_lex(yp_parser_t *parser) {
6660
7162
 
6661
7163
  // Otherwise we need to switch back to the parent lex mode and
6662
7164
  // return the end of the string.
6663
- if (*parser->current.end == '\r' && parser->current.end + 1 < parser->end && parser->current.end[1] == '\n') {
6664
- parser->current.end = breakpoint + 2;
6665
- yp_newline_list_append(&parser->newline_list, breakpoint + 1);
7165
+ size_t eol_length = match_eol_at(parser, breakpoint);
7166
+ if (eol_length) {
7167
+ parser->current.end = breakpoint + eol_length;
7168
+ yp_newline_list_append(&parser->newline_list, parser->current.end - 1);
6666
7169
  } else {
6667
- if (*parser->current.end == '\n') {
6668
- yp_newline_list_append(&parser->newline_list, parser->current.end);
6669
- }
6670
-
6671
7170
  parser->current.end = breakpoint + 1;
6672
7171
  }
6673
7172
 
6674
7173
  if (
6675
7174
  parser->lex_modes.current->as.string.label_allowed &&
6676
7175
  (peek(parser) == ':') &&
6677
- (peek_at(parser, 1) != ':')
7176
+ (peek_offset(parser, 1) != ':')
6678
7177
  ) {
6679
7178
  parser->current.end++;
6680
7179
  lex_state_set(parser, YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED);
@@ -6712,12 +7211,20 @@ parser_lex(yp_parser_t *parser) {
6712
7211
  // literally. In this case we'll skip past the next character and
6713
7212
  // find the next breakpoint.
6714
7213
  yp_unescape_type_t unescape_type = parser->lex_modes.current->as.string.interpolation ? YP_UNESCAPE_ALL : YP_UNESCAPE_MINIMAL;
6715
- size_t difference = yp_unescape_calculate_difference(breakpoint, parser->end, unescape_type, false, &parser->error_list);
7214
+ size_t difference = yp_unescape_calculate_difference(parser, breakpoint, unescape_type, false);
6716
7215
 
6717
- // If the result is an escaped newline, then we need to
6718
- // track that newline.
6719
- if (breakpoint[difference - 1] == '\n') {
6720
- yp_newline_list_append(&parser->newline_list, breakpoint + difference - 1);
7216
+ // If the result is an escaped newline ...
7217
+ if (*(breakpoint + difference - 1) == '\n') {
7218
+ if (parser->heredoc_end) {
7219
+ // ... if we are on the same line as a heredoc, flush the heredoc and
7220
+ // continue parsing after heredoc_end.
7221
+ parser->current.end = breakpoint + difference;
7222
+ parser_flush_heredoc_end(parser);
7223
+ LEX(YP_TOKEN_STRING_CONTENT);
7224
+ } else {
7225
+ // ... else track the newline.
7226
+ yp_newline_list_append(&parser->newline_list, breakpoint + difference - 1);
7227
+ }
6721
7228
  }
6722
7229
 
6723
7230
  breakpoint = yp_strpbrk(parser, breakpoint + difference, breakpoints, parser->end - (breakpoint + difference));
@@ -6770,7 +7277,7 @@ parser_lex(yp_parser_t *parser) {
6770
7277
 
6771
7278
  // If we are immediately following a newline and we have hit the
6772
7279
  // terminator, then we need to return the ending of the heredoc.
6773
- if (parser->current.start[-1] == '\n') {
7280
+ if (current_token_starts_line(parser)) {
6774
7281
  const char *start = parser->current.start;
6775
7282
  if (parser->lex_modes.current->as.heredoc.indent != YP_HEREDOC_INDENT_NONE) {
6776
7283
  start += yp_strspn_inline_whitespace(start, parser->end - start);
@@ -6780,12 +7287,10 @@ parser_lex(yp_parser_t *parser) {
6780
7287
  bool matched = true;
6781
7288
  bool at_end = false;
6782
7289
 
6783
- if ((start + ident_length < parser->end) && (start[ident_length] == '\n')) {
6784
- parser->current.end = start + ident_length + 1;
6785
- yp_newline_list_append(&parser->newline_list, start + ident_length);
6786
- } else if ((start + ident_length + 1 < parser->end) && (start[ident_length] == '\r') && (start[ident_length + 1] == '\n')) {
6787
- parser->current.end = start + ident_length + 2;
6788
- yp_newline_list_append(&parser->newline_list, start + ident_length + 1);
7290
+ size_t eol_length = match_eol_at(parser, start + ident_length);
7291
+ if (eol_length) {
7292
+ parser->current.end = start + ident_length + eol_length;
7293
+ yp_newline_list_append(&parser->newline_list, parser->current.end - 1);
6789
7294
  } else if (parser->end == (start + ident_length)) {
6790
7295
  parser->current.end = start + ident_length;
6791
7296
  at_end = true;
@@ -6850,19 +7355,10 @@ parser_lex(yp_parser_t *parser) {
6850
7355
  (start + ident_length <= parser->end) &&
6851
7356
  (strncmp(start, ident_start, ident_length) == 0)
6852
7357
  ) {
6853
- // Heredoc terminators must be followed by a newline or EOF to be valid.
6854
- if (start + ident_length == parser->end || start[ident_length] == '\n') {
6855
- parser->current.end = breakpoint + 1;
6856
- LEX(YP_TOKEN_STRING_CONTENT);
6857
- }
6858
-
6859
- // They can also be followed by a carriage return and then a
6860
- // newline. Be sure here that we don't accidentally read off the
6861
- // end.
7358
+ // Heredoc terminators must be followed by a newline, CRLF, or EOF to be valid.
6862
7359
  if (
6863
- (start + ident_length + 1 < parser->end) &&
6864
- (start[ident_length] == '\r') &&
6865
- (start[ident_length + 1] == '\n')
7360
+ start + ident_length == parser->end ||
7361
+ match_eol_at(parser, start + ident_length)
6866
7362
  ) {
6867
7363
  parser->current.end = breakpoint + 1;
6868
7364
  LEX(YP_TOKEN_STRING_CONTENT);
@@ -6881,17 +7377,14 @@ parser_lex(yp_parser_t *parser) {
6881
7377
  // stop looping before the newline and not after the
6882
7378
  // newline so that we can still potentially find the
6883
7379
  // terminator of the heredoc.
6884
- if (breakpoint + 1 < parser->end && breakpoint[1] == '\n') {
6885
- breakpoint++;
6886
- } else if (breakpoint + 2 < parser->end && breakpoint[1] == '\r' && breakpoint[2] == '\n') {
6887
- breakpoint += 2;
7380
+ size_t eol_length = match_eol_at(parser, breakpoint + 1);
7381
+ if (eol_length) {
7382
+ breakpoint += eol_length;
6888
7383
  } else {
6889
7384
  yp_unescape_type_t unescape_type = (quote == YP_HEREDOC_QUOTE_SINGLE) ? YP_UNESCAPE_MINIMAL : YP_UNESCAPE_ALL;
6890
- size_t difference = yp_unescape_calculate_difference(breakpoint, parser->end, unescape_type, false, &parser->error_list);
7385
+ size_t difference = yp_unescape_calculate_difference(parser, breakpoint, unescape_type, false);
6891
7386
 
6892
- if (breakpoint[difference - 1] == '\n') {
6893
- yp_newline_list_append(&parser->newline_list, breakpoint + difference - 1);
6894
- }
7387
+ yp_newline_list_check_append(&parser->newline_list, breakpoint + difference - 1);
6895
7388
 
6896
7389
  breakpoint = yp_strpbrk(parser, breakpoint + difference, breakpoints, parser->end - (breakpoint + difference));
6897
7390
  }
@@ -6945,7 +7438,7 @@ yp_regular_expression_node_create_and_unescape(yp_parser_t *parser, const yp_tok
6945
7438
  assert((content->end - content->start) >= 0);
6946
7439
  yp_string_shared_init(&node->unescaped, content->start, content->end);
6947
7440
 
6948
- yp_unescape_manipulate_string(parser, &node->unescaped, unescape_type, &parser->error_list);
7441
+ yp_unescape_manipulate_string(parser, &node->unescaped, unescape_type);
6949
7442
  return node;
6950
7443
  }
6951
7444
 
@@ -6956,7 +7449,7 @@ yp_symbol_node_create_and_unescape(yp_parser_t *parser, const yp_token_t *openin
6956
7449
  assert((content->end - content->start) >= 0);
6957
7450
  yp_string_shared_init(&node->unescaped, content->start, content->end);
6958
7451
 
6959
- yp_unescape_manipulate_string(parser, &node->unescaped, unescape_type, &parser->error_list);
7452
+ yp_unescape_manipulate_string(parser, &node->unescaped, unescape_type);
6960
7453
  return node;
6961
7454
  }
6962
7455
 
@@ -6967,7 +7460,7 @@ yp_string_node_create_and_unescape(yp_parser_t *parser, const yp_token_t *openin
6967
7460
  assert((content->end - content->start) >= 0);
6968
7461
  yp_string_shared_init(&node->unescaped, content->start, content->end);
6969
7462
 
6970
- yp_unescape_manipulate_string(parser, &node->unescaped, unescape_type, &parser->error_list);
7463
+ yp_unescape_manipulate_string(parser, &node->unescaped, unescape_type);
6971
7464
  return node;
6972
7465
  }
6973
7466
 
@@ -6978,7 +7471,7 @@ yp_xstring_node_create_and_unescape(yp_parser_t *parser, const yp_token_t *openi
6978
7471
  assert((content->end - content->start) >= 0);
6979
7472
  yp_string_shared_init(&node->unescaped, content->start, content->end);
6980
7473
 
6981
- yp_unescape_manipulate_string(parser, &node->unescaped, YP_UNESCAPE_ALL, &parser->error_list);
7474
+ yp_unescape_manipulate_string(parser, &node->unescaped, YP_UNESCAPE_ALL);
6982
7475
  return node;
6983
7476
  }
6984
7477
 
@@ -7315,22 +7808,156 @@ token_begins_expression_p(yp_token_type_t type) {
7315
7808
  }
7316
7809
  }
7317
7810
 
7318
- // Parse an expression with the given binding power that may be optionally
7319
- // prefixed by the * operator.
7320
- static yp_node_t *
7321
- parse_starred_expression(yp_parser_t *parser, yp_binding_power_t binding_power, const char *message) {
7322
- if (accept(parser, YP_TOKEN_USTAR)) {
7323
- yp_token_t operator = parser->previous;
7324
- yp_node_t *expression = parse_expression(parser, binding_power, "Expected expression after `*'.");
7325
- return (yp_node_t *) yp_splat_node_create(parser, &operator, expression);
7326
- }
7811
+ // Parse an expression with the given binding power that may be optionally
7812
+ // prefixed by the * operator.
7813
+ static yp_node_t *
7814
+ parse_starred_expression(yp_parser_t *parser, yp_binding_power_t binding_power, const char *message) {
7815
+ if (accept(parser, YP_TOKEN_USTAR)) {
7816
+ yp_token_t operator = parser->previous;
7817
+ yp_node_t *expression = parse_expression(parser, binding_power, "Expected expression after `*'.");
7818
+ return (yp_node_t *) yp_splat_node_create(parser, &operator, expression);
7819
+ }
7820
+
7821
+ return parse_expression(parser, binding_power, message);
7822
+ }
7823
+
7824
+ // Convert the given node into a valid target node.
7825
+ static yp_node_t *
7826
+ parse_target(yp_parser_t *parser, yp_node_t *target) {
7827
+ switch (YP_NODE_TYPE(target)) {
7828
+ case YP_NODE_MISSING_NODE:
7829
+ return target;
7830
+ case YP_NODE_CLASS_VARIABLE_READ_NODE:
7831
+ assert(sizeof(yp_class_variable_target_node_t) == sizeof(yp_class_variable_read_node_t));
7832
+ target->type = YP_NODE_CLASS_VARIABLE_TARGET_NODE;
7833
+ return target;
7834
+ case YP_NODE_CONSTANT_PATH_NODE:
7835
+ assert(sizeof(yp_constant_path_target_node_t) == sizeof(yp_constant_path_node_t));
7836
+ target->type = YP_NODE_CONSTANT_PATH_TARGET_NODE;
7837
+ return target;
7838
+ case YP_NODE_CONSTANT_READ_NODE:
7839
+ assert(sizeof(yp_constant_target_node_t) == sizeof(yp_constant_read_node_t));
7840
+ target->type = YP_NODE_CONSTANT_TARGET_NODE;
7841
+ return target;
7842
+ case YP_NODE_BACK_REFERENCE_READ_NODE:
7843
+ assert(sizeof(yp_global_variable_target_node_t) == sizeof(yp_back_reference_read_node_t));
7844
+ /* fallthrough */
7845
+ case YP_NODE_NUMBERED_REFERENCE_READ_NODE:
7846
+ assert(sizeof(yp_global_variable_target_node_t) == sizeof(yp_numbered_reference_read_node_t));
7847
+ yp_diagnostic_list_append(&parser->error_list, target->location.start, target->location.end, "Can't set variable");
7848
+ /* fallthrough */
7849
+ case YP_NODE_GLOBAL_VARIABLE_READ_NODE:
7850
+ assert(sizeof(yp_global_variable_target_node_t) == sizeof(yp_global_variable_read_node_t));
7851
+ target->type = YP_NODE_GLOBAL_VARIABLE_TARGET_NODE;
7852
+ return target;
7853
+ case YP_NODE_LOCAL_VARIABLE_READ_NODE:
7854
+ assert(sizeof(yp_local_variable_target_node_t) == sizeof(yp_local_variable_read_node_t));
7855
+ target->type = YP_NODE_LOCAL_VARIABLE_TARGET_NODE;
7856
+ return target;
7857
+ case YP_NODE_INSTANCE_VARIABLE_READ_NODE:
7858
+ assert(sizeof(yp_instance_variable_target_node_t) == sizeof(yp_instance_variable_read_node_t));
7859
+ target->type = YP_NODE_INSTANCE_VARIABLE_TARGET_NODE;
7860
+ return target;
7861
+ case YP_NODE_MULTI_WRITE_NODE:
7862
+ return target;
7863
+ case YP_NODE_SPLAT_NODE: {
7864
+ yp_splat_node_t *splat = (yp_splat_node_t *) target;
7865
+
7866
+ if (splat->expression != NULL) {
7867
+ splat->expression = parse_target(parser, splat->expression);
7868
+ }
7327
7869
 
7328
- return parse_expression(parser, binding_power, message);
7870
+ yp_token_t operator = not_provided(parser);
7871
+ yp_location_t location = { .start = NULL, .end = NULL };
7872
+
7873
+ yp_multi_write_node_t *multi_write = yp_multi_write_node_create(parser, &operator, NULL, &location, &location);
7874
+ yp_multi_write_node_targets_append(multi_write, (yp_node_t *) splat);
7875
+
7876
+ return (yp_node_t *) multi_write;
7877
+ }
7878
+ case YP_NODE_CALL_NODE: {
7879
+ yp_call_node_t *call = (yp_call_node_t *) target;
7880
+
7881
+ // If we have no arguments to the call node and we need this to be a
7882
+ // target then this is either a method call or a local variable write.
7883
+ if (
7884
+ (call->opening_loc.start == NULL) &&
7885
+ (call->arguments == NULL) &&
7886
+ (call->block == NULL)
7887
+ ) {
7888
+ if (call->receiver == NULL) {
7889
+ // When we get here, we have a local variable write, because it
7890
+ // was previously marked as a method call but now we have an =.
7891
+ // This looks like:
7892
+ //
7893
+ // foo = 1
7894
+ //
7895
+ // When it was parsed in the prefix position, foo was seen as a
7896
+ // method call with no receiver and no arguments. Now we have an
7897
+ // =, so we know it's a local variable write.
7898
+ const yp_location_t message = call->message_loc;
7899
+
7900
+ yp_parser_local_add_location(parser, message.start, message.end);
7901
+ yp_node_destroy(parser, target);
7902
+
7903
+ const yp_token_t name = { .type = YP_TOKEN_IDENTIFIER, .start = message.start, .end = message.end };
7904
+ target = (yp_node_t *) yp_local_variable_read_node_create(parser, &name, 0);
7905
+
7906
+ assert(sizeof(yp_local_variable_target_node_t) == sizeof(yp_local_variable_read_node_t));
7907
+ target->type = YP_NODE_LOCAL_VARIABLE_TARGET_NODE;
7908
+
7909
+ if (token_is_numbered_parameter(message.start, message.end)) {
7910
+ yp_diagnostic_list_append(&parser->error_list, message.start, message.end, "reserved for numbered parameter");
7911
+ }
7912
+
7913
+ return target;
7914
+ }
7915
+
7916
+ // The method name needs to change. If we previously had foo, we now
7917
+ // need foo=. In this case we'll allocate a new owned string, copy
7918
+ // the previous method name in, and append an =.
7919
+ size_t length = yp_string_length(&call->name);
7920
+
7921
+ char *name = calloc(length + 2, sizeof(char));
7922
+ if (name == NULL) return NULL;
7923
+
7924
+ snprintf(name, length + 2, "%.*s=", (int) length, yp_string_source(&call->name));
7925
+
7926
+ // Now switch the name to the new string.
7927
+ yp_string_free(&call->name);
7928
+ yp_string_owned_init(&call->name, name, length + 1);
7929
+
7930
+ return target;
7931
+ }
7932
+
7933
+ // If there is no call operator and the message is "[]" then this is
7934
+ // an aref expression, and we can transform it into an aset
7935
+ // expression.
7936
+ if (
7937
+ (call->operator_loc.start == NULL) &&
7938
+ (call->message_loc.start[0] == '[') &&
7939
+ (call->message_loc.end[-1] == ']') &&
7940
+ (call->block == NULL)
7941
+ ) {
7942
+ // Free the previous name and replace it with "[]=".
7943
+ yp_string_free(&call->name);
7944
+ yp_string_constant_init(&call->name, "[]=", 3);
7945
+ return target;
7946
+ }
7947
+ }
7948
+ /* fallthrough */
7949
+ default:
7950
+ // In this case we have a node that we don't know how to convert
7951
+ // into a target. We need to treat it as an error. For now, we'll
7952
+ // mark it as an error and just skip right past it.
7953
+ yp_diagnostic_list_append(&parser->error_list, target->location.start, target->location.end, "Unexpected write target.");
7954
+ return target;
7955
+ }
7329
7956
  }
7330
7957
 
7331
- // Convert the given node into a valid target node.
7958
+ // Convert the given node into a valid write node.
7332
7959
  static yp_node_t *
7333
- parse_target(yp_parser_t *parser, yp_node_t *target, yp_token_t *operator, yp_node_t *value) {
7960
+ parse_write(yp_parser_t *parser, yp_node_t *target, yp_token_t *operator, yp_node_t *value) {
7334
7961
  switch (YP_NODE_TYPE(target)) {
7335
7962
  case YP_NODE_MISSING_NODE:
7336
7963
  return target;
@@ -7377,18 +8004,15 @@ parse_target(yp_parser_t *parser, yp_node_t *target, yp_token_t *operator, yp_no
7377
8004
  yp_multi_write_node_t *multi_write = (yp_multi_write_node_t *) target;
7378
8005
  yp_multi_write_node_operator_loc_set(multi_write, operator);
7379
8006
 
7380
- if (value != NULL) {
7381
- multi_write->value = value;
7382
- multi_write->base.location.end = value->location.end;
7383
- }
7384
-
8007
+ multi_write->value = value;
8008
+ multi_write->base.location.end = value->location.end;
7385
8009
  return (yp_node_t *) multi_write;
7386
8010
  }
7387
8011
  case YP_NODE_SPLAT_NODE: {
7388
8012
  yp_splat_node_t *splat = (yp_splat_node_t *) target;
7389
8013
 
7390
8014
  if (splat->expression != NULL) {
7391
- splat->expression = parse_target(parser, splat->expression, operator, value);
8015
+ splat->expression = parse_write(parser, splat->expression, operator, value);
7392
8016
  }
7393
8017
 
7394
8018
  yp_location_t location = { .start = NULL, .end = NULL };
@@ -7441,12 +8065,10 @@ parse_target(yp_parser_t *parser, yp_node_t *target, yp_token_t *operator, yp_no
7441
8065
  // method call with no arguments. Now we have an =, so we know it's
7442
8066
  // a method call with an argument. In this case we will create the
7443
8067
  // arguments node, parse the argument, and add it to the list.
7444
- if (value) {
7445
- yp_arguments_node_t *arguments = yp_arguments_node_create(parser);
7446
- call->arguments = arguments;
7447
- yp_arguments_node_arguments_append(arguments, value);
7448
- target->location.end = arguments->base.location.end;
7449
- }
8068
+ yp_arguments_node_t *arguments = yp_arguments_node_create(parser);
8069
+ call->arguments = arguments;
8070
+ yp_arguments_node_arguments_append(arguments, value);
8071
+ target->location.end = arguments->base.location.end;
7450
8072
 
7451
8073
  // The method name needs to change. If we previously had foo, we now
7452
8074
  // need foo=. In this case we'll allocate a new owned string, copy
@@ -7474,15 +8096,13 @@ parse_target(yp_parser_t *parser, yp_node_t *target, yp_token_t *operator, yp_no
7474
8096
  (call->message_loc.end[-1] == ']') &&
7475
8097
  (call->block == NULL)
7476
8098
  ) {
7477
- if (value != NULL) {
7478
- if (call->arguments == NULL) {
7479
- call->arguments = yp_arguments_node_create(parser);
7480
- }
7481
-
7482
- yp_arguments_node_arguments_append(call->arguments, value);
7483
- target->location.end = value->location.end;
8099
+ if (call->arguments == NULL) {
8100
+ call->arguments = yp_arguments_node_create(parser);
7484
8101
  }
7485
8102
 
8103
+ yp_arguments_node_arguments_append(call->arguments, value);
8104
+ target->location.end = value->location.end;
8105
+
7486
8106
  // Free the previous name and replace it with "[]=".
7487
8107
  yp_string_free(&call->name);
7488
8108
  yp_string_constant_init(&call->name, "[]=", 3);
@@ -7494,9 +8114,7 @@ parse_target(yp_parser_t *parser, yp_node_t *target, yp_token_t *operator, yp_no
7494
8114
  // syntax error. In this case we'll fall through to our default
7495
8115
  // handling. We need to free the value that we parsed because there
7496
8116
  // is no way for us to attach it to the tree at this point.
7497
- if (value != NULL) {
7498
- yp_node_destroy(parser, value);
7499
- }
8117
+ yp_node_destroy(parser, value);
7500
8118
  }
7501
8119
  /* fallthrough */
7502
8120
  default:
@@ -7524,7 +8142,7 @@ parse_targets(yp_parser_t *parser, yp_node_t *first_target, yp_binding_power_t b
7524
8142
  // location that we know requires a multi write, as in the case of a for loop.
7525
8143
  // In this case we will set up the parsing loop slightly differently.
7526
8144
  if (first_target != NULL) {
7527
- first_target = parse_target(parser, first_target, &operator, NULL);
8145
+ first_target = parse_target(parser, first_target);
7528
8146
 
7529
8147
  if (!match_type_p(parser, YP_TOKEN_COMMA)) {
7530
8148
  return first_target;
@@ -7555,9 +8173,8 @@ parse_targets(yp_parser_t *parser, yp_node_t *first_target, yp_binding_power_t b
7555
8173
  yp_node_t *name = NULL;
7556
8174
 
7557
8175
  if (token_begins_expression_p(parser->current.type)) {
7558
- yp_token_t operator = not_provided(parser);
7559
8176
  name = parse_expression(parser, binding_power, "Expected an expression after '*'.");
7560
- name = parse_target(parser, name, &operator, NULL);
8177
+ name = parse_target(parser, name);
7561
8178
  }
7562
8179
 
7563
8180
  yp_node_t *splat = (yp_node_t *) yp_splat_node_create(parser, &star_operator, name);
@@ -7587,6 +8204,8 @@ parse_targets(yp_parser_t *parser, yp_node_t *first_target, yp_binding_power_t b
7587
8204
 
7588
8205
  if (YP_NODE_TYPE_P(child_target, YP_NODE_MULTI_WRITE_NODE)) {
7589
8206
  target = (yp_multi_write_node_t *) child_target;
8207
+ target->base.location.start = lparen.start;
8208
+ target->base.location.end = rparen.end;
7590
8209
  target->lparen_loc = (yp_location_t) { .start = lparen.start, .end = lparen.end };
7591
8210
  target->rparen_loc = (yp_location_t) { .start = rparen.start, .end = rparen.end };
7592
8211
  } else {
@@ -7603,6 +8222,7 @@ parse_targets(yp_parser_t *parser, yp_node_t *first_target, yp_binding_power_t b
7603
8222
  yp_multi_write_node_targets_append(target, child_target);
7604
8223
  }
7605
8224
 
8225
+ target->base.location.start = lparen.start;
7606
8226
  target->base.location.end = rparen.end;
7607
8227
  yp_multi_write_node_targets_append(result, (yp_node_t *) target);
7608
8228
  }
@@ -7625,7 +8245,7 @@ parse_targets(yp_parser_t *parser, yp_node_t *first_target, yp_binding_power_t b
7625
8245
  }
7626
8246
 
7627
8247
  yp_node_t *target = parse_expression(parser, binding_power, "Expected another expression after ','.");
7628
- target = parse_target(parser, target, &operator, NULL);
8248
+ target = parse_target(parser, target);
7629
8249
 
7630
8250
  yp_multi_write_node_targets_append(result, target);
7631
8251
  }
@@ -8085,7 +8705,6 @@ parse_parameters(
8085
8705
  bool looping = true;
8086
8706
 
8087
8707
  yp_do_loop_stack_push(parser, false);
8088
-
8089
8708
  yp_parameters_order_t order = YP_PARAMETERS_ORDER_NONE;
8090
8709
 
8091
8710
  do {
@@ -8377,8 +8996,7 @@ parse_rescues(yp_parser_t *parser, yp_begin_node_t *parent_node) {
8377
8996
  yp_rescue_node_operator_set(rescue, &parser->previous);
8378
8997
 
8379
8998
  yp_node_t *reference = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected an exception variable after `=>` in rescue statement.");
8380
- yp_token_t operator = not_provided(parser);
8381
- reference = parse_target(parser, reference, &operator, NULL);
8999
+ reference = parse_target(parser, reference);
8382
9000
 
8383
9001
  yp_rescue_node_reference_set(rescue, reference);
8384
9002
  break;
@@ -8408,8 +9026,7 @@ parse_rescues(yp_parser_t *parser, yp_begin_node_t *parent_node) {
8408
9026
  yp_rescue_node_operator_set(rescue, &parser->previous);
8409
9027
 
8410
9028
  yp_node_t *reference = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected an exception variable after `=>` in rescue statement.");
8411
- yp_token_t operator = not_provided(parser);
8412
- reference = parse_target(parser, reference, &operator, NULL);
9029
+ reference = parse_target(parser, reference);
8413
9030
 
8414
9031
  yp_rescue_node_reference_set(rescue, reference);
8415
9032
  break;
@@ -8961,14 +9578,10 @@ parse_string_part(yp_parser_t *parser) {
8961
9578
 
8962
9579
  static yp_node_t *
8963
9580
  parse_symbol(yp_parser_t *parser, yp_lex_mode_t *lex_mode, yp_lex_state_t next_state) {
8964
- bool lex_string = lex_mode->mode == YP_LEX_STRING;
8965
- bool can_be_interpolated = lex_string && lex_mode->as.string.interpolation;
8966
9581
  yp_token_t opening = parser->previous;
8967
9582
 
8968
- if (!lex_string) {
8969
- if (next_state != YP_LEX_STATE_NONE) {
8970
- lex_state_set(parser, next_state);
8971
- }
9583
+ if (lex_mode->mode != YP_LEX_STRING) {
9584
+ if (next_state != YP_LEX_STATE_NONE) lex_state_set(parser, next_state);
8972
9585
  yp_token_t symbol;
8973
9586
 
8974
9587
  switch (parser->current.type) {
@@ -8998,37 +9611,44 @@ parse_symbol(yp_parser_t *parser, yp_lex_mode_t *lex_mode, yp_lex_state_t next_s
8998
9611
  return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &symbol, &closing, YP_UNESCAPE_ALL);
8999
9612
  }
9000
9613
 
9001
- if (can_be_interpolated) {
9002
- // Create a node_list first. We'll use this to check if it should be an InterpolatedSymbolNode
9003
- // or a SymbolNode
9614
+ if (lex_mode->as.string.interpolation) {
9615
+ // If we have the end of the symbol, then we can return an empty symbol.
9616
+ if (match_type_p(parser, YP_TOKEN_STRING_END)) {
9617
+ if (next_state != YP_LEX_STATE_NONE) lex_state_set(parser, next_state);
9618
+ parser_lex(parser);
9619
+
9620
+ yp_token_t content = not_provided(parser);
9621
+ yp_token_t closing = parser->previous;
9622
+ return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &content, &closing, YP_UNESCAPE_NONE);
9623
+ }
9624
+
9625
+ // Now we can parse the first part of the symbol.
9626
+ yp_node_t *part = parse_string_part(parser);
9627
+
9628
+ // If we got a string part, then it's possible that we could transform
9629
+ // what looks like an interpolated symbol into a regular symbol.
9630
+ if (part && YP_NODE_TYPE_P(part, YP_NODE_STRING_NODE) && match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) {
9631
+ if (next_state != YP_LEX_STATE_NONE) lex_state_set(parser, next_state);
9632
+ parser_lex(parser);
9633
+
9634
+ return (yp_node_t *) yp_string_node_to_symbol_node(parser, (yp_string_node_t *) part, &opening, &parser->previous);
9635
+ }
9636
+
9637
+ // Create a node_list first. We'll use this to check if it should be an
9638
+ // InterpolatedSymbolNode or a SymbolNode.
9004
9639
  yp_node_list_t node_list = YP_EMPTY_NODE_LIST;
9640
+ if (part) yp_node_list_append(&node_list, part);
9005
9641
 
9006
9642
  while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) {
9007
- yp_node_t *part = parse_string_part(parser);
9008
- if (part != NULL) {
9643
+ if ((part = parse_string_part(parser)) != NULL) {
9009
9644
  yp_node_list_append(&node_list, part);
9010
9645
  }
9011
9646
  }
9012
9647
 
9013
- yp_node_t *res;
9014
- // If the only element on the node_list is a StringNode, we know this is a SymbolNode
9015
- // and not an InterpolatedSymbolNode
9016
- if (node_list.size == 1 && YP_NODE_TYPE_P(node_list.nodes[0], YP_NODE_STRING_NODE)) {
9017
- res = (yp_node_t *)yp_string_node_to_symbol_node(parser, (yp_string_node_t *)node_list.nodes[0]);
9018
- free(node_list.nodes);
9019
- }
9020
- else {
9021
- yp_interpolated_symbol_node_t *interpolated = yp_interpolated_symbol_node_create(parser, &opening, &node_list, &opening);
9022
- yp_interpolated_symbol_node_closing_set(interpolated, &parser->current);
9023
- res = (yp_node_t *) interpolated;
9024
- }
9025
-
9026
- if (next_state != YP_LEX_STATE_NONE) {
9027
- lex_state_set(parser, next_state);
9028
- }
9648
+ if (next_state != YP_LEX_STATE_NONE) lex_state_set(parser, next_state);
9029
9649
  expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for an interpolated symbol.");
9030
9650
 
9031
- return res;
9651
+ return (yp_node_t *) yp_interpolated_symbol_node_create(parser, &opening, &node_list, &parser->previous);
9032
9652
  }
9033
9653
 
9034
9654
  yp_token_t content;
@@ -9172,9 +9792,12 @@ parse_heredoc_common_whitespace(yp_parser_t *parser, yp_node_list_t *nodes) {
9172
9792
  const char *cur_char = content_loc->start;
9173
9793
 
9174
9794
  while (cur_char && cur_char < content_loc->end) {
9175
- // Any empty newlines aren't included in the minimum whitespace calculation
9176
- while (cur_char < content_loc->end && *cur_char == '\n') cur_char++;
9177
- while (cur_char + 1 < content_loc->end && *cur_char == '\r' && cur_char[1] == '\n') cur_char += 2;
9795
+ // Any empty newlines aren't included in the minimum whitespace
9796
+ // calculation.
9797
+ size_t eol_length;
9798
+ while ((eol_length = match_eol_at(parser, cur_char))) {
9799
+ cur_char += eol_length;
9800
+ }
9178
9801
 
9179
9802
  if (cur_char == content_loc->end) break;
9180
9803
 
@@ -9189,11 +9812,12 @@ parse_heredoc_common_whitespace(yp_parser_t *parser, yp_node_list_t *nodes) {
9189
9812
  cur_char++;
9190
9813
  }
9191
9814
 
9192
- // If we hit a newline, then we have encountered a line that contains
9193
- // only whitespace, and it shouldn't be considered in the calculation of
9194
- // common leading whitespace.
9195
- if (*cur_char == '\n') {
9196
- cur_char++;
9815
+ // If we hit a newline, then we have encountered a line that
9816
+ // contains only whitespace, and it shouldn't be considered in
9817
+ // the calculation of common leading whitespace.
9818
+ eol_length = match_eol_at(parser, cur_char);
9819
+ if (eol_length) {
9820
+ cur_char += eol_length;
9197
9821
  continue;
9198
9822
  }
9199
9823
 
@@ -9314,7 +9938,7 @@ parse_heredoc_dedent(yp_parser_t *parser, yp_node_t *node, yp_heredoc_quote_t qu
9314
9938
  yp_node_destroy(parser, node);
9315
9939
  } else {
9316
9940
  string->length = dest_length;
9317
- yp_unescape_manipulate_string(parser, string, (quote == YP_HEREDOC_QUOTE_SINGLE) ? YP_UNESCAPE_MINIMAL : YP_UNESCAPE_ALL, &parser->error_list);
9941
+ yp_unescape_manipulate_string(parser, string, (quote == YP_HEREDOC_QUOTE_SINGLE) ? YP_UNESCAPE_MINIMAL : YP_UNESCAPE_ALL);
9318
9942
  nodes->nodes[write_index++] = node;
9319
9943
  }
9320
9944
 
@@ -10071,10 +10695,8 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
10071
10695
  }
10072
10696
  case YP_TOKEN_PARENTHESIS_LEFT:
10073
10697
  case YP_TOKEN_PARENTHESIS_LEFT_PARENTHESES: {
10074
- yp_token_type_t current_token_type = parser->current.type;
10698
+ yp_token_t opening = parser->current;
10075
10699
  parser_lex(parser);
10076
-
10077
- yp_token_t opening = parser->previous;
10078
10700
  while (accept_any(parser, 2, YP_TOKEN_SEMICOLON, YP_TOKEN_NEWLINE));
10079
10701
 
10080
10702
  // If this is the end of the file or we match a right parenthesis, then
@@ -10093,7 +10715,7 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
10093
10715
  // If we hit a right parenthesis, then we're done parsing the parentheses
10094
10716
  // node, and we can check which kind of node we should return.
10095
10717
  if (match_type_p(parser, YP_TOKEN_PARENTHESIS_RIGHT)) {
10096
- if (current_token_type == YP_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
10718
+ if (opening.type == YP_TOKEN_PARENTHESIS_LEFT_PARENTHESES) {
10097
10719
  lex_state_set(parser, YP_LEX_STATE_ENDARG);
10098
10720
  }
10099
10721
  parser_lex(parser);
@@ -10111,6 +10733,8 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
10111
10733
 
10112
10734
  if (multi_statement->lparen_loc.start == NULL) {
10113
10735
  multi_write = (yp_multi_write_node_t *) statement;
10736
+ multi_write->base.location.start = lparen_loc.start;
10737
+ multi_write->base.location.end = rparen_loc.end;
10114
10738
  multi_write->lparen_loc = lparen_loc;
10115
10739
  multi_write->rparen_loc = rparen_loc;
10116
10740
  } else {
@@ -10213,7 +10837,7 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
10213
10837
  // fact a method call, not a constant read.
10214
10838
  if (
10215
10839
  match_type_p(parser, YP_TOKEN_PARENTHESIS_LEFT) ||
10216
- (binding_power <= YP_BINDING_POWER_ASSIGNMENT && (token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR))) ||
10840
+ (binding_power <= YP_BINDING_POWER_ASSIGNMENT && (token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 3, YP_TOKEN_UAMPERSAND, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR))) ||
10217
10841
  (yp_accepts_block_stack_p(parser) && match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_DO, YP_TOKEN_BRACE_LEFT))
10218
10842
  ) {
10219
10843
  yp_arguments_t arguments = YP_EMPTY_ARGUMENTS;
@@ -10336,7 +10960,7 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
10336
10960
  // can still be a method call if it is followed by arguments or
10337
10961
  // a block, so we need to check for that here.
10338
10962
  if (
10339
- (binding_power <= YP_BINDING_POWER_ASSIGNMENT && (token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR))) ||
10963
+ (binding_power <= YP_BINDING_POWER_ASSIGNMENT && (token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 3, YP_TOKEN_UAMPERSAND, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR))) ||
10340
10964
  (yp_accepts_block_stack_p(parser) && match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_DO, YP_TOKEN_BRACE_LEFT))
10341
10965
  ) {
10342
10966
  yp_arguments_t arguments = YP_EMPTY_ARGUMENTS;
@@ -10754,7 +11378,12 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
10754
11378
  return (yp_node_t *) yp_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous);
10755
11379
  }
10756
11380
 
10757
- yp_node_t *name = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected to find a class name after `class`.");
11381
+ yp_node_t *constant_path = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected to find a class name after `class`.");
11382
+ yp_token_t name = parser->previous;
11383
+ if (name.type != YP_TOKEN_CONSTANT) {
11384
+ yp_diagnostic_list_append(&parser->error_list, name.start, name.end, "Expected a constant name after `class`.");
11385
+ }
11386
+
10758
11387
  yp_token_t inheritance_operator;
10759
11388
  yp_node_t *superclass;
10760
11389
 
@@ -10795,7 +11424,7 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
10795
11424
  yp_constant_id_list_t locals = parser->current_scope->locals;
10796
11425
  yp_parser_scope_pop(parser);
10797
11426
  yp_do_loop_stack_pop(parser);
10798
- return (yp_node_t *) yp_class_node_create(parser, &locals, &class_keyword, name, &inheritance_operator, superclass, statements, &parser->previous);
11427
+ return (yp_node_t *) yp_class_node_create(parser, &locals, &class_keyword, constant_path, &name, &inheritance_operator, superclass, statements, &parser->previous);
10799
11428
  }
10800
11429
  case YP_TOKEN_KEYWORD_DEF: {
10801
11430
  yp_token_t def_keyword = parser->current;
@@ -10954,6 +11583,12 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
10954
11583
  break;
10955
11584
  }
10956
11585
  case YP_CASE_PARAMETER: {
11586
+ // If we're about to lex a label, we need to add the label
11587
+ // state to make sure the next newline is ignored.
11588
+ if (parser->current.type == YP_TOKEN_LABEL) {
11589
+ lex_state_set(parser, parser->lex_state | YP_LEX_STATE_LABEL);
11590
+ }
11591
+
10957
11592
  lparen = not_provided(parser);
10958
11593
  rparen = not_provided(parser);
10959
11594
  params = parse_parameters(parser, YP_BINDING_POWER_DEFINED, false, false, true);
@@ -11183,13 +11818,14 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
11183
11818
  parser_lex(parser);
11184
11819
 
11185
11820
  yp_token_t module_keyword = parser->previous;
11186
- yp_node_t *name = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected to find a module name after `module`.");
11821
+ yp_node_t *constant_path = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected to find a module name after `module`.");
11822
+ yp_token_t name;
11187
11823
 
11188
- // If we can recover from a syntax error that occurred while parsing the
11189
- // name of the module, then we'll handle that here.
11190
- if (YP_NODE_TYPE_P(name, YP_NODE_MISSING_NODE)) {
11191
- yp_token_t end_keyword = (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
11192
- return (yp_node_t *) yp_module_node_create(parser, NULL, &module_keyword, name, NULL, &end_keyword);
11824
+ // If we can recover from a syntax error that occurred while parsing
11825
+ // the name of the module, then we'll handle that here.
11826
+ if (YP_NODE_TYPE_P(constant_path, YP_NODE_MISSING_NODE)) {
11827
+ yp_token_t missing = (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end };
11828
+ return (yp_node_t *) yp_module_node_create(parser, NULL, &module_keyword, constant_path, &missing, NULL, &missing);
11193
11829
  }
11194
11830
 
11195
11831
  while (accept(parser, YP_TOKEN_COLON_COLON)) {
@@ -11198,7 +11834,15 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
11198
11834
  expect(parser, YP_TOKEN_CONSTANT, "Expected to find a module name after `::`.");
11199
11835
  yp_node_t *constant = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous);
11200
11836
 
11201
- name = (yp_node_t *)yp_constant_path_node_create(parser, name, &double_colon, constant);
11837
+ constant_path = (yp_node_t *) yp_constant_path_node_create(parser, constant_path, &double_colon, constant);
11838
+ }
11839
+
11840
+ // Here we retrieve the name of the module. If it wasn't a constant,
11841
+ // then it's possible that `module foo` was passed, which is a
11842
+ // syntax error. We handle that here as well.
11843
+ name = parser->previous;
11844
+ if (name.type != YP_TOKEN_CONSTANT) {
11845
+ yp_diagnostic_list_append(&parser->error_list, name.start, name.end, "Expected to find a module name after `module`.");
11202
11846
  }
11203
11847
 
11204
11848
  yp_parser_scope_push(parser, true);
@@ -11225,7 +11869,7 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
11225
11869
  yp_diagnostic_list_append(&parser->error_list, module_keyword.start, module_keyword.end, "Module definition in method body");
11226
11870
  }
11227
11871
 
11228
- return (yp_node_t *) yp_module_node_create(parser, &locals, &module_keyword, name, statements, &parser->previous);
11872
+ return (yp_node_t *) yp_module_node_create(parser, &locals, &module_keyword, constant_path, &name, statements, &parser->previous);
11229
11873
  }
11230
11874
  case YP_TOKEN_KEYWORD_NIL:
11231
11875
  parser_lex(parser);
@@ -11261,12 +11905,7 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
11261
11905
  expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `until` statement.");
11262
11906
  }
11263
11907
 
11264
- yp_until_node_t *until_node = yp_until_node_create(parser, &keyword, predicate, statements, 0);
11265
- if (parser->previous.type == YP_TOKEN_KEYWORD_END) {
11266
- until_node->base.location.end = parser->previous.end;
11267
- }
11268
-
11269
- return (yp_node_t *) until_node;
11908
+ return (yp_node_t *) yp_until_node_create(parser, &keyword, &parser->previous, predicate, statements, 0);
11270
11909
  }
11271
11910
  case YP_TOKEN_KEYWORD_WHILE: {
11272
11911
  yp_do_loop_stack_push(parser, true);
@@ -11287,11 +11926,7 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
11287
11926
  expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `while` statement.");
11288
11927
  }
11289
11928
 
11290
- yp_while_node_t *while_node = yp_while_node_create(parser, &keyword, predicate, statements, 0);
11291
- if (parser->previous.type == YP_TOKEN_KEYWORD_END) {
11292
- while_node->base.location.end = parser->previous.end;
11293
- }
11294
- return (yp_node_t *) while_node;
11929
+ return (yp_node_t *) yp_while_node_create(parser, &keyword, &parser->previous, predicate, statements, 0);
11295
11930
  }
11296
11931
  case YP_TOKEN_PERCENT_LOWER_I: {
11297
11932
  parser_lex(parser);
@@ -11797,30 +12432,32 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
11797
12432
  yp_accepts_block_stack_push(parser, true);
11798
12433
  parser_lex(parser);
11799
12434
 
11800
- yp_token_t opening = parser->previous;
12435
+ yp_token_t operator = parser->previous;
11801
12436
  yp_parser_scope_push(parser, false);
11802
12437
  yp_block_parameters_node_t *params;
11803
12438
 
11804
12439
  switch (parser->current.type) {
11805
12440
  case YP_TOKEN_PARENTHESIS_LEFT: {
11806
- yp_token_t block_parameters_opening = parser->current;
12441
+ yp_token_t opening = parser->current;
11807
12442
  parser_lex(parser);
11808
12443
 
11809
12444
  if (match_type_p(parser, YP_TOKEN_PARENTHESIS_RIGHT)) {
11810
- params = yp_block_parameters_node_create(parser, NULL, &block_parameters_opening);
12445
+ params = yp_block_parameters_node_create(parser, NULL, &opening);
11811
12446
  } else {
11812
- params = parse_block_parameters(parser, false, &block_parameters_opening, true);
12447
+ params = parse_block_parameters(parser, false, &opening, true);
11813
12448
  }
11814
12449
 
11815
12450
  accept(parser, YP_TOKEN_NEWLINE);
11816
12451
  expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected ')' after left parenthesis.");
11817
- yp_block_parameters_node_closing_set(params, &parser->previous);
11818
12452
 
12453
+ yp_block_parameters_node_closing_set(params, &parser->previous);
11819
12454
  break;
11820
12455
  }
11821
12456
  case YP_CASE_PARAMETER: {
12457
+ yp_accepts_block_stack_push(parser, false);
11822
12458
  yp_token_t opening = not_provided(parser);
11823
12459
  params = parse_block_parameters(parser, false, &opening, true);
12460
+ yp_accepts_block_stack_pop(parser);
11824
12461
  break;
11825
12462
  }
11826
12463
  default: {
@@ -11829,16 +12466,20 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
11829
12466
  }
11830
12467
  }
11831
12468
 
12469
+ yp_token_t opening;
11832
12470
  yp_node_t *body = NULL;
11833
12471
  parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting;
11834
12472
 
11835
12473
  if (accept(parser, YP_TOKEN_LAMBDA_BEGIN)) {
12474
+ opening = parser->previous;
12475
+
11836
12476
  if (!accept(parser, YP_TOKEN_BRACE_RIGHT)) {
11837
12477
  body = (yp_node_t *) parse_statements(parser, YP_CONTEXT_LAMBDA_BRACES);
11838
12478
  expect(parser, YP_TOKEN_BRACE_RIGHT, "Expecting '}' to close lambda block.");
11839
12479
  }
11840
12480
  } else {
11841
12481
  expect(parser, YP_TOKEN_KEYWORD_DO, "Expected a 'do' keyword or a '{' to open lambda block.");
12482
+ opening = parser->previous;
11842
12483
 
11843
12484
  if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_END, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) {
11844
12485
  body = (yp_node_t *) parse_statements(parser, YP_CONTEXT_LAMBDA_DO_END);
@@ -11855,7 +12496,7 @@ parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) {
11855
12496
  yp_constant_id_list_t locals = parser->current_scope->locals;
11856
12497
  yp_parser_scope_pop(parser);
11857
12498
  yp_accepts_block_stack_pop(parser);
11858
- return (yp_node_t *) yp_lambda_node_create(parser, &locals, &opening, params, body, &parser->previous);
12499
+ return (yp_node_t *) yp_lambda_node_create(parser, &locals, &operator, &opening, &parser->previous, params, body);
11859
12500
  }
11860
12501
  case YP_TOKEN_UPLUS: {
11861
12502
  parser_lex(parser);
@@ -12074,7 +12715,7 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
12074
12715
  case YP_CASE_WRITABLE: {
12075
12716
  parser_lex(parser);
12076
12717
  yp_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, "Expected a value after =.");
12077
- return parse_target(parser, node, &token, value);
12718
+ return parse_write(parser, node, &token, value);
12078
12719
  }
12079
12720
  case YP_NODE_SPLAT_NODE: {
12080
12721
  yp_splat_node_t *splat_node = (yp_splat_node_t *) node;
@@ -12083,7 +12724,7 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
12083
12724
  case YP_CASE_WRITABLE:
12084
12725
  parser_lex(parser);
12085
12726
  yp_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, "Expected a value after =.");
12086
- return parse_target(parser, (yp_node_t *) splat_node, &token, value);
12727
+ return parse_write(parser, (yp_node_t *) splat_node, &token, value);
12087
12728
  default:
12088
12729
  break;
12089
12730
  }
@@ -12105,19 +12746,57 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
12105
12746
  case YP_NODE_NUMBERED_REFERENCE_READ_NODE:
12106
12747
  yp_diagnostic_list_append(&parser->error_list, node->location.start, node->location.end, "Can't set variable");
12107
12748
  /* fallthrough */
12108
- case YP_NODE_CLASS_VARIABLE_READ_NODE:
12109
- case YP_NODE_CONSTANT_PATH_NODE:
12110
- case YP_NODE_CONSTANT_READ_NODE:
12111
- case YP_NODE_GLOBAL_VARIABLE_READ_NODE:
12112
- case YP_NODE_INSTANCE_VARIABLE_READ_NODE:
12113
- case YP_NODE_LOCAL_VARIABLE_READ_NODE: {
12749
+ case YP_NODE_GLOBAL_VARIABLE_READ_NODE: {
12750
+ parser_lex(parser);
12751
+
12752
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
12753
+ yp_node_t *result = (yp_node_t *) yp_global_variable_and_write_node_create(parser, node, &token, value);
12754
+
12755
+ yp_node_destroy(parser, node);
12756
+ return result;
12757
+ }
12758
+ case YP_NODE_CLASS_VARIABLE_READ_NODE: {
12759
+ parser_lex(parser);
12760
+
12761
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
12762
+ yp_node_t *result = (yp_node_t *) yp_class_variable_and_write_node_create(parser, node, &token, value);
12763
+
12764
+ yp_node_destroy(parser, node);
12765
+ return result;
12766
+ }
12767
+ case YP_NODE_CONSTANT_PATH_NODE: {
12768
+ parser_lex(parser);
12769
+
12770
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
12771
+ return (yp_node_t *) yp_constant_path_and_write_node_create(parser, (yp_constant_path_node_t *) node, &token, value);
12772
+ }
12773
+ case YP_NODE_CONSTANT_READ_NODE: {
12774
+ parser_lex(parser);
12775
+
12776
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
12777
+ yp_node_t *result = (yp_node_t *) yp_constant_and_write_node_create(parser, node, &token, value);
12778
+
12779
+ yp_node_destroy(parser, node);
12780
+ return result;
12781
+ }
12782
+ case YP_NODE_INSTANCE_VARIABLE_READ_NODE: {
12114
12783
  parser_lex(parser);
12115
12784
 
12116
- yp_token_t operator = not_provided(parser);
12117
- node = parse_target(parser, node, &operator, NULL);
12785
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
12786
+ yp_node_t *result = (yp_node_t *) yp_instance_variable_and_write_node_create(parser, node, &token, value);
12787
+
12788
+ yp_node_destroy(parser, node);
12789
+ return result;
12790
+ }
12791
+ case YP_NODE_LOCAL_VARIABLE_READ_NODE: {
12792
+ yp_local_variable_read_node_t *cast = (yp_local_variable_read_node_t *) node;
12793
+ parser_lex(parser);
12118
12794
 
12119
12795
  yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
12120
- return (yp_node_t *) yp_and_write_node_create(parser, node, &token, value);
12796
+ yp_node_t *result = (yp_node_t *) yp_local_variable_and_write_node_create(parser, node, &token, value, cast->constant_id, cast->depth);
12797
+
12798
+ yp_node_destroy(parser, node);
12799
+ return result;
12121
12800
  }
12122
12801
  case YP_NODE_CALL_NODE: {
12123
12802
  yp_call_node_t *call_node = (yp_call_node_t *) node;
@@ -12127,25 +12806,22 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
12127
12806
  // will transform it into a local variable write.
12128
12807
  if (yp_call_node_variable_call_p(call_node)) {
12129
12808
  yp_location_t message_loc = call_node->message_loc;
12130
- yp_parser_local_add_location(parser, message_loc.start, message_loc.end);
12809
+ yp_constant_id_t constant_id = yp_parser_local_add_location(parser, message_loc.start, message_loc.end);
12131
12810
 
12132
12811
  if (token_is_numbered_parameter(message_loc.start, message_loc.end)) {
12133
12812
  yp_diagnostic_list_append(&parser->error_list, message_loc.start, message_loc.end, "reserved for numbered parameter");
12134
12813
  }
12135
12814
 
12136
12815
  parser_lex(parser);
12137
-
12138
- yp_token_t operator = not_provided(parser);
12139
- node = parse_target(parser, node, &operator, NULL);
12140
-
12141
12816
  yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
12142
- return (yp_node_t *) yp_and_write_node_create(parser, node, &token, value);
12817
+ yp_node_t *result = (yp_node_t *) yp_local_variable_and_write_node_create(parser, node, &token, value, constant_id, 0);
12818
+
12819
+ yp_node_destroy(parser, node);
12820
+ return result;
12143
12821
  }
12144
12822
 
12145
12823
  parser_lex(parser);
12146
-
12147
- yp_token_t operator = not_provided(parser);
12148
- node = parse_target(parser, node, &operator, NULL);
12824
+ node = parse_target(parser, node);
12149
12825
 
12150
12826
  yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
12151
12827
  return (yp_node_t *) yp_call_operator_and_write_node_create(parser, (yp_call_node_t *) node, &token, value);
@@ -12171,19 +12847,57 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
12171
12847
  case YP_NODE_NUMBERED_REFERENCE_READ_NODE:
12172
12848
  yp_diagnostic_list_append(&parser->error_list, node->location.start, node->location.end, "Can't set variable");
12173
12849
  /* fallthrough */
12174
- case YP_NODE_CLASS_VARIABLE_READ_NODE:
12175
- case YP_NODE_CONSTANT_PATH_NODE:
12176
- case YP_NODE_CONSTANT_READ_NODE:
12177
- case YP_NODE_GLOBAL_VARIABLE_READ_NODE:
12178
- case YP_NODE_INSTANCE_VARIABLE_READ_NODE:
12179
- case YP_NODE_LOCAL_VARIABLE_READ_NODE: {
12850
+ case YP_NODE_GLOBAL_VARIABLE_READ_NODE: {
12851
+ parser_lex(parser);
12852
+
12853
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
12854
+ yp_node_t *result = (yp_node_t *) yp_global_variable_or_write_node_create(parser, node, &token, value);
12855
+
12856
+ yp_node_destroy(parser, node);
12857
+ return result;
12858
+ }
12859
+ case YP_NODE_CLASS_VARIABLE_READ_NODE: {
12860
+ parser_lex(parser);
12861
+
12862
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
12863
+ yp_node_t *result = (yp_node_t *) yp_class_variable_or_write_node_create(parser, node, &token, value);
12864
+
12865
+ yp_node_destroy(parser, node);
12866
+ return result;
12867
+ }
12868
+ case YP_NODE_CONSTANT_PATH_NODE: {
12869
+ parser_lex(parser);
12870
+
12871
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
12872
+ return (yp_node_t *) yp_constant_path_or_write_node_create(parser, (yp_constant_path_node_t *) node, &token, value);
12873
+ }
12874
+ case YP_NODE_CONSTANT_READ_NODE: {
12875
+ parser_lex(parser);
12876
+
12877
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
12878
+ yp_node_t *result = (yp_node_t *) yp_constant_or_write_node_create(parser, node, &token, value);
12879
+
12880
+ yp_node_destroy(parser, node);
12881
+ return result;
12882
+ }
12883
+ case YP_NODE_INSTANCE_VARIABLE_READ_NODE: {
12180
12884
  parser_lex(parser);
12181
12885
 
12182
- yp_token_t operator = not_provided(parser);
12183
- node = parse_target(parser, node, &operator, NULL);
12886
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
12887
+ yp_node_t *result = (yp_node_t *) yp_instance_variable_or_write_node_create(parser, node, &token, value);
12888
+
12889
+ yp_node_destroy(parser, node);
12890
+ return result;
12891
+ }
12892
+ case YP_NODE_LOCAL_VARIABLE_READ_NODE: {
12893
+ yp_local_variable_read_node_t *cast = (yp_local_variable_read_node_t *) node;
12894
+ parser_lex(parser);
12184
12895
 
12185
12896
  yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
12186
- return (yp_node_t *) yp_or_write_node_create(parser, node, &token, value);
12897
+ yp_node_t *result = (yp_node_t *) yp_local_variable_or_write_node_create(parser, node, &token, value, cast->constant_id, cast->depth);
12898
+
12899
+ yp_node_destroy(parser, node);
12900
+ return result;
12187
12901
  }
12188
12902
  case YP_NODE_CALL_NODE: {
12189
12903
  yp_call_node_t *call_node = (yp_call_node_t *) node;
@@ -12193,25 +12907,22 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
12193
12907
  // will transform it into a local variable write.
12194
12908
  if (yp_call_node_variable_call_p(call_node)) {
12195
12909
  yp_location_t message_loc = call_node->message_loc;
12196
- yp_parser_local_add_location(parser, message_loc.start, message_loc.end);
12910
+ yp_constant_id_t constant_id = yp_parser_local_add_location(parser, message_loc.start, message_loc.end);
12197
12911
 
12198
12912
  if (token_is_numbered_parameter(message_loc.start, message_loc.end)) {
12199
12913
  yp_diagnostic_list_append(&parser->error_list, message_loc.start, message_loc.end, "reserved for numbered parameter");
12200
12914
  }
12201
12915
 
12202
12916
  parser_lex(parser);
12203
-
12204
- yp_token_t operator = not_provided(parser);
12205
- node = parse_target(parser, node, &operator, NULL);
12206
-
12207
12917
  yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
12208
- return (yp_node_t *) yp_or_write_node_create(parser, node, &token, value);
12918
+ yp_node_t *result = (yp_node_t *) yp_local_variable_or_write_node_create(parser, node, &token, value, constant_id, 0);
12919
+
12920
+ yp_node_destroy(parser, node);
12921
+ return result;
12209
12922
  }
12210
12923
 
12211
12924
  parser_lex(parser);
12212
-
12213
- yp_token_t operator = not_provided(parser);
12214
- node = parse_target(parser, node, &operator, NULL);
12925
+ node = parse_target(parser, node);
12215
12926
 
12216
12927
  yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||=");
12217
12928
  return (yp_node_t *) yp_call_operator_or_write_node_create(parser, (yp_call_node_t *) node, &token, value);
@@ -12247,19 +12958,57 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
12247
12958
  case YP_NODE_NUMBERED_REFERENCE_READ_NODE:
12248
12959
  yp_diagnostic_list_append(&parser->error_list, node->location.start, node->location.end, "Can't set variable");
12249
12960
  /* fallthrough */
12250
- case YP_NODE_CLASS_VARIABLE_READ_NODE:
12251
- case YP_NODE_CONSTANT_PATH_NODE:
12252
- case YP_NODE_CONSTANT_READ_NODE:
12253
- case YP_NODE_GLOBAL_VARIABLE_READ_NODE:
12254
- case YP_NODE_INSTANCE_VARIABLE_READ_NODE:
12961
+ case YP_NODE_GLOBAL_VARIABLE_READ_NODE: {
12962
+ parser_lex(parser);
12963
+
12964
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
12965
+ yp_node_t *result = (yp_node_t *) yp_global_variable_operator_write_node_create(parser, node, &token, value);
12966
+
12967
+ yp_node_destroy(parser, node);
12968
+ return result;
12969
+ }
12970
+ case YP_NODE_CLASS_VARIABLE_READ_NODE: {
12971
+ parser_lex(parser);
12972
+
12973
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
12974
+ yp_node_t *result = (yp_node_t *) yp_class_variable_operator_write_node_create(parser, node, &token, value);
12975
+
12976
+ yp_node_destroy(parser, node);
12977
+ return result;
12978
+ }
12979
+ case YP_NODE_CONSTANT_PATH_NODE: {
12980
+ parser_lex(parser);
12981
+
12982
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
12983
+ return (yp_node_t *) yp_constant_path_operator_write_node_create(parser, (yp_constant_path_node_t *) node, &token, value);
12984
+ }
12985
+ case YP_NODE_CONSTANT_READ_NODE: {
12986
+ parser_lex(parser);
12987
+
12988
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
12989
+ yp_node_t *result = (yp_node_t *) yp_constant_operator_write_node_create(parser, node, &token, value);
12990
+
12991
+ yp_node_destroy(parser, node);
12992
+ return result;
12993
+ }
12994
+ case YP_NODE_INSTANCE_VARIABLE_READ_NODE: {
12995
+ parser_lex(parser);
12996
+
12997
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
12998
+ yp_node_t *result = (yp_node_t *) yp_instance_variable_operator_write_node_create(parser, node, &token, value);
12999
+
13000
+ yp_node_destroy(parser, node);
13001
+ return result;
13002
+ }
12255
13003
  case YP_NODE_LOCAL_VARIABLE_READ_NODE: {
13004
+ yp_local_variable_read_node_t *cast = (yp_local_variable_read_node_t *) node;
12256
13005
  parser_lex(parser);
12257
13006
 
12258
- yp_token_t operator = not_provided(parser);
12259
- node = parse_target(parser, node, &operator, NULL);
13007
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
13008
+ yp_node_t *result = (yp_node_t *) yp_local_variable_operator_write_node_create(parser, node, &token, value, cast->constant_id, cast->depth);
12260
13009
 
12261
- yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator");
12262
- return (yp_node_t *) yp_operator_write_node_create(parser, node, &token, value);
13010
+ yp_node_destroy(parser, node);
13011
+ return result;
12263
13012
  }
12264
13013
  case YP_NODE_CALL_NODE: {
12265
13014
  yp_call_node_t *call_node = (yp_call_node_t *) node;
@@ -12269,25 +13018,23 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
12269
13018
  // will transform it into a local variable write.
12270
13019
  if (yp_call_node_variable_call_p(call_node)) {
12271
13020
  yp_location_t message_loc = call_node->message_loc;
12272
- yp_parser_local_add_location(parser, message_loc.start, message_loc.end);
13021
+ yp_constant_id_t constant_id = yp_parser_local_add_location(parser, message_loc.start, message_loc.end);
12273
13022
 
12274
13023
  if (token_is_numbered_parameter(message_loc.start, message_loc.end)) {
12275
13024
  yp_diagnostic_list_append(&parser->error_list, message_loc.start, message_loc.end, "reserved for numbered parameter");
12276
13025
  }
12277
13026
 
12278
13027
  parser_lex(parser);
13028
+ yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
13029
+ yp_node_t *result = (yp_node_t *) yp_local_variable_operator_write_node_create(parser, node, &token, value, constant_id, 0);
12279
13030
 
12280
- yp_token_t operator = not_provided(parser);
12281
- node = parse_target(parser, node, &operator, NULL);
12282
-
12283
- yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&=");
12284
- return (yp_node_t *) yp_operator_write_node_create(parser, node, &token, value);
13031
+ yp_node_destroy(parser, node);
13032
+ return result;
12285
13033
  }
12286
13034
 
12287
- yp_token_t operator = not_provided(parser);
12288
- node = parse_target(parser, node, &operator, NULL);
12289
-
13035
+ node = parse_target(parser, node);
12290
13036
  parser_lex(parser);
13037
+
12291
13038
  yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator.");
12292
13039
  return (yp_node_t *) yp_call_operator_write_node_create(parser, (yp_call_node_t *) node, &token, value);
12293
13040
  }
@@ -12456,7 +13203,7 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
12456
13203
  yp_statements_node_body_append(statements, node);
12457
13204
 
12458
13205
  yp_node_t *predicate = parse_expression(parser, binding_power, "Expected a predicate after 'until'");
12459
- return (yp_node_t *) yp_until_node_create(parser, &token, predicate, statements, YP_NODE_TYPE_P(node, YP_NODE_BEGIN_NODE) ? YP_LOOP_FLAGS_BEGIN_MODIFIER : 0);
13206
+ return (yp_node_t *) yp_until_node_modifier_create(parser, &token, predicate, statements, YP_NODE_TYPE_P(node, YP_NODE_BEGIN_NODE) ? YP_LOOP_FLAGS_BEGIN_MODIFIER : 0);
12460
13207
  }
12461
13208
  case YP_TOKEN_KEYWORD_WHILE_MODIFIER: {
12462
13209
  parser_lex(parser);
@@ -12464,7 +13211,7 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
12464
13211
  yp_statements_node_body_append(statements, node);
12465
13212
 
12466
13213
  yp_node_t *predicate = parse_expression(parser, binding_power, "Expected a predicate after 'while'");
12467
- return (yp_node_t *) yp_while_node_create(parser, &token, predicate, statements, YP_NODE_TYPE_P(node, YP_NODE_BEGIN_NODE) ? YP_LOOP_FLAGS_BEGIN_MODIFIER : 0);
13214
+ return (yp_node_t *) yp_while_node_modifier_create(parser, &token, predicate, statements, YP_NODE_TYPE_P(node, YP_NODE_BEGIN_NODE) ? YP_LOOP_FLAGS_BEGIN_MODIFIER : 0);
12468
13215
  }
12469
13216
  case YP_TOKEN_QUESTION_MARK: {
12470
13217
  parser_lex(parser);
@@ -12502,7 +13249,7 @@ parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t
12502
13249
 
12503
13250
  if (
12504
13251
  (parser->current.type == YP_TOKEN_PARENTHESIS_LEFT) ||
12505
- (token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR))
13252
+ (token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 3, YP_TOKEN_UAMPERSAND, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR))
12506
13253
  ) {
12507
13254
  // If we have a constant immediately following a '::' operator, then
12508
13255
  // this can either be a constant path or a method call, depending on
@@ -12734,7 +13481,7 @@ yp_metadata_read_u32(const char *ptr) {
12734
13481
  // ]*
12735
13482
  // ]
12736
13483
  // ```
12737
- static void
13484
+ void
12738
13485
  yp_parser_metadata(yp_parser_t *parser, const char *metadata) {
12739
13486
  uint32_t filepath_size = yp_metadata_read_u32(metadata);
12740
13487
  metadata += 4;
@@ -12773,6 +13520,8 @@ yp_parser_metadata(yp_parser_t *parser, const char *metadata) {
12773
13520
  // Initialize a parser with the given start and end pointers.
12774
13521
  YP_EXPORTED_FUNCTION void
12775
13522
  yp_parser_init(yp_parser_t *parser, const char *source, size_t size, const char *filepath) {
13523
+ assert(source != NULL);
13524
+
12776
13525
  // Set filepath to the file that was passed
12777
13526
  if (!filepath) filepath = "";
12778
13527
  yp_string_t filepath_string;
@@ -12841,14 +13590,15 @@ yp_parser_init(yp_parser_t *parser, const char *source, size_t size, const char
12841
13590
  size_t newline_size = size / 22;
12842
13591
  yp_newline_list_init(&parser->newline_list, source, newline_size < 4 ? 4 : newline_size);
12843
13592
 
12844
- assert(source != NULL);
13593
+ // Skip past the UTF-8 BOM if it exists.
12845
13594
  if (size >= 3 && (unsigned char) source[0] == 0xef && (unsigned char) source[1] == 0xbb && (unsigned char) source[2] == 0xbf) {
12846
- // If the first three bytes of the source are the UTF-8 BOM, then we'll skip
12847
- // over them.
12848
13595
  parser->current.end += 3;
12849
- } else if (size >= 2 && source[0] == '#' && source[1] == '!') {
12850
- // If the first two bytes of the source are a shebang, then we'll indicate
12851
- // that the encoding comment is at the end of the shebang.
13596
+ parser->encoding_comment_start += 3;
13597
+ }
13598
+
13599
+ // If the first two bytes of the source are a shebang, then we'll indicate
13600
+ // that the encoding comment is at the end of the shebang.
13601
+ if (peek(parser) == '#' && peek_offset(parser, 1) == '!') {
12852
13602
  const char *encoding_comment_start = next_newline(source, (ptrdiff_t) size);
12853
13603
  if (encoding_comment_start) {
12854
13604
  parser->encoding_comment_start = encoding_comment_start + 1;