/ src / main.rs
main.rs
  1  use std::convert::Infallible;
  2  use std::env;
  3  use std::fs;
  4  use std::io::stderr;
  5  use std::io::{self, Write};
  6  use std::process::exit;
  7  
  8  struct Token {
  9  	token_type: String,
 10  	identifier: String
 11  }
 12  
 13  impl TryFrom<&str> for Token {
 14  	fn try_from(identifier: &str) -> Result<Token, ()> {
 15  		let token_type = match identifier {
 16  			"(" => String::from("LEFT_PAREN"),
 17  			")" => String::from("RIGHT_PAREN"),
 18  			"{" => String::from("LEFT_BRACE"),
 19  			"}" => String::from("RIGHT_BRACE"),
 20  			"*" => String::from("STAR"),
 21  			"." => String::from("DOT"),
 22  			"," => String::from("COMMA"),
 23  			"+" => String::from("PLUS"),
 24  			"-" => String::from("MINUS"),
 25  			";" => String::from("SEMICOLON"),
 26  			"=" => String::from("EQUALS"),
 27  			"==" => String::from("EQUALS_EQUALS"),
 28  			_ => return Err(())
 29  		};
 30  		Ok(Token {
 31  			identifier: identifier.to_string(),
 32  			token_type
 33  		})
 34  	}
 35  
 36  	type Error = ();
 37  }
 38  
 39  impl ToString for Token {
 40  	fn to_string(&self) -> String {
 41  		format!("{} {} {}", self.token_type, self.identifier, "null")
 42  	}
 43  }
 44  
 45  fn tokenize(input: &str) {
 46  	let mut current_line: u32 = 1;
 47  	let mut errors = false;
 48  	let mut tokens: Vec<Token> = vec![];
 49  
 50  	let skip_token = false;
 51  
 52  	for (idx, char) in input.char_indices() {
 53  		if skip_token { continue };
 54  
 55  		let token: Result<Token, ()> = if char != '=' {
 56  			char.to_string().as_str().try_into()
 57  		} else {
 58  			if char == '=' {
 59  				let next_char: Result<char, Infallible> = input.as_bytes()[idx+1].try_into();
 60  				if next_char.is_ok() && next_char.unwrap() == '=' {
 61  					"==".try_into()
 62  				} else {
 63  					char.to_string().as_str().try_into()
 64  				}
 65  			} else {
 66  				unreachable!()
 67  			}
 68  		};
 69  		if token.is_ok() {
 70  			let valid_token = token.unwrap();
 71  			println!("{} {} {}", valid_token.token_type, valid_token.identifier, "null");
 72  		} else {
 73  			errors = true;
 74  			stderr().write_all(format!("[line {current_line}] Error: Unexpected character: {char}\n").as_bytes()).unwrap();
 75  		}
 76  		if char == '\n' {
 77  			current_line += 1;
 78  		}
 79  	}
 80  
 81  	println!("EOF  null");
 82  	if errors {
 83  		exit(65)
 84  	}
 85  }
 86  
 87  fn main() {
 88      let args: Vec<String> = env::args().collect();
 89      if args.len() < 3 {
 90          writeln!(io::stderr(), "Usage: {} tokenize <filename>", args[0]).unwrap();
 91          return;
 92      }
 93  
 94      let command = &args[1];
 95      let filename = &args[2];
 96  
 97      match command.as_str() {
 98          "tokenize" => {
 99              // You can use print statements as follows for debugging, they'll be visible when running tests.
100              writeln!(io::stderr(), "Logs from your program will appear here!").unwrap();
101  
102              let file_contents = fs::read_to_string(filename).unwrap_or_else(|_| {
103                  writeln!(io::stderr(), "Failed to read file {}", filename).unwrap();
104                  String::new()
105              });
106  
107  			tokenize(&file_contents);
108          }
109          _ => {
110              writeln!(io::stderr(), "Unknown command: {}", command).unwrap();
111              return;
112          }
113      }
114  }