-
-
Save RandyMcMillan/a3fa7b7b450064853ae635b750dff44b to your computer and use it in GitHub Desktop.
lojban_gemini.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // --- Dependencies --- | |
| // This simple example relies only on standard library features. | |
| use std::error::Error; | |
| use std::fmt; | |
| // --- Error Handling --- | |
| /// A simple custom error type for parsing issues. | |
| #[derive(Debug)] | |
| struct ParseError { | |
| details: String, | |
| } | |
| impl ParseError { | |
| fn new(details: &str) -> ParseError { | |
| ParseError { details: details.to_string() } | |
| } | |
| } | |
| impl fmt::Display for ParseError { | |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
| write!(f, "Parse Error: {}", self.details) | |
| } | |
| } | |
| impl Error for ParseError {} | |
| /// Type alias for simplified error handling in the parser. | |
| type Result<T> = std::result::Result<T, ParseError>; | |
| // --- Command Definition --- | |
| /// Represents the unambiguous Lojban commands Gemini is designed to execute. | |
| #[derive(Debug, PartialEq)] | |
| enum GeminiCommand { | |
| /// ko sisku [query] | |
| Search { query: String }, | |
| /// ko pilno la .youtube. [query] | |
| UseYoutube { query: String }, | |
| /// ko skicu [topic] | |
| Summarize { topic: String }, | |
| /// ko finti [artifact] | |
| GenerateText { artifact: String }, | |
| /// ko finti le pixra be [subject] | |
| GenerateImage { subject: String }, | |
| /// ko pilnoi la .remindr. [task] | |
| SetReminder { task: String }, | |
| /// A command structure that couldn't be parsed. | |
| Unknown, | |
| } | |
| // --- Parser Function --- | |
| /// Parses a Lojban command string into a structured GeminiCommand. | |
| fn parse_lojban_command(input: &str) -> Result<GeminiCommand> { | |
| // 1. Normalize and split the input | |
| let parts: Vec<&str> = input.trim().splitn(3, ' ').collect(); | |
| if parts.len() < 2 || parts[0] != "ko" { | |
| return Err(ParseError::new("Invalid command structure. Must begin with 'ko'.")); | |
| } | |
| // Command keyword is always the second element | |
| let keyword = parts[1]; | |
| // The argument/sumti is the rest of the string after "ko [keyword]" | |
| let argument = parts.get(2).unwrap_or(&"").trim(); | |
| // The special case for "lo" is removed for simplicity, all commands require a sumti | |
| if argument.is_empty() { | |
| return Err(ParseError::new(&format!("Missing argument (sumti) for command: {}", keyword))); | |
| } | |
| // 2. Match the keyword and parse the argument | |
| match keyword { | |
| "sisku" => Ok(GeminiCommand::Search { query: argument.to_string() }), | |
| "pilno" => { | |
| // Check for the specific tool-use command: ko pilno la .youtube. [query] | |
| let tool_prefix = "la .youtube."; | |
| if argument.starts_with(tool_prefix) { | |
| // Ensure we handle the space after the tool name | |
| let query = argument.trim_start_matches(tool_prefix).trim(); | |
| if query.is_empty() { | |
| return Err(ParseError::new("Missing query after 'ko pilno la .youtube.'.")); | |
| } | |
| Ok(GeminiCommand::UseYoutube { query: query.to_string() }) | |
| } else { | |
| Err(ParseError::new("Unknown 'pilno' command or missing tool name (expected 'la .youtube.').")) | |
| } | |
| }, | |
| "skicu" => Ok(GeminiCommand::Summarize { topic: argument.to_string() }), | |
| "finti" => { | |
| // Check for the image generation case: ko finti le pixra be [subject] | |
| let image_prefix = "le pixra be"; | |
| if argument.starts_with(image_prefix) { | |
| // Extract the subject from after "le pixra be" | |
| let subject = argument.trim_start_matches(image_prefix).trim(); | |
| if subject.is_empty() { | |
| return Err(ParseError::new("Missing subject after 'ko finti le pixra be'.")); | |
| } | |
| Ok(GeminiCommand::GenerateImage { subject: subject.to_string() }) | |
| } else { | |
| // Default to general text/code generation | |
| Ok(GeminiCommand::GenerateText { artifact: argument.to_string() }) | |
| } | |
| }, | |
| "pilnoi" => { | |
| // Check for the reminder tool | |
| let reminder_prefix = "la .remindr."; | |
| if argument.starts_with(reminder_prefix) { | |
| let task = argument.trim_start_matches(reminder_prefix).trim(); | |
| if task.is_empty() { | |
| return Err(ParseError::new("Missing task after 'ko pilnoi la .remindr.'.")); | |
| } | |
| Ok(GeminiCommand::SetReminder { task: task.to_string() }) | |
| } else { | |
| Err(ParseError::new("Unknown 'pilnoi' tool specified.")) | |
| } | |
| } | |
| _ => Err(ParseError::new(&format!("Unknown Lojban keyword: {}", keyword))), | |
| } | |
| } | |
| // --- Main Function (Demonstration) --- | |
| fn main() { | |
| println!("Lojban Command Parser: Test Suite\n---"); | |
| let commands = vec![ | |
| "ko sisku le krasi progaritme", // Search | |
| "ko finti le pixra be le lartu gerku", // Generate Image | |
| "ko pilno la .youtube. le zmadu nanba be'o", // Use YouTube | |
| "ko pilnoi la .remindr. le nu djica le titnanba",// Set Reminder | |
| "ko skicu le gismu", // Summarize | |
| "ko finti le krasi", // Generate Text | |
| "ko pilno la .gugl.", // Invalid/Unknown pilno | |
| "sisku le nanmu", // Missing 'ko' | |
| "ko sisku", // Missing sumti | |
| ]; | |
| for cmd_str in commands { | |
| match parse_lojban_command(cmd_str) { | |
| Ok(command) => { | |
| println!("✅ **Input:** '{}'", cmd_str); | |
| println!(" **Parsed:** {:?}", command); | |
| }, | |
| Err(e) => { | |
| println!("❌ **Input:** '{}'", cmd_str); | |
| println!(" **Error:** {}", e); | |
| }, | |
| } | |
| println!("---"); | |
| } | |
| } | |
| // --- Test Module --- | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| // --- Success Tests --- | |
| #[test] | |
| fn test_search_command_success() { | |
| let input = "ko sisku le krasi progaritme"; | |
| let expected = GeminiCommand::Search { query: "le krasi progaritme".to_string() }; | |
| assert_eq!(parse_lojban_command(input).unwrap(), expected); | |
| } | |
| #[test] | |
| fn test_generate_image_command_success() { | |
| let input = "ko finti le pixra be le lartu gerku"; | |
| let expected = GeminiCommand::GenerateImage { subject: "le lartu gerku".to_string() }; | |
| assert_eq!(parse_lojban_command(input).unwrap(), expected); | |
| } | |
| #[test] | |
| fn test_generate_text_command_success() { | |
| let input = "ko finti le krasi"; | |
| let expected = GeminiCommand::GenerateText { artifact: "le krasi".to_string() }; | |
| assert_eq!(parse_lojban_command(input).unwrap(), expected); | |
| } | |
| #[test] | |
| fn test_youtube_command_success() { | |
| let input = "ko pilno la .youtube. le zmadu nanba be'o"; | |
| let expected = GeminiCommand::UseYoutube { query: "le zmadu nanba be'o".to_string() }; | |
| assert_eq!(parse_lojban_command(input).unwrap(), expected); | |
| } | |
| #[test] | |
| fn test_reminder_command_success() { | |
| let input = "ko pilnoi la .remindr. le nu djica le titnanba"; | |
| let expected = GeminiCommand::SetReminder { task: "le nu djica le titnanba".to_string() }; | |
| assert_eq!(parse_lojban_command(input).unwrap(), expected); | |
| } | |
| #[test] | |
| fn test_summarize_command_success() { | |
| let input = "ko skicu le gismu"; | |
| let expected = GeminiCommand::Summarize { topic: "le gismu".to_string() }; | |
| assert_eq!(parse_lojban_command(input).unwrap(), expected); | |
| } | |
| // --- Failure Tests --- | |
| #[test] | |
| fn test_missing_ko_failure() { | |
| let input = "sisku le nanmu"; | |
| assert!(parse_lojban_command(input).is_err()); | |
| assert!(parse_lojban_command(input).unwrap_err().to_string().contains("Must begin with 'ko'")); | |
| } | |
| #[test] | |
| fn test_missing_sumti_failure() { | |
| let input = "ko sisku"; | |
| assert!(parse_lojban_command(input).is_err()); | |
| assert!(parse_lojban_command(input).unwrap_err().to_string().contains("Missing argument (sumti) for command: sisku")); | |
| } | |
| #[test] | |
| fn test_unknown_keyword_failure() { | |
| let input = "ko klama le stizu"; | |
| assert!(parse_lojban_command(input).is_err()); | |
| assert!(parse_lojban_command(input).unwrap_err().to_string().contains("Unknown Lojban keyword: klama")); | |
| } | |
| #[test] | |
| fn test_unknown_pilno_tool_failure() { | |
| let input = "ko pilno la .gugl. le nanmu"; | |
| assert!(parse_lojban_command(input).is_err()); | |
| assert!(parse_lojban_command(input).unwrap_err().to_string().contains("expected 'la .youtube.'")); | |
| } | |
| #[test] | |
| fn test_incomplete_image_command_failure() { | |
| let input = "ko finti le pixra be"; | |
| assert!(parse_lojban_command(input).is_err()); | |
| assert!(parse_lojban_command(input).unwrap_err().to_string().contains("Missing subject after 'ko finti le pixra be'.")); | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=685ab604de7f247553c063375a148c91