fn main() { let input = "123 456 world"; println!("source: {}, parsed: {:?}", input, source(input)); let input = "(car cdr) cdr"; println!("source: {}, parsed: {:?}", input, source(input)); let input = "()())))((()))"; println!("source: {}, parsed: {:?}", input, source(input)); } fn advance_char(input: &str) -> &str { let mut chars = input.chars(); chars.next(); chars.as_str() } fn peek_char(input: &str) -> Option<char> { input.chars().next() } fn source(mut input: &str) -> Vec<Token> { let mut tokens = vec![]; while !input.is_empty() { input = if let (next_input, Some(token)) = token(input) { tokens.push(token); next_input } else { break; } } tokens } #[derive(Debug, PartialEq, Eq)] enum Token { Ident, Number, LParen, RParen, } fn token(i: &str) -> (&str, Option<Token>) { if let (i, Some(ident_res)) = ident(whitespace(i)) { return (i, Some(ident_res)); } if let (i, Some(number_res)) = number(whitespace(i)) { return (i, Some(number_res)); } if let (i, Some(lparen_res)) = lparen(whitespace(i)) { return (i, Some(lparen_res)); } if let (i, Some(rparen_res)) = rparen(whitespace(i)) { return (i, Some(rparen_res)); } (whitespace(i), None) } fn whitespace(mut input: &str) -> &str { while matches!(input.chars().next(), Some(' ')) { input = advance_char(input); } input } fn ident(mut input: &str) -> (&str, Option<Token>) { if matches!( peek_char(input), Some(_x @ ('a'..='z' | 'A'..='Z')) ) { input = advance_char(input); while matches!( peek_char(input), Some(_x @ ('a'..='z' | 'A'..='Z' | '0'..='9')) ) { input = advance_char(input); } (input, Some(Token::Ident)) } else { (input, None) } } fn number(mut input: &str) -> (&str, Option<Token>) { if matches!( peek_char(input), Some(_x @ ('-'|'+'|'.'|'0'..='9')) ) { input = advance_char(input); while matches!( peek_char(input), Some(_x @ ('.'|'0'..='9')) ) { input = advance_char(input); } (input, Some(Token::Number)) } else { (input, None) } } fn lparen(mut input: &str) -> (&str, Option<Token>) { if matches!(peek_char(input), Some('(')) { input = advance_char(input); (input, Some(Token::LParen)) } else { (input, None) } } fn rparen(mut input: &str) -> (&str, Option<Token>) { if matches!(peek_char(input), Some(')')) { input = advance_char(input); (input, Some(Token::RParen)) } else { (input, None) } } #[cfg(test)] mod test { use super::*; #[test] fn test_whitespace() { assert_eq!(whitespace(" "), ""); } #[test] fn test_indent() { assert_eq!(ident("Adam"), ""); } #[test] fn test_number() { assert_eq!(number("123.45 "), " "); } }
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.58s
Running `target/debug/ruscal`
source: 123 456 world, parsed: [Number, Number, Ident]
source: (car cdr) cdr, parsed: [LParen, Ident, Ident, RParen, Ident]
source: ()())))((())), parsed: [LParen, RParen, LParen, RParen, RParen, RParen, RParen, LParen, LParen, LParen, RParen, RParen, RParen]