/* Lexical analyzer.
   Copyright (C) 2025 Free Software Foundation, Inc.

GCC 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 3, or (at your option)
any later version.

GCC 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 GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */

%{
#define YY_NO_UNISTD_H
#define YY_USER_ACTION this->location.columns(yyleng);

#include <sstream>
#include "parser.hh"

#undef YY_DECL
#define YY_DECL yy::parser::symbol_type elna::boot::lexer::lex(driver& driver)
#define yyterminate() return yy::parser::make_YYEOF(this->location)
%}

%option c++ noyywrap never-interactive
%option yyclass="lexer"

%x IN_COMMENT

%%
%{
    this->location.step();
%}

<IN_COMMENT>{
\*\)                    BEGIN(INITIAL);
[^*\n]+                 ; /* Eat comment in chunks. */
\*                      ; /* Eat the lone star. */
\n+                     {
                            this->location.lines(yyleng);
                            this->location.step();
                        }
}
\(\*                    BEGIN(IN_COMMENT);
[ \t\r]                 {
                            this->location.step();
                        }
\n+                     {
                            this->location.lines(yyleng);
                        }
if                      {
                            return yy::parser::make_IF(this->location);
                        }
then                    {
                            return yy::parser::make_THEN(this->location);
                        }
else                    {
                            return yy::parser::make_ELSE(this->location);
                        }
elsif                   {
                            return yy::parser::make_ELSIF(this->location);
                        }
while                   {
                            return yy::parser::make_WHILE(this->location);
                        }
do                      {
                            return yy::parser::make_DO(this->location);
                        }
proc                    {
                            return yy::parser::make_PROCEDURE(this->location);
                        }
begin                   {
                            return yy::parser::make_BEGIN_BLOCK(this->location);
                        }
end                     {
                            return yy::parser::make_END_BLOCK(this->location);
                        }
extern                  {
                            return yy::parser::make_EXTERN(this->location);
                        }
const                   {
                            return yy::parser::make_CONST(this->location);
                        }
var                     {
                            return yy::parser::make_VAR(this->location);
                        }
type                    {
                            return yy::parser::make_TYPE(this->location);
                        }
record                  {
                            return yy::parser::make_RECORD(this->location);
                        }
union                   {
                            return yy::parser::make_UNION(this->location);
                        }
true                    {
                            return yy::parser::make_BOOLEAN(true, this->location);
                        }
false                   {
                            return yy::parser::make_BOOLEAN(false, this->location);
                        }
nil                     {
                            return yy::parser::make_NIL(this->location);
                        }
\&                      {
                            return yy::parser::make_AND(this->location);
                        }
xor                     {
                            return yy::parser::make_XOR(this->location);
                        }
\|                      {
                            return yy::parser::make_OR(this->location);
                        }
\~                      {
                            return yy::parser::make_NOT(this->location);
                        }
return                  {
                            return yy::parser::make_RETURN(this->location);
                        }
cast                    {
                            return yy::parser::make_CAST(this->location);
                        }
defer                   {
                            return yy::parser::make_DEFER(this->location);
                        }
[A-Za-z_][A-Za-z0-9_]*  {
                            return yy::parser::make_IDENTIFIER(yytext, this->location);
                        }
#[A-Za-z_][A-Za-z0-9_]* {
                            return yy::parser::make_TRAIT(yytext + 1, this->location);
                        }
[0-9]+u                 {
                            return yy::parser::make_WORD(strtoul(yytext, NULL, 10), this->location);
                        }
[0-9]+                  {
                            return yy::parser::make_INTEGER(strtol(yytext, NULL, 10), this->location);
                        }
[0-9]+\.[0-9]           {
                            return yy::parser::make_FLOAT(strtof(yytext, NULL), this->location);
                        }
'[[:print:]]'           {
                            if (yytext[1] == '\\' || yytext[1] == '\'')
                            {
                                REJECT;
                            }
                            else
                            {
                                return yy::parser::make_CHARACTER(std::string(yytext, 1, 1), this->location);
                            }
                        }
'\\x[0-9a-fA-F]{1,2}'   {
                            char character = static_cast<char>(std::stoi(yytext + 3, nullptr, 16));

                            return yy::parser::make_CHARACTER(std::string(&character, 1), this->location);
                        }
'\\[0nabtfrv\\'"?]'     {
                            char escape = escape_char(yytext[2]);
                            if (escape == escape_invalid_char)
                            {
                                REJECT;
                            }
                            return yy::parser::make_CHARACTER(std::string(&escape, 1), this->location);
                        }
\"[[:print:]]*\"        {
                            std::optional<std::string> result = escape_string(yytext);
                            if (!result.has_value())
                            {
                                REJECT;
                            }
                            return yy::parser::make_STRING(result.value(), this->location);
                        }
\(                      {
                            return yy::parser::make_LEFT_PAREN(this->location);
                        }
\)                      {
                            return yy::parser::make_RIGHT_PAREN(this->location);
                        }
\[                      {
                            return yy::parser::make_LEFT_SQUARE(this->location);
                        }
\]                      {
                            return yy::parser::make_RIGHT_SQUARE(this->location);
                        }
\<\<                    {
                            return yy::parser::make_SHIFT_LEFT(this->location);
                        }
\>\>                    {
                            return yy::parser::make_SHIFT_RIGHT(this->location);
                        }
\>=                     {
                            return yy::parser::make_GREATER_EQUAL(this->location);
                        }
\<=                     {
                            return yy::parser::make_LESS_EQUAL(this->location);
                        }
\>                      {
                            return yy::parser::make_GREATER_THAN(this->location);
                        }
\<                      {
                            return yy::parser::make_LESS_THAN(this->location);
                        }
\<\>                    {
                            return yy::parser::make_NOT_EQUAL(this->location);
                        }
=                       {
                            return yy::parser::make_EQUALS(this->location);
                        }
;                       {
                            return yy::parser::make_SEMICOLON(this->location);
                        }
\.                      {
                            return yy::parser::make_DOT(this->location);
                        }
,                       {
                            return yy::parser::make_COMMA(this->location);
                        }
\+                      {
                            return yy::parser::make_PLUS(this->location);
                        }
\->                     {
                            return yy::parser::make_ARROW(this->location);
                        }
\-                      {
                            return yy::parser::make_MINUS(this->location);
                        }
\*                      {
                            return yy::parser::make_MULTIPLICATION(this->location);
                        }
\/                      {
                            return yy::parser::make_DIVISION(this->location);
                        }
%                       {
                            return yy::parser::make_REMAINDER(this->location);
                        }
:=                      {
                            return yy::parser::make_ASSIGNMENT(this->location);
                        }
:                       {
                            return yy::parser::make_COLON(this->location);
                        }
\^                      {
                            return yy::parser::make_HAT(this->location);
                        }
@                       {
                            return yy::parser::make_AT(this->location);
                        }
!                       {
                            return yy::parser::make_EXCLAMATION(this->location);
                        }
.                       {
                            std::stringstream ss;

                            ss << "Illegal character 0x" << std::hex << static_cast<unsigned int>(yytext[0]);
                            driver.add_error<syntax_error>(ss.str(), driver.input_file, this->location);
                        }
%%