/**
 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cctype>
#include <errno.h>

#include <iterator>

#include "assembly-type.h"
#include "ins_emit.h"
#include "modifiers.h"
#include "opcode_parsing.h"
#include "operand_types_print.h"
#include "utils/number-utils.h"

namespace panda::pandasm {

bool Parser::ParseRecordFields()
{
    if (!open_ && *context_ == Token::Type::DEL_BRACE_L) {
        curr_record_->body_location.begin = GetCurrentPosition(false);
        open_ = true;

        ++context_;
    }

    curr_record_->body_presence = true;

    if (!open_) {
        context_.err = GetError("Expected keyword.", Error::ErrorType::ERR_BAD_KEYWORD);
        return false;
    }

    if (context_.Mask()) {
        return true;
    }

    if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
        curr_record_->body_location.end = GetCurrentPosition(true);
        ++context_;

        open_ = false;

        return true;
    }

    curr_record_->field_list.emplace_back(program_.lang);
    curr_fld_ = &(curr_record_->field_list[curr_record_->field_list.size() - 1]);
    curr_fld_->line_of_def = line_stric_;
    context_.ins_number = curr_record_->field_list.size();

    LOG(DEBUG, ASSEMBLER) << "parse line " << line_stric_ << " as field (.field name)";
    if (!ParseRecordField()) {
        if (context_.err.err != Error::ErrorType::ERR_NONE) {
            return false;
        }

        if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
            curr_record_->body_location.end = GetCurrentPosition(true);
            ++context_;
            open_ = false;
        } else {
            context_.err = GetError("Expected a new field on the next line.", Error::ErrorType::ERR_BAD_KEYWORD);
            return false;
        }
    }

    return true;
}

bool Parser::ParseFieldName()
{
    if (PrefixedValidName()) {
        std::string field_name = std::string(context_.GiveToken().data(), context_.GiveToken().length());

        auto match_names = [&field_name](const pandasm::Field &f) { return field_name == f.name; };
        const auto iter = std::find_if(curr_record_->field_list.begin(), curr_record_->field_list.end(), match_names);

        if (iter != curr_record_->field_list.end()) {
            if (iter->is_defined) {
                context_.err =
                    GetError("Repeating field names in the same record.", Error::ErrorType::ERR_REPEATING_FIELD_NAME);

                return false;
            }

            curr_record_->field_list.erase(iter);
        }

        curr_fld_ = &(curr_record_->field_list[curr_record_->field_list.size() - 1]);

        curr_fld_->name = field_name;

        ++context_;

        return true;
    }

    context_.err = GetError("Invalid name of field.", Error::ErrorType::ERR_BAD_OPERATION_NAME);

    return false;
}

bool Parser::ParseType(Type *type)
{
    ASSERT(TypeValidName());

    std::string component_name(context_.GiveToken());
    size_t rank = 0;

    ++context_;

    while (*context_ == Token::Type::DEL_SQUARE_BRACKET_L) {
        ++context_;
        if (*context_ != Token::Type::DEL_SQUARE_BRACKET_R) {
            context_.err = GetError("Expected ']'.", Error::ErrorType::ERR_BAD_ARRAY_TYPE_BOUND);
            return false;
        }
        ++context_;
        ++rank;
    }

    *type = Type(component_name, rank);

    if (type->IsArray()) {
        program_.array_types.insert(*type);
    }

    return true;
}

bool Parser::ParseFieldType()
{
    LOG(DEBUG, ASSEMBLER) << "started searching field type value (line " << line_stric_
                          << "): " << context_.tokens[context_.number - 1].whole_line;

    if (!TypeValidName()) {
        context_.err = GetError("Not a correct type.", Error::ErrorType::ERR_BAD_FIELD_VALUE_TYPE);
        return false;
    }

    if (!ParseType(&curr_fld_->type)) {
        return false;
    }

    curr_fld_->metadata->SetFieldType(curr_fld_->type);

    LOG(DEBUG, ASSEMBLER) << "field type found (line " << line_stric_ << "): " << context_.GiveToken();

    return true;
}

bool Parser::ParseRecordField()
{
    if (!ParseFieldType()) {
        return false;
    }

    if (context_.Mask()) {
        context_.err = GetError("Expected field name.", Error::ErrorType::ERR_BAD_FIELD_MISSING_NAME, +1);
        return false;
    }

    if (!ParseFieldName()) {
        return false;
    }

    if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
        curr_record_->body_location.end = GetCurrentPosition(true);
        ++context_;
        open_ = false;
        return true;
    }

    metadata_ = curr_fld_->metadata.get();
    ParseMetaDef();

    return context_.Mask();
}

bool Parser::IsConstArray()
{
    if ((curr_array_->literals_.size() >= INTRO_CONST_ARRAY_LITERALS_NUMBER) &&
        (curr_array_->literals_[0].tag_ == panda_file::LiteralTag::TAGVALUE)) {
        return true;
    }
    return false;
}

bool Parser::ArrayElementsValidNumber()
{
    if (!IsConstArray()) {
        return true;
    }

    ASSERT(curr_array_->literals_.size() > 1);

    auto init_size = std::get<uint32_t>(curr_array_->literals_[1].value_);
    if (init_size < 1) {
        return false;
    }
    if (curr_array_->literals_.size() != init_size + INTRO_CONST_ARRAY_LITERALS_NUMBER) {
        return false;
    }

    return true;
}

void Parser::ParseAsArray(const std::vector<Token> &tokens)
{
    LOG(DEBUG, ASSEMBLER) << "started parsing of array (line " << line_stric_ << "): " << tokens[0].whole_line;
    func_def_ = false;
    record_def_ = false;
    array_def_ = true;

    if (!open_) {
        ++context_;
    } else {
        context_.err =
            GetError("No one array can be defined inside another array.", Error::ErrorType::ERR_BAD_DEFINITION);
        return;
    }

    if (ParseArrayFullSign()) {
        if (!open_ && *context_ == Token::Type::DEL_BRACE_L) {
            ++context_;

            LOG(DEBUG, ASSEMBLER) << "array body is open, line " << line_stric_ << ": " << tokens[0].whole_line;

            open_ = true;
        }

        uint32_t iter_number = 1;
        if (IsConstArray()) {
            iter_number = std::get<uint32_t>(curr_array_->literals_[1].value_);
        }

        if (iter_number < 1) {
            context_.err =
                GetError("Сonstant array must contain at least one element.", Error::ErrorType::ERR_BAD_ARRAY_SIZE);
            return;
        }

        for (uint32_t i = 0; i < iter_number; i++) {
            if (open_ && !context_.Mask() && *context_ != Token::Type::DEL_BRACE_R) {
                ParseArrayElements();
            } else {
                break;
            }
        }

        if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
            if (!ArrayElementsValidNumber()) {
                context_.err =
                    GetError("Constant array must contain at least one element.", Error::ErrorType::ERR_BAD_ARRAY_SIZE);
                return;
            }
            LOG(DEBUG, ASSEMBLER) << "array body is closed, line " << line_stric_ << ": " << tokens[0].whole_line;

            ++context_;

            open_ = false;
        }
    }
}

bool Parser::ParseArrayElements()
{
    if (!open_ && *context_ == Token::Type::DEL_BRACE_L) {
        open_ = true;

        ++context_;
    }

    if (!open_) {
        context_.err = GetError("Expected keyword.", Error::ErrorType::ERR_BAD_KEYWORD);
        return false;
    }

    if (context_.Mask()) {
        return true;
    }

    if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
        if (!ArrayElementsValidNumber()) {
            context_.err =
                GetError("Constant array must contain at least one element.", Error::ErrorType::ERR_BAD_ARRAY_SIZE);
            return false;
        }
        ++context_;

        open_ = false;

        return true;
    }

    curr_array_->literals_.push_back(panda::pandasm::LiteralArray::Literal());
    curr_array_elem_ = &(curr_array_->literals_[curr_array_->literals_.size() - 1]);

    LOG(DEBUG, ASSEMBLER) << "parse line " << line_stric_ << " as array elem (.array_elem value)";
    if (!ParseArrayElement()) {
        if (context_.err.err != Error::ErrorType::ERR_NONE) {
            return false;
        }

        if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
            ++context_;
            open_ = false;

            if (!ArrayElementsValidNumber()) {
                context_.err =
                    GetError("Constant array must contain at least one element.", Error::ErrorType::ERR_BAD_ARRAY_SIZE);
                return false;
            }
        } else {
            context_.err =
                GetError("Expected a new array element on the next line.", Error::ErrorType::ERR_BAD_KEYWORD);
            return false;
        }
    }

    return true;
}

bool Parser::ParseArrayElement()
{
    if (IsConstArray()) {
        curr_array_elem_->tag_ =
            static_cast<panda_file::LiteralTag>(std::get<uint8_t>(curr_array_->literals_[0].value_));
    } else {
        if (!ParseArrayElementType()) {
            return false;
        }
    }

    if (context_.Mask()) {
        context_.err =
            GetError("Expected array element value.", Error::ErrorType::ERR_BAD_ARRAY_ELEMENT_MISSING_VALUE, +1);
        return false;
    }

    if (!ParseArrayElementValue()) {
        return false;
    }

    if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
        ++context_;
        open_ = false;

        if (!ArrayElementsValidNumber()) {
            context_.err =
                GetError("Constant array must contain at least one element.", Error::ErrorType::ERR_BAD_ARRAY_SIZE);
            return false;
        }
        return true;
    }

    return IsConstArray() ? true : context_.Mask();
}

bool Parser::ParseArrayElementType()
{
    LOG(DEBUG, ASSEMBLER) << "started searching array element type value (line " << line_stric_
                          << "): " << context_.tokens[context_.number - 1].whole_line;

    if (!TypeValidName()) {
        context_.err = GetError("Not a correct type.", Error::ErrorType::ERR_BAD_ARRAY_ELEMENT_VALUE_TYPE);
        return false;
    }

    Type type;
    if (!ParseType(&type)) {
        return false;
    }

    // workaround until #5776 is done
    auto type_name = type.GetName();
    std::replace(type_name.begin(), type_name.end(), '.', '/');
    auto type_with_slash = Type(type_name, 0);

    if (panda::pandasm::Type::IsPandaPrimitiveType(type.GetName())) {
        curr_array_elem_->tag_ = panda::pandasm::LiteralArray::GetArrayTagFromComponentType(type.GetId());
        if (program_.array_types.find(type) == program_.array_types.end()) {
            program_.array_types.emplace(type, 1);
        }
    } else if (panda::pandasm::Type::IsStringType(type_with_slash.GetName(), program_.lang)) {
        curr_array_elem_->tag_ = panda_file::LiteralTag::ARRAY_STRING;
        if (program_.array_types.find(type_with_slash) == program_.array_types.end()) {
            program_.array_types.emplace(type_with_slash, 1);
        }
    } else {
        return false;
    }

    LOG(DEBUG, ASSEMBLER) << "array element type found (line " << line_stric_ << "): " << context_.GiveToken();

    return true;
}

bool Parser::ParseArrayElementValueInteger()
{
    int64_t n;
    if (!ParseInteger(&n)) {
        context_.err =
            GetError("Invalid value of array integer element.", Error::ErrorType::ERR_BAD_ARRAY_ELEMENT_VALUE_INTEGER);
        return false;
    }
    if (curr_array_elem_->IsBoolValue()) {
        curr_array_elem_->value_ = static_cast<bool>(n);
    }
    if (curr_array_elem_->IsByteValue()) {
        curr_array_elem_->value_ = static_cast<uint8_t>(n);
    }
    if (curr_array_elem_->IsShortValue()) {
        curr_array_elem_->value_ = static_cast<uint16_t>(n);
    }
    if (curr_array_elem_->IsIntegerValue()) {
        curr_array_elem_->value_ = static_cast<uint32_t>(n);
    }
    if (curr_array_elem_->IsLongValue()) {
        curr_array_elem_->value_ = static_cast<uint64_t>(n);
    }
    return true;
}

bool Parser::ParseArrayElementValueFloat()
{
    double n;
    if (!ParseFloat(&n, !curr_array_elem_->IsFloatValue())) {
        context_.err =
            GetError("Invalid value of array float element.", Error::ErrorType::ERR_BAD_ARRAY_ELEMENT_VALUE_FLOAT);
        return false;
    }
    if (curr_array_elem_->IsFloatValue()) {
        curr_array_elem_->value_ = static_cast<float>(n);
    } else {
        curr_array_elem_->value_ = static_cast<double>(n);
    }
    return true;
}

bool Parser::ParseArrayElementValueString()
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    auto res = ParseStringLiteral();
    if (!res) {
        context_.err =
            GetError("Invalid value of array string element.", Error::ErrorType::ERR_BAD_ARRAY_ELEMENT_VALUE_STRING);
        return false;
    }
    curr_array_elem_->value_ = res.value();
    return true;
}

bool Parser::ParseArrayElementValue()
{
    if (curr_array_elem_->IsBoolValue() || curr_array_elem_->IsByteValue() || curr_array_elem_->IsShortValue() ||
        curr_array_elem_->IsIntegerValue() || curr_array_elem_->IsLongValue()) {
        if (!ParseArrayElementValueInteger()) {
            return false;
        }
    }
    if (curr_array_elem_->IsFloatValue() || curr_array_elem_->IsDoubleValue()) {
        if (!ParseArrayElementValueFloat()) {
            return false;
        }
    }
    if (curr_array_elem_->IsStringValue()) {
        if (!ParseArrayElementValueString()) {
            return false;
        }
    }

    ++context_;

    return true;
}

bool Parser::ParseFunctionCode()
{
    if (!open_ && *context_ == Token::Type::DEL_BRACE_L) {
        open_ = true;
        curr_func_->body_location.begin = GetCurrentPosition(false);
        ++context_;
    }

    curr_func_->body_presence = true;

    if (!open_) {
        context_.err = GetError("Expected keyword.", Error::ErrorType::ERR_BAD_KEYWORD);
        return false;
    }

    if (context_.Mask()) {
        return true;
    }

    if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
        curr_func_->body_location.end = GetCurrentPosition(true);
        ++context_;
        open_ = false;
        return true;
    }

    curr_ins_ = &curr_func_->ins.emplace_back();

    LOG(DEBUG, ASSEMBLER) << "parse line " << line_stric_
                          << " as instruction ([label:] operation [operand,] [# comment])";

    ParseFunctionInstruction();

    if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
        curr_func_->body_location.end = GetCurrentPosition(true);
        ++context_;
        open_ = false;
    }

    return true;
}

void Parser::ParseAsRecord(const std::vector<Token> &tokens)
{
    LOG(DEBUG, ASSEMBLER) << "started parsing of record (line " << line_stric_ << "): " << tokens[0].whole_line;
    func_def_ = false;
    record_def_ = true;
    array_def_ = false;

    if (!open_) {
        ++context_;
    } else {
        context_.err =
            GetError("No one record can be defined inside another record.", Error::ErrorType::ERR_BAD_DEFINITION);
        return;
    }

    if (ParseRecordFullSign()) {
        metadata_ = curr_record_->metadata.get();
        if (ParseMetaDef()) {
            if (!open_ && *context_ == Token::Type::DEL_BRACE_L) {
                curr_record_->body_location.begin = GetCurrentPosition(false);
                ++context_;

                LOG(DEBUG, ASSEMBLER) << "record body is open, line " << line_stric_ << ": " << tokens[0].whole_line;

                open_ = true;
            }

            if (open_ && !context_.Mask() && *context_ != Token::Type::DEL_BRACE_R) {
                ParseRecordFields();
            } else if (open_) {
                curr_record_->body_presence = true;
            }

            if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
                LOG(DEBUG, ASSEMBLER) << "record body is closed, line " << line_stric_ << ": " << tokens[0].whole_line;

                curr_record_->body_location.end = GetCurrentPosition(true);
                ++context_;

                open_ = false;
            }
        }
    }
}

void Parser::ParseAsFunction(const std::vector<Token> &tokens)
{
    LOG(DEBUG, ASSEMBLER) << "started parsing of function (line " << line_stric_ << "): " << tokens[0].whole_line;
    record_def_ = false;
    func_def_ = true;
    array_def_ = false;

    if (!open_) {
        ++context_;
    } else {
        context_.err =
            GetError("No one function can be defined inside another function.", Error::ErrorType::ERR_BAD_DEFINITION);
        return;
    }

    if (ParseFunctionFullSign()) {
        metadata_ = curr_func_->metadata.get();
        if (ParseMetaDef()) {
            if (!open_ && *context_ == Token::Type::DEL_BRACE_L) {
                curr_func_->body_location.begin = GetCurrentPosition(false);
                ++context_;

                LOG(DEBUG, ASSEMBLER) << "function body is open, line " << line_stric_ << ": " << tokens[0].whole_line;

                open_ = true;
            }

            if (open_ && !context_.Mask() && *context_ != Token::Type::DEL_BRACE_R) {
                ParseFunctionCode();
            } else if (open_) {
                curr_func_->body_presence = true;
            }

            if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
                LOG(DEBUG, ASSEMBLER) << "function body is closed, line " << line_stric_ << ": "
                                      << tokens[0].whole_line;

                curr_func_->body_location.end = GetCurrentPosition(true);
                ++context_;
                open_ = false;
            }
        }
    }
}

void Parser::ParseAsBraceRight(const std::vector<Token> &tokens)
{
    if (!open_) {
        context_.err =
            GetError("Delimiter '}' for the code area is outside a function.", Error::ErrorType::ERR_BAD_BOUND);
        return;
    }

    LOG(DEBUG, ASSEMBLER) << "body is closed (line " << line_stric_ << "): " << tokens[0].whole_line;

    open_ = false;
    if (func_def_) {
        curr_func_->body_location.end = GetCurrentPosition(true);
    } else if (record_def_) {
        curr_record_->body_location.end = GetCurrentPosition(true);
    } else if (array_def_) {
        if (!ArrayElementsValidNumber()) {
            context_.err =
                GetError("Constant array must contain at least one element.", Error::ErrorType::ERR_BAD_ARRAY_SIZE);
            return;
        }
    } else {
        LOG(FATAL, ASSEMBLER) << "Internal error: either function or record must be parsed here";
    }
    ++context_;
}

void Parser::ParseResetFunctionLabelsAndParams()
{
    if (open_ || err_.err != Error::ErrorType::ERR_NONE) {
        return;
    }

    for (const auto &f : program_.function_table) {
        for (const auto &k : f.second.label_table) {
            if (!k.second.file_location->is_defined) {
                context_.err = Error("This label does not exist.", line_stric_, Error::ErrorType::ERR_BAD_LABEL_EXT, "",
                                     k.second.file_location->bound_left, k.second.file_location->bound_right,
                                     k.second.file_location->whole_line);
                SetError();
            }
        }
    }

    for (const auto &t : context_.function_arguments_lists) {
        curr_func_ = &(program_.function_table.at(t.first));
        curr_func_->regs_num = static_cast<size_t>(curr_func_->value_of_first_param + 1);

        for (const auto &v : t.second) {
            if (!curr_func_->ins.empty() && curr_func_->ins.size() >= v.first &&
                !curr_func_->ins[v.first - 1].regs.empty()) {
                curr_func_->ins[v.first - 1].regs[v.second] +=
                    static_cast<uint16_t>(curr_func_->value_of_first_param + 1);
                size_t max_reg_number = (1 << curr_func_->ins[v.first - 1].MaxRegEncodingWidth());
                if (curr_func_->ins[v.first - 1].regs[v.second] >= max_reg_number) {
                    const auto &debug = curr_func_->ins[v.first - 1].ins_debug;
                    context_.err =
                        Error("Register width mismatch.", debug.line_number, Error::ErrorType::ERR_BAD_NAME_REG, "",
                              debug.bound_left, debug.bound_right, debug.whole_line);
                    SetError();
                    break;
                }
            }
        }
    }
}

void Parser::ParseResetFunctionTable()
{
    for (auto &k : program_.function_table) {
        if (!k.second.file_location->is_defined) {
            context_.err = Error("This function does not exist.", k.second.file_location->line_number,
                                 Error::ErrorType::ERR_BAD_ID_FUNCTION, "", k.second.file_location->bound_left,
                                 k.second.file_location->bound_right, k.second.file_location->whole_line);
            SetError();
        } else {
            for (auto insn_it = k.second.ins.begin(); insn_it != k.second.ins.end(); ++insn_it) {
                if (!(insn_it->IsCall() || insn_it->IsCallRange())) {
                    continue;
                }

                size_t diff = 1;
                auto func_name = insn_it->ids[0];

                if (!IsSignatureOrMangled(func_name)) {
                    const auto it_synonym = program_.function_synonyms.find(func_name);
                    if (it_synonym == program_.function_synonyms.end()) {
                        continue;
                    } else if (it_synonym->second.size() > 1) {
                        const auto &debug = insn_it->ins_debug;
                        context_.err = Error("Unable to resolve ambiguous function call", debug.line_number,
                                             Error::ErrorType::ERR_FUNCTION_MULTIPLE_ALTERNATIVES, "", debug.bound_left,
                                             debug.bound_right, debug.whole_line);
                        SetError();
                        break;
                    } else {
                        insn_it->ids[0] = program_.function_synonyms.at(func_name)[0];
                    }
                }

                if (insn_it->OperandListLength() - diff < program_.function_table.at(insn_it->ids[0]).GetParamsNum()) {
                    if (insn_it->IsCallRange() &&
                        (static_cast<int>(insn_it->regs.size()) - static_cast<int>(diff) >= 0)) {
                        continue;
                    }

                    const auto &debug = insn_it->ins_debug;
                    context_.err = Error("Function argument mismatch.", debug.line_number,
                                         Error::ErrorType::ERR_FUNCTION_ARGUMENT_MISMATCH, "", debug.bound_left,
                                         debug.bound_right, debug.whole_line);
                    SetError();
                }
            }
        }
    }
}

void Parser::ParseResetRecordTable()
{
    for (const auto &k : program_.record_table) {
        if (!k.second.file_location->is_defined) {
            context_.err = Error("This record does not exist.", k.second.file_location->line_number,
                                 Error::ErrorType::ERR_BAD_ID_RECORD, "", k.second.file_location->bound_left,
                                 k.second.file_location->bound_right, k.second.file_location->whole_line);
            SetError();
        } else if (k.second.HasImplementation() != k.second.body_presence) {
            context_.err = Error("Inconsistency of the definition of the record and its metadata.",
                                 k.second.file_location->line_number, Error::ErrorType::ERR_BAD_DEFINITION_RECORD, "",
                                 k.second.file_location->bound_left, k.second.file_location->bound_right,
                                 k.second.file_location->whole_line);
            SetError();
        } else {
            for (const auto &fld : k.second.field_list) {
                if (!fld.is_defined) {
                    context_.err =
                        Error("This field does not exist.", fld.line_of_def, Error::ErrorType::ERR_BAD_ID_FIELD, "",
                              fld.bound_left, fld.bound_right, fld.whole_line);
                    SetError();
                }
            }
        }
    }
}
void Parser::ParseResetTables()
{
    if (err_.err != Error::ErrorType::ERR_NONE) {
        return;
    }

    ParseResetFunctionTable();

    if (err_.err != Error::ErrorType::ERR_NONE) {
        return;
    }

    ParseResetRecordTable();
}

void Parser::ParseAsLanguageDirective()
{
    ++context_;

    if (context_.Mask()) {
        context_.err = GetError("Incorrect .language directive: Expected language",
                                Error::ErrorType::ERR_BAD_DIRECTIVE_DECLARATION);
        return;
    }

    auto lang = context_.GiveToken();
    auto res = panda::panda_file::LanguageFromString(lang);
    if (!res) {
        context_.err =
            GetError("Incorrect .language directive: Unknown language", Error::ErrorType::ERR_UNKNOWN_LANGUAGE);
        return;
    }

    ++context_;

    if (!context_.Mask()) {
        context_.err = GetError("Incorrect .language directive: Unexpected token",
                                Error::ErrorType::ERR_BAD_DIRECTIVE_DECLARATION);
    }

    program_.lang = res.value();
}

Function::CatchBlock Parser::PrepareCatchBlock(bool is_catchall, size_t size, size_t catchall_tokens_num,
                                               size_t catch_tokens_num)
{
    constexpr size_t TRY_BEGIN = 0;
    constexpr size_t TRY_END = 1;
    constexpr size_t CATCH_BEGIN = 2;
    constexpr size_t CATCH_END = 3;

    Function::CatchBlock catch_block;
    catch_block.whole_line = context_.tokens[0].whole_line;
    std::vector<std::string> label_names {"try block begin", "try block end", "catch block begin"};
    std::vector<std::string> labels;
    bool full_catch_block = (is_catchall && size == catchall_tokens_num) || (!is_catchall && size == catch_tokens_num);
    if (full_catch_block) {
        label_names.emplace_back("catch block end");
    }
    if (!is_catchall) {
        catch_block.exception_record = context_.GiveToken();
        ++context_;
    }

    bool skip_comma = is_catchall;
    for (auto label_name : label_names) {
        if (!skip_comma) {
            if (*context_ != Token::Type::DEL_COMMA) {
                context_.err = GetError("Expected comma.", Error::ErrorType::ERR_BAD_DIRECTIVE_DECLARATION);
                return catch_block;
            }
            ++context_;
        }
        skip_comma = false;
        if (!LabelValidName()) {
            context_.err =
                GetError(std::string("Invalid name of the ") + label_name + " label.", Error::ErrorType::ERR_BAD_LABEL);
            return catch_block;
        }
        labels.emplace_back(context_.GiveToken());
        AddObjectInTable(false, *label_table_);
        ++context_;
    }

    ASSERT(context_.Mask());
    catch_block.try_begin_label = labels[TRY_BEGIN];
    catch_block.try_end_label = labels[TRY_END];
    catch_block.catch_begin_label = labels[CATCH_BEGIN];
    if (full_catch_block) {
        catch_block.catch_end_label = labels[CATCH_END];
    } else {
        catch_block.catch_end_label = labels[CATCH_BEGIN];
    }
    return catch_block;
}

void Parser::ParseAsCatchDirective()
{
    ASSERT(*context_ == Token::Type::ID_CATCH || *context_ == Token::Type::ID_CATCHALL);

    constexpr size_t CATCH_DIRECTIVE_TOKENS_NUM = 8;
    constexpr size_t CATCHALL_DIRECTIVE_TOKENS_NUM = 6;
    constexpr size_t CATCH_FULL_DIRECTIVE_TOKENS_NUM = 10;
    constexpr size_t CATCHALL_FULL_DIRECTIVE_TOKENS_NUM = 8;

    bool is_catchall = *context_ == Token::Type::ID_CATCHALL;
    size_t size = context_.tokens.size();
    if (is_catchall && size != CATCHALL_DIRECTIVE_TOKENS_NUM && size != CATCHALL_FULL_DIRECTIVE_TOKENS_NUM) {
        context_.err = GetError(
            "Incorrect catch block declaration. Must be in the format: .catchall <try_begin_label>, <try_end_label>, "
            "<catch_begin_label>[, <catch_end_label>]",
            Error::ErrorType::ERR_BAD_DIRECTIVE_DECLARATION);
        return;
    }

    if (!is_catchall && size != CATCH_DIRECTIVE_TOKENS_NUM && size != CATCH_FULL_DIRECTIVE_TOKENS_NUM) {
        context_.err = GetError(
            "Incorrect catch block declaration. Must be in the format: .catch <exception_record>, <try_begin_label>, "
            "<try_end_label>, <catch_begin_label>[, <catch_end_label>]",
            Error::ErrorType::ERR_BAD_DIRECTIVE_DECLARATION);
        return;
    }

    ++context_;

    if (!is_catchall && !RecordValidName()) {
        context_.err = GetError("Invalid name of the exception record.", Error::ErrorType::ERR_BAD_RECORD_NAME);
        return;
    }

    Function::CatchBlock catch_block =
        PrepareCatchBlock(is_catchall, size, CATCHALL_FULL_DIRECTIVE_TOKENS_NUM, CATCH_FULL_DIRECTIVE_TOKENS_NUM);

    curr_func_->catch_blocks.push_back(catch_block);
}

void Parser::ParseAsCatchall(const std::vector<Token> &tokens)
{
    std::string directive_name = *context_ == Token::Type::ID_CATCH ? ".catch" : ".catchall";
    if (!func_def_) {
        context_.err = GetError(directive_name + " directive is located outside of a function body.",
                                Error::ErrorType::ERR_INCORRECT_DIRECTIVE_LOCATION);
        return;
    }

    LOG(DEBUG, ASSEMBLER) << "started parsing of " << directive_name << " directive (line " << line_stric_
                          << "): " << tokens[0].whole_line;

    ParseAsCatchDirective();
}

void Parser::ParseAsLanguage(const std::vector<Token> &tokens, bool &is_lang_parsed, bool &is_first_statement)
{
    if (is_lang_parsed) {
        context_.err = GetError("Multiple .language directives", Error::ErrorType::ERR_MULTIPLE_DIRECTIVES);
        return;
    }

    if (!is_first_statement) {
        context_.err = GetError(".language directive must be specified before any other declarations",
                                Error::ErrorType::ERR_INCORRECT_DIRECTIVE_LOCATION);
        return;
    }

    LOG(DEBUG, ASSEMBLER) << "started parsing of .language directive (line " << line_stric_
                          << "): " << tokens[0].whole_line;

    ParseAsLanguageDirective();

    is_lang_parsed = true;
}

bool Parser::ParseAfterLine(bool &is_first_statement)
{
    SetError();

    if (!context_.Mask() && err_.err == Error::ErrorType::ERR_NONE) {
        context_.err = GetError("There can be nothing after.", Error::ErrorType::ERR_BAD_END);
    }

    if (err_.err != Error::ErrorType::ERR_NONE) {
        LOG(DEBUG, ASSEMBLER) << "processing aborted (error detected)";
        return false;
    }

    LOG(DEBUG, ASSEMBLER) << "parsing of line " << line_stric_ << " is successful";

    SetError();

    is_first_statement = false;

    return true;
}

Expected<Program, Error> Parser::ParseAfterMainLoop(const std::string &file_name)
{
    ParseResetFunctionLabelsAndParams();

    if (open_ && err_.err == Error::ErrorType::ERR_NONE) {
        context_.err = Error("Code area is not closed.", curr_func_->file_location->line_number,
                             Error::ErrorType::ERR_BAD_CLOSE, "", 0, curr_func_->name.size(), curr_func_->name);
        SetError();
    }

    ParseResetTables();

    if (err_.err != Error::ErrorType::ERR_NONE) {
        return Unexpected(err_);
    }

    for (auto &func : program_.function_table) {
        if (func.second.HasImplementation()) {
            func.second.source_file = file_name;
        }
    }

    for (auto &rec : program_.record_table) {
        if (rec.second.HasImplementation()) {
            rec.second.source_file = file_name;
        }
    }

    return std::move(program_);
}

Expected<Program, Error> Parser::Parse(TokenSet &vectors_tokens, const std::string &file_name)
{
    bool is_lang_parsed = false;
    bool is_first_statement = true;

    for (const auto &tokens : vectors_tokens) {
        ++line_stric_;

        if (tokens.empty()) {
            continue;
        }

        LOG(DEBUG, ASSEMBLER) << "started parsing of line " << line_stric_ << ": " << tokens[0].whole_line;

        context_.Make(tokens);

        switch (*context_) {
            case Token::Type::ID_CATCH:
            case Token::Type::ID_CATCHALL: {
                ParseAsCatchall(tokens);
                break;
            }
            case Token::Type::ID_LANG: {
                ParseAsLanguage(tokens, is_lang_parsed, is_first_statement);
                break;
            }
            case Token::Type::ID_REC: {
                ParseAsRecord(tokens);
                break;
            }
            case Token::Type::ID_FUN: {
                ParseAsFunction(tokens);
                break;
            }
            case Token::Type::ID_ARR: {
                ParseAsArray(tokens);
                break;
            }
            case Token::Type::DEL_BRACE_R: {
                ParseAsBraceRight(tokens);
                break;
            }
            default: {
                if (func_def_) {
                    ParseFunctionCode();
                } else if (record_def_) {
                    ParseRecordFields();
                } else if (array_def_) {
                    ParseArrayElements();
                }
            }
        }
        if (!ParseAfterLine(is_first_statement)) {
            break;
        }
    }

    return ParseAfterMainLoop(file_name);
}

Expected<Program, Error> Parser::Parse(const std::string &source, const std::string &file_name)
{
    auto ss = std::stringstream(source);
    std::string line;

    Lexer l;
    std::vector<std::vector<Token>> v;

    while (std::getline(ss, line)) {
        auto [tokens, error] = l.TokenizeString(line);
        if (error.err != Error::ErrorType::ERR_NONE) {
            return Unexpected(error);
        }

        v.push_back(tokens);
    }

    return Parse(v, file_name);
}

void Parser::SetError()
{
    err_ = context_.err;
}

bool Parser::RegValidName()
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    if (curr_func_->GetParamsNum() > 0) {
        return context_.ValidateRegisterName('v') || context_.ValidateRegisterName('a', curr_func_->GetParamsNum() - 1);
    }

    return context_.ValidateRegisterName('v');
}

bool Parser::ParamValidName()
{
    return context_.ValidateParameterName(curr_func_->GetParamsNum());
}

bool IsAlphaNumeric(char c)
{
    return std::isalnum(c) != 0 || c == '_';
}

bool IsNonDigit(char c)
{
    return std::isalpha(c) != 0 || c == '_';
}

bool Parser::PrefixedValidName()
{
    auto s = context_.GiveToken();

    if (!IsNonDigit(s[0])) {
        return false;
    }

    size_t i = 1;

    while (i < s.size()) {
        if (s[i] == '.') {
            ++i;
            if (i >= s.size() || !IsNonDigit(s[i])) {
                return false;
            }
        } else if (!IsAlphaNumeric(s[i]) && s[i] != '$') {
            return false;
        }

        ++i;
    }

    return true;
}

bool Parser::RecordValidName()
{
    return PrefixedValidName();
}

bool Parser::FunctionValidName()
{
    return PrefixedValidName();
}

bool Parser::ArrayValidName()
{
    return PrefixedValidName();
}

bool Parser::LabelValidName()
{
    auto token = context_.GiveToken();

    if (!IsNonDigit(token[0])) {
        return false;
    }

    token.remove_prefix(1);

    for (auto i : token) {
        if (!IsAlphaNumeric(i)) {
            return false;
        }
    }

    return true;
}

bool Parser::ParseLabel()
{
    LOG(DEBUG, ASSEMBLER) << "label search started (line " << line_stric_ << "): " << context_.tokens[0].whole_line;

    context_++;

    if (*context_ == Token::Type::DEL_COLON) {
        context_--;
        if (LabelValidName()) {
            if (AddObjectInTable(true, *label_table_)) {
                curr_ins_->set_label = true;
                curr_ins_->label = context_.GiveToken();

                LOG(DEBUG, ASSEMBLER) << "label detected (line " << line_stric_ << "): " << context_.GiveToken();

                context_++;
                context_++;
                return !context_.Mask();
            }

            LOG(DEBUG, ASSEMBLER) << "label is detected (line " << line_stric_ << "): " << context_.GiveToken()
                                  << ", but this label already exists";

            context_.err = GetError("This label already exists.", Error::ErrorType::ERR_BAD_LABEL_EXT);

        } else {
            LOG(DEBUG, ASSEMBLER) << "label with non-standard character is detected, attempt to create a label is "
                                     "supposed, but this cannot be any label name (line "
                                  << line_stric_ << "): " << context_.GiveToken();

            context_.err = GetError(
                "Invalid name of label. Label contains only characters: '_', '0' - '9', 'a' - 'z', 'A' - 'Z'; and "
                "starts with any letter or with '_'.",
                Error::ErrorType::ERR_BAD_LABEL);
        }

        return false;
    }

    context_--;

    LOG(DEBUG, ASSEMBLER) << "label is not detected (line " << line_stric_ << ")";

    return true;
}

static Opcode TokenToOpcode(Token::Type id)
{
    ASSERT(id > Token::Type::OPERATION && id < Token::Type::KEYWORD);
    using utype = std::underlying_type_t<Token::Type>;
    return static_cast<Opcode>(static_cast<utype>(id) - static_cast<utype>(Token::Type::OPERATION) - 1);
}

bool Parser::ParseOperation()
{
    if (context_.Mask()) {
        LOG(DEBUG, ASSEMBLER) << "no more tokens (line " << line_stric_ << "): " << context_.tokens[0].whole_line;

        return false;
    }

    if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
        return false;
    }

    LOG(DEBUG, ASSEMBLER) << "operaion search started (line " << line_stric_ << "): " << context_.tokens[0].whole_line;

    if (*context_ > Token::Type::OPERATION && *context_ < Token::Type::KEYWORD) {
        SetOperationInformation();

        context_.UpSignOperation();
        curr_ins_->opcode = TokenToOpcode(context_.id);

        LOG(DEBUG, ASSEMBLER) << "operatiuon is detected (line " << line_stric_ << "): " << context_.GiveToken()
                              << " (operand type: " << OperandTypePrint(curr_ins_->opcode) << ")";

        context_++;
        return true;
    }

    LOG(DEBUG, ASSEMBLER) << "founded " << context_.GiveToken() << ", it is not an operation (line " << line_stric_
                          << ")";

    context_.err = GetError("Invalid operation.", Error::ErrorType::ERR_BAD_OPERATION_NAME);

    return false;
}

bool Parser::ParseOperandVreg()
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    if (*context_ != Token::Type::ID) {
        context_.err = GetError("Expected register.", Error::ErrorType::ERR_BAD_OPERAND, +1);
        return false;
    }

    std::string_view p = context_.GiveToken();

    if (p[0] == 'v') {
        p.remove_prefix(1);
        int64_t number = static_cast<int64_t>(ToNumber(p));

        if (number > *(context_.max_value_of_reg)) {
            *(context_.max_value_of_reg) = number;
        }

        curr_ins_->regs.push_back(static_cast<uint16_t>(number));
    } else if (p[0] == 'a') {
        p.remove_prefix(1);
        curr_ins_->regs.push_back(static_cast<uint16_t>(ToNumber(p)));
        context_.function_arguments_list->emplace_back(context_.ins_number, curr_ins_->regs.size() - 1);
    }

    ++context_;
    return true;
}

bool Parser::ParseOperandCall()
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    if (!FunctionValidName()) {
        context_.err = GetError("Invalid name of function.", Error::ErrorType::ERR_BAD_NAME_REG);
        return false;
    }

    const auto p = std::string(context_.GiveToken().data(), context_.GiveToken().length());
    curr_ins_->ids.emplace_back(p);

    AddObjectInTable(false, program_.function_table);

    ++context_;

    std::string func_signature {};

    if (!ParseOperandSignature(&func_signature)) {
        return false;
    }

    if (func_signature.empty()) {
        const auto it_synonym = program_.function_synonyms.find(curr_ins_->ids.back());
        if (it_synonym == program_.function_synonyms.end()) {
            return true;
        }

        if (it_synonym->second.size() > 1) {
            context_.err = GetError("Unable to resolve ambiguous function call",
                                    Error::ErrorType::ERR_FUNCTION_MULTIPLE_ALTERNATIVES);
            return false;
        }

        program_.function_table.erase(p);
    } else {
        curr_ins_->ids.back() += func_signature;

        if (program_.function_table.find(curr_ins_->ids.back()) == program_.function_table.end()) {
            auto node_handle = program_.function_table.extract(p);
            node_handle.key() = curr_ins_->ids.back();
            program_.function_table.insert(std::move(node_handle));
        } else {
            program_.function_table.erase(p);
        }
    }

    return true;
}

bool Parser::ParseOperandSignature(std::string *sign)
{
    if (*context_ != Token::Type::DEL_COLON) {
        // no signature provided
        return true;
    }

    ++context_;

    if (*context_ != Token::Type::DEL_BRACKET_L) {
        context_.err = GetError("Expected \'(\' before signature", Error::ErrorType::ERR_BAD_SIGNATURE);
        return false;
    }

    ++context_;

    *sign += ":(";

    if (!ParseOperandSignatureTypesList(sign)) {
        return false;
    }

    if (*context_ != Token::Type::DEL_BRACKET_R) {
        context_.err = GetError("Expected \')\' at the end of the signature", Error::ErrorType::ERR_BAD_SIGNATURE);
        return false;
    }

    *sign += ")";

    ++context_;

    return true;
}

bool Parser::ParseOperandSignatureTypesList(std::string *sign)
{
    bool comma = false;

    while (true) {
        if (context_.Mask()) {
            return true;
        }

        if (*context_ != Token::Type::DEL_COMMA && *context_ != Token::Type::ID) {
            break;
        }

        if (comma) {
            *sign += ",";
        }

        if (!ParseFunctionArgComma(comma)) {
            return false;
        }

        if (*context_ != Token::Type::ID) {
            context_.err = GetError("Expected signature arguments", Error::ErrorType::ERR_BAD_SIGNATURE_PARAMETERS);
            return false;
        }

        if (!TypeValidName()) {
            context_.err = GetError("Expected valid type", Error::ErrorType::ERR_BAD_TYPE);
            return false;
        }

        Type type;
        if (!ParseType(&type)) {
            return false;
        }

        *sign += type.GetName();
    }

    return true;
}

static bool IsOctal(char c)
{
    return c >= '0' && c <= '7';
}

static bool IsHex(char c)
{
    return std::isxdigit(c) != 0;
}

static uint8_t FromHex(char c)
{
    constexpr size_t DIGIT_NUM = 10;

    if (c >= '0' && c <= '9') {
        return c - '0';
    }

    if (c >= 'A' && c <= 'F') {
        return c - 'A' + DIGIT_NUM;
    }

    return c - 'a' + DIGIT_NUM;
}

static uint8_t FromOctal(char c)
{
    return c - '0';
}

Expected<char, Error> Parser::ParseOctalEscapeSequence(std::string_view s, size_t *i)
{
    constexpr size_t OCT_SHIFT = 3;

    size_t idx = *i;
    size_t n = 0;
    uint32_t r = 0;

    while (idx < s.length() && IsOctal(s[idx]) && n < OCT_SHIFT) {
        r |= FromOctal(s[idx++]);
        r <<= 3U;
        ++n;
    }

    r >>= 3U;
    *i += n;

    return r;
}

Expected<char, Error> Parser::ParseHexEscapeSequence(std::string_view s, size_t *i)
{
    constexpr size_t HEX_SHIFT = 2;

    uint32_t r = 0;
    size_t idx = *i;

    for (size_t j = 0; j < HEX_SHIFT; j++) {
        char v = s[(*i)++];

        if (!IsHex(v)) {
            return Unexpected(GetError("Invalid \\x escape sequence",
                                       Error::ErrorType::ERR_BAD_STRING_INVALID_HEX_ESCAPE_SEQUENCE, idx - HEX_SHIFT));
        }

        r |= FromHex(v);
        r <<= 4U;
    }

    r >>= 4U;

    return r;
}

Expected<char, Error> Parser::ParseEscapeSequence(std::string_view s, size_t *i)
{
    size_t idx = *i;

    char c = s[idx];

    if (IsOctal(c)) {
        return ParseOctalEscapeSequence(s, i);
    }

    ++(*i);

    switch (c) {
        case '\'':
        case '"':
        case '\\':
            return c;
        case 'a':
            return '\a';
        case 'b':
            return '\b';
        case 'f':
            return '\f';
        case 'n':
            return '\n';
        case 'r':
            return '\r';
        case 't':
            return '\t';
        case 'v':
            return '\v';
        default:
            break;
    }

    if (c == 'x') {
        return ParseHexEscapeSequence(s, i);
    }

    return Unexpected(
        GetError("Unknown escape sequence", Error::ErrorType::ERR_BAD_STRING_UNKNOWN_ESCAPE_SEQUENCE, idx - 1));
}

std::optional<std::string> Parser::ParseStringLiteral()
{
    auto token = context_.GiveToken();
    if (*context_ != Token::Type::ID_STRING) {
        context_.err = GetError("Expected string literal", Error::ErrorType::ERR_BAD_OPERAND);
        return {};
    }

    size_t i = 1; /* skip leading quote */
    size_t len = token.length();

    std::string s;

    while (i < len - 1) {
        char c = token[i++];

        if (c != '\\') {
            s.append(1, c);
            continue;
        }

        auto res = ParseEscapeSequence(token, &i);
        if (!res) {
            context_.err = res.Error();
            return {};
        }

        s.append(1, res.Value());
    }

    program_.strings.insert(s);

    return s;
}

