WIP: mx-sanebot: first pass at formalizing a parser

This commit is contained in:
Colin 2023-04-29 03:22:10 +00:00
parent 6f7b7ddb84
commit b0c68308b7
3 changed files with 124 additions and 0 deletions

View File

@ -1,4 +1,5 @@
mod msg_handler;
mod parsing;
use std::env;

View File

@ -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 = <help>
// BT = <bt>
// BT-ADD = <bt-add> MAYBE_ARGS
// MAYBE_ARGS = [SPACE [ARGS]]
// ARGS = ARG MAYBE_ARGS
// ARG = (not SPACE) [ARG]
pub(super) type Request = Then<Bang, Either<Help, Bt>>;
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<tt::Request> 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 {

View File

@ -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<const BYTE: u8>;
/// the two-item sequence of A followed by B.
pub struct Then<A, B>(pub A, pub B);
/// if A parses, then A, else parse B.
pub enum Either<A, B> {
A(A),
B(B),
}
// case-insensitive u8 character.
// type ILit<const BYTE: u8> = Either<Lit<{ BYTE.to_ascii_lowercase() }>, Lit<{ BYTE.to_ascii_uppercase() }>>;
pub type PResult<P, C> = std::result::Result<(C, P), P>;
pub trait Parser: Sized {
fn expect_byte(self, b: Option<u8>) -> PResult<Self, ()>;
fn expect<C: Parse>(self) -> PResult<Self, C>;
// {
// // support backtracking; i.e. don't modify `self` on failed parse
// match C::consume(self.clone()) {
// Ok(res) => res,
// Err(_) => self,
// }
// }
fn parse_all<C: Parse>(self) -> Result<C, ()> {
match self.expect::<Then<C, Eof>>() {
Ok((Then(c, _eof), _p)) => Ok(c),
Err(_p) => Err(()),
}
}
}
pub trait Parse: Sized {
fn consume<P: Parser>(p: P) -> PResult<P, Self>;
}
impl Parse for Eof {
fn consume<P: Parser>(p: P) -> PResult<P, Self> {
let (_, p) = p.expect_byte(None)?;
Ok((Self, p))
}
}
impl<const BYTE: u8> Parse for Lit<BYTE> {
fn consume<P: Parser>(p: P) -> PResult<P, Self> {
let (_, p) = p.expect_byte(Some(BYTE))?;
Ok((Self, p))
}
}
impl<A: Parse, B: Parse> Parse for Then<A, B> {
fn consume<P: Parser>(p: P) -> PResult<P, Self> {
let (a, p) = p.expect()?;
let (b, p) = p.expect()?;
Ok((Self(a, b), p))
}
}
impl<A: Parse, B: Parse> Parse for Either<A, B> {
fn consume<P: Parser>(p: P) -> PResult<P, Self> {
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)
}
}