Compare commits
7 Commits
3aced70356
...
9746b3f2bf
Author | SHA1 | Date | |
---|---|---|---|
9746b3f2bf | |||
c5b8fc28bd | |||
092fce61eb | |||
e647248e34 | |||
f4ae1b8601 | |||
eae2e17f41 | |||
87d6d29d60 |
@ -226,7 +226,10 @@ typedef struct
|
|||||||
/** Number of bytes of input text used to match. */
|
/** Number of bytes of input text used to match. */
|
||||||
size_t length;
|
size_t length;
|
||||||
|
|
||||||
/** Input text position delta. */
|
/** Input text position delta to end of token. */
|
||||||
|
<%= @grammar.prefix %>position_t end_delta_position;
|
||||||
|
|
||||||
|
/** Input text position delta to next code point after token end. */
|
||||||
<%= @grammar.prefix %>position_t delta_position;
|
<%= @grammar.prefix %>position_t delta_position;
|
||||||
|
|
||||||
/** Accepting lexer state from the match. */
|
/** Accepting lexer state from the match. */
|
||||||
@ -358,6 +361,7 @@ static size_t find_longest_match(<%= @grammar.prefix %>context_t * context,
|
|||||||
if (transition_state != INVALID_LEXER_STATE_ID)
|
if (transition_state != INVALID_LEXER_STATE_ID)
|
||||||
{
|
{
|
||||||
attempt_match.length += code_point_length;
|
attempt_match.length += code_point_length;
|
||||||
|
attempt_match.end_delta_position = attempt_match.delta_position;
|
||||||
if (code_point == '\n')
|
if (code_point == '\n')
|
||||||
{
|
{
|
||||||
attempt_match.delta_position.row++;
|
attempt_match.delta_position.row++;
|
||||||
@ -490,11 +494,22 @@ static size_t attempt_lex_token(<%= @grammar.prefix %>context_t * context, <%= @
|
|||||||
}
|
}
|
||||||
token_info.token = token_to_accept;
|
token_info.token = token_to_accept;
|
||||||
token_info.length = match_info.length;
|
token_info.length = match_info.length;
|
||||||
|
if (match_info.end_delta_position.row != 0u)
|
||||||
|
{
|
||||||
|
token_info.end_position.row = token_info.position.row + match_info.end_delta_position.row;
|
||||||
|
token_info.end_position.col = match_info.end_delta_position.col;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token_info.end_position.row = token_info.position.row;
|
||||||
|
token_info.end_position.col = token_info.position.col + match_info.end_delta_position.col;
|
||||||
|
}
|
||||||
*out_token_info = token_info;
|
*out_token_info = token_info;
|
||||||
return P_SUCCESS;
|
return P_SUCCESS;
|
||||||
|
|
||||||
case P_EOF:
|
case P_EOF:
|
||||||
token_info.token = TOKEN___EOF;
|
token_info.token = TOKEN___EOF;
|
||||||
|
token_info.end_position = token_info.position;
|
||||||
*out_token_info = token_info;
|
*out_token_info = token_info;
|
||||||
return P_SUCCESS;
|
return P_SUCCESS;
|
||||||
|
|
||||||
@ -551,6 +566,9 @@ size_t <%= @grammar.prefix %>lex(<%= @grammar.prefix %>context_t * context, <%=
|
|||||||
* Parser
|
* Parser
|
||||||
*************************************************************************/
|
*************************************************************************/
|
||||||
|
|
||||||
|
/** Invalid position value. */
|
||||||
|
#define INVALID_POSITION (<%= @grammar.prefix %>position_t){0xFFFFFFFFu, 0xFFFFFFFFu}
|
||||||
|
|
||||||
/** Reduce ID type. */
|
/** Reduce ID type. */
|
||||||
typedef <%= get_type_for(@parser.reduce_table.size) %> reduce_id_t;
|
typedef <%= get_type_for(@parser.reduce_table.size) %> reduce_id_t;
|
||||||
|
|
||||||
@ -666,6 +684,14 @@ typedef struct
|
|||||||
<% end %>
|
<% end %>
|
||||||
} state_value_t;
|
} state_value_t;
|
||||||
|
|
||||||
|
/** Common AST node structure. */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
<%= @grammar.prefix %>position_t position;
|
||||||
|
<%= @grammar.prefix %>position_t end_position;
|
||||||
|
void * fields[];
|
||||||
|
} ASTNode;
|
||||||
|
|
||||||
/** 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| %>
|
||||||
@ -949,9 +975,10 @@ size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context)
|
|||||||
/* We shifted a token, mark it consumed. */
|
/* We shifted a token, mark it consumed. */
|
||||||
<% if @grammar.ast %>
|
<% if @grammar.ast %>
|
||||||
<%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %> * token_ast_node = malloc(sizeof(<%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>));
|
<%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %> * token_ast_node = malloc(sizeof(<%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>));
|
||||||
|
token_ast_node->position = token_info.position;
|
||||||
|
token_ast_node->end_position = token_info.end_position;
|
||||||
token_ast_node->token = token;
|
token_ast_node->token = token;
|
||||||
token_ast_node->pvalue = token_info.pvalue;
|
token_ast_node->pvalue = token_info.pvalue;
|
||||||
token_ast_node->position = token_info.position;
|
|
||||||
state_values_stack_index(&statevalues, -1)->ast_node = token_ast_node;
|
state_values_stack_index(&statevalues, -1)->ast_node = token_ast_node;
|
||||||
<% else %>
|
<% else %>
|
||||||
state_values_stack_index(&statevalues, -1)->pvalue = token_info.pvalue;
|
state_values_stack_index(&statevalues, -1)->pvalue = token_info.pvalue;
|
||||||
@ -984,22 +1011,43 @@ size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context)
|
|||||||
}
|
}
|
||||||
else if (parser_reduce_table[reduce_index].n_states > 0)
|
else if (parser_reduce_table[reduce_index].n_states > 0)
|
||||||
{
|
{
|
||||||
void ** node_fields = calloc(parser_reduce_table[reduce_index].rule_set_node_field_array_size, sizeof(void *));
|
size_t n_fields = parser_reduce_table[reduce_index].rule_set_node_field_array_size;
|
||||||
|
ASTNode * node = (ASTNode *)malloc(sizeof(ASTNode) + n_fields * sizeof(void *));
|
||||||
|
node->position = INVALID_POSITION;
|
||||||
|
node->end_position = INVALID_POSITION;
|
||||||
|
for (size_t i = 0; i < n_fields; i++)
|
||||||
|
{
|
||||||
|
node->fields[i] = NULL;
|
||||||
|
}
|
||||||
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] = state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->ast_node;
|
node->fields[i] = state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->ast_node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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]] = state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->ast_node;
|
node->fields[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)->ast_node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reduced_parser_node = node_fields;
|
bool position_found = false;
|
||||||
|
for (size_t i = 0; i < n_fields; i++)
|
||||||
|
{
|
||||||
|
ASTNode * child = (ASTNode *)node->fields[i];
|
||||||
|
if ((child != NULL) && <%= @grammar.prefix %>position_valid(child->position))
|
||||||
|
{
|
||||||
|
if (!position_found)
|
||||||
|
{
|
||||||
|
node->position = child->position;
|
||||||
|
position_found = true;
|
||||||
|
}
|
||||||
|
node->end_position = child->end_position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reduced_parser_node = node;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
module <%= @grammar.modulename %>;
|
module <%= @grammar.modulename %>;
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
|
import core.stdc.stdlib : malloc;
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
* User code blocks
|
* User code blocks
|
||||||
*************************************************************************/
|
*************************************************************************/
|
||||||
@ -61,6 +63,15 @@ public struct <%= @grammar.prefix %>position_t
|
|||||||
|
|
||||||
/** Input text column (0-based). */
|
/** Input text column (0-based). */
|
||||||
uint col;
|
uint col;
|
||||||
|
|
||||||
|
/** Invalid position value. */
|
||||||
|
enum INVALID = <%= @grammar.prefix %>position_t(0xFFFF_FFFF, 0xFFFF_FFFF);
|
||||||
|
|
||||||
|
/** Return whether the position is valid. */
|
||||||
|
public @property bool valid()
|
||||||
|
{
|
||||||
|
return row != 0xFFFF_FFFFu;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<% if @grammar.ast %>
|
<% if @grammar.ast %>
|
||||||
@ -77,12 +88,22 @@ public union <%= @grammar.prefix %>value_t
|
|||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<% if @grammar.ast %>
|
<% if @grammar.ast %>
|
||||||
|
/** Common AST node structure. */
|
||||||
|
private struct ASTNode
|
||||||
|
{
|
||||||
|
<%= @grammar.prefix %>position_t position;
|
||||||
|
<%= @grammar.prefix %>position_t end_position;
|
||||||
|
void *[0] fields;
|
||||||
|
}
|
||||||
|
|
||||||
/** AST node types. @{ */
|
/** AST node types. @{ */
|
||||||
public struct <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>
|
public struct <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>
|
||||||
{
|
{
|
||||||
|
/* ASTNode fields must be present in the same order here. */
|
||||||
|
<%= @grammar.prefix %>position_t position;
|
||||||
|
<%= @grammar.prefix %>position_t end_position;
|
||||||
<%= @grammar.prefix %>token_t token;
|
<%= @grammar.prefix %>token_t token;
|
||||||
<%= @grammar.prefix %>value_t pvalue;
|
<%= @grammar.prefix %>value_t pvalue;
|
||||||
<%= @grammar.prefix %>position_t position;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<% @parser.rule_sets.each do |name, rule_set| %>
|
<% @parser.rule_sets.each do |name, rule_set| %>
|
||||||
@ -90,6 +111,8 @@ public struct <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>
|
|||||||
<% next if rule_set.optional? %>
|
<% next if rule_set.optional? %>
|
||||||
public struct <%= @grammar.ast_prefix %><%= name %><%= @grammar.ast_suffix %>
|
public struct <%= @grammar.ast_prefix %><%= name %><%= @grammar.ast_suffix %>
|
||||||
{
|
{
|
||||||
|
<%= @grammar.prefix %>position_t position;
|
||||||
|
<%= @grammar.prefix %>position_t end_position;
|
||||||
<% rule_set.ast_fields.each do |fields| %>
|
<% rule_set.ast_fields.each do |fields| %>
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
@ -107,9 +130,12 @@ public struct <%= @grammar.ast_prefix %><%= name %><%= @grammar.ast_suffix %>
|
|||||||
/** Lexed token information. */
|
/** Lexed token information. */
|
||||||
public struct <%= @grammar.prefix %>token_info_t
|
public struct <%= @grammar.prefix %>token_info_t
|
||||||
{
|
{
|
||||||
/** Text position where the token was found. */
|
/** Text position of first code point in token. */
|
||||||
<%= @grammar.prefix %>position_t position;
|
<%= @grammar.prefix %>position_t position;
|
||||||
|
|
||||||
|
/** Text position of last code point in token. */
|
||||||
|
<%= @grammar.prefix %>position_t end_position;
|
||||||
|
|
||||||
/** Number of input bytes used by the token. */
|
/** Number of input bytes used by the token. */
|
||||||
size_t length;
|
size_t length;
|
||||||
|
|
||||||
@ -373,7 +399,10 @@ private struct lexer_match_info_t
|
|||||||
/** Number of bytes of input text used to match. */
|
/** Number of bytes of input text used to match. */
|
||||||
size_t length;
|
size_t length;
|
||||||
|
|
||||||
/** Input text position delta. */
|
/** Input text position delta to end of token. */
|
||||||
|
<%= @grammar.prefix %>position_t end_delta_position;
|
||||||
|
|
||||||
|
/** Input text position delta to next code point after token end. */
|
||||||
<%= @grammar.prefix %>position_t delta_position;
|
<%= @grammar.prefix %>position_t delta_position;
|
||||||
|
|
||||||
/** Accepting lexer state from the match. */
|
/** Accepting lexer state from the match. */
|
||||||
@ -501,6 +530,7 @@ private size_t find_longest_match(<%= @grammar.prefix %>context_t * context,
|
|||||||
if (transition_state != INVALID_LEXER_STATE_ID)
|
if (transition_state != INVALID_LEXER_STATE_ID)
|
||||||
{
|
{
|
||||||
attempt_match.length += code_point_length;
|
attempt_match.length += code_point_length;
|
||||||
|
attempt_match.end_delta_position = attempt_match.delta_position;
|
||||||
if (code_point == '\n')
|
if (code_point == '\n')
|
||||||
{
|
{
|
||||||
attempt_match.delta_position.row++;
|
attempt_match.delta_position.row++;
|
||||||
@ -633,11 +663,22 @@ private size_t attempt_lex_token(<%= @grammar.prefix %>context_t * context, <%=
|
|||||||
}
|
}
|
||||||
token_info.token = token_to_accept;
|
token_info.token = token_to_accept;
|
||||||
token_info.length = match_info.length;
|
token_info.length = match_info.length;
|
||||||
|
if (match_info.end_delta_position.row != 0u)
|
||||||
|
{
|
||||||
|
token_info.end_position.row = token_info.position.row + match_info.end_delta_position.row;
|
||||||
|
token_info.end_position.col = match_info.end_delta_position.col;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
token_info.end_position.row = token_info.position.row;
|
||||||
|
token_info.end_position.col = token_info.position.col + match_info.end_delta_position.col;
|
||||||
|
}
|
||||||
*out_token_info = token_info;
|
*out_token_info = token_info;
|
||||||
return P_SUCCESS;
|
return P_SUCCESS;
|
||||||
|
|
||||||
case P_EOF:
|
case P_EOF:
|
||||||
token_info.token = TOKEN___EOF;
|
token_info.token = TOKEN___EOF;
|
||||||
|
token_info.end_position = token_info.position;
|
||||||
*out_token_info = token_info;
|
*out_token_info = token_info;
|
||||||
return P_SUCCESS;
|
return P_SUCCESS;
|
||||||
|
|
||||||
@ -997,7 +1038,7 @@ public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * cont
|
|||||||
{
|
{
|
||||||
/* We shifted a token, mark it consumed. */
|
/* We shifted a token, mark it consumed. */
|
||||||
<% if @grammar.ast %>
|
<% if @grammar.ast %>
|
||||||
<%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %> * token_ast_node = new <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>(token, token_info.pvalue, token_info.position);
|
<%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %> * token_ast_node = new <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>(token_info.position, token_info.end_position, token, token_info.pvalue);
|
||||||
statevalues[$-1].ast_node = token_ast_node;
|
statevalues[$-1].ast_node = token_ast_node;
|
||||||
<% else %>
|
<% else %>
|
||||||
statevalues[$-1].pvalue = token_info.pvalue;
|
statevalues[$-1].pvalue = token_info.pvalue;
|
||||||
@ -1030,26 +1071,43 @@ public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * cont
|
|||||||
}
|
}
|
||||||
else if (parser_reduce_table[reduce_index].n_states > 0)
|
else if (parser_reduce_table[reduce_index].n_states > 0)
|
||||||
{
|
{
|
||||||
void *[] node_fields = new void *[parser_reduce_table[reduce_index].rule_set_node_field_array_size];
|
size_t n_fields = parser_reduce_table[reduce_index].rule_set_node_field_array_size;
|
||||||
foreach (i; 0..parser_reduce_table[reduce_index].rule_set_node_field_array_size)
|
ASTNode * node = cast(ASTNode *)malloc(ASTNode.sizeof + n_fields * (void *).sizeof);
|
||||||
|
node.position = <%= @grammar.prefix %>position_t.INVALID;
|
||||||
|
node.end_position = <%= @grammar.prefix %>position_t.INVALID;
|
||||||
|
foreach (i; 0..n_fields)
|
||||||
{
|
{
|
||||||
node_fields[i] = null;
|
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].ast_node;
|
node.fields[i] = statevalues[$ - parser_reduce_table[reduce_index].n_states + i].ast_node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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].ast_node;
|
node.fields[parser_reduce_table[reduce_index].rule_set_node_field_index_map[i]] = statevalues[$ - parser_reduce_table[reduce_index].n_states + i].ast_node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
reduced_parser_node = node_fields.ptr;
|
bool position_found = false;
|
||||||
|
foreach (i; 0..n_fields)
|
||||||
|
{
|
||||||
|
ASTNode * child = cast(ASTNode *)node.fields[i];
|
||||||
|
if (child && child.position.valid)
|
||||||
|
{
|
||||||
|
if (!position_found)
|
||||||
|
{
|
||||||
|
node.position = child.position;
|
||||||
|
position_found = true;
|
||||||
|
}
|
||||||
|
node.end_position = child.end_position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reduced_parser_node = node;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -52,6 +52,9 @@ typedef struct
|
|||||||
uint32_t col;
|
uint32_t col;
|
||||||
} <%= @grammar.prefix %>position_t;
|
} <%= @grammar.prefix %>position_t;
|
||||||
|
|
||||||
|
/** Return whether the position is valid. */
|
||||||
|
#define <%= @grammar.prefix %>position_valid(p) ((p).row != 0xFFFFFFFFu)
|
||||||
|
|
||||||
/** User header code blocks. */
|
/** User header code blocks. */
|
||||||
<%= @grammar.code_blocks.fetch("header", "") %>
|
<%= @grammar.code_blocks.fetch("header", "") %>
|
||||||
|
|
||||||
@ -72,9 +75,11 @@ typedef union
|
|||||||
/** AST node types. @{ */
|
/** AST node types. @{ */
|
||||||
typedef struct <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>
|
typedef struct <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>
|
||||||
{
|
{
|
||||||
|
/* ASTNode fields must be present in the same order here. */
|
||||||
|
<%= @grammar.prefix %>position_t position;
|
||||||
|
<%= @grammar.prefix %>position_t end_position;
|
||||||
<%= @grammar.prefix %>token_t token;
|
<%= @grammar.prefix %>token_t token;
|
||||||
<%= @grammar.prefix %>value_t pvalue;
|
<%= @grammar.prefix %>value_t pvalue;
|
||||||
<%= @grammar.prefix %>position_t position;
|
|
||||||
} <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>;
|
} <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>;
|
||||||
|
|
||||||
<% @parser.rule_sets.each do |name, rule_set| %>
|
<% @parser.rule_sets.each do |name, rule_set| %>
|
||||||
@ -88,6 +93,8 @@ struct <%= name %>;
|
|||||||
<% next if rule_set.optional? %>
|
<% next if rule_set.optional? %>
|
||||||
typedef struct <%= @grammar.ast_prefix %><%= name %><%= @grammar.ast_suffix %>
|
typedef struct <%= @grammar.ast_prefix %><%= name %><%= @grammar.ast_suffix %>
|
||||||
{
|
{
|
||||||
|
<%= @grammar.prefix %>position_t position;
|
||||||
|
<%= @grammar.prefix %>position_t end_position;
|
||||||
<% rule_set.ast_fields.each do |fields| %>
|
<% rule_set.ast_fields.each do |fields| %>
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
@ -105,9 +112,12 @@ typedef struct <%= @grammar.ast_prefix %><%= name %><%= @grammar.ast_suffix %>
|
|||||||
/** Lexed token information. */
|
/** Lexed token information. */
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
/** Text position where the token was found. */
|
/** Text position of first code point in token. */
|
||||||
<%= @grammar.prefix %>position_t position;
|
<%= @grammar.prefix %>position_t position;
|
||||||
|
|
||||||
|
/** Text position of last code point in token. */
|
||||||
|
<%= @grammar.prefix %>position_t end_position;
|
||||||
|
|
||||||
/** Number of input bytes used by the token. */
|
/** Number of input bytes used by the token. */
|
||||||
size_t length;
|
size_t length;
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ Propane is a LALR Parser Generator (LPG) which:
|
|||||||
* 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 or D language outputs
|
* targets C or D language outputs
|
||||||
* optionally supports automatic full AST generation
|
* optionally supports automatic full AST generation
|
||||||
|
* tracks input text start and end positions for all matched tokens/rules
|
||||||
* is MIT-licensed
|
* is MIT-licensed
|
||||||
* is distributable as a standalone Ruby script
|
* is distributable as a standalone Ruby script
|
||||||
|
|
||||||
@ -35,9 +36,14 @@ Propane is typically invoked from the command-line as `./propane`.
|
|||||||
|
|
||||||
Usage: ./propane [options] <input-file> <output-file>
|
Usage: ./propane [options] <input-file> <output-file>
|
||||||
Options:
|
Options:
|
||||||
--log LOG Write log file
|
-h, --help Show this usage and exit.
|
||||||
--version Show program version and exit
|
--log LOG Write log file. This will show all parser states and their
|
||||||
-h, --help Show this usage and exit
|
associated shifts and reduces. It can be helpful when
|
||||||
|
debugging a grammar.
|
||||||
|
--version Show program version and exit.
|
||||||
|
-w Treat warnings as errors. This option will treat shift/reduce
|
||||||
|
conflicts as fatal errors and will print them to stderr in
|
||||||
|
addition to the log file.
|
||||||
|
|
||||||
The user must specify the path to a Propane input grammar file and a path to an
|
The user must specify the path to a Propane input grammar file and a path to an
|
||||||
output file.
|
output file.
|
||||||
@ -502,7 +508,7 @@ tokenid str;
|
|||||||
mystringvalue = "";
|
mystringvalue = "";
|
||||||
$mode(string);
|
$mode(string);
|
||||||
>>
|
>>
|
||||||
string: /[^"]+/ << mystringvalue += match; >>
|
string: /[^"]+/ << mystringvalue ~= match; >>
|
||||||
string: /"/ <<
|
string: /"/ <<
|
||||||
$mode(default);
|
$mode(default);
|
||||||
return $token(str);
|
return $token(str);
|
||||||
@ -762,6 +768,13 @@ A pointer to this instance is passed to the generated functions.
|
|||||||
The `p_position_t` structure contains two fields `row` and `col`.
|
The `p_position_t` structure contains two fields `row` and `col`.
|
||||||
These fields contain the 0-based row and column describing a parser position.
|
These fields contain the 0-based row and column describing a parser position.
|
||||||
|
|
||||||
|
For D targets, the `p_position_t` structure can be checked for validity by
|
||||||
|
querying the `valid` property.
|
||||||
|
|
||||||
|
For C targets, the `p_position_t` structure can be checked for validity by
|
||||||
|
calling `p_position_valid(pos)` where `pos` is a `p_position_t` structure
|
||||||
|
instance.
|
||||||
|
|
||||||
### AST Node Types
|
### AST Node Types
|
||||||
|
|
||||||
If AST generation mode is enabled, a structure type for each rule will be
|
If AST generation mode is enabled, a structure type for each rule will be
|
||||||
@ -772,13 +785,26 @@ AST node which refers to a raw parser token rather than a composite rule.
|
|||||||
|
|
||||||
#### AST Node Fields
|
#### AST Node Fields
|
||||||
|
|
||||||
A `Token` node has two fields:
|
All AST nodes have a `position` field specifying the text position of the
|
||||||
|
beginning of the matched token or rule, and an `end_position` field specifying
|
||||||
|
the text position of the end of the matched token or rule.
|
||||||
|
Each of these fields are instances of the `p_position_t` structure.
|
||||||
|
|
||||||
|
A `Token` node will always have a valid `position` and `end_position`.
|
||||||
|
A rule node may not have valid positions if the rule allows for an empty match.
|
||||||
|
In this case the `position` structure should be checked for validity before
|
||||||
|
using it.
|
||||||
|
For C targets this can be accomplished with
|
||||||
|
`if (p_position_valid(node->position))` and for D targets this can be
|
||||||
|
accomplished with `if (node.position.valid)`.
|
||||||
|
|
||||||
|
A `Token` node has the following additional fields:
|
||||||
|
|
||||||
* `token` which specifies which token was parsed (one of `TOKEN_*`)
|
* `token` which specifies which token was parsed (one of `TOKEN_*`)
|
||||||
* `pvalue` which specifies the parser value for the token. If a lexer user
|
* `pvalue` which specifies the parser value for the token. If a lexer user
|
||||||
code block assigned to `$$`, the assigned value will be stored here.
|
code block assigned to `$$`, the assigned value will be stored here.
|
||||||
|
|
||||||
The other generated AST node structures have fields generated based on the
|
AST node structures for rules contain generated fields based on the
|
||||||
right hand side components specified for all rules of a given name.
|
right hand side components specified for all rules of a given name.
|
||||||
|
|
||||||
In this example:
|
In this example:
|
||||||
@ -802,7 +828,7 @@ The `Items` structure will have fields:
|
|||||||
|
|
||||||
If a rule can be empty (for example in the second `Items` rule above), then
|
If a rule can be empty (for example in the second `Items` rule above), then
|
||||||
an instance of a pointer to that rule's generated AST node will be null if the
|
an instance of a pointer to that rule's generated AST node will be null if the
|
||||||
parser matches the empty rule definition.
|
parser matches the empty rule pattern.
|
||||||
|
|
||||||
The non-positional AST node field pointer will not be generated if there are
|
The non-positional AST node field pointer will not be generated if there are
|
||||||
multiple positions in which an instance of the node it points to could be
|
multiple positions in which an instance of the node it points to could be
|
||||||
@ -859,6 +885,24 @@ p_context_init(&context, input, input_length);
|
|||||||
size_t result = p_parse(&context);
|
size_t result = p_parse(&context);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `p_position_valid`
|
||||||
|
|
||||||
|
The `p_position_valid()` function is only generated for C targets.
|
||||||
|
it is used to determine whether or not a `p_position_t` structure is valid.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```
|
||||||
|
if (p_position_valid(node->position))
|
||||||
|
{
|
||||||
|
....
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For D targets, rather than using `p_position_valid()`, the `valid` property
|
||||||
|
function of the `p_position_t` structure can be queried
|
||||||
|
(e.g. `if (node.position.valid)`).
|
||||||
|
|
||||||
### `p_result`
|
### `p_result`
|
||||||
|
|
||||||
The `p_result()` function can be used to retrieve the final parse value after
|
The `p_result()` function can be used to retrieve the final parse value after
|
||||||
|
@ -4,11 +4,11 @@ class Propane
|
|||||||
USAGE = <<EOF
|
USAGE = <<EOF
|
||||||
Usage: #{$0} [options] <input-file> <output-file>
|
Usage: #{$0} [options] <input-file> <output-file>
|
||||||
Options:
|
Options:
|
||||||
|
-h, --help Show this usage and exit.
|
||||||
--log LOG Write log file. This will show all parser states and their
|
--log LOG Write log file. This will show all parser states and their
|
||||||
associated shifts and reduces. It can be helpful when
|
associated shifts and reduces. It can be helpful when
|
||||||
debugging a grammar.
|
debugging a grammar.
|
||||||
--version Show program version and exit.
|
--version Show program version and exit.
|
||||||
-h, --help Show this usage and exit.
|
|
||||||
-w Treat warnings as errors. This option will treat shift/reduce
|
-w Treat warnings as errors. This option will treat shift/reduce
|
||||||
conflicts as fatal errors and will print them to stderr in
|
conflicts as fatal errors and will print them to stderr in
|
||||||
addition to the log file.
|
addition to the log file.
|
||||||
|
@ -1081,17 +1081,17 @@ EOF
|
|||||||
expect(results.status).to eq 0
|
expect(results.status).to eq 0
|
||||||
end
|
end
|
||||||
|
|
||||||
it "stores the token position in the AST Token node" do
|
it "stores token and rule positions in AST nodes" do
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
ast;
|
ast;
|
||||||
|
|
||||||
token a;
|
token a;
|
||||||
token b;
|
token bb;
|
||||||
token c;
|
token c /c(.|\\n)*c/;
|
||||||
drop /\\s+/;
|
drop /\\s+/;
|
||||||
Start -> T T T;
|
Start -> T T T;
|
||||||
T -> a;
|
T -> a;
|
||||||
T -> b;
|
T -> bb;
|
||||||
T -> c;
|
T -> c;
|
||||||
EOF
|
EOF
|
||||||
run_propane(language: language)
|
run_propane(language: language)
|
||||||
@ -1100,6 +1100,26 @@ EOF
|
|||||||
expect(results.stderr).to eq ""
|
expect(results.stderr).to eq ""
|
||||||
expect(results.status).to eq 0
|
expect(results.status).to eq 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "stores invalid positions for empty rule matches" do
|
||||||
|
write_grammar <<EOF
|
||||||
|
ast;
|
||||||
|
|
||||||
|
token a;
|
||||||
|
token bb;
|
||||||
|
token c /c(.|\\n)*c/;
|
||||||
|
drop /\\s+/;
|
||||||
|
Start -> T Start;
|
||||||
|
Start -> ;
|
||||||
|
T -> a A;
|
||||||
|
A -> bb? c?;
|
||||||
|
EOF
|
||||||
|
run_propane(language: language)
|
||||||
|
compile("spec/test_ast_invalid_positions.#{language}", language: language)
|
||||||
|
results = run_test
|
||||||
|
expect(results.stderr).to eq ""
|
||||||
|
expect(results.status).to eq 0
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
102
spec/test_ast_invalid_positions.c
Normal file
102
spec/test_ast_invalid_positions.c
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#include "testparser.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char const * input = "\na\n bb ccc";
|
||||||
|
p_context_t context;
|
||||||
|
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
Start * start = p_result(&context);
|
||||||
|
|
||||||
|
assert_eq(1, start->pT1->pToken->position.row);
|
||||||
|
assert_eq(0, start->pT1->pToken->position.col);
|
||||||
|
assert_eq(1, start->pT1->pToken->end_position.row);
|
||||||
|
assert_eq(0, start->pT1->pToken->end_position.col);
|
||||||
|
assert(p_position_valid(start->pT1->pA->position));
|
||||||
|
assert_eq(2, start->pT1->pA->position.row);
|
||||||
|
assert_eq(2, start->pT1->pA->position.col);
|
||||||
|
assert_eq(2, start->pT1->pA->end_position.row);
|
||||||
|
assert_eq(7, start->pT1->pA->end_position.col);
|
||||||
|
assert_eq(1, start->pT1->position.row);
|
||||||
|
assert_eq(0, start->pT1->position.col);
|
||||||
|
assert_eq(2, start->pT1->end_position.row);
|
||||||
|
assert_eq(7, start->pT1->end_position.col);
|
||||||
|
|
||||||
|
assert_eq(1, start->position.row);
|
||||||
|
assert_eq(0, start->position.col);
|
||||||
|
assert_eq(2, start->end_position.row);
|
||||||
|
assert_eq(7, start->end_position.col);
|
||||||
|
|
||||||
|
input = "a\nbb";
|
||||||
|
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
start = p_result(&context);
|
||||||
|
|
||||||
|
assert_eq(0, start->pT1->pToken->position.row);
|
||||||
|
assert_eq(0, start->pT1->pToken->position.col);
|
||||||
|
assert_eq(0, start->pT1->pToken->end_position.row);
|
||||||
|
assert_eq(0, start->pT1->pToken->end_position.col);
|
||||||
|
assert(p_position_valid(start->pT1->pA->position));
|
||||||
|
assert_eq(1, start->pT1->pA->position.row);
|
||||||
|
assert_eq(0, start->pT1->pA->position.col);
|
||||||
|
assert_eq(1, start->pT1->pA->end_position.row);
|
||||||
|
assert_eq(1, start->pT1->pA->end_position.col);
|
||||||
|
assert_eq(0, start->pT1->position.row);
|
||||||
|
assert_eq(0, start->pT1->position.col);
|
||||||
|
assert_eq(1, start->pT1->end_position.row);
|
||||||
|
assert_eq(1, start->pT1->end_position.col);
|
||||||
|
|
||||||
|
assert_eq(0, start->position.row);
|
||||||
|
assert_eq(0, start->position.col);
|
||||||
|
assert_eq(1, start->end_position.row);
|
||||||
|
assert_eq(1, start->end_position.col);
|
||||||
|
|
||||||
|
input = "a\nc\nc";
|
||||||
|
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
start = p_result(&context);
|
||||||
|
|
||||||
|
assert_eq(0, start->pT1->pToken->position.row);
|
||||||
|
assert_eq(0, start->pT1->pToken->position.col);
|
||||||
|
assert_eq(0, start->pT1->pToken->end_position.row);
|
||||||
|
assert_eq(0, start->pT1->pToken->end_position.col);
|
||||||
|
assert(p_position_valid(start->pT1->pA->position));
|
||||||
|
assert_eq(1, start->pT1->pA->position.row);
|
||||||
|
assert_eq(0, start->pT1->pA->position.col);
|
||||||
|
assert_eq(2, start->pT1->pA->end_position.row);
|
||||||
|
assert_eq(0, start->pT1->pA->end_position.col);
|
||||||
|
assert_eq(0, start->pT1->position.row);
|
||||||
|
assert_eq(0, start->pT1->position.col);
|
||||||
|
assert_eq(2, start->pT1->end_position.row);
|
||||||
|
assert_eq(0, start->pT1->end_position.col);
|
||||||
|
|
||||||
|
assert_eq(0, start->position.row);
|
||||||
|
assert_eq(0, start->position.col);
|
||||||
|
assert_eq(2, start->end_position.row);
|
||||||
|
assert_eq(0, start->end_position.col);
|
||||||
|
|
||||||
|
input = "a";
|
||||||
|
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
start = p_result(&context);
|
||||||
|
|
||||||
|
assert_eq(0, start->pT1->pToken->position.row);
|
||||||
|
assert_eq(0, start->pT1->pToken->position.col);
|
||||||
|
assert_eq(0, start->pT1->pToken->end_position.row);
|
||||||
|
assert_eq(0, start->pT1->pToken->end_position.col);
|
||||||
|
assert(!p_position_valid(start->pT1->pA->position));
|
||||||
|
assert_eq(0, start->pT1->position.row);
|
||||||
|
assert_eq(0, start->pT1->position.col);
|
||||||
|
assert_eq(0, start->pT1->end_position.row);
|
||||||
|
assert_eq(0, start->pT1->end_position.col);
|
||||||
|
|
||||||
|
assert_eq(0, start->position.row);
|
||||||
|
assert_eq(0, start->position.col);
|
||||||
|
assert_eq(0, start->end_position.row);
|
||||||
|
assert_eq(0, start->end_position.col);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
104
spec/test_ast_invalid_positions.d
Normal file
104
spec/test_ast_invalid_positions.d
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import testparser;
|
||||||
|
import std.stdio;
|
||||||
|
import testutils;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
string input = "\na\n bb ccc";
|
||||||
|
p_context_t context;
|
||||||
|
p_context_init(&context, input);
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
Start * start = p_result(&context);
|
||||||
|
|
||||||
|
assert_eq(1, start.pT1.pToken.position.row);
|
||||||
|
assert_eq(0, start.pT1.pToken.position.col);
|
||||||
|
assert_eq(1, start.pT1.pToken.end_position.row);
|
||||||
|
assert_eq(0, start.pT1.pToken.end_position.col);
|
||||||
|
assert(start.pT1.pA.position.valid);
|
||||||
|
assert_eq(2, start.pT1.pA.position.row);
|
||||||
|
assert_eq(2, start.pT1.pA.position.col);
|
||||||
|
assert_eq(2, start.pT1.pA.end_position.row);
|
||||||
|
assert_eq(7, start.pT1.pA.end_position.col);
|
||||||
|
assert_eq(1, start.pT1.position.row);
|
||||||
|
assert_eq(0, start.pT1.position.col);
|
||||||
|
assert_eq(2, start.pT1.end_position.row);
|
||||||
|
assert_eq(7, start.pT1.end_position.col);
|
||||||
|
|
||||||
|
assert_eq(1, start.position.row);
|
||||||
|
assert_eq(0, start.position.col);
|
||||||
|
assert_eq(2, start.end_position.row);
|
||||||
|
assert_eq(7, start.end_position.col);
|
||||||
|
|
||||||
|
input = "a\nbb";
|
||||||
|
p_context_init(&context, input);
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
start = p_result(&context);
|
||||||
|
|
||||||
|
assert_eq(0, start.pT1.pToken.position.row);
|
||||||
|
assert_eq(0, start.pT1.pToken.position.col);
|
||||||
|
assert_eq(0, start.pT1.pToken.end_position.row);
|
||||||
|
assert_eq(0, start.pT1.pToken.end_position.col);
|
||||||
|
assert(start.pT1.pA.position.valid);
|
||||||
|
assert_eq(1, start.pT1.pA.position.row);
|
||||||
|
assert_eq(0, start.pT1.pA.position.col);
|
||||||
|
assert_eq(1, start.pT1.pA.end_position.row);
|
||||||
|
assert_eq(1, start.pT1.pA.end_position.col);
|
||||||
|
assert_eq(0, start.pT1.position.row);
|
||||||
|
assert_eq(0, start.pT1.position.col);
|
||||||
|
assert_eq(1, start.pT1.end_position.row);
|
||||||
|
assert_eq(1, start.pT1.end_position.col);
|
||||||
|
|
||||||
|
assert_eq(0, start.position.row);
|
||||||
|
assert_eq(0, start.position.col);
|
||||||
|
assert_eq(1, start.end_position.row);
|
||||||
|
assert_eq(1, start.end_position.col);
|
||||||
|
|
||||||
|
input = "a\nc\nc";
|
||||||
|
p_context_init(&context, input);
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
start = p_result(&context);
|
||||||
|
|
||||||
|
assert_eq(0, start.pT1.pToken.position.row);
|
||||||
|
assert_eq(0, start.pT1.pToken.position.col);
|
||||||
|
assert_eq(0, start.pT1.pToken.end_position.row);
|
||||||
|
assert_eq(0, start.pT1.pToken.end_position.col);
|
||||||
|
assert(start.pT1.pA.position.valid);
|
||||||
|
assert_eq(1, start.pT1.pA.position.row);
|
||||||
|
assert_eq(0, start.pT1.pA.position.col);
|
||||||
|
assert_eq(2, start.pT1.pA.end_position.row);
|
||||||
|
assert_eq(0, start.pT1.pA.end_position.col);
|
||||||
|
assert_eq(0, start.pT1.position.row);
|
||||||
|
assert_eq(0, start.pT1.position.col);
|
||||||
|
assert_eq(2, start.pT1.end_position.row);
|
||||||
|
assert_eq(0, start.pT1.end_position.col);
|
||||||
|
|
||||||
|
assert_eq(0, start.position.row);
|
||||||
|
assert_eq(0, start.position.col);
|
||||||
|
assert_eq(2, start.end_position.row);
|
||||||
|
assert_eq(0, start.end_position.col);
|
||||||
|
|
||||||
|
input = "a";
|
||||||
|
p_context_init(&context, input);
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
start = p_result(&context);
|
||||||
|
|
||||||
|
assert_eq(0, start.pT1.pToken.position.row);
|
||||||
|
assert_eq(0, start.pT1.pToken.position.col);
|
||||||
|
assert_eq(0, start.pT1.pToken.end_position.row);
|
||||||
|
assert_eq(0, start.pT1.pToken.end_position.col);
|
||||||
|
assert(!start.pT1.pA.position.valid);
|
||||||
|
assert_eq(0, start.pT1.position.row);
|
||||||
|
assert_eq(0, start.pT1.position.col);
|
||||||
|
assert_eq(0, start.pT1.end_position.row);
|
||||||
|
assert_eq(0, start.pT1.end_position.col);
|
||||||
|
|
||||||
|
assert_eq(0, start.position.row);
|
||||||
|
assert_eq(0, start.position.col);
|
||||||
|
assert_eq(0, start.end_position.row);
|
||||||
|
assert_eq(0, start.end_position.col);
|
||||||
|
}
|
@ -5,29 +5,80 @@
|
|||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
char const * input = "abc";
|
char const * input = "abbccc";
|
||||||
p_context_t context;
|
p_context_t context;
|
||||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
p_context_init(&context, (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(0, start->pT1->pToken->position.row);
|
assert_eq(0, start->pT1->pToken->position.row);
|
||||||
assert_eq(0, start->pT1->pToken->position.col);
|
assert_eq(0, start->pT1->pToken->position.col);
|
||||||
|
assert_eq(0, start->pT1->pToken->end_position.row);
|
||||||
|
assert_eq(0, start->pT1->pToken->end_position.col);
|
||||||
|
assert_eq(0, start->pT1->position.row);
|
||||||
|
assert_eq(0, start->pT1->position.col);
|
||||||
|
assert_eq(0, start->pT1->end_position.row);
|
||||||
|
assert_eq(0, start->pT1->end_position.col);
|
||||||
|
|
||||||
assert_eq(0, start->pT2->pToken->position.row);
|
assert_eq(0, start->pT2->pToken->position.row);
|
||||||
assert_eq(1, start->pT2->pToken->position.col);
|
assert_eq(1, start->pT2->pToken->position.col);
|
||||||
assert_eq(0, start->pT3->pToken->position.row);
|
assert_eq(0, start->pT2->pToken->end_position.row);
|
||||||
assert_eq(2, start->pT3->pToken->position.col);
|
assert_eq(2, start->pT2->pToken->end_position.col);
|
||||||
|
assert_eq(0, start->pT2->position.row);
|
||||||
|
assert_eq(1, start->pT2->position.col);
|
||||||
|
assert_eq(0, start->pT2->end_position.row);
|
||||||
|
assert_eq(2, start->pT2->end_position.col);
|
||||||
|
|
||||||
input = "\n\n a\nc\n\n a";
|
assert_eq(0, start->pT3->pToken->position.row);
|
||||||
|
assert_eq(3, start->pT3->pToken->position.col);
|
||||||
|
assert_eq(0, start->pT3->pToken->end_position.row);
|
||||||
|
assert_eq(5, start->pT3->pToken->end_position.col);
|
||||||
|
assert_eq(0, start->pT3->position.row);
|
||||||
|
assert_eq(3, start->pT3->position.col);
|
||||||
|
assert_eq(0, start->pT3->end_position.row);
|
||||||
|
assert_eq(5, start->pT3->end_position.col);
|
||||||
|
|
||||||
|
assert_eq(0, start->position.row);
|
||||||
|
assert_eq(0, start->position.col);
|
||||||
|
assert_eq(0, start->end_position.row);
|
||||||
|
assert_eq(5, start->end_position.col);
|
||||||
|
|
||||||
|
input = "\n\n bb\nc\ncc\n\n a";
|
||||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
p_context_init(&context, (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_eq(2, start->pT1->pToken->position.row);
|
assert_eq(2, start->pT1->pToken->position.row);
|
||||||
assert_eq(2, start->pT1->pToken->position.col);
|
assert_eq(2, start->pT1->pToken->position.col);
|
||||||
|
assert_eq(2, start->pT1->pToken->end_position.row);
|
||||||
|
assert_eq(3, start->pT1->pToken->end_position.col);
|
||||||
|
assert_eq(2, start->pT1->position.row);
|
||||||
|
assert_eq(2, start->pT1->position.col);
|
||||||
|
assert_eq(2, start->pT1->end_position.row);
|
||||||
|
assert_eq(3, start->pT1->end_position.col);
|
||||||
|
|
||||||
assert_eq(3, start->pT2->pToken->position.row);
|
assert_eq(3, start->pT2->pToken->position.row);
|
||||||
assert_eq(0, start->pT2->pToken->position.col);
|
assert_eq(0, start->pT2->pToken->position.col);
|
||||||
assert_eq(5, start->pT3->pToken->position.row);
|
assert_eq(4, start->pT2->pToken->end_position.row);
|
||||||
|
assert_eq(1, start->pT2->pToken->end_position.col);
|
||||||
|
assert_eq(3, start->pT2->position.row);
|
||||||
|
assert_eq(0, start->pT2->position.col);
|
||||||
|
assert_eq(4, start->pT2->end_position.row);
|
||||||
|
assert_eq(1, start->pT2->end_position.col);
|
||||||
|
|
||||||
|
assert_eq(6, start->pT3->pToken->position.row);
|
||||||
assert_eq(5, start->pT3->pToken->position.col);
|
assert_eq(5, start->pT3->pToken->position.col);
|
||||||
|
assert_eq(6, start->pT3->pToken->end_position.row);
|
||||||
|
assert_eq(5, start->pT3->pToken->end_position.col);
|
||||||
|
assert_eq(6, start->pT3->position.row);
|
||||||
|
assert_eq(5, start->pT3->position.col);
|
||||||
|
assert_eq(6, start->pT3->end_position.row);
|
||||||
|
assert_eq(5, start->pT3->end_position.col);
|
||||||
|
|
||||||
|
assert_eq(2, start->position.row);
|
||||||
|
assert_eq(2, start->position.col);
|
||||||
|
assert_eq(6, start->end_position.row);
|
||||||
|
assert_eq(5, start->end_position.col);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,26 +9,78 @@ int main()
|
|||||||
|
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
string input = "abc";
|
string input = "abbccc";
|
||||||
p_context_t context;
|
p_context_t context;
|
||||||
p_context_init(&context, input);
|
p_context_init(&context, 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(0, start.pT1.pToken.position.row);
|
assert_eq(0, start.pT1.pToken.position.row);
|
||||||
assert_eq(0, start.pT1.pToken.position.col);
|
assert_eq(0, start.pT1.pToken.position.col);
|
||||||
|
assert_eq(0, start.pT1.pToken.end_position.row);
|
||||||
|
assert_eq(0, start.pT1.pToken.end_position.col);
|
||||||
|
assert_eq(0, start.pT1.position.row);
|
||||||
|
assert_eq(0, start.pT1.position.col);
|
||||||
|
assert_eq(0, start.pT1.end_position.row);
|
||||||
|
assert_eq(0, start.pT1.end_position.col);
|
||||||
|
|
||||||
assert_eq(0, start.pT2.pToken.position.row);
|
assert_eq(0, start.pT2.pToken.position.row);
|
||||||
assert_eq(1, start.pT2.pToken.position.col);
|
assert_eq(1, start.pT2.pToken.position.col);
|
||||||
assert_eq(0, start.pT3.pToken.position.row);
|
assert_eq(0, start.pT2.pToken.end_position.row);
|
||||||
assert_eq(2, start.pT3.pToken.position.col);
|
assert_eq(2, start.pT2.pToken.end_position.col);
|
||||||
|
assert_eq(0, start.pT2.position.row);
|
||||||
|
assert_eq(1, start.pT2.position.col);
|
||||||
|
assert_eq(0, start.pT2.end_position.row);
|
||||||
|
assert_eq(2, start.pT2.end_position.col);
|
||||||
|
|
||||||
input = "\n\n a\nc\n\n a";
|
assert_eq(0, start.pT3.pToken.position.row);
|
||||||
|
assert_eq(3, start.pT3.pToken.position.col);
|
||||||
|
assert_eq(0, start.pT3.pToken.end_position.row);
|
||||||
|
assert_eq(5, start.pT3.pToken.end_position.col);
|
||||||
|
assert_eq(0, start.pT3.position.row);
|
||||||
|
assert_eq(3, start.pT3.position.col);
|
||||||
|
assert_eq(0, start.pT3.end_position.row);
|
||||||
|
assert_eq(5, start.pT3.end_position.col);
|
||||||
|
|
||||||
|
assert_eq(0, start.position.row);
|
||||||
|
assert_eq(0, start.position.col);
|
||||||
|
assert_eq(0, start.end_position.row);
|
||||||
|
assert_eq(5, start.end_position.col);
|
||||||
|
|
||||||
|
input = "\n\n bb\nc\ncc\n\n a";
|
||||||
p_context_init(&context, input);
|
p_context_init(&context, input);
|
||||||
assert(p_parse(&context) == P_SUCCESS);
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
start = p_result(&context);
|
start = p_result(&context);
|
||||||
|
|
||||||
assert_eq(2, start.pT1.pToken.position.row);
|
assert_eq(2, start.pT1.pToken.position.row);
|
||||||
assert_eq(2, start.pT1.pToken.position.col);
|
assert_eq(2, start.pT1.pToken.position.col);
|
||||||
|
assert_eq(2, start.pT1.pToken.end_position.row);
|
||||||
|
assert_eq(3, start.pT1.pToken.end_position.col);
|
||||||
|
assert_eq(2, start.pT1.position.row);
|
||||||
|
assert_eq(2, start.pT1.position.col);
|
||||||
|
assert_eq(2, start.pT1.end_position.row);
|
||||||
|
assert_eq(3, start.pT1.end_position.col);
|
||||||
|
|
||||||
assert_eq(3, start.pT2.pToken.position.row);
|
assert_eq(3, start.pT2.pToken.position.row);
|
||||||
assert_eq(0, start.pT2.pToken.position.col);
|
assert_eq(0, start.pT2.pToken.position.col);
|
||||||
assert_eq(5, start.pT3.pToken.position.row);
|
assert_eq(4, start.pT2.pToken.end_position.row);
|
||||||
|
assert_eq(1, start.pT2.pToken.end_position.col);
|
||||||
|
assert_eq(3, start.pT2.position.row);
|
||||||
|
assert_eq(0, start.pT2.position.col);
|
||||||
|
assert_eq(4, start.pT2.end_position.row);
|
||||||
|
assert_eq(1, start.pT2.end_position.col);
|
||||||
|
|
||||||
|
assert_eq(6, start.pT3.pToken.position.row);
|
||||||
assert_eq(5, start.pT3.pToken.position.col);
|
assert_eq(5, start.pT3.pToken.position.col);
|
||||||
|
assert_eq(6, start.pT3.pToken.end_position.row);
|
||||||
|
assert_eq(5, start.pT3.pToken.end_position.col);
|
||||||
|
assert_eq(6, start.pT3.position.row);
|
||||||
|
assert_eq(5, start.pT3.position.col);
|
||||||
|
assert_eq(6, start.pT3.end_position.row);
|
||||||
|
assert_eq(5, start.pT3.end_position.col);
|
||||||
|
|
||||||
|
assert_eq(2, start.position.row);
|
||||||
|
assert_eq(2, start.position.col);
|
||||||
|
assert_eq(6, start.end_position.row);
|
||||||
|
assert_eq(5, start.end_position.col);
|
||||||
}
|
}
|
||||||
|
@ -43,41 +43,57 @@ int main()
|
|||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info.position.row == 0u);
|
assert(token_info.position.row == 0u);
|
||||||
assert(token_info.position.col == 0u);
|
assert(token_info.position.col == 0u);
|
||||||
|
assert(token_info.end_position.row == 0u);
|
||||||
|
assert(token_info.end_position.col == 0u);
|
||||||
assert(token_info.length == 1u);
|
assert(token_info.length == 1u);
|
||||||
assert(token_info.token == TOKEN_int);
|
assert(token_info.token == TOKEN_int);
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info.position.row == 0u);
|
assert(token_info.position.row == 0u);
|
||||||
assert(token_info.position.col == 2u);
|
assert(token_info.position.col == 2u);
|
||||||
|
assert(token_info.end_position.row == 0u);
|
||||||
|
assert(token_info.end_position.col == 2u);
|
||||||
assert(token_info.length == 1u);
|
assert(token_info.length == 1u);
|
||||||
assert(token_info.token == TOKEN_plus);
|
assert(token_info.token == TOKEN_plus);
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info.position.row == 0u);
|
assert(token_info.position.row == 0u);
|
||||||
assert(token_info.position.col == 4u);
|
assert(token_info.position.col == 4u);
|
||||||
|
assert(token_info.end_position.row == 0u);
|
||||||
|
assert(token_info.end_position.col == 4u);
|
||||||
assert(token_info.length == 1u);
|
assert(token_info.length == 1u);
|
||||||
assert(token_info.token == TOKEN_int);
|
assert(token_info.token == TOKEN_int);
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info.position.row == 0u);
|
assert(token_info.position.row == 0u);
|
||||||
assert(token_info.position.col == 6u);
|
assert(token_info.position.col == 6u);
|
||||||
|
assert(token_info.end_position.row == 0u);
|
||||||
|
assert(token_info.end_position.col == 6u);
|
||||||
assert(token_info.length == 1u);
|
assert(token_info.length == 1u);
|
||||||
assert(token_info.token == TOKEN_times);
|
assert(token_info.token == TOKEN_times);
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info.position.row == 1u);
|
assert(token_info.position.row == 1u);
|
||||||
assert(token_info.position.col == 0u);
|
assert(token_info.position.col == 0u);
|
||||||
|
assert(token_info.end_position.row == 1u);
|
||||||
|
assert(token_info.end_position.col == 2u);
|
||||||
assert(token_info.length == 3u);
|
assert(token_info.length == 3u);
|
||||||
assert(token_info.token == TOKEN_int);
|
assert(token_info.token == TOKEN_int);
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info.position.row == 1u);
|
assert(token_info.position.row == 1u);
|
||||||
assert(token_info.position.col == 4u);
|
assert(token_info.position.col == 4u);
|
||||||
|
assert(token_info.end_position.row == 1u);
|
||||||
|
assert(token_info.end_position.col == 4u);
|
||||||
assert(token_info.length == 1u);
|
assert(token_info.length == 1u);
|
||||||
assert(token_info.token == TOKEN_plus);
|
assert(token_info.token == TOKEN_plus);
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info.position.row == 1u);
|
assert(token_info.position.row == 1u);
|
||||||
assert(token_info.position.col == 6u);
|
assert(token_info.position.col == 6u);
|
||||||
|
assert(token_info.end_position.row == 1u);
|
||||||
|
assert(token_info.end_position.col == 8u);
|
||||||
assert(token_info.length == 3u);
|
assert(token_info.length == 3u);
|
||||||
assert(token_info.token == TOKEN_int);
|
assert(token_info.token == TOKEN_int);
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info.position.row == 1u);
|
assert(token_info.position.row == 1u);
|
||||||
assert(token_info.position.col == 9u);
|
assert(token_info.position.col == 9u);
|
||||||
|
assert(token_info.end_position.row == 1u);
|
||||||
|
assert(token_info.end_position.col == 9u);
|
||||||
assert(token_info.length == 0u);
|
assert(token_info.length == 0u);
|
||||||
assert(token_info.token == TOKEN___EOF);
|
assert(token_info.token == TOKEN___EOF);
|
||||||
|
|
||||||
@ -85,6 +101,8 @@ int main()
|
|||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info.position.row == 0u);
|
assert(token_info.position.row == 0u);
|
||||||
assert(token_info.position.col == 0u);
|
assert(token_info.position.col == 0u);
|
||||||
|
assert(token_info.end_position.row == 0u);
|
||||||
|
assert(token_info.end_position.col == 0u);
|
||||||
assert(token_info.length == 0u);
|
assert(token_info.length == 0u);
|
||||||
assert(token_info.token == TOKEN___EOF);
|
assert(token_info.token == TOKEN___EOF);
|
||||||
|
|
||||||
|
@ -47,23 +47,23 @@ unittest
|
|||||||
p_context_t context;
|
p_context_t context;
|
||||||
p_context_init(&context, input);
|
p_context_init(&context, input);
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info == p_token_info_t(p_position_t(0, 0), 1, TOKEN_int));
|
assert(token_info == p_token_info_t(p_position_t(0, 0), p_position_t(0, 0), 1, TOKEN_int));
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info == p_token_info_t(p_position_t(0, 2), 1, TOKEN_plus));
|
assert(token_info == p_token_info_t(p_position_t(0, 2), p_position_t(0, 2), 1, TOKEN_plus));
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info == p_token_info_t(p_position_t(0, 4), 1, TOKEN_int));
|
assert(token_info == p_token_info_t(p_position_t(0, 4), p_position_t(0, 4), 1, TOKEN_int));
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info == p_token_info_t(p_position_t(0, 6), 1, TOKEN_times));
|
assert(token_info == p_token_info_t(p_position_t(0, 6), p_position_t(0, 6), 1, TOKEN_times));
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info == p_token_info_t(p_position_t(1, 0), 3, TOKEN_int));
|
assert(token_info == p_token_info_t(p_position_t(1, 0), p_position_t(1, 2), 3, TOKEN_int));
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info == p_token_info_t(p_position_t(1, 4), 1, TOKEN_plus));
|
assert(token_info == p_token_info_t(p_position_t(1, 4), p_position_t(1, 4), 1, TOKEN_plus));
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info == p_token_info_t(p_position_t(1, 6), 3, TOKEN_int));
|
assert(token_info == p_token_info_t(p_position_t(1, 6), p_position_t(1, 8), 3, TOKEN_int));
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info == p_token_info_t(p_position_t(1, 9), 0, TOKEN___EOF));
|
assert(token_info == p_token_info_t(p_position_t(1, 9), p_position_t(1, 9), 0, TOKEN___EOF));
|
||||||
|
|
||||||
p_context_init(&context, "");
|
p_context_init(&context, "");
|
||||||
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
assert(p_lex(&context, &token_info) == P_SUCCESS);
|
||||||
assert(token_info == p_token_info_t(p_position_t(0, 0), 0, TOKEN___EOF));
|
assert(token_info == p_token_info_t(p_position_t(0, 0), p_position_t(0, 0), 0, TOKEN___EOF));
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user