%option noyywrap noinput nounput
%option 8bit reentrant bison-bridge
%option warn nodefault
%option bison-locations

%{
/*
 *  Copyright(c) 2019-2023 rev.ng Labs Srl. All Rights Reserved.
 *
 *  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, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>
#include <stdbool.h>

#include "hex_regs.h"

#include "idef-parser.h"
#include "idef-parser.tab.h"

/* Keep track of scanner position for error message printout */
#define YY_USER_ACTION yylloc->first_column = yylloc->last_column; \
    for (int i = 0; yytext[i] != '\0'; i++) {   \
        yylloc->last_column++;                  \
    }

/* Global Error Counter */
int error_count;

%}

/* Definitions */
DIGIT                    [0-9]
LOWER_ID                 [a-z]
UPPER_ID                 [A-Z]
ID                       LOWER_ID|UPPER_ID
INST_NAME                [A-Z]+[0-9]_([A-Za-z]|[0-9]|_)+
HEX_DIGIT                [0-9a-fA-F]
REG_ID_32                e|s|d|t|u|v|x|y
REG_ID_64                ee|ss|dd|tt|uu|vv|xx|yy
SYS_ID_32                s|d
SYS_ID_64                ss|dd
PRED_ID                  d|s|t|u|v|e|x|x
IMM_ID                   r|s|S|u|U
VAR_ID                   [a-zA-Z_][a-zA-Z0-9_]*
SIGN_ID                  s|u
STRING_LIT               \"(\\.|[^"\\])*\"

/* Tokens */
%%

[ \t\f\v]+                { /* Ignore whitespaces. */ }
[\n\r]+                   { /* Ignore newlines. */ }
^#.*$                     { /* Ignore linemarkers. */ }

{INST_NAME}               { yylval->string = g_string_new(yytext);
                            return INAME; }
"fFLOAT"                 |
"fUNFLOAT"               |
"fDOUBLE"                |
"fUNDOUBLE"              |
"0.0"                    |
"0x1.0p52"               |
"0x1.0p-52"              { return FAIL; }
"in"                     { return IN; }
"R"{REG_ID_32}"V" {
                           yylval->rvalue.type = REGISTER_ARG;
                           yylval->rvalue.reg.type = GENERAL_PURPOSE;
                           yylval->rvalue.reg.id = yytext[1];
                           yylval->rvalue.reg.bit_width = 32;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.is_dotnew = false;
                           yylval->rvalue.signedness = SIGNED;
                           return REG; }
"R"{REG_ID_64}"V" {
                           yylval->rvalue.type = REGISTER_ARG;
                           yylval->rvalue.reg.type = GENERAL_PURPOSE;
                           yylval->rvalue.reg.id = yytext[1];
                           yylval->rvalue.reg.bit_width = 64;
                           yylval->rvalue.bit_width = 64;
                           yylval->rvalue.is_dotnew = false;
                           yylval->rvalue.signedness = SIGNED;
                           return REG; }
"MuV" {
                           yylval->rvalue.type = REGISTER_ARG;
                           yylval->rvalue.reg.type = MODIFIER;
                           yylval->rvalue.reg.id = 'u';
                           yylval->rvalue.reg.bit_width = 32;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.signedness = SIGNED;
                           return REG; }
"C"{REG_ID_32}"V" {
                           yylval->rvalue.type = REGISTER_ARG;
                           yylval->rvalue.reg.type = CONTROL;
                           yylval->rvalue.reg.id = yytext[1];
                           yylval->rvalue.reg.bit_width = 32;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.is_dotnew = false;
                           yylval->rvalue.signedness = SIGNED;
                           return REG; }
"C"{REG_ID_64}"V" {
                           yylval->rvalue.type = REGISTER_ARG;
                           yylval->rvalue.reg.type = CONTROL;
                           yylval->rvalue.reg.id = yytext[1];
                           yylval->rvalue.reg.bit_width = 64;
                           yylval->rvalue.bit_width = 64;
                           yylval->rvalue.is_dotnew = false;
                           yylval->rvalue.signedness = SIGNED;
                           return REG; }
{IMM_ID}"iV" {
                           yylval->rvalue.type = IMMEDIATE;
                           yylval->rvalue.signedness = SIGNED;
                           yylval->rvalue.imm.type = VARIABLE;
                           yylval->rvalue.imm.id = yytext[0];
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.is_dotnew = false;
                           return IMM; }
"P"{PRED_ID}"V" {
                           yylval->rvalue.type = PREDICATE;
                           yylval->rvalue.pred.id = yytext[1];
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.is_dotnew = false;
                           yylval->rvalue.signedness = SIGNED;
                           return PRED; }
"P"{PRED_ID}"N" {
                           yylval->rvalue.type = PREDICATE;
                           yylval->rvalue.pred.id = yytext[1];
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.is_dotnew = true;
                           yylval->rvalue.signedness = SIGNED;
                           return PRED; }
