diff --git a/include/prism.h b/include/prism.h index 08d216cbb5d..afd57483fd3 100644 --- a/include/prism.h +++ b/include/prism.h @@ -187,6 +187,16 @@ const char * pm_token_type_human(pm_token_type_t token_type); */ PRISM_EXPORTED_FUNCTION void pm_parser_errors_format(const pm_parser_t *parser, pm_buffer_t *buffer, bool colorize); +/** + * Visit each of the nodes in this subtree using the given visitor callback. + * + * @param node The node to visit. + * @param visitor The visitor callback to use. It should return `true` if the + * visitor should continue visiting nodes, and `false` if it should stop. + * @param data The optional data to pass to the visitor. + */ +PRISM_EXPORTED_FUNCTION void pm_node_visit(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data); + /** * @mainpage * diff --git a/include/prism/util/pm_buffer.h b/include/prism/util/pm_buffer.h index f0cca84af57..513633baa16 100644 --- a/include/prism/util/pm_buffer.h +++ b/include/prism/util/pm_buffer.h @@ -7,6 +7,7 @@ #define PRISM_BUFFER_H #include "prism/defines.h" +#include "prism/util/pm_char.h" #include #include @@ -59,7 +60,7 @@ PRISM_EXPORTED_FUNCTION bool pm_buffer_init(pm_buffer_t *buffer); * @param buffer The buffer to get the value of. * @returns The value of the buffer. */ -PRISM_EXPORTED_FUNCTION char * pm_buffer_value(pm_buffer_t *buffer); +PRISM_EXPORTED_FUNCTION char * pm_buffer_value(const pm_buffer_t *buffer); /** * Return the length of the buffer. @@ -67,7 +68,7 @@ PRISM_EXPORTED_FUNCTION char * pm_buffer_value(pm_buffer_t *buffer); * @param buffer The buffer to get the length of. * @returns The length of the buffer. */ -PRISM_EXPORTED_FUNCTION size_t pm_buffer_length(pm_buffer_t *buffer); +PRISM_EXPORTED_FUNCTION size_t pm_buffer_length(const pm_buffer_t *buffer); /** * Append the given amount of space as zeroes to the buffer. @@ -145,6 +146,21 @@ void pm_buffer_prepend_string(pm_buffer_t *buffer, const char *value, size_t len */ void pm_buffer_concat(pm_buffer_t *destination, const pm_buffer_t *source); +/** + * Clear the buffer by reducing its size to 0. This does not free the allocated + * memory, but it does allow the buffer to be reused. + * + * @param buffer The buffer to clear. + */ +void pm_buffer_clear(pm_buffer_t *buffer); + +/** + * Strip the whitespace from the end of the buffer. + * + * @param buffer The buffer to strip. + */ +void pm_buffer_rstrip(pm_buffer_t *buffer); + /** * Free the memory associated with the buffer. * diff --git a/include/prism/util/pm_string.h b/include/prism/util/pm_string.h index ddb153784f4..8a568a82380 100644 --- a/include/prism/util/pm_string.h +++ b/include/prism/util/pm_string.h @@ -124,6 +124,18 @@ size_t pm_string_memsize(const pm_string_t *string); */ void pm_string_ensure_owned(pm_string_t *string); +/** + * Compare the underlying lengths and bytes of two strings. Returns 0 if the + * strings are equal, a negative number if the left string is less than the + * right string, and a positive number if the left string is greater than the + * right string. + * + * @param left The left string to compare. + * @param right The right string to compare. + * @return The comparison result. + */ +int pm_string_compare(const pm_string_t *left, const pm_string_t *right); + /** * Returns the length associated with the string. * diff --git a/src/util/pm_buffer.c b/src/util/pm_buffer.c index 0ae94454288..129bed52cd3 100644 --- a/src/util/pm_buffer.c +++ b/src/util/pm_buffer.c @@ -32,7 +32,7 @@ pm_buffer_init(pm_buffer_t *buffer) { * Return the value of the buffer. */ char * -pm_buffer_value(pm_buffer_t *buffer) { +pm_buffer_value(const pm_buffer_t *buffer) { return buffer->value; } @@ -40,7 +40,7 @@ pm_buffer_value(pm_buffer_t *buffer) { * Return the length of the buffer. */ size_t -pm_buffer_length(pm_buffer_t *buffer) { +pm_buffer_length(const pm_buffer_t *buffer) { return buffer->length; } @@ -181,6 +181,25 @@ pm_buffer_concat(pm_buffer_t *destination, const pm_buffer_t *source) { } } +/** + * Clear the buffer by reducing its size to 0. This does not free the allocated + * memory, but it does allow the buffer to be reused. + */ +void +pm_buffer_clear(pm_buffer_t *buffer) { + buffer->length = 0; +} + +/** + * Strip the whitespace from the end of the buffer. + */ +void +pm_buffer_rstrip(pm_buffer_t *buffer) { + while (buffer->length > 0 && pm_char_is_whitespace((uint8_t) buffer->value[buffer->length - 1])) { + buffer->length--; + } +} + /** * Free the memory associated with the buffer. */ diff --git a/src/util/pm_string.c b/src/util/pm_string.c index e67fcee0ec1..8f3ef92c9b3 100644 --- a/src/util/pm_string.c +++ b/src/util/pm_string.c @@ -168,6 +168,26 @@ pm_string_ensure_owned(pm_string_t *string) { memcpy((void *) string->source, source, length); } +/** + * Compare the underlying lengths and bytes of two strings. Returns 0 if the + * strings are equal, a negative number if the left string is less than the + * right string, and a positive number if the left string is greater than the + * right string. + */ +int +pm_string_compare(const pm_string_t *left, const pm_string_t *right) { + size_t left_length = pm_string_length(left); + size_t right_length = pm_string_length(right); + + if (left_length < right_length) { + return -1; + } else if (left_length > right_length) { + return 1; + } + + return memcmp(pm_string_source(left), pm_string_source(right), left_length); +} + /** * Returns the length associated with the string. */ diff --git a/templates/src/node.c.erb b/templates/src/node.c.erb index 0552767f4df..a7135d598e0 100644 --- a/templates/src/node.c.erb +++ b/templates/src/node.c.erb @@ -160,3 +160,45 @@ pm_node_type_to_str(pm_node_type_t node_type) } return ""; } + +/** + * Visit each of the nodes in this subtree using the given visitor callback. + */ +PRISM_EXPORTED_FUNCTION void +pm_node_visit(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) { + if (!visitor(node, data)) return; + + switch (PM_NODE_TYPE(node)) { + <%- nodes.each do |node| -%> + <%- if (fields = node.fields.select { |field| field.is_a?(Prism::NodeField) || field.is_a?(Prism::OptionalNodeField) || field.is_a?(Prism::NodeListField) }).any? -%> + case <%= node.type %>: { + const pm_<%= node.human %>_t *cast = (const pm_<%= node.human %>_t *) node; + <%- fields.each do |field| -%> + + // Visit the <%= field.name %> field + <%- case field -%> + <%- when Prism::NodeField -%> + pm_node_visit((const pm_node_t *) cast-><%= field.name %>, visitor, data); + <%- when Prism::OptionalNodeField -%> + if (cast-><%= field.name %> != NULL) { + pm_node_visit((const pm_node_t *) cast-><%= field.name %>, visitor, data); + } + <%- when Prism::NodeListField -%> + const pm_node_list_t *<%= field.name %> = &cast-><%= field.name %>; + for (size_t index = 0; index < <%= field.name %>->size; index++) { + pm_node_visit(<%= field.name %>->nodes[index], visitor, data); + } + <%- end -%> + <%- end -%> + + break; + } + <%- else -%> + case <%= node.type %>: + break; + <%- end -%> + <%- end -%> + case PM_SCOPE_NODE: + break; + } +}