/*

 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.

 *

 * HDF is dual licensed: you can use it either under the terms of

 * the GPL, or the BSD license, at your option.

 * See the LICENSE file in the root of this repository for complete details.

 */



#include "parser/parser.h"

#include <regex>

#include "ast/ast_array_type.h"

#include "ast/ast_enum_type.h"

#include "ast/ast_list_type.h"

#include "ast/ast_map_type.h"

#include "ast/ast_parameter.h"

#include "ast/ast_sequenceable_type.h"

#include "ast/ast_smq_type.h"

#include "ast/ast_struct_type.h"

#include "ast/ast_union_type.h"

#include "util/logger.h"

#include "util/string_builder.h"



#define RE_DIGIT      "[0-9]+"

#define RE_IDENTIFIER "[a-zA-Z_][a-zA-Z0-9_]*"



#define RE_PACKAGE_NUM             3

#define RE_PACKAGE_INDEX           0

#define RE_PACKAGE_MAJOR_VER_INDEX 1

#define RE_PACKAGE_MINOR_VER_INDEX 2



namespace OHOS {

namespace HDI {

static const std::regex rePackage(RE_IDENTIFIER "(?:\\." RE_IDENTIFIER ")*\\.[V|v]"

                                                "(" RE_DIGIT ")_(" RE_DIGIT ")");

static const std::regex reImport(

    RE_IDENTIFIER "(?:\\." RE_IDENTIFIER ")*\\.[V|v]" RE_DIGIT "_" RE_DIGIT "." RE_IDENTIFIER);



bool Parser::Parse(const std::vector<String> &sourceFiles)

{

    for (const auto &file : sourceFiles) {

        if (!ParseOne(file)) {

            return false;

        }

    }



    return true;

}



bool Parser::ParseOne(const String &sourceFile)

{

    if (!Reset(sourceFile)) {

        return false;

    }



    bool ret = ParseFile();

    ret = CheckIntegrity() && ret;

    ret = AddAst(ast_) && ret;

    if (!ret || !errors_.empty()) {

        ShowError();

        return false;

    }



    return true;

}



bool Parser::Reset(const String &sourceFile)

{

    bool ret = lexer_.Reset(sourceFile);

    if (!ret) {

        Logger::E(TAG, "Fail to open file '%s'.", sourceFile.string());

        return false;

    }



    errors_.clear();

    ast_ = nullptr;

    return true;

}



bool Parser::ParseFile()

{

    ast_ = new AST();

    ast_->SetIdlFile(lexer_.GetFilePath());

    ast_->SetLicense(ParseLicense());



    if (!ParsePackage()) {

        return false;

    }



    if (!ParseImports()) {

        return false;

    }



    if (!ParseTypeDecls()) {

        return false;

    }



    SetAstFileType();

    return true;

}



String Parser::ParseLicense()

{

    Token token = lexer_.PeekToken(false);

    if (token.kind_ == TokenType::COMMENT_BLOCK) {

        lexer_.GetToken(false);

        return token.value_;

    }



    return String("");

}



bool Parser::ParsePackage()

{

    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::PACKAGE) {

        LogError(token, String::Format("expected 'package' before '%s' token", token.value_.string()));

        return false;

    }

    lexer_.GetToken();



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ID) {

        LogError(token, String::Format("expected name of package before '%s' token", token.value_.string()));

        lexer_.SkipToken(TokenType::SEMICOLON);

        return false;

    }

    String packageName = token.value_;

    lexer_.GetToken();



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::SEMICOLON) {

        LogError(token, String::Format("expected ';' before '%s' token", token.value_.string()));

        return false;

    }

    lexer_.GetToken();



    if (packageName.IsEmpty()) {

        LogError(String("package name is not expected."));

        return false;

    } else if (!CheckPackageName(lexer_.GetFilePath(), packageName)) {

        LogError(String::Format(

            "package name '%s' does not match file apth '%s'.", packageName.string(), lexer_.GetFilePath().string()));

        return false;

    }



    if (!ParserPackageInfo(packageName)) {

        LogError(String::Format("parse package '%s' infomation failed.", packageName.string()));

        return false;

    }



    return true;

}



bool Parser::ParserPackageInfo(const String &packageName)

{

    std::cmatch result;

    if (!std::regex_match(packageName.string(), result, rePackage)) {

        return false;

    }



    if (result.size() < RE_PACKAGE_NUM) {

        return false;

    }



    ast_->SetPackageName(result.str(RE_PACKAGE_INDEX).c_str());

    size_t majorVersion = std::atoi(result.str(RE_PACKAGE_MAJOR_VER_INDEX).c_str());

    size_t minorVersion = std::atoi(result.str(RE_PACKAGE_MINOR_VER_INDEX).c_str());

    ast_->SetVersion(majorVersion, minorVersion);

    return true;

}



bool Parser::ParseImports()

{

    Token token = lexer_.PeekToken();

    while (token.kind_ == TokenType::IMPORT || token.kind_ == TokenType::SEQ) {

        TokenType kind = token.kind_;

        lexer_.GetToken();



        token = lexer_.PeekToken();

        if (token.kind_ != TokenType::ID) {

            LogError(token, String::Format("expected identifier before '%s' token", token.value_.string()));

            lexer_.SkipToken(TokenType::SEMICOLON);

            token = lexer_.PeekToken();

            continue;

        }



        if (kind == TokenType::IMPORT) {

            ParseImportInfo();

        } else {

            ParseSequenceableInfo();

        }

        lexer_.GetToken();



        token = lexer_.PeekToken();

        if (token.kind_ != TokenType::SEMICOLON) {

            LogError(token, String::Format("expected ';' before '%s'.", token.value_.string()));

            return false;

        }

        lexer_.GetToken();



        token = lexer_.PeekToken();

    }



    return true;

}