bool Parser::ParseOperandString()
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    auto res = ParseStringLiteral();
    if (!res) {
        return false;
    }

    curr_ins_->ids.push_back(res.value());

    ++context_;

    return true;
}

bool Parser::ParseOperandComma()
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    if (context_++ != Token::Type::DEL_COMMA) {
        if (!context_.Mask() && *context_ != Token::Type::DEL_BRACKET_R) {
            --context_;
        }

        context_.err = GetError("Expected comma.", Error::ErrorType::ERR_BAD_NUMBER_OPERANDS);
        return false;
    }

    return true;
}

bool Parser::ParseInteger(int64_t *value)
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    if (*context_ != Token::Type::ID) {
        if (*context_ == Token::Type::DEL_BRACE_R) {
            --context_;
        }
        context_.err = GetError("Expected immediate.", Error::ErrorType::ERR_BAD_OPERAND, +1);
        return false;
    }

    std::string_view p = context_.GiveToken();
    if (!ValidateInteger(p)) {
        context_.err = GetError("Expected integer.", Error::ErrorType::ERR_BAD_INTEGER_NAME);
        return false;
    }

    *value = IntegerNumber(p);
    if (errno == ERANGE) {
        context_.err =
            GetError("Too large immediate (length is more than 64 bit).", Error::ErrorType::ERR_BAD_INTEGER_WIDTH);
        return false;
    }

    return true;
}