"+="                     { return INC; }
"-="                     { return DEC; }
"++"                     { return PLUSPLUS; }
"&="                     { return ANDA; }
"|="                     { return ORA; }
"^="                     { return XORA; }
"<<"                     { return ASL; }
">>"                     { return ASR; }
">>>"                    { return LSR; }
"=="                     { return EQ; }
"!="                     { return NEQ; }
"<="                     { return LTE; }
">="                     { return GTE; }
"&&"                     { return ANDL; }
"else"                   { return ELSE; }
"for"                    { return FOR; }
"fREAD_IREG"             { return ICIRC; }
"if"                     { return IF; }
"fFRAME_SCRAMBLE"        |
"fFRAME_UNSCRAMBLE"      { return FSCR; }
"fFRAMECHECK"            { return FCHK; }
"Constant_extended"      { return CONSTEXT; }
"fCL1_"{DIGIT}           { return LOCNT; }
"fbrev"                  { return BREV; }
"fSXTN"                  { return SXT; }
"fZXTN"                  { return ZXT; }
"fDF_MAX"                |
"fSF_MAX"                |
"fMAX"                   { return MAX; }
"fDF_MIN"                |
"fSF_MIN"                |
"fMIN"                   { return MIN; }
"fABS"                   { return ABS; }
"fRNDN"                  { return ROUND; }
"fCRND"                  { return CROUND; }
"fCRNDN"                 { return CROUND; }
"fPM_CIRI"               { return CIRCADD; }
"fPM_CIRR"               { return CIRCADD; }
"fCOUNTONES_"{DIGIT}     { return COUNTONES; }
"fSATN"                  { yylval->sat.signedness = SIGNED;
                           return SAT; }
"fSATUN"                 { yylval->sat.signedness = UNSIGNED;
                           return SAT; }
"fCONSTLL"               { yylval->cast.bit_width = 64;
                           yylval->cast.signedness = SIGNED;
                           return CAST; }
"fSE32_64"               { yylval->cast.bit_width = 64;
                           yylval->cast.signedness = SIGNED;
                           return CAST; }
"fCAST4_4u"              { yylval->cast.bit_width = 32;
                           yylval->cast.signedness = UNSIGNED;
                           return CAST; }
"fCAST4_8s"              { yylval->cast.bit_width = 64;
                           yylval->cast.signedness = SIGNED;
                           return CAST; }
"fCAST4_8u"              { return CAST4_8U; }
"fCAST4u"                { yylval->cast.bit_width = 32;
                           yylval->cast.signedness = UNSIGNED;
                           return CAST; }
"fNEWREG"                |
"fCAST4_4s"              |
"fCAST4s"                { yylval->cast.bit_width = 32;
                           yylval->cast.signedness = SIGNED;
                           return CAST; }
"fCAST8_8u"              { yylval->cast.bit_width = 64;
                           yylval->cast.signedness = UNSIGNED;
                           return CAST; }
"fCAST8u"                { yylval->cast.bit_width = 64;
                           yylval->cast.signedness = UNSIGNED;
                           return CAST; }
"fCAST8_8s"              |
"fCAST8s"                { yylval->cast.bit_width = 64;
                           yylval->cast.signedness = SIGNED;
                           return CAST; }
"fGETBIT"                { yylval->extract.bit_width = 1;
                           yylval->extract.storage_bit_width = 1;
                           yylval->extract.signedness = UNSIGNED;
                           return EXTRACT; }
"fGETBYTE"               { yylval->extract.bit_width = 8;
                           yylval->extract.storage_bit_width = 8;
                           yylval->extract.signedness = SIGNED;
                           return EXTRACT; }
"fGETUBYTE"              { yylval->extract.bit_width = 8;
                           yylval->extract.storage_bit_width = 8;
                           yylval->extract.signedness = UNSIGNED;
                           return EXTRACT; }
"fGETHALF"               { yylval->extract.bit_width = 16;
                           yylval->extract.storage_bit_width = 16;
                           yylval->extract.signedness = SIGNED;
                           return EXTRACT; }
"fGETUHALF"              { yylval->extract.bit_width = 16;
                           yylval->extract.storage_bit_width = 16;
                           yylval->extract.signedness = UNSIGNED;
                           return EXTRACT; }
"fGETWORD"               { yylval->extract.bit_width = 32;
                           yylval->extract.storage_bit_width = 64;
                           yylval->extract.signedness = SIGNED;
                           return EXTRACT; }
"fGETUWORD"              { yylval->extract.bit_width = 32;
                           yylval->extract.storage_bit_width = 64;
                           yylval->extract.signedness = UNSIGNED;
                           return EXTRACT; }
