Updated README and examples

This commit is contained in:
yhirose 2020-02-07 11:55:21 -05:00
parent 038bc06343
commit 9b89b19949
6 changed files with 227 additions and 70 deletions

View File

@ -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
-------------- --------------

View File

@ -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})

View File

@ -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
View 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
View 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

View File

@ -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);