Mercurial > code
changeset 426:cee5c74c1c83
Ini:
- Multiple fixes about using invalid iterators,
- Add more tests about line/position in ini::Error.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 14 Oct 2015 10:04:24 +0200 |
parents | bb550fbd85e9 |
children | aa9cc55338be |
files | C++/modules/Ini/Ini.cpp C++/tests/Ini/configs/empty.conf C++/tests/Ini/configs/error-badinclude.conf C++/tests/Ini/configs/error-nosection.conf C++/tests/Ini/configs/error-unterminatedsection.conf C++/tests/Ini/configs/simple.conf C++/tests/Ini/main.cpp CMakeLists.txt |
diffstat | 8 files changed, 128 insertions(+), 37 deletions(-) [+] |
line wrap: on
line diff
--- a/C++/modules/Ini/Ini.cpp Mon Oct 12 22:18:27 2015 +0200 +++ b/C++/modules/Ini/Ini.cpp Wed Oct 14 10:04:24 2015 +0200 @@ -206,10 +206,17 @@ while (it != end && isReserved(*it)) { // Single character tokens switch (*it) { + case '#': + /* Skip comments */ + while (it != end && *it != '\n') { + ++ it; + } + tokens.push_back({ TokenType::Comment, lineno, position }); + position = 0; + break; case '\n': ++lineno; position = 0; - case '#': case '[': case ']': case '\'': @@ -226,12 +233,14 @@ } else if (std::isspace(*it)) { while (it != end && std::isspace(*it) && *it != '\n') { value.push_back(*it++); + ++position; } tokens.push_back({ TokenType::Space, lineno, position, std::move(value) }); } else { while (it != end && !std::isspace(*it) && !isReserved(*it)) { value.push_back(*it++); + ++position; } tokens.push_back({ TokenType::Word, lineno, position, std::move(value) }); @@ -241,13 +250,8 @@ return tokens; } - void readComment(TokenStack::iterator &it, TokenStack::iterator end) + void readComment(TokenStack::iterator &it, TokenStack::iterator) { - while (it != end && it->type() != TokenType::NewLine) { - ++ it; - } - - // remove new line ++ it; } @@ -267,14 +271,26 @@ Section readSection(TokenStack::iterator &it, TokenStack::iterator end) { - if (++it == end || it->type() != TokenType::Word) { - throw Error(it->line(), it->position(), "word expected after [, got " + it->toString()); + // Empty [ + if (++it == end) { + throw Error{it[-1].line(), it[-1].position(), "section declaration expected, got <EOF>"}; + } + + // Get the section name + if (it->type() != TokenType::Word) { + throw Error{it->line(), it->position(), "word expected after [, got " + it->toString()}; } Section section(it->value()); - if (++it == end || it->type() != TokenType::SectionEnd) { - throw Error(it->line(), it->position(), "] expected, got " + it->toString()); + // [unterminated + if (++it == end) { + throw Error{it[-1].line(), it[-1].position(), "unterminated section"}; + } + + // Check if terminated + if (it->type() != TokenType::SectionEnd) { + throw Error{it->line(), it->position(), "] expected, got " + it->toString()}; } // Remove ] @@ -284,6 +300,7 @@ return section; } + // Now read all that is allowed to be in a section while (it != end && it->type() != TokenType::SectionBegin) { if (it->type() == TokenType::Space) { readSpace(it, end); @@ -294,7 +311,7 @@ } else if (it->type() == TokenType::Word) { section.push_back(readOption(it, end)); } else { - throw Error(it->line(), it->position(), "unexpected token " + it->toString()); + throw Error{it->line(), it->position(), "unexpected token " + it->toString()}; } } @@ -303,16 +320,17 @@ Option readOption(TokenStack::iterator &it, TokenStack::iterator end) { - std::string key = it->value(); - - if (++it == end) { - throw Error(it->line(), it->position(), "expected '=' after option declaration, got <EOF>"); - } + std::string key = it++->value(); readSpace(it, end); - if (it == end || it->type() != TokenType::Assign) { - throw Error(it->line(), it->position(), "expected '=' after option declaration, got " + it++->toString()); + if (it == end) { + throw Error{it[-1].line(), it[-1].position(), "expected '=' after option declaration, got <EOF>"}; + } + + if (it->type() != TokenType::Assign) { + ++ it; + throw Error{it[-1].line(), it[-1].position(), "expected '=' after option declaration, got " + it[-1].toString()}; } readSpace(++it, end); @@ -326,39 +344,51 @@ oss << it++->value(); } - if (it == end) - throw Error(save->line(), save->position(), "undisclosed quote: " + save->toString() + " expected"); + if (it == end) { + throw Error{save->line(), save->position(), "undisclosed quote: " + save->toString() + " expected"}; + } ++ it; } else if (it->type() == TokenType::Word) { oss << it++->value(); } else if (it->type() != TokenType::NewLine && it->type() != TokenType::Comment) { // No value requested, must be NewLine or comment - throw Error(it->line(), it->position(), "expected option value after '=', got " + it->toString()); + throw Error{it->line(), it->position(), "expected option value after '=', got " + it->toString()}; } - - return Option(std::move(key), oss.str()); + return Option{std::move(key), oss.str()}; } void readInclude(TokenStack::iterator &it, TokenStack::iterator end) { - if (++it == end || (it->type() != TokenType::Word || it->value() != "include")) { - throw Error(it->line(), it->position(), "expected `include' after '@' token, got " + it->toString()); + if (++it == end) { + throw Error{it[-1].line(), it[-1].position(), "expected `include` after '@' token, got <EOF>"}; + } + + if (it->type() != TokenType::Word && it->value() != "include") { + throw Error{it->line(), it->position(), "expected `include' after '@' token, got " + it->toString()}; } readSpace(++it, end); - // Quotes mandatory TokenStack::iterator save = it; - if (it == end || (it->type() != TokenType::QuoteSimple && it->type() != TokenType::QuoteDouble)) { - throw Error(it->line(), it->position(), "expected filename after @include statement"); + if (it == end) { + throw Error{it[-1].line(), it[-1].position(), "expected filename after @include statement, got <EOF>"}; + } + + // First quote + if (it->type() != TokenType::QuoteSimple && it->type() != TokenType::QuoteDouble) { + throw Error{it->line(), it->position(), "expected filename after @include statement"}; } // Filename - if (++it == end || it->type() != TokenType::Word) { - throw Error(it->line(), it->position(), "expected filename after @include statement"); + if (++it == end) { + throw Error{it[-1].line(), it[-1].position(), "expected filename after @include statement, got <EOF>"}; + } + + if (it->type() != TokenType::Word) { + throw Error{it->line(), it->position(), "expected filename after @include statement"}; } std::string value = it->value(); @@ -371,8 +401,11 @@ } // Must be closed with the same quote - if (++it == end || it->type() != save->type()) { - throw Error(save->line(), save->position(), "undiclosed quote: " + save->toString() + " expected"); + if (++it == end) { + throw Error{save->line(), save->position(), "undiclosed quote: " + save->toString() + " expected, got <EOF>"}; + } + if (it->type() != save->type()) { + throw Error{save->line(), save->position(), "undiclosed quote: " + save->toString() + " expected"}; } // Remove quote
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/empty.conf Wed Oct 14 10:04:24 2015 +0200 @@ -0,0 +1,1 @@ +# this file is completely empty \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/error-badinclude.conf Wed Oct 14 10:04:24 2015 +0200 @@ -0,0 +1,1 @@ +@include noquotes \ No newline at end of file
--- a/C++/tests/Ini/configs/error-nosection.conf Mon Oct 12 22:18:27 2015 +0200 +++ b/C++/tests/Ini/configs/error-nosection.conf Wed Oct 14 10:04:24 2015 +0200 @@ -1,1 +1,3 @@ +# this file has no section +# and it's not valid option = value
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/C++/tests/Ini/configs/error-unterminatedsection.conf Wed Oct 14 10:04:24 2015 +0200 @@ -0,0 +1,2 @@ +# This file has unterminated section +[forgot \ No newline at end of file
--- a/C++/tests/Ini/configs/simple.conf Mon Oct 12 22:18:27 2015 +0200 +++ b/C++/tests/Ini/configs/simple.conf Wed Oct 14 10:04:24 2015 +0200 @@ -2,3 +2,5 @@ option1=1 option2 =2 option3 = 3 + +# This file ends with a comment. \ No newline at end of file
--- a/C++/tests/Ini/main.cpp Mon Oct 12 22:18:27 2015 +0200 +++ b/C++/tests/Ini/main.cpp Wed Oct 14 10:04:24 2015 +0200 @@ -195,6 +195,19 @@ } /* -------------------------------------------------------- + * Empty + * -------------------------------------------------------- */ + +TEST(Empty, test) +{ + try { + ini::Document doc{"Ini/empty.conf"}; + } catch (const ini::Error &error) { + FAIL() << error.line() << ":" << error.position() << ": " << error.what(); + } +} + +/* -------------------------------------------------------- * Errors * -------------------------------------------------------- */ @@ -205,7 +218,9 @@ ini::Document doc("Ini/error-nosection.conf"); FAIL() << "Failure expected, got success"; - } catch (const std::exception &ex) { + } catch (const ini::Error &ex) { + ASSERT_EQ(3, ex.line()); + ASSERT_EQ(7, ex.position()); } } @@ -216,7 +231,9 @@ ini::Document doc("Ini/error-lineassigment.conf"); FAIL() << "Failure expected, got success"; - } catch (const std::exception &ex) { + } catch (const ini::Error &ex) { + ASSERT_EQ(3, ex.line()); + ASSERT_EQ(0, ex.position()); } } @@ -227,7 +244,9 @@ ini::Document doc("Ini/error-badcomment.conf"); FAIL() << "Failure expected, got success"; - } catch (const std::exception &ex) { + } catch (const ini::Error &ex) { + ASSERT_EQ(2, ex.line()); + ASSERT_EQ(9, ex.position()); } } @@ -238,7 +257,35 @@ ini::Document doc("Ini/error-badsection.conf"); FAIL() << "Failure expected, got success"; - } catch (const std::exception &ex) { + } catch (const ini::Error &ex) { + ASSERT_EQ(1, ex.line()); + ASSERT_EQ(1, ex.position()); + } +} + +TEST(Errors, unterminatedsection) +{ + // Section unfinished + try { + ini::Document doc("Ini/error-unterminatedsection.conf"); + + FAIL() << "Failure expected, got success"; + } catch (const ini::Error &ex) { + ASSERT_EQ(2, ex.line()); + ASSERT_EQ(8, ex.position()); + } +} + +TEST(Errors, badinclude) +{ + // Section unfinished + try { + ini::Document doc("Ini/error-badinclude.conf"); + + FAIL() << "Failure expected, got success"; + } catch (const ini::Error &ex) { + ASSERT_EQ(1, ex.line()); + ASSERT_EQ(17, ex.position()); } }
--- a/CMakeLists.txt Mon Oct 12 22:18:27 2015 +0200 +++ b/CMakeLists.txt Wed Oct 14 10:04:24 2015 +0200 @@ -232,10 +232,13 @@ ${INI_LIBRARIES} RESOURCES ${code_SOURCE_DIR}/C++/tests/Ini/configs/compact.conf + ${code_SOURCE_DIR}/C++/tests/Ini/configs/empty.conf ${code_SOURCE_DIR}/C++/tests/Ini/configs/error-badcomment.conf + ${code_SOURCE_DIR}/C++/tests/Ini/configs/error-badinclude.conf ${code_SOURCE_DIR}/C++/tests/Ini/configs/error-badsection.conf ${code_SOURCE_DIR}/C++/tests/Ini/configs/error-lineassigment.conf ${code_SOURCE_DIR}/C++/tests/Ini/configs/error-nosection.conf + ${code_SOURCE_DIR}/C++/tests/Ini/configs/error-unterminatedsection.conf ${code_SOURCE_DIR}/C++/tests/Ini/configs/includes.conf ${code_SOURCE_DIR}/C++/tests/Ini/configs/multi.conf ${code_SOURCE_DIR}/C++/tests/Ini/configs/novalue.conf