//
// lexer.h
// SCOL lexical analyser
// F.J. Alberti
// Created: 30/04/2001
// Last Modified: 28/05/2001
//
// Modification history:
// FA     28/05/2001      Added mode kModeScript for reading scripts
// FA     15/06/2001      Set kMaxStringLiteralLength to 16kb
// FA     17/07/2001      Move Token class and related constants to separate token.h file
// FA     17/07/2001      Remove MAXSIZETOKEN #definition (as it is declared in token.h)
// FA     22/11/2001      Disable C4244 and C4800 warnings for Microsoft's compiler
// FA     18/03/2002      Replace eatUpTo() method by the more low-level (and more effective) discardLine()
//

#ifndef _LEXER_H_
#define _LEXER_H_


#include <ctype.h>
#include <stdio.h>
#include "base.h"
#include "stack.h"
#include "textspan.h"
#include "token.h"


#if defined(_MSC_VER)
#  pragma warning (disable: 4244)
#  pragma warning (disable: 4800)
#endif


// Constraints
const uint kMaxPutback   = 5;
const int  kEof          = EOF;
//const uint kMaxLookahead = 1;


// Utilities
bool isWhite(int c);
bool is3xWhite(int c);
bool isLetter(int c);
bool isDigit(int c, uint base);
bool isDigit(int c);
bool isBaseSpecifier(int c);
bool isSpecial(int c);
bool a2f(const char* s, float* res);
bool f2a(float f, char* res);
bool h2i(const char* s, int* res);
bool i2h(int i, char* res);
bool a2i(const char* s, int* res);
bool a2i10(const char* s, int* res);
bool i2a(int i, char* res);


inline bool isWhite(int c)
{
  return isspace(c);
}


inline bool is3xWhite(int c)
{
  return c >= '\0' && c <= ' ';   // it contains isWhite()
}


inline bool isLetter(int c)
{
  return isalpha(c) || c == '_';
}


inline bool isDigit(int c)
{
  return isDigit(c, 10);
}





class Lexer {
public:
  enum Error {
    kErrOk               = 0,
    kErrIllegalCharacter = -1,
    kErrIllegalToken     = -2,
    kErrIllegalLength    = -3,
    kErrReadPastEnd      = -4,
    kErrCommentUnclosed  = -5
    //kErrIntegerOverflows = -6
  };

  enum Mode {
    kModePlain,
    kModeScript,                      // 28/05/2001 (Added support for scripts)
    kModeDotAddressing
  };
  
  Lexer(const char* source);
//Lexer(FILE* file);
  ~Lexer();
  
  void redirect(const char* source);  // deprecated
//void redirect(FILE* file);          // deprecated
  void advance();
  void rewind();                      // deprecated 
  void discardLine();
  const Token& look();
  const Token& next();
  bool accept(Token::Kind kind);
  const Token& current() const;
//bool readIdentifier();              // needed by Script::execute()?
//bool readLiteral();                 // needed by Script::execute()?
  bool eoi() const;
  Error err() const;
  bool ok() const;
  void setMode(Mode mode);
  Mode getMode() const;
  void printPosition(const Token& tok);
  void printPosition();
  void printSource(const TextSpan& span);

private:
  void skipWhites();
  void advance(Token& tok);
  int getChar();
  void putChar(int c);
  void readString(Token& tok);
  void readCharacter(Token& tok);
  void readNumber(Token& tok);
  void readIdentifier(Token& tok);
  void readOperator(Token& tok);

  const char*     _source;                // Input source text
  Mode            _mode;                  // Current mode
  TextPosition    _pos;                   // Current position
  Token           _look;                  // Lookahead token
  Token           _current;               // Current token (the one just read)
  bool            _eoi;                   // Reached end of input?
  Stack<char>     _unread;                // Characters put back and waiting to be reread
  Error           _err;                   // Error code
  uint*           _lineOffs;              // Line offsets cache (used only if printPosition() is called)
};


inline bool Lexer::accept(Token::Kind kind)
{
  return next().is(kind);
}


inline const Token& Lexer::current() const
{
  return _current;
}


inline bool Lexer::eoi() const
{
  return _eoi;
}


inline Lexer::Error Lexer::err() const
{
  return _err;
}


inline bool Lexer::ok() const
{
  return _err == 0;
}


inline void Lexer::setMode(Mode mode)
{
  _mode = mode;
}


inline Lexer::Mode Lexer::getMode() const
{
  return _mode;
}


inline void Lexer::printPosition()
{
  printPosition(_current);
}



//
// Analyser structure (old convention)
//

#define MAXSIZEBUF    (4*1024)


struct Manlyz
{
  Manlyz() : lex(0) {}
  ~Manlyz() {}

  Lexer lex;                  // embedded lexer instance
  char  *extbuf;              // buffer (coming from a file)
  char  buf[MAXSIZEBUF];      // buffer (coming from a string; extbuf == NULL)
  int   giveback;             // lookahead token
  char  tok[MAXSIZETOKEN+1];  // current token
  char  mess[1024];           // error message (used by parser)
};

typedef struct Manlyz* manlyz;


#define MANLZNF       -1    // Not found
#define MANLZEOF      -2    // End of file
#define MANLZTOOLONG  -3    // Token too long


// Open a package from a file
int Mopenpack(manlyz z, char *filename);

// Open a package from a string
int Mopenstring(manlyz z, char *s);

// Close a package
int Mclosepack(manlyz z);

// Read next token
int Mreadtok(manlyz z);

// Reinit package
int Mreinitpack(manlyz z);

// Show current context (last line and last token read underlined)
int Maffligne(manlyz z);


#endif // lexer.h

