From e362e83b996f9ba914c88630bec28b703c535dcc Mon Sep 17 00:00:00 2001 From: Greg Brown Date: Thu, 19 Nov 2020 13:57:14 +0000 Subject: Switch parsing core to syn --- src/nibble/mod.rs | 926 +++++++---------------------------------------- src/nibble/parse/iter.rs | 149 -------- src/nibble/parse/mod.rs | 61 ---- 3 files changed, 136 insertions(+), 1000 deletions(-) delete mode 100644 src/nibble/parse/iter.rs delete mode 100644 src/nibble/parse/mod.rs (limited to 'src') diff --git a/src/nibble/mod.rs b/src/nibble/mod.rs index 5b51049..478e0cf 100644 --- a/src/nibble/mod.rs +++ b/src/nibble/mod.rs @@ -4,183 +4,85 @@ use crate::ast::{ self, convert::{Context, Convert}, }; - -use self::parse::{ - iter::{Peek, PeekExt, PeekableTakeWhile}, - requires, Parse, ParseError, +use syn::ext::IdentExt; +use syn::punctuated::Punctuated; +use syn::{ + parse::{Parse, ParseStream}, + token, Result, }; -use std::convert::Infallible; -use std::str::FromStr; -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Ident { - name: String, +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct Epsilon { + paren_tok: token::Paren, } -impl Parse for Ident { - type Err = Infallible; - - fn parse>(mut iter: I) -> Result> { - // rustc can't predict type, but rust-analyzer can... - let name: String = PeekableTakeWhile::new(&mut iter, |c| c.is_alphabetic()).collect(); - if name.is_empty() { - if let Some(c) = iter.peek() { - Err(ParseError::InvalidCharacter(*c)) - } else { - Err(ParseError::EndOfFile) - } +impl Parse for Epsilon { + fn parse(input: ParseStream<'_>) -> Result { + let content; + let paren_tok = syn::parenthesized!(content in input); + if content.is_empty() { + Ok(Self { paren_tok }) } else { - Ok(Self { name }) + Err(content.error("expected empty parentheses")) } } } -impl FromStr for Ident { - type Err = ParseError<::Err>; - - fn from_str(s: &str) -> Result { - Ident::parse(s.chars().peekable()) +impl Convert for Epsilon { + fn convert(self, _: &Context) -> ast::Term { + ast::Term::Epsilon } } +type Ident = syn::Ident; + impl Convert for Ident { fn convert(self, context: &Context) -> ast::Term { use ast::Term; + let name = self.to_string(); - if let Some(variable) = context.get(&self.name) { + if let Some(variable) = context.get(&name) { Term::Variable(variable) } else { - Term::Call(self.name, vec![]) + Term::Call(name, vec![]) } } } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Literal { - contents: String, -} - -impl Parse for Literal { - type Err = LiteralError; - - fn parse>(mut iter: I) -> Result> { - fn parse_digit>( - mut iter: I, - radix: u32, - ) -> Result> { - Ok(iter - .next_if(|c| c.is_digit(radix)) - .ok_or(ParseError::EndOfFile)? - .map_err(|i| ParseError::InvalidCharacter(*i.peek().unwrap()))? - .to_digit(radix) - .unwrap()) - } - - /// Parse full character escape. - fn parse_escape>( - mut iter: I, - ) -> Result> { - requires(&mut iter, '\\')?; - match iter.peek().ok_or(ParseError::EndOfFile)? { - '\'' | '\"' | '\\' => Ok(iter.next().unwrap()), - 'n' => { - requires(iter, 'n')?; - Ok('\n') - } - 'r' => { - requires(iter, 'r')?; - Ok('\r') - } - 't' => { - requires(iter, 't')?; - Ok('\t') - } - '0' => { - requires(iter, '0')?; - Ok('\0') - } - 'x' => { - requires(&mut iter, 'x')?; - let upper = parse_digit(&mut iter, 8)?; - let ord = 16 * upper + parse_digit(&mut iter, 16)?; - std::char::from_u32(ord) - .ok_or(ParseError::Other(LiteralError::InvalidCharacterCode(ord))) - } - 'u' => { - requires(&mut iter, 'u')?; - requires(&mut iter, '{')?; - let mut ord = 0; - for _ in 1..=6 { - ord *= 16; - ord += parse_digit(&mut iter, 16)?; - iter.advance_while(|&c| c == '_'); - - if iter.peek() == Some(&'}') { - break; - } - } - requires(&mut iter, '}')?; - - std::char::from_u32(ord) - .ok_or(ParseError::Other(LiteralError::InvalidCharacterCode(ord))) - } - &c => Err(ParseError::InvalidCharacter(c)), - } - } - - requires(&mut iter, '\'')?; - - let mut s = String::new(); - - loop { - match iter.peek().ok_or(ParseError::EndOfFile)? { - '\'' => { - iter.next(); - return if s.is_empty() { - Err(ParseError::Other(LiteralError::EmptyLiteral)) - } else { - Ok(Literal { contents: s }) - }; - } - &c @ '\n' | &c @ '\r' | &c @ '\t' => return Err(ParseError::InvalidCharacter(c)), - '\\' => s.push(parse_escape(&mut iter)?), - _ => s.push(iter.next().unwrap()), - } - } - } -} +type Literal = syn::LitStr; impl Convert for Literal { fn convert(self, _context: &Context) -> ast::Term { - ast::Term::Literal(self.contents) + ast::Term::Literal(self.value()) } } -impl FromStr for Literal { - type Err = ParseError<::Err>; - - fn from_str(s: &str) -> Result { - Literal::parse(s.chars().peekable()) - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum LiteralError { - EmptyLiteral, - InvalidCharacterCode(u32), -} - #[derive(Clone, Debug, Eq, PartialEq)] pub struct Call { name: Ident, - args: Vec, + paren_tok: token::Paren, + args: Punctuated, +} + +impl Parse for Call { + fn parse(input: ParseStream<'_>) -> Result { + let args; + let name = input.call(Ident::parse_any)?; + let paren_tok = syn::parenthesized!(args in input); + let args = args.call(Punctuated::parse_terminated)?; + Ok(Self { + name, + paren_tok, + args, + }) + } } impl Convert for Call { fn convert(self, context: &Context) -> ast::Term { use ast::Term; Term::Call( - self.name.name, + self.name.to_string(), self.args.into_iter().map(|e| e.convert(context)).collect(), ) } @@ -188,31 +90,27 @@ impl Convert for Call { #[derive(Clone, Debug, Eq, PartialEq)] pub struct Fix { + bracket_token: token::Bracket, arg: Ident, + paren_token: token::Paren, expr: Expression, } impl Parse for Fix { - type Err = FixError; - - fn parse>(mut iter: I) -> Result> { - requires(&mut iter, '[')?; - let arg = Ident::parse(&mut iter).map_err(ParseError::map)?; - requires(&mut iter, ']')?; - requires(&mut iter, '(')?; - iter.advance_while(|c| c.is_whitespace()); - let expr = Expression::parse(Box::new(&mut iter as &mut dyn Peek)) - .map_err(ParseError::map)?; - requires(&mut iter, ')')?; - Ok(Self { arg, expr }) - } -} - -impl FromStr for Fix { - type Err = ParseError<::Err>; - - fn from_str(s: &str) -> Result { - Fix::parse(s.chars().peekable()) + fn parse(input: ParseStream<'_>) -> Result { + let arg; + let bracket_token = syn::bracketed!(arg in input); + let arg = arg.call(Ident::parse_any)?; + + let expr; + let paren_token = syn::parenthesized!(expr in input); + let expr = expr.parse()?; + Ok(Self { + bracket_token, + arg, + paren_token, + expr, + }) } } @@ -220,97 +118,86 @@ impl Convert for Fix { fn convert(self, context: &Context) -> ast::Term { use ast::Term; let expr = self.expr; - Term::Fix(Box::new(context.push(self.arg.name, |c| expr.convert(c)))) + Term::Fix(Box::new( + context.push(self.arg.to_string(), |c| expr.convert(c)), + )) } } #[derive(Clone, Debug, Eq, PartialEq)] -pub struct FixError(Box); +pub struct ParenExpression { + paren_tok: token::Paren, + expr: Expression, +} -impl From for FixError { - fn from(other: Infallible) -> Self { - match other {} +impl Parse for ParenExpression { + fn parse(input: ParseStream<'_>) -> Result { + let expr; + let paren_tok = syn::parenthesized!(expr in input); + let expr = expr.parse()?; + Ok(Self { paren_tok, expr }) } } -impl From for FixError { - fn from(other: ExpressionError) -> Self { - Self(Box::new(other)) +impl Convert for ParenExpression { + fn convert(self, context: &Context) -> ast::Term { + self.expr.convert(context) } } #[derive(Clone, Debug, Eq, PartialEq)] pub enum Term { - Epsilon, + Epsilon(Epsilon), Ident(Ident), Literal(Literal), Call(Call), Fix(Fix), - Parens(Expression), + Parens(ParenExpression), } impl Parse for Term { - type Err = TermError; - - fn parse>(mut iter: I) -> Result> { - match iter.peek().ok_or(ParseError::EndOfFile)? { - '(' => { - iter.next(); - - if iter.consume_if_next(&')') { - Ok(Self::Epsilon) - } else { - let expr = Expression::parse(Box::new(&mut iter as &mut dyn Peek)) - .map_err(ParseError::map)?; - requires(iter, ')').map(|_| Self::Parens(expr)) - } + fn parse(input: ParseStream<'_>) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(token::Paren) { + let content; + let paren_tok = syn::parenthesized!(content in input); + if content.is_empty() { + Ok(Self::Epsilon(Epsilon { paren_tok })) + } else { + content + .parse() + .map(|expr| Self::Parens(ParenExpression { paren_tok, expr })) } - '[' => Fix::parse(iter).map(Self::Fix).map_err(ParseError::map), - '\'' => Literal::parse(iter) - .map(Self::Literal) - .map_err(ParseError::map), - c if c.is_alphabetic() => { - let ident = Ident::parse(&mut iter).map_err(ParseError::map)?; - - if iter.consume_if_next(&'(') { - iter.advance_while(|c| c.is_whitespace()); - let mut args = Vec::new(); - loop { - args.push( - Expression::parse(Box::new(&mut iter as &mut dyn Peek)) - .map_err(ParseError::::map) - .map_err(ParseError::map)?, - ); - - if iter.consume_if_next(&')') { - return Ok(Self::Call(Call { name: ident, args })); - } - - requires(&mut iter, ',')?; - iter.advance_while(|c| c.is_whitespace()); - } - } else { - Ok(Self::Ident(ident)) - } + } else if lookahead.peek(Ident::peek_any) { + let name = input.call(Ident::parse_any)?; + if input.peek(token::Paren) { + let args; + let paren_tok = syn::parenthesized!(args in input); + args.call(Punctuated::parse_terminated).map(|args| { + Self::Call(Call { + name, + paren_tok, + args, + }) + }) + } else { + Ok(Self::Ident(name)) } - &c => Err(ParseError::InvalidCharacter(c)), + } else if lookahead.peek(token::Bracket) { + input.parse().map(Self::Fix) + } else if lookahead.peek(syn::LitStr) { + input.parse().map(Self::Literal) + } else { + Err(lookahead.error()) } } } -impl FromStr for Term { - type Err = ParseError<::Err>; - - fn from_str(s: &str) -> Result { - Term::parse(s.chars().peekable()) - } -} - impl Convert for Term { fn convert(self, context: &Context) -> ast::Term { use ast::Term; match self { - Self::Epsilon => Term::Epsilon, + Self::Epsilon(_) => Term::Epsilon, Self::Ident(i) => i.convert(context), Self::Literal(l) => l.convert(context), Self::Call(c) => c.convert(context), @@ -320,81 +207,16 @@ impl Convert for Term { } } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct CallError(Box); - -impl From for CallError { - fn from(other: ExpressionError) -> Self { - Self(Box::new(other)) - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub enum TermError { - Literal(LiteralError), - Call(CallError), - Fix(FixError), - Parens(Box), -} - -impl From for TermError { - fn from(other: Infallible) -> Self { - match other {} - } -} - -impl From for TermError { - fn from(other: ExpressionError) -> Self { - Self::Parens(Box::new(other)) - } -} - -impl From for TermError { - fn from(other: FixError) -> Self { - Self::Fix(other) - } -} - -impl From for TermError { - fn from(other: CallError) -> Self { - Self::Call(other) - } -} - -impl From for TermError { - fn from(other: LiteralError) -> Self { - Self::Literal(other) - } -} - #[derive(Clone, Debug, Eq, PartialEq)] pub struct Cat { - terms: Vec, + terms: Punctuated, } impl Parse for Cat { - type Err = SeqError; - - fn parse>(mut iter: I) -> Result> { - let mut terms = Vec::new(); - terms.push(Term::parse(&mut iter).map_err(ParseError::map)?); - iter.advance_while(|c| c.is_whitespace()); - - while iter.consume_if_next(&'.') { - iter.advance_while(|c| c.is_whitespace()); - terms.push(Term::parse(&mut iter).map_err(ParseError::map)?); - iter.advance_while(|c| c.is_whitespace()); - } - - Ok(Self { terms }) - } -} - -impl FromStr for Cat { - type Err = ParseError<::Err>; - - fn from_str(s: &str) -> Result { - Cat::parse(s.chars().peekable()) + fn parse(input: ParseStream<'_>) -> Result { + Ok(Self { + terms: input.call(Punctuated::parse_separated_nonempty)?, + }) } } @@ -407,41 +229,16 @@ impl Convert for Cat { } } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct SeqError(TermError); - -impl From for SeqError { - fn from(other: TermError) -> Self { - Self(other) - } -} - #[derive(Clone, Debug, Eq, PartialEq)] pub struct Alt { - cats: Vec, + cats: Punctuated, } impl Parse for Alt { - type Err = AltError; - - fn parse>(mut iter: I) -> Result> { - let mut seqs = Vec::new(); - seqs.push(Cat::parse(&mut iter).map_err(ParseError::map)?); - - while iter.consume_if_next(&'|') { - iter.advance_while(|c| c.is_whitespace()); - seqs.push(Cat::parse(&mut iter).map_err(ParseError::map)?); - } - - Ok(Self { cats: seqs }) - } -} - -impl FromStr for Alt { - type Err = ParseError<::Err>; - - fn from_str(s: &str) -> Result { - Alt::parse(s.chars().peekable()) + fn parse(input: ParseStream<'_>) -> Result { + Ok(Self { + cats: input.call(Punctuated::parse_separated_nonempty)?, + }) } } @@ -454,485 +251,34 @@ impl Convert for Alt { } } -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct AltError(SeqError); - -impl From for AltError { - fn from(other: SeqError) -> Self { - Self(other) - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Expression { - alt: Alt, -} - -impl Parse for Expression { - type Err = ExpressionError; - - fn parse>(iter: I) -> Result> { - Ok(Self { - alt: Alt::parse(iter).map_err(ParseError::map)?, - }) - } -} - -impl FromStr for Expression { - type Err = ParseError<::Err>; - - fn from_str(s: &str) -> Result { - Expression::parse(s.chars().peekable()) - } -} - -impl Convert for Expression { - fn convert(self, context: &Context) -> ast::Term { - self.alt.convert(context) - } -} - -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct ExpressionError(AltError); - -impl From for ExpressionError { - fn from(other: AltError) -> Self { - Self(other) - } -} +type Expression = Alt; #[cfg(test)] mod tests { - use super::{ - parse::{Parse, ParseError}, - Alt, Cat, Expression, Fix, Ident, Literal, LiteralError, Term, - }; - use crate::ast::{ - self, - convert::{Context, Convert}, - }; - use std::iter; - - macro_rules! parse_indent { - ($s:literal) => { - assert_eq!( - $s.parse(), - Ok(Ident { - name: $s.to_owned() - }) - ) - }; - } - - #[test] - fn parse_ident_simple() { - parse_indent!("x"); - parse_indent!("variable"); - } - - #[test] - fn parse_indent_unicode() { - parse_indent!("ℕꥇℜ"); - } - - #[test] - fn parse_indent_stops() { - let s = "variable then more text"; - let mut iter = s.chars().peekable(); - assert_eq!( - Ident::parse(&mut iter), - Ok(Ident { - name: "variable".to_owned() - }) - ); - assert_eq!(iter.collect::(), " then more text"); - } - - #[test] - fn parse_indent_not_empty() { - assert_eq!("".parse::(), Err(ParseError::EndOfFile)); - } - - #[test] - fn parse_indent_alphabetic_only() { - assert_eq!( - "123".parse::(), - Err(ParseError::InvalidCharacter('1')) - ); - } - - #[test] - fn convert_ident_variable() { - use ast::Term; - let context = Context::new(); - context.push("x".to_owned(), |c| { - assert_eq!( - Ident { - name: "x".to_owned() - } - .convert(c), - Term::Variable(0) - ); - - c.push("y".to_owned(), |c| { - assert_eq!( - Ident { - name: "y".to_owned() - } - .convert(c), - Term::Variable(0) - ); - - assert_eq!( - Ident { - name: "x".to_owned() - } - .convert(c), - Term::Variable(1) - ); - }) - }) - } - - #[test] - fn convert_ident_call() { - use ast::Term; - let context = Context::new(); - - assert_eq!( - Ident { - name: "call".to_owned() - } - .convert(&context), - Term::Call("call".to_owned(), vec![]) - ); - - assert_eq!( - Ident { - name: "x".to_owned() - } - .convert(&context), - Term::Call("x".to_owned(), vec![]) - ); - - context.push("x".to_owned(), |c| { - assert_eq!( - Ident { - name: "call".to_owned() - } - .convert(c), - Term::Call("call".to_owned(), vec![]) - ); - - assert_eq!( - Ident { - name: "x".to_owned() - } - .convert(c), - Term::Variable(0) - ); - }) - } - - macro_rules! parse_lit { - ($str:literal) => { - assert_eq!( - Literal::parse( - iter::once('\'') - .chain($str.escape_debug()) - .chain(iter::once('\''),) - .peekable() - ), - Ok(Literal { - contents: $str.to_owned() - }), - ) - }; - } - - #[test] - fn parse_literal_basic() { - parse_lit!("hello"); - parse_lit!("this has whitespace"); - } + use syn::parse_str; + use super::{Epsilon, Ident, Literal}; #[test] - fn parse_literal_quotes() { - parse_lit!(r#"quote ' " and backslash \ quoting"#); + fn parse_epsilon() { + assert!(parse_str::("()").is_ok()); + assert!(parse_str::("( )").is_ok()); + assert!(parse_str::("(").is_err()); + assert!(parse_str::(")").is_err()); + assert!(parse_str::("(x)").is_err()); } #[test] - fn parse_literal_ascii_escape() { - parse_lit!("newline \n tab \t carriage return \r"); - - assert_eq!( - r#"'\0'"#.parse(), - Ok(Literal { - contents: "\0".to_owned() - }) - ); + fn parse_ident() { + assert_eq!(parse_str::("x").unwrap().to_string(), "x"); + assert_eq!(parse_str::("x_yz").unwrap().to_string(), "x_yz"); + assert_eq!(parse_str::("a123").unwrap().to_string(), "a123"); + assert_eq!(parse_str::("𝒢𝒢").unwrap().to_string(), "𝒢𝒢"); + assert!(parse_str::("1").is_err()); + assert!(parse_str::("_").is_err()); } #[test] - fn parse_literal_hex_escape() { - assert_eq!( - r#"'\x20'"#.parse(), - Ok(Literal { - contents: " ".to_owned() - }) - ); - } - - #[test] - fn parse_literal_unicode_escape() { - parse_lit!("emoji ❤ escape \u{200c}"); - } - - #[test] - fn parse_literal_stops() { - let mut iter = "'hi there' Not quoted".chars().peekable(); - assert_eq!( - Literal::parse(&mut iter), - Ok(Literal { - contents: "hi there".to_owned() - }) - ); - assert_eq!(iter.collect::(), " Not quoted"); - } - - #[test] - fn parse_literal_needs_quotes() { - assert_eq!( - "hello".parse::(), - Err(ParseError::InvalidCharacter('h')) - ); - assert_eq!("'hello".parse::(), Err(ParseError::EndOfFile)); - } - - #[test] - fn parse_literal_not_empty() { - assert_eq!( - "''".parse::(), - Err(ParseError::Other(LiteralError::EmptyLiteral)) - ); - } - - #[test] - fn parse_literal_no_forbidden_whitespace() { - assert_eq!( - "'\n".parse::(), - Err(ParseError::InvalidCharacter('\n')) - ); - assert_eq!( - "'\r".parse::(), - Err(ParseError::InvalidCharacter('\r')) - ); - assert_eq!( - "'\t".parse::(), - Err(ParseError::InvalidCharacter('\t')) - ); - } - - #[test] - fn parse_literal_invalid_escape() { - assert_eq!( - r#"'\F"#.parse::(), - Err(ParseError::InvalidCharacter('F')) - ); - } - - #[test] - fn parse_literal_bad_hex_escape() { - assert_eq!( - r#"'\xF"#.parse::(), - Err(ParseError::InvalidCharacter('F')) - ); - assert_eq!( - r#"'\x0z"#.parse::(), - Err(ParseError::InvalidCharacter('z')) - ); - } - - #[test] - fn parse_literal_bad_unicode_escape() { - // Negative tests - assert_eq!( - r#"'\u0"#.parse::(), - Err(ParseError::InvalidCharacter('0')) - ); - assert_eq!( - r#"'\u{}"#.parse::(), - Err(ParseError::InvalidCharacter('}')) - ); - assert_eq!( - r#"'\u{z"#.parse::(), - Err(ParseError::InvalidCharacter('z')) - ); - assert_eq!( - r#"'\u{_"#.parse::(), - Err(ParseError::InvalidCharacter('_')) - ); - assert_eq!( - r#"'\u{0___h"#.parse::(), - Err(ParseError::InvalidCharacter('h')) - ); - assert_eq!( - r#"'\u{D800}"#.parse::(), - Err(ParseError::Other(LiteralError::InvalidCharacterCode( - 0xd800 - ))) - ); - assert_eq!( - r#"'\u{0000000"#.parse::(), - Err(ParseError::InvalidCharacter('0')) - ); - } - - #[test] - fn convert_literal() { - use ast::Term; - let context = Context::new(); - assert_eq!( - Literal { - contents: "hello".to_owned() - } - .convert(&context), - Term::Literal("hello".to_owned()) - ); - } - - #[test] - fn parse_fix_basic() { - let expr = Expression { - alt: Alt { - cats: vec![Cat { - terms: vec![Term::Epsilon], - }], - }, - }; - assert_eq!( - "[x](())".parse(), - Ok(Fix { - arg: Ident { - name: "x".to_owned() - }, - expr: expr.clone() - }) - ); - assert_eq!( - "[x](() )".parse(), - Ok(Fix { - arg: Ident { - name: "x".to_owned() - }, - expr: expr.clone() - }) - ); - assert_eq!( - "[x]( ())".parse(), - Ok(Fix { - arg: Ident { - name: "x".to_owned() - }, - expr - }) - ); - } - - #[test] - fn parse_fix_needs_arg() { - assert_eq!( - "x]()".parse::(), - Err(ParseError::InvalidCharacter('x')) - ); - assert_eq!( - "[]()".parse::(), - Err(ParseError::InvalidCharacter(']')) - ); - } - - #[test] - fn parse_fix_needs_parens() { - assert_eq!( - "[x]x".parse::(), - Err(ParseError::InvalidCharacter('x')) - ); - assert_eq!("[x](x".parse::(), Err(ParseError::EndOfFile)) - } - - #[test] - fn parse_fix_stops() { - let mut iter = "[x](()) Not in fix".chars().peekable(); - assert_eq!( - Fix::parse(&mut iter), - Ok(Fix { - arg: Ident { - name: "x".to_owned() - }, - expr: Expression { - alt: Alt { - cats: vec![Cat { - terms: vec![Term::Epsilon], - }], - }, - }, - }) - ); - - assert_eq!(iter.collect::(), " Not in fix"); - } - - #[test] - fn convert_fix_no_var() { - let context = Context::new(); - let expr = Expression { - alt: Alt { - cats: vec![Cat { - terms: vec![Term::Epsilon], - }], - }, - }; - assert_eq!( - Fix { - arg: Ident { - name: "x".to_owned() - }, - expr: expr.clone() - } - .convert(&context), - ast::Term::Fix(Box::new(expr.convert(&context))) - ) - } - - #[test] - fn convert_fix_var() { - use ast::Term; - let context = Context::new(); - let expr = Expression { - alt: Alt { - cats: vec![Cat { - terms: vec![super::Term::Ident(Ident { - name: "x".to_owned(), - })], - }], - }, - }; - - assert_eq!( - Fix { - arg: Ident { - name: "x".to_owned() - }, - expr - } - .convert(&context), - Term::Fix(Box::new(Term::Alt( - Box::new(Term::Cat( - Box::new(Term::Variable(0)), - Box::new(Term::Epsilon) - )), - Box::new(Term::Bottom) - ))) - ) + fn parse_literal() { + assert_eq!(parse_str::(r#""hello""#).unwrap().value(), "hello") } } diff --git a/src/nibble/parse/iter.rs b/src/nibble/parse/iter.rs deleted file mode 100644 index 1c635a3..0000000 --- a/src/nibble/parse/iter.rs +++ /dev/null @@ -1,149 +0,0 @@ -use std::iter::Peekable; - -pub use self::take_while::PeekableTakeWhile; - -/// An iterator with a `peek()` that returns an optional reference to the next -/// element. -/// -/// This trait is an extension of the `Peekable` type in the standard library. -pub trait Peek: Iterator { - /// Returns a reference to the [`next`] value without advancing the iterator. - /// - /// Like [`next`], if there is a value, it is wrapped in [`Some`]. But if - /// iteration is over, [`None`] is returned. - fn peek(&mut self) -> Option<&Self::Item>; -} - -impl Peek for &mut I { - fn peek(&mut self) -> Option<&Self::Item> { - I::peek(self) - } -} - -impl Peek for Box<&mut dyn Peek> { - fn peek(&mut self) -> Option<&Self::Item> { - (**self).peek() - } -} - -impl Peek for Peekable { - fn peek(&mut self) -> Option<&Self::Item> { - Peekable::peek(self) - } -} - -pub trait PeekExt: Peek { - /// Returns the [`next`] value and advance the iterator only if `pred` is true. - fn next_if bool>( - &mut self, - pred: P, - ) -> Option> { - let i = self.peek()?; - - if pred(i) { - Some(Ok(self.next().unwrap())) - } else { - Some(Err(self)) - } - } - - fn next_if_then< - P: FnOnce(&Self::Item) -> bool, - F: FnOnce(Self::Item) -> T, - G: FnOnce(&mut Self) -> T, - T, - >( - &mut self, - pred: P, - matched: F, - unmatched: G, - ) -> Option { - let i = self.peek()?; - - if pred(i) { - Some(matched(self.next().unwrap())) - } else { - Some(unmatched(self)) - } - } - - /// Advances the iterator only if the [`next`] item equals `val`. - fn next_if_eq(&mut self, val: &T) -> Option> - where - Self::Item: PartialEq, - { - self.next_if(|i| PartialEq::eq(i, val)) - .map(|r| r.map(|_| ()).map_err(|i| i.peek().unwrap())) - } - - fn consume_if_next(&mut self, val: &T) -> bool - where - Self::Item: PartialEq, - { - self.next_if_eq(val).map(|r| r.is_ok()).unwrap_or(false) - } - - /// Advance the iterator until `pred` is false, or `peek` returns [`None`]. - fn advance_while bool>(&mut self, mut pred: P) { - while self.next_if(&mut pred).map(|r| r.is_ok()).unwrap_or(false) { - // Condition already loops for us - } - } -} - -impl PeekExt for I {} - -mod take_while { - use super::Peek; - - #[derive(Clone, Debug, Eq, PartialEq)] - pub struct PeekableTakeWhile { - inner: I, - predicate: Option, - } - - impl PeekableTakeWhile - where - I: Peek, - F: FnMut(&::Item) -> bool, - { - pub fn new(iter: I, predicate: F) -> Self { - Self { - inner: iter, - predicate: Some(predicate), - } - } - } - - impl Iterator for PeekableTakeWhile - where - I: Peek, - F: FnMut(&::Item) -> bool, - { - type Item = ::Item; - - fn next(&mut self) -> Option { - // Can't inline because of lifetimes - let inner = &mut self.inner; - if self.predicate.as_mut().and_then(|p| inner.peek().map(p))? { - self.inner.next() - } else { - self.predicate = None; - None - } - } - } - - impl Peek for PeekableTakeWhile - where - I: Peek, - F: FnMut(&::Item) -> bool, - { - fn peek(&mut self) -> Option<&Self::Item> { - let inner = &mut self.inner; - self.predicate - .as_mut() - .and_then(move |p| inner.peek().filter(|i| p(i))) - } - } -} diff --git a/src/nibble/parse/mod.rs b/src/nibble/parse/mod.rs deleted file mode 100644 index 13c4c0e..0000000 --- a/src/nibble/parse/mod.rs +++ /dev/null @@ -1,61 +0,0 @@ -pub mod iter; - -use self::iter::Peek; -use self::iter::PeekExt; - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum ParseError { - EndOfFile, - InvalidCharacter(char), - Other(E), -} - -impl ParseError { - pub fn map>(other: ParseError) -> Self { - match other { - ParseError::EndOfFile => Self::EndOfFile, - ParseError::InvalidCharacter(c) => Self::InvalidCharacter(c), - ParseError::Other(t) => Self::Other(t.into()) - } - } -} - -/// Parse a value from an iterator. -pub trait Parse: Sized { - /// The associated error that can be returned from parsing. - type Err; - /// Parses `iter` to return a value of this type. - /// - /// This method may not reach the end of `iter`. If so, it must not consume any - /// characters in the stream that do not form part of the value. - /// - /// If parsing succeeds, return the value inside [`Ok`]. Otherwise, when the - /// iterator is ill-formatted, return an error specific to the inside [`Err`]. - /// The error type is specific to the implementation of the trait. - fn parse>(iter: I) -> Result>; -} - -/// Consumes the next item in `iter` if it equals `c`. Otherwise, returns an -/// appropriate error without advancing the iterator. -/// -/// # Examples -/// Basic usage -/// ``` -/// use chomp::nibble::parse::{requires, Parse, ParseError}; -/// use std::convert::Infallible; -/// -/// let s = "hello".to_owned(); -/// let mut iter = s.chars().peekable(); -/// assert_eq!(requires::<_, Infallible>(&mut iter, 'h'), Ok(())); -/// assert_eq!(requires::<_, Infallible>(&mut iter, 'e'), Ok(())); -/// assert_eq!(requires::<_, Infallible>(&mut iter, 'z'), Err(ParseError::InvalidCharacter('l'))); -/// assert_eq!(iter.next(), Some('l')); -/// assert_eq!(iter.next(), Some('l')); -/// assert_eq!(iter.next(), Some('o')); -/// assert_eq!(requires::<_, Infallible>(iter, '!'), Err(ParseError::EndOfFile)); -/// ``` -pub fn requires, E>(mut iter: I, c: char) -> Result<(), ParseError> { - iter.next_if_eq(&c) - .ok_or(ParseError::EndOfFile)? - .map_err(|&c| ParseError::InvalidCharacter(c)) -} -- cgit v1.2.3