mirror of
https://github.com/yhirose/cpp-peglib.git
synced 2024-12-22 20:05:31 +00:00
Added error recovery feature
This commit is contained in:
parent
befdd27075
commit
8dc6a287f8
@ -30,11 +30,20 @@ codeAstOptimized.renderer.$cursorLayer.element.style.opacity=0;
|
|||||||
$('#opt_mode').val(localStorage.getItem('optimazationMode') || 'all');
|
$('#opt_mode').val(localStorage.getItem('optimazationMode') || 'all');
|
||||||
$('#opt_rules').val(localStorage.getItem('optimazationRules'));
|
$('#opt_rules').val(localStorage.getItem('optimazationRules'));
|
||||||
|
|
||||||
|
function escapeHtml(unsafe) {
|
||||||
|
return unsafe
|
||||||
|
.replace(/&/g, "&")
|
||||||
|
.replace(/</g, "<")
|
||||||
|
.replace(/>/g, ">")
|
||||||
|
.replace(/"/g, """)
|
||||||
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
function generateErrorListHTML(errors) {
|
function generateErrorListHTML(errors) {
|
||||||
let html = '<ul>';
|
let html = '<ul>';
|
||||||
|
|
||||||
html += $.map(errors, function (x) {
|
html += $.map(errors, function (x) {
|
||||||
return '<li data-ln="' + x.ln + '" data-col="' + x.col + '"><span>' + x.ln + ':' + x.col + '</span> <span>' + x.msg + '</span></li>';
|
return '<li data-ln="' + x.ln + '" data-col="' + x.col + '"><span>' + x.ln + ':' + x.col + '</span> <span>' + escapeHtml(x.msg) + '</span></li>';
|
||||||
}).join('');
|
}).join('');
|
||||||
|
|
||||||
html += '<ul>';
|
html += '<ul>';
|
||||||
|
File diff suppressed because one or more lines are too long
BIN
docs/native.wasm
BIN
docs/native.wasm
Binary file not shown.
@ -129,7 +129,7 @@ int main(int argc, const char **argv) {
|
|||||||
if (opt_trace) {
|
if (opt_trace) {
|
||||||
size_t prev_pos = 0;
|
size_t prev_pos = 0;
|
||||||
parser.enable_trace(
|
parser.enable_trace(
|
||||||
[&](const char *name, const char *s, size_t /*n*/,
|
[&](const peg::Ope &ope, const char *s, size_t /*n*/,
|
||||||
const peg::SemanticValues & /*sv*/, const peg::Context &c,
|
const peg::SemanticValues & /*sv*/, const peg::Context &c,
|
||||||
const std::any & /*dt*/) {
|
const std::any & /*dt*/) {
|
||||||
auto pos = static_cast<size_t>(s - c.s);
|
auto pos = static_cast<size_t>(s - c.s);
|
||||||
@ -139,11 +139,18 @@ int main(int argc, const char **argv) {
|
|||||||
while (level--) {
|
while (level--) {
|
||||||
indent += "│";
|
indent += "│";
|
||||||
}
|
}
|
||||||
|
std::string name;
|
||||||
|
{
|
||||||
|
name = peg::TraceOpeName::get(const_cast<peg::Ope &>(ope));
|
||||||
|
|
||||||
|
auto lit = dynamic_cast<const peg::LiteralString *>(&ope);
|
||||||
|
if (lit) { name += " '" + lit->lit_ + "'"; }
|
||||||
|
}
|
||||||
std::cout << "E " << pos << backtrack << "\t" << indent << "┌" << name
|
std::cout << "E " << pos << backtrack << "\t" << indent << "┌" << name
|
||||||
<< " #" << c.trace_ids.back() << std::endl;
|
<< " #" << c.trace_ids.back() << std::endl;
|
||||||
prev_pos = static_cast<size_t>(pos);
|
prev_pos = static_cast<size_t>(pos);
|
||||||
},
|
},
|
||||||
[&](const char *name, const char *s, size_t /*n*/,
|
[&](const peg::Ope &ope, const char *s, size_t /*n*/,
|
||||||
const peg::SemanticValues &sv, const peg::Context &c,
|
const peg::SemanticValues &sv, const peg::Context &c,
|
||||||
const std::any & /*dt*/, size_t len) {
|
const std::any & /*dt*/, size_t len) {
|
||||||
auto pos = static_cast<size_t>(s - c.s);
|
auto pos = static_cast<size_t>(s - c.s);
|
||||||
@ -154,18 +161,24 @@ int main(int argc, const char **argv) {
|
|||||||
indent += "│";
|
indent += "│";
|
||||||
}
|
}
|
||||||
auto ret = len != static_cast<size_t>(-1) ? "└o " : "└x ";
|
auto ret = len != static_cast<size_t>(-1) ? "└o " : "└x ";
|
||||||
|
auto name = peg::TraceOpeName::get(const_cast<peg::Ope &>(ope));
|
||||||
std::stringstream choice;
|
std::stringstream choice;
|
||||||
if (sv.choice_count() > 0) {
|
if (sv.choice_count() > 0) {
|
||||||
choice << " " << sv.choice() << "/" << sv.choice_count();
|
choice << " " << sv.choice() << "/" << sv.choice_count();
|
||||||
}
|
}
|
||||||
std::string token;
|
std::string token;
|
||||||
if (!sv.tokens.empty()) {
|
if (!sv.tokens.empty()) {
|
||||||
token += " '";
|
token += ", token '";
|
||||||
token += sv.tokens[0];
|
token += sv.tokens[0];
|
||||||
token += "'";
|
token += "'";
|
||||||
}
|
}
|
||||||
|
std::string matched;
|
||||||
|
if (peg::success(len) &&
|
||||||
|
peg::TokenChecker::is_token(const_cast<peg::Ope &>(ope))) {
|
||||||
|
matched = ", match '" + peg::escape_characters(s, len) + "'";
|
||||||
|
}
|
||||||
std::cout << "L " << pos << "\t" << indent << ret << name << " #"
|
std::cout << "L " << pos << "\t" << indent << ret << name << " #"
|
||||||
<< c.trace_ids.back() << choice.str() << token << std::endl;
|
<< c.trace_ids.back() << choice.str() << token << matched << std::endl;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,19 +186,19 @@ int main(int argc, const char **argv) {
|
|||||||
parser.enable_ast();
|
parser.enable_ast();
|
||||||
|
|
||||||
std::shared_ptr<peg::Ast> ast;
|
std::shared_ptr<peg::Ast> ast;
|
||||||
if (!parser.parse_n(source.data(), source.size(), ast)) { return -1; }
|
auto ret = parser.parse_n(source.data(), source.size(), ast);
|
||||||
|
|
||||||
|
if (ast) {
|
||||||
if (opt_optimize) {
|
if (opt_optimize) {
|
||||||
ast = peg::AstOptimizer(opt_mode, opt_rules).optimize(ast);
|
ast = peg::AstOptimizer(opt_mode, opt_rules).optimize(ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << peg::ast_to_s(ast);
|
std::cout << peg::ast_to_s(ast);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ret) { return -1; }
|
||||||
} else {
|
} else {
|
||||||
if (!parser.parse_n(source.data(), source.size())) { return -1; }
|
if (!parser.parse_n(source.data(), source.size())) { return -1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: et ts=4 sw=4 cin cino={1s ff=unix
|
|
||||||
|
@ -934,5 +934,3 @@ TEST_CASE("Negated Class test", "[general]") {
|
|||||||
REQUIRE_FALSE(parser.parse("ABCZ_"));
|
REQUIRE_FALSE(parser.parse("ABCZ_"));
|
||||||
REQUIRE_FALSE(parser.parse(""));
|
REQUIRE_FALSE(parser.parse(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: et ts=4 sw=4 cin cino={1s ff=unix
|
|
||||||
|
144
test/test2.cc
144
test/test2.cc
@ -1,5 +1,6 @@
|
|||||||
#include "catch.hh"
|
#include "catch.hh"
|
||||||
#include <peglib.h>
|
#include <peglib.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
using namespace peg;
|
using namespace peg;
|
||||||
|
|
||||||
@ -218,6 +219,8 @@ TEST_CASE("Precedence climbing", "[precedence]") {
|
|||||||
T(S) <- < S > _
|
T(S) <- < S > _
|
||||||
)");
|
)");
|
||||||
|
|
||||||
|
REQUIRE(!!parser); // OK
|
||||||
|
|
||||||
parser.enable_packrat_parsing();
|
parser.enable_packrat_parsing();
|
||||||
|
|
||||||
// Setup actions
|
// Setup actions
|
||||||
@ -1072,4 +1075,143 @@ TEST_CASE("Dictionary invalid", "[dic]") {
|
|||||||
REQUIRE_FALSE(ret);
|
REQUIRE_FALSE(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: et ts=4 sw=4 cin cino={1s ff=unix
|
TEST_CASE("Error recovery 1", "[error]") {
|
||||||
|
parser pg(R"(
|
||||||
|
START <- __? SECTION*
|
||||||
|
|
||||||
|
SECTION <- HEADER __ ENTRIES __?
|
||||||
|
|
||||||
|
HEADER <- '[' _ CATEGORY (':' _ ATTRIBUTES)? ']'
|
||||||
|
|
||||||
|
CATEGORY <- < [-_a-zA-Z0-9 ]+ > _
|
||||||
|
ATTRIBUTES <- ATTRIBUTE (',' _ ATTRIBUTE)*
|
||||||
|
ATTRIBUTE <- < [-_a-zA-Z0-9]+ > _
|
||||||
|
|
||||||
|
ENTRIES <- (ENTRY (__ ENTRY)*)?
|
||||||
|
|
||||||
|
ENTRY <- ONE_WAY PHRASE ('|' _ PHRASE)*
|
||||||
|
/ PHRASE ('|' _ PHRASE)
|
||||||
|
/ %recover_to(__ / HEADER)
|
||||||
|
|
||||||
|
ONE_WAY <- PHRASE '=' _
|
||||||
|
PHRASE <- WORD (' ' WORD)* _
|
||||||
|
WORD <- < (![ \t\r\n=|[#] .)+ >
|
||||||
|
|
||||||
|
~__ <- _ (comment? nl _)+
|
||||||
|
~_ <- [ \t]*
|
||||||
|
|
||||||
|
comment <- ('#' (!nl .)*)
|
||||||
|
nl <- '\r'? '\n'
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE(!!pg); // OK
|
||||||
|
|
||||||
|
std::vector<std::string> errors{
|
||||||
|
R"(2:6: syntax error, unexpected '|', expecting <WORD>.)",
|
||||||
|
R"(4:4: syntax error, unexpected '\n', expecting <WORD>.)",
|
||||||
|
R"(8:4: syntax error, unexpected '\n', expecting <WORD>.)"
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
pg.log = [&](size_t ln, size_t col, const std::string &msg) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << ln << ":" << col << ": " << msg;
|
||||||
|
REQUIRE(ss.str() == errors[i++]);
|
||||||
|
};
|
||||||
|
|
||||||
|
pg.enable_ast();
|
||||||
|
|
||||||
|
std::shared_ptr<Ast> ast;
|
||||||
|
REQUIRE_FALSE(pg.parse(R"([Section1]
|
||||||
|
aaa || bbb
|
||||||
|
ccc = ddd
|
||||||
|
eee
|
||||||
|
|
||||||
|
[Section2]
|
||||||
|
|
||||||
|
fff
|
||||||
|
|
||||||
|
ggg hhh | iii
|
||||||
|
|
||||||
|
)", ast));
|
||||||
|
|
||||||
|
ast = peg::AstOptimizer(true, {"ENTRIES"}).optimize(ast);
|
||||||
|
|
||||||
|
REQUIRE(ast_to_s(ast) ==
|
||||||
|
R"(+ START
|
||||||
|
+ SECTION
|
||||||
|
- HEADER/0[CATEGORY] (Section1)
|
||||||
|
+ ENTRIES
|
||||||
|
+ ENTRY/2
|
||||||
|
+ ENTRY/0
|
||||||
|
- ONE_WAY/0[WORD] (ccc)
|
||||||
|
- PHRASE/0[WORD] (ddd)
|
||||||
|
+ ENTRY/2
|
||||||
|
+ SECTION
|
||||||
|
- HEADER/0[CATEGORY] (Section2)
|
||||||
|
+ ENTRIES
|
||||||
|
+ ENTRY/2
|
||||||
|
+ ENTRY/1
|
||||||
|
+ PHRASE
|
||||||
|
- WORD (ggg)
|
||||||
|
- WORD (hhh)
|
||||||
|
- PHRASE/0[WORD] (iii)
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Error recovery 2", "[error]") {
|
||||||
|
parser pg(R"(
|
||||||
|
START <- ENTRY ((',' ENTRY) / %recover_to(',' / Space))* (_ / %recover_to('!.'))
|
||||||
|
ENTRY <- '[' ITEM (',' ITEM)* ']'
|
||||||
|
ITEM <- WORD / NUM / %recover_to(',' / ']')
|
||||||
|
NUM <- [0-9]+ ![a-z]
|
||||||
|
WORD <- '"' [a-z]+ '"'
|
||||||
|
|
||||||
|
~_ <- Space+
|
||||||
|
Space <- [ \n]
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE(!!pg); // OK
|
||||||
|
|
||||||
|
std::vector<std::string> errors{
|
||||||
|
R"(1:6: syntax error, unexpected '],['.)",
|
||||||
|
R"(1:18: syntax error, unexpected 'z', expecting <NUM>.)",
|
||||||
|
R"(1:24: syntax error, unexpected ',"', expecting <WORD>.)",
|
||||||
|
R"(1:31: syntax error, unexpected 'ccc', expecting <NUM>.)",
|
||||||
|
R"(1:38: syntax error, unexpected 'ddd', expecting <NUM>.)",
|
||||||
|
R"(1:55: syntax error, unexpected '],[', expecting <WORD>.)",
|
||||||
|
R"(1:58: syntax error, unexpected '\n', expecting <NUM>.)",
|
||||||
|
R"(1:56: syntax error, unexpected ',[', expecting <Space>.)",
|
||||||
|
};
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
pg.log = [&](size_t ln, size_t col, const std::string &msg) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << ln << ":" << col << ": " << msg;
|
||||||
|
REQUIRE(ss.str() == errors[i++]);
|
||||||
|
};
|
||||||
|
|
||||||
|
pg.enable_ast();
|
||||||
|
|
||||||
|
std::shared_ptr<Ast> ast;
|
||||||
|
REQUIRE_FALSE(pg.parse(R"([000]],[111],[222z,"aaa,"bbb",ccc"],[ddd",444,555,"eee],[
|
||||||
|
)", ast));
|
||||||
|
|
||||||
|
ast = peg::AstOptimizer(true, {"ENTRIES"}).optimize(ast);
|
||||||
|
|
||||||
|
REQUIRE(ast_to_s(ast) ==
|
||||||
|
R"(+ START
|
||||||
|
- ENTRY/0[NUM] (000)
|
||||||
|
- ENTRY/0[NUM] (111)
|
||||||
|
+ ENTRY
|
||||||
|
+ ITEM/2
|
||||||
|
+ ITEM/2
|
||||||
|
- ITEM/0[WORD] ("bbb")
|
||||||
|
+ ITEM/2
|
||||||
|
+ ENTRY
|
||||||
|
+ ITEM/2
|
||||||
|
- ITEM/1[NUM] (444)
|
||||||
|
- ITEM/1[NUM] (555)
|
||||||
|
+ ITEM/2
|
||||||
|
)");
|
||||||
|
}
|
||||||
|
@ -300,5 +300,3 @@ TEST_CASE("PEG EndOfFile", "[peg]")
|
|||||||
REQUIRE(exact(g, "EndOfFile", "") == true);
|
REQUIRE(exact(g, "EndOfFile", "") == true);
|
||||||
REQUIRE(exact(g, "EndOfFile", " ") == false);
|
REQUIRE(exact(g, "EndOfFile", " ") == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: et ts=4 sw=4 cin cino={1s ff=unix
|
|
||||||
|
Loading…
Reference in New Issue
Block a user