--- a/rust/hedgewars-engine-messages/src/parser.rs Fri Oct 06 20:34:51 2023 +0200
+++ b/rust/hedgewars-engine-messages/src/parser.rs Fri Oct 06 22:37:44 2023 +0200
@@ -1,126 +1,169 @@
+use std::str;
+
+use nom::branch::alt;
+use nom::bytes::streaming::*;
+use nom::combinator::*;
+use nom::error::{ErrorKind, ParseError};
+use nom::multi::*;
+use nom::number::streaming::*;
+use nom::sequence::{pair, preceded, terminated, tuple};
+use nom::{Err, IResult, Parser};
+
use crate::messages::{
ConfigEngineMessage::*, EngineMessage::*, KeystrokeAction::*, SyncedEngineMessage::*,
UnorderedEngineMessage::*, *,
};
-use nom::{Err::Error, *};
-use std::str;
-macro_rules! eof_slice (
- ($i:expr,) => (
- {
- if ($i).input_len() == 0 {
- Ok(($i, $i))
- } else {
- Err(Error(error_position!($i, ErrorKind::Eof::<u32>)))
- }
+fn eof_slice<I>(i: I) -> IResult<I, I>
+where
+ I: nom::InputLength + Clone,
+{
+ if i.input_len() == 0 {
+ Ok((i.clone(), i))
+ } else {
+ Err(Err::Error(nom::error::Error::new(i, ErrorKind::Eof)))
}
- );
-);
+}
+fn unrecognized_message(input: &[u8]) -> IResult<&[u8], EngineMessage> {
+ map(rest, |i: &[u8]| Unknown(i.to_owned()))(input)
+}
-named!(unrecognized_message<&[u8], EngineMessage>,
- do_parse!(rest >> (Unknown))
-);
+fn string_tail(input: &[u8]) -> IResult<&[u8], String> {
+ map_res(rest, str::from_utf8)(input).map(|(i, s)| (i, s.to_owned()))
+}
-named!(string_tail<&[u8], String>, map!(map_res!(rest, str::from_utf8), String::from));
-
-named!(length_without_timestamp<&[u8], usize>,
- map_opt!(rest_len, |l| if l > 2 { Some(l - 2) } else { None } )
-);
+fn length_without_timestamp(input: &[u8]) -> IResult<&[u8], usize> {
+ map_opt(rest_len, |l| if l > 2 { Some(l - 2) } else { None })(input)
+}
-named!(synced_message<&[u8], SyncedEngineMessage>, alt!(
- do_parse!(tag!("L") >> (Left(Press)))
- | do_parse!(tag!("l") >> ( Left(Release) ))
- | do_parse!(tag!("R") >> ( Right(Press) ))
- | do_parse!(tag!("r") >> ( Right(Release) ))
- | do_parse!(tag!("U") >> ( Up(Press) ))
- | do_parse!(tag!("u") >> ( Up(Release) ))
- | do_parse!(tag!("D") >> ( Down(Press) ))
- | do_parse!(tag!("d") >> ( Down(Release) ))
- | do_parse!(tag!("Z") >> ( Precise(Press) ))
- | do_parse!(tag!("z") >> ( Precise(Release) ))
- | do_parse!(tag!("A") >> ( Attack(Press) ))
- | do_parse!(tag!("a") >> ( Attack(Release) ))
- | do_parse!(tag!("N") >> ( NextTurn ))
- | do_parse!(tag!("j") >> ( LongJump ))
- | do_parse!(tag!("J") >> ( HighJump ))
- | do_parse!(tag!("S") >> ( Switch ))
- | do_parse!(tag!(",") >> ( Skip ))
- | do_parse!(tag!("1") >> ( Timer(1) ))
- | do_parse!(tag!("2") >> ( Timer(2) ))
- | do_parse!(tag!("3") >> ( Timer(3) ))
- | do_parse!(tag!("4") >> ( Timer(4) ))
- | do_parse!(tag!("5") >> ( Timer(5) ))
- | do_parse!(tag!("p") >> x: be_i24 >> y: be_i24 >> ( Put(x, y) ))
- | do_parse!(tag!("P") >> x: be_i24 >> y: be_i24 >> ( CursorMove(x, y) ))
- | do_parse!(tag!("f") >> s: string_tail >> ( SyncedEngineMessage::TeamControlLost(s) ))
- | do_parse!(tag!("g") >> s: string_tail >> ( SyncedEngineMessage::TeamControlGained(s) ))
- | do_parse!(tag!("t") >> t: be_u8 >> ( Taunt(t) ))
- | do_parse!(tag!("w") >> w: be_u8 >> ( SetWeapon(w) ))
- | do_parse!(tag!("~") >> s: be_u8 >> ( Slot(s) ))
- | do_parse!(tag!("+") >> ( Heartbeat ))
-));
+fn synced_message(input: &[u8]) -> IResult<&[u8], SyncedEngineMessage> {
+ alt((
+ alt((
+ map(tag(b"L"), |_| Left(Press)),
+ map(tag(b"l"), |_| Left(Release)),
+ map(tag(b"R"), |_| Right(Press)),
+ map(tag(b"r"), |_| Right(Release)),
+ map(tag(b"U"), |_| Up(Press)),
+ map(tag(b"u"), |_| Up(Release)),
+ map(tag(b"D"), |_| Down(Press)),
+ map(tag(b"d"), |_| Down(Release)),
+ map(tag(b"Z"), |_| Precise(Press)),
+ map(tag(b"z"), |_| Precise(Release)),
+ map(tag(b"A"), |_| Attack(Press)),
+ map(tag(b"a"), |_| Attack(Release)),
+ map(tag(b"N"), |_| NextTurn),
+ map(tag(b"j"), |_| LongJump),
+ map(tag(b"J"), |_| HighJump),
+ map(tag(b"S"), |_| Switch),
+ )),
+ alt((
+ map(tag(b","), |_| Skip),
+ map(tag(b"1"), |_| Timer(1)),
+ map(tag(b"2"), |_| Timer(2)),
+ map(tag(b"3"), |_| Timer(3)),
+ map(tag(b"4"), |_| Timer(4)),
+ map(tag(b"5"), |_| Timer(5)),
+ map(tuple((tag(b"p"), be_i24, be_i24)), |(_, x, y)| Put(x, y)),
+ map(tuple((tag(b"P"), be_i24, be_i24)), |(_, x, y)| {
+ CursorMove(x, y)
+ }),
+ map(preceded(tag(b"f"), string_tail), TeamControlLost),
+ map(preceded(tag(b"g"), string_tail), TeamControlGained),
+ map(preceded(tag(b"t"), be_u8), Taunt),
+ map(preceded(tag(b"w"), be_u8), SetWeapon),
+ map(preceded(tag(b"~"), be_u8), Slot),
+ map(tag(b"+"), |_| Heartbeat),
+ )),
+ ))(input)
+}
-named!(unsynced_message<&[u8], UnsyncedEngineMessage>, alt!(
- do_parse!(tag!("F") >> s: string_tail >> ( UnsyncedEngineMessage::TeamControlLost(s) ))
- | do_parse!(tag!("G") >> s: string_tail >> ( UnsyncedEngineMessage::TeamControlGained(s) ))
- | do_parse!(tag!("h") >> s: string_tail >> ( UnsyncedEngineMessage::HogSay(s) ))
- | do_parse!(tag!("s") >> s: string_tail >> ( UnsyncedEngineMessage::ChatMessage(s)) )
- | do_parse!(tag!("b") >> s: string_tail >> ( UnsyncedEngineMessage::TeamMessage(s)) ) // TODO: wtf is the format
-));
+fn unsynced_message(input: &[u8]) -> IResult<&[u8], UnsyncedEngineMessage> {
+ alt((
+ map(
+ preceded(tag(b"F"), string_tail),
+ UnsyncedEngineMessage::TeamControlLost,
+ ),
+ map(
+ preceded(tag(b"G"), string_tail),
+ UnsyncedEngineMessage::TeamControlGained,
+ ),
+ map(
+ preceded(tag(b"h"), string_tail),
+ UnsyncedEngineMessage::HogSay,
+ ),
+ map(
+ preceded(tag(b"s"), string_tail),
+ UnsyncedEngineMessage::ChatMessage,
+ ),
+ map(
+ preceded(tag(b"b"), string_tail),
+ UnsyncedEngineMessage::TeamMessage,
+ ),
+ ))(input)
+}
-named!(unordered_message<&[u8], UnorderedEngineMessage>, alt!(
- do_parse!(tag!("?") >> ( Ping ))
- | do_parse!(tag!("!") >> ( Pong ))
- | do_parse!(tag!("E") >> s: string_tail >> ( UnorderedEngineMessage::Error(s)) )
- | do_parse!(tag!("W") >> s: string_tail >> ( Warning(s)) )
- | do_parse!(tag!("M") >> s: string_tail >> ( GameSetupChecksum(s)) )
- | do_parse!(tag!("o") >> ( StopSyncing ))
- | do_parse!(tag!("I") >> ( PauseToggled ))
-));
-
-named!(config_message<&[u8], ConfigEngineMessage>, alt!(
- do_parse!(tag!("C") >> (ConfigRequest))
- | do_parse!(tag!("eseed ") >> s: string_tail >> ( SetSeed(s)) )
- | do_parse!(tag!("e$feature_size ") >> s: string_tail >> ( SetFeatureSize(s.parse::<u8>().unwrap())) )
-));
-
-named!(timestamped_message<&[u8], (SyncedEngineMessage, u16)>,
- do_parse!(msg: length_value!(length_without_timestamp, terminated!(synced_message, eof_slice!()))
- >> timestamp: be_u16
- >> ((msg, timestamp))
- )
-);
+fn unordered_message(input: &[u8]) -> IResult<&[u8], UnorderedEngineMessage> {
+ alt((
+ map(tag(b"?"), |_| Ping),
+ map(tag(b"!"), |_| Pong),
+ map(preceded(tag(b"E"), string_tail), Error),
+ map(preceded(tag(b"W"), string_tail), Warning),
+ map(preceded(tag(b"M"), string_tail), GameSetupChecksum),
+ map(tag(b"o"), |_| StopSyncing),
+ map(tag(b"I"), |_| PauseToggled),
+ ))(input)
+}
-named!(unwrapped_message<&[u8], EngineMessage>,
- alt!(
- map!(timestamped_message, |(m, t)| Synced(m, t as u32))
- | do_parse!(tag!("#") >> (Synced(TimeWrap, 65535)))
- | map!(unordered_message, |m| Unordered(m))
- | map!(unsynced_message, |m| Unsynced(m))
- | map!(config_message, |m| Config(m))
- | unrecognized_message
-));
+fn config_message(input: &[u8]) -> IResult<&[u8], ConfigEngineMessage> {
+ alt((
+ map(tag(b"C"), |_| ConfigRequest),
+ map(preceded(tag(b"eseed "), string_tail), SetSeed),
+ map(preceded(tag(b"e$feature_size "), string_tail), |s| {
+ SetFeatureSize(s.parse().unwrap_or_default())
+ }),
+ ))(input)
+}
-named!(length_specifier<&[u8], u16>, alt!(
- verify!(map!(take!(1), |a : &[u8]| a[0] as u16), |l| l < 64)
- | map!(take!(2), |a| (a[0] as u16 - 64) * 256 + a[1] as u16 + 64)
- )
-);
-
-named!(empty_message<&[u8], EngineMessage>,
- do_parse!(tag!("\0") >> (Empty))
-);
+fn timestamped_message(input: &[u8]) -> IResult<&[u8], (SyncedEngineMessage, u16)> {
+ terminated(pair(synced_message, be_u16), eof_slice)(input)
+}
+fn unwrapped_message(input: &[u8]) -> IResult<&[u8], EngineMessage> {
+ alt((
+ map(timestamped_message, |(m, t)| {
+ EngineMessage::Synced(m, t as u32)
+ }),
+ map(tag(b"#"), |_| Synced(TimeWrap, 65535u32)),
+ map(unordered_message, Unordered),
+ map(unsynced_message, Unsynced),
+ map(config_message, Config),
+ unrecognized_message,
+ ))(input)
+}
-named!(non_empty_message<&[u8], EngineMessage>,
- length_value!(length_specifier, terminated!(unwrapped_message, eof_slice!())));
+fn length_specifier(input: &[u8]) -> IResult<&[u8], u16> {
+ alt((
+ verify(map(take(1usize), |a: &[u8]| a[0] as u16), |&l| l < 64),
+ map(take(2usize), |a: &[u8]| {
+ (a[0] as u16 - 64) * 256 + a[1] as u16 + 64
+ }),
+ ))(input)
+}
-named!(message<&[u8], EngineMessage>, alt!(
- empty_message
- | non_empty_message
- )
-);
+fn empty_message(input: &[u8]) -> IResult<&[u8], EngineMessage> {
+ map(tag(b"\0"), |_| Empty)(input)
+}
+
+fn non_empty_message(input: &[u8]) -> IResult<&[u8], EngineMessage> {
+ map_parser(length_data(length_specifier), unwrapped_message)(input)
+}
-named!(pub extract_messages<&[u8], Vec<EngineMessage> >, many0!(complete!(message)));
+fn message(input: &[u8]) -> IResult<&[u8], EngineMessage> {
+ alt((empty_message, non_empty_message))(input)
+}
+
+pub fn extract_messages(input: &[u8]) -> IResult<&[u8], Vec<EngineMessage>> {
+ many0(complete(message))(input)
+}
pub fn extract_message(buf: &[u8]) -> Option<(usize, EngineMessage)> {
let parse_result = message(buf);
@@ -178,10 +221,13 @@
#[test]
fn parse_incorrect_messages() {
assert_eq!(message(b"\x00"), Ok((&b""[..], Empty)));
- assert_eq!(message(b"\x01\x00"), Ok((&b""[..], Unknown)));
+ assert_eq!(message(b"\x01\x00"), Ok((&b""[..], Unknown(vec![0]))));
// garbage after correct message
- assert_eq!(message(b"\x04La\x01\x02"), Ok((&b""[..], Unknown)));
+ assert_eq!(
+ message(b"\x04La\x01\x02"),
+ Ok((&b""[..], Unknown(vec![76, 97, 1, 2])))
+ );
}
#[test]
@@ -194,6 +240,9 @@
assert_eq!(string_tail(b"abc"), Ok((&b""[..], String::from("abc"))));
assert_eq!(extract_message(b"\x02#"), None);
+
+ assert_eq!(synced_message(b"L"), Ok((&b""[..], Left(Press))));
+
assert_eq!(
extract_message(b"\x01#"),
Some((2, Synced(TimeWrap, 65535)))