%{
/****************************************************************************
    Copyright (C) 1987-2004 by Jeffery P. Hansen

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    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.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
****************************************************************************/
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include "yybasic.h"
#include "ycmalloc.h"
#include "vgrammar.h"

#ifndef YY_START
#define YY_START YYSTATE
#endif

struct lex_keywordentry {
    char *Keyword;
    int Value;
};

struct lex_keywordentry lex_verilog_words[] = {
  {"assign",ASSIGN},
  {"endmodule",ENDMODULE},
  {"inout",INOUT},
  {"input",INPUT},
  {"module",MODULE},
  {"output",OUTPUT},
  {"supply0",SUPPLY0},
  {"supply1",SUPPLY1},
  {"tran",TRAN},
  {"wire",WIRE},
};
int lex_verilog_num = sizeof(lex_verilog_words)/sizeof(lex_verilog_words[0]);

struct lex_keywordentry lex_anotate_words[] = {
  {"comment",COMMENT},
  {"enddecls",ENDDECLS},
  {"frame",FRAME},
  {"interface",BDESC},
  {"joint",JOINT},
  {"property",PROPERTY},
  {"root_module",ROOTMODULE},

  {"script",SCRIPT},
  {"version",VERSION},
};
int lex_anotate_num = sizeof(lex_anotate_words)/sizeof(lex_anotate_words[0]);

struct lex_keywordentry lex_aoption_words[] = {
  {"/bd",BDPORTS},
  {"/dp",DECPOS},
  {"/end",COMEND},
  {"/p",PORTS},
  {"/r",ROT},
  {"/sn",SHOWNAME},
  {"/st",STATE},
  {"/sz",SIZE},
  {"/w",WPLACE},
};
int lex_aoption_num = sizeof(lex_aoption_words)/sizeof(lex_aoption_words[0]);


struct lex_keywordentry lex_delay_words[] = {
  {"area",AREA},
  {"break",BREAK},
  {"case",CASE},
  {"default",DEFAULT},
  {"delay",DELAY},
  {"else",ELSE},
  {"if",IF},
  {"power",POWER},
  {"primitive",PRIMITIVE},
  {"return",RETURN},
  {"switch",SWITCH},
  {"technology",TECHNOLOGY},
  {"version",VERSION},
};
int lex_delay_num = sizeof(lex_delay_words)/sizeof(lex_delay_words[0]);

static int yc_last_mode;

int ycLiteral(char *Tok,struct lex_keywordentry *low,int len);
int ycString(char *S);

int ycLineNumber;
const char *ycFileName;

/*
  Start states:
	VR	Verilog
	BC	Block comment
	LC	Line comment
	AC	Annotation comment
	DD	Delay definition
	DDP	Delay definition parameter
*/
%}

%start	VR BC LC AC DD DDP VN

white	[ \t\r]
num	[0-9]
hex	[0-9A-Fa-f]
lit1	[A-Za-z_]
lit2	[A-Za-z0-9_]
litddp	[A-Za-z0-9_/*-]
vnset	[A-Za-z0-9.]

%%

<VR,DD,DDP>"/*"		{ yc_last_mode = YY_START; BEGIN BC; }
<BC>"*/"		{ BEGIN yc_last_mode; }
<BC>.			{ }
<BC>\n			{ ycLineNumber++; }
<VR,DD,DDP>"//"		{ yc_last_mode = YY_START; BEGIN LC; }
<LC>\n			{ ycLineNumber++; BEGIN yc_last_mode; }
<LC>.			{ }

<VR>"//:"		{ yc_last_mode = YY_START; BEGIN AC; return ANOTATE; }
<AC>\n			{ ycLineNumber++; BEGIN yc_last_mode; return ENDANOTATE; }

<VR,AC,DD>";"		{ return SEMI; }
<VR,AC,DD>":"		{ return COLON; }
<VR,AC,DD>","		{ return COMMA; }
<VR,AC,DD>"."		{ return DOT; }
<VR,AC,DD>"="		{ return ASGN; }
<VR,AC>"/"		{ return SLASH; }
<VR,AC,DD>"("		{ return LPAREN; }
<VR,AC,DD>")"		{ return RPAREN; }
<VR,AC,DD>"{"		{ return LBRACE; }
<VR,AC,DD>"}"		{ return RBRACE; }
<VR,AC,DD>"["		{ return LBRACK; }
<VR,AC,DD>"]"		{ return RBRACK; }
<VR,AC,DD>"!"		{ return NOT; }
<VR,AC,DD>"@"		{ return AT; }
<VR,AC,DD,DDP>">"	{ return GT; }
<VR,AC,DD,DDP>"<"	{ return LT; }

<DD>"||"		{ return OR; }
<DD>"&&"		{ return AND; }
<DD>">="		{ return GE; }
<DD>"<="		{ return LE; }
<DD>"=="		{ return EQ; }
<DD>"!="		{ return NE; }
<DD>"+"			{ return ADD; }
<DD>"-"			{ return SUB; }
<DD>"*"			{ return MUL; }
<DD>"**"		{ return POW; }
<DD>"/"			{ return DIV; }

<VR,AC,DD>\"([^\\\"]*("\\\\"|"\\\"")*)*\"	{ return ycString(yytext); }
<VR,AC,DD>{num}+"'h"{hex}+	{ yylval.S = yc_strdup(yytext); return HEX; } 
<DD>{num}+		{ sscanf(yytext,"%d",&yylval.I); return NUMBER; }
<VR,AC>"-"?{num}+	{ sscanf(yytext,"%d",&yylval.I); return NUMBER; }
<DDP>{litddp}+		{ return ycLiteral(yytext,lex_delay_words,lex_delay_num); }
<DD>{lit1}{lit2}*	{ return ycLiteral(yytext,lex_delay_words,lex_delay_num); }

