%{
/* -----------------------------------------------------------------------------
* COPYRIGHT  (c)  2003
* THE REGENTS OF THE GANNON UNIVERSITY
* ALL RIGHTS RESERVED
* 
* PERMISSION IS GRANTED TO USE, COPY, CREATE DERIVATIVE WORKS 
* AND REDISTRIBUTE THIS SOFTWARE AND SUCH DERIVATIVE WORKS FOR 
* ANY PURPOSE, SO LONG AS NO FEE IS CHARGED, AND SO LONG AS 
* THE COPYRIGHT NOTICE ABOVE, THIS GRANT OF PERMISSION, AND 
* THE DISCLAIMER BELOW APPEAR IN ALL COPIES MADE; AND SO LONG 
* AS THE NAME OF THE GANNON UNIVERSITY IS NOT USED IN ANY 
* ADVERTISING OR PUBLICITY PERTAINING TO THE USE OR 
* DISTRIBUTION OF THIS SOFTWARE WITHOUT SPECIFIC, WRITTEN 
* PRIOR AUTHORIZATION.
* 
* THIS SOFTWARE IS PROVIDED AS IS, WITHOUT REPRESENTATION 
* FROM GANNON UNIVERSITY AS TO ITS FITNESS 
* FOR ANY PURPOSE, AND WITHOUT WARRANTY BY THE 
* GANNON UNIVERSITY OF ANY KIND, EITHER EXPRESS OR 
* IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE 
* REGENTS OF THE GANNON UNIVERSITY SHALL NOT BE LIABLE 
* FOR ANY DAMAGES, INCLUDING SPECIAL, INDIRECT, INCIDENTAL, OR 
* CONSEQUENTIAL DAMAGES, WITH RESPECT TO ANY CLAIM ARISING OUT 
* HAS BEEN OR IS HEREAFTER ADVISED OF THE POSSIBILITY OF SUCH 
* DAMAGES.
----------------------------------------------------------------------------- */

/**************************************************************************************
 * File: spice.l
 *
 * Purpose: lexer for spicedeck, intended to flex
 *
 * Authod: Saudamini Baru
 *
 * RCS Version:
 *     $Id: spice.l,v 1.1.1.1 2002/05/01 14:23:27 mguthaus Exp $

 *     $Id: spice.l,v 1.1.1.1.1 2003/06/17 12:19:30 Michael A Riepe Exp $
 **************************************************************************************/

#include <string.h>
#include <stdlib.h>

#include "argument.hh"
#include "spice.tab.hh"

int lineno = 1;

static int is_data = 0; 

/* stuff to define a lexer that can be called recursively */
#define MAX_INCLUDE_DEPTH 11
YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
YY_BUFFER_STATE temp_yy_buffer;
int lineno_stack[MAX_INCLUDE_DEPTH];
int include_stack_ptr = 0;
static FILE *yyin_temp;
char *filename;
%}

%x Sargs
%x Sargnum
%x Sdata
%x Sdotdata
%x Sdotmodel
%x Sdotparam
%x Sinclude
%x Snormal
%x Sopenquote
%x Spluswait
%x Ssubckt

