summaryrefslogtreecommitdiff
path: root/prism/templates/src/node.c.erb
diff options
context:
space:
mode:
Diffstat (limited to 'prism/templates/src/node.c.erb')
-rw-r--r--prism/templates/src/node.c.erb398
1 files changed, 398 insertions, 0 deletions
diff --git a/prism/templates/src/node.c.erb b/prism/templates/src/node.c.erb
new file mode 100644
index 0000000000..e1c35f5a45
--- /dev/null
+++ b/prism/templates/src/node.c.erb
@@ -0,0 +1,398 @@
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+#include "prism/node.h"
+
+static void
+pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize);
+
+/**
+ * Calculate the size of the node list in bytes.
+ */
+static size_t
+pm_node_list_memsize(pm_node_list_t *node_list, pm_memsize_t *memsize) {
+ pm_node_t *node;
+ PM_NODE_LIST_FOREACH(node_list, index, node) pm_node_memsize_node(node, memsize);
+ return sizeof(pm_node_list_t) + (node_list->capacity * sizeof(pm_node_t *));
+}
+
+/**
+ * Attempts to grow the node list to the next size. If there is already
+ * capacity in the list, this function does nothing. Otherwise it reallocates
+ * the list to be twice as large as it was before. If the reallocation fails,
+ * this function returns false, otherwise it returns true.
+ */
+static bool
+pm_node_list_grow(pm_node_list_t *list, size_t size) {
+ size_t requested_size = list->size + size;
+
+ // If the requested size caused overflow, return false.
+ if (requested_size < list->size) return false;
+
+ // If the requested size is within the existing capacity, return true.
+ if (requested_size < list->capacity) return true;
+
+ // Otherwise, reallocate the list to be twice as large as it was before.
+ size_t next_capacity = list->capacity == 0 ? 4 : list->capacity * 2;
+
+ // If multiplying by 2 caused overflow, return false.
+ if (next_capacity < list->capacity) return false;
+
+ // If we didn't get enough by doubling, keep doubling until we do.
+ while (requested_size > next_capacity) {
+ size_t double_capacity = next_capacity * 2;
+
+ // Ensure we didn't overflow by multiplying by 2.
+ if (double_capacity < next_capacity) return false;
+ next_capacity = double_capacity;
+ }
+
+ pm_node_t **nodes = (pm_node_t **) xrealloc(list->nodes, sizeof(pm_node_t *) * next_capacity);
+ if (nodes == NULL) return false;
+
+ list->nodes = nodes;
+ list->capacity = next_capacity;
+ return true;
+}
+
+/**
+ * Append a new node onto the end of the node list.
+ */
+void
+pm_node_list_append(pm_node_list_t *list, pm_node_t *node) {
+ if (pm_node_list_grow(list, 1)) {
+ list->nodes[list->size++] = node;
+ }
+}
+
+/**
+ * Prepend a new node onto the beginning of the node list.
+ */
+void
+pm_node_list_prepend(pm_node_list_t *list, pm_node_t *node) {
+ if (pm_node_list_grow(list, 1)) {
+ memmove(list->nodes + 1, list->nodes, list->size * sizeof(pm_node_t *));
+ list->nodes[0] = node;
+ list->size++;
+ }
+}
+
+/**
+ * Concatenate the given node list onto the end of the other node list.
+ */
+void
+pm_node_list_concat(pm_node_list_t *list, pm_node_list_t *other) {
+ if (other->size > 0 && pm_node_list_grow(list, other->size)) {
+ memcpy(list->nodes + list->size, other->nodes, other->size * sizeof(pm_node_t *));
+ list->size += other->size;
+ }
+}
+
+/**
+ * Free the internal memory associated with the given node list.
+ */
+void
+pm_node_list_free(pm_node_list_t *list) {
+ if (list->capacity > 0) {
+ xfree(list->nodes);
+ *list = (pm_node_list_t) { 0 };
+ }
+}
+
+PRISM_EXPORTED_FUNCTION void
+pm_node_destroy(pm_parser_t *parser, pm_node_t *node);
+
+/**
+ * Destroy the nodes that are contained within the given node list.
+ */
+static void
+pm_node_list_destroy(pm_parser_t *parser, pm_node_list_t *list) {
+ pm_node_t *node;
+ PM_NODE_LIST_FOREACH(list, index, node) pm_node_destroy(parser, node);
+ pm_node_list_free(list);
+}
+
+/**
+ * Deallocate the space for a pm_node_t. Similarly to pm_node_alloc, we're not
+ * using the parser argument, but it's there to allow for the future possibility
+ * of pre-allocating larger memory pools.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_node_destroy(pm_parser_t *parser, pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ <%- nodes.each do |node| -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ case <%= node.type %>: {
+ <%- if node.fields.any? { |field| ![Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::FlagsField, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField].include?(field.class) } -%>
+ pm_<%= node.human %>_t *cast = (pm_<%= node.human %>_t *) node;
+ <%- end -%>
+ <%- node.fields.each do |field| -%>
+ <%- case field -%>
+ <%- when Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::FlagsField, Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::DoubleField -%>
+ <%- when Prism::Template::NodeField -%>
+ pm_node_destroy(parser, (pm_node_t *)cast-><%= field.name %>);
+ <%- when Prism::Template::OptionalNodeField -%>
+ if (cast-><%= field.name %> != NULL) {
+ pm_node_destroy(parser, (pm_node_t *)cast-><%= field.name %>);
+ }
+ <%- when Prism::Template::StringField -%>
+ pm_string_free(&cast-><%= field.name %>);
+ <%- when Prism::Template::NodeListField -%>
+ pm_node_list_destroy(parser, &cast-><%= field.name %>);
+ <%- when Prism::Template::ConstantListField -%>
+ pm_constant_id_list_free(&cast-><%= field.name %>);
+ <%- when Prism::Template::IntegerField -%>
+ pm_integer_free(&cast-><%= field.name %>);
+ <%- else -%>
+ <%- raise -%>
+ <%- end -%>
+ <%- end -%>
+ break;
+ }
+ <%- end -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ default:
+ assert(false && "unreachable");
+ break;
+ }
+ xfree(node);
+}
+
+static void
+pm_node_memsize_node(pm_node_t *node, pm_memsize_t *memsize) {
+ memsize->node_count++;
+
+ switch (PM_NODE_TYPE(node)) {
+ // We do not calculate memsize of a ScopeNode
+ // as it should never be generated
+ case PM_SCOPE_NODE:
+ return;
+ <%- nodes.each do |node| -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ case <%= node.type %>: {
+ pm_<%= node.human %>_t *cast = (pm_<%= node.human %>_t *) node;
+ memsize->memsize += sizeof(*cast);
+ <%- node.fields.each do |field| -%>
+ <%- case field -%>
+ <%- when Prism::Template::ConstantField, Prism::Template::OptionalConstantField, Prism::Template::UInt8Field, Prism::Template::UInt32Field, Prism::Template::FlagsField, Prism::Template::LocationField, Prism::Template::OptionalLocationField, Prism::Template::DoubleField -%>
+ <%- when Prism::Template::NodeField -%>
+ pm_node_memsize_node((pm_node_t *)cast-><%= field.name %>, memsize);
+ <%- when Prism::Template::OptionalNodeField -%>
+ if (cast-><%= field.name %> != NULL) {
+ pm_node_memsize_node((pm_node_t *)cast-><%= field.name %>, memsize);
+ }
+ <%- when Prism::Template::StringField -%>
+ memsize->memsize += (pm_string_memsize(&cast-><%= field.name %>) - sizeof(pm_string_t));
+ <%- when Prism::Template::NodeListField -%>
+ memsize->memsize += (pm_node_list_memsize(&cast-><%= field.name %>, memsize) - sizeof(pm_node_list_t));
+ <%- when Prism::Template::ConstantListField -%>
+ memsize->memsize += (pm_constant_id_list_memsize(&cast-><%= field.name %>) - sizeof(pm_constant_id_list_t));
+ <%- when Prism::Template::IntegerField -%>
+ memsize->memsize += (pm_integer_memsize(&cast-><%= field.name %>) - sizeof(pm_integer_t));
+ <%- else -%>
+ <%- raise -%>
+ <%- end -%>
+ <%- end -%>
+ break;
+ }
+ <%- end -%>
+#line <%= __LINE__ + 1 %> "<%= File.basename(__FILE__) %>"
+ }
+}
+
+/**
+ * Calculates the memory footprint of a given node.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_node_memsize(pm_node_t *node, pm_memsize_t *memsize) {
+ *memsize = (pm_memsize_t) { .memsize = 0, .node_count = 0 };
+ pm_node_memsize_node(node, memsize);
+}
+
+/**
+ * Returns a string representation of the given node type.
+ */
+PRISM_EXPORTED_FUNCTION const char *
+pm_node_type_to_str(pm_node_type_t node_type)
+{
+ switch (node_type) {
+<%- nodes.each do |node| -%>
+ case <%= node.type %>:
+ return "<%= node.type %>";
+<%- end -%>
+ }
+ return "";
+}
+
+/**
+ * Visit each of the nodes in this subtree using the given visitor callback. The
+ * callback function will be called for each node in the subtree. If it returns
+ * false, then that node's children will not be visited. If it returns true,
+ * then the children will be visited. The data parameter is treated as an opaque
+ * pointer and is passed to the visitor callback for consumers to use as they
+ * see fit.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_visit_node(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) {
+ if (visitor(node, data)) pm_visit_child_nodes(node, visitor, data);
+}
+
+/**
+ * Visit the children of the given node with the given callback. This is the
+ * default behavior for walking the tree that is called from pm_visit_node if
+ * the callback returns true.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_visit_child_nodes(const pm_node_t *node, bool (*visitor)(const pm_node_t *node, void *data), void *data) {
+ switch (PM_NODE_TYPE(node)) {
+ <%- nodes.each do |node| -%>
+ <%- if (fields = node.fields.select { |field| field.is_a?(Prism::Template::NodeField) || field.is_a?(Prism::Template::OptionalNodeField) || field.is_a?(Prism::Template::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::Template::NodeField -%>
+ pm_visit_node((const pm_node_t *) cast-><%= field.name %>, visitor, data);
+ <%- when Prism::Template::OptionalNodeField -%>
+ if (cast-><%= field.name %> != NULL) {
+ pm_visit_node((const pm_node_t *) cast-><%= field.name %>, visitor, data);
+ }
+ <%- when Prism::Template::NodeListField -%>
+ const pm_node_list_t *<%= field.name %> = &cast-><%= field.name %>;
+ for (size_t index = 0; index < <%= field.name %>->size; index++) {
+ pm_visit_node(<%= field.name %>->nodes[index], visitor, data);
+ }
+ <%- end -%>
+ <%- end -%>
+
+ break;
+ }
+ <%- else -%>
+ case <%= node.type %>:
+ break;
+ <%- end -%>
+ <%- end -%>
+ case PM_SCOPE_NODE:
+ break;
+ }
+}
+
+// We optionally support dumping to JSON. For systems that don't want or need
+// this functionality, it can be turned off with the PRISM_EXCLUDE_JSON define.
+#ifndef PRISM_EXCLUDE_JSON
+
+static void
+pm_dump_json_constant(pm_buffer_t *buffer, const pm_parser_t *parser, pm_constant_id_t constant_id) {
+ const pm_constant_t *constant = pm_constant_pool_id_to_constant(&parser->constant_pool, constant_id);
+ pm_buffer_append_byte(buffer, '"');
+ pm_buffer_append_source(buffer, constant->start, constant->length, PM_BUFFER_ESCAPING_JSON);
+ pm_buffer_append_byte(buffer, '"');
+}
+
+static void
+pm_dump_json_location(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_location_t *location) {
+ uint32_t start = (uint32_t) (location->start - parser->start);
+ uint32_t end = (uint32_t) (location->end - parser->start);
+ pm_buffer_append_format(buffer, "{\"start\":%" PRIu32 ",\"end\":%" PRIu32 "}", start, end);
+}
+
+/**
+ * Dump JSON to the given buffer.
+ */
+PRISM_EXPORTED_FUNCTION void
+pm_dump_json(pm_buffer_t *buffer, const pm_parser_t *parser, const pm_node_t *node) {
+ switch (PM_NODE_TYPE(node)) {
+ <%- nodes.each do |node| -%>
+ case <%= node.type %>: {
+ pm_buffer_append_string(buffer, "{\"type\":\"<%= node.name %>\",\"location\":", <%= node.name.bytesize + 22 %>);
+
+ const pm_<%= node.human %>_t *cast = (const pm_<%= node.human %>_t *) node;
+ pm_dump_json_location(buffer, parser, &cast->base.location);
+ <%- node.fields.each_with_index do |field, index| -%>
+
+ // Dump the <%= field.name %> field
+ pm_buffer_append_byte(buffer, ',');
+ pm_buffer_append_string(buffer, "\"<%= field.name %>\":", <%= field.name.bytesize + 3 %>);
+ <%- case field -%>
+ <%- when Prism::Template::NodeField -%>
+ pm_dump_json(buffer, parser, (const pm_node_t *) cast-><%= field.name %>);
+ <%- when Prism::Template::OptionalNodeField -%>
+ if (cast-><%= field.name %> != NULL) {
+ pm_dump_json(buffer, parser, (const pm_node_t *) cast-><%= field.name %>);
+ } else {
+ pm_buffer_append_string(buffer, "null", 4);
+ }
+ <%- when Prism::Template::NodeListField -%>
+ const pm_node_list_t *<%= field.name %> = &cast-><%= field.name %>;
+ pm_buffer_append_byte(buffer, '[');
+
+ for (size_t index = 0; index < <%= field.name %>->size; index++) {
+ if (index != 0) pm_buffer_append_byte(buffer, ',');
+ pm_dump_json(buffer, parser, <%= field.name %>->nodes[index]);
+ }
+ pm_buffer_append_byte(buffer, ']');
+ <%- when Prism::Template::StringField -%>
+ const pm_string_t *<%= field.name %> = &cast-><%= field.name %>;
+ pm_buffer_append_byte(buffer, '"');
+ pm_buffer_append_source(buffer, pm_string_source(<%= field.name %>), pm_string_length(<%= field.name %>), PM_BUFFER_ESCAPING_JSON);
+ pm_buffer_append_byte(buffer, '"');
+ <%- when Prism::Template::ConstantField -%>
+ pm_dump_json_constant(buffer, parser, cast-><%= field.name %>);
+ <%- when Prism::Template::OptionalConstantField -%>
+ if (cast-><%= field.name %> != PM_CONSTANT_ID_UNSET) {
+ pm_dump_json_constant(buffer, parser, cast-><%= field.name %>);
+ } else {
+ pm_buffer_append_string(buffer, "null", 4);
+ }
+ <%- when Prism::Template::ConstantListField -%>
+ const pm_constant_id_list_t *<%= field.name %> = &cast-><%= field.name %>;
+ pm_buffer_append_byte(buffer, '[');
+
+ for (size_t index = 0; index < <%= field.name %>->size; index++) {
+ if (index != 0) pm_buffer_append_byte(buffer, ',');
+ pm_dump_json_constant(buffer, parser, <%= field.name %>->ids[index]);
+ }
+ pm_buffer_append_byte(buffer, ']');
+ <%- when Prism::Template::LocationField -%>
+ pm_dump_json_location(buffer, parser, &cast-><%= field.name %>);
+ <%- when Prism::Template::OptionalLocationField -%>
+ if (cast-><%= field.name %>.start != NULL) {
+ pm_dump_json_location(buffer, parser, &cast-><%= field.name %>);
+ } else {
+ pm_buffer_append_string(buffer, "null", 4);
+ }
+ <%- when Prism::Template::UInt8Field -%>
+ pm_buffer_append_format(buffer, "%" PRIu8, cast-><%= field.name %>);
+ <%- when Prism::Template::UInt32Field -%>
+ pm_buffer_append_format(buffer, "%" PRIu32, cast-><%= field.name %>);
+ <%- when Prism::Template::FlagsField -%>
+ size_t flags = 0;
+ pm_buffer_append_byte(buffer, '[');
+ <%- found = flags.find { |flag| flag.name == field.kind }.tap { |found| raise "Expected to find #{field.kind}" unless found } -%>
+ <%- found.values.each_with_index do |value, index| -%>
+ if (PM_NODE_FLAG_P(cast, PM_<%= found.human.upcase %>_<%= value.name %>)) {
+ if (flags != 0) pm_buffer_append_byte(buffer, ',');
+ pm_buffer_append_string(buffer, "\"<%= value.name %>\"", <%= value.name.bytesize + 2 %>);
+ flags++;
+ }
+ <%- end -%>
+ pm_buffer_append_byte(buffer, ']');
+ <%- when Prism::Template::IntegerField -%>
+ pm_integer_string(buffer, &cast-><%= field.name %>);
+ <%- when Prism::Template::DoubleField -%>
+ pm_buffer_append_format(buffer, "%f", cast-><%= field.name %>);
+ <%- else -%>
+ <%- raise %>
+ <%- end -%>
+ <%- end -%>
+
+ pm_buffer_append_byte(buffer, '}');
+ break;
+ }
+ <%- end -%>
+ case PM_SCOPE_NODE:
+ break;
+ }
+}
+
+#endif