use std::io::Cursor; use neotoma::{ cache::ParsingCache, literal::Literal, optional::Optional, parser::{Parser, Source, parse}, result::{Error, ParseResult}, utf8class::Utf8Class, }; // Lisp atom parser - handles numbers, symbols, and strings #[derive(Clone)] struct LispAtom; impl Parser for LispAtom { type Output = String; fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, _context: &mut Ctx, ) -> ParseResult where S: neotoma::parser::Parsable, { // Try parsing a number first (require at least one digit) let number = Utf8Class::with_min("0123456789", 1); if let Ok(digits) = number.parse(source, cache, _context) { if !digits.is_empty() { return Ok(digits); } } // Try parsing a symbol (alphanumeric + some special chars, require at least one) let symbol_chars = Utf8Class::from_predicate_min(|c| c.is_alphanumeric() || "+-*/<>=!?".contains(c), 1); if let Ok(symbol) = symbol_chars.parse(source, cache, _context) { if !symbol.is_empty() { return Ok(symbol); } } Err(Error::NoMatch) } } // Lisp list parser - handles parenthesized expressions #[derive(Clone)] struct LispList; impl Parser for LispList { type Output = Vec; fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, _context: &mut Ctx, ) -> ParseResult where S: neotoma::parser::Parsable, { // Parse opening paren let open_paren = Literal::from_bytes_const(b"("); let _ = open_paren.parse(source, cache, _context)?; let mut expressions = Vec::new(); // Parse expressions until closing paren loop { // Skip whitespace first let whitespace = Optional::new(Utf8Class::whitespace()); let _ = whitespace.parse(source, cache, _context); // Try to parse closing paren - parse() handles backtracking automatically let close_paren = Literal::from_bytes_const(b")"); if close_paren.parse(source, cache, _context).is_ok() { return Ok(expressions); } // Parse an expression - for now just atoms (no recursion yet) let atom = LispAtom; if let Ok(result) = atom.parse(source, cache, _context) { expressions.push(LispExpr::Atom(result)); } else { // If we can't parse an atom and can't find closing paren, it's an error return Err(Error::NoMatch); } } } } #[derive(Debug, Clone, PartialEq)] enum LispExpr { Atom(String), List(Vec), } // Main expression parser #[derive(Clone)] struct LispExpression; impl Parser for LispExpression { type Output = LispExpr; fn read( &self, source: &mut Source, cache: &mut impl ParsingCache, _context: &mut Ctx, ) -> ParseResult where S: neotoma::parser::Parsable, { // Try parsing an atom first let atom = LispAtom; if let Ok(result) = atom.parse(source, cache, _context) { return Ok(LispExpr::Atom(result)); } // Try parsing a list let list = LispList; if let Ok(result) = list.parse(source, cache, _context) { return Ok(LispExpr::List(result)); } Err(Error::NoMatch) } } #[test] fn test_simple_lisp_atom() { let cursor = Cursor::new(b"42"); let mut source = Source::new(cursor); let parser = LispExpression; let result = parse(parser, &mut source).unwrap(); assert_eq!(result, LispExpr::Atom("42".to_string())); } #[test] fn test_lisp_symbol() { let cursor = Cursor::new(b"+"); let mut source = Source::new(cursor); let parser = LispExpression; let result = parse(parser, &mut source).unwrap(); assert_eq!(result, LispExpr::Atom("+".to_string())); } #[test] fn test_empty_lisp_list() { let cursor = Cursor::new(b"()"); let mut source = Source::new(cursor); let parser = LispExpression; let result = parse(parser, &mut source).unwrap(); assert_eq!(result, LispExpr::List(vec![])); } #[test] fn test_simple_lisp_list() { let cursor = Cursor::new(b"(+ 1 2)"); let mut source = Source::new(cursor); let parser = LispExpression; let result = parse(parser, &mut source).unwrap(); assert_eq!( result, LispExpr::List(vec![ LispExpr::Atom("+".to_string()), LispExpr::Atom("1".to_string()), LispExpr::Atom("2".to_string()), ]) ); } #[test] fn lisp_parsing() { // Test various lisp expressions to demonstrate the parser capabilities let test_cases = vec![ ("42", LispExpr::Atom("42".to_string())), ("hello", LispExpr::Atom("hello".to_string())), ("+", LispExpr::Atom("+".to_string())), ("()", LispExpr::List(vec![])), ( "(+ 1 2)", LispExpr::List(vec![ LispExpr::Atom("+".to_string()), LispExpr::Atom("1".to_string()), LispExpr::Atom("2".to_string()), ]), ), ( "(hello world)", LispExpr::List(vec![ LispExpr::Atom("hello".to_string()), LispExpr::Atom("world".to_string()), ]), ), ]; for (input, expected) in test_cases { let cursor = Cursor::new(input.as_bytes()); let mut source = Source::new(cursor); let parser = LispExpression; let result = parse(parser, &mut source).unwrap(); assert_eq!(result, expected, "Failed to parse: {input}"); } }