%x IN_PHP_SCRIPT
%x IN_PHP_OCOMMENT
%option stack

%{
#include <string.h>
#include "tokens.h"
#include "engine.h"

int phplex_lineno = 1;
int yyphplength = 0;
int yyphpsize = 0;
char *yyphpcomment = NULL;

static int  identifier(void);
static void reset_comment(void);
static int  cstyle_comment(void);
static void no_match(void);
static void gobble_string(char c);
static void scan_yytext(void);

#define YY_INPUT(buf, result, max_size)                                     \
    if (((result = fread(buf, 1, max_size, yyin)) == 0) && ferror(yyin)) { \
        YY_FATAL_ERROR("input in flex scanner failed");                     \
    } else {                                                                  \
        char *c, *end = (buf) + result - 1;                                 \
        for (c = (buf);  c < end;  c++) {                                   \
            if (*c == '\r') *c = ' ';                                       \
            if (*c == '\\' && *(c + 1) == '\n') {                           \
                memmove(c + 1, c + 2, end - c);                             \
                result--;                                                   \
                end--;                                                      \
                *c = '\r';                                                  \
            }                                                               \
        }                                                                   \
        if (*end == '\r') *end = ' ';                                       \
        if (*end == '\\') {                                                 \
            result--;                                                       \
            fseek(yyin, -1, SEEK_CUR);                                      \
        }                                                                   \
    }

%}



