Skip to content

Commit

Permalink
simplified scoping mechanism. added variable type information to thro…
Browse files Browse the repository at this point in the history
…w an error when assignments are inconsistent
  • Loading branch information
drexlerd committed Apr 23, 2024
1 parent 5ef95e8 commit 5a8d902
Show file tree
Hide file tree
Showing 25 changed files with 351 additions and 234 deletions.
2 changes: 2 additions & 0 deletions include/loki/details/pddl/declarations.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <cstddef>
#include <unordered_map>
#include <unordered_set>
#include <variant>
#include <vector>

Expand All @@ -42,6 +43,7 @@ using Requirements = const RequirementsImpl*;
class TypeImpl;
using Type = const TypeImpl*;
using TypeList = std::vector<Type>;
using TypeSet = std::unordered_set<Type>;

class ObjectImpl;
using Object = const ObjectImpl*;
Expand Down
6 changes: 6 additions & 0 deletions include/loki/details/pddl/exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ class MismatchedFunctionSkeletonTermListError : public SemanticParserError
MismatchedFunctionSkeletonTermListError(const FunctionSkeleton& function_skeleton, const TermList& term_list, const std::string& error_handler_output);
};

class IncompatibleObjectTypeError : public SemanticParserError
{
public:
IncompatibleObjectTypeError(const Object& object, const Variable& variable, const std::string& error_handler_output);
};

class UnexpectedDerivedPredicateInEffect : public SemanticParserError
{
public:
Expand Down
86 changes: 40 additions & 46 deletions include/loki/details/pddl/scope.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,59 +43,63 @@ namespace loki
/// The position points to the matched location
/// in the input stream and is used for error reporting.
template<typename T>
using BindingValueType = std::tuple<PDDLElement<T>, std::optional<Position>>;
using BindingValueType = std::pair<T, std::optional<Position>>;

/// @brief Datastructure to store bindings of a type T.
/// @brief Encapsulates the result of search for a binding with the corresponding ErrorHandler.
template<typename T>
using BindingMapType = std::unordered_map<std::string, BindingValueType<T>>;

/// @brief Encapsulates bindings for different types.
template<typename... Ts>
class Bindings
{
private:
std::tuple<BindingMapType<Ts>...> bindings;

public:
/// @brief Returns a binding if it exists.
template<typename T>
std::optional<BindingValueType<T>> get(const std::string& key) const;
using BindingSearchResult = std::tuple<T, std::optional<Position>, const PDDLErrorHandler&>;

/// @brief Inserts a binding of type T
template<typename T>
void insert(const std::string& key, const PDDLElement<T>& binding, const std::optional<Position>& position);
};
/// @brief Datastructure to store bindings of a type T.
template<typename T>
using Bindings = std::unordered_map<std::string, BindingValueType<T>>;

/// @brief Wraps bindings in a scope with reference to a parent scope.
class Scope
{
private:
const PDDLErrorHandler& m_error_handler;
const Scope* m_parent_scope;

Bindings<TypeImpl, ObjectImpl, PredicateImpl, FunctionSkeletonImpl, VariableImpl> bindings;
Bindings<Type> m_types;
Bindings<Object> m_objects;
Bindings<FunctionSkeleton> m_function_skeletons;
Bindings<Variable> m_variables;
Bindings<Predicate> m_predicates;
Bindings<Predicate> m_derived_predicates;

std::unordered_map<Variable, TypeSet> m_variable_types;

public:
explicit Scope(const Scope* parent_scope = nullptr);
Scope(const PDDLErrorHandler& error_handler, const Scope* parent_scope = nullptr);

// delete copy and move to avoid dangling references.
Scope(const Scope& other) = delete;
Scope& operator=(const Scope& other) = delete;
Scope(Scope&& other) = delete;
Scope& operator=(Scope&& other) = delete;

/// @brief Returns a binding if it exists.
template<typename T>
std::optional<BindingValueType<T>> get(const std::string& name) const;
/// @brief Return a binding if it exists.
std::optional<BindingSearchResult<Type>> get_type(const std::string& name) const;
std::optional<BindingSearchResult<Object>> get_object(const std::string& name) const;
std::optional<BindingSearchResult<FunctionSkeleton>> get_function_skeleton(const std::string& name) const;
std::optional<BindingSearchResult<Variable>> get_variable(const std::string& name) const;
std::optional<BindingSearchResult<Predicate>> get_predicate(const std::string& name) const;
std::optional<BindingSearchResult<Predicate>> get_derived_predicate(const std::string& name) const;
const TypeSet& get_variable_types(const Variable& variable) const;

/// @brief Insert a binding.
void insert_type(const std::string& name, const Type& type, const std::optional<Position>& position);
void insert_object(const std::string& name, const Object& object, const std::optional<Position>& position);
void insert_function_skeleton(const std::string& name, const FunctionSkeleton& function_skeleton, const std::optional<Position>& position);
void insert_variable(const std::string& name, const Variable& variable, const std::optional<Position>& position);
void insert_predicate(const std::string& name, const Predicate& predicate, const std::optional<Position>& position);
void insert_derived_predicate(const std::string& name, const Predicate& derived_predicate, const std::optional<Position>& position);
void insert_variable_types(const Variable& variable, const TypeSet& types);

/// @brief Insert a binding of type T.
template<typename T>
void insert(const std::string& name, const PDDLElement<T>& element, const std::optional<Position>& position);
/// @brief Get the error handler to print an error message.
const PDDLErrorHandler& get_error_handler() const;
};

/// @brief Encapsulates the result of search for a binding with the corresponding ErrorHandler.
template<typename T>
using ScopeStackSearchResult = std::tuple<const PDDLElement<T>, const std::optional<Position>, const PDDLErrorHandler&>;

/// @brief Implements a scoping mechanism to store bindings which are mappings from name to a pointer to a PDDL object
/// type and a position in the input stream that can be used to construct error messages with the given ErrorHandler.
///
Expand All @@ -113,12 +117,11 @@ using ScopeStackSearchResult = std::tuple<const PDDLElement<T>, const std::optio
class ScopeStack
{
private:
std::deque<std::unique_ptr<Scope>> m_stack;

const PDDLErrorHandler& m_error_handler;

const ScopeStack* m_parent;

std::deque<std::unique_ptr<Scope>> m_stack;

public:
ScopeStack(const PDDLErrorHandler& error_handler, const ScopeStack* parent = nullptr);

Expand All @@ -134,23 +137,14 @@ class ScopeStack
/// @brief Deletes the topmost scope from the stack.
void close_scope();

/// @brief Returns a binding if it exists.
template<typename T>
std::optional<ScopeStackSearchResult<T>> get(const std::string& name) const;

/// @brief Insert a binding of type T.
template<typename T>
void insert(const std::string& name, const PDDLElement<T>& element, const std::optional<Position>& position);

/// @brief Get the error handler to print an error message.
const PDDLErrorHandler& get_error_handler() const;
/// @brief Return a binding if it exists.
Scope& top();
const Scope& top() const;

// For testing purposes only.
const std::deque<std::unique_ptr<Scope>>& get_stack() const;
};

}