"fEXTRACTU_RANGE"        { return EXTRANGE; }
"fSETBIT"                { yylval->cast.bit_width = 1;
                           yylval->cast.signedness = SIGNED;
                           return DEPOSIT; }
"fSETBYTE"               { yylval->cast.bit_width = 8;
                           yylval->cast.signedness = SIGNED;
                           return DEPOSIT; }
"fSETHALF"               { yylval->cast.bit_width = 16;
                           yylval->cast.signedness = SIGNED;
                           return SETHALF; }
"fSETWORD"               { yylval->cast.bit_width = 32;
                           yylval->cast.signedness = SIGNED;
                           return DEPOSIT; }
"fINSERT_BITS"           { return INSBITS; }
"fSETBITS"               { return SETBITS; }
"fMPY16UU"               { yylval->mpy.first_bit_width = 16;
                           yylval->mpy.second_bit_width = 16;
                           yylval->mpy.first_signedness = UNSIGNED;
                           yylval->mpy.second_signedness = UNSIGNED;
                           return MPY; }
"fMPY16SU"               { yylval->mpy.first_bit_width = 16;
                           yylval->mpy.second_bit_width = 16;
                           yylval->mpy.first_signedness = SIGNED;
                           yylval->mpy.second_signedness = UNSIGNED;
                           return MPY; }
"fMPY16SS"               { yylval->mpy.first_bit_width = 16;
                           yylval->mpy.second_bit_width = 16;
                           yylval->mpy.first_signedness = SIGNED;
                           yylval->mpy.second_signedness = SIGNED;
                           return MPY; }
"fMPY32UU"               { yylval->mpy.first_bit_width = 32;
                           yylval->mpy.second_bit_width = 32;
                           yylval->mpy.first_signedness = UNSIGNED;
                           yylval->mpy.second_signedness = UNSIGNED;
                           return MPY; }
"fMPY32SU"               { yylval->mpy.first_bit_width = 32;
                           yylval->mpy.second_bit_width = 32;
                           yylval->mpy.first_signedness = SIGNED;
                           yylval->mpy.second_signedness = UNSIGNED;
                           return MPY; }
"fSFMPY"                 |
"fMPY32SS"               { yylval->mpy.first_bit_width = 32;
                           yylval->mpy.second_bit_width = 32;
                           yylval->mpy.first_signedness = SIGNED;
                           yylval->mpy.second_signedness = SIGNED;
                           return MPY; }
"fMPY3216SS"             { yylval->mpy.first_bit_width = 32;
                           yylval->mpy.second_bit_width = 16;
                           yylval->mpy.first_signedness = SIGNED;
                           yylval->mpy.second_signedness = SIGNED;
                           return MPY; }
"fMPY3216SU"             { yylval->mpy.first_bit_width = 32;
                           yylval->mpy.second_bit_width = 16;
                           yylval->mpy.first_signedness = SIGNED;
                           yylval->mpy.second_signedness = UNSIGNED;
                           return MPY; }
"fNEWREG_ST"             |
"fIMMEXT"                |
"fMUST_IMMEXT"           |
"fPASS"                  |
"fECHO"                  { return IDENTITY; }
"(size8u_t)"             { yylval->cast.bit_width = 64;
                           yylval->cast.signedness = UNSIGNED;
                           return CAST; }
"(unsigned int)"         { yylval->cast.bit_width = 32;
                           yylval->cast.signedness = UNSIGNED;
                           return CAST; }
"fREAD_PC()"             { return PC; }
"USR.LPCFG"              { return LPCFG; }
"LOAD_CANCEL(EA)"        { return LOAD_CANCEL; }
"STORE_CANCEL(EA)"       { return STORE_CANCEL; }
"CANCEL"                 { return CANCEL; }
"N"{LOWER_ID}"N"         { yylval->rvalue.type = REGISTER_ARG;
                           yylval->rvalue.reg.type = DOTNEW;
                           yylval->rvalue.reg.id = yytext[1];
                           yylval->rvalue.reg.bit_width = 32;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.signedness = UNSIGNED;
                           return REG; }
"fREAD_SP()"             |
"SP"                     { yylval->rvalue.type = REGISTER;
                           yylval->rvalue.reg.type = GENERAL_PURPOSE;
                           yylval->rvalue.reg.id = HEX_REG_SP;
                           yylval->rvalue.reg.bit_width = 32;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.signedness = UNSIGNED;
                           return REG; }
"fREAD_FP()"             |
"FP"                     { yylval->rvalue.type = REGISTER;
                           yylval->rvalue.reg.type = GENERAL_PURPOSE;
                           yylval->rvalue.reg.id = HEX_REG_FP;
                           yylval->rvalue.reg.bit_width = 32;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.signedness = UNSIGNED;
                           return REG; }
