Started reimplementing problem parser.

This commit is contained in:
2017-06-13 19:52:15 +02:00
parent 06b9632b70
commit a7c4fdb242
43 changed files with 2566 additions and 278 deletions

View File

@@ -12,6 +12,7 @@ file(GLOB detail_parsing_headers "../include/pddlparse/detail/parsing/*.h")
set(includes
${PROJECT_SOURCE_DIR}/include
${PROJECT_SOURCE_DIR}/../../lib/tokenize/include
${PROJECT_SOURCE_DIR}/../../lib/variant/include
)
set(sources

View File

@@ -0,0 +1,22 @@
#include <pddlparse/Parse.h>
#include <pddlparse/AST.h>
#include <pddlparse/detail/parsing/Description.h>
namespace pddl
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Parse
//
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::Description parseDescription(Context &context)
{
return detail::DescriptionParser(context).parse();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}

View File

@@ -0,0 +1,92 @@
#include <pddlparse/detail/Requirements.h>
#include <algorithm>
#include <pddlparse/detail/parsing/Requirement.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Requirements
//
////////////////////////////////////////////////////////////////////////////////////////////////////
bool hasRequirement(const ast::Domain &domain, ast::Requirement requirement)
{
const auto match = std::find_if(domain.requirements.cbegin(), domain.requirements.cend(),
[&](const auto &declaredRequirement)
{
return declaredRequirement == requirement;
});
return match != domain.requirements.cend();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool hasRequirement(const ast::Problem &problem, ast::Requirement requirement)
{
const auto match = std::find_if(problem.requirements.cbegin(), problem.requirements.cend(),
[&](const auto &declaredRequirement)
{
return declaredRequirement == requirement;
});
if (match != problem.requirements.cend())
return true;
return hasRequirement(problem.domain, requirement);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
bool hasRequirement(const ASTContext &astContext, ast::Requirement requirement)
{
if (astContext.problem)
return hasRequirement(*astContext.problem.value(), requirement);
return hasRequirement(*astContext.domain, requirement);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void checkRequirement(ast::Domain &domain, ast::Requirement requirement, Context &context)
{
if (hasRequirement(domain, requirement))
return;
context.warningCallback(context.tokenizer.location(), "requirement “" + std::string(toString(requirement)) + "” used but never declared");
domain.requirements.push_back(requirement);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void checkRequirement(ast::Problem &problem, ast::Requirement requirement, Context &context)
{
if (hasRequirement(problem, requirement))
return;
context.warningCallback(context.tokenizer.location(), "requirement “" + std::string(toString(requirement)) + "” used but never declared");
problem.requirements.push_back(requirement);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void checkRequirement(ASTContext &astContext, ast::Requirement requirement, Context &context)
{
if (astContext.problem)
checkRequirement(*astContext.problem.value(), requirement, context);
else
checkRequirement(*astContext.domain, requirement, context);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,68 @@
#include <pddlparse/detail/parsing/Action.h>
#include <pddlparse/AST.h>
// TODO: remove
#include <pddlparse/detail/parsing/Utils.h>
#include <pddlparse/detail/parsing/VariableDeclaration.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Action
//
////////////////////////////////////////////////////////////////////////////////////////////////////
void parseAndAddAction(Context &context, ast::Domain &domain)
{
auto &tokenizer = context.tokenizer;
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>(":");
tokenizer.expect<std::string>("action");
auto action = std::make_unique<ast::Action>();
action->name = tokenizer.getIdentifier();
tokenizer.expect<std::string>(":parameters");
tokenizer.expect<std::string>("(");
// Read parameters
action->parameters = parseVariableDeclarations(context, domain);
tokenizer.expect<std::string>(")");
// TODO: reimplement
skipSection(tokenizer);
/*
// Parse preconditions and effects
while (!tokenizer.testAndReturn(')'))
{
tokenizer.expect<std::string>(":");
if (tokenizer.testIdentifierAndSkip("precondition"))
// TODO: reimplement
//action->precondition = parsePreconditionExpression(context, expressionContext);
skipSection(tokenizer);
else if (tokenizer.testIdentifierAndSkip("effect"))
// TODO: reimplement
//action->effect = parseEffectExpression(context, expressionContext);
skipSection(tokenizer);
tokenizer.skipWhiteSpace();
}*/
// Store new action
domain.actions.emplace_back(std::move(action));
//tokenizer.expect<std::string>(")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,69 @@
#include <pddlparse/detail/parsing/Constant.h>
#include <pddlparse/AST.h>
#include <pddlparse/ParserException.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Constant
//
////////////////////////////////////////////////////////////////////////////////////////////////////
std::experimental::optional<ast::ConstantPointer> findConstant(const std::string &constantName, ast::ConstantDeclarations &constantDeclarations)
{
const auto matchingConstant = std::find_if(constantDeclarations.begin(), constantDeclarations.end(),
[&](const auto &constantDeclaration)
{
return constantDeclaration->name == constantName;
});
if (matchingConstant == constantDeclarations.end())
return std::experimental::nullopt;
return std::make_unique<ast::Constant>(matchingConstant->get());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::ConstantPointer parseConstant(Context &context, ast::Domain &domain)
{
auto &tokenizer = context.tokenizer;
const auto constantName = tokenizer.getIdentifier();
auto constant = findConstant(constantName, domain.constants);
if (constant)
return std::move(constant.value());
throw ParserException(tokenizer.location(), "constant “" + constantName + "” used but never declared");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::ConstantPointer parseConstant(Context &context, ast::Problem &problem)
{
auto &tokenizer = context.tokenizer;
const auto constantName = tokenizer.getIdentifier();
auto constant = findConstant(constantName, problem.domain->constants);
if (constant)
return std::move(constant.value());
auto object = findConstant(constantName, problem.objects);
if (object)
return std::move(object.value());
throw ParserException(tokenizer.location(), "constant “" + constantName + "” used but never declared");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,78 @@
#include <pddlparse/detail/parsing/ConstantDeclaration.h>
#include <pddlparse/AST.h>
#include <pddlparse/ParserException.h>
#include <pddlparse/detail/ASTCopy.h>
#include <pddlparse/detail/parsing/PrimitiveType.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// ConstantDeclaration
//
////////////////////////////////////////////////////////////////////////////////////////////////////
void parseAndAddUntypedConstantDeclaration(Context &context, ast::ConstantDeclarations &constantDeclarations)
{
auto &tokenizer = context.tokenizer;
auto constantName = tokenizer.getIdentifier();
assert(constantName != "-");
constantDeclarations.emplace_back(std::make_unique<ast::ConstantDeclaration>(std::move(constantName)));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void parseAndAddConstantDeclarations(Context &context, ast::Domain &domain, ast::ConstantDeclarations &constantDeclarations)
{
auto &tokenizer = context.tokenizer;
tokenizer.skipWhiteSpace();
// Index on the first element of the current inheritance list
size_t inheritanceIndex = 0;
while (tokenizer.currentCharacter() != ')')
{
parseAndAddUntypedConstantDeclaration(context, constantDeclarations);
tokenizer.skipWhiteSpace();
if (!tokenizer.testAndSkip<char>('-'))
continue;
// If existing, parse and store parent type
auto parentType = parsePrimitiveType(context, domain);
for (size_t i = inheritanceIndex; i < constantDeclarations.size(); i++)
constantDeclarations[i]->type = ast::deepCopy(parentType);
// All types up to now are labeled with their parent types
inheritanceIndex = constantDeclarations.size() + 1;
tokenizer.skipWhiteSpace();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void parseAndAddConstantDeclarations(Context &context, ast::Domain &domain)
{
parseAndAddConstantDeclarations(context, domain, domain.constants);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void parseAndAddConstantDeclarations(Context &context, ast::Problem &problem)
{
parseAndAddConstantDeclarations(context, *problem.domain, problem.objects);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,107 @@
#include <pddlparse/detail/parsing/Description.h>
#include <pddlparse/AST.h>
#include <pddlparse/ParserException.h>
#include <pddlparse/detail/parsing/Domain.h>
#include <pddlparse/detail/parsing/Problem.h>
#include <pddlparse/detail/parsing/Utils.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Description
//
////////////////////////////////////////////////////////////////////////////////////////////////////
DescriptionParser::DescriptionParser(Context &context)
: m_context{context},
m_domainPosition{-1},
m_problemPosition{-1}
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::Description DescriptionParser::parse()
{
auto &tokenizer = m_context.tokenizer;
tokenizer.removeComments(";", "\n", false);
findSections();
if (m_domainPosition == -1)
throw ParserException("no PDDL domain specified");
tokenizer.seek(m_domainPosition);
auto domain = DomainParser(m_context).parse();
// If no problem is given, return just the domain
if (m_problemPosition == -1)
return {std::move(domain), std::experimental::nullopt};
tokenizer.seek(m_problemPosition);
auto problem = ProblemParser(m_context, *domain).parse();
// TODO: check consistency
// * check typing requirement
// * check that typing is used consistently
// * check that constants, variables, and predicates aren't declared twice
// * check section order
// * check that preconditions and effects are well-formed
return {std::move(domain), std::move(problem)};
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void DescriptionParser::findSections()
{
auto &tokenizer = m_context.tokenizer;
tokenizer.skipWhiteSpace();
while (!tokenizer.atEnd())
{
const auto position = tokenizer.position();
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>("define");
tokenizer.expect<std::string>("(");
if (tokenizer.testAndSkip<std::string>("domain"))
{
if (m_domainPosition != -1)
throw ParserException(tokenizer.location(), "PDDL description may not contain two domains");
m_domainPosition = position;
skipSection(tokenizer);
skipSection(tokenizer);
}
else if (m_context.tokenizer.testAndSkip<std::string>("problem"))
{
if (m_problemPosition != -1)
throw ParserException("PDDL description may not contain two problems currently");
m_problemPosition = position;
skipSection(tokenizer);
skipSection(tokenizer);
}
else
{
const auto sectionIdentifier = tokenizer.get<std::string>();
throw ParserException(tokenizer.location(), "unknown PDDL section “" + sectionIdentifier + "");
}
tokenizer.skipWhiteSpace();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,300 @@
#include <pddlparse/detail/parsing/Domain.h>
#include <pddlparse/ParserException.h>
#include <pddlparse/detail/Requirements.h>
#include <pddlparse/detail/parsing/Action.h>
#include <pddlparse/detail/parsing/ConstantDeclaration.h>
#include <pddlparse/detail/parsing/PredicateDeclaration.h>
#include <pddlparse/detail/parsing/PrimitiveTypeDeclaration.h>
#include <pddlparse/detail/parsing/Requirement.h>
#include <pddlparse/detail/parsing/Utils.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Domain
//
////////////////////////////////////////////////////////////////////////////////////////////////////
DomainParser::DomainParser(Context &context)
: m_context{context},
m_requirementsPosition{-1},
m_typesPosition{-1},
m_constantsPosition{-1},
m_predicatesPosition{-1}
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::DomainPointer DomainParser::parse()
{
auto domain = std::make_unique<ast::Domain>();
findSections(*domain);
auto &tokenizer = m_context.tokenizer;
if (m_requirementsPosition != -1)
{
tokenizer.seek(m_requirementsPosition);
parseRequirementSection(*domain);
}
if (m_typesPosition != -1)
{
tokenizer.seek(m_typesPosition);
parseTypeSection(*domain);
}
if (m_constantsPosition != -1)
{
tokenizer.seek(m_constantsPosition);
parseConstantSection(*domain);
}
if (m_predicatesPosition != -1)
{
tokenizer.seek(m_predicatesPosition);
parsePredicateSection(*domain);
}
for (size_t i = 0; i < m_actionPositions.size(); i++)
if (m_actionPositions[i] != -1)
{
tokenizer.seek(m_actionPositions[i]);
parseActionSection(*domain);
}
computeDerivedRequirements(*domain);
return domain;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void DomainParser::findSections(ast::Domain &domain)
{
auto &tokenizer = m_context.tokenizer;
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>("define");
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>("domain");
domain.name = tokenizer.getIdentifier();
tokenizer.expect<std::string>(")");
const auto setSectionPosition =
[&](const std::string &sectionName, auto &sectionPosition, const auto value, bool unique = false)
{
if (unique && sectionPosition != -1)
{
tokenizer.seek(value);
throw tokenize::TokenizerException(tokenizer.location(), "only one “:" + sectionName + "” section allowed");
}
sectionPosition = value;
};
tokenizer.skipWhiteSpace();
// Find sections
while (tokenizer.currentCharacter() != ')')
{
const auto position = tokenizer.position();
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>(":");
const auto sectionIdentifierPosition = tokenizer.position();
// Save the parser position of the individual sections for later parsing
if (tokenizer.testIdentifierAndSkip("requirements"))
setSectionPosition("requirements", m_requirementsPosition, position, true);
else if (tokenizer.testIdentifierAndSkip("types"))
setSectionPosition("types", m_typesPosition, position, true);
else if (tokenizer.testIdentifierAndSkip("constants"))
setSectionPosition("constants", m_constantsPosition, position, true);
else if (tokenizer.testIdentifierAndSkip("predicates"))
setSectionPosition("predicates", m_predicatesPosition, position, true);
else if (tokenizer.testIdentifierAndSkip("action"))
{
m_actionPositions.emplace_back(-1);
setSectionPosition("action", m_actionPositions.back(), position);
}
else if (tokenizer.testIdentifierAndSkip("functions")
|| tokenizer.testIdentifierAndSkip("constraints")
|| tokenizer.testIdentifierAndSkip("durative-action")
|| tokenizer.testIdentifierAndSkip("derived"))
{
tokenizer.seek(sectionIdentifierPosition);
const auto sectionIdentifier = tokenizer.getIdentifier();
m_context.warningCallback(tokenizer.location(), "section type “" + sectionIdentifier + "” currently unsupported");
tokenizer.seek(sectionIdentifierPosition);
}
else
{
const auto sectionIdentifier = tokenizer.getIdentifier();
tokenizer.seek(position);
throw tokenize::TokenizerException(tokenizer.location(), "unknown domain section “" + sectionIdentifier + "");
}
// Skip section for now and parse it later
skipSection(tokenizer);
tokenizer.skipWhiteSpace();
}
tokenizer.expect<std::string>(")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void DomainParser::parseRequirementSection(ast::Domain &domain)
{
auto &tokenizer = m_context.tokenizer;
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>(":");
tokenizer.expect<std::string>("requirements");
while (tokenizer.currentCharacter() != ')')
{
tokenizer.expect<std::string>(":");
domain.requirements.emplace_back(parseRequirement(m_context));
tokenizer.skipWhiteSpace();
}
// TODO: do this check only once the problem is parsed
// If no requirements are specified, assume STRIPS
if (domain.requirements.empty())
domain.requirements.emplace_back(ast::Requirement::STRIPS);
tokenizer.expect<std::string>(")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void DomainParser::computeDerivedRequirements(ast::Domain &domain)
{
const auto addRequirementUnique =
[&](auto requirement)
{
if (hasRequirement(domain, requirement))
return;
domain.requirements.push_back(requirement);
};
if (hasRequirement(domain, ast::Requirement::ADL))
{
addRequirementUnique(ast::Requirement::STRIPS);
addRequirementUnique(ast::Requirement::Typing);
addRequirementUnique(ast::Requirement::NegativePreconditions);
addRequirementUnique(ast::Requirement::DisjunctivePreconditions);
addRequirementUnique(ast::Requirement::Equality);
addRequirementUnique(ast::Requirement::QuantifiedPreconditions);
addRequirementUnique(ast::Requirement::ConditionalEffects);
}
if (hasRequirement(domain, ast::Requirement::QuantifiedPreconditions))
{
addRequirementUnique(ast::Requirement::ExistentialPreconditions);
addRequirementUnique(ast::Requirement::UniversalPreconditions);
}
if (hasRequirement(domain, ast::Requirement::Fluents))
{
addRequirementUnique(ast::Requirement::NumericFluents);
addRequirementUnique(ast::Requirement::ObjectFluents);
}
if (hasRequirement(domain, ast::Requirement::TimedInitialLiterals))
addRequirementUnique(ast::Requirement::DurativeActions);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void DomainParser::parseTypeSection(ast::Domain &domain)
{
auto &tokenizer = m_context.tokenizer;
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>(":");
tokenizer.expect<std::string>("types");
checkRequirement(domain, ast::Requirement::Typing, m_context);
tokenizer.skipWhiteSpace();
// Store types and their parent types
while (tokenizer.currentCharacter() != ')')
{
if (tokenizer.currentCharacter() == '(')
throw ParserException(tokenizer.location(), "only primitive types are allowed in type section");
parseAndAddPrimitiveTypeDeclarations(m_context, domain);
tokenizer.skipWhiteSpace();
}
tokenizer.expect<std::string>(")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void DomainParser::parseConstantSection(ast::Domain &domain)
{
auto &tokenizer = m_context.tokenizer;
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>(":");
tokenizer.expect<std::string>("constants");
// Store constants
parseAndAddConstantDeclarations(m_context, domain);
tokenizer.expect<std::string>(")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void DomainParser::parsePredicateSection(ast::Domain &domain)
{
auto &tokenizer = m_context.tokenizer;
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>(":");
tokenizer.expect<std::string>("predicates");
tokenizer.skipWhiteSpace();
// Store predicates
parseAndAddPredicateDeclarations(m_context, domain);
tokenizer.expect<std::string>(")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void DomainParser::parseActionSection(ast::Domain &domain)
{
parseAndAddAction(m_context, domain);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,79 @@
#include <pddlparse/detail/parsing/InitialState.h>
#include <pddlparse/AST.h>
// TODO: remove
#include <pddlparse/detail/parsing/Utils.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// InitialState
//
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::InitialState parseInitialState(Context &context, ASTContext &)
{
auto &tokenizer = context.tokenizer;
ast::InitialState initialState;
// TODO: reimplement
/*const auto parseInitialStateElement =
[&]() -> ExpressionPointer
{
ExpressionPointer expression;
// TODO: do not allow negative initial state literals
if ((expression = parseLiteral(context, expressionContext))
|| (expression = expressions::At::parse(context, expressionContext, parseLiteral)))
{
return expression;
}
const auto position = tokenizer.position();
tokenizer.expect<std::string>("(");
const auto expressionIdentifierPosition = tokenizer.position();
if (tokenizer.testIdentifierAndSkip("="))
{
tokenizer.seek(expressionIdentifierPosition);
const auto expressionIdentifier = tokenizer.getIdentifier();
tokenizer.seek(position);
return expressions::Unsupported::parse(context);
}
tokenizer.seek(expressionIdentifierPosition);
const auto expressionIdentifier = tokenizer.getIdentifier();
tokenizer.seek(position);
throw tokenize::TokenizerException(tokenizer.location(), "expression type “" + expressionIdentifier + "” unknown or not allowed in this context");
};
tokenizer.skipWhiteSpace();
while (tokenizer.currentCharacter() != ')')
{
ast::Expression expression;
if ((expression = parseInitialStateElement()))
initialState->m_facts.emplace_back(std::move(expression));
tokenizer.skipWhiteSpace();
}*/
skipSection(tokenizer);
return initialState;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,53 @@
#include <pddlparse/detail/parsing/PredicateDeclaration.h>
#include <pddlparse/ParserException.h>
#include <pddlparse/detail/ASTContext.h>
#include <pddlparse/detail/parsing/VariableDeclaration.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// PredicateDeclaration
//
////////////////////////////////////////////////////////////////////////////////////////////////////
void parseAndAddPredicateDeclaration(Context &context, ast::Domain &domain)
{
auto &tokenizer = context.tokenizer;
tokenizer.expect<std::string>("(");
auto name = tokenizer.getIdentifier();
tokenizer.skipWhiteSpace();
// Parse parameters
auto parameters = parseVariableDeclarations(context, domain);
tokenizer.expect<std::string>(")");
domain.predicates.emplace_back(std::make_unique<ast::PredicateDeclaration>(std::move(name), std::move(parameters)));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void parseAndAddPredicateDeclarations(Context &context, ast::Domain &domain)
{
auto &tokenizer = context.tokenizer;
tokenizer.skipWhiteSpace();
while (tokenizer.currentCharacter() != ')')
{
parseAndAddPredicateDeclaration(context, domain);
tokenizer.skipWhiteSpace();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,55 @@
#include <pddlparse/detail/parsing/PrimitiveType.h>
#include <pddlparse/ParserException.h>
#include <pddlparse/detail/Requirements.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// PrimitiveType
//
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::PrimitiveTypePointer parsePrimitiveType(Context &context, ast::Domain &domain)
{
auto &tokenizer = context.tokenizer;
auto &types = domain.types;
tokenizer.skipWhiteSpace();
auto typeName = tokenizer.getIdentifier();
if (typeName.empty())
throw tokenize::TokenizerException(tokenizer.location(), "no type supplied");
auto matchingType = std::find_if(types.begin(), types.end(),
[&](auto &primitiveTypeDeclaration)
{
return primitiveTypeDeclaration->name == typeName;
});
if (matchingType == types.end())
{
// Only “object” is allowed as an implicit type
if (typeName == "object" || typeName == "objects")
{
context.warningCallback(tokenizer.location(), "primitive type “" + typeName + "” should be declared");
types.emplace_back(std::make_unique<ast::PrimitiveTypeDeclaration>(std::move(typeName)));
return std::make_unique<ast::PrimitiveType>(types.back().get());
}
else
throw tokenize::TokenizerException(tokenizer.location(), "type “" + typeName + "” used but never declared");
}
return std::make_unique<ast::PrimitiveType>(matchingType->get());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,77 @@
#include <pddlparse/detail/parsing/PrimitiveTypeDeclaration.h>
#include <pddlparse/ParserException.h>
#include <pddlparse/detail/ASTCopy.h>
#include <pddlparse/detail/parsing/PrimitiveType.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// PrimitiveTypeDeclaration
//
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::PrimitiveTypeDeclarationPointer &parseAndAddUntypedPrimitiveTypeDeclaration(Context &context, ast::Domain &domain)
{
auto &tokenizer = context.tokenizer;
auto typeName = tokenizer.getIdentifier();
auto &types = domain.types;
const auto matchingPrimitiveType = std::find_if(types.begin(), types.end(),
[&](const auto &primitiveType)
{
return primitiveType->name == typeName;
});
// Return existing primitive type
if (matchingPrimitiveType != types.cend())
return *matchingPrimitiveType;
types.emplace_back(std::make_unique<ast::PrimitiveTypeDeclaration>(std::move(typeName)));
return types.back();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void parseAndAddPrimitiveTypeDeclarations(Context &context, ast::Domain &domain)
{
auto &tokenizer = context.tokenizer;
tokenizer.skipWhiteSpace();
// Index on the first element of the current inheritance list
size_t inheritanceIndex = 0;
while (tokenizer.currentCharacter() != ')')
{
parseAndAddUntypedPrimitiveTypeDeclaration(context, domain);
tokenizer.skipWhiteSpace();
if (!tokenizer.testAndSkip<char>('-'))
continue;
// If existing, parse and store parent type
auto parentType = parsePrimitiveType(context, domain);
auto &types = domain.types;
for (size_t i = inheritanceIndex; i < types.size(); i++)
types[i]->parentTypes.emplace_back(ast::deepCopy(parentType));
// All types up to now are labeled with their parent types
inheritanceIndex = types.size() + 1;
tokenizer.skipWhiteSpace();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,297 @@
#include <pddlparse/detail/parsing/Problem.h>
#include <pddlparse/ParserException.h>
#include <pddlparse/detail/Requirements.h>
#include <pddlparse/detail/parsing/ConstantDeclaration.h>
#include <pddlparse/detail/parsing/InitialState.h>
#include <pddlparse/detail/parsing/Requirement.h>
#include <pddlparse/detail/parsing/Utils.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Problem
//
////////////////////////////////////////////////////////////////////////////////////////////////////
ProblemParser::ProblemParser(Context &context, ast::Domain &domain)
: m_context{context},
m_domain{domain},
m_domainPosition{-1},
m_requirementsPosition{-1},
m_objectsPosition{-1},
m_initialStatePosition{-1},
m_goalPosition{-1}
{
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::ProblemPointer ProblemParser::parse()
{
auto problem = std::make_unique<ast::Problem>(&m_domain);
findSections(*problem);
auto &tokenizer = m_context.tokenizer;
if (m_domainPosition == -1)
throw ParserException(tokenizer.location(), "problem description does not specify a corresponding domain");
tokenizer.seek(m_domainPosition);
parseDomainSection(*problem);
if (m_requirementsPosition != -1)
{
tokenizer.seek(m_requirementsPosition);
parseRequirementSection(*problem);
}
if (m_objectsPosition != -1)
{
tokenizer.seek(m_objectsPosition);
parseObjectSection(*problem);
}
if (m_initialStatePosition == -1)
throw ParserException(tokenizer.location(), "problem description does not specify an initial state");
tokenizer.seek(m_initialStatePosition);
parseInitialStateSection(*problem);
if (m_goalPosition == -1)
throw ParserException(tokenizer.location(), "problem description does not specify a goal");
tokenizer.seek(m_goalPosition);
parseGoalSection(*problem);
return problem;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void ProblemParser::findSections(ast::Problem &problem)
{
auto &tokenizer = m_context.tokenizer;
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>("define");
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>("problem");
problem.name = tokenizer.getIdentifier();
tokenizer.expect<std::string>(")");
const auto setSectionPosition =
[&](const std::string &sectionName, auto &sectionPosition, const auto value, bool unique = false)
{
if (unique && sectionPosition != -1)
{
tokenizer.seek(value);
throw tokenize::TokenizerException(tokenizer.location(), "only one “:" + sectionName + "” section allowed");
}
sectionPosition = value;
};
tokenizer.skipWhiteSpace();
while (tokenizer.currentCharacter() != ')')
{
const auto position = tokenizer.position();
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>(":");
const auto sectionIdentifierPosition = tokenizer.position();
if (tokenizer.testIdentifierAndSkip("domain"))
setSectionPosition("domain", m_domainPosition, position, true);
else if (tokenizer.testIdentifierAndSkip("requirements"))
setSectionPosition("requirements", m_requirementsPosition, position, true);
else if (tokenizer.testIdentifierAndSkip("objects"))
setSectionPosition("objects", m_objectsPosition, position, true);
else if (tokenizer.testIdentifierAndSkip("init"))
setSectionPosition("init", m_initialStatePosition, position, true);
else if (tokenizer.testIdentifierAndSkip("goal"))
setSectionPosition("goal", m_goalPosition, position, true);
else if (tokenizer.testIdentifierAndSkip("constraints")
|| tokenizer.testIdentifierAndSkip("metric")
|| tokenizer.testIdentifierAndSkip("length"))
{
tokenizer.seek(sectionIdentifierPosition);
const auto sectionIdentifier = tokenizer.getIdentifier();
m_context.warningCallback(tokenizer.location(), "section type “" + sectionIdentifier + "” currently unsupported");
tokenizer.seek(sectionIdentifierPosition);
}
else
{
const auto sectionIdentifier = tokenizer.getIdentifier();
tokenizer.seek(position);
throw tokenize::TokenizerException(tokenizer.location(), "unknown problem section “" + sectionIdentifier + "");
}
// Skip section for now and parse it later
skipSection(tokenizer);
tokenizer.skipWhiteSpace();
}
tokenizer.expect<std::string>(")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void ProblemParser::parseDomainSection(ast::Problem &problem)
{
auto &tokenizer = m_context.tokenizer;
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>(":");
tokenizer.expect<std::string>("domain");
tokenizer.skipWhiteSpace();
const auto domainName = tokenizer.getIdentifier();
if (problem.domain->name != domainName)
throw tokenize::TokenizerException(tokenizer.location(), "domains do not match (“" + problem.domain->name + "” and “" + domainName + "”)");
tokenizer.expect<std::string>(")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void ProblemParser::parseRequirementSection(ast::Problem &problem)
{
auto &tokenizer = m_context.tokenizer;
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>(":");
tokenizer.expect<std::string>("requirements");
while (tokenizer.currentCharacter() != ')')
{
tokenizer.expect<std::string>(":");
problem.requirements.emplace_back(parseRequirement(m_context));
tokenizer.skipWhiteSpace();
}
// TODO: do this check only once the problem is parsed
// If no requirements are specified, assume STRIPS
if (problem.requirements.empty())
problem.requirements.emplace_back(ast::Requirement::STRIPS);
tokenizer.expect<std::string>(")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// TODO: refactor, exists identically in DomainParser
void ProblemParser::computeDerivedRequirements(ast::Problem &problem)
{
const auto addRequirementUnique =
[&](const auto requirement)
{
if (hasRequirement(problem, requirement))
return;
problem.requirements.push_back(ast::Requirement(requirement));
};
if (hasRequirement(problem, ast::Requirement::ADL))
{
addRequirementUnique(ast::Requirement::STRIPS);
addRequirementUnique(ast::Requirement::Typing);
addRequirementUnique(ast::Requirement::NegativePreconditions);
addRequirementUnique(ast::Requirement::DisjunctivePreconditions);
addRequirementUnique(ast::Requirement::Equality);
addRequirementUnique(ast::Requirement::QuantifiedPreconditions);
addRequirementUnique(ast::Requirement::ConditionalEffects);
}
if (hasRequirement(problem, ast::Requirement::QuantifiedPreconditions))
{
addRequirementUnique(ast::Requirement::ExistentialPreconditions);
addRequirementUnique(ast::Requirement::UniversalPreconditions);
}
if (hasRequirement(problem, ast::Requirement::Fluents))
{
addRequirementUnique(ast::Requirement::NumericFluents);
addRequirementUnique(ast::Requirement::ObjectFluents);
}
if (hasRequirement(problem, ast::Requirement::TimedInitialLiterals))
addRequirementUnique(ast::Requirement::DurativeActions);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void ProblemParser::parseObjectSection(ast::Problem &problem)
{
auto &tokenizer = m_context.tokenizer;
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>(":");
tokenizer.expect<std::string>("objects");
// Store constants
parseAndAddConstantDeclarations(m_context, problem);
tokenizer.expect<std::string>(")");
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void ProblemParser::parseInitialStateSection(ast::Problem &problem)
{
auto &tokenizer = m_context.tokenizer;
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>(":");
tokenizer.expect<std::string>("init");
ASTContext astContext(problem);
// TODO: reimplement
//problem.initialState = parseInitialState(m_context, astContext);
//tokenizer.expect<std::string>(")");
skipSection(tokenizer);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
void ProblemParser::parseGoalSection(ast::Problem &problem)
{
auto &tokenizer = m_context.tokenizer;
tokenizer.expect<std::string>("(");
tokenizer.expect<std::string>(":");
tokenizer.expect<std::string>("goal");
ASTContext expressionContext(problem);
// TODO: reimplement
//problem.goal = parsePreconditionExpression(m_context, expressionContext);
//tokenizer.expect<std::string>(")");
skipSection(tokenizer);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,102 @@
#include <pddlparse/detail/parsing/Requirement.h>
#include <cstring>
#include <map>
#include <pddlparse/AST.h>
#include <pddlparse/ParserException.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Requirement
//
////////////////////////////////////////////////////////////////////////////////////////////////////
struct CompareStrings
{
bool operator()(const char *lhs, const char *rhs) const
{
return std::strcmp(lhs, rhs) < 0;
}
};
////////////////////////////////////////////////////////////////////////////////////////////////////
using RequirementNameMap = std::map<const char *, ast::Requirement, CompareStrings>;
static const RequirementNameMap requirementNameMap =
{
{"strips", ast::Requirement::STRIPS},
{"typing", ast::Requirement::Typing},
{"negative-preconditions", ast::Requirement::NegativePreconditions},
{"disjunctive-preconditions", ast::Requirement::DisjunctivePreconditions},
{"equality", ast::Requirement::Equality},
{"existential-preconditions", ast::Requirement::ExistentialPreconditions},
{"universal-preconditions", ast::Requirement::UniversalPreconditions},
{"quantified-preconditions", ast::Requirement::QuantifiedPreconditions},
{"conditional-effects", ast::Requirement::ConditionalEffects},
{"fluents", ast::Requirement::Fluents},
{"numeric-fluents", ast::Requirement::NumericFluents},
{"object-fluents", ast::Requirement::ObjectFluents},
{"adl", ast::Requirement::ADL},
{"durative-actions", ast::Requirement::DurativeActions},
{"duration-inequalities", ast::Requirement::DurationInequalities},
{"continuous-effects", ast::Requirement::ContinuousEffects},
{"derived-predicates", ast::Requirement::DerivedPredicates},
{"timed-initial-literals", ast::Requirement::TimedInitialLiterals},
{"preferences", ast::Requirement::Preferences},
{"constraints", ast::Requirement::Constraints},
{"action-costs", ast::Requirement::ActionCosts},
{"goal-utilities", ast::Requirement::GoalUtilities},
};
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::Requirement parseRequirement(Tokenizer &tokenizer)
{
const auto requirementName = tokenizer.getIdentifier();
const auto matchingRequirement = requirementNameMap.find(requirementName.c_str());
if (matchingRequirement == requirementNameMap.cend())
throw ParserException(tokenizer.location(), "unknown PDDL requirement “" + requirementName + "");
return matchingRequirement->second;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::Requirement parseRequirement(Context &context)
{
auto &tokenizer = context.tokenizer;
auto requirement = parseRequirement(tokenizer);
if (requirement == ast::Requirement::GoalUtilities)
context.warningCallback(tokenizer.location(), "requirement “goal-utilities” is not part of the PDDL 3.1 specification");
return requirement;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
const char *toString(const ast::Requirement &requirement)
{
const auto matchingRequirement = std::find_if(requirementNameMap.cbegin(), requirementNameMap.cend(),
[&](const auto &requirementNamePair)
{
return requirementNamePair.second == requirement;
});
assert(matchingRequirement != requirementNameMap.cend());
return matchingRequirement->first;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}

View File

@@ -0,0 +1,69 @@
#include <pddlparse/detail/parsing/VariableDeclaration.h>
#include <pddlparse/AST.h>
#include <pddlparse/ParserException.h>
#include <pddlparse/detail/ASTCopy.h>
#include <pddlparse/detail/parsing/PrimitiveType.h>
namespace pddl
{
namespace detail
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// VariableDeclaration
//
////////////////////////////////////////////////////////////////////////////////////////////////////
void parseAndAddUntypedVariableDeclaration(Context &context, ast::VariableDeclarations &variableDeclarations)
{
auto &tokenizer = context.tokenizer;
tokenizer.expect<std::string>("?");
auto variableName = tokenizer.getIdentifier();
assert(variableName != "-");
variableDeclarations.emplace_back(std::make_unique<ast::VariableDeclaration>(std::move(variableName)));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ast::VariableDeclarations parseVariableDeclarations(Context &context, ast::Domain &domain)
{
ast::VariableDeclarations variableDeclarations;
auto &tokenizer = context.tokenizer;
tokenizer.skipWhiteSpace();
// Index on the first element of the current inheritance list
size_t inheritanceIndex = 0;
while (tokenizer.currentCharacter() != ')')
{
parseAndAddUntypedVariableDeclaration(context, variableDeclarations);
tokenizer.skipWhiteSpace();
if (!tokenizer.testAndSkip<char>('-'))
continue;
// If existing, parse and store parent type
auto parentType = parsePrimitiveType(context, domain);
for (size_t i = inheritanceIndex; i < variableDeclarations.size(); i++)
variableDeclarations[i]->type = ast::deepCopy(parentType);
// All types up to now are labeled with their parent types
inheritanceIndex = variableDeclarations.size() + 1;
tokenizer.skipWhiteSpace();
}
return variableDeclarations;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
}
}