<commit_before>// Copyright (C) 2016 Jonathan Müller <jonathanmueller.dev@gmail.com>
// This file is subject to the license terms in the LICENSE file
// found in the top-level directory of this distribution.

#ifndef TYPE_SAFE_TAGGED_UNION_HPP_INCLUDED
#define TYPE_SAFE_TAGGED_UNION_HPP_INCLUDED

#include <new>

#include <type_safe/detail/aligned_union.hpp>
#include <type_safe/config.hpp>
#include <type_safe/strong_typedef.hpp>

namespace type_safe
{
    /// \exclude
    namespace detail
    {
        struct union_type_id : strong_typedef<union_type_id, std::size_t>,
                               strong_typedef_op::equality_comparison<union_type_id, bool>,
                               strong_typedef_op::relational_comparison<union_type_id, bool>
        {
            using strong_typedef<union_type_id, std::size_t>::strong_typedef;
        };

        //=== get_type_index ==//
        template <typename T, typename... Ts>
        struct get_type_index_impl;

        // type not found at all
        template <typename T>
        struct get_type_index_impl<T> : std::integral_constant<std::size_t, 0>
        {
        };

        // found type at the beginning
        template <typename T, typename... Tail>
        struct get_type_index_impl<T, T, Tail...> : std::integral_constant<std::size_t, 1>
        {
        };

        // type not in the beginning
        template <typename T, typename Head, typename... Tail>
        struct get_type_index_impl<T, Head, Tail...>
            : std::integral_constant<std::size_t, get_type_index_impl<T, Tail...>::value == 0u ?
                                                      0u :
                                                      1 + get_type_index_impl<T, Tail...>::value>
        {
        };

        template <typename T, typename... Types>
        using get_type_index = get_type_index_impl<T, typename std::decay<Types>::type...>;
    } // namespace detail

    /// Tag type so no explicit template instantiation of function parameters is required.
    /// \module variant
    template <typename T>
    struct union_type
    {
    };

    /// Very basic typelist.
    /// \module variant
    template <typename... Ts>
    struct union_types
    {
    };

    /// A tagged union.
    ///
    /// It is much like a plain old C `union`,
    /// but remembers which type it currently stores.
    /// It can either store one of the given types or no type at all.
    /// \notes Like the C `union` it does not automatically destroy the currently stored type,
    /// and copy operations are deleted.
    /// \module variant
    template <typename... Types>
    class tagged_union
    {
    public:
        using types = union_types<typename std::decay<Types>::type...>;

        /// The id of a type.
        ///
        /// It is a [ts::strong_typedef]() for `std::size_t`
        /// and provides equality and relational comparison.
        class type_id : public strong_typedef<type_id, std::size_t>,
                        public strong_typedef_op::equality_comparison<type_id, bool>,
                        public strong_typedef_op::relational_comparison<type_id, bool>
        {
        public:
            /// \effects Initializes it to an invalid value.
            /// \notes The invalid value compares less than all valid values.
            constexpr type_id() noexcept : strong_typedef<type_id, std::size_t>(0u)
            {
            }

            /// \effects Initializes it to the value of the type `T`.
            /// If `T` is not one of the types of the union types,
            /// it will be the same as the default constructor.
            template <typename T>
            constexpr type_id(union_type<T>) noexcept
            : type_id(detail::get_type_index<T, Types...>::value)
            {
            }

            /// \returns `true` if the id is valid,
            /// `false` otherwise.
            explicit operator bool() const noexcept
            {
                return *this != type_id();
            }

        private:
            explicit constexpr type_id(std::size_t value)
            : strong_typedef<type_id, std::size_t>(value)
            {
            }
        };

        /// A global invalid type id object.
        static constexpr type_id invalid_type = type_id();

        //=== constructors/destructors/assignment ===//
        tagged_union() noexcept = default;

        /// \notes Does not destroy the currently xtored type.
        ~tagged_union() noexcept = default;

        tagged_union(const tagged_union&) = delete;
        tagged_union& operator=(const tagged_union&) = delete;

        //=== modifiers ===//
        /// \effects Creates a new object of given type by perfectly forwarding `args`.
        /// \throws Anything thrown by `T`s constructor,
        /// in which case the union will stay empty.
        /// \requires The union must currently be empty.
        /// and `T` must be a valid type and constructible from the arguments.
        template <typename T, typename... Args>
        void emplace(union_type<T>, Args&&... args)
        {
            constexpr auto index = type_id(union_type<T>{});
            static_assert(index != invalid_type, "T must not be stored in variant");
            static_assert(std::is_constructible<T, Args&&...>::value,
                          "T not constructible from arguments");

            ::new (get_memory()) T(std::forward<Args>(args)...);
            cur_type_ = index;
        }

        /// \effects Destroys the currently stored type by calling its destructor,
        /// and setting the union to the empty state.
        /// \requires The union must currently store an object of the given type.
        template <typename T>
        void destroy(union_type<T> type) noexcept
        {
            check(type);
            value(type).~T();
            cur_type_ = invalid_type;
        }

        //=== accessors ===//
        /// \returns The [*type_id]() of the type currently stored,
        /// or [*invalid_type_id]() if there is none.
        const type_id& type() const noexcept
        {
            return cur_type_;
        }

        /// \returns `true` if there is a type stored,
        /// `false` otherwise.
        bool has_value() const noexcept
        {
            return type() != invalid_type;
        }

        /// \returns A (`const`) lvalue/rvalue reference to the currently stored type.
        /// \requires The union must currently store an object of the given type.
        /// \group value
        template <typename T>
        T& value(union_type<T> type) TYPE_SAFE_LVALUE_REF noexcept
        {
            check(type);
            return *static_cast<T*>(get_memory());
        }

        /// \group value
        template <typename T>
        const T& value(union_type<T> type) const TYPE_SAFE_LVALUE_REF noexcept
        {
            check(type);
            return *static_cast<const T*>(get_memory());
        }

#if TYPE_SAFE_USE_REF_QUALIFIERS
        /// \group value
        template <typename T>
            T&& value(union_type<T> type) && noexcept
        {
            check(type);
            return std::move(*static_cast<T*>(get_memory()));
        }

        /// \group value
        template <typename T>
        const T&& value(union_type<T> type) const && noexcept
        {
            check(type);
            return std::move(*static_cast<const T*>(get_memory()));
        }
#endif

    private:
        void* get_memory() noexcept
        {
            return static_cast<void*>(&storage_);
        }

        const void* get_memory() const noexcept
        {
            return static_cast<const void*>(&storage_);
        }

        template <typename T>
        void check(union_type<T> type) const noexcept
        {
            DEBUG_ASSERT(cur_type_ == type, detail::assert_handler{},
                         "different type stored in union");
        }

        using storage_t = detail::aligned_union_t<Types...>;
        storage_t storage_;
        type_id   cur_type_;
    };

    /// \exclude
    template <typename... Types>
    constexpr typename tagged_union<Types...>::type_id tagged_union<Types...>::invalid_type;

    /// \exclude
    namespace detail
    {
        template <class Union>
        class destroy_union;

        template <typename... Types>
        class destroy_union<tagged_union<Types...>>
        {
        public:
            static void destroy(tagged_union<Types...>& u) noexcept
            {
                auto idx = static_cast<std::size_t>(u.type()) - 1u;
                DEBUG_ASSERT(idx < sizeof...(Types), detail::assert_handler{});
                callbacks[idx](u);
            }

        private:
            template <typename T>
            static void destroy_impl(tagged_union<Types...>& u) noexcept
            {
                u.destroy(union_type<T>{});
            }

            using callback_type                        = void (*)(tagged_union<Types...>&);
            static constexpr callback_type callbacks[] = {&destroy_impl<Types>...};
        };
        template <typename... Types>
        constexpr typename destroy_union<tagged_union<Types...>>::callback_type
            destroy_union<tagged_union<Types...>>::callbacks[];

        template <class Union>
        class copy_union;

        template <typename... Types>
        class copy_union<tagged_union<Types...>>
        {
        public:
            static void copy(tagged_union<Types...>& dest, const tagged_union<Types...>& org)
            {
                DEBUG_ASSERT(!dest.has_value(), detail::assert_handler{});

                auto idx = static_cast<std::size_t>(org.type()) - 1u;
                DEBUG_ASSERT(idx < sizeof...(Types), detail::assert_handler{});
                callbacks[idx](dest, org);
            }

        private:
            template <typename T>
            static void copy_impl(tagged_union<Types...>& dest, const tagged_union<Types...>& org)
            {
                dest.emplace(union_type<T>{}, org.value(union_type<T>{}));
            }

            using callback_type = void (*)(tagged_union<Types...>&, const tagged_union<Types...>&);
            static constexpr callback_type callbacks[] = {&copy_impl<Types>...};
        };
        template <typename... Types>
        constexpr typename copy_union<tagged_union<Types...>>::callback_type
            copy_union<tagged_union<Types...>>::callbacks[];

        template <class Union>
        class move_union;

        template <typename... Types>
        class move_union<tagged_union<Types...>>
        {
        public:
            static void move(tagged_union<Types...>& dest, tagged_union<Types...>&& org)
            {
                DEBUG_ASSERT(!dest.has_value(), detail::assert_handler{});

                auto idx = static_cast<std::size_t>(org.type()) - 1u;
                DEBUG_ASSERT(idx < sizeof...(Types), detail::assert_handler{});
                callbacks[idx](dest, std::move(org));
            }

        private:
            template <typename T>
            static void move_impl(tagged_union<Types...>& dest, tagged_union<Types...>&& org)
            {
                dest.emplace(union_type<T>{}, std::move(org).value(union_type<T>{}));
            }

            using callback_type = void (*)(tagged_union<Types...>&, tagged_union<Types...>&&);
            static constexpr callback_type callbacks[] = {&move_impl<Types>...};
        };
        template <typename... Types>
        constexpr typename move_union<tagged_union<Types...>>::callback_type
            move_union<tagged_union<Types...>>::callbacks[];
    } // namespace detail

