fn main() { let s = "Hello world"; println!("source: {:?}, parsed:\n {:?}", s, source(s)); let s = "(123 456 ) world"; println!("source: {:?}, parsed:\n {:?}", s, source(s)); let s = "((car cdr) cdr)"; println!("source: {:?}, parsed:\n {:?}", s, source(s)); let s = "()())))((()))"; println!("source: {:?}, parsed:\n {:?}", s, source(s)); } 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) -> (&str, TokenTree) { let mut tokens = vec![]; while !input.is_empty() { input = if let Some((next_input, token)) = token(input) { match token { Token::LParen => { let (next_input, tt) = source(next_input); tokens.push(tt); next_input } Token::RParen => { return (next_input, TokenTree::Tree(tokens)) } _ => { tokens.push(TokenTree::Token(token)); next_input } } } else { break; } } (input, TokenTree::Tree(tokens)) } #[derive(Debug, PartialEq)] enum Token<'src> { Ident(&'src str), Number(f64), LParen, RParen, } #[derive(Debug, PartialEq)] enum TokenTree<'src> { Token(Token<'src>), Tree(Vec<TokenTree<'src>>), } fn token(input: &str) -> Option<(&str, Token)> { if let Some(res) = ident(whitespace(input)) { return Some(res); } if let Some(res) = number(whitespace(input)) { return Some(res); } if let Some(res) = lparen(whitespace(input)) { return Some(res); } if let Some(res) = rparen(whitespace(input)) { return Some(res); } None } fn whitespace(mut input: &str) -> &str { while matches!(peek_char(input), Some(' ')) { let mut chars = input.chars(); chars.next(); input = chars.as_str(); } input } fn ident(mut input: &str) -> Option<(&str, Token)> { let start = input; 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); } Some(( input, Token::Ident(&start[..(start.len() - input.len())]), )) } else { None } } fn number(mut input: &str) -> Option<(&str, Token)> { let start = input; 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); } if let Ok(num) = start[..(start.len() - input.len())].parse::<f64>() { Some((input, Token::Number(num))) } else { None } } else { None } } fn lparen(mut input: &str) -> Option<(&str, Token)> { if matches!(peek_char(input), Some('(')) { input = advance_char(input); Some((input, Token::LParen)) } else { None } } fn rparen(mut input: &str) -> Option<(&str, Token)> { if matches!(peek_char(input), Some(')')) { input = advance_char(input); Some((input, Token::RParen)) } else { None } } #[cfg(test)] mod test { use super::*; #[test] fn test_whitespace() { assert_eq!(whitespace(" "), ""); } #[test] fn test_ident() { assert_eq!(ident("Adam"), Some(("", Token::Ident("Adam")))); } #[test] fn test_number() { assert_eq!( number("123.45 "), Some((" ", Token::Number(123.45))) ); } }
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.85s
Running `target/debug/ruscal`
source: “Hello world”, parsed:
(“”, Tree([Token(Ident(“Hello”)), Token(Ident(“world”))]))
source: “(123 456 ) world”, parsed:
(“”, Tree([Tree([Token(Number(123.0)), Token(Number(456.0))]), Token(Ident(“world”))]))
source: “((car cdr) cdr)”, parsed:
(“”, Tree([Tree([Tree([Token(Ident(“car”)), Token(Ident(“cdr”))]), Token(Ident(“cdr”))])]))
source: “()())))((()))”, parsed:
(“))((()))”, Tree([Tree([]), Tree([])]))