| 679 | } |
| 680 | |
| 681 | GDScriptTokenizer::Token GDScriptTokenizerText::number() { |
| 682 | int base = 10; |
| 683 | bool has_decimal = false; |
| 684 | bool has_exponent = false; |
| 685 | bool has_error = false; |
| 686 | bool need_digits = false; |
| 687 | bool (*digit_check_func)(char32_t) = is_digit; |
| 688 | |
| 689 | // Sign before hexadecimal or binary. |
| 690 | if ((_peek(-1) == '+' || _peek(-1) == '-') && _peek() == '0') { |
| 691 | _advance(); |
| 692 | } |
| 693 | |
| 694 | if (_peek(-1) == '.') { |
| 695 | has_decimal = true; |
| 696 | } else if (_peek(-1) == '0') { |
| 697 | if (_peek() == 'x' || _peek() == 'X') { |
| 698 | // Hexadecimal. |
| 699 | base = 16; |
| 700 | digit_check_func = is_hex_digit; |
| 701 | need_digits = true; |
| 702 | _advance(); |
| 703 | } else if (_peek() == 'b' || _peek() == 'B') { |
| 704 | // Binary. |
| 705 | base = 2; |
| 706 | digit_check_func = is_binary_digit; |
| 707 | need_digits = true; |
| 708 | _advance(); |
| 709 | } |
| 710 | } |
| 711 | |
| 712 | if (base != 10 && is_underscore(_peek())) { // Disallow `0x_` and `0b_`. |
| 713 | Token error = make_error(vformat(R"(Unexpected underscore after "0%c".)", _peek(-1))); |
| 714 | error.start_column = column; |
| 715 | error.end_column = column + 1; |
| 716 | push_error(error); |
| 717 | has_error = true; |
| 718 | } |
| 719 | bool previous_was_underscore = false; // Allow `_` to be used in a number, for readability. |
| 720 | while (digit_check_func(_peek()) || is_underscore(_peek())) { |
| 721 | if (is_underscore(_peek())) { |
| 722 | if (previous_was_underscore) { |
| 723 | Token error = make_error(R"(Multiple underscores cannot be adjacent in a numeric literal.)"); |
| 724 | error.start_column = column; |
| 725 | error.end_column = column + 1; |
| 726 | push_error(error); |
| 727 | } |
| 728 | previous_was_underscore = true; |
| 729 | } else { |
| 730 | need_digits = false; |
| 731 | previous_was_underscore = false; |
| 732 | } |
| 733 | _advance(); |
| 734 | } |
| 735 | |
| 736 | // It might be a ".." token (instead of decimal point) so we check if it's not. |
| 737 | if (_peek() == '.' && _peek(1) != '.') { |
| 738 | if (base == 10 && !has_decimal) { |
nothing calls this directly
no test coverage detected