bool Parser::ParseFloat(double *value, bool is_64bit)
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    if (*context_ != Token::Type::ID) {
        if (*context_ == Token::Type::DEL_BRACE_R) {
            --context_;
        }
        context_.err = GetError("Expected immediate.", Error::ErrorType::ERR_BAD_OPERAND, +1);
        return false;
    }

    std::string_view p = context_.GiveToken();
    if (!ValidateFloat(p)) {
        context_.err = GetError("Expected float.", Error::ErrorType::ERR_BAD_FLOAT_NAME);
        return false;
    }

    *value = FloatNumber(p, is_64bit);
    if (errno == ERANGE) {
        context_.err =
            GetError("Too large immediate (length is more than 64 bit).", Error::ErrorType::ERR_BAD_FLOAT_WIDTH);
        return false;
    }

    return true;
}

bool Parser::ParseOperandInteger()
{
    int64_t n;
    if (!ParseInteger(&n)) {
        return false;
    }

    curr_ins_->imms.push_back(n);
    ++context_;
    return true;
}

bool Parser::ParseOperandFloat(bool is_64bit)
{
    double n;
    if (!ParseFloat(&n, is_64bit)) {
        return false;
    }

    curr_ins_->imms.push_back(n);
    ++context_;
    return true;
}

bool Parser::ParseOperandLabel()
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    if (!LabelValidName()) {
        context_.err = GetError("Invalid name of Label.", Error::ErrorType::ERR_BAD_NAME_ID);
        return false;
    }

    std::string_view p = context_.GiveToken();
    curr_ins_->ids.emplace_back(p.data(), p.length());
    AddObjectInTable(false, *label_table_);

    ++context_;

    return true;
}

