You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cpp-peglib/peglib.h

1307 lines
37 KiB

9 years ago
//
// peglib.h
//
// Copyright (c) 2015 Yuji Hirose. All rights reserved.
// MIT License
//
#ifndef _CPPEXPATLIB_PEGLIB_H_
#define _CPPEXPATLIB_PEGLIB_H_
#include <functional>
#include <string>
#include <memory>
#include <vector>
#include <map>
#include <cassert>
#include <cstring>
#include <initializer_list>
#include <iostream>
9 years ago
namespace peglib {
void* enabler;
/*-----------------------------------------------------------------------------
* any
9 years ago
*---------------------------------------------------------------------------*/
class any
9 years ago
{
public:
any() : content_(nullptr) {}
9 years ago
any(const any& rhs) : content_(rhs.clone()) {}
9 years ago
any(any&& rhs) : content_(rhs.content_) {
9 years ago
rhs.content_ = nullptr;
}
template <typename T>
any(const T& value) : content_(new holder<T>(value)) {}
9 years ago
any& operator=(const any& rhs) {
9 years ago
if (this != &rhs) {
if (content_) {
delete content_;
}
content_ = rhs.clone();
}
return *this;
}
any& operator=(any&& rhs) {
9 years ago
if (this != &rhs) {
if (content_) {
delete content_;
}
content_ = rhs.content_;
rhs.content_ = nullptr;
}
return *this;
}
template <typename T>
any& operator=(const T& value) {
if (content_) {
delete content_;
}
content_ = new holder<T>(value);
return *this;
}
~any() {
9 years ago
delete content_;
}
bool is_undefined() const {
return content_ == nullptr;
}
template <
typename T,
typename std::enable_if<!std::is_same<T, any>::value>::type*& = enabler
>
9 years ago
T& get() {
assert(content_);
auto p = dynamic_cast<holder<T>*>(content_);
assert(p);
if (!p) {
throw std::bad_cast();
}
return p->value_;
9 years ago
}
template <
typename T,
typename std::enable_if<std::is_same<T, any>::value>::type*& = enabler
>
T& get() {
return *this;
}
template <
typename T,
typename std::enable_if<!std::is_same<T, any>::value>::type*& = enabler
>
9 years ago
const T& get() const {
assert(content_);
auto p = dynamic_cast<holder<T>*>(content_);
assert(p);
if (!p) {
throw std::bad_cast();
}
return p->value_;
9 years ago
}
template <
typename T,
typename std::enable_if<std::is_same<T, any>::value>::type*& = enabler
>
const any& get() const {
return *this;
}
operator bool() const { return get<bool>(); }
operator char() const { return get<char>(); }
operator wchar_t() const { return get<wchar_t>(); }
operator unsigned char() const { return get<unsigned char>(); }
operator int() const { return get<int>(); }
operator unsigned int() const { return get<unsigned int>(); }
operator short() const { return get<short>(); }
operator unsigned short() const { return get<unsigned short>(); }
operator long() const { return get<long>(); }
operator unsigned long() const { return get<unsigned long>(); }
operator long long() const { return get<long long>(); }
operator unsigned long long() const { return get<unsigned long long>(); }
operator float() const { return get<float>(); }
operator double() const { return get<double>(); }
operator const std::string&() const { return get<std::string>(); }
#if defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015
#else
operator char16_t() const { return get<char16_t>(); }
operator char32_t() const { return get<char32_t>(); }
#endif
9 years ago
private:
struct placeholder {
virtual ~placeholder() {};
virtual placeholder* clone() const = 0;
};
template <typename T>
struct holder : placeholder {
holder(const T& value) : value_(value) {}
placeholder* clone() const override {
return new holder(value_);
}
T value_;
};
placeholder* clone() const {
return content_ ? content_->clone() : nullptr;
}
placeholder* content_;
};
/*-----------------------------------------------------------------------------
* PEG
*---------------------------------------------------------------------------*/
/*
* Semantic values
*/
typedef std::vector<any> Values;
9 years ago
/*
* Semantic action
*/
template <
typename R, typename F,
typename std::enable_if<!std::is_void<R>::value>::type*& = enabler,
typename... Args>
any call(F fn, Args&&... args) {
return any(fn(std::forward<Args>(args)...));
}
template <
typename R, typename F,
typename std::enable_if<std::is_void<R>::value>::type*& = enabler,
typename... Args>
any call(F fn, Args&&... args) {
fn(std::forward<Args>(args)...);
return any();
}
class Action
{
public:
Action() = default;
Action(const Action& rhs) : fn_(rhs.fn_) {}
//Action(Action&& rhs) : fn_(std::move(rhs.fn_)) {}
template <typename F, typename std::enable_if<!std::is_pointer<F>::value && !std::is_same<F, std::nullptr_t>::value>::type*& = enabler>
Action(F fn) : fn_(make_adaptor(fn, &F::operator())) {}
template <typename F, typename std::enable_if<std::is_pointer<F>::value>::type*& = enabler>
Action(F fn) : fn_(make_adaptor(fn, fn)) {}
template <typename F, typename std::enable_if<std::is_same<F, std::nullptr_t>::value>::type*& = enabler>
Action(F fn) {}
template <typename F, typename std::enable_if<!std::is_pointer<F>::value && !std::is_same<F, std::nullptr_t>::value>::type*& = enabler>
void operator=(F fn) {
fn_ = make_adaptor(fn, &F::operator());
}
template <typename F, typename std::enable_if<std::is_pointer<F>::value>::type*& = enabler>
void operator=(F fn) {
fn_ = make_adaptor(fn, fn);
}
template <typename F, typename std::enable_if<std::is_same<F, std::nullptr_t>::value>::type*& = enabler>
void operator=(F fn) {}
operator bool() const {
return (bool)fn_;
}
any operator()(const char* s, size_t l, const std::vector<any>& v, any& c) const {
return fn_(s, l, v, c);
}
private:
template <typename R>
struct TypeAdaptor {
TypeAdaptor(std::function<R (const char* s, size_t l, const std::vector<any>& v, any& c)> fn)
: fn_(fn) {}
any operator()(const char* s, size_t l, const std::vector<any>& v, any& c) {
return call<R>(fn_, s, l, v, c);
}
std::function<R (const char* s, size_t l, const std::vector<any>& v, any& c)> fn_;
};
template <typename R>
struct TypeAdaptor_s_l_v {
TypeAdaptor_s_l_v(std::function<R (const char* s, size_t l, const std::vector<any>& v)> fn)
: fn_(fn) {}
any operator()(const char* s, size_t l, const std::vector<any>& v, any& c) {
return call<R>(fn_, s, l, v);
}
std::function<R (const char* s, size_t l, const std::vector<any>& v)> fn_;
};
template <typename R>
struct TypeAdaptor_s_l {
TypeAdaptor_s_l(std::function<R (const char* s, size_t l)> fn) : fn_(fn) {}
any operator()(const char* s, size_t l, const std::vector<any>& v, any& c) {
return call<R>(fn_, s, l);
}
std::function<R (const char* s, size_t l)> fn_;
};
template <typename R>
struct TypeAdaptor_v_n {
TypeAdaptor_v_n(std::function<R (const std::vector<any>& v, any& c)> fn) : fn_(fn) {}
any operator()(const char* s, size_t l, const std::vector<any>& v, any& c) {
return call<R>(fn_, v, c);
}
std::function<R (const std::vector<any>& v, any& c)> fn_;
};
template <typename R>
struct TypeAdaptor_v {
TypeAdaptor_v(std::function<R (const std::vector<any>& v)> fn) : fn_(fn) {}
any operator()(const char* s, size_t l, const std::vector<any>& v, any& c) {
return call<R>(fn_, v);
}
std::function<R (const std::vector<any>& v)> fn_;
};
template <typename R>
struct TypeAdaptor_empty {
TypeAdaptor_empty(std::function<R ()> fn) : fn_(fn) {}
any operator()(const char* s, size_t l, const std::vector<any>& v, any& c) {
return call<R>(fn_);
}
std::function<R ()> fn_;
};
typedef std::function<any (const char* s, size_t l, const std::vector<any>& v, any& c)> Fty;
template<typename F, typename R>
Fty make_adaptor(F fn, R (F::*mf)(const char*, size_t, const std::vector<any>& v, any& c) const) {
return TypeAdaptor<R>(fn);
}
template<typename F, typename R>
Fty make_adaptor(F fn, R(*mf)(const char*, size_t, const std::vector<any>& v, any& c)) {
return TypeAdaptor<R>(fn);
}
template<typename F, typename R>
Fty make_adaptor(F fn, R (F::*mf)(const char*, size_t, const std::vector<any>& v) const) {
return TypeAdaptor_s_l_v<R>(fn);
}
template<typename F, typename R>
Fty make_adaptor(F fn, R(*mf)(const char*, size_t, const std::vector<any>& v)) {
return TypeAdaptor_s_l_v<R>(fn);
}
template<typename F, typename R>
Fty make_adaptor(F fn, R (F::*mf)(const char*, size_t) const) {
return TypeAdaptor_s_l<R>(fn);
}
template<typename F, typename R>
Fty make_adaptor(F fn, R (*mf)(const char*, size_t)) {
return TypeAdaptor_s_l<R>(fn);
}
template<typename F, typename R>
Fty make_adaptor(F fn, R (F::*mf)(const std::vector<any>& v, any& c) const) {
return TypeAdaptor_v_n<R>(fn);
}
template<typename F, typename R>
Fty make_adaptor(F fn, R (*mf)(const std::vector<any>& v, any& c)) {
return TypeAdaptor_v_n<R>(fn);
}
template<typename F, typename R>
Fty make_adaptor(F fn, R (F::*mf)(const std::vector<any>& v) const) {
return TypeAdaptor_v<R>(fn);
}
template<typename F, typename R>
Fty make_adaptor(F fn, R (*mf)(const std::vector<any>& v)) {
return TypeAdaptor_v<R>(fn);
}
template<typename F, typename R>
Fty make_adaptor(F fn, R (F::*mf)() const) {
return TypeAdaptor_empty<R>(fn);
}
template<typename F, typename R>
Fty make_adaptor(F fn, R (*mf)()) {
return TypeAdaptor_empty<R>(fn);
}
Fty fn_;
};
9 years ago
/*
* Result
9 years ago
*/
struct Result
9 years ago
{
bool ret;
size_t len;
size_t choice;
const char* ptr;
const std::string err; // TODO: should be `int`.
9 years ago
};
Result success(size_t len, size_t choice = 0) {
return Result{ true, len, choice, nullptr, std::string() };
9 years ago
}
Result fail(const char* ptr, std::string err = std::string(), std::string name = std::string()) {
return Result{ false, 0, (size_t)-1, ptr, err };
9 years ago
}
/*
* Parser operators
9 years ago
*/
class Ope
{
public:
virtual ~Ope() {};
virtual Result parse(const char* s, size_t l, Values& v, any& c) const = 0;
};
class Sequence : public Ope
9 years ago
{
public:
Sequence(const Sequence& rhs) : opes_(rhs.opes_) {}
9 years ago
#if defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015
// NOTE: Compiler Error C2797 on Visual Studio 2013
// "The C++ compiler in Visual Studio does not implement list
// initialization inside either a member initializer list or a non-static
// data member initializer. Before Visual Studio 2013 Update 3, this was
// silently converted to a function call, which could lead to bad code
// generation. Visual Studio 2013 Update 3 reports this as an error."
template <typename... Args>
Sequence(const Args& ...args) {
opes_ = std::vector<std::shared_ptr<Ope>>{ static_cast<std::shared_ptr<Ope>>(args)... };
9 years ago
}
#else
template <typename... Args>
Sequence(const Args& ...args) : opes_{ static_cast<std::shared_ptr<Ope>>(args)... } {}
9 years ago
#endif
Sequence(const std::vector<std::shared_ptr<Ope>>& opes) : opes_(opes) {}
Sequence(std::vector<std::shared_ptr<Ope>>&& opes) : opes_(std::move(opes)) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
size_t i = 0;
for (const auto& ope : opes_) {
const auto& rule = *ope;
auto r = rule.parse(s + i, l - i, v, c);
if (!r.ret) {
auto err = r.err;
if (err.empty()) {
err = "missing an element in the 'sequence'";
}
return fail(r.ptr, err);
}
i += r.len;
}
return success(i);
}
9 years ago
private:
std::vector<std::shared_ptr<Ope>> opes_;
9 years ago
};
class PrioritizedChoice : public Ope
9 years ago
{
public:
#if defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015
// NOTE: Compiler Error C2797 on Visual Studio 2013
// "The C++ compiler in Visual Studio does not implement list
// initialization inside either a member initializer list or a non-static
// data member initializer. Before Visual Studio 2013 Update 3, this was
// silently converted to a function call, which could lead to bad code
// generation. Visual Studio 2013 Update 3 reports this as an error."
template <typename... Args>
PrioritizedChoice(const Args& ...args) {
opes_ = std::vector<std::shared_ptr<Ope>>{ static_cast<std::shared_ptr<Ope>>(args)... };
9 years ago
}
#else
template <typename... Args>
PrioritizedChoice(const Args& ...args) : opes_{ static_cast<std::shared_ptr<Ope>>(args)... } {}
9 years ago
#endif
PrioritizedChoice(const std::vector<std::shared_ptr<Ope>>& opes) : opes_(opes) {}
PrioritizedChoice(std::vector<std::shared_ptr<Ope>>&& opes) : opes_(std::move(opes)) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
size_t id = 0;
for (const auto& ope : opes_) {
const auto& rule = *ope;
Values chldsv;
auto r = rule.parse(s, l, chldsv, c);
if (r.ret) {
if (!chldsv.empty()) {
for (const auto& x: chldsv) {
v.push_back(x);
}
}
return success(r.len, id);
}
id++;
}
return fail(s, "nothing was matched in the 'prioritized choice'");
}
size_t size() const { return opes_.size(); }
9 years ago
private:
std::vector<std::shared_ptr<Ope>> opes_;
9 years ago
};
class ZeroOrMore : public Ope
9 years ago
{
public:
ZeroOrMore(const std::shared_ptr<Ope>& ope) : ope_(ope) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
auto i = 0;
while (l - i > 0) {
const auto& rule = *ope_;
auto r = rule.parse(s + i, l - i, v, c);
if (!r.ret) {
break;
}
i += r.len;
}
return success(i);
}
9 years ago
private:
std::shared_ptr<Ope> ope_;
9 years ago
};
class OneOrMore : public Ope
9 years ago
{
public:
OneOrMore(const std::shared_ptr<Ope>& ope) : ope_(ope) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
const auto& rule = *ope_;
auto r = rule.parse(s, l, v, c);
if (!r.ret) {
auto err = r.err;
if (err.empty()) {
err = "nothing occurred in the 'one-or-more'";
}
return fail(r.ptr, r.err);
}
auto i = r.len;
while (l - i > 0) {
const auto& rule = *ope_;
auto r = rule.parse(s + i, l - i, v, c);
if (!r.ret) {
break;
}
i += r.len;
}
return success(i);
}
9 years ago
private:
std::shared_ptr<Ope> ope_;
9 years ago
};
class Option : public Ope
9 years ago
{
public:
Option(const std::shared_ptr<Ope>& ope) : ope_(ope) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
const auto& rule = *ope_;
auto r = rule.parse(s, l, v, c);
return success(r.ret ? r.len : 0);
}
9 years ago
private:
std::shared_ptr<Ope> ope_;
9 years ago
};
class AndPredicate : public Ope
9 years ago
{
public:
AndPredicate(const std::shared_ptr<Ope>& ope) : ope_(ope) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
const auto& rule = *ope_;
auto r = rule.parse(s, l, v, c);
if (r.ret) {
return success(0);
} else {
return fail(r.ptr, r.err);
}
}
9 years ago
private:
std::shared_ptr<Ope> ope_;
9 years ago
};
class NotPredicate : public Ope
9 years ago
{
public:
NotPredicate(const std::shared_ptr<Ope>& ope) : ope_(ope) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
const auto& rule = *ope_;
auto r = rule.parse(s, l, v, c);
if (r.ret) {
return fail(s);
} else {
return success(0);
}
}
9 years ago
private:
std::shared_ptr<Ope> ope_;
9 years ago
};
class LiteralString : public Ope
9 years ago
{
public:
LiteralString(const std::string& s) : lit_(s) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
9 years ago
auto i = 0u;
for (; i < lit_.size(); i++) {
if (i >= l || s[i] != lit_[i]) {
return fail(s);
9 years ago
}
}
return success(i);
}
private:
std::string lit_;
};
class CharacterClass : public Ope
9 years ago
{
public:
CharacterClass(const std::string& chars) : chars_(chars) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
// TODO: UTF8 support
9 years ago
if (l < 1) {
return fail(s);
9 years ago
}
auto ch = s[0];
auto i = 0u;
while (i < chars_.size()) {
if (i + 2 < chars_.size() && chars_[i + 1] == '-') {
if (chars_[i] <= ch && ch <= chars_[i + 2]) {
return success(1);
}
i += 3;
} else {
if (chars_[i] == ch) {
return success(1);
}
i += 1;
}
}
return fail(s);
9 years ago
}
private:
std::string chars_;
};
class Character : public Ope
9 years ago
{
public:
Character(char ch) : ch_(ch) {}
Result parse(const char* s, size_t l, Values& v, any& c) const {
// TODO: UTF8 support
9 years ago
if (l < 1 || s[0] != ch_) {
return fail(s);
9 years ago
}
return success(1);
}
private:
char ch_;
};
class AnyCharacter : public Ope
9 years ago
{
public:
Result parse(const char* s, size_t l, Values& v, any& c) const {
// TODO: UTF8 support
9 years ago
if (l < 1) {
return fail(s);
9 years ago
}
return success(1);
}
9 years ago
};
class Grouping : public Ope
9 years ago
{
public:
Grouping(const std::shared_ptr<Ope>& ope) : ope_(ope) {}
Grouping(const std::shared_ptr<Ope>& ope, std::function<void(const char* s, size_t l)> match) : ope_(ope), match_(match) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
assert(ope_);
const auto& rule = *ope_;
auto r = rule.parse(s, l, v, c);
if (r.ret && match_) {
match_(s, r.len);
}
return r;
}
9 years ago
private:
std::shared_ptr<Ope> ope_;
std::function<void(const char* s, size_t l)> match_;
9 years ago
};
class WeakHolder : public Ope
9 years ago
{
public:
WeakHolder(const std::shared_ptr<Ope>& ope) : weak_(ope) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
auto ope = weak_.lock();
assert(ope);
const auto& rule = *ope;
return rule.parse(s, l, v, c);
}
9 years ago
private:
std::weak_ptr<Ope> weak_;
9 years ago
};
/*
* Definition
*/
class Definition
{
public:
Definition()
: actions(1)
, holder_(std::make_shared<Holder>(this)) {}
9 years ago
Definition(const Definition& rhs)
: name(rhs.name)
, actions(1)
, holder_(rhs.holder_)
9 years ago
{
holder_->outer_ = this;
9 years ago
}
Definition(Definition&& rhs)
: name(std::move(rhs.name))
, actions(1)
, holder_(std::move(rhs.holder_))
9 years ago
{
holder_->outer_ = this;
9 years ago
}
Definition(const std::shared_ptr<Ope>& ope)
: actions(1)
, holder_(std::make_shared<Holder>(this))
9 years ago
{
holder_->ope_ = ope;
}
operator std::shared_ptr<Ope>() {
return std::make_shared<WeakHolder>(holder_);
9 years ago
}
Definition& operator<=(const std::shared_ptr<Ope>& ope) {
holder_->ope_ = ope;
return *this;
9 years ago
}
Result parse(const char* s, size_t l, Values& v, any& c) const {
return holder_->parse(s, l, v, c);
}
9 years ago
template <typename T>
Result parse(const char* s, size_t l, T& val) const {
Values v;
any c;
auto r = holder_->parse(s, l, v, c);
if (r.ret && !v.empty() && !v.front().is_undefined()) {
val = v[0].get<T>();
9 years ago
}
return r;
9 years ago
}
template <typename T>
Result parse(const char* s, T& val) const {
auto l = strlen(s);
return parse(s, l, val);
9 years ago
}
Result parse(const char* s) const {
auto l = strlen(s);
Values v;
any c;
return holder_->parse(s, l, v, c);
9 years ago
}
Definition& operator=(Action ac) {
assert(!actions.empty());
actions[0] = ac;
return *this;
}
Definition& operator=(std::initializer_list<Action> acs) {
actions = acs;
return *this;
}
template <typename T>
Definition& operator,(T fn) {
operator=(fn);
return *this;
}
std::string name;
std::vector<Action> actions;
9 years ago
private:
friend class DefinitionReference;
class Holder : public Ope
{
public:
Holder(Definition* outer)
: outer_(outer) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
if (!ope_) {
throw std::logic_error("Uninitialized definition ope was used...");
}
9 years ago
const auto& rule = *ope_;
Values chldsv;
auto r = rule.parse(s, l, chldsv, c);
if (r.ret) {
assert(!outer_->actions.empty());
auto id = r.choice + 1;
const auto& ac = (id < outer_->actions.size() && outer_->actions[id])
? outer_->actions[id]
: outer_->actions[0];
v.push_back(reduce(s, r.len, chldsv, c, ac));
}
return r;
9 years ago
}
private:
friend class Definition;
9 years ago
any reduce(const char* s, size_t l, const Values& v, any& c, const Action& action) const {
if (action) {
return action(s, l, v, c);
} else if (v.empty()) {
return any();
} else {
return v.front();
}
9 years ago
}
std::shared_ptr<Ope> ope_;
Definition* outer_;
};
9 years ago
Definition& operator=(const Definition& rhs);
Definition& operator=(Definition&& rhs);
9 years ago
std::shared_ptr<Holder> holder_;
};
9 years ago
class DefinitionReference : public Ope
{
public:
DefinitionReference(
const std::map<std::string, Definition>& grammar, const std::string& name)
: grammar_(grammar)
, name_(name) {}
9 years ago
Result parse(const char* s, size_t l, Values& v, any& c) const {
const auto& rule = *grammar_.at(name_).holder_;
return rule.parse(s, l, v, c);
9 years ago
}
private:
const std::map<std::string, Definition>& grammar_;
const std::string name_;
};
9 years ago
typedef Definition rule;
9 years ago
/*
* Factories
*/
template <typename... Args>
std::shared_ptr<Ope> seq(Args&& ...args) {
return std::make_shared<Sequence>(static_cast<std::shared_ptr<Ope>>(args)...);
9 years ago
}
template <typename... Args>
std::shared_ptr<Ope> cho(Args&& ...args) {
return std::make_shared<PrioritizedChoice>(static_cast<std::shared_ptr<Ope>>(args)...);
9 years ago
}
inline std::shared_ptr<Ope> zom(const std::shared_ptr<Ope>& ope) {
return std::make_shared<ZeroOrMore>(ope);
9 years ago
}
inline std::shared_ptr<Ope> oom(const std::shared_ptr<Ope>& ope) {
return std::make_shared<OneOrMore>(ope);
9 years ago
}
inline std::shared_ptr<Ope> opt(const std::shared_ptr<Ope>& ope) {
return std::make_shared<Option>(ope);
9 years ago
}
inline std::shared_ptr<Ope> apd(const std::shared_ptr<Ope>& ope) {
return std::make_shared<AndPredicate>(ope);
9 years ago
}
inline std::shared_ptr<Ope> npd(const std::shared_ptr<Ope>& ope) {
return std::make_shared<NotPredicate>(ope);
9 years ago
}
inline std::shared_ptr<Ope> lit(const std::string& lit) {
return std::make_shared<LiteralString>(lit);
9 years ago
}
inline std::shared_ptr<Ope> cls(const std::string& chars) {
return std::make_shared<CharacterClass>(chars);
9 years ago
}
inline std::shared_ptr<Ope> chr(char c) {
return std::make_shared<Character>(c);
9 years ago
}
inline std::shared_ptr<Ope> dot() {
return std::make_shared<AnyCharacter>();
9 years ago
}
inline std::shared_ptr<Ope> grp(const std::shared_ptr<Ope>& ope) {
return std::make_shared<Grouping>(ope);
9 years ago
}
inline std::shared_ptr<Ope> grp(const std::shared_ptr<Ope>& ope, std::function<void (const char* s, size_t l)> match) {
return std::make_shared<Grouping>(ope, match);
}
inline std::shared_ptr<Ope> ref(const std::map<std::string, Definition>& grammar, const std::string& name) {
return std::make_shared<DefinitionReference>(grammar, name);
9 years ago
}
/*-----------------------------------------------------------------------------
* PEG parser generator
*---------------------------------------------------------------------------*/
inline std::pair<size_t, size_t> line_info(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);
}
typedef std::map<std::string, Definition> Grammar;
typedef std::function<void (size_t, size_t, const std::string&)> Log;
class PEGParser
9 years ago
{
public:
static std::shared_ptr<Grammar> parse(const char* s, size_t l, std::string& start, Log log) {
static PEGParser instance;
return get().perform_core(s, l, start, log);
}
// For debuging purpose
static Grammar& grammar() {
return get().g;
}
9 years ago
private:
static PEGParser& get() {
static PEGParser instance;
return instance;
}
PEGParser() {
make_grammar();
setup_actions();
}
struct Context {
std::shared_ptr<Grammar> grammar = std::make_shared<Grammar>();
std::string start;
std::map<std::string, const char*> refs;
};
void make_grammar() {
// Setup PEG syntax parser
g["Grammar"] <= seq(g["Spacing"], oom(g["Definition"]), g["EndOfFile"]);
g["Definition"] <= seq(g["Identifier"], g["LEFTARROW"], g["Expression"]);
g["Expression"] <= seq(g["Sequence"], zom(seq(g["SLASH"], g["Sequence"])));
g["Sequence"] <= zom(g["Prefix"]);
g["Prefix"] <= seq(opt(cho(g["AND"], g["NOT"])), g["Suffix"]);
g["Suffix"] <= seq(g["Primary"], opt(cho(g["QUESTION"], g["STAR"], g["PLUS"])));
g["Primary"] <= cho(seq(g["Identifier"], npd(g["LEFTARROW"])),
seq(g["OPEN"], g["Expression"], g["CLOSE"]),
g["Literal"], g["Class"], g["DOT"]);
g["Identifier"] <= seq(g["IdentCont"], g["Spacing"]);
g["IdentCont"] <= seq(g["IdentStart"], zom(g["IdentRest"]));
g["IdentStart"] <= cls("a-zA-Z_");
g["IdentRest"] <= cho(g["IdentStart"], cls("0-9"));
g["Literal"] <= cho(seq(cls("'"), g["SQCont"], cls("'"), g["Spacing"]),
seq(cls("\""), g["DQCont"], cls("\""), g["Spacing"]));
g["SQCont"] <= zom(seq(npd(cls("'")), g["Char"]));
g["DQCont"] <= zom(seq(npd(cls("\"")), g["Char"]));
g["Class"] <= seq(chr('['), g["ClassCont"], chr(']'), g["Spacing"]);
g["ClassCont"] <= zom(seq(npd(chr(']')), g["Range"]));
g["Range"] <= cho(seq(g["Char"], chr('-'), g["Char"]), g["Char"]);
g["Char"] <= cho(seq(chr('\\'), cls("nrt'\"[]\\")),
seq(chr('\\'), cls("0-2"), cls("0-7"), cls("0-7")), // TODO: 0-2 should be 0-3. bug in the spec...
seq(chr('\\'), cls("0-7"), opt(cls("0-7"))),
seq(npd(chr('\\')), dot()));
g["LEFTARROW"] <= seq(lit("<-"), g["Spacing"]);
g["SLASH"] <= seq(chr('/'), g["Spacing"]);
g["AND"] <= seq(chr('&'), g["Spacing"]);
g["NOT"] <= seq(chr('!'), g["Spacing"]);
g["QUESTION"] <= seq(chr('?'), g["Spacing"]);
g["STAR"] <= seq(chr('*'), g["Spacing"]);
g["PLUS"] <= seq(chr('+'), g["Spacing"]);
g["OPEN"] <= seq(chr('('), g["Spacing"]);
g["CLOSE"] <= seq(chr(')'), g["Spacing"]);
g["DOT"] <= seq(chr('.'), g["Spacing"]);
g["Spacing"] <= zom(cho(g["Space"], g["Comment"]));
g["Comment"] <= seq(chr('#'), zom(seq(npd(g["EndOfLine"]), dot())), g["EndOfLine"]);
g["Space"] <= cho(chr(' '), chr('\t'), g["EndOfLine"]);
g["EndOfLine"] <= cho(lit("\r\n"), chr('\n'), chr('\r'));
g["EndOfFile"] <= npd(dot());
// Set definition names
for (auto& x: g) {
x.second.name = x.first;
}
}
9 years ago
void setup_actions() {
g["Definition"] = [&](const std::vector<any>& v, any& c) {
Context& cxt = *c.get<Context*>();
const auto& name = v[0].get<std::string>();
(*cxt.grammar)[name] <= v[2].get<std::shared_ptr<Ope>>();
(*cxt.grammar)[name].name = name;
if (cxt.start.empty()) {
cxt.start = name;
}
};
9 years ago
g["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;
}
};
9 years ago
g["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;
}
};
9 years ago
g["Prefix"] = [&](const std::vector<any>& v, any& c) {
std::shared_ptr<Ope> ope;
if (v.size() == 1) {
ope = v[0].get<std::shared_ptr<Ope>>();
} else {
assert(v.size() == 2);
auto tok = v[0].get<char>();
ope = v[1].get<std::shared_ptr<Ope>>();
if (tok == '&') {
ope = apd(ope);
} else { // '!'
ope = npd(ope);
}
}
return ope;
};
g["Suffix"] = [&](const std::vector<any>& v, any& c) {
auto ope = v[0].get<std::shared_ptr<Ope>>();
if (v.size() == 1) {
return ope;
} else {
assert(v.size() == 2);
auto tok = v[1].get<char>();
if (tok == '?') {
return opt(ope);
} else if (tok == '*') {
return zom(ope);
} else { // '+'
return oom(ope);
}
}
};
g["Primary"].actions = {
[&](const std::vector<any>& v) {
return v[0];
},
[&](const char* s, size_t l, const std::vector<any>& v, any& c) {
Context& cxt = *c.get<Context*>();
cxt.refs[v[0]] = s;
return ref(*cxt.grammar, v[0]);
},
[&](const std::vector<any>& v) {
return v[1];
}
};
g["IdentCont"] = [](const char*s, size_t l) {
return std::string(s, l);
};
g["Literal"] = [](const std::vector<any>& v) {
return lit(v[0]);
};
g["SQCont"] = [this](const char*s, size_t l) {
return resolve_escape_sequence(s, l);
};
g["DQCont"] = [this](const char*s, size_t l) {
return resolve_escape_sequence(s, l);
};
g["Class"] = [](const std::vector<any>& v) {
return cls(v[0]);
};
g["ClassCont"] = [this](const char*s, size_t l) {
return resolve_escape_sequence(s, l);
};
g["AND"] = [](const char*s, size_t l) { return *s; };
g["NOT"] = [](const char*s, size_t l) { return *s; };
g["QUESTION"] = [](const char*s, size_t l) { return *s; };
g["STAR"] = [](const char*s, size_t l) { return *s; };
g["PLUS"] = [](const char*s, size_t l) { return *s; };
g["DOT"] = []() {
return dot();
};
}
std::shared_ptr<Grammar> perform_core(const char* s, size_t l, std::string& start, Log log) {
Values v;
Context cxt;
any c = &cxt;
auto r = g["Grammar"].parse(s, l, v, c);
if (!r.ret) {
if (log) {
auto line = line_info(s, r.ptr);
log(line.first, line.second, r.err.empty() ? "syntax error" : r.err);
}
return nullptr;
}
for (const auto& x : cxt.refs) {
const auto& name = x.first;
auto ptr = x.second;
if (cxt.grammar->find(name) == cxt.grammar->end()) {
if (log) {
auto line = line_info(s, ptr);
log(line.first, line.second, "'" + name + "' is not defined.");
}
return nullptr;
}
}
start = cxt.start;
return cxt.grammar;
}
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;
9 years ago
}
}
return r;
9 years ago
}
Grammar g;
};
9 years ago
/*-----------------------------------------------------------------------------
* peg
9 years ago
*---------------------------------------------------------------------------*/
class peg
9 years ago
{
public:
peg(const char* s, size_t l, Log log = nullptr) {
grammar_ = PEGParser::parse(s, l, start_, log);
9 years ago
}
peg(const char* s, Log log = nullptr) {
auto l = strlen(s);
grammar_ = PEGParser::parse(s, l, start_, log);
}
operator bool() {
9 years ago
return grammar_ != nullptr;
}
template <typename T>
bool parse(const char* s, size_t l, T& out, bool exact = true) const {
if (grammar_ != nullptr) {
const auto& rule = (*grammar_)[start_];
auto r = rule.parse(s, l, out);
return r.ret && (!exact || r.len == l);
}
9 years ago
return false;
}
bool parse(const char* s, size_t l, bool exact = true) const {
9 years ago
if (grammar_ != nullptr) {
const auto& rule = (*grammar_)[start_];
auto r = rule.parse(s, l);
return r.ret && (!exact || r.len == l);
9 years ago
}
return false;
}
template <typename T>
bool parse(const char* s, T& out, bool exact = true) const {
auto l = strlen(s);
return parse(s, l, out, exact);
}
bool parse(const char* s, bool exact = true) const {
auto l = strlen(s);
return parse(s, l, exact);
}
bool lint(const char* s, size_t l, bool exact, Log log = nullptr) {
assert(grammar_);
9 years ago
if (grammar_ != nullptr) {
const auto& rule = (*grammar_)[start_];
auto r = rule.parse(s, l);
if (!r.ret) {
if (log) {
auto line = line_info(s, r.ptr);
log(line.first, line.second, r.err.empty() ? "syntax error" : r.err);
}
} else if (exact && r.len != l) {
auto line = line_info(s, s + r.len);
log(line.first, line.second, "garbage string at the end");
}
return r.ret && (!exact || r.len == l);
9 years ago
}
return false;
9 years ago
}
Definition& operator[](const char* s) {
return (*grammar_)[s];
9 years ago
}
private:
std::shared_ptr<Grammar> grammar_;
std::string start_;
};
} // namespace peglib
#endif
// vim: et ts=4 sw=4 cin cino={1s ff=unix