    /// \effects Destroys the type currently stored in the [ts::tagged_union](),
    /// by calling `u.destroy(union_type<T>{})`.
    /// \requires The union must currently store a type,
    /// i.e. `has_value()` must return `true`.
    /// \module variant
    template <typename... Types>
    void destroy(tagged_union<Types...>& u) noexcept
    {
        detail::destroy_union<tagged_union<Types...>>::destroy(u);
    }

    /// \effects Copies the type currently stored in one [ts::tagged_union]() to another.
    /// This is equivalent to calling `dest.emplace(union_type<T>{}, org.value(union_type<T>{}))` (1)/
    /// `dest.emplace(union_type<T>{}, std::move(org).value(union_type<T>{}))` (2),
    /// where `T` is the type currently stored in the union.
    /// \throws Anything by the copy/move constructor in which case nothing has changed.
    /// \requires `dest` must not store a type, but `org` must have one,
    /// and all types must be copyable/moveable.
    /// \group union_copy_move
    /// \module variant
    /// \unique_name copy_union
    template <typename... Types>
    void copy(tagged_union<Types...>& dest, const tagged_union<Types...>& org)
    {
        detail::copy_union<tagged_union<Types...>>::copy(dest, org);
    }

    /// \group union_copy_move
    /// \module variant
    /// \unique_name move_union
    template <typename... Types>
    void move(tagged_union<Types...>& dest, tagged_union<Types...>&& org)
    {
        detail::move_union<tagged_union<Types...>>::move(dest, std::move(org));
    }
} // namespace type_safe

#endif // TYPE_SAFE_TAGGED_UNION_HPP_INCLUDED
<commit_msg>Optimize tagged_union for trivial types<commit_after>// Copyright (C) 2016 Jonathan Müller <jonathanmueller.dev@gmail.com>
// This file is subject to the license terms in the LICENSE file
// found in the top-level directory of this distribution.

#ifndef TYPE_SAFE_TAGGED_UNION_HPP_INCLUDED
#define TYPE_SAFE_TAGGED_UNION_HPP_INCLUDED

#include <new>

#include <type_safe/detail/aligned_union.hpp>
#include <type_safe/detail/all_of.hpp>
#include <type_safe/config.hpp>
#include <type_safe/strong_typedef.hpp>

namespace type_safe
{
    /// \exclude
    namespace detail
    {
        struct union_type_id : strong_typedef<union_type_id, std::size_t>,
                               strong_typedef_op::equality_comparison<union_type_id, bool>,
                               strong_typedef_op::relational_comparison<union_type_id, bool>
        {
            using strong_typedef<union_type_id, std::size_t>::strong_typedef;
        };

        //=== get_type_index ==//
        template <typename T, typename... Ts>
        struct get_type_index_impl;

        // type not found at all
        template <typename T>
        struct get_type_index_impl<T> : std::integral_constant<std::size_t, 0>
        {
        };

        // found type at the beginning
        template <typename T, typename... Tail>
        struct get_type_index_impl<T, T, Tail...> : std::integral_constant<std::size_t, 1>
        {
        };

        // type not in the beginning
        template <typename T, typename Head, typename... Tail>
        struct get_type_index_impl<T, Head, Tail...>
            : std::integral_constant<std::size_t, get_type_index_impl<T, Tail...>::value == 0u ?
                                                      0u :
                                                      1 + get_type_index_impl<T, Tail...>::value>
        {
        };

        template <typename T, typename... Types>
        using get_type_index = get_type_index_impl<T, typename std::decay<Types>::type...>;

        template <class Union>
        class destroy_union;
        template <class Union>
        class copy_union;
        template <class Union>
        class move_union;
    } // namespace detail

    /// Tag type so no explicit template instantiation of function parameters is required.
    /// \module variant
    template <typename T>
    struct union_type
    {
    };

    /// Very basic typelist.
    /// \module variant
    template <typename... Ts>
    struct union_types
    {
    };

    /// A tagged union.
    ///
    /// It is much like a plain old C `union`,
    /// but remembers which type it currently stores.
    /// It can either store one of the given types or no type at all.
    /// \notes Like the C `union` it does not automatically destroy the currently stored type,
    /// and copy operations are deleted.
    /// \module variant
    template <typename... Types>
    class tagged_union
    {
        using trivial = detail::all_of<std::is_trivially_copyable<Types>::value...>;

        template <class Union>
        friend class detail::destroy_union;
        template <class Union>
        friend class detail::copy_union;
        template <class Union>
        friend class detail::move_union;

    public:
        using types = union_types<typename std::decay<Types>::type...>;

        /// The id of a type.
        ///
        /// It is a [ts::strong_typedef]() for `std::size_t`
        /// and provides equality and relational comparison.
        class type_id : public strong_typedef<type_id, std::size_t>,
                        public strong_typedef_op::equality_comparison<type_id, bool>,
                        public strong_typedef_op::relational_comparison<type_id, bool>
        {
        public:
            /// \effects Initializes it to an invalid value.
            /// \notes The invalid value compares less than all valid values.
            constexpr type_id() noexcept : strong_typedef<type_id, std::size_t>(0u)
            {
            }

            /// \effects Initializes it to the value of the type `T`.
            /// If `T` is not one of the types of the union types,
            /// it will be the same as the default constructor.
            template <typename T>
            constexpr type_id(union_type<T>) noexcept
            : type_id(detail::get_type_index<T, Types...>::value)
            {
            }

            /// \returns `true` if the id is valid,
            /// `false` otherwise.
            explicit operator bool() const noexcept
            {
                return *this != type_id();
            }

        private:
            explicit constexpr type_id(std::size_t value)
            : strong_typedef<type_id, std::size_t>(value)
            {
            }
        };

        /// A global invalid type id object.
        static constexpr type_id invalid_type = type_id();

        //=== constructors/destructors/assignment ===//
        tagged_union() noexcept = default;

        /// \notes Does not destroy the currently xtored type.
        ~tagged_union() noexcept = default;

        tagged_union(const tagged_union&) = delete;
        tagged_union& operator=(const tagged_union&) = delete;

        //=== modifiers ===//
        /// \effects Creates a new object of given type by perfectly forwarding `args`.
        /// \throws Anything thrown by `T`s constructor,
        /// in which case the union will stay empty.
        /// \requires The union must currently be empty.
        /// and `T` must be a valid type and constructible from the arguments.
        template <typename T, typename... Args>
        void emplace(union_type<T>, Args&&... args)
        {
            constexpr auto index = type_id(union_type<T>{});
            static_assert(index != invalid_type, "T must not be stored in variant");
            static_assert(std::is_constructible<T, Args&&...>::value,
                          "T not constructible from arguments");

            ::new (get_memory()) T(std::forward<Args>(args)...);
            cur_type_ = index;
        }

        /// \effects Destroys the currently stored type by calling its destructor,
        /// and setting the union to the empty state.
        /// \requires The union must currently store an object of the given type.
        template <typename T>
        void destroy(union_type<T> type) noexcept
        {
            check(type);
            value(type).~T();
            cur_type_ = invalid_type;
        }

        //=== accessors ===//
        /// \returns The [*type_id]() of the type currently stored,
        /// or [*invalid_type_id]() if there is none.
        const type_id& type() const noexcept
        {
            return cur_type_;
        }

        /// \returns `true` if there is a type stored,
        /// `false` otherwise.
        bool has_value() const noexcept
        {
            return type() != invalid_type;
        }

        /// \returns A (`const`) lvalue/rvalue reference to the currently stored type.
        /// \requires The union must currently store an object of the given type.
        /// \group value
        template <typename T>
        T& value(union_type<T> type) TYPE_SAFE_LVALUE_REF noexcept
        {
            check(type);
            return *static_cast<T*>(get_memory());
        }

        /// \group value
        template <typename T>
        const T& value(union_type<T> type) const TYPE_SAFE_LVALUE_REF noexcept
        {
            check(type);
            return *static_cast<const T*>(get_memory());
        }

#if TYPE_SAFE_USE_REF_QUALIFIERS
        /// \group value
        template <typename T>
            T&& value(union_type<T> type) && noexcept
        {
            check(type);
            return std::move(*static_cast<T*>(get_memory()));
        }

        /// \group value
        template <typename T>
        const T&& value(union_type<T> type) const && noexcept
        {
            check(type);
            return std::move(*static_cast<const T*>(get_memory()));
        }
#endif

    private:
        void* get_memory() noexcept
        {
            return static_cast<void*>(&storage_);
        }

        const void* get_memory() const noexcept
        {
            return static_cast<const void*>(&storage_);
        }

        template <typename T>
        void check(union_type<T> type) const noexcept
        {
            DEBUG_ASSERT(cur_type_ == type, detail::assert_handler{},
                         "different type stored in union");
        }

        using storage_t = detail::aligned_union_t<Types...>;
        storage_t storage_;
        type_id   cur_type_;
    };

    /// \exclude
    template <typename... Types>
    constexpr typename tagged_union<Types...>::type_id tagged_union<Types...>::invalid_type;

    /// \exclude
    namespace detail
    {
        template <class Union>
        class destroy_union;

        template <typename... Types>
        class destroy_union<tagged_union<Types...>>
        {
        public:
            static void destroy(tagged_union<Types...>& u) noexcept
            {
                if (!tagged_union<Types...>::trivial::value)
                {
                    auto idx = static_cast<std::size_t>(u.type()) - 1u;
                    DEBUG_ASSERT(idx < sizeof...(Types), detail::assert_handler{});
                    callbacks[idx](u);
                }
            }

        private:
            template <typename T>
            static void destroy_impl(tagged_union<Types...>& u) noexcept
            {
                u.destroy(union_type<T>{});
            }

            using callback_type                        = void (*)(tagged_union<Types...>&);
            static constexpr callback_type callbacks[] = {&destroy_impl<Types>...};
        };
        template <typename... Types>
        constexpr typename destroy_union<tagged_union<Types...>>::callback_type
            destroy_union<tagged_union<Types...>>::callbacks[];

        template <class Union>
        class copy_union;

