even less nom

This commit is contained in:
Robin Appelman 2023-03-10 21:48:15 +01:00
commit 19f9c9f973
4 changed files with 74 additions and 56 deletions

View file

@ -337,6 +337,16 @@ pub fn split_once(input: &str, delim: u8, offset: usize) -> Result<(&str, &str)>
})
}
pub fn take_until(input: &str, delim: u8) -> (&str, &str) {
debug_assert!(delim < 128); // only basic ascii
if let Some(end) = memchr(delim, input.as_bytes()) {
// safety, memchr returns indices that are inside the input length and we only split on ascii
unsafe { (input.get_unchecked(end..), input.get_unchecked(..end)) }
} else {
("", input)
}
}
pub fn skip(input: &str, count: usize) -> Result<&str> {
input.get(count..).ok_or(Error::Incomplete)
}

View file

@ -1,9 +1,8 @@
use crate::event::{param_parse, param_parse_with, parse_field, ParamIter};
use crate::event::{param_parse_with, parse_field, ParamIter};
use crate::raw_event::RawSubject;
use crate::{Event, IResult};
use crate::{Error, Event, IResult};
use nom::bytes::complete::{tag, take_while};
use nom::combinator::opt;
use crate::common::{skip, take_until};
#[derive(Debug, Event)]
pub struct RoundWinEvent<'a> {
@ -32,10 +31,10 @@ pub struct TournamentModeStartedEvent<'a> {
impl<'a> Event<'a> for TournamentModeStartedEvent<'a> {
fn parse(input: &'a str) -> IResult<Self> {
let (input, _) = tag("\nBlue Team: ")(input)?;
let (input, blue) = take_while(|c| c != '\n')(input)?;
let (input, _) = tag("\nRed Team: ")(input)?;
let (input, red) = take_while(|c| c != '\n')(input)?;
let input = skip(input, "\nBlue Team: ".len())?;
let (input, blue) = take_until(input, b'\n');
let input = skip(input, "\nRed Team: ".len())?;
let (input, red) = take_until(input, b'\n');
Ok((input, TournamentModeStartedEvent { blue, red }))
}
}
@ -58,20 +57,41 @@ pub struct PointCapturedEvent<'a> {
impl<'a> Event<'a> for PointCapturedEvent<'a> {
fn parse(input: &'a str) -> IResult<Self> {
let (input, cp) = opt(param_parse("cp"))(input)?;
let (input, cp_name) = opt(param_parse("cpname"))(input)?;
let (input, num_cappers) = opt(param_parse("numcappers"))(input)?;
let mut cp = Default::default();
let mut cp_name = Default::default();
let mut num_cappers = Default::default();
let mut players = Vec::new();
let mut params = ParamIter::new(input);
let mut params = ParamIter::new(input).peekable();
while let Some((name, value)) = params.peek() {
match *name {
"cp" => {
cp = Some(value.parse().map_err(Error::from)?);
let _ = params.next();
}
"cpname" => {
cp_name = Some(*value);
let _ = params.next();
}
"numcappers" => {
num_cappers = Some(value.parse().map_err(Error::from)?);
let _ = params.next();
}
_ => {
break;
}
}
}
loop {
match (params.next(), params.next()) {
(Some((subject_key, subject)), Some((position_key, position_str)))
if subject_key.starts_with("player") && position_key.starts_with("position") =>
if subject_key.starts_with("player")
&& position_key.starts_with("position") =>
{
players.push((parse_field(subject)?.1, parse_field(position_str)?.1));
}
_ => {}
_ => break,
}
}
Ok((

View file

@ -2,15 +2,12 @@ mod game;
mod medic;
mod player;
use crate::common::{skip, skip_matches, split_once};
use crate::common::{skip, skip_matches, split_once, take_until};
use crate::event::game::{RoundLengthEvent, RoundWinEvent};
use crate::raw_event::{against_subject_parser, RawSubject};
use crate::{err_incomplete, IResult, RawEvent, RawEventType, SubjectId};
pub use game::*;
pub use medic::*;
use nom::bytes::complete::{tag, take_while};
use nom::character::complete::digit1;
use nom::combinator::opt;
use nom::error::{ErrorKind, ParseError};
use nom::number::complete::float;
use nom::Err;
@ -253,9 +250,9 @@ fn quoted<'a, T, P: Fn(&'a str) -> IResult<'a, T>>(
parser: P,
) -> impl Fn(&'a str) -> IResult<'a, T> {
move |input| {
let (input, _) = tag(r#"""#)(input)?;
let (input, res) = parser(input)?;
let (input, _) = tag(r#"""#)(input)?;
let input = skip(input, 1)?;
let (inner, input) = split_once(input, b'"', 1)?;
let (_, res) = parser(inner)?;
Ok((input, res))
}
}
@ -297,45 +294,29 @@ fn parse_from_str<'a, T: FromStr + 'a>(input: &'a str) -> IResult<T> {
}
fn int(input: &str) -> IResult<i32> {
let (input, sign) = opt(tag("-"))(input)?;
let (input, raw) = digit1(input)?;
let val: i32 = raw
.parse()
.map_err(|_| Err::Error(nom::error::Error::new(raw, ErrorKind::Digit)))?;
Ok((input, if sign.is_some() { -val } else { val }))
let (input, sign) = skip_matches(input, b'-');
let (input, unsigned) = u_int(input)?;
let signed = unsigned as i32;
Ok((input, if sign { -signed } else { signed }))
}
fn u_int(input: &str) -> IResult<u32> {
let (input, quote) = opt(tag("\""))(input)?;
let (input, raw) = digit1(input)?;
let (input, raw) = take_until(input, b' ');
let val = raw.parse().map_err(|_| err_incomplete())?;
let input = if quote.is_some() {
tag("\"")(input)?.0
} else {
input
};
Ok((input, val))
}
pub fn position(input: &str) -> IResult<(i32, i32, i32)> {
let (input, x) = int(input)?;
let (input, _) = tag(" ")(input)?;
let (input, y) = int(input)?;
let (input, _) = tag(" ")(input)?;
let (input, z) = int(input)?;
Ok((input, (x, y, z)))
}
pub trait EventField<'a>: Sized + 'a {
fn parse_field(input: &'a str) -> IResult<Self>;
}
impl<'a> EventField<'a> for &'a str {
fn parse_field(input: &'a str) -> IResult<Self> {
if input.starts_with('"') {
quoted(take_while(|c| c != '"'))(input)
let (input, quoted) = skip_matches(input, b'"');
if quoted {
let (value, input) = split_once(input, b'"', 1)?;
Ok((input, value))
} else {
Ok(("", input))
}
@ -384,11 +365,11 @@ impl EventFieldFromStr for SocketAddr {}
impl<'a, T: EventField<'a>> EventField<'a> for (T, T, T) {
fn parse_field(input: &'a str) -> IResult<Self> {
let (input, x) = T::parse_field(input)?;
let (input, _) = tag(" ")(input)?;
let (input, y) = T::parse_field(input)?;
let (input, _) = tag(" ")(input)?;
let (input, z) = T::parse_field(input)?;
let (input, x) = parse_field(input)?;
let input = skip(input, 1)?;
let (input, y) = parse_field(input)?;
let input = skip(input, 1)?;
let (input, z) = parse_field(input)?;
Ok((input, (x, y, z)))
}
}

View file

@ -14,6 +14,7 @@ pub use raw_event::{RawEvent, RawEventType};
use std::collections::BTreeMap;
use std::convert::TryInto;
use std::fmt::Debug;
use std::num::ParseIntError;
pub use tf_log_parser_derive::Event;
use thiserror::Error;
@ -64,6 +65,12 @@ impl From<nom::error::Error<&'_ str>> for Error {
}
}
impl From<ParseIntError> for Error {
fn from(_: ParseIntError) -> Self {
Error::Malformed
}
}
type Result<O, E = Error> = std::result::Result<O, E>;
#[doc(hidden)]