void Parser::ParseImportInfo()

{

    Token token = lexer_.PeekToken();

    String importName = token.value_;

    if (importName.IsEmpty()) {

        LogError(token, String::Format("import name is empty"));

        return;

    }



    if (!CheckImport(importName)) {

        LogError(token, String::Format("import name is illegal"));

        return;

    }



    auto iter = allAsts_.find(importName);

    AutoPtr<AST> importAst = (iter != allAsts_.end()) ? iter->second : nullptr;

    if (importAst == nullptr) {

        LogError(token, String::Format("can not find idl file from import name '%s'", importName.string()));

        return;

    }



    AutoPtr<ASTInterfaceType> interfaceType = importAst->GetInterfaceDef();

    if (interfaceType != nullptr) {

        interfaceType->SetSerializable(true);

    }



    if (!ast_->AddImport(importAst)) {

        LogError(token, String::Format("multiple import of '%s'", importName.string()));

        return;

    }

}



void Parser::ParseSequenceableInfo()

{

    Token token = lexer_.PeekToken();

    String seqName = token.value_;

    if (seqName.IsEmpty()) {

        LogError(token, String::Format("sequenceable name is empty"));

        return;

    }



    AutoPtr<ASTSequenceableType> seqType = new ASTSequenceableType();

    int index = seqName.LastIndexOf('.');

    if (index != -1) {

        seqType->SetName(seqName.Substring(index + 1));

        seqType->SetNamespace(ast_->ParseNamespace(seqName.Substring(0, index + 1)));

    } else {

        seqType->SetName(seqName);

    }



    AutoPtr<AST> seqAst = new AST();

    seqAst->SetFullName(seqName);

    seqAst->AddSequenceableDef(seqType);

    seqAst->SetAStFileType(ASTFileType::AST_SEQUENCEABLE);

    ast_->AddImport(seqAst);

    AddAst(seqAst);

}



bool Parser::ParseTypeDecls()

{

    Token token = lexer_.PeekToken();

    while (token.kind_ != TokenType::END_OF_FILE) {

        switch (token.kind_) {

            case TokenType::BRACKETS_LEFT:

                ParseAttribute();

                break;

            case TokenType::INTERFACE:

                ParseInterface();

                break;

            case TokenType::ENUM:

                ParseEnumDeclaration();

                break;

            case TokenType::STRUCT:

                ParseStructDeclaration();

                break;

            case TokenType::UNION:

                ParseUnionDeclaration();

                break;

            default:

                LogError(token, String::Format("'%s' is not expected", token.value_.string()));

                lexer_.SkipToken(TokenType::SEMICOLON);

                break;

        }

        token = lexer_.PeekToken();

    }

    return true;

}



void Parser::ParseAttribute()

{

    AttrSet attrs = ParseAttributeInfo();

    Token token = lexer_.PeekToken();

    switch (token.kind_) {

        case TokenType::INTERFACE:

            ParseInterface(attrs);

            break;

        case TokenType::ENUM:

            ParseEnumDeclaration(attrs);

            break;

        case TokenType::STRUCT:

            ParseStructDeclaration(attrs);

            break;

        case TokenType::UNION:

            ParseUnionDeclaration(attrs);

            break;

        default:

            LogError(token, String::Format("'%s' is not expected", token.value_.string()));

            lexer_.SkipToken(token.kind_);

            break;

    }

}



AttrSet Parser::ParseAttributeInfo()

{

    AttrSet attrs;

    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::BRACKETS_LEFT) {

        LogError(token, String::Format("expected '[' before '%s' token", token.value_.string()));

        lexer_.SkipToken(token.kind_);

        return attrs;

    }

    lexer_.GetToken();



    token = lexer_.PeekToken();

    while (token.kind_ != TokenType::BRACKETS_RIGHT && token.kind_ != TokenType::END_OF_FILE) {

        if (!AprseAttrUnit(attrs)) {

            return attrs;

        }

        token = lexer_.PeekToken();

        if (token.kind_ == TokenType::COMMA) {

            lexer_.GetToken();

            token = lexer_.PeekToken();

            continue;

        }



        if (token.kind_ == TokenType::BRACKETS_RIGHT) {

            lexer_.GetToken();

            break;

        } else {

            LogError(token, String::Format("expected ',' or ']' before '%s' token", token.value_.string()));

            lexer_.SkipToken(TokenType::BRACKETS_RIGHT);

            break;

        }

    }



    return attrs;

}



bool Parser::AprseAttrUnit(AttrSet &attrs)

{

    Token token = lexer_.PeekToken();

    switch (token.kind_) {

        case TokenType::FULL:

        case TokenType::LITE:

        case TokenType::CALLBACK:

        case TokenType::ONEWAY: {

            if (attrs.find(token) != attrs.end()) {

                LogError(token, String::Format("Duplicate declared attributes '%s'", token.value_.string()));

            } else {

                attrs.insert(token);

            }

            lexer_.GetToken();

            break;

        }

        default:

            LogError(token, String::Format("'%s' is a illegal attribute", token.value_.string()));

            lexer_.SkipToken(TokenType::BRACKETS_RIGHT);

            return false;

    }

    return true;

}



void Parser::ParseInterface(const AttrSet &attrs)

{

    AutoPtr<ASTInterfaceType> interfaceType = new ASTInterfaceType;

    AutoPtr<ASTInfAttr> astAttr = ParseInfAttrInfo(attrs);

    interfaceType->SetAttribute(astAttr);



    lexer_.GetToken();

    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ID) {

        LogError(token, String::Format("expected interface name before '%s' token", token.value_.string()));

    } else {

        interfaceType->SetName(token.value_);

        interfaceType->SetNamespace(ast_->ParseNamespace(ast_->GetFullName()));

        interfaceType->SetLicense(ast_->GetLicense());

        if (!token.value_.Equals(ast_->GetName())) {

            LogError(token, String::Format("interface name '%s' is not equal idl file name", token.value_.string()));

        }

        lexer_.GetToken();

    }



    ParseInterfaceBody(interfaceType);

    ast_->AddInterfaceDef(interfaceType);

}



