Fix named optional rules - close #29
This commit is contained in:
parent
36c74e439e
commit
98e10d3d14
@ -641,13 +641,14 @@ This example uses the default start rule name of `Start`.
|
|||||||
|
|
||||||
A parser rule has zero or more fields on the right side of its definition.
|
A parser rule has zero or more fields on the right side of its definition.
|
||||||
Each of these fields is either a token name or a rule name.
|
Each of these fields is either a token name or a rule name.
|
||||||
|
A field can be immediately followed by a `?` character to signify that it is
|
||||||
|
optional.
|
||||||
A field can optionally be followed by a `:` and then a field alias name.
|
A field can optionally be followed by a `:` and then a field alias name.
|
||||||
If present, the field alias name is used to refer to the field value in user
|
If present, the field alias name is used to refer to the field value in user
|
||||||
code blocks, or if AST mode is active, the field alias name is used as the
|
code blocks, or if AST mode is active, the field alias name is used as the
|
||||||
field name in the generated AST node structure.
|
field name in the generated AST node structure.
|
||||||
A field can be immediately followed by a `?` character to signify that it is
|
An optional and named field must use the format `field?:name`.
|
||||||
optional.
|
Example:
|
||||||
Another example:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
token public;
|
token public;
|
||||||
@ -655,7 +656,7 @@ token private;
|
|||||||
token int;
|
token int;
|
||||||
token ident /[a-zA-Z_][a-zA-Z_0-9]*/;
|
token ident /[a-zA-Z_][a-zA-Z_0-9]*/;
|
||||||
token semicolon /;/;
|
token semicolon /;/;
|
||||||
IntegerDeclaration -> Visibility? int ident:name semicolon;
|
IntegerDeclaration -> Visibility?:visibility int ident:name semicolon;
|
||||||
Visibility -> public;
|
Visibility -> public;
|
||||||
Visibility -> private;
|
Visibility -> private;
|
||||||
```
|
```
|
||||||
@ -663,7 +664,7 @@ Visibility -> private;
|
|||||||
In a parser rule code block, parser values for the right side fields are
|
In a parser rule code block, parser values for the right side fields are
|
||||||
accessible as `$1` for the first field's parser value, `$2` for the second
|
accessible as `$1` for the first field's parser value, `$2` for the second
|
||||||
field's parser value, etc...
|
field's parser value, etc...
|
||||||
For the `IntegerDeclaration` rule, the third field value can also be referred
|
For the `IntegerDeclaration` rule, the first field value can also be referred to as `${visibility}` and the third field value can also be referred
|
||||||
to as `${name}`.
|
to as `${name}`.
|
||||||
The `$$` symbol accesses the output parser value for this rule.
|
The `$$` symbol accesses the output parser value for this rule.
|
||||||
The above examples demonstrate how the parser values for the rule components
|
The above examples demonstrate how the parser values for the rule components
|
||||||
|
|||||||
@ -198,7 +198,7 @@ class Propane
|
|||||||
if @ast && ptypename
|
if @ast && ptypename
|
||||||
raise Error.new("Multiple ptypes are unsupported in AST mode")
|
raise Error.new("Multiple ptypes are unsupported in AST 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
|
if @ast
|
||||||
consume!(/;/, "expected `;'")
|
consume!(/;/, "expected `;'")
|
||||||
|
|||||||
@ -1118,6 +1118,47 @@ EOF
|
|||||||
expect(results.status).to eq 0
|
expect(results.status).to eq 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "allows naming an optional rule component in AST generation mode" do
|
||||||
|
if language == "d"
|
||||||
|
write_grammar <<EOF
|
||||||
|
ast;
|
||||||
|
|
||||||
|
<<
|
||||||
|
import std.stdio;
|
||||||
|
>>
|
||||||
|
|
||||||
|
token a;
|
||||||
|
token b;
|
||||||
|
token c;
|
||||||
|
token d;
|
||||||
|
Start -> a?:a b R?:r;
|
||||||
|
R -> c d;
|
||||||
|
R -> d c;
|
||||||
|
EOF
|
||||||
|
else
|
||||||
|
write_grammar <<EOF
|
||||||
|
ast;
|
||||||
|
|
||||||
|
<<
|
||||||
|
#include <stdio.h>
|
||||||
|
>>
|
||||||
|
|
||||||
|
token a;
|
||||||
|
token b;
|
||||||
|
token c;
|
||||||
|
token d;
|
||||||
|
Start -> a?:a b R?:r;
|
||||||
|
R -> c d;
|
||||||
|
R -> d c;
|
||||||
|
EOF
|
||||||
|
end
|
||||||
|
run_propane(language: language)
|
||||||
|
compile("spec/test_named_optional_rule_component_ast.#{language}", language: language)
|
||||||
|
results = run_test
|
||||||
|
expect(results.stderr).to eq ""
|
||||||
|
expect(results.status).to eq 0
|
||||||
|
end
|
||||||
|
|
||||||
it "stores token and rule positions in AST nodes" do
|
it "stores token and rule positions in AST nodes" do
|
||||||
write_grammar <<EOF
|
write_grammar <<EOF
|
||||||
ast;
|
ast;
|
||||||
|
|||||||
45
spec/test_named_optional_rule_component_ast.c
Normal file
45
spec/test_named_optional_rule_component_ast.c
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include "testparser.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include "testutils.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
char const * input = "b";
|
||||||
|
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(start->a == NULL);
|
||||||
|
assert(start->pToken2 != NULL);
|
||||||
|
assert_eq(TOKEN_b, start->pToken2->token);
|
||||||
|
assert(start->pR3 == NULL);
|
||||||
|
assert(start->pR == NULL);
|
||||||
|
assert(start->r == NULL);
|
||||||
|
|
||||||
|
input = "abcd";
|
||||||
|
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
start = p_result(&context);
|
||||||
|
assert(start->a != NULL);
|
||||||
|
assert_eq(TOKEN_a, start->pToken1->token);
|
||||||
|
assert(start->pToken2 != NULL);
|
||||||
|
assert(start->pR3 != NULL);
|
||||||
|
assert(start->pR != NULL);
|
||||||
|
assert(start->r != NULL);
|
||||||
|
assert(start->pR == start->pR3);
|
||||||
|
assert(start->pR == start->r);
|
||||||
|
assert_eq(TOKEN_c, start->pR->pToken1->token);
|
||||||
|
|
||||||
|
input = "bdc";
|
||||||
|
p_context_init(&context, (uint8_t const *)input, strlen(input));
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
start = p_result(&context);
|
||||||
|
assert(start->a == NULL);
|
||||||
|
assert(start->pToken2 != NULL);
|
||||||
|
assert(start->r != NULL);
|
||||||
|
assert_eq(TOKEN_d, start->pR->pToken1->token);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
46
spec/test_named_optional_rule_component_ast.d
Normal file
46
spec/test_named_optional_rule_component_ast.d
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import testparser;
|
||||||
|
import std.stdio;
|
||||||
|
import testutils;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
string input = "b";
|
||||||
|
p_context_t context;
|
||||||
|
p_context_init(&context, input);
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
Start * start = p_result(&context);
|
||||||
|
assert(start.pToken1 is null);
|
||||||
|
assert(start.pToken2 !is null);
|
||||||
|
assert_eq(TOKEN_b, start.pToken2.token);
|
||||||
|
assert(start.pR3 is null);
|
||||||
|
assert(start.pR is null);
|
||||||
|
assert(start.r is null);
|
||||||
|
|
||||||
|
input = "abcd";
|
||||||
|
p_context_init(&context, input);
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
start = p_result(&context);
|
||||||
|
assert(start.pToken1 != null);
|
||||||
|
assert_eq(TOKEN_a, start.pToken1.token);
|
||||||
|
assert(start.pToken2 != null);
|
||||||
|
assert(start.pR3 != null);
|
||||||
|
assert(start.pR != null);
|
||||||
|
assert(start.r != null);
|
||||||
|
assert(start.pR == start.pR3);
|
||||||
|
assert(start.pR == start.r);
|
||||||
|
assert_eq(TOKEN_c, start.pR.pToken1.token);
|
||||||
|
|
||||||
|
input = "bdc";
|
||||||
|
p_context_init(&context, input);
|
||||||
|
assert(p_parse(&context) == P_SUCCESS);
|
||||||
|
start = p_result(&context);
|
||||||
|
assert(start.pToken1 is null);
|
||||||
|
assert(start.pToken2 !is null);
|
||||||
|
assert(start.pR !is null);
|
||||||
|
assert_eq(TOKEN_d, start.pR.pToken1.token);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user