Test multiple starting rules
This commit is contained in:
parent
5187cff24d
commit
193666d499
@ -738,6 +738,22 @@ Example:
|
||||
start MyStartRule;
|
||||
```
|
||||
|
||||
Multiple start rules can be specified, either with multiple `start` statements
|
||||
or one `start` statement listing multiple start rules.
|
||||
Example:
|
||||
|
||||
```
|
||||
start Module ModuleItem Statement Expression;
|
||||
```
|
||||
|
||||
When multiple start rules are specified, multiple `p_parse_*()` functions,
|
||||
`p_result_*()`, and `p_free_ast_*()` functions (in AST mode) are generated.
|
||||
A default `p_parse()`, `p_result()`, `p_free_ast()` are generated corresponding
|
||||
to the first start rule.
|
||||
Additionally, each start rule causes the generation of another version of each
|
||||
of these functions, for example `p_parse_Statement()`, `p_result_Statement()`,
|
||||
and `p_free_ast_Statement()`.
|
||||
|
||||
##> Specifying the parser module name - the `module` statement
|
||||
|
||||
The `module` statement can be used to specify the module name for a generated
|
||||
@ -1018,6 +1034,16 @@ p_context_init(&context, input, input_length);
|
||||
size_t result = p_parse(&context);
|
||||
```
|
||||
|
||||
When multiple start rules are specified, a separate parse function is generated
|
||||
for each which starts parsing at the given rule.
|
||||
For example, if `Statement` is specified as a start rule:
|
||||
|
||||
```
|
||||
size_t result = p_parse_Statement(&context);
|
||||
```
|
||||
|
||||
In this case, the parser will start parsing with the `Statement` rule.
|
||||
|
||||
### `p_position_valid`
|
||||
|
||||
The `p_position_valid()` function is only generated for C targets.
|
||||
@ -1056,6 +1082,23 @@ if (p_parse(&context) == P_SUCCESS)
|
||||
If AST generation mode is active, then the `p_result()` function returns a
|
||||
`Start *` pointing to the `Start` AST structure.
|
||||
|
||||
When multiple start rules are specified, a separate result function is generated
|
||||
for each which returns the parse result for the corresponding rule.
|
||||
For example, if `Statement` is specified as a start rule:
|
||||
|
||||
```
|
||||
p_context_t context;
|
||||
p_context_init(&context, input, input_length);
|
||||
size_t result = p_parse(&context);
|
||||
if (p_parse_Statement(&context) == P_SUCCESS)
|
||||
{
|
||||
result = p_result_Statement(&context);
|
||||
}
|
||||
```
|
||||
|
||||
In this case, the parser will start parsing with the `Statement` rule and the
|
||||
parse result from the `Statement` rule will be returned.
|
||||
|
||||
### `p_position`
|
||||
|
||||
The `p_position()` function can be used to retrieve the parser position where
|
||||
@ -1142,6 +1185,17 @@ If specified, the `free_token_node` function will be called during the
|
||||
`p_free_ast()` process to allow user code to free any memory associated with
|
||||
a token node's `pvalue`.
|
||||
|
||||
When multiple start rules are specified, a separate `p_free_ast` function is
|
||||
generated for each which frees the AST resulting from parsing the given rule.
|
||||
For example, if `Statement` is specified as a start rule:
|
||||
|
||||
```
|
||||
p_free_ast_Statement(statement_ast);
|
||||
```
|
||||
|
||||
In this case, Propane will free a `Statement` AST structure returned by the
|
||||
`p_parse_Statement(&context)` function.
|
||||
|
||||
##> Data
|
||||
|
||||
### `p_token_names`
|
||||
|
||||
@ -242,8 +242,11 @@ class Propane
|
||||
end
|
||||
|
||||
def parse_start_statement!
|
||||
if md = consume!(/start\s+(\w+)\s*;/)
|
||||
@start_rules << md[1] unless @start_rules.include?(md[1])
|
||||
if md = consume!(/start\s+([\w\s]*);/)
|
||||
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
|
||||
|
||||
|
||||
@ -1440,6 +1440,47 @@ EOF
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
|
||||
it "allows multiple starting rules" do
|
||||
write_grammar <<EOF
|
||||
ptype int;
|
||||
token a << $$ = 1; >>
|
||||
token b << $$ = 2; >>
|
||||
token c << $$ = 3; >>
|
||||
Start -> a b R;
|
||||
Start -> Bs:bs << $$ = $1; >>
|
||||
R -> c:c << $$ = $1; >>
|
||||
Bs -> << $$ = 0; >>
|
||||
Bs -> b:b Bs:bs << $$ = $1 + $2; >>
|
||||
start Start R Bs;
|
||||
EOF
|
||||
run_propane(language: language)
|
||||
compile("spec/test_starting_rules.#{language}", language: language)
|
||||
results = run_test(language: language)
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
|
||||
it "allows multiple starting rules in AST mode" do
|
||||
write_grammar <<EOF
|
||||
ast;
|
||||
ptype int;
|
||||
token a << $$ = 1; >>
|
||||
token b << $$ = 2; >>
|
||||
token c << $$ = 3; >>
|
||||
Start -> a b R;
|
||||
Start -> Bs:bs;
|
||||
R -> c:c;
|
||||
Bs -> ;
|
||||
Bs -> b:b Bs:bs;
|
||||
start Start R Bs;
|
||||
EOF
|
||||
run_propane(language: language)
|
||||
compile("spec/test_starting_rules_ast.#{language}", language: language)
|
||||
results = run_test(language: language)
|
||||
expect(results.stderr).to eq ""
|
||||
expect(results.status).to eq 0
|
||||
end
|
||||
|
||||
if %w[c cpp].include?(language)
|
||||
it "allows a user function to free token node memory in AST mode" do
|
||||
write_grammar <<EOF
|
||||
|
||||
27
spec/test_starting_rules.c
Normal file
27
spec/test_starting_rules.c
Normal file
@ -0,0 +1,27 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "testutils.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "bbbb";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
int result = p_result(&context);
|
||||
assert_eq(8, result);
|
||||
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse_Bs(&context) == P_SUCCESS);
|
||||
result = p_result_Bs(&context);
|
||||
assert_eq(8, result);
|
||||
|
||||
input = "c";
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse_R(&context) == P_SUCCESS);
|
||||
result = p_result_R(&context);
|
||||
assert_eq(3, result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
29
spec/test_starting_rules.d
Normal file
29
spec/test_starting_rules.d
Normal file
@ -0,0 +1,29 @@
|
||||
import testparser;
|
||||
import std.stdio;
|
||||
import testutils;
|
||||
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
string input = "bbbb";
|
||||
p_context_t context;
|
||||
p_context_init(&context, input);
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
int result = p_result(&context);
|
||||
assert(result == 8);
|
||||
|
||||
p_context_init(&context, input);
|
||||
assert(p_parse_Bs(&context) == P_SUCCESS);
|
||||
result = p_result_Bs(&context);
|
||||
assert(result == 8);
|
||||
|
||||
input = "c";
|
||||
p_context_init(&context, input);
|
||||
assert(p_parse_R(&context) == P_SUCCESS);
|
||||
result = p_result_R(&context);
|
||||
assert(result == 3);
|
||||
}
|
||||
37
spec/test_starting_rules_ast.c
Normal file
37
spec/test_starting_rules_ast.c
Normal file
@ -0,0 +1,37 @@
|
||||
#include "testparser.h"
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "testutils.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
char const * input = "bbbb";
|
||||
p_context_t context;
|
||||
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||
assert(p_parse(&context) == P_SUCCESS);
|
||||
Start * start = p_result(&context);
|
||||
assert_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_free_ast(start);
|
||||
|
||||
p_context_init(&context, (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_free_ast_Bs(bs);
|
||||
|
||||
input = "c";
|
||||
p_context_init(&context, (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_free_ast_R(r);
|
||||
|
||||
return 0;
|
||||
}
|
||||
36
spec/test_starting_rules_ast.d
Normal file
36
spec/test_starting_rules_ast.d
Normal file
@ -0,0 +1,36 @@
|
||||
import testparser;
|
||||
import std.stdio;
|
||||
import testutils;
|
||||
|
||||
int main()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
string input = "bbbb";
|
||||
p_context_t context;
|
||||
p_context_init(&context, 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_context_init(&context, 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);
|
||||
|
||||
input = "c";
|
||||
p_context_init(&context, input);
|
||||
assert(p_parse_R(&context) == P_SUCCESS);
|
||||
R * r = p_result_R(&context);
|
||||
assert(r.c);
|
||||
}
|
||||
@ -14,6 +14,24 @@ void assert_eq_size_t_i(size_t expected, size_t actual, char const * file, size_
|
||||
}
|
||||
}
|
||||
|
||||
void assert_ne_size_t_i(size_t expected, size_t actual, char const * file, size_t line)
|
||||
{
|
||||
if (expected == actual)
|
||||
{
|
||||
fprintf(stderr, "%s:%lu: expected not %lu, got %lu\n", file, line, expected, actual);
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void assert_not_null_i(void * ptr, char const * file, size_t line)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
{
|
||||
fprintf(stderr, "%s:%lu: expected not NULL\n", file, line);
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void str_init(str_t * str, char const * cs)
|
||||
{
|
||||
size_t length = strlen(cs);
|
||||
|
||||
@ -5,6 +5,16 @@ void assert_eq_size_t_i(size_t expected, size_t actual, char const * file, size_
|
||||
#define assert_eq(expected, actual) \
|
||||
assert_eq_size_t_i(expected, actual, __FILE__, __LINE__)
|
||||
|
||||
void assert_ne_size_t_i(size_t expected, size_t actual, char const * file, size_t line);
|
||||
|
||||
#define assert_ne(expected, actual) \
|
||||
assert_ne_size_t_i(expected, actual, __FILE__, __LINE__)
|
||||
|
||||
void assert_not_null_i(void * ptr, char const * file, size_t line);
|
||||
|
||||
#define assert_not_null(ptr) \
|
||||
assert_not_null_i(ptr, __FILE__, __LINE__)
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char * cs;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user