// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Parse syntax tree
//
// Code available from: http://www.veripool.org/verilator
//
//*************************************************************************
//
// Copyright 2003-2019 by Wilson Snyder.  This program is free software; you can
// redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
//*************************************************************************


#include "V3Ast.h"  // This must be before V3ParseBison.cpp, as we don't want #defines to conflict

//======================================================================
// The guts come from bison output

#include "V3ParseBison.c"

//======================================================================
// V3ParseImp functions requiring bison state

int V3ParseImp::bisonParse() {
    // Use --debugi-bison 9 to enable this
    if (PARSEP->debugBison()>=9) yydebug = 1;
    return yyparse();
}

const char* V3ParseImp::tokenName(int token) {
#if YYDEBUG || YYERROR_VERBOSE
    static const char** nameTablep = NULL;
    if (!nameTablep) {
        int size;
        for (size = 0; yytname[size]; ++size) ;
        nameTablep = new const char* [size];
        // Workaround bug in bison's which have '!' in yytname but not token values
        int iout = 0;
        for (int i = 0; yytname[i]; ++i) {
            if (yytname[i][0] == '\'') continue;
            nameTablep[iout++] = yytname[i];
        }
    }
    if (token >= 255) {
        return nameTablep[token-255];
    } else {
        static char ch[2]; ch[0] = token; ch[1] = '\0';
        return ch;
    }
#else
    return "";
#endif
}

void V3ParseImp::parserClear() {
    // Clear up any dynamic memory V3Parser required
    VARDTYPE(NULL);
}

//======================================================================
// V3ParseGrammar functions requiring bison state

void V3ParseGrammar::argWrapList(AstNodeFTaskRef* nodep) {
    // Convert list of expressions to list of arguments
    AstNode* outp = NULL;
    while (nodep->pinsp()) {
        AstNode* exprp = nodep->pinsp()->unlinkFrBack();
        // addNext can handle nulls:
        outp = AstNode::addNext(outp, new AstArg(exprp->fileline(), "", exprp));
    }
    if (outp) nodep->addPinsp(outp);
}

AstNode* V3ParseGrammar::createSupplyExpr(FileLine* fileline, string name, int value) {
    return new AstAssignW(fileline, new AstVarRef(fileline, name, true),
                          new AstConst(fileline, AstConst::StringToParse(),
                                       (value ? "'1" : "'0")));
}

AstRange* V3ParseGrammar::scrubRange(AstNodeRange* nrangep) {
    // Remove any UnsizedRange's from list
    for (AstNodeRange* nodep = nrangep, *nextp; nodep; nodep = nextp) {
        nextp = VN_CAST(nrangep->nextp(), NodeRange);
        if (!VN_IS(nodep, Range)) {
            nodep->v3error("Unsupported or syntax error: Unsized range in cell or other declaration");
            nodep->unlinkFrBack(); nodep->deleteTree(); VL_DANGLING(nodep);
        }
    }
    return VN_CAST(nrangep, Range);
}

AstNodeDType* V3ParseGrammar::createArray(AstNodeDType* basep,
                                          AstNodeRange* nrangep, bool isPacked) {
    // Split RANGE0-RANGE1-RANGE2
    // into ARRAYDTYPE0(ARRAYDTYPE1(ARRAYDTYPE2(BASICTYPE3), RANGE), RANGE)
    AstNodeDType* arrayp = basep;
    if (nrangep) {  // Maybe no range - return unmodified base type
        while (nrangep->nextp()) nrangep = VN_CAST(nrangep->nextp(), NodeRange);
        while (nrangep) {
            AstNodeRange* prevp = VN_CAST(nrangep->backp(), NodeRange);
            if (prevp) nrangep->unlinkFrBack();
            AstRange* rangep = VN_CAST(nrangep, Range);
            if (!rangep) {
                if (!VN_IS(nrangep, UnsizedRange)) {
                    nrangep->v3fatalSrc("Expected range or unsized range");
                }
                arrayp = new AstUnsizedArrayDType
                    (nrangep->fileline(), VFlagChildDType(), arrayp);
            } else if (isPacked) {
                arrayp = new AstPackArrayDType
                    (rangep->fileline(), VFlagChildDType(), arrayp, rangep);
            } else {
                arrayp = new AstUnpackArrayDType
                    (rangep->fileline(), VFlagChildDType(), arrayp, rangep);
            }
            nrangep = prevp;
        }
    }
    return arrayp;
}

