mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2025-01-22 21:35:29 +00:00
Macro support (fix #25)
This commit is contained in:
parent
beaa0346ca
commit
bbd8e68b71
23
README.md
23
README.md
@ -20,6 +20,7 @@ The PEG syntax is well described on page 2 in the [document](http://www.brynosau
|
|||||||
* `$name(` ... `)` (Capture scope operator)
|
* `$name(` ... `)` (Capture scope operator)
|
||||||
* `$name<` ... `>` (Named capture operator)
|
* `$name<` ... `>` (Named capture operator)
|
||||||
* `$name` (Backreference operator)
|
* `$name` (Backreference operator)
|
||||||
|
* `MACRO_NAME(` ... `)` (Parameterized rule or Macro)
|
||||||
|
|
||||||
This library also supports the linear-time parsing known as the [*Packrat*](http://pdos.csail.mit.edu/~baford/packrat/thesis/thesis.pdf) parsing.
|
This library also supports the linear-time parsing known as the [*Packrat*](http://pdos.csail.mit.edu/~baford/packrat/thesis/thesis.pdf) parsing.
|
||||||
|
|
||||||
@ -285,6 +286,28 @@ parser.parse("This is <b>a <u>test</b> text</u>."); // NG
|
|||||||
parser.parse("This is <b>a <u>test text</b>."); // NG
|
parser.parse("This is <b>a <u>test text</b>."); // NG
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Parameterized Rule or Macro
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
```peg
|
||||||
|
# Syntax
|
||||||
|
Start ← _ Expr
|
||||||
|
Expr ← Sum
|
||||||
|
Sum ← List(Product, SumOpe)
|
||||||
|
Product ← List(Value, ProOpe)
|
||||||
|
Value ← Number / T('(') Expr T(')')
|
||||||
|
|
||||||
|
# Token
|
||||||
|
SumOpe ← T('+' / '-')
|
||||||
|
ProOpe ← T('*' / '/')
|
||||||
|
Number ← T([0-9]+)
|
||||||
|
~_ ← [ \t\r\n]*
|
||||||
|
|
||||||
|
# Macro
|
||||||
|
List(I, D) ← I (D I)*
|
||||||
|
T(x) ← < x > _
|
||||||
|
```
|
||||||
|
|
||||||
AST generation
|
AST generation
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
204
test/test.cc
204
test/test.cc
@ -948,8 +948,210 @@ TEST_CASE("Japanese character", "[unicode]")
|
|||||||
)");
|
)");
|
||||||
|
|
||||||
auto ret = parser.parse(u8R"(サーバーを復旧します。)");
|
auto ret = parser.parse(u8R"(サーバーを復旧します。)");
|
||||||
|
REQUIRE(ret == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Macro simple test", "[macro]")
|
||||||
|
{
|
||||||
|
parser parser(R"(
|
||||||
|
S <- HELLO WORLD
|
||||||
|
HELLO <- T('hello')
|
||||||
|
WORLD <- T('world')
|
||||||
|
T(a) <- a [ \t]*
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE(parser.parse("hello \tworld "));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Macro two parameters", "[macro]")
|
||||||
|
{
|
||||||
|
parser parser(R"(
|
||||||
|
S <- HELLO_WORLD
|
||||||
|
HELLO_WORLD <- T('hello', 'world')
|
||||||
|
T(a, b) <- a [ \t]* b [ \t]*
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE(parser.parse("hello \tworld "));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Macro syntax error", "[macro]")
|
||||||
|
{
|
||||||
|
parser parser(R"(
|
||||||
|
S <- T('hello')
|
||||||
|
T (a) <- a [ \t]*
|
||||||
|
)");
|
||||||
|
|
||||||
|
bool ret = parser;
|
||||||
|
REQUIRE(ret == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Macro missing argument", "[macro]")
|
||||||
|
{
|
||||||
|
parser parser(R"(
|
||||||
|
S <- T ('hello')
|
||||||
|
T(a, b) <- a [ \t]* b
|
||||||
|
)");
|
||||||
|
|
||||||
|
bool ret = parser;
|
||||||
|
REQUIRE(ret == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Macro reference syntax error", "[macro]")
|
||||||
|
{
|
||||||
|
parser parser(R"(
|
||||||
|
S <- T ('hello')
|
||||||
|
T(a) <- a [ \t]*
|
||||||
|
)");
|
||||||
|
|
||||||
|
bool ret = parser;
|
||||||
|
REQUIRE(ret == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Macro invalid macro reference error", "[macro]")
|
||||||
|
{
|
||||||
|
parser parser(R"(
|
||||||
|
S <- T('hello')
|
||||||
|
T <- 'world'
|
||||||
|
)");
|
||||||
|
|
||||||
|
bool ret = parser;
|
||||||
|
REQUIRE(ret == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Macro calculator", "[macro]")
|
||||||
|
{
|
||||||
|
// Create a PEG parser
|
||||||
|
parser parser(R"(
|
||||||
|
# Grammar for simple calculator...
|
||||||
|
EXPRESSION <- _ LIST(TERM, TERM_OPERATOR)
|
||||||
|
TERM <- LIST(FACTOR, FACTOR_OPERATOR)
|
||||||
|
FACTOR <- NUMBER / T('(') EXPRESSION T(')')
|
||||||
|
TERM_OPERATOR <- T([-+])
|
||||||
|
FACTOR_OPERATOR <- T([/*])
|
||||||
|
NUMBER <- T([0-9]+)
|
||||||
|
~_ <- [ \t]*
|
||||||
|
LIST(I, D) <- I (D I)*
|
||||||
|
T(S) <- < S > _
|
||||||
|
)");
|
||||||
|
|
||||||
|
// Setup actions
|
||||||
|
auto reduce = [](const SemanticValues& sv) -> long {
|
||||||
|
auto result = sv[0].get<long>();
|
||||||
|
for (auto i = 1u; i < sv.size(); i += 2) {
|
||||||
|
auto num = sv[i + 1].get<long>();
|
||||||
|
auto ope = sv[i].get<char>();
|
||||||
|
switch (ope) {
|
||||||
|
case '+': result += num; break;
|
||||||
|
case '-': result -= num; break;
|
||||||
|
case '*': result *= num; break;
|
||||||
|
case '/': result /= num; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
parser["EXPRESSION"] = reduce;
|
||||||
|
parser["TERM"] = reduce;
|
||||||
|
parser["TERM_OPERATOR"] = [](const SemanticValues& sv) { return static_cast<char>(*sv.c_str()); };
|
||||||
|
parser["FACTOR_OPERATOR"] = [](const SemanticValues& sv) { return static_cast<char>(*sv.c_str()); };
|
||||||
|
parser["NUMBER"] = [](const SemanticValues& sv) { return atol(sv.c_str()); };
|
||||||
|
|
||||||
|
bool ret = parser;
|
||||||
|
REQUIRE(ret == true);
|
||||||
|
|
||||||
|
auto expr = " 1 + 2 * 3 * (4 - 5 + 6) / 7 - 8 ";
|
||||||
|
long val = 0;
|
||||||
|
ret = parser.parse(expr, val);
|
||||||
|
|
||||||
REQUIRE(ret == true);
|
REQUIRE(ret == true);
|
||||||
|
REQUIRE(val == -3);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Macro expression arguments", "[macro]")
|
||||||
|
{
|
||||||
|
parser parser(R"(
|
||||||
|
S <- M('hello' / 'Hello', 'world' / 'World')
|
||||||
|
M(arg0, arg1) <- arg0 [ \t]+ arg1
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE(parser.parse("Hello world"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Macro recursive", "[macro]")
|
||||||
|
{
|
||||||
|
parser parser(R"(
|
||||||
|
S <- M('abc')
|
||||||
|
M(s) <- !s / s ' ' M(s / '123') / s
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE(parser.parse(""));
|
||||||
|
REQUIRE(parser.parse("abc"));
|
||||||
|
REQUIRE(parser.parse("abc abc"));
|
||||||
|
REQUIRE(parser.parse("abc 123 abc"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Macro recursive2", "[macro]")
|
||||||
|
{
|
||||||
|
auto syntaxes = std::vector<const char*>{
|
||||||
|
"S <- M('abc') M(s) <- !s / s ' ' M(s* '-' '123') / s",
|
||||||
|
"S <- M('abc') M(s) <- !s / s ' ' M(s+ '-' '123') / s",
|
||||||
|
"S <- M('abc') M(s) <- !s / s ' ' M(s? '-' '123') / s",
|
||||||
|
"S <- M('abc') M(s) <- !s / s ' ' M(&s s+ '-' '123') / s",
|
||||||
|
"S <- M('abc') M(s) <- !s / s ' ' M(s '-' !s '123') / s",
|
||||||
|
"S <- M('abc') M(s) <- !s / s ' ' M(< s > '-' '123') / s",
|
||||||
|
"S <- M('abc') M(s) <- !s / s ' ' M(~s '-' '123') / s",
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& syntax: syntaxes) {
|
||||||
|
parser parser(syntax);
|
||||||
|
REQUIRE(parser.parse("abc abc-123"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Macro exclusive modifiers", "[macro]")
|
||||||
|
{
|
||||||
|
parser parser(R"(
|
||||||
|
S <- Modifiers(!"") _
|
||||||
|
Modifiers(Appeared) <- (!Appeared) (
|
||||||
|
Token('public') Modifiers(Appeared / 'public') /
|
||||||
|
Token('static') Modifiers(Appeared / 'static') /
|
||||||
|
Token('final') Modifiers(Appeared / 'final') /
|
||||||
|
"")
|
||||||
|
Token(t) <- t _
|
||||||
|
_ <- [ \t\r\n]*
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE(parser.parse("public"));
|
||||||
|
REQUIRE(parser.parse("static"));
|
||||||
|
REQUIRE(parser.parse("final"));
|
||||||
|
REQUIRE(parser.parse("public static final"));
|
||||||
|
REQUIRE(!parser.parse("public public"));
|
||||||
|
REQUIRE(!parser.parse("public static public"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Macro token check test", "[macro]")
|
||||||
|
{
|
||||||
|
parser parser(R"(
|
||||||
|
# Grammar for simple calculator...
|
||||||
|
EXPRESSION <- _ LIST(TERM, TERM_OPERATOR)
|
||||||
|
TERM <- LIST(FACTOR, FACTOR_OPERATOR)
|
||||||
|
FACTOR <- NUMBER / T('(') EXPRESSION T(')')
|
||||||
|
TERM_OPERATOR <- T([-+])
|
||||||
|
FACTOR_OPERATOR <- T([/*])
|
||||||
|
NUMBER <- T([0-9]+)
|
||||||
|
~_ <- [ \t]*
|
||||||
|
LIST(I, D) <- I (D I)*
|
||||||
|
T(S) <- < S > _
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE(parser["EXPRESSION"].is_token == false);
|
||||||
|
REQUIRE(parser["TERM"].is_token == false);
|
||||||
|
REQUIRE(parser["FACTOR"].is_token == false);
|
||||||
|
REQUIRE(parser["FACTOR_OPERATOR"].is_token == true);
|
||||||
|
REQUIRE(parser["NUMBER"].is_token == true);
|
||||||
|
REQUIRE(parser["_"].is_token == true);
|
||||||
|
REQUIRE(parser["LIST"].is_token == false);
|
||||||
|
REQUIRE(parser["T"].is_token == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("Line information test", "[line information]")
|
TEST_CASE("Line information test", "[line information]")
|
||||||
@ -1006,6 +1208,8 @@ TEST_CASE("PEG Definition", "[peg]")
|
|||||||
REQUIRE(exact(g, "Definition", " ") == false);
|
REQUIRE(exact(g, "Definition", " ") == false);
|
||||||
REQUIRE(exact(g, "Definition", "") == false);
|
REQUIRE(exact(g, "Definition", "") == false);
|
||||||
REQUIRE(exact(g, "Definition", "Definition = a / (b c) / d ") == false);
|
REQUIRE(exact(g, "Definition", "Definition = a / (b c) / d ") == false);
|
||||||
|
REQUIRE(exact(g, "Definition", "Macro(param) <- a ") == true);
|
||||||
|
REQUIRE(exact(g, "Definition", "Macro (param) <- a ") == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("PEG Expression", "[peg]")
|
TEST_CASE("PEG Expression", "[peg]")
|
||||||
|
Loading…
Reference in New Issue
Block a user