forked from beark/ftl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser_combinator.h
154 lines (130 loc) · 3.3 KB
/
parser_combinator.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include <sstream>
#include <ftl/either_trans.h>
#include <ftl/functional.h>
#ifndef PARSER_GEN_H
#define PARSER_GEN_H
/**
* Error reporting class.
*
* We could have used a string directly, but this thin wrapper conveys more
* semantic information to users of the library.
*/
class error {
public:
// Default versions of c-tors default, copy, and move are acceptable
/// Construct from string error message
explicit error(const std::string& msg) noexcept : e(msg) {}
explicit error(std::string&& msg) noexcept : e(std::move(msg)) {}
/// Access the error message
std::string message() const noexcept {
return e;
}
private:
std::string e;
};
namespace ftl {
template<>
struct monoid<error> {
static error id() {
return error(std::string(""));
}
static error append(const error& e1, const error& e2) {
if(e1.message().empty())
return error(e2.message());
if(e2.message().empty())
return error(e1.message());
return error(e1.message() + " or " + e2.message());
}
static constexpr bool instance = true;
};
}
/// Convenience function to reduce template gibberish
template<typename T>
ftl::either<error,T> fail(const std::string& s) {
return ftl::make_left<T>(error(s));
}
/// Convenience function to reduce template gibberish
template<typename T>
auto yield(T&& t) -> decltype(ftl::make_right<error>(std::forward<T>(t))) {
return ftl::make_right<error>(std::forward<T>(t));
}
/**
* A parser of Ts.
*
* This is the central data type of the library.
*
* \par Concepts
* \li Monad
* \li MonoidAlternative
*/
template<typename T>
using parser = ftl::eitherT<error,ftl::function<T(std::istream&)>>;
/**
* Function for running parsers.
*/
template<typename T>
ftl::either<error,T> run(parser<T> p, std::istream& is) {
return (*p)(is);
}
/* What follows is a basic set of blocks that a user of the library can
* combine with the various combinators available (operator||, monad instance,
* applicative instance, functor instance).
*/
/**
* Parses any one character.
*
* This parser can only fail if the end of stream has been reached.
*/
parser<char> anyChar();
/**
* Parses one specific character.
*
* This parser will fail if the next character in the stream is not equal
* to \c c.
*/
parser<char> parseChar(char c);
/**
* Parses any character except c.
*
* This parser will fail if the next character \em does equal \c c.
*/
parser<char> notChar(char c);
/**
* Parses one of the characters in str.
*
* This parser will fail if the next character in the stream does not appear
* in str.
*/
parser<char> oneOf(std::string str);
/**
* Greedily parses 0 or more of p.
*
* This parser cannot fail. If end of stream is reached or p fails on the
* first run, the result will be an empty string.
*/
parser<std::string> many(parser<char> p);
/**
* Greedily parses 1 or more of p.
*
* This parser will fail if the first attempt at parsing p fails.
*/
parser<std::string> many1(parser<char> p);
/**
* Lazily run the parser generated by f
*
* This is useful e.g. if you want a parser to recurse.
*/
template<typename T>
parser<T> lazy(ftl::function<parser<T>()> f) {
return parser<T>([f](std::istream& is) {
return (*f())(is);
});
}
/// \overload
template<typename T>
parser<T> lazy(parser<T>(*f)()) {
return parser<T>([f](std::istream& is) {
return (*f())(is);
});
}
#endif