write out nom-based parser
This commit is contained in:
parent
18537f9ab0
commit
56de3d34f1
|
@ -1,20 +1,20 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d663a8e9a99154b5fb793032533f6328da35e23aac63d5c152279aa8ba356825"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
|
||||
dependencies = [
|
||||
"nodrop",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
|
@ -25,7 +25,7 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
|||
name = "axfive-matrix-dicebot"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"nom",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -65,9 +65,9 @@ checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
|
|||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
checksum = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
|
@ -329,6 +329,20 @@ version = "1.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lexical-core"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f86d66d380c9c5a685aaac7a11818bdfa1f733198dfd9ec09c70b762cd12ad6f"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"rustc_version",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.69"
|
||||
|
@ -443,6 +457,23 @@ dependencies = [
|
|||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodrop"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6"
|
||||
dependencies = [
|
||||
"lexical-core",
|
||||
"memchr",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.29"
|
||||
|
@ -591,24 +622,6 @@ version = "0.1.56"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"thread_local",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
version = "0.5.2"
|
||||
|
@ -653,6 +666,15 @@ dependencies = [
|
|||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.3"
|
||||
|
@ -692,6 +714,21 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||
dependencies = [
|
||||
"semver-parser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "semver-parser"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.106"
|
||||
|
@ -757,6 +794,12 @@ version = "1.3.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05720e22615919e4734f6a99ceae50d00226c3c5aca406e102ebc33298214e0a"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.17"
|
||||
|
@ -782,15 +825,6 @@ dependencies = [
|
|||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.42"
|
||||
|
|
12
Cargo.toml
12
Cargo.toml
|
@ -7,15 +7,15 @@ edition = "2018"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
reqwest = "^0.10"
|
||||
serde_json = "^1"
|
||||
toml = "^0.5"
|
||||
regex = "^1.3"
|
||||
reqwest = "0.10"
|
||||
serde_json = "1"
|
||||
toml = "0.5"
|
||||
nom = "5"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "^1"
|
||||
version = "1"
|
||||
features = ['derive']
|
||||
|
||||
[dependencies.tokio]
|
||||
version = "^0.2"
|
||||
version = "0.2"
|
||||
features = ["rt-core", "macros", "time", "signal"]
|
||||
|
|
|
@ -1,128 +1,9 @@
|
|||
use regex::Regex;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParseError {
|
||||
reason: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "ParseError: {}", self.reason)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ParseError {}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Dice {
|
||||
count: u32,
|
||||
sides: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Roll {
|
||||
Dice(Dice),
|
||||
Bonus(u32),
|
||||
}
|
||||
|
||||
impl From<u32> for Roll {
|
||||
fn from(value: u32) -> Roll {
|
||||
Roll::Bonus(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Dice> for Roll {
|
||||
fn from(value: Dice) -> Roll {
|
||||
Roll::Dice(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Roll {
|
||||
type Err = Box<dyn Error>;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.trim();
|
||||
let regex = Regex::new(r"(?i)^(\d+)\s*(d\s*(\d+))?$")?;
|
||||
let captures = match regex.captures(s) {
|
||||
Some(captures) => captures,
|
||||
None => {
|
||||
return Err(ParseError {
|
||||
reason: format!("{:?} is not a legal Roll Part expression", s),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
};
|
||||
|
||||
match captures.get(2) {
|
||||
Some(_) => Ok(Dice {
|
||||
count: captures.get(1).unwrap().as_str().parse()?,
|
||||
sides: captures.get(3).unwrap().as_str().parse()?,
|
||||
}
|
||||
.into()),
|
||||
None => Ok(Roll::Bonus(captures.get(1).unwrap().as_str().parse()?)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Part {
|
||||
Plus(Roll),
|
||||
Minus(Roll),
|
||||
}
|
||||
|
||||
impl FromStr for Part {
|
||||
type Err = Box<dyn Error>;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.trim();
|
||||
|
||||
match s.chars().next() {
|
||||
Some('+') => Ok(Part::Plus(s[1..].parse()?)),
|
||||
Some('-') => Ok(Part::Minus(s[1..].parse()?)),
|
||||
Some(_) => Ok(Part::Plus(s.parse()?)),
|
||||
None => Err(ParseError {
|
||||
reason: format!("{:?} is not a legal Roll Part expression", s),
|
||||
}
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Expression(Vec<Part>);
|
||||
|
||||
impl FromStr for Expression {
|
||||
type Err = Box<dyn Error>;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.trim();
|
||||
let validation_regex = Regex::new(
|
||||
r"(?xi)^
|
||||
([-\+])?\s*(\d+)\s*(d\s*(\d+))?
|
||||
(\s*([-\+])\s*(\d+)\s*(d\s*(\d+))?)*
|
||||
$",
|
||||
)?;
|
||||
if !validation_regex.is_match(s) {
|
||||
return Err(ParseError {
|
||||
reason: format!("{:?} is not a legal dice expression", s),
|
||||
}
|
||||
.into());
|
||||
}
|
||||
|
||||
let part = Regex::new(r"(?i)[-\+]?\s*\d+\s*(d\s*(\d+))?")?;
|
||||
|
||||
let results: Result<_, _> = part.find_iter(s).map(|p| p.as_str().parse()).collect();
|
||||
Ok(Expression(results?))
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
let roll_string = std::env::args().skip(1).collect::<Vec<String>>().join(" ");
|
||||
let _roll_string = std::env::args().skip(1).collect::<Vec<String>>().join(" ");
|
||||
// first regex needs to be different because the sign is mandatory for the rest
|
||||
let expression: Expression = roll_string.parse()?;
|
||||
println!("{:?}", expression);
|
||||
//let expression = parse_expression(&roll_string);
|
||||
//println!("{:?}", expression);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod bot;
|
||||
pub mod matrix;
|
||||
pub mod parser;
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
use nom::{
|
||||
alt,
|
||||
tag,
|
||||
complete,
|
||||
named,
|
||||
many0,
|
||||
IResult,
|
||||
bytes::complete::{tag, take_while},
|
||||
character::complete::digit1,
|
||||
sequence::tuple
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Dice {
|
||||
count: u32,
|
||||
sides: u32,
|
||||
}
|
||||
|
||||
impl Dice {
|
||||
fn new(count: u32, sides: u32) -> Dice {
|
||||
Dice {
|
||||
count,
|
||||
sides,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum Element {
|
||||
Dice(Dice),
|
||||
Bonus(u32),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum Sign {
|
||||
Plus,
|
||||
Minus,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum SignedElement {
|
||||
Positive(Element),
|
||||
Negative(Element),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct ElementExpression(Vec<SignedElement>);
|
||||
|
||||
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)?;
|
||||
Ok((input, Dice::new(count.parse().unwrap(), sides.parse().unwrap())))
|
||||
}
|
||||
|
||||
// Parse a single digit expression. Does not eat whitespace
|
||||
fn parse_bonus(input: &str) -> IResult<&str, u32> {
|
||||
let (input, bonus) = digit1(input)?;
|
||||
Ok((input, bonus.parse().unwrap()))
|
||||
}
|
||||
|
||||
// Parse a sign expression. Eats whitespace.
|
||||
fn parse_sign(input: &str) -> IResult<&str, Sign> {
|
||||
let (input, _) = eat_whitespace(input)?;
|
||||
named!(sign(&str) -> Sign, alt!(
|
||||
tag!("+") => { |_| Sign::Plus } |
|
||||
tag!("-") => { |_| Sign::Minus }
|
||||
));
|
||||
|
||||
let (input, sign) = sign(input)?;
|
||||
Ok((input, sign))
|
||||
}
|
||||
|
||||
// Parse an element expression. Eats whitespace.
|
||||
fn parse_element(input: &str) -> IResult<&str, Element> {
|
||||
let (input, _) = eat_whitespace(input)?;
|
||||
named!(element(&str) -> Element, alt!(
|
||||
parse_dice => { |d| Element::Dice(d) } |
|
||||
parse_bonus => { |b| Element::Bonus(b) }
|
||||
));
|
||||
|
||||
let (input, element) = element(input)?;
|
||||
Ok((input, element))
|
||||
}
|
||||
|
||||
// Parse a signed element expression. Eats whitespace.
|
||||
fn parse_signed_element(input: &str) -> IResult<&str, SignedElement> {
|
||||
let (input, _) = eat_whitespace(input)?;
|
||||
let (input, sign) = parse_sign(input)?;
|
||||
let (input, _) = eat_whitespace(input)?;
|
||||
|
||||
let (input, element) = parse_element(input)?;
|
||||
let element = match sign {
|
||||
Sign::Plus => SignedElement::Positive(element),
|
||||
Sign::Minus => SignedElement::Negative(element),
|
||||
};
|
||||
Ok((input, element))
|
||||
}
|
||||
|
||||
// Parse a full element expression. Eats whitespace.
|
||||
fn parse_element_expression(input: &str) -> IResult<&str, ElementExpression> {
|
||||
named!(first_element(&str) -> SignedElement, alt!(
|
||||
parse_signed_element => { |e| e } |
|
||||
parse_element => { |e| SignedElement::Positive(e) }
|
||||
));
|
||||
let (input, first) = first_element(input)?;
|
||||
let (input, rest) = if input.trim().is_empty() {
|
||||
(input, vec![first])
|
||||
} else {
|
||||
named!(rest_elements(&str) -> Vec<SignedElement>, many0!(complete!(parse_signed_element)));
|
||||
let (input, mut rest) = rest_elements(input)?;
|
||||
rest.insert(0, first);
|
||||
(input, rest)
|
||||
};
|
||||
|
||||
Ok((input, ElementExpression(rest)))
|
||||
}
|
||||
|
||||
#[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)),
|
||||
]))));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue