Compare commits
2 Commits
dddb1b5089
...
89f1f84857
| Author | SHA1 | Date | |
|---|---|---|---|
| 89f1f84857 | |||
| 5fc712c6ee |
31
CHANGELOG.md
31
CHANGELOG.md
@ -1,3 +1,34 @@
|
|||||||
|
## v5.0.0
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
- Add Rust target language output.
|
||||||
|
|
||||||
|
### API Changes
|
||||||
|
|
||||||
|
- Tree generation mode now stores all tree nodes in a compact arena owned by
|
||||||
|
the parser context (a flat node array plus a shared child-link array).
|
||||||
|
This replaces the previous design of one heap allocation per node with
|
||||||
|
layout-punned typed structs.
|
||||||
|
- Tree nodes are now referenced by lightweight handles rather than pointers.
|
||||||
|
`p_result()` and the field accessors return handle values.
|
||||||
|
- The whole tree is freed together with the context by `p_context_delete()`.
|
||||||
|
The `p_tree_delete()` / `p_tree_delete_XXX()` functions have been removed;
|
||||||
|
tree node handles are only valid while the context is alive.
|
||||||
|
- Tree node field access changed per target language:
|
||||||
|
- C: per-field accessor functions (e.g. `p_Start_pItems(node)`) plus tree
|
||||||
|
walk macros (e.g. `p_tree_walk_Start(node, pItems, pItem, pToken1, token)`),
|
||||||
|
and generic accessors `p_node_valid()`, `p_node_position()`,
|
||||||
|
`p_node_end_position()`, `p_node_n_fields()`, `p_node_data()`, `p_node_id()`.
|
||||||
|
- C++: handle methods called with `()` (e.g. `node.pItems().pToken1().token()`),
|
||||||
|
plus the same C-style functions/macros for convenience.
|
||||||
|
- D: `@property` accessors preserving the previous field-access syntax
|
||||||
|
(e.g. `node.pItems.pToken1.token`); null checks use `.valid` instead of
|
||||||
|
`is null`.
|
||||||
|
- Tree-mode parser rule user code: `$$` and `$1` etc. now yield node handles.
|
||||||
|
Reference child fields through the target-language accessors described above
|
||||||
|
rather than through struct pointer members.
|
||||||
|
|
||||||
## v4.8.1
|
## v4.8.1
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|||||||
@ -6,7 +6,7 @@ Propane is a LALR Parser Generator (LPG) which:
|
|||||||
* generates a built-in lexer to tokenize input
|
* generates a built-in lexer to tokenize input
|
||||||
* supports UTF-8 lexer inputs
|
* supports UTF-8 lexer inputs
|
||||||
* generates a table-driven shift/reduce parser to parse input in linear time
|
* generates a table-driven shift/reduce parser to parse input in linear time
|
||||||
* targets C, C++, or D language outputs
|
* targets C, C++, D, or Rust language outputs
|
||||||
* optionally supports automatic full parse tree generation
|
* optionally supports automatic full parse tree generation
|
||||||
* supports starting parsing from multiple start rules
|
* supports starting parsing from multiple start rules
|
||||||
* tracks input text start and end positions for all matched tokens/rules
|
* tracks input text start and end positions for all matched tokens/rules
|
||||||
|
|||||||
40
UPGRADING.md
40
UPGRADING.md
@ -1,3 +1,43 @@
|
|||||||
|
## v5.0.0
|
||||||
|
|
||||||
|
The generated API for tree generation mode (`tree;`) has been changed
|
||||||
|
significantly for this version.
|
||||||
|
The lexer/parser value APIs for non-tree grammars are unchanged.
|
||||||
|
|
||||||
|
### Tree memory management
|
||||||
|
|
||||||
|
- Remove all calls to `p_tree_delete()` / `p_tree_delete_XXX()`. Tree nodes now
|
||||||
|
live in the parser context and are freed by `p_context_delete()`.
|
||||||
|
- Tree node handles (returned by `p_result()` and the field accessors) are only
|
||||||
|
valid while the context is alive. Do not use them after `p_context_delete()`.
|
||||||
|
|
||||||
|
### Tree node field access
|
||||||
|
|
||||||
|
Tree nodes are now referenced by handle values instead of pointers, and field
|
||||||
|
access differs per target language:
|
||||||
|
|
||||||
|
- C: replace `node->field` with the accessor function `p_TYPE_field(node)`, or
|
||||||
|
use the tree walk macro `p_tree_walk_TYPE(node, field1, field2, ...)`. Replace
|
||||||
|
`x != NULL` / `x == NULL` node checks with `p_node_valid(x)` /
|
||||||
|
`!p_node_valid(x)`. Read positions with `p_node_position(node)` /
|
||||||
|
`p_node_end_position(node)`, token payload with `p_TYPE_token(node)` /
|
||||||
|
`p_TYPE_pvalue(node)` or `p_node_data(node)->field`, and compare node identity
|
||||||
|
with `p_node_id(a) == p_node_id(b)`.
|
||||||
|
- C++: replace `node->field` with the handle method `node.field()`. Use
|
||||||
|
`node.valid()`, `node.position()`, `node.token()`, `node.pvalue()`, and
|
||||||
|
`node.data()->field` for user token fields. (The C-style functions and macros
|
||||||
|
above are also available.)
|
||||||
|
- D: replace pointer declarations (`Start * s`) with value handles (`Start s`)
|
||||||
|
and replace `x !is null` / `x is null` with `x.valid` / `!x.valid`. Field
|
||||||
|
access syntax (`node.field.field`) is otherwise unchanged.
|
||||||
|
|
||||||
|
### Tree-mode parser rule user code
|
||||||
|
|
||||||
|
In tree generation mode `$$` and `$1`, `$2`, ... now expand to node handles.
|
||||||
|
Reference child fields through the target-language accessors above (for example
|
||||||
|
`$$->pA->pToken1->pvalue` becomes `p_tree_walk_Start($$, pA, pToken1, pvalue)`
|
||||||
|
in C, `$$.pA().pToken1().pvalue()` in C++, and `$$.pA.pToken1.pvalue` in D).
|
||||||
|
|
||||||
## v4.0.0
|
## v4.0.0
|
||||||
|
|
||||||
### API Changes
|
### API Changes
|
||||||
|
|||||||
@ -68,6 +68,18 @@ const char * <%= @grammar.prefix %>token_names[] = {
|
|||||||
context->text_position.row = 1u;
|
context->text_position.row = 1u;
|
||||||
context->text_position.col = 1u;
|
context->text_position.col = 1u;
|
||||||
context->mode = <%= @lexer.mode_id("default") %>;
|
context->mode = <%= @lexer.mode_id("default") %>;
|
||||||
|
<% if @grammar.tree %>
|
||||||
|
|
||||||
|
/* Reserve node ID 0 as the null tree node. */
|
||||||
|
<% if @cpp %>
|
||||||
|
context-><%= @grammar.prefix %>tree_nodes.resize(1);
|
||||||
|
<% else %>
|
||||||
|
context-><%= @grammar.prefix %>tree_nodes_capacity = 16u;
|
||||||
|
context-><%= @grammar.prefix %>tree_nodes = (<%= @grammar.prefix %>node_data_t *)malloc(16u * sizeof(<%= @grammar.prefix %>node_data_t));
|
||||||
|
memset(&context-><%= @grammar.prefix %>tree_nodes[0], 0, sizeof(<%= @grammar.prefix %>node_data_t));
|
||||||
|
context-><%= @grammar.prefix %>tree_nodes_length = 1u;
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
@ -84,9 +96,27 @@ const char * <%= @grammar.prefix %>token_names[] = {
|
|||||||
*/
|
*/
|
||||||
void <%= @grammar.prefix %>context_delete(<%= @grammar.prefix %>context_t * context)
|
void <%= @grammar.prefix %>context_delete(<%= @grammar.prefix %>context_t * context)
|
||||||
{
|
{
|
||||||
|
<% if @grammar.tree && @grammar.free_token_node != "" %>
|
||||||
|
<% if @cpp %>
|
||||||
|
for (size_t i = 0u; i < context-><%= @grammar.prefix %>tree_nodes.size(); i++)
|
||||||
|
<% else %>
|
||||||
|
for (size_t i = 0u; i < context-><%= @grammar.prefix %>tree_nodes_length; i++)
|
||||||
|
<% end %>
|
||||||
|
{
|
||||||
|
if (context-><%= @grammar.prefix %>tree_nodes[i].is_token)
|
||||||
|
{
|
||||||
|
<%= @grammar.prefix %>node_data_t * token_tree_node = &context-><%= @grammar.prefix %>tree_nodes[i];
|
||||||
|
<%= expand_code(@grammar.free_token_node, false, nil, nil) %>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<% end %>
|
||||||
<% if @cpp %>
|
<% if @cpp %>
|
||||||
delete context;
|
delete context;
|
||||||
<% else %>
|
<% else %>
|
||||||
|
<% if @grammar.tree %>
|
||||||
|
free(context-><%= @grammar.prefix %>tree_nodes);
|
||||||
|
free(context-><%= @grammar.prefix %>tree_children);
|
||||||
|
<% end %>
|
||||||
free(context);
|
free(context);
|
||||||
<% end %>
|
<% end %>
|
||||||
}
|
}
|
||||||
@ -713,8 +743,8 @@ typedef struct
|
|||||||
size_t state_id;
|
size_t state_id;
|
||||||
|
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
/** tree node. */
|
/** Tree node ID. */
|
||||||
void * tree_node;
|
<%= @grammar.prefix %>node_id_t node_id;
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= @grammar.prefix %>position_t position;
|
<%= @grammar.prefix %>position_t position;
|
||||||
<%= @grammar.prefix %>position_t end_position;
|
<%= @grammar.prefix %>position_t end_position;
|
||||||
@ -723,18 +753,6 @@ typedef struct
|
|||||||
<% end %>
|
<% end %>
|
||||||
} state_value_t;
|
} state_value_t;
|
||||||
|
|
||||||
<% if @grammar.tree %>
|
|
||||||
/** Common tree node structure. */
|
|
||||||
typedef struct TreeNode_s
|
|
||||||
{
|
|
||||||
<%= @grammar.prefix %>position_t position;
|
|
||||||
<%= @grammar.prefix %>position_t end_position;
|
|
||||||
uint16_t n_fields;
|
|
||||||
uint8_t is_token;
|
|
||||||
struct TreeNode_s * fields[];
|
|
||||||
} TreeNode;
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
/** Parser shift table. */
|
/** Parser shift table. */
|
||||||
static const shift_t parser_shift_table[] = {
|
static const shift_t parser_shift_table[] = {
|
||||||
<% @parser.shift_table.each do |shift| %>
|
<% @parser.shift_table.each do |shift| %>
|
||||||
@ -872,6 +890,77 @@ static void state_values_stack_free(state_values_stack_t * stack)
|
|||||||
free(stack->entries);
|
free(stack->entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<% if @grammar.tree %>
|
||||||
|
/* Tree arena helpers. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new (zeroed) tree node in the context arena.
|
||||||
|
*
|
||||||
|
* @return The new node ID.
|
||||||
|
*/
|
||||||
|
static <%= @grammar.prefix %>node_id_t tree_new_node(<%= @grammar.prefix %>context_t * context)
|
||||||
|
{
|
||||||
|
<% if @cpp %>
|
||||||
|
<%= @grammar.prefix %>node_id_t id = (<%= @grammar.prefix %>node_id_t)context-><%= @grammar.prefix %>tree_nodes.size();
|
||||||
|
context-><%= @grammar.prefix %>tree_nodes.emplace_back();
|
||||||
|
return id;
|
||||||
|
<% else %>
|
||||||
|
if (context-><%= @grammar.prefix %>tree_nodes_length >= context-><%= @grammar.prefix %>tree_nodes_capacity)
|
||||||
|
{
|
||||||
|
size_t new_capacity = context-><%= @grammar.prefix %>tree_nodes_capacity * 2u;
|
||||||
|
<%= @grammar.prefix %>node_data_t * new_nodes = (<%= @grammar.prefix %>node_data_t *)malloc(new_capacity * sizeof(<%= @grammar.prefix %>node_data_t));
|
||||||
|
memcpy(new_nodes, context-><%= @grammar.prefix %>tree_nodes, context-><%= @grammar.prefix %>tree_nodes_length * sizeof(<%= @grammar.prefix %>node_data_t));
|
||||||
|
free(context-><%= @grammar.prefix %>tree_nodes);
|
||||||
|
context-><%= @grammar.prefix %>tree_nodes = new_nodes;
|
||||||
|
context-><%= @grammar.prefix %>tree_nodes_capacity = new_capacity;
|
||||||
|
}
|
||||||
|
<%= @grammar.prefix %>node_id_t id = (<%= @grammar.prefix %>node_id_t)context-><%= @grammar.prefix %>tree_nodes_length;
|
||||||
|
memset(&context-><%= @grammar.prefix %>tree_nodes[id], 0, sizeof(<%= @grammar.prefix %>node_data_t));
|
||||||
|
context-><%= @grammar.prefix %>tree_nodes_length += 1u;
|
||||||
|
return id;
|
||||||
|
<% end %>
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reserve n contiguous (zeroed) child slots in the shared children array.
|
||||||
|
*
|
||||||
|
* @return The offset of the first reserved slot.
|
||||||
|
*/
|
||||||
|
static <%= @grammar.prefix %>node_id_t tree_reserve_children(<%= @grammar.prefix %>context_t * context, size_t n)
|
||||||
|
{
|
||||||
|
<% if @cpp %>
|
||||||
|
<%= @grammar.prefix %>node_id_t offset = (<%= @grammar.prefix %>node_id_t)context-><%= @grammar.prefix %>tree_children.size();
|
||||||
|
context-><%= @grammar.prefix %>tree_children.resize(context-><%= @grammar.prefix %>tree_children.size() + n);
|
||||||
|
return offset;
|
||||||
|
<% else %>
|
||||||
|
size_t offset = context-><%= @grammar.prefix %>tree_children_length;
|
||||||
|
size_t needed = offset + n;
|
||||||
|
if (needed > context-><%= @grammar.prefix %>tree_children_capacity)
|
||||||
|
{
|
||||||
|
size_t new_capacity = context-><%= @grammar.prefix %>tree_children_capacity ? context-><%= @grammar.prefix %>tree_children_capacity : 1u;
|
||||||
|
while (new_capacity < needed)
|
||||||
|
{
|
||||||
|
new_capacity *= 2u;
|
||||||
|
}
|
||||||
|
<%= @grammar.prefix %>node_id_t * new_children = (<%= @grammar.prefix %>node_id_t *)malloc(new_capacity * sizeof(<%= @grammar.prefix %>node_id_t));
|
||||||
|
if (context-><%= @grammar.prefix %>tree_children != NULL)
|
||||||
|
{
|
||||||
|
memcpy(new_children, context-><%= @grammar.prefix %>tree_children, context-><%= @grammar.prefix %>tree_children_length * sizeof(<%= @grammar.prefix %>node_id_t));
|
||||||
|
free(context-><%= @grammar.prefix %>tree_children);
|
||||||
|
}
|
||||||
|
context-><%= @grammar.prefix %>tree_children = new_children;
|
||||||
|
context-><%= @grammar.prefix %>tree_children_capacity = new_capacity;
|
||||||
|
}
|
||||||
|
memset(&context-><%= @grammar.prefix %>tree_children[offset], 0, n * sizeof(<%= @grammar.prefix %>node_id_t));
|
||||||
|
context-><%= @grammar.prefix %>tree_children_length = needed;
|
||||||
|
return (<%= @grammar.prefix %>node_id_t)offset;
|
||||||
|
<% end %>
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tree node field accessor functions. */
|
||||||
|
<%= c_tree_accessor_defs %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
<% unless @grammar.tree %>
|
<% unless @grammar.tree %>
|
||||||
/**
|
/**
|
||||||
* Get the rule position (start or end) for the currently matched rule.
|
* Get the rule position (start or end) for the currently matched rule.
|
||||||
@ -936,7 +1025,7 @@ static <%= @grammar.prefix %>position_t get_rule_position(state_values_stack_t *
|
|||||||
* @retval P_USER_TERMINATED
|
* @retval P_USER_TERMINATED
|
||||||
* User requested to terminate parsing.
|
* User requested to terminate parsing.
|
||||||
*/
|
*/
|
||||||
static size_t parser_user_code(<%= @grammar.tree ? "void" : "#{@grammar.prefix}value_t" %> * _pvalue, uint32_t rule, state_values_stack_t * statevalues, uint32_t n_states, <%= @grammar.prefix %>context_t * context)
|
static size_t parser_user_code(<%= @grammar.tree ? "#{@grammar.prefix}node_id_t _node_id" : "#{@grammar.prefix}value_t * _pvalue" %>, uint32_t rule, state_values_stack_t * statevalues, uint32_t n_states, <%= @grammar.prefix %>context_t * context)
|
||||||
{
|
{
|
||||||
switch (rule)
|
switch (rule)
|
||||||
{
|
{
|
||||||
@ -1041,7 +1130,7 @@ static size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t start
|
|||||||
size_t reduced_rule_set = INVALID_ID;
|
size_t reduced_rule_set = INVALID_ID;
|
||||||
size_t last_shifted_rule_set_id = INVALID_ID;
|
size_t last_shifted_rule_set_id = INVALID_ID;
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
void * reduced_parser_node;
|
<%= @grammar.prefix %>node_id_t reduced_parser_node;
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= @grammar.prefix %>position_t reduced_position;
|
<%= @grammar.prefix %>position_t reduced_position;
|
||||||
<%= @grammar.prefix %>position_t reduced_end_position;
|
<%= @grammar.prefix %>position_t reduced_end_position;
|
||||||
@ -1087,7 +1176,7 @@ static size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t start
|
|||||||
{
|
{
|
||||||
/* Successful parse. */
|
/* Successful parse. */
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
context->parse_result = state_values_stack_index(&statevalues, -1)->tree_node;
|
context->parse_result = state_values_stack_index(&statevalues, -1)->node_id;
|
||||||
<% else %>
|
<% else %>
|
||||||
context->parse_result = state_values_stack_index(&statevalues, -1)->pvalue;
|
context->parse_result = state_values_stack_index(&statevalues, -1)->pvalue;
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -1114,7 +1203,7 @@ static size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t start
|
|||||||
context->input_index -= token_info.length;
|
context->input_index -= token_info.length;
|
||||||
context->text_position = token_info.position;
|
context->text_position = token_info.position;
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
context->parse_result = state_values_stack_index(&statevalues, -1)->tree_node;
|
context->parse_result = state_values_stack_index(&statevalues, -1)->node_id;
|
||||||
<% else %>
|
<% else %>
|
||||||
context->parse_result = state_values_stack_index(&statevalues, -1)->pvalue;
|
context->parse_result = state_values_stack_index(&statevalues, -1)->pvalue;
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -1137,11 +1226,8 @@ static size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t start
|
|||||||
{
|
{
|
||||||
/* We shifted a token, mark it consumed. */
|
/* We shifted a token, mark it consumed. */
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
<% if @cpp %>
|
<%= @grammar.prefix %>node_id_t token_node_id = tree_new_node(context);
|
||||||
<%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %> * token_tree_node = new <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>();
|
<%= @grammar.prefix %>node_data_t * token_tree_node = &context-><%= @grammar.prefix %>tree_nodes[token_node_id];
|
||||||
<% else %>
|
|
||||||
<%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %> * token_tree_node = (<%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %> *)malloc(sizeof(<%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>));
|
|
||||||
<% end %>
|
|
||||||
token_tree_node->position = token_info.position;
|
token_tree_node->position = token_info.position;
|
||||||
token_tree_node->end_position = token_info.end_position;
|
token_tree_node->end_position = token_info.end_position;
|
||||||
token_tree_node->n_fields = 0u;
|
token_tree_node->n_fields = 0u;
|
||||||
@ -1149,7 +1235,7 @@ static size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t start
|
|||||||
token_tree_node->token = token;
|
token_tree_node->token = token;
|
||||||
token_tree_node->pvalue = token_info.pvalue;
|
token_tree_node->pvalue = token_info.pvalue;
|
||||||
<%= expand_code(@grammar.on_token_node, false, nil, nil) %>
|
<%= expand_code(@grammar.on_token_node, false, nil, nil) %>
|
||||||
new_state_info->tree_node = token_tree_node;
|
new_state_info->node_id = token_node_id;
|
||||||
<% else %>
|
<% else %>
|
||||||
new_state_info->position = token_info.position;
|
new_state_info->position = token_info.position;
|
||||||
new_state_info->end_position = token_info.end_position;
|
new_state_info->end_position = token_info.end_position;
|
||||||
@ -1161,7 +1247,7 @@ static size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t start
|
|||||||
{
|
{
|
||||||
/* We shifted a RuleSet. */
|
/* We shifted a RuleSet. */
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
new_state_info->tree_node = reduced_parser_node;
|
new_state_info->node_id = reduced_parser_node;
|
||||||
<% else %>
|
<% else %>
|
||||||
new_state_info->pvalue = reduced_parser_value;
|
new_state_info->pvalue = reduced_parser_value;
|
||||||
new_state_info->position = reduced_position;
|
new_state_info->position = reduced_position;
|
||||||
@ -1191,50 +1277,54 @@ static size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t start
|
|||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
if (parser_reduce_table[reduce_index].propagate_optional_target)
|
if (parser_reduce_table[reduce_index].propagate_optional_target)
|
||||||
{
|
{
|
||||||
reduced_parser_node = state_values_stack_index(&statevalues, -1)->tree_node;
|
reduced_parser_node = state_values_stack_index(&statevalues, -1)->node_id;
|
||||||
}
|
}
|
||||||
else if (parser_reduce_table[reduce_index].n_states > 0)
|
else if (parser_reduce_table[reduce_index].n_states > 0)
|
||||||
{
|
{
|
||||||
size_t n_fields = parser_reduce_table[reduce_index].rule_set_node_field_array_size;
|
uint16_t n_fields = parser_reduce_table[reduce_index].rule_set_node_field_array_size;
|
||||||
size_t bytes = sizeof(TreeNode) + n_fields * sizeof(void *);
|
/* Reserve child slots. New slots are zero-initialized
|
||||||
TreeNode * node = (TreeNode *)malloc(bytes);
|
* (null node ID) so absent optional children remain null. */
|
||||||
memset(node, 0, bytes);
|
<%= @grammar.prefix %>node_id_t child_offset = tree_reserve_children(context, n_fields);
|
||||||
node->position = INVALID_POSITION;
|
|
||||||
node->end_position = INVALID_POSITION;
|
|
||||||
node->n_fields = n_fields;
|
|
||||||
if (parser_reduce_table[reduce_index].rule_set_node_field_index_map == NULL)
|
if (parser_reduce_table[reduce_index].rule_set_node_field_index_map == NULL)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < parser_reduce_table[reduce_index].n_states; i++)
|
for (size_t i = 0; i < parser_reduce_table[reduce_index].n_states; i++)
|
||||||
{
|
{
|
||||||
node->fields[i] = (TreeNode *)state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->tree_node;
|
context-><%= @grammar.prefix %>tree_children[child_offset + i] = state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->node_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < parser_reduce_table[reduce_index].n_states; i++)
|
for (size_t i = 0; i < parser_reduce_table[reduce_index].n_states; i++)
|
||||||
{
|
{
|
||||||
node->fields[parser_reduce_table[reduce_index].rule_set_node_field_index_map[i]] = (TreeNode *)state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->tree_node;
|
context-><%= @grammar.prefix %>tree_children[child_offset + parser_reduce_table[reduce_index].rule_set_node_field_index_map[i]] = state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->node_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
<%= @grammar.prefix %>node_id_t node_id = tree_new_node(context);
|
||||||
|
<%= @grammar.prefix %>node_data_t * node = &context-><%= @grammar.prefix %>tree_nodes[node_id];
|
||||||
|
node->position = INVALID_POSITION;
|
||||||
|
node->end_position = INVALID_POSITION;
|
||||||
|
node->child_offset = child_offset;
|
||||||
|
node->n_fields = n_fields;
|
||||||
|
node->is_token = 0u;
|
||||||
bool position_found = false;
|
bool position_found = false;
|
||||||
for (size_t i = 0; i < n_fields; i++)
|
for (uint16_t i = 0; i < n_fields; i++)
|
||||||
{
|
{
|
||||||
TreeNode * child = node->fields[i];
|
<%= @grammar.prefix %>node_id_t child_id = context-><%= @grammar.prefix %>tree_children[child_offset + i];
|
||||||
if ((child != NULL) && <%= @grammar.prefix %>position_valid(child->position))
|
if ((child_id != 0u) && <%= @grammar.prefix %>position_valid(context-><%= @grammar.prefix %>tree_nodes[child_id].position))
|
||||||
{
|
{
|
||||||
if (!position_found)
|
if (!position_found)
|
||||||
{
|
{
|
||||||
node->position = child->position;
|
node->position = context-><%= @grammar.prefix %>tree_nodes[child_id].position;
|
||||||
position_found = true;
|
position_found = true;
|
||||||
}
|
}
|
||||||
node->end_position = child->end_position;
|
node->end_position = context-><%= @grammar.prefix %>tree_nodes[child_id].end_position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reduced_parser_node = node;
|
reduced_parser_node = node_id;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reduced_parser_node = NULL;
|
reduced_parser_node = 0u;
|
||||||
}
|
}
|
||||||
<% if @grammar.parser_user_code_used? %>
|
<% if @grammar.parser_user_code_used? %>
|
||||||
if (parser_user_code(reduced_parser_node, parser_reduce_table[reduce_index].rule, &statevalues, parser_reduce_table[reduce_index].n_states, context) == P_USER_TERMINATED)
|
if (parser_user_code(reduced_parser_node, parser_reduce_table[reduce_index].rule, &statevalues, parser_reduce_table[reduce_index].n_states, context) == P_USER_TERMINATED)
|
||||||
@ -1310,14 +1400,14 @@ size_t <%= @grammar.prefix %>parse_inner_<%= start_rule %>(<%= @grammar.prefix %
|
|||||||
* @return Parse result value.
|
* @return Parse result value.
|
||||||
*/
|
*/
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context)
|
<%= h_type(@grammar.start_rules[0]) %> <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context)
|
||||||
{
|
{
|
||||||
return (<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> *) context->parse_result;
|
return <%= tree_handle(h_type(@grammar.start_rules[0]), "context->parse_result") %>;
|
||||||
}
|
}
|
||||||
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
|
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
|
||||||
<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * <%= @grammar.prefix %>result_<%= start_rule %>(<%= @grammar.prefix %>context_t * context)
|
<%= h_type(start_rule) %> <%= @grammar.prefix %>result_<%= start_rule %>(<%= @grammar.prefix %>context_t * context)
|
||||||
{
|
{
|
||||||
return (<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> *) context->parse_result;
|
return <%= tree_handle(h_type(start_rule), "context->parse_result") %>;
|
||||||
}
|
}
|
||||||
<% end %>
|
<% end %>
|
||||||
<% else %>
|
<% else %>
|
||||||
@ -1420,48 +1510,3 @@ size_t <%= @grammar.prefix %>user_terminate_code(<%= @grammar.prefix %>context_t
|
|||||||
{
|
{
|
||||||
return context->token;
|
return context->token;
|
||||||
}
|
}
|
||||||
<% if @grammar.tree %>
|
|
||||||
|
|
||||||
static void tree_delete(TreeNode * node)
|
|
||||||
{
|
|
||||||
if (node->is_token)
|
|
||||||
{
|
|
||||||
<%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %> * token_tree_node = (<%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %> *)node;
|
|
||||||
<%= expand_code(@grammar.free_token_node, false, nil, nil) %>
|
|
||||||
<% if @cpp %>
|
|
||||||
delete token_tree_node;
|
|
||||||
<% else %>
|
|
||||||
free(token_tree_node);
|
|
||||||
<% end %>
|
|
||||||
}
|
|
||||||
else if (node->n_fields > 0u)
|
|
||||||
{
|
|
||||||
for (size_t i = 0u; i < node->n_fields; i++)
|
|
||||||
{
|
|
||||||
if (node->fields[i] != NULL)
|
|
||||||
{
|
|
||||||
tree_delete(node->fields[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free all tree node memory.
|
|
||||||
*/
|
|
||||||
void <%= @grammar.prefix %>tree_delete(<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * tree)
|
|
||||||
{
|
|
||||||
tree_delete((TreeNode *)tree);
|
|
||||||
}
|
|
||||||
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Free all tree node memory.
|
|
||||||
*/
|
|
||||||
void <%= @grammar.prefix %>tree_delete_<%= start_rule %>(<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * tree)
|
|
||||||
{
|
|
||||||
tree_delete((TreeNode *)tree);
|
|
||||||
}
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
|
|||||||
@ -105,22 +105,23 @@ public <%= typestring %> <%= @grammar.prefix %>value_get<%= name == "default" ?
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
/** Common tree node structure. */
|
/** Tree node ID type (index into the context node arena). ID 0 is null. */
|
||||||
private struct TreeNode
|
public alias <%= @grammar.prefix %>node_id_t = uint;
|
||||||
{
|
|
||||||
<%= @grammar.prefix %>position_t position;
|
|
||||||
<%= @grammar.prefix %>position_t end_position;
|
|
||||||
ushort n_fields;
|
|
||||||
bool is_token;
|
|
||||||
void *[0] fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Tree node types. @{ */
|
/**
|
||||||
public struct <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>
|
* 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.
|
||||||
|
*/
|
||||||
|
private struct <%= @grammar.prefix %>node_data_t
|
||||||
{
|
{
|
||||||
/* TreeNode fields must be present in the same order here. */
|
|
||||||
<%= @grammar.prefix %>position_t position;
|
<%= @grammar.prefix %>position_t position;
|
||||||
<%= @grammar.prefix %>position_t end_position;
|
<%= @grammar.prefix %>position_t end_position;
|
||||||
|
<%= @grammar.prefix %>node_id_t child_offset;
|
||||||
ushort n_fields;
|
ushort n_fields;
|
||||||
bool is_token;
|
bool is_token;
|
||||||
<%= @grammar.prefix %>token_t token;
|
<%= @grammar.prefix %>token_t token;
|
||||||
@ -128,22 +129,84 @@ public struct <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>
|
|||||||
<%= @grammar.token_user_fields %>
|
<%= @grammar.token_user_fields %>
|
||||||
}
|
}
|
||||||
|
|
||||||
<% @parser.rule_sets.each do |name, rule_set| %>
|
/** Tree node handle types. @{ */
|
||||||
<% next if name.start_with?("$") %>
|
|
||||||
<% next if rule_set.optional? %>
|
/** Token tree node handle. */
|
||||||
public struct <%= @grammar.tree_prefix %><%= name %><%= @grammar.tree_suffix %>
|
public struct <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>
|
||||||
{
|
{
|
||||||
<%= @grammar.prefix %>position_t position;
|
private <%= @grammar.prefix %>context_t * __context;
|
||||||
<%= @grammar.prefix %>position_t end_position;
|
private <%= @grammar.prefix %>node_id_t __id;
|
||||||
ushort n_fields;
|
|
||||||
bool is_token;
|
this(<%= @grammar.prefix %>context_t * context, <%= @grammar.prefix %>node_id_t id)
|
||||||
<% rule_set.tree_fields.each do |fields| %>
|
|
||||||
union
|
|
||||||
{
|
{
|
||||||
<% fields.each do |field_name, type| %>
|
this.__context = context;
|
||||||
<%= type %> * <%= field_name %>;
|
this.__id = id;
|
||||||
<% end %>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return whether this handle refers to a valid (non-null) node. */
|
||||||
|
@property bool valid()
|
||||||
|
{
|
||||||
|
return __id != 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Access the underlying node record (token, pvalue, and user fields). */
|
||||||
|
@property ref <%= @grammar.prefix %>node_data_t __node()
|
||||||
|
{
|
||||||
|
return __context.<%= @grammar.prefix %>tree_nodes[__id];
|
||||||
|
}
|
||||||
|
|
||||||
|
alias __node this;
|
||||||
|
}
|
||||||
|
|
||||||
|
<% tree_node_rule_sets.each do |rule_set| %>
|
||||||
|
/** <%= rule_set.name %> tree node handle. */
|
||||||
|
public struct <%= @grammar.tree_prefix %><%= rule_set.name %><%= @grammar.tree_suffix %>
|
||||||
|
{
|
||||||
|
private <%= @grammar.prefix %>context_t * __context;
|
||||||
|
private <%= @grammar.prefix %>node_id_t __id;
|
||||||
|
|
||||||
|
this(<%= @grammar.prefix %>context_t * context, <%= @grammar.prefix %>node_id_t id)
|
||||||
|
{
|
||||||
|
this.__context = context;
|
||||||
|
this.__id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return whether this handle refers to a valid (non-null) node. */
|
||||||
|
@property bool valid()
|
||||||
|
{
|
||||||
|
return __id != 0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Text position of the first code point spanned by this node. */
|
||||||
|
@property <%= @grammar.prefix %>position_t position()
|
||||||
|
{
|
||||||
|
return __context.<%= @grammar.prefix %>tree_nodes[__id].position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Text position of the last code point spanned by this node. */
|
||||||
|
@property <%= @grammar.prefix %>position_t end_position()
|
||||||
|
{
|
||||||
|
return __context.<%= @grammar.prefix %>tree_nodes[__id].end_position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Number of child fields in this node. */
|
||||||
|
@property ushort n_fields()
|
||||||
|
{
|
||||||
|
return __id ? __context.<%= @grammar.prefix %>tree_nodes[__id].n_fields : cast(ushort)0u;
|
||||||
|
}
|
||||||
|
<% rule_set.tree_fields.each_with_index do |fields, i| %>
|
||||||
|
<% fields.each do |field_name, type| %>
|
||||||
|
|
||||||
|
/** Access the <%= field_name %> child node. */
|
||||||
|
@property <%= type %> <%= field_name %>()
|
||||||
|
{
|
||||||
|
if (__id == 0u)
|
||||||
|
{
|
||||||
|
return <%= type %>(__context, 0u);
|
||||||
|
}
|
||||||
|
return <%= type %>(__context, __context.<%= @grammar.prefix %>tree_children[__context.<%= @grammar.prefix %>tree_nodes[__id].child_offset + <%= i %>u]);
|
||||||
|
}
|
||||||
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,7 +259,13 @@ public struct <%= @grammar.prefix %>context_t
|
|||||||
|
|
||||||
/** Parse result value. */
|
/** Parse result value. */
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
void * parse_result;
|
<%= @grammar.prefix %>node_id_t parse_result;
|
||||||
|
|
||||||
|
/** Tree node arena. Node ID 0 is reserved as the null node. */
|
||||||
|
<%= @grammar.prefix %>node_data_t[] <%= @grammar.prefix %>tree_nodes;
|
||||||
|
|
||||||
|
/** Shared tree child links. */
|
||||||
|
<%= @grammar.prefix %>node_id_t[] <%= @grammar.prefix %>tree_children;
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= @grammar.prefix %>value_t parse_result;
|
<%= @grammar.prefix %>value_t parse_result;
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -268,6 +337,11 @@ private enum size_t INVALID_ID = cast(size_t)-1;
|
|||||||
context.text_position.row = 1u;
|
context.text_position.row = 1u;
|
||||||
context.text_position.col = 1u;
|
context.text_position.col = 1u;
|
||||||
context.mode = <%= @lexer.mode_id("default") %>;
|
context.mode = <%= @lexer.mode_id("default") %>;
|
||||||
|
<% if @grammar.tree %>
|
||||||
|
|
||||||
|
/* Reserve node ID 0 as the null tree node. */
|
||||||
|
context.<%= @grammar.prefix %>tree_nodes = new <%= @grammar.prefix %>node_data_t[](1);
|
||||||
|
<% end %>
|
||||||
|
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
@ -280,6 +354,16 @@ private enum size_t INVALID_ID = cast(size_t)-1;
|
|||||||
*/
|
*/
|
||||||
void <%= @grammar.prefix %>context_delete(<%= @grammar.prefix %>context_t * context)
|
void <%= @grammar.prefix %>context_delete(<%= @grammar.prefix %>context_t * context)
|
||||||
{
|
{
|
||||||
|
<% if @grammar.tree && @grammar.free_token_node != "" %>
|
||||||
|
foreach (ref node; context.<%= @grammar.prefix %>tree_nodes)
|
||||||
|
{
|
||||||
|
if (node.is_token)
|
||||||
|
{
|
||||||
|
<%= @grammar.prefix %>node_data_t * token_tree_node = &node;
|
||||||
|
<%= expand_code(@grammar.free_token_node, false, nil, nil) %>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
<% end %>
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
@ -888,8 +972,8 @@ private struct state_value_t
|
|||||||
size_t state_id;
|
size_t state_id;
|
||||||
|
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
/** Tree node. */
|
/** Tree node ID. */
|
||||||
void * tree_node;
|
<%= @grammar.prefix %>node_id_t node_id;
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= @grammar.prefix %>position_t position;
|
<%= @grammar.prefix %>position_t position;
|
||||||
<%= @grammar.prefix %>position_t end_position;
|
<%= @grammar.prefix %>position_t end_position;
|
||||||
@ -1007,7 +1091,7 @@ private <%= @grammar.prefix %>position_t get_rule_position(state_value_t[] state
|
|||||||
* @retval P_USER_TERMINATED
|
* @retval P_USER_TERMINATED
|
||||||
* User requested to terminate parsing.
|
* User requested to terminate parsing.
|
||||||
*/
|
*/
|
||||||
private size_t parser_user_code(<%= @grammar.tree ? "void" : "#{@grammar.prefix}value_t" %> * _pvalue, uint rule, state_value_t[] statevalues, uint n_states, <%= @grammar.prefix %>context_t * context)
|
private size_t parser_user_code(<%= @grammar.tree ? "#{@grammar.prefix}node_id_t _node_id" : "#{@grammar.prefix}value_t * _pvalue" %>, uint rule, state_value_t[] statevalues, uint n_states, <%= @grammar.prefix %>context_t * context)
|
||||||
{
|
{
|
||||||
switch (rule)
|
switch (rule)
|
||||||
{
|
{
|
||||||
@ -1112,7 +1196,7 @@ private size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t star
|
|||||||
size_t reduced_rule_set = INVALID_ID;
|
size_t reduced_rule_set = INVALID_ID;
|
||||||
size_t last_shifted_rule_set_id = INVALID_ID;
|
size_t last_shifted_rule_set_id = INVALID_ID;
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
void * reduced_parser_node;
|
<%= @grammar.prefix %>node_id_t reduced_parser_node;
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= @grammar.prefix %>position_t reduced_position;
|
<%= @grammar.prefix %>position_t reduced_position;
|
||||||
<%= @grammar.prefix %>position_t reduced_end_position;
|
<%= @grammar.prefix %>position_t reduced_end_position;
|
||||||
@ -1153,7 +1237,7 @@ private size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t star
|
|||||||
{
|
{
|
||||||
/* Successful parse. */
|
/* Successful parse. */
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
context.parse_result = statevalues[$-1].tree_node;
|
context.parse_result = statevalues[$-1].node_id;
|
||||||
<% else %>
|
<% else %>
|
||||||
context.parse_result = statevalues[$-1].pvalue;
|
context.parse_result = statevalues[$-1].pvalue;
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -1179,7 +1263,7 @@ private size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t star
|
|||||||
context.input_index -= token_info.length;
|
context.input_index -= token_info.length;
|
||||||
context.text_position = token_info.position;
|
context.text_position = token_info.position;
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
context.parse_result = statevalues[$-1].tree_node;
|
context.parse_result = statevalues[$-1].node_id;
|
||||||
<% else %>
|
<% else %>
|
||||||
context.parse_result = statevalues[$-1].pvalue;
|
context.parse_result = statevalues[$-1].pvalue;
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -1199,9 +1283,17 @@ private size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t star
|
|||||||
{
|
{
|
||||||
/* We shifted a token, mark it consumed. */
|
/* We shifted a token, mark it consumed. */
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
<%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %> * token_tree_node = new <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>(token_info.position, token_info.end_position, 0u, true, token, token_info.pvalue);
|
<%= @grammar.prefix %>node_id_t token_node_id = cast(<%= @grammar.prefix %>node_id_t)context.<%= @grammar.prefix %>tree_nodes.length;
|
||||||
|
context.<%= @grammar.prefix %>tree_nodes ~= <%= @grammar.prefix %>node_data_t.init;
|
||||||
|
<%= @grammar.prefix %>node_data_t * token_tree_node = &context.<%= @grammar.prefix %>tree_nodes[token_node_id];
|
||||||
|
token_tree_node.position = token_info.position;
|
||||||
|
token_tree_node.end_position = token_info.end_position;
|
||||||
|
token_tree_node.n_fields = 0u;
|
||||||
|
token_tree_node.is_token = true;
|
||||||
|
token_tree_node.token = token;
|
||||||
|
token_tree_node.pvalue = token_info.pvalue;
|
||||||
<%= expand_code(@grammar.on_token_node, false, nil, nil) %>
|
<%= expand_code(@grammar.on_token_node, false, nil, nil) %>
|
||||||
statevalues[$-1].tree_node = token_tree_node;
|
statevalues[$-1].node_id = token_node_id;
|
||||||
<% else %>
|
<% else %>
|
||||||
statevalues[$-1].position = token_info.position;
|
statevalues[$-1].position = token_info.position;
|
||||||
statevalues[$-1].end_position = token_info.end_position;
|
statevalues[$-1].end_position = token_info.end_position;
|
||||||
@ -1213,7 +1305,7 @@ private size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t star
|
|||||||
{
|
{
|
||||||
/* We shifted a RuleSet. */
|
/* We shifted a RuleSet. */
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
statevalues[$-1].tree_node = reduced_parser_node;
|
statevalues[$-1].node_id = reduced_parser_node;
|
||||||
<% else %>
|
<% else %>
|
||||||
statevalues[$-1].pvalue = reduced_parser_value;
|
statevalues[$-1].pvalue = reduced_parser_value;
|
||||||
statevalues[$-1].position = reduced_position;
|
statevalues[$-1].position = reduced_position;
|
||||||
@ -1242,55 +1334,56 @@ private size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t star
|
|||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
if (parser_reduce_table[reduce_index].propagate_optional_target)
|
if (parser_reduce_table[reduce_index].propagate_optional_target)
|
||||||
{
|
{
|
||||||
reduced_parser_node = statevalues[$ - 1].tree_node;
|
reduced_parser_node = statevalues[$ - 1].node_id;
|
||||||
}
|
}
|
||||||
else if (parser_reduce_table[reduce_index].n_states > 0)
|
else if (parser_reduce_table[reduce_index].n_states > 0)
|
||||||
{
|
{
|
||||||
size_t n_fields = parser_reduce_table[reduce_index].rule_set_node_field_array_size;
|
ushort n_fields = parser_reduce_table[reduce_index].rule_set_node_field_array_size;
|
||||||
size_t node_size = TreeNode.sizeof + n_fields * (void *).sizeof;
|
/* Reserve child slots. New slots are zero-initialized
|
||||||
TreeNode * node = cast(TreeNode *)malloc(node_size);
|
* (null node ID) so absent optional children remain null. */
|
||||||
GC.addRange(node, node_size);
|
<%= @grammar.prefix %>node_id_t child_offset = cast(<%= @grammar.prefix %>node_id_t)context.<%= @grammar.prefix %>tree_children.length;
|
||||||
node.position = <%= @grammar.prefix %>position_t.INVALID;
|
context.<%= @grammar.prefix %>tree_children.length += n_fields;
|
||||||
node.end_position = <%= @grammar.prefix %>position_t.INVALID;
|
|
||||||
node.n_fields = cast(ushort)n_fields;
|
|
||||||
node.is_token = false;
|
|
||||||
foreach (i; 0..n_fields)
|
|
||||||
{
|
|
||||||
node.fields[i] = null;
|
|
||||||
}
|
|
||||||
if (parser_reduce_table[reduce_index].rule_set_node_field_index_map is null)
|
if (parser_reduce_table[reduce_index].rule_set_node_field_index_map is null)
|
||||||
{
|
{
|
||||||
foreach (i; 0..parser_reduce_table[reduce_index].n_states)
|
foreach (i; 0..parser_reduce_table[reduce_index].n_states)
|
||||||
{
|
{
|
||||||
node.fields[i] = statevalues[$ - parser_reduce_table[reduce_index].n_states + i].tree_node;
|
context.<%= @grammar.prefix %>tree_children[child_offset + i] = statevalues[$ - parser_reduce_table[reduce_index].n_states + i].node_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (i; 0..parser_reduce_table[reduce_index].n_states)
|
foreach (i; 0..parser_reduce_table[reduce_index].n_states)
|
||||||
{
|
{
|
||||||
node.fields[parser_reduce_table[reduce_index].rule_set_node_field_index_map[i]] = statevalues[$ - parser_reduce_table[reduce_index].n_states + i].tree_node;
|
context.<%= @grammar.prefix %>tree_children[child_offset + parser_reduce_table[reduce_index].rule_set_node_field_index_map[i]] = statevalues[$ - parser_reduce_table[reduce_index].n_states + i].node_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
<%= @grammar.prefix %>node_id_t node_id = cast(<%= @grammar.prefix %>node_id_t)context.<%= @grammar.prefix %>tree_nodes.length;
|
||||||
|
context.<%= @grammar.prefix %>tree_nodes ~= <%= @grammar.prefix %>node_data_t.init;
|
||||||
|
<%= @grammar.prefix %>node_data_t * node = &context.<%= @grammar.prefix %>tree_nodes[node_id];
|
||||||
|
node.position = <%= @grammar.prefix %>position_t.INVALID;
|
||||||
|
node.end_position = <%= @grammar.prefix %>position_t.INVALID;
|
||||||
|
node.child_offset = child_offset;
|
||||||
|
node.n_fields = n_fields;
|
||||||
|
node.is_token = false;
|
||||||
bool position_found = false;
|
bool position_found = false;
|
||||||
foreach (i; 0..n_fields)
|
foreach (i; 0..n_fields)
|
||||||
{
|
{
|
||||||
TreeNode * child = cast(TreeNode *)node.fields[i];
|
<%= @grammar.prefix %>node_id_t child_id = context.<%= @grammar.prefix %>tree_children[child_offset + i];
|
||||||
if (child && child.position.valid)
|
if (child_id != 0u && context.<%= @grammar.prefix %>tree_nodes[child_id].position.valid)
|
||||||
{
|
{
|
||||||
if (!position_found)
|
if (!position_found)
|
||||||
{
|
{
|
||||||
node.position = child.position;
|
node.position = context.<%= @grammar.prefix %>tree_nodes[child_id].position;
|
||||||
position_found = true;
|
position_found = true;
|
||||||
}
|
}
|
||||||
node.end_position = child.end_position;
|
node.end_position = context.<%= @grammar.prefix %>tree_nodes[child_id].end_position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reduced_parser_node = node;
|
reduced_parser_node = node_id;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reduced_parser_node = null;
|
reduced_parser_node = 0u;
|
||||||
}
|
}
|
||||||
<% if @grammar.parser_user_code_used? %>
|
<% if @grammar.parser_user_code_used? %>
|
||||||
if (parser_user_code(reduced_parser_node, parser_reduce_table[reduce_index].rule, statevalues, parser_reduce_table[reduce_index].n_states, context) == P_USER_TERMINATED)
|
if (parser_user_code(reduced_parser_node, parser_reduce_table[reduce_index].rule, statevalues, parser_reduce_table[reduce_index].n_states, context) == P_USER_TERMINATED)
|
||||||
@ -1360,14 +1453,14 @@ public size_t <%= @grammar.prefix %>parse_inner_<%= start_rule %>(<%= @grammar.p
|
|||||||
* @return Parse result value.
|
* @return Parse result value.
|
||||||
*/
|
*/
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
public <%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context)
|
public <%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context)
|
||||||
{
|
{
|
||||||
return cast(<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> *)context.parse_result;
|
return <%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %>(context, context.parse_result);
|
||||||
}
|
}
|
||||||
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
|
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
|
||||||
public <%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * <%= @grammar.prefix %>result_<%= start_rule %>(<%= @grammar.prefix %>context_t * context)
|
public <%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> <%= @grammar.prefix %>result_<%= start_rule %>(<%= @grammar.prefix %>context_t * context)
|
||||||
{
|
{
|
||||||
return cast(<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> *)context.parse_result;
|
return <%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %>(context, context.parse_result);
|
||||||
}
|
}
|
||||||
<% end %>
|
<% end %>
|
||||||
<% else %>
|
<% else %>
|
||||||
@ -1383,34 +1476,6 @@ public <%= start_rule_type(i)[1] %> <%= @grammar.prefix %>result_<%= start_rule
|
|||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if @grammar.tree %>
|
|
||||||
private void tree_delete(TreeNode * node)
|
|
||||||
{
|
|
||||||
if (!node.is_token)
|
|
||||||
{
|
|
||||||
for (size_t i = 0u; i < node.n_fields; i++)
|
|
||||||
{
|
|
||||||
if (node.fields[i])
|
|
||||||
{
|
|
||||||
tree_delete(cast(TreeNode *)node.fields[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GC.removeRange(node);
|
|
||||||
free(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void <%= @grammar.prefix %>tree_delete(<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * tree)
|
|
||||||
{
|
|
||||||
tree_delete(cast(TreeNode *)tree);
|
|
||||||
}
|
|
||||||
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
|
|
||||||
void <%= @grammar.prefix %>tree_delete_<%= start_rule %>(<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * tree)
|
|
||||||
{
|
|
||||||
tree_delete(cast(TreeNode *)tree);
|
|
||||||
}
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current text input position.
|
* Get the current text input position.
|
||||||
|
|||||||
@ -8,6 +8,9 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
<% if @cpp %>
|
||||||
|
#include <vector>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
* Public types
|
* Public types
|
||||||
@ -88,47 +91,29 @@ static inline <%= typestring %> <%= @grammar.prefix %>value_get<%= name == "defa
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
/** Tree node types. @{ */
|
/** Tree node ID type (index into the context node arena). ID 0 is null. */
|
||||||
typedef struct <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>
|
typedef uint32_t <%= @grammar.prefix %>node_id_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 nonzero.
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
{
|
{
|
||||||
<% # TreeNode fields must be present in the same order here. # %>
|
|
||||||
<%= @grammar.prefix %>position_t position;
|
<%= @grammar.prefix %>position_t position;
|
||||||
<%= @grammar.prefix %>position_t end_position;
|
<%= @grammar.prefix %>position_t end_position;
|
||||||
|
<%= @grammar.prefix %>node_id_t child_offset;
|
||||||
uint16_t n_fields;
|
uint16_t n_fields;
|
||||||
uint8_t is_token;
|
uint8_t is_token;
|
||||||
<%= @grammar.token_user_fields %>
|
|
||||||
<%= @grammar.prefix %>token_t token;
|
<%= @grammar.prefix %>token_t token;
|
||||||
<%= @grammar.prefix %>value_t pvalue;
|
<%= @grammar.prefix %>value_t pvalue;
|
||||||
} <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>;
|
<%= @grammar.token_user_fields %>
|
||||||
|
} <%= @grammar.prefix %>node_data_t;
|
||||||
<% @parser.rule_sets.each do |name, rule_set| %>
|
|
||||||
<% next if name.start_with?("$") %>
|
|
||||||
<% next if rule_set.optional? %>
|
|
||||||
struct <%= @grammar.tree_prefix %><%= name %><%= @grammar.tree_suffix %>;
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<% @parser.rule_sets.each do |name, rule_set| %>
|
|
||||||
<% next if name.start_with?("$") %>
|
|
||||||
<% next if rule_set.optional? %>
|
|
||||||
typedef struct <%= @grammar.tree_prefix %><%= name %><%= @grammar.tree_suffix %>
|
|
||||||
{
|
|
||||||
<% # TreeNode fields must be present in the same order here. # %>
|
|
||||||
<%= @grammar.prefix %>position_t position;
|
|
||||||
<%= @grammar.prefix %>position_t end_position;
|
|
||||||
uint16_t n_fields;
|
|
||||||
uint8_t is_token;
|
|
||||||
<% rule_set.tree_fields.each do |fields| %>
|
|
||||||
union
|
|
||||||
{
|
|
||||||
<% fields.each do |field_name, type| %>
|
|
||||||
struct <%= type %> * <%= field_name %>;
|
|
||||||
<% end %>
|
|
||||||
};
|
|
||||||
<% end %>
|
|
||||||
} <%= @grammar.tree_prefix %><%= name %><%= @grammar.tree_suffix %>;
|
|
||||||
|
|
||||||
<% end %>
|
|
||||||
/** @} */
|
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
/** Lexed token information. */
|
/** Lexed token information. */
|
||||||
@ -179,7 +164,25 @@ typedef struct
|
|||||||
|
|
||||||
/** Parse result value. */
|
/** Parse result value. */
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
void * parse_result;
|
<%= @grammar.prefix %>node_id_t parse_result;
|
||||||
|
|
||||||
|
<% if @cpp %>
|
||||||
|
/** Tree node arena. Node ID 0 is reserved as the null node. */
|
||||||
|
std::vector<<%= @grammar.prefix %>node_data_t> <%= @grammar.prefix %>tree_nodes;
|
||||||
|
|
||||||
|
/** Shared tree child links. */
|
||||||
|
std::vector<<%= @grammar.prefix %>node_id_t> <%= @grammar.prefix %>tree_children;
|
||||||
|
<% else %>
|
||||||
|
/** Tree node arena. Node ID 0 is reserved as the null node. */
|
||||||
|
<%= @grammar.prefix %>node_data_t * <%= @grammar.prefix %>tree_nodes;
|
||||||
|
size_t <%= @grammar.prefix %>tree_nodes_length;
|
||||||
|
size_t <%= @grammar.prefix %>tree_nodes_capacity;
|
||||||
|
|
||||||
|
/** Shared tree child links. */
|
||||||
|
<%= @grammar.prefix %>node_id_t * <%= @grammar.prefix %>tree_children;
|
||||||
|
size_t <%= @grammar.prefix %>tree_children_length;
|
||||||
|
size_t <%= @grammar.prefix %>tree_children_capacity;
|
||||||
|
<% end %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= @grammar.prefix %>value_t parse_result;
|
<%= @grammar.prefix %>value_t parse_result;
|
||||||
<% end %>
|
<% end %>
|
||||||
@ -193,6 +196,10 @@ typedef struct
|
|||||||
<%= @grammar.context_user_fields %>
|
<%= @grammar.context_user_fields %>
|
||||||
} <%= @grammar.prefix %>context_t;
|
} <%= @grammar.prefix %>context_t;
|
||||||
|
|
||||||
|
<% if @grammar.tree %>
|
||||||
|
<%= c_tree_types_header %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
* Public data
|
* Public data
|
||||||
*************************************************************************/
|
*************************************************************************/
|
||||||
@ -217,9 +224,9 @@ size_t <%= @grammar.prefix %>parse_inner_<%= start_rule %>(<%= @grammar.prefix %
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if @grammar.tree %>
|
<% if @grammar.tree %>
|
||||||
<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context);
|
<%= h_type(@grammar.start_rules[0]) %> <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context);
|
||||||
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
|
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
|
||||||
<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * <%= @grammar.prefix %>result_<%= start_rule %>(<%= @grammar.prefix %>context_t * context);
|
<%= h_type(start_rule) %> <%= @grammar.prefix %>result_<%= start_rule %>(<%= @grammar.prefix %>context_t * context);
|
||||||
<% end %>
|
<% end %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<%= start_rule_type[1] %> <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context);
|
<%= start_rule_type[1] %> <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context);
|
||||||
@ -228,13 +235,6 @@ size_t <%= @grammar.prefix %>parse_inner_<%= start_rule %>(<%= @grammar.prefix %
|
|||||||
<% end %>
|
<% end %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if @grammar.tree %>
|
|
||||||
void <%= @grammar.prefix %>tree_delete(<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * tree);
|
|
||||||
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
|
|
||||||
void <%= @grammar.prefix %>tree_delete_<%= start_rule %>(<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * tree);
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<%= @grammar.prefix %>position_t <%= @grammar.prefix %>position(<%= @grammar.prefix %>context_t * context);
|
<%= @grammar.prefix %>position_t <%= @grammar.prefix %>position(<%= @grammar.prefix %>context_t * context);
|
||||||
|
|
||||||
void <%= @grammar.prefix %>set_position(<%= @grammar.prefix %>context_t * context, <%= @grammar.prefix %>position_t position);
|
void <%= @grammar.prefix %>set_position(<%= @grammar.prefix %>context_t * context, <%= @grammar.prefix %>position_t position);
|
||||||
|
|||||||
1052
assets/parser.rs.erb
Normal file
1052
assets/parser.rs.erb
Normal file
File diff suppressed because it is too large
Load Diff
@ -13,7 +13,7 @@ Propane is a LALR Parser Generator (LPG) which:
|
|||||||
* generates a built-in lexer to tokenize input
|
* generates a built-in lexer to tokenize input
|
||||||
* supports UTF-8 lexer inputs
|
* supports UTF-8 lexer inputs
|
||||||
* generates a table-driven shift/reduce parser to parse input in linear time
|
* generates a table-driven shift/reduce parser to parse input in linear time
|
||||||
* targets C, C++, or D language outputs
|
* targets C, C++, D, or Rust language outputs
|
||||||
* optionally supports automatic full parse tree generation
|
* optionally supports automatic full parse tree generation
|
||||||
* supports starting parsing from multiple start rules
|
* supports starting parsing from multiple start rules
|
||||||
* tracks input text start and end positions for all matched tokens/rules
|
* tracks input text start and end positions for all matched tokens/rules
|
||||||
@ -276,19 +276,40 @@ Parser rule code blocks are still supported in tree generation mode, but they
|
|||||||
behave differently than when tree generation mode is not active.
|
behave differently than when tree generation mode is not active.
|
||||||
The code block for a rule is executed after the rule has been matched and its
|
The code block for a rule is executed after the rule has been matched and its
|
||||||
tree node has been fully formed.
|
tree node has been fully formed.
|
||||||
Within the code block, `$$` refers to the tree node for the reduced rule, typed
|
Within the code block, `$$` refers to the tree node handle for the reduced
|
||||||
as a pointer to that rule's generated tree node structure.
|
rule, and the rule components are accessed positionally with `$1`, `$2`, `$3`,
|
||||||
The tree nodes for the rule components are accessed positionally with `$1`,
|
etc..., each a tree node handle for that component (a rule node or a `Token`
|
||||||
`$2`, `$3`, etc..., each typed as a pointer to the generated tree node structure
|
node).
|
||||||
for that component (a rule node or a `Token` node).
|
|
||||||
Field aliases (see the "Specifying parser rules" section) may also be used to
|
Field aliases (see the "Specifying parser rules" section) may also be used to
|
||||||
reference a component tree node by name; a field alias behaves identically to
|
reference a component tree node by name; a field alias behaves identically to
|
||||||
the positional reference for that component.
|
the positional reference for that component.
|
||||||
The positional position expansions (`${$.position}`, `${N.position}`, etc...)
|
|
||||||
are not available in tree generation mode; the `position` and `end_position`
|
|
||||||
fields of the tree nodes can be accessed directly instead.
|
|
||||||
|
|
||||||
Example:
|
Tree nodes are stored in a compact arena owned by the parser context and are
|
||||||
|
referenced by lightweight handles rather than pointers. The whole tree is freed
|
||||||
|
together with the context by `p_context_delete()`; there is no separate tree
|
||||||
|
delete function, and tree node handles are only valid while the context is
|
||||||
|
alive.
|
||||||
|
|
||||||
|
Child fields, positions, and token payloads are accessed through per-language
|
||||||
|
accessors on a node handle:
|
||||||
|
|
||||||
|
* C: field accessor functions `p_TYPE_field(node)` and tree walk macros
|
||||||
|
`p_tree_walk_TYPE(node, field1, field2, ...)`; generic accessors
|
||||||
|
`p_node_valid(node)`, `p_node_position(node)`, `p_node_end_position(node)`,
|
||||||
|
`p_node_n_fields(node)`, `p_node_data(node)` (a pointer to the node record,
|
||||||
|
for token payload and user fields), and `p_node_id(node)` (for identity
|
||||||
|
comparison).
|
||||||
|
* C++: handle methods called with `()`, e.g. `node.field()`, `node.valid()`,
|
||||||
|
`node.position()`, `node.token()`, `node.pvalue()`, and `node.data()`. The
|
||||||
|
C-style functions and macros above are also available.
|
||||||
|
* D: `@property` accessors, e.g. `node.field`, `node.valid`, `node.position`,
|
||||||
|
`node.token`, `node.pvalue`.
|
||||||
|
|
||||||
|
The positional position expansions (`${$.position}`, `${N.position}`, etc...)
|
||||||
|
are not available in tree generation mode; use the position accessors above
|
||||||
|
instead.
|
||||||
|
|
||||||
|
C example:
|
||||||
|
|
||||||
```
|
```
|
||||||
tree;
|
tree;
|
||||||
@ -297,9 +318,9 @@ Assignment -> ident equals Expr <<
|
|||||||
/* $$ is the Assignment tree node, $1 is the ident Token node, and $3 is
|
/* $$ is the Assignment tree node, $1 is the ident Token node, and $3 is
|
||||||
* the Expr rule node. */
|
* the Expr rule node. */
|
||||||
printf("assignment on row %d, col %d\n",
|
printf("assignment on row %d, col %d\n",
|
||||||
$$->position.row, $$->position.col);
|
p_node_position($$).row, p_node_position($$).col);
|
||||||
printf("target identifier ends on row %d, col %d\n",
|
printf("target identifier ends on row %d, col %d\n",
|
||||||
$1->end_position.row, $1->end_position.col);
|
p_node_end_position($1).row, p_node_end_position($1).col);
|
||||||
>>
|
>>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -1406,7 +1427,7 @@ In this case, the parser will start parsing with the `Statement` rule.
|
|||||||
|
|
||||||
For each start rule, a `p_parse_inner_XXX()` function is also generated.
|
For each start rule, a `p_parse_inner_XXX()` function is also generated.
|
||||||
This variant of the parser entry point accepts a caller-provided array of
|
This variant of the parser entry point accepts a caller-provided array of
|
||||||
"follow tokens" — tokens the caller allows to appear immediately after the
|
"follow tokens" -- tokens the caller allows to appear immediately after the
|
||||||
start rule in some outer grammar context.
|
start rule in some outer grammar context.
|
||||||
It is useful when embedding a Propane-generated sub-parser within an outer
|
It is useful when embedding a Propane-generated sub-parser within an outer
|
||||||
parser and the outer parser knows which tokens naturally terminate the
|
parser and the outer parser knows which tokens naturally terminate the
|
||||||
|
|||||||
@ -18,6 +18,8 @@ class Propane
|
|||||||
elsif output_file =~ %r{\.(cc|cpp|cxx)$}
|
elsif output_file =~ %r{\.(cc|cpp|cxx)$}
|
||||||
@cpp = true
|
@cpp = true
|
||||||
"c"
|
"c"
|
||||||
|
elsif output_file.end_with?(".rs")
|
||||||
|
"rust"
|
||||||
else
|
else
|
||||||
raise Error.new("Could not determine target language from output file name (#{output_file})")
|
raise Error.new("Could not determine target language from output file name (#{output_file})")
|
||||||
end
|
end
|
||||||
@ -31,7 +33,8 @@ class Propane
|
|||||||
extensions += %w[h]
|
extensions += %w[h]
|
||||||
end
|
end
|
||||||
extensions.each do |extension|
|
extensions.each do |extension|
|
||||||
template = Assets.get("parser.#{extension || @language}.erb")
|
template_language = @language == "rust" ? "rs" : @language
|
||||||
|
template = Assets.get("parser.#{extension || template_language}.erb")
|
||||||
if extension
|
if extension
|
||||||
output_file = @output_file.sub(%r{\.[a-z]+$}, ".#{extension}")
|
output_file = @output_file.sub(%r{\.[a-z]+$}, ".#{extension}")
|
||||||
else
|
else
|
||||||
@ -39,7 +42,12 @@ class Propane
|
|||||||
end
|
end
|
||||||
erb = ERB.new(template, trim_mode: "<>")
|
erb = ERB.new(template, trim_mode: "<>")
|
||||||
result = erb.result(binding.clone).lines.each_with_index.map do |line, i|
|
result = erb.result(binding.clone).lines.each_with_index.map do |line, i|
|
||||||
if line == "#linereset\n"
|
if @language == "rust"
|
||||||
|
# Rust has no #line directive support, so strip the directives that
|
||||||
|
# the grammar embeds in user code blocks.
|
||||||
|
line = line.sub(/^#line \d+ "[^"]*"/, "")
|
||||||
|
line == "#linereset\n" ? "" : line
|
||||||
|
elsif line == "#linereset\n"
|
||||||
%[#line #{i + 2} "#{output_file}"\n]
|
%[#line #{i + 2} "#{output_file}"\n]
|
||||||
else
|
else
|
||||||
line
|
line
|
||||||
@ -275,6 +283,8 @@ class Propane
|
|||||||
"context->user_terminate_code = (#{user_terminate_code}); return #{retval};"
|
"context->user_terminate_code = (#{user_terminate_code}); return #{retval};"
|
||||||
when "d"
|
when "d"
|
||||||
"context.user_terminate_code = (#{user_terminate_code}); return #{retval};"
|
"context.user_terminate_code = (#{user_terminate_code}); return #{retval};"
|
||||||
|
when "rust"
|
||||||
|
"context.user_terminate_code = (#{user_terminate_code}); return #{retval};"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
code = code.gsub(/\$\{context\.(\w+)\}/) do |match|
|
code = code.gsub(/\$\{context\.(\w+)\}/) do |match|
|
||||||
@ -284,6 +294,8 @@ class Propane
|
|||||||
"context->#{fieldname}"
|
"context->#{fieldname}"
|
||||||
when "d"
|
when "d"
|
||||||
"context.#{fieldname}"
|
"context.#{fieldname}"
|
||||||
|
when "rust"
|
||||||
|
"context.#{fieldname}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
code = code.gsub(/\$\{token\.(\w+)\}/) do |match|
|
code = code.gsub(/\$\{token\.(\w+)\}/) do |match|
|
||||||
@ -293,16 +305,21 @@ class Propane
|
|||||||
"token_tree_node->#{fieldname}"
|
"token_tree_node->#{fieldname}"
|
||||||
when "d"
|
when "d"
|
||||||
"token_tree_node.#{fieldname}"
|
"token_tree_node.#{fieldname}"
|
||||||
|
when "rust"
|
||||||
|
"token_tree_node.#{fieldname}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if parser
|
if parser
|
||||||
code = code.gsub(/\$\$/) do |match|
|
code = code.gsub(/\$\$/) do |match|
|
||||||
if @grammar.tree
|
if @grammar.tree
|
||||||
|
typename = "#{@grammar.tree_prefix}#{rule.name}#{@grammar.tree_suffix}"
|
||||||
case @language
|
case @language
|
||||||
when "c"
|
when "c"
|
||||||
"((#{@grammar.tree_prefix}#{rule.name}#{@grammar.tree_suffix} *)_pvalue)"
|
tree_handle(typename, "_node_id")
|
||||||
when "d"
|
when "d"
|
||||||
"(cast(#{@grammar.tree_prefix}#{rule.name}#{@grammar.tree_suffix} *)_pvalue)"
|
tree_handle(typename, "_node_id")
|
||||||
|
when "rust"
|
||||||
|
tree_handle(typename, "_node_id")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
case @language
|
case @language
|
||||||
@ -310,6 +327,8 @@ class Propane
|
|||||||
"_pvalue->v_#{rule.ptypename}"
|
"_pvalue->v_#{rule.ptypename}"
|
||||||
when "d"
|
when "d"
|
||||||
"_pvalue.v_#{rule.ptypename}"
|
"_pvalue.v_#{rule.ptypename}"
|
||||||
|
when "rust"
|
||||||
|
"(*_pvalue.v_#{rule.ptypename}_mut())"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -344,6 +363,8 @@ class Propane
|
|||||||
"out_token_info->pvalue"
|
"out_token_info->pvalue"
|
||||||
when "d"
|
when "d"
|
||||||
"out_token_info.pvalue"
|
"out_token_info.pvalue"
|
||||||
|
when "rust"
|
||||||
|
"out_token_info.pvalue"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
case @language
|
case @language
|
||||||
@ -351,6 +372,8 @@ class Propane
|
|||||||
"out_token_info->pvalue.v_#{pattern.ptypename}"
|
"out_token_info->pvalue.v_#{pattern.ptypename}"
|
||||||
when "d"
|
when "d"
|
||||||
"out_token_info.pvalue.v_#{pattern.ptypename}"
|
"out_token_info.pvalue.v_#{pattern.ptypename}"
|
||||||
|
when "rust"
|
||||||
|
"(*out_token_info.pvalue.v_#{pattern.ptypename}_mut())"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -360,6 +383,8 @@ class Propane
|
|||||||
"out_token_info->position"
|
"out_token_info->position"
|
||||||
when "d"
|
when "d"
|
||||||
"out_token_info.position"
|
"out_token_info.position"
|
||||||
|
when "rust"
|
||||||
|
"out_token_info.position"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
code = code.gsub(/\$\{end_position\}/) do |match|
|
code = code.gsub(/\$\{end_position\}/) do |match|
|
||||||
@ -368,6 +393,8 @@ class Propane
|
|||||||
"out_token_info->end_position"
|
"out_token_info->end_position"
|
||||||
when "d"
|
when "d"
|
||||||
"out_token_info.end_position"
|
"out_token_info.end_position"
|
||||||
|
when "rust"
|
||||||
|
"out_token_info.end_position"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
code = code.gsub(/\$mode\(([a-zA-Z_][a-zA-Z_0-9]*)\)/) do |match|
|
code = code.gsub(/\$mode\(([a-zA-Z_][a-zA-Z_0-9]*)\)/) do |match|
|
||||||
@ -381,6 +408,8 @@ class Propane
|
|||||||
"context->mode = #{mode_id}u"
|
"context->mode = #{mode_id}u"
|
||||||
when "d"
|
when "d"
|
||||||
"context.mode = #{mode_id}u"
|
"context.mode = #{mode_id}u"
|
||||||
|
when "rust"
|
||||||
|
"context.mode = #{mode_id}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -402,7 +431,7 @@ class Propane
|
|||||||
def parser_component_reference(rule, index)
|
def parser_component_reference(rule, index)
|
||||||
component = rule.components[index - 1]
|
component = rule.components[index - 1]
|
||||||
if @grammar.tree
|
if @grammar.tree
|
||||||
# In tree mode a component reference yields a pointer to that
|
# In tree mode a component reference yields a handle to that
|
||||||
# component's tree node. An optional component propagates its target
|
# component's tree node. An optional component propagates its target
|
||||||
# node (or null), so use the optional target's node type.
|
# node (or null), so use the optional target's node type.
|
||||||
if component.is_a?(RuleSet) && component.optional?
|
if component.is_a?(RuleSet) && component.optional?
|
||||||
@ -412,9 +441,11 @@ class Propane
|
|||||||
typename = "#{@grammar.tree_prefix}#{node_name}#{@grammar.tree_suffix}"
|
typename = "#{@grammar.tree_prefix}#{node_name}#{@grammar.tree_suffix}"
|
||||||
case @language
|
case @language
|
||||||
when "c"
|
when "c"
|
||||||
"((#{typename} *)state_values_stack_index(statevalues, -1 - (int)n_states + #{index})->tree_node)"
|
tree_handle(typename, "state_values_stack_index(statevalues, -1 - (int)n_states + #{index})->node_id")
|
||||||
when "d"
|
when "d"
|
||||||
"(cast(#{typename} *)statevalues[$-1-n_states+#{index}].tree_node)"
|
tree_handle(typename, "statevalues[$-1-n_states+#{index}].node_id")
|
||||||
|
when "rust"
|
||||||
|
tree_handle(typename, "statevalues[statevalues.len() - 1 - n_states + #{index}].node_id")
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
case @language
|
case @language
|
||||||
@ -422,10 +453,381 @@ class Propane
|
|||||||
"state_values_stack_index(statevalues, -1 - (int)n_states + #{index})->pvalue.v_#{component.ptypename}"
|
"state_values_stack_index(statevalues, -1 - (int)n_states + #{index})->pvalue.v_#{component.ptypename}"
|
||||||
when "d"
|
when "d"
|
||||||
"statevalues[$-1-n_states+#{index}].pvalue.v_#{component.ptypename}"
|
"statevalues[$-1-n_states+#{index}].pvalue.v_#{component.ptypename}"
|
||||||
|
when "rust"
|
||||||
|
"statevalues[statevalues.len() - 1 - n_states + #{index}].pvalue.get_v_#{component.ptypename}()"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Construct a tree node handle expression for the target language.
|
||||||
|
#
|
||||||
|
# A handle is a small value pairing the parser context with a node ID
|
||||||
|
# (an index into the context's node arena). All handle types share this
|
||||||
|
# layout; the distinct types exist for documentation and, in C, to drive
|
||||||
|
# the tree walk macro's type threading.
|
||||||
|
#
|
||||||
|
# @param typename [String]
|
||||||
|
# Handle type name.
|
||||||
|
# @param id_expr [String]
|
||||||
|
# Expression yielding the node ID.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# Handle constructor expression.
|
||||||
|
def tree_handle(typename, id_expr)
|
||||||
|
if @cpp
|
||||||
|
"(#{typename}{context, #{id_expr}})"
|
||||||
|
elsif @language == "c"
|
||||||
|
"((#{typename}){context, #{id_expr}})"
|
||||||
|
elsif @language == "rust"
|
||||||
|
"(#{typename} { context, id: #{id_expr} })"
|
||||||
|
else
|
||||||
|
"#{typename}(context, #{id_expr})"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the list of non-optional, non-internal rule sets that get a tree node
|
||||||
|
# handle type generated for them.
|
||||||
|
#
|
||||||
|
# @return [Array<Propane::RuleSet>]
|
||||||
|
# Rule sets with generated tree node handle types.
|
||||||
|
def tree_node_rule_sets
|
||||||
|
@parser.rule_sets.reject do |name, rule_set|
|
||||||
|
name.start_with?("$") || rule_set.optional?
|
||||||
|
end.map {|name, rule_set| rule_set}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Maximum number of chained fields supported by a single C tree walk macro
|
||||||
|
# invocation. Deeper navigation can be expressed by nesting walk calls.
|
||||||
|
C_TREE_WALK_MAX = 16
|
||||||
|
|
||||||
|
# Get the tree node handle type name for a node name.
|
||||||
|
#
|
||||||
|
# @param name [String]
|
||||||
|
# Rule set name, or "Token".
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# Handle type name.
|
||||||
|
def h_type(name)
|
||||||
|
"#{@grammar.tree_prefix}#{name}#{@grammar.tree_suffix}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Get the list of all tree node handle type names (Token plus rule sets).
|
||||||
|
#
|
||||||
|
# @return [Array<String>]
|
||||||
|
# Handle type names.
|
||||||
|
def tree_handle_types
|
||||||
|
[h_type("Token")] + tree_node_rule_sets.map {|rs| h_type(rs.name)}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Enumerate the navigation fields of a rule set's tree node.
|
||||||
|
#
|
||||||
|
# @yield [rtype, field_name, child_type, slot]
|
||||||
|
# Handle type name, field accessor name, child handle type, and child
|
||||||
|
# slot index.
|
||||||
|
def each_tree_field(rule_set)
|
||||||
|
rtype = h_type(rule_set.name)
|
||||||
|
rule_set.tree_fields.each_with_index do |fields, slot|
|
||||||
|
fields.each do |field_name, child_type|
|
||||||
|
yield rtype, field_name, child_type, slot
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate the C/C++ tree node handle type section for the header.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# Header handle section.
|
||||||
|
def c_tree_types_header
|
||||||
|
@cpp ? cpp_tree_types_header : c_only_tree_types_header
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate the C (non-C++) tree node handle type section for the header.
|
||||||
|
def c_only_tree_types_header
|
||||||
|
p = @grammar.prefix
|
||||||
|
out = []
|
||||||
|
out << "/** Tree node handle types. @{ */"
|
||||||
|
tree_handle_types.each do |t|
|
||||||
|
out << "typedef struct { #{p}context_t * __context; #{p}node_id_t __id; } #{t};"
|
||||||
|
end
|
||||||
|
out << ""
|
||||||
|
out << c_common_accessors_header
|
||||||
|
out << "/** @} */"
|
||||||
|
out.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate the C-style (function + macro) tree node accessors shared by the
|
||||||
|
# C and C++ headers. In C++ these are provided in addition to the handle
|
||||||
|
# methods so that C-style code (and the tree walk macros) also works.
|
||||||
|
def c_common_accessors_header
|
||||||
|
p = @grammar.prefix
|
||||||
|
out = []
|
||||||
|
out << "/** Generic tree node accessors (usable on any handle type). */"
|
||||||
|
out << "#define #{p}node_valid(h) ((h).__id != 0u)"
|
||||||
|
out << "#define #{p}node_id(h) ((h).__id)"
|
||||||
|
out << "#define #{p}node_data(h) (&(h).__context->#{p}tree_nodes[(h).__id])"
|
||||||
|
out << "#define #{p}node_position(h) ((h).__context->#{p}tree_nodes[(h).__id].position)"
|
||||||
|
out << "#define #{p}node_end_position(h) ((h).__context->#{p}tree_nodes[(h).__id].end_position)"
|
||||||
|
out << "#define #{p}node_n_fields(h) ((h).__id ? (h).__context->#{p}tree_nodes[(h).__id].n_fields : (uint16_t)0u)"
|
||||||
|
out << ""
|
||||||
|
out << "/** Tree node field accessor functions. */"
|
||||||
|
out << "#{p}token_t #{p}#{h_type("Token")}_token(#{h_type("Token")} node);"
|
||||||
|
out << "#{p}value_t #{p}#{h_type("Token")}_pvalue(#{h_type("Token")} node);"
|
||||||
|
tree_node_rule_sets.each do |rule_set|
|
||||||
|
each_tree_field(rule_set) do |rtype, field_name, child_type, slot|
|
||||||
|
out << "#{child_type} #{p}#{rtype}_#{field_name}(#{rtype} node);"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
out << ""
|
||||||
|
out << c_tree_walk_macros
|
||||||
|
out.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate the C tree walk macro machinery.
|
||||||
|
def c_tree_walk_macros
|
||||||
|
p = @grammar.prefix
|
||||||
|
max = C_TREE_WALK_MAX
|
||||||
|
out = []
|
||||||
|
out << "/* Tree walk macros: p_tree_walk_<Type>(handle, field, ...). */"
|
||||||
|
out << "#define #{p}CAT_(a, b) a##b"
|
||||||
|
out << "#define #{p}CAT(a, b) #{p}CAT_(a, b)"
|
||||||
|
out << "#define #{p}TA(t, f) #{p}CAT(#{p}CAT(#{p}CAT(#{p}TYPEAFTER_, t), _), f)"
|
||||||
|
out << "#define #{p}ACC(t, f) #{p}CAT(#{p}CAT(#{p}CAT(#{p}, t), _), f)"
|
||||||
|
argn = (1..max).map {|i| "_#{i}"}.join(", ")
|
||||||
|
rseq = (0..max).to_a.reverse.join(", ")
|
||||||
|
out << "#define #{p}ARG_N(#{argn}, N, ...) N"
|
||||||
|
out << "#define #{p}NARG(...) #{p}ARG_N(__VA_ARGS__, #{rseq})"
|
||||||
|
(1..max).each do |n|
|
||||||
|
fparams = (1..n).map {|k| "f#{k}"}.join(", ")
|
||||||
|
call = "h"
|
||||||
|
(1..n).each do |k|
|
||||||
|
texpr = "R"
|
||||||
|
(1...k).each {|j| texpr = "#{p}TA(#{texpr}, f#{j})"}
|
||||||
|
call = "#{p}ACC(#{texpr}, f#{k})(#{call})"
|
||||||
|
end
|
||||||
|
out << "#define #{p}tree_walk_#{n}(R, h, #{fparams}) #{call}"
|
||||||
|
end
|
||||||
|
out << "#define #{p}tree_walk_dispatch(R, h, ...) #{p}CAT(#{p}tree_walk_, #{p}NARG(__VA_ARGS__))(R, h, __VA_ARGS__)"
|
||||||
|
# Type transition map (navigation fields only).
|
||||||
|
tree_node_rule_sets.each do |rule_set|
|
||||||
|
each_tree_field(rule_set) do |rtype, field_name, child_type, slot|
|
||||||
|
out << "#define #{p}TYPEAFTER_#{rtype}_#{field_name} #{child_type}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# Per-handle-type walk entry points.
|
||||||
|
tree_handle_types.each do |t|
|
||||||
|
out << "#define #{p}tree_walk_#{t}(...) #{p}tree_walk_dispatch(#{t}, __VA_ARGS__)"
|
||||||
|
end
|
||||||
|
out.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate the C tree node accessor function definitions for the source.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# Accessor function definitions.
|
||||||
|
def c_tree_accessor_defs
|
||||||
|
p = @grammar.prefix
|
||||||
|
tt = h_type("Token")
|
||||||
|
out = []
|
||||||
|
out << "#{p}token_t #{p}#{tt}_token(#{tt} node)"
|
||||||
|
out << "{"
|
||||||
|
out << " return node.__context->#{p}tree_nodes[node.__id].token;"
|
||||||
|
out << "}"
|
||||||
|
out << ""
|
||||||
|
out << "#{p}value_t #{p}#{tt}_pvalue(#{tt} node)"
|
||||||
|
out << "{"
|
||||||
|
out << " return node.__context->#{p}tree_nodes[node.__id].pvalue;"
|
||||||
|
out << "}"
|
||||||
|
tree_node_rule_sets.each do |rule_set|
|
||||||
|
each_tree_field(rule_set) do |rtype, field_name, child_type, slot|
|
||||||
|
out << ""
|
||||||
|
out << "#{child_type} #{p}#{rtype}_#{field_name}(#{rtype} node)"
|
||||||
|
out << "{"
|
||||||
|
out << " #{child_type} result;"
|
||||||
|
out << " result.__context = node.__context;"
|
||||||
|
out << " if (node.__id == 0u)"
|
||||||
|
out << " {"
|
||||||
|
out << " result.__id = 0u;"
|
||||||
|
out << " return result;"
|
||||||
|
out << " }"
|
||||||
|
out << " result.__id = node.__context->#{p}tree_children[node.__context->#{p}tree_nodes[node.__id].child_offset + #{slot}u];"
|
||||||
|
out << " return result;"
|
||||||
|
out << "}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
out.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Generate the C++ tree node handle type section for the header.
|
||||||
|
def cpp_tree_types_header
|
||||||
|
p = @grammar.prefix
|
||||||
|
out = []
|
||||||
|
out << "/** Tree node handle types. @{ */"
|
||||||
|
tree_handle_types.each {|t| out << "struct #{t};"}
|
||||||
|
out << ""
|
||||||
|
# Token handle (all methods inline; no handle-typed returns).
|
||||||
|
tt = h_type("Token")
|
||||||
|
out << "struct #{tt}"
|
||||||
|
out << "{"
|
||||||
|
out << " #{p}context_t * __context;"
|
||||||
|
out << " #{p}node_id_t __id;"
|
||||||
|
out << " bool valid() const { return __id != 0u; }"
|
||||||
|
out << " #{p}node_data_t * data() const { return &__context->#{p}tree_nodes[__id]; }"
|
||||||
|
out << " #{p}position_t position() const { return __context->#{p}tree_nodes[__id].position; }"
|
||||||
|
out << " #{p}position_t end_position() const { return __context->#{p}tree_nodes[__id].end_position; }"
|
||||||
|
out << " uint16_t n_fields() const { return __id ? __context->#{p}tree_nodes[__id].n_fields : (uint16_t)0u; }"
|
||||||
|
out << " #{p}token_t token() const { return __context->#{p}tree_nodes[__id].token; }"
|
||||||
|
out << " #{p}value_t pvalue() const { return __context->#{p}tree_nodes[__id].pvalue; }"
|
||||||
|
out << "};"
|
||||||
|
out << ""
|
||||||
|
# Rule set handles: navigation methods declared, defined out-of-line below.
|
||||||
|
tree_node_rule_sets.each do |rule_set|
|
||||||
|
rtype = h_type(rule_set.name)
|
||||||
|
out << "struct #{rtype}"
|
||||||
|
out << "{"
|
||||||
|
out << " #{p}context_t * __context;"
|
||||||
|
out << " #{p}node_id_t __id;"
|
||||||
|
out << " bool valid() const { return __id != 0u; }"
|
||||||
|
out << " #{p}node_data_t * data() const { return &__context->#{p}tree_nodes[__id]; }"
|
||||||
|
out << " #{p}position_t position() const { return __context->#{p}tree_nodes[__id].position; }"
|
||||||
|
out << " #{p}position_t end_position() const { return __context->#{p}tree_nodes[__id].end_position; }"
|
||||||
|
out << " uint16_t n_fields() const { return __id ? __context->#{p}tree_nodes[__id].n_fields : (uint16_t)0u; }"
|
||||||
|
each_tree_field(rule_set) do |rt, field_name, child_type, slot|
|
||||||
|
out << " #{child_type} #{field_name}() const;"
|
||||||
|
end
|
||||||
|
out << "};"
|
||||||
|
out << ""
|
||||||
|
end
|
||||||
|
# Out-of-line navigation method bodies (all handle types now complete).
|
||||||
|
tree_node_rule_sets.each do |rule_set|
|
||||||
|
rtype = h_type(rule_set.name)
|
||||||
|
each_tree_field(rule_set) do |rt, field_name, child_type, slot|
|
||||||
|
out << "inline #{child_type} #{rtype}::#{field_name}() const"
|
||||||
|
out << "{"
|
||||||
|
out << " if (__id == 0u)"
|
||||||
|
out << " {"
|
||||||
|
out << " return #{child_type}{__context, 0u};"
|
||||||
|
out << " }"
|
||||||
|
out << " return #{child_type}{__context, __context->#{p}tree_children[__context->#{p}tree_nodes[__id].child_offset + #{slot}u]};"
|
||||||
|
out << "}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
out << ""
|
||||||
|
out << "/*"
|
||||||
|
out << " * C-style function and macro accessors, provided in addition to the handle"
|
||||||
|
out << " * methods above so that C-style code and the tree walk macros also work."
|
||||||
|
out << " */"
|
||||||
|
out << c_common_accessors_header
|
||||||
|
out << "/** @} */"
|
||||||
|
out.join("\n")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Rust keywords that must be escaped as raw identifiers when used as a
|
||||||
|
# generated identifier (e.g. a field alias named `type`).
|
||||||
|
RUST_KEYWORDS = %w[
|
||||||
|
as break const continue dyn else enum extern false fn for if impl in let
|
||||||
|
loop match mod move mut pub ref return static struct trait true type
|
||||||
|
unsafe use where while async await abstract become box do final macro
|
||||||
|
override priv typeof unsized virtual yield try gen
|
||||||
|
]
|
||||||
|
|
||||||
|
# Escape a name as a Rust raw identifier if it is a reserved keyword.
|
||||||
|
#
|
||||||
|
# @param name [String]
|
||||||
|
# Identifier name.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# Name, escaped as a raw identifier if necessary.
|
||||||
|
def rust_ident(name)
|
||||||
|
RUST_KEYWORDS.include?(name) ? "r##{name}" : name
|
||||||
|
end
|
||||||
|
|
||||||
|
# Map a ptype type string to a valid Rust type.
|
||||||
|
#
|
||||||
|
# The default ptype is a C "void *"; for Rust with no declared ptype we use
|
||||||
|
# the unit type instead.
|
||||||
|
#
|
||||||
|
# @param typestring [String]
|
||||||
|
# ptype type string.
|
||||||
|
#
|
||||||
|
# @return [String]
|
||||||
|
# Rust type string.
|
||||||
|
def rust_ptype(typestring)
|
||||||
|
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.
|
# Get the lex function to use.
|
||||||
#
|
#
|
||||||
# @return [String]
|
# @return [String]
|
||||||
@ -459,6 +861,8 @@ class Propane
|
|||||||
"uint8_t"
|
"uint8_t"
|
||||||
when "d"
|
when "d"
|
||||||
"ubyte"
|
"ubyte"
|
||||||
|
when "rust"
|
||||||
|
"u8"
|
||||||
end
|
end
|
||||||
elsif max <= 0xFFFF
|
elsif max <= 0xFFFF
|
||||||
case @language
|
case @language
|
||||||
@ -466,11 +870,15 @@ class Propane
|
|||||||
"uint16_t"
|
"uint16_t"
|
||||||
when "d"
|
when "d"
|
||||||
"ushort"
|
"ushort"
|
||||||
|
when "rust"
|
||||||
|
"u16"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
case @language
|
case @language
|
||||||
when "c"
|
when "c"
|
||||||
"uint32_t"
|
"uint32_t"
|
||||||
|
when "rust"
|
||||||
|
"u32"
|
||||||
else
|
else
|
||||||
"uint"
|
"uint"
|
||||||
end
|
end
|
||||||
|
|||||||
176
spec/json_parser.rust.propane
Normal file
176
spec/json_parser.rust.propane
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
<<header
|
||||||
|
pub const JSON_OBJECT: usize = 0;
|
||||||
|
pub const JSON_ARRAY: usize = 1;
|
||||||
|
pub const JSON_NUMBER: usize = 2;
|
||||||
|
pub const JSON_STRING: usize = 3;
|
||||||
|
pub const JSON_TRUE: usize = 4;
|
||||||
|
pub const JSON_FALSE: usize = 5;
|
||||||
|
pub const JSON_NULL: usize = 6;
|
||||||
|
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub enum JSONValue {
|
||||||
|
#[default]
|
||||||
|
Null,
|
||||||
|
Object(Vec<(String, JSONValue)>),
|
||||||
|
Array(Vec<JSONValue>),
|
||||||
|
Number(f64),
|
||||||
|
StringVal(String),
|
||||||
|
True,
|
||||||
|
False,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl JSONValue {
|
||||||
|
pub fn id(&self) -> usize {
|
||||||
|
match self {
|
||||||
|
JSONValue::Object(_) => JSON_OBJECT,
|
||||||
|
JSONValue::Array(_) => JSON_ARRAY,
|
||||||
|
JSONValue::Number(_) => JSON_NUMBER,
|
||||||
|
JSONValue::StringVal(_) => JSON_STRING,
|
||||||
|
JSONValue::True => JSON_TRUE,
|
||||||
|
JSONValue::False => JSON_FALSE,
|
||||||
|
JSONValue::Null => JSON_NULL,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn number(&self) -> f64 {
|
||||||
|
if let JSONValue::Number(n) = self { *n } else { 0.0 }
|
||||||
|
}
|
||||||
|
pub fn string(&self) -> &str {
|
||||||
|
if let JSONValue::StringVal(s) = self { s.as_str() } else { "" }
|
||||||
|
}
|
||||||
|
pub fn object_len(&self) -> usize {
|
||||||
|
if let JSONValue::Object(e) = self { e.len() } else { 0 }
|
||||||
|
}
|
||||||
|
pub fn array_len(&self) -> usize {
|
||||||
|
if let JSONValue::Array(e) = self { e.len() } else { 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>>
|
||||||
|
|
||||||
|
context_user_fields <<
|
||||||
|
pub string_value: String,
|
||||||
|
>>
|
||||||
|
|
||||||
|
ptype JSONValue;
|
||||||
|
|
||||||
|
drop /\s+/;
|
||||||
|
token lbrace /\{/;
|
||||||
|
token rbrace /\}/;
|
||||||
|
token lbracket /\[/;
|
||||||
|
token rbracket /\]/;
|
||||||
|
token comma /,/;
|
||||||
|
token colon /:/;
|
||||||
|
token number /-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][-+]?[0-9]+)?/ <<
|
||||||
|
let n: f64 = std::str::from_utf8(match_).unwrap().parse().unwrap();
|
||||||
|
$$ = JSONValue::Number(n);
|
||||||
|
>>
|
||||||
|
token true <<
|
||||||
|
$$ = JSONValue::True;
|
||||||
|
>>
|
||||||
|
token false <<
|
||||||
|
$$ = JSONValue::False;
|
||||||
|
>>
|
||||||
|
token null <<
|
||||||
|
$$ = JSONValue::Null;
|
||||||
|
>>
|
||||||
|
/"/ <<
|
||||||
|
$mode(string);
|
||||||
|
${context.string_value} = String::new();
|
||||||
|
>>
|
||||||
|
string: token string /"/ <<
|
||||||
|
$$ = JSONValue::StringVal(std::mem::take(&mut ${context.string_value}));
|
||||||
|
$mode(default);
|
||||||
|
>>
|
||||||
|
string: /\\"/ <<
|
||||||
|
${context.string_value}.push('"');
|
||||||
|
>>
|
||||||
|
string: /\\\\/ <<
|
||||||
|
${context.string_value}.push('\\');
|
||||||
|
>>
|
||||||
|
string: /\\\// <<
|
||||||
|
${context.string_value}.push('/');
|
||||||
|
>>
|
||||||
|
string: /\\b/ <<
|
||||||
|
${context.string_value}.push('\u{0008}');
|
||||||
|
>>
|
||||||
|
string: /\\f/ <<
|
||||||
|
${context.string_value}.push('\u{000C}');
|
||||||
|
>>
|
||||||
|
string: /\\n/ <<
|
||||||
|
${context.string_value}.push('\n');
|
||||||
|
>>
|
||||||
|
string: /\\r/ <<
|
||||||
|
${context.string_value}.push('\r');
|
||||||
|
>>
|
||||||
|
string: /\\t/ <<
|
||||||
|
${context.string_value}.push('\t');
|
||||||
|
>>
|
||||||
|
string: /\\u[0-9a-fA-F]{4}/ <<
|
||||||
|
/* Not actually going to encode the code point for this example... */
|
||||||
|
let s: String = ['{', match_[2] as char, match_[3] as char, match_[4] as char, match_[5] as char, '}'].iter().collect();
|
||||||
|
${context.string_value}.push_str(&s);
|
||||||
|
>>
|
||||||
|
string: /[^\\]/ <<
|
||||||
|
${context.string_value}.push(match_[0] as char);
|
||||||
|
>>
|
||||||
|
Start -> Value <<
|
||||||
|
$$ = $1;
|
||||||
|
>>
|
||||||
|
Value -> string <<
|
||||||
|
$$ = $1;
|
||||||
|
>>
|
||||||
|
Value -> number <<
|
||||||
|
$$ = $1;
|
||||||
|
>>
|
||||||
|
Value -> Object <<
|
||||||
|
$$ = $1;
|
||||||
|
>>
|
||||||
|
Value -> Array <<
|
||||||
|
$$ = $1;
|
||||||
|
>>
|
||||||
|
Value -> true <<
|
||||||
|
$$ = $1;
|
||||||
|
>>
|
||||||
|
Value -> false <<
|
||||||
|
$$ = $1;
|
||||||
|
>>
|
||||||
|
Value -> null <<
|
||||||
|
$$ = $1;
|
||||||
|
>>
|
||||||
|
Object -> lbrace rbrace <<
|
||||||
|
$$ = JSONValue::Object(Vec::new());
|
||||||
|
>>
|
||||||
|
Object -> lbrace KeyValues rbrace <<
|
||||||
|
$$ = $2;
|
||||||
|
>>
|
||||||
|
KeyValues -> KeyValue <<
|
||||||
|
$$ = $1;
|
||||||
|
>>
|
||||||
|
KeyValues -> KeyValues comma KeyValue <<
|
||||||
|
let mut obj = $1;
|
||||||
|
if let JSONValue::Object(kve) = $3 {
|
||||||
|
if let JSONValue::Object(entries) = &mut obj {
|
||||||
|
entries.extend(kve);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$$ = obj;
|
||||||
|
>>
|
||||||
|
KeyValue -> string colon Value <<
|
||||||
|
let name = if let JSONValue::StringVal(s) = $1 { s } else { String::new() };
|
||||||
|
$$ = JSONValue::Object(vec![(name, $3)]);
|
||||||
|
>>
|
||||||
|
Array -> lbracket rbracket <<
|
||||||
|
$$ = JSONValue::Array(Vec::new());
|
||||||
|
>>
|
||||||
|
Array -> lbracket Values rbracket <<
|
||||||
|
$$ = $2;
|
||||||
|
>>
|
||||||
|
Values -> Value <<
|
||||||
|
$$ = $1;
|
||||||
|
>>
|
||||||
|
Values -> Values comma Value <<
|
||||||
|
let mut arr = $1;
|
||||||
|
if let JSONValue::Array(elems) = &mut arr {
|
||||||
|
elems.push($3);
|
||||||
|
}
|
||||||
|
$$ = arr;
|
||||||
|
>>
|
||||||
80
spec/macros.rust.propane
Normal file
80
spec/macros.rust.propane
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<<
|
||||||
|
fn mylexfn(context: &mut p_context_t, out_token_info: &mut p_token_info_t) -> usize {
|
||||||
|
loop {
|
||||||
|
if context.expanding {
|
||||||
|
let ei = context.expand_i;
|
||||||
|
context.expand_i += 1;
|
||||||
|
if context.expand_i >= context.token_infos.len() {
|
||||||
|
context.expanding = false;
|
||||||
|
}
|
||||||
|
*out_token_info = context.token_infos[ei].clone();
|
||||||
|
return P_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lex_result = p_lex(context, out_token_info);
|
||||||
|
if lex_result != P_SUCCESS {
|
||||||
|
return lex_result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if out_token_info.token == TOKEN_macro {
|
||||||
|
context.defining = true;
|
||||||
|
} else if out_token_info.token == TOKEN_macroname {
|
||||||
|
if !context.defining {
|
||||||
|
context.expanding = true;
|
||||||
|
context.expand_i = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if out_token_info.token == TOKEN_lbrace {
|
||||||
|
if context.defining {
|
||||||
|
/* Capture the macro body tokens (up to the closing '}'). */
|
||||||
|
let mut infos: Vec<p_token_info_t> = Vec::new();
|
||||||
|
loop {
|
||||||
|
let mut ti = p_token_info_t::default();
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(context, &mut ti));
|
||||||
|
if ti.token == TOKEN_rbrace {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
infos.push(ti);
|
||||||
|
}
|
||||||
|
context.token_infos = infos;
|
||||||
|
context.defining = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
context.defining = false;
|
||||||
|
}
|
||||||
|
return lex_result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>>
|
||||||
|
|
||||||
|
context_user_fields <<
|
||||||
|
pub defining: bool,
|
||||||
|
pub expanding: bool,
|
||||||
|
pub expand_i: usize,
|
||||||
|
pub token_infos: Vec<p_token_info_t>,
|
||||||
|
pub nums: Vec<i64>,
|
||||||
|
>>
|
||||||
|
|
||||||
|
ptype i64;
|
||||||
|
|
||||||
|
lex_fn mylexfn;
|
||||||
|
|
||||||
|
drop /\s+/;
|
||||||
|
token lbrace /\{/;
|
||||||
|
token rbrace /\}/;
|
||||||
|
token plus /\+/;
|
||||||
|
token macro;
|
||||||
|
token macroname /@[a-zA-Z_]\w*/;
|
||||||
|
token num /\d+/ <<
|
||||||
|
let mut v: i64 = 0;
|
||||||
|
for c in match_ { v = v * 10 + (*c - b'0') as i64; }
|
||||||
|
$$ = v;
|
||||||
|
>>
|
||||||
|
|
||||||
|
Start -> Statements;
|
||||||
|
Statements -> ;
|
||||||
|
Statements -> Statement Statements;
|
||||||
|
Statement -> Add;
|
||||||
|
Statement -> MacroStart;
|
||||||
|
Add -> num plus num << $$ = $1 + $3; ${context.nums}.push($$); >>
|
||||||
|
MacroStart -> macro macroname lbrace;
|
||||||
41
spec/parse_inner_nested.rust.propane
Normal file
41
spec/parse_inner_nested.rust.propane
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<<
|
||||||
|
fn mylexfn(context: &mut p_context_t, out_token_info: &mut p_token_info_t) -> usize {
|
||||||
|
let result = p_lex(context, out_token_info);
|
||||||
|
if result != P_SUCCESS {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if out_token_info.token == TOKEN_lparen {
|
||||||
|
/* Reentrant nested parse of the parenthesized sub-expression. */
|
||||||
|
let inner_result = p_parse_inner_Start(context, &[TOKEN_rparen]);
|
||||||
|
if inner_result != P_SUCCESS {
|
||||||
|
return inner_result;
|
||||||
|
}
|
||||||
|
let value = p_result_Start(context);
|
||||||
|
/* p_parse_inner rewound the input so ')' was not consumed; consume it. */
|
||||||
|
let mut rparen_info = p_token_info_t::default();
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(context, &mut rparen_info));
|
||||||
|
assert_eq!(TOKEN_rparen, rparen_info.token);
|
||||||
|
out_token_info.token = TOKEN_num;
|
||||||
|
out_token_info.pvalue = p_value(value);
|
||||||
|
}
|
||||||
|
P_SUCCESS
|
||||||
|
}
|
||||||
|
>>
|
||||||
|
|
||||||
|
ptype i64;
|
||||||
|
|
||||||
|
lex_fn mylexfn;
|
||||||
|
|
||||||
|
drop /\s+/;
|
||||||
|
token lparen /\(/;
|
||||||
|
token rparen /\)/;
|
||||||
|
token plus /\+/;
|
||||||
|
token num /\d+/ <<
|
||||||
|
let mut v: i64 = 0;
|
||||||
|
for c in match_ { v = v * 10 + (*c - b'0') as i64; }
|
||||||
|
$$ = v;
|
||||||
|
>>
|
||||||
|
|
||||||
|
Start -> Expr << $$ = $1; >>
|
||||||
|
Expr -> num << $$ = $1; >>
|
||||||
|
Expr -> Expr plus num << $$ = $1 + $3; >>
|
||||||
44
spec/parse_inner_nested_tree.rust.propane
Normal file
44
spec/parse_inner_nested_tree.rust.propane
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<<
|
||||||
|
fn mylexfn(context: &mut p_context_t, out_token_info: &mut p_token_info_t) -> usize {
|
||||||
|
let result = p_lex(context, out_token_info);
|
||||||
|
if result != P_SUCCESS {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if out_token_info.token == TOKEN_lparen {
|
||||||
|
let start_position = out_token_info.position;
|
||||||
|
let inner_result = p_parse_inner_Start(context, &[TOKEN_rparen]);
|
||||||
|
if inner_result != P_SUCCESS {
|
||||||
|
return inner_result;
|
||||||
|
}
|
||||||
|
/* Read the inner subtree's span before re-borrowing context to lex. */
|
||||||
|
let inner = p_result_Start(context);
|
||||||
|
assert!(inner.valid());
|
||||||
|
let inner_start_col = inner.position().col;
|
||||||
|
let inner_end_col = inner.end_position().col;
|
||||||
|
let mut rparen_info = p_token_info_t::default();
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(context, &mut rparen_info));
|
||||||
|
assert_eq!(TOKEN_rparen, rparen_info.token);
|
||||||
|
assert_eq!(start_position.col + 1, inner_start_col);
|
||||||
|
assert_eq!(rparen_info.position.col - 1, inner_end_col);
|
||||||
|
/* Synthesize a num token spanning the whole "( ... )" group. */
|
||||||
|
out_token_info.token = TOKEN_num;
|
||||||
|
out_token_info.position = start_position;
|
||||||
|
out_token_info.end_position = rparen_info.end_position;
|
||||||
|
}
|
||||||
|
P_SUCCESS
|
||||||
|
}
|
||||||
|
>>
|
||||||
|
|
||||||
|
tree;
|
||||||
|
|
||||||
|
lex_fn mylexfn;
|
||||||
|
|
||||||
|
drop /\s+/;
|
||||||
|
token lparen /\(/;
|
||||||
|
token rparen /\)/;
|
||||||
|
token plus /\+/;
|
||||||
|
token num /\d+/;
|
||||||
|
|
||||||
|
Start -> Expr;
|
||||||
|
Expr -> num;
|
||||||
|
Expr -> Expr plus num;
|
||||||
@ -53,7 +53,7 @@ EOF
|
|||||||
if options[:args]
|
if options[:args]
|
||||||
command += options[:args]
|
command += options[:args]
|
||||||
else
|
else
|
||||||
command += %W[spec/run/testparser#{options[:name]}.propane spec/run/testparser#{options[:name]}.#{options[:language]} --log spec/run/testparser#{options[:name]}.log]
|
command += %W[spec/run/testparser#{options[:name]}.propane spec/run/testparser#{options[:name]}.#{lang_ext(options[:language])} --log spec/run/testparser#{options[:name]}.log]
|
||||||
end
|
end
|
||||||
command += (options[:extra_args] || [])
|
command += (options[:extra_args] || [])
|
||||||
if (options[:capture])
|
if (options[:capture])
|
||||||
@ -65,9 +65,16 @@ EOF
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Map a spec language name to the generated parser source file extension.
|
||||||
|
def lang_ext(language)
|
||||||
|
language == "rust" ? "rs" : language
|
||||||
|
end
|
||||||
|
|
||||||
def compile(test_files, options = {})
|
def compile(test_files, options = {})
|
||||||
test_files = Array(test_files).map do |test_file|
|
test_files = Array(test_files).map do |test_file|
|
||||||
if !File.exist?(test_file) && test_file.end_with?(".cpp")
|
if test_file.end_with?(".rust")
|
||||||
|
test_file.sub(%r{\.rust$}, ".rs")
|
||||||
|
elsif !File.exist?(test_file) && test_file.end_with?(".cpp")
|
||||||
test_file.sub(%r{\.cpp$}, ".c")
|
test_file.sub(%r{\.cpp$}, ".c")
|
||||||
else
|
else
|
||||||
test_file
|
test_file
|
||||||
@ -75,7 +82,7 @@ EOF
|
|||||||
end
|
end
|
||||||
options[:parsers] ||= [""]
|
options[:parsers] ||= [""]
|
||||||
parsers = options[:parsers].map do |name|
|
parsers = options[:parsers].map do |name|
|
||||||
"spec/run/testparser#{name}.#{options[:language]}"
|
"spec/run/testparser#{name}.#{lang_ext(options[:language])}"
|
||||||
end
|
end
|
||||||
case options[:language]
|
case options[:language]
|
||||||
when "c"
|
when "c"
|
||||||
@ -84,6 +91,21 @@ EOF
|
|||||||
command = [*%w[g++ -g -x c++ -Wall -o spec/run/testparser -Ispec -Ispec/run], *parsers, *test_files, "spec/testutils.c", "-lm"]
|
command = [*%w[g++ -g -x c++ -Wall -o spec/run/testparser -Ispec -Ispec/run], *parsers, *test_files, "spec/testutils.c", "-lm"]
|
||||||
when "d"
|
when "d"
|
||||||
command = [*%w[ldc2 -g --unittest -of spec/run/testparser -Ispec], *parsers, *test_files, "spec/testutils.d"]
|
command = [*%w[ldc2 -g --unittest -of spec/run/testparser -Ispec], *parsers, *test_files, "spec/testutils.d"]
|
||||||
|
when "rust"
|
||||||
|
# Compile each generated parser to an rlib, then compile the test crate
|
||||||
|
# against them. Safe Rust needs no valgrind, but it is run anyway.
|
||||||
|
externs = []
|
||||||
|
options[:parsers].each do |name|
|
||||||
|
crate = "testparser#{name}"
|
||||||
|
rlib = "spec/run/lib#{crate}.rlib"
|
||||||
|
rustc = [*%w[rustc --edition 2021 --crate-type=rlib -A warnings],
|
||||||
|
"--crate-name", crate, "-o", rlib, "spec/run/testparser#{name}.rs"]
|
||||||
|
expect(system(*rustc)).to be_truthy
|
||||||
|
externs += ["--extern", "#{crate}=#{rlib}"]
|
||||||
|
end
|
||||||
|
# rustc takes a single crate root; any additional Rust test files are
|
||||||
|
# expected to be pulled in as modules or provided by the parser crate.
|
||||||
|
command = [*%w[rustc --edition 2021 -A warnings -o spec/run/testparser], *externs, test_files.first]
|
||||||
end
|
end
|
||||||
result = system(*command)
|
result = system(*command)
|
||||||
expect(result).to be_truthy
|
expect(result).to be_truthy
|
||||||
@ -285,7 +307,7 @@ EOF
|
|||||||
expect(results.status).to_not eq 0
|
expect(results.status).to_not eq 0
|
||||||
end
|
end
|
||||||
|
|
||||||
%w[d c cpp].each do |language|
|
%w[d c cpp rust].each do |language|
|
||||||
|
|
||||||
context "#{language.upcase} language" do
|
context "#{language.upcase} language" do
|
||||||
|
|
||||||
@ -335,6 +357,16 @@ token int /\\d+/ <<
|
|||||||
$$ = v;
|
$$ = v;
|
||||||
>>
|
>>
|
||||||
Start -> int << $$ = $1; >>
|
Start -> int << $$ = $1; >>
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
ptype i64;
|
||||||
|
token int /\\d+/ <<
|
||||||
|
let mut v: i64 = 0;
|
||||||
|
for c in match_ { v = v * 10 + (*c - b'0') as i64; }
|
||||||
|
$$ = v;
|
||||||
|
>>
|
||||||
|
Start -> int << $$ = $1; >>
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -429,6 +461,31 @@ E3 -> E4 << $$ = $1; >>
|
|||||||
E3 -> E3 power E4 << $$ = pow($1, $3); >>
|
E3 -> E3 power E4 << $$ = pow($1, $3); >>
|
||||||
E4 -> integer << $$ = $1; >>
|
E4 -> integer << $$ = $1; >>
|
||||||
E4 -> lparen E1 rparen << $$ = $2; >>
|
E4 -> lparen E1 rparen << $$ = $2; >>
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
ptype u64;
|
||||||
|
token plus /\\+/;
|
||||||
|
token times /\\*/;
|
||||||
|
token power /\\*\\*/;
|
||||||
|
token integer /\\d+/ <<
|
||||||
|
let mut v: u64 = 0;
|
||||||
|
for c in match_ { v = v * 10 + (*c - b'0') as u64; }
|
||||||
|
$$ = v;
|
||||||
|
>>
|
||||||
|
token lparen /\\(/;
|
||||||
|
token rparen /\\)/;
|
||||||
|
drop /\\s+/;
|
||||||
|
|
||||||
|
Start -> E1 << $$ = $1; >>
|
||||||
|
E1 -> E2 << $$ = $1; >>
|
||||||
|
E1 -> E1 plus E2 << $$ = $1 + $3; >>
|
||||||
|
E2 -> E3 << $$ = $1; >>
|
||||||
|
E2 -> E2 times E3 << $$ = $1 * $3; >>
|
||||||
|
E3 -> E4 << $$ = $1; >>
|
||||||
|
E3 -> E3 power E4 << $$ = $1.pow($3 as u32); >>
|
||||||
|
E4 -> integer << $$ = $1; >>
|
||||||
|
E4 -> lparen E1 rparen << $$ = $2; >>
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -505,6 +562,16 @@ token def;
|
|||||||
Start -> Abcs def;
|
Start -> Abcs def;
|
||||||
Abcs -> ;
|
Abcs -> ;
|
||||||
Abcs -> abc Abcs;
|
Abcs -> abc Abcs;
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
token abc <<
|
||||||
|
println!("abc!");
|
||||||
|
>>
|
||||||
|
token def;
|
||||||
|
Start -> Abcs def;
|
||||||
|
Abcs -> ;
|
||||||
|
Abcs -> abc Abcs;
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -541,6 +608,12 @@ import std.stdio;
|
|||||||
token abc;
|
token abc;
|
||||||
/def/ << writeln("def!"); >>
|
/def/ << writeln("def!"); >>
|
||||||
Start -> abc;
|
Start -> abc;
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
token abc;
|
||||||
|
/def/ << println!("def!"); >>
|
||||||
|
Start -> abc;
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -583,6 +656,16 @@ token abc;
|
|||||||
return $token(abc);
|
return $token(abc);
|
||||||
>>
|
>>
|
||||||
Start -> abc;
|
Start -> abc;
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
token abc;
|
||||||
|
/def/ << println!("def!"); >>
|
||||||
|
/ghi/ <<
|
||||||
|
println!("ghi!");
|
||||||
|
return $token(abc);
|
||||||
|
>>
|
||||||
|
Start -> abc;
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -641,6 +724,25 @@ string: /"/ <<
|
|||||||
return $token(string);
|
return $token(string);
|
||||||
>>
|
>>
|
||||||
Start -> abc string def;
|
Start -> abc string def;
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
token abc;
|
||||||
|
token def;
|
||||||
|
tokenid string;
|
||||||
|
drop /\\s+/;
|
||||||
|
/"/ <<
|
||||||
|
println!("begin string mode");
|
||||||
|
$mode(string);
|
||||||
|
>>
|
||||||
|
string: /[^"]+/ <<
|
||||||
|
println!("captured string");
|
||||||
|
>>
|
||||||
|
string: /"/ <<
|
||||||
|
$mode(default);
|
||||||
|
return $token(string);
|
||||||
|
>>
|
||||||
|
Start -> abc string def;
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -699,6 +801,24 @@ default, identonly: drop /\\s+/;
|
|||||||
Start -> abc dot ident <<
|
Start -> abc dot ident <<
|
||||||
writeln("ident: ", $3);
|
writeln("ident: ", $3);
|
||||||
>>
|
>>
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
ptype u8;
|
||||||
|
token abc;
|
||||||
|
token def;
|
||||||
|
default, identonly: token ident /[a-z]+/ <<
|
||||||
|
$$ = match_[0];
|
||||||
|
$mode(default);
|
||||||
|
return $token(ident);
|
||||||
|
>>
|
||||||
|
token dot /\\./ <<
|
||||||
|
$mode(identonly);
|
||||||
|
>>
|
||||||
|
default, identonly: drop /\\s+/;
|
||||||
|
Start -> abc dot ident <<
|
||||||
|
println!("ident: {}", $3 as char);
|
||||||
|
>>
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -736,6 +856,14 @@ token b;
|
|||||||
Start -> A B << writeln("Start!"); >>
|
Start -> A B << writeln("Start!"); >>
|
||||||
A -> a << writeln("A!"); >>
|
A -> a << writeln("A!"); >>
|
||||||
B -> b << writeln("B!"); >>
|
B -> b << writeln("B!"); >>
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
token a;
|
||||||
|
token b;
|
||||||
|
Start -> A B << println!("Start!"); >>
|
||||||
|
A -> a << println!("A!"); >>
|
||||||
|
B -> b << println!("B!"); >>
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -751,7 +879,7 @@ EOF
|
|||||||
|
|
||||||
it "executes user code associated with a parser rule in tree mode" do
|
it "executes user code associated with a parser rule in tree mode" do
|
||||||
case language
|
case language
|
||||||
when "c", "cpp"
|
when "c"
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
tree;
|
tree;
|
||||||
context_user_fields <<
|
context_user_fields <<
|
||||||
@ -769,18 +897,49 @@ ptype int;
|
|||||||
token a << $$ = 11; >>
|
token a << $$ = 11; >>
|
||||||
token b << $$ = 22; >>
|
token b << $$ = 22; >>
|
||||||
Start -> A:ay B:bee C <<
|
Start -> A:ay B:bee C <<
|
||||||
${context.start_n_fields} = $$->n_fields;
|
${context.start_n_fields} = p_node_n_fields($$);
|
||||||
${context.start_a_value} = $$->pA->pToken1->pvalue;
|
${context.start_a_value} = p_tree_walk_Start($$, pA, pToken1, pvalue);
|
||||||
${context.a_value} = $1->pToken1->pvalue;
|
${context.a_value} = p_tree_walk_A($1, pToken1, pvalue);
|
||||||
${context.b_value} = $2->pToken1->pvalue;
|
${context.b_value} = p_tree_walk_B($2, pToken1, pvalue);
|
||||||
${context.b_token} = $2->pToken1->token;
|
${context.b_token} = p_tree_walk_B($2, pToken1, token);
|
||||||
${context.c_field_is_null} = ($$->pC == NULL) ? 1 : 0;
|
${context.c_field_is_null} = p_node_valid(p_Start_pC($$)) ? 0 : 1;
|
||||||
${context.alias_a_value} = ${ay}->pToken1->pvalue;
|
${context.alias_a_value} = p_tree_walk_A(${ay}, pToken1, pvalue);
|
||||||
${context.alias_b_value} = ${bee}->pToken1->pvalue;
|
${context.alias_b_value} = p_tree_walk_B(${bee}, pToken1, pvalue);
|
||||||
>>
|
>>
|
||||||
A -> a;
|
A -> a;
|
||||||
B -> b;
|
B -> b;
|
||||||
C -> << ${context.c_is_null} = ($$ == NULL) ? 1 : 0; >>
|
C -> << ${context.c_is_null} = p_node_valid($$) ? 0 : 1; >>
|
||||||
|
EOF
|
||||||
|
when "cpp"
|
||||||
|
write_grammar <<EOF
|
||||||
|
tree;
|
||||||
|
context_user_fields <<
|
||||||
|
int start_n_fields;
|
||||||
|
int start_a_value;
|
||||||
|
int a_value;
|
||||||
|
int b_value;
|
||||||
|
p_token_t b_token;
|
||||||
|
int c_is_null;
|
||||||
|
int c_field_is_null;
|
||||||
|
int alias_a_value;
|
||||||
|
int alias_b_value;
|
||||||
|
>>
|
||||||
|
ptype int;
|
||||||
|
token a << $$ = 11; >>
|
||||||
|
token b << $$ = 22; >>
|
||||||
|
Start -> A:ay B:bee C <<
|
||||||
|
${context.start_n_fields} = $$.n_fields();
|
||||||
|
${context.start_a_value} = $$.pA().pToken1().pvalue();
|
||||||
|
${context.a_value} = $1.pToken1().pvalue();
|
||||||
|
${context.b_value} = $2.pToken1().pvalue();
|
||||||
|
${context.b_token} = $2.pToken1().token();
|
||||||
|
${context.c_field_is_null} = $$.pC().valid() ? 0 : 1;
|
||||||
|
${context.alias_a_value} = ${ay}.pToken1().pvalue();
|
||||||
|
${context.alias_b_value} = ${bee}.pToken1().pvalue();
|
||||||
|
>>
|
||||||
|
A -> a;
|
||||||
|
B -> b;
|
||||||
|
C -> << ${context.c_is_null} = $$.valid() ? 0 : 1; >>
|
||||||
EOF
|
EOF
|
||||||
when "d"
|
when "d"
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
@ -805,13 +964,44 @@ Start -> A:ay B:bee C <<
|
|||||||
${context.a_value} = $1.pToken1.pvalue;
|
${context.a_value} = $1.pToken1.pvalue;
|
||||||
${context.b_value} = $2.pToken1.pvalue;
|
${context.b_value} = $2.pToken1.pvalue;
|
||||||
${context.b_token} = $2.pToken1.token;
|
${context.b_token} = $2.pToken1.token;
|
||||||
${context.c_field_is_null} = ($$.pC is null) ? 1 : 0;
|
${context.c_field_is_null} = ($$.pC.valid) ? 0 : 1;
|
||||||
${context.alias_a_value} = ${ay}.pToken1.pvalue;
|
${context.alias_a_value} = ${ay}.pToken1.pvalue;
|
||||||
${context.alias_b_value} = ${bee}.pToken1.pvalue;
|
${context.alias_b_value} = ${bee}.pToken1.pvalue;
|
||||||
>>
|
>>
|
||||||
A -> a;
|
A -> a;
|
||||||
B -> b;
|
B -> b;
|
||||||
C -> << ${context.c_is_null} = ($$ is null) ? 1 : 0; >>
|
C -> << ${context.c_is_null} = ($$.valid) ? 0 : 1; >>
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
tree;
|
||||||
|
context_user_fields <<
|
||||||
|
pub start_n_fields: i64,
|
||||||
|
pub start_a_value: i64,
|
||||||
|
pub a_value: i64,
|
||||||
|
pub b_value: i64,
|
||||||
|
pub b_token: p_token_t,
|
||||||
|
pub c_is_null: i64,
|
||||||
|
pub c_field_is_null: i64,
|
||||||
|
pub alias_a_value: i64,
|
||||||
|
pub alias_b_value: i64,
|
||||||
|
>>
|
||||||
|
ptype i64;
|
||||||
|
token a << $$ = 11; >>
|
||||||
|
token b << $$ = 22; >>
|
||||||
|
Start -> A:ay B:bee C <<
|
||||||
|
${context.start_n_fields} = $$.n_fields() as i64;
|
||||||
|
${context.start_a_value} = $$.pA().pToken1().pvalue();
|
||||||
|
${context.a_value} = $1.pToken1().pvalue();
|
||||||
|
${context.b_value} = $2.pToken1().pvalue();
|
||||||
|
${context.b_token} = $2.pToken1().token();
|
||||||
|
${context.c_field_is_null} = if $$.pC().valid() { 0 } else { 1 };
|
||||||
|
${context.alias_a_value} = ${ay}.pToken1().pvalue();
|
||||||
|
${context.alias_b_value} = ${bee}.pToken1().pvalue();
|
||||||
|
>>
|
||||||
|
A -> a;
|
||||||
|
B -> b;
|
||||||
|
C -> << ${context.c_is_null} = if $$.valid() { 0 } else { 1 }; >>
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -822,12 +1012,15 @@ EOF
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "parses lists" do
|
it "parses lists" do
|
||||||
|
ptype = language == "d" ? "uint" : (language == "rust" ? "u32" : "uint32_t")
|
||||||
|
zero = language == "rust" ? "0" : "0u"
|
||||||
|
one = language == "rust" ? "1" : "1u"
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
ptype #{language == "d" ? "uint" : "uint32_t"};
|
ptype #{ptype};
|
||||||
token a;
|
token a;
|
||||||
Start -> As << $$ = $1; >>
|
Start -> As << $$ = $1; >>
|
||||||
As -> << $$ = 0u; >>
|
As -> << $$ = #{zero}; >>
|
||||||
As -> As a << $$ = $1 + 1u; >>
|
As -> As a << $$ = $1 + #{one}; >>
|
||||||
EOF
|
EOF
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
compile("spec/test_parsing_lists.#{language}", language: language)
|
compile("spec/test_parsing_lists.#{language}", language: language)
|
||||||
@ -881,6 +1074,13 @@ token id /[a-zA-Z_][a-zA-Z0-9_]*/ <<
|
|||||||
writeln("Matched token is ", match);
|
writeln("Matched token is ", match);
|
||||||
>>
|
>>
|
||||||
Start -> id;
|
Start -> id;
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
token id /[a-zA-Z_][a-zA-Z0-9_]*/ <<
|
||||||
|
println!("Matched token is {}", std::str::from_utf8(match_).unwrap());
|
||||||
|
>>
|
||||||
|
Start -> id;
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -914,6 +1114,16 @@ token word /[a-z]+/ <<
|
|||||||
Start -> word <<
|
Start -> word <<
|
||||||
$$ = $1;
|
$$ = $1;
|
||||||
>>
|
>>
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
ptype u64;
|
||||||
|
token word /[a-z]+/ <<
|
||||||
|
$$ = match_length as u64;
|
||||||
|
>>
|
||||||
|
Start -> word <<
|
||||||
|
$$ = $1;
|
||||||
|
>>
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -1066,6 +1276,25 @@ Start -> Words;
|
|||||||
Words -> ;
|
Words -> ;
|
||||||
Words -> word Words;
|
Words -> word Words;
|
||||||
Words -> stop Words;
|
Words -> stop Words;
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
context_user_fields <<
|
||||||
|
pub last_start: p_position_t,
|
||||||
|
pub last_end: p_position_t,
|
||||||
|
>>
|
||||||
|
drop /\\s+/;
|
||||||
|
token word /[a-z]+/ <<
|
||||||
|
${context.last_start} = ${position};
|
||||||
|
${context.last_end} = ${end_position};
|
||||||
|
>>
|
||||||
|
token stop /!/ <<
|
||||||
|
$terminate(42);
|
||||||
|
>>
|
||||||
|
Start -> Words;
|
||||||
|
Words -> ;
|
||||||
|
Words -> word Words;
|
||||||
|
Words -> stop Words;
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -1128,6 +1357,21 @@ tokenid t;
|
|||||||
return $token(t);
|
return $token(t);
|
||||||
>>
|
>>
|
||||||
Start -> t;
|
Start -> t;
|
||||||
|
EOF
|
||||||
|
when "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
tokenid t;
|
||||||
|
/\\a/ << println!("A"); >>
|
||||||
|
/\\b/ << println!("B"); >>
|
||||||
|
/\\t/ << println!("T"); >>
|
||||||
|
/\\n/ << println!("N"); >>
|
||||||
|
/\\v/ << println!("V"); >>
|
||||||
|
/\\f/ << println!("F"); >>
|
||||||
|
/\\r/ << println!("R"); >>
|
||||||
|
/t/ <<
|
||||||
|
return $token(t);
|
||||||
|
>>
|
||||||
|
Start -> t;
|
||||||
EOF
|
EOF
|
||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -1164,7 +1408,7 @@ EOF
|
|||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
tree;
|
tree;
|
||||||
|
|
||||||
ptype int;
|
ptype #{language == "rust" ? "i64" : "int"};
|
||||||
|
|
||||||
token a << $$ = 11; >>
|
token a << $$ = 11; >>
|
||||||
token b << $$ = 22; >>
|
token b << $$ = 22; >>
|
||||||
@ -1208,7 +1452,7 @@ tree;
|
|||||||
tree_prefix P ;
|
tree_prefix P ;
|
||||||
tree_suffix S;
|
tree_suffix S;
|
||||||
|
|
||||||
ptype int;
|
ptype #{language == "rust" ? "i64" : "int"};
|
||||||
|
|
||||||
token a << $$ = 11; >>
|
token a << $$ = 11; >>
|
||||||
token b << $$ = 22; >>
|
token b << $$ = 22; >>
|
||||||
@ -1289,6 +1533,24 @@ Start -> a? b R? <<
|
|||||||
>>
|
>>
|
||||||
R -> c d << $$ = "cd"; >>
|
R -> c d << $$ = "cd"; >>
|
||||||
R (string) -> d c << $$ = "dc"; >>
|
R (string) -> d c << $$ = "dc"; >>
|
||||||
|
EOF
|
||||||
|
elsif language == "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
ptype i64;
|
||||||
|
ptype float = f64;
|
||||||
|
ptype string = String;
|
||||||
|
|
||||||
|
token a (float) << $$ = 1.5; >>
|
||||||
|
token b << $$ = 2; >>
|
||||||
|
token c << $$ = 3; >>
|
||||||
|
token d << $$ = 4; >>
|
||||||
|
Start -> a? b R? <<
|
||||||
|
println!("a: {}", $1);
|
||||||
|
println!("b: {}", $2);
|
||||||
|
println!("R: {}", $3);
|
||||||
|
>>
|
||||||
|
R -> c d << $$ = "cd".to_string(); >>
|
||||||
|
R (string) -> d c << $$ = "dc".to_string(); >>
|
||||||
EOF
|
EOF
|
||||||
else
|
else
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
@ -1319,7 +1581,7 @@ EOF
|
|||||||
expect(results.stderr).to eq ""
|
expect(results.stderr).to eq ""
|
||||||
expect(results.status).to eq 0
|
expect(results.status).to eq 0
|
||||||
verify_lines(results.stdout, [
|
verify_lines(results.stdout, [
|
||||||
"a: 0#{language == "d" ? "" : ".0"}",
|
"a: 0#{["d", "rust"].include?(language) ? "" : ".0"}",
|
||||||
"b: 2",
|
"b: 2",
|
||||||
"R: ",
|
"R: ",
|
||||||
"a: 1.5",
|
"a: 1.5",
|
||||||
@ -1356,6 +1618,18 @@ tree;
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
>>
|
>>
|
||||||
|
|
||||||
|
token a;
|
||||||
|
token b;
|
||||||
|
token c;
|
||||||
|
token d;
|
||||||
|
Start -> a? b R?;
|
||||||
|
R -> c d;
|
||||||
|
R -> d c;
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
if language == "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
tree;
|
||||||
token a;
|
token a;
|
||||||
token b;
|
token b;
|
||||||
token c;
|
token c;
|
||||||
@ -1367,6 +1641,7 @@ EOF
|
|||||||
end
|
end
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
compile("spec/test_optional_rule_component_tree.#{language}", language: language)
|
compile("spec/test_optional_rule_component_tree.#{language}", language: language)
|
||||||
|
# (rust grammar written above overrides the C/D form for the Rust run)
|
||||||
results = run_test(language: language)
|
results = run_test(language: language)
|
||||||
expect(results.stderr).to eq ""
|
expect(results.stderr).to eq ""
|
||||||
expect(results.status).to eq 0
|
expect(results.status).to eq 0
|
||||||
@ -1397,6 +1672,18 @@ tree;
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
>>
|
>>
|
||||||
|
|
||||||
|
token a;
|
||||||
|
token b;
|
||||||
|
token c;
|
||||||
|
token d;
|
||||||
|
Start -> a?:a b R?:r;
|
||||||
|
R -> c d;
|
||||||
|
R -> d c;
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
if language == "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
tree;
|
||||||
token a;
|
token a;
|
||||||
token b;
|
token b;
|
||||||
token c;
|
token c;
|
||||||
@ -1497,7 +1784,19 @@ EOF
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "allows specifying field aliases when tree mode is not enabled" do
|
it "allows specifying field aliases when tree mode is not enabled" do
|
||||||
if language == "d"
|
if language == "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
ptype String;
|
||||||
|
token id /[a-zA-Z_][a-zA-Z0-9_]*/ <<
|
||||||
|
$$ = std::str::from_utf8(match_).unwrap().to_string();
|
||||||
|
>>
|
||||||
|
drop /\\s+/;
|
||||||
|
Start -> id:first id:second <<
|
||||||
|
println!("first is {}", ${first});
|
||||||
|
println!("second is {}", ${second});
|
||||||
|
>>
|
||||||
|
EOF
|
||||||
|
elsif language == "d"
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
<<
|
<<
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
@ -1543,7 +1842,22 @@ EOF
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "aliases the correct field when multiple rules are in a rule set when tree mode is not enabled" do
|
it "aliases the correct field when multiple rules are in a rule set when tree mode is not enabled" do
|
||||||
if language == "d"
|
if language == "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
ptype String;
|
||||||
|
token id /[a-zA-Z_][a-zA-Z0-9_]*/ <<
|
||||||
|
$$ = std::str::from_utf8(match_).unwrap().to_string();
|
||||||
|
>>
|
||||||
|
drop /\\s+/;
|
||||||
|
Start -> id;
|
||||||
|
Start -> Foo;
|
||||||
|
Start -> id:first id:second <<
|
||||||
|
println!("first is {}", ${first});
|
||||||
|
println!("second is {}", ${second});
|
||||||
|
>>
|
||||||
|
Foo -> ;
|
||||||
|
EOF
|
||||||
|
elsif language == "d"
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
<<
|
<<
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
@ -1609,7 +1923,7 @@ EOF
|
|||||||
|
|
||||||
it "allows multiple starting rules" do
|
it "allows multiple starting rules" do
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
ptype int;
|
ptype #{language == "rust" ? "i64" : "int"};
|
||||||
token a << $$ = 1; >>
|
token a << $$ = 1; >>
|
||||||
token b << $$ = 2; >>
|
token b << $$ = 2; >>
|
||||||
token c << $$ = 3; >>
|
token c << $$ = 3; >>
|
||||||
@ -1629,7 +1943,7 @@ EOF
|
|||||||
|
|
||||||
it "supports parse_inner APIs that treat provided tokens as follow tokens" do
|
it "supports parse_inner APIs that treat provided tokens as follow tokens" do
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
ptype int;
|
ptype #{language == "rust" ? "i64" : "int"};
|
||||||
token a << $$ = 1; >>
|
token a << $$ = 1; >>
|
||||||
token b << $$ = 2; >>
|
token b << $$ = 2; >>
|
||||||
Start -> Y << $$ = $1; >>
|
Start -> Y << $$ = $1; >>
|
||||||
@ -1644,7 +1958,7 @@ EOF
|
|||||||
|
|
||||||
it "parse_inner APIs block success when the outer rule is unfinished" do
|
it "parse_inner APIs block success when the outer rule is unfinished" do
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
ptype int;
|
ptype #{language == "rust" ? "i64" : "int"};
|
||||||
token a << $$ = 1; >>
|
token a << $$ = 1; >>
|
||||||
token b << $$ = 2; >>
|
token b << $$ = 2; >>
|
||||||
token c << $$ = 3; >>
|
token c << $$ = 3; >>
|
||||||
@ -1660,7 +1974,7 @@ EOF
|
|||||||
|
|
||||||
it "parse_inner APIs work when the reduce state uses lookahead disambiguation" do
|
it "parse_inner APIs work when the reduce state uses lookahead disambiguation" do
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
ptype int;
|
ptype #{language == "rust" ? "i64" : "int"};
|
||||||
token a;
|
token a;
|
||||||
token b;
|
token b;
|
||||||
start Start;
|
start Start;
|
||||||
@ -1719,7 +2033,7 @@ EOF
|
|||||||
it "allows multiple starting rules in tree mode" do
|
it "allows multiple starting rules in tree mode" do
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
tree;
|
tree;
|
||||||
ptype int;
|
ptype #{language == "rust" ? "i64" : "int"};
|
||||||
token a << $$ = 1; >>
|
token a << $$ = 1; >>
|
||||||
token b << $$ = 2; >>
|
token b << $$ = 2; >>
|
||||||
token c << $$ = 3; >>
|
token c << $$ = 3; >>
|
||||||
@ -1764,7 +2078,16 @@ EOF
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "executes code blocks associated with drop statements" do
|
it "executes code blocks associated with drop statements" do
|
||||||
if language == "d"
|
if language == "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
drop /\\s+/;
|
||||||
|
drop /#(.*)\\n/ <<
|
||||||
|
eprint!("comment: {}", std::str::from_utf8(match_).unwrap());
|
||||||
|
>>
|
||||||
|
token a;
|
||||||
|
Start -> a;
|
||||||
|
EOF
|
||||||
|
elsif language == "d"
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
<<
|
<<
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
@ -1798,7 +2121,24 @@ EOF
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "allows user-defined context fields" do
|
it "allows user-defined context fields" do
|
||||||
if language == "d"
|
if language == "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
context_user_fields <<
|
||||||
|
pub comments: String,
|
||||||
|
pub acount: u32,
|
||||||
|
>>
|
||||||
|
drop /\\s+/;
|
||||||
|
drop /#(.*)\\n/ <<
|
||||||
|
${context.comments} += std::str::from_utf8(match_).unwrap();
|
||||||
|
>>
|
||||||
|
token a <<
|
||||||
|
${context.acount} += 1;
|
||||||
|
>>
|
||||||
|
Start -> As;
|
||||||
|
As -> ;
|
||||||
|
As -> a As;
|
||||||
|
EOF
|
||||||
|
elsif language == "d"
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
context_user_fields <<
|
context_user_fields <<
|
||||||
string comments;
|
string comments;
|
||||||
@ -1858,7 +2198,28 @@ EOF
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "allows custom token user fields" do
|
it "allows custom token user fields" do
|
||||||
if language == "d"
|
if language == "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
context_user_fields <<
|
||||||
|
pub comments: String,
|
||||||
|
>>
|
||||||
|
token_user_fields <<
|
||||||
|
pub comments: String,
|
||||||
|
>>
|
||||||
|
on_token_node <<
|
||||||
|
${token.comments} = std::mem::take(&mut ${context.comments});
|
||||||
|
>>
|
||||||
|
tree;
|
||||||
|
drop /\\s+/;
|
||||||
|
drop /#(.*)\\n/ <<
|
||||||
|
${context.comments} += std::str::from_utf8(match_).unwrap();
|
||||||
|
>>
|
||||||
|
token id /\\w+/;
|
||||||
|
Start -> IDs;
|
||||||
|
IDs -> ;
|
||||||
|
IDs -> id:id IDs;
|
||||||
|
EOF
|
||||||
|
elsif language == "d"
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
context_user_fields <<
|
context_user_fields <<
|
||||||
string comments;
|
string comments;
|
||||||
@ -1955,7 +2316,41 @@ EOF
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "allows a custom lex function" do
|
it "allows a custom lex function" do
|
||||||
if language == "d"
|
if language == "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
<<
|
||||||
|
fn mylexfn(context: &mut p_context_t, out_token_info: &mut p_token_info_t) -> usize {
|
||||||
|
let mut result = P_SUCCESS;
|
||||||
|
if context.count > 0 {
|
||||||
|
out_token_info.token = TOKEN_a;
|
||||||
|
out_token_info.pvalue = p_value(context.count);
|
||||||
|
context.count -= 1;
|
||||||
|
} else {
|
||||||
|
result = p_lex(context, out_token_info);
|
||||||
|
if out_token_info.token == TOKEN_c {
|
||||||
|
context.count = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
>>
|
||||||
|
|
||||||
|
context_user_fields <<
|
||||||
|
pub count: usize,
|
||||||
|
>>
|
||||||
|
ptype usize;
|
||||||
|
lex_fn mylexfn;
|
||||||
|
|
||||||
|
token a << $$ = 7; >>
|
||||||
|
token b << $$ = 8; >>
|
||||||
|
token c << $$ = 9; >>
|
||||||
|
Start -> << $$ = 0; >>
|
||||||
|
Start -> Start ID << $$ = ($1 << 4) | $2; >>
|
||||||
|
ID -> a << $$ = $1; >>
|
||||||
|
ID -> b << $$ = $1; >>
|
||||||
|
ID -> c << $$ = $1; >>
|
||||||
|
EOF
|
||||||
|
elsif language == "d"
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
<<
|
<<
|
||||||
private size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
|
private size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
|
||||||
@ -2037,7 +2432,19 @@ EOF
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "provides accessor functions to extract user values from a parser value" do
|
it "provides accessor functions to extract user values from a parser value" do
|
||||||
if language == "d"
|
if language == "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
ptype i64;
|
||||||
|
ptype float = f64;
|
||||||
|
ptype string = String;
|
||||||
|
|
||||||
|
drop /\\s+/;
|
||||||
|
token num /\\d+/ << $$ = 42; >>
|
||||||
|
token flt (float) /f/ << $$ = 1.5; >>
|
||||||
|
token str (string) /s/ << $$ = "hello".to_string(); >>
|
||||||
|
Start -> num flt str;
|
||||||
|
EOF
|
||||||
|
elsif language == "d"
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
ptype int;
|
ptype int;
|
||||||
ptype float = float;
|
ptype float = float;
|
||||||
@ -2070,7 +2477,49 @@ EOF
|
|||||||
end
|
end
|
||||||
|
|
||||||
it "allows accessing rule and component text positions" do
|
it "allows accessing rule and component text positions" do
|
||||||
if language == "d"
|
if language == "rust"
|
||||||
|
write_grammar <<EOF
|
||||||
|
drop /\\s+/;
|
||||||
|
token tok1;
|
||||||
|
token tok2;
|
||||||
|
token ident /[a-zA-Z_]\\w*/;
|
||||||
|
token num /\\d+/;
|
||||||
|
Num -> num;
|
||||||
|
Start -> ident Num <<
|
||||||
|
println!("ident start: {}, {}", ${1.position}.row, ${1.position}.col);
|
||||||
|
println!("ident end: {}, {}", ${1.end_position}.row, ${1.end_position}.col);
|
||||||
|
println!("Num start: {}, {}", ${2.position}.row, ${2.position}.col);
|
||||||
|
println!("Num end: {}, {}", ${2.end_position}.row, ${2.end_position}.col);
|
||||||
|
println!("Start start: {}, {}", ${$.position}.row, ${$.position}.col);
|
||||||
|
println!("Start end: {}, {}", ${$.end_position}.row, ${$.end_position}.col);
|
||||||
|
>>
|
||||||
|
R -> Empty tok2 <<
|
||||||
|
println!("Empty start: {}, {}", ${1.position}.row, ${1.position}.col);
|
||||||
|
println!("Empty end: {}, {}", ${1.end_position}.row, ${1.end_position}.col);
|
||||||
|
println!("tok2 start: {}, {}", ${2.position}.row, ${2.position}.col);
|
||||||
|
println!("tok2 end: {}, {}", ${2.end_position}.row, ${2.end_position}.col);
|
||||||
|
println!("R start: {}, {}", ${$.position}.row, ${$.position}.col);
|
||||||
|
println!("R end: {}, {}", ${$.end_position}.row, ${$.end_position}.col);
|
||||||
|
>>
|
||||||
|
R -> tok1 Empty <<
|
||||||
|
println!("tok1 start: {}, {}", ${1.position}.row, ${1.position}.col);
|
||||||
|
println!("tok1 end: {}, {}", ${1.end_position}.row, ${1.end_position}.col);
|
||||||
|
println!("Empty start: {}, {}", ${2.position}.row, ${2.position}.col);
|
||||||
|
println!("Empty end: {}, {}", ${2.end_position}.row, ${2.end_position}.col);
|
||||||
|
println!("R2 start: {}, {}", ${$.position}.row, ${$.position}.col);
|
||||||
|
println!("R2 end: {}, {}", ${$.end_position}.row, ${$.end_position}.col);
|
||||||
|
>>
|
||||||
|
Empty -> ;
|
||||||
|
Start -> R <<
|
||||||
|
println!("StartR start: {}, {}", ${$.position}.row, ${$.position}.col);
|
||||||
|
println!("StartR end: {}, {}", ${$.end_position}.row, ${$.end_position}.col);
|
||||||
|
>>
|
||||||
|
Start -> Empty <<
|
||||||
|
println!("StartEmpty start: {}, {}", ${$.position}.row, ${$.position}.col);
|
||||||
|
println!("StartEmpty end: {}, {}", ${$.end_position}.row, ${$.end_position}.col);
|
||||||
|
>>
|
||||||
|
EOF
|
||||||
|
elsif language == "d"
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
<<
|
<<
|
||||||
import std.stdio;
|
import std.stdio;
|
||||||
|
|||||||
67
spec/rewind.rust.propane
Normal file
67
spec/rewind.rust.propane
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<<
|
||||||
|
fn mylexfn(context: &mut p_context_t, out_token_info: &mut p_token_info_t) -> usize {
|
||||||
|
loop {
|
||||||
|
let result = p_lex(context, out_token_info);
|
||||||
|
if result != P_SUCCESS {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if out_token_info.token == TOKEN_repeat {
|
||||||
|
let mut count_info = p_token_info_t::default();
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(context, &mut count_info));
|
||||||
|
assert_eq!(TOKEN_num, count_info.token);
|
||||||
|
let mut brace_info = p_token_info_t::default();
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(context, &mut brace_info));
|
||||||
|
assert_eq!(TOKEN_lbrace, brace_info.token);
|
||||||
|
context.remaining = p_value_get(&count_info.pvalue);
|
||||||
|
context.body_index = p_input_index(context);
|
||||||
|
context.body_position = p_position(context);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if out_token_info.token == TOKEN_rbrace {
|
||||||
|
if context.remaining > 1 {
|
||||||
|
context.remaining -= 1;
|
||||||
|
let bi = context.body_index;
|
||||||
|
let bp = context.body_position;
|
||||||
|
p_set_input_index(context, bi);
|
||||||
|
p_set_position(context, bp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
context.remaining = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if out_token_info.token == TOKEN_num {
|
||||||
|
context.num_cols.push(out_token_info.position.col);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
>>
|
||||||
|
|
||||||
|
context_user_fields <<
|
||||||
|
pub nums: Vec<i64>,
|
||||||
|
pub num_cols: Vec<u32>,
|
||||||
|
pub remaining: i64,
|
||||||
|
pub body_index: usize,
|
||||||
|
pub body_position: p_position_t,
|
||||||
|
>>
|
||||||
|
|
||||||
|
ptype i64;
|
||||||
|
|
||||||
|
lex_fn mylexfn;
|
||||||
|
|
||||||
|
drop /\s+/;
|
||||||
|
token repeat /repeat/;
|
||||||
|
token lbrace /\{/;
|
||||||
|
token rbrace /\}/;
|
||||||
|
token plus /\+/;
|
||||||
|
token num /\d+/ <<
|
||||||
|
let mut v: i64 = 0;
|
||||||
|
for c in match_ { v = v * 10 + (*c - b'0') as i64; }
|
||||||
|
$$ = v;
|
||||||
|
>>
|
||||||
|
|
||||||
|
Start -> Statements;
|
||||||
|
Statements -> ;
|
||||||
|
Statements -> Statement Statements;
|
||||||
|
Statement -> Add;
|
||||||
|
Add -> num plus num << ${context.nums}.push($1 + $3); >>
|
||||||
16
spec/test_basic_math_grammar.rs
Normal file
16
spec/test_basic_math_grammar.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let cases: [(&[u8], u64); 4] = [
|
||||||
|
(b"1 + 2 * 3 + 4", 11),
|
||||||
|
(b"1 * 2 ** 4 * 3", 48),
|
||||||
|
(b"(1 + 2) * 3 + 4", 13),
|
||||||
|
(b"(2 * 2) ** 3 + 4 + 5", 73),
|
||||||
|
];
|
||||||
|
for (input, expected) in cases {
|
||||||
|
let mut context = p_context_new(input);
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
assert_eq!(expected, p_result(&context));
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
spec/test_custom_lex_fn.rs
Normal file
8
spec/test_custom_lex_fn.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"cbacba");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
assert_eq!(0x932187932187, p_result(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
7
spec/test_drop_code_block.rs
Normal file
7
spec/test_drop_code_block.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b" # comment 1\n# comment 2\na\n");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
35
spec/test_error_positions.rs
Normal file
35
spec/test_error_positions.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"a 42");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"a\n123\na a");
|
||||||
|
assert_eq!(P_UNEXPECTED_TOKEN, p_parse(&mut c));
|
||||||
|
assert_eq!(3, p_position(&c).row);
|
||||||
|
assert_eq!(4, p_position(&c).col);
|
||||||
|
assert_eq!(TOKEN_a, p_token(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"12");
|
||||||
|
assert_eq!(P_UNEXPECTED_TOKEN, p_parse(&mut c));
|
||||||
|
assert_eq!(1, p_position(&c).row);
|
||||||
|
assert_eq!(1, p_position(&c).col);
|
||||||
|
assert_eq!(TOKEN_num, p_token(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"a 12\n\nab");
|
||||||
|
assert_eq!(P_UNEXPECTED_INPUT, p_parse(&mut c));
|
||||||
|
assert_eq!(3, p_position(&c).row);
|
||||||
|
assert_eq!(2, p_position(&c).col);
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"a 12\n\na\n\n77\na \xAA");
|
||||||
|
assert_eq!(P_DECODE_ERROR, p_parse(&mut c));
|
||||||
|
assert_eq!(6, p_position(&c).row);
|
||||||
|
assert_eq!(5, p_position(&c).col);
|
||||||
|
assert_eq!("a", p_token_names[TOKEN_a as usize]);
|
||||||
|
assert_eq!("num", p_token_names[TOKEN_num as usize]);
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
7
spec/test_field_aliases.rs
Normal file
7
spec/test_field_aliases.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"foo1\nbar2");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
28
spec/test_input_index.rs
Normal file
28
spec/test_input_index.rs
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
assert_eq!(0, p_input_index(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"a b");
|
||||||
|
let mut ti = p_token_info_t::default();
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_a, ti.token);
|
||||||
|
assert_eq!(1, p_input_index(&c));
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_b, ti.token);
|
||||||
|
assert_eq!(3, p_input_index(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_Start(&mut c));
|
||||||
|
assert_eq!(2, p_input_index(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"abb");
|
||||||
|
let follow = [TOKEN_b];
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_inner_Start(&mut c, &follow));
|
||||||
|
assert_eq!(2, p_input_index(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
49
spec/test_lexer.rs
Normal file
49
spec/test_lexer.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn chk(ti: &p_token_info_t, row: u32, col: u32, erow: u32, ecol: u32, len: usize, token: p_token_t) {
|
||||||
|
assert_eq!(row, ti.position.row);
|
||||||
|
assert_eq!(col, ti.position.col);
|
||||||
|
assert_eq!(erow, ti.end_position.row);
|
||||||
|
assert_eq!(ecol, ti.end_position.col);
|
||||||
|
assert_eq!(len, ti.length);
|
||||||
|
assert_eq!(token, ti.token);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut cp: p_code_point_t = 0;
|
||||||
|
let mut cpl: u8 = 0;
|
||||||
|
|
||||||
|
assert_eq!(P_SUCCESS, p_decode_code_point(b"5", &mut cp, &mut cpl));
|
||||||
|
assert_eq!('5' as u32, cp);
|
||||||
|
assert_eq!(1, cpl);
|
||||||
|
|
||||||
|
assert_eq!(P_EOF, p_decode_code_point(b"", &mut cp, &mut cpl));
|
||||||
|
|
||||||
|
assert_eq!(P_SUCCESS, p_decode_code_point(b"\xC2\xA9", &mut cp, &mut cpl));
|
||||||
|
assert_eq!(0xA9, cp);
|
||||||
|
assert_eq!(2, cpl);
|
||||||
|
|
||||||
|
assert_eq!(P_SUCCESS, p_decode_code_point(b"\xf0\x9f\xa7\xa1", &mut cp, &mut cpl));
|
||||||
|
assert_eq!(0x1F9E1, cp);
|
||||||
|
assert_eq!(4, cpl);
|
||||||
|
|
||||||
|
assert_eq!(P_DECODE_ERROR, p_decode_code_point(b"\xf0\x9f\x27", &mut cp, &mut cpl));
|
||||||
|
assert_eq!(P_DECODE_ERROR, p_decode_code_point(b"\xf0\x9f\xa7\xFF", &mut cp, &mut cpl));
|
||||||
|
assert_eq!(P_DECODE_ERROR, p_decode_code_point(b"\xfe", &mut cp, &mut cpl));
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"5 + 4 * \n677 + 567");
|
||||||
|
let mut ti = p_token_info_t::default();
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut context, &mut ti)); chk(&ti, 1, 1, 1, 1, 1, TOKEN_int);
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut context, &mut ti)); chk(&ti, 1, 3, 1, 3, 1, TOKEN_plus);
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut context, &mut ti)); chk(&ti, 1, 5, 1, 5, 1, TOKEN_int);
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut context, &mut ti)); chk(&ti, 1, 7, 1, 7, 1, TOKEN_times);
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut context, &mut ti)); chk(&ti, 2, 1, 2, 3, 3, TOKEN_int);
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut context, &mut ti)); chk(&ti, 2, 5, 2, 5, 1, TOKEN_plus);
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut context, &mut ti)); chk(&ti, 2, 7, 2, 9, 3, TOKEN_int);
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut context, &mut ti)); chk(&ti, 2, 10, 2, 10, 0, TOKEN___EOF);
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"");
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut context, &mut ti)); chk(&ti, 1, 1, 1, 1, 0, TOKEN___EOF);
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
8
spec/test_lexer_match_text.rs
Normal file
8
spec/test_lexer_match_text.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"identifier_123");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
println!("pass1");
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
13
spec/test_lexer_modes.rs
Normal file
13
spec/test_lexer_modes.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"abc \"a string\" def");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
println!("pass1");
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"abc \"abc def\" def");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
println!("pass2");
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
13
spec/test_lexer_multiple_modes.rs
Normal file
13
spec/test_lexer_multiple_modes.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"abc.def");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
println!("pass1");
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"abc . abc");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
println!("pass2");
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
38
spec/test_lexer_positions.rs
Normal file
38
spec/test_lexer_positions.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"abc\n defg hi\n!");
|
||||||
|
let mut ti = p_token_info_t::default();
|
||||||
|
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_word, ti.token);
|
||||||
|
assert_eq!(1, c.last_start.row);
|
||||||
|
assert_eq!(1, c.last_start.col);
|
||||||
|
assert_eq!(1, c.last_end.row);
|
||||||
|
assert_eq!(3, c.last_end.col);
|
||||||
|
assert_eq!(c.last_start.row, ti.position.row);
|
||||||
|
assert_eq!(c.last_start.col, ti.position.col);
|
||||||
|
assert_eq!(c.last_end.row, ti.end_position.row);
|
||||||
|
assert_eq!(c.last_end.col, ti.end_position.col);
|
||||||
|
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_word, ti.token);
|
||||||
|
assert_eq!(2, c.last_start.row);
|
||||||
|
assert_eq!(3, c.last_start.col);
|
||||||
|
assert_eq!(2, c.last_end.row);
|
||||||
|
assert_eq!(6, c.last_end.col);
|
||||||
|
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_word, ti.token);
|
||||||
|
assert_eq!(2, c.last_start.row);
|
||||||
|
assert_eq!(8, c.last_start.col);
|
||||||
|
assert_eq!(2, c.last_end.row);
|
||||||
|
assert_eq!(9, c.last_end.col);
|
||||||
|
|
||||||
|
assert_eq!(P_USER_TERMINATED, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(42, p_user_terminate_code(&c));
|
||||||
|
assert_eq!(3, p_position(&c).row);
|
||||||
|
assert_eq!(1, p_position(&c).col);
|
||||||
|
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
13
spec/test_lexer_result_value.rs
Normal file
13
spec/test_lexer_result_value.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"x");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
assert_eq!(1, p_result(&context));
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"fabulous");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
assert_eq!(8, p_result(&context));
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
12
spec/test_lexer_unknown_character.rs
Normal file
12
spec/test_lexer_unknown_character.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"x");
|
||||||
|
assert_eq!(P_UNEXPECTED_INPUT, p_parse(&mut context));
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"123");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
assert_eq!(123, p_result(&context));
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
9
spec/test_macros.rs
Normal file
9
spec/test_macros.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = b"macro @m { 23 + 200 }\n66 + 100\n@m\n33 + 55\n@m\n";
|
||||||
|
let mut c = p_context_new(input);
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
assert_eq!(vec![166, 223, 88, 223], c.nums);
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
7
spec/test_match_backslashes.rs
Normal file
7
spec/test_match_backslashes.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"\x07\x08\t\n\x0b\x0c\rt");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
12
spec/test_multiple_parsers.rs
Normal file
12
spec/test_multiple_parsers.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use testparsermyp1 as m1;
|
||||||
|
use testparsermyp2 as m2;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context1 = m1::myp1_context_new(b"a\n1");
|
||||||
|
assert_eq!(m1::P_SUCCESS, m1::myp1_parse(&mut context1));
|
||||||
|
m1::myp1_context_delete(context1);
|
||||||
|
|
||||||
|
let mut context2 = m2::myp2_context_new(b"bcb");
|
||||||
|
assert_eq!(m2::P_SUCCESS, m2::myp2_parse(&mut context2));
|
||||||
|
m2::myp2_context_delete(context2);
|
||||||
|
}
|
||||||
@ -9,46 +9,42 @@ int main()
|
|||||||
p_context_t * context;
|
p_context_t * context;
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert(start->a == NULL);
|
assert(!p_node_valid(p_Start_a(start)));
|
||||||
assert(start->pToken2 != NULL);
|
assert(p_node_valid(p_Start_pToken2(start)));
|
||||||
assert_eq(TOKEN_b, start->pToken2->token);
|
assert_eq(TOKEN_b, p_tree_walk_Start(start, pToken2, token));
|
||||||
assert(start->pR3 == NULL);
|
assert(!p_node_valid(p_Start_pR3(start)));
|
||||||
assert(start->pR == NULL);
|
assert(!p_node_valid(p_Start_pR(start)));
|
||||||
assert(start->r == NULL);
|
assert(!p_node_valid(p_Start_r(start)));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "abcd";
|
input = "abcd";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start->a != NULL);
|
assert(p_node_valid(p_Start_a(start)));
|
||||||
assert_eq(TOKEN_a, start->pToken1->token);
|
assert_eq(TOKEN_a, p_tree_walk_Start(start, pToken1, token));
|
||||||
assert(start->pToken2 != NULL);
|
assert(p_node_valid(p_Start_pToken2(start)));
|
||||||
assert(start->pR3 != NULL);
|
assert(p_node_valid(p_Start_pR3(start)));
|
||||||
assert(start->pR != NULL);
|
assert(p_node_valid(p_Start_pR(start)));
|
||||||
assert(start->r != NULL);
|
assert(p_node_valid(p_Start_r(start)));
|
||||||
assert(start->pR == start->pR3);
|
assert(p_node_id(p_Start_pR(start)) == p_node_id(p_Start_pR3(start)));
|
||||||
assert(start->pR == start->r);
|
assert(p_node_id(p_Start_pR(start)) == p_node_id(p_Start_r(start)));
|
||||||
assert_eq(TOKEN_c, start->pR->pToken1->token);
|
assert_eq(TOKEN_c, p_tree_walk_Start(start, pR, pToken1, token));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "bdc";
|
input = "bdc";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start->a == NULL);
|
assert(!p_node_valid(p_Start_a(start)));
|
||||||
assert(start->pToken2 != NULL);
|
assert(p_node_valid(p_Start_pToken2(start)));
|
||||||
assert(start->r != NULL);
|
assert(p_node_valid(p_Start_r(start)));
|
||||||
assert_eq(TOKEN_d, start->pR->pToken1->token);
|
assert_eq(TOKEN_d, p_tree_walk_Start(start, pR, pToken1, token));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,40 +12,40 @@ unittest
|
|||||||
string input = "b";
|
string input = "b";
|
||||||
p_context_t * context = p_context_new(input);
|
p_context_t * context = p_context_new(input);
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert(start.pToken1 is null);
|
assert(!start.pToken1.valid);
|
||||||
assert(start.pToken2 !is null);
|
assert(start.pToken2.valid);
|
||||||
assert_eq(TOKEN_b, start.pToken2.token);
|
assert_eq(TOKEN_b, start.pToken2.token);
|
||||||
assert(start.pR3 is null);
|
assert(!start.pR3.valid);
|
||||||
assert(start.pR is null);
|
assert(!start.pR.valid);
|
||||||
assert(start.r is null);
|
assert(!start.r.valid);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "abcd";
|
input = "abcd";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start.pToken1 != null);
|
assert(start.pToken1.valid);
|
||||||
assert_eq(TOKEN_a, start.pToken1.token);
|
assert_eq(TOKEN_a, start.pToken1.token);
|
||||||
assert(start.pToken2 != null);
|
assert(start.pToken2.valid);
|
||||||
assert(start.pR3 != null);
|
assert(start.pR3.valid);
|
||||||
assert(start.pR != null);
|
assert(start.pR.valid);
|
||||||
assert(start.r != null);
|
assert(start.r.valid);
|
||||||
assert(start.pR == start.pR3);
|
assert(start.pR == start.pR3);
|
||||||
assert(start.pR == start.r);
|
assert(start.pR == start.r);
|
||||||
assert_eq(TOKEN_c, start.pR.pToken1.token);
|
assert_eq(TOKEN_c, start.pR.pToken1.token);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "bdc";
|
input = "bdc";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start.pToken1 is null);
|
assert(!start.pToken1.valid);
|
||||||
assert(start.pToken2 !is null);
|
assert(start.pToken2.valid);
|
||||||
assert(start.pR !is null);
|
assert(start.pR.valid);
|
||||||
assert_eq(TOKEN_d, start.pR.pToken1.token);
|
assert_eq(TOKEN_d, start.pR.pToken1.token);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
43
spec/test_named_optional_rule_component_tree.rs
Normal file
43
spec/test_named_optional_rule_component_tree.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"b");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
assert!(!start.a().valid());
|
||||||
|
assert!(start.pToken2().valid());
|
||||||
|
assert_eq!(TOKEN_b, start.pToken2().token());
|
||||||
|
assert!(!start.pR3().valid());
|
||||||
|
assert!(!start.pR().valid());
|
||||||
|
assert!(!start.r().valid());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"abcd");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
assert!(start.a().valid());
|
||||||
|
assert_eq!(TOKEN_a, start.pToken1().token());
|
||||||
|
assert!(start.pToken2().valid());
|
||||||
|
assert!(start.pR3().valid());
|
||||||
|
assert!(start.pR().valid());
|
||||||
|
assert!(start.r().valid());
|
||||||
|
assert_eq!(start.pR().node_id(), start.pR3().node_id());
|
||||||
|
assert_eq!(start.pR().node_id(), start.r().node_id());
|
||||||
|
assert_eq!(TOKEN_c, start.pR().pToken1().token());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"bdc");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
assert!(!start.a().valid());
|
||||||
|
assert!(start.pToken2().valid());
|
||||||
|
assert!(start.r().valid());
|
||||||
|
assert_eq!(TOKEN_d, start.pR().pToken1().token());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
9
spec/test_optional_rule_component.rs
Normal file
9
spec/test_optional_rule_component.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
for input in [&b"b"[..], &b"abcd"[..], &b"abdc"[..]] {
|
||||||
|
let mut context = p_context_new(input);
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,43 +9,39 @@ int main()
|
|||||||
p_context_t * context;
|
p_context_t * context;
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert(start->pToken1 == NULL);
|
assert(!p_node_valid(p_Start_pToken1(start)));
|
||||||
assert(start->pToken2 != NULL);
|
assert(p_node_valid(p_Start_pToken2(start)));
|
||||||
assert_eq(TOKEN_b, start->pToken2->token);
|
assert_eq(TOKEN_b, p_tree_walk_Start(start, pToken2, token));
|
||||||
assert(start->pR3 == NULL);
|
assert(!p_node_valid(p_Start_pR3(start)));
|
||||||
assert(start->pR == NULL);
|
assert(!p_node_valid(p_Start_pR(start)));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "abcd";
|
input = "abcd";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start->pToken1 != NULL);
|
assert(p_node_valid(p_Start_pToken1(start)));
|
||||||
assert_eq(TOKEN_a, start->pToken1->token);
|
assert_eq(TOKEN_a, p_tree_walk_Start(start, pToken1, token));
|
||||||
assert(start->pToken2 != NULL);
|
assert(p_node_valid(p_Start_pToken2(start)));
|
||||||
assert(start->pR3 != NULL);
|
assert(p_node_valid(p_Start_pR3(start)));
|
||||||
assert(start->pR != NULL);
|
assert(p_node_valid(p_Start_pR(start)));
|
||||||
assert(start->pR == start->pR3);
|
assert(p_node_id(p_Start_pR(start)) == p_node_id(p_Start_pR3(start)));
|
||||||
assert_eq(TOKEN_c, start->pR->pToken1->token);
|
assert_eq(TOKEN_c, p_tree_walk_Start(start, pR, pToken1, token));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "bdc";
|
input = "bdc";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start->pToken1 == NULL);
|
assert(!p_node_valid(p_Start_pToken1(start)));
|
||||||
assert(start->pToken2 != NULL);
|
assert(p_node_valid(p_Start_pToken2(start)));
|
||||||
assert(start->pR != NULL);
|
assert(p_node_valid(p_Start_pR(start)));
|
||||||
assert_eq(TOKEN_d, start->pR->pToken1->token);
|
assert_eq(TOKEN_d, p_tree_walk_Start(start, pR, pToken1, token));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,37 +12,37 @@ unittest
|
|||||||
string input = "b";
|
string input = "b";
|
||||||
p_context_t * context = p_context_new(input);
|
p_context_t * context = p_context_new(input);
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert(start.pToken1 is null);
|
assert(!start.pToken1.valid);
|
||||||
assert(start.pToken2 !is null);
|
assert(start.pToken2.valid);
|
||||||
assert_eq(TOKEN_b, start.pToken2.token);
|
assert_eq(TOKEN_b, start.pToken2.token);
|
||||||
assert(start.pR3 is null);
|
assert(!start.pR3.valid);
|
||||||
assert(start.pR is null);
|
assert(!start.pR.valid);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "abcd";
|
input = "abcd";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start.pToken1 != null);
|
assert(start.pToken1.valid);
|
||||||
assert_eq(TOKEN_a, start.pToken1.token);
|
assert_eq(TOKEN_a, start.pToken1.token);
|
||||||
assert(start.pToken2 != null);
|
assert(start.pToken2.valid);
|
||||||
assert(start.pR3 != null);
|
assert(start.pR3.valid);
|
||||||
assert(start.pR != null);
|
assert(start.pR.valid);
|
||||||
assert(start.pR == start.pR3);
|
assert(start.pR == start.pR3);
|
||||||
assert_eq(TOKEN_c, start.pR.pToken1.token);
|
assert_eq(TOKEN_c, start.pR.pToken1.token);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "bdc";
|
input = "bdc";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start.pToken1 is null);
|
assert(!start.pToken1.valid);
|
||||||
assert(start.pToken2 !is null);
|
assert(start.pToken2.valid);
|
||||||
assert(start.pR !is null);
|
assert(start.pR.valid);
|
||||||
assert_eq(TOKEN_d, start.pR.pToken1.token);
|
assert_eq(TOKEN_d, start.pR.pToken1.token);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
40
spec/test_optional_rule_component_tree.rs
Normal file
40
spec/test_optional_rule_component_tree.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"b");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
assert!(!start.pToken1().valid());
|
||||||
|
assert!(start.pToken2().valid());
|
||||||
|
assert_eq!(TOKEN_b, start.pToken2().token());
|
||||||
|
assert!(!start.pR3().valid());
|
||||||
|
assert!(!start.pR().valid());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"abcd");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
assert!(start.pToken1().valid());
|
||||||
|
assert_eq!(TOKEN_a, start.pToken1().token());
|
||||||
|
assert!(start.pToken2().valid());
|
||||||
|
assert!(start.pR3().valid());
|
||||||
|
assert!(start.pR().valid());
|
||||||
|
assert_eq!(start.pR().node_id(), start.pR3().node_id());
|
||||||
|
assert_eq!(TOKEN_c, start.pR().pToken1().token());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"bdc");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
assert!(!start.pToken1().valid());
|
||||||
|
assert!(start.pToken2().valid());
|
||||||
|
assert!(start.pR().valid());
|
||||||
|
assert_eq!(TOKEN_d, start.pR().pToken1().token());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
30
spec/test_parse_inner.rs
Normal file
30
spec/test_parse_inner.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"a");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_Start(&mut c));
|
||||||
|
assert_eq!(1, p_result_Start(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
assert_eq!(P_UNEXPECTED_TOKEN, p_parse_Start(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_inner_Start(&mut c, &[TOKEN_b]));
|
||||||
|
assert_eq!(1, p_result_Start(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
assert_eq!(P_UNEXPECTED_TOKEN, p_parse_inner_Start(&mut c, &[]));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"a");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_inner_Start(&mut c, &[TOKEN_b]));
|
||||||
|
assert_eq!(1, p_result_Start(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
assert_eq!(P_UNEXPECTED_TOKEN, p_parse_inner_Start(&mut c, &[TOKEN___EOF]));
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
17
spec/test_parse_inner_nested.rs
Normal file
17
spec/test_parse_inner_nested.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn eval(input: &[u8]) -> i64 {
|
||||||
|
let mut c = p_context_new(input);
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
let v = p_result(&c);
|
||||||
|
p_context_delete(c);
|
||||||
|
v
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(5, eval(b"2 + 3"));
|
||||||
|
assert_eq!(3, eval(b"(1 + 2)"));
|
||||||
|
assert_eq!(14, eval(b"2 + (3 + 4) + 5"));
|
||||||
|
assert_eq!(37, eval(b"2 + (10 + (20 + 5))"));
|
||||||
|
assert_eq!(15, eval(b"(1 + 2) + (3 + (4 + 5))"));
|
||||||
|
}
|
||||||
@ -3,21 +3,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "testutils.h"
|
#include "testutils.h"
|
||||||
|
|
||||||
/* Grammar (tree generation mode; parentheses handled by the lex function):
|
/* Grammar: see the D variant / spec. Tree generation mode; parentheses handled
|
||||||
* tree;
|
* by the lex function. Tree nodes live in the context arena. */
|
||||||
* lex_fn mylexfn;
|
|
||||||
* token lparen /\(/; token rparen /\)/; token plus /\+/; token num /\d+/;
|
|
||||||
* Start -> Expr;
|
|
||||||
* Expr -> num;
|
|
||||||
* Expr -> Expr plus num;
|
|
||||||
*
|
|
||||||
* The same lexer-driven nested parse as test_parse_inner_nested, but in tree
|
|
||||||
* generation mode. Each "( ... )" group is parsed by a reentrant
|
|
||||||
* p_parse_inner_Start() call from the lex function; the resulting subtree is
|
|
||||||
* discarded and a single synthesized num token is handed to the outer parse.
|
|
||||||
* The synthesized token's position is set to span the whole group ('(' start
|
|
||||||
* through ')' end), so this verifies that positions survive the nested-parse
|
|
||||||
* boundary and land correctly in the outer tree. */
|
|
||||||
|
|
||||||
size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
|
size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
|
||||||
{
|
{
|
||||||
@ -36,8 +23,8 @@ size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
|
|||||||
{
|
{
|
||||||
return inner_result;
|
return inner_result;
|
||||||
}
|
}
|
||||||
Start * inner = p_result_Start(context);
|
Start inner = p_result_Start(context);
|
||||||
assert_not_null(inner);
|
assert(p_node_valid(inner));
|
||||||
/* p_parse_inner rewound the input so that ')' was not consumed; consume
|
/* p_parse_inner rewound the input so that ')' was not consumed; consume
|
||||||
* it now. */
|
* it now. */
|
||||||
p_token_info_t rparen_info;
|
p_token_info_t rparen_info;
|
||||||
@ -45,10 +32,11 @@ size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
|
|||||||
assert(rparen_result == P_SUCCESS);
|
assert(rparen_result == P_SUCCESS);
|
||||||
assert(rparen_info.token == TOKEN_rparen);
|
assert(rparen_info.token == TOKEN_rparen);
|
||||||
/* The subtree covers the region strictly between the parentheses. */
|
/* The subtree covers the region strictly between the parentheses. */
|
||||||
assert_eq((size_t)(start_position.col + 1u), (size_t)inner->position.col);
|
assert_eq((size_t)(start_position.col + 1u), (size_t)p_node_position(inner).col);
|
||||||
assert_eq((size_t)(rparen_info.position.col - 1u), (size_t)inner->end_position.col);
|
assert_eq((size_t)(rparen_info.position.col - 1u), (size_t)p_node_end_position(inner).col);
|
||||||
p_tree_delete_Start(inner);
|
/* The inner subtree is discarded (the lexer synthesizes a num token in
|
||||||
/* Synthesize a num token spanning the entire "( ... )" group. */
|
* its place), but its nodes remain in the shared context arena and are
|
||||||
|
* freed with the context. */
|
||||||
out_token_info->token = TOKEN_num;
|
out_token_info->token = TOKEN_num;
|
||||||
out_token_info->position = start_position;
|
out_token_info->position = start_position;
|
||||||
out_token_info->end_position = rparen_info.end_position;
|
out_token_info->end_position = rparen_info.end_position;
|
||||||
@ -64,40 +52,39 @@ int main()
|
|||||||
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
|
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
|
|
||||||
Start * tree = p_result(context);
|
Start tree = p_result(context);
|
||||||
assert_not_null(tree);
|
assert(p_node_valid(tree));
|
||||||
|
|
||||||
/* Start -> Expr, where the top Expr is "Expr plus num". */
|
/* Start -> Expr, where the top Expr is "Expr plus num". */
|
||||||
Expr * top = tree->pExpr;
|
Expr top = p_Start_pExpr(tree);
|
||||||
assert_not_null(top);
|
assert(p_node_valid(top));
|
||||||
assert_not_null(top->pExpr);
|
assert(p_node_valid(p_Expr_pExpr(top)));
|
||||||
assert_not_null(top->pToken2);
|
assert(p_node_valid(p_Expr_pToken2(top)));
|
||||||
assert_not_null(top->pToken3);
|
assert(p_node_valid(p_Expr_pToken3(top)));
|
||||||
|
|
||||||
/* The '+' joining the two groups is at column 9. */
|
/* The '+' joining the two groups is at column 9. */
|
||||||
assert_eq(1u, (size_t)top->pToken2->position.row);
|
assert_eq(1u, (size_t)p_node_position(p_Expr_pToken2(top)).row);
|
||||||
assert_eq(9u, (size_t)top->pToken2->position.col);
|
assert_eq(9u, (size_t)p_node_position(p_Expr_pToken2(top)).col);
|
||||||
|
|
||||||
/* Right operand: synthesized num for "(5 + 6)", spanning columns 11..17. */
|
/* Right operand: synthesized num for "(5 + 6)", spanning columns 11..17. */
|
||||||
assert_eq(1u, (size_t)top->pToken3->position.row);
|
assert_eq(1u, (size_t)p_node_position(p_Expr_pToken3(top)).row);
|
||||||
assert_eq(11u, (size_t)top->pToken3->position.col);
|
assert_eq(11u, (size_t)p_node_position(p_Expr_pToken3(top)).col);
|
||||||
assert_eq(1u, (size_t)top->pToken3->end_position.row);
|
assert_eq(1u, (size_t)p_node_end_position(p_Expr_pToken3(top)).row);
|
||||||
assert_eq(17u, (size_t)top->pToken3->end_position.col);
|
assert_eq(17u, (size_t)p_node_end_position(p_Expr_pToken3(top)).col);
|
||||||
|
|
||||||
/* Left operand: Expr -> num, the synthesized num for "(3 + 4)", spanning
|
/* Left operand: Expr -> num, the synthesized num for "(3 + 4)", spanning
|
||||||
* columns 1..7. */
|
* columns 1..7. */
|
||||||
Expr * left = top->pExpr;
|
Expr left = p_Expr_pExpr(top);
|
||||||
assert_not_null(left->pToken1);
|
assert(p_node_valid(p_Expr_pToken1(left)));
|
||||||
assert_eq(1u, (size_t)left->pToken1->position.row);
|
assert_eq(1u, (size_t)p_node_position(p_Expr_pToken1(left)).row);
|
||||||
assert_eq(1u, (size_t)left->pToken1->position.col);
|
assert_eq(1u, (size_t)p_node_position(p_Expr_pToken1(left)).col);
|
||||||
assert_eq(1u, (size_t)left->pToken1->end_position.row);
|
assert_eq(1u, (size_t)p_node_end_position(p_Expr_pToken1(left)).row);
|
||||||
assert_eq(7u, (size_t)left->pToken1->end_position.col);
|
assert_eq(7u, (size_t)p_node_end_position(p_Expr_pToken1(left)).col);
|
||||||
|
|
||||||
/* The whole tree spans columns 1..17. */
|
/* The whole tree spans columns 1..17. */
|
||||||
assert_eq(1u, (size_t)tree->position.col);
|
assert_eq(1u, (size_t)p_node_position(tree).col);
|
||||||
assert_eq(17u, (size_t)tree->end_position.col);
|
assert_eq(17u, (size_t)p_node_end_position(tree).col);
|
||||||
|
|
||||||
p_tree_delete(tree);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -20,8 +20,8 @@ size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
|
|||||||
{
|
{
|
||||||
return inner_result;
|
return inner_result;
|
||||||
}
|
}
|
||||||
Start * inner = p_result_Start(context);
|
Start inner = p_result_Start(context);
|
||||||
assert(inner !is null);
|
assert(inner.valid);
|
||||||
/* p_parse_inner rewound the input so that ')' was not consumed; consume
|
/* p_parse_inner rewound the input so that ')' was not consumed; consume
|
||||||
* it now. */
|
* it now. */
|
||||||
p_token_info_t rparen_info;
|
p_token_info_t rparen_info;
|
||||||
@ -31,7 +31,9 @@ size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
|
|||||||
/* The subtree covers the region strictly between the parentheses. */
|
/* The subtree covers the region strictly between the parentheses. */
|
||||||
assert_eq(start_position.col + 1u, inner.position.col);
|
assert_eq(start_position.col + 1u, inner.position.col);
|
||||||
assert_eq(rparen_info.position.col - 1u, inner.end_position.col);
|
assert_eq(rparen_info.position.col - 1u, inner.end_position.col);
|
||||||
p_tree_delete_Start(inner);
|
/* The inner subtree is discarded (the lexer synthesizes a num token in
|
||||||
|
* its place), but its nodes remain in the shared context arena and are
|
||||||
|
* freed with the context. */
|
||||||
/* Synthesize a num token spanning the entire "( ... )" group. */
|
/* Synthesize a num token spanning the entire "( ... )" group. */
|
||||||
out_token_info.token = TOKEN_num;
|
out_token_info.token = TOKEN_num;
|
||||||
out_token_info.position = start_position;
|
out_token_info.position = start_position;
|
||||||
@ -53,15 +55,15 @@ unittest
|
|||||||
p_context_t * context = p_context_new(input);
|
p_context_t * context = p_context_new(input);
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
|
|
||||||
Start * tree = p_result(context);
|
Start tree = p_result(context);
|
||||||
assert(tree !is null);
|
assert(tree.valid);
|
||||||
|
|
||||||
/* Start -> Expr, where the top Expr is "Expr plus num". */
|
/* Start -> Expr, where the top Expr is "Expr plus num". */
|
||||||
Expr * top = tree.pExpr;
|
Expr top = tree.pExpr;
|
||||||
assert(top !is null);
|
assert(top.valid);
|
||||||
assert(top.pExpr !is null);
|
assert(top.pExpr.valid);
|
||||||
assert(top.pToken2 !is null);
|
assert(top.pToken2.valid);
|
||||||
assert(top.pToken3 !is null);
|
assert(top.pToken3.valid);
|
||||||
|
|
||||||
/* The '+' joining the two groups is at column 9. */
|
/* The '+' joining the two groups is at column 9. */
|
||||||
assert_eq(1u, top.pToken2.position.row);
|
assert_eq(1u, top.pToken2.position.row);
|
||||||
@ -75,8 +77,8 @@ unittest
|
|||||||
|
|
||||||
/* Left operand: Expr -> num, the synthesized num for "(3 + 4)", spanning
|
/* Left operand: Expr -> num, the synthesized num for "(3 + 4)", spanning
|
||||||
* columns 1..7. */
|
* columns 1..7. */
|
||||||
Expr * left = top.pExpr;
|
Expr left = top.pExpr;
|
||||||
assert(left.pToken1 !is null);
|
assert(left.pToken1.valid);
|
||||||
assert_eq(1u, left.pToken1.position.row);
|
assert_eq(1u, left.pToken1.position.row);
|
||||||
assert_eq(1u, left.pToken1.position.col);
|
assert_eq(1u, left.pToken1.position.col);
|
||||||
assert_eq(1u, left.pToken1.end_position.row);
|
assert_eq(1u, left.pToken1.end_position.row);
|
||||||
@ -86,6 +88,5 @@ unittest
|
|||||||
assert_eq(1u, tree.position.col);
|
assert_eq(1u, tree.position.col);
|
||||||
assert_eq(17u, tree.end_position.col);
|
assert_eq(17u, tree.end_position.col);
|
||||||
|
|
||||||
p_tree_delete(tree);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
36
spec/test_parse_inner_nested_tree.rs
Normal file
36
spec/test_parse_inner_nested_tree.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"(3 + 4) + (5 + 6)");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
{
|
||||||
|
let tree = p_result(&c);
|
||||||
|
assert!(tree.valid());
|
||||||
|
|
||||||
|
/* Start -> Expr, where the top Expr is "Expr plus num". */
|
||||||
|
let top = tree.pExpr();
|
||||||
|
assert!(top.valid());
|
||||||
|
assert!(top.pExpr().valid());
|
||||||
|
assert!(top.pToken2().valid());
|
||||||
|
assert!(top.pToken3().valid());
|
||||||
|
|
||||||
|
/* The '+' joining the two groups is at column 9. */
|
||||||
|
assert_eq!(1, top.pToken2().position().row);
|
||||||
|
assert_eq!(9, top.pToken2().position().col);
|
||||||
|
|
||||||
|
/* Right operand: synthesized num for "(5 + 6)", columns 11..17. */
|
||||||
|
assert_eq!(11, top.pToken3().position().col);
|
||||||
|
assert_eq!(17, top.pToken3().end_position().col);
|
||||||
|
|
||||||
|
/* Left operand: synthesized num for "(3 + 4)", columns 1..7. */
|
||||||
|
let left = top.pExpr();
|
||||||
|
assert!(left.pToken1().valid());
|
||||||
|
assert_eq!(1, left.pToken1().position().col);
|
||||||
|
assert_eq!(7, left.pToken1().end_position().col);
|
||||||
|
|
||||||
|
/* The whole tree spans columns 1..17. */
|
||||||
|
assert_eq!(1, tree.position().col);
|
||||||
|
assert_eq!(17, tree.end_position().col);
|
||||||
|
}
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
31
spec/test_parse_inner_recursive.rs
Normal file
31
spec/test_parse_inner_recursive.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"c");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_Start(&mut c));
|
||||||
|
assert_eq!(3, p_result_Start(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"acb");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_Start(&mut c));
|
||||||
|
assert_eq!(3, p_result_Start(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"ac");
|
||||||
|
assert_eq!(P_UNEXPECTED_TOKEN, p_parse_Start(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"ac");
|
||||||
|
assert_eq!(P_UNEXPECTED_TOKEN, p_parse_inner_Start(&mut c, &[TOKEN_b, TOKEN___EOF]));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"acb");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_inner_Start(&mut c, &[TOKEN_b]));
|
||||||
|
assert_eq!(3, p_result_Start(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"c");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_inner_Start(&mut c, &[TOKEN_b]));
|
||||||
|
assert_eq!(3, p_result_Start(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
47
spec/test_parse_inner_shared.rs
Normal file
47
spec/test_parse_inner_shared.rs
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"aba");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_Start(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"abb");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_Start(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_R1(&mut c));
|
||||||
|
assert_eq!(11, p_result_R1(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"abb");
|
||||||
|
assert_eq!(P_UNEXPECTED_TOKEN, p_parse_R1(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"abb");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_inner_R1(&mut c, &[TOKEN_b]));
|
||||||
|
assert_eq!(11, p_result_R1(&c));
|
||||||
|
let pos = p_position(&c);
|
||||||
|
assert_eq!(1, pos.row);
|
||||||
|
assert_eq!(3, pos.col);
|
||||||
|
let mut ti = p_token_info_t::default();
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_b, ti.token);
|
||||||
|
assert_eq!(3, ti.position.col);
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"aba");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_inner_R1(&mut c, &[TOKEN_a]));
|
||||||
|
assert_eq!(11, p_result_R1(&c));
|
||||||
|
let pos = p_position(&c);
|
||||||
|
assert_eq!(3, pos.col);
|
||||||
|
let mut ti = p_token_info_t::default();
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_a, ti.token);
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_inner_R1(&mut c, &[]));
|
||||||
|
assert_eq!(11, p_result_R1(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
@ -5,24 +5,8 @@
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
/* Grammar (tree generation mode, shared reduce state):
|
/* See the D variant / grammar comments for details. In tree mode the tree
|
||||||
* tree;
|
* nodes live in the context arena and are freed with p_context_delete(). */
|
||||||
* token a; token b;
|
|
||||||
* start Start;
|
|
||||||
* start R1;
|
|
||||||
* Start -> R1 a;
|
|
||||||
* Start -> R2 b;
|
|
||||||
* R1 -> a b;
|
|
||||||
* R2 -> a b;
|
|
||||||
*
|
|
||||||
* Exercises p_parse_inner_R1() with a non-EOF follow token in tree
|
|
||||||
* generation mode. Verifies:
|
|
||||||
* * The reduced tree for R1 is well-formed after a follow-token
|
|
||||||
* completion.
|
|
||||||
* * The follow token is not consumed and remains available for a
|
|
||||||
* subsequent p_lex() call.
|
|
||||||
* * p_tree_delete_R1() cleans up the returned tree without leaks
|
|
||||||
* (verified in CI via valgrind). */
|
|
||||||
|
|
||||||
/* Baseline: p_parse_R1 works on "ab" and the returned tree is
|
/* Baseline: p_parse_R1 works on "ab" and the returned tree is
|
||||||
* well-formed. */
|
* well-formed. */
|
||||||
@ -30,13 +14,12 @@ int main()
|
|||||||
char const * input = "ab";
|
char const * input = "ab";
|
||||||
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
|
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse_R1(context) == P_SUCCESS);
|
assert(p_parse_R1(context) == P_SUCCESS);
|
||||||
R1 * tree = p_result_R1(context);
|
R1 tree = p_result_R1(context);
|
||||||
assert_not_null(tree);
|
assert(p_node_valid(tree));
|
||||||
assert_not_null(tree->pToken1);
|
assert(p_node_valid(p_R1_pToken1(tree)));
|
||||||
assert_eq((size_t)TOKEN_a, (size_t)tree->pToken1->token);
|
assert_eq((size_t)TOKEN_a, (size_t)p_tree_walk_R1(tree, pToken1, token));
|
||||||
assert_not_null(tree->pToken2);
|
assert(p_node_valid(p_R1_pToken2(tree)));
|
||||||
assert_eq((size_t)TOKEN_b, (size_t)tree->pToken2->token);
|
assert_eq((size_t)TOKEN_b, (size_t)p_tree_walk_R1(tree, pToken2, token));
|
||||||
p_tree_delete_R1(tree);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,23 +33,23 @@ int main()
|
|||||||
assert(p_parse_inner_R1(context, follow_tokens, 1u) == P_SUCCESS);
|
assert(p_parse_inner_R1(context, follow_tokens, 1u) == P_SUCCESS);
|
||||||
|
|
||||||
/* Tree is well-formed. */
|
/* Tree is well-formed. */
|
||||||
R1 * tree = p_result_R1(context);
|
R1 tree = p_result_R1(context);
|
||||||
assert_not_null(tree);
|
assert(p_node_valid(tree));
|
||||||
assert_not_null(tree->pToken1);
|
assert(p_node_valid(p_R1_pToken1(tree)));
|
||||||
assert_eq((size_t)TOKEN_a, (size_t)tree->pToken1->token);
|
assert_eq((size_t)TOKEN_a, (size_t)p_tree_walk_R1(tree, pToken1, token));
|
||||||
assert_eq(1u, (size_t)tree->pToken1->position.row);
|
assert_eq(1u, (size_t)p_node_position(p_R1_pToken1(tree)).row);
|
||||||
assert_eq(1u, (size_t)tree->pToken1->position.col);
|
assert_eq(1u, (size_t)p_node_position(p_R1_pToken1(tree)).col);
|
||||||
assert_not_null(tree->pToken2);
|
assert(p_node_valid(p_R1_pToken2(tree)));
|
||||||
assert_eq((size_t)TOKEN_b, (size_t)tree->pToken2->token);
|
assert_eq((size_t)TOKEN_b, (size_t)p_tree_walk_R1(tree, pToken2, token));
|
||||||
assert_eq(1u, (size_t)tree->pToken2->position.row);
|
assert_eq(1u, (size_t)p_node_position(p_R1_pToken2(tree)).row);
|
||||||
assert_eq(2u, (size_t)tree->pToken2->position.col);
|
assert_eq(2u, (size_t)p_node_position(p_R1_pToken2(tree)).col);
|
||||||
|
|
||||||
/* The R1 tree covers positions 1..2 — the third `b` at column 3 is
|
/* The R1 tree covers positions 1..2 - the third `b` at column 3 is
|
||||||
* the follow token and is not part of the tree. */
|
* the follow token and is not part of the tree. */
|
||||||
assert_eq(1u, (size_t)tree->position.row);
|
assert_eq(1u, (size_t)p_node_position(tree).row);
|
||||||
assert_eq(1u, (size_t)tree->position.col);
|
assert_eq(1u, (size_t)p_node_position(tree).col);
|
||||||
assert_eq(1u, (size_t)tree->end_position.row);
|
assert_eq(1u, (size_t)p_node_end_position(tree).row);
|
||||||
assert_eq(2u, (size_t)tree->end_position.col);
|
assert_eq(2u, (size_t)p_node_end_position(tree).col);
|
||||||
|
|
||||||
/* Follow token remains in the input. */
|
/* Follow token remains in the input. */
|
||||||
p_position_t pos = p_position(context);
|
p_position_t pos = p_position(context);
|
||||||
@ -78,10 +61,6 @@ int main()
|
|||||||
assert_eq(1u, (size_t)token_info.position.row);
|
assert_eq(1u, (size_t)token_info.position.row);
|
||||||
assert_eq(3u, (size_t)token_info.position.col);
|
assert_eq(3u, (size_t)token_info.position.col);
|
||||||
|
|
||||||
/* p_tree_delete_R1 must free every node reachable from the tree
|
|
||||||
* without leaking anything. valgrind (invoked by the spec runner on
|
|
||||||
* Linux) will detect any missed frees. */
|
|
||||||
p_tree_delete_R1(tree);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -16,13 +16,13 @@ unittest
|
|||||||
string input = "ab";
|
string input = "ab";
|
||||||
p_context_t * context = p_context_new(input);
|
p_context_t * context = p_context_new(input);
|
||||||
assert(p_parse_R1(context) == P_SUCCESS);
|
assert(p_parse_R1(context) == P_SUCCESS);
|
||||||
R1 * tree = p_result_R1(context);
|
R1 tree = p_result_R1(context);
|
||||||
assert(tree !is null);
|
assert(tree.valid);
|
||||||
assert(tree.pToken1 !is null);
|
assert(tree.pToken1.valid);
|
||||||
assert(tree.pToken1.token == TOKEN_a);
|
assert(tree.pToken1.token == TOKEN_a);
|
||||||
assert(tree.pToken2 !is null);
|
assert(tree.pToken2.valid);
|
||||||
assert(tree.pToken2.token == TOKEN_b);
|
assert(tree.pToken2.token == TOKEN_b);
|
||||||
p_tree_delete_R1(tree);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Primary case: p_parse_inner_R1 with a non-EOF follow token completes
|
/* Primary case: p_parse_inner_R1 with a non-EOF follow token completes
|
||||||
@ -35,13 +35,13 @@ unittest
|
|||||||
assert(p_parse_inner_R1(context, follow_tokens) == P_SUCCESS);
|
assert(p_parse_inner_R1(context, follow_tokens) == P_SUCCESS);
|
||||||
|
|
||||||
/* Tree is well-formed. */
|
/* Tree is well-formed. */
|
||||||
R1 * tree = p_result_R1(context);
|
R1 tree = p_result_R1(context);
|
||||||
assert(tree !is null);
|
assert(tree.valid);
|
||||||
assert(tree.pToken1 !is null);
|
assert(tree.pToken1.valid);
|
||||||
assert(tree.pToken1.token == TOKEN_a);
|
assert(tree.pToken1.token == TOKEN_a);
|
||||||
assert(tree.pToken1.position.row == 1);
|
assert(tree.pToken1.position.row == 1);
|
||||||
assert(tree.pToken1.position.col == 1);
|
assert(tree.pToken1.position.col == 1);
|
||||||
assert(tree.pToken2 !is null);
|
assert(tree.pToken2.valid);
|
||||||
assert(tree.pToken2.token == TOKEN_b);
|
assert(tree.pToken2.token == TOKEN_b);
|
||||||
assert(tree.pToken2.position.row == 1);
|
assert(tree.pToken2.position.row == 1);
|
||||||
assert(tree.pToken2.position.col == 2);
|
assert(tree.pToken2.position.col == 2);
|
||||||
@ -63,6 +63,6 @@ unittest
|
|||||||
assert(token_info.position.row == 1);
|
assert(token_info.position.row == 1);
|
||||||
assert(token_info.position.col == 3);
|
assert(token_info.position.col == 3);
|
||||||
|
|
||||||
p_tree_delete_R1(tree);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
spec/test_parse_inner_tree.rs
Normal file
36
spec/test_parse_inner_tree.rs
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_R1(&mut c));
|
||||||
|
{
|
||||||
|
let tree = p_result_R1(&c);
|
||||||
|
assert!(tree.valid());
|
||||||
|
assert!(tree.pToken1().valid());
|
||||||
|
assert_eq!(TOKEN_a, tree.pToken1().token());
|
||||||
|
assert!(tree.pToken2().valid());
|
||||||
|
assert_eq!(TOKEN_b, tree.pToken2().token());
|
||||||
|
}
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"abb");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_inner_R1(&mut c, &[TOKEN_b]));
|
||||||
|
{
|
||||||
|
let tree = p_result_R1(&c);
|
||||||
|
assert!(tree.valid());
|
||||||
|
assert_eq!(TOKEN_a, tree.pToken1().token());
|
||||||
|
assert_eq!(1, tree.pToken1().position().row);
|
||||||
|
assert_eq!(1, tree.pToken1().position().col);
|
||||||
|
assert_eq!(TOKEN_b, tree.pToken2().token());
|
||||||
|
assert_eq!(2, tree.pToken2().position().col);
|
||||||
|
assert_eq!(1, tree.position().col);
|
||||||
|
assert_eq!(2, tree.end_position().col);
|
||||||
|
}
|
||||||
|
let pos = p_position(&c);
|
||||||
|
assert_eq!(3, pos.col);
|
||||||
|
let mut ti = p_token_info_t::default();
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_b, ti.token);
|
||||||
|
assert_eq!(3, ti.position.col);
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
9
spec/test_parser_identical_rules_lookahead.rs
Normal file
9
spec/test_parser_identical_rules_lookahead.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
for input in [&b"aba"[..], &b"abb"[..]] {
|
||||||
|
let mut context = p_context_new(input);
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
18
spec/test_parser_rule_from_multiple_states.rs
Normal file
18
spec/test_parser_rule_from_multiple_states.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"a");
|
||||||
|
assert_eq!(P_UNEXPECTED_TOKEN, p_parse(&mut context));
|
||||||
|
assert_eq!(1, p_position(&context).row);
|
||||||
|
assert_eq!(2, p_position(&context).col);
|
||||||
|
assert_eq!(TOKEN___EOF, p_token(&context));
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"a b");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"bb");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
7
spec/test_parser_rule_user_code.rs
Normal file
7
spec/test_parser_rule_user_code.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"ab");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
@ -28,11 +28,10 @@ int main()
|
|||||||
assert_eq(11, context->alias_a_value);
|
assert_eq(11, context->alias_a_value);
|
||||||
assert_eq(22, context->alias_b_value);
|
assert_eq(22, context->alias_b_value);
|
||||||
|
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert(start->pA != NULL);
|
assert(p_node_valid(p_Start_pA(start)));
|
||||||
assert(start->pB != NULL);
|
assert(p_node_valid(p_Start_pB(start)));
|
||||||
assert(start->pC == NULL);
|
assert(!p_node_valid(p_Start_pC(start)));
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -31,9 +31,9 @@ unittest
|
|||||||
assert_eq(11, context.alias_a_value);
|
assert_eq(11, context.alias_a_value);
|
||||||
assert_eq(22, context.alias_b_value);
|
assert_eq(22, context.alias_b_value);
|
||||||
|
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert(start.pA !is null);
|
assert(start.pA.valid);
|
||||||
assert(start.pB !is null);
|
assert(start.pB.valid);
|
||||||
assert(start.pC is null);
|
assert(!start.pC.valid);
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
24
spec/test_parser_user_code_tree.rs
Normal file
24
spec/test_parser_user_code_tree.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"ab");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
|
||||||
|
assert_eq!(3, context.start_n_fields);
|
||||||
|
assert_eq!(11, context.start_a_value);
|
||||||
|
assert_eq!(11, context.a_value);
|
||||||
|
assert_eq!(22, context.b_value);
|
||||||
|
assert_eq!(TOKEN_b, context.b_token);
|
||||||
|
assert_eq!(1, context.c_is_null);
|
||||||
|
assert_eq!(1, context.c_field_is_null);
|
||||||
|
assert_eq!(11, context.alias_a_value);
|
||||||
|
assert_eq!(22, context.alias_b_value);
|
||||||
|
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
assert!(start.pA().valid());
|
||||||
|
assert!(start.pB().valid());
|
||||||
|
assert!(!start.pC().valid());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
31
spec/test_parsing_json.rs
Normal file
31
spec/test_parsing_json.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"{}");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
assert_eq!(JSON_OBJECT, p_result(&c).id());
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"[]");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
assert_eq!(JSON_ARRAY, p_result(&c).id());
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"-45.6");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
assert_eq!(JSON_NUMBER, p_result(&c).id());
|
||||||
|
assert_eq!(-45.6, p_result(&c).number());
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"{\"hi\":true}");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
assert_eq!(JSON_OBJECT, p_result(&c).id());
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"[1, 2, \"three\", [4, 5], {\"six\": 6}]");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
let v = p_result(&c);
|
||||||
|
assert_eq!(JSON_ARRAY, v.id());
|
||||||
|
assert_eq!(5, v.array_len());
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
11
spec/test_parsing_lists.rs
Normal file
11
spec/test_parsing_lists.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let cases: [(&[u8], u32); 3] = [(b"a", 1), (b"", 0), (b"aaaaaaaaaaaaaaaa", 16)];
|
||||||
|
for (input, expected) in cases {
|
||||||
|
let mut context = p_context_new(input);
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
assert_eq!(expected, p_result(&context));
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
spec/test_pattern.rs
Normal file
13
spec/test_pattern.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"abcdef");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
println!("pass1");
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"defabcdef");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
println!("pass2");
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
19
spec/test_positions.rs
Normal file
19
spec/test_positions.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b" Hello\n\n 4200\n");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"\n tok2");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let mut c = p_context_new(b" tok1");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
7
spec/test_return_token_from_pattern.rs
Normal file
7
spec/test_return_token_from_pattern.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"defghidef");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
13
spec/test_rewind.rs
Normal file
13
spec/test_rewind.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
/* "repeat 3 { 10 + 20 } 5 + 5": the body "10 + 20" is expanded three
|
||||||
|
* times (recording 30 each time), followed by "5 + 5" (recording 10). */
|
||||||
|
let mut c = p_context_new(b"repeat 3 { 10 + 20 } 5 + 5");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
|
||||||
|
assert_eq!(vec![30, 30, 30, 10], c.nums);
|
||||||
|
assert_eq!(vec![12, 17, 12, 17, 12, 17, 22, 26], c.num_cols);
|
||||||
|
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
66
spec/test_set_position.rs
Normal file
66
spec/test_set_position.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut ti = p_token_info_t::default();
|
||||||
|
|
||||||
|
/* Baseline: default (1, 1). */
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
let pos = p_position(&c);
|
||||||
|
assert_eq!(1, pos.row);
|
||||||
|
assert_eq!(1, pos.col);
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_a, ti.token);
|
||||||
|
assert_eq!(1, ti.position.row);
|
||||||
|
assert_eq!(1, ti.position.col);
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
/* p_set_position overrides the initial position. */
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
p_set_position(&mut c, p_position_t { row: 5, col: 20 });
|
||||||
|
let pos = p_position(&c);
|
||||||
|
assert_eq!(5, pos.row);
|
||||||
|
assert_eq!(20, pos.col);
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_a, ti.token);
|
||||||
|
assert_eq!(5, ti.position.row);
|
||||||
|
assert_eq!(20, ti.position.col);
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_b, ti.token);
|
||||||
|
assert_eq!(5, ti.position.row);
|
||||||
|
assert_eq!(21, ti.position.col);
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
/* Set position before a full parse. */
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
p_set_position(&mut c, p_position_t { row: 3, col: 7 });
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_Start(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
/* Set position before a failing parse: error position is relative. */
|
||||||
|
let mut c = p_context_new(b"aa");
|
||||||
|
p_set_position(&mut c, p_position_t { row: 10, col: 2 });
|
||||||
|
assert_eq!(P_UNEXPECTED_TOKEN, p_parse_Start(&mut c));
|
||||||
|
let ep = p_position(&c);
|
||||||
|
assert_eq!(10, ep.row);
|
||||||
|
assert_eq!(3, ep.col);
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
/* p_set_input_index rewinds the byte cursor to re-read a section. */
|
||||||
|
let mut c = p_context_new(b"ab");
|
||||||
|
let start_index = p_input_index(&c);
|
||||||
|
let start_position = p_position(&c);
|
||||||
|
assert_eq!(0, start_index);
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_a, ti.token);
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_b, ti.token);
|
||||||
|
assert_eq!(2, p_input_index(&c));
|
||||||
|
p_set_input_index(&mut c, start_index);
|
||||||
|
p_set_position(&mut c, start_position);
|
||||||
|
assert_eq!(0, p_input_index(&c));
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_a, ti.token);
|
||||||
|
assert_eq!(1, ti.position.row);
|
||||||
|
assert_eq!(1, ti.position.col);
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
2
spec/test_start_rule.rs
Normal file
2
spec/test_start_rule.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
fn main() {
|
||||||
|
}
|
||||||
@ -9,11 +9,10 @@ int main()
|
|||||||
p_context_t * context;
|
p_context_t * context;
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
Top * top = p_result(context);
|
Top top = p_result(context);
|
||||||
assert(top->pToken != NULL);
|
assert(p_node_valid(p_Top_pToken(top)));
|
||||||
assert_eq(TOKEN_hi, top->pToken->token);
|
assert_eq(TOKEN_hi, p_tree_walk_Top(top, pToken, token));
|
||||||
|
|
||||||
p_tree_delete(top);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -13,7 +13,7 @@ unittest
|
|||||||
p_context_t * context;
|
p_context_t * context;
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
Top * top = p_result(context);
|
Top top = p_result(context);
|
||||||
assert(top.pToken !is null);
|
assert(top.pToken.valid);
|
||||||
assert_eq(TOKEN_hi, top.pToken.token);
|
assert_eq(TOKEN_hi, top.pToken.token);
|
||||||
}
|
}
|
||||||
|
|||||||
12
spec/test_start_rule_tree.rs
Normal file
12
spec/test_start_rule_tree.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"hi");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let top = p_result(&context);
|
||||||
|
assert!(top.pToken().valid());
|
||||||
|
assert_eq!(TOKEN_hi, top.pToken().token());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
18
spec/test_starting_rules.rs
Normal file
18
spec/test_starting_rules.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"bbbb");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
assert_eq!(8, p_result(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"bbbb");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_Bs(&mut c));
|
||||||
|
assert_eq!(8, p_result_Bs(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"c");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_R(&mut c));
|
||||||
|
assert_eq!(3, p_result_R(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
@ -9,31 +9,28 @@ int main()
|
|||||||
p_context_t * context;
|
p_context_t * context;
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert_not_null(start->bs);
|
assert(p_node_valid(p_Start_bs(start)));
|
||||||
assert_not_null(start->bs->b);
|
assert(p_node_valid(p_tree_walk_Start(start, bs, b)));
|
||||||
assert_not_null(start->bs->bs->b);
|
assert(p_node_valid(p_tree_walk_Start(start, bs, bs, b)));
|
||||||
assert_not_null(start->bs->bs->bs->b);
|
assert(p_node_valid(p_tree_walk_Start(start, bs, bs, bs, b)));
|
||||||
assert_not_null(start->bs->bs->bs->bs->b);
|
assert(p_node_valid(p_tree_walk_Start(start, bs, bs, bs, bs, b)));
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse_Bs(context) == P_SUCCESS);
|
assert(p_parse_Bs(context) == P_SUCCESS);
|
||||||
Bs * bs = p_result_Bs(context);
|
Bs bs = p_result_Bs(context);
|
||||||
assert_not_null(bs->b);
|
assert(p_node_valid(p_Bs_b(bs)));
|
||||||
assert_not_null(bs->bs->b);
|
assert(p_node_valid(p_tree_walk_Bs(bs, bs, b)));
|
||||||
assert_not_null(bs->bs->bs->b);
|
assert(p_node_valid(p_tree_walk_Bs(bs, bs, bs, b)));
|
||||||
assert_not_null(bs->bs->bs->bs->b);
|
assert(p_node_valid(p_tree_walk_Bs(bs, bs, bs, bs, b)));
|
||||||
p_tree_delete_Bs(bs);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "c";
|
input = "c";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse_R(context) == P_SUCCESS);
|
assert(p_parse_R(context) == P_SUCCESS);
|
||||||
R * r = p_result_R(context);
|
R r = p_result_R(context);
|
||||||
assert_not_null(r->c);
|
assert(p_node_valid(p_R_c(r)));
|
||||||
p_tree_delete_R(r);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -12,30 +12,30 @@ unittest
|
|||||||
string input = "bbbb";
|
string input = "bbbb";
|
||||||
p_context_t * context = p_context_new(input);
|
p_context_t * context = p_context_new(input);
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert(start.bs);
|
assert(start.bs.valid);
|
||||||
assert(start.bs.b);
|
assert(start.bs.b.valid);
|
||||||
assert(start.bs.bs.b);
|
assert(start.bs.bs.b.valid);
|
||||||
assert(start.bs.bs.bs.b);
|
assert(start.bs.bs.bs.b.valid);
|
||||||
assert(start.bs.bs.bs.bs.b);
|
assert(start.bs.bs.bs.bs.b.valid);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
assert(p_parse_Bs(context) == P_SUCCESS);
|
assert(p_parse_Bs(context) == P_SUCCESS);
|
||||||
Bs * bs = p_result_Bs(context);
|
Bs bs = p_result_Bs(context);
|
||||||
assert(bs.b);
|
assert(bs.b.valid);
|
||||||
assert(bs.bs.b);
|
assert(bs.bs.b.valid);
|
||||||
assert(bs.bs.bs.b);
|
assert(bs.bs.bs.b.valid);
|
||||||
assert(bs.bs.bs.bs.b);
|
assert(bs.bs.bs.bs.b.valid);
|
||||||
|
|
||||||
p_tree_delete_Bs(bs);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "c";
|
input = "c";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
assert(p_parse_R(context) == P_SUCCESS);
|
assert(p_parse_R(context) == P_SUCCESS);
|
||||||
R * r = p_result_R(context);
|
R r = p_result_R(context);
|
||||||
assert(r.c);
|
assert(r.c.valid);
|
||||||
|
|
||||||
p_tree_delete_R(r);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
34
spec/test_starting_rules_tree.rs
Normal file
34
spec/test_starting_rules_tree.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"bbbb");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
{
|
||||||
|
let start = p_result(&c);
|
||||||
|
assert!(start.bs().valid());
|
||||||
|
assert!(start.bs().b().valid());
|
||||||
|
assert!(start.bs().bs().b().valid());
|
||||||
|
assert!(start.bs().bs().bs().b().valid());
|
||||||
|
assert!(start.bs().bs().bs().bs().b().valid());
|
||||||
|
}
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"bbbb");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_Bs(&mut c));
|
||||||
|
{
|
||||||
|
let bs = p_result_Bs(&c);
|
||||||
|
assert!(bs.b().valid());
|
||||||
|
assert!(bs.bs().b().valid());
|
||||||
|
assert!(bs.bs().bs().b().valid());
|
||||||
|
assert!(bs.bs().bs().bs().b().valid());
|
||||||
|
}
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"c");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse_R(&mut c));
|
||||||
|
{
|
||||||
|
let r = p_result_R(&c);
|
||||||
|
assert!(r.c().valid());
|
||||||
|
}
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
@ -18,29 +18,22 @@ int main()
|
|||||||
p_context_t * context;
|
p_context_t * context;
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert(start->pIDs);
|
IDs ids = p_Start_pIDs(start);
|
||||||
assert(start->pIDs->id);
|
assert(p_node_valid(ids));
|
||||||
#ifdef __cplusplus
|
Token id0 = p_IDs_id(ids);
|
||||||
assert(start->pIDs->id->comments == "# c1\n# c2\n");
|
assert(p_node_valid(id0));
|
||||||
#else
|
assert(p_node_data(id0)->comments);
|
||||||
assert(start->pIDs->id->comments);
|
assert(strcmp(p_node_data(id0)->comments, "# c1\n# c2\n") == 0);
|
||||||
assert(strcmp(start->pIDs->id->comments, "# c1\n# c2\n") == 0);
|
IDs ids2 = p_IDs_pIDs(ids);
|
||||||
#endif
|
assert(p_node_valid(ids2));
|
||||||
assert(start->pIDs->pIDs);
|
Token id1 = p_IDs_id(ids2);
|
||||||
assert(start->pIDs->pIDs->id);
|
assert(p_node_valid(id1));
|
||||||
#ifdef __cplusplus
|
assert(p_node_data(id1)->comments);
|
||||||
assert(start->pIDs->pIDs->id->comments == "# s1\n# s2\n");
|
assert(strcmp(p_node_data(id1)->comments, "# s1\n# s2\n") == 0);
|
||||||
#else
|
|
||||||
assert(start->pIDs->pIDs->id->comments);
|
|
||||||
assert(strcmp(start->pIDs->pIDs->id->comments, "# s1\n# s2\n") == 0);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __cplusplus
|
|
||||||
free(context->comments);
|
free(context->comments);
|
||||||
#endif
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
p_tree_delete(start);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
31
spec/test_token_user_fields.cpp
Normal file
31
spec/test_token_user_fields.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include "testparser.h"
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char const * input =
|
||||||
|
"# c1\n"
|
||||||
|
"# c2\n"
|
||||||
|
"\n"
|
||||||
|
"first\n"
|
||||||
|
"\n \n \n"
|
||||||
|
" # s1\n"
|
||||||
|
" # s2\n"
|
||||||
|
"second\n";
|
||||||
|
p_context_t * context;
|
||||||
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
|
Start start = p_result(context);
|
||||||
|
assert(start.pIDs().valid());
|
||||||
|
assert(start.pIDs().id().valid());
|
||||||
|
assert(start.pIDs().id().data()->comments == "# c1\n# c2\n");
|
||||||
|
assert(start.pIDs().pIDs().valid());
|
||||||
|
assert(start.pIDs().pIDs().id().valid());
|
||||||
|
assert(start.pIDs().pIDs().id().data()->comments == "# s1\n# s2\n");
|
||||||
|
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@ -19,13 +19,13 @@ unittest
|
|||||||
"second\n";
|
"second\n";
|
||||||
p_context_t * context = p_context_new(input);
|
p_context_t * context = p_context_new(input);
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert(start.pIDs);
|
assert(start.pIDs.valid);
|
||||||
assert(start.pIDs.id);
|
assert(start.pIDs.id.valid);
|
||||||
assert(start.pIDs.id.comments == "# c1\n# c2\n");
|
assert(start.pIDs.id.comments == "# c1\n# c2\n");
|
||||||
assert(start.pIDs.pIDs);
|
assert(start.pIDs.pIDs.valid);
|
||||||
assert(start.pIDs.pIDs.id);
|
assert(start.pIDs.pIDs.id.valid);
|
||||||
assert(start.pIDs.pIDs.id.comments == "# s1\n# s2\n");
|
assert(start.pIDs.pIDs.id.comments == "# s1\n# s2\n");
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
17
spec/test_token_user_fields.rs
Normal file
17
spec/test_token_user_fields.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = b"# c1\n# c2\n\nfirst\n\n \n \n # s1\n # s2\nsecond\n";
|
||||||
|
let mut c = p_context_new(input);
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
{
|
||||||
|
let start = p_result(&c);
|
||||||
|
assert!(start.pIDs().valid());
|
||||||
|
assert!(start.pIDs().id().valid());
|
||||||
|
assert_eq!("# c1\n# c2\n", start.pIDs().id().data().comments.as_str());
|
||||||
|
assert!(start.pIDs().pIDs().valid());
|
||||||
|
assert!(start.pIDs().pIDs().id().valid());
|
||||||
|
assert_eq!("# s1\n# s2\n", start.pIDs().pIDs().id().data().comments.as_str());
|
||||||
|
}
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
@ -9,55 +9,52 @@ int main()
|
|||||||
p_context_t * context;
|
p_context_t * context;
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert(start->pItems1 != NULL);
|
assert(p_node_valid(p_Start_pItems1(start)));
|
||||||
assert(start->pItems != NULL);
|
assert(p_node_valid(p_Start_pItems(start)));
|
||||||
Items * items = start->pItems;
|
Items items = p_Start_pItems(start);
|
||||||
assert(items->pItem != NULL);
|
assert(p_node_valid(p_Items_pItem(items)));
|
||||||
assert(items->pItem->pToken1 != NULL);
|
assert(p_node_valid(p_tree_walk_Items(items, pItem, pToken1)));
|
||||||
assert_eq(TOKEN_a, items->pItem->pToken1->token);
|
assert_eq(TOKEN_a, p_tree_walk_Items(items, pItem, pToken1, token));
|
||||||
assert_eq(11, items->pItem->pToken1->pvalue);
|
assert_eq(11, p_tree_walk_Items(items, pItem, pToken1, pvalue));
|
||||||
assert(items->pItemsMore != NULL);
|
assert(p_node_valid(p_Items_pItemsMore(items)));
|
||||||
ItemsMore * itemsmore = items->pItemsMore;
|
ItemsMore itemsmore = p_Items_pItemsMore(items);
|
||||||
assert(itemsmore->pItem != NULL);
|
assert(p_node_valid(p_ItemsMore_pItem(itemsmore)));
|
||||||
assert(itemsmore->pItem->pItem != NULL);
|
assert(p_node_valid(p_tree_walk_ItemsMore(itemsmore, pItem, pItem)));
|
||||||
assert(itemsmore->pItem->pItem->pItem != NULL);
|
assert(p_node_valid(p_tree_walk_ItemsMore(itemsmore, pItem, pItem, pItem)));
|
||||||
assert(itemsmore->pItem->pItem->pItem->pToken1 != NULL);
|
assert(p_node_valid(p_tree_walk_ItemsMore(itemsmore, pItem, pItem, pItem, pToken1)));
|
||||||
assert_eq(TOKEN_b, itemsmore->pItem->pItem->pItem->pToken1->token);
|
assert_eq(TOKEN_b, p_tree_walk_ItemsMore(itemsmore, pItem, pItem, pItem, pToken1, token));
|
||||||
assert_eq(22, itemsmore->pItem->pItem->pItem->pToken1->pvalue);
|
assert_eq(22, p_tree_walk_ItemsMore(itemsmore, pItem, pItem, pItem, pToken1, pvalue));
|
||||||
assert(itemsmore->pItemsMore != NULL);
|
assert(p_node_valid(p_ItemsMore_pItemsMore(itemsmore)));
|
||||||
itemsmore = itemsmore->pItemsMore;
|
itemsmore = p_ItemsMore_pItemsMore(itemsmore);
|
||||||
assert(itemsmore->pItem != NULL);
|
assert(p_node_valid(p_ItemsMore_pItem(itemsmore)));
|
||||||
assert(itemsmore->pItem->pToken1 != NULL);
|
assert(p_node_valid(p_tree_walk_ItemsMore(itemsmore, pItem, pToken1)));
|
||||||
assert_eq(TOKEN_b, itemsmore->pItem->pToken1->token);
|
assert_eq(TOKEN_b, p_tree_walk_ItemsMore(itemsmore, pItem, pToken1, token));
|
||||||
assert_eq(22, itemsmore->pItem->pToken1->pvalue);
|
assert_eq(22, p_tree_walk_ItemsMore(itemsmore, pItem, pToken1, pvalue));
|
||||||
assert(itemsmore->pItemsMore == NULL);
|
assert(!p_node_valid(p_ItemsMore_pItemsMore(itemsmore)));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "";
|
input = "";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start->pItems == NULL);
|
assert(!p_node_valid(p_Start_pItems(start)));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "2 1";
|
input = "2 1";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start->pItems != NULL);
|
assert(p_node_valid(p_Start_pItems(start)));
|
||||||
assert(start->pItems->pItem != NULL);
|
assert(p_node_valid(p_tree_walk_Start(start, pItems, pItem)));
|
||||||
assert(start->pItems->pItem->pDual != NULL);
|
assert(p_node_valid(p_tree_walk_Start(start, pItems, pItem, pDual)));
|
||||||
assert(start->pItems->pItem->pDual->pTwo1 != NULL);
|
assert(p_node_valid(p_tree_walk_Start(start, pItems, pItem, pDual, pTwo1)));
|
||||||
assert(start->pItems->pItem->pDual->pOne2 != NULL);
|
assert(p_node_valid(p_tree_walk_Start(start, pItems, pItem, pDual, pOne2)));
|
||||||
assert(start->pItems->pItem->pDual->pTwo2 == NULL);
|
assert(!p_node_valid(p_tree_walk_Start(start, pItems, pItem, pDual, pTwo2)));
|
||||||
assert(start->pItems->pItem->pDual->pOne1 == NULL);
|
assert(!p_node_valid(p_tree_walk_Start(start, pItems, pItem, pDual, pOne1)));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
61
spec/test_tree.cpp
Normal file
61
spec/test_tree.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include "testparser.h"
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char const * input = "a, ((b)), b";
|
||||||
|
p_context_t * context;
|
||||||
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
|
Start start = p_result(context);
|
||||||
|
assert(start.pItems1().valid());
|
||||||
|
assert(start.pItems().valid());
|
||||||
|
Items items = start.pItems();
|
||||||
|
assert(items.pItem().valid());
|
||||||
|
assert(items.pItem().pToken1().valid());
|
||||||
|
assert_eq(TOKEN_a, items.pItem().pToken1().token());
|
||||||
|
assert_eq(11, items.pItem().pToken1().pvalue());
|
||||||
|
assert(items.pItemsMore().valid());
|
||||||
|
ItemsMore itemsmore = items.pItemsMore();
|
||||||
|
assert(itemsmore.pItem().valid());
|
||||||
|
assert(itemsmore.pItem().pItem().valid());
|
||||||
|
assert(itemsmore.pItem().pItem().pItem().valid());
|
||||||
|
assert(itemsmore.pItem().pItem().pItem().pToken1().valid());
|
||||||
|
assert_eq(TOKEN_b, itemsmore.pItem().pItem().pItem().pToken1().token());
|
||||||
|
assert_eq(22, itemsmore.pItem().pItem().pItem().pToken1().pvalue());
|
||||||
|
assert(itemsmore.pItemsMore().valid());
|
||||||
|
itemsmore = itemsmore.pItemsMore();
|
||||||
|
assert(itemsmore.pItem().valid());
|
||||||
|
assert(itemsmore.pItem().pToken1().valid());
|
||||||
|
assert_eq(TOKEN_b, itemsmore.pItem().pToken1().token());
|
||||||
|
assert_eq(22, itemsmore.pItem().pToken1().pvalue());
|
||||||
|
assert(!itemsmore.pItemsMore().valid());
|
||||||
|
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
input = "";
|
||||||
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
|
start = p_result(context);
|
||||||
|
assert(!start.pItems().valid());
|
||||||
|
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
input = "2 1";
|
||||||
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
|
start = p_result(context);
|
||||||
|
assert(start.pItems().valid());
|
||||||
|
assert(start.pItems().pItem().valid());
|
||||||
|
assert(start.pItems().pItem().pDual().valid());
|
||||||
|
assert(start.pItems().pItem().pDual().pTwo1().valid());
|
||||||
|
assert(start.pItems().pItem().pDual().pOne2().valid());
|
||||||
|
assert(!start.pItems().pItem().pDual().pTwo2().valid());
|
||||||
|
assert(!start.pItems().pItem().pDual().pOne1().valid());
|
||||||
|
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@ -12,51 +12,51 @@ unittest
|
|||||||
string input = "a, ((b)), b";
|
string input = "a, ((b)), b";
|
||||||
p_context_t * context = p_context_new(input);
|
p_context_t * context = p_context_new(input);
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert(start.pItems1 !is null);
|
assert(start.pItems1.valid);
|
||||||
assert(start.pItems !is null);
|
assert(start.pItems.valid);
|
||||||
Items * items = start.pItems;
|
Items items = start.pItems;
|
||||||
assert(items.pItem !is null);
|
assert(items.pItem.valid);
|
||||||
assert(items.pItem.pToken1 !is null);
|
assert(items.pItem.pToken1.valid);
|
||||||
assert_eq(TOKEN_a, items.pItem.pToken1.token);
|
assert_eq(TOKEN_a, items.pItem.pToken1.token);
|
||||||
assert_eq(11, items.pItem.pToken1.pvalue);
|
assert_eq(11, items.pItem.pToken1.pvalue);
|
||||||
assert(items.pItemsMore !is null);
|
assert(items.pItemsMore.valid);
|
||||||
ItemsMore * itemsmore = items.pItemsMore;
|
ItemsMore itemsmore = items.pItemsMore;
|
||||||
assert(itemsmore.pItem !is null);
|
assert(itemsmore.pItem.valid);
|
||||||
assert(itemsmore.pItem.pItem !is null);
|
assert(itemsmore.pItem.pItem.valid);
|
||||||
assert(itemsmore.pItem.pItem.pItem !is null);
|
assert(itemsmore.pItem.pItem.pItem.valid);
|
||||||
assert(itemsmore.pItem.pItem.pItem.pToken1 !is null);
|
assert(itemsmore.pItem.pItem.pItem.pToken1.valid);
|
||||||
assert_eq(TOKEN_b, itemsmore.pItem.pItem.pItem.pToken1.token);
|
assert_eq(TOKEN_b, itemsmore.pItem.pItem.pItem.pToken1.token);
|
||||||
assert_eq(22, itemsmore.pItem.pItem.pItem.pToken1.pvalue);
|
assert_eq(22, itemsmore.pItem.pItem.pItem.pToken1.pvalue);
|
||||||
assert(itemsmore.pItemsMore !is null);
|
assert(itemsmore.pItemsMore.valid);
|
||||||
itemsmore = itemsmore.pItemsMore;
|
itemsmore = itemsmore.pItemsMore;
|
||||||
assert(itemsmore.pItem !is null);
|
assert(itemsmore.pItem.valid);
|
||||||
assert(itemsmore.pItem.pToken1 !is null);
|
assert(itemsmore.pItem.pToken1.valid);
|
||||||
assert_eq(TOKEN_b, itemsmore.pItem.pToken1.token);
|
assert_eq(TOKEN_b, itemsmore.pItem.pToken1.token);
|
||||||
assert_eq(22, itemsmore.pItem.pToken1.pvalue);
|
assert_eq(22, itemsmore.pItem.pToken1.pvalue);
|
||||||
assert(itemsmore.pItemsMore is null);
|
assert(!itemsmore.pItemsMore.valid);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "";
|
input = "";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start.pItems is null);
|
assert(!start.pItems.valid);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "2 1";
|
input = "2 1";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start.pItems !is null);
|
assert(start.pItems.valid);
|
||||||
assert(start.pItems.pItem !is null);
|
assert(start.pItems.pItem.valid);
|
||||||
assert(start.pItems.pItem.pDual !is null);
|
assert(start.pItems.pItem.pDual.valid);
|
||||||
assert(start.pItems.pItem.pDual.pTwo1 !is null);
|
assert(start.pItems.pItem.pDual.pTwo1.valid);
|
||||||
assert(start.pItems.pItem.pDual.pOne2 !is null);
|
assert(start.pItems.pItem.pDual.pOne2.valid);
|
||||||
assert(start.pItems.pItem.pDual.pTwo2 is null);
|
assert(!start.pItems.pItem.pDual.pTwo2.valid);
|
||||||
assert(start.pItems.pItem.pDual.pOne1 is null);
|
assert(!start.pItems.pItem.pDual.pOne1.valid);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
46
spec/test_tree.rs
Normal file
46
spec/test_tree.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = b"a, ((b)), b";
|
||||||
|
let mut context = p_context_new(input);
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
assert!(start.pItems1().valid());
|
||||||
|
assert!(start.pItems().valid());
|
||||||
|
let items = start.pItems();
|
||||||
|
assert!(items.pItem().valid());
|
||||||
|
assert!(items.pItem().pToken1().valid());
|
||||||
|
assert_eq!(TOKEN_a, items.pItem().pToken1().token());
|
||||||
|
assert_eq!(11, items.pItem().pToken1().pvalue());
|
||||||
|
let itemsmore = items.pItemsMore();
|
||||||
|
assert!(itemsmore.pItem().pItem().pItem().pToken1().valid());
|
||||||
|
assert_eq!(TOKEN_b, itemsmore.pItem().pItem().pItem().pToken1().token());
|
||||||
|
assert_eq!(22, itemsmore.pItem().pItem().pItem().pToken1().pvalue());
|
||||||
|
assert!(itemsmore.pItemsMore().valid());
|
||||||
|
let itemsmore = itemsmore.pItemsMore();
|
||||||
|
assert_eq!(TOKEN_b, itemsmore.pItem().pToken1().token());
|
||||||
|
assert!(!itemsmore.pItemsMore().valid());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
/* Empty input yields a Start node with no Items child. */
|
||||||
|
let mut context = p_context_new(b"");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
assert!(!p_result(&context).pItems().valid());
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
/* Dual rule alternative field positions. */
|
||||||
|
let mut context = p_context_new(b"2 1");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
assert!(start.pItems().pItem().pDual().pTwo1().valid());
|
||||||
|
assert!(start.pItems().pItem().pDual().pOne2().valid());
|
||||||
|
assert!(!start.pItems().pItem().pDual().pTwo2().valid());
|
||||||
|
assert!(!start.pItems().pItem().pDual().pOne1().valid());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
println!("ok");
|
||||||
|
}
|
||||||
@ -9,12 +9,11 @@ int main()
|
|||||||
p_context_t * context;
|
p_context_t * context;
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
assert(start->a != NULL);
|
assert(p_node_valid(p_Start_a(start)));
|
||||||
assert(*start->a->pvalue == 1);
|
assert(*p_tree_walk_Start(start, a, pvalue) == 1);
|
||||||
assert(start->b != NULL);
|
assert(p_node_valid(p_Start_b(start)));
|
||||||
assert(*start->b->pvalue == 2);
|
assert(*p_tree_walk_Start(start, b, pvalue) == 2);
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,13 +9,12 @@ int main()
|
|||||||
p_context_t * context;
|
p_context_t * context;
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
|
|
||||||
assert_eq(TOKEN_a, start->first->pToken->token);
|
assert_eq(TOKEN_a, p_tree_walk_Start(start, first, pToken, token));
|
||||||
assert_eq(TOKEN_b, start->second->pToken->token);
|
assert_eq(TOKEN_b, p_tree_walk_Start(start, second, pToken, token));
|
||||||
assert_eq(TOKEN_c, start->third->pToken->token);
|
assert_eq(TOKEN_c, p_tree_walk_Start(start, third, pToken, token));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -12,11 +12,11 @@ unittest
|
|||||||
string input = "\na\nb\nc";
|
string input = "\na\nb\nc";
|
||||||
p_context_t * context = p_context_new(input);
|
p_context_t * context = p_context_new(input);
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
|
|
||||||
assert_eq(TOKEN_a, start.first.pToken.token);
|
assert_eq(TOKEN_a, start.first.pToken.token);
|
||||||
assert_eq(TOKEN_b, start.second.pToken.token);
|
assert_eq(TOKEN_b, start.second.pToken.token);
|
||||||
assert_eq(TOKEN_c, start.third.pToken.token);
|
assert_eq(TOKEN_c, start.third.pToken.token);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
13
spec/test_tree_field_aliases.rs
Normal file
13
spec/test_tree_field_aliases.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"\na\nb\nc");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
assert_eq!(TOKEN_a, start.first().pToken().token());
|
||||||
|
assert_eq!(TOKEN_b, start.second().pToken().token());
|
||||||
|
assert_eq!(TOKEN_c, start.third().pToken().token());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
@ -9,105 +9,113 @@ int main()
|
|||||||
p_context_t * context;
|
p_context_t * context;
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
|
T t1 = p_Start_pT1(start);
|
||||||
|
Token k1 = p_T_pToken(t1);
|
||||||
|
A a1 = p_T_pA(t1);
|
||||||
|
|
||||||
assert_eq(2, start->pT1->pToken->position.row);
|
assert_eq(2, p_node_position(k1).row);
|
||||||
assert_eq(1, start->pT1->pToken->position.col);
|
assert_eq(1, p_node_position(k1).col);
|
||||||
assert_eq(2, start->pT1->pToken->end_position.row);
|
assert_eq(2, p_node_end_position(k1).row);
|
||||||
assert_eq(1, start->pT1->pToken->end_position.col);
|
assert_eq(1, p_node_end_position(k1).col);
|
||||||
assert(p_position_valid(start->pT1->pA->position));
|
assert(p_position_valid(p_node_position(a1)));
|
||||||
assert_eq(3, start->pT1->pA->position.row);
|
assert_eq(3, p_node_position(a1).row);
|
||||||
assert_eq(3, start->pT1->pA->position.col);
|
assert_eq(3, p_node_position(a1).col);
|
||||||
assert_eq(3, start->pT1->pA->end_position.row);
|
assert_eq(3, p_node_end_position(a1).row);
|
||||||
assert_eq(8, start->pT1->pA->end_position.col);
|
assert_eq(8, p_node_end_position(a1).col);
|
||||||
assert_eq(2, start->pT1->position.row);
|
assert_eq(2, p_node_position(t1).row);
|
||||||
assert_eq(1, start->pT1->position.col);
|
assert_eq(1, p_node_position(t1).col);
|
||||||
assert_eq(3, start->pT1->end_position.row);
|
assert_eq(3, p_node_end_position(t1).row);
|
||||||
assert_eq(8, start->pT1->end_position.col);
|
assert_eq(8, p_node_end_position(t1).col);
|
||||||
|
|
||||||
assert_eq(2, start->position.row);
|
assert_eq(2, p_node_position(start).row);
|
||||||
assert_eq(1, start->position.col);
|
assert_eq(1, p_node_position(start).col);
|
||||||
assert_eq(3, start->end_position.row);
|
assert_eq(3, p_node_end_position(start).row);
|
||||||
assert_eq(8, start->end_position.col);
|
assert_eq(8, p_node_end_position(start).col);
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "a\nbb";
|
input = "a\nbb";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
|
t1 = p_Start_pT1(start);
|
||||||
|
k1 = p_T_pToken(t1);
|
||||||
|
a1 = p_T_pA(t1);
|
||||||
|
|
||||||
assert_eq(1, start->pT1->pToken->position.row);
|
assert_eq(1, p_node_position(k1).row);
|
||||||
assert_eq(1, start->pT1->pToken->position.col);
|
assert_eq(1, p_node_position(k1).col);
|
||||||
assert_eq(1, start->pT1->pToken->end_position.row);
|
assert_eq(1, p_node_end_position(k1).row);
|
||||||
assert_eq(1, start->pT1->pToken->end_position.col);
|
assert_eq(1, p_node_end_position(k1).col);
|
||||||
assert(p_position_valid(start->pT1->pA->position));
|
assert(p_position_valid(p_node_position(a1)));
|
||||||
assert_eq(2, start->pT1->pA->position.row);
|
assert_eq(2, p_node_position(a1).row);
|
||||||
assert_eq(1, start->pT1->pA->position.col);
|
assert_eq(1, p_node_position(a1).col);
|
||||||
assert_eq(2, start->pT1->pA->end_position.row);
|
assert_eq(2, p_node_end_position(a1).row);
|
||||||
assert_eq(2, start->pT1->pA->end_position.col);
|
assert_eq(2, p_node_end_position(a1).col);
|
||||||
assert_eq(1, start->pT1->position.row);
|
assert_eq(1, p_node_position(t1).row);
|
||||||
assert_eq(1, start->pT1->position.col);
|
assert_eq(1, p_node_position(t1).col);
|
||||||
assert_eq(2, start->pT1->end_position.row);
|
assert_eq(2, p_node_end_position(t1).row);
|
||||||
assert_eq(2, start->pT1->end_position.col);
|
assert_eq(2, p_node_end_position(t1).col);
|
||||||
|
|
||||||
assert_eq(1, start->position.row);
|
assert_eq(1, p_node_position(start).row);
|
||||||
assert_eq(1, start->position.col);
|
assert_eq(1, p_node_position(start).col);
|
||||||
assert_eq(2, start->end_position.row);
|
assert_eq(2, p_node_end_position(start).row);
|
||||||
assert_eq(2, start->end_position.col);
|
assert_eq(2, p_node_end_position(start).col);
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "a\nc\nc";
|
input = "a\nc\nc";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
|
t1 = p_Start_pT1(start);
|
||||||
|
k1 = p_T_pToken(t1);
|
||||||
|
a1 = p_T_pA(t1);
|
||||||
|
|
||||||
assert_eq(1, start->pT1->pToken->position.row);
|
assert_eq(1, p_node_position(k1).row);
|
||||||
assert_eq(1, start->pT1->pToken->position.col);
|
assert_eq(1, p_node_position(k1).col);
|
||||||
assert_eq(1, start->pT1->pToken->end_position.row);
|
assert_eq(1, p_node_end_position(k1).row);
|
||||||
assert_eq(1, start->pT1->pToken->end_position.col);
|
assert_eq(1, p_node_end_position(k1).col);
|
||||||
assert(p_position_valid(start->pT1->pA->position));
|
assert(p_position_valid(p_node_position(a1)));
|
||||||
assert_eq(2, start->pT1->pA->position.row);
|
assert_eq(2, p_node_position(a1).row);
|
||||||
assert_eq(1, start->pT1->pA->position.col);
|
assert_eq(1, p_node_position(a1).col);
|
||||||
assert_eq(3, start->pT1->pA->end_position.row);
|
assert_eq(3, p_node_end_position(a1).row);
|
||||||
assert_eq(1, start->pT1->pA->end_position.col);
|
assert_eq(1, p_node_end_position(a1).col);
|
||||||
assert_eq(1, start->pT1->position.row);
|
assert_eq(1, p_node_position(t1).row);
|
||||||
assert_eq(1, start->pT1->position.col);
|
assert_eq(1, p_node_position(t1).col);
|
||||||
assert_eq(3, start->pT1->end_position.row);
|
assert_eq(3, p_node_end_position(t1).row);
|
||||||
assert_eq(1, start->pT1->end_position.col);
|
assert_eq(1, p_node_end_position(t1).col);
|
||||||
|
|
||||||
assert_eq(1, start->position.row);
|
assert_eq(1, p_node_position(start).row);
|
||||||
assert_eq(1, start->position.col);
|
assert_eq(1, p_node_position(start).col);
|
||||||
assert_eq(3, start->end_position.row);
|
assert_eq(3, p_node_end_position(start).row);
|
||||||
assert_eq(1, start->end_position.col);
|
assert_eq(1, p_node_end_position(start).col);
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "a";
|
input = "a";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
|
t1 = p_Start_pT1(start);
|
||||||
|
k1 = p_T_pToken(t1);
|
||||||
|
a1 = p_T_pA(t1);
|
||||||
|
|
||||||
assert_eq(1, start->pT1->pToken->position.row);
|
assert_eq(1, p_node_position(k1).row);
|
||||||
assert_eq(1, start->pT1->pToken->position.col);
|
assert_eq(1, p_node_position(k1).col);
|
||||||
assert_eq(1, start->pT1->pToken->end_position.row);
|
assert_eq(1, p_node_end_position(k1).row);
|
||||||
assert_eq(1, start->pT1->pToken->end_position.col);
|
assert_eq(1, p_node_end_position(k1).col);
|
||||||
assert(!p_position_valid(start->pT1->pA->position));
|
assert(!p_position_valid(p_node_position(a1)));
|
||||||
assert_eq(1, start->pT1->position.row);
|
assert_eq(1, p_node_position(t1).row);
|
||||||
assert_eq(1, start->pT1->position.col);
|
assert_eq(1, p_node_position(t1).col);
|
||||||
assert_eq(1, start->pT1->end_position.row);
|
assert_eq(1, p_node_end_position(t1).row);
|
||||||
assert_eq(1, start->pT1->end_position.col);
|
assert_eq(1, p_node_end_position(t1).col);
|
||||||
|
|
||||||
assert_eq(1, start->position.row);
|
assert_eq(1, p_node_position(start).row);
|
||||||
assert_eq(1, start->position.col);
|
assert_eq(1, p_node_position(start).col);
|
||||||
assert_eq(1, start->end_position.row);
|
assert_eq(1, p_node_end_position(start).row);
|
||||||
assert_eq(1, start->end_position.col);
|
assert_eq(1, p_node_end_position(start).col);
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -12,7 +12,7 @@ unittest
|
|||||||
string input = "\na\n bb ccc";
|
string input = "\na\n bb ccc";
|
||||||
p_context_t * context = p_context_new(input);
|
p_context_t * context = p_context_new(input);
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
|
|
||||||
assert_eq(2, start.pT1.pToken.position.row);
|
assert_eq(2, start.pT1.pToken.position.row);
|
||||||
assert_eq(1, start.pT1.pToken.position.col);
|
assert_eq(1, start.pT1.pToken.position.col);
|
||||||
@ -33,7 +33,7 @@ unittest
|
|||||||
assert_eq(3, start.end_position.row);
|
assert_eq(3, start.end_position.row);
|
||||||
assert_eq(8, start.end_position.col);
|
assert_eq(8, start.end_position.col);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "a\nbb";
|
input = "a\nbb";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
@ -59,7 +59,7 @@ unittest
|
|||||||
assert_eq(2, start.end_position.row);
|
assert_eq(2, start.end_position.row);
|
||||||
assert_eq(2, start.end_position.col);
|
assert_eq(2, start.end_position.col);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "a\nc\nc";
|
input = "a\nc\nc";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
@ -85,7 +85,7 @@ unittest
|
|||||||
assert_eq(3, start.end_position.row);
|
assert_eq(3, start.end_position.row);
|
||||||
assert_eq(1, start.end_position.col);
|
assert_eq(1, start.end_position.col);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "a";
|
input = "a";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
@ -107,5 +107,5 @@ unittest
|
|||||||
assert_eq(1, start.end_position.row);
|
assert_eq(1, start.end_position.row);
|
||||||
assert_eq(1, start.end_position.col);
|
assert_eq(1, start.end_position.col);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
89
spec/test_tree_invalid_positions.rs
Normal file
89
spec/test_tree_invalid_positions.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"\na\n bb ccc");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
let t1 = start.pT1();
|
||||||
|
let k1 = t1.pToken();
|
||||||
|
let a1 = t1.pA();
|
||||||
|
assert_eq!(2, k1.position().row);
|
||||||
|
assert_eq!(1, k1.position().col);
|
||||||
|
assert_eq!(2, k1.end_position().row);
|
||||||
|
assert_eq!(1, k1.end_position().col);
|
||||||
|
assert!(a1.position().valid());
|
||||||
|
assert_eq!(3, a1.position().row);
|
||||||
|
assert_eq!(3, a1.position().col);
|
||||||
|
assert_eq!(3, a1.end_position().row);
|
||||||
|
assert_eq!(8, a1.end_position().col);
|
||||||
|
assert_eq!(2, t1.position().row);
|
||||||
|
assert_eq!(1, t1.position().col);
|
||||||
|
assert_eq!(3, t1.end_position().row);
|
||||||
|
assert_eq!(8, t1.end_position().col);
|
||||||
|
assert_eq!(2, start.position().row);
|
||||||
|
assert_eq!(1, start.position().col);
|
||||||
|
assert_eq!(3, start.end_position().row);
|
||||||
|
assert_eq!(8, start.end_position().col);
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"a\nbb");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
let t1 = start.pT1();
|
||||||
|
let k1 = t1.pToken();
|
||||||
|
let a1 = t1.pA();
|
||||||
|
assert_eq!(1, k1.position().row);
|
||||||
|
assert_eq!(1, k1.position().col);
|
||||||
|
assert!(a1.position().valid());
|
||||||
|
assert_eq!(2, a1.position().row);
|
||||||
|
assert_eq!(1, a1.position().col);
|
||||||
|
assert_eq!(2, a1.end_position().row);
|
||||||
|
assert_eq!(2, a1.end_position().col);
|
||||||
|
assert_eq!(1, t1.position().row);
|
||||||
|
assert_eq!(2, t1.end_position().row);
|
||||||
|
assert_eq!(2, t1.end_position().col);
|
||||||
|
assert_eq!(1, start.position().row);
|
||||||
|
assert_eq!(2, start.end_position().row);
|
||||||
|
assert_eq!(2, start.end_position().col);
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"a\nc\nc");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
let t1 = start.pT1();
|
||||||
|
let a1 = t1.pA();
|
||||||
|
assert!(a1.position().valid());
|
||||||
|
assert_eq!(2, a1.position().row);
|
||||||
|
assert_eq!(1, a1.position().col);
|
||||||
|
assert_eq!(3, a1.end_position().row);
|
||||||
|
assert_eq!(1, a1.end_position().col);
|
||||||
|
assert_eq!(1, t1.position().row);
|
||||||
|
assert_eq!(3, t1.end_position().row);
|
||||||
|
assert_eq!(1, t1.end_position().col);
|
||||||
|
assert_eq!(3, start.end_position().row);
|
||||||
|
assert_eq!(1, start.end_position().col);
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"a");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
let t1 = start.pT1();
|
||||||
|
let a1 = t1.pA();
|
||||||
|
assert!(!a1.position().valid());
|
||||||
|
assert_eq!(1, t1.position().row);
|
||||||
|
assert_eq!(1, t1.position().col);
|
||||||
|
assert_eq!(1, t1.end_position().row);
|
||||||
|
assert_eq!(1, t1.end_position().col);
|
||||||
|
assert_eq!(1, start.position().row);
|
||||||
|
assert_eq!(1, start.end_position().row);
|
||||||
|
assert_eq!(1, start.end_position().col);
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
@ -373,46 +373,45 @@ int main(int argc, char * argv[])
|
|||||||
context = p_context_new((const uint8_t *)input, strlen(input));
|
context = p_context_new((const uint8_t *)input, strlen(input));
|
||||||
size_t result = p_parse(context);
|
size_t result = p_parse(context);
|
||||||
assert_eq(P_SUCCESS, result);
|
assert_eq(P_SUCCESS, result);
|
||||||
PModule * pmod = p_result(context);
|
PModule pmod = p_result(context);
|
||||||
PModuleItems * pmis = pmod->pModuleItems;
|
PModuleItems pmis = p_PModule_pModuleItems(pmod);
|
||||||
PFunctionDefinition ** pfds;
|
PFunctionDefinition * pfds;
|
||||||
size_t n_pfds = 0u;
|
size_t n_pfds = 0u;
|
||||||
while (pmis != NULL)
|
while (p_node_valid(pmis))
|
||||||
{
|
{
|
||||||
PModuleItem * pmi = pmis->pModuleItem;
|
PModuleItem pmi = p_PModuleItems_pModuleItem(pmis);
|
||||||
if (pmi->pFunctionDefinition != NULL)
|
if (p_node_valid(p_PModuleItem_pFunctionDefinition(pmi)))
|
||||||
{
|
{
|
||||||
n_pfds++;
|
n_pfds++;
|
||||||
}
|
}
|
||||||
pmis = pmis->pModuleItems;
|
pmis = p_PModuleItems_pModuleItems(pmis);
|
||||||
}
|
}
|
||||||
pfds = (PFunctionDefinition **)malloc(n_pfds * sizeof(PModuleItems *));
|
pfds = (PFunctionDefinition *)malloc(n_pfds * sizeof(PFunctionDefinition));
|
||||||
pmis = pmod->pModuleItems;
|
pmis = p_PModule_pModuleItems(pmod);
|
||||||
size_t pfd_i = n_pfds;
|
size_t pfd_i = n_pfds;
|
||||||
while (pmis != NULL)
|
while (p_node_valid(pmis))
|
||||||
{
|
{
|
||||||
PModuleItem * pmi = pmis->pModuleItem;
|
PModuleItem pmi = p_PModuleItems_pModuleItem(pmis);
|
||||||
PFunctionDefinition * pfd = pmi->pFunctionDefinition;
|
PFunctionDefinition pfd = p_PModuleItem_pFunctionDefinition(pmi);
|
||||||
if (pfd != NULL)
|
if (p_node_valid(pfd))
|
||||||
{
|
{
|
||||||
pfd_i--;
|
pfd_i--;
|
||||||
assert(pfd_i < n_pfds);
|
assert(pfd_i < n_pfds);
|
||||||
pfds[pfd_i] = pfd;
|
pfds[pfd_i] = pfd;
|
||||||
}
|
}
|
||||||
pmis = pmis->pModuleItems;
|
pmis = p_PModuleItems_pModuleItems(pmis);
|
||||||
}
|
}
|
||||||
assert_eq(51, n_pfds);
|
assert_eq(51, n_pfds);
|
||||||
for (size_t i = 0; i < n_pfds; i++)
|
for (size_t i = 0; i < n_pfds; i++)
|
||||||
{
|
{
|
||||||
if (strncmp(expected[i].name, (const char *)pfds[i]->name->pvalue.s, strlen(expected[i].name)) != 0 ||
|
if (strncmp(expected[i].name, (const char *)p_node_data(p_PFunctionDefinition_name(pfds[i]))->pvalue.s, strlen(expected[i].name)) != 0 ||
|
||||||
(expected[i].token != pfds[i]->returntype->pType->pTypeBase->pToken1->token))
|
(expected[i].token != p_tree_walk_PFunctionDefinition(pfds[i], returntype, pType, pTypeBase, pToken1, token)))
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Index %lu: expected %s/%u, got %u\n", i, expected[i].name, expected[i].token, pfds[i]->returntype->pType->pTypeBase->pToken1->token);
|
fprintf(stderr, "Index %lu: expected %s/%u, got %u\n", i, expected[i].name, expected[i].token, p_tree_walk_PFunctionDefinition(pfds[i], returntype, pType, pTypeBase, pToken1, token));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
free(pfds);
|
free(pfds);
|
||||||
p_tree_delete(pmod);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -378,19 +378,19 @@ def main() -> int
|
|||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
size_t result = p_parse(context);
|
size_t result = p_parse(context);
|
||||||
assert_eq(P_SUCCESS, result);
|
assert_eq(P_SUCCESS, result);
|
||||||
PModule * pmod = p_result(context);
|
PModule pmod = p_result(context);
|
||||||
PModuleItems * pmis = pmod.pModuleItems;
|
PModuleItems pmis = pmod.pModuleItems;
|
||||||
PFunctionDefinition *[] pfds;
|
PFunctionDefinition[] pfds;
|
||||||
while (pmis !is null)
|
while (pmis.valid)
|
||||||
{
|
{
|
||||||
PModuleItem * pmi = pmis.pModuleItem;
|
PModuleItem pmi = pmis.pModuleItem;
|
||||||
if (pmi is null)
|
if (!pmi.valid)
|
||||||
{
|
{
|
||||||
stderr.writeln("pmi is null!!!?");
|
stderr.writeln("pmi is null!!!?");
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
PFunctionDefinition * pfd = pmi.pFunctionDefinition;
|
PFunctionDefinition pfd = pmi.pFunctionDefinition;
|
||||||
if (pfd !is null)
|
if (pfd.valid)
|
||||||
{
|
{
|
||||||
pfds = [pfd] ~ pfds;
|
pfds = [pfd] ~ pfds;
|
||||||
}
|
}
|
||||||
@ -405,5 +405,5 @@ def main() -> int
|
|||||||
stderr.writeln("Index ", i, ": expected ", expected[i].name, "/", expected[i].token, ", got ", pfds[i].name.pvalue.s, "/", pfds[i].returntype.pType.pTypeBase.pToken1.token);
|
stderr.writeln("Index ", i, ": expected ", expected[i].name, "/", expected[i].token, ", got ", pfds[i].name.pvalue.s, "/", pfds[i].returntype.pType.pTypeBase.pToken1.token);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p_tree_delete(pmod);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
85
spec/test_tree_node_memory_remains.rs
Normal file
85
spec/test_tree_node_memory_remains.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let entries: [(&str, &str, p_token_t); 51] = [
|
||||||
|
("byte_val", "byte", TOKEN_byte),
|
||||||
|
("short_val", "short", TOKEN_short),
|
||||||
|
("int_val", "int", TOKEN_int),
|
||||||
|
("long_val", "long", TOKEN_long),
|
||||||
|
("ssize_t_val", "ssize_t", TOKEN_ssize_t),
|
||||||
|
("byte_to_short", "short", TOKEN_short),
|
||||||
|
("byte_to_int", "int", TOKEN_int),
|
||||||
|
("byte_to_long", "long", TOKEN_long),
|
||||||
|
("byte_to_ssize_t", "ssize_t", TOKEN_ssize_t),
|
||||||
|
("short_to_byte", "byte", TOKEN_byte),
|
||||||
|
("short_to_int", "int", TOKEN_int),
|
||||||
|
("short_to_long", "long", TOKEN_long),
|
||||||
|
("short_to_ssize_t", "ssize_t", TOKEN_ssize_t),
|
||||||
|
("int_to_byte", "byte", TOKEN_byte),
|
||||||
|
("int_to_short", "short", TOKEN_short),
|
||||||
|
("int_to_long", "long", TOKEN_long),
|
||||||
|
("int_to_ssize_t", "ssize_t", TOKEN_ssize_t),
|
||||||
|
("long_to_byte", "byte", TOKEN_byte),
|
||||||
|
("long_to_short", "short", TOKEN_short),
|
||||||
|
("long_to_int", "int", TOKEN_int),
|
||||||
|
("long_to_ssize_t", "ssize_t", TOKEN_ssize_t),
|
||||||
|
("ssize_t_to_byte", "byte", TOKEN_byte),
|
||||||
|
("ssize_t_to_short", "short", TOKEN_short),
|
||||||
|
("ssize_t_to_int", "int", TOKEN_int),
|
||||||
|
("ssize_t_to_long", "long", TOKEN_long),
|
||||||
|
("ubyte_val", "ubyte", TOKEN_ubyte),
|
||||||
|
("ushort_val", "ushort", TOKEN_ushort),
|
||||||
|
("uint_val", "uint", TOKEN_uint),
|
||||||
|
("ulong_val", "ulong", TOKEN_ulong),
|
||||||
|
("size_t_val", "size_t", TOKEN_size_t),
|
||||||
|
("ubyte_to_ushort", "ushort", TOKEN_ushort),
|
||||||
|
("ubyte_to_uint", "uint", TOKEN_uint),
|
||||||
|
("ubyte_to_ulong", "ulong", TOKEN_ulong),
|
||||||
|
("ubyte_to_size_t", "size_t", TOKEN_size_t),
|
||||||
|
("ushort_to_ubyte", "ubyte", TOKEN_ubyte),
|
||||||
|
("ushort_to_uint", "uint", TOKEN_uint),
|
||||||
|
("ushort_to_ulong", "ulong", TOKEN_ulong),
|
||||||
|
("ushort_to_size_t", "size_t", TOKEN_size_t),
|
||||||
|
("uint_to_ubyte", "ubyte", TOKEN_ubyte),
|
||||||
|
("uint_to_ushort", "ushort", TOKEN_ushort),
|
||||||
|
("uint_to_ulong", "ulong", TOKEN_ulong),
|
||||||
|
("uint_to_size_t", "size_t", TOKEN_size_t),
|
||||||
|
("ulong_to_ubyte", "ubyte", TOKEN_ubyte),
|
||||||
|
("ulong_to_ushort", "ushort", TOKEN_ushort),
|
||||||
|
("ulong_to_uint", "uint", TOKEN_uint),
|
||||||
|
("ulong_to_size_t", "size_t", TOKEN_size_t),
|
||||||
|
("size_t_to_ubyte", "ubyte", TOKEN_ubyte),
|
||||||
|
("size_t_to_ushort", "ushort", TOKEN_ushort),
|
||||||
|
("size_t_to_int", "int", TOKEN_int),
|
||||||
|
("size_t_to_ulong", "ulong", TOKEN_ulong),
|
||||||
|
("main", "int", TOKEN_int),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut input = String::new();
|
||||||
|
for (name, ret, _) in entries.iter() {
|
||||||
|
input.push_str(&format!("def {}() -> {} {{\nreturn 0x42;\n}}\n", name, ret));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut c = p_context_new(input.as_bytes());
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
{
|
||||||
|
let pmod = p_result(&c);
|
||||||
|
let mut pfds = Vec::new();
|
||||||
|
let mut pmis = pmod.pModuleItems();
|
||||||
|
while pmis.valid() {
|
||||||
|
let pmi = pmis.pModuleItem();
|
||||||
|
assert!(pmi.valid());
|
||||||
|
let pfd = pmi.pFunctionDefinition();
|
||||||
|
if pfd.valid() {
|
||||||
|
pfds.insert(0, pfd);
|
||||||
|
}
|
||||||
|
pmis = pmis.pModuleItems();
|
||||||
|
}
|
||||||
|
assert_eq!(51, pfds.len());
|
||||||
|
for i in 0..pfds.len() {
|
||||||
|
assert_eq!(entries[i].0, pfds[i].name().data().pvalue.s.as_str());
|
||||||
|
assert_eq!(entries[i].2, pfds[i].returntype().pType().pTypeBase().pToken1().token());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
@ -9,55 +9,52 @@ int main()
|
|||||||
p_context_t * context;
|
p_context_t * context;
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
PStartS * start = p_result(context);
|
PStartS start = p_result(context);
|
||||||
assert(start->pItems1 != NULL);
|
assert(p_node_valid(p_PStartS_pItems1(start)));
|
||||||
assert(start->pItems != NULL);
|
assert(p_node_valid(p_PStartS_pItems(start)));
|
||||||
PItemsS * items = start->pItems;
|
PItemsS items = p_PStartS_pItems(start);
|
||||||
assert(items->pItem != NULL);
|
assert(p_node_valid(p_PItemsS_pItem(items)));
|
||||||
assert(items->pItem->pToken1 != NULL);
|
assert(p_node_valid(p_tree_walk_PItemsS(items, pItem, pToken1)));
|
||||||
assert_eq(TOKEN_a, items->pItem->pToken1->token);
|
assert_eq(TOKEN_a, p_tree_walk_PItemsS(items, pItem, pToken1, token));
|
||||||
assert_eq(11, items->pItem->pToken1->pvalue);
|
assert_eq(11, p_tree_walk_PItemsS(items, pItem, pToken1, pvalue));
|
||||||
assert(items->pItemsMore != NULL);
|
assert(p_node_valid(p_PItemsS_pItemsMore(items)));
|
||||||
PItemsMoreS * itemsmore = items->pItemsMore;
|
PItemsMoreS itemsmore = p_PItemsS_pItemsMore(items);
|
||||||
assert(itemsmore->pItem != NULL);
|
assert(p_node_valid(p_PItemsMoreS_pItem(itemsmore)));
|
||||||
assert(itemsmore->pItem->pItem != NULL);
|
assert(p_node_valid(p_tree_walk_PItemsMoreS(itemsmore, pItem, pItem)));
|
||||||
assert(itemsmore->pItem->pItem->pItem != NULL);
|
assert(p_node_valid(p_tree_walk_PItemsMoreS(itemsmore, pItem, pItem, pItem)));
|
||||||
assert(itemsmore->pItem->pItem->pItem->pToken1 != NULL);
|
assert(p_node_valid(p_tree_walk_PItemsMoreS(itemsmore, pItem, pItem, pItem, pToken1)));
|
||||||
assert_eq(TOKEN_b, itemsmore->pItem->pItem->pItem->pToken1->token);
|
assert_eq(TOKEN_b, p_tree_walk_PItemsMoreS(itemsmore, pItem, pItem, pItem, pToken1, token));
|
||||||
assert_eq(22, itemsmore->pItem->pItem->pItem->pToken1->pvalue);
|
assert_eq(22, p_tree_walk_PItemsMoreS(itemsmore, pItem, pItem, pItem, pToken1, pvalue));
|
||||||
assert(itemsmore->pItemsMore != NULL);
|
assert(p_node_valid(p_PItemsMoreS_pItemsMore(itemsmore)));
|
||||||
itemsmore = itemsmore->pItemsMore;
|
itemsmore = p_PItemsMoreS_pItemsMore(itemsmore);
|
||||||
assert(itemsmore->pItem != NULL);
|
assert(p_node_valid(p_PItemsMoreS_pItem(itemsmore)));
|
||||||
assert(itemsmore->pItem->pToken1 != NULL);
|
assert(p_node_valid(p_tree_walk_PItemsMoreS(itemsmore, pItem, pToken1)));
|
||||||
assert_eq(TOKEN_b, itemsmore->pItem->pToken1->token);
|
assert_eq(TOKEN_b, p_tree_walk_PItemsMoreS(itemsmore, pItem, pToken1, token));
|
||||||
assert_eq(22, itemsmore->pItem->pToken1->pvalue);
|
assert_eq(22, p_tree_walk_PItemsMoreS(itemsmore, pItem, pToken1, pvalue));
|
||||||
assert(itemsmore->pItemsMore == NULL);
|
assert(!p_node_valid(p_PItemsMoreS_pItemsMore(itemsmore)));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "";
|
input = "";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start->pItems == NULL);
|
assert(!p_node_valid(p_PStartS_pItems(start)));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "2 1";
|
input = "2 1";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start->pItems != NULL);
|
assert(p_node_valid(p_PStartS_pItems(start)));
|
||||||
assert(start->pItems->pItem != NULL);
|
assert(p_node_valid(p_tree_walk_PStartS(start, pItems, pItem)));
|
||||||
assert(start->pItems->pItem->pDual != NULL);
|
assert(p_node_valid(p_tree_walk_PStartS(start, pItems, pItem, pDual)));
|
||||||
assert(start->pItems->pItem->pDual->pTwo1 != NULL);
|
assert(p_node_valid(p_tree_walk_PStartS(start, pItems, pItem, pDual, pTwo1)));
|
||||||
assert(start->pItems->pItem->pDual->pOne2 != NULL);
|
assert(p_node_valid(p_tree_walk_PStartS(start, pItems, pItem, pDual, pOne2)));
|
||||||
assert(start->pItems->pItem->pDual->pTwo2 == NULL);
|
assert(!p_node_valid(p_tree_walk_PStartS(start, pItems, pItem, pDual, pTwo2)));
|
||||||
assert(start->pItems->pItem->pDual->pOne1 == NULL);
|
assert(!p_node_valid(p_tree_walk_PStartS(start, pItems, pItem, pDual, pOne1)));
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -12,51 +12,51 @@ unittest
|
|||||||
string input = "a, ((b)), b";
|
string input = "a, ((b)), b";
|
||||||
p_context_t * context = p_context_new(input);
|
p_context_t * context = p_context_new(input);
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
PStartS * start = p_result(context);
|
PStartS start = p_result(context);
|
||||||
assert(start.pItems1 !is null);
|
assert(start.pItems1.valid);
|
||||||
assert(start.pItems !is null);
|
assert(start.pItems.valid);
|
||||||
PItemsS * items = start.pItems;
|
PItemsS items = start.pItems;
|
||||||
assert(items.pItem !is null);
|
assert(items.pItem.valid);
|
||||||
assert(items.pItem.pToken1 !is null);
|
assert(items.pItem.pToken1.valid);
|
||||||
assert_eq(TOKEN_a, items.pItem.pToken1.token);
|
assert_eq(TOKEN_a, items.pItem.pToken1.token);
|
||||||
assert_eq(11, items.pItem.pToken1.pvalue);
|
assert_eq(11, items.pItem.pToken1.pvalue);
|
||||||
assert(items.pItemsMore !is null);
|
assert(items.pItemsMore.valid);
|
||||||
PItemsMoreS * itemsmore = items.pItemsMore;
|
PItemsMoreS itemsmore = items.pItemsMore;
|
||||||
assert(itemsmore.pItem !is null);
|
assert(itemsmore.pItem.valid);
|
||||||
assert(itemsmore.pItem.pItem !is null);
|
assert(itemsmore.pItem.pItem.valid);
|
||||||
assert(itemsmore.pItem.pItem.pItem !is null);
|
assert(itemsmore.pItem.pItem.pItem.valid);
|
||||||
assert(itemsmore.pItem.pItem.pItem.pToken1 !is null);
|
assert(itemsmore.pItem.pItem.pItem.pToken1.valid);
|
||||||
assert_eq(TOKEN_b, itemsmore.pItem.pItem.pItem.pToken1.token);
|
assert_eq(TOKEN_b, itemsmore.pItem.pItem.pItem.pToken1.token);
|
||||||
assert_eq(22, itemsmore.pItem.pItem.pItem.pToken1.pvalue);
|
assert_eq(22, itemsmore.pItem.pItem.pItem.pToken1.pvalue);
|
||||||
assert(itemsmore.pItemsMore !is null);
|
assert(itemsmore.pItemsMore.valid);
|
||||||
itemsmore = itemsmore.pItemsMore;
|
itemsmore = itemsmore.pItemsMore;
|
||||||
assert(itemsmore.pItem !is null);
|
assert(itemsmore.pItem.valid);
|
||||||
assert(itemsmore.pItem.pToken1 !is null);
|
assert(itemsmore.pItem.pToken1.valid);
|
||||||
assert_eq(TOKEN_b, itemsmore.pItem.pToken1.token);
|
assert_eq(TOKEN_b, itemsmore.pItem.pToken1.token);
|
||||||
assert_eq(22, itemsmore.pItem.pToken1.pvalue);
|
assert_eq(22, itemsmore.pItem.pToken1.pvalue);
|
||||||
assert(itemsmore.pItemsMore is null);
|
assert(!itemsmore.pItemsMore.valid);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "";
|
input = "";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start.pItems is null);
|
assert(!start.pItems.valid);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "2 1";
|
input = "2 1";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
assert_eq(P_SUCCESS, p_parse(context));
|
assert_eq(P_SUCCESS, p_parse(context));
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
assert(start.pItems !is null);
|
assert(start.pItems.valid);
|
||||||
assert(start.pItems.pItem !is null);
|
assert(start.pItems.pItem.valid);
|
||||||
assert(start.pItems.pItem.pDual !is null);
|
assert(start.pItems.pItem.pDual.valid);
|
||||||
assert(start.pItems.pItem.pDual.pTwo1 !is null);
|
assert(start.pItems.pItem.pDual.pTwo1.valid);
|
||||||
assert(start.pItems.pItem.pDual.pOne2 !is null);
|
assert(start.pItems.pItem.pDual.pOne2.valid);
|
||||||
assert(start.pItems.pItem.pDual.pTwo2 is null);
|
assert(!start.pItems.pItem.pDual.pTwo2.valid);
|
||||||
assert(start.pItems.pItem.pDual.pOne1 is null);
|
assert(!start.pItems.pItem.pDual.pOne1.valid);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
46
spec/test_tree_ps.rs
Normal file
46
spec/test_tree_ps.rs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = b"a, ((b)), b";
|
||||||
|
let mut context = p_context_new(input);
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
assert!(start.pItems1().valid());
|
||||||
|
assert!(start.pItems().valid());
|
||||||
|
let items = start.pItems();
|
||||||
|
assert!(items.pItem().valid());
|
||||||
|
assert!(items.pItem().pToken1().valid());
|
||||||
|
assert_eq!(TOKEN_a, items.pItem().pToken1().token());
|
||||||
|
assert_eq!(11, items.pItem().pToken1().pvalue());
|
||||||
|
let itemsmore = items.pItemsMore();
|
||||||
|
assert!(itemsmore.pItem().pItem().pItem().pToken1().valid());
|
||||||
|
assert_eq!(TOKEN_b, itemsmore.pItem().pItem().pItem().pToken1().token());
|
||||||
|
assert_eq!(22, itemsmore.pItem().pItem().pItem().pToken1().pvalue());
|
||||||
|
assert!(itemsmore.pItemsMore().valid());
|
||||||
|
let itemsmore = itemsmore.pItemsMore();
|
||||||
|
assert_eq!(TOKEN_b, itemsmore.pItem().pToken1().token());
|
||||||
|
assert!(!itemsmore.pItemsMore().valid());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
/* Empty input yields a Start node with no Items child. */
|
||||||
|
let mut context = p_context_new(b"");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
assert!(!p_result(&context).pItems().valid());
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
/* Dual rule alternative field positions. */
|
||||||
|
let mut context = p_context_new(b"2 1");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
assert!(start.pItems().pItem().pDual().pTwo1().valid());
|
||||||
|
assert!(start.pItems().pItem().pDual().pOne2().valid());
|
||||||
|
assert!(!start.pItems().pItem().pDual().pTwo2().valid());
|
||||||
|
assert!(!start.pItems().pItem().pDual().pOne1().valid());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
println!("ok");
|
||||||
|
}
|
||||||
@ -9,81 +9,91 @@ int main()
|
|||||||
p_context_t * context;
|
p_context_t * context;
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
|
T t1 = p_Start_pT1(start);
|
||||||
|
T t2 = p_Start_pT2(start);
|
||||||
|
T t3 = p_Start_pT3(start);
|
||||||
|
Token k1 = p_T_pToken(t1);
|
||||||
|
Token k2 = p_T_pToken(t2);
|
||||||
|
Token k3 = p_T_pToken(t3);
|
||||||
|
|
||||||
assert_eq(1, start->pT1->pToken->position.row);
|
assert_eq(1, p_node_position(k1).row);
|
||||||
assert_eq(1, start->pT1->pToken->position.col);
|
assert_eq(1, p_node_position(k1).col);
|
||||||
assert_eq(1, start->pT1->pToken->end_position.row);
|
assert_eq(1, p_node_end_position(k1).row);
|
||||||
assert_eq(1, start->pT1->pToken->end_position.col);
|
assert_eq(1, p_node_end_position(k1).col);
|
||||||
assert_eq(1, start->pT1->position.row);
|
assert_eq(1, p_node_position(t1).row);
|
||||||
assert_eq(1, start->pT1->position.col);
|
assert_eq(1, p_node_position(t1).col);
|
||||||
assert_eq(1, start->pT1->end_position.row);
|
assert_eq(1, p_node_end_position(t1).row);
|
||||||
assert_eq(1, start->pT1->end_position.col);
|
assert_eq(1, p_node_end_position(t1).col);
|
||||||
|
|
||||||
assert_eq(1, start->pT2->pToken->position.row);
|
assert_eq(1, p_node_position(k2).row);
|
||||||
assert_eq(2, start->pT2->pToken->position.col);
|
assert_eq(2, p_node_position(k2).col);
|
||||||
assert_eq(1, start->pT2->pToken->end_position.row);
|
assert_eq(1, p_node_end_position(k2).row);
|
||||||
assert_eq(3, start->pT2->pToken->end_position.col);
|
assert_eq(3, p_node_end_position(k2).col);
|
||||||
assert_eq(1, start->pT2->position.row);
|
assert_eq(1, p_node_position(t2).row);
|
||||||
assert_eq(2, start->pT2->position.col);
|
assert_eq(2, p_node_position(t2).col);
|
||||||
assert_eq(1, start->pT2->end_position.row);
|
assert_eq(1, p_node_end_position(t2).row);
|
||||||
assert_eq(3, start->pT2->end_position.col);
|
assert_eq(3, p_node_end_position(t2).col);
|
||||||
|
|
||||||
assert_eq(1, start->pT3->pToken->position.row);
|
assert_eq(1, p_node_position(k3).row);
|
||||||
assert_eq(4, start->pT3->pToken->position.col);
|
assert_eq(4, p_node_position(k3).col);
|
||||||
assert_eq(1, start->pT3->pToken->end_position.row);
|
assert_eq(1, p_node_end_position(k3).row);
|
||||||
assert_eq(6, start->pT3->pToken->end_position.col);
|
assert_eq(6, p_node_end_position(k3).col);
|
||||||
assert_eq(1, start->pT3->position.row);
|
assert_eq(1, p_node_position(t3).row);
|
||||||
assert_eq(4, start->pT3->position.col);
|
assert_eq(4, p_node_position(t3).col);
|
||||||
assert_eq(1, start->pT3->end_position.row);
|
assert_eq(1, p_node_end_position(t3).row);
|
||||||
assert_eq(6, start->pT3->end_position.col);
|
assert_eq(6, p_node_end_position(t3).col);
|
||||||
|
|
||||||
assert_eq(1, start->position.row);
|
assert_eq(1, p_node_position(start).row);
|
||||||
assert_eq(1, start->position.col);
|
assert_eq(1, p_node_position(start).col);
|
||||||
assert_eq(1, start->end_position.row);
|
assert_eq(1, p_node_end_position(start).row);
|
||||||
assert_eq(6, start->end_position.col);
|
assert_eq(6, p_node_end_position(start).col);
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "\n\n bb\nc\ncc\n\n a";
|
input = "\n\n bb\nc\ncc\n\n a";
|
||||||
context = p_context_new((uint8_t const *)input, strlen(input));
|
context = p_context_new((uint8_t const *)input, strlen(input));
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
start = p_result(context);
|
start = p_result(context);
|
||||||
|
t1 = p_Start_pT1(start);
|
||||||
|
t2 = p_Start_pT2(start);
|
||||||
|
t3 = p_Start_pT3(start);
|
||||||
|
k1 = p_T_pToken(t1);
|
||||||
|
k2 = p_T_pToken(t2);
|
||||||
|
k3 = p_T_pToken(t3);
|
||||||
|
|
||||||
assert_eq(3, start->pT1->pToken->position.row);
|
assert_eq(3, p_node_position(k1).row);
|
||||||
assert_eq(3, start->pT1->pToken->position.col);
|
assert_eq(3, p_node_position(k1).col);
|
||||||
assert_eq(3, start->pT1->pToken->end_position.row);
|
assert_eq(3, p_node_end_position(k1).row);
|
||||||
assert_eq(4, start->pT1->pToken->end_position.col);
|
assert_eq(4, p_node_end_position(k1).col);
|
||||||
assert_eq(3, start->pT1->position.row);
|
assert_eq(3, p_node_position(t1).row);
|
||||||
assert_eq(3, start->pT1->position.col);
|
assert_eq(3, p_node_position(t1).col);
|
||||||
assert_eq(3, start->pT1->end_position.row);
|
assert_eq(3, p_node_end_position(t1).row);
|
||||||
assert_eq(4, start->pT1->end_position.col);
|
assert_eq(4, p_node_end_position(t1).col);
|
||||||
|
|
||||||
assert_eq(4, start->pT2->pToken->position.row);
|
assert_eq(4, p_node_position(k2).row);
|
||||||
assert_eq(1, start->pT2->pToken->position.col);
|
assert_eq(1, p_node_position(k2).col);
|
||||||
assert_eq(5, start->pT2->pToken->end_position.row);
|
assert_eq(5, p_node_end_position(k2).row);
|
||||||
assert_eq(2, start->pT2->pToken->end_position.col);
|
assert_eq(2, p_node_end_position(k2).col);
|
||||||
assert_eq(4, start->pT2->position.row);
|
assert_eq(4, p_node_position(t2).row);
|
||||||
assert_eq(1, start->pT2->position.col);
|
assert_eq(1, p_node_position(t2).col);
|
||||||
assert_eq(5, start->pT2->end_position.row);
|
assert_eq(5, p_node_end_position(t2).row);
|
||||||
assert_eq(2, start->pT2->end_position.col);
|
assert_eq(2, p_node_end_position(t2).col);
|
||||||
|
|
||||||
assert_eq(7, start->pT3->pToken->position.row);
|
assert_eq(7, p_node_position(k3).row);
|
||||||
assert_eq(6, start->pT3->pToken->position.col);
|
assert_eq(6, p_node_position(k3).col);
|
||||||
assert_eq(7, start->pT3->pToken->end_position.row);
|
assert_eq(7, p_node_end_position(k3).row);
|
||||||
assert_eq(6, start->pT3->pToken->end_position.col);
|
assert_eq(6, p_node_end_position(k3).col);
|
||||||
assert_eq(7, start->pT3->position.row);
|
assert_eq(7, p_node_position(t3).row);
|
||||||
assert_eq(6, start->pT3->position.col);
|
assert_eq(6, p_node_position(t3).col);
|
||||||
assert_eq(7, start->pT3->end_position.row);
|
assert_eq(7, p_node_end_position(t3).row);
|
||||||
assert_eq(6, start->pT3->end_position.col);
|
assert_eq(6, p_node_end_position(t3).col);
|
||||||
|
|
||||||
assert_eq(3, start->position.row);
|
assert_eq(3, p_node_position(start).row);
|
||||||
assert_eq(3, start->position.col);
|
assert_eq(3, p_node_position(start).col);
|
||||||
assert_eq(7, start->end_position.row);
|
assert_eq(7, p_node_end_position(start).row);
|
||||||
assert_eq(6, start->end_position.col);
|
assert_eq(6, p_node_end_position(start).col);
|
||||||
|
|
||||||
p_tree_delete(start);
|
|
||||||
p_context_delete(context);
|
p_context_delete(context);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@ -12,7 +12,7 @@ unittest
|
|||||||
string input = "abbccc";
|
string input = "abbccc";
|
||||||
p_context_t * context = p_context_new(input);
|
p_context_t * context = p_context_new(input);
|
||||||
assert(p_parse(context) == P_SUCCESS);
|
assert(p_parse(context) == P_SUCCESS);
|
||||||
Start * start = p_result(context);
|
Start start = p_result(context);
|
||||||
|
|
||||||
assert_eq(1, start.pT1.pToken.position.row);
|
assert_eq(1, start.pT1.pToken.position.row);
|
||||||
assert_eq(1, start.pT1.pToken.position.col);
|
assert_eq(1, start.pT1.pToken.position.col);
|
||||||
@ -46,7 +46,7 @@ unittest
|
|||||||
assert_eq(1, start.end_position.row);
|
assert_eq(1, start.end_position.row);
|
||||||
assert_eq(6, start.end_position.col);
|
assert_eq(6, start.end_position.col);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
|
|
||||||
input = "\n\n bb\nc\ncc\n\n a";
|
input = "\n\n bb\nc\ncc\n\n a";
|
||||||
context = p_context_new(input);
|
context = p_context_new(input);
|
||||||
@ -85,5 +85,5 @@ unittest
|
|||||||
assert_eq(7, start.end_position.row);
|
assert_eq(7, start.end_position.row);
|
||||||
assert_eq(6, start.end_position.col);
|
assert_eq(6, start.end_position.col);
|
||||||
|
|
||||||
p_tree_delete(start);
|
p_context_delete(context);
|
||||||
}
|
}
|
||||||
|
|||||||
27
spec/test_tree_token_positions.rs
Normal file
27
spec/test_tree_token_positions.rs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let input = b"abbccc";
|
||||||
|
let mut context = p_context_new(input);
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
{
|
||||||
|
let start = p_result(&context);
|
||||||
|
let t1 = start.pT1();
|
||||||
|
let t2 = start.pT2();
|
||||||
|
let t3 = start.pT3();
|
||||||
|
assert_eq!(1, t1.position().col);
|
||||||
|
assert_eq!(1, t1.end_position().col);
|
||||||
|
assert_eq!(2, t2.position().col);
|
||||||
|
assert_eq!(3, t2.end_position().col);
|
||||||
|
assert_eq!(4, t3.position().col);
|
||||||
|
assert_eq!(6, t3.end_position().col);
|
||||||
|
/* Token node position within T1. */
|
||||||
|
assert_eq!(1, t1.pToken().position().col);
|
||||||
|
/* Overall start node spans the whole input. */
|
||||||
|
assert_eq!(1, start.position().col);
|
||||||
|
assert_eq!(6, start.end_position().col);
|
||||||
|
assert_eq!(3, start.n_fields());
|
||||||
|
}
|
||||||
|
p_context_delete(context);
|
||||||
|
println!("ok");
|
||||||
|
}
|
||||||
13
spec/test_user_code.rs
Normal file
13
spec/test_user_code.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut context = p_context_new(b"abcdef");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
println!("pass1");
|
||||||
|
p_context_delete(context);
|
||||||
|
|
||||||
|
let mut context = p_context_new(b"abcabcdef");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut context));
|
||||||
|
println!("pass2");
|
||||||
|
p_context_delete(context);
|
||||||
|
}
|
||||||
9
spec/test_user_context_fields.rs
Normal file
9
spec/test_user_context_fields.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"aaa\n\n\na\n # comment 1\na a aa\n\naa\n# comment 2\na\n");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
eprint!("comments: {}", c.comments);
|
||||||
|
eprintln!("acount: {}", c.acount);
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
12
spec/test_user_terminate.rs
Normal file
12
spec/test_user_terminate.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"aacc");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"abc");
|
||||||
|
assert_eq!(P_USER_TERMINATED, p_parse(&mut c));
|
||||||
|
assert_eq!(4200, p_user_terminate_code(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
12
spec/test_user_terminate_lexer.rs
Normal file
12
spec/test_user_terminate_lexer.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"a");
|
||||||
|
assert_eq!(P_SUCCESS, p_parse(&mut c));
|
||||||
|
p_context_delete(c);
|
||||||
|
|
||||||
|
let mut c = p_context_new(b"b");
|
||||||
|
assert_eq!(P_USER_TERMINATED, p_parse(&mut c));
|
||||||
|
assert_eq!(8675309, p_user_terminate_code(&c));
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
20
spec/test_value_accessors.rs
Normal file
20
spec/test_value_accessors.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
use testparser::*;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut c = p_context_new(b"42 f s");
|
||||||
|
let mut ti = p_token_info_t::default();
|
||||||
|
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_num, ti.token);
|
||||||
|
assert_eq!(42, p_value_get(&ti.pvalue));
|
||||||
|
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_flt, ti.token);
|
||||||
|
assert_eq!(1.5, p_value_get_float(&ti.pvalue));
|
||||||
|
|
||||||
|
assert_eq!(P_SUCCESS, p_lex(&mut c, &mut ti));
|
||||||
|
assert_eq!(TOKEN_str, ti.token);
|
||||||
|
assert_eq!("hello", p_value_get_string(&ti.pvalue));
|
||||||
|
|
||||||
|
p_context_delete(c);
|
||||||
|
}
|
||||||
148
spec/tree_node_memory_remains.rust.propane
Normal file
148
spec/tree_node_memory_remains.rust.propane
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
tree;
|
||||||
|
tree_prefix P;
|
||||||
|
|
||||||
|
<<header
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct TokenVal {
|
||||||
|
pub i64_: u64,
|
||||||
|
pub s: String,
|
||||||
|
pub dou: f64,
|
||||||
|
}
|
||||||
|
>>
|
||||||
|
|
||||||
|
ptype TokenVal;
|
||||||
|
|
||||||
|
# Keywords.
|
||||||
|
token byte;
|
||||||
|
token def;
|
||||||
|
token int;
|
||||||
|
token long;
|
||||||
|
token module;
|
||||||
|
token return;
|
||||||
|
token short;
|
||||||
|
token size_t;
|
||||||
|
token ssize_t;
|
||||||
|
token ubyte;
|
||||||
|
token uint;
|
||||||
|
token ulong;
|
||||||
|
token ushort;
|
||||||
|
|
||||||
|
# Symbols.
|
||||||
|
token arrow /->/;
|
||||||
|
token comma /,/;
|
||||||
|
token lbrace /\{/;
|
||||||
|
token lparen /\(/;
|
||||||
|
token rbrace /\}/;
|
||||||
|
token rparen /\)/;
|
||||||
|
token semicolon /;/;
|
||||||
|
|
||||||
|
# Integer literals.
|
||||||
|
token hex_int_l /0[xX][0-9a-fA-F][0-9a-fA-F_]*/ <<
|
||||||
|
$$.i64_ = 64;
|
||||||
|
>>
|
||||||
|
|
||||||
|
# Identifier.
|
||||||
|
token ident /\$?[a-zA-Z_][a-zA-Z_0-9]*\??/ <<
|
||||||
|
$$.s = std::str::from_utf8(match_).unwrap().to_string();
|
||||||
|
$mode(default);
|
||||||
|
return $token(ident);
|
||||||
|
>>
|
||||||
|
|
||||||
|
# Comments.
|
||||||
|
drop /#.*/;
|
||||||
|
|
||||||
|
# Whitespace.
|
||||||
|
drop /[ \r\n]*/;
|
||||||
|
|
||||||
|
start Module;
|
||||||
|
|
||||||
|
# Assignment operators - right associative
|
||||||
|
Expression -> Expression_Or:exp0;
|
||||||
|
|
||||||
|
# Logical OR operator - left associative
|
||||||
|
Expression_Or -> Expression_And:exp0;
|
||||||
|
|
||||||
|
# Logical AND operator - left associative
|
||||||
|
Expression_And -> Expression_Comp:exp0;
|
||||||
|
|
||||||
|
# Equality operators - left associative
|
||||||
|
Expression_Comp -> Expression_Relational:exp0;
|
||||||
|
|
||||||
|
# Relational operators - left associative
|
||||||
|
Expression_Relational -> Expression_REMatch:exp0;
|
||||||
|
|
||||||
|
# Regular expression - left associative
|
||||||
|
Expression_REMatch -> Expression_BinOr:exp0;
|
||||||
|
|
||||||
|
# Binary OR operator - left associative
|
||||||
|
Expression_BinOr -> Expression_Xor:exp0;
|
||||||
|
|
||||||
|
# Binary XOR operator - left associative
|
||||||
|
Expression_Xor -> Expression_BinAnd:exp0;
|
||||||
|
|
||||||
|
# Binary AND operator - left associative
|
||||||
|
Expression_BinAnd -> Expression_BitShift:exp0;
|
||||||
|
|
||||||
|
# Bit shift operators - left associative
|
||||||
|
Expression_BitShift -> Expression_Plus:exp0;
|
||||||
|
|
||||||
|
# Add/subtract operators - left associative
|
||||||
|
Expression_Plus -> Expression_Mul:exp0;
|
||||||
|
|
||||||
|
# Multiplication/divide/modulus operators - left associative
|
||||||
|
Expression_Mul -> Expression_Range:exp0;
|
||||||
|
|
||||||
|
# Range construction operators - left associative
|
||||||
|
Expression_Range -> Expression_UnaryPrefix:exp0;
|
||||||
|
|
||||||
|
# Unary prefix operators
|
||||||
|
Expression_UnaryPrefix -> Expression_Dot:exp0;
|
||||||
|
|
||||||
|
# Postfix operators
|
||||||
|
Expression_Dot -> Expression_Ident:exp0;
|
||||||
|
Expression_Dot -> Expression_Dot:exp1 lparen rparen;
|
||||||
|
|
||||||
|
# Literals, identifiers, and parenthesized expressions
|
||||||
|
Expression_Ident -> Literal;
|
||||||
|
Expression_Ident -> ident;
|
||||||
|
|
||||||
|
FunctionDefinition -> def ident:name lparen FunctionParameterList?:parameters rparen FunctionReturnType?:returntype lbrace Statements rbrace;
|
||||||
|
|
||||||
|
FunctionParameterList -> ident:name Type:type FunctionParameterListMore?:more;
|
||||||
|
FunctionParameterListMore -> comma ident:name Type:type FunctionParameterListMore?:more;
|
||||||
|
|
||||||
|
FunctionReturnType -> arrow Type;
|
||||||
|
|
||||||
|
Literal -> LiteralInteger;
|
||||||
|
LiteralInteger -> hex_int_l;
|
||||||
|
|
||||||
|
Module -> ModuleStatement? ModuleItems;
|
||||||
|
|
||||||
|
ModuleItem -> FunctionDefinition;
|
||||||
|
|
||||||
|
ModuleItems -> ;
|
||||||
|
ModuleItems -> ModuleItems ModuleItem;
|
||||||
|
|
||||||
|
ModulePath -> ident;
|
||||||
|
|
||||||
|
ModuleStatement -> module ModulePath semicolon;
|
||||||
|
|
||||||
|
ReturnStatement -> return Expression?:exp0 semicolon;
|
||||||
|
|
||||||
|
Statements -> ;
|
||||||
|
Statements -> Statements Statement;
|
||||||
|
Statement -> Expression semicolon;
|
||||||
|
Statement -> ReturnStatement;
|
||||||
|
|
||||||
|
Type -> TypeBase;
|
||||||
|
|
||||||
|
TypeBase -> byte;
|
||||||
|
TypeBase -> ubyte;
|
||||||
|
TypeBase -> short;
|
||||||
|
TypeBase -> ushort;
|
||||||
|
TypeBase -> int;
|
||||||
|
TypeBase -> uint;
|
||||||
|
TypeBase -> long;
|
||||||
|
TypeBase -> ulong;
|
||||||
|
TypeBase -> size_t;
|
||||||
|
TypeBase -> ssize_t;
|
||||||
Loading…
x
Reference in New Issue
Block a user