Skip to content

Instantly share code, notes, and snippets.

@CAD97
Created August 31, 2018 03:57
Show Gist options
  • Save CAD97/dd0320bc835b682205682d9001c0b740 to your computer and use it in GitHub Desktop.
Save CAD97/dd0320bc835b682205682d9001c0b740 to your computer and use it in GitHub Desktop.
A Vision for Pest
// builtin derives collapsed and other cleanup applied manually
mod parser {
use pest::{
error::Error,
iterators::{Pair, Pairs},
Parser,
};
pub struct MyParser;
#[allow(dead_code, non_camel_case_types)]
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub enum Rule {
EOI,
__entry__,
Term,
Sum,
Op,
}
impl Parser<Rule> for MyParser {
fn parse<'i>(rule: Rule, input: &'i str) -> Result<Pairs<'i, Rule>, Error<Rule>> {
mod rules {
use super::Rule;
use pest::{ParseResult, ParserState};
pub mod hidden {
use super::*;
#[inline]
#[allow(dead_code, non_snake_case, unused_variables)]
pub fn skip(
state: Box<ParserState<Rule>>,
) -> ParseResult<Box<ParserState<Rule>>> {
Ok(state)
}
}
pub mod visible {
use super::*;
#[inline]
#[allow(non_snake_case, unused_variables)]
pub fn __entry__(
state: Box<ParserState<Rule>>,
) -> ParseResult<Box<ParserState<Rule>>> {
state.rule(Rule::__entry__, |state| {
state.sequence(|state| {
self::SOI(state)
.and_then(|state| hidden::skip(state))
.and_then(|state| Term(state))
.and_then(|state| hidden::skip(state))
.and_then(|state| EOI(state))
})
})
}
#[inline]
#[allow(non_snake_case, unused_variables)]
pub fn Term(
state: Box<ParserState<Rule>>,
) -> ParseResult<Box<ParserState<Rule>>> {
state.rule(Rule::Term, |state| state.match_range('0'..'9'))
}
#[inline]
#[allow(non_snake_case, unused_variables)]
pub fn Sum(
state: Box<ParserState<Rule>>,
) -> ParseResult<Box<ParserState<Rule>>> {
state.rule(Rule::Sum, |state| {
state.sequence(|state| {
self::Term(state)
.and_then(|state| hidden::skip(state))
.and_then(|state| Op(state))
.and_then(|state| hidden::skip(state))
.and_then(|state| Term(state))
})
})
}
#[inline]
#[allow(non_snake_case, unused_variables)]
pub fn Op(
state: Box<ParserState<Rule>>,
) -> ParseResult<Box<ParserState<Rule>>> {
state.rule(Rule::Op, |state| {
state
.match_string("+")
.or_else(|state| state.match_string("-"))
})
}
#[inline]
#[allow(dead_code, non_snake_case, unused_variables)]
pub fn SOI(
state: Box<ParserState<Rule>>,
) -> ParseResult<Box<ParserState<Rule>>> {
state.start_of_input()
}
#[inline]
#[allow(dead_code, non_snake_case, unused_variables)]
pub fn EOI(
state: Box<ParserState<Rule>>,
) -> ParseResult<Box<ParserState<Rule>>> {
state.rule(Rule::EOI, |state| state.end_of_input())
}
}
pub use self::visible::*;
}
::pest::state(input, |state| match rule {
Rule::__entry__ => rules::__entry__(state),
Rule::Term => rules::Term(state),
Rule::Sum => rules::Sum(state),
Rule::Op => rules::Op(state),
Rule::EOI => rules::EOI(state),
})
}
}
}
mod ast {
use super::parser::{MyParser, Rule};
use pest::{iterators::Pair, Span};
use pest_deconstruct::{FromPest, PestDeconstruct};
struct Term<'i> {
span: Span<'i>,
}
impl<'i> FromPest<'i> for Term<'i> {
type Rule = Rule;
const RULE: Rule = Rule::Term;
fn from_pest(pest: Pair<'i, Rule>) -> Self {
let span = pest.as_span();
let mut it = pest.deconstruct();
Term { span }
}
}
struct Sum<'i> {
span: Span<'i>,
lhs: Term<'i>,
op: Op<'i>,
rhs: Term<'i>,
}
impl<'i> FromPest<'i> for Sum<'i> {
type Rule = Rule;
const RULE: Rule = Rule::Sum;
fn from_pest(pest: Pair<'i, Rule>) -> Self {
let span = pest.as_span();
let mut it = pest.deconstruct();
let lhs = it.next();
let op = it.next();
let rhs = it.next();
Sum { span, lhs, op, rhs }
}
}
struct Op<'i> {
span: Span<'i>,
}
impl<'i> FromPest<'i> for Op<'i> {
type Rule = Rule;
const RULE: Rule = Rule::Op;
fn from_pest(pest: Pair<'i, Rule>) -> Self {
let span = pest.as_span();
let mut it = pest.deconstruct();
Op { span }
}
}
}
mod parser {
#[derive(Parser)]
#[grammar = "grammar.pest"]
pub struct MyParser;
const _GRAMMAR: &str = include_str!("grammar.pest");
}
mod ast {
use super::parser::{MyParser, Rule};
use pest::{iterators::Pair, Span};
use pest_deconstruct::{FromPest, PestDeconstruct};
struct Term<'i> {
span: Span<'i>,
}
impl<'i> FromPest<'i> for Term<'i> {
type Rule = Rule;
const RULE: Rule = Rule::Term;
fn from_pest(pest: Pair<'i, Rule>) -> Self {
let span = pest.as_span();
let mut it = pest.deconstruct();
Term { span }
}
}
struct Sum<'i> {
span: Span<'i>,
lhs: Term<'i>,
op: Op<'i>,
rhs: Term<'i>,
}
impl<'i> FromPest<'i> for Sum<'i> {
type Rule = Rule;
const RULE: Rule = Rule::Sum;
fn from_pest(pest: Pair<'i, Rule>) -> Self {
let span = pest.as_span();
let mut it = pest.deconstruct();
let lhs = it.next();
let op = it.next();
let rhs = it.next();
Sum { span, lhs, op, rhs }
}
}
struct Op<'i> {
span: Span<'i>,
}
impl<'i> FromPest<'i> for Op<'i> {
type Rule = Rule;
const RULE: Rule = Rule::Op;
fn from_pest(pest: Pair<'i, Rule>) -> Self {
let span = pest.as_span();
let mut it = pest.deconstruct();
Op { span }
}
}
}
// manually expanded
// not even close to compileable
use pest::{Span, Parse};
/// # Grammar
///
/// ```pest
/// Term = { '0'..'9' }
/// ```
struct Term<'i> {
span: Span<'i>,
}
impl<'i> Parse<'i, HList![Span<'i>]> for Term<'i> {
fn parse(source: &'i str) -> PartialParseResult<Self> {
::pest::parse(source)
.match_range('0'..'9')
.construct()
}
fn construct(parts: HList![Span<'i>]) -> Self {
Term {
span: parts.0,
}
}
}
/// # Grammar
///
/// ```pest
/// Sum = { Term ~ Op ~ Term }
/// ```
#[derive(Parse)]
struct Sum<'i> {
span: Span<'i>,
lhs: Term<'i>,
op: Op<'i>,
rhs: Term<'i>,
}
impl<'i> Parse<'i, HList![Span<'i>, Term<'i>, Op<'i>, Term<'i>]> for Sum<'i> {
fn parse(source: &'i str) -> PartialParseResult<Self> {
::pest::parse(source)
.match_type::<Term<'i>>()
.match_type::<Op<'i>>()
.match_type::<Term<'i>>()
.construct()
}
fn construct(mut parts: HList![Span<'i>, Term<'i>, Op<'i>, Term<'i>]) -> Self {
Sum {
span: parts.0,
lhs: parts.1,
op: parts.2,
rhs: parts.3,
}
}
}
/// # Grammar
///
/// ```pest
/// Op = { "+" | "-" }
/// ```
#[derive(Parse)]
struct Op<'i> {
span: Span<'i>,
}
impl<'i> Parse<'i, HList![Span<'i>]> for Op<'i> {
fn parse(source: &'i str) -> PartialParseResult<Self> {
::pest::parse(source)
.match_range('0'..'9')
.construct()
}
fn construct(parts: HList![Span<'i>]) -> Self {
Op {
span: parts.0,
}
}
}
// does not compile
use pest::{Span, Parse};
/// # Grammar
///
/// ```pest
/// Term = { '0'..'9' }
/// ```
#[derive(Parse)]
struct Term<'i> {
span: Span<'i>,
}
/// # Grammar
///
/// ```pest
/// Sum = { Term ~ Op ~ Term }
/// ```
#[derive(Parse)]
struct Sum<'i> {
span: Span<'i>,
lhs: Term<'i>,
op: Op<'i>,
rhs: Term<'i>,
}
/// # Grammar
///
/// ```pest
/// Op = { "+" | "-" }
/// ```
#[derive(Parse)]
struct Op<'i> {
span: Span<'i>,
}
__entry__ = { SOI ~ Term ~ EOI }
Term = { '0'..'9' }
Sum = { Term ~ Op ~ Term }
Op = { "+" | "-" }
use pest::{RuleType, iterators::{Pair, Pairs}};
use std::iter::Peekable;
pub trait FromPest<'a> {
type Rule: RuleType;
const RULE: Self::Rule;
fn from_pest(pest: Pair<'a, Self::Rule>) -> Self;
}
pub trait PestDeconstruct<'i> {
type Rule: RuleType;
fn deconstruct(self) -> PestDeconstructor<'i, Self::Rule>;
}
impl<'i, R: RuleType> PestDeconstruct<'i> for Pair<'i, R> {
type Rule = R;
fn deconstruct(self) -> PestDeconstructor<'i, R> {
PestDeconstructor(self.into_inner().peekable())
}
}
#[derive(Clone, Debug)]
pub struct PestDeconstructor<'i, R: RuleType>(Peekable<Pairs<'i, R>>);
impl<'i, R: RuleType> Drop for PestDeconstructor<'i, R> {
fn drop(&mut self) {
assert_eq!(
self.0.next(),
None,
"PestDeconstructor was not fully exhausted"
)
}
}
impl<'i, R: RuleType> PestDeconstructor<'i, R> {
pub fn next<T: FromPest<'i, Rule=R>>(&mut self) -> T {
self.next_opt().unwrap_or_else(|| {
panic!(
"expected {:?} child, got {:?}",
T::RULE,
self.0.next().map(|pair| pair.as_rule())
)
})
}
pub fn next_opt<T: FromPest<'i, Rule=R>>(&mut self) -> Option<T> {
// ugly to work around no nll
if self.0.peek().is_some() {
if self.0.peek().unwrap().as_rule() == T::RULE {
return Some(T::from_pest(self.0.next().unwrap()))
}
}
None
}
pub fn next_or_default<T: FromPest<'i, Rule=R> + Default>(&mut self) -> T {
self.next_opt().unwrap_or_default()
}
pub fn next_many<T: FromPest<'i, Rule=R>>(&mut self) -> Vec<T> {
let mut children = vec![];
while let Some(child) = self.next_opt() {
children.push(child);
}
children
}
pub fn discard(self) {
let _ = self.0;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment