patrick
/
plasp
Archived
1
0
Fork 0

Made Parser seekable for easier maintenance.

This commit is contained in:
Patrick Lühne 2016-06-07 18:41:01 +02:00
parent 27e76fc035
commit eea3272c56
11 changed files with 221 additions and 134 deletions

View File

@ -28,7 +28,7 @@ class Description
const Domain &domain() const;
private:
Description(std::istream &istream);
Description();
void parseContent();
void parseSection();

View File

@ -47,6 +47,8 @@ class Description
private:
Description();
void parseContent(utils::Parser &parser);
void parseVersionSection(utils::Parser &parser) const;
void parseMetricSection(utils::Parser &parser);
void parseVariablesSection(utils::Parser &parser);

View File

@ -23,7 +23,7 @@ class Logger
void setPedantic(bool isPedantic = true);
void parserWarning(const Parser &parser, const std::string &text);
void parserWarning(const Parser &parser, const std::string &message);
private:
bool m_isPedantic;

View File

@ -3,8 +3,11 @@
#include <iostream>
#include <iterator>
#include <sstream>
#include <vector>
#include <boost/filesystem.hpp>
namespace plasp
{
namespace utils
@ -19,22 +22,39 @@ namespace utils
class Parser
{
public:
explicit Parser(std::istream &istream);
using Position = std::stringstream::pos_type;
void setFileName(std::string fileName);
const std::string &fileName() const;
struct Coordinate
{
std::string sectionName;
size_t row;
size_t column;
};
void resetPosition();
struct StreamDelimiter
{
Position position;
std::string sectionName;
};
size_t row() const;
size_t column() const;
public:
explicit Parser();
explicit Parser(std::string streamName, std::istream &istream);
void readStream(std::string streamName, std::istream &istream);
void readFile(const boost::filesystem::path &path);
void reset();
void seek(Position position);
Position position() const;
Coordinate coordinate() const;
void setCaseSensitive(bool isCaseInsensitive = true);
char currentCharacter() const;
void advance();
bool advanceIf(char expectedCharacter);
bool atEndOfFile() const;
bool atEndOfStream() const;
template<typename Type>
Type parse();
@ -64,16 +84,11 @@ class Parser
uint64_t parseIntegerBody();
std::istream &m_istream;
std::string m_fileName;
std::istreambuf_iterator<char> m_position;
mutable std::stringstream m_stream;
size_t m_row;
size_t m_column;
std::vector<StreamDelimiter> m_streamDelimiters;
bool m_isCaseSensitive;
bool m_atEndOfFile;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -116,7 +131,7 @@ void Parser::skipWhiteSpace(WhiteSpacePredicate whiteSpacePredicate)
{
checkStream();
while (!atEndOfFile() && whiteSpacePredicate(currentCharacter()))
while (!atEndOfStream() && whiteSpacePredicate(currentCharacter()))
advance();
}

View File

@ -31,8 +31,10 @@ class ParserException: public std::exception
}
explicit ParserException(const utils::Parser &parser, const std::string &message)
: m_message{parser.fileName() + ":" + std::to_string(parser.row()) + ":" + std::to_string(parser.column()) + " " + message}
{
const auto coordinate = parser.coordinate();
m_message = coordinate.sectionName + ":" + std::to_string(coordinate.row) + ":" + std::to_string(coordinate.column) + " " + message;
}
~ParserException() throw()

View File

@ -31,8 +31,10 @@ class ParserWarning: public std::exception
}
explicit ParserWarning(const utils::Parser &parser, const std::string &message)
: m_message{parser.fileName() + ":" + std::to_string(parser.row()) + ":" + std::to_string(parser.column()) + " " + message}
{
const auto coordinate = parser.coordinate();
m_message = coordinate.sectionName + ":" + std::to_string(coordinate.row) + ":" + std::to_string(coordinate.column) + " " + message;
}
~ParserWarning() throw()

View File

@ -18,9 +18,8 @@ namespace pddl
//
////////////////////////////////////////////////////////////////////////////////////////////////////
Description::Description(std::istream &istream)
: m_parser(istream),
m_context(m_parser),
Description::Description()
: m_context(m_parser),
m_domain{std::make_unique<Domain>(Domain(m_context))},
m_problem{std::make_unique<Problem>(Problem(m_context, *m_domain))}
{
@ -31,9 +30,9 @@ Description::Description(std::istream &istream)
Description Description::fromStream(std::istream &istream)
{
Description description(istream);
Description description;
description.m_parser.setFileName("std::cin");
description.m_parser.readStream("std::cin", istream);
description.parseContent();
description.checkConsistency();
@ -47,29 +46,15 @@ Description Description::fromFiles(const std::vector<std::string> &paths)
{
BOOST_ASSERT(!paths.empty());
std::for_each(paths.cbegin(), paths.cend(),
[&](const auto &path)
{
if (!boost::filesystem::is_regular_file(path))
throw std::runtime_error("File does not exist: \"" + path + "\"");
});
std::ifstream fileStream;
Description description(fileStream);
Description description;
std::for_each(paths.cbegin(), paths.cend(),
[&](const auto &path)
{
fileStream.close();
fileStream.clear();
fileStream.open(path, std::ios::in);
description.m_parser.setFileName(path);
description.m_parser.resetPosition();
description.parseContent();
description.m_parser.readFile(path);
});
description.parseContent();
description.checkConsistency();
return description;
@ -92,7 +77,7 @@ void Description::parseContent()
{
m_context.parser.skipWhiteSpace();
if (m_context.parser.atEndOfFile())
if (m_context.parser.atEndOfStream())
return;
m_context.parser.expect<std::string>("(");

View File

@ -30,18 +30,11 @@ Description::Description()
Description Description::fromStream(std::istream &istream)
{
utils::Parser parser;
parser.readStream("std::cin", istream);
Description description;
utils::Parser parser(istream);
description.parseVersionSection(parser);
description.parseMetricSection(parser);
description.parseVariablesSection(parser);
description.parseMutexSection(parser);
description.parseInitialStateSection(parser);
description.parseGoalSection(parser);
description.parseOperatorSection(parser);
description.parseAxiomSection(parser);
description.parseContent(parser);
return description;
}
@ -53,9 +46,13 @@ Description Description::fromFile(const boost::filesystem::path &path)
if (!boost::filesystem::is_regular_file(path))
throw std::runtime_error("File does not exist: \"" + path.string() + "\"");
std::ifstream fileStream(path.string(), std::ios::in);
utils::Parser parser;
parser.readFile(path);
return Description::fromStream(fileStream);
Description description;
description.parseContent(parser);
return description;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -146,6 +143,20 @@ bool Description::usesConditionalEffects() const
////////////////////////////////////////////////////////////////////////////////////////////////////
void Description::parseContent(utils::Parser &parser)
{
parseVersionSection(parser);
parseMetricSection(parser);
parseVariablesSection(parser);
parseMutexSection(parser);
parseInitialStateSection(parser);
parseGoalSection(parser);
parseOperatorSection(parser);
parseAxiomSection(parser);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void Description::parseVersionSection(utils::Parser &parser) const
{
parser.expect<std::string>("begin_version");

View File

@ -27,12 +27,16 @@ void Logger::setPedantic(bool isPedantic)
////////////////////////////////////////////////////////////////////////////////////////////////////
void Logger::parserWarning(const Parser &parser, const std::string &text)
void Logger::parserWarning(const Parser &parser, const std::string &message)
{
if (m_isPedantic)
throw ParserWarning(parser, text);
throw ParserWarning(parser, message);
std::cerr << "Warning: " << parser.fileName() << ":" << parser.row() << ":" << parser.column() << " " << text << std::endl;
const auto coordinate = parser.coordinate();
std::cerr << "Warning: " << coordinate.sectionName << ":"
<< std::to_string(coordinate.row) + ":" + std::to_string(coordinate.column)
<< " " << message << std::endl;
}
////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -1,6 +1,7 @@
#include <plasp/utils/Parser.h>
#include <algorithm>
#include <fstream>
#include <boost/assert.hpp>
@ -21,56 +22,116 @@ const std::istreambuf_iterator<char> Parser::EndOfFile = std::istreambuf_iterato
////////////////////////////////////////////////////////////////////////////////////////////////////
Parser::Parser(std::istream &istream)
: m_istream(istream),
m_position(m_istream),
m_row{1},
m_column{1},
m_isCaseSensitive{true},
m_atEndOfFile{false}
Parser::Parser()
: m_isCaseSensitive{true}
{
std::setlocale(LC_NUMERIC, "C");
// Dont skip whitespace
istream.exceptions(std::istream::badbit);
m_stream.exceptions(std::istream::badbit);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void Parser::setFileName(std::string fileName)
Parser::Parser(std::string streamName, std::istream &istream)
: Parser()
{
m_fileName = fileName;
readStream(streamName, istream);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
const std::string &Parser::fileName() const
void Parser::readStream(std::string streamName, std::istream &istream)
{
return m_fileName;
// Store position of new section
const auto position = m_stream.tellp();
m_streamDelimiters.push_back({position, streamName});
m_stream << istream.rdbuf();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void Parser::resetPosition()
void Parser::readFile(const boost::filesystem::path &path)
{
m_row = 1;
m_column = 1;
m_atEndOfFile = false;
m_position = std::istreambuf_iterator<char>(m_istream);
if (!boost::filesystem::is_regular_file(path))
throw std::runtime_error("File does not exist: \"" + path.string() + "\"");
std::ifstream fileStream(path.string(), std::ios::in);
readStream(path.string(), fileStream);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
size_t Parser::row() const
void Parser::reset()
{
return m_row;
m_stream.clear();
seek(std::ios::beg);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
size_t Parser::column() const
void Parser::seek(Position position)
{
return m_column;
m_stream.clear();
m_stream.seekg(position);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Parser::Position Parser::position() const
{
return m_stream.tellg();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
Parser::Coordinate Parser::coordinate() const
{
const auto currentPosition = position();
// Find current section
auto currentFile = std::find_if(m_streamDelimiters.crbegin(), m_streamDelimiters.crend(),
[&](const auto &fileDelimiter)
{
return currentPosition >= fileDelimiter.position;
});
// If the parser is at the end of the stream, still count from the beginning of the last section
if (currentFile == m_streamDelimiters.crend())
currentFile = m_streamDelimiters.crbegin();
// Go back to beginning of section
m_stream.clear();
m_stream.seekg(currentFile->position);
size_t row = 1;
size_t column = 1;
// Compute the coordinate character by character
while (true)
{
if (currentPosition == -1 && atEndOfStream())
break;
else if (currentPosition >= 0 && position() >= currentPosition)
break;
const auto character = currentCharacter();
if (character == '\n')
{
row++;
column = 1;
}
else if (std::isblank(character) || std::isprint(character))
column++;
m_stream.ignore(1);
}
return {currentFile->sectionName, row, column};
}
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -84,29 +145,27 @@ void Parser::setCaseSensitive(bool isCaseSensitive)
char Parser::currentCharacter() const
{
checkStream();
if (m_isCaseSensitive)
return *m_position;
return m_stream.peek();
return std::tolower(*m_position);
return std::tolower(m_stream.peek());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool Parser::atEndOfFile() const
bool Parser::atEndOfStream() const
{
return m_position.equal(EndOfFile);
return position() == -1;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void Parser::checkStream() const
{
if (atEndOfFile())
if (atEndOfStream())
throw ParserException(*this, "Reading past end of file");
if (m_istream.fail())
if (m_stream.fail())
throw ParserException(*this);
}
@ -115,18 +174,7 @@ void Parser::checkStream() const
void Parser::advance()
{
checkStream();
const auto character = currentCharacter();
if (character == '\n')
{
m_row++;
m_column = 1;
}
else if (std::isblank(character) || std::isprint(character))
m_column++;
m_position++;
m_stream.ignore(1);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -246,7 +294,7 @@ uint64_t Parser::parseIntegerBody()
uint64_t value = 0;
while (!atEndOfFile())
while (!atEndOfStream())
{
const auto character = currentCharacter();

View File

@ -9,7 +9,7 @@
TEST(UtilsTests, ParseSimple)
{
std::stringstream s("identifier 5 \n-51\t 0 1 expected unexpected");
plasp::utils::Parser p(s);
plasp::utils::Parser p("input", s);
ASSERT_EQ(p.parse<std::string>(), "identifier");
ASSERT_EQ(p.parse<size_t>(), 5u);
@ -19,6 +19,8 @@ TEST(UtilsTests, ParseSimple)
ASSERT_NO_THROW(p.expect<std::string>("expected"));
ASSERT_THROW(p.expect<std::string>("expected"), plasp::utils::ParserException);
// TODO: test case-insensitive input
}
////////////////////////////////////////////////////////////////////////////////////////////////////
@ -26,7 +28,7 @@ TEST(UtilsTests, ParseSimple)
TEST(UtilsTests, ParseUnsignedNumbers)
{
std::stringstream s("100 200 -300 -400");
plasp::utils::Parser p(s);
plasp::utils::Parser p("input", s);
ASSERT_EQ(p.parse<int>(), 100);
ASSERT_EQ(p.parse<size_t>(), 200u);
@ -39,13 +41,13 @@ TEST(UtilsTests, ParseUnsignedNumbers)
TEST(UtilsTests, ParseEndOfFile)
{
std::stringstream s1("test");
plasp::utils::Parser p1(s1);
plasp::utils::Parser p1("input", s1);
ASSERT_NO_THROW(p1.expect<std::string>("test"));
ASSERT_THROW(p1.parse<std::string>(), plasp::utils::ParserException);
std::stringstream s2("test1 test2 test3");
plasp::utils::Parser p2(s2);
plasp::utils::Parser p2("input", s2);
ASSERT_NO_THROW(p2.expect<std::string>("test1"));
ASSERT_NO_THROW(p2.expect<std::string>("test2"));
@ -53,13 +55,13 @@ TEST(UtilsTests, ParseEndOfFile)
ASSERT_THROW(p2.parse<std::string>(), plasp::utils::ParserException);
std::stringstream s3("-127");
plasp::utils::Parser p3(s3);
plasp::utils::Parser p3("input", s3);
p3.expect<int>(-127);
ASSERT_THROW(p3.parse<int>(), plasp::utils::ParserException);
std::stringstream s4("128 -1023 -4095");
plasp::utils::Parser p4(s4);
plasp::utils::Parser p4("input", s4);
ASSERT_NO_THROW(p4.expect<size_t>(128));
ASSERT_NO_THROW(p4.expect<int>(-1023));
@ -67,13 +69,13 @@ TEST(UtilsTests, ParseEndOfFile)
ASSERT_THROW(p4.parse<int>(), plasp::utils::ParserException);
std::stringstream s5("0");
plasp::utils::Parser p5(s5);
plasp::utils::Parser p5("input", s5);
p5.expect<bool>(false);
ASSERT_THROW(p5.parse<bool>(), plasp::utils::ParserException);
std::stringstream s6("0 1 0");
plasp::utils::Parser p6(s6);
plasp::utils::Parser p6("input", s6);
ASSERT_NO_THROW(p6.expect<bool>(false));
ASSERT_NO_THROW(p6.expect<bool>(true));
@ -86,74 +88,90 @@ TEST(UtilsTests, ParseEndOfFile)
TEST(UtilsTests, ParserPosition)
{
std::stringstream s("123 \n4\ntest1\n test2\ntest3 \ntest4\n\n\n\n");
plasp::utils::Parser p(s);
plasp::utils::Parser p("input", s);
ASSERT_EQ(p.row(), 1u);
ASSERT_EQ(p.column(), 1u);
plasp::utils::Parser::Coordinate c;
c = p.coordinate();
ASSERT_EQ(c.row, 1u);
ASSERT_EQ(c.column, 1u);
ASSERT_EQ(p.currentCharacter(), '1');
ASSERT_NO_THROW(p.advance());
ASSERT_EQ(p.row(), 1u);
ASSERT_EQ(p.column(), 2u);
c = p.coordinate();
ASSERT_EQ(c.row, 1u);
ASSERT_EQ(c.column, 2u);
ASSERT_EQ(p.currentCharacter(), '2');
ASSERT_NO_THROW(p.advance());
ASSERT_EQ(p.row(), 1u);
ASSERT_EQ(p.column(), 3u);
c = p.coordinate();
ASSERT_EQ(c.row, 1u);
ASSERT_EQ(c.column, 3u);
ASSERT_EQ(p.currentCharacter(), '3');
ASSERT_NO_THROW(p.advance());
ASSERT_EQ(p.row(), 1u);
ASSERT_EQ(p.column(), 4u);
c = p.coordinate();
ASSERT_EQ(c.row, 1u);
ASSERT_EQ(c.column, 4u);
ASSERT_EQ(p.currentCharacter(), ' ');
ASSERT_NO_THROW(p.advance());
ASSERT_EQ(p.row(), 1u);
ASSERT_EQ(p.column(), 5u);
c = p.coordinate();
ASSERT_EQ(c.row, 1u);
ASSERT_EQ(c.column, 5u);
ASSERT_EQ(p.currentCharacter(), '\n');
ASSERT_NO_THROW(p.advance());
ASSERT_EQ(p.row(), 2u);
ASSERT_EQ(p.column(), 1u);
c = p.coordinate();
ASSERT_EQ(c.row, 2u);
ASSERT_EQ(c.column, 1u);
ASSERT_EQ(p.currentCharacter(), '4');
ASSERT_NO_THROW(p.advance());
ASSERT_NO_THROW(p.expect<std::string>("test1"));
ASSERT_EQ(p.row(), 3u);
ASSERT_EQ(p.column(), 6u);
c = p.coordinate();
ASSERT_EQ(c.row, 3u);
ASSERT_EQ(c.column, 6u);
ASSERT_NO_THROW(p.expect<std::string>("test2"));
ASSERT_EQ(p.row(), 4u);
ASSERT_EQ(p.column(), 7u);
c = p.coordinate();
ASSERT_EQ(c.row, 4u);
ASSERT_EQ(c.column, 7u);
ASSERT_NO_THROW(p.expect<std::string>("test3"));
ASSERT_EQ(p.row(), 5u);
ASSERT_EQ(p.column(), 6u);
c = p.coordinate();
ASSERT_EQ(c.row, 5u);
ASSERT_EQ(c.column, 6u);
ASSERT_NO_THROW(p.skipLine());
ASSERT_EQ(p.row(), 6u);
ASSERT_EQ(p.column(), 1u);
c = p.coordinate();
ASSERT_EQ(c.row, 6u);
ASSERT_EQ(c.column, 1u);
ASSERT_NO_THROW(p.skipLine());
ASSERT_EQ(p.row(), 7u);
ASSERT_EQ(p.column(), 1u);
c = p.coordinate();
ASSERT_EQ(c.row, 7u);
ASSERT_EQ(c.column, 1u);
ASSERT_NO_THROW(p.skipWhiteSpace());
ASSERT_TRUE(p.atEndOfFile());
ASSERT_EQ(p.row(), 10u);
ASSERT_EQ(p.column(), 1u);
c = p.coordinate();
ASSERT_EQ(c.row, 10u);
ASSERT_EQ(c.column, 1u);
ASSERT_TRUE(p.atEndOfStream());
// TODO: test parser with multiple sections
}
////////////////////////////////////////////////////////////////////////////////////////////////////