LNUM    [0-9]+
DNUM    ([0-9]*[\.][0-9]+)|([0-9]+[\.][0-9]*)
EXPONENT_DNUM   (({LNUM}|{DNUM})[eE][+-]?{LNUM})
HNUM    "0x"[0-9a-fA-F]+
LABEL   [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
WHITESPACE [ \n\r\t]+
TABS_AND_SPACES [ \t]*
TOKENS [;:,.\[\]()|^&+-/*=%!~$<>?@]
NEWLINE ("\r"|"\n"|"\r\n")


%%

<INITIAL>"<?"|"<script"{WHITESPACE}+"language"{WHITESPACE}*"="{WHITESPACE}*("php"|"\"php\""|"\'php\'"){WHITESPACE}*">"  {
    BEGIN(IN_PHP_SCRIPT);
    scan_yytext();
    return PHP_IN_SCRIPT;
}
   

<INITIAL>"<%="|"<?="  {
    BEGIN(IN_PHP_SCRIPT);
    return PHP_IN_SCRIPT;
}

<INITIAL>"<%"  {

    BEGIN(IN_PHP_SCRIPT);
    return PHP_IN_SCRIPT;
}


<INITIAL>"<?php"([ \t]|{NEWLINE})  {
    
    BEGIN(IN_PHP_SCRIPT);
    scan_yytext();
    return PHP_IN_SCRIPT;
}



<IN_PHP_SCRIPT,IN_PHP_OCOMMENT>("?>"|"</script"{WHITESPACE}*">"){NEWLINE}?  {
    BEGIN(INITIAL);
    scan_yytext();
    return PHP_IN_SCRIPT;

}

<IN_PHP_SCRIPT,IN_PHP_OCOMMENT>"%>"{NEWLINE}?  {
    BEGIN(INITIAL);
    scan_yytext();
    return PHP_IN_SCRIPT;
}




<IN_PHP_SCRIPT>"#"|"//"		{
                                  BEGIN(IN_PHP_OCOMMENT);
                                  return COMMENT;
                                }
 

<IN_PHP_SCRIPT>"/*"		{return cstyle_comment();}
<IN_PHP_SCRIPT>"$"	       {return '$';}
<IN_PHP_SCRIPT>"old_function"  {return FUNCTION;}
<IN_PHP_SCRIPT>"function"|"cfunction" {return FUNCTION;}
<IN_PHP_SCRIPT>"const"		{return CONST;}
<IN_PHP_SCRIPT>"return"	{return RETURN;}
<IN_PHP_SCRIPT>"if"		{return IF;}
<IN_PHP_SCRIPT>"elseif"	{return ELSEIF;}
<IN_PHP_SCRIPT>"else"		{return ELSE;}
<IN_PHP_SCRIPT>"while"		{return WHILE;}
<IN_PHP_SCRIPT>"endwhile"	{return ENDWHILE;}
<IN_PHP_SCRIPT>"do"		{return DO;}
<IN_PHP_SCRIPT>"for"		{return FOR;}
<IN_PHP_SCRIPT>"endfor"	{return ENDFOR;}
<IN_PHP_SCRIPT>"foreach"	{return FOREACH;}
<IN_PHP_SCRIPT>"endforeach"	{return ENDFOREACH;}
<IN_PHP_SCRIPT>"declare"	{return DECLARE;}
<IN_PHP_SCRIPT>"enddeclare"	{return ENDDECLARE;}
<IN_PHP_SCRIPT>"as"		{return AS;}
<IN_PHP_SCRIPT>"switch"	{return SWITCH;}
<IN_PHP_SCRIPT>"endswitch"	{return ENDSWITCH;}
<IN_PHP_SCRIPT>"case"		{return CASE;}
<IN_PHP_SCRIPT>"default"	{return DEFAULT;}
<IN_PHP_SCRIPT>"break"		{return BREAK;}
<IN_PHP_SCRIPT>"continue"	{return CONTINUE;}
<IN_PHP_SCRIPT>"print"		{return PRINT;}
<IN_PHP_SCRIPT>"class"		{return CLASS;}
<IN_PHP_SCRIPT>"extends"	{return EXTENDS;}
<IN_PHP_SCRIPT>"var"		{return VAR;}
<IN_PHP_SCRIPT>"=>"            {return DOUBLE_ARROW;}
<IN_PHP_SCRIPT>"++"		{return INC_OP;}
<IN_PHP_SCRIPT>"--"		{return DEC_OP;}
<IN_PHP_SCRIPT>"==="           {return T_EQUAL;}
<IN_PHP_SCRIPT>"!=="	        {return T_NOTEQUAL;}	
<IN_PHP_SCRIPT>"=="		{return EQ_OP;}
<IN_PHP_SCRIPT>"!="|"<>"	{return NE_OP;}
<IN_PHP_SCRIPT>"<="		{return LE_OP;}
<IN_PHP_SCRIPT>">="		{return GE_OP;}
<IN_PHP_SCRIPT>"+="		{return ADD_ASSIGN;}
<IN_PHP_SCRIPT>"-="		{return SUB_ASSIGN;}
<IN_PHP_SCRIPT>"*="		{return MUL_ASSIGN;}
<IN_PHP_SCRIPT>"/="		{return DIV_ASSIGN;}
<IN_PHP_SCRIPT>".="		{return CONCAT_ASSIGN;}
<IN_PHP_SCRIPT>"%="		{return MOD_ASSIGN;}
<IN_PHP_SCRIPT>"<<="		{return LEFT_ASSIGN;}
<IN_PHP_SCRIPT>">>="		{return RIGHT_ASSIGN;}
<IN_PHP_SCRIPT>"&="		{return AND_ASSIGN;}
<IN_PHP_SCRIPT>"|="		{return OR_ASSIGN;}
<IN_PHP_SCRIPT>"^="		{return XOR_ASSIGN;}
<IN_PHP_SCRIPT>"||"		{return OR_OP;}
<IN_PHP_SCRIPT>"&&"		{return AND_OP;}
<IN_PHP_SCRIPT>"OR"		{return OR_OP;}
<IN_PHP_SCRIPT>"AND"		{return AND_OP;}
<IN_PHP_SCRIPT>"XOR"		{return XOR_OP;}
<IN_PHP_SCRIPT>"<<"		{return LEFT_OP;}
<IN_PHP_SCRIPT>">>"		{return RIGHT_OP;}
<IN_PHP_SCRIPT>{HNUM}		{return HEX_CONST;}
<IN_PHP_SCRIPT>{DNUM}		{return DEC_CONST;}
<IN_PHP_SCRIPT>{LNUM}		{return DEC_CONST;}
<IN_PHP_SCRIPT>{EXPONENT_DNUM}	{return DEC_CONST;}
<IN_PHP_SCRIPT>{LABEL}		{return identifier();}
<IN_PHP_SCRIPT>";"                     { return ';'; }
<IN_PHP_SCRIPT>"{"                     { return '{'; }
<IN_PHP_SCRIPT>"}"                     { return '}'; }
<IN_PHP_SCRIPT>","                     { return ','; }
<IN_PHP_SCRIPT>":"                     { return ':'; }
<IN_PHP_SCRIPT>"="                     { return '='; }
<IN_PHP_SCRIPT>"("                     { return '('; }
<IN_PHP_SCRIPT>")"                     { return ')'; }
<IN_PHP_SCRIPT>"["                     { return '['; }
<IN_PHP_SCRIPT>"]"                     { return ']'; }
<IN_PHP_SCRIPT>"."                     { return '.'; }
<IN_PHP_SCRIPT>"&"                     { return '&'; }
<IN_PHP_SCRIPT>"!"                     { return '!'; }
<IN_PHP_SCRIPT>"~"                     { return '~'; }
<IN_PHP_SCRIPT>"-"                     { return '-'; }
<IN_PHP_SCRIPT>"+"                     { return '+'; }
<IN_PHP_SCRIPT>"*"                     { return '*'; }
<IN_PHP_SCRIPT>"/"                     { return '/'; }
<IN_PHP_SCRIPT>"%"                     { return '%'; }
<IN_PHP_SCRIPT>"<"                     { return '<'; }
<IN_PHP_SCRIPT>"`"                     { return '`'; }

<IN_PHP_SCRIPT>">"                     { return '>'; }
<IN_PHP_SCRIPT>"^"                     { return '^'; }
<IN_PHP_SCRIPT>"@"		       {return '@'; }
<IN_PHP_SCRIPT>"|"                     { return '|'; }
<IN_PHP_SCRIPT>"?"                     { return '?'; }
<IN_PHP_SCRIPT>("\"")                  { gobble_string('"'); return STRING_CONST; }
<IN_PHP_SCRIPT>("'")                  { gobble_string('\''); return STRING_CONST; }


<*>[ \t\v\f]               { /* eat white space */ }
<IN_PHP_OCOMMENT>[\n\r]   { BEGIN(IN_PHP_SCRIPT); phplex_lineno++; } 
<INITIAL,IN_PHP_SCRIPT>[\n\r]                  { phplex_lineno++; }
<IN_PHP_OCOMMENT>.		       { /* eat it! */}
<IN_PHP_SCRIPT>.                       { no_match(); }
<INITIAL>.			       { /* it's just HTML, we don't care */}

%%


int yywrap(void)
{
    return 1;
}



static
void gobble_string(char which)
{

  int bslash = 0;
  char c;
  while ((c = input()) && c != -1)
  {

    switch(c)  {

      case '\\':
                 if (!bslash)
                   bslash = 1;
                 else
                   bslash = 0;
                 break;
      case '\n':
                 phplex_lineno++;
                 bslash = 0;
                 break;
      default:
                 if (c == which && !bslash)  {
                   return;
                 }
                 bslash = 0;
                 break;
    }
  }
}

static 
void scan_yytext(void)
{

    char *tmp;
    tmp = yytext;
    while(*tmp)  {
      if(*tmp == '\n' || *tmp == '\r')
        phplex_lineno++;
      tmp++;
    }
}

         
static
int identifier(void)
{
    char *  c;

    while ((c = strchr(yytext, '\r')) != (char *)NULL)
    {
        memmove(c, c + 1, strlen(c));
        phplex_lineno++;
    }
    return IDENTIFIER;
}

static
void no_match(void)
{
    fprintf(stderr, "%s:%d: warning: bad token `%s'\n", current_file, phplex_lineno, yytext);
}

static
void accumulate_comment(char *data, int length)
{
    int     need;
    char *  text = yyphpcomment;

    need = yyphplength + length + 1;
    need = (need + 127) / 128 * 128;
    if (need > yyphpsize)
    {
        text = (char *)(yyphpsize ? realloc(yyphpcomment, need) : malloc(need));
        if (text == (char *)NULL)
            return;
        yyphpsize = need;
        yyphpcomment = text;
    }
    memcpy(yyphpcomment + yyphplength, data, length);
    yyphplength += length;
    *(yyphpcomment + yyphplength) = '\0';
}

static
void reset_comment(void)
{
    if (yyphpcomment != (char *)NULL)
        *yyphpcomment = '\0';
    yyphplength = 0;
}

static
int cstyle_comment(void)
{
    char    c;

    reset_comment();
    while ((c = input()) && c != -1)
    {
        accumulate_comment(&c, 1);
        if (c == '\n' || c == '\r')
            phplex_lineno++;
        while (c == '*')
        {
            if (!(c = input()) || c == -1) {
                return COMMENT;
            }
            if (c == '\n' || c == '\r')
                phplex_lineno++;
            if (c == '/')  {
                return COMMENT;
            } else
            {
                char tmp[2] = { '*', c };
                accumulate_comment(tmp, sizeof(tmp));
            }
        }
    }
    return COMMENT;
}

