unsynn/predicates.rs
1//! Parse predicates for compile-time parser control.
2//!
3//! This module provides zero-cost abstractions for controlling parser behavior at compile time.
4//! All predicate types are zero-sized and have no runtime overhead.
5//!
6//! # Base Values
7//!
8//! - [`Enable`] - Always succeeds without consuming tokens
9//! - [`Disable`] - Always fails without consuming tokens
10//! - [`TokensRemain`] - Succeeds only when tokens remain in the stream
11//!
12//! # Logical Operators
13//!
14//! - [`AllOf`] - Logical AND (all must succeed, 2-4 operands)
15//! - [`AnyOf`] - Logical OR (at least one must succeed, 2-4 operands)
16//! - [`OneOf`] - Logical XOR (exactly one must succeed, 2-4 operands)
17//! - [`Not`] - Logical NOT (succeeds if inner fails)
18//!
19//! # Example
20//!
21//! The `predicateflag` macro creates newtype wrappers that can implement custom traits for
22//! compile-time context validation:
23//!
24//! ```rust
25//! use unsynn::*;
26//! use unsynn::predicates::*;
27//!
28//!
29//! unsynn! {
30//! // Define a custom context predicate marker-trait
31//! trait ExpressionContext: PredicateOp;
32//!
33//! // Creates newtype implementing ExpressionContext
34//! predicateflag InExpression = Enable for ExpressionContext;
35//!
36//! // Only accepts ExpressionContext predicates
37//! struct StructLiteral<P: ExpressionContext> {
38//! _guard: P,
39//! name: Ident,
40//! }
41//! }
42//! ```
43//!
44//! See the **[Parse Predicates]** chapter in the Cookbook for detailed usage examples.
45//!
46//! [Parse Predicates]: https://docs.rs/unsynn/latest/unsynn/#parse-predicates
47
48use crate::{
49 Cons, Either, Error, Except, Expect, Parse, Parser, Result, ToTokens, TokenIter, TokenStream,
50 TokenTree,
51};
52use std::any::TypeId;
53use std::marker::PhantomData;
54
55/// Marker trait for compile-time parser predicates.
56///
57/// All predicate types are zero-sized with no runtime cost.
58/// The `'static` bound is required for type identity checking with [`PredicateCmp`].
59pub trait PredicateOp: Parser + ToTokens + Clone + 'static {}
60
61// =============================================================================
62// BASE VALUES
63// =============================================================================
64
65/// Always succeeds without consuming tokens.
66///
67/// # Examples
68///
69/// ```rust
70/// use unsynn::*;
71///
72/// let mut tokens = "foo bar".to_token_iter();
73/// let result = Enable::parse(&mut tokens);
74/// assert!(result.is_ok());
75/// assert_eq!(tokens.clone().count(), 2); // No tokens consumed
76/// ```
77#[derive(Debug, Clone, Default)]
78pub struct Enable;
79
80impl Parser for Enable {
81 #[inline]
82 #[mutants::skip] // Mutant Ok(Default::default()) is equivalent since Enable: Default
83 fn parser(_tokens: &mut TokenIter) -> Result<Self> {
84 Ok(Enable)
85 }
86}
87
88impl ToTokens for Enable {
89 #[inline]
90 fn to_tokens(&self, _tokens: &mut TokenStream) {}
91}
92
93impl PredicateOp for Enable {}
94
95/// Always fails without consuming tokens.
96///
97/// Note: Does not implement [`Default`].
98///
99/// # Examples
100///
101/// ```rust
102/// use unsynn::*;
103///
104/// let mut tokens = "foo bar".to_token_iter();
105/// let result = Disable::parse(&mut tokens);
106/// assert!(result.is_err());
107/// ```
108#[derive(Debug, Clone)]
109pub struct Disable;
110
111impl Parser for Disable {
112 #[inline]
113 fn parser(tokens: &mut TokenIter) -> Result<Self> {
114 Error::unexpected_token(None, tokens)
115 }
116}
117
118impl ToTokens for Disable {
119 #[inline]
120 fn to_tokens(&self, _tokens: &mut TokenStream) {}
121}
122
123impl PredicateOp for Disable {}
124
125/// Succeeds only when tokens remain in the stream.
126///
127/// # Examples
128///
129/// ```rust
130/// use unsynn::*;
131/// use unsynn::predicates::*;
132///
133/// let mut tokens = "foo".to_token_iter();
134/// assert!(TokensRemain::parse(&mut tokens).is_ok());
135///
136/// let mut empty = "".to_token_iter();
137/// assert!(TokensRemain::parse(&mut empty).is_err());
138/// ```
139#[derive(Debug, Clone)]
140pub struct TokensRemain;
141
142impl Parser for TokensRemain {
143 #[inline]
144 fn parser(tokens: &mut TokenIter) -> Result<Self> {
145 Expect::<TokenTree>::parse(tokens)?;
146 Ok(TokensRemain)
147 }
148}
149
150impl ToTokens for TokensRemain {
151 #[inline]
152 fn to_tokens(&self, _tokens: &mut TokenStream) {}
153}
154
155impl Default for TokensRemain {
156 #[inline]
157 fn default() -> Self {
158 TokensRemain
159 }
160}
161
162impl PredicateOp for TokensRemain {}
163
164// =============================================================================
165// LOGICAL NOT
166// =============================================================================
167
168/// Logical NOT: succeeds if inner predicate fails.
169///
170/// # Examples
171///
172/// ```rust
173/// use unsynn::*;
174///
175/// let mut tokens = "".to_token_iter();
176/// assert!(Not::<Disable>::parse(&mut tokens).is_ok());
177/// assert!(Not::<Enable>::parse(&mut tokens).is_err());
178/// ```
179#[derive(Debug, Clone)]
180pub struct Not<T: PredicateOp>(Except<T>);
181
182impl<T: PredicateOp> Parser for Not<T> {
183 #[inline]
184 fn parser(tokens: &mut TokenIter) -> Result<Self> {
185 Except::<T>::parse(tokens).map(Not)
186 }
187}
188
189impl<T: PredicateOp> ToTokens for Not<T> {
190 #[inline]
191 #[mutants::skip] // Inner type produces no tokens, so mutation to () is equivalent
192 fn to_tokens(&self, tokens: &mut TokenStream) {
193 self.0.to_tokens(tokens);
194 }
195}
196
197impl<T: PredicateOp> Default for Not<T>
198where
199 Except<T>: Default,
200{
201 #[inline]
202 fn default() -> Self {
203 Not(Except::default())
204 }
205}
206
207impl<T: PredicateOp> PredicateOp for Not<T> {}
208
209// =============================================================================
210// LOGICAL AND (2-4 operands)
211// =============================================================================
212
213/// Logical AND: 2-4 predicates must all succeed.
214///
215/// C and D default to [`Enable`] (always succeeds).
216///
217/// # Examples
218///
219/// ```rust
220/// use unsynn::*;
221///
222/// let mut tokens = "".to_token_iter();
223/// // Two predicates
224/// assert!(AllOf::<Enable, Enable>::parse(&mut tokens).is_ok());
225/// assert!(AllOf::<Enable, Disable>::parse(&mut tokens).is_err());
226/// // Four predicates
227/// assert!(AllOf::<Enable, Enable, Enable, Enable>::parse(&mut tokens).is_ok());
228/// ```
229#[derive(Debug, Clone)]
230pub struct AllOf<
231 A: PredicateOp,
232 B: PredicateOp,
233 C: PredicateOp + 'static = Enable,
234 D: PredicateOp + 'static = Enable,
235>(Cons<A, B, C, D>);
236
237impl<A: PredicateOp, B: PredicateOp, C: PredicateOp, D: PredicateOp> Parser for AllOf<A, B, C, D> {
238 #[inline]
239 fn parser(tokens: &mut TokenIter) -> Result<Self> {
240 Cons::<A, B, C, D>::parse(tokens).map(AllOf)
241 }
242}
243
244impl<A: PredicateOp, B: PredicateOp, C: PredicateOp, D: PredicateOp> ToTokens
245 for AllOf<A, B, C, D>
246{
247 #[inline]
248 #[mutants::skip] // Inner type produces no tokens, so mutation to () is equivalent
249 fn to_tokens(&self, tokens: &mut TokenStream) {
250 self.0.to_tokens(tokens);
251 }
252}
253
254impl<A: PredicateOp, B: PredicateOp, C: PredicateOp, D: PredicateOp> Default for AllOf<A, B, C, D>
255where
256 Cons<A, B, C, D>: Default,
257{
258 #[inline]
259 fn default() -> Self {
260 AllOf(Cons::default())
261 }
262}
263
264impl<A: PredicateOp, B: PredicateOp, C: PredicateOp, D: PredicateOp> PredicateOp
265 for AllOf<A, B, C, D>
266{
267}
268
269// =============================================================================
270// LOGICAL OR (2-4 operands)
271// =============================================================================
272
273/// Logical OR: 2-4 predicates, at least one must succeed.
274///
275/// C and D default to [`Disable`] (always fails).
276///
277/// # Examples
278///
279/// ```rust
280/// use unsynn::*;
281///
282/// let mut tokens = "".to_token_iter();
283/// // Two predicates
284/// assert!(AnyOf::<Enable, Disable>::parse(&mut tokens).is_ok());
285/// assert!(AnyOf::<Disable, Disable>::parse(&mut tokens).is_err());
286/// // Four predicates
287/// assert!(AnyOf::<Disable, Disable, Enable, Disable>::parse(&mut tokens).is_ok());
288/// ```
289#[derive(Debug, Clone)]
290pub struct AnyOf<
291 A: PredicateOp,
292 B: PredicateOp,
293 C: PredicateOp + 'static = Disable,
294 D: PredicateOp + 'static = Disable,
295>(Either<A, B, C, D>);
296
297impl<A: PredicateOp, B: PredicateOp, C: PredicateOp, D: PredicateOp> Parser for AnyOf<A, B, C, D> {
298 #[inline]
299 fn parser(tokens: &mut TokenIter) -> Result<Self> {
300 Either::<A, B, C, D>::parse(tokens).map(AnyOf)
301 }
302}
303
304impl<A: PredicateOp, B: PredicateOp, C: PredicateOp, D: PredicateOp> ToTokens
305 for AnyOf<A, B, C, D>
306{
307 #[inline]
308 #[mutants::skip] // Inner type produces no tokens, so mutation to () is equivalent
309 fn to_tokens(&self, tokens: &mut TokenStream) {
310 self.0.to_tokens(tokens);
311 }
312}
313
314impl<A: PredicateOp, B: PredicateOp, C: PredicateOp, D: PredicateOp> Default for AnyOf<A, B, C, D>
315where
316 Either<A, B, C, D>: Default,
317{
318 #[inline]
319 fn default() -> Self {
320 AnyOf(Either::default())
321 }
322}
323
324impl<A: PredicateOp, B: PredicateOp, C: PredicateOp, D: PredicateOp> PredicateOp
325 for AnyOf<A, B, C, D>
326{
327}
328
329// =============================================================================
330// LOGICAL XOR (2-4 operands)
331// =============================================================================
332
333/// Logical XOR: exactly one of 2-4 predicates must succeed.
334///
335/// C and D default to [`Disable`].
336///
337/// # Examples
338///
339/// ```rust
340/// use unsynn::*;
341///
342/// let mut tokens = "".to_token_iter();
343/// // Two predicates
344/// assert!(OneOf::<Enable, Disable>::parse(&mut tokens).is_ok());
345/// assert!(OneOf::<Enable, Enable>::parse(&mut tokens).is_err());
346/// // Four predicates
347/// assert!(OneOf::<Enable, Disable, Disable, Disable>::parse(&mut tokens).is_ok());
348/// assert!(OneOf::<Enable, Enable, Disable, Disable>::parse(&mut tokens).is_err());
349/// ```
350#[allow(clippy::type_complexity)]
351#[derive(Debug, Clone)]
352pub struct OneOf<
353 A: PredicateOp + 'static,
354 B: PredicateOp + 'static,
355 C: PredicateOp + 'static = Disable,
356 D: PredicateOp + 'static = Disable,
357>(
358 AnyOf<
359 AllOf<A, Not<B>, Not<C>, Not<D>>,
360 AllOf<Not<A>, B, Not<C>, Not<D>>,
361 AllOf<Not<A>, Not<B>, C, Not<D>>,
362 AllOf<Not<A>, Not<B>, Not<C>, D>,
363 >,
364);
365
366impl<A: PredicateOp, B: PredicateOp, C: PredicateOp, D: PredicateOp> Parser for OneOf<A, B, C, D> {
367 #[inline]
368 fn parser(tokens: &mut TokenIter) -> Result<Self> {
369 AnyOf::<
370 AllOf<A, Not<B>, Not<C>, Not<D>>,
371 AllOf<Not<A>, B, Not<C>, Not<D>>,
372 AllOf<Not<A>, Not<B>, C, Not<D>>,
373 AllOf<Not<A>, Not<B>, Not<C>, D>,
374 >::parse(tokens)
375 .map(OneOf)
376 }
377}
378
379impl<A: PredicateOp, B: PredicateOp, C: PredicateOp, D: PredicateOp> ToTokens
380 for OneOf<A, B, C, D>
381{
382 #[inline]
383 #[mutants::skip] // Inner type produces no tokens, so mutation to () is equivalent
384 fn to_tokens(&self, tokens: &mut TokenStream) {
385 self.0.to_tokens(tokens);
386 }
387}
388
389impl<A: PredicateOp, B: PredicateOp, C: PredicateOp, D: PredicateOp> Default for OneOf<A, B, C, D>
390where
391 AnyOf<
392 AllOf<A, Not<B>, Not<C>, Not<D>>,
393 AllOf<Not<A>, B, Not<C>, Not<D>>,
394 AllOf<Not<A>, Not<B>, C, Not<D>>,
395 AllOf<Not<A>, Not<B>, Not<C>, D>,
396 >: Default,
397{
398 #[inline]
399 fn default() -> Self {
400 OneOf(AnyOf::default())
401 }
402}
403
404impl<A: PredicateOp, B: PredicateOp, C: PredicateOp, D: PredicateOp> PredicateOp
405 for OneOf<A, B, C, D>
406{
407}
408
409// =============================================================================
410// TYPE IDENTITY CHECKING
411// =============================================================================
412
413/// Predicate that compares type `A` with type `B` at runtime.
414///
415/// Acts like `Same` when `A == B` (=[`Enable`]), and `Different`when `A != B` (=[`Disable`]).
416/// This enables distinguishing between different predicate flags at compile time through
417/// runtime type checks (which are usually optimized away by the compiler).
418///
419/// # Examples
420///
421/// ```rust
422/// use unsynn::*;
423/// //use unsynn::predicates::*;
424///
425/// unsynn! {
426/// predicatetrait Context;
427/// predicateflag InExpr for Context;
428/// predicateflag InStmt for Context;
429///
430/// // Only accepts InExpr, rejects InStmt
431/// struct OnlyInExpr<C: Context> {
432/// _guard: PredicateCmp<C, InExpr>,
433/// content: Ident,
434/// }
435///
436/// // Accepts InExpr but NOT InStmt
437/// struct ExprButNotStmt<C: Context> {
438/// _guard: AllOf<PredicateCmp<C, InExpr>, Not<PredicateCmp<C, InStmt>>>,
439/// content: Ident,
440/// }
441/// }
442/// ```
443#[derive(Debug)]
444pub struct PredicateCmp<
445 A: PredicateOp,
446 B: PredicateOp,
447 Same: PredicateOp = Enable,
448 Different: PredicateOp = Disable,
449>(PhantomData<(A, B, Same, Different)>);
450
451impl<A: PredicateOp, B: PredicateOp, Same: PredicateOp, Different: PredicateOp> PredicateOp
452 for PredicateCmp<A, B, Same, Different>
453{
454}
455
456impl<A: PredicateOp, B: PredicateOp, Same: PredicateOp, Different: PredicateOp>
457 PredicateCmp<A, B, Same, Different>
458{
459 /// Create a new `PredicateCmp` instance.
460 ///
461 /// This is primarily useful for testing or when you need to manually
462 /// construct the predicate.
463 #[inline]
464 #[must_use]
465 pub const fn new() -> Self {
466 PredicateCmp(PhantomData)
467 }
468}
469
470// Manual Clone impl to avoid requiring A: Clone, B: Clone bounds on type parameters.
471// We implement Clone explicitly even though PredicateCmp is Copy because the type
472// parameters A and B don't need to be Clone - they're only used in PhantomData.
473#[mutants::skip]
474#[allow(clippy::expl_impl_clone_on_copy)]
475impl<A: PredicateOp, B: PredicateOp, Same: PredicateOp, Different: PredicateOp> Clone
476 for PredicateCmp<A, B, Same, Different>
477{
478 fn clone(&self) -> Self {
479 *self
480 }
481}
482
483impl<A: PredicateOp, B: PredicateOp, Same: PredicateOp, Different: PredicateOp> Copy
484 for PredicateCmp<A, B, Same, Different>
485{
486}
487
488impl<A: PredicateOp, B: PredicateOp, Same: PredicateOp, Different: PredicateOp> Parser
489 for PredicateCmp<A, B, Same, Different>
490{
491 #[inline]
492 fn parser(tokens: &mut TokenIter) -> Result<Self> {
493 // Check type equality at compile/runtime
494 if TypeId::of::<A>() == TypeId::of::<B>() {
495 // Types match
496 Same::parse(tokens)?;
497 } else {
498 // Types don't match
499 Different::parse(tokens)?;
500 }
501 Ok(Self(PhantomData))
502 }
503}
504
505impl<A: PredicateOp, B: PredicateOp, Same: PredicateOp, Different: PredicateOp> ToTokens
506 for PredicateCmp<A, B, Same, Different>
507{
508 #[inline]
509 fn to_tokens(&self, _tokens: &mut TokenStream) {
510 // Zero-sized, emits nothing
511 }
512}
513
514impl<A: PredicateOp, B: PredicateOp, Same: PredicateOp, Different: PredicateOp> Default
515 for PredicateCmp<A, B, Same, Different>
516{
517 #[inline]
518 fn default() -> Self {
519 // Check at const time if possible, otherwise runtime
520 if TypeId::of::<A>() == TypeId::of::<B>() {
521 PredicateCmp(PhantomData)
522 } else {
523 // This will panic if called with mismatched types
524 // But it's safe because Default should only be called when it's valid
525 panic!("PredicateCmp::default() called with mismatched types")
526 }
527 }
528}