        template <typename... Types>
        class copy_union<tagged_union<Types...>>
        {
        public:
            static void copy(tagged_union<Types...>& dest, const tagged_union<Types...>& org)
            {
                DEBUG_ASSERT(!dest.has_value(), detail::assert_handler{});

                if (tagged_union<Types...>::trivial::value)
                    dest.storage_ = org.storage_;
                else
                {
                    auto idx = static_cast<std::size_t>(org.type()) - 1u;
                    DEBUG_ASSERT(idx < sizeof...(Types), detail::assert_handler{});
                    callbacks[idx](dest, org);
                }
            }

        private:
            template <typename T>
            static void copy_impl(tagged_union<Types...>& dest, const tagged_union<Types...>& org)
            {
                dest.emplace(union_type<T>{}, org.value(union_type<T>{}));
            }

            using callback_type = void (*)(tagged_union<Types...>&, const tagged_union<Types...>&);
            static constexpr callback_type callbacks[] = {&copy_impl<Types>...};
        };
        template <typename... Types>
        constexpr typename copy_union<tagged_union<Types...>>::callback_type
            copy_union<tagged_union<Types...>>::callbacks[];

        template <class Union>
        class move_union;

        template <typename... Types>
        class move_union<tagged_union<Types...>>
        {
        public:
            static void move(tagged_union<Types...>& dest, tagged_union<Types...>&& org)
            {
                DEBUG_ASSERT(!dest.has_value(), detail::assert_handler{});

                if (tagged_union<Types...>::trivial::value)
                    dest.storage_ = org.storage_;
                else
                {
                    auto idx = static_cast<std::size_t>(org.type()) - 1u;
                    DEBUG_ASSERT(idx < sizeof...(Types), detail::assert_handler{});
                    callbacks[idx](dest, std::move(org));
                }
            }

        private:
            template <typename T>
            static void move_impl(tagged_union<Types...>& dest, tagged_union<Types...>&& org)
            {
                dest.emplace(union_type<T>{}, std::move(org).value(union_type<T>{}));
            }

            using callback_type = void (*)(tagged_union<Types...>&, tagged_union<Types...>&&);
            static constexpr callback_type callbacks[] = {&move_impl<Types>...};
        };
        template <typename... Types>
        constexpr typename move_union<tagged_union<Types...>>::callback_type
            move_union<tagged_union<Types...>>::callbacks[];
    } // namespace detail

    /// \effects Destroys the type currently stored in the [ts::tagged_union](),
    /// by calling `u.destroy(union_type<T>{})`.
    /// \requires The union must currently store a type,
    /// i.e. `has_value()` must return `true`.
    /// \module variant
    template <typename... Types>
    void destroy(tagged_union<Types...>& u) noexcept
    {
        detail::destroy_union<tagged_union<Types...>>::destroy(u);
    }

    /// \effects Copies the type currently stored in one [ts::tagged_union]() to another.
    /// This is equivalent to calling `dest.emplace(union_type<T>{}, org.value(union_type<T>{}))` (1)/
    /// `dest.emplace(union_type<T>{}, std::move(org).value(union_type<T>{}))` (2),
    /// where `T` is the type currently stored in the union.
    /// \throws Anything by the copy/move constructor in which case nothing has changed.
    /// \requires `dest` must not store a type, but `org` must have one,
    /// and all types must be copyable/moveable.
    /// \group union_copy_move
    /// \module variant
    /// \unique_name copy_union
    template <typename... Types>
    void copy(tagged_union<Types...>& dest, const tagged_union<Types...>& org)
    {
        detail::copy_union<tagged_union<Types...>>::copy(dest, org);
    }

    /// \group union_copy_move
    /// \module variant
    /// \unique_name move_union
    template <typename... Types>
    void move(tagged_union<Types...>& dest, tagged_union<Types...>&& org)
    {
        detail::move_union<tagged_union<Types...>>::move(dest, std::move(org));
    }
} // namespace type_safe

#endif // TYPE_SAFE_TAGGED_UNION_HPP_INCLUDED
<|endoftext|>