AutoPtr<ASTInfAttr> Parser::ParseInfAttrInfo(const AttrSet &attrs)

{

    AutoPtr<ASTInfAttr> infAttr = new ASTInfAttr;



    for (const auto &attr : attrs) {

        switch (attr.kind_) {

            case TokenType::FULL:

                infAttr->isFull_ = true;

                break;

            case TokenType::LITE:

                infAttr->isLite_ = true;

                break;

            case TokenType::CALLBACK:

                infAttr->isCallback_ = true;

                break;

            case TokenType::ONEWAY:

                infAttr->isOneWay_ = true;

                break;

            default:

                LogError(attr, String::Format("illegal attribute of interface"));

                break;

        }

    }



    return infAttr;

}



void Parser::ParseInterfaceBody(const AutoPtr<ASTInterfaceType> &interface)

{

    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::BRACES_LEFT) {

        LogError(token, String::Format("expected '{' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    token = lexer_.PeekToken();

    while (token.kind_ != TokenType::BRACES_RIGHT && token.kind_ != TokenType::END_OF_FILE) {

        interface->AddMethod(ParseMethod());

        token = lexer_.PeekToken();

    }



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::BRACES_RIGHT) {

        LogError(token, String::Format("expected '{' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    interface->AddVersionMethod(CreateGetVersionMethod());

}



AutoPtr<ASTMethod> Parser::ParseMethod()

{

    AutoPtr<ASTMethod> method = new ASTMethod();

    method->SetAttribute(ParseMethodAttr());



    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ID) {

        LogError(token, String::Format("expected method name before '%s' token", token.value_.string()));

    } else {

        method->SetName(token.value_);

        lexer_.GetToken();

    }



    ParseMethodParamList(method);



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::SEMICOLON) {

        LogError(token, String::Format("expected ';' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    return method;

}



AutoPtr<ASTMethodAttr> Parser::ParseMethodAttr()

{

    AutoPtr<ASTMethodAttr> attr = new ASTMethodAttr();

    Token token = lexer_.PeekToken();

    if (token.kind_ == TokenType::ID) {

        return attr;

    }



    if (token.kind_ != TokenType::BRACKETS_LEFT) {

        LogError(token, String::Format("expected '[' before '%s' token", token.value_.string()));

        lexer_.SkipUntilToken(TokenType::ID);

        return attr;

    }



    lexer_.GetToken();

    token = lexer_.PeekToken();

    while (token.kind_ != TokenType::BRACKETS_RIGHT) {

        switch (token.kind_) {

            case TokenType::FULL:

                attr->isFull_ = true;

                break;

            case TokenType::LITE:

                attr->isLite_ = true;

                break;

            case TokenType::ONEWAY:

                attr->isOneWay_ = true;

                break;

            default:

                LogError(token, String::Format("expected attribute before '%s' token", token.value_.string()));

                lexer_.SkipUntilToken(TokenType::BRACKETS_RIGHT);

                return attr;

        }



        lexer_.GetToken();

        token = lexer_.PeekToken();

        if (token.kind_ == TokenType::BRACKETS_RIGHT) {

            lexer_.GetToken();

            break;

        }



        if (token.kind_ != TokenType::COMMA) {

            LogError(token, String::Format("expected ',' before '%s' token", token.value_.string()));

            lexer_.SkipUntilToken(TokenType::BRACKETS_RIGHT);

            return attr;

        }

        lexer_.GetToken();

        token = lexer_.PeekToken();

    }

    return attr;

}



AutoPtr<ASTMethod> Parser::CreateGetVersionMethod()

{

    AutoPtr<ASTMethod> method = new ASTMethod();

    method->SetName("GetVersion");



    AutoPtr<ASTType> type = ast_->FindType("unsigned int");

    if (type == nullptr) {

        type = new ASTUintType();

    }

    AutoPtr<ASTParameter> majorParam = new ASTParameter("majorVer", ParamAttr::PARAM_OUT, type);

    AutoPtr<ASTParameter> minorParam = new ASTParameter("minorVer", ParamAttr::PARAM_OUT, type);



    method->AddParameter(majorParam);

    method->AddParameter(minorParam);

    return method;

}



void Parser::ParseMethodParamList(const AutoPtr<ASTMethod> &method)

{

    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::PARENTHESES_LEFT) {

        LogError(token, String::Format("expected '(' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    token = lexer_.PeekToken();

    if (token.kind_ == TokenType::PARENTHESES_RIGHT) {

        lexer_.GetToken();

        return;

    }



    while (token.kind_ != TokenType::PARENTHESES_RIGHT && token.kind_ != TokenType::END_OF_FILE) {

        method->AddParameter(ParseParam());

        token = lexer_.PeekToken();

        if (token.kind_ == TokenType::COMMA) {

            lexer_.GetToken();

            token = lexer_.PeekToken();

            if (token.kind_ == TokenType::PARENTHESES_RIGHT) {

                LogError(token, String::Format(""));

            }

            continue;

        }



        if (token.kind_ == TokenType::PARENTHESES_RIGHT) {

            lexer_.GetToken();

            break;

        } else {

            LogError(token, String::Format("expected ',' or ')' before '%s' token", token.value_.string()));

            lexer_.SkipToken(TokenType::PARENTHESES_RIGHT);

            break;

        }

    }

}



AutoPtr<ASTParameter> Parser::ParseParam()

{

    AutoPtr<ASTParamAttr> paramAttr = ParseParamAttr();

    AutoPtr<ASTType> paramType = ParseType();

    String paramName = "";



    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ID) {

        LogError(token, String::Format("expected param name before '%s' token", token.value_.string()));

    } else {

        paramName = token.value_;

        lexer_.GetToken();

    }



    return new ASTParameter(paramName, paramAttr, paramType);

}



AutoPtr<ASTParamAttr> Parser::ParseParamAttr()

{

    AutoPtr<ASTParamAttr> attr = new ASTParamAttr(ParamAttr::PARAM_IN);

    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::BRACKETS_LEFT) {

        LogError(token, String::Format("expected '[' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    token = lexer_.PeekToken();

    if (token.kind_ == TokenType::IN) {

        attr->value_ = ParamAttr::PARAM_IN;

        lexer_.GetToken();

    } else if (token.kind_ == TokenType::OUT) {

        attr->value_ = ParamAttr::PARAM_OUT;

        lexer_.GetToken();

    } else {

        LogError(token, String::Format("expected 'in' or 'out' attribute before '%s' token", token.value_.string()));

    }



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::BRACKETS_RIGHT) {

        LogError(token, String::Format("expected ']' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    return attr;

}



AutoPtr<ASTType> Parser::ParseType()

{

    AutoPtr<ASTType> type = nullptr;

    Token token = lexer_.PeekToken();

    switch (token.kind_) {

        case TokenType::BOOLEAN:

        case TokenType::BYTE:

        case TokenType::SHORT:

        case TokenType::INT:

        case TokenType::LONG:

        case TokenType::STRING:

        case TokenType::FLOAT:

        case TokenType::DOUBLE:

        case TokenType::FD:

            type = ast_->FindType(token.value_);

            lexer_.GetToken();

            break;

        case TokenType::UNSIGNED:

            type = ParseUnsignedType();

            break;

        case TokenType::LIST:

            type = ParseListType();

            break;

        case TokenType::MAP:

            type = ParseMapType();

            break;

        case TokenType::SMQ:

            type = ParseSmqType();

            break;

        case TokenType::ENUM:

        case TokenType::STRUCT:

        case TokenType::UNION:

        case TokenType::ID:

        case TokenType::SEQ:

            type = ParseUserDefType();

            break;

        default:

            LogError(token, String::Format("'%s' of type is illegal", token.value_.string()));

            return nullptr;

    }

    if (type == nullptr) {

        LogError(token, String::Format("this type was not declared in this scope"));

    }

    if (!CheckType(token, type)) {

        return nullptr;

    }

    if (lexer_.PeekToken().kind_ == TokenType::BRACKETS_LEFT) {

        type = ParseArrayType(type);

    }

    return type;

}



AutoPtr<ASTType> Parser::ParseUnsignedType()

{

    AutoPtr<ASTType> type = nullptr;

    String namePrefix = lexer_.GetToken().value_;

    Token token = lexer_.PeekToken();

    switch (token.kind_) {

        case TokenType::CHAR:

        case TokenType::SHORT:

        case TokenType::INT:

        case TokenType::LONG:

            type = ast_->FindType(namePrefix + " " + token.value_);

            lexer_.GetToken();

            break;

        default:

            LogError(token, String::Format("'unsigned %s' was not declared in the idl file", token.value_.string()));

            break;

    }



    return type;

}



AutoPtr<ASTType> Parser::ParseArrayType(const AutoPtr<ASTType> &elementType)

{

    lexer_.GetToken(); // '['



    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::BRACKETS_RIGHT) {

        LogError(token, String::Format("expected ']' before '%s' token", token.value_.string()));

        return nullptr;

    }

    lexer_.GetToken(); // ']'



    if (elementType == nullptr) {

        return nullptr;

    }



    AutoPtr<ASTArrayType> arrayType = new ASTArrayType();

    arrayType->SetElementType(elementType);

    AutoPtr<ASTType> type = ast_->FindType(arrayType->ToString());



    if (type == nullptr) {

        ast_->AddType(arrayType.Get());

        type = arrayType.Get();

    }



    return type;

}



AutoPtr<ASTType> Parser::ParseListType()

{

    lexer_.GetToken(); // List



    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ANGLE_BRACKETS_LEFT) {

        LogError(token, String::Format("expected '<' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken(); // '<'

    }



    AutoPtr<ASTType> type = ParseType(); // element type

    if (type == nullptr) {

        lexer_.SkipToken(TokenType::ANGLE_BRACKETS_RIGHT);

        return nullptr;

    }



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ANGLE_BRACKETS_RIGHT) {

        LogError(token, String::Format("expected '>' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken(); // '>'

    }



    AutoPtr<ASTListType> list = new ASTListType();

    list->SetElementType(type);



    AutoPtr<ASTType> ret = ast_->FindType(list->ToString());

    if (ret == nullptr) {

        ast_->AddType(list.Get());

        ret = list.Get();

    }



    return ret;

}



AutoPtr<ASTType> Parser::ParseMapType()

{

    lexer_.GetToken(); // 'Map'



    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ANGLE_BRACKETS_LEFT) {

        LogError(token, String::Format("expected '<' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken(); // '<'

    }



    AutoPtr<ASTType> keyType = ParseType(); // key type

    if (keyType == nullptr) {

        LogError(token, String::Format("key type '%s' is illegal", token.value_.string()));

        lexer_.SkipToken(TokenType::ANGLE_BRACKETS_RIGHT);

        return nullptr;

    }



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::COMMA) {

        LogError(token, String::Format("expected ',' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken(); // ','

    }



    AutoPtr<ASTType> valueType = ParseType();

    if (valueType == nullptr) {

        LogError(token, String::Format("key type '%s' is illegal", token.value_.string()));

        lexer_.SkipToken(TokenType::ANGLE_BRACKETS_RIGHT);

        return nullptr;

    }



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ANGLE_BRACKETS_RIGHT) {

        LogError(token, String::Format("expected '>' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    AutoPtr<ASTMapType> map = new ASTMapType();

    map->SetKeyType(keyType);

    map->SetValueType(valueType);



    AutoPtr<ASTType> ret = ast_->FindType(map->ToString());

    if (ret == nullptr) {

        ast_->AddType(map.Get());

        ret = map.Get();

    }



    return ret;

}



AutoPtr<ASTType> Parser::ParseSmqType()

{

    lexer_.GetToken(); // 'SharedMemQueue'



    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ANGLE_BRACKETS_LEFT) {

        LogError(token, String::Format("expected '<' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken(); // '<'

    }



    AutoPtr<ASTType> InnerType = ParseType();

    if (InnerType == nullptr) {

        lexer_.SkipToken(TokenType::ANGLE_BRACKETS_RIGHT);

        return nullptr;

    }



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ANGLE_BRACKETS_RIGHT) {

        LogError(token, String::Format("expected '>' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken(); // '>'

    }



    AutoPtr<ASTSmqType> type = new ASTSmqType();

    type->SetInnerType(InnerType);

    AutoPtr<ASTType> ret = ast_->FindType(type->ToString());

    if (ret == nullptr) {

        ast_->AddType(type.Get());

        ret = type.Get();

    }



    return ret;

}



AutoPtr<ASTType> Parser::ParseUserDefType()

{

    Token token = lexer_.GetToken();

    if (token.kind_ == TokenType::ID) {

        return ast_->FindType(token.value_);

    }



    String typePrefix = token.value_;



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ID) {

        LogError(token, String::Format("expected identifier before '%s' token", token.value_.string()));

        return nullptr;

    } else {

        lexer_.GetToken();

    }



    String typeName = typePrefix + " " + token.value_;

    AutoPtr<ASTType> type = ast_->FindType(typeName);

    if (type != nullptr) {

        ast_->AddType(type);

    }

    return type;

}



void Parser::ParseEnumDeclaration(const AttrSet &attrs)

{

    AutoPtr<ASTEnumType> enumType = new ASTEnumType;

    enumType->SetAttribute(ParseUserDefTypeAttr(attrs));



    lexer_.GetToken();

    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ID) {

        LogError(token, String::Format("expected enum type name before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

        enumType->SetName(token.value_);

    }



    token = lexer_.PeekToken();

    if (token.kind_ == TokenType::COLON || token.kind_ == TokenType::BRACES_LEFT) {

        enumType->SetBaseType(ParseEnumBaseType());

    } else {

        LogError(token, String::Format("expected ':' or '{' before '%s' token", token.value_.string()));

    }



    ParserEnumMember(enumType);

    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::BRACES_RIGHT) {

        LogError(token, String::Format("expected '}' before '%s' token", token.value_.string()));

        return;

    } else {

        lexer_.GetToken();

    }



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::SEMICOLON) {

        LogError(token, String::Format("expected ';' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    ast_->AddTypeDefinition(enumType.Get());

}



AutoPtr<ASTType> Parser::ParseEnumBaseType()

{

    AutoPtr<ASTType> baseType = nullptr;

    Token token = lexer_.PeekToken();

    if (token.kind_ == TokenType::COLON) {

        lexer_.GetToken();



        token = lexer_.PeekToken();

        baseType = ParseType();

        if (baseType == nullptr) {

            return nullptr;

        }



        switch (baseType->GetTypeKind()) {

            case TypeKind::TYPE_BYTE:

            case TypeKind::TYPE_SHORT:

            case TypeKind::TYPE_INT:

            case TypeKind::TYPE_LONG:

            case TypeKind::TYPE_UCHAR:

            case TypeKind::TYPE_USHORT:

            case TypeKind::TYPE_UINT:

            case TypeKind::TYPE_ULONG:

                break;

            default:

                LogError(token, String::Format("illegal base type of enum", baseType->ToString().string()));

                return nullptr;

        }



        token = lexer_.PeekToken();

        if (token.kind_ != TokenType::BRACES_LEFT) {

            LogError(token, String::Format("expected '{' before '%s' token", token.value_.string()));

        }

        lexer_.GetToken();

    } else {

        lexer_.GetToken();

        AutoPtr<ASTType> baseType = ast_->FindType("int");

    }

    return baseType;

}



void Parser::ParserEnumMember(const AutoPtr<ASTEnumType> &enumType)

{

    Token token = lexer_.PeekToken();

    while (token.kind_ == TokenType::ID) {

        AutoPtr<ASTEnumValue> enumValue = new ASTEnumValue(token.value_);

        lexer_.GetToken();



        token = lexer_.PeekToken();

        if (token.kind_ == TokenType::ASSIGN) {

            lexer_.GetToken();

            token = lexer_.PeekToken();

            enumValue->SetExprValue(ParseExpr());

        }



        enumValue->SetType(enumType->GetBaseType());

        enumType->AddMember(enumValue);



        token = lexer_.PeekToken();

        if (token.kind_ == TokenType::COMMA) {

            lexer_.GetToken();

            token = lexer_.PeekToken();

            continue;

        }



        if (token.kind_ != TokenType::BRACES_RIGHT) {

            LogError(token, String::Format("expected ',' or '}' before '%s' token", token.value_.string()));

        }

    }

}



void Parser::ParseStructDeclaration(const AttrSet &attrs)

{

    AutoPtr<ASTStructType> structType = new ASTStructType;

    structType->SetAttribute(ParseUserDefTypeAttr(attrs));



    lexer_.GetToken();

    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ID) {

        LogError(token, String::Format("expected struct name before '%s' token", token.value_.string()));

    } else {

        structType->SetName(token.value_);

        lexer_.GetToken();

    }



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::BRACES_LEFT) {

        LogError(token, String::Format("expected '{' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    ParseStructMember(structType);



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::BRACES_RIGHT) {

        LogError(token, String::Format("expected '}' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::SEMICOLON) {

        LogError(token, String::Format("expected ';' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    ast_->AddTypeDefinition(structType.Get());

}



void Parser::ParseStructMember(const AutoPtr<ASTStructType> &structType)

{

    Token token = lexer_.PeekToken();

    while (token.kind_ != TokenType::BRACES_RIGHT && token.kind_ != TokenType::END_OF_FILE) {

        AutoPtr<ASTType> memberType = ParseType();

        if (memberType == nullptr) {

            lexer_.SkipToken(TokenType::SEMICOLON);

            token = lexer_.PeekToken();

            continue;

        }



        String typeName = memberType->ToString();

        token = lexer_.PeekToken();

        if (token.kind_ != TokenType::ID) {

            LogError(token, String::Format("expected member name before '%s' token", token.value_.string()));

            lexer_.SkipToken(TokenType::SEMICOLON);

            token = lexer_.PeekToken();

            continue;

        }



        lexer_.GetToken();

        String memberName = token.value_;

        structType->AddMember(memberType, memberName);



        token = lexer_.PeekToken();

        if (token.kind_ == TokenType::SEMICOLON) {

            lexer_.GetToken();

            token = lexer_.PeekToken();

            continue;

        }



        if (token.kind_ != TokenType::BRACES_RIGHT) {

            LogError(token, String::Format("expected ',' or '}' before '%s' token", token.value_.string()));

        }

    }

}



void Parser::ParseUnionDeclaration(const AttrSet &attrs)

{

    AutoPtr<ASTUnionType> unionType = new ASTUnionType;

    unionType->SetAttribute(ParseUserDefTypeAttr(attrs));



    lexer_.GetToken();

    Token token = lexer_.PeekToken();

    if (token.kind_ != TokenType::ID) {

        LogError(token, String::Format("expected struct name before '%s' token", token.value_.string()));

    } else {

        unionType->SetName(token.value_);

        lexer_.GetToken();

    }



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::BRACES_LEFT) {

        LogError(token, String::Format("expected '{' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    ParseUnionMember(unionType);



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::BRACES_RIGHT) {

        LogError(token, String::Format("expected '}' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    token = lexer_.PeekToken();

    if (token.kind_ != TokenType::SEMICOLON) {

        LogError(token, String::Format("expected ';' before '%s' token", token.value_.string()));

    } else {

        lexer_.GetToken();

    }



    ast_->AddTypeDefinition(unionType.Get());

}



void Parser::ParseUnionMember(const AutoPtr<ASTUnionType> &unionType)

{

    Token token = lexer_.PeekToken();

    while (token.kind_ != TokenType::BRACES_RIGHT && token.kind_ != TokenType::END_OF_FILE) {

        AutoPtr<ASTType> memberType = ParseType();

        if (memberType == nullptr) {

            lexer_.SkipToken(TokenType::SEMICOLON);

            token = lexer_.PeekToken();

            continue;

        }



        String typeName = memberType->ToString();

        token = lexer_.PeekToken();

        if (token.kind_ != TokenType::ID) {

            LogError(token, String::Format("expected member name before '%s' token", token.value_.string()));

            lexer_.SkipToken(TokenType::SEMICOLON);

            token = lexer_.PeekToken();

            continue;

        }

        lexer_.GetToken();



        String memberName = token.value_;

        if (!AddUnionMember(unionType, memberType, memberName)) {

            LogError(token,

                String::Format("union not support this type or name of member duplicate '%s'", token.value_.string()));

        }



        token = lexer_.PeekToken();

        if (token.kind_ == TokenType::SEMICOLON) {

            lexer_.GetToken();

            token = lexer_.PeekToken();

            continue;

        }



        if (token.kind_ != TokenType::BRACES_RIGHT) {

            LogError(token, String::Format("expected ',' or '}' before '%s' token", token.value_.string()));

        }

    }

}



bool Parser::AddUnionMember(const AutoPtr<ASTUnionType> &unionType, const AutoPtr<ASTType> &type, const String &name)

{

    for (size_t i = 0; i < unionType->GetMemberNumber(); i++) {

        String memberName = unionType->GetMemberName(i);

        if (name.Equals(memberName)) {

            return false;

        }

    }



    if ((type->GetTypeKind() < TypeKind::TYPE_BOOLEAN || type->GetTypeKind() > TypeKind::TYPE_ULONG) &&

        (type->GetTypeKind() < TypeKind::TYPE_ENUM || type->GetTypeKind() > TypeKind::TYPE_UNION)) {

        return false;

    }



    unionType->AddMember(type, name);

    return true;

}



AutoPtr<ASTTypeAttr> Parser::ParseUserDefTypeAttr(const AttrSet &attrs)

{

    AutoPtr<ASTTypeAttr> attribute = new ASTTypeAttr();

    for (const auto &token : attrs) {

        switch (token.kind_) {

            case TokenType::FULL:

                attribute->isFull_ = true;

                break;

            case TokenType::LITE:

                attribute->isLite_ = true;

                break;

            default:

                LogError(token, String::Format("invalid attribute '%s' for type decl", token.value_.string()));

                break;

        }

    }



    return attribute;

}



AutoPtr<ASTExpr> Parser::ParseExpr()

{

    return ParseAndExpr();

}



AutoPtr<ASTExpr> Parser::ParseAndExpr()

{

    AutoPtr<ASTExpr> left = ParseXorExpr();

    Token token = lexer_.PeekToken();

    while (token.kind_ == TokenType::AND) {

        lexer_.GetToken();

        AutoPtr<ASTBinaryExpr> expr = new ASTBinaryExpr;

        expr->op_ = BinaryOpKind::AND;

        expr->lExpr_ = left;

        expr->rExpr_ = ParseXorExpr();



        left = expr.Get();

        token = lexer_.PeekToken();

    }

    return left;

}



AutoPtr<ASTExpr> Parser::ParseXorExpr()

{

    AutoPtr<ASTExpr> left = ParseOrExpr();

    Token token = lexer_.PeekToken();

    while (token.kind_ == TokenType::XOR) {

        lexer_.GetToken();

        AutoPtr<ASTBinaryExpr> expr = new ASTBinaryExpr;

        expr->op_ = BinaryOpKind::XOR;

        expr->lExpr_ = left;

        expr->rExpr_ = ParseOrExpr();



        left = expr.Get();

        token = lexer_.PeekToken();

    }

    return left;

}



AutoPtr<ASTExpr> Parser::ParseOrExpr()

{

    AutoPtr<ASTExpr> left = ParseShiftExpr();

    Token token = lexer_.PeekToken();

    while (token.kind_ == TokenType::OR) {

        lexer_.GetToken();

        AutoPtr<ASTBinaryExpr> expr = new ASTBinaryExpr;

        expr->op_ = BinaryOpKind::OR;

        expr->lExpr_ = left;

        expr->rExpr_ = ParseShiftExpr();



        left = expr.Get();

        token = lexer_.PeekToken();

    }

    return left;

}



AutoPtr<ASTExpr> Parser::ParseShiftExpr()

{

    AutoPtr<ASTExpr> left = ParseAddExpr();

    Token token = lexer_.PeekToken();

    while (token.kind_ == TokenType::LEFT_SHIFT || token.kind_ == TokenType::RIGHT_SHIFT) {

        lexer_.GetToken();

        BinaryOpKind op = (token.kind_ == TokenType::LEFT_SHIFT) ? BinaryOpKind::LSHIFT : BinaryOpKind::RSHIFT;

        AutoPtr<ASTBinaryExpr> expr = new ASTBinaryExpr;

        expr->op_ = op;

        expr->lExpr_ = left;

        expr->rExpr_ = ParseAddExpr();



        left = expr.Get();

        token = lexer_.PeekToken();

    }

    return left;

}



AutoPtr<ASTExpr> Parser::ParseAddExpr()

{

    AutoPtr<ASTExpr> left = ParseMulExpr();

    Token token = lexer_.PeekToken();

    while (token.kind_ == TokenType::ADD || token.kind_ == TokenType::SUB) {

        lexer_.GetToken();

        BinaryOpKind op = (token.kind_ == TokenType::ADD) ? BinaryOpKind::ADD : BinaryOpKind::SUB;

        AutoPtr<ASTBinaryExpr> expr = new ASTBinaryExpr;

        expr->op_ = op;

        expr->lExpr_ = left;

        expr->rExpr_ = ParseMulExpr();



        left = expr.Get();

        token = lexer_.PeekToken();

    }

    return left;

}



AutoPtr<ASTExpr> Parser::ParseMulExpr()

{

    AutoPtr<ASTExpr> left = ParseUnaryExpr();

    Token token = lexer_.PeekToken();

    while (

        token.kind_ == TokenType::STAR || token.kind_ == TokenType::SLASH || token.kind_ == TokenType::PERCENT_SIGN) {

        lexer_.GetToken();

        BinaryOpKind op = BinaryOpKind::MUL;

        if (token.kind_ == TokenType::SLASH) {

            op = BinaryOpKind::DIV;

        } else if (token.kind_ == TokenType::PERCENT_SIGN) {

            op = BinaryOpKind::MOD;

        }

        AutoPtr<ASTBinaryExpr> expr = new ASTBinaryExpr;

        expr->op_ = op;

        expr->lExpr_ = left;

        expr->rExpr_ = ParseUnaryExpr();



        left = expr.Get();

        token = lexer_.PeekToken();

    }

    return left;

}



AutoPtr<ASTExpr> Parser::ParseUnaryExpr()

{

    Token token = lexer_.PeekToken();

    switch (token.kind_) {

        case TokenType::ADD:

        case TokenType::SUB:

        case TokenType::TILDE: {

            lexer_.GetToken();

            AutoPtr<ASTUnaryExpr> expr = new ASTUnaryExpr;

            expr->op_ = UnaryOpKind::PLUS;

            if (token.kind_ == TokenType::SUB) {

                expr->op_ = UnaryOpKind::MINUS;

            } else if (token.kind_ == TokenType::TILDE) {

                expr->op_ = UnaryOpKind::TILDE;

            }



            expr->expr_ = ParseUnaryExpr();

            return expr.Get();

        }

        default:

            return ParsePrimaryExpr();

    }

}



AutoPtr<ASTExpr> Parser::ParsePrimaryExpr()

{

    Token token = lexer_.PeekToken();

    switch (token.kind_) {

        case TokenType::PARENTHESES_LEFT: {

            lexer_.GetToken();

            AutoPtr<ASTExpr> expr = ParseExpr();

            token = lexer_.PeekToken();

            if (token.kind_ != TokenType::PARENTHESES_RIGHT) {

                LogError(token, String::Format("expected ')' before %s token", token.value_.string()));

            } else {

                lexer_.GetToken();

                expr->isParenExpr = true;

            }

            return expr;

        }

        case TokenType::NUM:

            return ParseNumExpr();

        default:

            LogError(token, String::Format("this expression is not supported"));

            lexer_.SkipUntilToken(TokenType::COMMA);

            return nullptr;

    }

}



AutoPtr<ASTExpr> Parser::ParseNumExpr()

{

    Token token = lexer_.GetToken();

    AutoPtr<ASTNumExpr> expr = new ASTNumExpr;

    expr->value_ = token.value_;

    return expr.Get();

}



bool Parser::CheckType(const Token &token, const AutoPtr<ASTType> &type)

{

    if (type == nullptr) {

        return false;

    }



    if (Options::GetInstance().GetTargetLanguage().Equals("c")) {

        if (type->IsSequenceableType()) {

            LogError(token, String::Format("The sequenceable type is not supported by c language."));

            return false;

        }



        if (type->IsSmqType()) {

            LogError(token, String::Format("The smq type is not supported by c language."));

            return false;

        }



        if (Options::GetInstance().DoGenerateKernelCode()) {

            switch (type->GetTypeKind()) {

                case TypeKind::TYPE_FLOAT:

                case TypeKind::TYPE_DOUBLE:

                case TypeKind::TYPE_FILEDESCRIPTOR:

                case TypeKind::TYPE_INTERFACE:

                    LogError(token,

                        String::Format("The '%s' type is not supported by c language.", type->ToString().string()));

                    break;

                default:

                    break;

            }

        }

    } else if (Options::GetInstance().GetTargetLanguage().Equals("java")) {

        switch (type->GetTypeKind()) {

            case TypeKind::TYPE_UCHAR:

            case TypeKind::TYPE_USHORT:

            case TypeKind::TYPE_UINT:

            case TypeKind::TYPE_ULONG:

            case TypeKind::TYPE_ENUM:

            case TypeKind::TYPE_STRUCT:

            case TypeKind::TYPE_UNION:

            case TypeKind::TYPE_SMQ:

            case TypeKind::TYPE_UNKNOWN:

                LogError(token,

                    String::Format("The '%s' type is not supported by java language.", type->ToString().string()));

                return false;

            default:

                break;

        }

    }



    return true;

}



void Parser::SetAstFileType()

{

    if (ast_->GetInterfaceDef() != nullptr) {

        if (ast_->GetInterfaceDef()->IsCallback()) {

            ast_->SetAStFileType(ASTFileType::AST_ICALLBACK);

        } else {

            ast_->SetAStFileType(ASTFileType::AST_IFACE);

        }

    } else {

        ast_->SetAStFileType(ASTFileType::AST_TYPES);

    }

}



bool Parser::CheckIntegrity()

{

    if (ast_ == nullptr) {

        LogError(String("ast is nullptr."));

        return false;

    }



    if (ast_->GetName().IsEmpty()) {

        LogError(String("ast's name is empty."));

        return false;

    }



    if (ast_->GetPackageName().IsEmpty()) {

        LogError(String("ast's package name is empty."));

        return false;

    }



    switch (ast_->GetASTFileType()) {

        case ASTFileType::AST_IFACE: {

            return CheckInterfaceAst();

        }

        case ASTFileType::AST_ICALLBACK: {

            return CheckCallbackAst();

        }

        case ASTFileType::AST_SEQUENCEABLE: {

            LogError(String("it's impossible that ast is sequenceable."));

            return false;

        }

        case ASTFileType::AST_TYPES: {

            if (ast_->GetInterfaceDef() != nullptr) {

                LogError(String("custom ast cannot has interface."));

                return false;

            }

            break;

        }

        default:

            break;

    }



    return true;

}



bool Parser::CheckInterfaceAst()

{

    AutoPtr<ASTInterfaceType> interface = ast_->GetInterfaceDef();

    if (interface == nullptr) {

        LogError(String("ast's interface is empty."));

        return false;

    }



    if (ast_->GetTypeDefinitionNumber() > 0) {

        LogError(String("interface ast cannot has custom types."));

        return false;

    }



    if (interface->GetMethodNumber() == 0) {

        LogError(String("interface ast has no method."));

        return false;

    }

    return true;

}



bool Parser::CheckCallbackAst()

{

    AutoPtr<ASTInterfaceType> interface = ast_->GetInterfaceDef();

    if (interface == nullptr) {

        LogError(String("ast's interface is empty."));

        return false;

    }



    if (!interface->IsCallback()) {

        LogError(String("ast is callback, but ast's interface is not callback."));

        return false;

    }

    return true;

}



/*

 * For example

 * filePath: ./ohos/interface/foo/v1_0/IFoo.idl

 * package OHOS.Hdi.foo.v1_0;

 */

bool Parser::CheckPackageName(const String &filePath, const String &packageName)

{

    String pkgToPath = Options::GetInstance().GetPackagePath(packageName);



    int index = filePath.LastIndexOf(File::separator);

    if (index == -1) {

        return false;

    }



    String parentDir = filePath.Substring(0, index);

    return parentDir.Equals(pkgToPath);

}



bool Parser::CheckImport(const String &importName)

{

    if (!std::regex_match(importName.string(), reImport)) {

        LogError(String::Format("invalid impirt name '%s'", importName.string()));

        return false;

    }



    String idlFilePath = Options::GetInstance().GetImportFilePath(importName);

    if (!File::CheckValid(idlFilePath)) {

        LogError(String::Format("can not import '%s'", idlFilePath.string()));

        return false;

    }

    return true;

}



bool Parser::AddAst(const AutoPtr<AST> &ast)

{

    if (ast == nullptr) {

        LogError(String("ast is nullptr."));

        return false;

    }



    allAsts_[ast->GetFullName()] = ast;

    return true;

}



void Parser::LogError(const String &message)

{

    errors_.push_back(message);

}



void Parser::LogError(const Token &token, const String &message)

{

    errors_.push_back(String::Format("[%s] error:%s", LocInfo(token).string(), message.string()));

}



void Parser::ShowError()

{

    for (const auto &errMsg : errors_) {

        Logger::E(TAG, "%s", errMsg.string());

    }

}

} // namespace HDI

} // namespace OHOS