// -*- 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: Internal header for lex interfacing
///
/// Authors: Wilson Snyder
///
/// Code available from: https://www.veripool.org/verilog-perl
///
/// This header provides the interface between the lex proper VParseLex.l/.cpp
/// and the class implementation file VParse.cpp
/// It is not intended for user applications.
///
//*************************************************************************

#ifndef _VPARSELEX_H_		// Guard
#define _VPARSELEX_H_ 1

#include "VFileLine.h"
#include "VParseGrammar.h"

//======================================================================
// Externs created by flex
// We add a prefix so that other lexers/flexers in the same program won't collide.
#ifndef yy_create_buffer
# define yy_create_buffer VParseLex_create_buffer
# define yy_delete_buffer VParseLex_delete_buffer
# define yy_scan_buffer VParseLex_scan_buffer
# define yy_scan_string VParseLex_scan_string
# define yy_scan_bytes VParseLex_scan_bytes
# define yy_flex_debug VParseLex_flex_debug
# define yy_init_buffer VParseLex_init_buffer
# define yy_flush_buffer VParseLex_flush_buffer
# define yy_load_buffer_state VParseLex_load_buffer_state
# define yy_switch_to_buffer VParseLex_switch_to_buffer
# define yyin VParseLexin
# define yyleng VParseLexleng
# define yylex VParseLexlex
# define yyout VParseLexout
# define yyrestart VParseLexrestart
# define yytext VParseLextext
#endif

#ifndef YY_BUFFER_STATE
struct yy_buffer_state;
typedef struct yy_buffer_state *YY_BUFFER_STATE;
# define YY_BUF_SIZE 16384
#endif

extern int yylex();
extern void yyrestart(FILE*);

YY_BUFFER_STATE yy_create_buffer (FILE *file, int size);
YY_BUFFER_STATE yy_scan_bytes(const char *bytes, int len);
void yy_switch_to_buffer(YY_BUFFER_STATE new_buffer);
void yy_delete_buffer(YY_BUFFER_STATE b);

class VParse;

//======================================================================
/// Class entry for each lexer state

class VParseLex {
  public:	// Used only by VParseLex.cpp and VParse.cpp
    VParse* 	m_parsep;	///< Current parser
    bool	m_inCellDefine;	///< In a `celldefine

    int		m_prevLexToken;		///< previous parsed token (for lexer)
    bool	m_ahead;		///< aheadToken is valid
    int		m_aheadToken;		///< Token we read ahead
    VParseBisonYYSType m_aheadVal;	///< aheadToken's value

    int		m_pvstate;		///< "pure virtual" detection

    // Parse state
    YY_BUFFER_STATE  m_yyState;	///< flex input state

    // State to lexer
    static VParseLex* s_currentLexp;	///< Current lexing point
    static VParseBisonYYSType* s_yylvalp;
    int	prevLexToken() { return m_prevLexToken; } // Parser -> lexer communication

    // CONSTRUCTORS
    VParseLex(VParse* parsep) {
	m_parsep = parsep;
	m_inCellDefine = false;
	m_prevLexToken = 0;
	m_ahead = false;
	m_pvstate = 0;

	m_yyState = yy_create_buffer(NULL, YY_BUF_SIZE);
	s_currentLexp = this;
	yyrestart(NULL);
	debug(0);
    }
    ~VParseLex() {
	yy_delete_buffer(m_yyState);
	s_currentLexp = NULL;
    }

    void restart() { yyrestart(NULL); }

    // Internal Utilities
    static bool symEscapeless(const char* textp, size_t leng) {
	// Are \ escapes needed to print this symbol?
	if (leng<1) return false;  // Probably not a valid identifier, but better than a core dump...
	if (!isalpha(textp[0]) && textp[0] != '_') return false;
	const char* cp = textp;
	for (size_t tleng=leng; tleng; tleng--, cp++) {
	    if (!isalnum(*cp) && *cp != '_') return false;
	}
	if (VParse::isKeyword(textp, leng)) return false;
	return true;
    }

    /// Called by VParse.cpp to inform lexer
    void unputString(const char* textp);
    void unputString(const char* textp, size_t length);

    void debug(int level);
    void language(const char* value);

    int lexToBison(VParseBisonYYSType* yylvalp);
private:
    void unused();
    int yylexReadTok();
    int lexToken(VParseBisonYYSType* yylvalp);
};

#endif // Guard
