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) {
// (2) Make a parser
auto grammar = R"(
parser parser(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);
assert((bool)parser == true);
// (3) Setup actions
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:
```cpp
@ -326,6 +341,46 @@ List(I, D) ← I (D I)*
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
--------------

View File

@ -17,3 +17,9 @@ target_link_libraries(calc2 ${add_link_deps})
add_executable(calc3 calc3.cc)
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 <assert.h>
#include <iostream>
#include <cstdlib>
using namespace peg;
using namespace std;
int main(int argc, const char** argv)
{
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;
};
int main(void) {
// (2) Make a parser
parser parser(R"(
EXPRESSION <- _ TERM (TERM_OPERATOR TERM)*
TERM <- FACTOR (FACTOR_OPERATOR FACTOR)*
FACTOR <- NUMBER / '(' _ EXPRESSION ')' _
TERM_OPERATOR <- < [-+] > _
FACTOR_OPERATOR <- < [/*] > _
NUMBER <- < [0-9]+ > _
~_ <- [ \t\r\n]*
# Grammar for Calculator...
Additive <- Multitive '+' Additive / Multitive
Multitive <- Primary '*' Multitive / Primary
Primary <- '(' Additive ')' / Number
Number <- < [0-9]+ >
%whitespace <- [ \t]*
)");
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()); };
assert((bool)parser == true);
auto expr = argv[1];
long val = 0;
if (parser.parse(expr, val)) {
std::cout << expr << " = " << val << std::endl;
return 0;
}
// (3) Setup actions
parser["Additive"] = [](const SemanticValues& sv) {
switch (sv.choice()) {
case 0: // "Multitive '+' Additive"
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
auto reduce = [](const SemanticValues& sv) -> long {
parser["EXPRESSION"] = [](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]);
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;
@ -234,10 +234,8 @@ TEST_CASE("Precedence climbing", "[precedence]")
}
return result;
};
parser["EXPRESSION"] = reduce;
parser["OPERATOR"] = [](const SemanticValues& sv) { return static_cast<char>(*sv.c_str()); };
parser["NUMBER"] = [](const SemanticValues& sv) { return atol(sv.c_str()); };
parser["OPERATOR"] = [](const SemanticValues& sv) { return *sv.c_str(); };
parser["NUMBER"] = [](const SemanticValues& sv) { return atol(sv.c_str()); };
bool ret = parser;
REQUIRE(ret == true);