// -*- C++ -*-
//*************************************************************************
//
// Copyright 2000-2024 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.
//
// This program 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.
//
//*************************************************************************
/// \file
/// \brief Verilog::Parse: Parses verilog code
///
/// Authors: Wilson Snyder
///
/// Code available from: https://www.veripool.org/verilog-perl
///
//*************************************************************************

#ifndef _VPREPROC_H_
#define _VPREPROC_H_ 1

#include <string>
#include <map>
#include <set>
#include <deque>
#include <iostream>
using namespace std;
#include "VFileLine.h"
#include "VSymTable.h"

class VParseLex;  // Be sure not to include it, or the Bison class will get upset
class VParseGrammar;  // Be sure not to include it, or the Lex class will get upset
struct VParseBisonYYSType;

struct av;

struct VParseHashElem {
    const char* keyp;
    enum {ELEM_STR, ELEM_INT} val_type;
    string val_str;  // When val_type==ELEM_STR
    int val_int;  // When val_type==ELEM_INT
    VParseHashElem() { keyp = NULL; }
    ~VParseHashElem() {}
};

//**********************************************************************
// VParse

class VParse {
private:
    // MEMBERS
    // Mode
    bool	m_sigParser;	///< SigParser not simple Verilog::Parser

    // State
    VFileLine*	m_inFilelinep;	///< Next token's starting point
    int		m_debug;	///< Debugging level
    VParseLex*	m_lexp;		///< Current lexer state (NULL = closed)
    VParseGrammar* m_grammarp;	///< Current bison state (NULL = closed)
    bool	m_eof;		///< At end of file
    bool	m_callbackMasterEna; ///< Callbacks are enabled

    bool	m_useUnreadback;///< Need m_unreadback tracking
    bool	m_useProtected;	///< Need `protected tracking
    bool	m_usePinselects;///< Need bit-select parsing
    string	m_unreadback;	///< Otherwise unprocessed whitespace before current token
    deque<string> m_buffers;	///< Buffer of characters to process

    int		m_anonNum;	///< Number of next anonymous object

    VSymStack	m_syms;		///< Symbol stack

    VAstEnt*	m_symTableNextId;	///< Symbol table for next lexer lookup

public:  // But for internalish use only
    // METHODS
    int lexToBison(VParseBisonYYSType* yylvalp);
    bool eofToLex() const { return m_eof && m_buffers.empty(); }
    bool inCellDefine() const;
    size_t inputToLex(char* buf, size_t max_size);

    // Symbol table
    VSymStack&	syms() { return m_syms; }
    VAstEnt* symTableNextId() const { return m_symTableNextId; }
    void symTableNextId(VAstEnt* entp) {
	if (debug()) {
	    if (entp) cout <<"symTableNextId under "<<entp<<"-"<<entp->type().ascii()<<endl;
	    else cout <<"symTableNextId under NULL"<<endl;
	}
	m_symTableNextId = entp;
    }
    string symObjofUpward() {
	return m_syms.objofUpward();
    }
    void symPushNew(VAstType type, const string& name) {
	symPushNewUnder(type, name, NULL);
    }
    void symPushNewUnder(VAstType type, const string& name, VAstEnt* parentp) {
	if (!parentp) parentp = m_syms.currentSymp();
	m_syms.pushScope(parentp->replaceInsert(type,name));
    }
    void symPushNewAnon(VAstType type) {
	string name = "__anon";
	name += type.ascii() + cvtToStr(++m_anonNum);
	symPushNew(type,name);
    }
    void symPopScope(VAstType type) {
	if (m_syms.curType() != type) {
	    string msg = (string)("Symbols suggest ending a '")+m_syms.curType().ascii()+"' but parser thinks ending a '"+type.ascii()+"'";
	    this->error(msg);
	    return;
	}
	m_syms.popScope(inFilelinep());
    }

private:
    void fakeBison();

public:
    // CONSTRUCTORS
    VParse(VFileLine* filelinep, av* symsp,
	   bool sigParser, bool useUnreadbackFlag, bool useProtected, bool usePinselects);
    virtual ~VParse();

