/*
* Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
* See included license file for license details.
*/

%option c++
/* %option prefix="Elftosb" */
%option yylineno
%option never-interactive
%option yyclass="ElftosbLexer"
%option noyywrap

%{
#include "ElftosbLexer.h"
#include <stdlib.h>
#include <limits.h>
#include <string>
#include "HexValues.h"
#include "Value.h"

using namespace elftosb;

//! Always executed before all other actions when a token is matched.
//! This action just assign the first and last lines of the token to
//! the current line. In most cases this is correct.
#define YY_USER_ACTION  do {                                                                    \
                                                       m_location.m_firstLine = m_line;                \
                                                       m_location.m_lastLine = m_line;         \
                                               } while (0);

%}

DIGIT           [0-9]
HEXDIGIT        [0-9a-fA-F]
BINDIGIT        [0-1]
IDENT           [a-zA-Z_][a-zA-Z0-9_]*
ESC                     \\(x{HEXDIGIT}{2}|.)

/* start conditions */
%x blob mlcmt

%%

options                 { return TOK_OPTIONS; }
constants               { return TOK_CONSTANTS; }
sources                 { return TOK_SOURCES; }
filters                 { return TOK_FILTERS; }
section                 { return TOK_SECTION; }
extern                  { return TOK_EXTERN; }
from                    { return TOK_FROM; }
raw                             { return TOK_RAW; }
load                    { return TOK_LOAD; }
jump                    { return TOK_JUMP; }
call                    { return TOK_CALL; }
mode                    { return TOK_MODE; }
if                              { return TOK_IF; }
else                    { return TOK_ELSE; }
defined                 { return TOK_DEFINED; }
info                    { return TOK_INFO; }
warning                 { return TOK_WARNING; }
error                   { return TOK_ERROR; }
sizeof                  { return TOK_SIZEOF; }
dcd                             { return TOK_DCD; }
hab                             { return TOK_HAB; }
ivt             { return TOK_IVT; }

[whb]/[^a-zA-Z_0-9]                                     {       // must be followed by any non-ident char
                                                                               int_size_t theSize;
                                                                               switch (yytext[0])
                                                                               {
                                                                                       case 'w':
                                                                                               theSize = kWordSize;
                                                                                               break;
                                                                                       case 'h':
                                                                                               theSize = kHalfWordSize;
                                                                                               break;
                                                                                       case 'b':
                                                                                               theSize = kByteSize;
                                                                                               break;
                                                                               }
                                                                               m_symbolValue.m_int = new elftosb::SizedIntegerValue(0, theSize);
                                                                               return TOK_INT_SIZE;
                                                                       }

true|yes                                                        {
                                                                               m_symbolValue.m_int = new elftosb::SizedIntegerValue(1, kWordSize);
                                                                               return TOK_INT_LITERAL;
                                                                       }

false|no                                                        {
                                                                               m_symbolValue.m_int = new elftosb::SizedIntegerValue(0, kWordSize);
                                                                               return TOK_INT_LITERAL;
                                                                       }

{IDENT}                                                         {
                                                                               m_symbolValue.m_str = new std::string(yytext);
                                                                               if (isSourceName(m_symbolValue.m_str))
                                                                               {
                                                                                       return TOK_SOURCE_NAME;
                                                                               }
                                                                               else
                                                                               {
                                                                                       return TOK_IDENT;
                                                                               }
                                                                       }

({DIGIT}+|0x{HEXDIGIT}+|0b{BINDIGIT}+)([ \t]*[GMK])?                    {
                                                                               int base = 0;
                                                                               uint32_t value;
                                                                               int mult;

                                                                               // check for binary number
                                                                               if (yytext[0] == '0' && yytext[1] == 'b')
                                                                               {
                                                                                       base = 2;               // this is a binary number
                                                                                       yytext += 2;    // skip over the "0b"
                                                                               }

                                                                               // convert value
                                                                               value = (uint32_t)strtoul(yytext, NULL, base);

                                                                               // find multiplier
                                                                               switch (yytext[strlen(yytext) - 1])
                                                                               {
                                                                                       case 'G':
                                                                                               mult = 1024 * 1024 * 1024;
                                                                                               break;
                                                                                       case 'M':
                                                                                               mult = 1024 * 1024;
                                                                                               break;
                                                                                       case 'K':
                                                                                               mult = 1024;
                                                                                               break;
                                                                                       default:
                                                                                               mult = 1;
                                                                                               break;
                                                                               }

                                                                               // set resulting symbol value
                                                                               m_symbolValue.m_int = new elftosb::SizedIntegerValue(value * mult, kWordSize);
                                                                               return TOK_INT_LITERAL;
                                                                       }

