/* * This file is generated by Propane. */ #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(non_upper_case_globals)] #![allow(dead_code)] #![allow(unused_variables)] #![allow(unused_parens)] /************************************************************************** * User code blocks *************************************************************************/ <%= @grammar.code_blocks.fetch("", "") %> /************************************************************************** * Public types *************************************************************************/ /* Result codes. */ pub const <%= @grammar.prefix.upcase %>SUCCESS: usize = 0; pub const <%= @grammar.prefix.upcase %>DECODE_ERROR: usize = 1; pub const <%= @grammar.prefix.upcase %>UNEXPECTED_INPUT: usize = 2; pub const <%= @grammar.prefix.upcase %>UNEXPECTED_TOKEN: usize = 3; pub const <%= @grammar.prefix.upcase %>DROP: usize = 4; pub const <%= @grammar.prefix.upcase %>EOF: usize = 5; pub const <%= @grammar.prefix.upcase %>USER_TERMINATED: usize = 6; /** Token type. */ pub type <%= @grammar.prefix %>token_t = <%= get_type_for(@grammar.terminate_token_id) %>; /** Token IDs. */ <% @grammar.tokens.each_with_index do |token, index| %> pub const TOKEN_<%= token.code_name %>: <%= @grammar.prefix %>token_t = <%= index %>; <% unless token.id == index %> <% raise "Token ID (#{token.id}) does not match index (#{index}) for token #{token.name}!" %> <% end %> <% end %> pub const INVALID_TOKEN_ID: <%= @grammar.prefix %>token_t = <%= @grammar.invalid_token_id %>; pub const TERMINATE_TOKEN_ID: <%= @grammar.prefix %>token_t = <%= @grammar.terminate_token_id %>; /** Code point type. */ pub type <%= @grammar.prefix %>code_point_t = u32; /** * A structure to keep track of input position. * * This is useful for reporting errors, etc... */ #[derive(Clone, Copy, Default, PartialEq)] pub struct <%= @grammar.prefix %>position_t { /** Input text row (1-based). */ pub row: u32, /** Input text column (1-based). */ pub col: u32, } impl <%= @grammar.prefix %>position_t { /** Return whether the position is valid. */ pub fn valid(&self) -> bool { self.row != 0 } } /** An invalid position value. */ const INVALID_POSITION: <%= @grammar.prefix %>position_t = <%= @grammar.prefix %>position_t { row: 0, col: 0 }; <% if @grammar.tree %> /** Parser values type. */ pub type <%= @grammar.prefix %>value_t = <%= rust_ptype(@grammar.ptype) %>; <% else %> /** Parser values type(s). */ #[derive(Clone, Default)] pub enum <%= @grammar.prefix %>value_t { #[default] __None, <% @grammar.ptypes.each do |name, typestring| %> v_<%= name %>(<%= rust_ptype(typestring) %>), <% end %> } impl <%= @grammar.prefix %>value_t { <% @grammar.ptypes.each do |name, typestring| %> fn v_<%= name %>_mut(&mut self) -> &mut <%= rust_ptype(typestring) %> { match self { <%= @grammar.prefix %>value_t::v_<%= name %>(v) => v, _ => unreachable!() } } fn get_v_<%= name %>(&self) -> <%= rust_ptype(typestring) %> { match self { <%= @grammar.prefix %>value_t::v_<%= name %>(v) => v.clone(), _ => Default::default() } } <% end %> } /** Parser value constructor(s) and accessor(s). */ <% @grammar.ptypes.each do |name, typestring| %> <% suffix = name == "default" ? "" : "_#{name}" %> pub fn <%= @grammar.prefix %>value<%= suffix %>(v: <%= rust_ptype(typestring) %>) -> <%= @grammar.prefix %>value_t { <%= @grammar.prefix %>value_t::v_<%= name %>(v) } pub fn <%= @grammar.prefix %>value_get<%= suffix %>(pvalue: &<%= @grammar.prefix %>value_t) -> <%= rust_ptype(typestring) %> { pvalue.get_v_<%= name %>() } <% end %> <% end %> <% if @grammar.tree %> /** Tree node ID type (index into the context node arena). ID 0 is null. */ pub type <%= @grammar.prefix %>node_id_t = u32; /** * 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. */ #[derive(Clone, Default)] pub struct <%= @grammar.prefix %>token_info_t { /** Text position of first code point in token. */ pub position: <%= @grammar.prefix %>position_t, /** Text position of last code point in token. */ pub end_position: <%= @grammar.prefix %>position_t, /** Number of input bytes used by the token. */ pub length: usize, /** Token that was lexed. */ pub token: <%= @grammar.prefix %>token_t, /** Parser value associated with the token. */ pub pvalue: <%= @grammar.prefix %>value_t, } /** * Lexer and parser context. * * The user must allocate an instance of this structure and pass it to any * public API function. */ #[derive(Default)] pub struct <%= @grammar.prefix %>context_t { /* Lexer context data. */ /** Input text. */ input: Vec, /** Input text index (byte offset). */ input_index: usize, /** Input text position (row/column). */ text_position: <%= @grammar.prefix %>position_t, /** Current lexer mode. */ mode: usize, /* Parser context data. */ /** Parse result value. */ <% if @grammar.tree %> parse_result: <%= @grammar.prefix %>node_id_t, /** Tree node arena. Node ID 0 is reserved as the null node. */ <%= @grammar.prefix %>tree_nodes: Vec<<%= @grammar.prefix %>node_data_t>, /** Shared tree child links (CSR layout). */ <%= @grammar.prefix %>tree_children: Vec<<%= @grammar.prefix %>node_id_t>, <% else %> parse_result: <%= @grammar.prefix %>value_t, <% end %> /** Unexpected token received. */ token: <%= @grammar.prefix %>token_t, /** User terminate code. */ pub user_terminate_code: usize, <%= @grammar.context_user_fields %> } /************************************************************************** * Public data *************************************************************************/ /** Token names. */ pub const <%= @grammar.prefix %>token_names: [&str; <%= @grammar.tokens.size %>] = [ <% @grammar.tokens.each do |token| %> "<%= token.name %>", <% end %> ]; /************************************************************************** * Private types *************************************************************************/ <% if @grammar.prefix.upcase != "P_" %> /* Result codes. */ const P_SUCCESS: usize = 0; const P_DECODE_ERROR: usize = 1; const P_UNEXPECTED_INPUT: usize = 2; const P_UNEXPECTED_TOKEN: usize = 3; const P_DROP: usize = 4; const P_EOF: usize = 5; const P_USER_TERMINATED: usize = 6; <% end %> /* An invalid ID value. */ const INVALID_ID: usize = usize::MAX; /************************************************************************** * State initialization *************************************************************************/ /** * Allocate and initialize lexer/parser context structure. * * Deinitialize and deallocate with <%= @grammar.prefix %>context_delete(). * * @param input * Text input. * * @return Context structure for lexer/parser. */ pub fn <%= @grammar.prefix %>context_new(input: &[u8]) -> <%= @grammar.prefix %>context_t { let mut context = <%= @grammar.prefix %>context_t::default(); /* Lexer initialization. */ context.input = input.to_vec(); context.text_position.row = 1; context.text_position.col = 1; context.mode = <%= @lexer.mode_id("default") %>; <% if @grammar.tree %> /* Reserve node ID 0 as the null tree node. */ context.<%= @grammar.prefix %>tree_nodes.push(<%= @grammar.prefix %>node_data_t::default()); <% end %> context } /** * Deinitialize and deallocate lexer/parser context structure. * * @param context * Lexer/parser context structure. */ <% free_token_node_used = @grammar.tree && @grammar.free_token_node != "" %> <% if free_token_node_used %> /* The context is taken as mutable for the benefit of the free_token_node user * code block below, which is permitted but not required to modify the node it * is freeing. */ #[allow(unused_mut)] <% end %> pub fn <%= @grammar.prefix %>context_delete(<%= free_token_node_used ? "mut " : "" %>context: <%= @grammar.prefix %>context_t) { <% if free_token_node_used %> for i in 0..context.<%= @grammar.prefix %>tree_nodes.len() { if context.<%= @grammar.prefix %>tree_nodes[i].is_token { let token_node_id = i; <%= expand_code(@grammar.free_token_node, false, nil, nil).gsub(/\btoken_tree_node\b/, "context.#{@grammar.prefix}tree_nodes[token_node_id]") %> } } <% end %> } /************************************************************************** * Decoder *************************************************************************/ /** * Decode a UTF-8 code point. * * @param input * Text input to decode. * @param out_code_point * The decoded code point is stored here if the return value is P_SUCCESS. * @param out_code_point_length * The number of bytes the code point used is stored here if the return value * is P_SUCCESS. * * @retval P_SUCCESS on a successful code point decode * @retval P_DECODE_ERROR when an encoding error is observed * @retval P_EOF when the end of the text input is reached */ pub fn <%= @grammar.prefix %>decode_code_point(input: &[u8], out_code_point: &mut <%= @grammar.prefix %>code_point_t, out_code_point_length: &mut u8) -> usize { if input.len() == 0 { return P_EOF; } let c = input[0]; let mut code_point: <%= @grammar.prefix %>code_point_t; let code_point_length: u8; if (c & 0x80u8) == 0u8 { code_point = c as <%= @grammar.prefix %>code_point_t; code_point_length = 1; } else { let following_bytes: usize; if (c & 0xE0u8) == 0xC0u8 { code_point = (c & 0x1Fu8) as <%= @grammar.prefix %>code_point_t; following_bytes = 1; } else if (c & 0xF0u8) == 0xE0u8 { code_point = (c & 0x0Fu8) as <%= @grammar.prefix %>code_point_t; following_bytes = 2; } else if (c & 0xF8u8) == 0xF0u8 { code_point = (c & 0x07u8) as <%= @grammar.prefix %>code_point_t; following_bytes = 3; } else if (c & 0xFCu8) == 0xF8u8 { code_point = (c & 0x03u8) as <%= @grammar.prefix %>code_point_t; following_bytes = 4; } else if (c & 0xFEu8) == 0xFCu8 { code_point = (c & 0x01u8) as <%= @grammar.prefix %>code_point_t; following_bytes = 5; } else { return P_DECODE_ERROR; } if input.len() <= following_bytes { return P_DECODE_ERROR; } code_point_length = (following_bytes + 1) as u8; for i in 0..following_bytes { let b = input[i + 1]; if (b & 0xC0u8) != 0x80u8 { return P_DECODE_ERROR; } code_point = (code_point << 6) | ((b & 0x3Fu8) as <%= @grammar.prefix %>code_point_t); } } *out_code_point = code_point; *out_code_point_length = code_point_length; P_SUCCESS } /************************************************************************** * Lexer *************************************************************************/ type lexer_state_id_t = <%= get_type_for(@lexer.state_table.size) %>; /** Invalid lexer state ID. */ const INVALID_LEXER_STATE_ID: lexer_state_id_t = <%= @lexer.state_table.size %>; /** Invalid lexer user code ID. */ <% user_code_id_count = (@grammar.patterns.map(&:code_id).compact.max || 0) + 1 %> const INVALID_USER_CODE_ID: <%= get_type_for(user_code_id_count) %> = <%= user_code_id_count %>; /** * Lexer transition table entry. * * An incoming code point matching the range for a transition entry will cause * the lexer to progress to the destination state. */ #[derive(Clone, Copy)] struct lexer_transition_t { /** First code point in the range for this transition. */ first: <%= @grammar.prefix %>code_point_t, /** Last code point in the range for this transition. */ last: <%= @grammar.prefix %>code_point_t, /** Destination lexer state ID for this transition. */ destination_state: lexer_state_id_t, } /** Lexer state table entry. */ #[derive(Clone, Copy)] struct lexer_state_t { /** Index to the transition table for this state. */ transition_table_index: <%= get_type_for(@lexer.transition_table.size - 1) %>, /** Number of transition table entries for this state. */ n_transitions: <%= get_type_for(@lexer.state_table.map {|ste| ste[:n_transitions]}.max) %>, /** Lexer token formed at this state. */ token: <%= @grammar.prefix %>token_t, /** Lexer user code ID to execute at this state. */ code_id: <%= get_type_for(user_code_id_count) %>, /** Whether this state matches a lexer pattern. */ accepts: bool, } /** Lexer mode table entry. */ #[derive(Clone, Copy)] struct lexer_mode_t { /** Offset in the state table to be used for this mode. */ state_table_offset: u32, } /** * Lexer match info structure. * * This structure holds output values from the lexer upon a successful pattern * match. */ #[derive(Clone, Copy, Default)] struct lexer_match_info_t { /** Number of bytes of input text used to match. */ length: usize, /** Input text position delta to end of token. */ end_delta_position: <%= @grammar.prefix %>position_t, /** Input text position delta to next code point after token end. */ delta_position: <%= @grammar.prefix %>position_t, /** Accepting lexer state from the match (state ID, or INVALID). */ accepting_state: lexer_state_id_t, } /** Lexer transition table. */ static lexer_transition_table: [lexer_transition_t; <%= @lexer.transition_table.size %>] = [ <% @lexer.transition_table.each do |transition_table_entry| %> lexer_transition_t { first: <%= transition_table_entry[:first] %>, last: <%= transition_table_entry[:last] %>, destination_state: <%= transition_table_entry[:destination] %> }, <% end %> ]; /** Lexer state table. */ static lexer_state_table: [lexer_state_t; <%= @lexer.state_table.size %>] = [ <% @lexer.state_table.each do |state_table_entry| %> lexer_state_t { transition_table_index: <%= state_table_entry[:transition_table_index] %>, n_transitions: <%= state_table_entry[:n_transitions] %>, token: <%= state_table_entry[:token] || "INVALID_TOKEN_ID" %>, code_id: <%= state_table_entry[:code_id] || "INVALID_USER_CODE_ID" %>, accepts: <%= state_table_entry[:accepts] %> }, <% end %> ]; /** Lexer mode table. */ static lexer_mode_table: [lexer_mode_t; <%= @lexer.mode_table.size %>] = [ <% @lexer.mode_table.each do |mode_table_entry| %> lexer_mode_t { state_table_offset: <%= mode_table_entry[:state_table_offset] %> }, <% end %> ]; /** * Execute user code associated with a lexer pattern. * * @param context * Lexer/parser context structure. * @param code_id * The ID of the user code block to execute. * @param match_text * Matched text for this pattern. * @param out_token_info * Lexer token info in progress. * * @return Token to accept, or invalid token if the user code does * not explicitly return a token. */ fn lexer_user_code(context: &mut <%= @grammar.prefix %>context_t, code_id: <%= get_type_for(user_code_id_count) %>, match_text: &[u8], out_token_info: &mut <%= @grammar.prefix %>token_info_t) -> <%= @grammar.prefix %>token_t { match code_id { <% @grammar.patterns.each do |pattern| %> <% if pattern.code_id %> <%= pattern.code_id %> => { <% unless @grammar.tree %> out_token_info.pvalue = <%= @grammar.prefix %>value_t::v_<%= pattern.ptypename %>(Default::default()); <% end %> <%= expand_code(pattern.code, false, nil, pattern) %> } <% end %> <% end %> _ => {} } INVALID_TOKEN_ID } /** * Check if there is a transition from the current lexer state to another * based on the given input code point. * * @param current_state * Current lexer state. * @param code_point * Input code point. * * @return Lexer state to transition to, or INVALID_LEXER_STATE_ID if none. */ fn check_lexer_transition(current_state: u32, code_point: u32) -> lexer_state_id_t { let transition_table_index = lexer_state_table[current_state as usize].transition_table_index as u32; for i in 0..(lexer_state_table[current_state as usize].n_transitions as u32) { let t = &lexer_transition_table[(transition_table_index + i) as usize]; if (t.first <= code_point) && (code_point <= t.last) { return t.destination_state; } } INVALID_LEXER_STATE_ID } /** * Find the longest lexer pattern match at the current position. * * @param context * Lexer/parser context structure. * @param[out] out_match_info * The longest match information is stored here if the return value is * P_SUCCESS or P_DECODE_ERROR. * @param[out] out_unexpected_input_length * The unexpected input length is stored here if the return value is * P_UNEXPECTED_INPUT. * * @reval P_SUCCESS * A token was successfully lexed. * @reval P_DECODE_ERROR * The decoder encountered invalid text encoding. * @reval P_UNEXPECTED_INPUT * Input text does not match any lexer pattern. * @retval P_EOF * The end of the text input was reached. */ fn find_longest_match(context: &<%= @grammar.prefix %>context_t, out_match_info: &mut lexer_match_info_t, out_unexpected_input_length: &mut usize) -> usize { let mut longest_match = lexer_match_info_t::default(); longest_match.accepting_state = INVALID_LEXER_STATE_ID; let mut attempt_match = lexer_match_info_t::default(); attempt_match.accepting_state = INVALID_LEXER_STATE_ID; *out_match_info = longest_match; let mut current_state: u32 = lexer_mode_table[context.mode].state_table_offset; loop { let input_index = context.input_index + attempt_match.length; let input = &context.input[input_index..]; let mut code_point: <%= @grammar.prefix %>code_point_t = 0; let mut code_point_length: u8 = 0; let result = <%= @grammar.prefix %>decode_code_point(input, &mut code_point, &mut code_point_length); match result { P_SUCCESS => { let transition_state = check_lexer_transition(current_state, code_point); if transition_state != INVALID_LEXER_STATE_ID { attempt_match.length += code_point_length as usize; attempt_match.end_delta_position = attempt_match.delta_position; if code_point == '\n' as u32 { attempt_match.delta_position.row += 1; attempt_match.delta_position.col = 1; } else { attempt_match.delta_position.col += 1; } current_state = transition_state as u32; if lexer_state_table[current_state as usize].accepts { attempt_match.accepting_state = current_state as lexer_state_id_t; longest_match = attempt_match; } } else if longest_match.length > 0 { *out_match_info = longest_match; return P_SUCCESS; } else { *out_unexpected_input_length = attempt_match.length + code_point_length as usize; return P_UNEXPECTED_INPUT; } } P_EOF => { /* We hit EOF. */ if longest_match.length > 0 { *out_match_info = longest_match; return P_SUCCESS; } else if attempt_match.length != 0 { /* There is a partial match - error! */ *out_unexpected_input_length = attempt_match.length; return P_UNEXPECTED_INPUT; } else { return P_EOF; } } P_DECODE_ERROR => { /* If we see a decode error, we may be partially in the middle of * matching a pattern, so return the attempted match info so that * the input text position can be updated. */ *out_match_info = attempt_match; return result; } _ => { return result; } } } } /** * Attempt to lex the next token in the input stream. * * @param context * Lexer/parser context structure. * @param[out] out_token_info * The lexed token information is stored here if the return value is * P_SUCCESS. * * @reval P_SUCCESS * A token was successfully lexed. * @reval P_DECODE_ERROR * The decoder encountered invalid text encoding. * @reval P_UNEXPECTED_INPUT * Input text does not match any lexer pattern. * @retval P_DROP * A drop pattern was matched so the lexer should continue. * @retval P_USER_TERMINATED * User code has requested to terminate the lexer. */ fn attempt_lex_token(context: &mut <%= @grammar.prefix %>context_t, out_token_info: &mut <%= @grammar.prefix %>token_info_t) -> usize { let mut token_info = <%= @grammar.prefix %>token_info_t::default(); token_info.position = context.text_position; token_info.token = INVALID_TOKEN_ID; let mut match_info = lexer_match_info_t::default(); let mut unexpected_input_length: usize = 0; let result = find_longest_match(context, &mut match_info, &mut unexpected_input_length); match result { P_SUCCESS => { let mut token_to_accept = lexer_state_table[match_info.accepting_state as usize].token; /* Calculate the token length and start/end positions before invoking * the lexer user code so that the user code can access them. The * context input text position tracking is not updated until after the * user code has run so that it is left unchanged if the user code * requests to terminate the lexer. */ token_info.length = match_info.length; if match_info.end_delta_position.row != 0 { token_info.end_position.row = token_info.position.row + match_info.end_delta_position.row; token_info.end_position.col = match_info.end_delta_position.col; } else { token_info.end_position.row = token_info.position.row; token_info.end_position.col = token_info.position.col + match_info.end_delta_position.col; } if lexer_state_table[match_info.accepting_state as usize].code_id != INVALID_USER_CODE_ID { let match_start = context.input_index; let match_slice = context.input[match_start..(match_start + match_info.length)].to_vec(); let user_code_token = lexer_user_code(context, lexer_state_table[match_info.accepting_state as usize].code_id, &match_slice, &mut token_info); /* A TERMINATE_TOKEN_ID return code from lexer_user_code() means * that the user code is requesting to terminate the lexer. */ if user_code_token == TERMINATE_TOKEN_ID { return P_USER_TERMINATED; } /* An invalid token returned from lexer_user_code() means that the * user code did not explicitly return a token. So only override * the token to return if the user code does explicitly return a * token. */ if user_code_token != INVALID_TOKEN_ID { token_to_accept = user_code_token; } } /* Update the input position tracking. */ context.input_index += match_info.length; context.text_position.row += match_info.delta_position.row; if match_info.delta_position.row != 0 { context.text_position.col = match_info.delta_position.col; } else { context.text_position.col += match_info.delta_position.col; } if token_to_accept == INVALID_TOKEN_ID { return P_DROP; } token_info.token = token_to_accept; *out_token_info = token_info; P_SUCCESS } P_EOF => { token_info.token = TOKEN___EOF; token_info.end_position = token_info.position; *out_token_info = token_info; P_SUCCESS } P_DECODE_ERROR => { /* Update the input position tracking. */ context.input_index += match_info.length; context.text_position.row += match_info.delta_position.row; if match_info.delta_position.row != 0 { context.text_position.col = match_info.delta_position.col; } else { context.text_position.col += match_info.delta_position.col; } result } _ => { result } } } /** * Lex the next token in the input stream. * * @param context * Lexer/parser context structure. * @param[out] out_token_info * The lexed token information is stored here if the return value is * P_SUCCESS. * * @reval P_SUCCESS * A token was successfully lexed. * @reval P_DECODE_ERROR * The decoder encountered invalid text encoding. * @reval P_UNEXPECTED_INPUT * Input text does not match any lexer pattern. * @retval P_USER_TERMINATED * User code has requested to terminate the lexer. */ pub fn <%= @grammar.prefix %>lex(context: &mut <%= @grammar.prefix %>context_t, out_token_info: &mut <%= @grammar.prefix %>token_info_t) -> usize { loop { let result = attempt_lex_token(context, out_token_info); if result != P_DROP { return result; } } } /************************************************************************** * Parser *************************************************************************/ /** Reduce ID type. */ type reduce_id_t = <%= get_type_for(@parser.reduce_table.size) %>; /** * A symbol ID can hold either a token ID or a rule set ID. * * Token IDs and rule set IDs share the same namespace, with rule set IDs * beginning after token IDs end. */ type symbol_id_t = <%= get_type_for(@parser.rule_sets.map(&:last).map(&:id).max) %>; /** Parser state ID type. */ type parser_state_id_t = <%= get_type_for(@parser.state_table.size) %>; /** Parser rule ID type. */ type rule_id_t = <%= get_type_for(@grammar.rules.size) %>; /** Parser shift ID type. */ type shift_id_t = <%= get_type_for(@parser.shift_table.size) %>; /** Shift table entry. */ #[derive(Clone, Copy)] struct shift_t { /** Token or rule set ID. */ symbol_id: symbol_id_t, /** Parser state to shift to. */ state_id: parser_state_id_t, } /** Reduce table entry. */ #[derive(Clone, Copy)] struct reduce_t { /** Lookahead token. */ token: <%= @grammar.prefix %>token_t, /** * Rule ID. * * This is used to execute the parser user code block associated with a * grammar rule. */ rule: rule_id_t, /** * Rule set ID. * * This is used as the new top symbol ID of the parse stack after this * reduce action. */ rule_set: symbol_id_t, /** * Number of states leading to this reduce action. * * This is the number of entries popped from the parse stack after this * reduce action. */ n_states: parser_state_id_t, <% if @grammar.tree %> /** * Map of rule components to rule set child fields (None for a flat map). */ rule_set_node_field_index_map: Option<&'static [u16]>, /** * Number of rule set tree node fields. */ rule_set_node_field_array_size: u16, /** * Whether this rule was a generated optional rule that matched the * optional target. In this case, propagate the matched target node up * instead of making a new node for this rule. */ propagate_optional_target: bool, <% end %> } /** Parser state entry. */ #[derive(Clone, Copy)] struct parser_state_t { /** First shift table entry for this parser state. */ shift_table_index: shift_id_t, /** Number of shift table entries for this parser state. */ n_shift_entries: shift_id_t, /** First reduce table entry for this parser state. */ reduce_table_index: reduce_id_t, /** Number of reduce table entries for this parser state. */ n_reduce_entries: reduce_id_t, } /** * Structure to hold a state ID and value pair. * * A stack of these structures makes up the parse stack. */ #[derive(Clone, Default)] struct state_value_t { /** Parser state ID. */ state_id: usize, <% if @grammar.tree %> /** Tree node ID. */ node_id: <%= @grammar.prefix %>node_id_t, <% else %> position: <%= @grammar.prefix %>position_t, end_position: <%= @grammar.prefix %>position_t, /** Parser value from this state. */ pvalue: <%= @grammar.prefix %>value_t, <% end %> } /** Parser shift table. */ static parser_shift_table: [shift_t; <%= @parser.shift_table.size %>] = [ <% @parser.shift_table.each do |shift| %> shift_t { symbol_id: <%= shift[:symbol].id %>, state_id: <%= shift[:state_id] %> }, <% end %> ]; <% if @grammar.tree %> <% @grammar.rules.each do |rule| %> <% unless rule.flat_rule_set_node_field_index_map? %> static r_<%= rule.name.gsub("$", "_") %><%= rule.id %>_node_field_index_map: [u16; <%= rule.rule_set_node_field_index_map.size %>] = [<%= rule.rule_set_node_field_index_map.map {|v| v.to_s}.join(", ") %>]; <% end %> <% end %> <% end %> /** Parser reduce table. */ static parser_reduce_table: [reduce_t; <%= @parser.reduce_table.size %>] = [ <% @parser.reduce_table.each do |reduce| %> reduce_t { token: <%= reduce[:token_id] %>, /* Token: <%= reduce[:token] ? reduce[:token].name : "(any)" %> */ rule: <%= reduce[:rule_id] %>, /* Rule ID */ rule_set: <%= reduce[:rule_set_id] %>, /* Rule set ID (<%= reduce[:rule].rule_set.name %>) */ n_states: <%= reduce[:n_states] %>, /* Number of states */ <% if @grammar.tree %> <% if reduce[:rule].flat_rule_set_node_field_index_map? %> rule_set_node_field_index_map: None, <% else %> rule_set_node_field_index_map: Some(&r_<%= reduce[:rule].name.gsub("$", "_") %><%= reduce[:rule].id %>_node_field_index_map), <% end %> rule_set_node_field_array_size: <%= reduce[:rule].rule_set.tree_fields.size %>, propagate_optional_target: <%= reduce[:propagate_optional_target] %>, <% end %> }, <% end %> ]; /** Parser state table. */ static parser_state_table: [parser_state_t; <%= @parser.state_table.size %>] = [ <% @parser.state_table.each do |state| %> parser_state_t { shift_table_index: <%= state[:shift_index] %>, n_shift_entries: <%= state[:n_shifts] %>, reduce_table_index: <%= state[:reduce_index] %>, n_reduce_entries: <%= state[:n_reduces] %> }, <% end %> ]; <% if @grammar.tree %> /* Tree arena helpers. */ /** Allocate a new (zeroed) tree node in the context arena. */ fn tree_new_node(context: &mut <%= @grammar.prefix %>context_t) -> <%= @grammar.prefix %>node_id_t { let id = context.<%= @grammar.prefix %>tree_nodes.len() as <%= @grammar.prefix %>node_id_t; context.<%= @grammar.prefix %>tree_nodes.push(<%= @grammar.prefix %>node_data_t::default()); id } /** Reserve n contiguous (zeroed) child slots in the shared children array. */ fn tree_reserve_children(context: &mut <%= @grammar.prefix %>context_t, n: usize) -> <%= @grammar.prefix %>node_id_t { let offset = context.<%= @grammar.prefix %>tree_children.len() as <%= @grammar.prefix %>node_id_t; let new_len = context.<%= @grammar.prefix %>tree_children.len() + n; context.<%= @grammar.prefix %>tree_children.resize(new_len, 0); offset } <% end %> <% unless @grammar.tree %> /** * Get the rule position (start or end) for the currently matched rule. */ fn get_rule_position(statevalues: &[state_value_t], i: usize, n_states: usize, get_end: bool) -> <%= @grammar.prefix %>position_t { let len = statevalues.len(); if n_states > 0 { if i == 0 { if get_end { for j in 0..n_states { let sv = &statevalues[len - 1 - j]; if sv.end_position.valid() { return sv.end_position; } } } else { for j in 0..n_states { let sv = &statevalues[len - n_states + j]; if sv.position.valid() { return sv.position; } } } } else { if get_end { return statevalues[len - 1 - n_states + i].end_position; } else { return statevalues[len - 1 - n_states + i].position; } } } INVALID_POSITION } <% end %> <% if !@grammar.tree || @grammar.parser_user_code_used? %> /** * Execute user code associated with a parser rule. * * @param rule The ID of the rule. * * @retval P_SUCCESS * Continue parsing. * @retval P_USER_TERMINATED * User requested to terminate parsing. */ fn parser_user_code(context: &mut <%= @grammar.prefix %>context_t, <%= @grammar.tree ? "_node_id: #{@grammar.prefix}node_id_t" : "_pvalue: &mut #{@grammar.prefix}value_t" %>, rule: u32, statevalues: &[state_value_t], n_states: usize) -> usize { match rule { <% @grammar.rules.each do |rule| %> <% if rule.code %> <%= rule.id %> => { <% unless @grammar.tree %> *_pvalue = <%= @grammar.prefix %>value_t::v_<%= rule.ptypename %>(Default::default()); <% end %> <%= expand_code(rule.code, true, rule, nil) %> } <% end %> <% end %> _ => {} } P_SUCCESS } <% end %> /** * Check if the parser should shift to a new state. * * @param state_id * Parser state ID. * @param symbol_id * Incoming token/rule set ID. * * @return State to shift to, or INVALID_ID if none. */ fn check_shift(state_id: usize, symbol_id: usize) -> usize { let start = parser_state_table[state_id].shift_table_index as usize; let end = start + parser_state_table[state_id].n_shift_entries as usize; for i in start..end { if parser_shift_table[i].symbol_id as usize == symbol_id { return parser_shift_table[i].state_id as usize; } } INVALID_ID } /** * Check if the parser should reduce to a new state. * * @param state_id * Parser state ID. * @param token * Incoming token. * * @return Reduce table index to reduce with, or INVALID_ID if none. */ fn check_reduce(state_id: usize, token: <%= @grammar.prefix %>token_t) -> usize { let start = parser_state_table[state_id].reduce_table_index as usize; let end = start + parser_state_table[state_id].n_reduce_entries as usize; for i in start..end { if (parser_reduce_table[i].token == token) || (parser_reduce_table[i].token == INVALID_TOKEN_ID) { return i; } } INVALID_ID } /** * Run the parser. * * @param context * Lexer/parser context structure. * @param start_state_id * ID of the state in which to start. * @param start_rule_set_id * Rule set ID for the requested start rule. Only used when * @p follow_tokens is non-empty, to gate follow-token shift success. * @param follow_tokens * Optional slice of caller-provided follow tokens (tokens expected to * appear immediately after the start rule in some outer context). Used to * drive the "parse inner" retry logic. May be null/empty for a standard * parse. * * @retval P_SUCCESS * The parser successfully matched the input text. The parse result value * can be accessed with <%= @grammar.prefix %>result(). * @retval P_UNEXPECTED_TOKEN * An unexpected token was encountered that does not match any grammar rule. * The function p_token(&context) can be used to get the unexpected token. * @reval P_DECODE_ERROR * The decoder encountered invalid text encoding. * @reval P_UNEXPECTED_INPUT * Input text does not match any lexer pattern. */ fn parse_from(context: &mut <%= @grammar.prefix %>context_t, start_state_id: usize, start_rule_set_id: usize, follow_tokens: &[<%= @grammar.prefix %>token_t]) -> usize { let mut token_info = <%= @grammar.prefix %>token_info_t::default(); let mut token: <%= @grammar.prefix %>token_t = INVALID_TOKEN_ID; let mut statevalues: Vec = Vec::new(); let mut reduced_rule_set: usize = INVALID_ID; let mut last_shifted_rule_set_id: usize = INVALID_ID; <% if @grammar.tree %> let mut reduced_parser_node: <%= @grammar.prefix %>node_id_t = 0; <% else %> let mut reduced_position: <%= @grammar.prefix %>position_t = INVALID_POSITION; let mut reduced_end_position: <%= @grammar.prefix %>position_t = INVALID_POSITION; let mut reduced_parser_value: <%= @grammar.prefix %>value_t = Default::default(); <% end %> statevalues.push(state_value_t::default()); let sv_len = statevalues.len(); statevalues[sv_len - 1].state_id = start_state_id; loop { if token == INVALID_TOKEN_ID { let lexer_result = <%= lex_fn %>(context, &mut token_info); if lexer_result != P_SUCCESS { return lexer_result; } token = token_info.token; } /* For a "parse inner" operation, determine once per iteration whether * the current token is a member of the caller-provided follow token * set. Used by both the shift-side and reduce-side retries below. */ let mut token_is_follow = false; for &ft in follow_tokens { if token == ft { token_is_follow = true; break; } } let mut shift_state: usize = INVALID_ID; if reduced_rule_set != INVALID_ID { shift_state = check_shift(statevalues[statevalues.len() - 1].state_id, reduced_rule_set); } if shift_state == INVALID_ID { shift_state = check_shift(statevalues[statevalues.len() - 1].state_id, token as usize); if (shift_state != INVALID_ID) && (token == TOKEN___EOF) { /* Successful parse. */ <% if @grammar.tree %> context.parse_result = statevalues[statevalues.len() - 1].node_id; <% else %> context.parse_result = statevalues[statevalues.len() - 1].pvalue.clone(); <% end %> return P_SUCCESS; } if (shift_state == INVALID_ID) && token_is_follow { /* For a "parse inner" operation, if the incoming token is one * of the caller's follow tokens, retry the shift as * TOKEN___EOF. Only consider the parse complete if the reduced * start rule is the only thing on the parse stack (i.e. the * initial state plus a single shifted start rule set entry). */ let retry_shift_state = check_shift(statevalues[statevalues.len() - 1].state_id, TOKEN___EOF as usize); if (retry_shift_state != INVALID_ID) && (statevalues.len() == 2) && (last_shifted_rule_set_id == start_rule_set_id) { /* Successful parse via follow token. Rewind the input * position so that the follow token is not consumed from * the input stream and remains available for a subsequent * call to <%= @grammar.prefix %>lex() or a * <%= @grammar.prefix %>parse*() function. */ context.input_index -= token_info.length; context.text_position = token_info.position; <% if @grammar.tree %> context.parse_result = statevalues[statevalues.len() - 1].node_id; <% else %> context.parse_result = statevalues[statevalues.len() - 1].pvalue.clone(); <% end %> return P_SUCCESS; } } } if shift_state != INVALID_ID { /* We have something to shift. Track the last shifted rule set ID * (INVALID_ID if we just shifted a token) so the follow-token * shift retry can gate success on the reduced start rule being the * only thing on top of the initial state. */ last_shifted_rule_set_id = reduced_rule_set; statevalues.push(state_value_t::default()); let new_index = statevalues.len() - 1; statevalues[new_index].state_id = shift_state; if reduced_rule_set == INVALID_ID { /* We shifted a token, mark it consumed. */ <% if @grammar.tree %> let token_node_id = tree_new_node(context); { let token_tree_node = &mut context.<%= @grammar.prefix %>tree_nodes[token_node_id as usize]; token_tree_node.position = token_info.position; token_tree_node.end_position = token_info.end_position; token_tree_node.n_fields = 0; token_tree_node.is_token = true; token_tree_node.token = token; token_tree_node.pvalue = token_info.pvalue.clone(); } <%= expand_code(@grammar.on_token_node, false, nil, nil).gsub(/\btoken_tree_node\b/, "context.#{@grammar.prefix}tree_nodes[token_node_id as usize]") %> statevalues[new_index].node_id = token_node_id; <% else %> statevalues[new_index].position = token_info.position; statevalues[new_index].end_position = token_info.end_position; statevalues[new_index].pvalue = token_info.pvalue.clone(); <% end %> token = INVALID_TOKEN_ID; } else { /* We shifted a RuleSet. */ <% if @grammar.tree %> statevalues[new_index].node_id = reduced_parser_node; <% else %> statevalues[new_index].pvalue = reduced_parser_value.clone(); statevalues[new_index].position = reduced_position; statevalues[new_index].end_position = reduced_end_position; reduced_parser_value = Default::default(); <% end %> reduced_rule_set = INVALID_ID; } continue; } let mut reduce_index = check_reduce(statevalues[statevalues.len() - 1].state_id, token); if (reduce_index == INVALID_ID) && token_is_follow { /* For a "parse inner" operation, if the incoming token is one of * the caller's follow tokens, retry the reduce lookup as * TOKEN___EOF. Whatever reduce_index results (if any) is used * regardless of which rule set it reduces to; this allows chains * of reductions leading up to the start rule. */ reduce_index = check_reduce(statevalues[statevalues.len() - 1].state_id, TOKEN___EOF); } if reduce_index != INVALID_ID { /* We have something to reduce. */ let n_states = parser_reduce_table[reduce_index].n_states as usize; <% if @grammar.tree %> if parser_reduce_table[reduce_index].propagate_optional_target { reduced_parser_node = statevalues[statevalues.len() - 1].node_id; } else if n_states > 0 { let n_fields = parser_reduce_table[reduce_index].rule_set_node_field_array_size; let child_offset = tree_reserve_children(context, n_fields as usize); match parser_reduce_table[reduce_index].rule_set_node_field_index_map { None => { for i in 0..n_states { context.<%= @grammar.prefix %>tree_children[child_offset as usize + i] = statevalues[statevalues.len() - n_states + i].node_id; } } Some(map) => { for i in 0..n_states { context.<%= @grammar.prefix %>tree_children[child_offset as usize + map[i] as usize] = statevalues[statevalues.len() - n_states + i].node_id; } } } let node_id = tree_new_node(context); context.<%= @grammar.prefix %>tree_nodes[node_id as usize].position = INVALID_POSITION; context.<%= @grammar.prefix %>tree_nodes[node_id as usize].end_position = INVALID_POSITION; context.<%= @grammar.prefix %>tree_nodes[node_id as usize].child_offset = child_offset; context.<%= @grammar.prefix %>tree_nodes[node_id as usize].n_fields = n_fields; context.<%= @grammar.prefix %>tree_nodes[node_id as usize].is_token = false; let mut position_found = false; for i in 0..(n_fields as usize) { let child_id = context.<%= @grammar.prefix %>tree_children[child_offset as usize + i]; if (child_id != 0) && context.<%= @grammar.prefix %>tree_nodes[child_id as usize].position.valid() { if !position_found { let p = context.<%= @grammar.prefix %>tree_nodes[child_id as usize].position; context.<%= @grammar.prefix %>tree_nodes[node_id as usize].position = p; position_found = true; } let ep = context.<%= @grammar.prefix %>tree_nodes[child_id as usize].end_position; context.<%= @grammar.prefix %>tree_nodes[node_id as usize].end_position = ep; } } reduced_parser_node = node_id; } else { reduced_parser_node = 0; } <% if @grammar.parser_user_code_used? %> if parser_user_code(context, reduced_parser_node, parser_reduce_table[reduce_index].rule as u32, &statevalues, n_states) == P_USER_TERMINATED { return P_USER_TERMINATED; } <% end %> <% else %> let mut reduced_parser_value2: <%= @grammar.prefix %>value_t = Default::default(); if parser_user_code(context, &mut reduced_parser_value2, parser_reduce_table[reduce_index].rule as u32, &statevalues, n_states) == P_USER_TERMINATED { return P_USER_TERMINATED; } reduced_parser_value = reduced_parser_value2; if n_states > 0 { reduced_position = get_rule_position(&statevalues, 0, n_states, false); reduced_end_position = get_rule_position(&statevalues, 0, n_states, true); } else { reduced_position = INVALID_POSITION; reduced_end_position = INVALID_POSITION; } <% end %> reduced_rule_set = parser_reduce_table[reduce_index].rule_set as usize; let new_len = statevalues.len() - n_states; statevalues.truncate(new_len); continue; } /* A token was successfully lexed, so the input text position was * advanced. However, this is an unexpected token, so we want to reset * the context text position to point to the token rather than the text * after it, so that if the caller wants to report the error position, * it will point to the correct position of the unexpected token. */ context.text_position = token_info.position; context.token = token; return P_UNEXPECTED_TOKEN; } } pub fn <%= @grammar.prefix %>parse(context: &mut <%= @grammar.prefix %>context_t) -> usize { parse_from(context, 0, <%= @parser.rule_sets[@grammar.start_rules[0]].id %>, &[]) } <% @grammar.start_rules.each_with_index do |start_rule, i| %> pub fn <%= @grammar.prefix %>parse_<%= start_rule %>(context: &mut <%= @grammar.prefix %>context_t) -> usize { parse_from(context, <%= i %>, <%= @parser.rule_sets[start_rule].id %>, &[]) } pub fn <%= @grammar.prefix %>parse_inner_<%= start_rule %>(context: &mut <%= @grammar.prefix %>context_t, follow_tokens: &[<%= @grammar.prefix %>token_t]) -> usize { parse_from(context, <%= i %>, <%= @parser.rule_sets[start_rule].id %>, follow_tokens) } <% end %> /** * Get the parse result value. * * @param context * Lexer/parser context structure. * * @return Parse result value. */ <% if @grammar.tree %> pub fn <%= @grammar.prefix %>result(context: &<%= @grammar.prefix %>context_t) -> <%= h_type(@grammar.start_rules[0]) %><'_> { <%= tree_handle(h_type(@grammar.start_rules[0]), "context.parse_result") %> } <% @grammar.start_rules.each_with_index do |start_rule, i| %> pub fn <%= @grammar.prefix %>result_<%= start_rule %>(context: &<%= @grammar.prefix %>context_t) -> <%= h_type(start_rule) %><'_> { <%= tree_handle(h_type(start_rule), "context.parse_result") %> } <% end %> <% else %> pub fn <%= @grammar.prefix %>result(context: &<%= @grammar.prefix %>context_t) -> <%= rust_ptype(start_rule_type[1]) %> { context.parse_result.get_v_<%= start_rule_type[0] %>() } <% @grammar.start_rules.each_with_index do |start_rule, i| %> pub fn <%= @grammar.prefix %>result_<%= start_rule %>(context: &<%= @grammar.prefix %>context_t) -> <%= rust_ptype(start_rule_type(i)[1]) %> { context.parse_result.get_v_<%= start_rule_type(i)[0] %>() } <% end %> <% end %> /** * Get the current text input position. * * @param context * Lexer/parser context structure. * * @return Current text position. */ pub fn <%= @grammar.prefix %>position(context: &<%= @grammar.prefix %>context_t) -> <%= @grammar.prefix %>position_t { context.text_position } /** * Set the current text input position. * * This can be used to set the initial text position to something other than * (1, 1) for a nested parse operation so that error positions reported by * subsequent lexer/parser calls are relative to a larger enclosing document. * * @param context * Lexer/parser context structure. * @param position * Text position to set. */ pub fn <%= @grammar.prefix %>set_position(context: &mut <%= @grammar.prefix %>context_t, position: <%= @grammar.prefix %>position_t) { context.text_position = position; } /** * Get the current input text byte offset. * * @param context * Lexer/parser context structure. * * @return Current input text byte offset (measured from the start of the * input text passed to <%= @grammar.prefix %>context_new()). */ pub fn <%= @grammar.prefix %>input_index(context: &<%= @grammar.prefix %>context_t) -> usize { context.input_index } /** * Set the current input text byte offset. * * This moves the lexer's read cursor to the given byte offset (measured from * the start of the input text passed to <%= @grammar.prefix %>context_new()). * It can be used together with <%= @grammar.prefix %>set_position() to rewind * the input part-way through a parse in order to re-read an earlier section of * the input. The byte offset is not validated; the caller is responsible for * providing an offset within the bounds of the input text. A value previously * returned by <%= @grammar.prefix %>input_index() is a suitable argument. * * @param context * Lexer/parser context structure. * @param input_index * Input text byte offset to set. */ pub fn <%= @grammar.prefix %>set_input_index(context: &mut <%= @grammar.prefix %>context_t, input_index: usize) { context.input_index = input_index; } /** * Get the user terminate code. * * @param context * Lexer/parser context structure. * * @return User terminate code. */ pub fn <%= @grammar.prefix %>user_terminate_code(context: &<%= @grammar.prefix %>context_t) -> usize { context.user_terminate_code } /** * Get the parse token. * * @return Parse token. */ pub fn <%= @grammar.prefix %>token(context: &<%= @grammar.prefix %>context_t) -> <%= @grammar.prefix %>token_t { context.token }