summaryrefslogtreecommitdiff
path: root/prism
diff options
context:
space:
mode:
Diffstat (limited to 'prism')
-rw-r--r--prism/extension.c21
-rw-r--r--prism/templates/src/node.c.erb23
-rw-r--r--prism/templates/src/prettyprint.c.erb31
-rw-r--r--prism/util/pm_integer.c105
-rw-r--r--prism/util/pm_integer.h9
5 files changed, 134 insertions, 55 deletions
diff --git a/prism/extension.c b/prism/extension.c
index 3eccb20e5a..d0ffa2f936 100644
--- a/prism/extension.c
+++ b/prism/extension.c
@@ -953,9 +953,10 @@ named_captures(VALUE self, VALUE source) {
/**
* call-seq:
- * Debug::integer_parse(source) -> Integer
+ * Debug::integer_parse(source) -> [Integer, String]
*
- * Parses the given source string and returns the integer it represents.
+ * Parses the given source string and returns the integer it represents, as well
+ * as a decimal string representation.
*/
static VALUE
integer_parse(VALUE self, VALUE source) {
@@ -965,17 +966,27 @@ integer_parse(VALUE self, VALUE source) {
pm_integer_t integer = { 0 };
pm_integer_parse(&integer, PM_INTEGER_BASE_UNKNOWN, start, start + length);
- VALUE result = UINT2NUM(integer.head.value);
+ VALUE number = UINT2NUM(integer.head.value);
size_t shift = 0;
for (pm_integer_word_t *node = integer.head.next; node != NULL; node = node->next) {
VALUE receiver = rb_funcall(UINT2NUM(node->value), rb_intern("<<"), 1, ULONG2NUM(++shift * 32));
- result = rb_funcall(receiver, rb_intern("|"), 1, result);
+ number = rb_funcall(receiver, rb_intern("|"), 1, number);
}
- if (integer.negative) result = rb_funcall(result, rb_intern("-@"), 0);
+ if (integer.negative) number = rb_funcall(number, rb_intern("-@"), 0);
+
+ pm_buffer_t buffer = { 0 };
+ pm_integer_string(&buffer, &integer);
+
+ VALUE string = rb_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer));
+ pm_buffer_free(&buffer);
pm_integer_free(&integer);
+ VALUE result = rb_ary_new_capa(2);
+ rb_ary_push(result, number);
+ rb_ary_push(result, string);
+
return result;
}
diff --git a/prism/templates/src/node.c.erb b/prism/templates/src/node.c.erb
index 554645c3e7..f9c58c82e1 100644
--- a/prism/templates/src/node.c.erb
+++ b/prism/templates/src/node.c.erb
@@ -277,28 +277,7 @@ pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *no
<%- end -%>
pm_buffer_append_byte(buffer, ']');
<%- when Prism::IntegerField -%>
- {
- const pm_integer_t *integer = &cast-><%= field.name %>;
- if (integer->length == 0) {
- if (integer->negative) pm_buffer_append_byte(buffer, '-');
- pm_buffer_append_string(buffer, "%" PRIu32, integer->head.value);
- } else if (integer->length == 1) {
- if (integer->negative) pm_buffer_append_byte(buffer, '-');
- pm_buffer_append_format(buffer, "%" PRIu64, ((uint64_t) integer->head.value) | (((uint64_t) integer->head.next->value) << 32));
- } else {
- pm_buffer_append_byte(buffer, '{');
- pm_buffer_append_format(buffer, "\"negative\": %s", integer->negative ? "true" : "false");
- pm_buffer_append_string(buffer, ",\"values\":[", 11);
-
- const pm_integer_word_t *node = &integer->head;
- while (node != NULL) {
- pm_buffer_append_format(buffer, "%" PRIu32, node->value);
- node = node->next;
- if (node != NULL) pm_buffer_append_byte(buffer, ',');
- }
- pm_buffer_append_string(buffer, "]}", 2);
- }
- }
+ pm_integer_string(buffer, &cast-><%= field.name %>);
<%- when Prism::DoubleField -%>
pm_buffer_append_format(buffer, "%f", cast-><%= field.name %>);
<%- else -%>
diff --git a/prism/templates/src/prettyprint.c.erb b/prism/templates/src/prettyprint.c.erb
index 67a2f444e6..2278d0d38c 100644
--- a/prism/templates/src/prettyprint.c.erb
+++ b/prism/templates/src/prettyprint.c.erb
@@ -128,34 +128,9 @@ prettyprint_node(pm_buffer_t *output_buffer, const pm_parser_t *parser, const pm
pm_buffer_append_byte(output_buffer, '\n');
<%- when Prism::IntegerField -%>
const pm_integer_t *integer = &cast-><%= field.name %>;
- if (integer->length == 0) {
- pm_buffer_append_byte(output_buffer, ' ');
- if (integer->negative) pm_buffer_append_byte(output_buffer, '-');
- pm_buffer_append_format(output_buffer, "%" PRIu32 "\n", integer->head.value);
- } else if (integer->length == 1) {
- pm_buffer_append_byte(output_buffer, ' ');
- if (integer->negative) pm_buffer_append_byte(output_buffer, '-');
- pm_buffer_append_format(output_buffer, "%" PRIu64 "\n", ((uint64_t) integer->head.value) | (((uint64_t) integer->head.next->value) << 32));
- } else {
- pm_buffer_append_byte(output_buffer, ' ');
-
- const pm_integer_word_t *node = &integer->head;
- uint32_t index = 0;
-
- while (node != NULL) {
- if (index != 0) pm_buffer_append_string(output_buffer, " | ", 3);
- pm_buffer_append_format(output_buffer, "%" PRIu32, node->value);
-
- if (index != 0) {
- pm_buffer_append_string(output_buffer, " << ", 4);
- pm_buffer_append_format(output_buffer, "%" PRIu32, index * 32);
- }
-
- node = node->next;
- index++;
- }
- pm_buffer_append_string(output_buffer, "]\n", 2);
- }
+ pm_buffer_append_byte(output_buffer, ' ');
+ pm_integer_string(output_buffer, integer);
+ pm_buffer_append_byte(output_buffer, '\n');
<%- when Prism::DoubleField -%>
pm_buffer_append_format(output_buffer, " %f\n", cast-><%= field.name %>);
<%- else -%>
diff --git a/prism/util/pm_integer.c b/prism/util/pm_integer.c
index e39108a05b..98a09243ff 100644
--- a/prism/util/pm_integer.c
+++ b/prism/util/pm_integer.c
@@ -15,6 +15,31 @@ pm_integer_node_create(pm_integer_t *integer, uint32_t value) {
}
/**
+ * Copy one integer onto another.
+ */
+static void
+pm_integer_copy(pm_integer_t *dest, const pm_integer_t *src) {
+ dest->negative = src->negative;
+ dest->length = 0;
+
+ dest->head.value = src->head.value;
+ dest->head.next = NULL;
+
+ pm_integer_word_t *dest_current = &dest->head;
+ const pm_integer_word_t *src_current = src->head.next;
+
+ while (src_current != NULL) {
+ dest_current->next = pm_integer_node_create(dest, src_current->value);
+ if (dest_current->next == NULL) return;
+
+ dest_current = dest_current->next;
+ src_current = src_current->next;
+ }
+
+ dest_current->next = NULL;
+}
+
+/**
* Add a 32-bit integer to an integer.
*/
static void
@@ -59,6 +84,37 @@ pm_integer_multiply(pm_integer_t *integer, uint32_t multiplier) {
}
/**
+ * Divide an individual word by a 32-bit integer. This will recursively divide
+ * any subsequent nodes in the linked list.
+ */
+static uint32_t
+pm_integer_divide_word(pm_integer_t *integer, pm_integer_word_t *word, uint32_t dividend) {
+ uint32_t remainder = 0;
+ if (word->next != NULL) {
+ remainder = pm_integer_divide_word(integer, word->next, dividend);
+
+ if (integer->length > 0 && word->next->value == 0) {
+ free(word->next);
+ word->next = NULL;
+ integer->length--;
+ }
+ }
+
+ uint64_t value = ((uint64_t) remainder << 32) | word->value;
+ word->value = (uint32_t) (value / dividend);
+ return (uint32_t) (value % dividend);
+}
+
+/**
+ * Divide an integer by a 32-bit integer. In practice, this is only 10 so that
+ * we can format it as a string. It returns the remainder of the division.
+ */
+static uint32_t
+pm_integer_divide(pm_integer_t *integer, uint32_t dividend) {
+ return pm_integer_divide_word(integer, &integer->head, dividend);
+}
+
+/**
* Return the value of a digit in a uint32_t.
*/
static uint32_t
@@ -178,6 +234,55 @@ pm_integer_compare(const pm_integer_t *left, const pm_integer_t *right) {
}
/**
+ * Convert an integer to a decimal string.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer) {
+ if (integer->negative) {
+ pm_buffer_append_byte(buffer, '-');
+ }
+
+ switch (integer->length) {
+ case 0: {
+ const uint32_t value = integer->head.value;
+ pm_buffer_append_format(buffer, "%" PRIu32, value);
+ return;
+ }
+ case 1: {
+ const uint64_t value = ((uint64_t) integer->head.value) | (((uint64_t) integer->head.next->value) << 32);
+ pm_buffer_append_format(buffer, "%" PRIu64, value);
+ return;
+ }
+ default: {
+ // First, allocate a buffer that we'll copy the decimal digits into.
+ size_t length = (integer->length + 1) * 10;
+ char *digits = calloc(length, sizeof(char));
+ if (digits == NULL) return;
+
+ // Next, create a new integer that we'll use to store the result of
+ // the division and modulo operations.
+ pm_integer_t copy;
+ pm_integer_copy(&copy, integer);
+
+ // Then, iterate through the integer, dividing by 10 and storing the
+ // result in the buffer.
+ char *ending = digits + length - 1;
+ char *current = ending;
+
+ while (copy.length > 0 || copy.head.value > 0) {
+ uint32_t remainder = pm_integer_divide(&copy, 10);
+ *current-- = (char) ('0' + remainder);
+ }
+
+ // Finally, append the string to the buffer and free the digits.
+ pm_buffer_append_string(buffer, current + 1, (size_t) (ending - current));
+ free(digits);
+ return;
+ }
+ }
+}
+
+/**
* Recursively destroy the linked list of an integer.
*/
static void
diff --git a/prism/util/pm_integer.h b/prism/util/pm_integer.h
index a80db633bb..4e080870ae 100644
--- a/prism/util/pm_integer.h
+++ b/prism/util/pm_integer.h
@@ -7,6 +7,7 @@
#define PRISM_NUMBER_H
#include "prism/defines.h"
+#include "prism/util/pm_buffer.h"
#include <assert.h>
#include <stdbool.h>
@@ -105,6 +106,14 @@ size_t pm_integer_memsize(const pm_integer_t *integer);
int pm_integer_compare(const pm_integer_t *left, const pm_integer_t *right);
/**
+ * Convert an integer to a decimal string.
+ *
+ * @param buffer The buffer to append the string to.
+ * @param integer The integer to convert to a string.
+ */
+PRISM_EXPORTED_FUNCTION void pm_integer_string(pm_buffer_t *buffer, const pm_integer_t *integer);
+
+/**
* Free the internal memory of an integer. This memory will only be allocated if
* the integer exceeds the size of a single node in the linked list.
*