<VR>{lit1}{lit2}*	{ return ycLiteral(yytext,lex_verilog_words,lex_verilog_num); }
<AC>{lit1}{lit2}*	{ return ycLiteral(yytext,lex_anotate_words,lex_anotate_num); }
<AC>"/"{lit1}{lit2}*	{ return ycLiteral(yytext,lex_aoption_words,lex_aoption_num); }

<VN>{vnset}+		{ yylval.S = yc_strdup(yytext); return VERNUM; }
<VR,AC,DD,DDP,VN>{white}+	{ }
<VR,DD,DDP,VN>"\n"		{ ycLineNumber++; }

.			{ yyerror("Illegal character (%d) '%c'.\n",*yytext,*yytext); return BOGOCHAR; }


%%

int ycKeyCmp(const char *S1,const char *S2)
{
  for (;*S1;S1++,S2++) {
    int C1 = islower(*S1) ? toupper(*S1) : *S1;
    int C2 = islower(*S2) ? toupper(*S2) : *S2;
    if (C1 != C2) return (C1 < C2) ? -1 : 1;
  }
  return *S2 ? -1 : 0;
}

int ycLookup(char *Tok,struct lex_keywordentry *low,int len)
{
  struct lex_keywordentry *high = low+len-1;
  struct lex_keywordentry *K;

  while (low <= high) {
    K = low + (high-low)/2;
    switch (ycKeyCmp(K->Keyword,Tok)) {
    case  0 :
      return K->Value;
    case -1 :
      low = K + 1;
      break;
    case  1 :
      high = K - 1;
      break;
    }
  }

  return -1;
}

int ycIsKW(char *Tok)
{
  if (ycLookup(Tok,lex_anotate_words,lex_anotate_num) >= 0) return 1;
  if (ycLookup(Tok,lex_verilog_words,lex_verilog_num) >= 0) return 1;
  return 0;
}

/*
    Parse a "literal-like" token.  If it is in a keyword table, return the
    token for that keyword, otherwise treat it as a literal.
*/
int ycLiteral(char *Tok,struct lex_keywordentry *low,int len)
{
  int t;

  if (!Tok) Tok = yytext;
  yylval.S = "???";

  t = ycLookup(Tok,low,len);
  yylval.S = yc_strdup(Tok);

  return t >= 0 ? t : LITERAL;
}

int ycString(char *S)
{
  S = yylval.S = yc_strdup(S+1);
  S[strlen(S)-1] = 0;

  for (;*S;S++)
    if (*S == '\\' && S[1]) {
      memmove(S,S+1,strlen(S+1)+1);
      S++;
    }

  return STRING;
}

void BeginVR() { BEGIN VR; }
void BeginAC() { BEGIN AC; }
void BeginLC() { BEGIN LC; }
void BeginBC() { BEGIN BC; }
void BeginDD() { BEGIN DD; }
void BeginDDP() { BEGIN DDP; }
void BeginVN() { BEGIN VN; }

int yywrap()
{
  return 1;
}