bool Parser::ParseOperandId()
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    if (*context_ != Token::Type::ID) {
        context_.err = GetError("Expected Label.", Error::ErrorType::ERR_BAD_OPERAND);
        return false;
    }
    if (!LabelValidName()) {
        context_.err = GetError("Invalid name of Label.", Error::ErrorType::ERR_BAD_NAME_ID);
        return false;
    }

    std::string_view p = context_.GiveToken();
    curr_ins_->ids.emplace_back(p.data(), p.length());
    AddObjectInTable(false, *label_table_);

    ++context_;

    return true;
}

bool Parser::ParseOperandType(Type::VerificationType ver_type)
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    if (*context_ != Token::Type::ID) {
        context_.err = GetError("Expected type.", Error::ErrorType::ERR_BAD_OPERAND);
        return false;
    }
    if (!TypeValidName()) {
        context_.err = GetError("Invalid name of type.", Error::ErrorType::ERR_BAD_NAME_ID);
        return false;
    }

    Type type;
    if (!ParseType(&type)) {
        return false;
    }

    bool is_object = (context_.GiveToken() == "]") ? (false) : (true);

    if (is_object) {
        AddObjectInTable(false, program_.record_table);

        if (ver_type == Type::VerificationType::TYPE_ID_ARRAY) {
            GetWarning("Unexpected type_id recieved! Expected array, but object given",
                       Error::ErrorType::WAR_UNEXPECTED_TYPE_ID);
        }
    } else {
        if (!type.IsArrayContainsPrimTypes() &&
            program_.record_table.find(type.GetComponentName()) == program_.record_table.end()) {
            std::string ComponentName = type.GetComponentName();
            context_.token = ComponentName;
            AddObjectInTable(false, program_.record_table);
        }

        if (ver_type == Type::VerificationType::TYPE_ID_OBJECT) {
            GetWarning("Unexpected type_id recieved! Expected object, but array given",
                       Error::ErrorType::WAR_UNEXPECTED_TYPE_ID);
        }
    }

    curr_ins_->ids.push_back(type.GetName());

    return true;
}

