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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
|
#include <boost/algorithm/string.hpp>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <deque>
#include <map>
#include <string>
#include <utility>
#include <vector>
using namespace std::string_literals;
using BNF = std::map<std::string, std::vector<std::vector<std::string>>>;
using Terminals = std::set<std::string>;
using ProgramNode = std::deque<std::string>;
using PathElement = std::pair<std::string, size_t>; // Name, Index
std::vector<std::string> split(std::string s)
{
std::vector<std::string> result;
boost::algorithm::split(result, s, boost::algorithm::is_any_of(s), boost::algorithm::token_compress_on);
while (result.size() > 0 && result.back() == ""s)
result.pop_back();
return result;
}
std::vector<PathElement> GetPath(std::string Token, BNF ReverseBNF, std::string Top, Terminals terminals = {}, std::vector<PathElement> PreviousPath = {})
{
throw std::runtime_error("Compile error");
return {}; // TODO
}
BNF Reverse(BNF bnf){
return {}; // TODO
}
using index_t = size_t;
struct TreeNode {
index_t parent;
std::vector<index_t> childs;
std::string name;
};
struct Tree {
std::map<index_t, TreeNode> nodes; // index 0 = non existing; index starting at 1
index_t node_num{};
index_t root{};
index_t last{};
bool Valid() const {
// Start symbol?
// All terminal symbols?
return true; // TODO
}
bool Add(char c) {
// TODO
//node_num ++;
//nodes.emplace(node_num, {});
return false;
}
};
void CheckCandidates(std::deque<Tree>& candidates, std::string& token, std::vector<std::string>& result)
{
bool valid{false};
for (const auto& ct : candidates) {
if (ct.Valid()) {
if (valid)
throw std::runtime_error("Found ambiguous token "s + token);
result.push_back(token);
token.clear();
valid = true;
}
}
if (!valid)
throw std::runtime_error("Invalid token: "s + token);
candidates.clear();
}
std::vector<std::string> Lex(std::string s, std::string Top, BNF bnf)
{
std::vector<std::string> result;
std::string token;
BNF ReverseBNF{ Reverse(bnf)};
std::string Whitespace{"\t \n\r"};
std::deque<Tree> candidates;
for (size_t pos{0}; pos < s.size(); pos++) {
char c{s[pos]};
if (Whitespace.find(c) != std::string::npos) { // found whitespace character
if (candidates.empty()) { // skip
if (!token.empty())
throw std::runtime_error("Expected empty token, got "s + token);
} else { // check candidates
CheckCandidates(candidates, token, result);
}
} else { // no whitespace: try to add to tree
std::deque<index_t> EraseList;
int i = 0;
for (auto ct = candidates.begin(); ct != candidates.end(); ct++, i++) {
if (!ct->Add(c)) {
EraseList.push_front(i); // no candidate anymore
}
}
// new candidate: starting with c
auto element {ReverseBNF.find(std::string(1, c))};
if (element != ReverseBNF.end()) {
candidates.emplace_back();
candidates.back().Add(c);
}
if (candidates.size() - EraseList.size() > 0) { // Added to some candidates: Continue growing token
token.push_back(c);
for (const auto& i : EraseList)
candidates.erase(candidates.begin() + i);
} else { // no candidates left: new tree
CheckCandidates(candidates, token, result);
}
}
}
// Final evaluation
if (candidates.empty()) { // skip
if (!token.empty())
throw std::runtime_error("Expected empty token at end of input, got "s + token);
} else { // check candidates
CheckCandidates(candidates, token, result);
}
return result;
}
ProgramNode Compile(std::vector<std::string> Tokens, std::string Top, BNF bnf, Terminals terminals)
{
BNF ReverseBNF{ Reverse(bnf)};
if (Tokens.size()){
std::string Token = Tokens[0];
auto Path = GetPath(Token, ReverseBNF, Top, terminals);
if (Path.size()) {
size_t Index{1};
while (Index < Tokens.size()) {
Path = GetPath(Token, ReverseBNF, Top, terminals, Path);
Index++;
}
} else
throw std::runtime_error("Invalid token: "s + Token);
} else
throw std::runtime_error("No tokens!");
return {};
}
class Test: public ::testing::Test {
protected:
Test(){}
~Test() override {}
};
TEST_F(Test, BNF) {
std::string LexTop{"preprocessing-token"};
BNF LexBNF{
{"preprocessing-token", {{"identifier"},
{"preprocessing-op-or-punc"},
{"pp-number"}}},
{"identifier", {{"identifier-nondigit"},
{"identifier", "identifier-nondigit"},
{"identifier", "digit"}}},
{"digit", {{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9" }}},
{"identifier-nondigit", {{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "_"}}},
{"preprocessing-op-or-punc", {{";"},
{"="}}},
{"pp-number", {{"digit"},
{"pp-number", "digit"}}}
};
std::string Top{"program"};
BNF bnf{
{"program", {{"statement-list"}}},
{"statement-list", {{"statement", "statement-list"},
{}, }},
{"statement", {{"assigmnent", ";"}}},
{"assignment", {{"identifier", "=", "identifier"}}}
};
std::set<std::string> Terminals{"identifier", "=", ";"};
std::string Code{"a = b ; c = d ; e = f ;"};
auto tokens = Lex(Code, LexTop, LexBNF);
auto Program = Compile(tokens, Top, bnf, Terminals);
}
int main(int argc, char* argv[]) {
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
|