#include <iostream>

#include <string>

#include <fstream>

#include <streambuf>

#include <vector>

#include <sstream>

#include <algorithm>

#include <string.h>

#include <stdio.h>

#include <map>

#include <unordered_map>

#include <functional>

//std::hash isnt const ¯\_(ツ)_/¯

const int hash ( const char * string )

{

int sum = 0 ;

for ( int i = 0 ; i < strlen ( string ) ; i ++ )

{

sum + = ( int ) string [ i ] ;

}

return sum + ( ( int ) string [ 0 ] - string [ strlen ( string ) - 1 ] ) ;

}

//have to use constexpr in switch statement lol

constexpr int ce_hash ( const char * string )

{

int sum = 0 ;

for ( int i = 0 ; i < strlen ( string ) ; i ++ )

{

sum + = ( int ) string [ i ] ;

}

//add the extra part because "jlt" is the same as "sub" without it

return sum + ( ( int ) string [ 0 ] - string [ strlen ( string ) - 1 ] ) ;

}

enum class EToken : int

{

SET = 0 ,

PRINT = 1 ,

ADD = 2 ,

MUL = 3 ,

SUB = 4 ,

DIV = 5 ,

JMP = 6 ,

LABEL = 7 ,

INPUT = 8 ,

COMPARE = 9 ,

JGT = 10 ,

JLT = 11 ,

JEQ = 12

} ;

enum class EComparisonResult

{

LESS,

GREATER,

EQUAL

} ;

const char * _ETokenStrings [ ] = {

"Set" ,

"Print" ,

"Add" ,

"Mul" ,

"Sub" ,

"Div" ,

"Jmp" ,

"Label" ,

"Input" ,

"Compare"

} ;

struct Token

{

EToken token ;

std :: vector < std :: string > arguments ;

void generate_tokens ( std :: vector < std :: string > line )

{

//TODO: cant switch strings lol

int l_hash = hash ( line [ 0 ] . c_str ( ) ) ;

switch ( l_hash )

{

case ce_hash ( "set" ) :

this - > token = EToken :: SET ;

break ;

case ce_hash ( "print" ) :

this - > token = EToken :: PRINT ;

break ;

case ce_hash ( "add" ) :

this - > token = EToken :: ADD ;

break ;

case ce_hash ( "mul" ) :

this - > token = EToken :: MUL ;

break ;

case ce_hash ( "sub" ) :

this - > token = EToken :: SUB ;

break ;

case ce_hash ( "div" ) :

this - > token = EToken :: DIV ;

break ;

case ce_hash ( "jmp" ) :

this - > token = EToken :: JMP ;

break ;

case ce_hash ( "label" ) :

this - > token = EToken :: LABEL ;

break ;

case ce_hash ( "input" ) :

this - > token = EToken :: INPUT ;

break ;

case ce_hash ( "compare" ) :

this - > token = EToken :: COMPARE ;

break ;

case ce_hash ( "jgt" ) :

this - > token = EToken :: JGT ;

break ;

case ce_hash ( "jlt" ) :

this - > token = EToken :: JLT ;

break ;

case ce_hash ( "jeq" ) :

this - > token = EToken :: JEQ ;

break ;

default :

std :: cout << "unknown instr: " << line [ 0 ] << std :: endl ;

}

for ( auto i = line. begin ( ) + 1 ; i ! = line. end ( ) ; ++ i )

{

arguments. push_back ( * i ) ;

}

}

} ;

std :: string file_to_string ( std :: string file_name )

{

std :: ifstream t ( file_name ) ;

return std :: string ( ( std :: istreambuf_iterator < char > ( t ) ) ,

std :: istreambuf_iterator < char > ( ) ) ;

}

std :: vector < std :: string > string_to_lines ( std :: string string )

{

std :: vector < std :: string > lines ;

std :: istringstream stream ;

stream. str ( string. c_str ( ) ) ;

for ( std :: string line ; std :: getline ( stream, line ) ; )

{

//if it contains nothing, or is a comment, dont add the line

if ( line == "" || line. at ( 0 ) == '-' )

{

continue ;

}

lines. push_back ( line ) ;

}

return lines ;

}

//separates a vector of strings into a vector of words in lines

std :: vector < std :: vector < std :: string >> lines_to_words ( std :: vector < std :: string > lines, std :: vector < char > delimeters )

{

std :: vector < std :: vector < std :: string >> result ;

for ( int i = 0 ; i < lines. size ( ) ; i ++ )

{

std :: vector < std :: string > words ;

std :: string current_word ;

//loop over chars in string

std :: string string = lines. at ( i ) ;

//for each word

for ( char & c : string )

{

if ( std :: find ( delimeters. begin ( ) , delimeters. end ( ) , c ) ! = delimeters. end ( ) || ( c == string. at ( string. size ( ) - 1 ) ) )

{

//c is a delimeter

words. push_back ( current_word ) ;

current_word = std :: string ( "" ) ;

}

else

{

current_word + = c ;

}

}

result. push_back ( words ) ;

}

return result ;

}

struct Program

