text:
/* Copyright (C) 2010-2019, The Regents of The University of Michigan.
 All rights reserved.

 This software was developed as part of the The Vulcan project in the Intelligent Robotics Lab
 under the direction of Benjamin Kuipers, kuipers@umich.edu. Use of this code is governed by an
 MIT-style License that can be found at "https://github.com/h2ssh/Vulcan".
*/

/**
* \file     area_transition_detector.cpp
* \author   Collin Johnson
*
* Implementation of AreaTransitionDetector.
*/

#include <hssh/local_topological/event_detection/area_transition_detector.h>
#include <hssh/local_topological/local_topo_map.h>
#include <hssh/local_topological/area.h>
#include <hssh/local_topological/events/area_transition.h>
#include <utils/algorithm_ext.h>
#include <boost/optional/optional_io.hpp>
#include <iostream>

#define DEBUG_TRANSITIONS

namespace vulcan
{
namespace hssh
{

// If more than one possibility, go with the area that is the same as the current area
LocalAreaPtr area_containing_pose(pose_t pose,
                                  const LocalAreaPtr& currentArea,
                                  const math::Rectangle<float>& currentBoundary,
                                  const LocalTopoMap& map);
LocalAreaPtr extent_containing_pose(pose_t pose, const LocalAreaVec& areas);
bool is_uncertain_area(const LocalAreaPtr& area);
gateway_crossing_t find_crossing_gateway(const LocalArea&     previous,
                                         const LocalArea&     current,
                                         const utils::PoseTrace::ConstIter& beginPoses,
                                         const utils::PoseTrace::ConstIter& endPoses,
                                         std::ostream& log);

AreaTransitionDetector::AreaTransitionDetector(const area_transition_detector_params_t& params)
: isInitialArea_(true)
, poses_(0.01f, 100000) // store up to 1000m of driving
, params_(params)
, transitionLog_("transitions.log")
{
}

LocalAreaEventVec AreaTransitionDetector::detectEvents(const LocalTopoMap& map)
{
    static int updateCount = 0;
    ++updateCount;

    /*
    * A transition occurs when the gateway boundary between two areas is crossed.
    */
    LocalAreaEventVec events;

    // Nothing to process if there are no poses available.
    if(poses_.empty())
    {
        return events;
    }

    // For the initial area, there isn't any prior area to disambiguate with
    // Search for a valid pose in the first area. If a pose has no unambiguous area, then it should be erased.
    LocalAreaPtr currentArea;

    while(!poses_.empty() && !currentArea)
    {
        currentArea = area_containing_pose(poses_.front(),
                                           LocalAreaPtr(),
                                           previousBoundary_,
                                           map);
        // Erase poses that don't belong to an area
        if(!currentArea)
        {
            transitionLog_ << "Popped null pose: " << poses_.front() << '\n';
            poses_.pop_front();
        }
    }

    int64_t latestEventTime = 0;

    if(!currentArea)
    {
        std::cout << "Not currently in an area.\n";
        return events;
    }

    previousBoundary_ = currentArea->boundary();

    // The first certain area in which the robot is located always generates an event
    if(isInitialArea_ && !is_uncertain_area(currentArea))
    {
        pose_t currentPose = poses_.front();
        events.emplace_back(new AreaTransitionEvent(currentPose.timestamp,
                                                    localPoses_.nearest_value(currentPose.timestamp),
                                                    currentArea));
        isInitialArea_ = false;
    }

    int entryIndex = 0;
    double distSinceLastEvent = 0.0;
    double turnSinceLastEvent = 0.0;

    // meters to move before automatically going to next area
    // without an event being detected -- caused by a small pose jump or mapping error, so doesn't need to be large
    const double kMaxFailureDistBeforeSkip = 0.5;

    // Distance traveled during a failure condition -- a gateway transition not being detected.
    double failureDist = 0.0;
    std::size_t failStartIndex = poses_.size();
    Gateway lastGateway = latestGateway_;
    LocalAreaEventPtr deferredEvent;    // currently deferred event when a destination was entered

    for(std::size_t n = 0; n < poses_.size(); ++n)
    {
        auto& pose = poses_[n];

        // Only consider poses up until the time of the current map. Since there is processing delay, don't try
        // and create events based on poses in the LPM not considered in the current map labeling.
        if((pose.timestamp > map.timestamp()) && !params_.ignoreStaleMapTimestamps)
        {
            break;
        }

        // Maintain how long the robot has traveled since the last event. In the case of crossing the same gateway,
        // then some minimum amount of travel must have occurred before the event will fire. This hysteresis is
        // necessary to keep small changes in gateway positions from causing spurious area transitions
        if((pose.timestamp > latestEventTime) && (n > 0))
        {
            distSinceLastEvent += distance_between_points(poses_[n-1].toPoint(), poses_[n].toPoint());
            turnSinceLastEvent += angle_diff_abs(poses_[n].theta, poses_[n-1].theta);
        }

        LocalAreaPtr poseArea = area_containing_pose(pose, currentArea, previousBoundary_, map);

        // If we aren't in an area or the area hasn't changed, then no event has occurred for this pose
        if(!poseArea || (poseArea == currentArea) || (poseArea->type() == AreaType::destination))
        {
            continue;
        }

        transitionLog_ << "Update " << updateCount << ": Area changed from " << currentArea->boundary() << " to "
            << poseArea->boundary() << " at " << poses_[n] << " Time (ms): " << (poses_[n].timestamp / 1000)
            << " Dist since last event:" << distSinceLastEvent << '\n';

        // See if a gateway has been crossed
        auto crossing = find_crossing_gateway(*currentArea,
                                              *poseArea,
                                              poses_.begin() + entryIndex,
                                              poses_.begin() + n + 1,
                                              transitionLog_); // want to include this pose in the search

        // If so, consider creating an event
        if(crossing.gateway)
        {
            bool isUncertainArea = is_uncertain_area(poseArea);

            transitionLog_ << "Update " << updateCount << ": Crossed gateway: " << crossing.gateway
                << " Similar to previous? " << lastGateway.isSimilarTo(crossing.gateway.get())
                << " Time(ms): " << (crossing.timestamp / 1000)
                << " Dist since last event: " << distSinceLastEvent << "m Turn: " << turnSinceLastEvent
                << " Uncertain? " << isUncertainArea << '\n';

            // If the gateway is similar to the last gateway crossed, then a twitch in the map might have caused
            // this new event to be detected. Only fire an event if the robot has moved enough since last crossing
            // this same gateway. If the gateways aren't similar, then fire away!
            if(false     //!lastGateway.isSimilarTo(crossing.gateway.get())
                || (distSinceLastEvent > params_.minDistBetweenSameGatewayTransitions)
                || (turnSinceLastEvent > M_PI_2))
            {
                auto event = std::make_shared<AreaTransitionEvent>(crossing.timestamp,
                                                                   localPoses_.nearest_value(crossing.timestamp),
                                                                   currentArea,
                                                                   poseArea,
                                                                   crossing.gateway.get());
                // If a second event occurs while an event has been deferred, then the deferred event
                // should become a real event, as the area was both entered and exited and thus is stable enough
                // to add to the map
                if(isUncertainArea && !deferredEvent)
                {
                    deferredEvent = event;
                    transitionLog_ << "Deferring event: " << event->description() << '\n';
                }
                else
                {
                    // If a deferred event was hanging around, then it needs to get added first as it must have
                    // come before this latest event
                    if(deferredEvent)
                    {
                        transitionLog_ << "Finalizing deferred event: " << deferredEvent->description() << '\n';
                        events.push_back(deferredEvent);
                        deferredEvent.reset();
                    }

                    transitionLog_ << "Adding event: " << event->description() << '\n';
                    events.push_back(event);

                    // When an event is officially created, then store the state so the next update will start
                    // with the correct value. Don't not do this for deferred events because they might get thrown
                    // away and we don't want to then miss detecting other events.
                    // Deferred events will get detected over and over until the ambiguity is resolved.
                    latestEventTime = crossing.timestamp;
                    latestGateway_ = crossing.gateway.get();
                    previousBoundary_ = currentArea->boundary();
                }
            }

            // Whenever a gateway is detected crossed, update the position of the gateway and the time at which
            // it was crossed to keep small pose twitches from slowly accumlating and resulting in an event firing.
            distSinceLastEvent = 0.0;   // reset the distance travelled by the robot, as a transition was detected
            turnSinceLastEvent = 0.0;   // reset the rotation of the robot, since a transition was detected
            failureDist = 0.0;  // a successful crossing was found, so no failure has occurred
            failStartIndex = poses_.size();
            lastGateway = crossing.gateway.get();

            // Only change the current area if a gateway was actually crossed
            entryIndex = n;
            currentArea = poseArea;
        }
        // NOTE: This error isn't critical because it only happens when the robot pose is in a cell right on the
        // boundary of two areas, which will be cleaned up on the next iteration of the transition detector.
        else
        {
            transitionLog_ << "WARNING:AreaTransitionDetector: Did not find an entry gateway when the areas changed!\n"
                << "\tOld area:" << currentArea->boundary() << "\n\tNew area:" << poseArea->boundary()
                << " Failure distance: " << failureDist << '\n';
            if(n > 0)
            {
                failureDist += distance_between_points(poses_[n-1].toPoint(), poses_[n].toPoint());
            }

            failStartIndex = std::min(failStartIndex, n);

            // Jump back to the previous failure point, but with the new index set
            if(failureDist > kMaxFailureDistBeforeSkip)
            {
                transitionLog_ << "INFO::AreaTransitionDetector: Reached the max failure distance. Jumping from idx "
                    << n << " to " << failStartIndex << " with new area " << currentArea->boundary() << '\n';

                // Only change the current area if a gateway was actually crossed
                currentArea = poseArea;
                previousBoundary_ = currentArea->boundary();
                n = failStartIndex;
                failureDist = 0.0;  // a successful crossing was found, so no failure has occurred
                failStartIndex = poses_.size();
            }
        }
    }

    // Erase all poses up to the point where the last event occurred to ensure no duplicate events happen
    poses_.clearBefore(latestEventTime + 1);
    localPoses_.erase_before(latestEventTime + 1);

    return events;
}

void AreaTransitionDetector::addPose(const LocalPose& pose)
{
    poses_.addPose(pose.pose());
    localPoses_.push_back(pose);
}

LocalAreaPtr area_containing_pose(pose_t pose,
                                  const LocalAreaPtr& currentArea,
                                  const math::Rectangle<float>& currentBoundary,
                                  const LocalTopoMap& map)
{
    auto containingAreas = map.allAreasContaining(pose.toPoint());

    // If no area, then a nullptr
    if(containingAreas.empty())
    {
        return nullptr;
    }
    // If one area, then return it
    if(containingAreas.size() == 1)
    {
        return containingAreas.front();
    }
    // Otherwise, try to stay in the previous area if possible.
    else
    {
        // If the extent has a unique area, that's where we are
        auto extentArea = extent_containing_pose(pose, containingAreas);

        if(extentArea)
        {
            return extentArea;
        }

        // Prefer remaining in the same area if possible
        if(utils::contains(containingAreas, currentArea))
        {
            return currentArea;
        }

        // Otherwise, select the area with the most overlap with the current boundary -- which is needed if the areas
        // in the local topo map change slightly for some reason.
        auto maxIt = std::max_element(containingAreas.begin(),
                                      containingAreas.end(),
                                      [&currentBoundary](const LocalAreaPtr& lhs, const LocalAreaPtr& rhs) {
            return lhs->boundary().intersection(currentBoundary).area() >
                rhs->boundary().intersection(currentBoundary).area();
        });

        assert(maxIt != containingAreas.end());
        return *maxIt;
    }
}

LocalAreaPtr extent_containing_pose(pose_t pose, const LocalAreaVec& areas)
{
    auto position = pose.toPoint();
    std::vector<bool> in;

    for(auto& a : areas)
    {
        in.push_back(a->extent().cellContains(position));
    }

    // If match more than one or none, right on the border, so say we aren't anywhere.
    if(std::count(in.begin(), in.end(), true) != 1)
    {
        return nullptr;
    }

    // Otherwise, find the area that we're in.
    return areas[std::distance(in.begin(), std::find(in.begin(), in.end(), true))];
}

bool is_uncertain_area(const LocalAreaPtr& area)
{
    // If there isn't an area, obviously it is uncertain!
    if(!area)
    {
        return true;
    }

    return (area->type() == AreaType::destination) && (area->extent().frontierRatio() > 0.01);
}

gateway_crossing_t find_crossing_gateway(const LocalArea&     previous,
                                         const LocalArea&     current,
                                         const utils::PoseTrace::ConstIter& beginPoses,
                                         const utils::PoseTrace::ConstIter& endPoses,
                                         std::ostream& log)
{
    gateway_crossing_t finalCrossing{boost::none, 0};

    // Search through all poses for the last transition between previous and current. Up to this point, there
    // may have been some twitches between the two that weren't deemed significant. Thus, the final pose going between
    // the two areas is the one that matters.
    for(auto nextIt = beginPoses + 1; nextIt != endPoses; ++nextIt)
    {
        pose_t prevPose = *(nextIt - 1);
        pose_t currentPose = *nextIt;

        // The area has changed -- find the transition gateway
        auto entryCrossing = current.findTransitionGateway(prevPose, currentPose);
        auto exitCrossing  = previous.findTransitionGateway(currentPose, prevPose);

        // If a gateway was crossed, then return that information
        if(entryCrossing.gateway && (entryCrossing.gateway == exitCrossing.gateway))
        {
            finalCrossing = entryCrossing;
            log << "Crossed " << *entryCrossing.gateway << " at " << currentPose << " Time(ms):"
                << (entryCrossing.timestamp / 1000) << '\n';
        }
    }

    return finalCrossing;
}

} // namespace hssh
} // namespace vulcan


