#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