diff options
Diffstat (limited to 'prism')
-rw-r--r-- | prism/extension.c | 21 | ||||
-rw-r--r-- | prism/templates/src/node.c.erb | 23 | ||||
-rw-r--r-- | prism/templates/src/prettyprint.c.erb | 31 | ||||
-rw-r--r-- | prism/util/pm_integer.c | 105 | ||||
-rw-r--r-- | prism/util/pm_integer.h | 9 |
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(©, 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(©, 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. * |