19 Commits
Author SHA1 Message Date
josh 2e82339300 add docs 2026-04-10 16:29:09 -04:00
josh 0e5b0b1f8a complete adding lex_fn 2026-04-10 16:03:45 -04:00
josh e2db9c95c5 wip 2026-04-09 22:49:21 -04:00
josh e992e6344a Update CHANGELOG for v4.1.0 2026-02-28 22:40:55 -05:00
josh a05a55ceb8 v4.1.0 2026-02-28 22:38:40 -05:00
josh e9ecf33f58 Run valgrind on D targets in addition to C and C++ 2026-02-28 21:50:13 -05:00
josh dd687d0299 Add p_context_delete() and p_tree_delete() for D targets 2026-02-28 21:12:35 -05:00
josh d440d0442d Expand token_user_fields unit test 2026-02-22 09:38:42 -05:00
josh f9b4563f94 v4.0.0 2026-02-21 22:43:47 -05:00
josh 334990840e Update README and CHANGELOG for v4.0.0 2026-02-21 22:19:54 -05:00
josh f584389a29 Reorganize Propane Grammar File user guide section 2026-02-21 22:06:09 -05:00
josh de3fb0d120 Combine free_token_user_fields and free_token_node statements 2026-02-21 21:37:23 -05:00
josh d4ad67c23d Allow user to specify custom token node fields 2026-02-21 21:06:40 -05:00
josh ff61dd05d9 Rename p_free_tree() to p_tree_delete() 2026-02-17 19:24:50 -05:00
josh 6fd5186159 Replace p_context_init() with p_context_new()/p_context_delete() 2026-02-17 18:40:33 -05:00
josh 9f2fe6f84b Add token_node and token_user_fields grammar statements 2026-02-14 21:00:53 -05:00
josh 78adf86103 Allow user-defined context fields 2026-02-13 23:53:18 -05:00
josh f4bc719aed Allow drop patterns to execute lexer user code blocks 2026-02-13 20:45:08 -05:00
josh 43c0f50874 Fix UPGRADING.md link in CHANGELOG.md 2026-02-11 00:34:32 -05:00
86 changed files with 1917 additions and 902 deletions

No files matched your search

