Mercurial > code
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 |