    // ACCESSORS
    /// Insert given file into this point in input stream
    int debug() const { return m_debug; }	///< Set debugging level
    void debug(int level);			///< Set debugging level
    void parse(const string& text);		///< Add given text to
    void setEof();				///< Got a end of file
    bool sigParser() const { return m_sigParser; }
    void language(const char* valuep);
    void callbackMasterEna(bool flag) { m_callbackMasterEna=flag; }
    bool callbackMasterEna() const { return m_callbackMasterEna; }
    bool useProtected() const { return m_useProtected; }
    bool usePinSelects() const { return m_usePinselects; }

    VFileLine* inFilelinep() const;		///< File/Line number for last callback
    void inFileline(const string& filename, int lineno) { m_inFilelinep = m_inFilelinep->create(filename, lineno); }
    void inFilelineInc() { m_inFilelinep = inFilelinep()->create(inFilelinep()->lineno()+1); }
    void inLineDirective(const char* text) { int ign; m_inFilelinep = inFilelinep()->lineDirective(text, ign/*ref*/); }

    string unreadback() const { return (m_useUnreadback ? m_unreadback : "new(...,use_unreadback=>0) was used"); }
    void unreadback(const string& text) { if (m_useUnreadback && callbackMasterEna()) m_unreadback = text; }
    void unreadbackCat(const string& text) { if (m_useUnreadback && callbackMasterEna()) m_unreadback += text; }
    void unreadbackCat(const char* textp, size_t len) { unreadbackCat(string(textp,len)); }

    // The default behavior is to pass all unknown `defines right through.
    // This lets the user determine how to report the errors.  It also nicely
    // allows `celldefine and such to remain in the output stream.

    // CONTROL METHODS
    // These options control how the parsing proceeds

    // CALLBACK METHODS
    // This probably will want to be overridden for given child users of this class.

    // CALLBACKGEN_H_VIRTUAL_0
    // CALLBACKGEN_GENERATED_BEGIN - GENERATED AUTOMATICALLY by callbackgen
    // Verilog::Parser Callback methods
    virtual void attributeCb(VFileLine* fl, const string& text) = 0;
    virtual void commentCb(VFileLine* fl, const string& text) = 0;
    virtual void endparseCb(VFileLine* fl, const string& text) = 0;
    virtual void keywordCb(VFileLine* fl, const string& text) = 0;
    virtual void numberCb(VFileLine* fl, const string& text) = 0;
    virtual void operatorCb(VFileLine* fl, const string& text) = 0;
    virtual void preprocCb(VFileLine* fl, const string& text) = 0;
    virtual void stringCb(VFileLine* fl, const string& text) = 0;
    virtual void symbolCb(VFileLine* fl, const string& text) = 0;
    virtual void sysfuncCb(VFileLine* fl, const string& text) = 0;
    // Verilog::SigParser Callback methods
    virtual void classCb(VFileLine* fl, const string& kwd, const string& name, const string& virt) = 0;
    virtual void contassignCb(VFileLine* fl, const string& kwd, const string& lhs, const string& rhs) = 0;
    virtual void covergroupCb(VFileLine* fl, const string& kwd, const string& name) = 0;
    virtual void defparamCb(VFileLine* fl, const string& kwd, const string& lhs, const string& rhs) = 0;
    virtual void endcellCb(VFileLine* fl, const string& kwd) = 0;
    virtual void endclassCb(VFileLine* fl, const string& kwd) = 0;
    virtual void endgroupCb(VFileLine* fl, const string& kwd) = 0;
    virtual void endinterfaceCb(VFileLine* fl, const string& kwd) = 0;
    virtual void endmodportCb(VFileLine* fl, const string& kwd) = 0;
    virtual void endmoduleCb(VFileLine* fl, const string& kwd) = 0;
    virtual void endpackageCb(VFileLine* fl, const string& kwd) = 0;
    virtual void endprogramCb(VFileLine* fl, const string& kwd) = 0;
    virtual void endtaskfuncCb(VFileLine* fl, const string& kwd) = 0;
    virtual void functionCb(VFileLine* fl, const string& kwd, const string& name, const string& data_type) = 0;
    virtual void importCb(VFileLine* fl, const string& package, const string& id) = 0;
    virtual void instantCb(VFileLine* fl, const string& mod, const string& cell, const string& range) = 0;
    virtual void interfaceCb(VFileLine* fl, const string& kwd, const string& name) = 0;
    virtual void modportCb(VFileLine* fl, const string& kwd, const string& name) = 0;
    virtual void moduleCb(VFileLine* fl, const string& kwd, const string& name, bool, bool celldefine) = 0;
    virtual void packageCb(VFileLine* fl, const string& kwd, const string& name) = 0;
    virtual void parampinCb(VFileLine* fl, const string& name, const string& conn, int index) = 0;
    virtual void pinCb(VFileLine* fl, const string& name, const string& conn, int index) = 0;
    virtual void pinselectsCb(VFileLine* fl, const string& name, unsigned int arraycnt2, unsigned int elemcnt2, const VParseHashElem* conns2, int index) = 0;
    virtual void portCb(VFileLine* fl, const string& name, const string& objof, const string& direction, const string& data_type
	, const string& array, int index) = 0;
    virtual void programCb(VFileLine* fl, const string& kwd, const string& name) = 0;
    virtual void taskCb(VFileLine* fl, const string& kwd, const string& name) = 0;
    virtual void varCb(VFileLine* fl, const string& kwd, const string& name, const string& objof, const string& net
	, const string& data_type, const string& array, const string& value) = 0;
    // CALLBACKGEN_GENERATED_END - GENERATED AUTOMATICALLY by callbackgen