{

std :: unordered_map < std :: string , int > variables ;

std :: unordered_map < std :: string , int > labels ;

unsigned int pc = 0 ;

std :: vector < Token > tokens ;

EComparisonResult last_result ;

void read_tokens ( std :: vector < std :: vector < std :: string >> strings )

{

for ( auto & line : strings )

{

Token token ;

token. generate_tokens ( line ) ;

tokens. push_back ( token ) ;

}

}

void dump ( )

{

for ( auto it = this - > variables. begin ( ) ; it ! = this - > variables. end ( ) ; it ++ )

{

std :: cout << "Variable: (" << it - > first << ", " << it - > second << ")" << std :: endl ;

}

for ( auto it = this - > labels. begin ( ) ; it ! = this - > labels. end ( ) ; it ++ )

{

std :: cout << "Label: (" << it - > first << ", " << it - > second << ")" << std :: endl ;

}

}

void run ( )

{

while ( true )

{

if ( pc >= this - > tokens. size ( ) )

{

std :: cout << "Done!" << std :: endl ;

break ;

}

Token currentToken = this - > tokens [ this - > pc ++ ] ;

auto is_num = [ ] ( const std :: string & s ) - > bool {

std :: string :: const_iterator it = s. begin ( ) ;

while ( it ! = s. end ( ) && std :: isdigit ( * it ) ) ++ it ;

return ! s. empty ( ) && it == s. end ( ) ;

} ;

switch ( currentToken. token )

{

case EToken :: SET :

if ( is_num ( currentToken. arguments [ 1 ] ) )

{

if ( this - > variables. find ( currentToken. arguments [ 0 ] ) == this - > variables. end ( ) )

{

this - > variables. insert ( { currentToken. arguments [ 0 ] , std :: stoi ( currentToken. arguments [ 1 ] ) } ) ;

}

else

{

this - > variables. at ( currentToken. arguments [ 0 ] ) = std :: stoi ( currentToken. arguments [ 1 ] ) ;

}

}

else

{

if ( this - > variables. find ( currentToken. arguments [ 0 ] ) == this - > variables. end ( ) )

{

this - > variables. insert ( { currentToken. arguments [ 0 ] , this - > variables [ currentToken. arguments [ 1 ] ] } ) ;

}

else

{

this - > variables. at ( currentToken. arguments [ 0 ] ) = this - > variables [ currentToken. arguments [ 1 ] ] ;

}

}

break ;

case EToken :: PRINT :

std :: cout << this - > variables [ currentToken. arguments [ 0 ] ] << std :: endl ;

break ;

case EToken :: ADD :

this - > variables [ currentToken. arguments [ 1 ] ] + = std :: stoi ( currentToken. arguments [ 0 ] ) ;

break ;

case EToken :: MUL :

this - > variables [ currentToken. arguments [ 1 ] ] * = std :: stoi ( currentToken. arguments [ 0 ] ) ;

break ;

case EToken :: SUB :

this - > variables [ currentToken. arguments [ 1 ] ] - = std :: stoi ( currentToken. arguments [ 0 ] ) ;

break ;

case EToken :: DIV :

this - > variables [ currentToken. arguments [ 1 ] ] / = std :: stoi ( currentToken. arguments [ 0 ] ) ;

break ;

case EToken :: JMP :

this - > pc = this - > labels [ currentToken. arguments [ 0 ] ] ;

break ;

case EToken :: LABEL :

this - > labels. insert ( { currentToken. arguments [ 0 ] , this - > pc } ) ;

break ;

case EToken :: INPUT :

if ( this - > variables. find ( "$input" ) == this - > variables. end ( ) )

{

int v ;

std :: cin >> v ;

this - > variables. insert ( { "$input" , v } ) ;

}

else

{

int v ;

std :: cin >> v ;

this - > variables. at ( "$input" ) = v ;

}

break ;

case EToken :: COMPARE : {

int value = this - > variables. at ( currentToken. arguments [ 0 ] ) ;

int other ;

if ( is_num ( currentToken. arguments [ 1 ] ) )

{

other = std :: stoi ( currentToken. arguments [ 1 ] ) ;

}

else

{

other = variables. at ( currentToken. arguments [ 1 ] ) ;

}

if ( value > other )

{

last_result = EComparisonResult :: GREATER ;

}

else if ( value < other )

{

last_result = EComparisonResult :: LESS ;

}

else

{

last_result = EComparisonResult :: EQUAL ;

}

break ;

}

case EToken :: JGT :

if ( last_result == EComparisonResult :: GREATER )

{

this - > pc = this - > labels [ currentToken. arguments [ 0 ] ] ;

}

break ;

case EToken :: JLT :

if ( last_result == EComparisonResult :: LESS )

{

this - > pc = this - > labels [ currentToken. arguments [ 0 ] ] ;

}

break ;

case EToken :: JEQ :

if ( last_result == EComparisonResult :: EQUAL )

{

this - > pc = this - > labels [ currentToken. arguments [ 0 ] ] ;

}

break ;

default :

std :: cout << "unrecognized tokentype" << std :: endl ;

}

}

}

} ;

int main ( int argc, char ** argv )

{

if ( argc < 2 )

{

std :: cout << "Requires at least one argument!" << std :: endl ;

return - 1 ;

}

std :: string file_contents = file_to_string ( argv [ 1 ] ) ;

std :: vector < std :: string > lines = string_to_lines ( file_contents ) ;

std :: vector < char > delimeters = { ' ' , ',' , ';' } ;

std :: vector < std :: vector < std :: string > > words = lines_to_words ( lines, delimeters ) ;

Program program ;

program. read_tokens ( words ) ;

program. run ( ) ;

return 0 ;