Added error handling.

This commit is contained in:
yhirose 2015-02-11 23:57:00 -05:00
parent 97641ed257
commit 45024d3aaa

126
peglib.h
View File

@ -13,10 +13,10 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
#include <map> #include <map>
#include <set>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <initializer_list> #include <initializer_list>
#include <iostream>
namespace peglib { namespace peglib {
@ -352,18 +352,19 @@ private:
*/ */
struct Match struct Match
{ {
Match(bool _ret, size_t _len, size_t _id) : ret(_ret), len(_len), id(_id) {}
bool ret; bool ret;
size_t len; size_t len;
size_t id; size_t choice;
const char* ptr;
const std::string msg;
}; };
Match success(size_t len, size_t id = 0) { Match success(size_t len, size_t choice = 0) {
return Match(true, len, id); return Match{ true, len, choice, nullptr, std::string() };
} }
Match fail() { Match fail(const char* ptr, std::string msg = std::string()) {
return Match(false, 0, -1); return Match{ false, 0, (size_t)-1, ptr, msg };
} }
/* /*
@ -411,7 +412,7 @@ public:
for (const auto& ope : opes_) { for (const auto& ope : opes_) {
auto m = ope->parse(s + i, l - i, v); auto m = ope->parse(s + i, l - i, v);
if (!m.ret) { if (!m.ret) {
return fail(); return fail(m.ptr, m.msg);
} }
i += m.len; i += m.len;
} }
@ -462,7 +463,7 @@ public:
} }
id++; id++;
} }
return fail(); return fail(s, "no choice candidate was matched");
} }
size_t size() const { return opes_.size(); } size_t size() const { return opes_.size(); }
@ -500,7 +501,7 @@ public:
Match parse_core(const char* s, size_t l, Values& v) const { Match parse_core(const char* s, size_t l, Values& v) const {
auto m = ope_->parse(s, l, v); auto m = ope_->parse(s, l, v);
if (!m.ret) { if (!m.ret) {
return fail(); return fail(m.ptr, m.msg);
} }
auto i = m.len; auto i = m.len;
while (l - i > 0) { while (l - i > 0) {
@ -541,7 +542,7 @@ public:
if (m.ret) { if (m.ret) {
return success(0); return success(0);
} else { } else {
return fail(); return fail(m.ptr, m.msg);
} }
} }
@ -557,7 +558,7 @@ public:
Match parse_core(const char* s, size_t l, Values& v) const { Match parse_core(const char* s, size_t l, Values& v) const {
auto m = ope_->parse(s, l, v); auto m = ope_->parse(s, l, v);
if (m.ret) { if (m.ret) {
return fail(); return fail(s);
} else { } else {
return success(0); return success(0);
} }
@ -576,7 +577,7 @@ public:
auto i = 0u; auto i = 0u;
for (; i < lit_.size(); i++) { for (; i < lit_.size(); i++) {
if (i >= l || s[i] != lit_[i]) { if (i >= l || s[i] != lit_[i]) {
return fail(); return fail(s);
} }
} }
return success(i); return success(i);
@ -594,7 +595,7 @@ public:
Match parse_core(const char* s, size_t l, Values& v) const { Match parse_core(const char* s, size_t l, Values& v) const {
// TODO: UTF8 support // TODO: UTF8 support
if (l < 1) { if (l < 1) {
return fail(); return fail(s);
} }
auto ch = s[0]; auto ch = s[0];
auto i = 0u; auto i = 0u;
@ -611,7 +612,7 @@ public:
i += 1; i += 1;
} }
} }
return fail(); return fail(s);
} }
private: private:
@ -626,7 +627,7 @@ public:
Match parse_core(const char* s, size_t l, Values& v) const { Match parse_core(const char* s, size_t l, Values& v) const {
// TODO: UTF8 support // TODO: UTF8 support
if (l < 1 || s[0] != ch_) { if (l < 1 || s[0] != ch_) {
return fail(); return fail(s);
} }
return success(1); return success(1);
} }
@ -641,7 +642,7 @@ public:
Match parse_core(const char* s, size_t l, Values& v) const { Match parse_core(const char* s, size_t l, Values& v) const {
// TODO: UTF8 support // TODO: UTF8 support
if (l < 1) { if (l < 1) {
return fail(); return fail(s);
} }
return success(1); return success(1);
} }
@ -730,6 +731,11 @@ public:
return *this; return *this;
} }
Match parse_with_match(const char* s, size_t l) const {
Values v;
return holder_->parse(s, l, v);
}
template <typename T> template <typename T>
bool parse(const char* s, size_t l, T& val) const { bool parse(const char* s, size_t l, T& val) const {
Values v; Values v;
@ -800,7 +806,7 @@ private:
assert(!outer_->actions.empty()); assert(!outer_->actions.empty());
auto id = m.id + 1; auto id = m.choice + 1;
const auto& ac = (id < outer_->actions.size() && outer_->actions[id]) const auto& ac = (id < outer_->actions.size() && outer_->actions[id])
? outer_->actions[id] ? outer_->actions[id]
: outer_->actions[0]; : outer_->actions[0];
@ -979,7 +985,27 @@ inline Grammar make_peg_grammar()
return g; return g;
} }
inline std::shared_ptr<Grammar> make_grammar(const char* syntax, std::string& start) inline std::pair<size_t, size_t> find_line(const char* s, const char* ptr) {
auto p = s;
auto col_ptr = p;
auto no = 1;
while (p < ptr) {
if (*p == '\n') {
no++;
col_ptr = p + 1;
}
p++;
}
auto col = p - col_ptr + 1;
return std::make_pair(no, col);
}
inline std::shared_ptr<Grammar> make_grammar(
const char* syntax, size_t syntax_len, std::string& start,
std::function<void (size_t, size_t, const std::string)> log = nullptr)
{ {
Grammar peg = make_peg_grammar(); Grammar peg = make_peg_grammar();
@ -988,7 +1014,7 @@ inline std::shared_ptr<Grammar> make_grammar(const char* syntax, std::string& st
start.clear(); start.clear();
// Setup actions // Setup actions
std::set<std::string> refs; std::map<std::string, const char*> refs;
peg["Definition"] = [&](const std::vector<Any>& v) { peg["Definition"] = [&](const std::vector<Any>& v) {
const auto& name = v[0].get<std::string>(); const auto& name = v[0].get<std::string>();
@ -1051,9 +1077,16 @@ inline std::shared_ptr<Grammar> make_grammar(const char* syntax, std::string& st
}; };
peg["Primary"].actions = { peg["Primary"].actions = {
[&](const std::vector<Any>& v) { return v[0]; }, [&](const std::vector<Any>& v) {
[&](const std::vector<Any>& v) { refs.insert(v[0]); return ref(*grammar, v[0]); }, return v[0];
[&](const std::vector<Any>& v) { return v[1]; } },
[&](const char* s, size_t l, const std::vector<Any>& v) {
refs[v[0]] = s;
return ref(*grammar, v[0]);
},
[&](const std::vector<Any>& v) {
return v[1];
}
}; };
peg["IdentCont"] = [](const char*s, size_t l) { peg["IdentCont"] = [](const char*s, size_t l) {
@ -1081,16 +1114,35 @@ inline std::shared_ptr<Grammar> make_grammar(const char* syntax, std::string& st
return any(); return any();
}; };
if (peg["Grammar"].parse(syntax)) { auto m = peg["Grammar"].parse_with_match(syntax, syntax_len);
for (const auto& name : refs) { if (!m.ret) {
if (grammar->find(name) == grammar->end()) { if (log) {
auto line = find_line(syntax, m.ptr);
log(line.first, line.second, m.msg.empty() ? "syntax error" : m.msg);
}
return nullptr; return nullptr;
} }
}
return grammar;
}
for (const auto& x : refs) {
const auto& name = x.first;
auto ptr = x.second;
if (grammar->find(name) == grammar->end()) {
if (log) {
auto line = find_line(syntax, ptr);
log(line.first, line.second, "'" + name + "' is not defined.");
}
return nullptr; return nullptr;
}
}
return grammar;
}
inline std::shared_ptr<Grammar> make_grammar(
const char* syntax, std::string& start,
std::function<void (size_t, size_t, const std::string)> log = nullptr)
{
return make_grammar(syntax, strlen(syntax), start, log);
} }
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
@ -1104,8 +1156,8 @@ public:
return grammar_ != nullptr; return grammar_ != nullptr;
} }
bool load_syntax(const char* syntax) { bool load_syntax(const char* s, size_t l, std::function<void (size_t, size_t, const std::string&)> log) {
grammar_ = make_grammar(syntax, start_); grammar_ = make_grammar(s, l, start_, log);
return grammar_ != nullptr; return grammar_ != nullptr;
} }
@ -1145,11 +1197,15 @@ private:
std::string start_; std::string start_;
}; };
inline Parser make_parser(const char* syntax) { inline Parser make_parser(const char* s, size_t l, std::function<void (size_t, size_t, const std::string&)> log = nullptr) {
Parser parser; Parser parser;
if (!parser.load_syntax(syntax)) { parser.load_syntax(s, l, log);
throw std::logic_error("PEG syntax error."); return parser;
} }
inline Parser make_parser(const char* s, std::function<void (size_t, size_t, const std::string&)> log = nullptr) {
Parser parser;
parser.load_syntax(s, strlen(s), log);
return parser; return parser;
} }