    // CALLBACKGEN_KEYWORDS
    // CALLBACKGEN_GENERATED_BEGIN - GENERATED AUTOMATICALLY by callbackgen
    static bool isKeyword(const char* kwd, int leng) {
	static set<string> s_map;
	if (s_map.empty()) {
	    const char* kwds[] = {
		"accept_on","alias","always","always_comb","always_ff","always_latch","and",
		"assert","assign","assume","automatic","before","begin","bind",
		"bins","binsof","bit","break","buf","bufif0","bufif1",
		"byte","case","casex","casez","cell","chandle","checker",
		"class","clocking","cmos","config","const","constraint","context",
		"continue","cover","covergroup","coverpoint","cross","deassign","default",
		"defparam","design","disable","dist","do","edge","else",
		"end","endcase","endchecker","endclass","endclocking","endconfig","endfunction",
		"endgenerate","endgroup","endinterface","endmodule","endpackage","endprimitive","endprogram",
		"endproperty","endsequence","endspecify","endtable","endtask","enum","event",
		"eventually","expect","export","extends","extern","final","first_match",
		"for","force","foreach","forever","fork","forkjoin","function",
		"generate","genvar","global","highz0","highz1","if","iff",
		"ifnone","ignore_bins","illegal_bins","implements","implies","import","incdir",
		"include","initial","inout","input","inside","instance","int",
		"integer","interconnect","interface","intersect","join","join_any","join_none",
		"large","let","liblist","library","local","localparam","logic",
		"longint","macromodule","matches","medium","modport","module","nand",
		"negedge","nettype","new","nexttime","nmos","nor","noshowcancelled",
		"not","notif0","notif1","null","or","output","package",
		"packed","parameter","pmos","posedge","primitive","priority","program",
		"property","protected","pull0","pull1","pulldown","pullup","pulsestyle_ondetect",
		"pulsestyle_onevent","pure","rand","randc","randcase","randsequence","rcmos",
		"real","realtime","ref","reg","reject_on","release","repeat",
		"restrict","return","rnmos","rpmos","rtran","rtranif0","rtranif1",
		"s_always","s_eventually","s_nexttime","s_until","s_until_with","scalared","sequence",
		"shortint","shortreal","showcancelled","signed","small","soft","solve",
		"specify","specparam","static","strength","string","strong","strong0",
		"strong1","struct","super","supply0","supply1","sync_accept_on","sync_reject_on",
		"table","tagged","task","this","throughout","time","timeprecision",
		"timeunit","tran","tranif0","tranif1","tri","tri0","tri1",
		"triand","trior","trireg","type","typedef","union","unique",
		"unique0","unsigned","until","until_with","untyped","use","uwire",
		"var","vectored","virtual","void","wait","wait_order","wand",
		"weak","weak0","weak1","while","wildcard","wire","with",
		"within","wor","xnor","xor",""};
	    for (const char** k=kwds; **k; k++) s_map.insert(*k);
	}
	string str(kwd,leng);
	return s_map.end() != s_map.find(str);
    }
    // CALLBACKGEN_GENERATED_END - GENERATED AUTOMATICALLY by callbackgen

    // UTILITIES
    void error(const string& msg) { inFilelinep()->error(msg); }	///< Report a error
    void fatal(const string& msg) { inFilelinep()->fatal(msg); }	///< Report a fatal error
};

#endif // Guard
