/* 
 * Copyright (c) 2001 Secure Software Solutions
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

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

int clex_lineno = 1;
int yyclength = 0;
int yycsize = 0;
char *yyccomment = NULL;

static int  identifier(void);
static int  string_const(void);
static int  preprocessor(void);
static void reset_comment(void);
static int  cstyle_comment(void);
static void no_match(void);

#define YY_INPUT(buf, result, max_size)                                     \
    if (((result = fread(buf, 1, max_size, yycin)) == 0) && ferror(yycin)) {    \
        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(yycin, -1, SEEK_CUR);                                      \
        }                                                                   \
    }

%}

%%

"#"                     { return preprocessor(); }
"/*"                    { return cstyle_comment(); }
"//".*                  { reset_comment();  return COMMENT; }

"auto"                  { return AUTO; }
"break"                 { return BREAK; }
"case"                  { return CASE; }
"char"                  { return CHAR; }
"const"                 { return CONST; }
"continue"              { return CONTINUE; }
"default"               { return DEFAULT; }
"do"                    { return DO; }
"double"                { return DOUBLE; }
"else"                  { return ELSE; }
"enum"                  { return ENUM; }
"extern"                { return EXTERN; }
"float"                 { return FLOAT; }
"for"                   { return FOR; }
"goto"                  { return GOTO; }
"if"                    { return IF; }
"int"                   { return INT; }
"long"                  { return LONG; }
"register"              { return REGISTER; }
"return"                { return RETURN; }
"short"                 { return SHORT; }
"signed"                { return SIGNED; }
"sizeof"                { return SIZEOF; }
"static"                { return STATIC; }
"struct"                { return STRUCT; }
"switch"                { return SWITCH; }
"typedef"               { return TYPEDEF; }
"union"                 { return UNION; }
"unsigned"              { return UNSIGNED; }
"void"                  { return VOID; }
"volatile"              { return VOLATILE; }
"while"                 { return WHILE; }

[a-zA-Z_]([a-zA-Z_]|[0-9]|\$|[\r])* { return identifier(); }

0[xX][a-fA-F0-9]+(u|U|l|L)*     { return HEX_CONST; }
0[0-9]+(u|U|l|L)*               { return OCT_CONST; }
[0-9]+(u|U|l|L)*                { return DEC_CONST; }
'(\\.|[^\\'])+'                 { return CHAR_CONST; }

[0-9]+[Ee][+-]?[0-9]+(f|F|l|L)*              { return FLOAT_CONST; }
[0-9]*"."[0-9]+([Ee][+-]?[0-9]+)?(f|F|l|L)*  { return FLOAT_CONST; }
[0-9]+"."[0-9]*([Ee][+-]?[0-9]+)?(f|F|l|L)*  { return FLOAT_CONST; }

\"(\\.|[^\\"])*\"       { return string_const(); }

">>="                   { return RIGHT_ASSIGN; }
"<<="                   { return LEFT_ASSIGN; }
"+="                    { return ADD_ASSIGN; }
"-="                    { return SUB_ASSIGN; }
"*="                    { return MUL_ASSIGN; }
"/="                    { return DIV_ASSIGN; }
"%="                    { return MOD_ASSIGN; }
"&="                    { return AND_ASSIGN; }
"^="                    { return XOR_ASSIGN; }
"|="                    { return OR_ASSIGN; }
">>"                    { return RIGHT_OP; }
"<<"                    { return LEFT_OP; }
"++"                    { return INC_OP; }
"--"                    { return DEC_OP; }
"->"                    { return PTR_OP; }
"&&"                    { return AND_OP; }
"||"                    { return OR_OP; }
"<="                    { return LE_OP; }
">="                    { return GE_OP; }
"=="                    { return EQ_OP; }
"!="                    { return NE_OP; }
";"                     { return ';'; }
"{"                     { return '{'; }
"}"                     { return '}'; }
","                     { return ','; }
":"                     { return ':'; }
"="                     { return '='; }
"("                     { return '('; }
")"                     { return ')'; }
"["                     { return '['; }
"]"                     { return ']'; }
"."                     { return '.'; }
"&"                     { return '&'; }
"!"                     { return '!'; }
"~"                     { return '~'; }
"-"                     { return '-'; }
"+"                     { return '+'; }
"*"                     { return '*'; }
"/"                     { return '/'; }
"%"                     { return '%'; }
"<"                     { return '<'; }
">"                     { return '>'; }
"^"                     { return '^'; }
"|"                     { return '|'; }
"?"                     { return '?'; }
 
[ \t\v\f]               { /* eat white space */ }
[\n\r]                  { clex_lineno++; }
.                       { no_match(); }

%%

int yywrap(void)
{
    return 1;
}

static
int identifier(void)
{
    char *  c;

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

static
int string_const(void)
{
    char *  c;

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

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

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

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

static
int cstyle_comment(void)
{
    char    c;

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

    return COMMENT;
}

static
int preprocessor(void)
{
    char    c;

    while ((c = input()) && c != -1)
    {
        if (c == '\n')
        {
            clex_lineno++;
            break;
        }
        if (c == '\r')
            clex_lineno++;

        /* handle multi-line comments beginning on a preprocessor line */
        if (c == '/')
        {
            if (!(c = input()) || c == -1)
                break;
            if (c == '*')
            {
                int save_lineno = clex_lineno;

                cstyle_comment();
                if (clex_lineno != save_lineno)
                    return COMMENT;
                continue;
            }
            unput(c);
        }
    }

    return JUNK;
}

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