comparison C++/modules/Ini/Ini.cpp @ 430:625f5d64b093

Ini: - Add support of lists ( "a", "b" ), - Add more noexcept, - Add some assert.
author David Demelier <markand@malikania.fr>
date Wed, 14 Oct 2015 20:19:28 +0200
parents 31bddece9860
children 88df9c580c36
comparison
equal deleted inserted replaced
429:31bddece9860 430:625f5d64b093
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */ 17 */
18 18
19 #include <cassert>
19 #include <cctype> 20 #include <cctype>
20 #include <iostream> 21 #include <iostream>
21 #include <iterator> 22 #include <iterator>
22 #include <fstream> 23 #include <fstream>
23 #include <sstream> 24 #include <sstream>
31 32
32 namespace { 33 namespace {
33 34
34 using namespace ini; 35 using namespace ini;
35 36
36 using Iterator = std::istreambuf_iterator<char>; 37 using StreamIterator = std::istreambuf_iterator<char>;
37 using TokenIterator = std::vector<Token>::const_iterator; 38 using TokenIterator = std::vector<Token>::const_iterator;
38 39
39 inline bool isAbsolute(const std::string &path) noexcept 40 inline bool isAbsolute(const std::string &path) noexcept
40 { 41 {
41 #if defined(_WIN32) 42 #if defined(_WIN32)
54 { 55 {
55 /* Custom version because std::isspace includes \n as space */ 56 /* Custom version because std::isspace includes \n as space */
56 return c == ' ' || c == '\t'; 57 return c == ' ' || c == '\t';
57 } 58 }
58 59
60 inline bool isList(char c) noexcept
61 {
62 return c == '(' || c == ')' || c == ',';
63 }
64
59 inline bool isReserved(char c) noexcept 65 inline bool isReserved(char c) noexcept
60 { 66 {
61 return c == '[' || c == ']' || c == '@' || c == '#' || c == '=' || c == '\'' || c == '"'; 67 return isList(c) || isQuote(c) || c == '[' || c == ']' || c == '@' || c == '#' || c == '=';
62 } 68 }
63 69
64 void analyzeLine(int &line, int &column, Iterator &it) 70 void analyzeLine(int &line, int &column, StreamIterator &it) noexcept
65 { 71 {
72 assert(*it == '\n');
73
66 ++ line; 74 ++ line;
67 ++ it; 75 ++ it;
68 column = 0; 76 column = 0;
69 } 77 }
70 78
71 void analyzeComment(int &column, Iterator &it, Iterator end) 79 void analyzeComment(int &column, StreamIterator &it, StreamIterator end) noexcept
72 { 80 {
81 assert(*it == '#');
82
73 while (it != end && *it != '\n') { 83 while (it != end && *it != '\n') {
74 ++ column; 84 ++ column;
75 ++ it; 85 ++ it;
76 } 86 }
77 } 87 }
78 88
79 void analyzeSpaces(int &column, Iterator &it, Iterator end) 89 void analyzeSpaces(int &column, StreamIterator &it, StreamIterator end) noexcept
80 { 90 {
91 assert(isSpace(*it));
92
81 while (it != end && isSpace(*it)) { 93 while (it != end && isSpace(*it)) {
82 ++ column; 94 ++ column;
83 ++ it; 95 ++ it;
84 } 96 }
85 } 97 }
86 98
87 void analyzeSection(Tokens &list, int &line, int &column, Iterator &it, Iterator end) 99 void analyzeList(Tokens &list, int line, int &column, StreamIterator &it) noexcept
88 { 100 {
101 assert(isList(*it));
102
103 switch (*it++) {
104 case '(':
105 list.emplace_back(Token::ListBegin, line, column++);
106 break;
107 case ')':
108 list.emplace_back(Token::ListEnd, line, column++);
109 break;
110 case ',':
111 list.emplace_back(Token::Comma, line, column++);
112 break;
113 default:
114 break;
115 }
116 }
117
118 void analyzeSection(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
119 {
120 assert(*it == '[');
121
89 std::string value; 122 std::string value;
90 int save = column; 123 int save = column;
91 124
92 /* Read section name */ 125 /* Read section name */
93 ++ it; 126 ++ it;
107 } 140 }
108 141
109 /* Remove ']' */ 142 /* Remove ']' */
110 ++ it; 143 ++ it;
111 144
112 list.push_back({ Token::Section, line, save, std::move(value) }); 145 list.emplace_back(Token::Section, line, save, std::move(value));
113 } 146 }
114 147
115 void analyzeAssign(Tokens &list, int &line, int &column, Iterator &it) 148 void analyzeAssign(Tokens &list, int &line, int &column, StreamIterator &it)
116 { 149 {
150 assert(*it == '=');
151
117 list.push_back({ Token::Assign, line, column++ }); 152 list.push_back({ Token::Assign, line, column++ });
118 ++ it; 153 ++ it;
119 } 154 }
120 155
121 void analyzeQuotedWord(Tokens &list, int &line, int &column, Iterator &it, Iterator end) 156 void analyzeQuotedWord(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
122 { 157 {
123 std::string value; 158 std::string value;
124 int save = column; 159 int save = column;
125 char quote = *it++; 160 char quote = *it++;
126 161
138 ++ it; 173 ++ it;
139 174
140 list.push_back({ Token::QuotedWord, line, save, std::move(value) }); 175 list.push_back({ Token::QuotedWord, line, save, std::move(value) });
141 } 176 }
142 177
143 void analyzeWord(Tokens &list, int &line, int &column, Iterator &it, Iterator end) 178 void analyzeWord(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
144 { 179 {
180 assert(!isReserved(*it));
181
145 std::string value; 182 std::string value;
146 int save = column; 183 int save = column;
147 184
148 while (it != end && !std::isspace(*it) && !isReserved(*it)) { 185 while (it != end && !std::isspace(*it) && !isReserved(*it)) {
149 ++ column; 186 ++ column;
151 } 188 }
152 189
153 list.push_back({ Token::Word, line, save, std::move(value) }); 190 list.push_back({ Token::Word, line, save, std::move(value) });
154 } 191 }
155 192
156 void analyzeInclude(Tokens &list, int &line, int &column, Iterator &it, Iterator end) 193 void analyzeInclude(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
157 { 194 {
195 assert(*it == '@');
196
158 std::string include; 197 std::string include;
159 int save = column; 198 int save = column;
160 199
161 /* Read include */ 200 /* Read include */
162 ++ it; 201 ++ it;
170 } 209 }
171 210
172 list.push_back({ Token::Include, line, save }); 211 list.push_back({ Token::Include, line, save });
173 } 212 }
174 213
175 Tokens analyze(std::istreambuf_iterator<char> &it, std::istreambuf_iterator<char> end) 214 Tokens analyze(StreamIterator &it, StreamIterator end)
176 { 215 {
177 Tokens list; 216 Tokens list;
178 int line = 1; 217 int line = 1;
179 int column = 0; 218 int column = 0;
180 219
191 analyzeSpaces(column, it, end); 230 analyzeSpaces(column, it, end);
192 } else if (*it == '@') { 231 } else if (*it == '@') {
193 analyzeInclude(list, line, column, it, end); 232 analyzeInclude(list, line, column, it, end);
194 } else if (isQuote(*it)) { 233 } else if (isQuote(*it)) {
195 analyzeQuotedWord(list, line, column, it, end); 234 analyzeQuotedWord(list, line, column, it, end);
235 } else if (isList(*it)) {
236 analyzeList(list, line, column, it);
196 } else { 237 } else {
197 analyzeWord(list, line, column, it, end); 238 analyzeWord(list, line, column, it, end);
198 } 239 }
199 } 240 }
200 241
201 return list; 242 return list;
202 } 243 }
203 244
245 void parseOptionValueSimple(Option &option, TokenIterator &it)
246 {
247 assert(it->type() == Token::Word || it->type() == Token::QuotedWord);
248
249 option.push_back((it++)->value());
250 }
251
252 void parseOptionValueList(Option &option, TokenIterator &it, TokenIterator end)
253 {
254 assert(it->type() == Token::ListBegin);
255
256 TokenIterator save = it++;
257
258 while (it != end && it->type() != Token::ListEnd) {
259 switch (it->type()) {
260 case Token::Comma:
261 /* Previous must be a word */
262 if (it[-1].type() != Token::Word && it[-1].type() != Token::QuotedWord) {
263 throw Error{it->line(), it->column(), "unexpected comma after '" + it[-1].value() + "'"};
264 }
265
266 ++ it;
267 break;
268 case Token::Word:
269 case Token::QuotedWord:
270 option.push_back((it++)->value());
271 break;
272 default:
273 throw Error{it->line(), it->column(), "unexpected '" + it[-1].value() + "' in list construct"};
274 break;
275 }
276 }
277
278 if (it == end) {
279 throw Error{save->line(), save->column(), "unterminated list construct"};
280 }
281
282 /* Remove ) */
283 ++ it;
284 }
285
204 void parseOption(Section &sc, TokenIterator &it, TokenIterator end) 286 void parseOption(Section &sc, TokenIterator &it, TokenIterator end)
205 { 287 {
206 std::string key = it->value(); 288 Option option{it->value()};
207 std::string value;
208 289
209 TokenIterator save = it; 290 TokenIterator save = it;
210 291
211 /* No '=' or something else? */ 292 /* No '=' or something else? */
212 if (++it == end) { 293 if (++it == end) {
217 } 298 }
218 299
219 /* Empty options are allowed so just test for words */ 300 /* Empty options are allowed so just test for words */
220 if (++it != end) { 301 if (++it != end) {
221 if (it->type() == Token::Word || it->type() == Token::QuotedWord) { 302 if (it->type() == Token::Word || it->type() == Token::QuotedWord) {
222 value = it++->value(); 303 parseOptionValueSimple(option, it);
223 } 304 } else if (it->type() == Token::ListBegin) {
224 } 305 parseOptionValueList(option, it, end);
225 306 }
226 sc.emplace_back(std::move(key), std::move(value)); 307 }
308
309 sc.push_back(std::move(option));
227 } 310 }
228 311
229 void parseInclude(Document &doc, TokenIterator &it, TokenIterator end) 312 void parseInclude(Document &doc, TokenIterator &it, TokenIterator end)
230 { 313 {
231 TokenIterator save = it; 314 TokenIterator save = it;
338 parse(*this, analyze(file)); 421 parse(*this, analyze(file));
339 } 422 }
340 423
341 Document::Document(const Buffer &buffer) 424 Document::Document(const Buffer &buffer)
342 { 425 {
343 dump(analyze(buffer));
344 parse(*this, analyze(buffer)); 426 parse(*this, analyze(buffer));
345 } 427 }
346 428
347 void Document::dump(const Tokens &tokens) 429 void Document::dump(const Tokens &tokens)
348 { 430 {
349 for (const Token &token: tokens) { 431 for (const Token &token: tokens) {
350 // TODO: type 432 // TODO: add better description
351 std::cout << token.line() << ":" << token.column() << ": " << token.value() << std::endl; 433 std::cout << token.line() << ":" << token.column() << ": " << token.value() << std::endl;
352 } 434 }
353 } 435 }
354 436
355 } // !ini 437 } // !ini