#/* Verilog.xs -- Verilog Booter  -*- C++ -*-
#*********************************************************************
#*
#* DESCRIPTION: Verilog::Parser Perl XS interface
#*
#* Author: Wilson Snyder <wsnyder@wsnyder.org>
#*
#* Code available from: http://www.veripool.org/
#*
#*********************************************************************
#*
#* Copyright 2000-2014 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.
#*
#* You should have received a copy of the Perl Artistic License
#* along with this module; see the file COPYING.  If not, see
#* www.cpan.org
#*
#***********************************************************************
#* Note with C++ XS libraries, the CLASS parameter is implied...
#***********************************************************************/

/* Mine: */
#include "VParse.h"
#include "VSymTable.h"
#include "VAst.h"
#include <cstring>
#include <deque>

/* Perl */
extern "C" {
# include "EXTERN.h"
# include "perl.h"
# include "XSUB.h"
}

#ifdef open
# undef open	/* Perl 64 bit on solaris has a nasty hack that redefines open */
#endif

class VFileLineParseXs;

#//**********************************************************************
#// Parseressor derived classes, so we can override the callbacks to call perl.

class VParserXs : public VParse {
public:
    SV*		m_self;	// Class called from (the hash, not SV pointing to the hash)
    VFileLine*	m_cbFilelinep;	///< Last callback's starting point
    deque<VFileLineParseXs*> m_filelineps;

    // CALLBACKGEN_H_MEMBERS
    // CALLBACKGEN_GENERATED_BEGIN - GENERATED AUTOMATICALLY by callbackgen
    struct {  // Bit packed to help the cache
        bool m_useCb_attribute:1;
        bool m_useCb_class:1;
        bool m_useCb_comment:1;
        bool m_useCb_contassign:1;
        bool m_useCb_covergroup:1;
        bool m_useCb_defparam:1;
        bool m_useCb_endcell:1;
        bool m_useCb_endclass:1;
        bool m_useCb_endgroup:1;
        bool m_useCb_endinterface:1;
        bool m_useCb_endmodport:1;
        bool m_useCb_endmodule:1;
        bool m_useCb_endpackage:1;
        bool m_useCb_endparse:1;
        bool m_useCb_endprogram:1;
        bool m_useCb_endtaskfunc:1;
        bool m_useCb_function:1;
        bool m_useCb_import:1;
        bool m_useCb_instant:1;
        bool m_useCb_interface:1;
        bool m_useCb_keyword:1;
        bool m_useCb_modport:1;
        bool m_useCb_module:1;
        bool m_useCb_number:1;
        bool m_useCb_operator:1;
        bool m_useCb_package:1;
        bool m_useCb_parampin:1;
        bool m_useCb_pin:1;
        bool m_useCb_port:1;
        bool m_useCb_preproc:1;
        bool m_useCb_program:1;
        bool m_useCb_string:1;
        bool m_useCb_symbol:1;
        bool m_useCb_sysfunc:1;
        bool m_useCb_task:1;
        bool m_useCb_var:1;
    };
    // CALLBACKGEN_GENERATED_END - GENERATED AUTOMATICALLY by callbackgen

    VFileLine* cbFilelinep() const { return m_cbFilelinep; }
    void cbFileline(VFileLine* filelinep) { m_cbFilelinep = filelinep; }

    VParserXs(VFileLine* filelinep, av* symsp, bool sigparser, bool useUnreadback)
	: VParse(filelinep, symsp, sigparser, useUnreadback)
	, m_cbFilelinep(filelinep)
	{ set_cb_use(); }
    virtual ~VParserXs();

