diff --git a/assets/parser.rs.erb b/assets/parser.rs.erb index 1c2e8ec..79c727b 100644 --- a/assets/parser.rs.erb +++ b/assets/parser.rs.erb @@ -109,7 +109,84 @@ pub fn <%= @grammar.prefix %>value_get<%= suffix %>(pvalue: &<%= @grammar.prefix /** Tree node ID type (index into the context node arena). ID 0 is null. */ pub type <%= @grammar.prefix %>node_id_t = u32; -<%= rust_tree_types %> +/** + * Tree node record. + * + * All tree nodes are stored contiguously in the context node arena. Child + * links are stored in a shared children array: a node's children + * occupy children[child_offset .. child_offset + n_fields]. Token payload + * fields (token, pvalue, and any user fields) are only meaningful when + * is_token is true. + */ +#[derive(Clone, Default)] +pub struct <%= @grammar.prefix %>node_data_t { + pub position: <%= @grammar.prefix %>position_t, + pub end_position: <%= @grammar.prefix %>position_t, + pub child_offset: <%= @grammar.prefix %>node_id_t, + pub n_fields: u16, + pub is_token: bool, + pub token: <%= @grammar.prefix %>token_t, + pub pvalue: <%= @grammar.prefix %>value_t, +<% unless @grammar.token_user_fields.to_s.strip.empty? %> +<%= @grammar.token_user_fields %> +<% end %> +} + +/** Tree node handle types. */ + +/** Token tree node handle. */ +#[derive(Clone, Copy)] +pub struct <%= h_type("Token") %><'a> { context: &'a <%= @grammar.prefix %>context_t, id: <%= @grammar.prefix %>node_id_t } + +impl<'a> <%= h_type("Token") %><'a> { + /** Return whether this handle refers to a valid (non-null) node. */ + pub fn valid(&self) -> bool { self.id != 0 } + /** Return the node ID (for identity comparison). */ + pub fn node_id(&self) -> <%= @grammar.prefix %>node_id_t { self.id } + /** Access the underlying node record (token, pvalue, and user fields). */ + pub fn data(&self) -> &'a <%= @grammar.prefix %>node_data_t { &self.context.<%= @grammar.prefix %>tree_nodes[self.id as usize] } + /** Text position of the first code point spanned by this node. */ + pub fn position(&self) -> <%= @grammar.prefix %>position_t { self.context.<%= @grammar.prefix %>tree_nodes[self.id as usize].position } + /** Text position of the last code point spanned by this node. */ + pub fn end_position(&self) -> <%= @grammar.prefix %>position_t { self.context.<%= @grammar.prefix %>tree_nodes[self.id as usize].end_position } + /** Number of child fields in this node. */ + pub fn n_fields(&self) -> u16 { if self.id != 0 { self.context.<%= @grammar.prefix %>tree_nodes[self.id as usize].n_fields } else { 0 } } + /** Token ID for this token node. */ + pub fn token(&self) -> <%= @grammar.prefix %>token_t { self.context.<%= @grammar.prefix %>tree_nodes[self.id as usize].token } + /** Parser value associated with this token node. */ + pub fn pvalue(&self) -> <%= @grammar.prefix %>value_t { self.context.<%= @grammar.prefix %>tree_nodes[self.id as usize].pvalue.clone() } +} +<% tree_node_rule_sets.each do |rule_set| %> + +/** <%= rule_set.name %> tree node handle. */ +#[derive(Clone, Copy)] +pub struct <%= h_type(rule_set.name) %><'a> { context: &'a <%= @grammar.prefix %>context_t, id: <%= @grammar.prefix %>node_id_t } + +impl<'a> <%= h_type(rule_set.name) %><'a> { + /** Return whether this handle refers to a valid (non-null) node. */ + pub fn valid(&self) -> bool { self.id != 0 } + /** Return the node ID (for identity comparison). */ + pub fn node_id(&self) -> <%= @grammar.prefix %>node_id_t { self.id } + /** Access the underlying node record. */ + pub fn data(&self) -> &'a <%= @grammar.prefix %>node_data_t { &self.context.<%= @grammar.prefix %>tree_nodes[self.id as usize] } + /** Text position of the first code point spanned by this node. */ + pub fn position(&self) -> <%= @grammar.prefix %>position_t { self.context.<%= @grammar.prefix %>tree_nodes[self.id as usize].position } + /** Text position of the last code point spanned by this node. */ + pub fn end_position(&self) -> <%= @grammar.prefix %>position_t { self.context.<%= @grammar.prefix %>tree_nodes[self.id as usize].end_position } + /** Number of child fields in this node. */ + pub fn n_fields(&self) -> u16 { if self.id != 0 { self.context.<%= @grammar.prefix %>tree_nodes[self.id as usize].n_fields } else { 0 } } +<% each_tree_field(rule_set) do |rt, field_name, child_type, slot| %> + + /** Access the <%= field_name %> child node. */ + pub fn <%= rust_ident(field_name) %>(&self) -> <%= child_type %><'a> { + if self.id == 0 { + return <%= child_type %> { context: self.context, id: 0 }; + } + <%= child_type %> { context: self.context, id: self.context.<%= @grammar.prefix %>tree_children[self.context.<%= @grammar.prefix %>tree_nodes[self.id as usize].child_offset as usize + <%= slot %>] } + } +<% end %> +} +<% end %> <% end %> /** Lexed token information. */ diff --git a/lib/propane/generator.rb b/lib/propane/generator.rb index 3d8a218..3612341 100644 --- a/lib/propane/generator.rb +++ b/lib/propane/generator.rb @@ -755,79 +755,6 @@ class Propane typestring == "void *" ? "()" : typestring end - # Generate the Rust tree node record and handle types. - # - # Mirrors the C tree node record plus the C++ handle structs: each rule set - # and the Token node get a handle type ({context, id}) with accessor methods. - # - # @return [String] - # Rust tree node type definitions. - def rust_tree_types - p = @grammar.prefix - out = [] - out << "/** Tree node record. */" - out << "#[derive(Clone, Default)]" - out << "pub struct #{p}node_data_t {" - out << " pub position: #{p}position_t," - out << " pub end_position: #{p}position_t," - out << " pub child_offset: #{p}node_id_t," - out << " pub n_fields: u16," - out << " pub is_token: bool," - out << " pub token: #{p}token_t," - out << " pub pvalue: #{p}value_t," - unless @grammar.token_user_fields.to_s.strip.empty? - out << @grammar.token_user_fields - end - out << "}" - out << "" - out << "/** Tree node handle types. */" - tree_handle_types.each do |t| - out << "#[derive(Clone, Copy)]" - out << "pub struct #{t}<'a> { context: &'a #{p}context_t, id: #{p}node_id_t }" - end - out << "" - # Common accessors for every handle type. - tree_handle_types.each do |t| - out << "impl<'a> #{t}<'a> {" - out << " /** Return whether this handle refers to a valid (non-null) node. */" - out << " pub fn valid(&self) -> bool { self.id != 0 }" - out << " /** Return the node ID (for identity comparison). */" - out << " pub fn node_id(&self) -> #{p}node_id_t { self.id }" - out << " /** Access the underlying node record. */" - out << " pub fn data(&self) -> &'a #{p}node_data_t { &self.context.#{p}tree_nodes[self.id as usize] }" - out << " /** Text position of the first code point spanned by this node. */" - out << " pub fn position(&self) -> #{p}position_t { self.context.#{p}tree_nodes[self.id as usize].position }" - out << " /** Text position of the last code point spanned by this node. */" - out << " pub fn end_position(&self) -> #{p}position_t { self.context.#{p}tree_nodes[self.id as usize].end_position }" - out << " /** Number of child fields in this node. */" - out << " pub fn n_fields(&self) -> u16 { if self.id != 0 { self.context.#{p}tree_nodes[self.id as usize].n_fields } else { 0 } }" - if t == h_type("Token") - out << " /** Token ID for this token node. */" - out << " pub fn token(&self) -> #{p}token_t { self.context.#{p}tree_nodes[self.id as usize].token }" - out << " /** Parser value associated with this token node. */" - out << " pub fn pvalue(&self) -> #{p}value_t { self.context.#{p}tree_nodes[self.id as usize].pvalue.clone() }" - end - out << "}" - end - out << "" - # Navigation accessors for rule set handles. - tree_node_rule_sets.each do |rule_set| - rtype = h_type(rule_set.name) - out << "impl<'a> #{rtype}<'a> {" - each_tree_field(rule_set) do |rt, field_name, child_type, slot| - out << " /** Access the #{field_name} child node. */" - out << " pub fn #{rust_ident(field_name)}(&self) -> #{child_type}<'a> {" - out << " if self.id == 0 {" - out << " return #{child_type} { context: self.context, id: 0 };" - out << " }" - out << " #{child_type} { context: self.context, id: self.context.#{p}tree_children[self.context.#{p}tree_nodes[self.id as usize].child_offset as usize + #{slot}] }" - out << " }" - end - out << "}" - end - out.join("\n") - end - # Get the lex function to use. # # @return [String]