"fREAD_LR()"             |
"LR"                     { yylval->rvalue.type = REGISTER;
                           yylval->rvalue.reg.type = GENERAL_PURPOSE;
                           yylval->rvalue.reg.id = HEX_REG_LR;
                           yylval->rvalue.reg.bit_width = 32;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.signedness = UNSIGNED;
                           return REG; }
"fREAD_GP()"             |
"GP"                     { yylval->rvalue.type = REGISTER;
                           yylval->rvalue.reg.type = CONTROL;
                           yylval->rvalue.reg.id = HEX_REG_GP;
                           yylval->rvalue.reg.bit_width = 32;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.signedness = UNSIGNED;
                           return REG; }
"LC"[01]                 { yylval->rvalue.type = REGISTER;
                           yylval->rvalue.reg.type = CONTROL;
                           yylval->rvalue.reg.id = HEX_REG_LC0
                                                 + (yytext[2] - '0') * 2;
                           yylval->rvalue.reg.bit_width = 32;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.signedness = UNSIGNED;
                           return REG; }
"SA"[01]                 { yylval->rvalue.type = REGISTER;
                           yylval->rvalue.reg.type = CONTROL;
                           yylval->rvalue.reg.id = HEX_REG_SA0
                                                 + (yytext[2] - '0') * 2;
                           yylval->rvalue.reg.bit_width = 32;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.signedness = UNSIGNED;
                           return REG; }
"fREAD_P0()"             { yylval->rvalue.type = PREDICATE;
                           yylval->rvalue.pred.id = '0';
                           yylval->rvalue.bit_width = 32;
                           return PRED; }
[pP]{DIGIT}              { yylval->rvalue.type = PREDICATE;
                           yylval->rvalue.pred.id = yytext[1];
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.is_dotnew = false;
                           return PRED; }
[pP]{DIGIT}[nN]          { yylval->rvalue.type = PREDICATE;
                           yylval->rvalue.pred.id = yytext[1];
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.is_dotnew = true;
                           return PRED; }
"fLSBNEW"                { return LSBNEW; }
"N"                      { yylval->rvalue.type = IMMEDIATE;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.imm.type = VARIABLE;
                           yylval->rvalue.imm.id = 'N';
                           return IMM; }
"i"                      { yylval->rvalue.type = IMMEDIATE;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.signedness = SIGNED;
                           yylval->rvalue.imm.type = I;
                           return IMM; }
{SIGN_ID}                { if (yytext[0] == 'u') {
                               yylval->signedness = UNSIGNED;
                           } else {
                               yylval->signedness = SIGNED;
                           }
                           return SIGN;
                         }
"0x"{HEX_DIGIT}+         |
{DIGIT}+                 { yylval->rvalue.type = IMMEDIATE;
                           yylval->rvalue.bit_width = 32;
                           yylval->rvalue.signedness = SIGNED;
                           yylval->rvalue.imm.type = VALUE;
                           yylval->rvalue.imm.value = strtoull(yytext, NULL, 0);
                           return IMM; }
"0x"{HEX_DIGIT}+"ULL"    |
{DIGIT}+"ULL"            { yylval->rvalue.type = IMMEDIATE;
                           yylval->rvalue.bit_width = 64;
                           yylval->rvalue.signedness = UNSIGNED;
                           yylval->rvalue.imm.type = VALUE;
                           yylval->rvalue.imm.value = strtoull(yytext, NULL, 0);
                           return IMM; }
"fLOAD"                  { return LOAD; }
"fSTORE"                 { return STORE; }
"fROTL"                  { return ROTL; }
"fCARRY_FROM_ADD"        { return CARRY_FROM_ADD; }
"fADDSAT64"              { return ADDSAT64; }
"size"[1248][us]"_t"     { /* Handles "size_t" variants of int types */
                           const unsigned int bits_per_byte = 8;
                           const unsigned int bytes = yytext[4] - '0';
                           yylval->rvalue.bit_width = bits_per_byte * bytes;
                           if (yytext[5] == 'u') {
                               yylval->rvalue.signedness = UNSIGNED;
                           } else {
                               yylval->rvalue.signedness = SIGNED;
                           }
                           return TYPE_SIZE_T; }
"unsigned"               { return TYPE_UNSIGNED; }
"long"                   { return TYPE_LONG; }
"int"                    { return TYPE_INT; }
"const"                  { /* Emit no token */ }
{VAR_ID}                 { /* Variable name, we adopt the C names convention */
                           yylval->rvalue.type = VARID;
                           yylval->rvalue.var.name = g_string_new(yytext);
                           /* Default to an unknown signedness and 0 width. */
                           yylval->rvalue.bit_width = 0;
                           yylval->rvalue.signedness = UNKNOWN_SIGNEDNESS;
                           return VAR; }
"fatal("{STRING_LIT}")"  { /* Emit no token */ }
"fHINTJR(RsV)"           { /* Emit no token */ }
.                        { return yytext[0]; }

%%