text:
/*
 * Copyright 2016-2017 Internet Corporation for Assigned Names and Numbers.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
 */

/*
 * Developed by Sinodun IT (www.sinodun.com)
 */

#ifndef LOG_HPP
#define LOG_HPP

#include "no-register-warning.hpp"
#include <boost/log/trivial.hpp>

#define LOG_ERROR       BOOST_LOG_TRIVIAL(error)
#define LOG_WARN        BOOST_LOG_TRIVIAL(warning)
#define LOG_INFO        BOOST_LOG_TRIVIAL(info)

void init_logging();

#endif

ext: hpp

max_stars_repo_name: dns-stats/dns-stats-compactor


text:
#include <boost/test/unit_test.hpp>
#include <boost/test/data/test_case.hpp>
#include <boost/test/data/monomorphic.hpp>
#include <boost/algorithm/string/split.hpp>

#include <string>
#include <vector>
#include <memory>

#include <tao/pegtl.hpp>

#include "../../include/ast.hpp"
#include "../../include/grammar.hpp"

#include "test_setup.hpp"

namespace data = boost::unit_test::data;
using tao::pegtl::parse;
using tao::pegtl::string_input;
namespace pt = tao::pegtl::parse_tree;
namespace gr = rhea::grammar;
namespace ast = rhea::ast;

