WIP: mx-sanebot: first pass at formalizing a parser
This commit is contained in:
parent
6f7b7ddb84
commit
b0c68308b7
|
@ -1,4 +1,5 @@
|
||||||
mod msg_handler;
|
mod msg_handler;
|
||||||
|
mod parsing;
|
||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,41 @@ use std::fmt;
|
||||||
use std::process;
|
use std::process;
|
||||||
use std::str;
|
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;
|
pub struct MessageHandler;
|
||||||
|
|
||||||
impl MessageHandler {
|
impl MessageHandler {
|
||||||
|
@ -31,6 +66,15 @@ enum Request {
|
||||||
Bt,
|
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 {
|
impl Request {
|
||||||
fn evaluate(self) -> Response {
|
fn evaluate(self) -> Response {
|
||||||
match self {
|
match self {
|
||||||
|
|
79
pkgs/mx-sanebot/src/parsing.rs
Normal file
79
pkgs/mx-sanebot/src/parsing.rs
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user