/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * Author: pgess <v.melnychenko@xreate.org>
 * File: ast.cpp
 */

/**
 * \file    ast.h
 * \brief   Syntax Tree and related code
 *
 * \sa xreate::AST
 */

#include "ast.h"
#include "ExternLayer.h"
#include "analysis/typeinference.h"

#include <stdexcept>
#include <iostream>

//TODO BDecl. forbid multiple body declaration (ExprTyped)

namespace std {

    std::size_t
    hash<xreate::ScopedSymbol>::operator()(xreate::ScopedSymbol const& s) const {
        return s.id ^ (s.version << 2);
    }

    bool
    equal_to<xreate::ScopedSymbol>::operator()(const xreate::ScopedSymbol& __x, const xreate::ScopedSymbol& __y) const {
        return __x.id == __y.id && __x.version == __y.version;
    }

    size_t
    hash<xreate::Symbol>::operator()(xreate::Symbol const& s) const {
        return hash<xreate::ScopedSymbol>()(s.identifier) ^ ((long int) s.scope << 1);
    }

    bool
    equal_to<xreate::Symbol>::operator()(const xreate::Symbol& __x, const xreate::Symbol& __y) const {
        return __x == __y;
    };
}

using namespace std;

namespace xreate {

Atom<Identifier_t>::Atom(const std::wstring& value) {
    __value = wstring_to_utf8(value);
}

Atom<Identifier_t>::Atom(std::string && name) : __value(name) {
}

const std::string&
Atom<Identifier_t>::get() const {
    return __value;
}

Atom<Number_t>::Atom(wchar_t* value) {
    //DEBT reconsider number literal recognition
    __value = wcstol(value, 0, 10);
}

Atom<Number_t>::Atom(int value)
: __value(value) {
}

double
Atom<Number_t>::get()const {
    return __value;
}

Atom<String_t>::Atom(const std::wstring& value) {
    assert(value.size() >= 2);
    __value = wstring_to_utf8(value.substr(1, value.size() - 2));
}

Atom<String_t>::Atom(std::string && name) : __value(name) {}

const std::string&
Atom<String_t>::get() const {
    return __value;
}

class ExpressionHints {
public:

    static bool
    isStringValueValid(const Expression& e) {
        switch (e.__state) {
            case Expression::INVALID:
                assert(false);

            case Expression::IDENT:
            case Expression::STRING:
                return true;

            case Expression::NUMBER:
            case Expression::BINDING:
                return false;

            case Expression::COMPOUND:
            {
                switch (e.op) {
                    case Operator::CALL:
                        return true;

                    default: return false;
                }
            }
        }

        return false;
    }

    static bool
    isDoubleValueValid(const Expression& e) {
        switch (e.__state) {
            case Expression::NUMBER:
                return true;

            case Expression::INVALID:
                assert(false);

            case Expression::IDENT:
            case Expression::STRING:
            case Expression::BINDING:
                return false;

            case Expression::COMPOUND: {
                switch (e.op) {
                    case Operator::VARIANT:
                        return true;
                    default: return false;
                }
            }
        }

        return false;
    }
};

class TypesResolver {
private:
    const AST* ast;
    std::map<std::string, TypeAnnotation> scope;
    std::map<TypeAnnotation, int> signatures;

    ExpandedType expandType(const TypeAnnotation &t, const std::vector<TypeAnnotation> &args = std::vector<TypeAnnotation>()) {
        return TypesResolver(ast, scope, signatures)(t, args);
    }

    std::vector<TypeAnnotation>
    expandOperands(const std::vector<TypeAnnotation>& operands) {
        std::vector<TypeAnnotation> pack;

        pack.reserve(operands.size());
        std::transform(operands.begin(), operands.end(), std::inserter(pack, pack.end()),
                [this](const TypeAnnotation & t) {
                    return expandType(t);
                });

        return pack;
    }

public:

    TypesResolver(const AST* root, const std::map<std::string, TypeAnnotation>& scopeOuter = std::map<std::string, TypeAnnotation>(),
            std::map<TypeAnnotation, int> signaturesOuter = std::map<TypeAnnotation, int>())
    : ast(root), scope(scopeOuter), signatures(signaturesOuter) {
    }

