mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2025-01-22 13:25:30 +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<` ... `>` (Named capture 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.
|
||||
|
||||
@ -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
|
||||
```
|
||||
|
||||
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
|
||||
--------------
|
||||
|
||||
|
204
test/test.cc
204
test/test.cc
@ -948,8 +948,210 @@ TEST_CASE("Japanese character", "[unicode]")
|
||||
)");
|
||||
|
||||
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(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]")
|
||||
@ -1006,6 +1208,8 @@ TEST_CASE("PEG Definition", "[peg]")
|
||||
REQUIRE(exact(g, "Definition", " ") == false);
|
||||
REQUIRE(exact(g, "Definition", "") == 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]")
|
||||
|
Loading…
Reference in New Issue
Block a user