From 90ae9c142cbca969b7f5643982540f85add891bd Mon Sep 17 00:00:00 2001 From: "Taylor C. Richberger" Date: Mon, 20 Apr 2020 21:15:13 -0600 Subject: [PATCH] lay command groundwork. Maybe need to make parsing more ergonomic. --- src/bin/dicebot-cmd.rs | 14 +++++ src/bin/dicebot-roll.rs | 13 ----- src/commands.rs | 30 +++++++++++ src/commands/parser.rs | 115 ++++++++++++++++++++++++++++++++++++++++ src/dice/parser.rs | 10 +--- src/lib.rs | 2 + src/parser.rs | 13 +++++ 7 files changed, 175 insertions(+), 22 deletions(-) create mode 100644 src/bin/dicebot-cmd.rs delete mode 100644 src/bin/dicebot-roll.rs create mode 100644 src/commands.rs create mode 100644 src/commands/parser.rs create mode 100644 src/parser.rs diff --git a/src/bin/dicebot-cmd.rs b/src/bin/dicebot-cmd.rs new file mode 100644 index 0000000..6bdc5b3 --- /dev/null +++ b/src/bin/dicebot-cmd.rs @@ -0,0 +1,14 @@ +use axfive_matrix_dicebot::dice::parser::parse_element_expression; +use axfive_matrix_dicebot::roll::{Roll, Rolled}; +use axfive_matrix_dicebot::commands::Command; +use std::error::Error; + +fn main() -> Result<(), String> { + let command = std::env::args().skip(1).collect::>().join(" "); + let command: Command = match Command::parse(&command) { + Ok(command) => command.1, + Err(e) => return Err(format!("{}", e)), + }; + println!("{}", command.execute()); + Ok(()) +} diff --git a/src/bin/dicebot-roll.rs b/src/bin/dicebot-roll.rs deleted file mode 100644 index 2a6b4b1..0000000 --- a/src/bin/dicebot-roll.rs +++ /dev/null @@ -1,13 +0,0 @@ -use axfive_matrix_dicebot::dice::parser::parse_element_expression; -use axfive_matrix_dicebot::roll::{Roll, Rolled}; -use std::error::Error; - -fn main() -> Result<(), String> { - let roll_string = std::env::args().skip(1).collect::>().join(" "); - let (_tail, expression) = match parse_element_expression(&roll_string) { - Ok(response) => response, - Err(e) => return Err(e.to_string()), - }; - println!("{}", expression.roll()); - Ok(()) -} diff --git a/src/commands.rs b/src/commands.rs new file mode 100644 index 0000000..6e5626d --- /dev/null +++ b/src/commands.rs @@ -0,0 +1,30 @@ +use crate::dice::ElementExpression; +use crate::roll::{Roll, Rolled}; +use nom::error::ErrorKind; +use nom::IResult; +pub mod parser; + +pub struct RollCommand(ElementExpression); + +pub enum Command { + Roll(RollCommand), +} + +impl Command { + pub fn parse<'a>(input: &'a str) -> IResult<&'a str, Command> { + parser::parse_command(input) + } + + // Type subject to change + pub fn execute(self) -> String { + match self { + Command::Roll(command) => command.execute(), + } + } +} + +impl RollCommand { + pub fn execute(self) -> String { + self.0.roll().to_string() + } +} diff --git a/src/commands/parser.rs b/src/commands/parser.rs new file mode 100644 index 0000000..c314b72 --- /dev/null +++ b/src/commands/parser.rs @@ -0,0 +1,115 @@ +use nom::{ + alt, + bytes::complete::{tag, take_while}, + character::complete::digit1, + complete, many0, named, + sequence::tuple, + tag, IResult, +}; + +use crate::parser::eat_whitespace; +use crate::commands::{Command, RollCommand}; +use crate::dice::parser::parse_element_expression; + +// Parse a roll expression. +fn parse_roll(input: &str) -> IResult<&str, Command> { + named!(invocation(&str) -> &str, alt!(complete!(tag!("!r")) | complete!(tag!("!roll")))); + let (input, _) = eat_whitespace(input)?; + let (input, _) = invocation(input)?; + let (input, _) = eat_whitespace(input)?; + let (input, expression) = parse_element_expression(input)?; + Ok((input, Command::Roll(RollCommand(expression)))) +} + +// Parse a command expression. +pub fn parse_command(input: &str) -> IResult<&str, Command> { + // Add new commands to alt! + named!(command(&str) -> Command, alt!(parse_roll)); + command(input) +} + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn dice_test() { + assert_eq!(parse_dice("2d4"), Ok(("", Dice::new(2, 4)))); + assert_eq!(parse_dice("20d40"), Ok(("", Dice::new(20, 40)))); + assert_eq!(parse_dice("8d7"), Ok(("", Dice::new(8, 7)))); + } + + #[test] + fn element_test() { + assert_eq!( + parse_element(" \t\n\r\n 8d7 \n"), + Ok((" \n", Element::Dice(Dice::new(8, 7)))) + ); + assert_eq!( + parse_element(" \t\n\r\n 8 \n"), + Ok((" \n", Element::Bonus(8))) + ); + } + + #[test] + fn signed_element_test() { + assert_eq!( + parse_signed_element("+ 7"), + Ok(("", SignedElement::Positive(Element::Bonus(7)))) + ); + assert_eq!( + parse_signed_element(" \t\n\r\n- 8 \n"), + Ok((" \n", SignedElement::Negative(Element::Bonus(8)))) + ); + assert_eq!( + parse_signed_element(" \t\n\r\n- 8d4 \n"), + Ok(( + " \n", + SignedElement::Negative(Element::Dice(Dice::new(8, 4))) + )) + ); + assert_eq!( + parse_signed_element(" \t\n\r\n+ 8d4 \n"), + Ok(( + " \n", + SignedElement::Positive(Element::Dice(Dice::new(8, 4))) + )) + ); + } + + #[test] + fn element_expression_test() { + assert_eq!( + parse_element_expression("8d4"), + Ok(( + "", + ElementExpression(vec![SignedElement::Positive(Element::Dice(Dice::new( + 8, 4 + )))]) + )) + ); + assert_eq!( + parse_element_expression(" - 8d4 \n "), + Ok(( + " \n ", + ElementExpression(vec![SignedElement::Negative(Element::Dice(Dice::new( + 8, 4 + )))]) + )) + ); + assert_eq!( + parse_element_expression("\t3d4 + 7 - 5 - 6d12 + 1d1 + 53 1d5 "), + Ok(( + " 1d5 ", + ElementExpression(vec![ + SignedElement::Positive(Element::Dice(Dice::new(3, 4))), + SignedElement::Positive(Element::Bonus(7)), + SignedElement::Negative(Element::Bonus(5)), + SignedElement::Negative(Element::Dice(Dice::new(6, 12))), + SignedElement::Positive(Element::Dice(Dice::new(1, 1))), + SignedElement::Positive(Element::Bonus(53)), + ]) + )) + ); + } +} + diff --git a/src/dice/parser.rs b/src/dice/parser.rs index 46efe4d..c282cbd 100644 --- a/src/dice/parser.rs +++ b/src/dice/parser.rs @@ -8,6 +8,7 @@ use nom::{ }; use crate::dice::{Dice, Element, ElementExpression, SignedElement}; +use crate::parser::eat_whitespace; #[derive(Debug, PartialEq, Eq, Clone, Copy)] enum Sign { @@ -15,15 +16,6 @@ enum Sign { Minus, } -fn is_whitespace(input: char) -> bool { - input == ' ' || input == '\n' || input == '\t' || input == '\r' -} - -fn eat_whitespace(input: &str) -> IResult<&str, ()> { - let (input, _) = take_while(is_whitespace)(input)?; - Ok((input, ())) -} - // Parse a dice expression. Does not eat whitespace fn parse_dice(input: &str) -> IResult<&str, Dice> { let (input, (count, _, sides)) = tuple((digit1, tag("d"), digit1))(input)?; diff --git a/src/lib.rs b/src/lib.rs index 8f10660..f92d8f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,3 +2,5 @@ pub mod bot; pub mod dice; pub mod matrix; pub mod roll; +pub mod commands; +mod parser; diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..0a11e17 --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,13 @@ +use nom::{ + bytes::complete::take_while, + IResult, +}; + +fn is_whitespace(input: char) -> bool { + input == ' ' || input == '\n' || input == '\t' || input == '\r' +} + +pub fn eat_whitespace(input: &str) -> IResult<&str, ()> { + let (input, _) = take_while(is_whitespace)(input)?; + Ok((input, ())) +}