<commit_before>class Compiler
{
public:
    void compileFunction(Symbol functionDefinitionStatement)
    {
        int stackAdjust = offsetOf(functionDefinitionStatement);
        if (stackAdjust != 0) {
            add(Symbol(atomStackPointer));
            add(Symbol(atomIntegerConstant, -stackAdjust));
            add(Symbol(atomAdd));
            add(Symbol(atomSetStackPointer));
        }
        _stackOffset = 0;
        compileStatementSequence(functionDefinitionStatement[4].array());
        if (stackAdjust != 0) {
            add(Symbol(atomStackPointer));
            add(Symbol(atomIntegerConstant, stackAdjust));
            add(Symbol(atomAdd));
            add(Symbol(atomSetStackPointer));
        }
        add(Symbol(atomReturn));
    }
    SymbolList compiledProgram() const { return _compiledProgram; }
private:
    void compileStatementSequence(SymbolArray program)
    {
        for (int i = 0; i < program.count(); ++i)
            compileStatement(program[i]);
    }
    void finishBasicBlock(int nextLabel)
    {
        Symbol block(atomBasicBlock, SymbolArray(_basicBlock), _label, nextLabel);
        block.setLabel(_label);
        _compiledProgram.add(block);
        _basicBlock = SymbolList();
        _label = nextLabel;
    }
    void compileStatement(Symbol statement)
    {
        switch (statement.atom()) {
            case atomExpressionStatement:
                compileExpression(statement[1].symbol());
                break;
            case atomFunctionDefinitionStatement:
                {
                    Compiler compiler;
                    compiler.compileFunction(statement);
                    _compiledProgram.add(compiler.compiledProgram());
                }
                break;
            case atomFromStatement:
                // TODO
                break;
            case atomVariableDefinitionStatement:
                addAddressOf(statement[2].symbol());
                compileExpression(statement[3].symbol());
                add(Symbol(atomStore));
                break;
            case atomAssignmentStatement:
                addAddressOf(statement[1].symbol());
                compileExpression(statement[2].symbol());
                add(Symbol(atomStore));
                break;
            case atomAddAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomAdd));
                add(Symbol(atomStore));
                break;
            case atomSubtractAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomSubtract));
                add(Symbol(atomStore));
                break;
            case atomMultiplyAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomMultiply));
                add(Symbol(atomStore));
                break;
            case atomDivideAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomDivide));
                add(Symbol(atomStore));
                break;
            case atomModuloAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomModulo));
                add(Symbol(atomStore));
                break;
            case atomShiftLeftAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomLeftShift));
                add(Symbol(atomStore));
                break;
            case atomShiftRightAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomRightShift));
                add(Symbol(atomStore));
                break;
            case atomAndAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomBitwiseAnd));
                add(Symbol(atomStore));
                break;
            case atomOrAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomBitwiseOr));
                add(Symbol(atomStore));
                break;
            case atomXorAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomBitwiseXor));
                add(Symbol(atomStore));
                break;
            case atomPowerAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomPower));
                add(Symbol(atomStore));
                break;
            case atomCompoundStatement:
                compileStatementSequence(statement[1].array());
                break;
            case atomTypeAliasStatement:
                break;
            case atomNothingStatement:
                break;
            case atomIncrementStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                add(Symbol(atomIntegerConstant, 1));
                add(Symbol(atomAdd));
                add(Symbol(atomStore));
                break;
            case atomDecrementStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                add(Symbol(atomIntegerConstant, 1));
                add(Symbol(atomSubtract));
                add(Symbol(atomStore));
                break;
            case atomIfStatement:
                {
                    int falseClause = Symbol::newLabel();
                    int done = Symbol::newLabel();
                    compileExpression(statement[1].symbol());
                    add(Symbol(atomNot));
                    addJumpIfTrue(falseClause);
                    compileStatement(statement[2].symbol());
                    addGoto(done);
                    addLabel(falseClause);
                    compileStatement(statement[3].symbol());
                    addLabel(done);
                }
                break;
            case atomSwitchStatement:
            case atomReturnStatement:
            case atomIncludeStatement:
            case atomBreakStatement:
            case atomContinueStatement:
                // TODO
                break;
            case atomForeverStatement:
                {
                    int done = Symbol::newLabel();
                    int start = getLabel();
                    _breakContinueStack.push(BreakContinueStackEntry(done, start));
                    compileStatement(statement[1].symbol());
                    addGoto(start);
                    addLabel(done);
                    _breakContinueStack.pop();
                }
                break;
            case atomWhileStatement:
                {
                    int done = Symbol::newLabel();
                    int final = Symbol::newLabel();
                    int start = getLabel();
                    _breakContinueStack.push(BreakContinueStackEntry(final, start));
                    compileStatement(statement[1].symbol());
                    compileExpression(statement[2].symbol());
                    add(Symbol(atomNot));
                    addJumpIfTrue(done);
                    compileStatement(statement[3].symbol());
                    addGoto(start);
                    _breakContinueStack.pop();
                    addLabel(done);
                    compileStatement(statement[4].symbol());
                }
                break;
            case atomUntilStatement:
                {
                    int done = Symbol::newLabel();
                    int final = Symbol::newLabel();
                    int start = getLabel();
                    _breakContinueStack.push(BreakContinueStackEntry(final, start));
                    compileStatement(statement[1].symbol());
                    compileExpression(statement[2].symbol());
                    addJumpIfTrue(done);
                    compileStatement(statement[3].symbol());
                    addGoto(start);
                    _breakContinueStack.pop();
                    addLabel(done);
                    compileStatement(statement[4].symbol());
                }
                break;
            case atomForStatement:
                {
                    compileStatement(statement[1].symbol());
                    int done = Symbol::newLabel();
                    int final = Symbol::newLabel();
                    int start = getLabel();
                    _breakContinueStack.push(BreakContinueStackEntry(final, start));
                    compileExpression(statement[2].symbol());
                    add(Symbol(atomNot));
                    addJumpIfTrue(done);
                    compileStatement(statement[3].symbol());
                    compileStatement(statement[4].symbol());
                    addGoto(start);
                    _breakContinueStack.pop();
                    addLabel(done);
                    compileStatement(statement[5].symbol());
                }
                break;
        }
    }
    // Add instructions to push the value of expression onto the stack.
    void compileExpression(Symbol expression)
    {
        switch (expression.atom()) {
            case atomLogicalOr:
                {
                    int pushRight = Symbol::newLabel();
                    int pushTrue = Symbol::newLabel();
                    int done = Symbol::newLabel();
                    compileExpression(expression[1].symbol());
                    addJumpIfTrue(pushTrue);
                    addLabel(pushRight);
                    compileExpression(expression[2].symbol());
                    addGoto(done);
                    addLabel(pushTrue);
                    add(Symbol(atomTrue));
                    addLabel(done);
                }
                break;
            case atomLogicalAnd:
                {
                    int pushRight = Symbol::newLabel();
                    int pushFalse = Symbol::newLabel();
                    int done = Symbol::newLabel();
                    compileExpression(expression[1].symbol());
                    add(Symbol(atomNot));
                    addJumpIfTrue(pushFalse);
                    compileExpression(expression[2].symbol());
                    addGoto(done);
                    addLabel(pushFalse);
                    add(Symbol(atomFalse));
                    addLabel(done);
                }
                break;
            case atomDot:
                // TODO
                break;
            case atomDereference:
                addAddressOf(expression[1].symbol());
                add(Symbol(atomDereference));
                break;
            case atomAddressOf:
                addAddressOf(expression[1].symbol());
                break;
            case atomFunctionCall:
                {
                    //_stackOffsetStack.push(_stackOffset);
                    SymbolArray arguments = expression[2].array();
                    for (int i = arguments.count() - 1; i >= 0; --i)
                        compileExpression(arguments[i]);
                    compileExpression(expression[1].symbol());
                    add(Symbol(atomCall));
                }
                break;
            case atomIntegerConstant:
            case atomStringConstant:
                add(expression);
                break;
            case atomIdentifier:
                {
                    Symbol definition = Symbol::labelled(labelOf(expression));
                    addAddressOf(definition);
                    add(Symbol(atomDereference));
                }
                break;
            case atomTrue:
            case atomFalse:
            case atomNull:
                add(expression);
                break;
        }
    }
    void addAddressOf(Symbol symbol)
    {
        int offset = offsetOf(symbol);
        add(Symbol(atomStackPointer));  // TODO: there might be temporaries on the stack - keep track and correct for this
        add(Symbol(atomIntegerConstant, offset + _stackOffset - 4));
        add(Symbol(atomAdd));
    }
    void add(Symbol symbol)
    {
        _basicBlock.add(symbol);
        _blockEnds = false;
        _atBlockStart = false;
        int adjust = 0;
        switch (symbol.atom()) {
            case atomBitwiseOr:
            case atomBitwiseXor:
            case atomBitwiseAnd:
            case atomEqualTo:
            case atomNotEqualTo:
            case atomLessThanOrEqualTo:
            case atomGreaterThanOrEqualTo:
            case atomLessThan:
            case atomGreaterThan:
            case atomLeftShift:
            case atomRightShift:
            case atomAdd:
            case atomSubtract:
            case atomMultiply:
            case atomDivide:
            case atomModulo:
            case atomPower:
            case atomGoto:
            case atomDrop:
            case atomStringConcatenate:
            case atomStringEqualTo:
            case atomStringNotEqualTo:
            case atomStringLessThanOrEqualTo:
            case atomStringGreaterThanOrEqualTo:
            case atomStringLessThan:
            case atomStringGreaterThan:
            case atomStringIntegerMultiply:
            case atomPrintFunction:
                adjust = -4;
                break;

            case atomNot:
            case atomNegative:
            case atomDereference:
                adjust = 0;
                break;

            case atomStringConstant:
            case atomIntegerConstant:
            case atomTrue:
            case atomFalse:
            case atomNull:
            case atomStackPointer:
            case atomDuplicate:
                adjust = 4;
                break;

            case atomCall:
                //adjust = _stackOffsetStack.pop() - _stackOffset;
                adjust = -4;
                break;

            case atomJumpIfTrue:
            case atomStore:
                adjust = -8;
                break;
        }
        _stackOffset += adjust;
    }
    void addGoto(int destination)
    {
        add(Symbol(atomIntegerConstant, destination));
        add(Symbol(atomGoto));
        checkBlockStackOffset(destination);
        _blockEnds = true;
    }
    void addJumpIfTrue(int destination)
    {
        add(Symbol(atomIntegerConstant, destination));
        add(Symbol(atomJumpIfTrue));
        checkBlockStackOffset(destination);
    }
    void addLabel(int label)
    {
        int follows = label;
        if (_blockEnds)
            follows = -1;
        else
            checkBlockStackOffset(follows);
        Symbol block(atomBasicBlock, SymbolArray(_basicBlock), _label, follows);
        checkBlockStackOffset(_label);
        block.setLabel(_label);
        _compiledProgram.add(block);
        _basicBlock = SymbolList();
        _label = label;
        _atBlockStart = true;
    }
    int getLabel()
    {
        if (!_atBlockStart)
            addLabel(Symbol::newLabel());
        return _label;
    }
    void checkBlockStackOffset(int label)
    {
        if (_blockStackOffsets.hasKey(label)) {
            int stackOffset = _blockStackOffsets[label];
            if (stackOffset != _stackOffset) {
                static String error("Stack offset mismatch. Expected ");
                static String error2(" found ");
                throw Exception(error + String::decimal(stackOffset) + error2 + String::decimal(_stackOffset));
            }
        }
        else
            _blockStackOffsets.add(label, _stackOffset);
    }

    SymbolList _compiledProgram;
    SymbolList _basicBlock;
    int _label;
    bool _blockEnds;
    bool _atBlockStart;
    int _stackOffset;
    Stack<int> _stackOffsetStack;
    HashTable<int, int> _blockStackOffsets;

    class BreakContinueStackEntry
    {
    public:
        BreakContinueStackEntry(int breakLabel, int continueLabel)
          : _breakLabel(breakLabel), _continueLabel(continueLabel) { }
        int _breakLabel;
        int _continueLabel;
    };
    Stack<BreakContinueStackEntry> _breakContinueStack;
};
<commit_msg>Post-call stack fixup<commit_after>class Compiler
{
public:
    void compileFunction(Symbol functionDefinitionStatement)
    {
        int stackAdjust = offsetOf(functionDefinitionStatement);
        if (stackAdjust != 0) {
            add(Symbol(atomStackPointer));
            add(Symbol(atomIntegerConstant, -stackAdjust));
            add(Symbol(atomAdd));
            add(Symbol(atomSetStackPointer));
        }
        _stackOffset = 0;
        compileStatementSequence(functionDefinitionStatement[4].array());
        if (stackAdjust != 0) {
            add(Symbol(atomStackPointer));
            add(Symbol(atomIntegerConstant, stackAdjust));
            add(Symbol(atomAdd));
            add(Symbol(atomSetStackPointer));
        }
        add(Symbol(atomReturn));
    }
    SymbolList compiledProgram() const { return _compiledProgram; }
private:
    void compileStatementSequence(SymbolArray program)
    {
        for (int i = 0; i < program.count(); ++i)
            compileStatement(program[i]);
    }
    void finishBasicBlock(int nextLabel)
    {
        Symbol block(atomBasicBlock, SymbolArray(_basicBlock), _label, nextLabel);
        block.setLabel(_label);
        _compiledProgram.add(block);
        _basicBlock = SymbolList();
        _label = nextLabel;
    }
    void compileStatement(Symbol statement)
    {
        switch (statement.atom()) {
            case atomExpressionStatement:
                {
                    Symbol expression = statement[1].symbol();
                    compileExpression(expression);
                    // Pop (unused) return value from stack
                    Symbol type = typeOf(expression).atom();
                    int adjust = (sizeOf(type) + 3) & -4;
                    while (adjust != 0) {
                        add(Symbol(atomDrop));
                        adjust -= 4;
                    }
                }
                break;
            case atomFunctionDefinitionStatement:
                {
                    Compiler compiler;
                    compiler.compileFunction(statement);
                    _compiledProgram.add(compiler.compiledProgram());
                }
                break;
            case atomFromStatement:
                // TODO
                break;
            case atomVariableDefinitionStatement:
                addAddressOf(statement[2].symbol());
                compileExpression(statement[3].symbol());
                add(Symbol(atomStore));
                break;
            case atomAssignmentStatement:
                addAddressOf(statement[1].symbol());
                compileExpression(statement[2].symbol());
                add(Symbol(atomStore));
                break;
            case atomAddAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomAdd));
                add(Symbol(atomStore));
                break;
            case atomSubtractAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomSubtract));
                add(Symbol(atomStore));
                break;
            case atomMultiplyAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomMultiply));
                add(Symbol(atomStore));
                break;
            case atomDivideAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomDivide));
                add(Symbol(atomStore));
                break;
            case atomModuloAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomModulo));
                add(Symbol(atomStore));
                break;
            case atomShiftLeftAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomLeftShift));
                add(Symbol(atomStore));
                break;
            case atomShiftRightAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomRightShift));
                add(Symbol(atomStore));
                break;
            case atomAndAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomBitwiseAnd));
                add(Symbol(atomStore));
                break;
            case atomOrAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomBitwiseOr));
                add(Symbol(atomStore));
                break;
            case atomXorAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomBitwiseXor));
                add(Symbol(atomStore));
                break;
            case atomPowerAssignmentStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                compileExpression(statement[2].symbol());
                add(Symbol(atomPower));
                add(Symbol(atomStore));
                break;
            case atomCompoundStatement:
                compileStatementSequence(statement[1].array());
                break;
            case atomTypeAliasStatement:
                break;
            case atomNothingStatement:
                break;
            case atomIncrementStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                add(Symbol(atomIntegerConstant, 1));
                add(Symbol(atomAdd));
                add(Symbol(atomStore));
                break;
            case atomDecrementStatement:
                addAddressOf(statement[1].symbol());
                add(Symbol(atomDuplicate));
                add(Symbol(atomDereference));
                add(Symbol(atomIntegerConstant, 1));
                add(Symbol(atomSubtract));
                add(Symbol(atomStore));
                break;
            case atomIfStatement:
                {
                    int falseClause = Symbol::newLabel();
                    int done = Symbol::newLabel();
                    compileExpression(statement[1].symbol());
                    add(Symbol(atomNot));
                    addJumpIfTrue(falseClause);
                    compileStatement(statement[2].symbol());
                    addGoto(done);
                    addLabel(falseClause);
                    compileStatement(statement[3].symbol());
                    addLabel(done);
                }
                break;
            case atomSwitchStatement:
            case atomReturnStatement:
            case atomIncludeStatement:
            case atomBreakStatement:
            case atomContinueStatement:
                // TODO
                break;
            case atomForeverStatement:
                {
                    int done = Symbol::newLabel();
                    int start = getLabel();
                    _breakContinueStack.push(BreakContinueStackEntry(done, start));
                    compileStatement(statement[1].symbol());
                    addGoto(start);
                    addLabel(done);
                    _breakContinueStack.pop();
                }
                break;
            case atomWhileStatement:
                {
                    int done = Symbol::newLabel();
                    int final = Symbol::newLabel();
                    int start = getLabel();
                    _breakContinueStack.push(BreakContinueStackEntry(final, start));
                    compileStatement(statement[1].symbol());
                    compileExpression(statement[2].symbol());
                    add(Symbol(atomNot));
                    addJumpIfTrue(done);
                    compileStatement(statement[3].symbol());
                    addGoto(start);
                    _breakContinueStack.pop();
                    addLabel(done);
                    compileStatement(statement[4].symbol());
                }
                break;
            case atomUntilStatement:
                {
                    int done = Symbol::newLabel();
                    int final = Symbol::newLabel();
                    int start = getLabel();
                    _breakContinueStack.push(BreakContinueStackEntry(final, start));
                    compileStatement(statement[1].symbol());
                    compileExpression(statement[2].symbol());
                    addJumpIfTrue(done);
                    compileStatement(statement[3].symbol());
                    addGoto(start);
                    _breakContinueStack.pop();
                    addLabel(done);
                    compileStatement(statement[4].symbol());
                }
                break;
            case atomForStatement:
                {
                    compileStatement(statement[1].symbol());
                    int done = Symbol::newLabel();
                    int final = Symbol::newLabel();
                    int start = getLabel();
                    _breakContinueStack.push(BreakContinueStackEntry(final, start));
                    compileExpression(statement[2].symbol());
                    add(Symbol(atomNot));
                    addJumpIfTrue(done);
                    compileStatement(statement[3].symbol());
                    compileStatement(statement[4].symbol());
                    addGoto(start);
                    _breakContinueStack.pop();
                    addLabel(done);
                    compileStatement(statement[5].symbol());
                }
                break;
        }
    }
    // Add instructions to push the value of expression onto the stack.
    void compileExpression(Symbol expression)
    {
        switch (expression.atom()) {
            case atomLogicalOr:
                {
                    int pushRight = Symbol::newLabel();
                    int pushTrue = Symbol::newLabel();
                    int done = Symbol::newLabel();
                    compileExpression(expression[1].symbol());
                    addJumpIfTrue(pushTrue);
                    addLabel(pushRight);
                    compileExpression(expression[2].symbol());
                    addGoto(done);
                    addLabel(pushTrue);
                    add(Symbol(atomTrue));
                    addLabel(done);
                }
                break;
            case atomLogicalAnd:
                {
                    int pushRight = Symbol::newLabel();
                    int pushFalse = Symbol::newLabel();
                    int done = Symbol::newLabel();
                    compileExpression(expression[1].symbol());
                    add(Symbol(atomNot));
                    addJumpIfTrue(pushFalse);
                    compileExpression(expression[2].symbol());
                    addGoto(done);
                    addLabel(pushFalse);
                    add(Symbol(atomFalse));
                    addLabel(done);
                }
                break;
            case atomDot:
                // TODO
                break;
            case atomDereference:
                addAddressOf(expression[1].symbol());
                add(Symbol(atomDereference));
                break;
            case atomAddressOf:
                addAddressOf(expression[1].symbol());
                break;
            case atomFunctionCall:
                {
                    //_stackOffsetStack.push(_stackOffset);
                    SymbolArray arguments = expression[2].array();
                    for (int i = arguments.count() - 1; i >= 0; --i)
                        compileExpression(arguments[i]);
                    Symbol function = expression[1].symbol();
                    compileExpression(function);
                    add(Symbol(atomCall));
                    int adjust = 0;
                    for (int i = 0 ; i < arguments.count(); ++i) {
                        Symbol type = typeOf(arguments[i]);
                        adjust += (sizeOf(type) + 3) & -4;
                    }
                    if (adjust != 0) {
                        // Pop arguments from stack.
                        // TODO: need to move the return value first.
                        add(Symbol(atomStackPointer));
                        add(Symbol(atomIntegerConstant, adjust));

                    }

                }
                break;
            case atomIntegerConstant:
            case atomStringConstant:
                add(expression);
                break;
            case atomIdentifier:
                {
                    Symbol definition = Symbol::labelled(labelOf(expression));
                    addAddressOf(definition);
                    add(Symbol(atomDereference));
                }
                break;
            case atomTrue:
            case atomFalse:
            case atomNull:
                add(expression);
                break;
        }
    }
    void addAddressOf(Symbol symbol)
    {
        int offset = offsetOf(symbol);
        add(Symbol(atomStackPointer));  // TODO: there might be temporaries on the stack - keep track and correct for this
        add(Symbol(atomIntegerConstant, offset + _stackOffset - 4));
        add(Symbol(atomAdd));
    }
    void add(Symbol symbol)
    {
        _basicBlock.add(symbol);
        _blockEnds = false;
        _atBlockStart = false;
        int adjust = 0;
        switch (symbol.atom()) {
            case atomBitwiseOr:
            case atomBitwiseXor:
            case atomBitwiseAnd:
            case atomEqualTo:
            case atomNotEqualTo:
            case atomLessThanOrEqualTo:
            case atomGreaterThanOrEqualTo:
            case atomLessThan:
            case atomGreaterThan:
            case atomLeftShift:
            case atomRightShift:
            case atomAdd:
            case atomSubtract:
            case atomMultiply:
            case atomDivide:
            case atomModulo:
            case atomPower:
            case atomGoto:
            case atomDrop:
            case atomStringConcatenate:
            case atomStringEqualTo:
            case atomStringNotEqualTo:
            case atomStringLessThanOrEqualTo:
            case atomStringGreaterThanOrEqualTo:
            case atomStringLessThan:
            case atomStringGreaterThan:
            case atomStringIntegerMultiply:
            case atomPrintFunction:
                adjust = -4;
                break;

            case atomNot:
            case atomNegative:
            case atomDereference:
                adjust = 0;
                break;

            case atomStringConstant:
            case atomIntegerConstant:
            case atomTrue:
            case atomFalse:
            case atomNull:
            case atomStackPointer:
            case atomDuplicate:
                adjust = 4;
                break;

            case atomCall:
                //adjust = _stackOffsetStack.pop() - _stackOffset;
                adjust = -4;
                break;

            case atomJumpIfTrue:
            case atomStore:
                adjust = -8;
                break;
        }
        _stackOffset += adjust;
    }
    void addGoto(int destination)
    {
        add(Symbol(atomIntegerConstant, destination));
        add(Symbol(atomGoto));
        checkBlockStackOffset(destination);
        _blockEnds = true;
    }
    void addJumpIfTrue(int destination)
    {
        add(Symbol(atomIntegerConstant, destination));
        add(Symbol(atomJumpIfTrue));
        checkBlockStackOffset(destination);
    }
    void addLabel(int label)
    {
        int follows = label;
        if (_blockEnds)
            follows = -1;
        else
            checkBlockStackOffset(follows);
        Symbol block(atomBasicBlock, SymbolArray(_basicBlock), _label, follows);
        checkBlockStackOffset(_label);
        block.setLabel(_label);
        _compiledProgram.add(block);
        _basicBlock = SymbolList();
        _label = label;
        _atBlockStart = true;
    }
    int getLabel()
    {
        if (!_atBlockStart)
            addLabel(Symbol::newLabel());
        return _label;
    }
    void checkBlockStackOffset(int label)
    {
        if (_blockStackOffsets.hasKey(label)) {
            int stackOffset = _blockStackOffsets[label];
            if (stackOffset != _stackOffset) {
                static String error("Stack offset mismatch. Expected ");
                static String error2(" found ");
                throw Exception(error + String::decimal(stackOffset) + error2 + String::decimal(_stackOffset));
            }
        }
        else
            _blockStackOffsets.add(label, _stackOffset);
    }

    SymbolList _compiledProgram;
    SymbolList _basicBlock;
    int _label;
    bool _blockEnds;
    bool _atBlockStart;
    int _stackOffset;
    Stack<int> _stackOffsetStack;
    HashTable<int, int> _blockStackOffsets;

    class BreakContinueStackEntry
    {
    public:
        BreakContinueStackEntry(int breakLabel, int continueLabel)
          : _breakLabel(breakLabel), _continueLabel(continueLabel) { }
        int _breakLabel;
        int _continueLabel;
    };
    Stack<BreakContinueStackEntry> _breakContinueStack;
};
<|endoftext|>


