lay command groundwork. Maybe need to make parsing more ergonomic.
This commit is contained in:
parent
d0f3ec7ad2
commit
90ae9c142c
|
@ -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(())
|
||||||
|
}
|
|
@ -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(())
|
|
||||||
}
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)),
|
||||||
|
])
|
||||||
|
))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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)?;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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, ()))
|
||||||
|
}
|
Loading…
Reference in New Issue