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 }