/**CFile****************************************************************

  FileName    [cbaReadBlif.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Verilog parser.]

  Synopsis    [Parses several flavors of word-level Verilog.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - November 29, 2014.]

  Revision    [$Id: cbaReadBlif.c,v 1.00 2014/11/29 00:00:00 alanmi Exp $]

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

#include "cba.h"
#include "cbaPrs.h"

ABC_NAMESPACE_IMPL_START

////////////////////////////////////////////////////////////////////////
///                        DECLARATIONS                              ///
////////////////////////////////////////////////////////////////////////

// BLIF keywords
typedef enum { 
    CBA_BLIF_NONE = 0, // 0:   unused
    CBA_BLIF_MODEL,    // 1:   .model
    CBA_BLIF_INOUTS,   // 2:   .inouts
    CBA_BLIF_INPUTS,   // 3:   .inputs
    CBA_BLIF_OUTPUTS,  // 4:   .outputs
    CBA_BLIF_NAMES,    // 5:   .names
    CBA_BLIF_SUBCKT,   // 6:   .subckt
    CBA_BLIF_GATE,     // 7:   .gate
    CBA_BLIF_LATCH,    // 8:   .latch
    CBA_BLIF_SHORT,    // 9:   .short
    CBA_BLIF_END,      // 10:  .end
    CBA_BLIF_UNKNOWN   // 11:  unknown
} Cba_BlifType_t;

const char * s_BlifTypes[CBA_BLIF_UNKNOWN+1] = {
    NULL,              // 0:   unused
    ".model",          // 1:   .model   
    ".inouts",         // 2:   .inputs
    ".inputs",         // 3:   .inputs
    ".outputs",        // 4:   .outputs
    ".names",          // 5:   .names
    ".subckt",         // 6:   .subckt
    ".gate",           // 7:   .gate
    ".latch",          // 8:   .latch
    ".short",          // 9:   .short
    ".end",            // 10:  .end
    NULL               // 11:  unknown
};

static inline void Cba_PrsAddBlifDirectives( Cba_Prs_t * p )
{
    int i;
    for ( i = 1; s_BlifTypes[i]; i++ )
        Abc_NamStrFindOrAdd( p->pDesign->pNames, (char *)s_BlifTypes[i],   NULL );
    assert( Abc_NamObjNumMax(p->pDesign->pNames) == i );
    Abc_NamStrFindOrAdd( p->pDesign->pFuncs, (char *)" 0\n",   NULL );  // default const 0 function
    Abc_NamStrFindOrAdd( p->pDesign->pFuncs, (char *)"1 1\n",   NULL ); // default buffer function
    assert( Abc_NamObjNumMax(p->pDesign->pFuncs) == 3 );
}


////////////////////////////////////////////////////////////////////////
///                     FUNCTION DEFINITIONS                         ///
////////////////////////////////////////////////////////////////////////

/**Function*************************************************************

  Synopsis    [Reading characters.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int  Cba_CharIsSpace( char c )                { return c == ' ' || c == '\t' || c == '\r';              }
static inline int  Cba_CharIsStop( char c )                 { return c == '#' || c == '\\' || c == '\n' || c == '=';  }
static inline int  Cba_CharIsLit( char c )                  { return c == '0' || c == '1'  || c == '-';               }

static inline int  Cba_PrsIsSpace( Cba_Prs_t * p )          { return Cba_CharIsSpace(*p->pCur);                       }
static inline int  Cba_PrsIsStop( Cba_Prs_t * p )           { return Cba_CharIsStop(*p->pCur);                        }
static inline int  Cba_PrsIsLit( Cba_Prs_t * p )            { return Cba_CharIsLit(*p->pCur);                         }

static inline int  Cba_PrsIsChar( Cba_Prs_t * p, char c )   { return *p->pCur == c;                                   }
static inline int  Cba_PrsIsChar2( Cba_Prs_t * p, char c )  { return *p->pCur++ == c;                                 }

static inline void Cba_PrsSkip( Cba_Prs_t * p )             { p->pCur++;                                              }
static inline char Cba_PrsSkip2( Cba_Prs_t * p )            { return *p->pCur++;                                      }


/**Function*************************************************************

  Synopsis    [Reading names.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline void Cba_PrsSkipToChar( Cba_Prs_t * p, char c )  
{ 
    while ( !Cba_PrsIsChar(p, c) ) 
        Cba_PrsSkip(p);
}
static inline void Cba_PrsSkipSpaces( Cba_Prs_t * p )
{
    while ( 1 )
    {
        while ( Cba_PrsIsSpace(p) )
            Cba_PrsSkip(p);
        if ( Cba_PrsIsChar(p, '\\') )
        {
            Cba_PrsSkipToChar( p, '\n' );
            Cba_PrsSkip(p);
            continue;
        }
        if ( Cba_PrsIsChar(p, '#') )  
            Cba_PrsSkipToChar( p, '\n' );
        break;
    }
    assert( !Cba_PrsIsSpace(p) );
}
static inline int Cba_PrsReadName( Cba_Prs_t * p )
{
    char * pStart;
    Cba_PrsSkipSpaces( p );
    if ( Cba_PrsIsChar(p, '\n') )
        return 0;
    pStart = p->pCur;
    while ( !Cba_PrsIsSpace(p) && !Cba_PrsIsStop(p) )
        Cba_PrsSkip(p);
    if ( pStart == p->pCur )
        return 0;
    return Abc_NamStrFindOrAddLim( p->pDesign->pNames, pStart, p->pCur, NULL );
}
static inline int Cba_PrsReadList( Cba_Prs_t * p )
{
    int iToken;
    Vec_IntClear( &p->vTemp );
    while ( (iToken = Cba_PrsReadName(p)) )
        Vec_IntPush( &p->vTemp, iToken );
    if ( Vec_IntSize(&p->vTemp) == 0 )                return Cba_PrsErrorSet(p, "Signal list is empty.", 1);
    return 0;
}
static inline int Cba_PrsReadList2( Cba_Prs_t * p )
{
    int iToken;
    Vec_IntFill( &p->vTemp, 1, -1 );
    while ( (iToken = Cba_PrsReadName(p)) )
        Vec_IntPush( &p->vTemp, iToken );
    iToken = Vec_IntPop(&p->vTemp);
    if ( Vec_IntSize(&p->vTemp) == 0 )                return Cba_PrsErrorSet(p, "Signal list is empty.", 1);
    Vec_IntWriteEntry( &p->vTemp, 0, iToken );
    return 0;
}
static inline int Cba_PrsReadList3( Cba_Prs_t * p )
{
    Vec_IntClear( &p->vTemp );
    while ( !Cba_PrsIsChar(p, '\n') )
    {
        int iToken = Cba_PrsReadName(p);
        if ( iToken == 0 )              return Cba_PrsErrorSet(p, "Cannot read formal name.", 1);
        Vec_IntPush( &p->vTemp, iToken );
        Cba_PrsSkipSpaces( p );
        if ( !Cba_PrsIsChar2(p, '=') )  return Cba_PrsErrorSet(p, "Cannot find symbol \"=\".", 1);
        iToken = Cba_PrsReadName(p);
        if ( iToken == 0 )              return Cba_PrsErrorSet(p, "Cannot read actual name.", 1);
        Vec_IntPush( &p->vTemp, iToken );
        Cba_PrsSkipSpaces( p );
    }
    if ( Vec_IntSize(&p->vTemp) == 0 )  return Cba_PrsErrorSet(p, "Cannot read a list of formal/actual names.", 1);
    if ( Vec_IntSize(&p->vTemp) % 2  )  return Cba_PrsErrorSet(p, "The number of formal/actual names is not even.", 1);
    return 0;
}

/**Function*************************************************************

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Cba_PrsReadCube( Cba_Prs_t * p )
{
    assert( Cba_PrsIsLit(p) );
    while ( Cba_PrsIsLit(p) )
        Vec_StrPush( &p->vCover, Cba_PrsSkip2(p) );
    Cba_PrsSkipSpaces( p );
    if ( Cba_PrsIsChar(p, '\n') )
    {
        if ( Vec_StrSize(&p->vCover) != 1 )            return Cba_PrsErrorSet(p, "Cannot read cube.", 1);
        // fix single literal cube by adding space
        Vec_StrPush( &p->vCover, Vec_StrEntry(&p->vCover,0) );
        Vec_StrWriteEntry( &p->vCover, 0, ' ' );
        Vec_StrPush( &p->vCover, '\n' );
        return 0;
    }
    if ( !Cba_PrsIsLit(p) )                           return Cba_PrsErrorSet(p, "Cannot read output literal.", 1);
    Vec_StrPush( &p->vCover, ' ' );
    Vec_StrPush( &p->vCover, Cba_PrsSkip2(p) );
    Vec_StrPush( &p->vCover, '\n' );
    Cba_PrsSkipSpaces( p );
    if ( !Cba_PrsIsChar(p, '\n') )                    return Cba_PrsErrorSet(p, "Cannot read end of cube.", 1);
    return 0;
}
static inline void Cba_PrsSaveCover( Cba_Prs_t * p )
{
    int iToken;
    assert( Vec_StrSize(&p->vCover) > 0 );
    Vec_StrPush( &p->vCover, '\0' );
    iToken = Abc_NamStrFindOrAdd( p->pDesign->pFuncs, Vec_StrArray(&p->vCover), NULL );
    assert( Vec_IntEntryLast(&p->vFuncsCur) == 1 );
    Vec_IntWriteEntry( &p->vFuncsCur, Vec_IntSize(&p->vFuncsCur)-1, iToken );
    Vec_StrClear( &p->vCover );
}

/**Function*************************************************************

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
static inline int Cba_PrsReadInouts( Cba_Prs_t * p )
{
    if ( Cba_PrsReadList(p) )    return 1;
    Vec_IntAppend( &p->vInoutsCur, &p->vTemp );
    return 0;
}
static inline int Cba_PrsReadInputs( Cba_Prs_t * p )
{
    if ( Cba_PrsReadList(p) )    return 1;
    Vec_IntAppend( &p->vInputsCur, &p->vTemp );
    return 0;
}
static inline int Cba_PrsReadOutputs( Cba_Prs_t * p )
{
    if ( Cba_PrsReadList(p) )    return 1;
    Vec_IntAppend( &p->vOutputsCur, &p->vTemp );
    return 0;
}
static inline int Cba_PrsReadNode( Cba_Prs_t * p )
{
    if ( Cba_PrsReadList2(p) )   return 1;
    // save results
    Vec_IntPush( &p->vTypesCur, CBA_OBJ_NODE );
    Vec_IntPush( &p->vFuncsCur, 1 ); // default const 0 function
    Vec_IntPush( &p->vFaninsCur, Cba_ManHandleArray(p->pDesign, &p->vTemp) ); 
    return 0;
}
static inline int Cba_PrsReadBox( Cba_Prs_t * p, int fGate )
{
    int iToken = Cba_PrsReadName(p);
    if ( iToken == 0 )           return Cba_PrsErrorSet(p, "Cannot read model name.", 1);
    if ( Cba_PrsReadList3(p) )   return 1;
    // save results
    Vec_IntPush( &p->vTypesCur, CBA_OBJ_BOX );
    Vec_IntPush( &p->vFuncsCur, iToken );
    Vec_IntPush( &p->vFaninsCur, Cba_ManHandleArray(p->pDesign, &p->vTemp) ); 
    return 0;
}
static inline int Cba_PrsReadLatch( Cba_Prs_t * p )
{
    int iToken = Cba_PrsReadName(p);
    Vec_IntFill( &p->vTemp, 2, -1 );
    if ( iToken == 0 )                 return Cba_PrsErrorSet(p, "Cannot read latch input.", 1);
    Vec_IntWriteEntry( &p->vTemp, 1, iToken );
    iToken = Cba_PrsReadName(p);
    if ( iToken == 0 )                 return Cba_PrsErrorSet(p, "Cannot read latch output.", 1);
    Vec_IntWriteEntry( &p->vTemp, 0, iToken );
    Cba_PrsSkipSpaces( p );
    if ( Cba_PrsIsChar(p, '0') )
        iToken = 0;
    else if ( Cba_PrsIsChar(p, '1') )
        iToken = 1;
    else 
        iToken = 2;
    Cba_PrsSkipToChar( p, '\n' );
    // save results
    Vec_IntPush( &p->vTypesCur, CBA_OBJ_LATCH );
    Vec_IntPush( &p->vFuncsCur, iToken );
    Vec_IntPush( &p->vFaninsCur, Cba_ManHandleArray(p->pDesign, &p->vTemp) ); 
    return 0;
}
static inline int Cba_PrsReadShort( Cba_Prs_t * p )
{
    int iToken = Cba_PrsReadName(p);
    Vec_IntFill( &p->vTemp, 2, -1 );
    if ( iToken == 0 )                 return Cba_PrsErrorSet(p, "Cannot read .short input.", 1);
    Vec_IntWriteEntry( &p->vTemp, 1, iToken );
    iToken = Cba_PrsReadName(p);
    if ( iToken == 0 )                 return Cba_PrsErrorSet(p, "Cannot read .short output.", 1);
    Vec_IntWriteEntry( &p->vTemp, 0, iToken );
    Cba_PrsSkipSpaces( p );
    if ( !Cba_PrsIsChar(p, '\n') )     return Cba_PrsErrorSet(p, "Trailing symbols on .short line.", 1);
    // save results
    Vec_IntPush( &p->vTypesCur, CBA_OBJ_NODE );
    Vec_IntPush( &p->vFuncsCur, 2 );   // default buffer function
    Vec_IntPush( &p->vFaninsCur, Cba_ManHandleArray(p->pDesign, &p->vTemp) ); 
    return 0;
}
static inline int Cba_PrsReadModel( Cba_Prs_t * p )
{
    if ( p->iModuleName > 0 )                      return Cba_PrsErrorSet(p, "Parsing previous model is unfinished.", 1);
    p->iModuleName = Cba_PrsReadName(p);
    Cba_PrsSkipSpaces( p );
    if ( !Cba_PrsIsChar(p, '\n') )                 return Cba_PrsErrorSet(p, "Trailing symbols on .model line.", 1);
    return 0;
}
static inline int Cba_PrsReadEnd( Cba_Prs_t * p )
{
    if ( p->iModuleName == 0 )                     return Cba_PrsErrorSet(p, "Directive .end without .model.", 1);
    //printf( "Saving model \"%s\".\n", Abc_NamStr(p->pDesign->pNames, p->iModuleName) );
    Cba_PrsAddCurrentModel( p, p->iModuleName );
    p->iModuleName = 0;
    Cba_PrsSkipSpaces( p );
    if ( !Cba_PrsIsChar(p, '\n') )                 return Cba_PrsErrorSet(p, "Trailing symbols on .end line.", 1);
    return 0;
}

static inline int Cba_PrsReadDirective( Cba_Prs_t * p )
{
    int iToken;
    if ( !Cba_PrsIsChar(p, '.') )
        return Cba_PrsReadCube( p );
    if ( Vec_StrSize(&p->vCover) > 0 ) // SOP was specified for the previous node
        Cba_PrsSaveCover( p );
    iToken = Cba_PrsReadName( p );  
    if ( iToken == CBA_BLIF_MODEL )
        return Cba_PrsReadModel( p );
    if ( iToken == CBA_BLIF_INOUTS )
        return Cba_PrsReadInouts( p );
    if ( iToken == CBA_BLIF_INPUTS )
        return Cba_PrsReadInputs( p );
    if ( iToken == CBA_BLIF_OUTPUTS )
        return Cba_PrsReadOutputs( p );
    if ( iToken == CBA_BLIF_NAMES )
        return Cba_PrsReadNode( p );
    if ( iToken == CBA_BLIF_SUBCKT )
        return Cba_PrsReadBox( p, 0 );
    if ( iToken == CBA_BLIF_GATE )
        return Cba_PrsReadBox( p, 1 );
    if ( iToken == CBA_BLIF_LATCH )
        return Cba_PrsReadLatch( p );
    if ( iToken == CBA_BLIF_SHORT )
        return Cba_PrsReadShort( p );
    if ( iToken == CBA_BLIF_END )
        return Cba_PrsReadEnd( p );
    printf( "Cannot read directive \"%s\".\n", Abc_NamStr(p->pDesign->pNames, iToken) );
    return 1;
}
static inline int Cba_PrsReadLines( Cba_Prs_t * p )
{
    while ( p->pCur[1] != '\0' )
    {
        assert( Cba_PrsIsChar(p, '\n') );
        Cba_PrsSkip(p);
        Cba_PrsSkipSpaces( p );
        if ( Cba_PrsIsChar(p, '\n') )
            continue;
        if ( Cba_PrsReadDirective(p) )   
            return 1;
    }
    return 0;
}

/**Function*************************************************************

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Cba_Man_t * Cba_PrsReadBlif( char * pFileName )
{
    Cba_Man_t * pDesign = NULL;
    Cba_Prs_t * p = Cba_PrsAlloc( pFileName );
    if ( p == NULL )
        return NULL;
    Cba_PrsAddBlifDirectives( p );
    Cba_PrsReadLines( p );
    if ( Cba_PrsErrorPrint(p) )
        ABC_SWAP( Cba_Man_t *, pDesign, p->pDesign );
    Cba_PrsFree( p );
    return pDesign;
}

/**Function*************************************************************

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Cba_PrsReadBlifTest( char * pFileName )
{
    abctime clk = Abc_Clock();
    extern void Cba_PrsWriteBlif( char * pFileName, Cba_Man_t * pDes );
    Cba_Man_t * p = Cba_PrsReadBlif( "aga/ray/ray_hie_oper.blif" );
    if ( !p ) return;
    printf( "Finished reading %d networks. ", Cba_ManNtkNum(p) );
    printf( "NameIDs = %d. ", Abc_NamObjNumMax(p->pNames) );
    printf( "Memory = %.2f MB. ", 1.0*Cba_ManMemory(p)/(1<<20) );
    Abc_PrintTime( 1, "Time", Abc_Clock() - clk );
//    Abc_NamPrint( p->pDesign->pNames );
    Cba_PrsWriteBlif( "aga/ray/ray_hie_oper_out.blif", p );
    Cba_ManFree( p );
}


////////////////////////////////////////////////////////////////////////
///                       END OF FILE                                ///
////////////////////////////////////////////////////////////////////////


ABC_NAMESPACE_IMPL_END