AstVar* V3ParseGrammar::createVariable(FileLine* fileline, string name,
                                       AstNodeRange* arrayp, AstNode* attrsp) {
    AstNodeDType* dtypep = GRAMMARP->m_varDTypep;
    UINFO(5,"  creVar "<<name<<"  decl="<<GRAMMARP->m_varDecl
          <<"  io="<<GRAMMARP->m_varIO<<"  dt="<<(dtypep?"set":"")<<endl);
    if (GRAMMARP->m_varIO == VDirection::NONE
        && GRAMMARP->m_varDecl == AstVarType::PORT) {
        // Just a port list with variable name (not v2k format); AstPort already created
        if (dtypep) fileline->v3error("Unsupported: Ranges ignored in port-lists");
        return NULL;
    }
    if (GRAMMARP->m_varDecl == AstVarType::WREAL) {
        // dtypep might not be null, might be implicit LOGIC before we knew better
        dtypep = new AstBasicDType(fileline, AstBasicDTypeKwd::DOUBLE);
    }
    if (!dtypep) {  // Created implicitly
        dtypep = new AstBasicDType(fileline, LOGIC_IMPLICIT);
    } else {  // May make new variables with same type, so clone
        dtypep = dtypep->cloneTree(false);
    }
    //UINFO(0,"CREVAR "<<fileline->ascii()<<" decl="<<GRAMMARP->m_varDecl.ascii()<<" io="<<GRAMMARP->m_varIO.ascii()<<endl);
    AstVarType type = GRAMMARP->m_varDecl;
    if (type == AstVarType::UNKNOWN) {
        if (GRAMMARP->m_varIO.isAny()) {
            type = AstVarType::PORT;
        } else {
            fileline->v3fatalSrc("Unknown signal type declared");
        }
    }
    if (type == AstVarType::GENVAR) {
        if (arrayp) fileline->v3error("Genvars may not be arrayed: "<<name);
    }

    // Split RANGE0-RANGE1-RANGE2 into
    // ARRAYDTYPE0(ARRAYDTYPE1(ARRAYDTYPE2(BASICTYPE3), RANGE), RANGE)
    AstNodeDType* arrayDTypep = createArray(dtypep, arrayp, false);

    AstVar* nodep = new AstVar(fileline, type, name, VFlagChildDType(), arrayDTypep);
    nodep->addAttrsp(attrsp);
    if (GRAMMARP->m_varDecl != AstVarType::UNKNOWN) nodep->combineType(GRAMMARP->m_varDecl);
    if (GRAMMARP->m_varIO != VDirection::NONE) {
        nodep->declDirection(GRAMMARP->m_varIO);
        nodep->direction(GRAMMARP->m_varIO);
    }

    if (GRAMMARP->m_varDecl == AstVarType::SUPPLY0) {
        nodep->addNext(V3ParseGrammar::createSupplyExpr(fileline, nodep->name(), 0));
    }
    if (GRAMMARP->m_varDecl == AstVarType::SUPPLY1) {
        nodep->addNext(V3ParseGrammar::createSupplyExpr(fileline, nodep->name(), 1));
    }
    if (VN_IS(dtypep, ParseTypeDType)) {
        // Parser needs to know what is a type
        AstNode* newp = new AstTypedefFwd(fileline, name);
        nodep->addNext(newp);
        SYMP->reinsert(newp);
    }
    // Don't set dtypep in the ranging;
    // We need to autosize parameters and integers separately
    //
    // Propagate from current module tracing state
    if (nodep->isGenVar()) nodep->trace(false);
    else if (nodep->isParam() && !v3Global.opt.traceParams()) nodep->trace(false);
    else nodep->trace(allTracingOn(nodep->fileline()));

    // Remember the last variable created, so we can attach attributes to it in later parsing
    GRAMMARP->m_varAttrp = nodep;
    PARSEP->tagNodep(GRAMMARP->m_varAttrp);
    return nodep;
}

string V3ParseGrammar::deQuote(FileLine* fileline, string text) {
    // Fix up the quoted strings the user put in, for example "\"" becomes "
    // Reverse is V3OutFormatter::quoteNameControls(...)
    bool quoted = false;
    string newtext;
    unsigned char octal_val = 0;
    int octal_digits = 0;
    for (string::const_iterator cp = text.begin(); cp != text.end(); ++cp) {
        if (quoted) {
            if (isdigit(*cp)) {
                octal_val = octal_val*8 + (*cp-'0');
                if (++octal_digits == 3) {
                    octal_digits = 0;
                    quoted = false;
                    newtext += octal_val;
                }
            } else {
                if (octal_digits) {
                    // Spec allows 1-3 digits
                    octal_digits = 0;
                    quoted = false;
                    newtext += octal_val;
                    --cp;  // Backup to reprocess terminating character as non-escaped
                    continue;
                }
                quoted = false;
                if (*cp == 'n') newtext += '\n';
                else if (*cp == 'a') newtext += '\a';  // SystemVerilog 3.1
                else if (*cp == 'f') newtext += '\f';  // SystemVerilog 3.1
                else if (*cp == 'r') newtext += '\r';
                else if (*cp == 't') newtext += '\t';
                else if (*cp == 'v') newtext += '\v';  // SystemVerilog 3.1
                else if (*cp == 'x' && isxdigit(cp[1]) && isxdigit(cp[2])) {  // SystemVerilog 3.1
#define vl_decodexdigit(c) ((isdigit(c)?((c)-'0'):(tolower((c))-'a'+10)))
                    newtext += (char)(16*vl_decodexdigit(cp[1]) + vl_decodexdigit(cp[2]));
                    cp += 2;
                }
                else if (isalnum(*cp)) {
                    fileline->v3error("Unknown escape sequence: \\"<<*cp);
                    break;
                }
                else newtext += *cp;
            }
        }
        else if (*cp == '\\') {
            quoted = true;
            octal_digits = 0;
        }
        else if (*cp != '"') {
            newtext += *cp;
        }
    }
    return newtext;
}
