/***************************************************************
 * ptbl.c
 * Table Management Package
 *
 *   Copyright (C) 2008-2009 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <spudmonkey@racsa.co.cr>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name NuttX nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ***************************************************************/

/***************************************************************
 * Included Files
 ***************************************************************/

#include <sys/types.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "config.h"
#include "keywords.h"
#include "pasdefs.h"
#include "ptdefs.h"
#include "pedefs.h"

#include "pas.h"
#include "ptbl.h"
#include "perr.h"

/***************************************************************
 * Private Function Prototypes
 ***************************************************************/

static STYPE *addSymbol(char *name, int16_t type);

/***************************************************************
 * Public Variables
 ***************************************************************/

STYPE *parentInteger = NULL;
STYPE *parentString  = NULL;

/***************************************************************
 * Private Variables
 ***************************************************************/
/* NOTES in the following:
 * (1) Standard Pascal reserved word
 * (2) Standard Pascal Function
 * (3) Standard Pascal Procedure
 * (4) Extended (or non-standard) Pascal reserved word
 * (5) Extended (or non-standard) Pascal function
 * (6) Extended (or non-standard) Pascal procedure
 */

static const RTYPE rsw[] =                        /* Reserved word list */
{
  {"ABS",            tFUNC,           txABS},     /* (2) */
  {"AND",            tAND,            txNONE},    /* (1) */
  {"ARCTAN",         tFUNC,           txARCTAN},  /* (2) */
  {"ARRAY",          tARRAY,          txNONE},    /* (1) */
  {"BEGIN",          tBEGIN,          txNONE},    /* (1) */
  {"CASE",           tCASE,           txNONE},    /* (1) */
  {"CHR",            tFUNC,           txCHR},     /* (2) */
  {"CONST",          tCONST,          txNONE},    /* (1) */
  {"COS",            tFUNC,           txCOS},     /* (2) */
  {"DIV",            tDIV,            txNONE},    /* (1) */
  {"DO",             tDO,             txNONE},    /* (1) */
  {"DOWNTO",         tDOWNTO,         txNONE},    /* (1) */
  {"ELSE",           tELSE,           txNONE},    /* (1) */
  {"END",            tEND,            txNONE},    /* (1) */
  {"EOF",            tFUNC,           txEOF},     /* (2) */
  {"EOLN",           tFUNC,           txEOLN},    /* (2) */
  {"EXP",            tFUNC,           txEXP},     /* (2) */
  {"FILE",           tFILE,           txNONE},    /* (1) */
  {"FOR",            tFOR,            txNONE},    /* (1) */
  {"FUNCTION",       tFUNCTION,       txNONE},    /* (1) */
  {"GET",            tPROC,           txGET},     /* (3) */
  {"GETENV",         tFUNC,           txGETENV},  /* (5) */
  {"GOTO",           tGOTO,           txNONE},    /* (1) */
  {"IF",             tIF,             txNONE},    /* (1) */
  {"IMPLEMENTATION", tIMPLEMENTATION, txNONE},    /* (4) */
  {"IN",             tIN,             txNONE},    /* (1) */
  {"INTERFACE",      tINTERFACE,      txNONE},    /* (4) */
  {"LABEL",          tLABEL,          txNONE},    /* (1) */
  {"LN",             tFUNC,           txLN},      /* (2) */
  {"MOD",            tMOD,            txNONE},    /* (1) */
  {"NEW",            tPROC,           txNEW},     /* (3) */
  {"NOT",            tNOT,            txNONE},    /* (1) */
  {"ODD",            tFUNC,           txODD},     /* (2) */
  {"OF",             tOF,             txNONE},    /* (1) */
  {"OR",             tOR,             txNONE},    /* (1) */
  {"ORD",            tFUNC,           txORD},     /* (2) */
  {"PACK",           tPROC,           txPACK},    /* (3) */
  {"PACKED",         tPACKED,         txNONE},    /* (1) */
  {"PAGE",           tPROC,           txPAGE},    /* (3) */
  {"PRED",           tFUNC,           txPRED},    /* (2) */
  {"PROCEDURE",      tPROCEDURE,      txNONE},    /* (1) */
  {"PROGRAM",        tPROGRAM,        txNONE},    /* (1) */
  {"PUT",            tPROC,           txPUT},     /* (3) */
  {"READ",           tPROC,           txREAD},    /* (3) */
  {"READLN",         tPROC,           txREADLN},  /* (3) */
  {"RECORD",         tRECORD,         txNONE},    /* (1) */
  {"REPEAT",         tREPEAT,         txNONE},    /* (1) */
  {"RESET",          tPROC,           txRESET},   /* (3) */
  {"REWRITE",        tPROC,           txREWRITE}, /* (3) */
  {"ROUND",          tFUNC,           txROUND},   /* (2) */
  {"SET",            tSET,            txNONE},    /* (1) */
  {"SHL",            tSHL,            txNONE},    /* (4) */
  {"SHR",            tSHR,            txNONE},    /* (4) */
  {"SIN",            tFUNC,           txSIN},     /* (2) */
  {"SQR",            tFUNC,           txSQR},     /* (2) */
  {"SQRT",           tFUNC,           txSQRT},    /* (2) */
  {"SUCC",           tFUNC,           txSUCC},    /* (2) */
  {"THEN",           tTHEN,           txNONE},    /* (1) */
  {"TO",             tTO,             txNONE},    /* (1) */
  {"TRUNC",          tFUNC,           txTRUNC},   /* (2) */
  {"TYPE",           tTYPE,           txNONE},    /* (1) */
  {"UNIT",           tUNIT,           txNONE},    /* (4) */
  {"UNPACK",         tPROC,           txUNPACK},  /* (3) */
  {"UNTIL",          tUNTIL,          txNONE},    /* (1) */
  {"USES",           tUSES,           txNONE},    /* (4) */
  {"VAL",            tPROC,           txVAL},     /* (6) */
  {"VAR",            tVAR,            txNONE},    /* (1) */
  {"WHILE",          tWHILE,          txNONE},    /* (1) */
  {"WITH",           tWITH,           txNONE},    /* (1) */
  {"WRITE",          tPROC,           txWRITE},   /* (3) */
  {"WRITELN",        tPROC,           txWRITELN}, /* (3) */
  {NULL,             0,               txNONE}     /* List terminator */
};