bool Parser::ParseOperandLiteralArray()
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    if (*context_ != Token::Type::ID) {
        context_.err = GetError("Expected array id.", Error::ErrorType::ERR_BAD_OPERAND);
        return false;
    }
    if (!ArrayValidName()) {
        context_.err = GetError("Invalid name of array.", Error::ErrorType::ERR_BAD_NAME_ID);
        return false;
    }

    std::string_view p = context_.GiveToken();
    auto array_id = std::string(p.data(), p.length());

    if (program_.literalarray_table.find(array_id) == program_.literalarray_table.end()) {
        context_.err = GetError("No array was found for this array id", Error::ErrorType::ERR_BAD_ID_ARRAY);
        return false;
    }

    curr_ins_->ids.emplace_back(p.data(), p.length());

    ++context_;

    return true;
}

bool Parser::ParseOperandField()
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    if (*context_ != Token::Type::ID) {
        context_.err = GetError("Expected field.", Error::ErrorType::ERR_BAD_OPERAND);
        return false;
    }
    if (!PrefixedValidName()) {
        context_.err = GetError("Invalid name of field.", Error::ErrorType::ERR_BAD_NAME_ID);
        return false;
    }

    std::string_view p = context_.GiveToken();
    std::string record_full_name = std::string(p);
    // Some names of records in pandastdlib starts with 'panda.', therefore,
    // the record name is before the second dot, and the field name is after the second dot
    auto pos_point = record_full_name.find_last_of('.');
    std::string record_name = record_full_name.substr(0, pos_point);
    std::string field_name = record_full_name.substr(pos_point + 1);

    auto it_record = program_.record_table.find(record_name);
    if (it_record == program_.record_table.end()) {
        context_.token = record_name;
        AddObjectInTable(false, program_.record_table);
        it_record = program_.record_table.find(record_name);
    }

    auto it_field = std::find_if(it_record->second.field_list.begin(), it_record->second.field_list.end(),
                                 [&field_name](pandasm::Field &field) { return field_name == field.name; });

    if (!field_name.empty() && it_field == it_record->second.field_list.end()) {
        it_record->second.field_list.emplace_back(program_.lang);
        auto &field = it_record->second.field_list.back();
        field.name = field_name;
        field.line_of_def = line_stric_;
        field.whole_line = context_.tokens[context_.number - 1].whole_line;
        field.bound_left = context_.tokens[context_.number - 1].bound_left + record_name.length() + 1;
        field.bound_right = context_.tokens[context_.number - 1].bound_right;
        field.is_defined = false;
    }

    curr_ins_->ids.emplace_back(p.data(), p.length());

    ++context_;

    return true;
}