    ExpandedType
    operator()(const TypeAnnotation &t, const std::vector<TypeAnnotation> &args = std::vector<TypeAnnotation>()) {
        //assert(args.size() == t.bindings.size()); // invalid number of arguments
        for (size_t i = 0; i < args.size(); ++i) {
            scope[t.bindings.at(i)] = args.at(i);
        }

        switch (t.__operator) {
            case TypeOperator::ARRAY:
            {
                assert(t.__operands.size() == 1);

                Expanded<TypeAnnotation> elTy = expandType(t.__operands.at(0));
                return ExpandedType(TypeAnnotation(tag_array, elTy, 0));
            }

            case TypeOperator::STRUCT:
            {
                std::vector<TypeAnnotation>&& packOperands = expandOperands(t.__operands);
                auto typNew = TypeAnnotation(TypeOperator::STRUCT, move(packOperands));
                typNew.fields = t.fields;

                return ExpandedType(move(typNew));
            };

            case TypeOperator::VARIANT:
            {
                std::vector<TypeAnnotation>&& packOperands = expandOperands(t.__operands);
                auto typNew = TypeAnnotation(TypeOperator::VARIANT, move(packOperands));
                typNew.fields = t.fields;

                return ExpandedType(move(typNew));
            };

            case TypeOperator::CALL:
            {
                std::string alias = t.__valueCustom;

                //find in local scope:
                TypeAnnotation ty;
                if (scope.count(alias)) {
                    ty = scope.at(alias);

                } else if (ast->__indexTypeAliases.count(alias)) {
                    ty = ast->__indexTypeAliases.at(alias);

                } else {
                    assert(false && "Undefined or external type");
                }

                std::vector<TypeAnnotation>&& operands = expandOperands(t.__operands);
                TypeAnnotation signature(TypeOperator::CALL, move(operands));
                signature.__valueCustom = alias;

                if (signatures.count(signature)) {
                    auto link = TypeAnnotation(TypeOperator::LINK,{});
                    link.conjuctionId = signatures.at(signature);

                    return ExpandedType(move(link));
                }

                int cid = signatures.size();
                signatures[signature] = cid;
                TypeAnnotation tyResult = expandType(ty, operands);
                tyResult.conjuctionId = cid;

                return ExpandedType(move(tyResult));
            };

            case TypeOperator::CUSTOM:
            {
                std::string alias = t.__valueCustom;

                /*
                if (signatures.count(alias)) {
                    return ExpandedType(TypeAnnotation(TypeOperator::LINK, {t}));
                }
                signatures[alias].emplace(t);
                 */

                //find in local scope:
                if (scope.count(alias)) {
                    return expandType(scope.at(alias));
                }

                // find in general scope:
                if (ast->__indexTypeAliases.count(alias)) {
                    return expandType(ast->__indexTypeAliases.at(t.__valueCustom));
                }

                //if type is unknown keep it as is.
                return ExpandedType(TypeAnnotation(t));
            };

            case TypeOperator::ACCESS:
            {
                std::string alias = t.__valueCustom;
                ExpandedType tyAlias = ExpandedType(TypeAnnotation());

                //find in local scope:
                if (scope.count(alias)) {
                    tyAlias = expandType(scope.at(alias));

                    //find in global scope:
                } else if ((ast->__indexTypeAliases.count(alias))) {
                    tyAlias = expandType(ast->__indexTypeAliases.at(alias));

                } else {
                    assert(false && "Undefined or external type");
                }

                assert(tyAlias->__operator == TypeOperator::STRUCT);

                for (const string& field : t.fields) {
                    auto fieldIt = std::find(tyAlias->fields.begin(), tyAlias->fields.end(), field);
                    assert(fieldIt != tyAlias->fields.end() && "unknown field");

                    int fieldId = fieldIt - tyAlias->fields.begin();
                    tyAlias = expandType(tyAlias->__operands.at(fieldId));
                }

                return tyAlias;
            }

            case TypeOperator::NONE:
            {
                return ExpandedType(TypeAnnotation(t));
            }

            default:
                assert(false);
        }

        assert(false);
        return ExpandedType(TypeAnnotation());
    }
};

TypeAnnotation::TypeAnnotation()
: __operator(TypeOperator::NONE), __value(TypePrimitive::Invalid) {
}

TypeAnnotation::TypeAnnotation(TypePrimitive typ)
: __value(typ) {
}

TypeAnnotation::TypeAnnotation(TypeOperator op, std::initializer_list<TypeAnnotation> operands)
: __operator(op), __operands(operands) {

}

TypeAnnotation::TypeAnnotation(TypeOperator op, std::vector<TypeAnnotation>&& operands)
: __operator(op), __operands(operands) {
}

TypeAnnotation::TypeAnnotation(llvm_array_tag, TypeAnnotation typ, int size)
: TypeAnnotation(TypeOperator::ARRAY,{typ}) {
    __size = size;
}

bool
TypeAnnotation::isValid() const {
    return !(__value == TypePrimitive::Invalid && __operator == TypeOperator::NONE);
}

bool
TypeAnnotation::operator<(const TypeAnnotation& t) const {
    if (__operator != t.__operator) return __operator < t.__operator;

    if (__operator == TypeOperator::NONE)
        return __value < t.__value;

    if (__operator == TypeOperator::CALL || __operator == TypeOperator::CUSTOM || __operator == TypeOperator::ACCESS) {
        if (__valueCustom != t.__valueCustom)
            return __valueCustom < t.__valueCustom;
    }

    return __operands < t.__operands;
}

/*
TypeAnnotation (struct_tag, std::initializer_list<TypeAnnotation>)
{}
 */

void
TypeAnnotation::addBindings(std::vector<Atom<Identifier_t>>&& params) {
    bindings.reserve(bindings.size() + params.size());

    std::transform(params.begin(), params.end(), std::inserter(bindings, bindings.end()),
            [](const Atom<Identifier_t>& ident) {
                return ident.get(); });
}

void
TypeAnnotation::addFields(std::vector<Atom<Identifier_t>>&& listFields) {
    fields.reserve(fields.size() + listFields.size());

    std::transform(listFields.begin(), listFields.end(), std::inserter(fields, fields.end()),
            [](const Atom<Identifier_t>& ident) {
                return ident.get(); });
}

unsigned int Expression::nextVacantId = 0;

Expression::Expression(const Atom<Number_t>& number)
: Expression() {
    __state = NUMBER;
    op = Operator::NONE;
    __valueD = number.get();
}

Expression::Expression(const Atom<String_t>& a)
: Expression() {
    __state = STRING;
    op = Operator::NONE;
    __valueS = a.get();
}

Expression::Expression(const Atom<Identifier_t> &ident)
: Expression() {
    __state = IDENT;
    op = Operator::NONE;
    __valueS = ident.get();
}

Expression::Expression(const Operator &oprt, std::initializer_list<Expression> params)
: Expression() {
    __state = COMPOUND;
    op = oprt;

    if (op == Operator::CALL) {
        assert(params.size() > 0);
        Expression arg = *params.begin();

        assert(arg.__state == Expression::IDENT);
        __valueS = std::move(arg.__valueS);

        operands.insert(operands.end(), params.begin() + 1, params.end());
        return;
    }

    operands.insert(operands.end(), params.begin(), params.end());
}

void
Expression::setOp(Operator oprt) {
    op = oprt;

    switch (op) {
        case Operator::NONE:
            __state = INVALID;
            break;

        default:
            __state = COMPOUND;
            break;
    }
}

void
Expression::addArg(Expression &&arg) {
    operands.push_back(arg);
}

void
Expression::addTags(const std::list<Expression> tags) const {
    std::transform(tags.begin(), tags.end(), std::inserter(this->tags, this->tags.end()),
            [](const Expression & tag) {
                return make_pair(tag.getValueString(), tag);
            });
}

void
Expression::addBindings(std::initializer_list<Atom<Identifier_t>> params) {
    addBindings(params.begin(), params.end());
}

void
Expression::bindType(TypeAnnotation t) {
    type = move(t);
}

void
Expression::addBlock(ManagedScpPtr scope) {
    blocks.push_back(scope.operator->());
}

const std::vector<Expression>&
Expression::getOperands() const {
    return operands;
}

double
Expression::getValueDouble() const {
    return __valueD;
}

const std::string&
Expression::getValueString() const {
    return __valueS;
}

void
Expression::setValue(const Atom<Identifier_t>&& v) {
    __valueS = v.get();
}

void Expression::setValueDouble(double value) {
    __valueD = value;
}

bool
Expression::isValid() const {
    return (__state != INVALID);
}

bool
Expression::isDefined() const {
    return (__state != BINDING && __state != INVALID);
}

Expression::Expression()
: __state(INVALID), op(Operator::NONE), id(nextVacantId++) {
}

namespace details { namespace inconsistent {

AST::AST() {
    Attachments::init<versions::VariableVersion>();
    Attachments::init<IdentifierSymbol>();
    Attachments::init<SymbolAlias>();
    Attachments::init<TypeInferred>();
}

void
AST::addInterfaceData(const ASTInterface& interface, Expression&& data) {
    __interfacesData.emplace(interface, move(data));
}

void
AST::addDFAData(Expression &&data) {
    __dfadata.push_back(data);
}

void
AST::addExternData(ExternData &&data) {
    __externdata.insert(__externdata.end(), data.entries.begin(), data.entries.end());
}

void
AST::add(Function* f) {
    __functions.push_back(f);
    __indexFunctions.emplace(f->getName(), __functions.size() - 1);
}

void
AST::add(MetaRuleAbstract *r) {
    __rules.push_back(r);
}

void
AST::add(TypeAnnotation t, Atom<Identifier_t> alias) {
    if (t.__operator == TypeOperator::VARIANT) {
        for (int i = 0, size = t.fields.size(); i < size; ++i) {
            __dictVariants.emplace(t.fields[i], make_pair(t, i));
        }
    }

    __indexTypeAliases.emplace(alias.get(), move(t));
}

ManagedScpPtr
AST::add(CodeScope* scope) {
    this->__scopes.push_back(scope);
    return ManagedScpPtr(this->__scopes.size() - 1, &this->__scopes);
}

std::string
AST::getModuleName() {
    const std::string name = "moduleTest";

    return name;
}

ManagedPtr<Function>
AST::findFunction(const std::string& name) {
    int count = __indexFunctions.count(name);
    if (!count) {
        return ManagedFnPtr::Invalid();
    }

    assert(count == 1);

    auto range = __indexFunctions.equal_range(name);
    return ManagedPtr<Function>(range.first->second, &this->__functions);
}

std::list<ManagedFnPtr>
AST::getAllFunctions() const {
    const size_t size = __functions.size();

    std::list<ManagedFnPtr> result;
    for (size_t i = 0; i < size; ++i) {
        result.push_back(ManagedFnPtr(i, &this->__functions));
    }

    return result;
}

//TASK select default  specializations
std::list<ManagedFnPtr>
AST::getFunctionSpecializations(const std::string& fnName) const {
    auto functions = __indexFunctions.equal_range(fnName);

    std::list<ManagedFnPtr> result;
    std::transform(functions.first, functions.second, inserter(result, result.end()),
            [this](auto f) {
                return ManagedFnPtr(f.second, &this->__functions);
            });

    return result;
}

template<>
ManagedPtr<Function>
AST::begin<Function>() {
    return ManagedPtr<Function>(0, &this->__functions);
}

template<>
ManagedPtr<CodeScope>
AST::begin<CodeScope>() {
    return ManagedPtr<CodeScope>(0, &this->__scopes);
}

template<>
ManagedPtr<MetaRuleAbstract>
AST::begin<MetaRuleAbstract>() {
    return ManagedPtr<MetaRuleAbstract>(0, &this->__rules);
}

void
AST::recognizeVariantConstructor(Expression& function) {
    assert(function.op == Operator::CALL);

    std::string variant = function.getValueString();
    if (!__dictVariants.count(variant)) {
        return;
    }

    auto record = __dictVariants.at(variant);
    const TypeAnnotation& typ = record.first;

    function.op = Operator::VARIANT;
    function.setValueDouble(record.second);
    function.type = typ;
}

Atom<Number_t>
AST::recognizeVariantConstructor(Atom<Identifier_t> ident) {
    std::string variant = ident.get();
    assert(__dictVariants.count(variant) && "Can't recognize variant constructor");
    auto record = __dictVariants.at(variant);

    return Atom<Number_t>(record.second);
}

void
AST::postponeIdentifier(CodeScope* scope, const Expression& id) {
    bucketUnrecognizedIdentifiers.emplace(scope, id);
}

void
AST::recognizePostponedIdentifiers() {
    for (const auto& identifier : bucketUnrecognizedIdentifiers) {
        if (!identifier.first->recognizeIdentifier(identifier.second)) {
            //exception: Ident not found
            std::cout << "Unknown symbol: " << identifier.second.getValueString() << std::endl;
            assert(false && "Symbol not found");
        }
    }
}

xreate::AST*
AST::finalize() {
    //all finalization steps:
    recognizePostponedIdentifiers();

    return reinterpret_cast<xreate::AST*> (this);
}

} } //namespace details::incomplete

Expanded<TypeAnnotation>
AST::findType(const std::string& name) {
    // find in general scope:
    if (__indexTypeAliases.count(name))
        return expandType(__indexTypeAliases.at(name));

    //if type is unknown keep it as is.
    TypeAnnotation t(TypeOperator::CUSTOM,{});
    t.__valueCustom = name;
    return ExpandedType(move(t));
}

Expanded<TypeAnnotation>
AST::expandType(const TypeAnnotation &t) const {
    return TypesResolver(this)(t);
}

ExpandedType
AST::getType(const Expression& expression) {
    return typeinference::getType(expression, *this);
}

Function::Function(const Atom<Identifier_t>& name)
: __entry(new CodeScope(0)) {
    __name = name.get();
}

void
Function::addTag(Expression&& tag, const TagModifier mod) {
    string name = tag.getValueString();
    __tags.emplace(move(name), move(tag));
}

const std::map<std::string, Expression>&
Function::getTags() const {
    return __tags;
}

CodeScope*
Function::getEntryScope() const {
    return __entry;
}

void
Function::addBinding(Atom <Identifier_t>&& name, Expression&& argument) {
    __entry->addBinding(move(name), move(argument));
}

const std::string&
Function::getName() const {
    return __name;
}

ScopedSymbol
CodeScope::registerIdentifier(const Expression& identifier) {
    versions::VariableVersion version = Attachments::get<versions::VariableVersion>(identifier, versions::VERSION_NONE);

    auto result = __identifiers.emplace(identifier.getValueString(), __vCounter);
    if (result.second) {
        ++__vCounter;
        return { __vCounter - 1, version };
    }

    return { result.first->second, version };
}

bool
CodeScope::recognizeIdentifier(const Expression& identifier) const {
    versions::VariableVersion version = Attachments::get<versions::VariableVersion>(identifier, versions::VERSION_NONE);
    const std::string& name = identifier.getValueString();

    //search identifier in the current block
    if (__identifiers.count(name)) {
        VNameId id = __identifiers.at(name);

        Symbol s;
        s.identifier = ScopedSymbol{id, version};
        s.scope = const_cast<CodeScope*> (this);
        Attachments::put<IdentifierSymbol>(identifier, s);

        return true;
    }

    //search in the parent scope
    if (__parent) {
        return __parent->recognizeIdentifier(identifier);
    }

    return false;
}

ScopedSymbol
CodeScope::getSymbol(const std::string& alias) {
    assert(__identifiers.count(alias));
    VNameId id = __identifiers.at(alias);

    return {id, versions::VERSION_NONE };
}

void
CodeScope::addBinding(Expression&& var, Expression&& argument) {
    argument.__state = Expression::BINDING;

    __bindings.push_back(var.getValueString());
    ScopedSymbol binding = registerIdentifier(var);
    __declarations[binding] = move(argument);
}

Symbol
CodeScope::addDefinition(Expression&& var, Expression&& body) {
    ScopedSymbol s = registerIdentifier(var);
    __declarations[s] = move(body);

    return Symbol{s, this};
}

CodeScope::CodeScope(CodeScope* parent)
: __parent(parent) {
}

CodeScope::~CodeScope() {
}

void
CodeScope::setBody(const Expression &body) {
    assert(__declarations.count(ScopedSymbol::RetSymbol)==0 && "Attempt to reassign scope body");
    __declarations[ScopedSymbol::RetSymbol] = body;
}

const Expression&
CodeScope::getBody() const{
    return __declarations.at(ScopedSymbol::RetSymbol);
}

const Expression&
CodeScope::getDefinition(const Symbol& symbol, bool flagAllowUndefined){
    const CodeScope* self = symbol.scope;
    return self->getDefinition(symbol.identifier, flagAllowUndefined);
}

const Expression&
CodeScope::getDefinition(const ScopedSymbol& symbol, bool flagAllowUndefined) const{
    static Expression expressionInvalid;

    if (!__declarations.count(symbol)){
        if (flagAllowUndefined) return expressionInvalid;
        assert(false && "Symbol's declaration not found");
    }

    return __declarations.at(symbol);
}

void
RuleArguments::add(const Atom<Identifier_t> &arg, DomainAnnotation typ) {
    emplace_back(arg.get(), typ);
}

void
RuleGuards::add(Expression&& e) {
    push_back(e);
}

MetaRuleAbstract::
MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards)
: __args(std::move(args)), __guards(std::move(guards)) {
}

