aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Newton <kddnewton@gmail.com>2024-05-08 15:32:24 -0400
committerKevin Newton <kddnewton@gmail.com>2024-05-21 14:27:46 -0400
commit89efb94fec9c78caab7ec4079bfe9e3f4e56a9a4 (patch)
tree7dd8b635def05ffa9b8bc15d4805c792b00dad22
parent42930d28a48bf7495180e81fd6e5cf742f3f47c7 (diff)
downloadruby-89efb94fec9c78caab7ec4079bfe9e3f4e56a9a4.tar.gz
[ruby/prism] Reconfigure rationals
This eliminates the subnode on RationalNode and replaces it with two integer fields, which represent the ratio for the rational. It also reduces those two integers if they both fit into 32 bits. Importantly, this PR does not implement bignum reduction. That's something I'd like to consider for the future, but it's simple enough for now to leave them unreduced, which makes it more useful than it used to be. https://github.com/ruby/prism/commit/86e06c7068
-rw-r--r--lib/prism/node_ext.rb2
-rw-r--r--lib/prism/translation/parser/compiler.rb24
-rw-r--r--prism/config.yml17
-rw-r--r--prism/prism.c72
-rw-r--r--prism/static_literals.c118
-rw-r--r--prism/util/pm_integer.c38
-rw-r--r--prism/util/pm_integer.h15
-rw-r--r--test/prism/snapshots/numbers.txt73
-rw-r--r--test/prism/snapshots/patterns.txt42
-rw-r--r--test/prism/snapshots/seattlerb/ruby21_numbers.txt14
-rw-r--r--test/prism/snapshots/symbols.txt7
-rw-r--r--test/prism/snapshots/unparser/corpus/literal/literal.txt26
-rw-r--r--test/prism/snapshots/unparser/corpus/semantic/literal.txt13
-rw-r--r--test/prism/snapshots/whitequark/complex.txt13
-rw-r--r--test/prism/snapshots/whitequark/rational.txt13
-rw-r--r--test/prism/snapshots/whitequark/ruby_bug_11873_a.txt24
16 files changed, 273 insertions, 238 deletions
diff --git a/lib/prism/node_ext.rb b/lib/prism/node_ext.rb
index ceec76b8d6..4761e5b9b2 100644
--- a/lib/prism/node_ext.rb
+++ b/lib/prism/node_ext.rb
@@ -103,7 +103,7 @@ module Prism
class RationalNode < Node
# Returns the value of the node as a Ruby Rational.
def value
- Rational(numeric.is_a?(IntegerNode) ? numeric.value : slice.chomp("r"))
+ Rational(numerator, denominator)
end
end
diff --git a/lib/prism/translation/parser/compiler.rb b/lib/prism/translation/parser/compiler.rb
index a6c3118efd..c3fa68231c 100644
--- a/lib/prism/translation/parser/compiler.rb
+++ b/lib/prism/translation/parser/compiler.rb
@@ -881,7 +881,7 @@ module Prism
# 1i
# ^^
def visit_imaginary_node(node)
- visit_numeric(node, builder.complex([imaginary_value(node), srange(node.location)]))
+ visit_numeric(node, builder.complex([Complex(0, node.numeric.value), srange(node.location)]))
end
# { foo: }
@@ -1514,7 +1514,7 @@ module Prism
# 1r
# ^^
def visit_rational_node(node)
- visit_numeric(node, builder.rational([rational_value(node), srange(node.location)]))
+ visit_numeric(node, builder.rational([node.value, srange(node.location)]))
end
# redo
@@ -1940,12 +1940,6 @@ module Prism
forwarding
end
- # Because we have mutated the AST to allow for newlines in the middle of
- # a rational, we need to manually handle the value here.
- def imaginary_value(node)
- Complex(0, node.numeric.is_a?(RationalNode) ? rational_value(node.numeric) : node.numeric.value)
- end
-
# Negate the value of a numeric node. This is a special case where you
# have a negative sign on one line and then a number on the next line.
# In normal Ruby, this will always be a method call. The parser gem,
@@ -1955,7 +1949,9 @@ module Prism
case receiver.type
when :integer_node, :float_node
receiver.copy(value: -receiver.value, location: message_loc.join(receiver.location))
- when :rational_node, :imaginary_node
+ when :rational_node
+ receiver.copy(numerator: -receiver.numerator, location: message_loc.join(receiver.location))
+ when :imaginary_node
receiver.copy(numeric: numeric_negate(message_loc, receiver.numeric), location: message_loc.join(receiver.location))
end
end
@@ -1974,16 +1970,6 @@ module Prism
parameters.block.nil?
end
- # Because we have mutated the AST to allow for newlines in the middle of
- # a rational, we need to manually handle the value here.
- def rational_value(node)
- if node.numeric.is_a?(IntegerNode)
- Rational(node.numeric.value)
- else
- Rational(node.slice.gsub(/\s/, "").chomp("r"))
- end
- end
-
# Locations in the parser gem AST are generated using this class. We
# store a reference to its constant to make it slightly faster to look
# up.
diff --git a/prism/config.yml b/prism/config.yml
index cfcfd2e7aa..08441ca4de 100644
--- a/prism/config.yml
+++ b/prism/config.yml
@@ -3231,8 +3231,21 @@ nodes:
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- name: RationalNode
fields:
- - name: numeric
- type: node
+ - name: flags
+ type: flags
+ kind: IntegerBaseFlags
+ - name: numerator
+ type: integer
+ comment: |
+ The numerator of the rational number.
+
+ 1.5r # numerator 3
+ - name: denominator
+ type: integer
+ comment: |
+ The denominator of the rational number.
+
+ 1.5r # denominator 2
comment: |
Represents a rational number literal.
diff --git a/prism/prism.c b/prism/prism.c
index e146799393..a47aa08ce2 100644
--- a/prism/prism.c
+++ b/prism/prism.c
@@ -4290,7 +4290,7 @@ pm_float_node_imaginary_create(pm_parser_t *parser, const pm_token_t *token) {
}
/**
- * Allocate and initialize a new FloatNode node from a FLOAT_RATIONAL token.
+ * Allocate and initialize a new RationalNode node from a FLOAT_RATIONAL token.
*/
static pm_rational_node_t *
pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) {
@@ -4300,16 +4300,44 @@ pm_float_node_rational_create(pm_parser_t *parser, const pm_token_t *token) {
*node = (pm_rational_node_t) {
{
.type = PM_RATIONAL_NODE,
- .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .flags = PM_INTEGER_BASE_FLAGS_DECIMAL | PM_NODE_FLAG_STATIC_LITERAL,
.location = PM_LOCATION_TOKEN_VALUE(token)
},
- .numeric = (pm_node_t *) pm_float_node_create(parser, &((pm_token_t) {
- .type = PM_TOKEN_FLOAT,
- .start = token->start,
- .end = token->end - 1
- }))
+ .numerator = { 0 },
+ .denominator = { 0 }
};
+ const uint8_t *start = token->start;
+ const uint8_t *end = token->end - 1; // r
+
+ while (start < end && *start == '0') start++; // 0.1 -> .1
+ while (end > start && end[-1] == '0') end--; // 1.0 -> 1.
+
+ size_t length = (size_t) (end - start);
+ if (length == 1) {
+ node->denominator.value = 1;
+ return node;
+ }
+
+ const uint8_t *point = memchr(start, '.', length);
+ assert(point && "should have a decimal point");
+
+ uint8_t *digits = malloc(length - 1);
+ if (digits == NULL) {
+ fputs("[pm_float_node_rational_create] Failed to allocate memory", stderr);
+ abort();
+ }
+
+ memcpy(digits, start, (unsigned long) (point - start));
+ memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1));
+ pm_integer_parse(&node->numerator, PM_INTEGER_BASE_DEFAULT, digits, digits + length - 1);
+
+ digits[0] = '1';
+ memset(digits + 1, '0', (size_t) (end - point - 1));
+ pm_integer_parse(&node->denominator, PM_INTEGER_BASE_DEFAULT, digits, digits + (end - point));
+ free(digits);
+
+ pm_integers_reduce(&node->numerator, &node->denominator);
return node;
}
@@ -4943,7 +4971,7 @@ pm_integer_node_imaginary_create(pm_parser_t *parser, pm_node_flags_t base, cons
}
/**
- * Allocate and initialize a new IntegerNode node from an INTEGER_RATIONAL
+ * Allocate and initialize a new RationalNode node from an INTEGER_RATIONAL
* token.
*/
static pm_rational_node_t *
@@ -4954,16 +4982,24 @@ pm_integer_node_rational_create(pm_parser_t *parser, pm_node_flags_t base, const
*node = (pm_rational_node_t) {
{
.type = PM_RATIONAL_NODE,
- .flags = PM_NODE_FLAG_STATIC_LITERAL,
+ .flags = base | PM_NODE_FLAG_STATIC_LITERAL,
.location = PM_LOCATION_TOKEN_VALUE(token)
},
- .numeric = (pm_node_t *) pm_integer_node_create(parser, base, &((pm_token_t) {
- .type = PM_TOKEN_INTEGER,
- .start = token->start,
- .end = token->end - 1
- }))
+ .numerator = { 0 },
+ .denominator = { .value = 1, 0 }
};
+ pm_integer_base_t integer_base = PM_INTEGER_BASE_DECIMAL;
+ switch (base) {
+ case PM_INTEGER_BASE_FLAGS_BINARY: integer_base = PM_INTEGER_BASE_BINARY; break;
+ case PM_INTEGER_BASE_FLAGS_OCTAL: integer_base = PM_INTEGER_BASE_OCTAL; break;
+ case PM_INTEGER_BASE_FLAGS_DECIMAL: break;
+ case PM_INTEGER_BASE_FLAGS_HEXADECIMAL: integer_base = PM_INTEGER_BASE_HEXADECIMAL; break;
+ default: assert(false && "unreachable"); break;
+ }
+
+ pm_integer_parse(&node->numerator, integer_base, token->start, token->end - 1);
+
return node;
}
@@ -16887,10 +16923,12 @@ parse_negative_numeric(pm_node_t *node) {
cast->value = -cast->value;
break;
}
- case PM_RATIONAL_NODE:
- node->location.start--;
- parse_negative_numeric(((pm_rational_node_t *) node)->numeric);
+ case PM_RATIONAL_NODE: {
+ pm_rational_node_t *cast = (pm_rational_node_t *) node;
+ cast->base.location.start--;
+ cast->numerator.negative = true;
break;
+ }
case PM_IMAGINARY_NODE:
node->location.start--;
parse_negative_numeric(((pm_imaginary_node_t *) node)->numeric);
diff --git a/prism/static_literals.c b/prism/static_literals.c
index 08f61c81fa..754a2d26b4 100644
--- a/prism/static_literals.c
+++ b/prism/static_literals.c
@@ -59,6 +59,25 @@ murmur_hash(const uint8_t *key, size_t length) {
}
/**
+ * Hash the value of an integer and return it.
+ */
+static uint32_t
+integer_hash(const pm_integer_t *integer) {
+ uint32_t hash;
+ if (integer->values) {
+ hash = murmur_hash((const uint8_t *) integer->values, sizeof(uint32_t) * integer->length);
+ } else {
+ hash = murmur_hash((const uint8_t *) &integer->value, sizeof(uint32_t));
+ }
+
+ if (integer->negative) {
+ hash ^= murmur_scramble((uint32_t) 1);
+ }
+
+ return hash;
+}
+
+/**
* Return the hash of the given node. It is important that nodes that have
* equivalent static literal values have the same hash. This is because we use
* these hashes to look for duplicates.
@@ -68,19 +87,8 @@ node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node)
switch (PM_NODE_TYPE(node)) {
case PM_INTEGER_NODE: {
// Integers hash their value.
- const pm_integer_t *integer = &((const pm_integer_node_t *) node)->value;
- uint32_t hash;
- if (integer->values) {
- hash = murmur_hash((const uint8_t *) integer->values, sizeof(uint32_t) * integer->length);
- } else {
- hash = murmur_hash((const uint8_t *) &integer->value, sizeof(uint32_t));
- }
-
- if (integer->negative) {
- hash ^= murmur_scramble((uint32_t) 1);
- }
-
- return hash;
+ const pm_integer_node_t *cast = (const pm_integer_node_t *) node;
+ return integer_hash(&cast->value);
}
case PM_SOURCE_LINE_NODE: {
// Source lines hash their line number.
@@ -94,11 +102,9 @@ node_hash(const pm_static_literals_metadata_t *metadata, const pm_node_t *node)
return murmur_hash((const uint8_t *) value, sizeof(double));
}
case PM_RATIONAL_NODE: {
- // Rationals hash their numeric value. Because their numeric value
- // is stored as a subnode, we hash that node and then mix in the
- // fact that this is a rational node.
- const pm_node_t *numeric = ((const pm_rational_node_t *) node)->numeric;
- return node_hash(metadata, numeric) ^ murmur_scramble((uint32_t) node->type);
+ // Rationals hash their numerator and denominator.
+ const pm_rational_node_t *cast = (const pm_rational_node_t *) node;
+ return integer_hash(&cast->numerator) ^ integer_hash(&cast->denominator) ^ murmur_scramble((uint32_t) cast->base.type);
}
case PM_IMAGINARY_NODE: {
// Imaginaries hash their numeric value. Because their numeric value
@@ -275,8 +281,15 @@ pm_compare_number_nodes(const pm_static_literals_metadata_t *metadata, const pm_
switch (PM_NODE_TYPE(left)) {
case PM_IMAGINARY_NODE:
return pm_compare_number_nodes(metadata, ((const pm_imaginary_node_t *) left)->numeric, ((const pm_imaginary_node_t *) right)->numeric);
- case PM_RATIONAL_NODE:
- return pm_compare_number_nodes(metadata, ((const pm_rational_node_t *) left)->numeric, ((const pm_rational_node_t *) right)->numeric);
+ case PM_RATIONAL_NODE: {
+ const pm_rational_node_t *left_rational = (const pm_rational_node_t *) left;
+ const pm_rational_node_t *right_rational = (const pm_rational_node_t *) right;
+
+ int result = pm_integer_compare(&left_rational->denominator, &right_rational->denominator);
+ if (result != 0) return result;
+
+ return pm_integer_compare(&left_rational->numerator, &right_rational->numerator);
+ }
case PM_INTEGER_NODE:
return pm_compare_integer_nodes(metadata, left, right);
case PM_FLOAT_NODE:
@@ -456,7 +469,7 @@ pm_static_literal_positive_p(const pm_node_t *node) {
case PM_INTEGER_NODE:
return !((const pm_integer_node_t *) node)->value.negative;
case PM_RATIONAL_NODE:
- return pm_static_literal_positive_p(((const pm_rational_node_t *) node)->numeric);
+ return !((const pm_rational_node_t *) node)->numerator.negative;
case PM_IMAGINARY_NODE:
return pm_static_literal_positive_p(((const pm_imaginary_node_t *) node)->numeric);
default:
@@ -466,43 +479,6 @@ pm_static_literal_positive_p(const pm_node_t *node) {
}
/**
- * Inspect a rational node that wraps a float node. This is going to be a
- * poor-man's version of the Ruby `Rational#to_s` method, because we're not
- * going to try to reduce the rational by finding the GCD. We'll leave that for
- * a future improvement.
- */
-static void
-pm_rational_inspect(pm_buffer_t *buffer, pm_rational_node_t *node) {
- const uint8_t *start = node->base.location.start;
- const uint8_t *end = node->base.location.end - 1; // r
-
- while (start < end && *start == '0') start++; // 0.1 -> .1
- while (end > start && end[-1] == '0') end--; // 1.0 -> 1.
- size_t length = (size_t) (end - start);
-
- const uint8_t *point = memchr(start, '.', length);
- assert(point && "should have a decimal point");
-
- uint8_t *digits = malloc(length - 1);
- if (digits == NULL) return;
-
- memcpy(digits, start, (unsigned long) (point - start));
- memcpy(digits + (point - start), point + 1, (unsigned long) (end - point - 1));
-
- pm_integer_t numerator = { 0 };
- pm_integer_parse(&numerator, PM_INTEGER_BASE_DECIMAL, digits, digits + length - 1);
-
- pm_buffer_append_byte(buffer, '(');
- pm_integer_string(buffer, &numerator);
- pm_buffer_append_string(buffer, "/1", 2);
- for (size_t index = 0; index < (size_t) (end - point - 1); index++) pm_buffer_append_byte(buffer, '0');
- pm_buffer_append_byte(buffer, ')');
-
- pm_integer_free(&numerator);
- free(digits);
-}
-
-/**
* Create a string-based representation of the given static literal.
*/
static inline void
@@ -544,7 +520,9 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
pm_buffer_append_string(buffer, "(0", 2);
if (pm_static_literal_positive_p(numeric)) pm_buffer_append_byte(buffer, '+');
pm_static_literal_inspect_node(buffer, metadata, numeric);
- if (PM_NODE_TYPE_P(numeric, PM_RATIONAL_NODE)) pm_buffer_append_byte(buffer, '*');
+ if (PM_NODE_TYPE_P(numeric, PM_RATIONAL_NODE)) {
+ pm_buffer_append_byte(buffer, '*');
+ }
pm_buffer_append_string(buffer, "i)", 2);
break;
}
@@ -555,22 +533,12 @@ pm_static_literal_inspect_node(pm_buffer_t *buffer, const pm_static_literals_met
pm_buffer_append_string(buffer, "nil", 3);
break;
case PM_RATIONAL_NODE: {
- const pm_node_t *numeric = ((const pm_rational_node_t *) node)->numeric;
-
- switch (PM_NODE_TYPE(numeric)) {
- case PM_INTEGER_NODE:
- pm_buffer_append_byte(buffer, '(');
- pm_static_literal_inspect_node(buffer, metadata, numeric);
- pm_buffer_append_string(buffer, "/1)", 3);
- break;
- case PM_FLOAT_NODE:
- pm_rational_inspect(buffer, (pm_rational_node_t *) node);
- break;
- default:
- assert(false && "unreachable");
- break;
- }
-
+ const pm_rational_node_t *rational = (const pm_rational_node_t *) node;
+ pm_buffer_append_byte(buffer, '(');
+ pm_integer_string(buffer, &rational->numerator);
+ pm_buffer_append_byte(buffer, '/');
+ pm_integer_string(buffer, &rational->denominator);
+ pm_buffer_append_byte(buffer, ')');
break;
}
case PM_REGULAR_EXPRESSION_NODE: {
diff --git a/prism/util/pm_integer.c b/prism/util/pm_integer.c
index e523bae90b..015789ccec 100644
--- a/prism/util/pm_integer.c
+++ b/prism/util/pm_integer.c
@@ -473,13 +473,16 @@ pm_integer_parse_big(pm_integer_t *integer, uint32_t multiplier, const uint8_t *
*/
PRISM_EXPORTED_FUNCTION void
pm_integer_parse(pm_integer_t *integer, pm_integer_base_t base, const uint8_t *start, const uint8_t *end) {
- // Ignore unary +. Unary + is parsed differently and will not end up here.
+ // Ignore unary +. Unary - is parsed differently and will not end up here.
// Instead, it will modify the parsed integer later.
if (*start == '+') start++;
// Determine the multiplier from the base, and skip past any prefixes.
uint32_t multiplier = 10;
switch (base) {
+ case PM_INTEGER_BASE_DEFAULT:
+ while (*start == '0') start++; // 01 -> 1
+ break;
case PM_INTEGER_BASE_BINARY:
start += 2; // 0b
multiplier = 2;
@@ -573,6 +576,39 @@ pm_integer_compare(const pm_integer_t *left, const pm_integer_t *right) {
}
/**
+ * Reduce a ratio of integers to its simplest form.
+ */
+void pm_integers_reduce(pm_integer_t *numerator, pm_integer_t *denominator) {
+ // If either the numerator or denominator do not fit into a 32-bit integer,
+ // then this function is a no-op. In the future, we may consider reducing
+ // even the larger numbers, but for now we're going to keep it simple.
+ if (
+ // If the numerator doesn't fit into a 32-bit integer, return early.
+ numerator->length != 0 ||
+ // If the denominator doesn't fit into a 32-bit integer, return early.
+ denominator->length != 0 ||
+ // If the numerator is 0, then return early.
+ numerator->value == 0 ||
+ // If the denominator is 1, then return early.
+ denominator->value == 1
+ ) return;
+
+ // Find the greatest common divisor of the numerator and denominator.
+ uint32_t divisor = numerator->value;
+ uint32_t remainder = denominator->value;
+
+ while (remainder != 0) {
+ uint32_t temporary = remainder;
+ remainder = divisor % remainder;
+ divisor = temporary;
+ }
+
+ // Divide the numerator and denominator by the greatest common divisor.
+ numerator->value /= divisor;
+ denominator->value /= divisor;
+}
+
+/**
* Convert an integer to a decimal string.
*/
PRISM_EXPORTED_FUNCTION void
diff --git a/prism/util/pm_integer.h b/prism/util/pm_integer.h
index 7f172988b3..d2a1808b19 100644
--- a/prism/util/pm_integer.h
+++ b/prism/util/pm_integer.h
@@ -48,6 +48,9 @@ typedef struct {
* from the string itself.
*/
typedef enum {
+ /** The default decimal base, with no prefix. Leading 0s will be ignored. */
+ PM_INTEGER_BASE_DEFAULT,
+
/** The binary base, indicated by a 0b or 0B prefix. */
PM_INTEGER_BASE_BINARY,
@@ -101,6 +104,18 @@ size_t pm_integer_memsize(const pm_integer_t *integer);
int pm_integer_compare(const pm_integer_t *left, const pm_integer_t *right);
/**
+ * Reduce a ratio of integers to its simplest form.
+ *
+ * If either the numerator or denominator do not fit into a 32-bit integer, then
+ * this function is a no-op. In the future, we may consider reducing even the
+ * larger numbers, but for now we're going to keep it simple.
+ *
+ * @param numerator The numerator of the ratio.
+ * @param denominator The denominator of the ratio.
+ */
+void pm_integers_reduce(pm_integer_t *numerator, pm_integer_t *denominator);
+
+/**
* Convert an integer to a decimal string.
*
* @param buffer The buffer to append the string to.
diff --git a/test/prism/snapshots/numbers.txt b/test/prism/snapshots/numbers.txt
index 740f3f5a2a..58aea454fa 100644
--- a/test/prism/snapshots/numbers.txt
+++ b/test/prism/snapshots/numbers.txt
@@ -65,52 +65,48 @@
│ ├── flags: decimal
│ └── value: 1
├── @ RationalNode (location: (41,0)-(41,2))
- │ └── numeric:
- │ @ IntegerNode (location: (41,0)-(41,1))
- │ ├── flags: decimal
- │ └── value: 1
+ │ ├── flags: decimal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ IntegerNode (location: (43,0)-(43,2))
│ ├── flags: decimal
│ └── value: -1
├── @ ImaginaryNode (location: (45,0)-(45,3))
│ └── numeric:
│ @ RationalNode (location: (45,0)-(45,2))
- │ └── numeric:
- │ @ IntegerNode (location: (45,0)-(45,1))
- │ ├── flags: decimal
- │ └── value: 1
+ │ ├── flags: decimal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ RationalNode (location: (47,0)-(47,4))
- │ └── numeric:
- │ @ FloatNode (location: (47,0)-(47,3))
- │ └── value: 1.2
+ │ ├── flags: decimal
+ │ ├── numerator: 6
+ │ └── denominator: 5
├── @ ImaginaryNode (location: (49,0)-(49,5))
│ └── numeric:
│ @ RationalNode (location: (49,0)-(49,4))
- │ └── numeric:
- │ @ FloatNode (location: (49,0)-(49,3))
- │ └── value: 1.2
+ │ ├── flags: decimal
+ │ ├── numerator: 6
+ │ └── denominator: 5
├── @ ImaginaryNode (location: (51,0)-(51,4))
│ └── numeric:
│ @ RationalNode (location: (51,0)-(51,3))
- │ └── numeric:
- │ @ IntegerNode (location: (51,0)-(51,2))
- │ ├── flags: decimal
- │ └── value: -1
+ │ ├── flags: decimal
+ │ ├── numerator: -1
+ │ └── denominator: 1
├── @ RationalNode (location: (53,0)-(53,5))
- │ └── numeric:
- │ @ FloatNode (location: (53,0)-(53,4))
- │ └── value: -1.2
+ │ ├── flags: decimal
+ │ ├── numerator: -6
+ │ └── denominator: 5
├── @ ImaginaryNode (location: (55,0)-(55,6))
│ └── numeric:
│ @ RationalNode (location: (55,0)-(55,5))
- │ └── numeric:
- │ @ FloatNode (location: (55,0)-(55,4))
- │ └── value: -1.2
+ │ ├── flags: decimal
+ │ ├── numerator: -6
+ │ └── denominator: 5
├── @ RationalNode (location: (57,0)-(57,4))
- │ └── numeric:
- │ @ IntegerNode (location: (57,0)-(57,3))
- │ ├── flags: octal
- │ └── value: 1
+ │ ├── flags: octal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ ImaginaryNode (location: (59,0)-(59,4))
│ └── numeric:
│ @ IntegerNode (location: (59,0)-(59,3))
@@ -119,15 +115,13 @@
├── @ ImaginaryNode (location: (61,0)-(61,5))
│ └── numeric:
│ @ RationalNode (location: (61,0)-(61,4))
- │ └── numeric:
- │ @ IntegerNode (location: (61,0)-(61,3))
- │ ├── flags: octal
- │ └── value: 1
+ │ ├── flags: octal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ RationalNode (location: (63,0)-(63,4))
- │ └── numeric:
- │ @ IntegerNode (location: (63,0)-(63,3))
- │ ├── flags: decimal
- │ └── value: 1
+ │ ├── flags: decimal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ ImaginaryNode (location: (65,0)-(65,4))
│ └── numeric:
│ @ IntegerNode (location: (65,0)-(65,3))
@@ -136,7 +130,6 @@
└── @ ImaginaryNode (location: (67,0)-(67,5))
└── numeric:
@ RationalNode (location: (67,0)-(67,4))
- └── numeric:
- @ IntegerNode (location: (67,0)-(67,3))
- ├── flags: binary
- └── value: 1
+ ├── flags: binary
+ ├── numerator: 1
+ └── denominator: 1
diff --git a/test/prism/snapshots/patterns.txt b/test/prism/snapshots/patterns.txt
index 16298e7984..17aa23b4b9 100644
--- a/test/prism/snapshots/patterns.txt
+++ b/test/prism/snapshots/patterns.txt
@@ -86,10 +86,9 @@
│ │ └── block: ∅
│ ├── pattern:
│ │ @ RationalNode (location: (5,7)-(5,9))
- │ │ └── numeric:
- │ │ @ IntegerNode (location: (5,7)-(5,8))
- │ │ ├── flags: decimal
- │ │ └── value: 1
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 1
+ │ │ └── denominator: 1
│ └── operator_loc: (5,4)-(5,6) = "=>"
├── @ MatchRequiredNode (location: (6,0)-(6,11))
│ ├── value:
@@ -598,16 +597,14 @@
│ │ ├── flags: ∅
│ │ ├── left:
│ │ │ @ RationalNode (location: (31,7)-(31,9))
- │ │ │ └── numeric:
- │ │ │ @ IntegerNode (location: (31,7)-(31,8))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
+ │ │ │ ├── flags: decimal
+ │ │ │ ├── numerator: 1
+ │ │ │ └── denominator: 1
│ │ ├── right:
│ │ │ @ RationalNode (location: (31,13)-(31,15))
- │ │ │ └── numeric:
- │ │ │ @ IntegerNode (location: (31,13)-(31,14))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
+ │ │ │ ├── flags: decimal
+ │ │ │ ├── numerator: 1
+ │ │ │ └── denominator: 1
│ │ └── operator_loc: (31,10)-(31,12) = ".."
│ └── operator_loc: (31,4)-(31,6) = "=>"
├── @ MatchRequiredNode (location: (32,0)-(32,19))
@@ -2461,10 +2458,9 @@
│ │ └── block: ∅
│ ├── pattern:
│ │ @ RationalNode (location: (108,7)-(108,9))
- │ │ └── numeric:
- │ │ @ IntegerNode (location: (108,7)-(108,8))
- │ │ ├── flags: decimal
- │ │ └── value: 1
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 1
+ │ │ └── denominator: 1
│ └── operator_loc: (108,4)-(108,6) = "in"
├── @ MatchPredicateNode (location: (109,0)-(109,11))
│ ├── value:
@@ -3017,10 +3013,9 @@
│ │ └── @ InNode (location: (139,10)-(139,20))
│ │ ├── pattern:
│ │ │ @ RationalNode (location: (139,13)-(139,15))
- │ │ │ └── numeric:
- │ │ │ @ IntegerNode (location: (139,13)-(139,14))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
+ │ │ │ ├── flags: decimal
+ │ │ │ ├── numerator: 1
+ │ │ │ └── denominator: 1
│ │ ├── statements: ∅
│ │ ├── in_loc: (139,10)-(139,12) = "in"
│ │ └── then_loc: (139,16)-(139,20) = "then"
@@ -3758,10 +3753,9 @@
│ │ │ │ @ StatementsNode (location: (166,13)-(166,15))
│ │ │ │ └── body: (length: 1)
│ │ │ │ └── @ RationalNode (location: (166,13)-(166,15))
- │ │ │ │ └── numeric:
- │ │ │ │ @ IntegerNode (location: (166,13)-(166,14))
- │ │ │ │ ├── flags: decimal
- │ │ │ │ └── value: 1
+ │ │ │ │ ├── flags: decimal
+ │ │ │ │ ├── numerator: 1
+ │ │ │ │ └── denominator: 1
│ │ │ ├── consequent: ∅
│ │ │ └── end_keyword_loc: ∅
│ │ ├── statements: ∅
diff --git a/test/prism/snapshots/seattlerb/ruby21_numbers.txt b/test/prism/snapshots/seattlerb/ruby21_numbers.txt
index 34a3452d1b..e7eec943f8 100644
--- a/test/prism/snapshots/seattlerb/ruby21_numbers.txt
+++ b/test/prism/snapshots/seattlerb/ruby21_numbers.txt
@@ -12,16 +12,14 @@
│ │ ├── flags: decimal
│ │ └── value: 1
│ ├── @ RationalNode (location: (1,5)-(1,7))
- │ │ └── numeric:
- │ │ @ IntegerNode (location: (1,5)-(1,6))
- │ │ ├── flags: decimal
- │ │ └── value: 2
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 2
+ │ │ └── denominator: 1
│ └── @ ImaginaryNode (location: (1,9)-(1,12))
│ └── numeric:
│ @ RationalNode (location: (1,9)-(1,11))
- │ └── numeric:
- │ @ IntegerNode (location: (1,9)-(1,10))
- │ ├── flags: decimal
- │ └── value: 3
+ │ ├── flags: decimal
+ │ ├── numerator: 3
+ │ └── denominator: 1
├── opening_loc: (1,0)-(1,1) = "["
└── closing_loc: (1,12)-(1,13) = "]"
diff --git a/test/prism/snapshots/symbols.txt b/test/prism/snapshots/symbols.txt
index dbd3a4d030..48ff0d634f 100644
--- a/test/prism/snapshots/symbols.txt
+++ b/test/prism/snapshots/symbols.txt
@@ -146,10 +146,9 @@
│ │ ├── @ FloatNode (location: (29,4)-(29,7))
│ │ │ └── value: 1.0
│ │ ├── @ RationalNode (location: (29,9)-(29,11))
- │ │ │ └── numeric:
- │ │ │ @ IntegerNode (location: (29,9)-(29,10))
- │ │ │ ├── flags: decimal
- │ │ │ └── value: 1
+ │ │ │ ├── flags: decimal
+ │ │ │ ├── numerator: 1
+ │ │ │ └── denominator: 1
│ │ └── @ ImaginaryNode (location: (29,13)-(29,15))
│ │ └── numeric:
│ │ @ IntegerNode (location: (29,13)-(29,14))
diff --git a/test/prism/snapshots/unparser/corpus/literal/literal.txt b/test/prism/snapshots/unparser/corpus/literal/literal.txt
index 98b88e11ce..ddb10456bf 100644
--- a/test/prism/snapshots/unparser/corpus/literal/literal.txt
+++ b/test/prism/snapshots/unparser/corpus/literal/literal.txt
@@ -316,18 +316,17 @@
│ ├── flags: decimal
│ └── value: 1
├── @ RationalNode (location: (19,0)-(19,2))
- │ └── numeric:
- │ @ IntegerNode (location: (19,0)-(19,1))
- │ ├── flags: decimal
- │ └── value: 1
+ │ ├── flags: decimal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ RationalNode (location: (20,0)-(20,4))
- │ └── numeric:
- │ @ FloatNode (location: (20,0)-(20,3))
- │ └── value: 1.5
+ │ ├── flags: decimal
+ │ ├── numerator: 3
+ │ └── denominator: 2
├── @ RationalNode (location: (21,0)-(21,4))
- │ └── numeric:
- │ @ FloatNode (location: (21,0)-(21,3))
- │ └── value: 1.3
+ │ ├── flags: decimal
+ │ ├── numerator: 13
+ │ └── denominator: 10
├── @ ImaginaryNode (location: (22,0)-(22,2))
│ └── numeric:
│ @ IntegerNode (location: (22,0)-(22,1))
@@ -354,10 +353,9 @@
├── @ ImaginaryNode (location: (27,0)-(27,3))
│ └── numeric:
│ @ RationalNode (location: (27,0)-(27,2))
- │ └── numeric:
- │ @ IntegerNode (location: (27,0)-(27,1))
- │ ├── flags: decimal
- │ └── value: 1
+ │ ├── flags: decimal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ InterpolatedStringNode (location: (28,0)-(28,11))
│ ├── flags: ∅
│ ├── opening_loc: ∅
diff --git a/test/prism/snapshots/unparser/corpus/semantic/literal.txt b/test/prism/snapshots/unparser/corpus/semantic/literal.txt
index ef666890be..448207f5d3 100644
--- a/test/prism/snapshots/unparser/corpus/semantic/literal.txt
+++ b/test/prism/snapshots/unparser/corpus/semantic/literal.txt
@@ -4,14 +4,13 @@
@ StatementsNode (location: (1,0)-(14,10))
└── body: (length: 14)
├── @ RationalNode (location: (1,0)-(1,4))
- │ └── numeric:
- │ @ FloatNode (location: (1,0)-(1,3))
- │ └── value: 1.0
+ │ ├── flags: decimal
+ │ ├── numerator: 1
+ │ └── denominator: 1
├── @ RationalNode (location: (2,0)-(2,3))
- │ └── numeric:
- │ @ IntegerNode (location: (2,0)-(2,2))
- │ ├── flags: decimal
- │ └── value: 0
+ │ ├── flags: decimal
+ │ ├── numerator: 0
+ │ └── denominator: 1
├── @ IntegerNode (location: (3,0)-(3,3))
│ ├── flags: hexadecimal
│ └── value: 1
diff --git a/test/prism/snapshots/whitequark/complex.txt b/test/prism/snapshots/whitequark/complex.txt
index e688585a5f..bc748db09b 100644
--- a/test/prism/snapshots/whitequark/complex.txt
+++ b/test/prism/snapshots/whitequark/complex.txt
@@ -10,9 +10,9 @@
├── @ ImaginaryNode (location: (3,0)-(3,6))
│ └── numeric:
│ @ RationalNode (location: (3,0)-(3,5))
- │ └── numeric:
- │ @ FloatNode (location: (3,0)-(3,4))
- │ └── value: 42.1
+ │ ├── flags: decimal
+ │ ├── numerator: 421
+ │ └── denominator: 10
├── @ ImaginaryNode (location: (5,0)-(5,3))
│ └── numeric:
│ @ IntegerNode (location: (5,0)-(5,2))
@@ -21,7 +21,6 @@
└── @ ImaginaryNode (location: (7,0)-(7,4))
└── numeric:
@ RationalNode (location: (7,0)-(7,3))
- └── numeric:
- @ IntegerNode (location: (7,0)-(7,2))
- ├── flags: decimal
- └── value: 42
+ ├── flags: decimal
+ ├── numerator: 42
+ └── denominator: 1
diff --git a/test/prism/snapshots/whitequark/rational.txt b/test/prism/snapshots/whitequark/rational.txt
index 90bbd17929..e8c8eed508 100644
--- a/test/prism/snapshots/whitequark/rational.txt
+++ b/test/prism/snapshots/whitequark/rational.txt
@@ -4,11 +4,10 @@
@ StatementsNode (location: (1,0)-(3,3))
└── body: (length: 2)
├── @ RationalNode (location: (1,0)-(1,5))
- │ └── numeric:
- │ @ FloatNode (location: (1,0)-(1,4))
- │ └── value: 42.1
+ │ ├── flags: decimal
+ │ ├── numerator: 421
+ │ └── denominator: 10
└── @ RationalNode (location: (3,0)-(3,3))
- └── numeric:
- @ IntegerNode (location: (3,0)-(3,2))
- ├── flags: decimal
- └── value: 42
+ ├── flags: decimal
+ ├── numerator: 42
+ └── denominator: 1
diff --git a/test/prism/snapshots/whitequark/ruby_bug_11873_a.txt b/test/prism/snapshots/whitequark/ruby_bug_11873_a.txt
index 93418e6448..831d57e30d 100644
--- a/test/prism/snapshots/whitequark/ruby_bug_11873_a.txt
+++ b/test/prism/snapshots/whitequark/ruby_bug_11873_a.txt
@@ -225,9 +225,9 @@
│ │ │ ├── closing_loc: (7,7)-(7,8) = ")"
│ │ │ └── block: ∅
│ │ └── @ RationalNode (location: (7,10)-(7,14))
- │ │ └── numeric:
- │ │ @ FloatNode (location: (7,10)-(7,13))
- │ │ └── value: 1.0
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 1
+ │ │ └── denominator: 1
│ ├── closing_loc: ∅
│ └── block:
│ @ BlockNode (location: (7,15)-(7,21))
@@ -519,9 +519,9 @@
│ │ │ ├── closing_loc: (17,8)-(17,9) = ")"
│ │ │ └── block: ∅
│ │ └── @ RationalNode (location: (17,11)-(17,15))
- │ │ └── numeric:
- │ │ @ FloatNode (location: (17,11)-(17,14))
- │ │ └── value: 1.0
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 1
+ │ │ └── denominator: 1
│ ├── closing_loc: ∅
│ └── block:
│ @ BlockNode (location: (17,16)-(17,22))
@@ -833,9 +833,9 @@
│ │ │ ├── opening_loc: (27,3)-(27,4) = "{"
│ │ │ └── closing_loc: (27,7)-(27,8) = "}"
│ │ └── @ RationalNode (location: (27,10)-(27,14))
- │ │ └── numeric:
- │ │ @ FloatNode (location: (27,10)-(27,13))
- │ │ └── value: 1.0
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 1
+ │ │ └── denominator: 1
│ ├── closing_loc: ∅
│ └── block:
│ @ BlockNode (location: (27,15)-(27,21))
@@ -1152,9 +1152,9 @@
│ │ │ ├── opening_loc: (37,3)-(37,4) = "{"
│ │ │ └── closing_loc: (37,8)-(37,9) = "}"
│ │ └── @ RationalNode (location: (37,11)-(37,15))
- │ │ └── numeric:
- │ │ @ FloatNode (location: (37,11)-(37,14))
- │ │ └── value: 1.0
+ │ │ ├── flags: decimal
+ │ │ ├── numerator: 1
+ │ │ └── denominator: 1
│ ├── closing_loc: ∅
│ └── block:
│ @ BlockNode (location: (37,16)-(37,22))