bool Parser::ParseOperandNone()
{
    if (context_.err.err != Error::ErrorType::ERR_NONE) {
        return false;
    }

    if (open_ && *context_ == Token::Type::DEL_BRACE_R) {
        return false;
    }

    if (!context_.Mask()) {
        context_.err = GetError("Invalid number of operands.", Error::ErrorType::ERR_BAD_NUMBER_OPERANDS);
        --context_;
        return false;
    }
    return true;
}

bool Parser::ParseRecordFullSign()
{
    return ParseRecordName();
}

bool Parser::ParseFunctionFullSign()
{
    if (!ParseFunctionReturn()) {
        return false;
    }

    if (!ParseFunctionName()) {
        return false;
    }

    if (*context_ == Token::Type::DEL_BRACKET_L) {
        ++context_;

        if (ParseFunctionArgs()) {
            if (*context_ == Token::Type::DEL_BRACKET_R) {
                ++context_;

                return UpdateFunctionName();
            }
            context_.err = GetError("Expected ')'.", Error::ErrorType::ERR_BAD_ARGS_BOUND);
        }
    } else {
        context_.err = GetError("Expected '('.", Error::ErrorType::ERR_BAD_ARGS_BOUND);
    }

    return false;
}

bool Parser::UpdateFunctionName()
{
    auto signature = GetFunctionSignatureFromName(curr_func_->name, curr_func_->params);
    auto iter = program_.function_table.find(signature);

    if (iter == program_.function_table.end() || !iter->second.file_location->is_defined) {
        program_.function_synonyms[curr_func_->name].push_back(signature);
        program_.function_table.erase(signature);
        auto node_handle = program_.function_table.extract(curr_func_->name);
        node_handle.key() = signature;
        program_.function_table.insert(std::move(node_handle));
        curr_func_->name = signature;
        context_.max_value_of_reg = &(curr_func_->value_of_first_param);
        context_.function_arguments_list = &(context_.function_arguments_lists[curr_func_->name]);

        return true;
    }

    context_.err = GetError("This function already exists.", Error::ErrorType::ERR_BAD_ID_FUNCTION);

    return false;
}

