1// Copyright 2017 Google Inc. All rights reserved.
2//
3// Licensed under either of MIT or Apache License, Version 2.0,
4// at your option.
5//
6// Use of this source code is governed by a MIT-style
7// license that can be found in the LICENSE file or at
8// https://opensource.org/licenses/MIT.
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14// http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22//! Simple recognizer combinators.
23
24// This version is similar to a similar one in the "lang" module of
25// xi-editor, but is stripped down to only the needed combinators.
26
27use std::ops;
28
29pub trait Recognize {
30 fn p(&self, s: &[u8]) -> Option<usize>;
31}
32
33impl<F: Fn(&[u8]) -> Option<usize>> Recognize for F {
34 #[inline(always)]
35 fn p(&self, s: &[u8]) -> Option<usize> {
36 self(s)
37 }
38}
39
40pub struct OneByte<F>(pub F);
41
42impl<F: Fn(u8) -> bool> Recognize for OneByte<F> {
43 #[inline(always)]
44 fn p(&self, s: &[u8]) -> Option<usize> {
45 if s.is_empty() || !self.0(s[0]) {
46 None
47 } else {
48 Some(1)
49 }
50 }
51}
52
53impl Recognize for u8 {
54 #[inline(always)]
55 fn p(&self, s: &[u8]) -> Option<usize> {
56 OneByte(|b: u8| b == *self).p(s)
57 }
58}
59
60/// Use Inclusive(a..b) to indicate an inclusive range. When a...b syntax becomes
61/// stable, we can get rid of this and switch to that.
62pub struct Inclusive<T>(pub T);
63
64impl Recognize for Inclusive<ops::Range<u8>> {
65 #[inline(always)]
66 fn p(&self, s: &[u8]) -> Option<usize> {
67 OneByte(|x: u8| x >= self.0.start && x <= self.0.end).p(s)
68 }
69}
70
71impl<'a> Recognize for &'a [u8] {
72 #[inline(always)]
73 fn p(&self, s: &[u8]) -> Option<usize> {
74 let len: usize = self.len();
75 if s.len() >= len && &s[..len] == *self {
76 Some(len)
77 } else {
78 None
79 }
80 }
81}
82
83impl<'a> Recognize for &'a str {
84 #[inline(always)]
85 fn p(&self, s: &[u8]) -> Option<usize> {
86 self.as_bytes().p(s)
87 }
88}
89
90impl<P1: Recognize, P2: Recognize> Recognize for (P1, P2) {
91 #[inline(always)]
92 fn p(&self, s: &[u8]) -> Option<usize> {
93 self.0.p(s).and_then(|len1: usize|
94 self.1.p(&s[len1..]).map(|len2: usize|
95 len1 + len2))
96 }
97}
98
99/// Choice from two heterogeneous alternatives.
100pub struct Alt<P1, P2>(pub P1, pub P2);
101
102impl<P1: Recognize, P2: Recognize> Recognize for Alt<P1, P2> {
103 #[inline(always)]
104 fn p(&self, s: &[u8]) -> Option<usize> {
105 self.0.p(s).or_else(|| self.1.p(s))
106 }
107}
108
109/// Choice from a homogenous slice of parsers.
110pub struct OneOf<'a, P: 'a>(pub &'a [P]);
111
112impl<'a, P: Recognize> Recognize for OneOf<'a, P> {
113 #[inline]
114 fn p(&self, s: &[u8]) -> Option<usize> {
115 for ref p: &&'a P in self.0 {
116 if let Some(len: usize) = p.p(s) {
117 return Some(len);
118 }
119 }
120 None
121 }
122}
123
124pub struct OneOrMore<P>(pub P);
125
126impl<P: Recognize> Recognize for OneOrMore<P> {
127 #[inline]
128 fn p(&self, s: &[u8]) -> Option<usize> {
129 let mut i: usize = 0;
130 let mut count: i32 = 0;
131 while let Some(len: usize) = self.0.p(&s[i..]) {
132 i += len;
133 count += 1;
134 }
135 if count >= 1 {
136 Some(i)
137 } else {
138 None
139 }
140 }
141}
142
143pub struct ZeroOrMore<P>(pub P);
144
145impl<P: Recognize> Recognize for ZeroOrMore<P> {
146 #[inline]
147 fn p(&self, s: &[u8]) -> Option<usize> {
148 let mut i: usize = 0;
149 while let Some(len: usize) = self.0.p(&s[i..]) {
150 i += len;
151 }
152 Some(i)
153 }
154}
155