MetaRuleAbstract::~MetaRuleAbstract() {
}

RuleWarning::
RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom<String_t>&& message)
: MetaRuleAbstract(std::move(args), std::move(guards)), __message(message.get()), __condition(condition) {
}

RuleWarning::~RuleWarning() {
}

void
RuleWarning::compile(ClaspLayer& layer) {
    //TODO restore addRuleWarning
    //layer.addRuleWarning(*this);
}

bool operator<(const ScopedSymbol& s1, const ScopedSymbol& s2) {
    return (s1.id < s2.id) || (s1.id == s2.id && s1.version < s2.version);
}

bool operator==(const ScopedSymbol& s1, const ScopedSymbol& s2) {
    return (s1.id == s2.id) && (s1.version == s2.version);
}

bool operator<(const Symbol& s1, const Symbol& s2) {
    return (s1.scope < s2.scope) || (s1.scope == s2.scope && s1.identifier < s2.identifier);
}

bool operator==(const Symbol& s1, const Symbol& s2) {
    return (s1.scope == s2.scope) && (s1.identifier == s2.identifier);
}

bool operator<(const Expression&a, const Expression&b) {
    if (a.__state != b.__state) return a.__state < b.__state;
    assert(a.__state != Expression::INVALID);
    switch (a.__state) {
        case Expression::IDENT:
        case Expression::STRING:
            return a.getValueString() < b.getValueString();

        case Expression::NUMBER:
            return a.getValueDouble() < b.getValueDouble();

        case Expression::COMPOUND:
        {
            assert(a.blocks.size() == 0);
            assert(b.blocks.size() == 0);

            if (a.op != b.op) {
                return a.op < b.op;
            }

            bool flagAValid = ExpressionHints::isStringValueValid(a);
            bool flagBValid = ExpressionHints::isStringValueValid(b);

            if (flagAValid != flagBValid) {
                return flagAValid < flagBValid;
            }

            if (flagAValid) {
                if (a.getValueString() != b.getValueString()) {
                    return a.getValueString() < b.getValueString();
                }
            }

            flagAValid = ExpressionHints::isDoubleValueValid(a);
            flagBValid = ExpressionHints::isDoubleValueValid(b);

            if (flagAValid != flagBValid) {
                return flagAValid < flagBValid;
            }

            if (flagAValid) {
                if (a.getValueDouble() != b.getValueDouble()) {
                    return a.getValueDouble() < b.getValueDouble();
                }
            }

            if (a.operands.size() != b.operands.size()) {
                return (a.operands.size() < b.operands.size());
            }

            for (size_t i = 0; i < a.operands.size(); ++i) {
                bool result = a.operands[i] < b.operands[i];
                if (result) return true;
            }

            return false;
        }

        case Expression::BINDING:
        case Expression::INVALID:
            assert(false);
    }

    return false;
}

bool
Expression::operator==(const Expression& other) const {
    if (this->__state != other.__state) return false;

    if (ExpressionHints::isStringValueValid(*this)) {
        if (this->__valueS != other.__valueS) return false;
    }

    if (ExpressionHints::isDoubleValueValid(*this)) {
        if (this->__valueD != other.__valueD) return false;
    }

    if (this->__state != Expression::COMPOUND) {
        return true;
    }

    if (this->op != other.op) {
        return false;
    }

    if (this->operands.size() != other.operands.size()) {
        return false;
    }

    for (size_t i = 0; i<this->operands.size(); ++i) {
        if (!(this->operands[i] == other.operands[i])) return false;
    }

    assert(!this->blocks.size());
    assert(!other.blocks.size());

    return true;
}

const ScopedSymbol
ScopedSymbol::RetSymbol = ScopedSymbol{0, versions::VERSION_NONE};

} //end of namespace xreate