bool Parser::ParseArrayFullSign()
{
    if (!ParseArrayName()) {
        return false;
    }

    if (*context_ == Token::Type::DEL_BRACE_L) {
        return true;
    }

    curr_array_->literals_.push_back(panda::pandasm::LiteralArray::Literal());
    curr_array_elem_ = &(curr_array_->literals_[curr_array_->literals_.size() - 1]);

    if (!ParseArrayElementType()) {
        context_.err = GetError("Invalid array type for static array.", Error::ErrorType::ERR_BAD_ARRAY_TYPE);
        return false;
    }

    curr_array_elem_->value_ = static_cast<uint8_t>(curr_array_elem_->tag_);
    curr_array_elem_->tag_ = panda_file::LiteralTag::TAGVALUE;

    if (*context_ == Token::Type::DEL_BRACE_L) {
        context_.err = GetError("No array size for static array.", Error::ErrorType::ERR_BAD_ARRAY_SIZE);
        return false;
    }

    curr_array_->literals_.push_back(panda::pandasm::LiteralArray::Literal());
    curr_array_elem_ = &(curr_array_->literals_[curr_array_->literals_.size() - 1]);
    curr_array_elem_->tag_ = panda_file::LiteralTag::INTEGER;

    if (!ParseArrayElementValueInteger()) {
        context_.err =
            GetError("Invalid value for static array size value.", Error::ErrorType::ERR_BAD_ARRAY_SIZE_VALUE);
        return false;
    }

    ++context_;

    return true;
}

bool Parser::ParseRecordName()
{
    LOG(DEBUG, ASSEMBLER) << "started searching for record name (line " << line_stric_
                          << "): " << context_.tokens[context_.number - 1].whole_line;

    if (!RecordValidName()) {
        if (*context_ == Token::Type::DEL_BRACKET_L) {
            context_.err = GetError("No record name.", Error::ErrorType::ERR_BAD_RECORD_NAME);
            return false;
        }
        context_.err = GetError("Invalid name of the record.", Error::ErrorType::ERR_BAD_RECORD_NAME);
        return false;
    }

    auto iter = program_.record_table.find(std::string(context_.GiveToken().data(), context_.GiveToken().length()));

    if (iter == program_.record_table.end() || !iter->second.file_location->is_defined) {
        SetRecordInformation();
    } else {
        context_.err = GetError("This record already exists.", Error::ErrorType::ERR_BAD_ID_RECORD);
        return false;
    }

    LOG(DEBUG, ASSEMBLER) << "record name found (line " << line_stric_ << "): " << context_.GiveToken();

    ++context_;

    return true;
}

void Parser::SetRecordInformation()
{
    AddObjectInTable(true, program_.record_table);
    curr_record_ = &(program_.record_table.at(std::string(context_.GiveToken().data(), context_.GiveToken().length())));
}

bool Parser::ParseFunctionName()
{
    LOG(DEBUG, ASSEMBLER) << "started searching for function name (line " << line_stric_
                          << "): " << context_.tokens[context_.number - 1].whole_line;

    if (!FunctionValidName()) {
        if (*context_ == Token::Type::DEL_BRACKET_L) {
            context_.err = GetError("No function name.", Error::ErrorType::ERR_BAD_FUNCTION_NAME);
            return false;
        }
        context_.err = GetError("Invalid name of the function.", Error::ErrorType::ERR_BAD_FUNCTION_NAME);
        return false;
    }

    // names are mangled, so no need to check for same names here
    SetFunctionInformation();

    LOG(DEBUG, ASSEMBLER) << "function name found (line " << line_stric_ << "): " << context_.GiveToken();

    ++context_;

    return true;
}

void Parser::SetFunctionInformation()
{
    std::string p = std::string(context_.GiveToken());
    AddObjectInTable(true, program_.function_table);
    curr_func_ = &(program_.function_table.at(p));
    label_table_ = &(curr_func_->label_table);
    curr_func_->return_type = context_.curr_func_return_type;
}

bool Parser::ParseArrayName()
{
    LOG(DEBUG, ASSEMBLER) << "started searching for array name (line " << line_stric_
                          << "): " << context_.tokens[context_.number - 1].whole_line;

    if (!ArrayValidName()) {
        if (*context_ == Token::Type::DEL_BRACKET_L) {
            context_.err = GetError("No array name.", Error::ErrorType::ERR_BAD_ARRAY_NAME);
            return false;
        }
        context_.err = GetError("Invalid name of the array.", Error::ErrorType::ERR_BAD_ARRAY_NAME);
        return false;
    }

    auto iter =
        program_.literalarray_table.find(std::string(context_.GiveToken().data(), context_.GiveToken().length()));

    if (iter == program_.literalarray_table.end()) {
        SetArrayInformation();
    } else {
        context_.err = GetError("This array already exists.", Error::ErrorType::ERR_BAD_ID_ARRAY);
        return false;
    }

    LOG(DEBUG, ASSEMBLER) << "array id found (line " << line_stric_ << "): " << context_.GiveToken();

    ++context_;

    return true;
}

void Parser::SetArrayInformation()
{
    program_.literalarray_table.try_emplace(std::string(context_.GiveToken().data(), context_.GiveToken().length()),
                                            panda::pandasm::LiteralArray());

    curr_array_ =
        &(program_.literalarray_table.at(std::string(context_.GiveToken().data(), context_.GiveToken().length())));
}

void Parser::SetOperationInformation()
{
    context_.ins_number = curr_func_->ins.size();
    auto &curr_debug = curr_func_->ins.back().ins_debug;
    curr_debug.line_number = line_stric_;
    curr_debug.whole_line = context_.tokens[context_.number - 1].whole_line;
    curr_debug.bound_left = context_.tokens[context_.number - 1].bound_left;
    curr_debug.bound_right = context_.tokens[context_.number - 1].bound_right;
}

bool Parser::ParseFunctionReturn()
{
    LOG(DEBUG, ASSEMBLER) << "started searching for return function value (line " << line_stric_
                          << "): " << context_.tokens[context_.number - 1].whole_line;

    if (!TypeValidName()) {
        if (*context_ == Token::Type::DEL_BRACKET_L) {
            context_.err = GetError("No return type.", Error::ErrorType::ERR_BAD_FUNCTION_RETURN_VALUE);
            return false;
        }
        context_.err = GetError("Not a return type.", Error::ErrorType::ERR_BAD_FUNCTION_RETURN_VALUE);
        return false;
    }

    if (!ParseType(&context_.curr_func_return_type)) {
        return false;
    }

    LOG(DEBUG, ASSEMBLER) << "return type found (line " << line_stric_ << "): " << context_.GiveToken();

    return true;
}

bool Parser::TypeValidName()
{
    if (Type::GetId(context_.GiveToken()) != panda_file::Type::TypeId::REFERENCE) {
        return true;
    }

    return PrefixedValidName();
}