    // CALLBACKGEN_CB_USE
    // CALLBACKGEN_GENERATED_BEGIN - GENERATED AUTOMATICALLY by callbackgen
    void set_cb_use() {
       m_useCb_attribute = true;
       m_useCb_class = true;
       m_useCb_comment = true;
       m_useCb_contassign = true;
       m_useCb_covergroup = true;
       m_useCb_defparam = true;
       m_useCb_endcell = true;
       m_useCb_endclass = true;
       m_useCb_endgroup = true;
       m_useCb_endinterface = true;
       m_useCb_endmodport = true;
       m_useCb_endmodule = true;
       m_useCb_endpackage = true;
       m_useCb_endparse = true;
       m_useCb_endprogram = true;
       m_useCb_endtaskfunc = true;
       m_useCb_function = true;
       m_useCb_import = true;
       m_useCb_instant = true;
       m_useCb_interface = true;
       m_useCb_keyword = true;
       m_useCb_modport = true;
       m_useCb_module = true;
       m_useCb_number = true;
       m_useCb_operator = true;
       m_useCb_package = true;
       m_useCb_parampin = true;
       m_useCb_pin = true;
       m_useCb_port = true;
       m_useCb_preproc = true;
       m_useCb_program = true;
       m_useCb_string = true;
       m_useCb_symbol = true;
       m_useCb_sysfunc = true;
       m_useCb_task = true;
       m_useCb_var = true;
   }
    // CALLBACKGEN_GENERATED_END - GENERATED AUTOMATICALLY by callbackgen
    // CALLBACKGEN_H_VIRTUAL
    // CALLBACKGEN_GENERATED_BEGIN - GENERATED AUTOMATICALLY by callbackgen
    // Verilog::Parser Callback methods
    virtual void attributeCb(VFileLine* fl, const string& text);
    virtual void commentCb(VFileLine* fl, const string& text);
    virtual void endparseCb(VFileLine* fl, const string& text);
    virtual void keywordCb(VFileLine* fl, const string& text);
    virtual void numberCb(VFileLine* fl, const string& text);
    virtual void operatorCb(VFileLine* fl, const string& text);
    virtual void preprocCb(VFileLine* fl, const string& text);
    virtual void stringCb(VFileLine* fl, const string& text);
    virtual void symbolCb(VFileLine* fl, const string& text);
    virtual void sysfuncCb(VFileLine* fl, const string& text);
    // Verilog::SigParser Callback methods
    virtual void classCb(VFileLine* fl, const string& kwd, const string& name, const string& virt);
    virtual void contassignCb(VFileLine* fl, const string& kwd, const string& lhs, const string& rhs);
    virtual void covergroupCb(VFileLine* fl, const string& kwd, const string& name);
    virtual void defparamCb(VFileLine* fl, const string& kwd, const string& lhs, const string& rhs);
    virtual void endcellCb(VFileLine* fl, const string& kwd);
    virtual void endclassCb(VFileLine* fl, const string& kwd);
    virtual void endgroupCb(VFileLine* fl, const string& kwd);
    virtual void endinterfaceCb(VFileLine* fl, const string& kwd);
    virtual void endmodportCb(VFileLine* fl, const string& kwd);
    virtual void endmoduleCb(VFileLine* fl, const string& kwd);
    virtual void endpackageCb(VFileLine* fl, const string& kwd);
    virtual void endprogramCb(VFileLine* fl, const string& kwd);
    virtual void endtaskfuncCb(VFileLine* fl, const string& kwd);
    virtual void functionCb(VFileLine* fl, const string& kwd, const string& name, const string& data_type);
    virtual void importCb(VFileLine* fl, const string& package, const string& id);
    virtual void instantCb(VFileLine* fl, const string& mod, const string& cell, const string& range);
    virtual void interfaceCb(VFileLine* fl, const string& kwd, const string& name);
    virtual void modportCb(VFileLine* fl, const string& kwd, const string& name);
    virtual void moduleCb(VFileLine* fl, const string& kwd, const string& name, bool, bool celldefine);
    virtual void packageCb(VFileLine* fl, const string& kwd, const string& name);
    virtual void parampinCb(VFileLine* fl, const string& name, const string& conn, int index);
    virtual void pinCb(VFileLine* fl, const string& name, const string& conn, int index);
    virtual void portCb(VFileLine* fl, const string& name, const string& objof, const string& direction, const string& data_type
	, const string& array, int index);
    virtual void programCb(VFileLine* fl, const string& kwd, const string& name);
    virtual void taskCb(VFileLine* fl, const string& kwd, const string& name);
    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);
    // CALLBACKGEN_GENERATED_END - GENERATED AUTOMATICALLY by callbackgen

    void useCbEna(const char* name, bool flag);
    void call(string* rtnStrp, int params, const char* method, ...);
};

class VFileLineParseXs : public VFileLine {
    VParserXs*	m_vParserp;		// Parser handling the errors

public:
    VFileLineParseXs(VParserXs* pp) : VFileLine(true), m_vParserp(pp) { if (pp) pushFl(); }
    virtual ~VFileLineParseXs() { }
    virtual VFileLine* create(const string& filename, int lineno) {
	VFileLineParseXs* filelp = new VFileLineParseXs(m_vParserp);
	filelp->init(filename, lineno);
	return filelp;
    }
    virtual void error(const string& msg);	// Report a error at given location
    void setParser(VParserXs* pp) {
	m_vParserp=pp;
	pushFl(); // The very first construction used pp=NULL, as pp wasn't created yet so make it now
    }
    // Record the structure so we can delete it later
    void pushFl() { m_vParserp->m_filelineps.push_back(this); }
};

#//**********************************************************************
#// Overrides error handling virtual functions to invoke callbacks

void VFileLineParseXs::error(const string& msg) {
    static string holdmsg; holdmsg = msg;
    m_vParserp->cbFileline(this);
    // Call always, not just if callbacks enabled
    m_vParserp->call(NULL, 1,"error",holdmsg.c_str());
}

#//**********************************************************************
#// Overrides of virtual functions to invoke callbacks

#include "Parser_callbackgen.cpp"

#//**********************************************************************
#// VParserXs functions

VParserXs::~VParserXs() {
    for (deque<VFileLineParseXs*>::iterator it=m_filelineps.begin(); it!=m_filelineps.end(); ++it) {
	delete *it;
    }
}

#//**********************************************************************
#// General callback invoker

void VParserXs::call (
    string* rtnStrp,	/* If non-null, load return value here */
    int params,		/* Number of parameters */
    const char* method,	/* Name of method to call */
    ...)		/* Arguments to pass to method's @_ */
{
    // Call $perlself->method (passedparam1, parsedparam2)
    if (debug()) cout << "CALLBACK "<<method<<endl;
    va_list ap;
    va_start(ap, method);
    {
	dSP;				/* Initialize stack pointer */
	ENTER;				/* everything created after here */
	SAVETMPS;			/* ...is a temporary variable. */
	PUSHMARK(SP);			/* remember the stack pointer */
	SV* selfsv = newRV_inc(m_self);	/* $self-> */
	XPUSHs(sv_2mortal(selfsv));

	while (params--) {
	    char* text = va_arg(ap, char *);
	    SV* sv;
	    if (text) {
		sv = sv_2mortal(newSVpv (text, 0));
	    } else {
		sv = &PL_sv_undef;
	    }
	    XPUSHs(sv);			/* token */
	}

	PUTBACK;			/* make local stack pointer global */

	if (rtnStrp) {
	    int rtnCount = perl_call_method ((char*)method, G_SCALAR);
	    SPAGAIN;			/* refresh stack pointer */
	    if (rtnCount > 0) {
		SV* sv = POPs;
		//printf("RTN %ld %d %s\n", SvTYPE(sv),SvTRUE(sv),SvPV_nolen(sv));
#ifdef SvPV_nolen	// Perl 5.6 and later
		*rtnStrp = SvPV_nolen(sv);
#else
		*rtnStrp = SvPV(sv,PL_na);
#endif
	    }
	    PUTBACK;
	} else {
	    perl_call_method ((char*)method, G_DISCARD | G_VOID);
	}

	FREETMPS;			/* free that return value */
	LEAVE;				/* ...and the XPUSHed "mortal" args.*/
    }
    va_end(ap);
}

#//**********************************************************************

MODULE = Verilog::Parser  PACKAGE = Verilog::Parser

#//**********************************************************************
#// self->_new (class, sigparser)