\'(.|ESC)\'|\'(.|ESC){2}\'|\'(.|ESC){4}\'               {
                                                                               uint32_t value = 0;
                                                                               int_size_t theSize;
                                                                               int len = strlen(yytext);
                                                                               if (len >= 3)
                                                                               {
                                                                                       value = yytext[1];
                                                                                       theSize = kByteSize;
                                                                               }
                                                                               if (len >= 4)
                                                                               {
                                                                                       value = (value << 8) | yytext[2];
                                                                                       theSize = kHalfWordSize;
                                                                               }
                                                                               if (len >= 6)
                                                                               {
                                                                                       value = (value << 8) | yytext[3];
                                                                                       value = (value << 8) | yytext[4];
                                                                                       theSize = kWordSize;
                                                                               }
                                                                               m_symbolValue.m_int = new elftosb::SizedIntegerValue(value, theSize);
                                                                               return TOK_INT_LITERAL;
                                                                       }

\$[\.\*a-zA-Z0-9_\[\]\^\?\-]+                                   {
                                                                               // remove $ from string
                                                                               m_symbolValue.m_str = new std::string(&yytext[1]);
                                                                               return TOK_SECTION_NAME;
                                                                       }


"/*"                                { BEGIN(mlcmt); }

"{{"                                                            {
                                                                               m_blob = new Blob();
                                                                               m_blobFirstLine = yylineno;
                                                                               BEGIN(blob);
                                                                       }

"{"                                                                     { return '{'; }

"}"                                                                     { return '}'; }

"("                                                                     { return '('; }

")"                                                                     { return ')'; }

"["                                                                     { return '['; }

"]"                                                                     { return ']'; }

"="                                                                     { return '='; }

","                                                                     { return ','; }

":"                                                                     { return ':'; }

";"                                                                     { return ';'; }

"."                                                                     { return '.'; }

">"                                                                     { return '>'; }

".."                                                            { return TOK_DOT_DOT; }

"+"                                                                     { return '+'; }

"-"                                                                     { return '-'; }

"*"                                                                     { return '*'; }

"/"                                                                     { return '/'; }

"%"                                                                     { return '%'; }

"~"                                                                     { return '~'; }

"^"                                                                     { return '^'; }

"<<"                                                            { return TOK_LSHIFT; }

">>"                                                            { return TOK_RSHIFT; }

"&"                                                                     { return '&'; }

"|"                                                                     { return '|'; }

"**"                                                            { return TOK_POWER; }

"<"                                                                     { return '<'; }

">="                                                            { return TOK_GEQ; }

"<="                                                            { return TOK_LEQ; }

"=="                                                            { return TOK_EQ; }

"!="                                                            { return TOK_NEQ; }

"&&"                                                            { return TOK_AND; }

"||"                                                            { return TOK_OR; }

"!"                                                                     { return '!'; }

\"(ESC|[^\"])*\"                                        {
                                                                               // get rid of quotes
                                                                               yytext++;
                                                                               yytext[strlen(yytext) - 1] = 0;
//                                                                              processStringEscapes(yytext, yytext);
                                                                               m_symbolValue.m_str = new std::string(yytext);
                                                                               return TOK_STRING_LITERAL;
                                                                       }

<blob>{HEXDIGIT}{2}                                     {
                                                                               uint8_t x = (hexCharToInt(yytext[0]) << 4) | hexCharToInt(yytext[1]);
                                                                               m_blob->append(&x, 1);
                                                                       }

<blob>"}}"                                                      {
                                                                               BEGIN(INITIAL);
                                                                               m_symbolValue.m_blob = m_blob;
                                                                               m_blob = NULL;
                                                                               m_location.m_firstLine = m_blobFirstLine;
                                                                               return TOK_BLOB;
                                                                       }

<mlcmt>\*\/                         {
                                       // end of multi-line comment, return to initial state
                                       BEGIN(INITIAL);
                                   }


(#|\/\/).*$                                                     /* absorb single-line comment */

<*>[ \t]                                                        /* eat up whitespace in all states */

<*>(\r\n|\r|\n)                                         {
                                                                               /* eat up whitespace and count lines in all states */
                                                                               m_line++;
                                                                       }

<mlcmt>.                            /* ignore all other chars in a multi-line comment */

<*>.                                                            {
                                                                               /* all other chars produce errors */
                                                                               char msg[50];
                                                                               sprintf(msg, "unexpected character '%c' on line %d", yytext[0], m_line);
                                                                               LexerError(msg);
                                                                       }

%%

// verbatim code copied to the bottom of the output