/* -*- C++ -*- */

/* Rename all exported vars from yyXXX to SkelXXX */
%option prefix="Skel"

/* Case insensitive matching for tags.            */
%option case-insensitive

/* Count lines...                                 */
%option yylineno

/* No default rule */
%option nodefault

/* Don't init yyin and yyout to stdin & stdout    */
%option nostdinit

/* Don't check for an interactive lexer...        */
%option never-interactive

/* Batch lexer, not interactive...                */
%option batch

/* Don't use the yywrap facility...               */
%option noyywrap

/* Make an 8 bit clean scanner...                 */
%option 8bit

/* Enable equilivance classes...                  */
%option ecs

/* Write output to SkelLexer.cpp...               */
%option outfile="SkelLexer.cpp"

/* Alternate state for when lexing in a tag...    */
%x InTag
%{

#include "SkelRep.h"
#include "SkelParser.h"

/* LitString decodes a string like "Test\"\\ \"" into Test"\ " */
static char *LitString(char *);

/* VarString decodes ($VarName) or $(VarName) into $VarName */
static char *VarString(char *Str);

#ifdef WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
%}

MSPlugin <MSPLUGIN
MSInsert <MSINSERT
MSIf     <MSIF
MSElIf   <MSELIF
MSElsIf  <MSELSIF
MSElseIf <MSELSEIF
MSElse   <MSELSE[ \t\n]*>
MSEndIf  <MSENDIF[ \t\n]*>
MSSIf    <[/]MSIF[ \t\n]*>

Raw      (([^<]|[\n])|(<[^M])|(<M[^S]))+|<

Var1     ([(]$[^)]+[)])
Var2     ($[(][^)]+[)])


/* Bare literal strings (LitStr) are letters, numbers, and .'s */
/* Quoted literal strings, can have anything up to their quote character */
LitStr   [a-zA-Z0-9.]+
LitStr2  \"([^\"]|\\\")*\"
LitStr3  '[^']*'
%%

{MSPlugin}  { BEGIN(InTag); return MSPLUGIN; }
{MSInsert}  { BEGIN(InTag); return MSINSERT; }
{MSIf}      { BEGIN(InTag); return MSIF;     }
{MSElse}    {               return MSELSE;   }
{MSElIf}    { BEGIN(InTag); return MSELSEIF; }
{MSElsIf}   { BEGIN(InTag); return MSELSEIF; }
{MSElseIf}  { BEGIN(InTag); return MSELSEIF; }
{MSEndIf}   {               return MSENDIF;  }
{MSSIf}     {               return MSENDIF;  }

<InTag>{LitStr}   { Skellval.StrData = LitString(yytext); return LITSTRING; }
<InTag>{LitStr2}  { Skellval.StrData = LitString(yytext); return LITSTRING; }
<InTag>{LitStr3}  { Skellval.StrData = LitString(yytext); return LITSTRING; }
<InTag>{Var1}     { Skellval.StrData = VarString(yytext); return VARIABLE; }
<InTag>{Var2}     { Skellval.StrData = VarString(yytext); return VARIABLE; }

<InTag>">"        { BEGIN(INITIAL); return '>'; }
<InTag>[=+&|()!}{] { return yytext[0]; }
<InTag>"=="       { return EQ;  }
<InTag>"<>"       { return NEQ; }
<InTag>"!="       { return NEQ; }
<InTag>"&&"       { return '&'; }
<InTag>"||"       { return '|'; }

<InTag>[ \t\n]    { /* Ignore whitespace */ }
<InTag>.          { return yytext[0]; }

{Raw}             { Skellval.StrData = strdup(yytext); return RAWDATA; }


%%

static char *VarString(char *Str) {
  Str[strlen(Str)-1] = 0;
  Str[1] = '$';
  return strdup(Str+1);
}

static char *LitString(char *Str) {
  int S1Length = strlen(Str);
  char *S = (char*)malloc(S1Length+1);

  // Remove starting and ending quotes 
  if (Str[0] == '\'' || Str[0] == '\"') {
    Str[S1Length-1] = 0; 
    Str++; 
  } 

  char *Src = Str, *Dest = S;
  while (*Src != 0) {
    if (*Src == '\\') {
      Src++;
      switch (*Src) {
      case 0:     // Null...
	return S;
      case 'n':   //   \n  = Newline
	*Dest = '\n';
	break;
      case 't':   //   \t  = Tab
	*Dest = '\t';
	break;
      case 'e':   //   \e  = esc
	*Dest = '\033';
	break;
      case 'r':   //   \r  = Carriage return
	*Dest = '\r';
	break;
      case 'f':   //   \f  = Form feed
	*Dest = '\f';
	break;
      case 'b':   //   \b  = Backspace
	*Dest = '\b';
	break;
      case 'a':   //   \a  = Alert (beep)
	*Dest = '\a';
	break;
      default:
	*Dest = *Src;
	break;
      }

    } else {
      *Dest = *Src;
    }

    Src++;
    Dest++;
  }

  *Dest = 0;
  return S;
}