+26 -2
View File
@@ -1,3 +1,27 @@
## v4.1.0
### New Features
- Add `p_context_delete()` and `p_tree_delete()` for D targets.
## v4.0.0
### New Features
- Add `context_user_fields` statement to allow custom context user fields.
- Add `token_user_fields` statement to allow custom token user fields.
- Add `on_token_node` statement to allow custom code when constructing token nodes.
- Add `free_token_node` statement to allow custom code when freeing token nodes.
- Add `p_context_delete()`.
- Allow `drop` patterns to execute lexer user code blocks.
### Breaking Changes
- Replace `p_context_init()` with `p_context_new()` and `p_context_delete()`.
- Renamed `p_free_tree()` to `p_tree_delete()`.
- The `free_token_node` statement now takes a user code block instead of a
function name parameter.
## v3.0.0 ## v3.0.0
### New Features ### New Features
@@ -12,9 +36,9 @@
- Document `p_lex()` and `p_token_info_t` in user guide (#37) - Document `p_lex()` and `p_token_info_t` in user guide (#37)
### Breaking changes ### Breaking Changes
- Rename AST generation mode to tree generation mode (see [UPGRADING.md]()) - Rename AST generation mode to tree generation mode (see [UPGRADING.md](UPGRADING.md))
## v2.3.0 ## v2.3.0
+15 -1
View File
@@ -1,6 +1,20 @@
## v4.0.0
### API Changes
- Replace any calls to `p_context_init()` with `p_context_new()`.
- Replace any references to the address of a statically allocated context
structure with the pointer returned from `p_context_init()` (e.g. `&context`
-> `context`).
- Add a call to `p_context_delete()` (for C or C++) after lexing/parsing to
reclaim context memory.
- Rename `p_free_tree()` calls to `p_tree_delete()`.
- Change `free_token_node` statement calls from taking a function name argument
to taking a user code block.
## v3.0.0 ## v3.0.0
### Grammar changes ### Grammar Changes
- Rename `ast;` statement to `tree;`. - Rename `ast;` statement to `tree;`.
- Rename `ast_prefix;` statement to `tree_prefix;`. - Rename `ast_prefix;` statement to `tree_prefix;`.
+57 -25
View File
@@ -43,30 +43,52 @@ const char * <%= @grammar.prefix %>token_names[] = {
*************************************************************************/ *************************************************************************/
/** /**
* Initialize lexer/parser context structure. * Allocate and initialize lexer/parser context structure.
*
* Deinitialize and deallocate with <%= @grammar.prefix %>context_delete().
* *
* @param[out] context
* Lexer/parser context structure.
* @param input * @param input
* Text input. * Text input.
* @param input_length * @param input_length
* Text input length. * Text input length.
*
* @return Context structure for lexer/parser.
*/ */
void <%= @grammar.prefix %>context_init(<%= @grammar.prefix %>context_t * context, uint8_t const * input, size_t input_length) <%= @grammar.prefix %>context_t * <%= @grammar.prefix %>context_new(uint8_t const * input, size_t input_length)
{ {
/* New default-initialized context structure. */ <% if @cpp %>
<%= @grammar.prefix %>context_t newcontext; <%= @grammar.prefix %>context_t * context = new <%= @grammar.prefix %>context_t();
memset(&newcontext, 0, sizeof(newcontext)); <% else %>
<%= @grammar.prefix %>context_t * context = (<%= @grammar.prefix %>context_t *)calloc(1, sizeof(<%= @grammar.prefix %>context_t));
<% end %>
/* Lexer initialization. */ /* Lexer initialization. */
newcontext.input = input; context->input = input;
newcontext.input_length = input_length; context->input_length = input_length;
newcontext.text_position.row = 1u; context->text_position.row = 1u;
newcontext.text_position.col = 1u; context->text_position.col = 1u;
newcontext.mode = <%= @lexer.mode_id("default") %>; context->mode = <%= @lexer.mode_id("default") %>;
/* Copy to the user's context structure. */ return context;
*context = newcontext; }
/**
* Deinitialize and deallocate lexer/parser context structure.
*
* For C++, destructors will be called for any context user fields. However, if
* pointers are used to store allocated resources, the user should free them
* before calling this function.
*
* @param context
* Lexer/parser context structure allocated with <%= @grammar.prefix %>context_new().
*/
void <%= @grammar.prefix %>context_delete(<%= @grammar.prefix %>context_t * context)
{
<% if @cpp %>
delete context;
<% else %>
free(context);
<% end %>
} }
/************************************************************************** /**************************************************************************
@@ -694,6 +716,7 @@ typedef struct
<% end %> <% end %>
} state_value_t; } state_value_t;
<% if @grammar.tree %>
/** Common tree node structure. */ /** Common tree node structure. */
typedef struct TreeNode_s typedef struct TreeNode_s
{ {
@@ -703,6 +726,7 @@ typedef struct TreeNode_s
uint8_t is_token; uint8_t is_token;
struct TreeNode_s * fields[]; struct TreeNode_s * fields[];
} TreeNode; } TreeNode;
<% end %>
/** Parser shift table. */ /** Parser shift table. */
static const shift_t parser_shift_table[] = { static const shift_t parser_shift_table[] = {
@@ -957,7 +981,7 @@ static size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t start
{ {
if (token == INVALID_TOKEN_ID) if (token == INVALID_TOKEN_ID)
{ {
size_t lexer_result = <%= @grammar.prefix %>lex(context, &token_info); size_t lexer_result = <%= lex_fn %>(context, &token_info);
if (lexer_result != P_SUCCESS) if (lexer_result != P_SUCCESS)
{ {
result = lexer_result; result = lexer_result;
@@ -994,13 +1018,18 @@ 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.tree_prefix %>Token<%= @grammar.tree_suffix %> * token_tree_node = new <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>();
<% 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 %>)); <%= @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;
token_tree_node->is_token = 1u; token_tree_node->is_token = 1u;
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) %>
state_values_stack_index(&statevalues, -1)->tree_node = token_tree_node; state_values_stack_index(&statevalues, -1)->tree_node = token_tree_node;
<% else %> <% else %>
state_values_stack_index(&statevalues, -1)->pvalue = token_info.pvalue; state_values_stack_index(&statevalues, -1)->pvalue = token_info.pvalue;
@@ -1186,14 +1215,17 @@ size_t <%= @grammar.prefix %>user_terminate_code(<%= @grammar.prefix %>context_t
} }
<% if @grammar.tree %> <% if @grammar.tree %>
static void free_tree_node(TreeNode * node) static void tree_delete(TreeNode * node)
{ {
if (node->is_token) if (node->is_token)
{ {
<% if @grammar.free_token_node %> <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %> * token_tree_node = (<%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %> *)node;
<%= @grammar.free_token_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 %> <% end %>
/* TODO: free value_t */
} }
else if (node->n_fields > 0u) else if (node->n_fields > 0u)
{ {
@@ -1201,28 +1233,28 @@ static void free_tree_node(TreeNode * node)
{ {
if (node->fields[i] != NULL) if (node->fields[i] != NULL)
{ {
free_tree_node(node->fields[i]); tree_delete(node->fields[i]);
}
} }
} }
free(node); free(node);
} }
}
/** /**
* Free all tree node memory. * Free all tree node memory.
*/ */
void <%= @grammar.prefix %>free_tree(<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * tree) void <%= @grammar.prefix %>tree_delete(<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * tree)
{ {
free_tree_node((TreeNode *)tree); tree_delete((TreeNode *)tree);
} }
<% @grammar.start_rules.each_with_index do |start_rule, i| %> <% @grammar.start_rules.each_with_index do |start_rule, i| %>
/** /**
* Free all tree node memory. * Free all tree node memory.
*/ */
void <%= @grammar.prefix %>free_tree_<%= start_rule %>(<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * tree) void <%= @grammar.prefix %>tree_delete_<%= start_rule %>(<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * tree)
{ {
free_tree_node((TreeNode *)tree); tree_delete((TreeNode *)tree);
} }
<% end %> <% end %>
<% end %> <% end %>
+76 -14
View File
@@ -9,7 +9,7 @@ module <%= @grammar.modulename %>;
<% end %> <% end %>
import core.memory; import core.memory;
import core.stdc.stdlib : malloc; import core.stdc.stdlib : malloc, free;
/************************************************************************** /**************************************************************************
* User code blocks * User code blocks
@@ -86,6 +86,14 @@ public union <%= @grammar.prefix %>value_t
<%= typestring %> v_<%= name %>; <%= typestring %> v_<%= name %>;
<% end %> <% end %>
} }
/** Parser value constructor(s). */
<% @grammar.ptypes.each do |name, typestring| %>
public <%= @grammar.prefix %>value_t <%= @grammar.prefix %>value<%= name == "default" ? "" : "_#{name}" %>(T)(T v)
{
return <%= @grammar.prefix %>value_t(v_<%= name %>: v);
}
<% end %>
<% end %> <% end %>
<% if @grammar.tree %> <% if @grammar.tree %>
@@ -94,6 +102,8 @@ private struct TreeNode
{ {
<%= @grammar.prefix %>position_t position; <%= @grammar.prefix %>position_t position;
<%= @grammar.prefix %>position_t end_position; <%= @grammar.prefix %>position_t end_position;
ushort n_fields;
bool is_token;
void *[0] fields; void *[0] fields;
} }
@@ -103,8 +113,11 @@ public struct <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>
/* TreeNode fields must be present in the same order here. */ /* 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;
ushort n_fields;
bool is_token;
<%= @grammar.prefix %>token_t token; <%= @grammar.prefix %>token_t token;
<%= @grammar.prefix %>value_t pvalue; <%= @grammar.prefix %>value_t pvalue;
<%= @grammar.token_user_fields %>
} }
<% @parser.rule_sets.each do |name, rule_set| %> <% @parser.rule_sets.each do |name, rule_set| %>
@@ -114,6 +127,8 @@ public struct <%= @grammar.tree_prefix %><%= name %><%= @grammar.tree_suffix %>
{ {
<%= @grammar.prefix %>position_t position; <%= @grammar.prefix %>position_t position;
<%= @grammar.prefix %>position_t end_position; <%= @grammar.prefix %>position_t end_position;
ushort n_fields;
bool is_token;
<% rule_set.tree_fields.each do |fields| %> <% rule_set.tree_fields.each do |fields| %>
union union
{ {
@@ -183,6 +198,8 @@ public struct <%= @grammar.prefix %>context_t
/** User terminate code. */ /** User terminate code. */
size_t user_terminate_code; size_t user_terminate_code;
<%= @grammar.context_user_fields %>
} }
/************************************************************************** /**************************************************************************
@@ -222,26 +239,39 @@ private enum size_t INVALID_ID = cast(size_t)-1;
*************************************************************************/ *************************************************************************/
/** /**
* Initialize lexer/parser context structure. * Allocate and initialize lexer/parser context structure.
*
* Deinitialize and deallocate with <%= @grammar.prefix %>context_delete().
* *
* @param[out] context
* Lexer/parser context structure.
* @param input * @param input
* Text input. * Text input.
* @param input_length
* Text input length.
*
* @return Context structure for lexer/parser.
*/ */
public void <%= @grammar.prefix %>context_init(<%= @grammar.prefix %>context_t * context, string input) <%= @grammar.prefix %>context_t * <%= @grammar.prefix %>context_new(string input)
{ {
/* New default-initialized context structure. */ /* New default-initialized context structure. */
<%= @grammar.prefix %>context_t newcontext; <%= @grammar.prefix %>context_t * context = new <%= @grammar.prefix %>context_t;
/* Lexer initialization. */ /* Lexer initialization. */
newcontext.input = input; context.input = input;
newcontext.text_position.row = 1u; context.text_position.row = 1u;
newcontext.text_position.col = 1u; context.text_position.col = 1u;
newcontext.mode = <%= @lexer.mode_id("default") %>; context.mode = <%= @lexer.mode_id("default") %>;
/* Copy to the user's context structure. */ return context;
*context = newcontext; }
/**
* Deinitialize and deallocate lexer/parser context structure.
*
* @param context
* Lexer/parser context structure allocated with <%= @grammar.prefix %>context_new().
*/
void <%= @grammar.prefix %>context_delete(<%= @grammar.prefix %>context_t * context)
{
} }
/************************************************************************** /**************************************************************************
@@ -1015,7 +1045,7 @@ private size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t star
{ {
if (token == INVALID_TOKEN_ID) if (token == INVALID_TOKEN_ID)
{ {
size_t lexer_result = <%= @grammar.prefix %>lex(context, &token_info); size_t lexer_result = <%= lex_fn %>(context, &token_info);
if (lexer_result != P_SUCCESS) if (lexer_result != P_SUCCESS)
{ {
return lexer_result; return lexer_result;
@@ -1049,7 +1079,8 @@ 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, token, token_info.pvalue); <%= @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);
<%= expand_code(@grammar.on_token_node, false, nil, nil) %>
statevalues[$-1].tree_node = token_tree_node; statevalues[$-1].tree_node = token_tree_node;
<% else %> <% else %>
statevalues[$-1].pvalue = token_info.pvalue; statevalues[$-1].pvalue = token_info.pvalue;
@@ -1088,6 +1119,8 @@ private size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t star
GC.addRange(node, node_size); GC.addRange(node, node_size);
node.position = <%= @grammar.prefix %>position_t.INVALID; node.position = <%= @grammar.prefix %>position_t.INVALID;
node.end_position = <%= @grammar.prefix %>position_t.INVALID; node.end_position = <%= @grammar.prefix %>position_t.INVALID;
node.n_fields = cast(ushort)n_fields;
node.is_token = false;
foreach (i; 0..n_fields) foreach (i; 0..n_fields)
{ {
node.fields[i] = null; node.fields[i] = null;
@@ -1195,6 +1228,35 @@ 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.
* *
+16 -3
View File
@@ -69,6 +69,14 @@ typedef union
<%= typestring %> v_<%= name %>; <%= typestring %> v_<%= name %>;
<% end %> <% end %>
} <%= @grammar.prefix %>value_t; } <%= @grammar.prefix %>value_t;
/** Parser value constructor(s). */
<% @grammar.ptypes.each do |name, typestring| %>
static inline <%= @grammar.prefix %>value_t <%= @grammar.prefix %>value<%= name == "default" ? "" : "_#{name}" %>(<%= typestring %> v)
{
return (<%= @grammar.prefix %>value_t){.v_<%= name %> = v};
}
<% end %>
<% end %> <% end %>
<% if @grammar.tree %> <% if @grammar.tree %>
@@ -80,6 +88,7 @@ typedef struct <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>
<%= @grammar.prefix %>position_t end_position; <%= @grammar.prefix %>position_t end_position;
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.tree_prefix %>Token<%= @grammar.tree_suffix %>;
@@ -172,6 +181,8 @@ typedef struct
/** User terminate code. */ /** User terminate code. */
size_t user_terminate_code; size_t user_terminate_code;
<%= @grammar.context_user_fields %>
} <%= @grammar.prefix %>context_t; } <%= @grammar.prefix %>context_t;
/************************************************************************** /**************************************************************************
@@ -181,7 +192,9 @@ typedef struct
/** Token names. */ /** Token names. */
extern const char * <%= @grammar.prefix %>token_names[]; extern const char * <%= @grammar.prefix %>token_names[];
void <%= @grammar.prefix %>context_init(<%= @grammar.prefix %>context_t * context, uint8_t const * input, size_t input_length); <%= @grammar.prefix %>context_t * <%= @grammar.prefix %>context_new(uint8_t const * input, size_t input_length);
void <%= @grammar.prefix %>context_delete(<%= @grammar.prefix %>context_t * context);
size_t <%= @grammar.prefix %>decode_code_point(uint8_t const * input, size_t input_length, size_t <%= @grammar.prefix %>decode_code_point(uint8_t const * input, size_t input_length,
<%= @grammar.prefix %>code_point_t * out_code_point, uint8_t * out_code_point_length); <%= @grammar.prefix %>code_point_t * out_code_point, uint8_t * out_code_point_length);
@@ -206,9 +219,9 @@ size_t <%= @grammar.prefix %>parse_<%= start_rule %>(<%= @grammar.prefix %>conte
<% end %> <% end %>
<% if @grammar.tree %> <% if @grammar.tree %>
void <%= @grammar.prefix %>free_tree(<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * 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| %> <% @grammar.start_rules.each_with_index do |start_rule, i| %>
void <%= @grammar.prefix %>free_tree_<%= start_rule %>(<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * tree); void <%= @grammar.prefix %>tree_delete_<%= start_rule %>(<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * tree);
<% end %> <% end %>
<% end %> <% end %>
+509 -291
View File
@@ -151,8 +151,35 @@ needed by a `ptype` directive, for example:
### Lexer pattern code blocks ### Lexer pattern code blocks
#### C/C++
The lexer code block is passed the following arguments:
* `match` - a pointer points to the text matched by the lexer pattern.
* `match_length` - length of the matched text.
Example: Example:
```
ptype long;
token integer /\d+/ <<
long v = 0;
for (size_t i = 0u; i < match_length; i++)
{
v *= 10;
v += (match[i] - '0');
}
$$ = v;
>>
```
#### D
The lexer code block is passed the following arguments:
* `match` - a slice containing the text matched by the lexer pattern.
``` ```
ptype ulong; ptype ulong;
@@ -167,7 +194,7 @@ token integer /\d+/ <<
>> >>
``` ```
Lexer code blocks appear following a `token` or pattern expression. Lexer code blocks appear following a `drop`, `token`, or pattern expression.
User code in a lexer code block will be executed when the lexer matches the User code in a lexer code block will be executed when the lexer matches the
given pattern. given pattern.
Assignment to the `$$` symbol will associate a parser value with the lexed Assignment to the `$$` symbol will associate a parser value with the lexed
@@ -194,7 +221,409 @@ Parser rule code blocks are not available in tree generation mode.
In tree generation mode, a full parse tree is automatically constructed in In tree generation mode, a full parse tree is automatically constructed in
memory for user code to traverse after parsing is complete. memory for user code to traverse after parsing is complete.
##> Tree generation mode - the `tree` statement ##> `context_user_fields` statement - adding custom fields to the context
Propane uses a context structure for lexer and parser operations.
Custom fields may be added to the context structure by using the grammar
`context_user_fields` statement.
This allows lexer pattern or parser rule code blocks to access user-defined
fields within the context structure.
Example:
```
context_user_fields <<
int mycontextval;
>>
```
Lexer user code blocks or parser user code blocks can access user-defined
context fields by using the `${context.<field>}` syntax.
C++ example:
```
context_user_fields <<
std::string comments;
>>
drop /#(.*)\n/ <<
/* Accumulate comments before the next parser tree node. */
${context.comments} += std::string((const char *)match, match_length);
>>
```
If a pointer to any allocated memory is stored in a user-defined context field,
it is up to the user to free any memory when the program is finished using the
context structure.
##> `drop` statement - ignoring input patterns
A `drop` statement can be used to specify a lexer pattern that when matched
should result in the matched input being dropped and lexing continuing after
the matched input.
A common use for a `drop` statement would be to ignore whitespace sequences in
the user input.
Example:
```
drop /\s+/;
```
See also ${#Regular expression syntax}.
## `free_token_node` statement - freeing user-allocated memory in token node fields
If user lexer code block allocates memory to store in a token node's `pvalue`
or any custom token user fields store pointers to allocated memory, the
`free_token_node` grammar statement can be used to provide a code block which
can be used to free memory properly.
Example freeing `pvalue` (C):
```
tree;
free_token_node <<
free(${token.pvalue});
>>
ptype int *;
token a <<
$$ = (int *)malloc(sizeof(int));
*$$ = 1;
>>
token b <<
$$ = (int *)malloc(sizeof(int));
*$$ = 2;
>>
Start -> a:a b:b;
```
Example freeing custom token user fields (C):
```
token_user_fields <<
char * comments;
>>
on_token_node <<
${token.comments} = (char *)malloc(some_len);
>>
free_token_node <<
free(${token.comments});
>>
```
The `free_token_node` statement user code block is not emitted for D language
since D has a garbage collector.
##> `lex_fn` statement - specifying a custom lexer function
Propane generates both a lexer and a parser.
By default, the parser uses the generated `p_lex()` function directly to
return information for the next lexed token from the input stream.
However, the user can specify a custom lex function.
This function may or may not use the Propane generated `p_lex()` function under
the hood.
For example, a token sequence could be injected or repeated from a previously
saved macro definition.
Example (C/C++):
```
<<
static size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
{
static size_t count;
size_t result = P_SUCCESS;
if (count > 0)
{
out_token_info->token = TOKEN_a;
out_token_info->pvalue = p_value(count);
count--;
}
else
{
result = p_lex(context, out_token_info);
if (out_token_info->token == TOKEN_c)
{
count = 3;
}
}
return result;
}
>>
lex_fn mylexfn;
```
The `lex_fn` statement takes one argument specifying the name of the custom
lexer function.
The user must supply a value for the `token` field of the `p_token_info_t`
output structure so that the parser knows what token was lexed.
Additionally, if the parser user code makes use of the token's pvalue, then
the lexer function must supply a value for the `pvalue` field of the
`p_token_info_t` structure.
The `p_value()` generated API function could be useful for specifying
parser values to associate with the lexed token when tree generation is not
enabled.
When tree generation is enabled, the `pvalue` field can be set to an instance
of whatever type the user has defined as the `ptype` type.
##> `module` statement - specifying the generated parser module name
The `module` statement can be used to specify the module name for a generated
D module.
```
module proj.parser;
```
If a module statement is not present, then the generated D module will not
contain a module statement and the default module name will be used.
##> `on_tree_node` statement - custom initialization of a token tree node
The `on_token_node` statement can be used to provide code that initializes
any token user fields when a token tree node instance is created.
For example (C++):
```
context_user_fields <<
std::string comments;
>>
token_user_fields <<
std::string comments;
>>
on_token_node <<
${token.comments} = ${context.comments};
${context.comments} = "";
>>
drop /#(.*)\n/ <<
/* Accumulate comments before the next parser tree node. */
${context.comments} += std::string((const char *)match, match_length);
>>
```
##> `prefix` statement - specifying the generated API prefix
By default the public API (types, constants, and functions) of the generated
lexer and parser uses a prefix of `p_`.
This prefix can be changed with the `prefix` statement.
Example:
```
prefix myparser_;
```
With a parser generated with this `prefix` statement, instead of calling
`p_context_new()` you would call `myparser_context_new()`.
The `prefix` statement can be optionally used if you would like to change the
prefix used by your generated lexer and parser to something other than the
default.
It can also be used when generating multiple lexers/parsers to be used in the
same program to avoid symbol collisions.
##> `ptype` statement - specifying parser value types
The `ptype` statement is used to define parser value type(s).
Example:
```
ptype void *;
```
This defines the default parser value type to be `void *` (this is, in fact,
the default parser value type if the grammar file does not specify otherwise).
Each defined lexer token type and parser rule has an associated parser value
type.
When the lexer runs, each lexed token has a parser value associated with it.
When the parser runs, each instance of a reduced rule has a parser value
associated with it.
Propane supports using different parser value types for different rules and
token types.
The example `ptype` statement above defines the default parser value type.
A parser value type name can optionally be specified following the `ptype`
keyword.
For example:
```
ptype Value;
ptype array = Value[];
ptype dict = Value[string];
Object -> lbrace rbrace << $$ = new Value(); >>
Values (array) -> Value << $$ = [$1]; >>
Values -> Values comma Value << $$ = $1 ~ [$3]; >>
KeyValue (dict) -> string colon Value << $$ = [$1: $3]; >>
```
In this example, the default parser value type is `Value`.
A parser value type named `array` is defined to mean `Value[]`.
A parser value type named `dict` is defined to mean `Value[string]`.
Any defined tokens or rules that do not specify a parser value type will have
the default parser value type associated with them.
To associate a different parser value type with a token or rule, write the
parser value type name in parentheses following the name of the token or rule.
In this example:
* a reduced `Object`'s parser value has a type of `Value`.
* a reduced `Values`'s parser value has a type of `Value[]`.
* a reduced `KeyValue`'s parser value has a type of `Value[string]`.
When tree generation mode is active, the `ptype` functionality works differently.
In this mode, only one `ptype` is used by the parser.
Lexer user code blocks may assign a parse value to the generated `Token` node
by assigning to `$$` within a lexer code block.
The type of the parse value `$$` is given by the global `ptype` type.
##> `start` statement - specifying the parser start rule name
The start rule can be changed from the default of `Start` by using the `start`
statement.
Example:
```
start MyStartRule;
```
Multiple start rules can be specified, either with multiple `start` statements
or one `start` statement listing multiple start rules.
Example:
```
start Module ModuleItem Statement Expression;
```
When multiple start rules are specified, multiple `p_parse_*()` functions,
`p_result_*()`, and `p_tree_delete_*()` functions (in tree mode) are generated.
A default `p_parse()`, `p_result()`, `p_tree_delete()` are generated corresponding
to the first start rule.
Additionally, each start rule causes the generation of another version of each
of these functions, for example `p_parse_Statement()`, `p_result_Statement()`,
and `p_tree_delete_Statement()`.
##> `token` statement - specifying tokens
The `token` statement allows defining a lexer token and a pattern to match that
token.
The name of the token must be specified immediately following the `token`
keyword.
A regular expression pattern may optionally follow the token name.
If a regular expression pattern is not specified, the name of the token is
taken to be the pattern.
See also: ${#Regular expression syntax}.
Example:
```
token for;
```
In this example, the token name is `for` and the pattern to match it is
`/for/`.
Example:
```
token lbrace /\{/;
```
In this example, the token name is `lbrace` and a single left curly brace will
match it.
The `token` statement can also include a user code block.
The user code block will be executed whenever the token is matched by the
lexer.
Example:
```
token if << writeln("'if' keyword lexed"); >>
```
The `token` statement is actually a shortcut statement for a combination of a
`tokenid` statement and a pattern statement.
To define a lexer token without an associated pattern to match it, use a
`tokenid` statement.
To define a lexer pattern that may or may not result in a matched token, use
a pattern statement.
##> `tokenid` statement - defining tokens without a matching pattern
The `tokenid` statement can be used to define a token without associating it
with a lexer pattern that matches it.
Example:
```
tokenid string;
```
The `tokenid` statement can be useful when defining a token that may optionally
be returned by user code associated with a pattern.
It is also useful when lexer modes and multiple lexer patterns are required to
build up a full token.
A common example is parsing a string.
See the ${#Lexer modes} chapter for more information.
##> `token_user_fields` statement - adding custom token fields
When tree generation mode is active, Propane generates a tree node structure
and a token node structure for each matching rule and token instance in the
input string.
The user may add custom fields to token tree nodes using the `token_user_fields`
statement.
The code block supplied to the `token_user_fields` is inserted in the `struct`
generated by the parser to hold a token tree node.
Example (D/C++):
```
token_user_fields <<
string mytokenval;
>>
```
The `on_token_node` statement can be used to provide code that initializes
any token user fields when a token tree node instance is created.
For example (C++):
```
context_user_fields <<
std::string comments;
>>
token_user_fields <<
std::string comments;
>>
on_token_node <<
${token.comments} = ${context.comments};
${context.comments} = "";
>>
drop /#(.*)\n/ <<
/* Accumulate comments before the next parser tree node. */
${context.comments} += std::string((const char *)match, match_length);
>>
```
If a pointer to any allocated memory is stored in a user-defined context field,
the `free_token_node` statement can be used to supply a code block which
will be executed immediately before the token node is freed.
For C++, the `delete` statement is used to free the token tree node, so the
destructor for any custom token user fields will be called.
##> `tree` statement - tree generation mode
To activate tree generation mode, place the `tree` statement in your grammar file: To activate tree generation mode, place the `tree` statement in your grammar file:
@@ -257,10 +686,9 @@ example parse:
``` ```
string input = "a, ((b)), b"; string input = "a, ((b)), b";
p_context_t context; p_context_t * context = p_context_new(input);
p_context_init(&context, 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 !is null);
assert(start.pItems !is null); assert(start.pItems !is null);
Items * items = start.pItems; Items * items = start.pItems;
@@ -308,10 +736,9 @@ Then the types would be used as such instead:
``` ```
string input = "a, ((b)), b"; string input = "a, ((b)), b";
p_context_t context; p_context_t * context = p_context_new(input);
p_context_init(&context, input); assert_eq(P_SUCCESS, p_parse(context));
assert_eq(P_SUCCESS, p_parse(&context)); ABCStartXYZ * start = p_result(context);
ABCStartXYZ * start = p_result(&context);
assert(start.pItems1 !is null); assert(start.pItems1 !is null);
assert(start.pItems !is null); assert(start.pItems !is null);
ABCItemsXYZ * items = start.pItems; ABCItemsXYZ * items = start.pItems;
@@ -327,102 +754,7 @@ assert(itemsmore.pItem.pItem.pItem !is null);
assert(itemsmore.pItem.pItem.pItem.pToken1 !is null); assert(itemsmore.pItem.pItem.pItem.pToken1 !is null);
``` ```
## Freeing user-allocated memory in token node `pvalue`: the `free_token_node` statement ##> Specifying a lexer pattern
If user lexer code block allocates memory to store in a token node's `pvalue`,
the `free_token_node` grammar statement can be used to specify the name of a
function which will be called during the `p_free_tree()` call to free the memory
associated with a token node.
Example:
```
<<
static void free_token(Token * token)
{
free(token->pvalue);
}
>>
tree;
free_token_node free_token;
ptype int *;
token a <<
$$ = (int *)malloc(sizeof(int));
*$$ = 1;
>>
token b <<
$$ = (int *)malloc(sizeof(int));
*$$ = 2;
>>
Start -> a:a b:b;
```
##> Specifying tokens - the `token` statement
The `token` statement allows defining a lexer token and a pattern to match that
token.
The name of the token must be specified immediately following the `token`
keyword.
A regular expression pattern may optionally follow the token name.
If a regular expression pattern is not specified, the name of the token is
taken to be the pattern.
See also: ${#Regular expression syntax}.
Example:
```
token for;
```
In this example, the token name is `for` and the pattern to match it is
`/for/`.
Example:
```
token lbrace /\{/;
```
In this example, the token name is `lbrace` and a single left curly brace will
match it.
The `token` statement can also include a user code block.
The user code block will be executed whenever the token is matched by the
lexer.
Example:
```
token if << writeln("'if' keyword lexed"); >>
```
The `token` statement is actually a shortcut statement for a combination of a
`tokenid` statement and a pattern statement.
To define a lexer token without an associated pattern to match it, use a
`tokenid` statement.
To define a lexer pattern that may or may not result in a matched token, use
a pattern statement.
##> Defining tokens without a matching pattern - the `tokenid` statement
The `tokenid` statement can be used to define a token without associating it
with a lexer pattern that matches it.
Example:
```
tokenid string;
```
The `tokenid` statement can be useful when defining a token that may optionally
be returned by user code associated with a pattern.
It is also useful when lexer modes and multiple lexer patterns are required to
build up a full token.
A common example is parsing a string.
See the ${#Lexer modes} chapter for more information.
##> Specifying a lexer pattern - the pattern statement
A pattern statement is used to define a lexer pattern that can execute user A pattern statement is used to define a lexer pattern that can execute user
code but may not result in a matched token. code but may not result in a matched token.
@@ -437,23 +769,6 @@ This can be especially useful with ${#Lexer modes}.
See also ${#Regular expression syntax}. See also ${#Regular expression syntax}.
##> Ignoring input sections - the `drop` statement
A `drop` statement can be used to specify a lexer pattern that when matched
should result in the matched input being dropped and lexing continuing after
the matched input.
A common use for a `drop` statement would be to ignore whitespace sequences in
the user input.
Example:
```
drop /\s+/;
```
See also ${#Regular expression syntax}.
##> Regular expression syntax ##> Regular expression syntax
A regular expression ("regex") is used to define lexer patterns in `token`, A regular expression ("regex") is used to define lexer patterns in `token`,
@@ -592,63 +907,7 @@ token dot /\./ <<
default, identonly: drop /\s+/; default, identonly: drop /\s+/;
``` ```
##> Specifying parser value types - the `ptype` statement ##> Specifying parser rules
The `ptype` statement is used to define parser value type(s).
Example:
```
ptype void *;
```
This defines the default parser value type to be `void *` (this is, in fact,
the default parser value type if the grammar file does not specify otherwise).
Each defined lexer token type and parser rule has an associated parser value
type.
When the lexer runs, each lexed token has a parser value associated with it.
When the parser runs, each instance of a reduced rule has a parser value
associated with it.
Propane supports using different parser value types for different rules and
token types.
The example `ptype` statement above defines the default parser value type.
A parser value type name can optionally be specified following the `ptype`
keyword.
For example:
```
ptype Value;
ptype array = Value[];
ptype dict = Value[string];
Object -> lbrace rbrace << $$ = new Value(); >>
Values (array) -> Value << $$ = [$1]; >>
Values -> Values comma Value << $$ = $1 ~ [$3]; >>
KeyValue (dict) -> string colon Value << $$ = [$1: $3]; >>
```
In this example, the default parser value type is `Value`.
A parser value type named `array` is defined to mean `Value[]`.
A parser value type named `dict` is defined to mean `Value[string]`.
Any defined tokens or rules that do not specify a parser value type will have
the default parser value type associated with them.
To associate a different parser value type with a token or rule, write the
parser value type name in parentheses following the name of the token or rule.
In this example:
* a reduced `Object`'s parser value has a type of `Value`.
* a reduced `Values`'s parser value has a type of `Value[]`.
* a reduced `KeyValue`'s parser value has a type of `Value[string]`.
When tree generation mode is active, the `ptype` functionality works differently.
In this mode, only one `ptype` is used by the parser.
Lexer user code blocks may assign a parse value to the generated `Token` node
by assigning to `$$` within a lexer code block.
The type of the parse value `$$` is given by the global `ptype` type.
##> Specifying a parser rule - the rule statement
Rule statements create parser rules which define the grammar that will be Rule statements create parser rules which define the grammar that will be
parsed by the generated parser. parsed by the generated parser.
@@ -729,67 +988,6 @@ can be used to produce the parser value for the accepted rule.
Parser rule code blocks are not allowed and not used when tree generation mode Parser rule code blocks are not allowed and not used when tree generation mode
is active. is active.
##> Specifying the parser start rule name - the `start` statement
The start rule can be changed from the default of `Start` by using the `start`
statement.
Example:
```
start MyStartRule;
```
Multiple start rules can be specified, either with multiple `start` statements
or one `start` statement listing multiple start rules.
Example:
```
start Module ModuleItem Statement Expression;
```
When multiple start rules are specified, multiple `p_parse_*()` functions,
`p_result_*()`, and `p_free_tree_*()` functions (in tree mode) are generated.
A default `p_parse()`, `p_result()`, `p_free_tree()` are generated corresponding
to the first start rule.
Additionally, each start rule causes the generation of another version of each
of these functions, for example `p_parse_Statement()`, `p_result_Statement()`,
and `p_free_tree_Statement()`.
##> Specifying the parser module name - the `module` statement
The `module` statement can be used to specify the module name for a generated
D module.
```
module proj.parser;
```
If a module statement is not present, then the generated D module will not
contain a module statement and the default module name will be used.
##> Specifying the generated API prefix - the `prefix` statement
By default the public API (types, constants, and functions) of the generated
lexer and parser uses a prefix of `p_`.
This prefix can be changed with the `prefix` statement.
Example:
```
prefix myparser_;
```
With a parser generated with this `prefix` statement, instead of calling
`p_context_init()` you would call `myparser_context_init()`.
The `prefix` statement can be optionally used if you would like to change the
prefix used by your generated lexer and parser to something other than the
default.
It can also be used when generating multiple lexers/parsers to be used in the
same program to avoid symbol collisions.
##> User termination of the lexer or parser ##> User termination of the lexer or parser
Propane supports allowing lexer or parser user code blocks to terminate Propane supports allowing lexer or parser user code blocks to terminate
@@ -864,6 +1062,17 @@ 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 calling `p_position_valid(pos)` where `pos` is a `p_position_t` structure
instance. instance.
### `p_value_t`
If tree generation mode is enabled, the `p_value_t` type is defined to be the
type given to the `ptype` statement in the grammar file.
If tree generation mode is not enabled, there could be more than one `ptype`
given, so the `p_value_t` type is a union of all possible `ptype` types.
In this case, the API functions `p_value()` and `p_value_XXX()` for each given
`ptype` name `XXX` are generated to return `p_value_t` instances holding the
corresponding `ptype`.
### `p_token_info_t` ### `p_token_info_t`
The `p_token_info_t` structure contains the following fields: The `p_token_info_t` structure contains the following fields:
@@ -963,27 +1172,30 @@ fields `pExpB`, `pExpB3`, and `right` will all point to the same child node
##> Functions ##> Functions
### `p_context_init` ### `p_context_new`
The `p_context_init()` function must be called to initialize the context The `p_context_new()` function must be called to allocate and initialize the
structure. context structure.
The input to be used for lexing/parsing is passed in when initializing the The input to be used for lexing/parsing is passed in when initializing the
context structure. context structure.
C example: C example:
``` ```
p_context_t context; p_context_t * context = p_context_new(input, input_length);
p_context_init(&context, input, input_length);
``` ```
D example: D example:
``` ```
p_context_t context; p_context_t * context = p_context_new(input);
p_context_init(&context, input);
``` ```
### `p_context_delete`
The `p_context_delete()` function must be called to deinitialize and deallocate
a context structure allocated by `p_context_init()`.
### `p_lex` ### `p_lex`
The `p_lex()` function is the main entry point to the lexer. The `p_lex()` function is the main entry point to the lexer.
@@ -995,10 +1207,9 @@ generated lexer in a standalone mode.
Example: Example:
``` ```
p_context_t context; p_context_t * context = p_context_new(input, input_length);
p_context_init(&context, input, input_length);
p_token_info_t token_info; p_token_info_t token_info;
size_t result = p_lex(&context, &token_info); size_t result = p_lex(context, &token_info);
switch (result) switch (result)
{ {
case P_DECODE_ERROR: case P_DECODE_ERROR:
@@ -1030,9 +1241,8 @@ It must be passed a pointer to an initialized context structure.
Example: Example:
``` ```
p_context_t context; p_context_t * context = p_context_new(input, input_length);
p_context_init(&context, input, input_length); size_t result = p_parse(context);
size_t result = p_parse(&context);
``` ```
When multiple start rules are specified, a separate parse function is generated When multiple start rules are specified, a separate parse function is generated
@@ -1040,7 +1250,7 @@ for each which starts parsing at the given rule.
For example, if `Statement` is specified as a start rule: For example, if `Statement` is specified as a start rule:
``` ```
size_t result = p_parse_Statement(&context); size_t result = p_parse_Statement(context);
``` ```
In this case, the parser will start parsing with the `Statement` rule. In this case, the parser will start parsing with the `Statement` rule.
@@ -1071,12 +1281,11 @@ The `p_result()` function can be used to retrieve the final parse value after
Example: Example:
``` ```
p_context_t context; p_context_t * context = p_context_new(input, input_length);
p_context_init(&context, input, input_length); size_t result = p_parse(context);
size_t result = p_parse(&context); if (p_parse(context) == P_SUCCESS)
if (p_parse(&context) == P_SUCCESS)
{ {
result = p_result(&context); result = p_result(context);
} }
``` ```
@@ -1088,12 +1297,11 @@ for each which returns the parse result for the corresponding rule.
For example, if `Statement` is specified as a start rule: For example, if `Statement` is specified as a start rule:
``` ```
p_context_t context; p_context_t * context = p_context_new(input, input_length);
p_context_init(&context, input, input_length); size_t result = p_parse(context);
size_t result = p_parse(&context); if (p_parse_Statement(context) == P_SUCCESS)
if (p_parse_Statement(&context) == P_SUCCESS)
{ {
result = p_result_Statement(&context); result = p_result_Statement(context);
} }
``` ```
@@ -1108,12 +1316,11 @@ an error occurred.
Example: Example:
``` ```
p_context_t context; p_context_t * context = p_context_new(input, input_length);
p_context_init(&context, input, input_length); size_t result = p_parse(context);
size_t result = p_parse(&context); if (p_parse(context) == P_UNEXPECTED_TOKEN)
if (p_parse(&context) == P_UNEXPECTED_TOKEN)
{ {
p_position_t error_position = p_position(&context); p_position_t error_position = p_position(context);
fprintf(stderr, "Error: unexpected token at row %u column %u\n", fprintf(stderr, "Error: unexpected token at row %u column %u\n",
error_position.row + 1, error_position.col + 1); error_position.row + 1, error_position.col + 1);
} }
@@ -1130,9 +1337,9 @@ They have no particular meaning to Propane.
Example: Example:
``` ```
if (p_parse(&context) == P_USER_TERMINATED) if (p_parse(context) == P_USER_TERMINATED)
{ {
size_t user_terminate_code = p_user_terminate_code(&context); size_t user_terminate_code = p_user_terminate_code(context);
} }
``` ```
@@ -1146,9 +1353,9 @@ indicate what token the parser was not expecting.
Example: Example:
``` ```
if (p_parse(&context) == P_UNEXPECTED_TOKEN) if (p_parse(context) == P_UNEXPECTED_TOKEN)
{ {
p_token_t unexpected_token = p_token(&context); p_token_t unexpected_token = p_token(context);
} }
``` ```
@@ -1172,30 +1379,42 @@ assert(code_point == 0x1F9E1u);
assert(code_point_length == 4u); assert(code_point_length == 4u);
``` ```
### `p_free_tree` ### `p_tree_delete`
The `p_free_tree()` function can be used to free the memory used by the tree. The `p_tree_delete()` function can be used to free the memory used by the tree.
It should be passed the same value that is returned by `p_result()`. It should be passed the same value that is returned by `p_result()`.
The `p_free_tree()` function is only available for C/C++ output targets.
Note that if any lexer user code block allocates memory to store in a token's Note that if any lexer user code block allocates memory to store in a token's
`pvalue`, in order to properly free this memory a `free_token_node` function `pvalue`, in order to properly free this memory the `free_token_node` statement
should be specified in the grammar file. should be used to provide a code block that frees this memory.
If specified, the `free_token_node` function will be called during the If specified, the `free_token_node` code block will be executed during the
`p_free_tree()` process to allow user code to free any memory associated with `p_tree_delete()` process to allow user code to free any memory associated with
a token node's `pvalue`. a token node's `pvalue`.
When multiple start rules are specified, a separate `p_free_tree` function is When multiple start rules are specified, a separate `p_tree_delete` function is
generated for each which frees the tree resulting from parsing the given rule. generated for each which frees the tree resulting from parsing the given rule.
For example, if `Statement` is specified as a start rule: For example, if `Statement` is specified as a start rule:
``` ```
p_free_tree_Statement(statement_tree); p_tree_delete_Statement(statement_tree);
``` ```
In this case, Propane will free a `Statement` tree structure returned by the In this case, Propane will free a `Statement` tree structure returned by the
`p_parse_Statement(&context)` function. `p_parse_Statement(context)` function.
### `p_value`
When tree generation mode is not active, the `p_value_t` union can hold one of
several different possible value types.
The `p_value(v)` function returns an instance of the `p_value_t` with the
default member set to the value of `v`.
A `p_value_XXX(v)` function is set for each user-defined `ptype` name with
the user-given name in place of the `XXX`.
These functions are useful for custom lexer functions which need to return a
parser value corresponding to a lexed token.
##> Data ##> Data
@@ -1207,14 +1426,13 @@ It is indexed by the token ID.
C example: C example:
``` ```
p_context_t context; p_context_t * context = p_context_new(input, input_length);
p_context_init(&context, input, input_length); size_t result = p_parse(context);
size_t result = p_parse(&context); if (p_parse(context) == P_UNEXPECTED_TOKEN)
if (p_parse(&context) == P_UNEXPECTED_TOKEN)
{ {
p_position_t error_position = p_position(&context); p_position_t error_position = p_position(context);
fprintf(stderr, "Error: unexpected token `%s' at row %u column %u\n", fprintf(stderr, "Error: unexpected token `%s' at row %u column %u\n",
p_token_names[context.token], p_token_names[context->token],
error_position.row + 1, error_position.col + 1); error_position.row + 1, error_position.col + 1);
} }
``` ```
+1 -1
View File
@@ -20,7 +20,7 @@ syn match propaneOperator "->"
syn match propaneFieldAlias ":[a-zA-Z0-9_]\+" contains=propaneFieldOperator syn match propaneFieldAlias ":[a-zA-Z0-9_]\+" contains=propaneFieldOperator
syn match propaneFieldOperator ":" contained syn match propaneFieldOperator ":" contained
syn match propaneOperator "?" syn match propaneOperator "?"
syn keyword propaneKeyword drop free_token_node module prefix ptype start token tokenid tree tree_prefix tree_suffix syn keyword propaneKeyword drop free_token_node free_token_user_fields module prefix ptype start token token_user_fields tokenid tree tree_prefix tree_suffix
syn region propaneRegex start="/" end="/" skip="\v\\\\|\\/" syn region propaneRegex start="/" end="/" skip="\v\\\\|\\/"
+32 -1
View File
@@ -13,8 +13,13 @@ class Propane
@language = @language =
if output_file.end_with?(".d") if output_file.end_with?(".d")
"d" "d"
else elsif output_file.end_with?(".c")
"c" "c"
elsif output_file =~ %r{\.(cc|cpp|cxx)$}
@cpp = true
"c"
else
raise Error.new("Could not determine target language from output file name (#{output_file})")
end end
@options = options @options = options
process_grammar! process_grammar!
@@ -265,6 +270,24 @@ class Propane
"context.user_terminate_code = (#{user_terminate_code}); return #{retval};" "context.user_terminate_code = (#{user_terminate_code}); return #{retval};"
end end
end end
code = code.gsub(/\$\{context\.(\w+)\}/) do |match|
fieldname = $1
case @language
when "c"
"context->#{fieldname}"
when "d"
"context.#{fieldname}"
end
end
code = code.gsub(/\$\{token\.(\w+)\}/) do |match|
fieldname = $1
case @language
when "c"
"token_tree_node->#{fieldname}"
when "d"
"token_tree_node.#{fieldname}"
end
end
if parser if parser
code = code.gsub(/\$\$/) do |match| code = code.gsub(/\$\$/) do |match|
case @language case @language
@@ -331,6 +354,14 @@ class Propane
code code
end end
# Get the lex function to use.
#
# @return [String]
# Lex function to use.
def lex_fn
@grammar.lex_fn || "#{@grammar.prefix}lex"
end
# Get the parser value type for the start rule. # Get the parser value type for the start rule.
# #
# @return [Array<String>] # @return [Array<String>]
+62 -9
View File
@@ -5,6 +5,8 @@ class Propane
# Reserve identifiers beginning with a double-underscore for internal use. # Reserve identifiers beginning with a double-underscore for internal use.
IDENTIFIER_REGEX = /(?:[a-zA-Z]|_[a-zA-Z0-9])[a-zA-Z_0-9]*/ IDENTIFIER_REGEX = /(?:[a-zA-Z]|_[a-zA-Z0-9])[a-zA-Z_0-9]*/
attr_reader :context_user_fields
attr_reader :lex_fn
attr_reader :tree attr_reader :tree
attr_reader :tree_prefix attr_reader :tree_prefix
attr_reader :tree_suffix attr_reader :tree_suffix
@@ -17,6 +19,8 @@ class Propane
attr_reader :code_blocks attr_reader :code_blocks
attr_reader :ptypes attr_reader :ptypes
attr_reader :prefix attr_reader :prefix
attr_reader :on_token_node
attr_reader :token_user_fields
def initialize(input) def initialize(input)
@patterns = [] @patterns = []
@@ -33,7 +37,10 @@ class Propane
@tree = false @tree = false
@tree_prefix = "" @tree_prefix = ""
@tree_suffix = "" @tree_suffix = ""
@free_token_node = nil @free_token_node = ""
@context_user_fields = nil
@on_token_node = ""
@token_user_fields = nil
parse_grammar! parse_grammar!
@start_rules << "Start" if @start_rules.empty? @start_rules << "Start" if @start_rules.empty?
end end
@@ -62,11 +69,15 @@ class Propane
if parse_white_space! if parse_white_space!
elsif parse_comment_line! elsif parse_comment_line!
elsif @modeline.nil? && parse_mode_label! elsif @modeline.nil? && parse_mode_label!
elsif parse_context_user_fields_statement!
elsif parse_lex_fn!
elsif parse_tree_statement! elsif parse_tree_statement!
elsif parse_tree_prefix_statement! elsif parse_tree_prefix_statement!
elsif parse_tree_suffix_statement! elsif parse_tree_suffix_statement!
elsif parse_free_token_node_statement! elsif parse_free_token_node_statement!
elsif parse_module_statement! elsif parse_module_statement!
elsif parse_on_token_node_statement!
elsif parse_token_user_fields_statement!
elsif parse_ptype_statement! elsif parse_ptype_statement!
elsif parse_pattern_statement! elsif parse_pattern_statement!
elsif parse_start_statement! elsif parse_start_statement!
@@ -98,6 +109,22 @@ class Propane
consume!(/#.*\n/) consume!(/#.*\n/)
end end
def parse_context_user_fields_statement!
if md = consume!(/context_user_fields\b\s*/)
unless code = parse_code_block!
raise Error.new("Line #{@line_number}: expected code block")
end
@context_user_fields ||= ""
@context_user_fields += code
end
end
def parse_lex_fn!
if md = consume!(/lex_fn\b\s*(\w+)\s*;/)
@lex_fn = md[1]
end
end
def parse_tree_statement! def parse_tree_statement!
if consume!(/tree\s*;/) if consume!(/tree\s*;/)
@tree = true @tree = true
@@ -116,12 +143,6 @@ class Propane
end end
end end
def parse_free_token_node_statement!
if md = consume!(/free_token_node\s+(\w+)\s*;/)
@free_token_node = md[1]
end
end
def parse_module_statement! def parse_module_statement!
if consume!(/module\s+/) if consume!(/module\s+/)
md = consume!(/([\w.]+)\s*/, "expected module name") md = consume!(/([\w.]+)\s*/, "expected module name")
@@ -132,6 +153,34 @@ class Propane
end end
end end
def parse_on_token_node_statement!
if md = consume!(/on_token_node\b\s*/)
unless code = parse_code_block!
raise Error.new("Line #{@line_number}: expected code block")
end
@on_token_node += code
end
end
def parse_token_user_fields_statement!
if md = consume!(/token_user_fields\b\s*/)
unless code = parse_code_block!
raise Error.new("Line #{@line_number}: expected code block")
end
@token_user_fields ||= ""
@token_user_fields += code
end
end
def parse_free_token_node_statement!
if md = consume!(/free_token_node\b\s*/)
unless code = parse_code_block!
raise Error.new("Line #{@line_number}: expected code block")
end
@free_token_node += code
end
end
def parse_ptype_statement! def parse_ptype_statement!
if consume!(/ptype\s+/) if consume!(/ptype\s+/)
name = "default" name = "default"
@@ -195,8 +244,10 @@ class Propane
raise Error.new("Line #{@line_number}: expected pattern to follow `drop'") raise Error.new("Line #{@line_number}: expected pattern to follow `drop'")
end end
consume!(/\s+/) consume!(/\s+/)
consume!(/;/, "expected `;'") unless code = parse_code_block!
@patterns << Pattern.new(pattern: pattern, line_number: @line_number, modes: get_modes_from_modeline) consume!(/;/, "expected `;' or code block")
end
@patterns << Pattern.new(pattern: pattern, line_number: @line_number, code: code, modes: get_modes_from_modeline)
@modeline = nil @modeline = nil
true true
end end
@@ -285,6 +336,8 @@ class Propane
end end
elsif md = consume!(%r{(.)}) elsif md = consume!(%r{(.)})
pattern += md[1] pattern += md[1]
elsif @input == "" || @input.start_with?("\n")
raise Error.new("Line #{@line_number}: Unterminated pattern; expected `/`")
end end
end end
pattern pattern
+1 -1
View File
@@ -1,3 +1,3 @@
class Propane class Propane
VERSION = "3.0.0" VERSION = "4.1.0"
end end
+282 -11
View File
@@ -14,6 +14,7 @@ describe Propane do
end end
def run_propane(options = {}) def run_propane(options = {})
options[:language] ||= "d"
@statics[:build_test_id] ||= 0 @statics[:build_test_id] ||= 0
@statics[:build_test_id] += 1 @statics[:build_test_id] += 1
if ENV["dist_specs"] if ENV["dist_specs"]
@@ -94,17 +95,15 @@ EOF
File.binwrite("spec/run/.stdout", stdout) File.binwrite("spec/run/.stdout", stdout)
stderr.sub!(/^.*modules passed unittests\n/, "") stderr.sub!(/^.*modules passed unittests\n/, "")
results = Results.new(stdout, stderr, status) results = Results.new(stdout, stderr, status)
if %w[c cpp].include?(options[:language])
stdout, stderr, status = Open3.capture3("valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose spec/run/testparser") stdout, stderr, status = Open3.capture3("valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes --verbose spec/run/testparser")
vgout = stdout + stderr vgout = stdout + stderr
File.binwrite("spec/run/.vgout", vgout) File.binwrite("spec/run/.vgout", vgout)
vgout.scan(/lost: (\d+) bytes/) do |match| vgout.scan(/(?:definitely|indirectly) lost: (\d+) bytes/) do |match|
bytes = $1.to_i bytes = $1.to_i
if bytes > 0 if bytes > 0
raise "Valgrind detected memory leak" raise "Valgrind detected memory leak"
end end
end end
end
results results
end end
@@ -1430,6 +1429,9 @@ EOF
expect(results.stdout).to match /first is foo1.*second is bar2/m expect(results.stdout).to match /first is foo1.*second is bar2/m
end end
# D garbage collector was freeing memory for nodes when it should not
# have been. This was due to pointers being in the TreeNode that was
# allocated by C malloc() function instead of allocated using D.
it "does not free memory allocated for tree nodes" do it "does not free memory allocated for tree nodes" do
ext = language == "cpp" ? "c" : language ext = language == "cpp" ? "c" : language
write_grammar(File.read("spec/tree_node_memory_remains.#{ext}.propane")) write_grammar(File.read("spec/tree_node_memory_remains.#{ext}.propane"))
@@ -1484,14 +1486,10 @@ EOF
if %w[c cpp].include?(language) if %w[c cpp].include?(language)
it "allows a user function to free token node memory in tree mode" do it "allows a user function to free token node memory in tree mode" do
write_grammar <<EOF write_grammar <<EOF
<<
static void free_token(Token * token)
{
free(token->pvalue);
}
>>
tree; tree;
free_token_node free_token; free_token_node <<
free(${token.pvalue});
>>
ptype int *; ptype int *;
token a << token a <<
$$ = (int *)malloc(sizeof(int)); $$ = (int *)malloc(sizeof(int));
@@ -1504,12 +1502,285 @@ token b <<
Start -> a:a b:b; Start -> a:a b:b;
EOF EOF
run_propane(language: language) run_propane(language: language)
compile("spec/test_free_tree_token_node_memory.#{language}", language: language) compile("spec/test_tree_delete_token_node_memory.#{language}", language: language)
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
end end
end end
it "executes code blocks associated with drop statements" do
if language == "d"
write_grammar <<EOF
<<
import std.stdio;
>>
drop /\\s+/;
drop /#(.*)\\n/ <<
stderr.write("comment: ", match);
>>
token a;
Start -> a;
EOF
else
write_grammar <<EOF
<<
#include <stdio.h>
#include <string.h>
>>
drop /\\s+/;
drop /#(.*)\\n/ <<
fprintf(stderr, "comment: %.*s", (int)match_length, match);
>>
token a;
Start -> a;
EOF
end
run_propane(language: language)
compile("spec/test_drop_code_block.#{language}", language: language)
results = run_test(language: language)
expect(results.stderr).to match %r{comment: # comment 1\n.*comment: # comment 2}m
expect(results.status).to eq 0
end
it "allows user-defined context fields" do
if language == "d"
write_grammar <<EOF
context_user_fields <<
string comments;
uint acount;
>>
drop /\\s+/;
drop /#(.*)\\n/ <<
${context.comments} ~= match;
>>
token a <<
${context.acount}++;
>>
Start -> As;
As -> ;
As -> a As;
EOF
else
write_grammar <<EOF
<<
#include <string.h>
#include <stdlib.h>
>>
context_user_fields <<
char * comments;
unsigned int acount;
>>
drop /\\s+/;
drop /#(.*)\\n/ <<
size_t cur_len = 0u;
if (${context.comments} != NULL)
cur_len = strlen(${context.comments});
char * commentsnew = (char *)malloc(cur_len + match_length + 1);
if (${context.comments} != NULL)
memcpy(commentsnew, ${context.comments}, cur_len);
memcpy(&commentsnew[cur_len], match, match_length);
commentsnew[cur_len + match_length] = '\\0';
if (${context.comments} != NULL)
{
free(${context.comments});
}
${context.comments} = commentsnew;
>>
token a <<
${context.acount}++;
>>
Start -> As;
As -> ;
As -> a As;
EOF
end
run_propane(language: language)
compile("spec/test_user_context_fields.#{language}", language: language)
results = run_test(language: language)
expect(results.stderr).to include %r{comments: # comment 1\n# comment 2}
expect(results.stderr).to include %r{acount: 11\n}
expect(results.status).to eq 0
end
it "allows custom token user fields" do
if language == "d"
write_grammar <<EOF
context_user_fields <<
string comments;
>>
token_user_fields <<
string comments;
>>
on_token_node <<
${token.comments} = ${context.comments};
${context.comments} = "";
>>
tree;
drop /\\s+/;
drop /#(.*)\\n/ <<
${context.comments} ~= match;
>>
token id /\\w+/;
Start -> IDs;
IDs -> ;
IDs -> id:id IDs;
EOF
elsif language == "c"
write_grammar <<EOF
<<
#include <string.h>
#include <stdlib.h>
>>
context_user_fields <<
char * comments;
>>
token_user_fields <<
char * comments;
>>
free_token_node <<
free(${token.comments});
>>
on_token_node <<
${token.comments} = ${context.comments};
${context.comments} = (char *)malloc(1);
${context.comments}[0] = '\\0';
>>
tree;
drop /\\s+/;
drop /#(.*)\\n/ <<
size_t cur_len = 0u;
if (${context.comments} != NULL)
cur_len = strlen(${context.comments});
char * commentsnew = (char *)malloc(cur_len + match_length + 1);
if (${context.comments} != NULL)
memcpy(commentsnew, ${context.comments}, cur_len);
memcpy(&commentsnew[cur_len], match, match_length);
commentsnew[cur_len + match_length] = '\\0';
if (${context.comments} != NULL)
{
free(${context.comments});
}
${context.comments} = commentsnew;
>>
token id /\\w+/;
Start -> IDs;
IDs -> ;
IDs -> id:id IDs;
EOF
else # C++
write_grammar <<EOF
<<header
#include <string>
>>
context_user_fields <<
std::string comments;
>>
token_user_fields <<
std::string comments;
>>
on_token_node <<
${token.comments} = ${context.comments};
${context.comments} = "";
>>
tree;
drop /\\s+/;
drop /#(.*)\\n/ <<
${context.comments} += std::string((const char *)match, match_length);
>>
token id /\\w+/;
Start -> IDs;
IDs -> ;
IDs -> id:id IDs;
EOF
end
run_propane(language: language)
compile("spec/test_token_user_fields.#{language}", language: language)
results = run_test(language: language)
expect(results.status).to eq 0
end
it "allows a custom lex function" do
if language == "d"
write_grammar <<EOF
<<
private size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
{
static size_t count;
size_t result = P_SUCCESS;
if (count > 0)
{
out_token_info.token = TOKEN_a;
out_token_info.pvalue = p_value(count);
count--;
}
else
{
result = p_lex(context, out_token_info);
if (out_token_info.token == TOKEN_c)
{
count = 3;
}
}
return result;
}
>>
ptype size_t;
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
else
write_grammar <<EOF
<<
static size_t mylexfn(p_context_t * context, p_token_info_t * out_token_info)
{
static size_t count;
size_t result = P_SUCCESS;
if (count > 0)
{
out_token_info->token = TOKEN_a;
out_token_info->pvalue = p_value(count);
count--;
}
else
{
result = p_lex(context, out_token_info);
if (out_token_info->token == TOKEN_c)
{
count = 3;
}
}
return result;
}
>>
ptype size_t;
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
end
run_propane(language: language)
compile("spec/test_custom_lex_fn.#{language}", language: language)
results = run_test(language: language)
expect(results.status).to eq 0
end
end end
end end
end end
+17 -13
View File
@@ -5,25 +5,29 @@
int main() int main()
{ {
char const * input = "1 + 2 * 3 + 4"; char const * input = "1 + 2 * 3 + 4";
p_context_t context; p_context_t * context;
p_context_init(&context, (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));
assert_eq(11, p_result(&context)); assert_eq(11, p_result(context));
p_context_delete(context);
input = "1 * 2 ** 4 * 3"; input = "1 * 2 ** 4 * 3";
p_context_init(&context, (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));
assert_eq(48, p_result(&context)); assert_eq(48, p_result(context));
p_context_delete(context);
input = "(1 + 2) * 3 + 4"; input = "(1 + 2) * 3 + 4";
p_context_init(&context, (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));
assert_eq(13, p_result(&context)); assert_eq(13, p_result(context));
p_context_delete(context);
input = "(2 * 2) ** 3 + 4 + 5"; input = "(2 * 2) ** 3 + 4 + 5";
p_context_init(&context, (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));
assert_eq(73, p_result(&context)); assert_eq(73, p_result(context));
p_context_delete(context);
return 0; return 0;
} }
+13 -13
View File
@@ -10,23 +10,23 @@ int main()
unittest unittest
{ {
string input = "1 + 2 * 3 + 4"; string input = "1 + 2 * 3 + 4";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert_eq(P_SUCCESS, p_parse(&context)); assert_eq(P_SUCCESS, p_parse(context));
assert_eq(11, p_result(&context)); assert_eq(11, p_result(context));
input = "1 * 2 ** 4 * 3"; input = "1 * 2 ** 4 * 3";
p_context_init(&context, input); context = p_context_new(input);
assert_eq(P_SUCCESS, p_parse(&context)); assert_eq(P_SUCCESS, p_parse(context));
assert_eq(48, p_result(&context)); assert_eq(48, p_result(context));
input = "(1 + 2) * 3 + 4"; input = "(1 + 2) * 3 + 4";
p_context_init(&context, input); context = p_context_new(input);
assert_eq(P_SUCCESS, p_parse(&context)); assert_eq(P_SUCCESS, p_parse(context));
assert_eq(13, p_result(&context)); assert_eq(13, p_result(context));
input = "(2 * 2) ** 3 + 4 + 5"; input = "(2 * 2) ** 3 + 4 + 5";
p_context_init(&context, input); context = p_context_new(input);
assert_eq(P_SUCCESS, p_parse(&context)); assert_eq(P_SUCCESS, p_parse(context));
assert_eq(73, p_result(&context)); assert_eq(73, p_result(context));
} }
+15
View File
@@ -0,0 +1,15 @@
#include "testparser.h"
#include "testutils.h"
#include <string.h>
int main()
{
char const * input = "cbacba";
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
assert_eq(P_SUCCESS, p_parse(context));
size_t result = p_result(context);
assert_eq(0x932187932187, result);
p_context_delete(context);
return 0;
}
+18
View File
@@ -0,0 +1,18 @@
import testparser;
import std.stdio;
import testutils;
int main()
{
return 0;
}
unittest
{
string input = "cbacba";
p_context_t * context = p_context_new(input);
assert_eq(P_SUCCESS, p_parse(context));
size_t result = p_result(context);
assert_eq(0x932187932187, result);
p_context_delete(context);
}
+15
View File
@@ -0,0 +1,15 @@
#include "testparser.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
int main()
{
char const * input = " # comment 1\n# comment 2\na\n";
p_context_t * context;
context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(context) == P_SUCCESS);
p_context_delete(context);
return 0;
}
+16
View File
@@ -0,0 +1,16 @@
import testparser;
import std.stdio;
import testutils;
int main()
{
return 0;
}
unittest
{
string input = " # comment 1\n# comment 2\na\n";
p_context_t * context;
context = p_context_new(input);
assert(p_parse(context) == P_SUCCESS);
}
+26 -21
View File
@@ -5,38 +5,43 @@
int main() int main()
{ {
char const * input = "a 42"; char const * input = "a 42";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
p_context_delete(context);
input = "a\n123\na a"; input = "a\n123\na a";
p_context_init(&context, (uint8_t const *)input, strlen(input)); context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(&context) == P_UNEXPECTED_TOKEN); assert(p_parse(context) == P_UNEXPECTED_TOKEN);
assert(p_position(&context).row == 3); assert(p_position(context).row == 3);
assert(p_position(&context).col == 4); assert(p_position(context).col == 4);
assert(p_token(&context) == TOKEN_a); assert(p_token(context) == TOKEN_a);
p_context_delete(context);
input = "12"; input = "12";
p_context_init(&context, (uint8_t const *)input, strlen(input)); context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(&context) == P_UNEXPECTED_TOKEN); assert(p_parse(context) == P_UNEXPECTED_TOKEN);
assert(p_position(&context).row == 1); assert(p_position(context).row == 1);
assert(p_position(&context).col == 1); assert(p_position(context).col == 1);
assert(p_token(&context) == TOKEN_num); assert(p_token(context) == TOKEN_num);
p_context_delete(context);
input = "a 12\n\nab"; input = "a 12\n\nab";
p_context_init(&context, (uint8_t const *)input, strlen(input)); context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(&context) == P_UNEXPECTED_INPUT); assert(p_parse(context) == P_UNEXPECTED_INPUT);
assert(p_position(&context).row == 3); assert(p_position(context).row == 3);
assert(p_position(&context).col == 2); assert(p_position(context).col == 2);
p_context_delete(context);
input = "a 12\n\na\n\n77\na \xAA"; input = "a 12\n\na\n\n77\na \xAA";
p_context_init(&context, (uint8_t const *)input, strlen(input)); context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(&context) == P_DECODE_ERROR); assert(p_parse(context) == P_DECODE_ERROR);
assert(p_position(&context).row == 6); assert(p_position(context).row == 6);
assert(p_position(&context).col == 5); assert(p_position(context).col == 5);
assert(strcmp(p_token_names[TOKEN_a], "a") == 0); assert(strcmp(p_token_names[TOKEN_a], "a") == 0);
assert(strcmp(p_token_names[TOKEN_num], "num") == 0); assert(strcmp(p_token_names[TOKEN_num], "num") == 0);
p_context_delete(context);
return 0; return 0;
} }
+17 -17
View File
@@ -9,31 +9,31 @@ int main()
unittest unittest
{ {
string input = "a 42"; string input = "a 42";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
input = "a\n123\na a"; input = "a\n123\na a";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_UNEXPECTED_TOKEN); assert(p_parse(context) == P_UNEXPECTED_TOKEN);
assert(p_position(&context) == p_position_t(3, 4)); assert(p_position(context) == p_position_t(3, 4));
assert(p_token(&context) == TOKEN_a); assert(p_token(context) == TOKEN_a);
input = "12"; input = "12";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_UNEXPECTED_TOKEN); assert(p_parse(context) == P_UNEXPECTED_TOKEN);
assert(p_position(&context) == p_position_t(1, 1)); assert(p_position(context) == p_position_t(1, 1));
assert(p_token(&context) == TOKEN_num); assert(p_token(context) == TOKEN_num);
input = "a 12\n\nab"; input = "a 12\n\nab";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_UNEXPECTED_INPUT); assert(p_parse(context) == P_UNEXPECTED_INPUT);
assert(p_position(&context) == p_position_t(3, 2)); assert(p_position(context) == p_position_t(3, 2));
input = "a 12\n\na\n\n77\na \xAA"; input = "a 12\n\na\n\n77\na \xAA";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_DECODE_ERROR); assert(p_parse(context) == P_DECODE_ERROR);
assert(p_position(&context) == p_position_t(6, 5)); assert(p_position(context) == p_position_t(6, 5));
assert(p_token_names[TOKEN_a] == "a"); assert(p_token_names[TOKEN_a] == "a");
assert(p_token_names[TOKEN_num] == "num"); assert(p_token_names[TOKEN_num] == "num");
+4 -3
View File
@@ -6,8 +6,9 @@
int main() int main()
{ {
char const * input = "foo1\nbar2"; char const * input = "foo1\nbar2";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
p_context_delete(context);
return 0; return 0;
} }
+3 -3
View File
@@ -9,7 +9,7 @@ int main()
unittest unittest
{ {
string input = "foo1\nbar2"; string input = "foo1\nbar2";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
} }
+14 -12
View File
@@ -38,73 +38,75 @@ int main()
p_token_info_t token_info; p_token_info_t token_info;
char const * input = "5 + 4 * \n677 + 567"; char const * input = "5 + 4 * \n677 + 567";
p_context_t context; p_context_t * context;
p_context_init(&context, (uint8_t const *)input, strlen(input)); context = p_context_new((uint8_t const *)input, strlen(input));
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 == 1u); assert(token_info.position.col == 1u);
assert(token_info.end_position.row == 1u); assert(token_info.end_position.row == 1u);
assert(token_info.end_position.col == 1u); assert(token_info.end_position.col == 1u);
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 == 1u); assert(token_info.position.row == 1u);
assert(token_info.position.col == 3u); assert(token_info.position.col == 3u);
assert(token_info.end_position.row == 1u); assert(token_info.end_position.row == 1u);
assert(token_info.end_position.col == 3u); assert(token_info.end_position.col == 3u);
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 == 5u); assert(token_info.position.col == 5u);
assert(token_info.end_position.row == 1u); assert(token_info.end_position.row == 1u);
assert(token_info.end_position.col == 5u); assert(token_info.end_position.col == 5u);
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 == 1u); assert(token_info.position.row == 1u);
assert(token_info.position.col == 7u); assert(token_info.position.col == 7u);
assert(token_info.end_position.row == 1u); assert(token_info.end_position.row == 1u);
assert(token_info.end_position.col == 7u); assert(token_info.end_position.col == 7u);
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 == 2u); assert(token_info.position.row == 2u);
assert(token_info.position.col == 1u); assert(token_info.position.col == 1u);
assert(token_info.end_position.row == 2u); assert(token_info.end_position.row == 2u);
assert(token_info.end_position.col == 3u); assert(token_info.end_position.col == 3u);
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 == 2u); assert(token_info.position.row == 2u);
assert(token_info.position.col == 5u); assert(token_info.position.col == 5u);
assert(token_info.end_position.row == 2u); assert(token_info.end_position.row == 2u);
assert(token_info.end_position.col == 5u); assert(token_info.end_position.col == 5u);
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 == 2u); assert(token_info.position.row == 2u);
assert(token_info.position.col == 7u); assert(token_info.position.col == 7u);
assert(token_info.end_position.row == 2u); assert(token_info.end_position.row == 2u);
assert(token_info.end_position.col == 9u); assert(token_info.end_position.col == 9u);
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 == 2u); assert(token_info.position.row == 2u);
assert(token_info.position.col == 10u); assert(token_info.position.col == 10u);
assert(token_info.end_position.row == 2u); assert(token_info.end_position.row == 2u);
assert(token_info.end_position.col == 10u); assert(token_info.end_position.col == 10u);
assert(token_info.length == 0u); assert(token_info.length == 0u);
assert(token_info.token == TOKEN___EOF); assert(token_info.token == TOKEN___EOF);
p_context_delete(context);
p_context_init(&context, (uint8_t const *)"", 0u); context = p_context_new((uint8_t const *)"", 0u);
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 == 1u); assert(token_info.position.col == 1u);
assert(token_info.end_position.row == 1u); assert(token_info.end_position.row == 1u);
assert(token_info.end_position.col == 1u); assert(token_info.end_position.col == 1u);
assert(token_info.length == 0u); assert(token_info.length == 0u);
assert(token_info.token == TOKEN___EOF); assert(token_info.token == TOKEN___EOF);
p_context_delete(context);
return 0; return 0;
} }
+12 -12
View File
@@ -44,26 +44,26 @@ unittest
{ {
p_token_info_t token_info; p_token_info_t token_info;
string input = "5 + 4 * \n677 + 567"; string input = "5 + 4 * \n677 + 567";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(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(1, 1), p_position_t(1, 1), 1, TOKEN_int)); assert(token_info == p_token_info_t(p_position_t(1, 1), p_position_t(1, 1), 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(1, 3), p_position_t(1, 3), 1, TOKEN_plus)); assert(token_info == p_token_info_t(p_position_t(1, 3), p_position_t(1, 3), 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, 5), p_position_t(1, 5), 1, TOKEN_int)); assert(token_info == p_token_info_t(p_position_t(1, 5), p_position_t(1, 5), 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(1, 7), p_position_t(1, 7), 1, TOKEN_times)); assert(token_info == p_token_info_t(p_position_t(1, 7), p_position_t(1, 7), 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(2, 1), p_position_t(2, 3), 3, TOKEN_int)); assert(token_info == p_token_info_t(p_position_t(2, 1), p_position_t(2, 3), 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(2, 5), p_position_t(2, 5), 1, TOKEN_plus)); assert(token_info == p_token_info_t(p_position_t(2, 5), p_position_t(2, 5), 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(2, 7), p_position_t(2, 9), 3, TOKEN_int)); assert(token_info == p_token_info_t(p_position_t(2, 7), p_position_t(2, 9), 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(2, 10), p_position_t(2, 10), 0, TOKEN___EOF)); assert(token_info == p_token_info_t(p_position_t(2, 10), p_position_t(2, 10), 0, TOKEN___EOF));
p_context_init(&context, ""); context = p_context_new("");
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, 1), p_position_t(1, 1), 0, TOKEN___EOF)); assert(token_info == p_token_info_t(p_position_t(1, 1), p_position_t(1, 1), 0, TOKEN___EOF));
} }
+4 -3
View File
@@ -6,10 +6,11 @@
int main() int main()
{ {
char const * input = "identifier_123"; char const * input = "identifier_123";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
printf("pass1\n"); printf("pass1\n");
p_context_delete(context);
return 0; return 0;
} }
+3 -3
View File
@@ -9,8 +9,8 @@ int main()
unittest unittest
{ {
string input = `identifier_123`; string input = `identifier_123`;
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
writeln("pass1"); writeln("pass1");
} }
+7 -5
View File
@@ -6,15 +6,17 @@
int main() int main()
{ {
char const * input = "abc \"a string\" def"; char const * input = "abc \"a string\" def";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
printf("pass1\n"); printf("pass1\n");
p_context_delete(context);
input = "abc \"abc def\" def"; input = "abc \"abc def\" def";
p_context_init(&context, (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);
printf("pass2\n"); printf("pass2\n");
p_context_delete(context);
return 0; return 0;
} }
+5 -5
View File
@@ -9,13 +9,13 @@ int main()
unittest unittest
{ {
string input = `abc "a string" def`; string input = `abc "a string" def`;
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
writeln("pass1"); writeln("pass1");
input = `abc "abc def" def`; input = `abc "abc def" def`;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
writeln("pass2"); writeln("pass2");
} }
+7 -5
View File
@@ -6,15 +6,17 @@
int main() int main()
{ {
char const * input = "abc.def"; char const * input = "abc.def";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
printf("pass1\n"); printf("pass1\n");
p_context_delete(context);
input = "abc . abc"; input = "abc . abc";
p_context_init(&context, (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);
printf("pass2\n"); printf("pass2\n");
p_context_delete(context);
return 0; return 0;
} }
+5 -5
View File
@@ -9,13 +9,13 @@ int main()
unittest unittest
{ {
string input = `abc.def`; string input = `abc.def`;
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
writeln("pass1"); writeln("pass1");
input = `abc . abc`; input = `abc . abc`;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
writeln("pass2"); writeln("pass2");
} }
+9 -7
View File
@@ -5,15 +5,17 @@
int main() int main()
{ {
char const * input = "x"; char const * input = "x";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
assert(p_result(&context) == 1u); assert(p_result(context) == 1u);
p_context_delete(context);
input = "fabulous"; input = "fabulous";
p_context_init(&context, (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);
assert(p_result(&context) == 8u); assert(p_result(context) == 8u);
p_context_delete(context);
return 0; return 0;
} }
+7 -7
View File
@@ -9,13 +9,13 @@ int main()
unittest unittest
{ {
string input = `x`; string input = `x`;
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
assert(p_result(&context) == 1u); assert(p_result(context) == 1u);
input = `fabulous`; input = `fabulous`;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
assert(p_result(&context) == 8u); assert(p_result(context) == 8u);
} }
+8 -6
View File
@@ -5,14 +5,16 @@
int main() int main()
{ {
char const * input = "x"; char const * input = "x";
p_context_t context; p_context_t * context;
p_context_init(&context, (uint8_t const *)input, strlen(input)); context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(&context) == P_UNEXPECTED_INPUT); assert(p_parse(context) == P_UNEXPECTED_INPUT);
p_context_delete(context);
input = "123"; input = "123";
p_context_init(&context, (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);
assert(p_result(&context) == 123u); assert(p_result(context) == 123u);
p_context_delete(context);
return 0; return 0;
} }
+6 -6
View File
@@ -9,12 +9,12 @@ int main()
unittest unittest
{ {
string input = `x`; string input = `x`;
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_UNEXPECTED_INPUT); assert(p_parse(context) == P_UNEXPECTED_INPUT);
input = `123`; input = `123`;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
assert(p_result(&context) == 123u); assert(p_result(context) == 123u);
} }
+4 -3
View File
@@ -5,9 +5,10 @@
int main() int main()
{ {
char const * input = "\a\b\t\n\v\f\rt"; char const * input = "\a\b\t\n\v\f\rt";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
p_context_delete(context);
return 0; return 0;
} }
+3 -3
View File
@@ -9,7 +9,7 @@ int main()
unittest unittest
{ {
string input = "\a\b\t\n\v\f\rt"; string input = "\a\b\t\n\v\f\rt";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
} }
+8 -6
View File
@@ -6,14 +6,16 @@
int main() int main()
{ {
char const * input1 = "a\n1"; char const * input1 = "a\n1";
myp1_context_t context1; myp1_context_t * context1;
myp1_context_init(&context1, (uint8_t const *)input1, strlen(input1)); context1 = myp1_context_new((uint8_t const *)input1, strlen(input1));
assert(myp1_parse(&context1) == MYP1_SUCCESS); assert(myp1_parse(context1) == MYP1_SUCCESS);
myp1_context_delete(context1);
char const * input2 = "bcb"; char const * input2 = "bcb";
myp2_context_t context2; myp2_context_t * context2;
myp2_context_init(&context2, (uint8_t const *)input2, strlen(input2)); context2 = myp2_context_new((uint8_t const *)input2, strlen(input2));
assert(myp2_parse(&context2) == MYP2_SUCCESS); assert(myp2_parse(context2) == MYP2_SUCCESS);
myp2_context_delete(context2);
return 0; return 0;
} }
+6 -6
View File
@@ -10,12 +10,12 @@ int main()
unittest unittest
{ {
string input1 = "a\n1"; string input1 = "a\n1";
myp1_context_t context1; myp1_context_t * context1;
myp1_context_init(&context1, input1); context1 = myp1_context_new(input1);
assert(myp1_parse(&context1) == MYP1_SUCCESS); assert(myp1_parse(context1) == MYP1_SUCCESS);
string input2 = "bcb"; string input2 = "bcb";
myp2_context_t context2; myp2_context_t * context2;
myp2_context_init(&context2, input2); context2 = myp2_context_new(input2);
assert(myp2_parse(&context2) == MYP2_SUCCESS); assert(myp2_parse(context2) == MYP2_SUCCESS);
} }
+16 -13
View File
@@ -6,10 +6,10 @@
int main() int main()
{ {
char const * input = "b"; char const * input = "b";
p_context_t context; p_context_t * context;
p_context_init(&context, (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(start->a == NULL);
assert(start->pToken2 != NULL); assert(start->pToken2 != NULL);
assert_eq(TOKEN_b, start->pToken2->token); assert_eq(TOKEN_b, start->pToken2->token);
@@ -17,12 +17,13 @@ int main()
assert(start->pR == NULL); assert(start->pR == NULL);
assert(start->r == NULL); assert(start->r == NULL);
p_free_tree(start); p_tree_delete(start);
p_context_delete(context);
input = "abcd"; input = "abcd";
p_context_init(&context, (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(start->a != NULL);
assert_eq(TOKEN_a, start->pToken1->token); assert_eq(TOKEN_a, start->pToken1->token);
assert(start->pToken2 != NULL); assert(start->pToken2 != NULL);
@@ -33,18 +34,20 @@ int main()
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_free_tree(start); p_tree_delete(start);
p_context_delete(context);
input = "bdc"; input = "bdc";
p_context_init(&context, (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(start->a == NULL);
assert(start->pToken2 != NULL); assert(start->pToken2 != NULL);
assert(start->r != NULL); assert(start->r != NULL);
assert_eq(TOKEN_d, start->pR->pToken1->token); assert_eq(TOKEN_d, start->pR->pToken1->token);
p_free_tree(start); p_tree_delete(start);
p_context_delete(context);
return 0; return 0;
} }
+15 -10
View File
@@ -10,10 +10,9 @@ int main()
unittest unittest
{ {
string input = "b"; string input = "b";
p_context_t context; p_context_t * context = p_context_new(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(start.pToken1 is null); assert(start.pToken1 is null);
assert(start.pToken2 !is null); assert(start.pToken2 !is null);
assert_eq(TOKEN_b, start.pToken2.token); assert_eq(TOKEN_b, start.pToken2.token);
@@ -21,10 +20,12 @@ unittest
assert(start.pR is null); assert(start.pR is null);
assert(start.r is null); assert(start.r is null);
p_tree_delete(start);
input = "abcd"; input = "abcd";
p_context_init(&context, 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 != null);
assert_eq(TOKEN_a, start.pToken1.token); assert_eq(TOKEN_a, start.pToken1.token);
assert(start.pToken2 != null); assert(start.pToken2 != null);
@@ -35,12 +36,16 @@ unittest
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);
input = "bdc"; input = "bdc";
p_context_init(&context, 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 is null);
assert(start.pToken2 !is null); assert(start.pToken2 !is null);
assert(start.pR !is null); assert(start.pR !is null);
assert_eq(TOKEN_d, start.pR.pToken1.token); assert_eq(TOKEN_d, start.pR.pToken1.token);
p_tree_delete(start);
} }
+10 -7
View File
@@ -5,17 +5,20 @@
int main() int main()
{ {
char const * input = "b"; char const * input = "b";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
p_context_delete(context);
input = "abcd"; input = "abcd";
p_context_init(&context, (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);
p_context_delete(context);
input = "abdc"; input = "abdc";
p_context_init(&context, (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);
p_context_delete(context);
return 0; return 0;
} }
+7 -7
View File
@@ -9,15 +9,15 @@ int main()
unittest unittest
{ {
string input = "b"; string input = "b";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
input = "abcd"; input = "abcd";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
input = "abdc"; input = "abdc";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
} }
+16 -13
View File
@@ -6,22 +6,23 @@
int main() int main()
{ {
char const * input = "b"; char const * input = "b";
p_context_t context; p_context_t * context;
p_context_init(&context, (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(start->pToken1 == NULL);
assert(start->pToken2 != NULL); assert(start->pToken2 != NULL);
assert_eq(TOKEN_b, start->pToken2->token); assert_eq(TOKEN_b, start->pToken2->token);
assert(start->pR3 == NULL); assert(start->pR3 == NULL);
assert(start->pR == NULL); assert(start->pR == NULL);
p_free_tree(start); p_tree_delete(start);
p_context_delete(context);
input = "abcd"; input = "abcd";
p_context_init(&context, (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(start->pToken1 != NULL);
assert_eq(TOKEN_a, start->pToken1->token); assert_eq(TOKEN_a, start->pToken1->token);
assert(start->pToken2 != NULL); assert(start->pToken2 != NULL);
@@ -30,18 +31,20 @@ int main()
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_free_tree(start); p_tree_delete(start);
p_context_delete(context);
input = "bdc"; input = "bdc";
p_context_init(&context, (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(start->pToken1 == NULL);
assert(start->pToken2 != NULL); assert(start->pToken2 != NULL);
assert(start->pR != NULL); assert(start->pR != NULL);
assert_eq(TOKEN_d, start->pR->pToken1->token); assert_eq(TOKEN_d, start->pR->pToken1->token);
p_free_tree(start); p_tree_delete(start);
p_context_delete(context);
return 0; return 0;
} }
+15 -10
View File
@@ -10,20 +10,21 @@ int main()
unittest unittest
{ {
string input = "b"; string input = "b";
p_context_t context; p_context_t * context = p_context_new(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(start.pToken1 is null); assert(start.pToken1 is null);
assert(start.pToken2 !is null); assert(start.pToken2 !is null);
assert_eq(TOKEN_b, start.pToken2.token); assert_eq(TOKEN_b, start.pToken2.token);
assert(start.pR3 is null); assert(start.pR3 is null);
assert(start.pR is null); assert(start.pR is null);
p_tree_delete(start);
input = "abcd"; input = "abcd";
p_context_init(&context, 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 != null);
assert_eq(TOKEN_a, start.pToken1.token); assert_eq(TOKEN_a, start.pToken1.token);
assert(start.pToken2 != null); assert(start.pToken2 != null);
@@ -32,12 +33,16 @@ unittest
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);
input = "bdc"; input = "bdc";
p_context_init(&context, 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 is null);
assert(start.pToken2 !is null); assert(start.pToken2 !is null);
assert(start.pR !is null); assert(start.pR !is null);
assert_eq(TOKEN_d, start.pR.pToken1.token); assert_eq(TOKEN_d, start.pR.pToken1.token);
p_tree_delete(start);
} }
+7 -5
View File
@@ -5,13 +5,15 @@
int main() int main()
{ {
char const * input = "aba"; char const * input = "aba";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
p_context_delete(context);
input = "abb"; input = "abb";
p_context_init(&context, (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);
p_context_delete(context);
return 0; return 0;
} }
+5 -5
View File
@@ -9,11 +9,11 @@ int main()
unittest unittest
{ {
string input = "aba"; string input = "aba";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
input = "abb"; input = "abb";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
} }
+13 -10
View File
@@ -5,20 +5,23 @@
int main() int main()
{ {
char const * input = "a"; char const * input = "a";
p_context_t context; p_context_t * context;
p_context_init(&context, (uint8_t const *)input, strlen(input)); context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(&context) == P_UNEXPECTED_TOKEN); assert(p_parse(context) == P_UNEXPECTED_TOKEN);
assert(p_position(&context).row == 1); assert(p_position(context).row == 1);
assert(p_position(&context).col == 2); assert(p_position(context).col == 2);
assert(context.token == TOKEN___EOF); assert(context->token == TOKEN___EOF);
p_context_delete(context);
input = "a b"; input = "a b";
p_context_init(&context, (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);
p_context_delete(context);
input = "bb"; input = "bb";
p_context_init(&context, (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);
p_context_delete(context);
return 0; return 0;
} }
+8 -8
View File
@@ -9,17 +9,17 @@ int main()
unittest unittest
{ {
string input = "a"; string input = "a";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_UNEXPECTED_TOKEN); assert(p_parse(context) == P_UNEXPECTED_TOKEN);
assert(p_position(&context) == p_position_t(1, 2)); assert(p_position(context) == p_position_t(1, 2));
assert(context.token == TOKEN___EOF); assert(context.token == TOKEN___EOF);
input = "a b"; input = "a b";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
input = "bb"; input = "bb";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
} }
+4 -3
View File
@@ -5,9 +5,10 @@
int main() int main()
{ {
char const * input = "ab"; char const * input = "ab";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
p_context_delete(context);
return 0; return 0;
} }
+3 -3
View File
@@ -9,7 +9,7 @@ int main()
unittest unittest
{ {
string input = "ab"; string input = "ab";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
} }
+30 -23
View File
@@ -6,51 +6,58 @@
int main() int main()
{ {
char const * input = ""; char const * input = "";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
p_context_delete(context);
input = "{}"; input = "{}";
p_context_init(&context, (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);
assert(p_result(&context)->id == JSON_OBJECT); assert(p_result(context)->id == JSON_OBJECT);
p_context_delete(context);
input = "[]"; input = "[]";
p_context_init(&context, (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);
assert(p_result(&context)->id == JSON_ARRAY); assert(p_result(context)->id == JSON_ARRAY);
p_context_delete(context);
input = "-45.6"; input = "-45.6";
p_context_init(&context, (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);
assert(p_result(&context)->id == JSON_NUMBER); assert(p_result(context)->id == JSON_NUMBER);
assert(p_result(&context)->number == -45.6); assert(p_result(context)->number == -45.6);
p_context_delete(context);
input = "2E-2"; input = "2E-2";
p_context_init(&context, (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);
assert(p_result(&context)->id == JSON_NUMBER); assert(p_result(context)->id == JSON_NUMBER);
assert(p_result(&context)->number == 0.02); assert(p_result(context)->number == 0.02);
p_context_delete(context);
input = "{\"hi\":true}"; input = "{\"hi\":true}";
p_context_init(&context, (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);
JSONValue * o = p_result(&context); JSONValue * o = p_result(context);
assert(o->id == JSON_OBJECT); assert(o->id == JSON_OBJECT);
assert_eq(1, o->object.size); assert_eq(1, o->object.size);
assert(strcmp(o->object.entries[0].name, "hi") == 0); assert(strcmp(o->object.entries[0].name, "hi") == 0);
assert(o->object.entries[0].value->id == JSON_TRUE); assert(o->object.entries[0].value->id == JSON_TRUE);
p_context_delete(context);
input = "{\"ff\": false, \"nn\": null}"; input = "{\"ff\": false, \"nn\": null}";
p_context_init(&context, (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);
o = p_result(&context); o = p_result(context);
assert(o->id == JSON_OBJECT); assert(o->id == JSON_OBJECT);
assert_eq(2, o->object.size); assert_eq(2, o->object.size);
assert(strcmp(o->object.entries[0].name, "ff") == 0); assert(strcmp(o->object.entries[0].name, "ff") == 0);
assert(o->object.entries[0].value->id == JSON_FALSE); assert(o->object.entries[0].value->id == JSON_FALSE);
assert(strcmp(o->object.entries[1].name, "nn") == 0); assert(strcmp(o->object.entries[1].name, "nn") == 0);
assert(o->object.entries[1].value->id == JSON_NULL); assert(o->object.entries[1].value->id == JSON_NULL);
p_context_delete(context);
return 0; return 0;
} }
+25 -25
View File
@@ -10,45 +10,45 @@ int main()
unittest unittest
{ {
string input = ``; string input = ``;
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
input = `{}`; input = `{}`;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
assert(cast(JSONObject)p_result(&context)); assert(cast(JSONObject)p_result(context));
input = `[]`; input = `[]`;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
assert(cast(JSONArray)p_result(&context)); assert(cast(JSONArray)p_result(context));
input = `-45.6`; input = `-45.6`;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
assert(cast(JSONNumber)p_result(&context)); assert(cast(JSONNumber)p_result(context));
assert((cast(JSONNumber)p_result(&context)).value == -45.6); assert((cast(JSONNumber)p_result(context)).value == -45.6);
input = `2E-2`; input = `2E-2`;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
assert(cast(JSONNumber)p_result(&context)); assert(cast(JSONNumber)p_result(context));
assert((cast(JSONNumber)p_result(&context)).value == 0.02); assert((cast(JSONNumber)p_result(context)).value == 0.02);
input = `{"hi":true}`; input = `{"hi":true}`;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
assert(cast(JSONObject)p_result(&context)); assert(cast(JSONObject)p_result(context));
JSONObject o = cast(JSONObject)p_result(&context); JSONObject o = cast(JSONObject)p_result(context);
assert(o.value["hi"]); assert(o.value["hi"]);
assert(cast(JSONTrue)o.value["hi"]); assert(cast(JSONTrue)o.value["hi"]);
input = `{"ff": false, "nn": null}`; input = `{"ff": false, "nn": null}`;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
assert(cast(JSONObject)p_result(&context)); assert(cast(JSONObject)p_result(context));
o = cast(JSONObject)p_result(&context); o = cast(JSONObject)p_result(context);
assert(o.value["ff"]); assert(o.value["ff"]);
assert(cast(JSONFalse)o.value["ff"]); assert(cast(JSONFalse)o.value["ff"]);
assert(o.value["nn"]); assert(o.value["nn"]);
+13 -10
View File
@@ -5,20 +5,23 @@
int main() int main()
{ {
char const * input = "a"; char const * input = "a";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
assert(p_result(&context) == 1u); assert(p_result(context) == 1u);
p_context_delete(context);
input = ""; input = "";
p_context_init(&context, (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);
assert(p_result(&context) == 0u); assert(p_result(context) == 0u);
p_context_delete(context);
input = "aaaaaaaaaaaaaaaa"; input = "aaaaaaaaaaaaaaaa";
p_context_init(&context, (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);
assert(p_result(&context) == 16u); assert(p_result(context) == 16u);
p_context_delete(context);
return 0; return 0;
} }
+10 -10
View File
@@ -9,18 +9,18 @@ int main()
unittest unittest
{ {
string input = "a"; string input = "a";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
assert(p_result(&context) == 1u); assert(p_result(context) == 1u);
input = ""; input = "";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
assert(p_result(&context) == 0u); assert(p_result(context) == 0u);
input = "aaaaaaaaaaaaaaaa"; input = "aaaaaaaaaaaaaaaa";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
assert(p_result(&context) == 16u); assert(p_result(context) == 16u);
} }
+7 -5
View File
@@ -6,15 +6,17 @@
int main() int main()
{ {
char const * input = "abcdef"; char const * input = "abcdef";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
printf("pass1\n"); printf("pass1\n");
p_context_delete(context);
input = "defabcdef"; input = "defabcdef";
p_context_init(&context, (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);
printf("pass2\n"); printf("pass2\n");
p_context_delete(context);
return 0; return 0;
} }
+5 -5
View File
@@ -9,13 +9,13 @@ int main()
unittest unittest
{ {
string input = "abcdef"; string input = "abcdef";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
writeln("pass1"); writeln("pass1");
input = "defabcdef"; input = "defabcdef";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
writeln("pass2"); writeln("pass2");
} }
+4 -3
View File
@@ -5,9 +5,10 @@
int main() int main()
{ {
char const * input = "defghidef"; char const * input = "defghidef";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
p_context_delete(context);
return 0; return 0;
} }
+3 -3
View File
@@ -9,7 +9,7 @@ int main()
unittest unittest
{ {
string input = "defghidef"; string input = "defghidef";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
} }
+6 -5
View File
@@ -6,14 +6,15 @@
int main() int main()
{ {
char const * input = "hi"; char const * input = "hi";
p_context_t context; p_context_t * context;
p_context_init(&context, (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(top->pToken != NULL);
assert_eq(TOKEN_hi, top->pToken->token); assert_eq(TOKEN_hi, top->pToken->token);
p_free_tree(top); p_tree_delete(top);
p_context_delete(context);
return 0; return 0;
} }
+4 -4
View File
@@ -10,10 +10,10 @@ int main()
unittest unittest
{ {
string input = "hi"; string input = "hi";
p_context_t context; p_context_t * context;
p_context_init(&context, 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 !is null);
assert_eq(TOKEN_hi, top.pToken.token); assert_eq(TOKEN_hi, top.pToken.token);
} }
+13 -10
View File
@@ -6,22 +6,25 @@
int main() int main()
{ {
char const * input = "bbbb"; char const * input = "bbbb";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
int result = p_result(&context); int result = p_result(context);
assert_eq(8, result); assert_eq(8, result);
p_context_delete(context);
p_context_init(&context, (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);
result = p_result_Bs(&context); result = p_result_Bs(context);
assert_eq(8, result); assert_eq(8, result);
p_context_delete(context);
input = "c"; input = "c";
p_context_init(&context, (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);
result = p_result_R(&context); result = p_result_R(context);
assert_eq(3, result); assert_eq(3, result);
p_context_delete(context);
return 0; return 0;
} }
+10 -10
View File
@@ -10,20 +10,20 @@ int main()
unittest unittest
{ {
string input = "bbbb"; string input = "bbbb";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
int result = p_result(&context); int result = p_result(context);
assert(result == 8); assert(result == 8);
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse_Bs(&context) == P_SUCCESS); assert(p_parse_Bs(context) == P_SUCCESS);
result = p_result_Bs(&context); result = p_result_Bs(context);
assert(result == 8); assert(result == 8);
input = "c"; input = "c";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse_R(&context) == P_SUCCESS); assert(p_parse_R(context) == P_SUCCESS);
result = p_result_R(&context); result = p_result_R(context);
assert(result == 3); assert(result == 3);
} }
+16 -13
View File
@@ -6,32 +6,35 @@
int main() int main()
{ {
char const * input = "bbbb"; char const * input = "bbbb";
p_context_t context; p_context_t * context;
p_context_init(&context, (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_not_null(start->bs);
assert_not_null(start->bs->b); assert_not_null(start->bs->b);
assert_not_null(start->bs->bs->b); assert_not_null(start->bs->bs->b);
assert_not_null(start->bs->bs->bs->b); assert_not_null(start->bs->bs->bs->b);
assert_not_null(start->bs->bs->bs->bs->b); assert_not_null(start->bs->bs->bs->bs->b);
p_free_tree(start); p_tree_delete(start);
p_context_delete(context);
p_context_init(&context, (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_not_null(bs->b);
assert_not_null(bs->bs->b); assert_not_null(bs->bs->b);
assert_not_null(bs->bs->bs->b); assert_not_null(bs->bs->bs->b);
assert_not_null(bs->bs->bs->bs->b); assert_not_null(bs->bs->bs->bs->b);
p_free_tree_Bs(bs); p_tree_delete_Bs(bs);
p_context_delete(context);
input = "c"; input = "c";
p_context_init(&context, (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_not_null(r->c);
p_free_tree_R(r); p_tree_delete_R(r);
p_context_delete(context);
return 0; return 0;
} }
+15 -10
View File
@@ -10,27 +10,32 @@ int main()
unittest unittest
{ {
string input = "bbbb"; string input = "bbbb";
p_context_t context; p_context_t * context = p_context_new(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(start.bs); assert(start.bs);
assert(start.bs.b); assert(start.bs.b);
assert(start.bs.bs.b); assert(start.bs.bs.b);
assert(start.bs.bs.bs.b); assert(start.bs.bs.bs.b);
assert(start.bs.bs.bs.bs.b); assert(start.bs.bs.bs.bs.b);
p_context_init(&context, input); p_tree_delete(start);
assert(p_parse_Bs(&context) == P_SUCCESS);
Bs * bs = p_result_Bs(&context); context = p_context_new(input);
assert(p_parse_Bs(context) == P_SUCCESS);
Bs * bs = p_result_Bs(context);
assert(bs.b); assert(bs.b);
assert(bs.bs.b); assert(bs.bs.b);
assert(bs.bs.bs.b); assert(bs.bs.bs.b);
assert(bs.bs.bs.bs.b); assert(bs.bs.bs.bs.b);
p_tree_delete_Bs(bs);
input = "c"; input = "c";
p_context_init(&context, 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);
p_tree_delete_R(r);
} }
+46
View File
@@ -0,0 +1,46 @@
#include "testparser.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
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);
assert(start->pIDs->id);
#ifdef __cplusplus
assert(start->pIDs->id->comments == "# c1\n# c2\n");
#else
assert(start->pIDs->id->comments);
assert(strcmp(start->pIDs->id->comments, "# c1\n# c2\n") == 0);
#endif
assert(start->pIDs->pIDs);
assert(start->pIDs->pIDs->id);
#ifdef __cplusplus
assert(start->pIDs->pIDs->id->comments == "# s1\n# s2\n");
#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);
#endif
p_context_delete(context);
p_tree_delete(start);
return 0;
}
+31
View File
@@ -0,0 +1,31 @@
import testparser;
import std.stdio;
int main()
{
return 0;
}
unittest
{
string input =
"# c1\n" ~
"# c2\n" ~
"\n" ~
"first\n" ~
"\n \n \n" ~
" # s1\n" ~
" # s2\n" ~
"second\n";
p_context_t * context = p_context_new(input);
assert(p_parse(context) == P_SUCCESS);
Start * start = p_result(context);
assert(start.pIDs);
assert(start.pIDs.id);
assert(start.pIDs.id.comments == "# c1\n# c2\n");
assert(start.pIDs.pIDs);
assert(start.pIDs.pIDs.id);
assert(start.pIDs.pIDs.id.comments == "# s1\n# s2\n");
p_tree_delete(start);
}
+16 -13
View File
@@ -6,10 +6,10 @@
int main() int main()
{ {
char const * input = "a, ((b)), b"; char const * input = "a, ((b)), b";
p_context_t context; p_context_t * context;
p_context_init(&context, (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(start->pItems1 != NULL);
assert(start->pItems != NULL); assert(start->pItems != NULL);
Items * items = start->pItems; Items * items = start->pItems;
@@ -33,20 +33,22 @@ int main()
assert_eq(22, itemsmore->pItem->pToken1->pvalue); assert_eq(22, itemsmore->pItem->pToken1->pvalue);
assert(itemsmore->pItemsMore == NULL); assert(itemsmore->pItemsMore == NULL);
p_free_tree(start); p_tree_delete(start);
p_context_delete(context);
input = ""; input = "";
p_context_init(&context, (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(start->pItems == NULL);
p_free_tree(start); p_tree_delete(start);
p_context_delete(context);
input = "2 1"; input = "2 1";
p_context_init(&context, (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(start->pItems != NULL);
assert(start->pItems->pItem != NULL); assert(start->pItems->pItem != NULL);
assert(start->pItems->pItem->pDual != NULL); assert(start->pItems->pItem->pDual != NULL);
@@ -55,7 +57,8 @@ int main()
assert(start->pItems->pItem->pDual->pTwo2 == NULL); assert(start->pItems->pItem->pDual->pTwo2 == NULL);
assert(start->pItems->pItem->pDual->pOne1 == NULL); assert(start->pItems->pItem->pDual->pOne1 == NULL);
p_free_tree(start); p_tree_delete(start);
p_context_delete(context);
return 0; return 0;
} }
+15 -10
View File
@@ -10,10 +10,9 @@ int main()
unittest unittest
{ {
string input = "a, ((b)), b"; string input = "a, ((b)), b";
p_context_t context; p_context_t * context = p_context_new(input);
p_context_init(&context, 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 !is null);
assert(start.pItems !is null); assert(start.pItems !is null);
Items * items = start.pItems; Items * items = start.pItems;
@@ -37,16 +36,20 @@ unittest
assert_eq(22, itemsmore.pItem.pToken1.pvalue); assert_eq(22, itemsmore.pItem.pToken1.pvalue);
assert(itemsmore.pItemsMore is null); assert(itemsmore.pItemsMore is null);
p_tree_delete(start);
input = ""; input = "";
p_context_init(&context, 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 is null);
p_tree_delete(start);
input = "2 1"; input = "2 1";
p_context_init(&context, 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 !is null);
assert(start.pItems.pItem !is null); assert(start.pItems.pItem !is null);
assert(start.pItems.pItem.pDual !is null); assert(start.pItems.pItem.pDual !is null);
@@ -54,4 +57,6 @@ unittest
assert(start.pItems.pItem.pDual.pOne2 !is null); assert(start.pItems.pItem.pDual.pOne2 !is null);
assert(start.pItems.pItem.pDual.pTwo2 is null); assert(start.pItems.pItem.pDual.pTwo2 is null);
assert(start.pItems.pItem.pDual.pOne1 is null); assert(start.pItems.pItem.pDual.pOne1 is null);
p_tree_delete(start);
} }
@@ -6,14 +6,15 @@
int main() int main()
{ {
char const * input = "ab"; char const * input = "ab";
p_context_t context; p_context_t * context;
p_context_init(&context, (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(start->a != NULL);
assert(*start->a->pvalue == 1); assert(*start->a->pvalue == 1);
assert(start->b != NULL); assert(start->b != NULL);
assert(*start->b->pvalue == 2); assert(*start->b->pvalue == 2);
p_free_tree(start); p_tree_delete(start);
p_context_delete(context);
} }
+6 -5
View File
@@ -6,16 +6,17 @@
int main() int main()
{ {
char const * input = "\na\nb\nc"; char const * input = "\na\nb\nc";
p_context_t context; p_context_t * context;
p_context_init(&context, (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, 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_free_tree(start); p_tree_delete(start);
p_context_delete(context);
return 0; return 0;
} }
+5 -4
View File
@@ -10,12 +10,13 @@ int main()
unittest unittest
{ {
string input = "\na\nb\nc"; string input = "\na\nb\nc";
p_context_t context; p_context_t * context = p_context_new(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(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);
} }
+21 -17
View File
@@ -6,10 +6,10 @@
int main() int main()
{ {
char const * input = "\na\n bb ccc"; char const * input = "\na\n bb ccc";
p_context_t context; p_context_t * context;
p_context_init(&context, (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(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);
@@ -30,12 +30,13 @@ int main()
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_free_tree(start); p_tree_delete(start);
p_context_delete(context);
input = "a\nbb"; input = "a\nbb";
p_context_init(&context, (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_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);
@@ -56,12 +57,13 @@ int main()
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_free_tree(start); p_tree_delete(start);
p_context_delete(context);
input = "a\nc\nc"; input = "a\nc\nc";
p_context_init(&context, (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_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);
@@ -82,12 +84,13 @@ int main()
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_free_tree(start); p_tree_delete(start);
p_context_delete(context);
input = "a"; input = "a";
p_context_init(&context, (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_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);
@@ -104,7 +107,8 @@ int main()
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_free_tree(start); p_tree_delete(start);
p_context_delete(context);
return 0; return 0;
} }
+20 -13
View File
@@ -10,10 +10,9 @@ int main()
unittest unittest
{ {
string input = "\na\n bb ccc"; string input = "\na\n bb ccc";
p_context_t context; p_context_t * context = p_context_new(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(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);
@@ -34,10 +33,12 @@ 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);
input = "a\nbb"; input = "a\nbb";
p_context_init(&context, 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_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);
@@ -58,10 +59,12 @@ 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);
input = "a\nc\nc"; input = "a\nc\nc";
p_context_init(&context, 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_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);
@@ -82,10 +85,12 @@ 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);
input = "a"; input = "a";
p_context_init(&context, 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_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);
@@ -101,4 +106,6 @@ unittest
assert_eq(1, start.position.col); assert_eq(1, start.position.col);
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);
} }
+6 -5
View File
@@ -369,11 +369,11 @@ int main(int argc, char * argv[])
{"size_t_to_ulong", TOKEN_ulong}, {"size_t_to_ulong", TOKEN_ulong},
{"main", TOKEN_int}, {"main", TOKEN_int},
}; };
p_context_t context; p_context_t * context;
p_context_init(&context, (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 = pmod->pModuleItems;
PFunctionDefinition ** pfds; PFunctionDefinition ** pfds;
size_t n_pfds = 0u; size_t n_pfds = 0u;
@@ -412,7 +412,8 @@ int main(int argc, char * argv[])
} }
free(pfds); free(pfds);
p_free_tree(pmod); p_tree_delete(pmod);
p_context_delete(context);
return 0; return 0;
} }
+5 -4
View File
@@ -374,11 +374,11 @@ def main() -> int
Expected("size_t_to_ulong", TOKEN_ulong), Expected("size_t_to_ulong", TOKEN_ulong),
Expected("main", TOKEN_int), Expected("main", TOKEN_int),
]; ];
p_context_t context; p_context_t * context;
p_context_init(&context, 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 !is null)
@@ -405,4 +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);
} }
+16 -13
View File
@@ -6,10 +6,10 @@
int main() int main()
{ {
char const * input = "a, ((b)), b"; char const * input = "a, ((b)), b";
p_context_t context; p_context_t * context;
p_context_init(&context, (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(start->pItems1 != NULL);
assert(start->pItems != NULL); assert(start->pItems != NULL);
PItemsS * items = start->pItems; PItemsS * items = start->pItems;
@@ -33,20 +33,22 @@ int main()
assert_eq(22, itemsmore->pItem->pToken1->pvalue); assert_eq(22, itemsmore->pItem->pToken1->pvalue);
assert(itemsmore->pItemsMore == NULL); assert(itemsmore->pItemsMore == NULL);
p_free_tree(start); p_tree_delete(start);
p_context_delete(context);
input = ""; input = "";
p_context_init(&context, (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(start->pItems == NULL);
p_free_tree(start); p_tree_delete(start);
p_context_delete(context);
input = "2 1"; input = "2 1";
p_context_init(&context, (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(start->pItems != NULL);
assert(start->pItems->pItem != NULL); assert(start->pItems->pItem != NULL);
assert(start->pItems->pItem->pDual != NULL); assert(start->pItems->pItem->pDual != NULL);
@@ -55,7 +57,8 @@ int main()
assert(start->pItems->pItem->pDual->pTwo2 == NULL); assert(start->pItems->pItem->pDual->pTwo2 == NULL);
assert(start->pItems->pItem->pDual->pOne1 == NULL); assert(start->pItems->pItem->pDual->pOne1 == NULL);
p_free_tree(start); p_tree_delete(start);
p_context_delete(context);
return 0; return 0;
} }
+15 -10
View File
@@ -10,10 +10,9 @@ int main()
unittest unittest
{ {
string input = "a, ((b)), b"; string input = "a, ((b)), b";
p_context_t context; p_context_t * context = p_context_new(input);
p_context_init(&context, 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 !is null);
assert(start.pItems !is null); assert(start.pItems !is null);
PItemsS * items = start.pItems; PItemsS * items = start.pItems;
@@ -37,16 +36,20 @@ unittest
assert_eq(22, itemsmore.pItem.pToken1.pvalue); assert_eq(22, itemsmore.pItem.pToken1.pvalue);
assert(itemsmore.pItemsMore is null); assert(itemsmore.pItemsMore is null);
p_tree_delete(start);
input = ""; input = "";
p_context_init(&context, 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 is null);
p_tree_delete(start);
input = "2 1"; input = "2 1";
p_context_init(&context, 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 !is null);
assert(start.pItems.pItem !is null); assert(start.pItems.pItem !is null);
assert(start.pItems.pItem.pDual !is null); assert(start.pItems.pItem.pDual !is null);
@@ -54,4 +57,6 @@ unittest
assert(start.pItems.pItem.pDual.pOne2 !is null); assert(start.pItems.pItem.pDual.pOne2 !is null);
assert(start.pItems.pItem.pDual.pTwo2 is null); assert(start.pItems.pItem.pDual.pTwo2 is null);
assert(start.pItems.pItem.pDual.pOne1 is null); assert(start.pItems.pItem.pDual.pOne1 is null);
p_tree_delete(start);
} }
+11 -9
View File
@@ -6,10 +6,10 @@
int main() int main()
{ {
char const * input = "abbccc"; char const * input = "abbccc";
p_context_t context; p_context_t * context;
p_context_init(&context, (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(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);
@@ -43,12 +43,13 @@ int main()
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_free_tree(start); 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";
p_context_init(&context, (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_eq(3, start->pT1->pToken->position.row); assert_eq(3, start->pT1->pToken->position.row);
assert_eq(3, start->pT1->pToken->position.col); assert_eq(3, start->pT1->pToken->position.col);
@@ -82,7 +83,8 @@ int main()
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_free_tree(start); p_tree_delete(start);
p_context_delete(context);
return 0; return 0;
} }
+10 -7
View File
@@ -10,10 +10,9 @@ int main()
unittest unittest
{ {
string input = "abbccc"; string input = "abbccc";
p_context_t context; p_context_t * context = p_context_new(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(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);
@@ -47,10 +46,12 @@ 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);
input = "\n\n bb\nc\ncc\n\n a"; input = "\n\n bb\nc\ncc\n\n a";
p_context_init(&context, 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_eq(3, start.pT1.pToken.position.row); assert_eq(3, start.pT1.pToken.position.row);
assert_eq(3, start.pT1.pToken.position.col); assert_eq(3, start.pT1.pToken.position.col);
@@ -83,4 +84,6 @@ unittest
assert_eq(3, start.position.col); assert_eq(3, start.position.col);
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);
} }
+7 -5
View File
@@ -6,15 +6,17 @@
int main() int main()
{ {
char const * input = "abcdef"; char const * input = "abcdef";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
printf("pass1\n"); printf("pass1\n");
p_context_delete(context);
input = "abcabcdef"; input = "abcabcdef";
p_context_init(&context, (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);
printf("pass2\n"); printf("pass2\n");
p_context_delete(context);
return 0; return 0;
} }
+5 -5
View File
@@ -9,13 +9,13 @@ int main()
unittest unittest
{ {
string input = "abcdef"; string input = "abcdef";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
writeln("pass1"); writeln("pass1");
input = "abcabcdef"; input = "abcabcdef";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
writeln("pass2"); writeln("pass2");
} }
+20
View File
@@ -0,0 +1,20 @@
#include "testparser.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char const * input = "aaa\n\n\na\n # comment 1\na a aa\n\naa\n# comment 2\na\n";
p_context_t * context;
context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(context) == P_SUCCESS);
fprintf(stderr, "comments: %s", context->comments);
fprintf(stderr, "acount: %u\n", context->acount);
free(context->comments);
p_context_delete(context);
return 0;
}
+19
View File
@@ -0,0 +1,19 @@
import testparser;
import std.stdio;
import testutils;
int main()
{
return 0;
}
unittest
{
string input = "aaa\n\n\na\n # comment 1\na a aa\n\naa\n# comment 2\na\n";
p_context_t * context;
context = p_context_new(input);
assert(p_parse(context) == P_SUCCESS);
stderr.writeln("comments: ", context.comments);
stderr.writeln("acount: ", context.acount);
}
+8 -6
View File
@@ -6,14 +6,16 @@
int main() int main()
{ {
char const * input = "aacc"; char const * input = "aacc";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
p_context_delete(context);
input = "abc"; input = "abc";
p_context_init(&context, (uint8_t const *)input, strlen(input)); context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(&context) == P_USER_TERMINATED); assert(p_parse(context) == P_USER_TERMINATED);
assert(p_user_terminate_code(&context) == 4200); assert(p_user_terminate_code(context) == 4200);
p_context_delete(context);
return 0; return 0;
} }
+6 -6
View File
@@ -9,12 +9,12 @@ int main()
unittest unittest
{ {
string input = "aacc"; string input = "aacc";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
input = "abc"; input = "abc";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_USER_TERMINATED); assert(p_parse(context) == P_USER_TERMINATED);
assert(p_user_terminate_code(&context) == 4200); assert(p_user_terminate_code(context) == 4200);
} }
+8 -6
View File
@@ -6,14 +6,16 @@
int main() int main()
{ {
char const * input = "a"; char const * input = "a";
p_context_t context; p_context_t * context;
p_context_init(&context, (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);
p_context_delete(context);
input = "b"; input = "b";
p_context_init(&context, (uint8_t const *)input, strlen(input)); context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(&context) == P_USER_TERMINATED); assert(p_parse(context) == P_USER_TERMINATED);
assert(p_user_terminate_code(&context) == 8675309); assert(p_user_terminate_code(context) == 8675309);
p_context_delete(context);
return 0; return 0;
} }
+6 -6
View File
@@ -9,12 +9,12 @@ int main()
unittest unittest
{ {
string input = "a"; string input = "a";
p_context_t context; p_context_t * context;
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_SUCCESS); assert(p_parse(context) == P_SUCCESS);
input = "b"; input = "b";
p_context_init(&context, input); context = p_context_new(input);
assert(p_parse(&context) == P_USER_TERMINATED); assert(p_parse(context) == P_USER_TERMINATED);
assert(p_user_terminate_code(&context) == 8675309); assert(p_user_terminate_code(context) == 8675309);
} }