static STYPE *symbolTable;             /* Symbol Table */

/**************************************************************/

const RTYPE *findReservedWord (char *name)
{
  register const RTYPE *ptr;           /* Point into reserved word list */
  register int16_t cmp;                 /* 0=equal; >0=past it */

  for (ptr = rsw; (ptr->rname); ptr++) /* Try each each reserved word */
    {
      cmp = strcmp(ptr->rname, name);  /* Check if names match */
      if (!cmp)                        /* Check if names match */
        return ptr;                    /* Return pointer to entry if match */
      else if (cmp > 0)                /* Exit early if we are past it */
        break;
    } /* end for */

  return (RTYPE*)NULL;                 /* return NULL pointer if no match */

} /* fnd findReservedWord */  

/***************************************************************/

STYPE *findSymbol (char *inName)
{
   register int16_t i;                /* loop index */

   for (i=nsym-1; i>=sym_strt; i--)
     if (symbolTable[i].sName)
       if (!strcmp(symbolTable[i].sName, inName))
         return &symbolTable[i];
   return (STYPE*)NULL;

} /* end findSymbol */

/***************************************************************/

static STYPE *addSymbol(char *name, int16_t type)
{
   TRACE(lstFile,"[addSymbol]");

   /* Check for Symbol Table overflow */
   if (nsym >= MAX_SYM) {

     fatal(eOVF);
     return (STYPE *)NULL;

   } /* end if */
   else {

     /* Clear all elements of the symbol table entry */
     memset(&symbolTable[nsym], 0, sizeof(STYPE));

     /* Set the elements which are independent of sKind */
     symbolTable[nsym].sName  = name;
     symbolTable[nsym].sKind  = type;
     symbolTable[nsym].sLevel = level;

     return &symbolTable[nsym++];

   } /* end else */

} /* end addSymbol */

/***************************************************************/

STYPE *addTypeDefine(char *name, uint8_t type, uint16_t size, STYPE *parent)
{
   STYPE *typePtr;

   TRACE(lstFile,"[addTypeDefine]");

   /* Get a slot in the symbol table */

   typePtr = addSymbol(name, sTYPE);
   if (typePtr)
     {
       /* Add the type definition to the symbol table
        * NOTES:
        * 1. The minValue and maxValue fields (for scalar and subrange)
        *    types must be set external to this function
        * 2. For most variables, allocated size/type (rsize/rtype) and
        *    the clone size/type are the same.  If this is not the case,
        *    external logic will need to clarify this as well.
        * 3. We assume that there are no special flags associated with
        *    the type.
        */

       typePtr->sParm.t.type     = type;
       typePtr->sParm.t.rtype    = type;
       typePtr->sParm.t.flags    = 0;
       typePtr->sParm.t.asize    = size;
       typePtr->sParm.t.rsize    = size;
       typePtr->sParm.t.parent   = parent;

     } /* end if */

   /* Return a pointer to the new constant symbol */

   return typePtr;

} /* end addTypeDefine */