<commit_before>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_HARFBUZZ_SHAPER_HPP
#define MAPNIK_HARFBUZZ_SHAPER_HPP

// mapnik
#include <mapnik/text/text_properties.hpp>
#include <mapnik/text/text_line.hpp>
#include <mapnik/text/face.hpp>
#include <mapnik/text/font_feature_settings.hpp>
#include <mapnik/text/itemizer.hpp>
#include <mapnik/safe_cast.hpp>
#include <mapnik/font_engine_freetype.hpp>

// stl
#include <list>
#include <type_traits>

// harfbuzz
#include <harfbuzz/hb.h>
#include <harfbuzz/hb-ft.h>

// icu
#include <unicode/uscript.h>

namespace mapnik
{

static inline hb_script_t _icu_script_to_script(UScriptCode script)
{
    if (script == USCRIPT_INVALID_CODE) return HB_SCRIPT_INVALID;
        return hb_script_from_string(uscript_getShortName(script), -1);
}

static inline const uint16_t * uchar_to_utf16(const UChar* src)
{
   static_assert(sizeof(UChar) == sizeof(uint16_t),"UChar is eq size to uint16_t");
#if defined(_MSC_VER)
   return reinterpret_cast<const uint16_t *>(src);
#else
   return src;
#endif
}

struct harfbuzz_shaper
{
static void shape_text(text_line & line,
                       text_itemizer & itemizer,
                       std::map<unsigned,double> & width_map,
                       face_manager_freetype & font_manager,
                       double scale_factor)
{
    unsigned start = line.first_char();
    unsigned end = line.last_char();
    std::size_t length = end - start;
    if (!length) return;

    std::list<text_item> const& list = itemizer.itemize(start, end);

    line.reserve(length);

    auto hb_buffer_deleter = [](hb_buffer_t * buffer) { hb_buffer_destroy(buffer);};
    const std::unique_ptr<hb_buffer_t, decltype(hb_buffer_deleter)> buffer(hb_buffer_create(),hb_buffer_deleter);
    hb_buffer_pre_allocate(buffer.get(), safe_cast<int>(length));
    mapnik::value_unicode_string const& text = itemizer.text();

    for (auto const& text_item : list)
    {
        face_set_ptr face_set = font_manager.get_face_set(text_item.format_->face_name, text_item.format_->fontset);
        double size = text_item.format_->text_size * scale_factor;
        face_set->set_unscaled_character_sizes();
        std::size_t num_faces = face_set->size();
        std::size_t pos = 0;
        font_feature_settings const& ff_settings = text_item.format_->ff_settings;
        int ff_count = safe_cast<int>(ff_settings.count());

        struct glyph_face_info
        {
            face_ptr face;
            hb_glyph_info_t glyph;
            hb_glyph_position_t position;
        };
        std::vector<glyph_face_info> glyphinfos;
        unsigned valid_glyphs = 0;

        for (auto const& face : *face_set)
        {
            ++pos;
            hb_buffer_clear_contents(buffer.get());
            hb_buffer_add_utf16(buffer.get(), uchar_to_utf16(text.getBuffer()), text.length(), text_item.start, static_cast<int>(text_item.end - text_item.start));
            hb_buffer_set_direction(buffer.get(), (text_item.dir == UBIDI_RTL)?HB_DIRECTION_RTL:HB_DIRECTION_LTR);
            hb_buffer_set_script(buffer.get(), _icu_script_to_script(text_item.script));
            hb_font_t *font(hb_ft_font_create(face->get_face(), nullptr));
            // https://github.com/mapnik/test-data-visual/pull/25
            #if HB_VERSION_MAJOR > 0
             #if HB_VERSION_ATLEAST(1, 0 , 5)
            hb_ft_font_set_load_flags(font,FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING);
             #endif
            #endif
            hb_shape(font, buffer.get(), ff_settings.get_features(), ff_count);
            hb_font_destroy(font);

            unsigned num_glyphs = hb_buffer_get_length(buffer.get());

            if (num_glyphs > glyphinfos.size())
            {
                glyphinfos.resize(num_glyphs);
            }

            hb_glyph_info_t *glyphs = hb_buffer_get_glyph_infos(buffer.get(), nullptr);
            hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer.get(), nullptr);

            // Check if all glyphs are valid.
            for (unsigned i=0; i<num_glyphs; ++i)
            {
                if (glyphs[i].codepoint && !glyphinfos[i].glyph.codepoint)
                {
                    glyphinfos[i] = { face, glyphs[i], positions[i] };
                    ++valid_glyphs;
                }
            }
            if (valid_glyphs < num_glyphs && (pos < num_faces))
            {
                //Try next font in fontset
                continue;
            }

            double max_glyph_height = 0;
            for (unsigned i=0; i<num_glyphs; ++i)
            {
                auto& gpos = positions[i];
                auto& glyph = glyphs[i];
                face_ptr theface = face;
                if (glyphinfos[i].glyph.codepoint)
                {
                    gpos = glyphinfos[i].position;
                    glyph = glyphinfos[i].glyph;
                    theface = glyphinfos[i].face;
                }
                unsigned char_index = glyph.cluster;
                glyph_info g(glyph.codepoint,char_index,text_item.format_);
                if (theface->glyph_dimensions(g))
                {
                    g.face = theface;
                    g.scale_multiplier = size / theface->get_face()->units_per_EM;
                    //Overwrite default advance with better value provided by HarfBuzz
                    g.unscaled_advance = gpos.x_advance;
                    g.offset.set(gpos.x_offset * g.scale_multiplier, gpos.y_offset * g.scale_multiplier);
                    double tmp_height = g.height();
                    if (tmp_height > max_glyph_height) max_glyph_height = tmp_height;
                    width_map[char_index] += g.advance();
                    line.add_glyph(std::move(g), scale_factor);
                }
            }
            line.update_max_char_height(max_glyph_height);
            break; //When we reach this point the current font had all glyphs.
        }
    }
}
};
} // namespace mapnik

