use crate::Word; #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) enum Token { Label(String), Operator(String), Symbol(String), SymbolDef(String), Number(Word), Address(Word), String(String), } pub fn split(mut line: String) -> Vec { line.push('\n'); let mut res = Vec::new(); let mut buffer = String::new(); let mut has_label = false; let mut string_started = false; let mut skip_next = false; let mut address = false; for c in line.chars() { buffer.push(c); if skip_next || (string_started && c != '"') { skip_next = false; continue; } match c { ':' => if ! has_label { buffer.pop(); res.push(Token::Label(buffer.clone())); buffer.clear(); has_label = true; } else { panic!("Found double labels!"); }, ' ' | '\t' => { buffer.pop(); if buffer.is_empty() { continue; } if (has_label && (res.len() == 1)) || (!has_label && (res.is_empty())) { res.push(Token::Operator(buffer.clone())); buffer.clear(); } }, '"' => { buffer.pop(); if string_started { res.push(Token::String(buffer.clone())); buffer.clear(); } string_started = !string_started; }, ',' | '\n' | ';' => { buffer.pop(); if buffer.is_empty() { if c == ';' { break; } else { continue; } } if let Some(num) = buffer.parse::().ok() { if address { address = false; res.push(Token::Address(num)); } else { res.push(Token::Number(num)); } } else if res.len() == has_label as usize { res.push(Token::Operator(buffer.clone())); } else { res.push(Token::Symbol(buffer.clone())); } buffer.clear(); if c == ';' { break; } }, '=' => if buffer.is_empty() { panic!("Cannot begin the line with an =") } else if let Token::Operator(name) = &res[0] { res[0] = Token::SymbolDef(name.clone()); buffer.clear(); } else { panic!("Before an = you may only have a symbol name!"); }, '\\' => skip_next = true, '%' => { buffer.pop(); address = true; }, '-' => if address { panic!("Addresses cannot be negative!"); }, 'A'..='Z' | 'a'..='z' | '0'..='9' | '_' | '.' | '$' | '?' => {}, _ => panic!("Found invalid character '{}'", c), } } res } #[cfg(test)] mod tests { use super::*; #[test] fn splits_label() { let res = split("label1:".to_string()); assert_eq!(res.len(), 1); assert!(matches!(res[0], Token::Label(_))); if let Token::Label(name) = &res[0] { assert_eq!(name, "label1"); } } #[test] fn splits_lowercase_instruction_no_args() { let res = split("instr".to_string()); assert_eq!(res.len(), 1); assert!(matches!(res[0], Token::Operator(_))); if let Token::Operator(name) = &res[0] { assert_eq!(name, "instr"); } } #[test] fn splits_pascal_instruction_no_args() { let res = split("Instr".to_string()); assert_eq!(res.len(), 1); assert!(matches!(res[0], Token::Operator(_))); if let Token::Operator(name) = &res[0] { assert_eq!(name, "Instr"); } } #[test] fn splits_uppercase_instruction_no_args() { let res = split("INSTR".to_string()); assert_eq!(res.len(), 1); assert!(matches!(res[0], Token::Operator(_))); if let Token::Operator(name) = &res[0] { assert_eq!(name, "INSTR"); } } #[test] fn splits_instruction_symbol_arg() { let res = split("instr x".to_string()); assert_eq!(res.len(), 2); assert!(matches!(res[0], Token::Operator(_))); if let Token::Operator(name) = &res[0] { assert_eq!(name, "instr"); } assert!(matches!(res[1], Token::Symbol(_))); if let Token::Symbol(name) = &res[1] { assert_eq!(name, "x"); } } #[test] fn splits_instruction_two_symbol_arg() { let res = split("instr x, y".to_string()); assert_eq!(res.len(), 3); assert!(matches!(res[0], Token::Operator(_))); if let Token::Operator(name) = &res[0] { assert_eq!(name, "instr"); } assert!(matches!(res[1], Token::Symbol(_))); if let Token::Symbol(name) = &res[1] { assert_eq!(name, "x"); } assert!(matches!(res[2], Token::Symbol(_))); if let Token::Symbol(name) = &res[2] { assert_eq!(name, "y"); } } #[test] fn splits_instruction_two_num_arg() { let res = split("instr 819, 1".to_string()); assert_eq!(res.len(), 3); assert!(matches!(res[0], Token::Operator(_))); if let Token::Operator(name) = &res[0] { assert_eq!(name, "instr"); } assert!(matches!(res[1], Token::Number(_))); if let Token::Number(value) = &res[1] { assert_eq!(value, &819); } assert!(matches!(res[2], Token::Number(_))); if let Token::Number(value) = &res[2] { assert_eq!(value, &1); } } #[test] fn splits_definition() { let res = split("somevar = 61".to_string()); assert_eq!(res.len(), 2); assert!(matches!(res[0], Token::SymbolDef(_))); if let Token::SymbolDef(name) = &res[0] { assert_eq!(name, "somevar"); } } #[test] fn splits_number() { let res = split("561".to_string()); assert_eq!(res.len(), 1); assert!(matches!(res[0], Token::Number(_))); if let Token::Number(value) = &res[0] { assert_eq!(value, &561); } } #[test] fn splits_address() { let res = split("%561".to_string()); assert_eq!(res.len(), 1); assert!(matches!(res[0], Token::Address(_))); if let Token::Address(value) = &res[0] { assert_eq!(value, &561); } } #[test] fn splits_string() { let res = split("\"Hello World!\"".to_string()); assert_eq!(res.len(), 1); assert!(matches!(res[0], Token::String(_))); if let Token::String(value) = &res[0] { assert_eq!(value, "Hello World!"); } } }