/***************************************************************/

STYPE *addConstant(char *name, uint8_t type, int32_t *value, STYPE *parent)
{
   STYPE *constPtr;

   TRACE(lstFile,"[addConstant]");

   /* Get a slot in the symbol table */
   constPtr = addSymbol(name, type);
   if (constPtr) {

     /* Add the value of the constant to the symbol table */
     if (type == tREAL_CONST)
       constPtr->sParm.c.val.f = *((double*) value);
     else
       constPtr->sParm.c.val.i = *value;
     constPtr->sParm.c.parent = parent;

   } /* end if */

   /* Return a pointer to the new constant symbol */

   return constPtr;

} /* end addConstant */

/***************************************************************/

STYPE *addStringConst(char *name, uint32_t offset, uint32_t size)
{
  STYPE *stringPtr;

  TRACE(lstFile,"[addStringConst]");

  /* Get a slot in the symbol table */

  stringPtr = addSymbol(name, sSTRING_CONST);
  if (stringPtr)
    {
      /* Add the value of the constant to the symbol table */

      stringPtr->sParm.s.offset = offset;
      stringPtr->sParm.s.size   = size;
    } /* end if */

  /* Return a pointer to the new string symbol */

  return stringPtr;

} /* end addString */

/***************************************************************/

STYPE *addFile(char *name, uint16_t fileNumber)
{
   STYPE *filePtr;

   TRACE(lstFile,"[addFile]");

   /* Get a slot in the symbol table */
   filePtr = addSymbol(name, sFILE);
   if (filePtr) {

     /* Add the fileNumber to the symbol table */
     filePtr->sParm.fileNumber = fileNumber;

   } /* end if */

   /* Return a pointer to the new file symbol */

   return filePtr;

} /* end addFile */

/***************************************************************/

STYPE *addProcedure(char *name, uint8_t type, uint16_t label,
                    uint16_t nParms, STYPE *parent)
{
   STYPE *procPtr;

   TRACE(lstFile,"[addProcedure]");

   /* Get a slot in the symbol table */
   procPtr = addSymbol(name, type);
   if (procPtr)
     {
       /* Add the procedure/function definition to the symbol table */

       procPtr->sParm.p.label    = label;
       procPtr->sParm.p.nParms   = nParms;
       procPtr->sParm.p.flags    = 0;
       procPtr->sParm.p.symIndex = 0;
       procPtr->sParm.p.parent   = parent;
     } /* end if */

   /* Return a pointer to the new procedure/function symbol */

   return procPtr;

} /* end addProcedure */

/***************************************************************/

STYPE *addVariable(char *name, uint8_t type, uint16_t offset,
                   uint16_t size, STYPE *parent)
{
  STYPE *varPtr;

  TRACE(lstFile,"[addVariable]");

  /* Get a slot in the symbol table */

  varPtr = addSymbol(name, type);
  if (varPtr)
    {
      /* Add the variable to the symbol table */

      varPtr->sParm.v.offset   = offset;
      varPtr->sParm.v.size     = size;
      varPtr->sParm.v.flags    = 0;
      varPtr->sParm.v.symIndex = 0;
      varPtr->sParm.v.parent   = parent;
    } /* end if */

  /* Return a pointer to the new variable symbol */

  return varPtr;

} /* end addFile */

/***************************************************************/

STYPE *addLabel(char *name, uint16_t label)
{
  STYPE *labelPtr;

  TRACE(lstFile,"[addLabel]");

  /* Get a slot in the symbol table */

  labelPtr = addSymbol(name, sLABEL);
  if (labelPtr)
    {
      /* Add the label to the symbol table */

      labelPtr->sParm.l.label = label;
      labelPtr->sParm.l.unDefined = true;
    } /* end if */

  /* Return a pointer to the new label symbol */

  return labelPtr;

} /* end addFile */

/***************************************************************/