#endif // MAPNIK_HARFBUZZ_SHAPER_HPP
<commit_msg>fix khmer problem<commit_after>/*****************************************************************************
 *
 * This file is part of Mapnik (c++ mapping toolkit)
 *
 * Copyright (C) 2015 Artem Pavlenko
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *****************************************************************************/

#ifndef MAPNIK_HARFBUZZ_SHAPER_HPP
#define MAPNIK_HARFBUZZ_SHAPER_HPP

// mapnik
#include <mapnik/text/text_properties.hpp>
#include <mapnik/text/text_line.hpp>
#include <mapnik/text/face.hpp>
#include <mapnik/text/font_feature_settings.hpp>
#include <mapnik/text/itemizer.hpp>
#include <mapnik/safe_cast.hpp>
#include <mapnik/font_engine_freetype.hpp>

// stl
#include <list>
#include <type_traits>

// harfbuzz
#include <harfbuzz/hb.h>
#include <harfbuzz/hb-ft.h>

// icu
#include <unicode/uscript.h>

namespace mapnik
{

static inline hb_script_t _icu_script_to_script(UScriptCode script)
{
    if (script == USCRIPT_INVALID_CODE) return HB_SCRIPT_INVALID;
        return hb_script_from_string(uscript_getShortName(script), -1);
}

static inline const uint16_t * uchar_to_utf16(const UChar* src)
{
   static_assert(sizeof(UChar) == sizeof(uint16_t),"UChar is eq size to uint16_t");
#if defined(_MSC_VER)
   return reinterpret_cast<const uint16_t *>(src);
#else
   return src;
#endif
}

struct harfbuzz_shaper
{
static void shape_text(text_line & line,
                       text_itemizer & itemizer,
                       std::map<unsigned,double> & width_map,
                       face_manager_freetype & font_manager,
                       double scale_factor)
{
    unsigned start = line.first_char();
    unsigned end = line.last_char();
    std::size_t length = end - start;
    if (!length) return;

    std::list<text_item> const& list = itemizer.itemize(start, end);

    line.reserve(length);

    auto hb_buffer_deleter = [](hb_buffer_t * buffer) { hb_buffer_destroy(buffer);};
    const std::unique_ptr<hb_buffer_t, decltype(hb_buffer_deleter)> buffer(hb_buffer_create(),hb_buffer_deleter);
    hb_buffer_pre_allocate(buffer.get(), safe_cast<int>(length));
    mapnik::value_unicode_string const& text = itemizer.text();

    for (auto const& text_item : list)
    {
        face_set_ptr face_set = font_manager.get_face_set(text_item.format_->face_name, text_item.format_->fontset);
        double size = text_item.format_->text_size * scale_factor;
        face_set->set_unscaled_character_sizes();
        std::size_t num_faces = face_set->size();
        std::size_t pos = 0;
        font_feature_settings const& ff_settings = text_item.format_->ff_settings;
        int ff_count = safe_cast<int>(ff_settings.count());

        struct glyph_face_info
        {
            face_ptr face;
            hb_glyph_info_t glyph;
            hb_glyph_position_t position;
        };
        std::vector<glyph_face_info> glyphinfos;
        unsigned valid_glyphs = 0;

        for (auto const& face : *face_set)
        {
            ++pos;
            hb_buffer_clear_contents(buffer.get());
            hb_buffer_add_utf16(buffer.get(), uchar_to_utf16(text.getBuffer()), text.length(), text_item.start, static_cast<int>(text_item.end - text_item.start));
            hb_buffer_set_direction(buffer.get(), (text_item.dir == UBIDI_RTL)?HB_DIRECTION_RTL:HB_DIRECTION_LTR);
            hb_buffer_set_script(buffer.get(), _icu_script_to_script(text_item.script));
            hb_font_t *font(hb_ft_font_create(face->get_face(), nullptr));
            // https://github.com/mapnik/test-data-visual/pull/25
            #if HB_VERSION_MAJOR > 0
             #if HB_VERSION_ATLEAST(1, 0 , 5)
            hb_ft_font_set_load_flags(font,FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING);
             #endif
            #endif
            hb_shape(font, buffer.get(), ff_settings.get_features(), ff_count);
            hb_font_destroy(font);

            unsigned num_glyphs = hb_buffer_get_length(buffer.get());

            if (num_glyphs > glyphinfos.size())
            {
                glyphinfos.resize(num_glyphs);
            }

            hb_glyph_info_t *glyphs = hb_buffer_get_glyph_infos(buffer.get(), nullptr);
            hb_glyph_position_t *positions = hb_buffer_get_glyph_positions(buffer.get(), nullptr);

            // Check if all glyphs are valid.
            for (unsigned i=0; i<num_glyphs; ++i)
            {
                if (glyphs[i].codepoint)
                {
                    if (!glyphinfos[i].glyph.codepoint)
                    {
                        ++valid_glyphs;
                    }
                    glyphinfos[i] = { face, glyphs[i], positions[i] };
                }
            }
            if (valid_glyphs < num_glyphs && (pos < num_faces))
            {
                //Try next font in fontset
                continue;
            }

            double max_glyph_height = 0;
            for (unsigned i=0; i<num_glyphs; ++i)
            {
                auto& gpos = positions[i];
                auto& glyph = glyphs[i];
                face_ptr theface = face;
                if (glyphinfos[i].glyph.codepoint)
                {
                    gpos = glyphinfos[i].position;
                    glyph = glyphinfos[i].glyph;
                    theface = glyphinfos[i].face;
                }
                unsigned char_index = glyph.cluster;
                glyph_info g(glyph.codepoint,char_index,text_item.format_);
                if (theface->glyph_dimensions(g))
                {
                    g.face = theface;
                    g.scale_multiplier = size / theface->get_face()->units_per_EM;
                    //Overwrite default advance with better value provided by HarfBuzz
                    g.unscaled_advance = gpos.x_advance;
                    g.offset.set(gpos.x_offset * g.scale_multiplier, gpos.y_offset * g.scale_multiplier);
                    double tmp_height = g.height();
                    if (tmp_height > max_glyph_height) max_glyph_height = tmp_height;
                    width_map[char_index] += g.advance();
                    line.add_glyph(std::move(g), scale_factor);
                }
            }
            line.update_max_char_height(max_glyph_height);
            break; //When we reach this point the current font had all glyphs.
        }
    }
}
};
} // namespace mapnik

