|
|
|
@ -1018,178 +1018,201 @@ inline std::pair<size_t, size_t> line_info(const char* s, const char* ptr) { |
|
|
|
|
return std::make_pair(no, col); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline std::string resolve_escape_sequence(const char*s, size_t l) { |
|
|
|
|
std::string r; |
|
|
|
|
r.reserve(l); |
|
|
|
|
for (auto i = 0u; i < l; i++) { |
|
|
|
|
auto ch = s[i]; |
|
|
|
|
if (ch == '\\') { |
|
|
|
|
i++;
|
|
|
|
|
switch (s[i]) { |
|
|
|
|
case 'n': r += '\n'; break; |
|
|
|
|
case 'r': r += '\r'; break; |
|
|
|
|
case 't': r += '\t'; break; |
|
|
|
|
case '\'': r += '\''; break; |
|
|
|
|
case '"': r += '"'; break; |
|
|
|
|
case '[': r += '['; break; |
|
|
|
|
case ']': r += ']'; break; |
|
|
|
|
case '\\': r += '\\'; break; |
|
|
|
|
default: { |
|
|
|
|
// TODO: Octal number support
|
|
|
|
|
assert(false); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
r += ch; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return r; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
class GrammarGenerator |
|
|
|
|
{ |
|
|
|
|
Grammar peg = make_peg_grammar(); |
|
|
|
|
|
|
|
|
|
// Prepare output variables
|
|
|
|
|
auto grammar = std::make_shared<Grammar>(); |
|
|
|
|
start.clear(); |
|
|
|
|
public: |
|
|
|
|
static GrammarGenerator& instance() { |
|
|
|
|
static GrammarGenerator instance; |
|
|
|
|
return instance; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Setup actions
|
|
|
|
|
std::map<std::string, const char*> refs; |
|
|
|
|
std::shared_ptr<Grammar> perform( |
|
|
|
|
const char* syntax, |
|
|
|
|
size_t syntax_len, |
|
|
|
|
std::string& start, |
|
|
|
|
std::function<void (size_t, size_t, const std::string&)> log = nullptr) { |
|
|
|
|
|
|
|
|
|
peg["Definition"] = [&](const std::vector<Any>& v) { |
|
|
|
|
const auto& name = v[0].get<std::string>(); |
|
|
|
|
(*grammar)[name] <= v[2].get<std::shared_ptr<Ope>>(); |
|
|
|
|
(*grammar)[name].name = name; |
|
|
|
|
auto grammar = std::make_shared<Grammar>(); |
|
|
|
|
start.clear(); |
|
|
|
|
std::map<std::string, const char*> refs; |
|
|
|
|
|
|
|
|
|
if (start.empty()) { |
|
|
|
|
start = name; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
peg["Definition"] = [&](const std::vector<Any>& v) { |
|
|
|
|
const auto& name = v[0].get<std::string>(); |
|
|
|
|
(*grammar)[name] <= v[2].get<std::shared_ptr<Ope>>(); |
|
|
|
|
(*grammar)[name].name = name; |
|
|
|
|
|
|
|
|
|
peg["Expression"] = [&](const std::vector<Any>& v) { |
|
|
|
|
if (v.size() == 1) { |
|
|
|
|
return v[0].get<std::shared_ptr<Ope>>(); |
|
|
|
|
} else { |
|
|
|
|
std::vector<std::shared_ptr<Ope>> opes; |
|
|
|
|
for (auto i = 0u; i < v.size(); i++) { |
|
|
|
|
if (!(i % 2)) { |
|
|
|
|
opes.push_back(v[i].get<std::shared_ptr<Ope>>()); |
|
|
|
|
} |
|
|
|
|
if (start.empty()) { |
|
|
|
|
start = name; |
|
|
|
|
} |
|
|
|
|
const std::shared_ptr<Ope> ope = std::make_shared<PrioritizedChoice>(opes); |
|
|
|
|
return ope; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
peg["Sequence"] = [&](const std::vector<Any>& v) { |
|
|
|
|
if (v.size() == 1) { |
|
|
|
|
return v[0].get<std::shared_ptr<Ope>>(); |
|
|
|
|
} else { |
|
|
|
|
std::vector<std::shared_ptr<Ope>> opes; |
|
|
|
|
for (const auto& x: v) { |
|
|
|
|
opes.push_back(x.get<std::shared_ptr<Ope>>()); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
peg["Primary"].actions = { |
|
|
|
|
[&](const std::vector<Any>& v) { |
|
|
|
|
return v[0]; |
|
|
|
|
}, |
|
|
|
|
[&](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]; |
|
|
|
|
} |
|
|
|
|
const std::shared_ptr<Ope> ope = std::make_shared<Sequence>(opes); |
|
|
|
|
return ope; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
peg["Prefix"] = [&](const std::vector<Any>& v, const std::vector<std::string>& n) { |
|
|
|
|
std::shared_ptr<Ope> ope; |
|
|
|
|
if (v.size() == 1) { |
|
|
|
|
ope = v[0].get<std::shared_ptr<Ope>>(); |
|
|
|
|
} else { |
|
|
|
|
assert(v.size() == 2); |
|
|
|
|
ope = v[1].get<std::shared_ptr<Ope>>(); |
|
|
|
|
if (n[0] == "AND") { |
|
|
|
|
ope = apd(ope); |
|
|
|
|
} else { // "NOT"
|
|
|
|
|
ope = npd(ope); |
|
|
|
|
auto m = peg["Grammar"].parse_with_match(syntax, syntax_len); |
|
|
|
|
if (!m.ret) { |
|
|
|
|
if (log) { |
|
|
|
|
auto line = line_info(syntax, m.ptr); |
|
|
|
|
log(line.first, line.second, m.msg.empty() ? "syntax error" : m.msg); |
|
|
|
|
} |
|
|
|
|
return nullptr; |
|
|
|
|
} |
|
|
|
|
return ope; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
peg["Suffix"] = [&](const std::vector<Any>& v, const std::vector<std::string>& n) { |
|
|
|
|
auto ope = v[0].get<std::shared_ptr<Ope>>(); |
|
|
|
|
if (v.size() == 1) { |
|
|
|
|
return ope; |
|
|
|
|
} else { |
|
|
|
|
assert(v.size() == 2); |
|
|
|
|
if (n[1] == "QUESTION") { |
|
|
|
|
return opt(ope); |
|
|
|
|
} else if (n[1] == "STAR") { |
|
|
|
|
return zom(ope); |
|
|
|
|
} else { // "PLUS"
|
|
|
|
|
return oom(ope); |
|
|
|
|
for (const auto& x : refs) { |
|
|
|
|
const auto& name = x.first; |
|
|
|
|
auto ptr = x.second; |
|
|
|
|
if (grammar->find(name) == grammar->end()) { |
|
|
|
|
if (log) { |
|
|
|
|
auto line = line_info(syntax, ptr); |
|
|
|
|
log(line.first, line.second, "'" + name + "' is not defined."); |
|
|
|
|
} |
|
|
|
|
return nullptr; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
peg["Primary"].actions = { |
|
|
|
|
[&](const std::vector<Any>& v) { |
|
|
|
|
return v[0]; |
|
|
|
|
}, |
|
|
|
|
[&](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]; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
return grammar; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
peg["IdentCont"] = [](const char*s, size_t l) { |
|
|
|
|
return std::string(s, l); |
|
|
|
|
}; |
|
|
|
|
private: |
|
|
|
|
GrammarGenerator() : peg(make_peg_grammar()) { |
|
|
|
|
initialize(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
peg["Literal"] = [](const std::vector<Any>& v) { |
|
|
|
|
return lit(v[0]); |
|
|
|
|
}; |
|
|
|
|
peg["SQCont"] = [](const char*s, size_t l) { |
|
|
|
|
return resolve_escape_sequence(s, l); |
|
|
|
|
}; |
|
|
|
|
peg["DQCont"] = [](const char*s, size_t l) { |
|
|
|
|
return resolve_escape_sequence(s, l); |
|
|
|
|
}; |
|
|
|
|
void initialize() { |
|
|
|
|
|
|
|
|
|
peg["Class"] = [](const std::vector<Any>& v) { |
|
|
|
|
return cls(v[0]); |
|
|
|
|
}; |
|
|
|
|
peg["ClassCont"] = [](const char*s, size_t l) { |
|
|
|
|
return resolve_escape_sequence(s, l); |
|
|
|
|
}; |
|
|
|
|
peg["Expression"] = [&](const std::vector<Any>& v) { |
|
|
|
|
if (v.size() == 1) { |
|
|
|
|
return v[0].get<std::shared_ptr<Ope>>(); |
|
|
|
|
} else { |
|
|
|
|
std::vector<std::shared_ptr<Ope>> opes; |
|
|
|
|
for (auto i = 0u; i < v.size(); i++) { |
|
|
|
|
if (!(i % 2)) { |
|
|
|
|
opes.push_back(v[i].get<std::shared_ptr<Ope>>()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
const std::shared_ptr<Ope> ope = std::make_shared<PrioritizedChoice>(opes); |
|
|
|
|
return ope; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
peg["DOT"] = []() { |
|
|
|
|
return any(); |
|
|
|
|
}; |
|
|
|
|
peg["Sequence"] = [&](const std::vector<Any>& v) { |
|
|
|
|
if (v.size() == 1) { |
|
|
|
|
return v[0].get<std::shared_ptr<Ope>>(); |
|
|
|
|
} else { |
|
|
|
|
std::vector<std::shared_ptr<Ope>> opes; |
|
|
|
|
for (const auto& x: v) { |
|
|
|
|
opes.push_back(x.get<std::shared_ptr<Ope>>()); |
|
|
|
|
} |
|
|
|
|
const std::shared_ptr<Ope> ope = std::make_shared<Sequence>(opes); |
|
|
|
|
return ope; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
auto m = peg["Grammar"].parse_with_match(syntax, syntax_len); |
|
|
|
|
if (!m.ret) { |
|
|
|
|
if (log) { |
|
|
|
|
auto line = line_info(syntax, m.ptr); |
|
|
|
|
log(line.first, line.second, m.msg.empty() ? "syntax error" : m.msg); |
|
|
|
|
} |
|
|
|
|
return nullptr; |
|
|
|
|
} |
|
|
|
|
peg["Prefix"] = [&](const std::vector<Any>& v, const std::vector<std::string>& n) { |
|
|
|
|
std::shared_ptr<Ope> ope; |
|
|
|
|
if (v.size() == 1) { |
|
|
|
|
ope = v[0].get<std::shared_ptr<Ope>>(); |
|
|
|
|
} else { |
|
|
|
|
assert(v.size() == 2); |
|
|
|
|
ope = v[1].get<std::shared_ptr<Ope>>(); |
|
|
|
|
if (n[0] == "AND") { |
|
|
|
|
ope = apd(ope); |
|
|
|
|
} else { // "NOT"
|
|
|
|
|
ope = npd(ope); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return ope; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
for (const auto& x : refs) { |
|
|
|
|
const auto& name = x.first; |
|
|
|
|
auto ptr = x.second; |
|
|
|
|
if (grammar->find(name) == grammar->end()) { |
|
|
|
|
if (log) { |
|
|
|
|
auto line = line_info(syntax, ptr); |
|
|
|
|
log(line.first, line.second, "'" + name + "' is not defined."); |
|
|
|
|
peg["Suffix"] = [&](const std::vector<Any>& v, const std::vector<std::string>& n) { |
|
|
|
|
auto ope = v[0].get<std::shared_ptr<Ope>>(); |
|
|
|
|
if (v.size() == 1) { |
|
|
|
|
return ope; |
|
|
|
|
} else { |
|
|
|
|
assert(v.size() == 2); |
|
|
|
|
if (n[1] == "QUESTION") { |
|
|
|
|
return opt(ope); |
|
|
|
|
} else if (n[1] == "STAR") { |
|
|
|
|
return zom(ope); |
|
|
|
|
} else { // "PLUS"
|
|
|
|
|
return oom(ope); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
peg["IdentCont"] = [](const char*s, size_t l) { |
|
|
|
|
return std::string(s, l); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
peg["Literal"] = [](const std::vector<Any>& v) { |
|
|
|
|
return lit(v[0]); |
|
|
|
|
}; |
|
|
|
|
peg["SQCont"] = [this](const char*s, size_t l) { |
|
|
|
|
return resolve_escape_sequence(s, l); |
|
|
|
|
}; |
|
|
|
|
peg["DQCont"] = [this](const char*s, size_t l) { |
|
|
|
|
return resolve_escape_sequence(s, l); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
peg["Class"] = [](const std::vector<Any>& v) { |
|
|
|
|
return cls(v[0]); |
|
|
|
|
}; |
|
|
|
|
peg["ClassCont"] = [this](const char*s, size_t l) { |
|
|
|
|
return resolve_escape_sequence(s, l); |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
peg["DOT"] = []() { |
|
|
|
|
return any(); |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::string resolve_escape_sequence(const char*s, size_t l) { |
|
|
|
|
std::string r; |
|
|
|
|
r.reserve(l); |
|
|
|
|
for (auto i = 0u; i < l; i++) { |
|
|
|
|
auto ch = s[i]; |
|
|
|
|
if (ch == '\\') { |
|
|
|
|
i++;
|
|
|
|
|
switch (s[i]) { |
|
|
|
|
case 'n': r += '\n'; break; |
|
|
|
|
case 'r': r += '\r'; break; |
|
|
|
|
case 't': r += '\t'; break; |
|
|
|
|
case '\'': r += '\''; break; |
|
|
|
|
case '"': r += '"'; break; |
|
|
|
|
case '[': r += '['; break; |
|
|
|
|
case ']': r += ']'; break; |
|
|
|
|
case '\\': r += '\\'; break; |
|
|
|
|
default: { |
|
|
|
|
// TODO: Octal number support
|
|
|
|
|
assert(false); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
r += ch; |
|
|
|
|
} |
|
|
|
|
return nullptr; |
|
|
|
|
} |
|
|
|
|
return r; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return grammar; |
|
|
|
|
Grammar peg; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
{ |
|
|
|
|
return GrammarGenerator::instance().perform(syntax, syntax_len, start, log); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
inline std::shared_ptr<Grammar> make_grammar( |
|
|
|
@ -1210,7 +1233,10 @@ public: |
|
|
|
|
return grammar_ != nullptr; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool load_syntax(const char* s, size_t l, std::function<void (size_t, size_t, const std::string&)> log = nullptr) { |
|
|
|
|
bool load_syntax( |
|
|
|
|
const char* s, size_t l, |
|
|
|
|
std::function<void (size_t, size_t, const std::string&)> log = nullptr) { |
|
|
|
|
|
|
|
|
|
grammar_ = make_grammar(s, l, start_, log); |
|
|
|
|
return grammar_ != nullptr; |
|
|
|
|
} |
|
|
|
|