59 Commits
Author SHA1 Message Date
josh 64383cb0f4 v4.7.0 2026-07-08 19:41:39 -04:00
josh 256b7f9277 Update CHANGELOG for v4.7.0 2026-07-08 19:08:53 -04:00
josh 98390429a8 Allow parser user code blocks in tree mode 2026-07-07 17:54:01 -04:00
josh b04c0ad205 Do not highlight rule components as propane keywords 2026-07-05 20:52:59 -04:00
josh 453be990b6 v4.6.0 2026-07-02 22:13:54 -04:00
josh 3c619acd29 Update CHANGELOG for v4.6.0 2026-07-02 21:34:30 -04:00
josh ca9c23f96b Add lexer user code expansions to access lexer match text positions 2026-07-02 21:27:55 -04:00
josh 1729546d69 Track rule component positions - close #41 2026-07-02 16:42:50 -04:00
josh 7942a3be97 Remove pvalue from state_info_t if AST mode is active - close #42 2026-07-02 08:14:49 -04:00
josh c28aebddb4 Fix user guide and generated parser comments for position row and col to be 1-based 2026-07-01 23:02:47 -04:00
josh cb914e8bb0 v4.5.0 2026-06-29 23:49:01 -04:00
josh ece95dd7e0 Fix propane.vim keyword detection 2026-06-29 23:48:43 -04:00
josh 232af68081 Attempt to autodetect target language (D/C++) in extra/vim/syntax/propane.vim 2026-06-29 23:02:57 -04:00
josh 23834bef82 Update keyword list in extra/vim/syntax/propane.vim 2026-06-29 23:02:05 -04:00
josh 7b698d7b31 Add noline grammar statement to skip emitting #line directives 2026-06-29 22:41:07 -04:00
josh 45843da0df Fix #line reset directives 2026-06-29 22:21:45 -04:00
josh f27a8688f7 Add CHANGELOG for v4.4.0 2026-06-29 22:15:01 -04:00
josh c459507612 v4.4.0 2026-06-22 23:46:32 -04:00
josh feacbc907d Update gems 2026-06-22 23:46:06 -04:00
josh 5ac3ad1655 Add p_value_get() / p_value_get_XXX() accessors
Allow user code to access specific pvalue values, especially when
multiple ptypes are in use.
2026-06-22 22:12:29 -04:00
josh b641c64425 v4.3.0 2026-05-22 18:16:29 -07:00
josh 5245302f49 Update CHANGELOG for v4.3.0 2026-05-22 18:11:58 -07:00
josh 7a140623ff Use #line for user code blocks to report input grammar position for errors 2026-05-21 16:24:25 -07:00
josh 42035c3a55 Update gems 2026-05-06 08:40:37 -04:00
josh d97f0f8f49 v4.2.0 2026-04-10 18:57:49 -04:00
josh 98e2beb425 Update CHANGELOG for v4.2.0 2026-04-10 18:57:01 -04:00
josh 30565545ee Update gems 2026-04-10 18:52:41 -04:00
josh cd75077959 Update copyright years 2026-04-10 16:36:55 -04:00
josh 51b09a5799 Support custom lex function 2026-04-10 16:36:16 -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
josh 243ee0f19f v3.0.0 2026-02-11 00:21:55 -05:00
josh 8d2efcc19b Update gems 2026-02-11 00:21:37 -05:00
josh 9c787f0e89 Update CHANGELOG.md and UPGRADING.md for v3.0.0 2026-02-09 22:08:20 -05:00
josh ef6a0f9552 Update README and CHANGELOG overview features list 2026-02-09 22:00:39 -05:00
josh 77ec7c9de4 Rename AST generation mode to tree generation mode 2026-02-09 21:59:00 -05:00
josh 6a87bb2d56 Add support for multiple starting rules - close #38 2026-02-09 21:21:49 -05:00
josh cb426b4be1 Add free_token_node grammar statement 2026-02-08 20:33:00 -05:00
josh addf27d837 Turn on valgrind for C, C++ target specs 2026-02-08 19:24:11 -05:00
josh 9c03d20083 Add p_free_ast() to free AST node memory for C targets 2026-02-08 00:02:24 -05:00
josh e223d03d7c Use more concise RSpec output 2026-02-07 19:18:36 -05:00
josh be6a9ca2c1 Rename Rakefile -> Rakefile.rb 2026-02-07 19:18:25 -05:00
josh 072af73b1e Support C++ output language 2026-02-07 18:59:01 -05:00
josh ac8ed4bf5a Update gems 2026-01-15 20:34:31 -05:00
josh 8a393f554a Document p_lex and p_token_info_t in user guide - fix #37 2026-01-15 20:22:54 -05:00
114 changed files with 3939 additions and 1191 deletions

No files matched your search