STYPE *addField(char *name, STYPE *record)
{
   STYPE *fieldPtr;

   TRACE(lstFile,"[addField]");

   /* Get a slot in the symbol table */
   fieldPtr = addSymbol(name, sRECORD_OBJECT);
   if (fieldPtr) {

     /* Add the field to the symbol table */
     fieldPtr->sParm.r.record = record;

   } /* end if */

   /* Return a pointer to the new variable symbol */

   return fieldPtr;

} /* end addField */

/***************************************************************/

void primeSymbolTable(unsigned long symbolTableSize)
{
  int32_t trueValue   = -1;
  int32_t falseValue  =  0;
  int32_t maxintValue = MAXINT;
  STYPE *typePtr;
  register int16_t i;

  TRACE(lstFile,"[primeSymbolTable]");

  /* Allocate and initialize symbol table */

  symbolTable = malloc(symbolTableSize * sizeof(STYPE));
  if (!symbolTable)
    {
      fatal(eNOMEMORY);
    }

  nsym = 0;

  /* Add the standard constants to the symbol table */

  (void)addConstant("TRUE",   tBOOLEAN_CONST, &trueValue,   NULL);
  (void)addConstant("FALSE",  tBOOLEAN_CONST, &falseValue,  NULL);
  (void)addConstant("MAXINT", tINT_CONST,     &maxintValue, NULL);
  (void)addConstant("NIL",    tNIL,           &falseValue,  NULL);

  /* Add the standard types to the symbol table */

  typePtr = addTypeDefine("INTEGER", sINT, sINT_SIZE, NULL);
  if (typePtr)
    {
      parentInteger             = typePtr;
      typePtr->sParm.t.minValue = MININT;
      typePtr->sParm.t.maxValue = MAXINT;
    } /* end if */

  typePtr = addTypeDefine("BOOLEAN", sBOOLEAN, sBOOLEAN_SIZE, NULL);
  if (typePtr)
    {
      typePtr->sParm.t.minValue = falseValue;
      typePtr->sParm.t.maxValue = trueValue;
    } /* end if */

  typePtr = addTypeDefine("REAL", sREAL, sREAL_SIZE, NULL);

  typePtr = addTypeDefine("CHAR", sCHAR, sCHAR_SIZE, NULL);
  if (typePtr)
    {
      typePtr->sParm.t.minValue = MINCHAR;
      typePtr->sParm.t.maxValue = MAXCHAR;
    } /* end if */

  typePtr = addTypeDefine("TEXT", sFILE_OF, sCHAR_SIZE, NULL);
  if (typePtr)
    {
      typePtr->sParm.t.subType  = sCHAR;
      typePtr->sParm.t.minValue = MINCHAR;
      typePtr->sParm.t.maxValue = MAXCHAR;
    } /* end if */

  /* Add some enhanced Pascal standard" types to the symbol table
   *
   * string is represent by a 256 byte memory regions consisting of
   * one byte for the valid string length plus 255 bytes for string
   * storage
   */

  typePtr = addTypeDefine("STRING", sSTRING, sSTRING_SIZE, NULL);
  if (typePtr)
    {
      parentString              = typePtr;
      typePtr->sParm.t.rtype    = sRSTRING;
      typePtr->sParm.t.subType  = sCHAR;
      typePtr->sParm.t.rsize    = sRSTRING_SIZE;
      typePtr->sParm.t.flags    = STYPE_VARSIZE;
      typePtr->sParm.t.minValue = MINCHAR;
      typePtr->sParm.t.maxValue = MAXCHAR;
    } /* end if */

  /* Add the standard files to the symbol table */

  (void)addFile("INPUT",   0);
  (void)addFile("OUTPUT",  0);

  /* Initialize files table */

  for (i = 0; i <= MAX_FILES; i++)
    {
      files [i].defined = 0;
      files [i].flevel  = 0;
      files [i].ftype   = 0;
      files [i].faddr   = 0;
      files [i].fsize   = 0;
    } /* end for */
} /* end primeSymbolTable */

/***************************************************************/

void verifyLabels(int32_t symIndex)
{
   register int16_t i;                 /* loop index */

   for (i=symIndex; i < nsym; i++)
     if ((symbolTable[i].sKind == sLABEL)
     &&  (symbolTable[i].sParm.l.unDefined))
       error (eUNDEFLABEL);
} /* end verifyLabels */

/***************************************************************/

