prism 0.29.0 → 0.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -1
- data/CONTRIBUTING.md +0 -4
- data/README.md +1 -0
- data/config.yml +66 -9
- data/docs/fuzzing.md +1 -1
- data/docs/ripper_translation.md +22 -0
- data/ext/prism/api_node.c +30 -12
- data/ext/prism/extension.c +107 -372
- data/ext/prism/extension.h +1 -1
- data/include/prism/ast.h +138 -70
- data/include/prism/diagnostic.h +7 -2
- data/include/prism/node.h +0 -21
- data/include/prism/parser.h +23 -25
- data/include/prism/regexp.h +17 -8
- data/include/prism/static_literals.h +3 -2
- data/include/prism/util/pm_char.h +1 -2
- data/include/prism/util/pm_constant_pool.h +0 -8
- data/include/prism/util/pm_integer.h +16 -9
- data/include/prism/util/pm_string.h +0 -8
- data/include/prism/version.h +2 -2
- data/include/prism.h +0 -11
- data/lib/prism/compiler.rb +3 -0
- data/lib/prism/dispatcher.rb +14 -0
- data/lib/prism/dot_visitor.rb +22 -3
- data/lib/prism/dsl.rb +7 -2
- data/lib/prism/ffi.rb +24 -3
- data/lib/prism/inspect_visitor.rb +10 -8
- data/lib/prism/mutation_compiler.rb +6 -1
- data/lib/prism/node.rb +166 -241
- data/lib/prism/node_ext.rb +21 -5
- data/lib/prism/parse_result/comments.rb +0 -7
- data/lib/prism/parse_result/newlines.rb +101 -11
- data/lib/prism/parse_result.rb +17 -0
- data/lib/prism/reflection.rb +3 -1
- data/lib/prism/serialize.rb +80 -67
- data/lib/prism/translation/parser/compiler.rb +134 -114
- data/lib/prism/translation/parser.rb +6 -1
- data/lib/prism/translation/ripper.rb +8 -6
- data/lib/prism/translation/ruby_parser.rb +23 -5
- data/lib/prism/visitor.rb +3 -0
- data/lib/prism.rb +0 -4
- data/prism.gemspec +1 -4
- data/rbi/prism/node.rbi +63 -6
- data/rbi/prism/visitor.rbi +3 -0
- data/rbi/prism.rbi +6 -0
- data/sig/prism/dsl.rbs +4 -1
- data/sig/prism/mutation_compiler.rbs +1 -0
- data/sig/prism/node.rbs +28 -4
- data/sig/prism/visitor.rbs +1 -0
- data/sig/prism.rbs +21 -0
- data/src/diagnostic.c +27 -17
- data/src/node.c +408 -1666
- data/src/prettyprint.c +49 -6
- data/src/prism.c +958 -991
- data/src/regexp.c +133 -68
- data/src/serialize.c +6 -1
- data/src/static_literals.c +63 -84
- data/src/token_type.c +2 -2
- data/src/util/pm_constant_pool.c +0 -8
- data/src/util/pm_integer.c +39 -11
- data/src/util/pm_string.c +0 -12
- data/src/util/pm_strpbrk.c +32 -6
- metadata +2 -5
- data/include/prism/util/pm_string_list.h +0 -44
- data/lib/prism/debug.rb +0 -249
- data/src/util/pm_string_list.c +0 -28
    
        data/src/regexp.c
    CHANGED
    
    | @@ -1,9 +1,14 @@ | |
| 1 1 | 
             
            #include "prism/regexp.h"
         | 
| 2 2 |  | 
| 3 | 
            +
            #define PM_REGEXP_PARSE_DEPTH_MAX 4096
         | 
| 4 | 
            +
             | 
| 3 5 | 
             
            /**
         | 
| 4 6 | 
             
             * This is the parser that is going to handle parsing regular expressions.
         | 
| 5 7 | 
             
             */
         | 
| 6 8 | 
             
            typedef struct {
         | 
| 9 | 
            +
                /** The parser that is currently being used. */
         | 
| 10 | 
            +
                pm_parser_t *parser;
         | 
| 11 | 
            +
             | 
| 7 12 | 
             
                /** A pointer to the start of the source that we are parsing. */
         | 
| 8 13 | 
             
                const uint8_t *start;
         | 
| 9 14 |  | 
| @@ -13,39 +18,42 @@ typedef struct { | |
| 13 18 | 
             
                /** A pointer to the end of the source that we are parsing. */
         | 
| 14 19 | 
             
                const uint8_t *end;
         | 
| 15 20 |  | 
| 16 | 
            -
                /** A list of named captures that we've found. */
         | 
| 17 | 
            -
                pm_string_list_t *named_captures;
         | 
| 18 | 
            -
             | 
| 19 21 | 
             
                /** Whether the encoding has changed from the default. */
         | 
| 20 22 | 
             
                bool encoding_changed;
         | 
| 21 23 |  | 
| 22 24 | 
             
                /** The encoding of the source. */
         | 
| 23 25 | 
             
                const pm_encoding_t *encoding;
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                /** The callback to call when a named capture group is found. */
         | 
| 28 | 
            +
                pm_regexp_name_callback_t name_callback;
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                /** The data to pass to the name callback. */
         | 
| 31 | 
            +
                void *name_data;
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                /** The callback to call when a parse error is found. */
         | 
| 34 | 
            +
                pm_regexp_error_callback_t error_callback;
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                /** The data to pass to the error callback. */
         | 
| 37 | 
            +
                void *error_data;
         | 
| 24 38 | 
             
            } pm_regexp_parser_t;
         | 
| 25 39 |  | 
| 26 40 | 
             
            /**
         | 
| 27 | 
            -
             *  | 
| 41 | 
            +
             * Append an error to the parser.
         | 
| 28 42 | 
             
             */
         | 
| 29 | 
            -
            static void
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                 | 
| 32 | 
            -
                    .start = start,
         | 
| 33 | 
            -
                    .cursor = start,
         | 
| 34 | 
            -
                    .end = end,
         | 
| 35 | 
            -
                    .named_captures = named_captures,
         | 
| 36 | 
            -
                    .encoding_changed = encoding_changed,
         | 
| 37 | 
            -
                    .encoding = encoding
         | 
| 38 | 
            -
                };
         | 
| 43 | 
            +
            static inline void
         | 