#include "scope.tpp"

#endif
87 changes: 0 additions & 87 deletions include/loki/details/pddl/scope.tpp

This file was deleted.

4 changes: 4 additions & 0 deletions include/loki/details/pddl/type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ class TypeImpl : public Base<TypeImpl>
const std::string& get_name() const;
const TypeList& get_bases() const;
};

/// @brief Collects all types from a hierarchy.
extern TypeSet collect_types_from_hierarchy(const TypeList& types);

}

#endif
9 changes: 9 additions & 0 deletions src/pddl/exceptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
#include "loki/details/pddl/function.hpp"
#include "loki/details/pddl/function_skeleton.hpp"
#include "loki/details/pddl/object.hpp"
#include "loki/details/pddl/parameter.hpp"
#include "loki/details/pddl/predicate.hpp"
#include "loki/details/pddl/variable.hpp"

#include <string>

Expand Down Expand Up @@ -152,6 +154,13 @@ UnexpectedDerivedPredicateInEffect::UnexpectedDerivedPredicateInEffect(const std
{
}

IncompatibleObjectTypeError::IncompatibleObjectTypeError(const Object& object, const Variable& variable, const std::string& error_handler_output) :
SemanticParserError("The object with name \"" + object->get_name() + "\" does not satisfy the type requirement of variable with name \""
+ variable->get_name() + "\".",
error_handler_output)
{
}

ExpectedDerivedPredicate::ExpectedDerivedPredicate(const std::string& name, const std::string& error_handler_output) :
SemanticParserError("The predicate with name \"" + name + "\" is not a derived predicate.", error_handler_output)
{
Expand Down
4 changes: 2 additions & 2 deletions src/pddl/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ Domain parse(const ast::Domain& domain_node, Context& context)
{
if (!context.requirements->test(RequirementEnum::TYPING))
{
throw UndefinedRequirementError(RequirementEnum::TYPING, context.scopes.get_error_handler()(domain_node.types.value(), ""));
throw UndefinedRequirementError(RequirementEnum::TYPING, context.scopes.top().get_error_handler()(domain_node.types.value(), ""));
}
types = parse(domain_node.types.value(), context);
}
Expand Down Expand Up @@ -127,7 +127,7 @@ Problem parse(const ast::Problem& problem_node, Context& context, const Domain&
const auto domain_name = parse(problem_node.domain_name.name);
if (domain_name != domain->get_name())
{
throw MismatchedDomainError(domain, domain_name, context.scopes.get_error_handler()(problem_node.domain_name, ""));
throw MismatchedDomainError(domain, domain_name, context.scopes.top().get_error_handler()(problem_node.domain_name, ""));
}
/* Problem name section */
const auto problem_name = parse(problem_node.problem_name.name);
Expand Down
18 changes: 9 additions & 9 deletions src/pddl/parser/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ Term TermDeclarationTermVisitor::operator()(const ast::Name& node) const
{
const auto constant_name = parse(node);
// Test for undefined constant.
const auto binding = context.scopes.get<ObjectImpl>(constant_name);
const auto binding = context.scopes.top().get_object(constant_name);
if (!binding.has_value())
{
throw UndefinedConstantError(constant_name, context.scopes.get_error_handler()(node, ""));
throw UndefinedConstantError(constant_name, context.scopes.top().get_error_handler()(node, ""));
}
// Constant are not tracked and hence must not be untracked.
// Construct Term and return it
Expand All @@ -63,17 +63,17 @@ Term TermDeclarationTermVisitor::operator()(const ast::Variable& node) const
{
const auto variable = parse(node, context);
// Test for multiple definition
const auto binding = context.scopes.get<VariableImpl>(variable->get_name());
const auto binding = context.scopes.top().get_variable(variable->get_name());
if (binding.has_value())
{
const auto message_1 = context.scopes.get_error_handler()(node, "Defined here:");
const auto message_1 = context.scopes.top().get_error_handler()(node, "Defined here:");
const auto [_constant, position, error_handler] = binding.value();
assert(position.has_value());
const auto message_2 = error_handler(position.value(), "First defined here:");
throw MultiDefinitionVariableError(variable->get_name(), message_1 + message_2);
}
// Add binding to scope
context.scopes.insert(variable->get_name(), variable, node);
context.scopes.top().insert_variable(variable->get_name(), variable, node);
// Construct Term and return it
const auto term = context.factories.get_or_create_term_variable(variable);
// Add position of PDDL object
Expand All @@ -87,10 +87,10 @@ Term TermReferenceTermVisitor::operator()(const ast::Name& node) const
{
const auto object_name = parse(node);
// Test for undefined constant.
const auto binding = context.scopes.get<ObjectImpl>(object_name);
const auto binding = context.scopes.top().get_object(object_name);
if (!binding.has_value())
{
throw UndefinedConstantError(object_name, context.scopes.get_error_handler()(node, ""));
throw UndefinedConstantError(object_name, context.scopes.top().get_error_handler()(node, ""));
}
// Construct Term and return it
const auto [object, _position, _error_handler] = binding.value();
Expand All @@ -105,10 +105,10 @@ Term TermReferenceTermVisitor::operator()(const ast::Variable& node) const
{
const auto variable = parse(node, context);
// Test for undefined variable
const auto binding = context.scopes.get<VariableImpl>(variable->get_name());
const auto binding = context.scopes.top().get_variable(variable->get_name());
if (!binding.has_value())
{
throw UndefinedVariableError(variable->get_name(), context.scopes.get_error_handler()(node, ""));
throw UndefinedVariableError(variable->get_name(), context.scopes.top().get_error_handler()(node, ""));
}
// Construct Term and return it
const auto term = context.factories.get_or_create_term_variable(variable);
Expand Down
Loading

0 comments on commit 5a8d902

Please sign in to comment.