#endif // MAPNIK_HARFBUZZ_SHAPER_HPP
<|endoftext|>


<commit_before>//
// Copyright (c) 2013-2014 Christoph Malek
// See LICENSE for more information.
//

#ifndef RJ_CORE_GAME_HANDLER_HPP
#define RJ_CORE_GAME_HANDLER_HPP

#include "game.hpp"
#include "game_window.hpp"
#include "render.hpp"
#include <rectojump/game/background/background_manager.hpp>
#include <rectojump/game/camera.hpp>
#include <rectojump/game/editor/editor.hpp>
#include <rectojump/game/main_menu/main_menu.hpp>
#include <rectojump/game/popup_manager.hpp>
#include <rectojump/global/common.hpp>
#include <rectojump/global/config_settings.hpp>
#include <rectojump/shared/data_manager.hpp>
#include <rectojump/shared/level_manager/level_manager.hpp>

#include <mlk/containers/container_utl.h>
#include <mlk/tools/bitset.h>

namespace rj
{
	class game_handler
	{
		game_window& m_game_window;
		data_manager& m_datamgr;
		level_manager& m_lvmgr;

		camera m_default_camera;
		render<game_handler> m_render;
		background_manager m_backgroundmgr;
		game<game_handler> m_game;
		editor<game_handler, decltype(m_game)> m_editor;
		main_menu<game_handler> m_mainmenu;
		popup_manager<game_handler> m_popupmgr;
		debug_info<game_handler, decltype(m_game)> m_debug_info;

		mlk::ebitset<state, state::num> m_current_states;

	public:
		game_handler(game_window& gw, data_manager& dm, level_manager& lm) :
			m_game_window{gw},
			m_datamgr{dm},
			m_lvmgr{lm},
			m_default_camera{gw, {settings::get_window_size<vec2f>() / 2.f, settings::get_window_size<vec2f>()}},
			m_render{*this},
			m_backgroundmgr{m_render},
			m_game{*this},
			m_editor{*this},
			m_mainmenu{*this},
			m_popupmgr{*this},
			m_debug_info{*this}
		{this->init();}

		void load_level(const level_id& id)
		{
			auto& lv(m_lvmgr.get_level(id));
			if(!lv.is_valid())
			{
				m_popupmgr.create_popup<popup_type::error>("Failed to load level: not a valid level");
				return;
			}

			m_backgroundmgr.clear();

			this->deactivate_state(state::main_menu);
			this->activate_state(state::game);

			m_game.load_level(lv);
		}

		template<typename... Args>
		void render_object(Args&&... args)
		{m_game_window.draw(std::forward<Args>(args)...);}

		template<state active_state, typename On_Input, typename... Input_Type_Args>
		void add_input(On_Input&& func, Input_Type_Args&&... keys_btns)
		{
			static_assert(sizeof...(keys_btns) > 0, "at least one key/button/wheel argument needed");
			add_input_helper<(sizeof...(keys_btns) > 1), typename std::tuple_element<0, std::tuple<Input_Type_Args...>>::type, active_state, On_Input, Input_Type_Args...>
			{*this, std::forward<On_Input>(func), std::forward<Input_Type_Args>(keys_btns)...};
		}

		// getters
		auto get_render() noexcept
		-> decltype(m_render)&
		{return m_render;}

		auto get_gamewindow() noexcept
		-> decltype(m_game_window)&
		{return m_game_window;}

		auto get_game() noexcept
		-> decltype(m_game)&
		{return m_game;}

		auto get_backgroundmgr() noexcept
		-> decltype(m_backgroundmgr)&
		{return m_backgroundmgr;}

		auto get_mainmenu() noexcept
		-> decltype(m_mainmenu)&
		{return m_mainmenu;}

		auto get_datamgr() noexcept
		-> decltype(m_datamgr)&
		{return m_datamgr;}

		auto get_levelmgr() noexcept
		-> decltype(m_lvmgr)&
		{return m_lvmgr;}

		auto get_debuginfo() noexcept
		-> decltype(m_debug_info)&
		{return m_debug_info;}

		auto get_popupmgr() noexcept
		-> decltype(m_popupmgr)&
		{return m_popupmgr;}

	private:
		void init()
		{
			m_current_states |= state::main_menu;

			m_game_window.get_updater().on_update += [this](dur duration){this->update(duration);};
			m_game_window.get_updater().on_render += [this]{this->render();};

			settings::on_changed() +=
			[this]{m_game_window.set_size(settings::get_window_size());};

			m_mainmenu.get_menu_start()->get_items().on_event("editor",
			[this]
			{
				this->deactivate_state(state::main_menu);
				this->activate_state(state::editor);
				m_editor.on_activate();
			});

			this->init_pointers();
			this->init_input();
		}

		void init_pointers() noexcept
		{
			m_game.set_levelmgr(&m_lvmgr);
		}

		void init_input()
		{
			// global input
			on_key_pressed(key::F) +=
			[this]
			{
				m_game_window.toggle_fullscreen();
				rj::settings::set_fullscreen(m_game_window.get_fullscreen());
			};

			on_key_pressed(key::T) +=
			[this]{m_game_window.toggle_titlebar();};

			on_keys_pressed(key::LShift, key::D) +=
			[this]{this->toggle_state(state::debug_info);};

			on_keys_pressed(key::LShift, key::Q) +=
			[this]{m_game_window.stop();};

			// window input
			on_key_pressed(key::Escape) +=
			[this]{if(!this->is_active(state::main_menu)) return; m_game_window.stop();};

			// game input
			on_key_pressed(key::P) +=
			[this]
			{
				if(this->is_active(state::game))
				{
					m_current_states.remove(state::game);
					m_current_states |= state::game_menu;
					return;
				}
				else if(this->is_active(state::game_menu))
				{
					m_current_states.remove(state::game_menu);
					m_current_states |= state::game;
					return;
				}
			};

			on_key_pressed(key::D) +=
			[this]{if(!this->is_active(state::game)) return; m_game.get_world().c_player();};

			on_btn_pressed(btn::Left) +=
			[this](const vec2f& pos)
			{
				if(!this->is_active(state::game))
					return;
				m_game.get_world().c_ent(pos);
			};

			on_btn_pressed(btn::Right) +=
			[this](const vec2f& pos)
			{
				if(!this->is_active(state::game))
					return;
				m_game.get_world().c_ent_death(pos);
			};

			on_key_pressed(key::A) +=
			[this]
			{
				if(!this->is_active(state::game))
					return;
				m_game.get_world().c_world();
			};
		}

		void activate_state(state s)
		{m_current_states |= s;}

		void deactivate_state(state s)
		{m_current_states.remove(s);}

		void toggle_state(state s)
		{m_current_states.toggle(s);}

		bool is_active(state s) const
		{return m_current_states & s;}

		void set_only_state(state s)
		{
			m_current_states.remove_all();
			m_current_states |= s;
		}

		void update(dur duration)
		{
			m_backgroundmgr.update(duration);

			if(this->is_active(state::game))
				m_game.update(duration);

			else if(this->is_active(state::editor))
			{
				m_editor.update(duration);
				m_game.get_world().get_entityhandler().update(duration);
			}

			else if(this->is_active(state::main_menu))
				m_mainmenu.update(duration);

			if(this->is_active(state::debug_info))
				m_debug_info.update(duration);

			m_popupmgr.update(duration);
		}

		void render()
		{
m_default_camera.set_changes();
			m_backgroundmgr.render();

			// render game when in game menu
			if(this->is_active(state::game) || this->is_active(state::game_menu))
				m_game.render();

			else if(this->is_active(state::editor))
			{
				m_editor.render();
//				m_game.get_world().get_entityhandler().render();
			}

			else if(this->is_active(state::main_menu))
				m_mainmenu.render();
m_default_camera.set_changes();
			if(this->is_active(state::debug_info))
				m_debug_info.render();

			m_popupmgr.render();

		}

		// multi_args: true, Input_Type = key
		template<bool multi_args, typename Input_Type, state s, typename On_Input, typename... Input_Type_Args>
		struct add_input_helper
		{
			add_input_helper(const game_handler& gh, On_Input&& func, Input_Type_Args&&... keys)
			{
				on_keys_pressed(std::forward<Input_Type_Args>(keys)...) +=
				[&gh, func]{if(gh.is_active(s)) func();};
			}
		};

		// multi_args: false, Input_Type = key
		template<typename Input_Type, state s, typename On_Input, typename... Input_Type_Args>
		struct add_input_helper<false, Input_Type, s, On_Input, Input_Type_Args...>
		{
			add_input_helper(const game_handler& gh, On_Input&& func, Input_Type_Args&&... k)
			{
				on_key_pressed(std::forward<Input_Type_Args>(k)...) +=
				[&gh, func]{if(gh.is_active(s)) func();};
			}
		};

		// multi_args: false, Input_Type = btn
		template<state s, typename On_Input, typename... Input_Type_Args>
		struct add_input_helper<false, btn, s, On_Input, Input_Type_Args...>
		{
			add_input_helper(const game_handler& gh, On_Input&& func, Input_Type_Args&&... b)
			{
				on_btn_pressed(std::forward<Input_Type_Args>(b)...) +=
				[&gh, func](const vec2f& pos){if(gh.is_active(s)) func(pos);};
			}
		};

