%{

#include <string>
#include <rlib/stdio.hpp>
#include <cassert>
#include <cstdlib>

extern int yylex();
extern int yyparse();

typedef struct yy_buffer_state * YY_BUFFER_STATE;
extern YY_BUFFER_STATE yy_scan_string(const char * str);
void yyerror(int64_t *result, std::string s);

uint32_t ryy_read_cpu_reg_value(int64_t which_reg);
uint32_t ryy_read_memory_value(uint32_t addr);
%}

%union {
    int64_t ival;
}

%token<ival> T_INT T_REG // I represent REG by: (int)(REG_NAME[1] << 8 + REG_NAME[2])
%left T_LOGICAL_AND
%left T_EQUAL T_NEQUAL
%left '+' '-'
%left '*' '/'

%type<ival> expr

%start entry

%parse-param {int64_t *result}

%%
entry:                          {*result = 0;}
      | expr                    {*result = $1;}
;

expr: T_INT                     { $$ = $1; }
      | expr '+' expr           { $$ = $1 + $3; }
      | expr '-' expr           { $$ = $1 - $3; }
      | expr '*' expr           { $$ = $1 * $3; }
      | expr '/' expr           { $$ = $1 / $3; }
      | expr T_EQUAL expr       { $$ = ($1 == $3); }
      | expr T_NEQUAL expr      { $$ = ($1 != $3); }
      | expr T_LOGICAL_AND expr { $$ = ($1 && $3); }
      | '(' expr ')'            { $$ = $2; }
      | '-' expr                { $$ = 0 - $2; }
      | '*' expr                { $$ = ryy_read_memory_value((uint32_t)$2); }
      | T_REG                   { $$ = ryy_read_cpu_reg_value($1); }
;

%%

#include "monitor/expr_impl/parser_interface.h"

int64_t parse_one(std::string expr) {
    yy_scan_string(expr.c_str());
    int64_t result;
    assert(0 == yyparse(&result));
    return result;
}

int ryy_test_main() {
    while(!std::cin.eof()) {
        rlib::println(parse_one(rlib::scanln()));
    }
    return 0;
}

void yyerror(int64_t *result, std::string msg) {
    rlib::println("Parse error:", msg);
    exit(1);
}

// v-cpu related code:

#include "cpu/reg.h"
#include "memory/memory.h"
#include <rlib/macro.hpp>

uint32_t ryy_read_cpu_reg_value(int64_t which_reg) {
#define CODE_OF(reg) (((uint32_t)RLIB_MACRO_TO_CSTR(reg)[1] << 8) + RLIB_MACRO_TO_CSTR(reg)[2])
    switch(which_reg) {
        case CODE_OF(eax):
            return cpu.eax;
        case CODE_OF(ebx):
            return cpu.ebx;
        case CODE_OF(ecx):
            return cpu.ecx;
        case CODE_OF(edx):
            return cpu.edx;
        case CODE_OF(esp):
            return cpu.esp;
        case CODE_OF(ebp):
            return cpu.ebp;
        case CODE_OF(esi):
            return cpu.esi;
        case CODE_OF(edi):
            return cpu.edi;
        case CODE_OF(eip):
            return cpu.eip;
        default:
            throw std::invalid_argument("Invalid register name.");
    }
#undef CODE_OF
}

uint32_t ryy_read_memory_value(uint32_t addr) {
    return vaddr_read(addr, 4);
}
