Test multiple starting rules
This commit is contained in:
parent
5187cff24d
commit
193666d499
@ -738,6 +738,22 @@ Example:
|
|||||||
start MyStartRule;
|
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
|
##> Specifying the parser module name - the `module` statement
|
||||||
|
|
||||||
The `module` statement can be used to specify the module name for a generated
|
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);
|
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`
|
### `p_position_valid`
|
||||||
|
|
||||||
The `p_position_valid()` function is only generated for C targets.
|
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
|
If AST generation mode is active, then the `p_result()` function returns a
|
||||||
`Start *` pointing to the `Start` AST structure.
|
`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`
|
### `p_position`
|
||||||
|
|
||||||
The `p_position()` function can be used to retrieve the parser position where
|
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
|
`p_free_ast()` process to allow user code to free any memory associated with
|
||||||
a token node's `pvalue`.
|
a token node's `pvalue`.
|
||||||
|
|
||||||
|
When multiple start rules are specified, a separate `p_free_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
|
##> Data
|
||||||
|
|
||||||
### `p_token_names`
|
### `p_token_names`
|
||||||
|
|||||||
@ -242,8 +242,11 @@ 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_rules << md[1] unless @start_rules.include?(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
|
||||||
|
|
||||||
|
|||||||
@ -1440,6 +1440,47 @@ EOF
|
|||||||
expect(results.status).to eq 0
|
expect(results.status).to eq 0
|
||||||
end
|
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)
|
if %w[c cpp].include?(language)
|
||||||
it "allows a user function to free token node memory in AST mode" do
|
it "allows a user function to free token node memory in AST mode" do
|
||||||
write_grammar <<EOF
|
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)
|
void str_init(str_t * str, char const * cs)
|
||||||
{
|
{
|
||||||
size_t length = strlen(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) \
|
#define assert_eq(expected, actual) \
|
||||||
assert_eq_size_t_i(expected, actual, __FILE__, __LINE__)
|
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
|
typedef struct
|
||||||
{
|
{
|
||||||
char * cs;
|
char * cs;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user