		// multi_args: false, Input_Type = wheel
		template<state s, typename On_Input, typename... Input_Type_Args>
		struct add_input_helper<false, wheel, s, On_Input, Input_Type_Args...>
		{
			add_input_helper(const game_handler& gh, On_Input&& func, Input_Type_Args&&... w)
			{
				on_mousewheel(std::forward<Input_Type_Args>(w)...) +=
				[&gh, func](const vec2f& pos){if(gh.is_active(s)) func(pos);};
			}
		};
	};
}

#endif // RJ_CORE_GAME_HANDLER_HPP
<commit_msg>game_handler: added data_store (currently just with sf::Texture as storage)<commit_after>//
// Copyright (c) 2013-2014 Christoph Malek
// See LICENSE for more information.
//

#ifndef RJ_CORE_GAME_HANDLER_HPP
#define RJ_CORE_GAME_HANDLER_HPP

#include "game.hpp"
#include "game_window.hpp"
#include "render.hpp"
#include <rectojump/game/background/background_manager.hpp>
#include <rectojump/game/camera.hpp>
#include <rectojump/game/editor/editor.hpp>
#include <rectojump/game/main_menu/main_menu.hpp>
#include <rectojump/game/popup_manager.hpp>
#include <rectojump/global/common.hpp>
#include <rectojump/global/config_settings.hpp>
#include <rectojump/shared/level_manager/level_manager.hpp>
#include <rectojump/shared/data_manager.hpp>
#include <rectojump/shared/data_store.hpp>

#include <mlk/containers/container_utl.h>
#include <mlk/tools/bitset.h>

namespace rj
{
	class game_handler
	{
	public:
		// type decls
		using data_store_type = data_store<sf::Texture>;

	private:
		game_window& m_game_window;
		data_manager& m_datamgr;
		level_manager& m_lvmgr;

		data_store_type m_datastore{m_datamgr.get_all_containing_as_map_as<sf::Texture>(".png")};
		camera m_default_camera;
		render<game_handler> m_render;
		background_manager m_backgroundmgr;
		game<game_handler> m_game;
		editor<game_handler, decltype(m_game)> m_editor;
		main_menu<game_handler> m_mainmenu;
		popup_manager<game_handler> m_popupmgr;
		debug_info<game_handler, decltype(m_game)> m_debug_info;

		mlk::ebitset<state, state::num> m_current_states;

	public:
		game_handler(game_window& gw, data_manager& dm, level_manager& lm) :
			m_game_window{gw},
			m_datamgr{dm},
			m_lvmgr{lm},
			m_default_camera{gw, {settings::get_window_size<vec2f>() / 2.f, settings::get_window_size<vec2f>()}},
			m_render{*this},
			m_backgroundmgr{m_render},
			m_game{*this},
			m_editor{*this},
			m_mainmenu{*this},
			m_popupmgr{*this},
			m_debug_info{*this}
		{this->init();}

		void load_level(const level_id& id)
		{
			auto& lv(m_lvmgr.get_level(id));
			if(!lv.is_valid())
			{
				m_popupmgr.create_popup<popup_type::error>("Failed to load level: not a valid level");
				return;
			}

			m_backgroundmgr.clear();

			this->deactivate_state(state::main_menu);
			this->activate_state(state::game);

			m_game.load_level(lv);
		}

		template<typename... Args>
		void render_object(Args&&... args)
		{m_game_window.draw(std::forward<Args>(args)...);}

		template<state active_state, typename On_Input, typename... Input_Type_Args>
		void add_input(On_Input&& func, Input_Type_Args&&... keys_btns)
		{
			static_assert(sizeof...(keys_btns) > 0, "at least one key/button/wheel argument needed");
			add_input_helper<(sizeof...(keys_btns) > 1), typename std::tuple_element<0, std::tuple<Input_Type_Args...>>::type, active_state, On_Input, Input_Type_Args...>
			{*this, std::forward<On_Input>(func), std::forward<Input_Type_Args>(keys_btns)...};
		}

		// getters
		auto get_render() noexcept
		-> decltype(m_render)&
		{return m_render;}

		auto get_gamewindow() noexcept
		-> decltype(m_game_window)&
		{return m_game_window;}

		auto get_game() noexcept
		-> decltype(m_game)&
		{return m_game;}

		auto get_backgroundmgr() noexcept
		-> decltype(m_backgroundmgr)&
		{return m_backgroundmgr;}

		auto get_mainmenu() noexcept
		-> decltype(m_mainmenu)&
		{return m_mainmenu;}

		auto get_datastore() noexcept
		-> decltype(m_datastore)&
		{return m_datastore;}

		auto get_datamgr() noexcept
		-> decltype(m_datamgr)&
		{return m_datamgr;}

		auto get_levelmgr() noexcept
		-> decltype(m_lvmgr)&
		{return m_lvmgr;}

		auto get_debuginfo() noexcept
		-> decltype(m_debug_info)&
		{return m_debug_info;}

		auto get_popupmgr() noexcept
		-> decltype(m_popupmgr)&
		{return m_popupmgr;}

	private:
		void init()
		{
			m_current_states |= state::main_menu;

			m_game_window.get_updater().on_update += [this](dur duration){this->update(duration);};
			m_game_window.get_updater().on_render += [this]{this->render();};

			settings::on_changed() +=
			[this]{m_game_window.set_size(settings::get_window_size());};

			m_mainmenu.get_menu_start()->get_items().on_event("editor",
			[this]
			{
				this->deactivate_state(state::main_menu);
				this->activate_state(state::editor);
				m_editor.on_activate();
			});

			this->init_pointers();
			this->init_input();
		}

		void init_pointers() noexcept
		{
			m_game.set_levelmgr(&m_lvmgr);
		}

		void init_input()
		{
			// global input
			on_keys_pressed(key::LShift, key::F) +=
			[this]
			{
				m_game_window.toggle_fullscreen();
				rj::settings::set_fullscreen(m_game_window.get_fullscreen());
			};

			on_keys_pressed(key::LShift, key::T) +=
			[this]{m_game_window.toggle_titlebar();};

			on_keys_pressed(key::LShift, key::D) +=
			[this]{this->toggle_state(state::debug_info);};

			on_keys_pressed(key::LShift, key::Q) +=
			[this]{m_game_window.stop();};

			// window input
			on_key_pressed(key::Escape) +=
			[this]{if(!this->is_active(state::main_menu)) return; m_game_window.stop();};

			// game input
			on_key_pressed(key::P) +=
			[this]
			{
				if(this->is_active(state::game))
				{
					m_current_states.remove(state::game);
					m_current_states |= state::game_menu;
					return;
				}
				else if(this->is_active(state::game_menu))
				{
					m_current_states.remove(state::game_menu);
					m_current_states |= state::game;
					return;
				}
			};

			on_key_pressed(key::D) +=
			[this]{if(!this->is_active(state::game)) return; m_game.get_world().c_player();};
		}

		void activate_state(state s)
		{m_current_states |= s;}

		void deactivate_state(state s)
		{m_current_states.remove(s);}

		void toggle_state(state s)
		{m_current_states.toggle(s);}

		bool is_active(state s) const
		{return m_current_states & s;}

		void set_only_state(state s)
		{
			m_current_states.remove_all();
			m_current_states |= s;
		}

		void update(dur duration)
		{
			m_backgroundmgr.update(duration);

			if(this->is_active(state::game))
				m_game.update(duration);

			else if(this->is_active(state::editor))
			{
				m_editor.update(duration);
				m_game.get_world().get_entityhandler().update(duration);
			}

			else if(this->is_active(state::main_menu))
				m_mainmenu.update(duration);

			if(this->is_active(state::debug_info))
				m_debug_info.update(duration);

			m_popupmgr.update(duration);
		}

		void render()
		{
			m_default_camera.set_changes();
			m_backgroundmgr.render();

			m_default_camera.set_changes();
			// render game when in game menu
			if(this->is_active(state::game) || this->is_active(state::game_menu))
				m_game.render();

			else if(this->is_active(state::editor))
				m_editor.render();

			else if(this->is_active(state::main_menu))
				m_mainmenu.render();

			m_default_camera.set_changes();
			if(this->is_active(state::debug_info))
				m_debug_info.render();

			m_popupmgr.render();
		}

		// multi_args: true, Input_Type = key
		template<bool multi_args, typename Input_Type, state s, typename On_Input, typename... Input_Type_Args>
		struct add_input_helper
		{
			add_input_helper(const game_handler& gh, On_Input&& func, Input_Type_Args&&... keys)
			{
				on_keys_pressed(std::forward<Input_Type_Args>(keys)...) +=
				[&gh, func]{if(gh.is_active(s)) func();};
			}
		};

		// multi_args: false, Input_Type = key
		template<typename Input_Type, state s, typename On_Input, typename... Input_Type_Args>
		struct add_input_helper<false, Input_Type, s, On_Input, Input_Type_Args...>
		{
			add_input_helper(const game_handler& gh, On_Input&& func, Input_Type_Args&&... k)
			{
				on_key_pressed(std::forward<Input_Type_Args>(k)...) +=
				[&gh, func]{if(gh.is_active(s)) func();};
			}
		};

		// multi_args: false, Input_Type = btn
		template<state s, typename On_Input, typename... Input_Type_Args>
		struct add_input_helper<false, btn, s, On_Input, Input_Type_Args...>
		{
			add_input_helper(const game_handler& gh, On_Input&& func, Input_Type_Args&&... b)
			{
				on_btn_pressed(std::forward<Input_Type_Args>(b)...) +=
				[&gh, func](const vec2f& pos){if(gh.is_active(s)) func(pos);};
			}
		};

		// multi_args: false, Input_Type = wheel
		template<state s, typename On_Input, typename... Input_Type_Args>
		struct add_input_helper<false, wheel, s, On_Input, Input_Type_Args...>
		{
			add_input_helper(const game_handler& gh, On_Input&& func, Input_Type_Args&&... w)
			{
				on_mousewheel(std::forward<Input_Type_Args>(w)...) +=
				[&gh, func](const vec2f& pos){if(gh.is_active(s)) func(pos);};
			}
		};
	};
}

#endif // RJ_CORE_GAME_HANDLER_HPP
<|endoftext|>