mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2024-12-22 20:05:31 +00:00
Updated README and examples
This commit is contained in:
parent
038bc06343
commit
9b89b19949
75
README.md
75
README.md
@ -42,23 +42,16 @@ using namespace std;
|
|||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
// (2) Make a parser
|
// (2) Make a parser
|
||||||
auto grammar = R"(
|
parser parser(R"(
|
||||||
# Grammar for Calculator...
|
# Grammar for Calculator...
|
||||||
Additive <- Multitive '+' Additive / Multitive
|
Additive <- Multitive '+' Additive / Multitive
|
||||||
Multitive <- Primary '*' Multitive / Primary
|
Multitive <- Primary '*' Multitive / Primary
|
||||||
Primary <- '(' Additive ')' / Number
|
Primary <- '(' Additive ')' / Number
|
||||||
Number <- < [0-9]+ >
|
Number <- < [0-9]+ >
|
||||||
%whitespace <- [ \t]*
|
%whitespace <- [ \t]*
|
||||||
)";
|
)");
|
||||||
|
|
||||||
parser parser;
|
assert((bool)parser == true);
|
||||||
|
|
||||||
parser.log = [](size_t line, size_t col, const string& msg) {
|
|
||||||
cerr << line << ":" << col << ": " << msg << "\n";
|
|
||||||
};
|
|
||||||
|
|
||||||
auto ok = parser.load_grammar(grammar);
|
|
||||||
assert(ok);
|
|
||||||
|
|
||||||
// (3) Setup actions
|
// (3) Setup actions
|
||||||
parser["Additive"] = [](const SemanticValues& sv) {
|
parser["Additive"] = [](const SemanticValues& sv) {
|
||||||
@ -93,6 +86,28 @@ int main(void) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To show syntax errors in grammar text:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
auto grammar = R"(
|
||||||
|
# Grammar for Calculator...
|
||||||
|
Additive <- Multitive '+' Additive / Multitive
|
||||||
|
Multitive <- Primary '*' Multitive / Primary
|
||||||
|
Primary <- '(' Additive ')' / Number
|
||||||
|
Number <- < [0-9]+ >
|
||||||
|
%whitespace <- [ \t]*
|
||||||
|
)";
|
||||||
|
|
||||||
|
parser parser;
|
||||||
|
|
||||||
|
parser.log = [](size_t line, size_t col, const string& msg) {
|
||||||
|
cerr << line << ":" << col << ": " << msg << "\n";
|
||||||
|
};
|
||||||
|
|
||||||
|
auto ok = parser.load_grammar(grammar);
|
||||||
|
assert(ok);
|
||||||
|
```
|
||||||
|
|
||||||
There are four semantic actions available:
|
There are four semantic actions available:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@ -326,6 +341,46 @@ List(I, D) ← I (D I)*
|
|||||||
T(x) ← < x > _
|
T(x) ← < x > _
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Parsing expressions by precedence climbing altorithm
|
||||||
|
----------------------------------------------------
|
||||||
|
|
||||||
|
*cpp-peglib* supports [operator-precedence parsering](https://en.wikipedia.org/wiki/Operator-precedence_parser) by [**precedence climbing algorithm**](https://eli.thegreenplace.net/2012/08/02/parsing-expressions-by-precedence-climbing)
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
parser parser(R"(
|
||||||
|
EXPRESSION <- ATOM (OPERATOR ATOM)* {
|
||||||
|
precedence
|
||||||
|
L - +
|
||||||
|
L / *
|
||||||
|
}
|
||||||
|
ATOM <- NUMBER / '(' EXPRESSION ')'
|
||||||
|
OPERATOR <- < [-+/*] >
|
||||||
|
NUMBER <- < '-'? [0-9]+ >
|
||||||
|
%whitespace <- [ \t\r\n]*
|
||||||
|
)");
|
||||||
|
|
||||||
|
parser["EXPRESSION"] = [](const SemanticValues& sv) -> long {
|
||||||
|
auto result = any_cast<long>(sv[0]);
|
||||||
|
if (sv.size() > 1) {
|
||||||
|
auto ope = any_cast<char>(sv[1]);
|
||||||
|
auto num = any_cast<long>(sv[2]);
|
||||||
|
switch (ope) {
|
||||||
|
case '+': result += num; break;
|
||||||
|
case '-': result -= num; break;
|
||||||
|
case '*': result *= num; break;
|
||||||
|
case '/': result /= num; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
parser["OPERATOR"] = [](const SemanticValues& sv) { return *sv.c_str(); };
|
||||||
|
parser["NUMBER"] = [](const SemanticValues& sv) { return atol(sv.c_str()); };
|
||||||
|
|
||||||
|
long val;
|
||||||
|
parser.parse(" -1 + (1 + 2) * 3 - -1", val);
|
||||||
|
assert(val == 9);
|
||||||
|
```
|
||||||
|
|
||||||
AST generation
|
AST generation
|
||||||
--------------
|
--------------
|
||||||
|
|
||||||
|
@ -17,3 +17,9 @@ target_link_libraries(calc2 ${add_link_deps})
|
|||||||
|
|
||||||
add_executable(calc3 calc3.cc)
|
add_executable(calc3 calc3.cc)
|
||||||
target_link_libraries(calc3 ${add_link_deps})
|
target_link_libraries(calc3 ${add_link_deps})
|
||||||
|
|
||||||
|
add_executable(calc4 calc4.cc)
|
||||||
|
target_link_libraries(calc4 ${add_link_deps})
|
||||||
|
|
||||||
|
add_executable(calc5 calc5.cc)
|
||||||
|
target_link_libraries(calc5 ${add_link_deps})
|
||||||
|
@ -1,64 +1,51 @@
|
|||||||
//
|
|
||||||
// calc.cc
|
|
||||||
//
|
|
||||||
// Copyright (c) 2015 Yuji Hirose. All rights reserved.
|
|
||||||
// MIT License
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <peglib.h>
|
#include <peglib.h>
|
||||||
|
#include <assert.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
using namespace peg;
|
using namespace peg;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
int main(int argc, const char** argv)
|
int main(void) {
|
||||||
{
|
// (2) Make a parser
|
||||||
if (argc < 2 || std::string("--help") == argv[1]) {
|
|
||||||
std::cout << "usage: calc [formula]" << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto reduce = [](const SemanticValues& sv) -> long {
|
|
||||||
auto result = any_cast<long>(sv[0]);
|
|
||||||
for (auto i = 1u; i < sv.size(); i += 2) {
|
|
||||||
auto num = any_cast<long>(sv[i + 1]);
|
|
||||||
auto ope = any_cast<char>(sv[i]);
|
|
||||||
switch (ope) {
|
|
||||||
case '+': result += num; break;
|
|
||||||
case '-': result -= num; break;
|
|
||||||
case '*': result *= num; break;
|
|
||||||
case '/': result /= num; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
parser parser(R"(
|
parser parser(R"(
|
||||||
EXPRESSION <- _ TERM (TERM_OPERATOR TERM)*
|
# Grammar for Calculator...
|
||||||
TERM <- FACTOR (FACTOR_OPERATOR FACTOR)*
|
Additive <- Multitive '+' Additive / Multitive
|
||||||
FACTOR <- NUMBER / '(' _ EXPRESSION ')' _
|
Multitive <- Primary '*' Multitive / Primary
|
||||||
TERM_OPERATOR <- < [-+] > _
|
Primary <- '(' Additive ')' / Number
|
||||||
FACTOR_OPERATOR <- < [/*] > _
|
Number <- < [0-9]+ >
|
||||||
NUMBER <- < [0-9]+ > _
|
%whitespace <- [ \t]*
|
||||||
~_ <- [ \t\r\n]*
|
|
||||||
)");
|
)");
|
||||||
|
|
||||||
parser["EXPRESSION"] = reduce;
|
assert((bool)parser == true);
|
||||||
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()); };
|
|
||||||
|
|
||||||
auto expr = argv[1];
|
// (3) Setup actions
|
||||||
long val = 0;
|
parser["Additive"] = [](const SemanticValues& sv) {
|
||||||
if (parser.parse(expr, val)) {
|
switch (sv.choice()) {
|
||||||
std::cout << expr << " = " << val << std::endl;
|
case 0: // "Multitive '+' Additive"
|
||||||
return 0;
|
return any_cast<int>(sv[0]) + any_cast<int>(sv[1]);
|
||||||
}
|
default: // "Multitive"
|
||||||
|
return any_cast<int>(sv[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
std::cout << "syntax error..." << std::endl;
|
parser["Multitive"] = [](const SemanticValues& sv) {
|
||||||
|
switch (sv.choice()) {
|
||||||
|
case 0: // "Primary '*' Multitive"
|
||||||
|
return any_cast<int>(sv[0]) * any_cast<int>(sv[1]);
|
||||||
|
default: // "Primary"
|
||||||
|
return any_cast<int>(sv[0]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return -1;
|
parser["Number"] = [](const SemanticValues& sv) {
|
||||||
|
return stoi(sv.token(), nullptr, 10);
|
||||||
|
};
|
||||||
|
|
||||||
|
// (4) Parse
|
||||||
|
parser.enable_packrat_parsing(); // Enable packrat parsing.
|
||||||
|
|
||||||
|
int val;
|
||||||
|
parser.parse(" (1 + 2) * 3 ", val);
|
||||||
|
|
||||||
|
assert(val == 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: et ts=4 sw=4 cin cino={1s ff=unix
|
|
||||||
|
42
example/calc4.cc
Normal file
42
example/calc4.cc
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#include <peglib.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace peg;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
parser parser(R"(
|
||||||
|
EXPRESSION <- ATOM (OPERATOR ATOM)* {
|
||||||
|
precedence
|
||||||
|
L - +
|
||||||
|
L / *
|
||||||
|
}
|
||||||
|
ATOM <- NUMBER / '(' EXPRESSION ')'
|
||||||
|
OPERATOR <- < [-+/*] >
|
||||||
|
NUMBER <- < '-'? [0-9]+ >
|
||||||
|
%whitespace <- [ \t\r\n]*
|
||||||
|
)");
|
||||||
|
|
||||||
|
parser["EXPRESSION"] = [](const SemanticValues& sv) -> long {
|
||||||
|
auto result = any_cast<long>(sv[0]);
|
||||||
|
if (sv.size() > 1) {
|
||||||
|
auto ope = any_cast<char>(sv[1]);
|
||||||
|
auto num = any_cast<long>(sv[2]);
|
||||||
|
switch (ope) {
|
||||||
|
case '+': result += num; break;
|
||||||
|
case '-': result -= num; break;
|
||||||
|
case '*': result *= num; break;
|
||||||
|
case '/': result /= num; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
parser["OPERATOR"] = [](const SemanticValues& sv) { return *sv.c_str(); };
|
||||||
|
parser["NUMBER"] = [](const SemanticValues& sv) { return atol(sv.c_str()); };
|
||||||
|
|
||||||
|
long val;
|
||||||
|
parser.parse(" -1 + (1 + 2) * 3 - -1", val);
|
||||||
|
|
||||||
|
assert(val == 9);
|
||||||
|
}
|
69
example/calc5.cc
Normal file
69
example/calc5.cc
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
//
|
||||||
|
// calc5.cc
|
||||||
|
//
|
||||||
|
// Copyright (c) 2015 Yuji Hirose. All rights reserved.
|
||||||
|
// MIT License
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <peglib.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
using namespace peg;
|
||||||
|
|
||||||
|
int main(int argc, const char** argv)
|
||||||
|
{
|
||||||
|
if (argc < 2 || std::string("--help") == argv[1]) {
|
||||||
|
std::cout << "usage: calc5 [formula]" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::function<long (const Ast&)> eval = [&](const Ast& ast) {
|
||||||
|
if (ast.name == "NUMBER") {
|
||||||
|
return stol(ast.token);
|
||||||
|
} else {
|
||||||
|
const auto& nodes = ast.nodes;
|
||||||
|
auto result = eval(*nodes[0]);
|
||||||
|
if (nodes.size() > 1) {
|
||||||
|
auto ope = nodes[1]->token[0];
|
||||||
|
auto num = eval(*nodes[2]);
|
||||||
|
switch (ope) {
|
||||||
|
case '+': result += num; break;
|
||||||
|
case '-': result -= num; break;
|
||||||
|
case '*': result *= num; break;
|
||||||
|
case '/': result /= num; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
parser parser(R"(
|
||||||
|
EXPRESSION <- ATOM (OPERATOR ATOM)* {
|
||||||
|
precedence
|
||||||
|
L - +
|
||||||
|
L / *
|
||||||
|
}
|
||||||
|
ATOM <- NUMBER / '(' EXPRESSION ')'
|
||||||
|
OPERATOR <- < [-+/*] >
|
||||||
|
NUMBER <- < '-'? [0-9]+ >
|
||||||
|
%whitespace <- [ \t\r\n]*
|
||||||
|
)");
|
||||||
|
|
||||||
|
parser.enable_ast();
|
||||||
|
|
||||||
|
auto expr = argv[1];
|
||||||
|
std::shared_ptr<Ast> ast;
|
||||||
|
if (parser.parse(expr, ast)) {
|
||||||
|
ast = AstOptimizer(true).optimize(ast);
|
||||||
|
std::cout << ast_to_s(ast);
|
||||||
|
std::cout << expr << " = " << eval(*ast) << std::endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "syntax error..." << std::endl;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: et ts=4 sw=4 cin cino={1s ff=unix
|
14
test/test.cc
14
test/test.cc
@ -220,11 +220,11 @@ TEST_CASE("Precedence climbing", "[precedence]")
|
|||||||
)");
|
)");
|
||||||
|
|
||||||
// Setup actions
|
// Setup actions
|
||||||
auto reduce = [](const SemanticValues& sv) -> long {
|
parser["EXPRESSION"] = [](const SemanticValues& sv) -> long {
|
||||||
auto result = any_cast<long>(sv[0]);
|
auto result = any_cast<long>(sv[0]);
|
||||||
for (auto i = 1u; i < sv.size(); i += 2) {
|
if (sv.size() > 1) {
|
||||||
auto num = any_cast<long>(sv[i + 1]);
|
auto ope = any_cast<char>(sv[1]);
|
||||||
auto ope = any_cast<char>(sv[i]);
|
auto num = any_cast<long>(sv[2]);
|
||||||
switch (ope) {
|
switch (ope) {
|
||||||
case '+': result += num; break;
|
case '+': result += num; break;
|
||||||
case '-': result -= num; break;
|
case '-': result -= num; break;
|
||||||
@ -234,10 +234,8 @@ TEST_CASE("Precedence climbing", "[precedence]")
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
parser["OPERATOR"] = [](const SemanticValues& sv) { return *sv.c_str(); };
|
||||||
parser["EXPRESSION"] = reduce;
|
parser["NUMBER"] = [](const SemanticValues& sv) { return atol(sv.c_str()); };
|
||||||
parser["OPERATOR"] = [](const SemanticValues& sv) { return static_cast<char>(*sv.c_str()); };
|
|
||||||
parser["NUMBER"] = [](const SemanticValues& sv) { return atol(sv.c_str()); };
|
|
||||||
|
|
||||||
bool ret = parser;
|
bool ret = parser;
|
||||||
REQUIRE(ret == true);
|
REQUIRE(ret == true);
|
||||||
|
Loading…
Reference in New Issue
Block a user