Space       [\r\t\b ]
NotSpace    [^\r\t\b ]
Alpha       [a-zA-Z]
AlphaU      [a-zA-Z_]
AlphaNum    [a-zA-Z0-9]
AlphaNumU   [a-zA-Z0-9_]
AlphaNumUD  [a-zA-Z0-9_.]
AlphaNumSym [a-zA-Z0-9_.!@#%^&*+~:;?\]\[]
Digit       [0-9]
DigitU      [0-9_]
Number      ({Digit}+)
Integer     ([+-]?{Number})
Decimal     ([+-]?{Digit}*"."{Number})
Float       (({Integer}|{Decimal})[eE][+-]?{Number})
NodeID      ({Alpha}{AlphaNumSym}*)
Val         ({Integer}|{Decimal}|{Float})
Unit        [fFpPnNuUmMkKgG]
ScaleFact   ({Unit}|"MEG"|"meg"|"MI"|"mi")
Filepath    [a-zA-Z0-9_.+~?/]


%%

    /* first line is the title by definition */
^.*$ { BEGIN Snormal; yylval.sval=strdup(yytext); return TITLE;}

    /* comments */
<Snormal>^"*".*$        { yylval.sval=strdup(yytext); return COMMENT; }

    /* Device Instance cards */
<Snormal>^[Cc]{AlphaNumSym}{1,96} { BEGIN Sargs; yylval.sval=strdup(yytext); return CAPACITOR; }
<Snormal>^[Dd]{AlphaNumSym}{1,96} { BEGIN Sargs; yylval.sval=strdup(yytext); return DIODE; }
<Snormal>^[Ii]{AlphaNumSym}{1,96} { BEGIN Sargs; yylval.sval=strdup(yytext); return ISOURCE; }
<Snormal>^[Jj]{AlphaNumSym}{1,96} { BEGIN Sargs; yylval.sval=strdup(yytext); return JFET; }
<Snormal>^[Ll]{AlphaNumSym}{1,96} { BEGIN Sargs; yylval.sval=strdup(yytext); return INDUCTOR; }
<Snormal>^[Mm]{AlphaNumSym}{1,96} { BEGIN Sargs; yylval.sval=strdup(yytext); return MOSFET; }
<Snormal>^[Qq]{AlphaNumSym}{1,96} { BEGIN Sargs; yylval.sval=strdup(yytext); return BJT; }
<Snormal>^[Rr]{AlphaNumSym}{1,96} { BEGIN Sargs; yylval.sval=strdup(yytext); return RESISTOR; }
<Snormal>^[Vv]{AlphaNumSym}{1,96} { BEGIN Sargs; yylval.sval=strdup(yytext); return VSOURCE; }
<Snormal>^[Xx]{AlphaNumSym}{1,96} { BEGIN Sargs; yylval.sval=strdup(yytext); return SUBCKTINST; }
<Snormal>^[Kk]{AlphaNumSym}{1,96} { BEGIN Sargs; yylval.sval=strdup(yytext); return COUPLING; }
    /* subcircuits, macros */
<Snormal>^".SUBCKT"|".subckt"       { BEGIN Sargs; return SUBCKT; }
<Snormal>^".MACRO"|".macro"         { BEGIN Sargs; return SUBCKT; }

    /* Control cards */
<Snormal>^".ALTER"|".alter"         { return ALTER; }  
<Snormal>^".CONTROL"|".control"     { BEGIN Sargs; return CONTROL; }  
<Snormal>^".DATA"|".data"           { is_data = 1; BEGIN Sargs; return DATA; }
<Snormal>^".DCVOLT"|".dcvolt"       { BEGIN Sargs; return DCVOLT; }  
<Snormal>^".DEL LIB"|".del lib"     { BEGIN Sargs; return DELLIB; }  
<Snormal>^".END"|".end"             { BEGIN Sargs; return END; }
<Snormal>^".ENDL"|".endl"           { BEGIN Sargs; return ENDL; } 
<Snormal>^".ENDS"|".ends"           { BEGIN Sargs; return ENDS; }
<Snormal>^".EOM"|".eom"             { BEGIN Sargs; return ENDS; }
<Snormal>^".GLOBAL"|".global"       { BEGIN Sargs; return GLOBAL; }
<Snormal>^".IC"|".ic"               { BEGIN Sargs; return ICVOLT; }  
<Snormal>^".LIB"|".lib"             { BEGIN Sargs; return LIB; } 
<Snormal>^".MODEL"|".model"         { BEGIN Sargs; return MODEL; }
<Snormal>^".NODESET"|".nodeset"     { BEGIN Sargs; return NODESET; }
<Snormal>^".OPTIONS"|".options"     { BEGIN Sargs; return OPTIONS; }  
<Snormal>^".PARAM"|".param"         { BEGIN Sargs; return PARAM; }
<Snormal>^".PC"|".pc"               { BEGIN Sargs; return PC; }  
<Snormal>^(".PROT"{AlphaNumU}*)|(".prot"{AlphaNumU}*)           { return PROTECT; }  
<Snormal>^".SYS"|".sys"             { BEGIN Sargs; return SYSTEM; }  
<Snormal>^(".UNPROT"{AlphaNumU}*)|(".unprot"{AlphaNumU}*)       { return UNPROTECT; }  
<Snormal>^".WIDTH"|".width"         { BEGIN Sargs; return WIDTH; }  

    /* .INCLUDE directives - call lexer recursively on supplied filename */
<Snormal>^(".INC"{AlphaNumU}*)|(".inc"{AlphaNumU}*)             { BEGIN Sinclude;}  

    /* Analysis cards */
<Snormal>^".AC"|".ac"               { BEGIN Sargs; return ACSWEEP; }  
<Snormal>^".DC"|".dc"               { BEGIN Sargs; return DCSWEEP; }  
<Snormal>^".FOUR"|".four"           { BEGIN Sargs; return FOURIER; }  
<Snormal>^".NET"|".net"             { BEGIN Sargs; return NET; }  
<Snormal>^".DISTO"|".disto"         { BEGIN Sargs; return DISTORTION; }  
<Snormal>^".NOISE"|".noise"         { BEGIN Sargs; return NOISE; }  
<Snormal>^".OP"|".op"               { BEGIN Sargs; return OPPOINT; }  
<Snormal>^".PZ"|".pz"               { BEGIN Sargs; return POLEZERO; }  
<Snormal>^".SAMPLE"|".sample"       { BEGIN Sargs; return SAMPLE; }  
<Snormal>^".SENS"|".sens"           { BEGIN Sargs; return SENS; }  
<Snormal>^".TEMP"|".temp"           { BEGIN Sargs; return TEMP; }  
<Snormal>^".TF"|".tf"               { BEGIN Sargs; return TRANSFER; }  
<Snormal>^".TRAN"|".tran"           { BEGIN Sargs; return TRAN; }  

    /* Output cards */
<Snormal>^".MEASURE"|".measure"     { BEGIN Sargs; return MEASURE; }  
<Snormal>^".MEAS"|".meas"     { BEGIN Sargs; return MEASURE; }  
<Snormal>^".GRAPH"|".graph"         { BEGIN Sargs; return GRAPH; }  
<Snormal>^".PLOT"|".plot"           { BEGIN Sargs; return PLOT; }  
<Snormal>^".PRINT"|".print"         { BEGIN Sargs; return PRINT; }  

    /* remaining normal state details */
<Snormal>{Space}        { ; }
<Snormal>\n             { lineno++; }
<Snormal>.              { return yytext[0]; }

    /* Sargs state */
    /* an aregument can be a node name (N765s, 1234), numeric (123, 123.0, 123.0M), */
    /* or parameter (L=) or function [PULSE ( arg arg ...)] */

<Sargs>{Alpha}{AlphaNumSym}* { yylval.sval=strdup(yytext); return ARGIDENT; }
<Sargs>{Integer} { BEGIN Sargnum; yylval.sval=strdup(yytext); return ARGINT; }
<Sargs>{Decimal} { BEGIN Sargnum; yylval.sval=strdup(yytext); return ARGDECIMAL; }
<Sargs>{Float} {yylval.sval=strdup(yytext); return ARGFLOAT; }
<Sargs>['"] {BEGIN Sopenquote; return OPENQUOTE; }
<Sargs>{Space}        {;}
<Sargs>\n             { BEGIN Spluswait; lineno++; }
<Sargs>.              { return yytext[0]; }

<Sargnum>{Alpha}+ { BEGIN Sargs; yylval.sval=strdup(yytext); return ARGUNIT; }
<Sargnum>{Space}      { BEGIN Sargs; }
<Sargnum>\n           { BEGIN Spluswait; lineno++; }
<Sargnum>.            { BEGIN Sargs; return yytext[0]; }

    /* After newline in Sparam, check if next line starts with '+'         */
    /* if so, continue device arguments.  Otherwise, return token to       */
    /* stream and return to Snormal state.  Note: without the unput('\n'), */
    /* returned char will no longer match begin-of-line patterns.          */
    /* The .DATA statement is a special case, handled with is_data flag.   */

<Spluswait>^[^+\n] {
    yyless(0);
    if (is_data)
        BEGIN Sargs;
    else {
        unput('\n');
        BEGIN Snormal;
        return ARGSEND;
    }
}
<Spluswait>^".ENDDATA"|".enddata"     { is_data=0; BEGIN Snormal; return ENDDATA; }
<Spluswait>^"+" { BEGIN Sargs; }
<Spluswait>^\n { BEGIN Snormal; lineno++; if (!is_data) return ARGSEND; }
<Spluswait><<EOF>>        { BEGIN Snormal; if (!is_data) return ARGSEND; }

    /* Note: quoted strings can contain either literal strings for the         */
    /* .SYS, .LIB, .INC cards, or it can contain an algebraic expression       */
    /* in most places that a numeric argument is exprected.  It is impossible  */
    /* to tell which unless the specific context is known.  So we cannot       */
    /* parse expressions here.  There are passed back as strings and will have */
    /* to be parsed as expressions later.                                      */
<Sopenquote>[^'"\n]+ {yylval.sval=strdup(yytext); return QUOTESTRING; }
<Sopenquote>['"] { BEGIN Sargs; return CLOSEQUOTE; }
<Sopenquote>\n { lineno++; }

    /* swith lex buffer to a .INCLUDE file */
<Sinclude>{Space}* { ; } 
<Sinclude>["']{Filepath}+["'] { /* got the .INCLUDE filename */
    if ( include_stack_ptr >= MAX_INCLUDE_DEPTH ) {
        fprintf( stderr, "Parse error: .INCLUDEs nested too deeply.\n" );
        yyterminate();
    }
    lineno_stack[include_stack_ptr] = lineno++;
    include_stack[include_stack_ptr++] = YY_CURRENT_BUFFER;
    filename = strtok(yytext,"\"\' ");
    yyin_temp = yyin;
    yyin = fopen( filename, "r" );
    if (!yyin ) {
        fprintf(stderr, "Parse error: could not read .INCLUDE file %s, skipping...\n",filename);
        yyin = yyin_temp;
        include_stack_ptr--;
    }
    else {
        yy_switch_to_buffer(yy_create_buffer( yyin, YY_BUF_SIZE ) );
        lineno = 1;
    }
    BEGIN Snormal;
}
<Sinclude>\n   { BEGIN Snormal; lineno++; }
<Sinclude>.     { return yytext[0]; }
     
    /* Action to take on an end-of-file.  Unwind rtecursive buffers, or terminate */
<Snormal><<EOF>> {
    if ( --include_stack_ptr < 0 ) {
        yyterminate();
    }
    else {
        lineno = lineno_stack[include_stack_ptr];
        temp_yy_buffer = YY_CURRENT_BUFFER;
        yy_switch_to_buffer(include_stack[include_stack_ptr] );
        yy_delete_buffer(temp_yy_buffer);
    }
}

%%