bool Parser::ParseFunctionArg()
{
    if (*context_ != Token::Type::ID) {
        context_.err = GetError("Expected identifier.", Error::ErrorType::ERR_BAD_FUNCTION_PARAMETERS);
        return false;
    }

    if (!TypeValidName()) {
        context_.err = GetError("Expected parameter type.", Error::ErrorType::ERR_BAD_TYPE);
        return false;
    }

    Type type;
    if (!ParseType(&type)) {
        return false;
    }

    if (context_.Mask()) {
        return false;
    }

    if (*context_ != Token::Type::ID) {
        context_.err = GetError("Expected identifier.", Error::ErrorType::ERR_BAD_FUNCTION_PARAMETERS);
        return false;
    }

    if (!ParamValidName()) {
        context_.err = GetError("Incorrect name of parameter.", Error::ErrorType::ERR_BAD_PARAM_NAME);
        return false;
    }

    ++context_;

    Function::Parameter parameter(type, program_.lang);
    metadata_ = parameter.metadata.get();

    if (*context_ == Token::Type::DEL_LT && !ParseMetaDef()) {
        return false;
    }

    curr_func_->params.push_back(std::move(parameter));

    return true;
}

bool Parser::ParseFunctionArgComma(bool &comma)
{
    if (comma && *context_ != Token::Type::DEL_COMMA) {
        context_.err = GetError("Expected comma.", Error::ErrorType::ERR_BAD_NUMBER_OPERANDS);
        return false;
    }

    if (comma) {
        ++context_;
    }

    comma = true;

    return true;
}

bool Parser::ParseFunctionArgs()
{
    LOG(DEBUG, ASSEMBLER) << "started searching for function parameters (line " << line_stric_
                          << "): " << context_.tokens[context_.number - 1].whole_line;

    bool comma = false;

    while (true) {
        if (context_.Mask()) {
            return false;
        }

        if (context_.id != Token::Type::DEL_COMMA && context_.id != Token::Type::ID) {
            break;
        }

        if (!ParseFunctionArgComma(comma)) {
            return false;
        }

        if (!ParseFunctionArg()) {
            return false;
        }
    }

    LOG(DEBUG, ASSEMBLER) << "parameters found (line " << line_stric_ << "): ";

    return true;
}

bool Parser::ParseMetaDef()
{
    LOG(DEBUG, ASSEMBLER) << "started searching for meta information (line " << line_stric_
                          << "): " << context_.tokens[context_.number - 1].whole_line;

    bool flag = false;

    if (context_.Mask()) {
        return false;
    }

    if (*context_ == Token::Type::DEL_LT) {
        flag = true;
        ++context_;
    }

    if (!ParseMetaList(flag)) {
        return false;
    }

    if (!flag && *context_ == Token::Type::DEL_GT) {
        context_.err = GetError("Expected '<'.", Error::ErrorType::ERR_BAD_METADATA_BOUND);
        ++context_;
        return false;
    }

    LOG(DEBUG, ASSEMBLER) << "searching for meta information (line " << line_stric_ << ") is successful";

    if (flag && context_.err.err == Error::ErrorType::ERR_NONE) {
        ++context_;
    }

    return true;
}

void Parser::SetMetadataContextError(const Metadata::Error &err, bool has_value)
{
    constexpr int64_t NO_VALUE_OFF = -1;
    constexpr int64_t SPECIAL_OFF = -2;
    constexpr int64_t STANDARD_OFF = -3;

    switch (err.GetType()) {
        case Metadata::Error::Type::UNKNOWN_ATTRIBUTE: {
            context_.err = GetError(err.GetMessage(), Error::ErrorType::ERR_BAD_METADATA_UNKNOWN_ATTRIBUTE, 0,
                                    has_value ? STANDARD_OFF : NO_VALUE_OFF);
            break;
        }
        case Metadata::Error::Type::MISSING_ATTRIBUTE: {
            context_.err = GetError(err.GetMessage(), Error::ErrorType::ERR_BAD_METADATA_MISSING_ATTRIBUTE);
            break;
        }
        case Metadata::Error::Type::MISSING_VALUE: {
            context_.err = GetError(err.GetMessage(), Error::ErrorType::ERR_BAD_METADATA_MISSING_VALUE);
            break;
        }
        case Metadata::Error::Type::UNEXPECTED_ATTRIBUTE: {
            context_.err = GetError(err.GetMessage(), Error::ErrorType::ERR_BAD_METADATA_UNEXPECTED_ATTRIBUTE, 0,
                                    has_value ? STANDARD_OFF : NO_VALUE_OFF);
            break;
        }
        case Metadata::Error::Type::UNEXPECTED_VALUE: {
            context_.err =
                GetError(err.GetMessage(), Error::ErrorType::ERR_BAD_METADATA_UNEXPECTED_VALUE, 0, SPECIAL_OFF);
            break;
        }
        case Metadata::Error::Type::INVALID_VALUE: {
            context_.err = GetError(err.GetMessage(), Error::ErrorType::ERR_BAD_METADATA_INVALID_VALUE, 0, -1);
            break;
        }
        case Metadata::Error::Type::MULTIPLE_ATTRIBUTE: {
            context_.err = GetError(err.GetMessage(), Error::ErrorType::ERR_BAD_METADATA_MULTIPLE_ATTRIBUTE, 0,
                                    has_value ? STANDARD_OFF : NO_VALUE_OFF);
            break;
        }
        default: {
            UNREACHABLE();
        }
    }
}

bool Parser::ParseMetaListComma(bool &comma, bool eq)
{
    if (!eq && comma && *context_ != Token::Type::DEL_COMMA) {
        context_.err = GetError("Expected comma.", Error::ErrorType::ERR_BAD_NUMBER_OPERANDS);
        return false;
    }

    if (!eq && comma) {
        ++context_;
    }

    comma = true;

    return true;
}

bool Parser::MeetExpMetaList(bool eq)
{
    if (!eq && *context_ != Token::Type::ID) {
        context_.err = GetError("Expected identifier.", Error::ErrorType::ERR_BAD_DEFINITION_METADATA, +1);
        return false;
    }

    if (eq && *context_ != Token::Type::ID && *context_ != Token::Type::ID_STRING) {
        context_.err =
            GetError("Expected identifier or string literal.", Error::ErrorType::ERR_BAD_DEFINITION_METADATA, +1);
        return false;
    }

    if (!eq && !PrefixedValidName()) {
        context_.err = GetError("Invalid attribute name.", Error::ErrorType::ERR_BAD_NAME_ID);
        return false;
    }

    return true;
}

bool Parser::BuildMetaListAttr(bool &eq, std::string &attribute_name, std::string &attribute_value)
{
    if (eq && *context_ == Token::Type::ID_STRING) {
        auto res = ParseStringLiteral();
        if (!res) {
            return false;
        }

        attribute_value = res.value();
    } else if (eq) {
        std::string sign {};
        attribute_value = context_.GiveToken();
        ++context_;

        if (!ParseOperandSignature(&sign)) {
            return false;
        }

        --context_;
        attribute_value += sign;
    } else {
        attribute_name = context_.GiveToken();
    }

    ++context_;

    if (context_.Mask()) {
        return false;
    }

    if (*context_ == Token::Type::DEL_EQ) {
        if (eq) {
            context_.err = GetError("'=' was not expected.", Error::ErrorType::ERR_BAD_NOEXP_DELIM);
            return false;
        }

        ++context_;
        eq = true;
    } else {
        std::optional<Metadata::Error> res;
        bool has_value = eq;
        if (has_value) {
            res = metadata_->SetAttributeValue(attribute_name, attribute_value);
        } else {
            res = metadata_->SetAttribute(attribute_name);
        }

        eq = false;

        if (res) {
            auto err = res.value();
            SetMetadataContextError(err, has_value);
            return false;
        }
    }

    return true;
}

bool Parser::ParseMetaList(bool flag)
{
    if (!flag && !context_.Mask() && *context_ != Token::Type::DEL_GT && *context_ != Token::Type::DEL_BRACE_L) {
        context_.err = GetError("No meta data expected.", Error::ErrorType::ERR_BAD_DEFINITION_METADATA);
        return false;
    }

    bool comma = false;
    bool eq = false;

    std::string attribute_name;
    std::string attribute_value;

    while (true) {
        if (context_.Mask()) {
            context_.err = GetError("Expected '>'.", Error::ErrorType::ERR_BAD_METADATA_BOUND, +1);
            return false;
        }

        if (context_.id != Token::Type::DEL_COMMA && context_.id != Token::Type::ID &&
            context_.id != Token::Type::ID_STRING && context_.id != Token::Type::DEL_EQ) {
            break;
        }

        if (!ParseMetaListComma(comma, eq)) {
            return false;
        }

        if (!MeetExpMetaList(eq)) {
            return false;
        }

        if (!BuildMetaListAttr(eq, attribute_name, attribute_value)) {
            return false;
        }
    }

    if (flag && *context_ != Token::Type::DEL_GT) {
        context_.err = GetError("Expected '>'.", Error::ErrorType::ERR_BAD_METADATA_BOUND);
        ++context_;

        return false;
    }

    auto res = metadata_->ValidateData();
    if (res) {
        auto err = res.value();
        SetMetadataContextError(err, false);
        return false;
    }

    return true;
}

bool Parser::ParseFunctionInstruction()
{
    if (ParseLabel()) {
        if (ParseOperation()) {
            if (ParseOperands()) {
                return true;
            }
        }
    }

    return context_.Mask();
}

}  // namespace panda::pandasm