-1
View File
@@ -1,3 +1,2 @@
--format documentation
--color --color
--require spec_helper --require spec_helper
+94
View File
@@ -1,3 +1,97 @@
## v4.7.0
### New Features
- Support parser rule user code blocks in tree generation mode.
### Fixes
- propane.vim: do not highlight rule components as propane keywords
## v4.6.0
### New Features
- Add lexer user code API to access matched input text positions
- Track rule component text positions and add parser user code API to access
### Fixes
- Fixed a few user guide and source comments related to text input positions
## v4.5.0
### New Features
- Add `noline` grammar statement to skip emitting `#line` directives
- Attempt to autodetect target language (D/C++) in extra/vim/syntax/propane.vim
### Fixes
- Fix #line reset directives
- Update keyword list in extra/vim/syntax/propane.vim
- Fix propane.vim keyword detection
## v4.4.0
### New Features
- Add p_value_get() / p_value_get_XXX() accessors
## v4.3.0
### New Features
- Use #line for user code blocks to report input grammar position for errors.
## v4.2.0
### New Features
- Add support for a custom lex function.
## 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
### New Features
- Add support for multiple starting rules (#38)
- Add `p_free_tree()` functions to reclaim generated tree memory
- Add `free_token_node` grammar statement to reclaim user-allocated memory stored in a Token tree node `pvalue` field
- Add valgrind memory leak tests to unit tests
- Fix build issues for C++ to officially support C++ target output
### Improvements
- Document `p_lex()` and `p_token_info_t` in user guide (#37)
### Breaking Changes
- Rename AST generation mode to tree generation mode (see [UPGRADING.md](UPGRADING.md))
## v2.3.0 ## v2.3.0
### New Features ### New Features
+13 -11
View File
@@ -2,39 +2,41 @@ GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
base64 (0.3.0) base64 (0.3.0)
date (3.4.1) date (3.5.1)
diff-lcs (1.6.2) diff-lcs (1.6.2)
docile (1.4.1) docile (1.4.1)
erb (5.0.2) erb (6.0.4)
psych (5.2.6) psych (5.4.0)
date date
stringio stringio
rake (13.3.0) rake (13.4.2)
rdoc (6.14.2) rdoc (7.2.0)
erb erb
psych (>= 4.0.0) psych (>= 4.0.0)
tsort
redcarpet (3.6.1) redcarpet (3.6.1)
rspec (3.13.1) rspec (3.13.2)
rspec-core (~> 3.13.0) rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0) rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0) rspec-mocks (~> 3.13.0)
rspec-core (3.13.5) rspec-core (3.13.6)
rspec-support (~> 3.13.0) rspec-support (~> 3.13.0)
rspec-expectations (3.13.5) rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0) rspec-support (~> 3.13.0)
rspec-mocks (3.13.5) rspec-mocks (3.13.8)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0) rspec-support (~> 3.13.0)
rspec-support (3.13.4) rspec-support (3.13.7)
simplecov (0.22.0) simplecov (0.22.0)
docile (~> 1.1) docile (~> 1.1)
simplecov-html (~> 0.11) simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1) simplecov_json_formatter (~> 0.1)
simplecov-html (0.13.2) simplecov-html (0.13.2)
simplecov_json_formatter (0.1.4) simplecov_json_formatter (0.1.4)
stringio (3.1.7) stringio (3.2.0)
syntax (1.2.2) syntax (1.2.2)
tsort (0.2.0)
PLATFORMS PLATFORMS
ruby ruby
@@ -49,4 +51,4 @@ DEPENDENCIES
syntax syntax
BUNDLED WITH BUNDLED WITH
2.3.7 4.0.14
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT) The MIT License (MIT)
Copyright (c) 2010-2024 Josh Holtrop Copyright (c) 2010-2026 Josh Holtrop
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+4 -2
View File
@@ -6,8 +6,10 @@ Propane is a LALR Parser Generator (LPG) which:
* generates a built-in lexer to tokenize input * generates a built-in lexer to tokenize input
* supports UTF-8 lexer inputs * supports UTF-8 lexer inputs
* generates a table-driven shift/reduce parser to parse input in linear time * generates a table-driven shift/reduce parser to parse input in linear time
* targets C or D language outputs * targets C, C++, or D language outputs
* optionally supports automatic full AST generation * optionally supports automatic full parse tree generation
* supports starting parsing from multiple start rules
* tracks input text start and end positions for all matched tokens/rules
* is MIT-licensed * is MIT-licensed
* is distributable as a standalone Ruby script * is distributable as a standalone Ruby script
+14
View File
@@ -1,5 +1,7 @@
require "rake/clean" require "rake/clean"
require "rspec/core/rake_task" require "rspec/core/rake_task"
require "simplecov"
require "stringio"
CLEAN.include %w[spec/run gen .yardoc yard coverage dist] CLEAN.include %w[spec/run gen .yardoc yard coverage dist]
@@ -12,6 +14,18 @@ RSpec::Core::RakeTask.new(:spec, :example_pattern) do |task, args|
task.rspec_opts = %W[-e "#{args.example_pattern}" -f documentation] task.rspec_opts = %W[-e "#{args.example_pattern}" -f documentation]
end end
end end
task :spec do |task, args|
unless ENV["dist_specs"]
original_stdout = $stdout
sio = StringIO.new
$stdout = sio
SimpleCov.collate Dir["coverage/.resultset.json"]
$stdout = original_stdout
sio.string.lines.each do |line|
$stdout.write(line) unless line =~ /Coverage report generated for/
end
end
end
# dspec task is useful to test the distributable release script, but is not # dspec task is useful to test the distributable release script, but is not
# useful for coverage information. # useful for coverage information.
+21
View File
@@ -0,0 +1,21 @@
## 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
### Grammar Changes
- Rename `ast;` statement to `tree;`.
- Rename `ast_prefix;` statement to `tree_prefix;`.
- Rename `ast_suffix;` statement to `tree_suffix;`.
+264 -75
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 %>
} }
/************************************************************************** /**************************************************************************
@@ -464,6 +486,22 @@ static size_t attempt_lex_token(<%= @grammar.prefix %>context_t * context, <%= @
case P_SUCCESS: case P_SUCCESS:
{ {
<%= @grammar.prefix %>token_t token_to_accept = match_info.accepting_state->token; <%= @grammar.prefix %>token_t token_to_accept = match_info.accepting_state->token;
/* Calculate the token length and start/end positions before invoking
* the lexer user code so that the user code can access them. The
* context input text position tracking is not updated until after the
* user code has run so that it is left unchanged if the user code
* requests to terminate the lexer. */
token_info.length = match_info.length;
if (match_info.end_delta_position.row != 0u)
{
token_info.end_position.row = token_info.position.row + match_info.end_delta_position.row;
token_info.end_position.col = match_info.end_delta_position.col;
}
else
{
token_info.end_position.row = token_info.position.row;
token_info.end_position.col = token_info.position.col + match_info.end_delta_position.col;
}
if (match_info.accepting_state->code_id != INVALID_USER_CODE_ID) if (match_info.accepting_state->code_id != INVALID_USER_CODE_ID)
{ {
uint8_t const * match = &context->input[context->input_index]; uint8_t const * match = &context->input[context->input_index];
@@ -502,17 +540,6 @@ static size_t attempt_lex_token(<%= @grammar.prefix %>context_t * context, <%= @
return P_DROP; return P_DROP;
} }
token_info.token = token_to_accept; token_info.token = token_to_accept;
token_info.length = match_info.length;
if (match_info.end_delta_position.row != 0u)
{
token_info.end_position.row = token_info.position.row + match_info.end_delta_position.row;
token_info.end_position.col = match_info.end_delta_position.col;
}
else
{
token_info.end_position.row = token_info.position.row;
token_info.end_position.col = token_info.position.col + match_info.end_delta_position.col;
}
*out_token_info = token_info; *out_token_info = token_info;
} }
return P_SUCCESS; return P_SUCCESS;
@@ -638,7 +665,7 @@ typedef struct
* reduce action. * reduce action.
*/ */
parser_state_id_t n_states; parser_state_id_t n_states;
<% if @grammar.ast %> <% if @grammar.tree %>
/** /**
* Map of rule components to rule set child fields. * Map of rule components to rule set child fields.
@@ -646,7 +673,7 @@ typedef struct
uint16_t const * rule_set_node_field_index_map; uint16_t const * rule_set_node_field_index_map;
/** /**
* Number of rule set AST node fields. * Number of rule set tree node fields.
*/ */
uint16_t rule_set_node_field_array_size; uint16_t rule_set_node_field_array_size;
@@ -685,22 +712,28 @@ typedef struct
/** Parser state ID. */ /** Parser state ID. */
size_t state_id; size_t state_id;
<% if @grammar.tree %>
/** tree node. */
void * tree_node;
<% else %>
<%= @grammar.prefix %>position_t position;
<%= @grammar.prefix %>position_t end_position;
/** Parser value from this state. */ /** Parser value from this state. */
<%= @grammar.prefix %>value_t pvalue; <%= @grammar.prefix %>value_t pvalue;
<% if @grammar.ast %>
/** AST node. */
void * ast_node;
<% end %> <% end %>
} state_value_t; } state_value_t;
/** Common AST node structure. */ <% if @grammar.tree %>
typedef struct /** Common tree node structure. */
typedef struct TreeNode_s
{ {
<%= @grammar.prefix %>position_t position; <%= @grammar.prefix %>position_t position;
<%= @grammar.prefix %>position_t end_position; <%= @grammar.prefix %>position_t end_position;
void * fields[]; uint16_t n_fields;
} ASTNode; uint8_t is_token;
struct TreeNode_s * fields[];
} TreeNode;
<% end %>
/** Parser shift table. */ /** Parser shift table. */
static const shift_t parser_shift_table[] = { static const shift_t parser_shift_table[] = {
@@ -709,7 +742,7 @@ static const shift_t parser_shift_table[] = {
<% end %> <% end %>
}; };
<% if @grammar.ast %> <% if @grammar.tree %>
<% @grammar.rules.each do |rule| %> <% @grammar.rules.each do |rule| %>
<% unless rule.flat_rule_set_node_field_index_map? %> <% unless rule.flat_rule_set_node_field_index_map? %>
const uint16_t r_<%= rule.name.gsub("$", "_") %><%= rule.id %>_node_field_index_map[<%= rule.rule_set_node_field_index_map.size %>] = {<%= rule.rule_set_node_field_index_map.map {|v| v.to_s}.join(", ") %>}; const uint16_t r_<%= rule.name.gsub("$", "_") %><%= rule.id %>_node_field_index_map[<%= rule.rule_set_node_field_index_map.size %>] = {<%= rule.rule_set_node_field_index_map.map {|v| v.to_s}.join(", ") %>};
@@ -724,14 +757,14 @@ static const reduce_t parser_reduce_table[] = {
<%= reduce[:token_id] %>u, /* Token: <%= reduce[:token] ? reduce[:token].name : "(any)" %> */ <%= reduce[:token_id] %>u, /* Token: <%= reduce[:token] ? reduce[:token].name : "(any)" %> */
<%= reduce[:rule_id] %>u, /* Rule ID */ <%= reduce[:rule_id] %>u, /* Rule ID */
<%= reduce[:rule_set_id] %>u, /* Rule set ID (<%= reduce[:rule].rule_set.name %>) */ <%= reduce[:rule_set_id] %>u, /* Rule set ID (<%= reduce[:rule].rule_set.name %>) */
<% if @grammar.ast %> <% if @grammar.tree %>
<%= reduce[:n_states] %>u, /* Number of states */ <%= reduce[:n_states] %>u, /* Number of states */
<% if reduce[:rule].flat_rule_set_node_field_index_map? %> <% if reduce[:rule].flat_rule_set_node_field_index_map? %>
NULL, /* No rule set node field index map (flat map) */ NULL, /* No rule set node field index map (flat map) */
<% else %> <% else %>
&r_<%= reduce[:rule].name.gsub("$", "_") %><%= reduce[:rule].id %>_node_field_index_map[0], /* Rule set node field index map */ &r_<%= reduce[:rule].name.gsub("$", "_") %><%= reduce[:rule].id %>_node_field_index_map[0], /* Rule set node field index map */
<% end %> <% end %>
<%= reduce[:rule].rule_set.ast_fields.size %>, /* Number of AST fields */ <%= reduce[:rule].rule_set.tree_fields.size %>, /* Number of tree fields */
<%= reduce[:propagate_optional_target] %>}, /* Propagate optional target? */ <%= reduce[:propagate_optional_target] %>}, /* Propagate optional target? */
<% else %> <% else %>
<%= reduce[:n_states] %>u}, <%= reduce[:n_states] %>u},
@@ -805,7 +838,7 @@ static void state_values_stack_push(state_values_stack_t * stack)
if (current_length >= current_capacity) if (current_length >= current_capacity)
{ {
size_t const new_capacity = current_capacity * 2u; size_t const new_capacity = current_capacity * 2u;
state_value_t * new_entries = malloc(new_capacity * sizeof(state_value_t)); state_value_t * new_entries = (state_value_t *)malloc(new_capacity * sizeof(state_value_t));
memcpy(new_entries, stack->entries, current_length * sizeof(state_value_t)); memcpy(new_entries, stack->entries, current_length * sizeof(state_value_t));
free(stack->entries); free(stack->entries);
stack->capacity = new_capacity; stack->capacity = new_capacity;
@@ -839,7 +872,60 @@ static void state_values_stack_free(state_values_stack_t * stack)
free(stack->entries); free(stack->entries);
} }
<% unless @grammar.ast %> <% unless @grammar.tree %>
/**
* Get the rule position (start or end) for the currently matched rule.
*/
static <%= @grammar.prefix %>position_t get_rule_position(state_values_stack_t * statevalues, size_t i, size_t n_states, bool get_end)
{
if (n_states > 0u)
{
if (i == 0u)
{
if (get_end)
{
int stack_index = -1;
for (size_t j = 0u; j < n_states; j++)
{
state_value_t * sv = state_values_stack_index(statevalues, stack_index - (int)j);
if (<%= @grammar.prefix %>position_valid(sv->end_position))
{
return sv->end_position;
}
}
}
else
{
int stack_index = -(int)n_states;
for (size_t j = 0u; j < n_states; j++)
{
state_value_t * sv = state_values_stack_index(statevalues, stack_index + (int)j);
if (<%= @grammar.prefix %>position_valid(sv->position))
{
return sv->position;
}
}
}
}
else
{
if (get_end)
{
return state_values_stack_index(statevalues, -1 - (int)n_states + (int)i)->end_position;
}
else
{
return state_values_stack_index(statevalues, -1 - (int)n_states + (int)i)->position;
}
}
}
<%= @grammar.prefix %>position_t empty_pos;
memset(&empty_pos, 0, sizeof(empty_pos));
return empty_pos;
}
<% end %>
<% if !@grammar.tree || @grammar.parser_user_code_used? %>
/** /**
* Execute user code associated with a parser rule. * Execute user code associated with a parser rule.
* *
@@ -850,7 +936,7 @@ static void state_values_stack_free(state_values_stack_t * stack)
* @retval P_USER_TERMINATED * @retval P_USER_TERMINATED
* User requested to terminate parsing. * User requested to terminate parsing.
*/ */
static size_t parser_user_code(<%= @grammar.prefix %>value_t * _pvalue, uint32_t rule, state_values_stack_t * statevalues, uint32_t n_states, <%= @grammar.prefix %>context_t * context) static size_t parser_user_code(<%= @grammar.tree ? "void" : "#{@grammar.prefix}value_t" %> * _pvalue, uint32_t rule, state_values_stack_t * statevalues, uint32_t n_states, <%= @grammar.prefix %>context_t * context)
{ {
switch (rule) switch (rule)
{ {
@@ -922,6 +1008,8 @@ static size_t check_reduce(size_t state_id, <%= @grammar.prefix %>token_t token)
* *
* @param context * @param context
* Lexer/parser context structure. * Lexer/parser context structure.
* @start_state_id
* ID of the state in which to start.
* *
* @retval P_SUCCESS * @retval P_SUCCESS
* The parser successfully matched the input text. The parse result value * The parser successfully matched the input text. The parse result value
@@ -934,25 +1022,28 @@ static size_t check_reduce(size_t state_id, <%= @grammar.prefix %>token_t token)
* @reval P_UNEXPECTED_INPUT * @reval P_UNEXPECTED_INPUT
* Input text does not match any lexer pattern. * Input text does not match any lexer pattern.
*/ */
size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context) static size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t start_state_id)
{ {
<%= @grammar.prefix %>token_info_t token_info; <%= @grammar.prefix %>token_info_t token_info;
<%= @grammar.prefix %>token_t token = INVALID_TOKEN_ID; <%= @grammar.prefix %>token_t token = INVALID_TOKEN_ID;
state_values_stack_t statevalues; state_values_stack_t statevalues;
size_t reduced_rule_set = INVALID_ID; size_t reduced_rule_set = INVALID_ID;
<% if @grammar.ast %> <% if @grammar.tree %>
void * reduced_parser_node; void * reduced_parser_node;
<% else %> <% else %>
<%= @grammar.prefix %>position_t reduced_position;
<%= @grammar.prefix %>position_t reduced_end_position;
<%= @grammar.prefix %>value_t reduced_parser_value; <%= @grammar.prefix %>value_t reduced_parser_value;
<% end %> <% end %>
state_values_stack_init(&statevalues); state_values_stack_init(&statevalues);
state_values_stack_push(&statevalues); state_values_stack_push(&statevalues);
state_values_stack_index(&statevalues, -1)->state_id = start_state_id;
size_t result; size_t result;
for (;;) for (;;)
{ {
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;
@@ -971,8 +1062,8 @@ size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context)
if ((shift_state != INVALID_ID) && (token == TOKEN___EOF)) if ((shift_state != INVALID_ID) && (token == TOKEN___EOF))
{ {
/* Successful parse. */ /* Successful parse. */
<% if @grammar.ast %> <% if @grammar.tree %>
context->parse_result = (<%= @grammar.ast_prefix %><%= @grammar.start_rule %><%= @grammar.ast_suffix %> *)state_values_stack_index(&statevalues, -1)->ast_node; context->parse_result = state_values_stack_index(&statevalues, -1)->tree_node;
<% else %> <% else %>
context->parse_result = state_values_stack_index(&statevalues, -1)->pvalue; context->parse_result = state_values_stack_index(&statevalues, -1)->pvalue;
<% end %> <% end %>
@@ -984,29 +1075,41 @@ size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context)
{ {
/* We have something to shift. */ /* We have something to shift. */
state_values_stack_push(&statevalues); state_values_stack_push(&statevalues);
state_values_stack_index(&statevalues, -1)->state_id = shift_state; state_value_t * new_state_info = state_values_stack_index(&statevalues, -1);
new_state_info->state_id = shift_state;
if (reduced_rule_set == INVALID_ID) if (reduced_rule_set == INVALID_ID)
{ {
/* We shifted a token, mark it consumed. */ /* We shifted a token, mark it consumed. */
<% if @grammar.ast %> <% if @grammar.tree %>
<%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %> * token_ast_node = malloc(sizeof(<%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>)); <% if @cpp %>
token_ast_node->position = token_info.position; <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %> * token_tree_node = new <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>();
token_ast_node->end_position = token_info.end_position;
token_ast_node->token = token;
token_ast_node->pvalue = token_info.pvalue;
state_values_stack_index(&statevalues, -1)->ast_node = token_ast_node;
<% else %> <% else %>
state_values_stack_index(&statevalues, -1)->pvalue = token_info.pvalue; <%= @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->end_position = token_info.end_position;
token_tree_node->n_fields = 0u;
token_tree_node->is_token = 1u;
token_tree_node->token = token;
token_tree_node->pvalue = token_info.pvalue;
<%= expand_code(@grammar.on_token_node, false, nil, nil) %>
new_state_info->tree_node = token_tree_node;
<% else %>
new_state_info->position = token_info.position;
new_state_info->end_position = token_info.end_position;
new_state_info->pvalue = token_info.pvalue;
<% end %> <% end %>
token = INVALID_TOKEN_ID; token = INVALID_TOKEN_ID;
} }
else else
{ {
/* We shifted a RuleSet. */ /* We shifted a RuleSet. */
<% if @grammar.ast %> <% if @grammar.tree %>
state_values_stack_index(&statevalues, -1)->ast_node = reduced_parser_node; new_state_info->tree_node = reduced_parser_node;
<% else %> <% else %>
state_values_stack_index(&statevalues, -1)->pvalue = reduced_parser_value; new_state_info->pvalue = reduced_parser_value;
new_state_info->position = reduced_position;
new_state_info->end_position = reduced_end_position;
<%= @grammar.prefix %>value_t new_parse_result; <%= @grammar.prefix %>value_t new_parse_result;
memset(&new_parse_result, 0, sizeof(new_parse_result)); memset(&new_parse_result, 0, sizeof(new_parse_result));
reduced_parser_value = new_parse_result; reduced_parser_value = new_parse_result;
@@ -1020,39 +1123,38 @@ size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context)
if (reduce_index != INVALID_ID) if (reduce_index != INVALID_ID)
{ {
/* We have something to reduce. */ /* We have something to reduce. */
<% if @grammar.ast %> <% if @grammar.tree %>
if (parser_reduce_table[reduce_index].propagate_optional_target) if (parser_reduce_table[reduce_index].propagate_optional_target)
{ {
reduced_parser_node = state_values_stack_index(&statevalues, -1)->ast_node; reduced_parser_node = state_values_stack_index(&statevalues, -1)->tree_node;
} }
else if (parser_reduce_table[reduce_index].n_states > 0) else if (parser_reduce_table[reduce_index].n_states > 0)
{ {
size_t n_fields = parser_reduce_table[reduce_index].rule_set_node_field_array_size; size_t n_fields = parser_reduce_table[reduce_index].rule_set_node_field_array_size;
ASTNode * node = (ASTNode *)malloc(sizeof(ASTNode) + n_fields * sizeof(void *)); size_t bytes = sizeof(TreeNode) + n_fields * sizeof(void *);
TreeNode * node = (TreeNode *)malloc(bytes);
memset(node, 0, bytes);
node->position = INVALID_POSITION; node->position = INVALID_POSITION;
node->end_position = INVALID_POSITION; node->end_position = INVALID_POSITION;
for (size_t i = 0; i < n_fields; i++) node->n_fields = n_fields;
{
node->fields[i] = NULL;
}
if (parser_reduce_table[reduce_index].rule_set_node_field_index_map == NULL) if (parser_reduce_table[reduce_index].rule_set_node_field_index_map == NULL)
{ {
for (size_t i = 0; i < parser_reduce_table[reduce_index].n_states; i++) for (size_t i = 0; i < parser_reduce_table[reduce_index].n_states; i++)
{ {
node->fields[i] = state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->ast_node; node->fields[i] = (TreeNode *)state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->tree_node;
} }
} }
else else
{ {
for (size_t i = 0; i < parser_reduce_table[reduce_index].n_states; i++) for (size_t i = 0; i < parser_reduce_table[reduce_index].n_states; i++)
{ {
node->fields[parser_reduce_table[reduce_index].rule_set_node_field_index_map[i]] = state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->ast_node; node->fields[parser_reduce_table[reduce_index].rule_set_node_field_index_map[i]] = (TreeNode *)state_values_stack_index(&statevalues, -(int)parser_reduce_table[reduce_index].n_states + (int)i)->tree_node;
} }
} }
bool position_found = false; bool position_found = false;
for (size_t i = 0; i < n_fields; i++) for (size_t i = 0; i < n_fields; i++)
{ {
ASTNode * child = (ASTNode *)node->fields[i]; TreeNode * child = node->fields[i];
if ((child != NULL) && <%= @grammar.prefix %>position_valid(child->position)) if ((child != NULL) && <%= @grammar.prefix %>position_valid(child->position))
{ {
if (!position_found) if (!position_found)
@@ -1069,14 +1171,32 @@ size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context)
{ {
reduced_parser_node = NULL; reduced_parser_node = NULL;
} }
<% if @grammar.parser_user_code_used? %>
if (parser_user_code(reduced_parser_node, parser_reduce_table[reduce_index].rule, &statevalues, parser_reduce_table[reduce_index].n_states, context) == P_USER_TERMINATED)
{
state_values_stack_free(&statevalues);
return P_USER_TERMINATED;
}
<% end %>
<% else %> <% else %>
<%= @grammar.prefix %>value_t reduced_parser_value2; <%= @grammar.prefix %>value_t reduced_parser_value2;
memset(&reduced_parser_value2, 0, sizeof(reduced_parser_value2)); memset(&reduced_parser_value2, 0, sizeof(reduced_parser_value2));
if (parser_user_code(&reduced_parser_value2, parser_reduce_table[reduce_index].rule, &statevalues, parser_reduce_table[reduce_index].n_states, context) == P_USER_TERMINATED) if (parser_user_code(&reduced_parser_value2, parser_reduce_table[reduce_index].rule, &statevalues, parser_reduce_table[reduce_index].n_states, context) == P_USER_TERMINATED)
{ {
state_values_stack_free(&statevalues);
return P_USER_TERMINATED; return P_USER_TERMINATED;
} }
reduced_parser_value = reduced_parser_value2; reduced_parser_value = reduced_parser_value2;
if (parser_reduce_table[reduce_index].n_states > 0u)
{
reduced_position = get_rule_position(&statevalues, 0u, parser_reduce_table[reduce_index].n_states, false);
reduced_end_position = get_rule_position(&statevalues, 0u, parser_reduce_table[reduce_index].n_states, true);
}
else
{
memset(&reduced_position, 0, sizeof(reduced_position));
memset(&reduced_end_position, 0, sizeof(reduced_end_position));
}
<% end %> <% end %>
reduced_rule_set = parser_reduce_table[reduce_index].rule_set; reduced_rule_set = parser_reduce_table[reduce_index].rule_set;
state_values_stack_pop(&statevalues, parser_reduce_table[reduce_index].n_states); state_values_stack_pop(&statevalues, parser_reduce_table[reduce_index].n_states);
@@ -1097,6 +1217,19 @@ size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context)
return result; return result;
} }
size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context)
{
return parse_from(context, 0u);
}
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
size_t <%= @grammar.prefix %>parse_<%= start_rule %>(<%= @grammar.prefix %>context_t * context)
{
return parse_from(context, <%= i %>u);
}
<% end %>
/** /**
* Get the parse result value. * Get the parse result value.
* *
@@ -1105,18 +1238,29 @@ size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context)
* *
* @return Parse result value. * @return Parse result value.
*/ */
<% if @grammar.ast %> <% if @grammar.tree %>
<%= @grammar.ast_prefix %><%= @grammar.start_rule %><%= @grammar.ast_suffix %> * <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context) <%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context)
{
return (<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> *) context->parse_result;
}
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * <%= @grammar.prefix %>result_<%= start_rule %>(<%= @grammar.prefix %>context_t * context)
{
return (<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> *) context->parse_result;
}
<% end %>
<% else %> <% else %>
<%= start_rule_type[1] %> <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context) <%= start_rule_type[1] %> <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context)
<% end %>
{ {
<% if @grammar.ast %>
return context->parse_result;
<% else %>
return context->parse_result.v_<%= start_rule_type[0] %>; return context->parse_result.v_<%= start_rule_type[0] %>;
<% end %>
} }
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
<%= start_rule_type(i)[1] %> <%= @grammar.prefix %>result_<%= start_rule %>(<%= @grammar.prefix %>context_t * context)
{
return context->parse_result.v_<%= start_rule_type(i)[0] %>;
}
<% end %>
<% end %>
/** /**
* Get the current text input position. * Get the current text input position.
@@ -1153,3 +1297,48 @@ size_t <%= @grammar.prefix %>user_terminate_code(<%= @grammar.prefix %>context_t
{ {
return context->token; return context->token;
} }
<% if @grammar.tree %>
static void tree_delete(TreeNode * node)
{
if (node->is_token)
{
<%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %> * token_tree_node = (<%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %> *)node;
<%= expand_code(@grammar.free_token_node, false, nil, nil) %>
<% if @cpp %>
delete token_tree_node;
<% else %>
free(token_tree_node);
<% end %>
}
else if (node->n_fields > 0u)
{
for (size_t i = 0u; i < node->n_fields; i++)
{
if (node->fields[i] != NULL)
{
tree_delete(node->fields[i]);
}
}
free(node);
}
}
/**
* Free all tree node memory.
*/
void <%= @grammar.prefix %>tree_delete(<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * tree)
{
tree_delete((TreeNode *)tree);
}
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
/**
* Free all tree node memory.
*/
void <%= @grammar.prefix %>tree_delete_<%= start_rule %>(<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * tree)
{
tree_delete((TreeNode *)tree);
}
<% end %>
<% end %>
+246 -71
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
@@ -59,10 +59,10 @@ public alias <%= @grammar.prefix %>code_point_t = uint;
*/ */
public struct <%= @grammar.prefix %>position_t public struct <%= @grammar.prefix %>position_t
{ {
/** Input text row (0-based). */ /** Input text row (1-based). */
uint row; uint row;
/** Input text column (0-based). */ /** Input text column (1-based). */
uint col; uint col;
/** Invalid position value. */ /** Invalid position value. */
@@ -75,7 +75,7 @@ public struct <%= @grammar.prefix %>position_t
} }
} }
<% if @grammar.ast %> <% if @grammar.tree %>
/** Parser values type. */ /** Parser values type. */
public alias <%= @grammar.prefix %>value_t = <%= @grammar.ptype %>; public alias <%= @grammar.prefix %>value_t = <%= @grammar.ptype %>;
<% else %> <% else %>
@@ -86,35 +86,58 @@ 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 %>
<% if @grammar.ast %> /** Parser value accessor(s). */
/** Common AST node structure. */ <% @grammar.ptypes.each do |name, typestring| %>
private struct ASTNode public <%= typestring %> <%= @grammar.prefix %>value_get<%= name == "default" ? "" : "_#{name}" %>(<%= @grammar.prefix %>value_t * pvalue)
{
return pvalue.v_<%= name %>;
}
<% end %>
<% end %>
<% if @grammar.tree %>
/** Common tree node structure. */
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;
} }
/** AST node types. @{ */ /** Tree node types. @{ */
public struct <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %> public struct <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>
{ {
/* ASTNode 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| %>
<% next if name.start_with?("$") %> <% next if name.start_with?("$") %>
<% next if rule_set.optional? %> <% next if rule_set.optional? %>
public struct <%= @grammar.ast_prefix %><%= name %><%= @grammar.ast_suffix %> 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;
<% rule_set.ast_fields.each do |fields| %> ushort n_fields;
bool is_token;
<% rule_set.tree_fields.each do |fields| %>
union union
{ {
<% fields.each do |field_name, type| %> <% fields.each do |field_name, type| %>
@@ -172,8 +195,8 @@ public struct <%= @grammar.prefix %>context_t
/* Parser context data. */ /* Parser context data. */
/** Parse result value. */ /** Parse result value. */
<% if @grammar.ast %> <% if @grammar.tree %>
<%= @grammar.ast_prefix %><%= @grammar.start_rule %><%= @grammar.ast_suffix %> * parse_result; void * parse_result;
<% else %> <% else %>
<%= @grammar.prefix %>value_t parse_result; <%= @grammar.prefix %>value_t parse_result;
<% end %> <% end %>
@@ -183,6 +206,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 +247,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)
{
} }
/************************************************************************** /**************************************************************************
@@ -627,6 +665,22 @@ private size_t attempt_lex_token(<%= @grammar.prefix %>context_t * context, <%=
{ {
case P_SUCCESS: case P_SUCCESS:
<%= @grammar.prefix %>token_t token_to_accept = match_info.accepting_state.token; <%= @grammar.prefix %>token_t token_to_accept = match_info.accepting_state.token;
/* Calculate the token length and start/end positions before invoking
* the lexer user code so that the user code can access them. The
* context input text position tracking is not updated until after the
* user code has run so that it is left unchanged if the user code
* requests to terminate the lexer. */
token_info.length = match_info.length;
if (match_info.end_delta_position.row != 0u)
{
token_info.end_position.row = token_info.position.row + match_info.end_delta_position.row;
token_info.end_position.col = match_info.end_delta_position.col;
}
else
{
token_info.end_position.row = token_info.position.row;
token_info.end_position.col = token_info.position.col + match_info.end_delta_position.col;
}
if (match_info.accepting_state.code_id != INVALID_USER_CODE_ID) if (match_info.accepting_state.code_id != INVALID_USER_CODE_ID)
{ {
string match = context.input[context.input_index..(context.input_index + match_info.length)]; string match = context.input[context.input_index..(context.input_index + match_info.length)];
@@ -665,17 +719,6 @@ private size_t attempt_lex_token(<%= @grammar.prefix %>context_t * context, <%=
return P_DROP; return P_DROP;
} }
token_info.token = token_to_accept; token_info.token = token_to_accept;
token_info.length = match_info.length;
if (match_info.end_delta_position.row != 0u)
{
token_info.end_position.row = token_info.position.row + match_info.end_delta_position.row;
token_info.end_position.col = match_info.end_delta_position.col;
}
else
{
token_info.end_position.row = token_info.position.row;
token_info.end_position.col = token_info.position.col + match_info.end_delta_position.col;
}
*out_token_info = token_info; *out_token_info = token_info;
return P_SUCCESS; return P_SUCCESS;
@@ -797,7 +840,7 @@ private struct reduce_t
* reduce action. * reduce action.
*/ */
parser_state_id_t n_states; parser_state_id_t n_states;
<% if @grammar.ast %> <% if @grammar.tree %>
/** /**
* Map of rule components to rule set child fields. * Map of rule components to rule set child fields.
@@ -805,7 +848,7 @@ private struct reduce_t
immutable(ushort) * rule_set_node_field_index_map; immutable(ushort) * rule_set_node_field_index_map;
/** /**
* Number of rule set AST node fields. * Number of rule set tree node fields.
*/ */
ushort rule_set_node_field_array_size; ushort rule_set_node_field_array_size;
@@ -844,12 +887,14 @@ private struct state_value_t
/** Parser state ID. */ /** Parser state ID. */
size_t state_id; size_t state_id;
<% if @grammar.tree %>
/** Tree node. */
void * tree_node;
<% else %>
<%= @grammar.prefix %>position_t position;
<%= @grammar.prefix %>position_t end_position;
/** Parser value from this state. */ /** Parser value from this state. */
<%= @grammar.prefix %>value_t pvalue; <%= @grammar.prefix %>value_t pvalue;
<% if @grammar.ast %>
/** AST node. */
void * ast_node;
<% end %> <% end %>
this(size_t state_id) this(size_t state_id)
@@ -865,7 +910,7 @@ private immutable shift_t[] parser_shift_table = [
<% end %> <% end %>
]; ];
<% if @grammar.ast %> <% if @grammar.tree %>
<% @grammar.rules.each do |rule| %> <% @grammar.rules.each do |rule| %>
<% unless rule.flat_rule_set_node_field_index_map? %> <% unless rule.flat_rule_set_node_field_index_map? %>
immutable ushort[<%= rule.rule_set_node_field_index_map.size %>] r_<%= rule.name.gsub("$", "_") %><%= rule.id %>_node_field_index_map = [<%= rule.rule_set_node_field_index_map.map {|v| v.to_s}.join(", ") %>]; immutable ushort[<%= rule.rule_set_node_field_index_map.size %>] r_<%= rule.name.gsub("$", "_") %><%= rule.id %>_node_field_index_map = [<%= rule.rule_set_node_field_index_map.map {|v| v.to_s}.join(", ") %>];
@@ -880,14 +925,14 @@ private immutable reduce_t[] parser_reduce_table = [
<%= reduce[:token_id] %>u, /* Token: <%= reduce[:token] ? reduce[:token].name : "(any)" %> */ <%= reduce[:token_id] %>u, /* Token: <%= reduce[:token] ? reduce[:token].name : "(any)" %> */
<%= reduce[:rule_id] %>u, /* Rule ID */ <%= reduce[:rule_id] %>u, /* Rule ID */
<%= reduce[:rule_set_id] %>u, /* Rule set ID (<%= reduce[:rule].rule_set.name %>) */ <%= reduce[:rule_set_id] %>u, /* Rule set ID (<%= reduce[:rule].rule_set.name %>) */
<% if @grammar.ast %> <% if @grammar.tree %>
<%= reduce[:n_states] %>u, /* Number of states */ <%= reduce[:n_states] %>u, /* Number of states */
<% if reduce[:rule].flat_rule_set_node_field_index_map? %> <% if reduce[:rule].flat_rule_set_node_field_index_map? %>
null, /* No rule set node field index map (flat map) */ null, /* No rule set node field index map (flat map) */
<% else %> <% else %>
&r_<%= reduce[:rule].name.gsub("$", "_") %><%= reduce[:rule].id %>_node_field_index_map[0], /* Rule set node field index map */ &r_<%= reduce[:rule].name.gsub("$", "_") %><%= reduce[:rule].id %>_node_field_index_map[0], /* Rule set node field index map */
<% end %> <% end %>
<%= reduce[:rule].rule_set.ast_fields.size %>, /* Number of AST fields */ <%= reduce[:rule].rule_set.tree_fields.size %>, /* Number of tree fields */
<%= reduce[:propagate_optional_target] %>), /* Propagate optional target? */ <%= reduce[:propagate_optional_target] %>), /* Propagate optional target? */
<% else %> <% else %>
<%= reduce[:n_states] %>u), /* Number of states */ <%= reduce[:n_states] %>u), /* Number of states */
@@ -902,7 +947,56 @@ private immutable parser_state_t[] parser_state_table = [
<% end %> <% end %>
]; ];
<% unless @grammar.ast %> <% unless @grammar.tree %>
/**
* Get the rule position (start or end) for the currently matched rule.
*/
private <%= @grammar.prefix %>position_t get_rule_position(state_value_t[] statevalues, size_t i, size_t n_states, bool get_end)
{
if (n_states > 0u)
{
if (i == 0u)
{
if (get_end)
{
for (size_t j = 0u; j < n_states; j++)
{
state_value_t * sv = &statevalues[$-1-j];
if (sv.end_position.valid)
{
return sv.end_position;
}
}
}
else
{
for (size_t j = 0u; j < n_states; j++)
{
state_value_t * sv = &statevalues[$-n_states+j];
if (sv.position.valid)
{
return sv.position;
}
}
}
}
else
{
if (get_end)
{
return statevalues[$-1-n_states+i].end_position;
}
else
{
return statevalues[$-1-n_states+i].position;
}
}
}
return <%= @grammar.prefix %>position_t.INVALID;
}
<% end %>
<% if !@grammar.tree || @grammar.parser_user_code_used? %>
/** /**
* Execute user code associated with a parser rule. * Execute user code associated with a parser rule.
* *
@@ -913,7 +1007,7 @@ private immutable parser_state_t[] parser_state_table = [
* @retval P_USER_TERMINATED * @retval P_USER_TERMINATED
* User requested to terminate parsing. * User requested to terminate parsing.
*/ */
private size_t parser_user_code(<%= @grammar.prefix %>value_t * _pvalue, uint rule, state_value_t[] statevalues, uint n_states, <%= @grammar.prefix %>context_t * context) private size_t parser_user_code(<%= @grammar.tree ? "void" : "#{@grammar.prefix}value_t" %> * _pvalue, uint rule, state_value_t[] statevalues, uint n_states, <%= @grammar.prefix %>context_t * context)
{ {
switch (rule) switch (rule)
{ {
@@ -985,6 +1079,8 @@ private size_t check_reduce(size_t state_id, <%= @grammar.prefix %>token_t token
* *
* @param context * @param context
* Lexer/parser context structure. * Lexer/parser context structure.
* @start_state_id
* ID of the state in which to start.
* *
* @retval P_SUCCESS * @retval P_SUCCESS
* The parser successfully matched the input text. The parse result value * The parser successfully matched the input text. The parse result value
@@ -997,22 +1093,25 @@ private size_t check_reduce(size_t state_id, <%= @grammar.prefix %>token_t token
* @reval P_UNEXPECTED_INPUT * @reval P_UNEXPECTED_INPUT
* Input text does not match any lexer pattern. * Input text does not match any lexer pattern.
*/ */
public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context) private size_t parse_from(<%= @grammar.prefix %>context_t * context, size_t start_state_id)
{ {
<%= @grammar.prefix %>token_info_t token_info; <%= @grammar.prefix %>token_info_t token_info;
<%= @grammar.prefix %>token_t token = INVALID_TOKEN_ID; <%= @grammar.prefix %>token_t token = INVALID_TOKEN_ID;
state_value_t[] statevalues = new state_value_t[](1); state_value_t[] statevalues = new state_value_t[](1);
statevalues[0].state_id = start_state_id;
size_t reduced_rule_set = INVALID_ID; size_t reduced_rule_set = INVALID_ID;
<% if @grammar.ast %> <% if @grammar.tree %>
void * reduced_parser_node; void * reduced_parser_node;
<% else %> <% else %>
<%= @grammar.prefix %>position_t reduced_position;
<%= @grammar.prefix %>position_t reduced_end_position;
<%= @grammar.prefix %>value_t reduced_parser_value; <%= @grammar.prefix %>value_t reduced_parser_value;
<% end %> <% end %>
for (;;) for (;;)
{ {
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;
@@ -1030,8 +1129,8 @@ public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * cont
if ((shift_state != INVALID_ID) && (token == TOKEN___EOF)) if ((shift_state != INVALID_ID) && (token == TOKEN___EOF))
{ {
/* Successful parse. */ /* Successful parse. */
<% if @grammar.ast %> <% if @grammar.tree %>
context.parse_result = cast(<%= @grammar.ast_prefix %><%= @grammar.start_rule %><%= @grammar.ast_suffix %> *)statevalues[$-1].ast_node; context.parse_result = statevalues[$-1].tree_node;
<% else %> <% else %>
context.parse_result = statevalues[$-1].pvalue; context.parse_result = statevalues[$-1].pvalue;
<% end %> <% end %>
@@ -1045,10 +1144,13 @@ public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * cont
if (reduced_rule_set == INVALID_ID) if (reduced_rule_set == INVALID_ID)
{ {
/* We shifted a token, mark it consumed. */ /* We shifted a token, mark it consumed. */
<% if @grammar.ast %> <% if @grammar.tree %>
<%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %> * token_ast_node = new <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %>(token_info.position, token_info.end_position, token, token_info.pvalue); <%= @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);
statevalues[$-1].ast_node = token_ast_node; <%= expand_code(@grammar.on_token_node, false, nil, nil) %>
statevalues[$-1].tree_node = token_tree_node;
<% else %> <% else %>
statevalues[$-1].position = token_info.position;
statevalues[$-1].end_position = token_info.end_position;
statevalues[$-1].pvalue = token_info.pvalue; statevalues[$-1].pvalue = token_info.pvalue;
<% end %> <% end %>
token = INVALID_TOKEN_ID; token = INVALID_TOKEN_ID;
@@ -1056,10 +1158,12 @@ public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * cont
else else
{ {
/* We shifted a RuleSet. */ /* We shifted a RuleSet. */
<% if @grammar.ast %> <% if @grammar.tree %>
statevalues[$-1].ast_node = reduced_parser_node; statevalues[$-1].tree_node = reduced_parser_node;
<% else %> <% else %>
statevalues[$-1].pvalue = reduced_parser_value; statevalues[$-1].pvalue = reduced_parser_value;
statevalues[$-1].position = reduced_position;
statevalues[$-1].end_position = reduced_end_position;
<%= @grammar.prefix %>value_t new_parse_result; <%= @grammar.prefix %>value_t new_parse_result;
reduced_parser_value = new_parse_result; reduced_parser_value = new_parse_result;
<% end %> <% end %>
@@ -1072,19 +1176,21 @@ public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * cont
if (reduce_index != INVALID_ID) if (reduce_index != INVALID_ID)
{ {
/* We have something to reduce. */ /* We have something to reduce. */
<% if @grammar.ast %> <% if @grammar.tree %>
if (parser_reduce_table[reduce_index].propagate_optional_target) if (parser_reduce_table[reduce_index].propagate_optional_target)
{ {
reduced_parser_node = statevalues[$ - 1].ast_node; reduced_parser_node = statevalues[$ - 1].tree_node;
} }
else if (parser_reduce_table[reduce_index].n_states > 0) else if (parser_reduce_table[reduce_index].n_states > 0)
{ {
size_t n_fields = parser_reduce_table[reduce_index].rule_set_node_field_array_size; size_t n_fields = parser_reduce_table[reduce_index].rule_set_node_field_array_size;
size_t node_size = ASTNode.sizeof + n_fields * (void *).sizeof; size_t node_size = TreeNode.sizeof + n_fields * (void *).sizeof;
ASTNode * node = cast(ASTNode *)malloc(node_size); TreeNode * node = cast(TreeNode *)malloc(node_size);
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;
@@ -1093,20 +1199,20 @@ public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * cont
{ {
foreach (i; 0..parser_reduce_table[reduce_index].n_states) foreach (i; 0..parser_reduce_table[reduce_index].n_states)
{ {
node.fields[i] = statevalues[$ - parser_reduce_table[reduce_index].n_states + i].ast_node; node.fields[i] = statevalues[$ - parser_reduce_table[reduce_index].n_states + i].tree_node;
} }
} }
else else
{ {
foreach (i; 0..parser_reduce_table[reduce_index].n_states) foreach (i; 0..parser_reduce_table[reduce_index].n_states)
{ {
node.fields[parser_reduce_table[reduce_index].rule_set_node_field_index_map[i]] = statevalues[$ - parser_reduce_table[reduce_index].n_states + i].ast_node; node.fields[parser_reduce_table[reduce_index].rule_set_node_field_index_map[i]] = statevalues[$ - parser_reduce_table[reduce_index].n_states + i].tree_node;
} }
} }
bool position_found = false; bool position_found = false;
foreach (i; 0..n_fields) foreach (i; 0..n_fields)
{ {
ASTNode * child = cast(ASTNode *)node.fields[i]; TreeNode * child = cast(TreeNode *)node.fields[i];
if (child && child.position.valid) if (child && child.position.valid)
{ {
if (!position_found) if (!position_found)
@@ -1123,6 +1229,12 @@ public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * cont
{ {
reduced_parser_node = null; reduced_parser_node = null;
} }
<% if @grammar.parser_user_code_used? %>
if (parser_user_code(reduced_parser_node, parser_reduce_table[reduce_index].rule, statevalues, parser_reduce_table[reduce_index].n_states, context) == P_USER_TERMINATED)
{
return P_USER_TERMINATED;
}
<% end %>
<% else %> <% else %>
<%= @grammar.prefix %>value_t reduced_parser_value2; <%= @grammar.prefix %>value_t reduced_parser_value2;
if (parser_user_code(&reduced_parser_value2, parser_reduce_table[reduce_index].rule, statevalues, parser_reduce_table[reduce_index].n_states, context) == P_USER_TERMINATED) if (parser_user_code(&reduced_parser_value2, parser_reduce_table[reduce_index].rule, statevalues, parser_reduce_table[reduce_index].n_states, context) == P_USER_TERMINATED)
@@ -1130,6 +1242,16 @@ public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * cont
return P_USER_TERMINATED; return P_USER_TERMINATED;
} }
reduced_parser_value = reduced_parser_value2; reduced_parser_value = reduced_parser_value2;
if (parser_reduce_table[reduce_index].n_states > 0u)
{
reduced_position = get_rule_position(statevalues, 0u, parser_reduce_table[reduce_index].n_states, false);
reduced_end_position = get_rule_position(statevalues, 0u, parser_reduce_table[reduce_index].n_states, true);
}
else
{
reduced_position = <%= @grammar.prefix %>position_t.INVALID;
reduced_end_position = <%= @grammar.prefix %>position_t.INVALID;
}
<% end %> <% end %>
reduced_rule_set = parser_reduce_table[reduce_index].rule_set; reduced_rule_set = parser_reduce_table[reduce_index].rule_set;
statevalues.length -= parser_reduce_table[reduce_index].n_states; statevalues.length -= parser_reduce_table[reduce_index].n_states;
@@ -1147,6 +1269,19 @@ public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * cont
} }
} }
public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context)
{
return parse_from(context, 0u);
}
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
public size_t <%= @grammar.prefix %>parse_<%= start_rule %>(<%= @grammar.prefix %>context_t * context)
{
return parse_from(context, <%= i %>u);
}
<% end %>
/** /**
* Get the parse result value. * Get the parse result value.
* *
@@ -1155,18 +1290,58 @@ public size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * cont
* *
* @return Parse result value. * @return Parse result value.
*/ */
<% if @grammar.ast %> <% if @grammar.tree %>
public <%= @grammar.ast_prefix %><%= @grammar.start_rule %><%= @grammar.ast_suffix %> * <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context) public <%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context)
{
return cast(<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> *)context.parse_result;
}
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
public <%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * <%= @grammar.prefix %>result_<%= start_rule %>(<%= @grammar.prefix %>context_t * context)
{
return cast(<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> *)context.parse_result;
}
<% end %>
<% else %> <% else %>
public <%= start_rule_type[1] %> <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context) public <%= start_rule_type[1] %> <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context)
<% end %>
{ {
<% if @grammar.ast %>
return context.parse_result;
<% else %>
return context.parse_result.v_<%= start_rule_type[0] %>; return context.parse_result.v_<%= start_rule_type[0] %>;
<% end %>
} }
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
public <%= start_rule_type(i)[1] %> <%= @grammar.prefix %>result_<%= start_rule %>(<%= @grammar.prefix %>context_t * context)
{
return context.parse_result.v_<%= start_rule_type(i)[0] %>;
}
<% 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.
+58 -16
View File
@@ -45,10 +45,10 @@ typedef uint32_t <%= @grammar.prefix %>code_point_t;
*/ */
typedef struct typedef struct
{ {
/** Input text row (0-based). */ /** Input text row (1-based). */
uint32_t row; uint32_t row;
/** Input text column (0-based). */ /** Input text column (1-based). */
uint32_t col; uint32_t col;
} <%= @grammar.prefix %>position_t; } <%= @grammar.prefix %>position_t;
@@ -58,7 +58,7 @@ typedef struct
/** User header code blocks. */ /** User header code blocks. */
<%= @grammar.code_blocks.fetch("header", "") %> <%= @grammar.code_blocks.fetch("header", "") %>
<% if @grammar.ast %> <% if @grammar.tree %>
/** Parser values type. */ /** Parser values type. */
typedef <%= @grammar.ptype %> <%= @grammar.prefix %>value_t; typedef <%= @grammar.ptype %> <%= @grammar.prefix %>value_t;
<% else %> <% else %>
@@ -69,18 +69,37 @@ 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 %>
<% if @grammar.ast %> /** Parser value accessor(s). */
/** AST node types. @{ */ <% @grammar.ptypes.each do |name, typestring| %>
typedef struct <%= @grammar.ast_prefix %>Token<%= @grammar.ast_suffix %> static inline <%= typestring %> <%= @grammar.prefix %>value_get<%= name == "default" ? "" : "_#{name}" %>(<%= @grammar.prefix %>value_t const * pvalue)
{ {
/* ASTNode fields must be present in the same order here. */ return pvalue->v_<%= name %>;
}
<% end %>
<% end %>
<% if @grammar.tree %>
/** Tree node types. @{ */
typedef struct <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>
{
<% # 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;
uint16_t n_fields;
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.ast_prefix %>Token<%= @grammar.ast_suffix %>; } <%= @grammar.tree_prefix %>Token<%= @grammar.tree_suffix %>;
<% @parser.rule_sets.each do |name, rule_set| %> <% @parser.rule_sets.each do |name, rule_set| %>
<% next if name.start_with?("$") %> <% next if name.start_with?("$") %>
@@ -91,11 +110,14 @@ struct <%= name %>;
<% @parser.rule_sets.each do |name, rule_set| %> <% @parser.rule_sets.each do |name, rule_set| %>
<% next if name.start_with?("$") %> <% next if name.start_with?("$") %>
<% next if rule_set.optional? %> <% next if rule_set.optional? %>
typedef struct <%= @grammar.ast_prefix %><%= name %><%= @grammar.ast_suffix %> typedef struct <%= @grammar.tree_prefix %><%= name %><%= @grammar.tree_suffix %>
{ {
<% # TreeNode fields must be present in the same order here. # %>
<%= @grammar.prefix %>position_t position; <%= @grammar.prefix %>position_t position;
<%= @grammar.prefix %>position_t end_position; <%= @grammar.prefix %>position_t end_position;
<% rule_set.ast_fields.each do |fields| %> uint16_t n_fields;
uint8_t is_token;
<% rule_set.tree_fields.each do |fields| %>
union union
{ {
<% fields.each do |field_name, type| %> <% fields.each do |field_name, type| %>
@@ -103,7 +125,7 @@ typedef struct <%= @grammar.ast_prefix %><%= name %><%= @grammar.ast_suffix %>
<% end %> <% end %>
}; };
<% end %> <% end %>
} <%= @grammar.ast_prefix %><%= name %><%= @grammar.ast_suffix %>; } <%= @grammar.tree_prefix %><%= name %><%= @grammar.tree_suffix %>;
<% end %> <% end %>
/** @} */ /** @} */
@@ -156,8 +178,8 @@ typedef struct
/* Parser context data. */ /* Parser context data. */
/** Parse result value. */ /** Parse result value. */
<% if @grammar.ast %> <% if @grammar.tree %>
<%= @grammar.ast_prefix %><%= @grammar.start_rule %><%= @grammar.ast_suffix %> * parse_result; void * parse_result;
<% else %> <% else %>
<%= @grammar.prefix %>value_t parse_result; <%= @grammar.prefix %>value_t parse_result;
<% end %> <% end %>
@@ -167,6 +189,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;
/************************************************************************** /**************************************************************************
@@ -176,7 +200,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);
@@ -184,11 +210,27 @@ size_t <%= @grammar.prefix %>decode_code_point(uint8_t const * input, size_t inp
size_t <%= @grammar.prefix %>lex(<%= @grammar.prefix %>context_t * context, <%= @grammar.prefix %>token_info_t * out_token_info); size_t <%= @grammar.prefix %>lex(<%= @grammar.prefix %>context_t * context, <%= @grammar.prefix %>token_info_t * out_token_info);
size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context); size_t <%= @grammar.prefix %>parse(<%= @grammar.prefix %>context_t * context);
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
size_t <%= @grammar.prefix %>parse_<%= start_rule %>(<%= @grammar.prefix %>context_t * context);
<% end %>
<% if @grammar.ast %> <% if @grammar.tree %>
<%= @grammar.ast_prefix %><%= @grammar.start_rule %><%= @grammar.ast_suffix %> * <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context); <%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context);
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * <%= @grammar.prefix %>result_<%= start_rule %>(<%= @grammar.prefix %>context_t * context);
<% end %>
<% else %> <% else %>
<%= start_rule_type[1] %> <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context); <%= start_rule_type[1] %> <%= @grammar.prefix %>result(<%= @grammar.prefix %>context_t * context);
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
<%= start_rule_type(i)[1] %> <%= @grammar.prefix %>result_<%= start_rule %>(<%= @grammar.prefix %>context_t * context);
<% end %>
<% end %>
<% if @grammar.tree %>
void <%= @grammar.prefix %>tree_delete(<%= @grammar.tree_prefix %><%= @grammar.start_rules[0] %><%= @grammar.tree_suffix %> * tree);
<% @grammar.start_rules.each_with_index do |start_rule, i| %>
void <%= @grammar.prefix %>tree_delete_<%= start_rule %>(<%= @grammar.tree_prefix %><%= start_rule %><%= @grammar.tree_suffix %> * tree);
<% end %>
<% end %> <% end %>
<%= @grammar.prefix %>position_t <%= @grammar.prefix %>position(<%= @grammar.prefix %>context_t * context); <%= @grammar.prefix %>position_t <%= @grammar.prefix %>position(<%= @grammar.prefix %>context_t * context);
+793 -299
View File
File diff suppressed because it is too large. Load diff
+20 -2
View File
@@ -8,7 +8,11 @@ if exists("b:current_syntax")
endif endif
if !exists("b:propane_subtype") if !exists("b:propane_subtype")
if search('\<import\s\+\%(std\|core\)\.', 'nw') > 0
let b:propane_subtype = "d" let b:propane_subtype = "d"
else
let b:propane_subtype = "cpp"
endif
endif endif
exe "syn include @propaneTarget syntax/".b:propane_subtype.".vim" exe "syn include @propaneTarget syntax/".b:propane_subtype.".vim"
@@ -16,18 +20,32 @@ exe "syn include @propaneTarget syntax/".b:propane_subtype.".vim"
syn region propaneTarget matchgroup=propaneDelimiter start="<<" end=">>$" contains=@propaneTarget keepend syn region propaneTarget matchgroup=propaneDelimiter start="<<" end=">>$" contains=@propaneTarget keepend
syn match propaneComment "#.*" syn match propaneComment "#.*"
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 ast ast_prefix ast_suffix drop module prefix ptype start token tokenid " The right-hand side of a rule (after '->' up to '<<' or ';') lists symbol
" names that may coincide with propane keywords (e.g. 'start', 'token',
" 'tree'). Wrap it in a region that excludes keyword matches so those names
" are not highlighted as keywords. The '<<' is left unconsumed so the
" propaneTarget region can still match it.
syn region propaneRuleRhs matchgroup=propaneOperator start="->" end="\ze<<" end=";" contains=propaneFieldAlias,propaneRuleOperator,propaneComment keepend
syn match propaneRuleOperator "?" contained
" Keywords that introduce a user-defined name. The name is consumed by
" propaneName via nextgroup so a name matching a keyword (e.g. 'token start')
" is not highlighted as a keyword. These must be a match (not syn keyword)
" because a syn keyword always wins over a contained nextgroup match.
syn match propaneNameDecl "\<\%(tokenid\|token\|lex_fn\|module\|start\|tree_prefix\|tree_suffix\)\>" nextgroup=propaneName skipwhite
syn match propaneName "\<\h\w*\>" contained
syn match propaneKeyword "\<\%(context_user_fields\|drop\|free_token_node\|noline\|on_token_node\|prefix\|ptype\|token_user_fields\|tree\)\>"
syn region propaneRegex start="/" end="/" skip="\v\\\\|\\/" syn region propaneRegex start="/" end="/" skip="\v\\\\|\\/"
hi def link propaneComment Comment hi def link propaneComment Comment
hi def link propaneKeyword Keyword hi def link propaneKeyword Keyword
hi def link propaneNameDecl Keyword
hi def link propaneRegex String hi def link propaneRegex String
hi def link propaneOperator Operator hi def link propaneOperator Operator
hi def link propaneRuleOperator Operator
hi def link propaneFieldOperator Operator hi def link propaneFieldOperator Operator
hi def link propaneDelimiter Delimiter hi def link propaneDelimiter Delimiter
hi def link propaneFieldAlias Identifier hi def link propaneFieldAlias Identifier
+1 -1
View File
@@ -33,7 +33,7 @@ class Propane
def run(input_file, output_file, log_file, options) def run(input_file, output_file, log_file, options)
begin begin
grammar = Grammar.new(File.read(input_file)) grammar = Grammar.new(File.read(input_file), input_file)
generator = Generator.new(grammar, output_file, log_file, options) generator = Generator.new(grammar, output_file, log_file, options)
generator.generate generator.generate
rescue Error => e rescue Error => e
+139 -29
View File
@@ -11,25 +11,40 @@ class Propane
@log = StringIO.new @log = StringIO.new
end end
@language = @language =
if output_file =~ /\.([a-z]+)$/ if output_file.end_with?(".d")
$1
else
"d" "d"
elsif output_file.end_with?(".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!
end end
def generate def generate
extensions = [@language] extensions = [nil]
if @language == "c" if @language == "c"
extensions += %w[h] extensions += %w[h]
end end
extensions.each do |extension| extensions.each do |extension|
template = Assets.get("parser.#{extension}.erb") template = Assets.get("parser.#{extension || @language}.erb")
erb = ERB.new(template, trim_mode: "<>") if extension
output_file = @output_file.sub(%r{\.[a-z]+$}, ".#{extension}") output_file = @output_file.sub(%r{\.[a-z]+$}, ".#{extension}")
result = erb.result(binding.clone) else
output_file = @output_file
end
erb = ERB.new(template, trim_mode: "<>")
result = erb.result(binding.clone).lines.each_with_index.map do |line, i|
if line == "#linereset\n"
%[#line #{i + 2} "#{output_file}"\n]
else
line
end
end.join
File.open(output_file, "wb") do |fh| File.open(output_file, "wb") do |fh|
fh.write(result) fh.write(result)
end end
@@ -67,12 +82,15 @@ class Propane
end end
tokens_by_name[token.name] = token tokens_by_name[token.name] = token
end end
# Check for user start rule. # Create real start rule(s).
unless @grammar.rules.find {|rule| rule.name == @grammar.start_rule} real_start_rules = @grammar.start_rules.map do |start_rule|
raise Error.new("Start rule `#{@grammar.start_rule}` not found") unless @grammar.rules.find {|rule| rule.name == start_rule}
raise Error.new("Start rule `#{start_rule}` not found")
end end
# Add "real" start rule. Rule.new("$#{start_rule}", [start_rule, "$EOF"], nil, nil, nil)
@grammar.rules.unshift(Rule.new("$Start", [@grammar.start_rule, "$EOF"], nil, nil, nil)) end
# Add real start rules before user-given rules.
@grammar.rules = real_start_rules + @grammar.rules
# Generate and add rules for optional components. # Generate and add rules for optional components.
generate_optional_component_rules!(tokens_by_name) generate_optional_component_rules!(tokens_by_name)
# Build rule sets. # Build rule sets.
@@ -167,7 +185,8 @@ class Propane
end end
end end
@grammar.rules << Rule.new(component, [], nil, ptypename, rule.line_number) @grammar.rules << Rule.new(component, [], nil, ptypename, rule.line_number)
@grammar.rules << Rule.new(component, [c], "$$ = $1;\n", ptypename, rule.line_number) optcode = @grammar.tree ? nil : "$$ = $1;\n"
@grammar.rules << Rule.new(component, [c], optcode, ptypename, rule.line_number)
optional_rules_added << component optional_rules_added << component
end end
end end
@@ -258,8 +277,34 @@ 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|
if @grammar.tree
case @language
when "c"
"((#{@grammar.tree_prefix}#{rule.name}#{@grammar.tree_suffix} *)_pvalue)"
when "d"
"(cast(#{@grammar.tree_prefix}#{rule.name}#{@grammar.tree_suffix} *)_pvalue)"
end
else
case @language case @language
when "c" when "c"
"_pvalue->v_#{rule.ptypename}" "_pvalue->v_#{rule.ptypename}"
@@ -267,31 +312,33 @@ class Propane
"_pvalue.v_#{rule.ptypename}" "_pvalue.v_#{rule.ptypename}"
end end
end end
code = code.gsub(/\$(\d+)/) do |match|
index = $1.to_i
case @language
when "c"
"state_values_stack_index(statevalues, -1 - (int)n_states + #{index})->pvalue.v_#{rule.components[index - 1].ptypename}"
when "d"
"statevalues[$-1-n_states+#{index}].pvalue.v_#{rule.components[index - 1].ptypename}"
end end
code = code.gsub(/\$(\d+)/) do |match|
parser_component_reference(rule, $1.to_i)
end
code = code.gsub(/\$\{(\$|\d+)\.position\}/) do |match|
index = $1.to_i
"get_rule_position(statevalues, #{index}, n_states, false)"
end
code = code.gsub(/\$\{(\$|\d+)\.end_position\}/) do |match|
index = $1.to_i
"get_rule_position(statevalues, #{index}, n_states, true)"
end end
code = code.gsub(/\$\{(\w+)\}/) do |match| code = code.gsub(/\$\{(\w+)\}/) do |match|
aliasname = $1 aliasname = $1
if index = rule.aliases[aliasname] if index = rule.aliases[aliasname]
case @language # Field aliases are just a named reference to a positional rule
when "c" # component, so reuse the same expansion as `$1', `$2', etc. Note
"state_values_stack_index(statevalues, -(int)n_states + #{index})->pvalue.v_#{rule.components[index].ptypename}" # that rule.aliases stores a 0-based component index, so add 1 to
when "d" # convert it to the 1-based index used for positional references.
"statevalues[$-n_states+#{index}].pvalue.v_#{rule.components[index].ptypename}" parser_component_reference(rule, index + 1)
end
else else
raise Error.new("Field alias '#{aliasname}' not found") raise Error.new("Field alias '#{aliasname}' not found")
end end
end end
else else
code = code.gsub(/\$\$/) do |match| code = code.gsub(/\$\$/) do |match|
if @grammar.ast if @grammar.tree
case @language case @language
when "c" when "c"
"out_token_info->pvalue" "out_token_info->pvalue"
@@ -307,6 +354,22 @@ class Propane
end end
end end
end end
code = code.gsub(/\$\{position\}/) do |match|
case @language
when "c"
"out_token_info->position"
when "d"
"out_token_info.position"
end
end
code = code.gsub(/\$\{end_position\}/) do |match|
case @language
when "c"
"out_token_info->end_position"
when "d"
"out_token_info.end_position"
end
end
code = code.gsub(/\$mode\(([a-zA-Z_][a-zA-Z_0-9]*)\)/) do |match| code = code.gsub(/\$mode\(([a-zA-Z_][a-zA-Z_0-9]*)\)/) do |match|
mode_name = $1 mode_name = $1
mode_id = @lexer.mode_id(mode_name) mode_id = @lexer.mode_id(mode_name)
@@ -324,13 +387,60 @@ class Propane
code code
end end
# Expand a positional reference to a parser rule component.
#
# This is used to expand `$1', `$2', etc. as well as field aliases (which
# are just named references to a positional rule component).
#
# @param rule [Rule]
# The Rule containing the user code.
# @param index [Integer]
# 1-based index of the rule component to reference.
#
# @return [String]
# Expanded rule component reference.
def parser_component_reference(rule, index)
component = rule.components[index - 1]
if @grammar.tree
# In tree mode a component reference yields a pointer to that
# component's tree node. An optional component propagates its target
# node (or null), so use the optional target's node type.
if component.is_a?(RuleSet) && component.optional?
component = component.option_target
end
node_name = component.is_a?(Token) ? "Token" : component.name
typename = "#{@grammar.tree_prefix}#{node_name}#{@grammar.tree_suffix}"
case @language
when "c"
"((#{typename} *)state_values_stack_index(statevalues, -1 - (int)n_states + #{index})->tree_node)"
when "d"
"(cast(#{typename} *)statevalues[$-1-n_states+#{index}].tree_node)"
end
else
case @language
when "c"
"state_values_stack_index(statevalues, -1 - (int)n_states + #{index})->pvalue.v_#{component.ptypename}"
when "d"
"statevalues[$-1-n_states+#{index}].pvalue.v_#{component.ptypename}"
end
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>]
# Start rule parser value type name and type string. # Start rule parser value type name and type string.
def start_rule_type def start_rule_type(start_rule_index = 0)
start_rule = @grammar.rules.find do |rule| start_rule = @grammar.rules.find do |rule|
rule.name == @grammar.start_rule rule.name == @grammar.start_rules[start_rule_index]
end end
[start_rule.ptypename, @grammar.ptypes[start_rule.ptypename]] [start_rule.ptypename, @grammar.ptypes[start_rule.ptypename]]
end end
+131 -45
View File
@@ -5,21 +5,27 @@ 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 :ast attr_reader :context_user_fields
attr_reader :ast_prefix attr_reader :lex_fn
attr_reader :ast_suffix attr_reader :tree
attr_reader :tree_prefix
attr_reader :tree_suffix
attr_reader :free_token_node
attr_reader :modulename attr_reader :modulename
attr_reader :patterns attr_reader :patterns
attr_reader :rules attr_accessor :rules
attr_reader :start_rule attr_reader :start_rules
attr_reader :tokens attr_reader :tokens
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, filename)
@filename = filename
@patterns = [] @patterns = []
@start_rule = "Start" @start_rules = []
@tokens = [] @tokens = []
@rules = [] @rules = []
@code_blocks = {} @code_blocks = {}
@@ -29,10 +35,15 @@ class Propane
@input = input.gsub("\r\n", "\n") @input = input.gsub("\r\n", "\n")
@ptypes = {"default" => "void *"} @ptypes = {"default" => "void *"}
@prefix = "p_" @prefix = "p_"
@ast = false @tree = false
@ast_prefix = "" @tree_prefix = ""
@ast_suffix = "" @tree_suffix = ""
@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?
end end
def ptype def ptype
@@ -47,6 +58,10 @@ class Propane
@tokens.size + 1 @tokens.size + 1
end end
def parser_user_code_used?
@rules.any? {|r| r.code}
end
private private
def parse_grammar! def parse_grammar!
@@ -59,10 +74,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_ast_statement! elsif parse_context_user_fields_statement!
elsif parse_ast_prefix_statement! elsif parse_lex_fn!
elsif parse_ast_suffix_statement! elsif parse_tree_statement!
elsif parse_tree_prefix_statement!
elsif parse_tree_suffix_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!
@@ -72,6 +92,7 @@ class Propane
elsif parse_rule_statement! elsif parse_rule_statement!
elsif parse_code_block_statement! elsif parse_code_block_statement!
elsif parse_prefix_statement! elsif parse_prefix_statement!
elsif parse_noline_statement!
else else
if @input.size > 25 if @input.size > 25
@input = @input.slice(0..20) + "..." @input = @input.slice(0..20) + "..."
@@ -94,21 +115,37 @@ class Propane
consume!(/#.*\n/) consume!(/#.*\n/)
end end
def parse_ast_statement! def parse_context_user_fields_statement!
if consume!(/ast\s*;/) if md = consume!(/context_user_fields\b\s*/)
@ast = true unless code = parse_code_block!
raise Error.new("Line #{@line_number}: expected code block")
end
@context_user_fields ||= ""
@context_user_fields += code
end end
end end
def parse_ast_prefix_statement! def parse_lex_fn!
if md = consume!(/ast_prefix\s+(\w+)\s*;/) if md = consume!(/lex_fn\b\s*(\w+)\s*;/)
@ast_prefix = md[1] @lex_fn = md[1]
end end
end end
def parse_ast_suffix_statement! def parse_tree_statement!
if md = consume!(/ast_suffix\s+(\w+)\s*;/) if consume!(/tree\s*;/)
@ast_suffix = md[1] @tree = true
end
end
def parse_tree_prefix_statement!
if md = consume!(/tree_prefix\s+(\w+)\s*;/)
@tree_prefix = md[1]
end
end
def parse_tree_suffix_statement!
if md = consume!(/tree_suffix\s+(\w+)\s*;/)
@tree_suffix = md[1]
end end
end end
@@ -122,12 +159,40 @@ 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"
if md = consume!(/(#{IDENTIFIER_REGEX})\s*=\s*/) if md = consume!(/(#{IDENTIFIER_REGEX})\s*=\s*/)
if @ast if @tree
raise Error.new("Multiple ptypes are unsupported in AST mode") raise Error.new("Multiple ptypes are unsupported in tree mode")
end end
name = md[1] name = md[1]
end end
@@ -141,8 +206,8 @@ class Propane
md = consume!(/(#{IDENTIFIER_REGEX})\s*/, "expected token name") md = consume!(/(#{IDENTIFIER_REGEX})\s*/, "expected token name")
name = md[1] name = md[1]
if md = consume!(/\((#{IDENTIFIER_REGEX})\)\s*/) if md = consume!(/\((#{IDENTIFIER_REGEX})\)\s*/)
if @ast if @tree
raise Error.new("Multiple ptypes are unsupported in AST mode") raise Error.new("Multiple ptypes are unsupported in tree mode")
end end
ptypename = md[1] ptypename = md[1]
end end
@@ -165,8 +230,8 @@ class Propane
md = consume!(/(#{IDENTIFIER_REGEX})\s*/, "expected token name") md = consume!(/(#{IDENTIFIER_REGEX})\s*/, "expected token name")
name = md[1] name = md[1]
if md = consume!(/\((#{IDENTIFIER_REGEX})\)\s*/) if md = consume!(/\((#{IDENTIFIER_REGEX})\)\s*/)
if @ast if @tree
raise Error.new("Multiple ptypes are unsupported in AST mode") raise Error.new("Multiple ptypes are unsupported in tree mode")
end end
ptypename = md[1] ptypename = md[1]
end end
@@ -185,8 +250,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
@@ -195,18 +262,14 @@ class Propane
def parse_rule_statement! def parse_rule_statement!
if md = consume!(/(#{IDENTIFIER_REGEX})\s*(?:\((#{IDENTIFIER_REGEX})\))?\s*->\s*/) if md = consume!(/(#{IDENTIFIER_REGEX})\s*(?:\((#{IDENTIFIER_REGEX})\))?\s*->\s*/)
rule_name, ptypename = *md[1, 2] rule_name, ptypename = *md[1, 2]
if @ast && ptypename if @tree && ptypename
raise Error.new("Multiple ptypes are unsupported in AST mode") raise Error.new("Multiple ptypes are unsupported in tree mode")
end end
md = consume!(/((?:#{IDENTIFIER_REGEX}\??(?::#{IDENTIFIER_REGEX})?\s*)*)\s*/, "expected rule component list") md = consume!(/((?:#{IDENTIFIER_REGEX}\??(?::#{IDENTIFIER_REGEX})?\s*)*)\s*/, "expected rule component list")
components = md[1].strip.split(/\s+/) components = md[1].strip.split(/\s+/)
if @ast
consume!(/;/, "expected `;'")
else
unless code = parse_code_block! unless code = parse_code_block!
consume!(/;/, "expected `;' or code block") consume!(/;/, "expected `;' or code block")
end end
end
@rules << Rule.new(rule_name, components, code, ptypename, @line_number) @rules << Rule.new(rule_name, components, code, ptypename, @line_number)
@modeline = nil @modeline = nil
true true
@@ -217,8 +280,8 @@ class Propane
if pattern = parse_pattern! if pattern = parse_pattern!
consume!(/\s+/) consume!(/\s+/)
if md = consume!(/\((#{IDENTIFIER_REGEX})\)\s*/) if md = consume!(/\((#{IDENTIFIER_REGEX})\)\s*/)
if @ast if @tree
raise Error.new("Multiple ptypes are unsupported in AST mode") raise Error.new("Multiple ptypes are unsupported in tree mode")
end end
ptypename = md[1] ptypename = md[1]
end end
@@ -232,16 +295,25 @@ class Propane
end end
def parse_start_statement! def parse_start_statement!
if md = consume!(/start\s+(\w+)\s*;/) if md = consume!(/start\s+([\w\s]*);/)
@start_rule = md[1] start_rules = md[1].split(/\s+/).map(&:strip)
start_rules.each do |start_rule|
@start_rules << start_rule unless @start_rules.include?(start_rule)
end
end end
end end
def parse_code_block_statement! def parse_code_block_statement!
if md = consume!(/<<([a-z]*)(.*?)>>\n/m) if md = consume!(/<<([a-z]*)(.*?)>>\n/m)
name, code = md[1..2] name, code = md[1..2]
code.sub!(/\A\n/, "") code = code.chomp
code += "\n" unless code.end_with?("\n") unless @noline
if code.start_with?("\n")
code = %[#line #{@line_number + 1} "#{@filename}"#{code}\n#linereset\n]
else
code = %[#line #{@line_number} "#{@filename}"\n#{code}\n#linereset\n]
end
end
if @code_blocks[name] if @code_blocks[name]
@code_blocks[name] += code @code_blocks[name] += code
else else
@@ -259,6 +331,13 @@ class Propane
end end
end end
def parse_noline_statement!
if md = consume!(/noline\s*;/)
@noline = true
true
end
end
def parse_pattern! def parse_pattern!
if md = consume!(%r{/}) if md = consume!(%r{/})
pattern = "" pattern = ""
@@ -272,6 +351,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
@@ -280,9 +361,14 @@ class Propane
def parse_code_block! def parse_code_block!
if md = consume!(/<<(.*?)>>\n/m) if md = consume!(/<<(.*?)>>\n/m)
code = md[1] code = md[1].chomp
code.sub!(/\A\n/, "") unless @noline
code += "\n" unless code.end_with?("\n") if code.start_with?("\n")
code = %[#line #{@line_number + 1} "#{@filename}"#{code}\n#linereset\n]
else
code = %[#line #{@line_number} "#{@filename}"\n#{code}\n#linereset\n]
end
end
code code
end end
end end
+12 -3
View File
@@ -16,11 +16,20 @@ class Propane
@warnings = Set.new @warnings = Set.new
@errors = Set.new @errors = Set.new
@options = options @options = options
start_item = Item.new(grammar.rules.first, 0) start_items = grammar.rules[0...grammar.start_rules.length].map do |start_rule|
eval_item_sets = Set[ItemSet.new([start_item])] Item.new(start_rule, 0)
end
start_item_sets = start_items.map {|item| ItemSet.new([item])}
eval_item_sets = Set[*start_item_sets]
while eval_item_sets.size > 0 while eval_item_sets.size > 0
item_set = eval_item_sets.first item_set =
if start_item_sets.size > 0
# Ensure we evaluate start_item_sets first in order
start_item_sets.slice!(0)
else
eval_item_sets.first
end
eval_item_sets.delete(item_set) eval_item_sets.delete(item_set)
unless @item_sets_set.include?(item_set) unless @item_sets_set.include?(item_set)
item_set.id = @item_sets.size item_set.id = @item_sets.size
+1 -1
View File
@@ -36,7 +36,7 @@ class Propane
# @return [Array<Integer>] # @return [Array<Integer>]
# Map this rule's components to their positions in the parent RuleSet's # Map this rule's components to their positions in the parent RuleSet's
# node field pointer array. This is used for AST construction. # node field pointer array. This is used for tree construction.
attr_accessor :rule_set_node_field_index_map attr_accessor :rule_set_node_field_index_map
# Construct a Rule. # Construct a Rule.
+23 -23
View File
@@ -4,8 +4,8 @@ class Propane
class RuleSet class RuleSet
# @return [Array<Hash>] # @return [Array<Hash>]
# AST fields. # tree fields.
attr_reader :ast_fields attr_reader :tree_fields
# @return [Integer] # @return [Integer]
# ID of the RuleSet. # ID of the RuleSet.
@@ -100,28 +100,28 @@ class Propane
# Finalize a RuleSet after adding all Rules to it. # Finalize a RuleSet after adding all Rules to it.
def finalize(grammar) def finalize(grammar)
if grammar.ast if grammar.tree
build_ast_fields(grammar) build_tree_fields(grammar)
end end
end end
private private
# Build the set of AST fields for this RuleSet. # Build the set of tree fields for this RuleSet.
# #
# This is an Array of Hashes. Each entry in the Array corresponds to a # This is an Array of Hashes. Each entry in the Array corresponds to a
# field location in the AST node. The entry is a Hash. It could have one or # field location in the tree node. The entry is a Hash. It could have one or
# two keys. It will always have the field name with a positional suffix as # two keys. It will always have the field name with a positional suffix as
# a key. It may also have the field name without the positional suffix if # a key. It may also have the field name without the positional suffix if
# that field only exists in one position across all Rules in the RuleSet. # that field only exists in one position across all Rules in the RuleSet.
# #
# @return [void] # @return [void]
def build_ast_fields(grammar) def build_tree_fields(grammar)
field_ast_node_indexes = {} field_tree_node_indexes = {}
field_indexes_across_all_rules = {} field_indexes_across_all_rules = {}
# Stores the index into @ast_fields by field alias name. # Stores the index into @tree_fields by field alias name.
field_aliases = {} field_aliases = {}
@ast_fields = [] @tree_fields = []
@rules.each do |rule| @rules.each do |rule|
rule.components.each_with_index do |component, i| rule.components.each_with_index do |component, i|
if component.is_a?(RuleSet) && component.optional? if component.is_a?(RuleSet) && component.optional?
@@ -132,25 +132,25 @@ class Propane
else else
node_name = component.name node_name = component.name
end end
struct_name = "#{grammar.ast_prefix}#{node_name}#{grammar.ast_suffix}" struct_name = "#{grammar.tree_prefix}#{node_name}#{grammar.tree_suffix}"
field_name = "p#{node_name}#{i + 1}" field_name = "p#{node_name}#{i + 1}"
unless field_ast_node_indexes[field_name] unless field_tree_node_indexes[field_name]
field_ast_node_indexes[field_name] = @ast_fields.size field_tree_node_indexes[field_name] = @tree_fields.size
@ast_fields << {field_name => struct_name} @tree_fields << {field_name => struct_name}
end end
rule.aliases.each do |alias_name, index| rule.aliases.each do |alias_name, index|
if index == i if index == i
alias_ast_fields_index = field_ast_node_indexes[field_name] alias_tree_fields_index = field_tree_node_indexes[field_name]
if field_aliases[alias_name] && field_aliases[alias_name] != alias_ast_fields_index if field_aliases[alias_name] && field_aliases[alias_name] != alias_tree_fields_index
raise Error.new("Error: conflicting AST node field positions for alias `#{alias_name}` in rule #{rule.name} defined on line #{rule.line_number}") raise Error.new("Error: conflicting tree node field positions for alias `#{alias_name}` in rule #{rule.name} defined on line #{rule.line_number}")
end end
field_aliases[alias_name] = alias_ast_fields_index field_aliases[alias_name] = alias_tree_fields_index
@ast_fields[alias_ast_fields_index][alias_name] = @ast_fields[alias_ast_fields_index].first[1] @tree_fields[alias_tree_fields_index][alias_name] = @tree_fields[alias_tree_fields_index].first[1]
end end
end end
field_indexes_across_all_rules[node_name] ||= Set.new field_indexes_across_all_rules[node_name] ||= Set.new
field_indexes_across_all_rules[node_name] << field_ast_node_indexes[field_name] field_indexes_across_all_rules[node_name] << field_tree_node_indexes[field_name]
rule.rule_set_node_field_index_map[i] = field_ast_node_indexes[field_name] rule.rule_set_node_field_index_map[i] = field_tree_node_indexes[field_name]
end end
end end
field_indexes_across_all_rules.each do |node_name, indexes_across_all_rules| field_indexes_across_all_rules.each do |node_name, indexes_across_all_rules|
@@ -158,8 +158,8 @@ class Propane
# If this field was only seen in one position across all rules, # If this field was only seen in one position across all rules,
# then add an alias to the positional field name that does not # then add an alias to the positional field name that does not
# include the position. # include the position.
@ast_fields[indexes_across_all_rules.first]["p#{node_name}"] = @tree_fields[indexes_across_all_rules.first]["p#{node_name}"] =
"#{grammar.ast_prefix}#{node_name}#{grammar.ast_suffix}" "#{grammar.tree_prefix}#{node_name}#{grammar.tree_suffix}"
end end
end end
end end
+1 -1
View File
@@ -1,3 +1,3 @@
class Propane class Propane
VERSION = "2.3.0" VERSION = "4.7.0"
end end
+2 -2
View File
@@ -120,11 +120,11 @@ string: /\\t/ <<
>> >>
string: /\\u[0-9a-fA-F]{4}/ << string: /\\u[0-9a-fA-F]{4}/ <<
/* Not actually going to encode the code point for this example... */ /* Not actually going to encode the code point for this example... */
char s[] = {'{', match[2], match[3], match[4], match[5], '}', 0}; char s[] = {'{', (char)match[2], (char)match[3], (char)match[4], (char)match[5], '}', 0};
str_append(&string_value, s); str_append(&string_value, s);
>> >>
string: /[^\\]/ << string: /[^\\]/ <<
char s[] = {match[0], 0}; char s[] = {(char)match[0], 0};
str_append(&string_value, s); str_append(&string_value, s);
>> >>
Start -> Value << Start -> Value <<
+3 -3
View File
@@ -5,7 +5,7 @@
JSONValue * JSONValue_new(size_t id) JSONValue * JSONValue_new(size_t id)
{ {
JSONValue * jv = calloc(1, sizeof(JSONValue)); JSONValue * jv = (JSONValue *)calloc(1, sizeof(JSONValue));
jv->id = id; jv->id = id;
return jv; return jv;
} }
@@ -29,7 +29,7 @@ void JSONObject_append(JSONValue * object, char const * name, JSONValue * value)
} }
} }
size_t const new_size = size + 1; size_t const new_size = size + 1;
void * new_entries = malloc(sizeof(object->object.entries[0]) * new_size); JSONObjectEntry * new_entries = (JSONObjectEntry *)malloc(sizeof(object->object.entries[0]) * new_size);
if (size > 0) if (size > 0)
{ {
memcpy(new_entries, object->object.entries, size * sizeof(object->object.entries[0])); memcpy(new_entries, object->object.entries, size * sizeof(object->object.entries[0]));
@@ -52,7 +52,7 @@ void JSONArray_append(JSONValue * array, JSONValue * value)
{ {
size_t const size = array->array.size; size_t const size = array->array.size;
size_t const new_size = size + 1; size_t const new_size = size + 1;
JSONValue ** new_entries = malloc(sizeof(JSONValue *) * new_size); JSONValue ** new_entries = (JSONValue **)malloc(sizeof(JSONValue *) * new_size);
if (array->array.size > 0) if (array->array.size > 0)
{ {
memcpy(new_entries, array->array.entries, sizeof(JSONValue *) * size); memcpy(new_entries, array->array.entries, sizeof(JSONValue *) * size);
+7 -5
View File
@@ -11,6 +11,12 @@
#define JSON_FALSE 5u #define JSON_FALSE 5u
#define JSON_NULL 6u #define JSON_NULL 6u
typedef struct JSONObjectEntry_s
{
char const * name;
struct JSONValue_s * value;
} JSONObjectEntry;
typedef struct JSONValue_s typedef struct JSONValue_s
{ {
size_t id; size_t id;
@@ -19,11 +25,7 @@ typedef struct JSONValue_s
struct struct
{ {
size_t size; size_t size;
struct JSONObjectEntry * entries;
{
char const * name;
struct JSONValue_s * value;
} * entries;
} object; } object;
struct struct
{ {
+44 -9
View File
@@ -28,7 +28,7 @@ B -> <<
b = 0; b = 0;
>> >>
EOF EOF
grammar = Grammar.new(input) grammar = Grammar.new(input, "test.propane")
expect(grammar.modulename).to eq "a.b" expect(grammar.modulename).to eq "a.b"
expect(grammar.ptype).to eq "XYZ *" expect(grammar.ptype).to eq "XYZ *"
expect(grammar.ptypes).to eq("default" => "XYZ *") expect(grammar.ptypes).to eq("default" => "XYZ *")
@@ -62,7 +62,7 @@ EOF
expect(o).to_not be_nil expect(o).to_not be_nil
expect(o.pattern).to eq "token_with_code" expect(o.pattern).to eq "token_with_code"
expect(o.line_number).to eq 11 expect(o.line_number).to eq 11
expect(o.code).to eq "Code for the token\n" expect(o.code).to eq %[#line 12 "test.propane"\nCode for the token\n#linereset\n]
o = grammar.tokens.find {|token| token.name == "token_with_no_pattern"} o = grammar.tokens.find {|token| token.name == "token_with_no_pattern"}
expect(o).to_not be_nil expect(o).to_not be_nil
@@ -83,7 +83,7 @@ EOF
expect(o.name).to eq "A" expect(o.name).to eq "A"
expect(o.components).to eq %w[B] expect(o.components).to eq %w[B]
expect(o.line_number).to eq 19 expect(o.line_number).to eq 19
expect(o.code).to eq " a = 42;\n" expect(o.code).to eq %[#line 20 "test.propane"\n a = 42;\n#linereset\n]
o = grammar.rules[1] o = grammar.rules[1]
expect(o.name).to eq "B" expect(o.name).to eq "B"
@@ -95,7 +95,7 @@ EOF
expect(o.name).to eq "B" expect(o.name).to eq "B"
expect(o.components).to eq [] expect(o.components).to eq []
expect(o.line_number).to eq 23 expect(o.line_number).to eq 23
expect(o.code).to eq " b = 0;\n" expect(o.code).to eq %[#line 24 "test.propane"\n b = 0;\n#linereset\n]
end end
it "parses code segments with semicolons" do it "parses code segments with semicolons" do
@@ -113,7 +113,7 @@ tokenid token_with_no_pattern;
prefix myparser_; prefix myparser_;
EOF EOF
grammar = Grammar.new(input) grammar = Grammar.new(input, "test.propane")
expect(grammar.prefix).to eq "myparser_" expect(grammar.prefix).to eq "myparser_"
o = grammar.tokens.find {|token| token.name == "code1"} o = grammar.tokens.find {|token| token.name == "code1"}
@@ -122,7 +122,7 @@ EOF
o = grammar.patterns.find {|pattern| pattern.token == o} o = grammar.patterns.find {|pattern| pattern.token == o}
expect(o).to_not be_nil expect(o).to_not be_nil
expect(o.code).to eq " a = b;\n return c;\n" expect(o.code).to eq %[#line 2 "test.propane"\n a = b;\n return c;\n#linereset\n]
o = grammar.tokens.find {|token| token.name == "code2"} o = grammar.tokens.find {|token| token.name == "code2"}
expect(o).to_not be_nil expect(o).to_not be_nil
@@ -130,7 +130,42 @@ EOF
o = grammar.patterns.find {|pattern| pattern.token == o} o = grammar.patterns.find {|pattern| pattern.token == o}
expect(o).to_not be_nil expect(o).to_not be_nil
expect(o.code).to eq %[ writeln("Hello there");\n] expect(o.code).to eq %[#line 7 "test.propane"\n writeln("Hello there");\n#linereset\n]
end
it "does not emit #line directives with noline statement" do
input = <<EOF
noline;
token code1 <<
a = b;
return c;
>>
token code2 <<
writeln("Hello there");
>>
tokenid token_with_no_pattern;
prefix myparser_;
EOF
grammar = Grammar.new(input, "test.propane")
expect(grammar.prefix).to eq "myparser_"
o = grammar.tokens.find {|token| token.name == "code1"}
expect(o).to_not be_nil
o = grammar.patterns.find {|pattern| pattern.token == o}
expect(o).to_not be_nil
expect(o.code).to eq %[\n a = b;\n return c;]
o = grammar.tokens.find {|token| token.name == "code2"}
expect(o).to_not be_nil
o = grammar.patterns.find {|pattern| pattern.token == o}
expect(o).to_not be_nil
expect(o.code).to eq %[\n writeln("Hello there");]
end end
it "supports mode labels" do it "supports mode labels" do
@@ -144,7 +179,7 @@ m2: /bar/ <<
drop /q/; drop /q/;
m3: drop /r/; m3: drop /r/;
EOF EOF
grammar = Grammar.new(input) grammar = Grammar.new(input, "test.propane")
o = grammar.tokens.find {|token| token.name == "a"} o = grammar.tokens.find {|token| token.name == "a"}
expect(o).to_not be_nil expect(o).to_not be_nil
@@ -197,7 +232,7 @@ tokenid int(integer);
Start (node) -> R; Start (node) -> R;
R -> abc int; R -> abc int;
EOF EOF
grammar = Grammar.new(input) grammar = Grammar.new(input, "test.propane")
o = grammar.tokens.find {|token| token.name == "abc"} o = grammar.tokens.find {|token| token.name == "abc"}
expect(o).to_not be_nil expect(o).to_not be_nil
+1 -1
View File
@@ -51,7 +51,7 @@ class TestLexer
end end
def run(grammar, input) def run(grammar, input)
grammar = Propane::Grammar.new(grammar) grammar = Propane::Grammar.new(grammar, "test.propane")
token_dfa = Propane::Lexer::DFA.new(grammar.patterns) token_dfa = Propane::Lexer::DFA.new(grammar.patterns)
test_lexer = TestLexer.new(token_dfa) test_lexer = TestLexer.new(token_dfa)
test_lexer.lex(input) test_lexer.lex(input)
+747 -96
View File
File diff suppressed because it is too large. Load diff
+5
View File
@@ -2,6 +2,10 @@ unless ENV["dist_specs"]
require "bundler/setup" require "bundler/setup"
require "simplecov" require "simplecov"
class MyFormatter
def format(*args)
end
end
SimpleCov.start do SimpleCov.start do
add_filter "/spec/" add_filter "/spec/"
add_filter "/.bundle/" add_filter "/.bundle/"
@@ -12,6 +16,7 @@ unless ENV["dist_specs"]
end end
project_name "Propane" project_name "Propane"
merge_timeout 3600 merge_timeout 3600
formatter(MyFormatter)
end end
RSpec.configure do |config| RSpec.configure do |config|
+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");
} }
+50
View File
@@ -0,0 +1,50 @@
#include "testparser.h"
#include <assert.h>
#include <string.h>
int main()
{
char const * input = "abc\n defg hi\n!";
p_context_t * context = p_context_new((uint8_t const *)input, strlen(input));
p_token_info_t token_info;
/* First token "abc" on row 1, cols 1-3. */
assert(p_lex(context, &token_info) == P_SUCCESS);
assert(token_info.token == TOKEN_word);
assert(context->last_start.row == 1u);
assert(context->last_start.col == 1u);
assert(context->last_end.row == 1u);
assert(context->last_end.col == 3u);
/* The lexer code block observed the same positions reported to the caller. */
assert(context->last_start.row == token_info.position.row);
assert(context->last_start.col == token_info.position.col);
assert(context->last_end.row == token_info.end_position.row);
assert(context->last_end.col == token_info.end_position.col);
/* Second token "defg" on row 2, cols 3-6. */
assert(p_lex(context, &token_info) == P_SUCCESS);
assert(token_info.token == TOKEN_word);
assert(context->last_start.row == 2u);
assert(context->last_start.col == 3u);
assert(context->last_end.row == 2u);
assert(context->last_end.col == 6u);
/* Third token "hi" on row 2, cols 8-9. */
assert(p_lex(context, &token_info) == P_SUCCESS);
assert(token_info.token == TOKEN_word);
assert(context->last_start.row == 2u);
assert(context->last_start.col == 8u);
assert(context->last_end.row == 2u);
assert(context->last_end.col == 9u);
/* The "!" stop token terminates the lexer. The context input text position
* must not be updated when the lexer user code requests termination, so it
* still points at the "!" token on row 3, col 1. */
assert(p_lex(context, &token_info) == P_USER_TERMINATED);
assert(p_user_terminate_code(context) == 42u);
assert(context->text_position.row == 3u);
assert(context->text_position.col == 1u);
p_context_delete(context);
return 0;
}
+42
View File
@@ -0,0 +1,42 @@
import testparser;
import std.stdio;
int main()
{
return 0;
}
unittest
{
string input = "abc\n defg hi\n!";
p_context_t * context = p_context_new(input);
p_token_info_t token_info;
/* First token "abc" on row 1, cols 1-3. */
assert(p_lex(context, &token_info) == P_SUCCESS);
assert(token_info.token == TOKEN_word);
assert(context.last_start == p_position_t(1, 1));
assert(context.last_end == p_position_t(1, 3));
/* The lexer code block observed the same positions reported to the caller. */
assert(context.last_start == token_info.position);
assert(context.last_end == token_info.end_position);
/* Second token "defg" on row 2, cols 3-6. */
assert(p_lex(context, &token_info) == P_SUCCESS);
assert(token_info.token == TOKEN_word);
assert(context.last_start == p_position_t(2, 3));
assert(context.last_end == p_position_t(2, 6));
/* Third token "hi" on row 2, cols 8-9. */
assert(p_lex(context, &token_info) == P_SUCCESS);
assert(token_info.token == TOKEN_word);
assert(context.last_start == p_position_t(2, 8));
assert(context.last_end == p_position_t(2, 9));
/* The "!" stop token terminates the lexer. The context input text position
* must not be updated when the lexer user code requests termination, so it
* still points at the "!" token on row 3, col 1. */
assert(p_lex(context, &token_info) == P_USER_TERMINATED);
assert(p_user_terminate_code(context) == 42u);
assert(context.text_position == p_position_t(3, 1));
}
+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);
} }
@@ -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,10 +17,13 @@ int main()
assert(start->pR == NULL); assert(start->pR == NULL);
assert(start->r == NULL); assert(start->r == NULL);
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);
@@ -31,15 +34,21 @@ 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_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_tree_delete(start);
p_context_delete(context);
return 0; return 0;
} }
@@ -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);
} }
@@ -6,20 +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_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);
@@ -28,15 +31,21 @@ 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_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_tree_delete(start);
p_context_delete(context);
return 0; return 0;
} }
@@ -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);
} }
+39
View File
@@ -0,0 +1,39 @@
#include "testparser.h"
#include <assert.h>
#include <string.h>
#include "testutils.h"
int main()
{
char const * input = "ab";
p_context_t * context;
context = p_context_new((uint8_t const *)input, strlen(input));
assert_eq(P_SUCCESS, p_parse(context));
/* The parser user code recorded values accessed via $$, $1, and $2 while
* the tree node for the Start rule was being formed. */
assert_eq(3, context->start_n_fields);
assert_eq(11, context->start_a_value);
assert_eq(11, context->a_value);
assert_eq(22, context->b_value);
assert_eq(TOKEN_b, context->b_token);
/* The empty-matched rule C has a null $$ tree node, and its field in the
* Start node is null as well. */
assert_eq(1, context->c_is_null);
assert_eq(1, context->c_field_is_null);
/* Field aliases reference the same component tree nodes as the positional
* references. */
assert_eq(11, context->alias_a_value);
assert_eq(22, context->alias_b_value);
Start * start = p_result(context);
assert(start->pA != NULL);
assert(start->pB != NULL);
assert(start->pC == NULL);
p_tree_delete(start);
p_context_delete(context);
return 0;
}
+39
View File
@@ -0,0 +1,39 @@
import testparser;
import std.stdio;
import testutils;
int main()
{
return 0;
}
unittest
{
string input = "ab";
p_context_t * context = p_context_new(input);
assert_eq(P_SUCCESS, p_parse(context));
/* The parser user code recorded values accessed via $$, $1, and $2 while
* the tree node for the Start rule was being formed. */
assert_eq(3, context.start_n_fields);
assert_eq(11, context.start_a_value);
assert_eq(11, context.a_value);
assert_eq(22, context.b_value);
assert_eq(TOKEN_b, context.b_token);
/* The empty-matched rule C has a null $$ tree node, and its field in the
* Start node is null as well. */
assert_eq(1, context.c_is_null);
assert_eq(1, context.c_field_is_null);
/* Field aliases reference the same component tree nodes as the positional
* references. */
assert_eq(11, context.alias_a_value);
assert_eq(22, context.alias_b_value);
Start * start = p_result(context);
assert(start.pA !is null);
assert(start.pB !is null);
assert(start.pC is null);
p_tree_delete(start);
}
+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");
} }
+29
View File
@@ -0,0 +1,29 @@
#include "testparser.h"
#include <assert.h>
#include <string.h>
#include <stdio.h>
int main()
{
char const * input = " Hello\n\n 4200\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);
printf("\n");
input = "\n tok2";
context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(context) == P_SUCCESS);
p_context_delete(context);
printf("\n");
input = " tok1";
context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(context) == P_SUCCESS);
p_context_delete(context);
return 0;
}
+27
View File
@@ -0,0 +1,27 @@
import testparser;
import std.stdio;
int main()
{
return 0;
}
unittest
{
string input = " Hello\n\n 4200\n";
p_context_t * context;
context = p_context_new(input);
assert(p_parse(context) == P_SUCCESS);
writeln();
input = "\n tok2";
context = p_context_new(input);
assert(p_parse(context) == P_SUCCESS);
writeln();
input = " tok1";
context = p_context_new(input);
assert(p_parse(context) == P_SUCCESS);
}
+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);
} }
-17
View File
@@ -1,17 +0,0 @@
#include "testparser.h"
#include <assert.h>
#include <string.h>
#include "testutils.h"
int main()
{
char const * input = "hi";
p_context_t context;
p_context_init(&context, (uint8_t const *)input, strlen(input));
assert_eq(P_SUCCESS, p_parse(&context));
Top * top = p_result(&context);
assert(top->pToken != NULL);
assert_eq(TOKEN_hi, top->pToken->token);
return 0;
}
+20
View File
@@ -0,0 +1,20 @@
#include "testparser.h"
#include <assert.h>
#include <string.h>
#include "testutils.h"
int main()
{
char const * input = "hi";
p_context_t * context;
context = p_context_new((uint8_t const *)input, strlen(input));
assert_eq(P_SUCCESS, p_parse(context));
Top * top = p_result(context);
assert(top->pToken != NULL);
assert_eq(TOKEN_hi, top->pToken->token);
p_tree_delete(top);
p_context_delete(context);
return 0;
}
@@ -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);
} }
+30
View File
@@ -0,0 +1,30 @@
#include "testparser.h"
#include <assert.h>
#include <string.h>
#include "testutils.h"
int main()
{
char const * input = "bbbb";
p_context_t * context;
context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse(context) == P_SUCCESS);
int result = p_result(context);
assert_eq(8, result);
p_context_delete(context);
context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse_Bs(context) == P_SUCCESS);
result = p_result_Bs(context);
assert_eq(8, result);
p_context_delete(context);
input = "c";
context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse_R(context) == P_SUCCESS);
result = p_result_R(context);
assert_eq(3, result);
p_context_delete(context);
return 0;
}
+29
View File
@@ -0,0 +1,29 @@
import testparser;
import std.stdio;
import testutils;
int main()
{
return 0;
}
unittest
{
string input = "bbbb";
p_context_t * context;
context = p_context_new(input);
assert(p_parse(context) == P_SUCCESS);
int result = p_result(context);
assert(result == 8);
context = p_context_new(input);
assert(p_parse_Bs(context) == P_SUCCESS);
result = p_result_Bs(context);
assert(result == 8);
input = "c";
context = p_context_new(input);
assert(p_parse_R(context) == P_SUCCESS);
result = p_result_R(context);
assert(result == 3);
}
+40
View File
@@ -0,0 +1,40 @@
#include "testparser.h"
#include <assert.h>
#include <string.h>
#include "testutils.h"
int main()
{
char const * input = "bbbb";
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_not_null(start->bs);
assert_not_null(start->bs->b);
assert_not_null(start->bs->bs->b);
assert_not_null(start->bs->bs->bs->b);
assert_not_null(start->bs->bs->bs->bs->b);
p_tree_delete(start);
p_context_delete(context);
context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse_Bs(context) == P_SUCCESS);
Bs * bs = p_result_Bs(context);
assert_not_null(bs->b);
assert_not_null(bs->bs->b);
assert_not_null(bs->bs->bs->b);
assert_not_null(bs->bs->bs->bs->b);
p_tree_delete_Bs(bs);
p_context_delete(context);
input = "c";
context = p_context_new((uint8_t const *)input, strlen(input));
assert(p_parse_R(context) == P_SUCCESS);
R * r = p_result_R(context);
assert_not_null(r->c);
p_tree_delete_R(r);
p_context_delete(context);
return 0;
}
+41
View File
@@ -0,0 +1,41 @@
import testparser;
import std.stdio;
import testutils;
int main()
{
return 0;
}
unittest
{
string input = "bbbb";
p_context_t * context = p_context_new(input);
assert(p_parse(context) == P_SUCCESS);
Start * start = p_result(context);
assert(start.bs);
assert(start.bs.b);
assert(start.bs.bs.b);
assert(start.bs.bs.bs.b);
assert(start.bs.bs.bs.bs.b);
p_tree_delete(start);
context = p_context_new(input);
assert(p_parse_Bs(context) == P_SUCCESS);
Bs * bs = p_result_Bs(context);
assert(bs.b);
assert(bs.bs.b);
assert(bs.bs.bs.b);
assert(bs.bs.bs.bs.b);
p_tree_delete_Bs(bs);
input = "c";
context = p_context_new(input);
assert(p_parse_R(context) == P_SUCCESS);
R * r = p_result_R(context);
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);
}
+19 -10
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,16 +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_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_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);
@@ -51,5 +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_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);
} }
+20
View File
@@ -0,0 +1,20 @@
#include "testparser.h"
#include <assert.h>
#include <string.h>
#include "testutils.h"
int main()
{
char const * input = "ab";
p_context_t * context;
context = p_context_new((uint8_t const *)input, strlen(input));
assert_eq(P_SUCCESS, p_parse(context));
Start * start = p_result(context);
assert(start->a != NULL);
assert(*start->a->pvalue == 1);
assert(start->b != NULL);
assert(*start->b->pvalue == 2);
p_tree_delete(start);
p_context_delete(context);
}
@@ -6,14 +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_tree_delete(start);
p_context_delete(context);
return 0; return 0;
} }
@@ -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);
} }
@@ -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,10 +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_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);
@@ -54,10 +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_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);
@@ -78,10 +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_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);
@@ -98,5 +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_tree_delete(start);
p_context_delete(context);
return 0; return 0;
} }
@@ -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);
} }
@@ -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;
@@ -386,7 +386,7 @@ int main(int argc, char * argv[])
} }
pmis = pmis->pModuleItems; pmis = pmis->pModuleItems;
} }
pfds = malloc(n_pfds * sizeof(PModuleItems *)); pfds = (PFunctionDefinition **)malloc(n_pfds * sizeof(PModuleItems *));
pmis = pmod->pModuleItems; pmis = pmod->pModuleItems;
size_t pfd_i = n_pfds; size_t pfd_i = n_pfds;
while (pmis != NULL) while (pmis != NULL)
@@ -411,5 +411,9 @@ int main(int argc, char * argv[])
} }
} }
free(pfds);
p_tree_delete(pmod);
p_context_delete(context);
return 0; return 0;
} }
@@ -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);
} }
+19 -10
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,16 +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_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_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);
@@ -51,5 +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_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);
} }
@@ -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,10 +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_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);
@@ -80,5 +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_tree_delete(start);
p_context_delete(context);
return 0; return 0;
} }
@@ -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);
} }
Loaded 100 of 114 files, more files were not shown because too many files have changed in this diff. Show more