|
| 1 | +use peg::{Parse, ParseElem, RuleResult}; |
| 2 | + |
| 3 | +/// The default implementation of the parsing traits for `[T]` expects `T` to be |
| 4 | +/// `Copy`, as in the `[u8]` or simple enum cases. This wrapper exposes the |
| 5 | +/// elements by `&T` reference, which is `Copy`. |
| 6 | +pub struct SliceByRef<'a, T>(pub &'a [T]); |
| 7 | + |
| 8 | +impl<'a , T> Parse for SliceByRef<'a, T> { |
| 9 | + type PositionRepr = usize; |
| 10 | + fn start(&self) -> usize { |
| 11 | + 0 |
| 12 | + } |
| 13 | + |
| 14 | + fn is_eof(&self, pos: usize) -> bool { |
| 15 | + pos >= self.0.len() |
| 16 | + } |
| 17 | + |
| 18 | + fn position_repr(&self, pos: usize) -> usize { |
| 19 | + pos |
| 20 | + } |
| 21 | +} |
| 22 | + |
| 23 | +impl<'a, T: 'a> ParseElem<'a> for SliceByRef<'a, T> { |
| 24 | + type Element = &'a T; |
| 25 | + |
| 26 | + fn parse_elem(&'a self, pos: usize) -> RuleResult<&'a T> { |
| 27 | + match self.0[pos..].first() { |
| 28 | + Some(c) => RuleResult::Matched(pos + 1, c), |
| 29 | + None => RuleResult::Failed, |
| 30 | + } |
| 31 | + } |
| 32 | +} |
| 33 | + |
| 34 | +#[derive(PartialEq)] |
| 35 | +pub enum TokenType { |
| 36 | + Word, |
| 37 | + Number, |
| 38 | +} |
| 39 | + |
| 40 | +pub struct Token { |
| 41 | + pub token_type: TokenType, |
| 42 | + pub term: String, |
| 43 | +} |
| 44 | + |
| 45 | +peg::parser!{ |
| 46 | + grammar tokenparser<'a>() for SliceByRef<'a, Token> { |
| 47 | + // The [] syntax works just like (and expands into) an arm of a match |
| 48 | + // in regular Rust, so you can use a pattern that matches one field |
| 49 | + // and ignores the rest |
| 50 | + pub rule word_by_field() = [ Token { token_type: TokenType::Word, .. } ] |
| 51 | + |
| 52 | + // Or capture the token as a variable and then test it with an if guard. |
| 53 | + pub rule word_by_eq() = [t if t.token_type == TokenType::Word] |
| 54 | + |
| 55 | + // You could wrap this in a rule that accepts the TokenType as an argument |
| 56 | + rule tok(ty: TokenType) -> &'input Token = [t if t.token_type == ty] |
| 57 | + pub rule number() = tok(TokenType::Number) |
| 58 | + } |
| 59 | +} |
| 60 | + |
| 61 | +fn main() { |
| 62 | + let word_tok = vec![ |
| 63 | + Token { token_type: TokenType::Word, term: "foo".into() } |
| 64 | + ]; |
| 65 | + |
| 66 | + let number_tok = vec![ |
| 67 | + Token { token_type: TokenType::Number, term: "123".into() } |
| 68 | + ]; |
| 69 | + |
| 70 | + assert!(tokenparser::word_by_field(&SliceByRef(&word_tok[..])).is_ok()); |
| 71 | + assert!(tokenparser::word_by_eq(&SliceByRef(&word_tok[..])).is_ok()); |
| 72 | + assert!(tokenparser::number(&SliceByRef(&number_tok[..])).is_ok()); |
| 73 | +} |
0 commit comments