/*
Ousía
Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include "ParserScope.hpp"
namespace ousia {
/* Class ParserScopeBase */
ParserScopeBase::ParserScopeBase() {}
ParserScopeBase::ParserScopeBase(const NodeVector &nodes) : nodes(nodes)
{
}
Rooted ParserScopeBase::resolve(const Rtti &type,
const std::vector &path,
Logger &logger)
{
// Go up the stack and try to resolve the
for (auto it = nodes.rbegin(); it != nodes.rend(); it++) {
std::vector res = (*it)->resolve(type, path);
// Abort if the object could not be resolved
if (res.empty()) {
continue;
}
// Log an error if the object is not unique
if (res.size() > 1) {
logger.error(std::string("The reference \"") +
Utils::join(path, ".") + ("\" is ambigous!"));
logger.note("Referenced objects are:", SourceLocation{},
MessageMode::NO_CONTEXT);
for (const ResolutionResult &r : res) {
logger.note(Utils::join(r.path(), "."), *(r.node));
}
}
return res[0].node;
}
return nullptr;
}
/* Class DeferredResolution */
DeferredResolution::DeferredResolution(const NodeVector &nodes,
const std::vector &path,
const Rtti &type,
ResolutionResultCallback resultCallback,
Handle owner)
: scope(nodes),
resultCallback(resultCallback),
path(path),
type(type),
owner(owner)
{
}
bool DeferredResolution::resolve(
const std::unordered_multiset &ignore, Logger &logger)
{
// Fork the logger to prevent error messages from being shown if we actively
// ignore the resolution result
LoggerFork loggerFork = logger.fork();
Rooted res = scope.resolve(type, path, loggerFork);
if (res != nullptr) {
if (!ignore.count(res.get())) {
loggerFork.commit();
try {
// Push the location onto the logger default location stack
GuardedLogger loggerGuard(logger, *owner);
resultCallback(res, owner, logger);
}
catch (LoggableException ex) {
logger.log(ex);
}
return true;
}
} else {
loggerFork.commit();
}
return false;
}
void DeferredResolution::fail(Logger &logger)
{
try {
resultCallback(nullptr, owner, logger);
}
catch (LoggableException ex) {
logger.log(ex);
}
}
/* Class ParserScope */
ParserScope::ParserScope(const NodeVector &nodes,
const std::vector &flags)
: ParserScopeBase(nodes), flags(flags), topLevelDepth(nodes.size())
{
}
ParserScope::ParserScope() : topLevelDepth(0) {}
bool ParserScope::checkUnwound(Logger &logger) const
{
if (nodes.size() != topLevelDepth) {
logger.error("Not all open elements have been closed!",
SourceLocation{}, MessageMode::NO_CONTEXT);
logger.note("Still open elements are: ", SourceLocation{},
MessageMode::NO_CONTEXT);
for (size_t i = topLevelDepth + 1; i < nodes.size(); i++) {
logger.note(std::string("Element of interal type ") +
nodes[i]->type().name +
std::string(" defined here:"),
*nodes[i]);
}
return false;
}
return true;
}
ParserScope ParserScope::fork() { return ParserScope{nodes, flags}; }
bool ParserScope::join(const ParserScope &fork, Logger &logger)
{
// Make sure the fork has been unwound
if (!fork.checkUnwound(logger)) {
return false;
}
// Insert the deferred resolutions of the fork into our own deferred
// resolution list
deferred.insert(deferred.end(), fork.deferred.begin(), fork.deferred.end());
awaitingResolution.insert(fork.awaitingResolution.begin(),
fork.awaitingResolution.end());
return true;
}
void ParserScope::push(Handle node)
{
const size_t currentDepth = nodes.size();
if (currentDepth == topLevelDepth) {
topLevelNodes.push_back(node);
}
nodes.push_back(node);
}
void ParserScope::pop()
{
// Make sure pop is not called without an element on the stack
const size_t currentDepth = nodes.size();
if (currentDepth == topLevelDepth) {
throw LoggableException{"No element here to end!"};
}
// Remove all flags from the stack that were set for higher stack depths.
size_t newLen = 0;
for (ssize_t i = flags.size() - 1; i >= 0; i--) {
if (flags[i].depth < currentDepth) {
newLen = i + 1;
break;
}
}
flags.resize(newLen);
// Remove the element from the stack
nodes.pop_back();
}
NodeVector ParserScope::getTopLevelNodes() const { return topLevelNodes; }
Rooted ParserScope::getRoot() const { return nodes.front(); }
Rooted ParserScope::getLeaf() const { return nodes.back(); }
Rooted ParserScope::select(RttiSet types, int maxDepth)
{
ssize_t minDepth = 0;
if (maxDepth >= 0) {
minDepth = static_cast(nodes.size()) - (maxDepth + 1);
}
for (ssize_t i = nodes.size() - 1; i >= minDepth; i--) {
if (nodes[i]->type().isOneOf(types)) {
return nodes[i];
}
}
return nullptr;
}
void ParserScope::setFlag(ParserFlag flag, bool value)
{
// Fetch the current stack depth
const size_t currentDepth = nodes.size();
// Try to change the value of the flag if it was already set on the same
// stack depth
for (auto it = flags.rbegin(); it != flags.rend(); it++) {
if (it->depth == currentDepth) {
if (it->flag == flag) {
it->value = value;
return;
}
} else {
break;
}
}
// Insert a new element into the flags list
flags.emplace_back(currentDepth, flag, value);
}
bool ParserScope::getFlag(ParserFlag flag)
{
for (auto it = flags.crbegin(); it != flags.crend(); it++) {
if (it->flag == flag) {
return it->value;
}
}
return false;
}
bool ParserScope::resolve(const Rtti &type,
const std::vector &path,
Handle owner, Logger &logger,
ResolutionImposterCallback imposterCallback,
ResolutionResultCallback resultCallback)
{
if (!resolve(type, path, owner, logger, resultCallback)) {
resultCallback(imposterCallback(), owner, logger);
return false;
}
return true;
}
bool ParserScope::resolve(const Rtti &type,
const std::vector &path,
Handle owner, Logger &logger,
ResolutionResultCallback resultCallback)
{
// Try to directly resolve the node
Rooted res = ParserScopeBase::resolve(type, path, logger);
if (res != nullptr && !awaitingResolution.count(res.get())) {
try {
resultCallback(res, owner, logger);
}
catch (LoggableException ex) {
logger.log(ex, *owner);
}
return true;
}
// Mark the owner as "awaitingResolution", preventing it from being returned
// as resolution result
if (owner != nullptr) {
awaitingResolution.insert(owner.get());
}
deferred.emplace_back(nodes, path, type, resultCallback, owner);
return false;
}
bool ParserScope::resolveType(const std::vector &path,
Handle owner, Logger &logger,
ResolutionResultCallback resultCallback)
{
// Check whether the given path denotes an array, if yes recursively resolve
// the inner type and wrap it in an array type (this allows multi
// dimensional arrays).
if (!path.empty()) {
const std::string &last = path.back();
if (last.size() >= 2 && last.substr(last.size() - 2, 2) == "[]") {
// Type ends with "[]", remove this from the last element in the
// list
std::vector p = path;
p.back() = p.back().substr(0, last.size() - 2);
// Resolve the rest of the type
return resolveType(p, owner, logger,
[resultCallback](Handle resolved,
Handle owner,
Logger &logger) {
if (resolved != nullptr) {
Rooted arr{new ArrayType{resolved.cast()}};
resultCallback(arr, owner, logger);
} else {
resultCallback(nullptr, owner, logger);
}
});
}
}
// Requested type is not an array, call the usual resolve function
return resolve(RttiTypes::Type, path, owner, logger, resultCallback);
}
bool ParserScope::resolveType(const std::string &name, Handle owner,
Logger &logger,
ResolutionResultCallback resultCallback)
{
return resolveType(Utils::split(name, '.'), owner, logger, resultCallback);
}
bool ParserScope::resolveTypeWithValue(const std::vector &path,
Handle owner, Variant &value,
Logger &logger,
ResolutionResultCallback resultCallback)
{
// Fork the parser scope -- constants need to be resolved in the same
// context as this resolve call
std::shared_ptr scope = std::make_shared(fork());
return resolveType(
path, owner, logger,
[=](Handle resolved, Handle owner, Logger &logger) mutable {
// Abort if the lookup failed
if (resolved == nullptr) {
resultCallback(resolved, owner, logger);
return;
}
// Fetch the type reference and the manager reference
Rooted type = resolved.cast();
Manager *mgr = &type->getManager();
// The type has been resolved, try to resolve magic values as
// constants and postpone calling the callback function until
// all magic values have been resolved
std::shared_ptr isAsync = std::make_shared(false);
std::shared_ptr magicCount = std::make_shared(0);
type->build(value, logger, [=](Variant &magicValue, bool isValid,
ManagedUid innerTypeUid) mutable {
// Fetch the inner type
Rooted innerType =
dynamic_cast(mgr->getManaged(innerTypeUid));
if (innerType == nullptr) {
return;
}
// Fetch a pointer at the variant
Variant *magicValuePtr = &magicValue;
// Increment the number of encountered magic values
(*magicCount)++;
// Try to resolve the value as constant
std::string constantName = magicValue.asMagic();
scope->resolve(constantName, owner, logger,
[=](Handle resolved,
Handle owner,
Logger &logger) mutable {
if (resolved != nullptr) {
// Make sure the constant is of the correct inner type
Rooted constant = resolved.cast();
Rooted constantType = constant->getType();
if (constantType != innerType) {
logger.error(
std::string("Expected value of type \"") +
innerType->getName() +
std::string("\" but found constant \"") +
constant->getName() +
std::string(" of type \"") +
constantType->getName() + "\" instead.",
*owner);
} else if (!isValid) {
logger.error("Identifier \"" + constantName +
"\" is not a valid " +
innerType->getName(),
*owner);
}
// Nevertheless, no matter what happened, set the value
// of the original magic variant to the given constant
*magicValuePtr = constant->getValue();
}
// Decrement the number of magic values, call the callback
// function if all magic values have been resolved
(*magicCount)--;
if ((*magicCount) == 0 && (*isAsync)) {
resultCallback(resolved, owner, logger);
}
});
});
// Now we are asynchronous
(*isAsync) = true;
// Directly call the callback function if there were no magic values
// involved
if ((*magicCount) == 0) {
resultCallback(resolved, owner, logger);
}
});
}
bool ParserScope::resolveTypeWithValue(const std::string &name,
Handle owner, Variant &value,
Logger &logger,
ResolutionResultCallback resultCallback)
{
return resolveTypeWithValue(Utils::split(name, '.'), owner, value, logger,
resultCallback);
}
bool ParserScope::performDeferredResolution(Logger &logger)
{
// Repeat the resolution process as long as something has changed in the
// last iteration (resolving a node may cause other nodes to be resolvable).
while (true) {
// Iterate over all deferred resolution processes,
bool hasChange = false;
for (auto it = deferred.begin(); it != deferred.end();) {
if (it->resolve(awaitingResolution, logger)) {
if (it->owner != nullptr) {
awaitingResolution.erase(it->owner.get());
}
it = deferred.erase(it);
hasChange = true;
} else {
it++;
}
}
// Abort if nothing has changed in the last iteration
if (!hasChange) {
// In a last step, clear the "awaitingResolution" list to allow
// cyclical dependencies to be resolved
if (!awaitingResolution.empty()) {
awaitingResolution.clear();
} else {
break;
}
}
}
// We were successful if there are no more deferred resolutions
if (deferred.empty()) {
return true;
}
// Output error messages for all elements for which resolution did not
// succeed.
for (auto &failed : deferred) {
failed.fail(logger);
logger.error(std::string("Could not resolve ") + failed.type.name +
std::string(" \"") + Utils::join(failed.path, ".") +
std::string("\""),
*failed.owner);
}
deferred.clear();
return false;
}
}