namespace {
    template <typename GrammarNode>
    std::unique_ptr<ast::parser_node> tree_builder(string_input<>& in)
    {
        return pt::parse<
            GrammarNode,
            ast::parser_node,
            ast::tree_selector
        >(in);
    }

    // Datasets

    // Test cases
    BOOST_AUTO_TEST_SUITE (AST_builder)

    BOOST_AUTO_TEST_CASE (builder_bare_expression)
    {
        std::string sample { "1.23;" };

        BOOST_TEST_MESSAGE("Parsing bare expression " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::bare_expression>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(FloatingPoint,1.23,3))"));
    }

    BOOST_AUTO_TEST_CASE (builder_fully_qualified_expression)
    {
        std::string sample { "foo:bar:baz;" };

        BOOST_TEST_MESSAGE("Parsing fully-qualified identifier expression " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::bare_expression>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(BareExpression,(FullyQualified,foo,bar,baz))"));
    }

    BOOST_AUTO_TEST_CASE (builder_relative_identifier_expression)
    {
        std::string sample { ":foo:bar;" };

        BOOST_TEST_MESSAGE("Parsing relative identifier expression " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::bare_expression>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(BareExpression,(RelativeIdentifier,foo,bar))"));
    }

    BOOST_AUTO_TEST_CASE (builder_binop_expression)
    {
        std::string sample { "42 + 24;" };

        BOOST_TEST_MESSAGE("Parsing binary operation " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::bare_expression>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(BinaryOp,0,(Integral,42,0),(Integral,24,0)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_unaryop_expression)
    {
        std::string sample { "not x;" };

        BOOST_TEST_MESSAGE("Parsing unary operation " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::bare_expression>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(UnaryOp,2,(Identifier,x)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_literal_expressions)
    {
        std::string boolean_lit { "true;" };
        std::string nothing_lit { "nothing;" };
        std::string symbol_lit { "@foo;" };
        std::string short_hex_lit { "0x1234;" };
        std::string long_hex_lit { "0x0000111122223333;" };
        std::string string_lit { "\"abc\";" };

        BOOST_TEST_MESSAGE("Parsing boolean literal " << boolean_lit);
        string_input<> in1(boolean_lit, "test");

        auto tree = tree_builder<gr::bare_expression>(in1);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(Boolean,true))"));

        BOOST_TEST_MESSAGE("Parsing nothing literal " << nothing_lit);
        string_input<> in2(nothing_lit, "test");

        tree = tree_builder<gr::bare_expression>(in2);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(Nothing))"));

        BOOST_TEST_MESSAGE("Parsing symbol literal " << symbol_lit);
        string_input<> in3(symbol_lit, "test");

        tree = tree_builder<gr::bare_expression>(in3);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(Symbol,foo))"));

        BOOST_TEST_MESSAGE("Parsing short hex literal " << short_hex_lit);
        string_input<> in4(short_hex_lit, "test");

        tree = tree_builder<gr::bare_expression>(in4);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(Integral,4660,5))"));

        BOOST_TEST_MESSAGE("Parsing long hex literal " << long_hex_lit);
        string_input<> in5(long_hex_lit, "test");

        tree = tree_builder<gr::bare_expression>(in5);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(BareExpression,(Integral,18765284782899,7))"));

        BOOST_TEST_MESSAGE("Parsing string literal " << string_lit);
        string_input<> in6(string_lit, "test");

        tree = tree_builder<gr::bare_expression>(in6);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(BareExpression,(String,\"abc\"))"));
    }

    BOOST_AUTO_TEST_CASE (builder_vector_expressions)
    {
        std::string array_expr { "[1,2,3];" };
        std::string list_expr { "(1,2,3);" };
        std::string tuple_expr { "({1,2,3});" };
        std::string symlist_expr { "@{a,b,c};" };
        std::string dictionary_expr { "{@a: 1, @b: 2};" };

        BOOST_TEST_MESSAGE("Parsing array expression " << array_expr);
        string_input<> in1(array_expr, "test");

        auto tree = tree_builder<gr::bare_expression>(in1);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(Array,(Integral,1,0),(Integral,2,0),(Integral,3,0)))"));

        BOOST_TEST_MESSAGE("Parsing list expression " << list_expr);
        string_input<> in2(list_expr, "test");

        tree = tree_builder<gr::bare_expression>(in2);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(List,(Integral,1,0),(Integral,2,0),(Integral,3,0)))"));

        BOOST_TEST_MESSAGE("Parsing tuple expression " << tuple_expr);
        string_input<> in3(tuple_expr, "test");

        tree = tree_builder<gr::bare_expression>(in3);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(Tuple,(Integral,1,0),(Integral,2,0),(Integral,3,0)))"));