static VParserXs *
VParserXs::_new (SV* SELF, AV* symsp, bool sigparser, bool useUnreadback)
PROTOTYPE: $$$$
CODE:
{
    if (CLASS) {}  /* Prevent unused warning */
    if (!SvROK(SELF)) { warn("${Package}::$func_name() -- SELF is not a hash reference"); }
    VFileLineParseXs* filelinep = new VFileLineParseXs(NULL/*ok,for initial*/);
    VParserXs* parserp = new VParserXs(filelinep, symsp, sigparser, useUnreadback);
    filelinep->setParser(parserp);
    parserp->m_self = SvRV(SELF);
    RETVAL = parserp;
}
OUTPUT: RETVAL

#//**********************************************************************
#// self->_DESTROY()

void
VParserXs::_DESTROY()
PROTOTYPE: $
CODE:
{
    delete THIS;
}

#//**********************************************************************
#// self->debug(level)

void
VParserXs::_debug (level)
int level
PROTOTYPE: $$
CODE:
{
    THIS->debug(level);
    VAstEnt::debug(level);
}

#//**********************************************************************
#// self->_callback_master_enable(flag)
#// Turn off callbacks during std:: parsing

void
VParserXs::_callback_master_enable (flag)
bool flag
PROTOTYPE: $$
CODE:
{
    THIS->callbackMasterEna(flag);
}

#//**********************************************************************
#// self->_use_cb(name,flag)
#// Turn off specified callback

void
VParserXs::_use_cb(const char* name, bool flag)
PROTOTYPE: $$$
CODE:
{
    THIS->useCbEna(name,flag);
}

#//**********************************************************************
#// self->eof()

void
VParserXs::eof ()
PROTOTYPE: $
CODE:
{
    THIS->setEof();
}
#//**********************************************************************
#// self->filename([setit])

const char *
VParserXs::filename (const char* flagp="")
PROTOTYPE: $;$
CODE:
{
    if (!THIS) XSRETURN_UNDEF;
    if (items > 1) {
	THIS->inFileline(flagp, THIS->inFilelinep()->lineno());
	THIS->cbFileline(THIS->inFilelinep());
    }
    RETVAL = THIS->cbFilelinep()->filename().c_str();
}
OUTPUT: RETVAL

#//**********************************************************************
#// self->language()

void
VParserXs::language (valuep)
const char* valuep
PROTOTYPE: $$
CODE:
{
    if (items > 1) {
        THIS->language(valuep);
    }
}

#//**********************************************************************
#// self->lineno([setit])

int
VParserXs::lineno (int flag=0)
PROTOTYPE: $;$
CODE:
{
    if (!THIS) XSRETURN_UNDEF;
    if (items > 1) {
	THIS->inFileline(THIS->inFilelinep()->filename(), flag);
	THIS->cbFileline(THIS->inFilelinep());
    }
    RETVAL = (THIS->cbFilelinep()->lineno());
}
OUTPUT: RETVAL

#//**********************************************************************
#// self->parse()

void
VParserXs::parse (const char* textp)
PROTOTYPE: $$
CODE:
{
    THIS->parse(textp);
}

#//**********************************************************************
#// self->selftest()

void
VParserXs::selftest ()
PROTOTYPE: $
CODE:
{
    VSymStack::selftest();
    assert(VParse::isKeyword("wire",strlen("wire")));
    assert(!VParse::isKeyword("wire99",strlen("wide99")));
}

#//**********************************************************************
#// self->unreadback()

SV*
VParserXs::unreadback (const char* flagp="")
PROTOTYPE: $;$
CODE:
{
    if (!THIS) XSRETURN_UNDEF;
    // Set RETVAL to a SV before we replace with the new value, and c_str may change
    RETVAL = newSVpv(THIS->unreadback().c_str(), THIS->unreadback().length());
    if (items > 1) {
	THIS->unreadback(flagp);
    }
}
OUTPUT: RETVAL

#//**********************************************************************
#// self->unreadbackCat()

void
VParserXs::unreadbackCat (SV* textsvp)
PROTOTYPE: $$
CODE:
{
    if (!THIS) XSRETURN_UNDEF;
    STRLEN textlen;
    const char* textp = SvPV(textsvp, textlen);
    THIS->unreadbackCat(textp, textlen);
}