#if CONFIG_DEBUG
const char noName[] = "********";
void dumpTables(void)
{
  register int16_t i;

  fprintf(lstFile,"\nSYMBOL TABLE:\n");
  fprintf(lstFile,"[  Addr  ]     NAME KIND LEVL\n");

  for (i = 0; i < nsym; i++)
    {
      fprintf(lstFile,"[%08lx] ", (uint32_t)&symbolTable[i]);

      if (symbolTable[i].sName)
        fprintf(lstFile, "%8s", symbolTable[i].sName);
      else
        fprintf(lstFile, "%8s", noName);

      fprintf(lstFile," %04x %04x ",
              symbolTable[i].sKind,
              symbolTable[i].sLevel);

      switch (symbolTable[i].sKind)
        {
          /* Constants */

        case tINT_CONST :
        case tCHAR_CONST :
        case tBOOLEAN_CONST :
        case tNIL :
        case sSCALAR :
          fprintf(lstFile, "val=%ld parent=[%08lx]\n",
                  symbolTable[i].sParm.c.val.i,
                  (unsigned long)symbolTable[i].sParm.c.parent);
          break;
        case tREAL_CONST :
          fprintf(lstFile, "val=%f parent=[%08lx]\n",
                  symbolTable[i].sParm.c.val.f,
                  (unsigned long)symbolTable[i].sParm.c.parent);
          break;

          /* Types */

        case sTYPE  :
          fprintf(lstFile,
                  "type=%02x rtype=%02x subType=%02x flags=%02x "
                  "asize=%ld rsize=%ld minValue=%ld maxValue=%ld "
                  "parent=[%08lx]\n",
                  symbolTable[i].sParm.t.type,
                  symbolTable[i].sParm.t.rtype,
                  symbolTable[i].sParm.t.subType,
                  symbolTable[i].sParm.t.flags,
                  symbolTable[i].sParm.t.asize,
                  symbolTable[i].sParm.t.rsize,
                  symbolTable[i].sParm.t.minValue,
                  symbolTable[i].sParm.t.maxValue,
                  (unsigned long)symbolTable[i].sParm.t.parent);
          break;

          /* Procedures/Functions */

          /* Procedures and Functions */

        case sPROC :
        case sFUNC :
          fprintf(lstFile,
                  "label=L%04x nParms=%d flags=%02x parent=[%08lx]\n",
                  symbolTable[i].sParm.p.label,
                  symbolTable[i].sParm.p.nParms,
                  symbolTable[i].sParm.p.flags,
                  (unsigned long)symbolTable[i].sParm.p.parent);
          break;

          /* Labels */

        case sLABEL :
          fprintf(lstFile, "label=L%04x unDefined=%d\n",
                  symbolTable[i].sParm.l.label,
                  symbolTable[i].sParm.l.unDefined);
          break;

          /* Files */

        case sFILE :
          fprintf(lstFile, "fileNumber=%d\n",
                  symbolTable[i].sParm.fileNumber);
          break;

          /* Variables */

        case sINT :
        case sBOOLEAN :
        case sCHAR  :
        case sREAL :
        case sTEXT :
        case sARRAY :
        case sPOINTER :
        case sVAR_PARM :
        case sRECORD :
        case sFILE_OF :
          fprintf(lstFile, "offset=%ld size=%ld flags=%02x parent=[%08lx]\n",
                  symbolTable[i].sParm.v.offset,
                  symbolTable[i].sParm.v.size,
                  symbolTable[i].sParm.v.flags,
                  (unsigned long)symbolTable[i].sParm.v.parent);
          break;

          /* Record objects */

        case sRECORD_OBJECT :
          fprintf(lstFile,
                  "offset=%ld size=%ld record=[%08lx] parent=[%08lx]\n",
                  symbolTable[i].sParm.r.offset,
                  symbolTable[i].sParm.r.size,
                  (unsigned long)symbolTable[i].sParm.r.record,
                  (unsigned long)symbolTable[i].sParm.r.parent);
          break;

          /* Constant strings */

        case sSTRING_CONST :
          fprintf(lstFile, "offset=%04lx size=%ld\n",
                  symbolTable[i].sParm.s.offset,
                  symbolTable[i].sParm.s.size);
          break;

        default :
          fprintf(lstFile, "Unknown sKind\n");
          break;
     
        } /* end switch */
    } /* end for */

} /* end dumpTables */
#endif

/***************************************************************/