        BOOST_TEST_MESSAGE("Parsing symbol list expression " << symlist_expr);
        string_input<> in4(symlist_expr, "test");

        tree = tree_builder<gr::bare_expression>(in4);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(SymbolList,(Symbol,a),(Symbol,b),(Symbol,c)))"));

        BOOST_TEST_MESSAGE("Parsing dictionary expression " << dictionary_expr);
        string_input<> in5(dictionary_expr, "test");

        tree = tree_builder<gr::bare_expression>(in5);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(Dictionary,(DictionaryEntry,(Symbol,a),(Integral,1,0)),(DictionaryEntry,(Symbol,b),(Integral,2,0))))"));
    }

    BOOST_AUTO_TEST_CASE (builder_member_expression)
    {
        std::string sample { "x.y;" };

        BOOST_TEST_MESSAGE("Parsing member expression " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::bare_expression>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(Member,(Identifier,y),(Identifier,x)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_subscript_expression)
    {
        std::string sample { "x[y];" };

        BOOST_TEST_MESSAGE("Parsing subscript operation " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::bare_expression>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(Subscript,(Identifier,x),(Identifier,y)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_ternary_expression)
    {
        std::string sample { "(if a then b else c);" };

        BOOST_TEST_MESSAGE("Parsing ternary operation " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::bare_expression>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(TernaryOp,(Identifier,a),(Identifier,b),(Identifier,c)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_cast_expression)
    {
        std::string sample { "x as long;" };

        BOOST_TEST_MESSAGE("Parsing cast operation " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::bare_expression>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(Cast,(Identifier,x),(Typename,(Identifier,long),null,null)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_typecheck_expression)
    {
        std::string sample { "x is long;" };

        BOOST_TEST_MESSAGE("Parsing type-check operation " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::bare_expression>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(TypeCheck,(Identifier,x),(Typename,(Identifier,long),null,null)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_function_call_expressions)
    {
        std::string empty_args { "f();" };
        std::string pos_args { "f(1,2);" };
        std::string named_args { "f(a: 1, b: 2);" };

        BOOST_TEST_MESSAGE("Parsing function call with empty arguments " << empty_args);
        string_input<> in_empty(empty_args, "test");

        auto tree = tree_builder<gr::bare_expression>(in_empty);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(Call,(Identifier,f)))"));

        BOOST_TEST_MESSAGE("Parsing function call with positional arguments " << pos_args);
        string_input<> in_pos(pos_args, "test");

        tree = tree_builder<gr::bare_expression>(in_pos);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(BareExpression,(Call,(Identifier,f),(Integral,1,0),(Integral,2,0)))"));

        BOOST_TEST_MESSAGE("Parsing function call with named arguments " << named_args);
        string_input<> in_named(named_args, "test");

        tree = tree_builder<gr::bare_expression>(in_named);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() ==
            "(BareExpression,(Call,(Identifier,f),(NamedArgument,a,(Integral,1,0)),(NamedArgument,b,(Integral,2,0))))"));
    }

    BOOST_AUTO_TEST_CASE (builder_variable_declaration)
    {
        std::string sample { "var x = y * z;" };

        BOOST_TEST_MESSAGE("Parsing variable declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(Variable,(Identifier,x),(BinaryOp,2,(Identifier,y),(Identifier,z)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_constant_declaration)
    {
        std::string sample { "const x = 42;" };

        BOOST_TEST_MESSAGE("Parsing constant declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(Constant,(Identifier,x),(Integral,42,0))"));
    }

    BOOST_AUTO_TEST_CASE (builder_assign_statement)
    {
        std::string sample { "foo = bar ** 2;" };

        BOOST_TEST_MESSAGE("Parsing assignment " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(Assign,(Identifier,foo),(BinaryOp,5,(Identifier,bar),(Integral,2,0)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_compound_assign_statement)
    {
        std::string sample { "i -= 1;" };

        BOOST_TEST_MESSAGE("Parsing compound assignment " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(CompoundAssign,(Identifier,i),(Integral,1,0),1)"));
    }

    BOOST_AUTO_TEST_CASE (builder_enum_declaration)
    {
        std::string sample { "type En = @{a,b,c};" };

        BOOST_TEST_MESSAGE("Parsing enum declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(Enum,(Identifier,En),(SymbolList,(Symbol,a),(Symbol,b),(Symbol,c)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_type_alias_declaration)
    {
        std::string sample { "type T = |A,B|;" };

        BOOST_TEST_MESSAGE("Parsing type alias declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(Alias,(Identifier,T),(Variant,(Typename,(Identifier,A),null,null),(Typename,(Identifier,B),null,null)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_structure_declaration)
    {
        std::string sample { "type Person = { name: string, age: integer };" };

        BOOST_TEST_MESSAGE("Parsing structure declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(Structure,(Identifier,Person),(TypePair,name,(Typename,(Identifier,string),null,null)),(TypePair,age,(Typename,(Identifier,integer),null,null)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_do_statement)
    {
        std::string sample { "do foo;" };

        BOOST_TEST_MESSAGE("Parsing 'do' statement " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(Do,(Identifier,foo))"));
    }

    BOOST_AUTO_TEST_CASE (builder_statement_block)
    {
        std::string sample { "{do foo; do bar;}" };

        BOOST_TEST_MESSAGE("Parsing statement block " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement_block>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(Block,(Do,(Identifier,foo)),(Do,(Identifier,bar)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_if_statement)
    {
        std::string no_else { "if x do foo;" };

        BOOST_TEST_MESSAGE("Parsing 'if' statement " << no_else);
        string_input<> in(no_else, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(If,(Identifier,x),(Do,(Identifier,foo)),null)"));

        std::string with_else { "if y do foo; else do bar;"};

        BOOST_TEST_MESSAGE("Parsing 'if-else' statement " << with_else);
        string_input<> in2(with_else, "test");

        tree = tree_builder<gr::statement>(in2);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(If,(Identifier,y),(Do,(Identifier,foo)),(Do,(Identifier,bar)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_unless_statement)
    {
        std::string sample { "unless foo do foo;" };

        BOOST_TEST_MESSAGE("Parsing 'unless' statement " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(If,(Identifier,foo),null,(Do,(Identifier,foo)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_while_statement)
    {
        std::string sample { "while (x < 10) { x += 1; }" };

        BOOST_TEST_MESSAGE("Parsing 'while' statement " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() ==
            "(While,(BinaryOp,10,(Identifier,x),(Integral,10,0)),(Block,(CompoundAssign,(Identifier,x),(Integral,1,0),0)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_for_statement)
    {
        std::string sample { "for i in range { do foo; }" };

        BOOST_TEST_MESSAGE("Parsing 'for' statement " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(For,i,(Identifier,range),(Block,(Do,(Identifier,foo))))"));
    }

    BOOST_AUTO_TEST_CASE (builder_break_continue_statements)
    {
        std::string sample { "{ break; continue; }" };

        BOOST_TEST_MESSAGE("Parsing 'break' and 'continue' statements " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement_block>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(Block,(Break),(Continue))"));
    }

    BOOST_AUTO_TEST_CASE (builder_return_statement)
    {
        std::string sample { "return false;" };

        BOOST_TEST_MESSAGE("Parsing return statement " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(Return,(Boolean,false))"));
    }

    BOOST_AUTO_TEST_CASE (builder_extern_declaration)
    {
        std::string sample { "extern foo;" };

        BOOST_TEST_MESSAGE("Parsing extern declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(Extern,foo)"));
    }

    BOOST_AUTO_TEST_CASE (builder_typenames)
    {
        std::string simple { "var x as y;" };
        std::string fq_type { "var x as foo:bar:baz;" };
        std::string relative_type { "var x as :foo:bar;" };
        std::string generic_type { "var x as list <string>;" };
        std::string array_type { "var x as byte[16];" };
        std::string array_generic_type { "var x as list <string> [10];" };
        std::string multid_array_type { "var x as byte [256][16];" };
        std::string variant_type { "var x as |integer, string|;" };
        std::string optional_type { "var x as |integer|?;" };

        BOOST_TEST_MESSAGE("Parsing simple typename declaration " << simple);
        string_input<> in_simple(simple, "test");

        auto tree = tree_builder<gr::statement>(in_simple);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(TypeDeclaration,(Identifier,x),(Typename,(Identifier,y),null,null))"));

        BOOST_TEST_MESSAGE("Parsing fully-qualified typename declaration " << fq_type);
        string_input<> in_fq(fq_type, "test");

        tree = tree_builder<gr::statement>(in_fq);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(TypeDeclaration,(Identifier,x),(Typename,(FullyQualified,foo,bar,baz),null,null))"));

        BOOST_TEST_MESSAGE("Parsing relative typename declaration " << relative_type);
        string_input<> in_relative(relative_type, "test");

        tree = tree_builder<gr::statement>(in_relative);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(TypeDeclaration,(Identifier,x),(Typename,(RelativeIdentifier,foo,bar),null,null))"));

        BOOST_TEST_MESSAGE("Parsing generic typename declaration " << generic_type);
        string_input<> in_generic(generic_type, "test");

        tree = tree_builder<gr::statement>(in_generic);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(TypeDeclaration,(Identifier,x),(Typename,(Identifier,list),(GenericTypename,(Typename,(Identifier,string),null,null)),null))"));

        BOOST_TEST_MESSAGE("Parsing array typename declaration " << array_type);
        string_input<> in_array(array_type, "test");

        tree = tree_builder<gr::statement>(in_array);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(TypeDeclaration,(Identifier,x),(Typename,(Identifier,byte),null,(ArrayTypename,(Integral,16,0))))"));

        BOOST_TEST_MESSAGE("Parsing array and generic typename declaration " << array_generic_type);
        string_input<> in_array_generic(array_generic_type, "test");

        tree = tree_builder<gr::statement>(in_array_generic);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(TypeDeclaration,(Identifier,x),(Typename,(Identifier,list),(GenericTypename,(Typename,(Identifier,string),null,null)),(ArrayTypename,(Integral,10,0))))"));

        BOOST_TEST_MESSAGE("Parsing multidimensional array typename declaration " << multid_array_type);
        string_input<> in_mdarray(multid_array_type, "test");

        tree = tree_builder<gr::statement>(in_mdarray);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(TypeDeclaration,(Identifier,x),(Typename,(Identifier,byte),null,(ArrayTypename,(Integral,256,0),(Integral,16,0))))"));

        BOOST_TEST_MESSAGE("Parsing variant declaration " << variant_type);
        string_input<> in_variant(variant_type, "test");

        tree = tree_builder<gr::statement>(in_variant);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(TypeDeclaration,(Identifier,x),(Variant,(Typename,(Identifier,integer),null,null),(Typename,(Identifier,string),null,null)))"));

        BOOST_TEST_MESSAGE("Parsing optional declaration " << optional_type);
        string_input<> in_optional(optional_type, "test");

        tree = tree_builder<gr::statement>(in_optional);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(TypeDeclaration,(Identifier,x),(Optional,(Typename,(Identifier,integer),null,null)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_simple_basic_function_def)
    {
        std::string sample { "def f = { return true; }" };

        BOOST_TEST_MESSAGE("Parsing simple function definition " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() == "(Def,0,f,null,null,(Conditions),(Block,(Return,(Boolean,true))))"));
    }

    BOOST_AUTO_TEST_CASE (builder_simple_basic_generice_function_def)
    {
        std::string sample { "def f <t : T> = { return true; }" };

        BOOST_TEST_MESSAGE("Parsing simple function definition " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Def,0,f,(GenericTypes,(TypePair,t,(Typename,(Identifier,T),null,null))),null,null,(Conditions),(Block,(Return,(Boolean,true))))"));
    }

    BOOST_AUTO_TEST_CASE (builder_basic_function_def_return_type)
    {
        std::string sample { "def f [boolean] = { return true; }" };

        BOOST_TEST_MESSAGE("Parsing function definition with return type " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST((node->to_string() ==
            "(Def,0,f,(Typename,(Identifier,boolean),null,null),null,(Conditions),(Block,(Return,(Boolean,true))))"));
    }

    BOOST_AUTO_TEST_CASE (builder_basic_function_def_with_args)
    {
        std::string sample { "def f { t: T } = { return true; }" };

        BOOST_TEST_MESSAGE("Parsing function definition with arguments " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() ==
            "(Def,0,f,null,(Arguments,(TypePair,t,(Typename,(Identifier,T),null,null))),(Conditions),(Block,(Return,(Boolean,true))))"));
    }

    BOOST_AUTO_TEST_CASE (builder_basic_function_def_with_condition)
    {
        std::string sample { "def f { t: T } with { t.p? } = { return true; }" };

        BOOST_TEST_MESSAGE("Parsing function definition with conditions " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() ==
            "(Def,0,f,null,(Arguments,(TypePair,t,(Typename,(Identifier,T),null,null))),(Conditions,(Condition,t,(PredicateCall,(Identifier,p)))),(Block,(Return,(Boolean,true))))"));
    }

    BOOST_AUTO_TEST_CASE (builder_basic_function_def_with_condition_arguments)
    {
        std::string sample { "def f { t: T } with { t.p(42)? } = { return true; }" };

        BOOST_TEST_MESSAGE("Parsing function definition with a condition taking arguments " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() ==
            "(Def,0,f,null,(Arguments,(TypePair,t,(Typename,(Identifier,T),null,null))),(Conditions,(Condition,t,(PredicateCall,(Identifier,p),(Integral,42,0)))),(Block,(Return,(Boolean,true))))"));
    }

    BOOST_AUTO_TEST_CASE (builder_basic_function_def_with_wildcard_arg)
    {
        std::string sample { "def f { t: * } = { return true; }" };

        BOOST_TEST_MESSAGE("Parsing function definition with wildcard argument " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() ==
            "(Def,0,f,null,(Arguments,(TypePair,t,(Typename,(Identifier,$$wildcard$$),null,null))),(Conditions),(Block,(Return,(Boolean,true))))"));
    }

    BOOST_AUTO_TEST_CASE (builder_throw_statement)
    {
        std::string sample { "throw foo;" };

        BOOST_TEST_MESSAGE("Parsing throw statement " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Throw,(Identifier,foo))"));
    }

    BOOST_AUTO_TEST_CASE (builder_try_statement)
    {
        std::string sample { "try { do x; };" };

        BOOST_TEST_MESSAGE("Parsing try statement " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Try,(Block,(Do,(Identifier,x))),null)"));
    }

    BOOST_AUTO_TEST_CASE (builder_try_statement_with_finally)
    {
        std::string sample { "try { do x; } finally { do y; };" };

        BOOST_TEST_MESSAGE("Parsing try statement with finally " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Try,(Block,(Do,(Identifier,x))),(Finally,(Block,(Do,(Identifier,y)))))"));
    }

    BOOST_AUTO_TEST_CASE (builder_try_statement_with_catch)
    {
        std::string sample { "try { do x; } catch { e : E } { do y; };" };

        BOOST_TEST_MESSAGE("Parsing try statement with catch " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());
        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Try,(Block,(Do,(Identifier,x))),null,(Catch,(TypePair,e,(Typename,(Identifier,E),null,null)),(Block,(Do,(Identifier,y)))))"));
    }

    BOOST_AUTO_TEST_CASE (builder_concept_definition)
    {
        std::string sample { "concept C <T> = { T .= foo }" };

        BOOST_TEST_MESSAGE("Parsing concept definition with member check " << sample);
        string_input<> in1(sample, "test");

        auto tree = tree_builder<gr::statement>(in1);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Concept,C,T,(MemberCheck,T,foo))"));

        sample = "concept C <T> = { T => foo <T> -> string }";

        BOOST_TEST_MESSAGE("Parsing concept definition with function check " << sample);
        string_input<> in2(sample, "test");

        tree = tree_builder<gr::statement>(in2);

        node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Concept,C,T,(FunctionCheck,T,(Identifier,foo),0,(Typename,(Identifier,string),null,null),(Typename,(Identifier,T),null,null)))"));
    }

    BOOST_AUTO_TEST_CASE (builder_use_statement)
    {
        std::string sample { "use foo;" };

        BOOST_TEST_MESSAGE("Parsing simple use declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Use,(ModuleName,foo))"));
    }

    BOOST_AUTO_TEST_CASE (builder_use_statement_relative)
    {
        std::string sample { "use :relative_id;" };

        BOOST_TEST_MESSAGE("Parsing relative use declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Use,(ModuleName,:relative_id))"));
    }

    BOOST_AUTO_TEST_CASE (builder_use_statement_fully_qualified)
    {
        std::string sample { "use foo:bar:baz;" };

        BOOST_TEST_MESSAGE("Parsing fully-qualified use declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Use,(ModuleName,foo:bar:baz))"));
    }

    BOOST_AUTO_TEST_CASE (builder_module_declaration)
    {
        std::string sample { "module my_module;" };

        BOOST_TEST_MESSAGE("Parsing simple module declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(ModuleDef,(ModuleName,my_module))"));
    }

    BOOST_AUTO_TEST_CASE (builder_module_declaration_fully_qualified)
    {
        std::string sample { "module org:example:my_module;" };

        BOOST_TEST_MESSAGE("Parsing fully-qualified module declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(ModuleDef,(ModuleName,org:example:my_module))"));
    }

    BOOST_AUTO_TEST_CASE (builder_import_declaration)
    {
        std::string sample { "import { foo, bar } from my_module;" };

        BOOST_TEST_MESSAGE("Parsing import declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Import,(ModuleName,my_module),(Identifier,foo),(Identifier,bar))"));
    }

    BOOST_AUTO_TEST_CASE (builder_import_declaration_fully_qualified)
    {
        std::string sample { "import { foo, bar } from org:example:my_module;" };

        BOOST_TEST_MESSAGE("Parsing import declaration with fully-qualified module " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Import,(ModuleName,org:example:my_module),(Identifier,foo),(Identifier,bar))"));
    }

    BOOST_AUTO_TEST_CASE (builder_export_declaration)
    {
        std::string sample { "export { foo, bar };" };

        BOOST_TEST_MESSAGE("Parsing export declaration " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::statement>(in);

        auto node = ast::internal::create_statement_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() == "(Export,(Identifier,foo),(Identifier,bar))"));
    }

    BOOST_AUTO_TEST_CASE (builder_simple_program_definition)
    {
        std::string sample { "def main = { return true; }" };

        BOOST_TEST_MESSAGE("Parsing program definition " << sample);
        string_input<> in(sample, "test");

        auto tree = tree_builder<gr::program_definition>(in);

        auto node = ast::internal::create_top_level_node(tree->children.front().get());

        BOOST_TEST_MESSAGE((node->position));
        BOOST_TEST_MESSAGE((node->to_string()));
        BOOST_TEST((node->to_string() ==
            "(Program,(Def,0,main,null,null,(Conditions),(Block,(Return,(Boolean,true)))))"));
    }

    BOOST_AUTO_TEST_SUITE_END ()
}

ext: cpp

max_stars_repo_name: momikey/rhea

max_issues_repo_name: momikey/rhea

max_forks_repo_name: momikey/rhea


text:
//==================================================================================================
/*!

  Copyright 2015 NumScale SAS
  Copyright 2015 J.T. Lapreste

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
*/
//==================================================================================================
#include <boost/simd/function/scalar/realpow.hpp>
#include <simd_test.hpp>
#include <boost/simd/constant/inf.hpp>
#include <boost/simd/constant/minf.hpp>
#include <boost/simd/constant/nan.hpp>
#include <boost/simd/constant/one.hpp>
#include <boost/simd/constant/mone.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/constant/mzero.hpp>

STF_CASE_TPL (" realpow",  STF_IEEE_TYPES)
{
  namespace bs = boost::simd;
  namespace bd = boost::dispatch;
  using bs::realpow;

  using r_t = decltype(realpow(T(), T()));

  // return type conformity test
  STF_TYPE_IS(r_t, T);

  // specific values tests
#ifndef BOOST_SIMD_NO_INVALIDS
  STF_ULP_EQUAL(realpow(bs::Inf<T>(), bs::Inf<T>()), bs::Inf<r_t>(), 0);
  STF_ULP_EQUAL(realpow(bs::Nan<T>(), bs::Nan<T>()), bs::Nan<r_t>(), 0);
#endif
  STF_ULP_EQUAL(realpow(bs::Mone<T>(), bs::Mone<T>()), bs::Mone<r_t>(), 0);
  STF_ULP_EQUAL(realpow(bs::One<T>(), bs::One<T>()), bs::One<r_t>(), 0);
  STF_ULP_EQUAL(realpow(bs::Zero<T>(), bs::Zero<T>()), bs::One<r_t>(), 0);
  STF_ULP_EQUAL(realpow(T(-1),T(5)), T(-1), 0);
  STF_ULP_EQUAL(realpow(T(-1),T(6)), T(1), 0);
}


text:
#pragma once

/**
 *
 * Partly adopted from https://github.com/friedmud/variadic_table
 *
 * GNU LESSER GENERAL PUBLIC LICENSE
 * Version 2.1, February 1999
 *
 * Copyright (C) 1991, 1999 Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * Everyone is permitted to copy and distribute verbatim copies
 * of this license document, but changing it is not allowed.
 *
 * [This is the first released version of the Lesser GPL.  It also counts
 * as the successor of the GNU Library Public License, version 2, hence
 * the version number 2.1.]
 *
 */

#include <map>
#include <tuple>
#include <vector>
#include <string>
#include <iostream>
#include <unordered_map>

#include <boost/lexical_cast.hpp>

namespace util
{

    /**
     * A printable table with data of different types.
     */
    template <typename ...T>
    class Table
    {
    public:

        /* Types */

        using data_type = std::tuple<T...>;

    protected:

        /* Members */

        std::vector<std::string> m_headers;
        std::vector<data_type> m_rows;
        std::vector<std::size_t> m_col_widths;
        std::size_t m_total_width;

    public:

        /* Constructors */

        Table(const std::vector<std::string>& headers) : m_headers(headers)
        {
            if (headers.size() != sizeof...(T))
            {
                throw std::invalid_argument("Number of headers doesn't match the number of columns.");
            }
        };

        template <typename K, typename V>
        Table(const std::vector<std::string>& headers, const std::map<K, V>& map) : m_headers(headers)
        {
            if (headers.size() != 2)
            {
                throw std::invalid_argument("Number of headers has to be 2 for tables from maps.");
            }
            for (const auto& entry : map)
            {
                add_row(entry.first, entry.second);
            }
        };

        template <typename K, typename V>
        Table(const std::vector<std::string>& headers, const std::unordered_map<K, V>& map) : m_headers(headers)
        {
            if (headers.size() != 2)
            {
                throw std::invalid_argument("Number of headers has to be 2 for tables from maps.");
            }
            for (const auto& entry : map)
            {
                add_row(entry.first, entry.second);
            }
        };

        /* Methods */

        const bool empty() const
        {
            return m_rows.empty();
        }

        const std::size_t column_count() const
        {
            return m_headers.size();
        }

        const std::size_t row_count() const
        {
            return m_rows.size();
        }

        void add_row(T... entry)
        {
            m_rows.push_back(std::make_tuple(entry...));
        }

    protected:

    /* Helpers */

        template <typename ValueType>
        std::size_t length(const ValueType& value) const
        {
            return boost::lexical_cast<std::string>(value).length();
        }

        /**
         * Recursion end
         */
        template <typename TupleType>
        void size_columns(TupleType&&, std::vector<std::size_t>& col_widths, std::integral_constant<
            std::size_t,
            std::tuple_size<typename std::remove_reference<TupleType>::type>::value>
        ) {
            return;
        }

        /**
         * Recursion step
         */
        template <std::size_t I, typename TupleType,
            typename = typename std::enable_if<I != std::tuple_size<typename std::remove_reference<TupleType>::type>::value>::type>
        void size_columns(TupleType&& tuple, std::vector<std::size_t>& col_widths, std::integral_constant<std::size_t, I>)
        {
            col_widths.at(I) = std::max(col_widths.at(I), length(std::get<I>(tuple)));
            size_columns(
                std::forward<TupleType>(tuple),
                col_widths,
                std::integral_constant<std::size_t, I + 1>()
            );
        }

        /**
         * Recursion start
         */
        template <typename TupleType>
        void size_columns(TupleType&& tuple, std::vector<std::size_t>& col_widths)
        {
            size_columns(
                std::forward<TupleType>(tuple),
                col_widths,
                std::integral_constant<std::size_t, 0>()
            );
        }

        void resize()
        {
            // Determine the width for each column
            for (std::size_t i = 0; i < column_count(); i++)
            {
                m_col_widths.push_back(m_headers.at(i).length());
            }
            for (data_type& row : m_rows)
            {
                size_columns(row, m_col_widths);
            }

            // Determine the total width
            // Each column gets printed as | content |, so the total
            // width is #cols * (width[col] + 2 * ' ' + 1 * '|') + 1
            m_total_width = 0;
            for (std::size_t col_width : m_col_widths)
            {
                m_total_width += col_width + 3;
            }
            m_total_width += 1;
        }

        /* Printing Helpers */

        /**
         * Recursion end
         */
        template <typename TupleType, typename StreamType>
        void print_cells(TupleType&&, StreamType&, std::integral_constant<
            std::size_t,
            std::tuple_size<typename std::remove_reference<TupleType>::type>::value>
        ) {
            return;
        }

        /**
         * Recursion step
         */
        template <std::size_t I, typename TupleType, typename StreamType,
            typename = typename std::enable_if<I != std::tuple_size<typename std::remove_reference<TupleType>::type>::value>::type>
        void print_cells(TupleType&& tuple, StreamType& stream, std::integral_constant<std::size_t, I>)
        {
            // Print current tuple value
            std::string cell = boost::lexical_cast<std::string>(std::get<I>(tuple));
            stream << ' '
                << cell
                << std::string(m_col_widths.at(I) - cell.length(), ' ')
                << ' '
                << '|';
            // Print next value
            print_cells(std::forward<TupleType>(tuple), stream, std::integral_constant<std::size_t, I + 1>());
        }

        /**
         * Recursion start
         */
        template <typename TupleType, typename StreamType>
        void print_cells(TupleType&& tuple, StreamType& stream)
        {
            print_cells(std::forward<TupleType>(tuple), stream, std::integral_constant<std::size_t, 0>());
        }

    public:

        /* Printing methods */

        template <typename StreamType>
        void print(StreamType& stream)
        {
            // Resize table
            resize();

            // Print the top line
            stream << std::string(m_total_width, '-') << '\n';

            // Print the headers (left-aligned)
            stream << '|';
            for (std::size_t i = 0; i < column_count(); i++)
            {
                stream << ' '
                    << m_headers.at(i)
                    << std::string(m_col_widths.at(i) - m_headers.at(i).length(), ' ')
                    << ' '
                    << '|';
            }
            stream << '\n';

            // Print the divider
            stream << std::string(m_total_width, '-') << '\n';

            if (!m_rows.empty())
            {
                // Print the rows (left-aligned)
                for (data_type& row : m_rows)
                {
                    stream << '|';
                    print_cells(row, stream);
                    stream << '\n';
                }
            }
            else
            {
                // Print an empty row
                stream << '|' << std::string(m_total_width - 2, ' ') << '|' << '\n';
            }

            // Print the bottom line
            stream << std::string(m_total_width, '-') << std::endl;
        }

    };

}

max_stars_repo_name: PatrickLindemann/warzone-osm-processor

max_issues_repo_name: PatrickLindemann/warzone-osm-processor

max_forks_repo_name: PatrickLindemann/warzone-osm-processor