lay command groundwork. Maybe need to make parsing more ergonomic.

This commit is contained in:
Taylor C. Richberger 2020-04-20 21:15:13 -06:00
parent d0f3ec7ad2
commit 90ae9c142c
7 changed files with 175 additions and 22 deletions

14
src/bin/dicebot-cmd.rs Normal file
View File

@ -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::<Vec<String>>().join(" ");
let command: Command = match Command::parse(&command) {
Ok(command) => command.1,
Err(e) => return Err(format!("{}", e)),
};
println!("{}", command.execute());
Ok(())
}

View File

@ -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::<Vec<String>>().join(" ");
let (_tail, expression) = match parse_element_expression(&roll_string) {
Ok(response) => response,
Err(e) => return Err(e.to_string()),
};
println!("{}", expression.roll());
Ok(())
}

30
src/commands.rs Normal file
View File

@ -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()
}
}

115
src/commands/parser.rs Normal file
View File

@ -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)),
])
))
);
}
}

View File

@ -8,6 +8,7 @@ use nom::{
}; };
use crate::dice::{Dice, Element, ElementExpression, SignedElement}; use crate::dice::{Dice, Element, ElementExpression, SignedElement};
use crate::parser::eat_whitespace;
#[derive(Debug, PartialEq, Eq, Clone, Copy)] #[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum Sign { enum Sign {
@ -15,15 +16,6 @@ enum Sign {
Minus, 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 // Parse a dice expression. Does not eat whitespace
fn parse_dice(input: &str) -> IResult<&str, Dice> { fn parse_dice(input: &str) -> IResult<&str, Dice> {
let (input, (count, _, sides)) = tuple((digit1, tag("d"), digit1))(input)?; let (input, (count, _, sides)) = tuple((digit1, tag("d"), digit1))(input)?;

View File

@ -2,3 +2,5 @@ pub mod bot;
pub mod dice; pub mod dice;
pub mod matrix; pub mod matrix;
pub mod roll; pub mod roll;
pub mod commands;
mod parser;

13
src/parser.rs Normal file
View File

@ -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, ()))
}