/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

%top{
    /* Avoid redefinition of integer limits on platforms without inttypes.h. */
    #include "charmony.h"
}

%{
    #include "CFC.h"
    #include "CFCParseHeader.h"

    /* Dupe yytext and invoke Lemon-generated parser. */
    #define PARSE(token_type) \
        CFCParseHeader(CFCParser_current_parser, token_type, \
			CFCParser_dupe(CFCParser_current_state, yytext), \
			CFCParser_current_state)

    struct cfc_StringID {
        const char *string;
        int token_type;
    };
    struct cfc_StringID reserved_word_map[] = {
        {"NULL", CFC_TOKENTYPE_NULL },
        {"abstract", CFC_TOKENTYPE_ABSTRACT },
        {"bool", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"char", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"class", CFC_TOKENTYPE_CLASS },
        {"const", CFC_TOKENTYPE_CONST },
        {"decremented", CFC_TOKENTYPE_DECREMENTED },
        {"double", CFC_TOKENTYPE_FLOAT_TYPE_NAME },
        {"false", CFC_TOKENTYPE_FALSE },
        {"final", CFC_TOKENTYPE_FINAL },
        {"float", CFC_TOKENTYPE_FLOAT_TYPE_NAME },
        {"incremented", CFC_TOKENTYPE_INCREMENTED },
        {"inert", CFC_TOKENTYPE_INERT },
        {"inherits", CFC_TOKENTYPE_INHERITS },
        {"inline", CFC_TOKENTYPE_INLINE },
        {"int", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"int16_t", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"int32_t", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"int64_t", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"int8_t", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"long", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"nickname", CFC_TOKENTYPE_NICKNAME },
        {"nullable", CFC_TOKENTYPE_NULLABLE },
        {"parcel", CFC_TOKENTYPE_PARCEL },
        {"public", CFC_TOKENTYPE_PUBLIC },
        {"short", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"size_t", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"true", CFC_TOKENTYPE_TRUE },
        {"uint16_t", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"uint32_t", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"uint64_t", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"uint8_t", CFC_TOKENTYPE_INTEGER_TYPE_NAME },
        {"va_list", CFC_TOKENTYPE_VA_LIST },
        {"void", CFC_TOKENTYPE_VOID },
    };
    #define NUM_RESERVED_WORDS \
        (sizeof(reserved_word_map) / sizeof(struct cfc_StringID))

    static int
    S_compare(const void *va, const void *vb) {
        const char *a = (const char*)va;
        struct cfc_StringID *b
            = (struct cfc_StringID*)vb;
        return strcmp(a, b->string);
    }

    static int
    S_identifier_or_keyword(const char *word) {
        struct cfc_StringID *got = (struct cfc_StringID*)
            bsearch(word, reserved_word_map, NUM_RESERVED_WORDS,
                    sizeof(struct cfc_StringID), S_compare);
        if (got) {
            return got->token_type;
        }
        else {
            return CFC_TOKENTYPE_IDENTIFIER;
        }
    }
%}

%option noyywrap
%option nodefault
%option noinput
%option nounput
%option yylineno
%option never-interactive

%x CBLOCK

%%
::         { PARSE(CFC_TOKENTYPE_SCOPE_OP); }
[*]        { PARSE(CFC_TOKENTYPE_ASTERISK); }
\{         { PARSE(CFC_TOKENTYPE_LEFT_CURLY_BRACE); }
\}         { PARSE(CFC_TOKENTYPE_RIGHT_CURLY_BRACE); }
[\[]       { PARSE(CFC_TOKENTYPE_LEFT_SQUARE_BRACKET); }
[\]]       { PARSE(CFC_TOKENTYPE_RIGHT_SQUARE_BRACKET); }
[\(]       { PARSE(CFC_TOKENTYPE_LEFT_PAREN); }
[\)]       { PARSE(CFC_TOKENTYPE_RIGHT_PAREN); }
\.\.\.     { PARSE(CFC_TOKENTYPE_ELLIPSIS); }
,          { PARSE(CFC_TOKENTYPE_COMMA); }
;          { PARSE(CFC_TOKENTYPE_SEMICOLON); }
=          { PARSE(CFC_TOKENTYPE_EQUALS); }

-?0x[0-9A-Fa-f]+       { PARSE(CFC_TOKENTYPE_HEX_LITERAL); }
-?[0-9]+\.[0-9]+       { PARSE(CFC_TOKENTYPE_FLOAT_LITERAL); }
-?[0-9]+               { PARSE(CFC_TOKENTYPE_INTEGER_LITERAL); }
\"([^\"\\]|\\.)*\"     { PARSE(CFC_TOKENTYPE_STRING_LITERAL); }

[a-zA-Z_][a-zA-Z0-9_]* { PARSE(S_identifier_or_keyword(yytext)); }

__C__[[:space:]]*    { BEGIN(CBLOCK);  PARSE(CFC_TOKENTYPE_CBLOCK_START); }
<CBLOCK>__END_C__    { BEGIN(INITIAL); PARSE(CFC_TOKENTYPE_CBLOCK_CLOSE); }
<CBLOCK>__END_C_[^_]   { PARSE(CFC_TOKENTYPE_BLOB); }
<CBLOCK>[^_]+          { PARSE(CFC_TOKENTYPE_BLOB); }
<CBLOCK>_+             { PARSE(CFC_TOKENTYPE_BLOB); }
           
    /* Parse docucomments, but skip ordinary comments */
"/**"([^*]|"*"[^/])*"*/" { PARSE(CFC_TOKENTYPE_DOCUCOMMENT); }
"/*"([^*]|"*"[^/])*"*/"

[ \t\r\n]  /* Skip whitespace. */
<*>.       { 
                printf("Bad input character '%s' at line %d\n", yytext, yylineno);
                yyterminate();
           }
<<EOF>>    { yyterminate(); }
%%