| 44 | 
            +
            pm_regexp_parse_error(pm_regexp_parser_t *parser, const uint8_t *start, const uint8_t *end, const char *message) {
         | 
| 45 | 
            +
                parser->error_callback(start, end, message, parser->error_data);
         | 
| 39 46 | 
             
            }
         | 
| 40 47 |  | 
| 41 48 | 
             
            /**
         | 
| 42 | 
            -
             * This appends a new string to the list of named captures.
         | 
| 49 | 
            +
             * This appends a new string to the list of named captures. This function
         | 
| 50 | 
            +
             * assumes the caller has already checked the validity of the name callback.
         | 
| 43 51 | 
             
             */
         | 
| 44 52 | 
             
            static void
         | 
| 45 53 | 
             
            pm_regexp_parser_named_capture(pm_regexp_parser_t *parser, const uint8_t *start, const uint8_t *end) {
         | 
| 46 54 | 
             
                pm_string_t string;
         | 
| 47 55 | 
             
                pm_string_shared_init(&string, start, end);
         | 
| 48 | 
            -
                 | 
| 56 | 
            +
                parser->name_callback(&string, parser->name_data);
         | 
| 49 57 | 
             
                pm_string_free(&string);
         | 
| 50 58 | 
             
            }
         | 
| 51 59 |  | 
| @@ -217,21 +225,24 @@ pm_regexp_parse_range_quantifier(pm_regexp_parser_t *parser) { | |
| 217 225 | 
             
             */
         | 
| 218 226 | 
             
            static bool
         | 
| 219 227 | 
             
            pm_regexp_parse_quantifier(pm_regexp_parser_t *parser) {
         | 
| 220 | 
            -
                 | 
| 221 | 
            -
             | 
| 222 | 
            -
             | 
| 223 | 
            -
             | 
| 224 | 
            -
             | 
| 225 | 
            -
             | 
| 226 | 
            -
             | 
| 227 | 
            -
                         | 
| 228 | 
            -
             | 
| 229 | 
            -
             | 
| 230 | 
            -
             | 
| 231 | 
            -
             | 
| 232 | 
            -
             | 
| 233 | 
            -
             | 
| 228 | 
            +
                while (!pm_regexp_char_is_eof(parser)) {
         | 
| 229 | 
            +
                    switch (*parser->cursor) {
         | 
| 230 | 
            +
                        case '*':
         | 
| 231 | 
            +
                        case '+':
         | 
| 232 | 
            +
                        case '?':
         | 
| 233 | 
            +
                            parser->cursor++;
         | 
| 234 | 
            +
                            break;
         | 
| 235 | 
            +
                        case '{':
         | 
| 236 | 
            +
                            parser->cursor++;
         | 
| 237 | 
            +
                            if (!pm_regexp_parse_range_quantifier(parser)) return false;
         | 
| 238 | 
            +
                            break;
         | 
| 239 | 
            +
                        default:
         | 
| 240 | 
            +
                            // In this case there is no quantifier.
         | 
| 241 | 
            +
                            return true;
         | 
| 242 | 
            +
                    }
         | 
| 234 243 | 
             
                }
         | 
| 244 | 
            +
             | 
| 245 | 
            +
                return true;
         | 
| 235 246 | 
             
            }
         | 
| 236 247 |  | 
| 237 248 | 
             
            /**
         | 
| @@ -255,20 +266,20 @@ pm_regexp_parse_posix_class(pm_regexp_parser_t *parser) { | |
| 255 266 |  | 
| 256 267 | 
             
            // Forward declaration because character sets can be nested.
         | 
| 257 268 | 
             
            static bool
         | 
| 258 | 
            -
            pm_regexp_parse_lbracket(pm_regexp_parser_t *parser);
         | 
| 269 | 
            +
            pm_regexp_parse_lbracket(pm_regexp_parser_t *parser, uint16_t depth);
         | 
| 259 270 |  | 
| 260 271 | 
             
            /**
         | 
| 261 272 | 
             
             * match-char-set : '[' '^'? (match-range | match-char)* ']'
         | 
| 262 273 | 
             
             *                ;
         | 
| 263 274 | 
             
             */
         | 
| 264 275 | 
             
            static bool
         | 
| 265 | 
            -
            pm_regexp_parse_character_set(pm_regexp_parser_t *parser) {
         | 
| 276 | 
            +
            pm_regexp_parse_character_set(pm_regexp_parser_t *parser, uint16_t depth) {
         | 
| 266 277 | 
             
                pm_regexp_char_accept(parser, '^');
         | 
| 267 278 |  | 
| 268 279 | 
             
                while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ']') {
         | 
| 269 280 | 
             
                    switch (*parser->cursor++) {
         | 
| 270 281 | 
             
                        case '[':
         | 
| 271 | 
            -
                            pm_regexp_parse_lbracket(parser);
         | 
| 282 | 
            +
                            pm_regexp_parse_lbracket(parser, (uint16_t) (depth + 1));
         | 
| 272 283 | 
             
                            break;
         | 
| 273 284 | 
             
                        case '\\':
         | 
| 274 285 | 
             
                            if (!pm_regexp_char_is_eof(parser)) {
         | 
| @@ -288,7 +299,18 @@ pm_regexp_parse_character_set(pm_regexp_parser_t *parser) { | |
| 288 299 | 
             
             * A left bracket can either mean a POSIX class or a character set.
         | 
| 289 300 | 
             
             */
         | 
| 290 301 | 
             
            static bool
         | 
| 291 | 
            -
            pm_regexp_parse_lbracket(pm_regexp_parser_t *parser) {
         | 
| 302 | 
            +
            pm_regexp_parse_lbracket(pm_regexp_parser_t *parser, uint16_t depth) {
         | 
| 303 | 
            +
                if (depth >= PM_REGEXP_PARSE_DEPTH_MAX) {
         | 
| 304 | 
            +
                    pm_regexp_parse_error(parser, parser->start, parser->end, "parse depth limit over");
         | 
| 305 | 
            +
                    return false;
         | 
| 306 | 
            +
                }
         | 
| 307 | 
            +
             | 
| 308 | 
            +
                if ((parser->cursor < parser->end) && parser->cursor[0] == ']') {
         | 
| 309 | 
            +
                    parser->cursor++;
         | 
| 310 | 
            +
                    pm_regexp_parse_error(parser, parser->cursor - 1, parser->cursor, "empty char-class");
         | 
| 311 | 
            +
                    return true;
         | 
| 312 | 
            +
                }
         | 
| 313 | 
            +
             | 
| 292 314 | 
             
                const uint8_t *reset = parser->cursor;
         | 
| 293 315 |  | 
| 294 316 | 
             
                if ((parser->cursor + 2 < parser->end) && parser->cursor[0] == '[' && parser->cursor[1] == ':') {
         | 
| @@ -298,13 +320,13 @@ pm_regexp_parse_lbracket(pm_regexp_parser_t *parser) { | |
| 298 320 | 
             
                    parser->cursor = reset;
         | 
| 299 321 | 
             
                }
         | 
| 300 322 |  | 
| 301 | 
            -
                return pm_regexp_parse_character_set(parser);
         | 
| 323 | 
            +
                return pm_regexp_parse_character_set(parser, depth);
         | 
| 302 324 | 
             
            }
         | 
| 303 325 |  | 
| 304 326 | 
             
            // Forward declaration here since parsing groups needs to go back up the grammar
         | 
| 305 327 | 
             
            // to parse expressions within them.
         | 
| 306 328 | 
             
            static bool
         | 
| 307 | 
            -
            pm_regexp_parse_expression(pm_regexp_parser_t *parser);
         | 
| 329 | 
            +
            pm_regexp_parse_expression(pm_regexp_parser_t *parser, uint16_t depth);
         | 
| 308 330 |  | 
| 309 331 | 
             
            /**
         | 
| 310 332 | 
             
             * These are the states of the options that are configurable on the regular
         | 
| @@ -418,17 +440,27 @@ pm_regexp_options_remove(pm_regexp_options_t *options, uint8_t key) { | |
| 418 440 | 
             
             * * (?imxdau-imx:subexp)          - turn on and off configuration for an expression
         | 
| 419 441 | 
             
             */
         | 
| 420 442 | 
             
            static bool
         | 
| 421 | 
            -
            pm_regexp_parse_group(pm_regexp_parser_t *parser) {
         | 
| 443 | 
            +
            pm_regexp_parse_group(pm_regexp_parser_t *parser, uint16_t depth) {
         | 
| 444 | 
            +
                const uint8_t *group_start = parser->cursor;
         | 
| 445 | 
            +
             | 
| 422 446 | 
             
                // First, parse any options for the group.
         | 
| 423 447 | 
             
                if (pm_regexp_char_accept(parser, '?')) {
         | 
| 424 448 | 
             
                    if (pm_regexp_char_is_eof(parser)) {
         | 
| 449 | 
            +
                        pm_regexp_parse_error(parser, group_start, parser->cursor, "end pattern in group");
         | 
| 425 450 | 
             
                        return false;
         | 
| 426 451 | 
             
                    }
         | 
| 452 | 
            +
             | 
| 427 453 | 
             
                    pm_regexp_options_t options;
         | 
| 428 454 | 
             
                    pm_regexp_options_init(&options);
         | 
| 429 455 |  | 
| 430 456 | 
             
                    switch (*parser->cursor) {
         | 
| 431 457 | 
             
                        case '#': { // inline comments
         | 
| 458 | 
            +
                            parser->cursor++;
         | 
| 459 | 
            +
                            if (pm_regexp_char_is_eof(parser)) {
         | 
| 460 | 
            +
                                pm_regexp_parse_error(parser, group_start, parser->cursor, "end pattern in group");
         | 
| 461 | 
            +
                                return false;
         | 
| 462 | 
            +
                            }
         | 
| 463 | 
            +
             | 
| 432 464 | 
             
                            if (parser->encoding_changed && parser->encoding->multibyte) {
         | 
| 433 465 | 
             
                                bool escaped = false;
         | 
| 434 466 |  | 
| @@ -472,6 +504,7 @@ pm_regexp_parse_group(pm_regexp_parser_t *parser) { | |
| 472 504 | 
             
                        case '<':
         | 
| 473 505 | 
             
                            parser->cursor++;
         | 
| 474 506 | 
             
                            if (pm_regexp_char_is_eof(parser)) {
         | 
| 507 | 
            +
                                pm_regexp_parse_error(parser, group_start, parser->cursor, "end pattern with unmatched parenthesis");
         | 
| 475 508 | 
             
                                return false;
         | 
| 476 509 | 
             
                            }
         | 
| 477 510 |  | 
| @@ -485,7 +518,15 @@ pm_regexp_parse_group(pm_regexp_parser_t *parser) { | |
| 485 518 | 
             
                                    if (!pm_regexp_char_find(parser, '>')) {
         | 
| 486 519 | 
             
                                        return false;
         | 
| 487 520 | 
             
                                    }
         | 
| 488 | 
            -
             | 
| 521 | 
            +
             | 
| 522 | 
            +
                                    if (parser->cursor - start == 1) {
         | 
| 523 | 
            +
                                        pm_regexp_parse_error(parser, start, parser->cursor, "group name is empty");
         | 
| 524 | 
            +
                                    }
         | 
| 525 | 
            +
             | 
| 526 | 
            +
                                    if (parser->name_callback != NULL) {
         | 
| 527 | 
            +
                                        pm_regexp_parser_named_capture(parser, start, parser->cursor - 1);
         | 
| 528 | 
            +
                                    }
         | 
| 529 | 
            +
             | 
| 489 530 | 
             
                                    break;
         | 
| 490 531 | 
             
                                }
         | 
| 491 532 | 
             
                            }
         | 
| @@ -496,7 +537,10 @@ pm_regexp_parse_group(pm_regexp_parser_t *parser) { | |
| 496 537 | 
             
                                return false;
         | 
| 497 538 | 
             
                            }
         | 
| 498 539 |  | 
| 499 | 
            -
                             | 
| 540 | 
            +
                            if (parser->name_callback != NULL) {
         | 
| 541 | 
            +
                                pm_regexp_parser_named_capture(parser, start, parser->cursor - 1);
         | 
| 542 | 
            +
                            }
         | 
| 543 | 
            +
             | 
| 500 544 | 
             
                            break;
         | 
| 501 545 | 
             
                        }
         | 
| 502 546 | 
             
                        case '(': // conditional expression
         | 
| @@ -535,20 +579,25 @@ pm_regexp_parse_group(pm_regexp_parser_t *parser) { | |
| 535 579 | 
             
                            }
         | 
| 536 580 | 
             
                            break;
         | 
| 537 581 | 
             
                        default:
         | 
| 538 | 
            -
                             | 
| 582 | 
            +
                            parser->cursor++;
         | 
| 583 | 
            +
                            pm_regexp_parse_error(parser, parser->cursor - 1, parser->cursor, "undefined group option");
         | 
| 584 | 
            +
                            break;
         | 
| 539 585 | 
             
                    }
         | 
| 540 586 | 
             
                }
         | 
| 541 587 |  | 
| 542 588 | 
             
                // Now, parse the expressions within this group.
         | 
| 543 589 | 
             
                while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ')') {
         | 
| 544 | 
            -
                    if (!pm_regexp_parse_expression(parser)) {
         | 
| 590 | 
            +
                    if (!pm_regexp_parse_expression(parser, (uint16_t) (depth + 1))) {
         | 
| 545 591 | 
             
                        return false;
         | 
| 546 592 | 
             
                    }
         | 
| 547 593 | 
             
                    pm_regexp_char_accept(parser, '|');
         | 
| 548 594 | 
             
                }
         | 
| 549 595 |  | 
| 550 596 | 
             
                // Finally, make sure we have a closing parenthesis.
         | 
| 551 | 
            -
                 | 
| 597 | 
            +
                if (pm_regexp_char_expect(parser, ')')) return true;
         | 
| 598 | 
            +
             | 
| 599 | 
            +
                pm_regexp_parse_error(parser, group_start, parser->cursor, "end pattern with unmatched parenthesis");
         | 
| 600 | 
            +
                return false;
         | 
| 552 601 | 
             
            }
         | 
| 553 602 |  | 
| 554 603 | 
             
            /**
         | 
| @@ -564,12 +613,12 @@ pm_regexp_parse_group(pm_regexp_parser_t *parser) { | |
| 564 613 | 
             
             *      ;
         | 
| 565 614 | 
             
             */
         | 
| 566 615 | 
             
            static bool
         | 
| 567 | 
            -
            pm_regexp_parse_item(pm_regexp_parser_t *parser) {
         | 
| 616 | 
            +
            pm_regexp_parse_item(pm_regexp_parser_t *parser, uint16_t depth) {
         | 
| 568 617 | 
             
                switch (*parser->cursor) {
         | 
| 569 618 | 
             
                    case '^':
         | 
| 570 619 | 
             
                    case '$':
         | 
| 571 620 | 
             
                        parser->cursor++;
         | 
| 572 | 
            -
                        return  | 
| 621 | 
            +
                        return pm_regexp_parse_quantifier(parser);
         | 
| 573 622 | 
             
                    case '\\':
         | 
| 574 623 | 
             
                        parser->cursor++;
         | 
| 575 624 | 
             
                        if (!pm_regexp_char_is_eof(parser)) {
         | 
| @@ -578,10 +627,20 @@ pm_regexp_parse_item(pm_regexp_parser_t *parser) { | |
| 578 627 | 
             
                        return pm_regexp_parse_quantifier(parser);
         | 
| 579 628 | 
             
                    case '(':
         | 
| 580 629 | 
             
                        parser->cursor++;
         | 
| 581 | 
            -
                        return pm_regexp_parse_group(parser) && pm_regexp_parse_quantifier(parser);
         | 
| 630 | 
            +
                        return pm_regexp_parse_group(parser, depth) && pm_regexp_parse_quantifier(parser);
         | 
| 582 631 | 
             
                    case '[':
         | 
| 583 632 | 
             
                        parser->cursor++;
         | 
| 584 | 
            -
                        return pm_regexp_parse_lbracket(parser) && pm_regexp_parse_quantifier(parser);
         | 
| 633 | 
            +
                        return pm_regexp_parse_lbracket(parser, depth) && pm_regexp_parse_quantifier(parser);
         | 
| 634 | 
            +
                    case '*':
         | 
| 635 | 
            +
                    case '?':
         | 
| 636 | 
            +
                    case '+':
         | 
| 637 | 
            +
                        parser->cursor++;
         | 
| 638 | 
            +
                        pm_regexp_parse_error(parser, parser->cursor - 1, parser->cursor, "target of repeat operator is not specified");
         | 
| 639 | 
            +
                        return true;
         | 
| 640 | 
            +
                    case ')':
         | 
| 641 | 
            +
                        parser->cursor++;
         | 
| 642 | 
            +
                        pm_regexp_parse_error(parser, parser->cursor - 1, parser->cursor, "unmatched close parenthesis");
         | 
| 643 | 
            +
                        return true;
         | 
| 585 644 | 
             
                    default: {
         | 
| 586 645 | 
             
                        size_t width;
         | 
| 587 646 | 
             
                        if (!parser->encoding_changed) {
         | 
| @@ -603,13 +662,18 @@ pm_regexp_parse_item(pm_regexp_parser_t *parser) { | |
| 603 662 | 
             
             *            ;
         | 
| 604 663 | 
             
             */
         | 
| 605 664 | 
             
            static bool
         | 
| 606 | 
            -
            pm_regexp_parse_expression(pm_regexp_parser_t *parser) {
         | 
| 607 | 
            -
                if ( | 
| 665 | 
            +
            pm_regexp_parse_expression(pm_regexp_parser_t *parser, uint16_t depth) {
         | 
| 666 | 
            +
                if (depth >= PM_REGEXP_PARSE_DEPTH_MAX) {
         | 
| 667 | 
            +
                    pm_regexp_parse_error(parser, parser->start, parser->end, "parse depth limit over");
         | 
| 668 | 
            +
                    return false;
         | 
| 669 | 
            +
                }
         | 
| 670 | 
            +
             | 
| 671 | 
            +
                if (!pm_regexp_parse_item(parser, depth)) {
         | 
| 608 672 | 
             
                    return false;
         | 
| 609 673 | 
             
                }
         | 
| 610 674 |  | 
| 611 675 | 
             
                while (!pm_regexp_char_is_eof(parser) && *parser->cursor != ')' && *parser->cursor != '|') {
         | 
| 612 | 
            -
                    if (!pm_regexp_parse_item(parser)) {
         | 
| 676 | 
            +
                    if (!pm_regexp_parse_item(parser, depth)) {
         | 
| 613 677 | 
             
                        return false;
         | 
| 614 678 | 
             
                    }
         | 
| 615 679 | 
             
                }
         | 
| @@ -625,29 +689,30 @@ pm_regexp_parse_expression(pm_regexp_parser_t *parser) { | |
| 625 689 | 
             
             */
         | 
| 626 690 | 
             
            static bool
         | 
| 627 691 | 
             
            pm_regexp_parse_pattern(pm_regexp_parser_t *parser) {
         | 
| 628 | 
            -
                 | 
| 629 | 
            -
                    (
         | 
| 630 | 
            -
             | 
| 631 | 
            -
             | 
| 632 | 
            -
             | 
| 633 | 
            -
             | 
| 634 | 
            -
                    ) &&
         | 
| 635 | 
            -
                    (
         | 
| 636 | 
            -
                        // Return now if we've parsed the entire pattern.
         | 
| 637 | 
            -
                        pm_regexp_char_is_eof(parser) ||
         | 
| 638 | 
            -
                        // Otherwise, we should have a pipe character.
         | 
| 639 | 
            -
                        (pm_regexp_char_expect(parser, '|') && pm_regexp_parse_pattern(parser))
         | 
| 640 | 
            -
                    )
         | 
| 641 | 
            -
                );
         | 
| 692 | 
            +
                do {
         | 
| 693 | 
            +
                    if (pm_regexp_char_is_eof(parser)) return true;
         | 
| 694 | 
            +
                    if (!pm_regexp_parse_expression(parser, 0)) return false;
         | 
| 695 | 
            +
                } while (pm_regexp_char_accept(parser, '|'));
         | 
| 696 | 
            +
             | 
| 697 | 
            +
                return pm_regexp_char_is_eof(parser);
         | 
| 642 698 | 
             
            }
         | 
| 643 699 |  | 
| 644 700 | 
             
            /**
         | 
| 645 701 | 
             
             * Parse a regular expression and extract the names of all of the named capture
         | 
| 646 702 | 
             
             * groups.
         | 
| 647 703 | 
             
             */
         | 
| 648 | 
            -
            PRISM_EXPORTED_FUNCTION  | 
| 649 | 
            -
             | 
| 650 | 
            -
                pm_regexp_parser_t  | 
| 651 | 
            -
             | 
| 652 | 
            -
             | 
| 704 | 
            +
            PRISM_EXPORTED_FUNCTION void
         | 
| 705 | 
            +
            pm_regexp_parse(pm_parser_t *parser, const uint8_t *source, size_t size, pm_regexp_name_callback_t name_callback, void *name_data, pm_regexp_error_callback_t error_callback, void *error_data) {
         | 
| 706 | 
            +
                pm_regexp_parse_pattern(&(pm_regexp_parser_t) {
         | 
| 707 | 
            +
                    .parser = parser,
         | 
| 708 | 
            +
                    .start = source,
         | 
| 709 | 
            +
                    .cursor = source,
         | 
| 710 | 
            +
                    .end = source + size,
         | 
| 711 | 
            +
                    .encoding_changed = parser->encoding_changed,
         | 
| 712 | 
            +
                    .encoding = parser->encoding,
         | 
| 713 | 
            +
                    .name_callback = name_callback,
         | 
| 714 | 
            +
                    .name_data = name_data,
         | 
| 715 | 
            +
                    .error_callback = error_callback,
         | 
| 716 | 
            +
                    .error_data = error_data
         | 
| 717 | 
            +
                });
         | 
| 653 718 | 
             
            }
         | 
    
        data/src/serialize.c
    CHANGED
    
    | @@ -1198,6 +1198,9 @@ pm_serialize_node(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { | |
| 1198 1198 | 
             
                        pm_serialize_location(parser, &((pm_interpolated_x_string_node_t *)node)->closing_loc, buffer);
         | 
| 1199 1199 | 
             
                        break;
         | 
| 1200 1200 | 
             
                    }
         | 
| 1201 | 
            +
                    case PM_IT_LOCAL_VARIABLE_READ_NODE: {
         | 
| 1202 | 
            +
                        break;
         | 
| 1203 | 
            +
                    }
         | 
| 1201 1204 | 
             
                    case PM_IT_PARAMETERS_NODE: {
         | 
| 1202 1205 | 
             
                        break;
         | 
| 1203 1206 | 
             
                    }
         | 
| @@ -1550,7 +1553,9 @@ pm_serialize_node(pm_parser_t *parser, pm_node_t *node, pm_buffer_t *buffer) { | |
| 1550 1553 | 
             
                        break;
         | 
| 1551 1554 | 
             
                    }
         | 
| 1552 1555 | 
             
                    case PM_RATIONAL_NODE: {
         | 
| 1553 | 
            -
                         | 
| 1556 | 
            +
                        pm_buffer_append_varuint(buffer, (uint32_t)(node->flags & ~PM_NODE_FLAG_COMMON_MASK));
         | 
| 1557 | 
            +
                        pm_serialize_integer(&((pm_rational_node_t *)node)->numerator, buffer);
         | 
| 1558 | 
            +
                        pm_serialize_integer(&((pm_rational_node_t *)node)->denominator, buffer);
         | 
| 1554 1559 | 
             
                        break;
         | 
| 1555 1560 | 
             
                    }
         | 
| 1556 1561 | 
             
                    case PM_REDO_NODE: {
         | 
    
        data/src/static_literals.c
    CHANGED
    
    | @@ -58,6 +58,25 @@ murmur_hash(const uint8_t *key, size_t length) { | |
| 58 58 | 
             
                return hash;
         | 
| 59 59 | 
             
            }
         | 
| 60 60 |  | 
| 61 | 
            +
            /**
         | 
| 62 | 
            +
             * Hash the value of an integer and return it.
         | 
| 63 | 
            +
             */
         | 
| 64 | 
            +
            static uint32_t
         | 
| 65 | 
            +
            integer_hash(const pm_integer_t *integer) {
         | 
| 66 | 
            +
                uint32_t hash;
         | 
| 67 | 
            +
                if (integer->values) {
         | 
| 68 | 
            +
                    hash = murmur_hash((const uint8_t *) integer->values, sizeof(uint32_t) * integer->length);
         | 
| 69 | 
            +
                } else {
         | 
| 70 | 
            +
                    hash = murmur_hash((const uint8_t *) &integer->value, sizeof(uint32_t));
         | 
| 71 | 
            +
                }
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                if (integer->negative) {
         | 
| 74 | 
            +
                    hash ^= murmur_scramble((uint32_t) 1);
         | 
| 75 | 
            +
                }
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                return hash;
         | 
| 78 | 
            +
            }
         | 
| 79 | 
            +
             | 
| 61 80 | 
             
            /**
         | 
| 62 81 | 
             
             * Return the hash of the given node. It is important that nodes that have
         | 
| 63 82 | 
             
             * equivalent static literal values have the same hash. This is because we use
         | 
| @@ -68,19 +87,8 @@ node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) | |
| 68 87 | 
             
                switch (PM_NODE_TYPE(node)) {
         | 
| 69 88 | 
             
                    case PM_INTEGER_NODE: {
         | 
| 70 89 | 
             
                        // Integers hash their value.
         | 
| 71 | 
            -
                        const  | 
| 72 | 
            -
                         | 
| 73 | 
            -
                        if (integer->values) {
         | 
| 74 | 
            -
                            hash = murmur_hash((const uint8_t *) integer->values, sizeof(uint32_t) * integer->length);
         | 
| 75 | 
            -
                        } else {
         | 
| 76 | 
            -
                            hash = murmur_hash((const uint8_t *) &integer->value, sizeof(uint32_t));
         | 
| 77 | 
            -
                        }
         | 
| 78 | 
            -
             | 
| 79 | 
            -
                        if (integer->negative) {
         | 
| 80 | 
            -
                            hash ^= murmur_scramble((uint32_t) 1);
         | 
| 81 | 
            -
                        }
         | 
| 82 | 
            -
             | 
| 83 | 
            -
                        return hash;
         | 
| 90 | 
            +
                        const pm_integer_node_t *cast = (const pm_integer_node_t *) node;
         | 
| 91 | 
            +
                        return integer_hash(&cast->value);
         | 
| 84 92 | 
             
                    }
         | 
| 85 93 | 
             
                    case PM_SOURCE_LINE_NODE: {
         | 
| 86 94 | 
             
                        // Source lines hash their line number.
         | 
| @@ -94,11 +102,9 @@ node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) | |
| 94 102 | 
             
                        return murmur_hash((const uint8_t *) value, sizeof(double));
         | 
| 95 103 | 
             
                    }
         | 
| 96 104 | 
             
                    case PM_RATIONAL_NODE: {
         | 
| 97 | 
            -
                        // Rationals hash their  | 
| 98 | 
            -
                         | 
| 99 | 
            -
                         | 
| 100 | 
            -
                        const pm_node_t *numeric = ((const pm_rational_node_t *) node)->numeric;
         | 
| 101 | 
            -
                        return node_hash(metadata, numeric) ^ murmur_scramble((uint32_t) node->type);
         | 
| 105 | 
            +
                        // Rationals hash their numerator and denominator.
         | 
| 106 | 
            +
                        const pm_rational_node_t *cast = (const pm_rational_node_t *) node;
         | 
| 107 | 
            +
                        return integer_hash(&cast->numerator) ^ integer_hash(&cast->denominator) ^ murmur_scramble((uint32_t) cast->base.type);
         | 
| 102 108 | 
             
                    }
         | 
| 103 109 | 
             
                    case PM_IMAGINARY_NODE: {
         | 
| 104 110 | 
             
                        // Imaginaries hash their numeric value. Because their numeric value
         | 
| @@ -148,7 +154,7 @@ node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node) | |
| 148 154 | 
             
             * and must be able to compare all node types that will be stored in this hash.
         | 
| 149 155 | 
             
             */
         | 
| 150 156 | 
             
            static pm_node_t *
         | 
| 151 | 
            -
            pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *metadata, pm_node_t *node, int (*compare)(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right)) {
         | 
| 157 | 
            +
            pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *metadata, pm_node_t *node, bool replace, int (*compare)(const pm_static_literals_metadata_t *metadata, const pm_node_t *left, const pm_node_t *right)) {
         | 
| 152 158 | 
             
                // If we are out of space, we need to resize the hash. This will cause all
         | 
| 153 159 | 
             
                // of the nodes to be rehashed and reinserted into the new hash.
         | 
| 154 160 | 
             
                if (hash->size * 2 >= hash->capacity) {
         | 
| @@ -196,9 +202,14 @@ pm_node_hash_insert(pm_node_hash_t *hash, const pm_static_literals_metadata_t *m | |
| 196 202 | 
             
                // already in the hash. Otherwise, we can just increment the size and insert
         | 
| 197 203 | 
             
                // the new node.
         | 
| 198 204 | 
             
                pm_node_t *result = hash->nodes[index];
         | 
| 199 | 
            -
                if (result == NULL) hash->size++;
         | 
| 200 205 |  | 
| 201 | 
            -
                 | 
| 206 | 
            +
                if (result == NULL) {
         | 
| 207 | 
            +
                    hash->size++;
         | 
| 208 | 
            +
                    hash->nodes[index] = node;
         | 
| 209 | 
            +
                } else if (replace) {
         | 
| 210 | 
            +
                    hash->nodes[index] = node;
         | 
| 211 | 
            +
                }
         | 
| 212 | 
            +
             | 
| 202 213 | 
             
                return result;
         | 
| 203 214 | 
             
            }
         | 
| 204 215 |  | 
| @@ -275,8 +286,15 @@ pm_compare_number_nodes(const pm_static_literals_metadata_t *metadata, const pm_ | |
| 275 286 | 
             
                switch (PM_NODE_TYPE(left)) {
         | 
| 276 287 | 
             
                    case PM_IMAGINARY_NODE:
         | 
| 277 288 | 
             
                        return pm_compare_number_nodes(metadata, ((const pm_imaginary_node_t *) left)->numeric, ((const pm_imaginary_node_t *) right)->numeric);
         | 
| 278 | 
            -
                    case PM_RATIONAL_NODE:
         | 
| 279 | 
            -
                         | 
| 289 | 
            +
                    case PM_RATIONAL_NODE: {
         | 
| 290 | 
            +
                        const pm_rational_node_t *left_rational = (const pm_rational_node_t *) left;
         | 
| 291 | 
            +
                        const pm_rational_node_t *right_rational = (const pm_rational_node_t *) right;
         | 
| 292 | 
            +
             | 
| 293 | 
            +
                        int result = pm_integer_compare(&left_rational->denominator, &right_rational->denominator);
         | 
| 294 | 
            +
                        if (result != 0) return result;
         | 
| 295 | 
            +
             | 
| 296 | 
            +
                        return pm_integer_compare(&left_rational->numerator, &right_rational->numerator);
         | 
| 297 | 
            +
                    }
         | 
| 280 298 | 
             
                    case PM_INTEGER_NODE:
         | 
| 281 299 | 
             
                        return pm_compare_integer_nodes(metadata, left, right);
         | 
| 282 300 | 
             
                    case PM_FLOAT_NODE:
         | 
| @@ -335,7 +353,7 @@ pm_compare_regular_expression_nodes(PRISM_ATTRIBUTE_UNUSED const pm_static_liter | |
| 335 353 | 
             
             * Add a node to the set of static literals.
         | 
| 336 354 | 
             
             */
         | 
| 337 355 | 
             
            pm_node_t *
         | 
| 338 | 
            -
            pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node) {
         | 
| 356 | 
            +
            pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line, pm_static_literals_t *literals, pm_node_t *node, bool replace) {
         | 
| 339 357 | 
             
                switch (PM_NODE_TYPE(node)) {
         | 
| 340 358 | 
             
                    case PM_INTEGER_NODE:
         | 
| 341 359 | 
             
                    case PM_SOURCE_LINE_NODE:
         | 
| @@ -347,6 +365,7 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line | |
| 347 365 | 
             
                                .encoding_name = NULL
         | 
| 348 366 | 
             
                            },
         | 
| 349 367 | 
             
                            node,
         | 
| 368 | 
            +
                            replace,
         | 
| 350 369 | 
             
                            pm_compare_integer_nodes
         | 
| 351 370 | 
             
                        );
         | 
| 352 371 | 
             
                    case PM_FLOAT_NODE:
         | 
| @@ -358,6 +377,7 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line | |
| 358 377 | 
             
                                .encoding_name = NULL
         | 
| 359 378 | 
             
                            },
         | 
| 360 379 | 
             
                            node,
         | 
| 380 | 
            +
                            replace,
         | 
| 361 381 | 
             
                            pm_compare_float_nodes
         | 
| 362 382 | 
             
                        );
         | 
| 363 383 | 
             
                    case PM_RATIONAL_NODE:
         | 
| @@ -370,6 +390,7 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line | |
| 370 390 | 
             
                                .encoding_name = NULL
         | 
| 371 391 | 
             
                            },
         | 
| 372 392 | 
             
                            node,
         | 
| 393 | 
            +
                            replace,
         | 
| 373 394 | 
             
                            pm_compare_number_nodes
         | 
| 374 395 | 
             
                        );
         | 
| 375 396 | 
             
                    case PM_STRING_NODE:
         | 
| @@ -382,6 +403,7 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line | |
| 382 403 | 
             
                                .encoding_name = NULL
         | 
| 383 404 | 
             
                            },
         | 
| 384 405 | 
             
                            node,
         | 
| 406 | 
            +
                            replace,
         | 
| 385 407 | 
             
                            pm_compare_string_nodes
         | 
| 386 408 | 
             
                        );
         | 
| 387 409 | 
             
                    case PM_REGULAR_EXPRESSION_NODE:
         | 
| @@ -393,6 +415,7 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line | |
| 393 415 | 
             
                                .encoding_name = NULL
         | 
| 394 416 | 
             
                            },
         | 
| 395 417 | 
             
                            node,
         | 
| 418 | 
            +
                            replace,
         | 
| 396 419 | 
             
                            pm_compare_regular_expression_nodes
         | 
| 397 420 | 
             
                        );
         | 
| 398 421 | 
             
                    case PM_SYMBOL_NODE:
         | 
| @@ -404,26 +427,27 @@ pm_static_literals_add(const pm_newline_list_t *newline_list, int32_t start_line | |
| 404 427 | 
             
                                .encoding_name = NULL
         | 
| 405 428 | 
             
                            },
         | 
| 406 429 | 
             
                            node,
         | 
| 430 | 
            +
                            replace,
         | 
| 407 431 | 
             
                            pm_compare_string_nodes
         | 
| 408 432 | 
             
                        );
         | 
| 409 433 | 
             
                    case PM_TRUE_NODE: {
         | 
| 410 434 | 
             
                        pm_node_t *duplicated = literals->true_node;
         | 
| 411 | 
            -
                        literals->true_node = node;
         | 
| 435 | 
            +
                        if ((duplicated == NULL) || replace) literals->true_node = node;
         | 
| 412 436 | 
             
                        return duplicated;
         | 
| 413 437 | 
             
                    }
         | 
| 414 438 | 
             
                    case PM_FALSE_NODE: {
         | 
| 415 439 | 
             
                        pm_node_t *duplicated = literals->false_node;
         | 
| 416 | 
            -
                        literals->false_node = node;
         | 
| 440 | 
            +
                        if ((duplicated == NULL) || replace) literals->false_node = node;
         | 
| 417 441 | 
             
                        return duplicated;
         | 
| 418 442 | 
             
                    }
         | 
| 419 443 | 
             
                    case PM_NIL_NODE: {
         | 
| 420 444 | 
             
                        pm_node_t *duplicated = literals->nil_node;
         | 
| 421 | 
            -
                        literals->nil_node = node;
         | 
| 445 | 
            +
                        if ((duplicated == NULL) || replace) literals->nil_node = node;
         | 
| 422 446 | 
             
                        return duplicated;
         | 
| 423 447 | 
             
                    }
         | 
| 424 448 | 
             
                    case PM_SOURCE_ENCODING_NODE: {
         | 
| 425 449 | 
             
                        pm_node_t *duplicated = literals->source_encoding_node;
         | 
| 426 | 
            -
                        literals->source_encoding_node = node;
         | 
| 450 | 
            +
                        if ((duplicated == NULL) || replace) literals->source_encoding_node = node;
         | 
| 427 451 | 
             
                        return duplicated;
         | 
| 428 452 | 
             
                    }
         | 
| 429 453 | 
             
                    default:
         | 
| @@ -456,7 +480,7 @@ pm_static_literal_positive_p(const pm_node_t *node) { | |
| 456 480 | 
             
                    case PM_INTEGER_NODE:
         | 
| 457 481 | 
             
                        return !((const pm_integer_node_t *) node)->value.negative;
         | 
| 458 482 | 
             
                    case PM_RATIONAL_NODE:
         | 
| 459 | 
            -
                        return  | 
| 483 | 
            +
                        return !((const pm_rational_node_t *) node)->numerator.negative;
         | 
| 460 484 | 
             
                    case PM_IMAGINARY_NODE:
         | 
| 461 485 | 
             
                        return pm_static_literal_positive_p(((const pm_imaginary_node_t *) node)->numeric);
         | 
| 462 486 | 
             
                    default:
         | 
| @@ -465,43 +489,6 @@ pm_static_literal_positive_p(const pm_node_t *node) { | |
| 465 489 | 
             
                }
         | 
| 466 490 | 
             
            }
         | 
| 467 491 |  | 
| 468 | 
            -
            /**
         | 
| 469 | 
            -
             * Inspect a rational node that wraps a float node. This is going to be a
         | 
| 470 | 
            -
             * poor-man's version of the Ruby `Rational#to_s` method, because we're not
         | 
| 471 | 
            -
             * going to try to reduce the rational by finding the GCD. We'll leave that for
         | 
| 472 | 
            -
             * a future improvement.
         | 
| 473 | 
            -
             */
         | 
| 474 | 
            -
            static void
         | 
| 475 | 
            -
            pm_rational_inspect(pm_buffer_t *buffer, pm_rational_node_t *node) {
         | 
| 476 | 
            -
                const uint8_t *start = node->base.location.start;
         | 
| 477 | 
            -
                const uint8_t *end = node->base.location.end - 1; // r
         | 
| 478 | 
            -
             | 
| 479 | 
            -
                while (start < end && *start == '0') start++; // 0.1 -> .1
         | 
| 480 | 
            -
                while (end > start && end[-1] == '0') end--; // 1.0 -> 1.
         | 
| 481 | 
            -
                size_t length = (size_t) (end - start);
         | 
| 482 | 
            -
             | 
| 483 | 
            -
                const uint8_t *point = memchr(start, '.', length);
         | 
| 484 | 
            -
                assert(point && "should have a decimal point");
         | 
| 485 | 
            -
             | 
| 486 | 
            -
                uint8_t *digits = malloc(length - 1);
         | 
| 487 | 
            -
                if (digits == NULL) return;
         | 
| 488 | 
            -
             | 
| 489 | 
            -
                memcpy(digits, start, (unsigned long) (point - start));
         | 
| 490 | 
            -
                memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1));
         | 
| 491 | 
            -
             | 
| 492 | 
            -
                pm_integer_t numerator = { 0 };
         | 
| 493 | 
            -
                pm_integer_parse(&numerator, PM_INTEGER_BASE_DECIMAL, digits, digits + length - 1);
         | 
| 494 | 
            -
             | 
| 495 | 
            -
                pm_buffer_append_byte(buffer, '(');
         | 
| 496 | 
            -
                pm_integer_string(buffer, &numerator);
         | 
| 497 | 
            -
                pm_buffer_append_string(buffer, "/1", 2);
         | 
| 498 | 
            -
                for (size_t index = 0; index < (size_t) (end - point - 1); index++) pm_buffer_append_byte(buffer, '0');
         | 
| 499 | 
            -
                pm_buffer_append_byte(buffer, ')');
         | 
| 500 | 
            -
             | 
| 501 | 
            -
                pm_integer_free(&numerator);
         | 
| 502 | 
            -
                free(digits);
         | 
| 503 | 
            -
            }
         | 
| 504 | 
            -
             | 
| 505 492 | 
             
            /**
         | 
| 506 493 | 
             
             * Create a string-based representation of the given static literal.
         | 
| 507 494 | 
             
             */
         | 
| @@ -544,7 +531,9 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met | |
| 544 531 | 
             
                        pm_buffer_append_string(buffer, "(0", 2);
         | 
| 545 532 | 
             
                        if (pm_static_literal_positive_p(numeric)) pm_buffer_append_byte(buffer, '+');
         | 
| 546 533 | 
             
                        pm_static_literal_inspect_node(buffer, metadata, numeric);
         | 
| 547 | 
            -
                        if (PM_NODE_TYPE_P(numeric, PM_RATIONAL_NODE))  | 
| 534 | 
            +
                        if (PM_NODE_TYPE_P(numeric, PM_RATIONAL_NODE)) {
         | 
| 535 | 
            +
                            pm_buffer_append_byte(buffer, '*');
         | 
| 536 | 
            +
                        }
         | 
| 548 537 | 
             
                        pm_buffer_append_string(buffer, "i)", 2);
         | 
| 549 538 | 
             
                        break;
         | 
| 550 539 | 
             
                    }
         | 
| @@ -555,22 +544,12 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met | |
| 555 544 | 
             
                        pm_buffer_append_string(buffer, "nil", 3);
         | 
| 556 545 | 
             
                        break;
         | 
| 557 546 | 
             
                    case PM_RATIONAL_NODE: {
         | 
| 558 | 
            -
                        const  | 
| 559 | 
            -
             | 
| 560 | 
            -
                         | 
| 561 | 
            -
             | 
| 562 | 
            -
             | 
| 563 | 
            -
             | 
| 564 | 
            -
                                pm_buffer_append_string(buffer, "/1)", 3);
         | 
| 565 | 
            -
                                break;
         | 
| 566 | 
            -
                            case PM_FLOAT_NODE:
         | 
| 567 | 
            -
                                pm_rational_inspect(buffer, (pm_rational_node_t *) node);
         | 
| 568 | 
            -
                                break;
         | 
| 569 | 
            -
                            default:
         | 
| 570 | 
            -
                                assert(false && "unreachable");
         | 
| 571 | 
            -
                                break;
         | 
| 572 | 
            -
                        }
         | 
| 573 | 
            -
             | 
| 547 | 
            +
                        const pm_rational_node_t *rational = (const pm_rational_node_t *) node;
         | 
| 548 | 
            +
                        pm_buffer_append_byte(buffer, '(');
         | 
| 549 | 
            +
                        pm_integer_string(buffer, &rational->numerator);
         | 
| 550 | 
            +
                        pm_buffer_append_byte(buffer, '/');
         | 
| 551 | 
            +
                        pm_integer_string(buffer, &rational->denominator);
         | 
| 552 | 
            +
                        pm_buffer_append_byte(buffer, ')');
         | 
| 574 553 | 
             
                        break;
         | 
| 575 554 | 
             
                    }
         | 
| 576 555 | 
             
                    case PM_REGULAR_EXPRESSION_NODE: {
         | 
| @@ -624,7 +603,7 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met | |
| 624 603 | 
             
            /**
         | 
| 625 604 | 
             
             * Create a string-based representation of the given static literal.
         | 
| 626 605 | 
             
             */
         | 
| 627 | 
            -
             | 
| 606 | 
            +
            void
         | 
| 628 607 | 
             
            pm_static_literal_inspect(pm_buffer_t *buffer, const pm_newline_list_t *newline_list, int32_t start_line, const char *encoding_name, const pm_node_t *node) {
         | 
| 629 608 | 
             
                pm_static_literal_inspect_node(
         | 
| 630 609 | 
             
                    buffer,
         |