From b0c68308b7da7bb17924929076a4f25e450218ff Mon Sep 17 00:00:00 2001 From: Colin Date: Sat, 29 Apr 2023 03:22:10 +0000 Subject: [PATCH] WIP: mx-sanebot: first pass at formalizing a parser --- pkgs/mx-sanebot/src/main.rs | 1 + pkgs/mx-sanebot/src/msg_handler.rs | 44 +++++++++++++++++ pkgs/mx-sanebot/src/parsing.rs | 79 ++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 pkgs/mx-sanebot/src/parsing.rs diff --git a/pkgs/mx-sanebot/src/main.rs b/pkgs/mx-sanebot/src/main.rs index 6e0e4edc..e7ae9d2d 100644 --- a/pkgs/mx-sanebot/src/main.rs +++ b/pkgs/mx-sanebot/src/main.rs @@ -1,4 +1,5 @@ mod msg_handler; +mod parsing; use std::env; diff --git a/pkgs/mx-sanebot/src/msg_handler.rs b/pkgs/mx-sanebot/src/msg_handler.rs index 013c4503..f4f523dc 100644 --- a/pkgs/mx-sanebot/src/msg_handler.rs +++ b/pkgs/mx-sanebot/src/msg_handler.rs @@ -3,6 +3,41 @@ use std::fmt; use std::process; use std::str; +use super::parsing; + + +mod tt { + pub(super) use super::parsing::{ + Either, + Lit, + Then, + }; + + // grammar: + // REQUEST = (HELP | BT | BT-ADD) + // HELP = + // BT = + // BT-ADD = MAYBE_ARGS + // MAYBE_ARGS = [SPACE [ARGS]] + // ARGS = ARG MAYBE_ARGS + // ARG = (not SPACE) [ARG] + pub(super) type Request = Then>; + + pub(super) type Bang = Lit<{ '!' as u8 }>; + + pub(super) type Help = Then< + Lit<{ 'H' as u8 }>, Then< + Lit<{ 'E' as u8 }>, Then< + Lit<{ 'L' as u8 }>, + Lit<{ 'P' as u8 }>, + >>>; + + pub(super) type Bt = Then< + Lit<{ 'B' as u8 }>, + Lit<{ 'T' as u8 }>, + >; +} + pub struct MessageHandler; impl MessageHandler { @@ -31,6 +66,15 @@ enum Request { Bt, } +impl From for Request { + fn from(t: tt::Request) -> Self { + match t { + tt::Then(_bang, tt::Either::A(_help)) => Self::Help, + tt::Then(_bang, tt::Either::B(_bt)) => Self::Bt, + } + } +} + impl Request { fn evaluate(self) -> Response { match self { diff --git a/pkgs/mx-sanebot/src/parsing.rs b/pkgs/mx-sanebot/src/parsing.rs new file mode 100644 index 00000000..4e75f039 --- /dev/null +++ b/pkgs/mx-sanebot/src/parsing.rs @@ -0,0 +1,79 @@ +/// for internal use. +/// parses only if the parser has no more bytes to yield. +struct Eof; + +/// literal byte (character). +pub struct Lit; + +/// the two-item sequence of A followed by B. +pub struct Then(pub A, pub B); + +/// if A parses, then A, else parse B. +pub enum Either { + A(A), + B(B), +} + +// case-insensitive u8 character. +// type ILit = Either, Lit<{ BYTE.to_ascii_uppercase() }>>; + + +pub type PResult = std::result::Result<(C, P), P>; +pub trait Parser: Sized { + fn expect_byte(self, b: Option) -> PResult; + fn expect(self) -> PResult; + // { + // // support backtracking; i.e. don't modify `self` on failed parse + // match C::consume(self.clone()) { + // Ok(res) => res, + // Err(_) => self, + // } + // } + fn parse_all(self) -> Result { + match self.expect::>() { + Ok((Then(c, _eof), _p)) => Ok(c), + Err(_p) => Err(()), + } + } +} + +pub trait Parse: Sized { + fn consume(p: P) -> PResult; +} + +impl Parse for Eof { + fn consume(p: P) -> PResult { + let (_, p) = p.expect_byte(None)?; + Ok((Self, p)) + } +} + +impl Parse for Lit { + fn consume(p: P) -> PResult { + let (_, p) = p.expect_byte(Some(BYTE))?; + Ok((Self, p)) + } +} + +impl Parse for Then { + fn consume(p: P) -> PResult { + let (a, p) = p.expect()?; + let (b, p) = p.expect()?; + Ok((Self(a, b), p)) + } +} + +impl Parse for Either { + fn consume(p: P) -> PResult { + let p = match p.expect() { + Ok((a, p)) => { return Ok((Self::A(a), p)); }, + Err(p) => p, + }; + let p = match p.expect() { + Ok((b, p)) => { return Ok((Self::B(b), p)); }, + Err(p) => p, + }; + Err(p) + } +} +