// -*- 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::Preproc: Preprocess 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 <iostream>
using namespace std;
#include "VFileLine.h"

/// Generic opaque pointer to VPreProcImp implementation class.
struct VPreProcOpaque {
    virtual ~VPreProcOpaque() {}
};
class VDefine;

//**********************************************************************
// VPreProc
/// Verilog Preprocessor.
////
/// This defines a preprocessor.  Functions are virtual so users can override them.
/// After creating, call openFile(), then getline() in a loop.  The class will to the rest...

class VPreProc {
public:
    VPreProc();
    void configure(VFileLine* filelinep);
    virtual ~VPreProc();

    // STATE
private:
    int		m_keepComments;
    int		m_keepWhitespace;
    bool	m_lineDirectives;
    bool	m_pedantic;
    bool	m_synthesis;

public:
    // CONSTANTS
    enum MiscConsts {
 	DEFINE_RECURSION_LEVEL_MAX = 1000,	// How many `def substitutions before an error
 	INCLUDE_DEPTH_MAX = 500,		// How many `includes deep before an error
	STREAM_DEPTH_LEVEL_MAX = 2000,		// How many streams deep (sometimes `def deep) before an error
	//					// Set more than DEFINE_RECURSION_LEVEL_MAX or INCLUDE_DEPTH_MAX
 	NEWLINES_VS_TICKLINE = 20		// Use `line in place of this many newlines
    };

    // ACCESSORS
    /// Insert given file into this point in input stream
    void openFile(string filename, VFileLine* filelinep=NULL);
    void debug(int level);	///< Set debugging level
    string getall(size_t approx_chunk);	///< Return all lines, or at least approx_chunk bytes. (Null if done.)
    string getline();		///< Return next line/lines. (Null if done.)
    bool isEof();		///< Return true on EOF.
    void insertUnreadback(string text);

    VFileLine* fileline();	///< File/Line number for last getline call

    // 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
    int  keepComments() { return m_keepComments; }
    void keepComments(int flag) { m_keepComments=flag; }	// Return comments, 0=no, 1=yes, 2=callback
    int  keepWhitespace() { return m_keepWhitespace; }
    void keepWhitespace(int flag) { m_keepWhitespace=flag; }	// Return extra whitespace
    bool lineDirectives() { return m_lineDirectives; }
    void lineDirectives(bool flag) { m_lineDirectives=flag; }	// Insert `line directives
    bool pedantic() { return m_pedantic; }
    void pedantic(bool flag) { m_pedantic=flag; }	// Obey standard; Don't substitute `error
    bool synthesis() { return m_synthesis; }
    void synthesis(bool flag) { m_synthesis=flag; }	// Ignore translate off

    // CALLBACK METHODS
    // This probably will want to be overridden for given child users of this class.
    virtual void comment(string cmt) = 0;		///< Comment detected (if keepComments==2)
    virtual void include(string filename) = 0;	///< Request a include file be processed
    virtual void define(string name, string value, string params) = 0; ///< `define without any parameters
    virtual void undef(string name) = 0;		///< Remove a definition
    virtual void undefineall() = 0;			///< Remove all non-command-line definitions
    virtual bool defExists(string name) = 0;	///< Return true if define exists
    virtual string defParams(string name) = 0;	///< Return parameter list if define exists
    virtual string defValue(string name) = 0;	///< Return value of given define (should exist)
    virtual string defSubstitute(string substitute) = 0;	///< Return value to substitute for given post-parameter value

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

private:
    VPreProcOpaque*	m_opaquep;	///< Pointer to parser's implementation data.
};

#endif // Guard
