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::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)?;
|
||||
|
|
|
@ -2,3 +2,5 @@ pub mod bot;
|
|||
pub mod dice;
|
||||
pub mod matrix;
|
||||
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