use regex ;

use std :: error ;

use std :: fmt ;

use std :: str :: FromStr ;

pub enum Token {

Add ,

Mult ,

Store ,

Fetch ,

Equ ,

Push ,

Pop ,

Goto ,

Load ,

Out ,

If ,

Debug ,

Comma ,

Register ( u8 ) ,

Literal ( u8 ) ,

End

}

# [ derive ( Debug ) ]

struct LexingError {

cause : String

}

impl fmt :: Display for LexingError {

fn fmt ( & self , f : & mut fmt :: Formatter ) -> fmt :: Result {

f.write_str ( "attempted to access illegal register index" )

}

}

impl error :: Error for LexingError {

fn description ( & self ) -> & str {

"attempted to access illegal register index"

}

}

impl LexingError {

fn new ( cause : String ) -> LexingError {

LexingError { cause }

}

}

//There is definitely a better way to handle errors

pub fn lex_string ( mut input : String ) -> Result < Vec < Token >, Box < error :: Error >> {

let mut result = Vec ::< Token >:: new ( ) ;

let pattern = regex :: Regex :: new ( r"([a-z]+)|(r)([0-4])|(\d+)" ) ? ;

for c in pattern.captures_iter ( input.to_lowercase ( ) .as_str ( ) ) {

match & c [ 1 ] {

"r" => {

match & c [ 2 ] {

"0" => result.push ( Token :: Register ( 0 ) ) ,

"1" => result.push ( Token :: Register ( 1 ) ) ,

"2" => result.push ( Token :: Register ( 2 ) ) ,

"3" => result.push ( Token :: Register ( 3 ) ) ,

"4" => result.push ( Token :: Register ( 4 ) ) ,

other => {

//Is there a way to handle this error more cleanly?

return Err ( Box :: new ( LexingError :: new ( String :: from_str ( other ) ? ) ) )

}

}

if let Some ( _ ) = c.get ( 3 ) {

result.push ( Token :: Comma ) ;

}

}

"add" => result.push ( Token :: Add ) ,

"mult" => result.push ( Token :: Mult ) ,

"store" => result.push ( Token :: Store ) ,

"fetch" => result.push ( Token :: Fetch ) ,

"equ" => result.push ( Token :: Equ ) ,

"push" => result.push ( Token :: Push ) ,

"pop" => result.push ( Token :: Pop ) ,

"goto" => result.push ( Token :: Goto ) ,

"load" => result.push ( Token :: Load ) ,

"out" => result.push ( Token :: Out ) ,

"if" => result.push ( Token :: If ) ,

"debug" => result.push ( Token :: Debug ) ,

"end" => result.push ( Token :: End ) ,

s => {

let value = s.parse ::< u8 > ( ) ? ;

result.push ( Token :: Literal ( value ) ) ;

}

} ;

}

Ok ( result )