Created
August 31, 2018 03:57
-
-
Save CAD97/dd0320bc835b682205682d9001c0b740 to your computer and use it in GitHub Desktop.
A Vision for Pest
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 } | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 } | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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, | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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>, | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
__entry__ = { SOI ~ Term ~ EOI } | |
Term = { '0'..'9' } | |
Sum = { Term ~ Op ~ Term } | |
Op = { "+" | "-" } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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