1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#[allow(unused)]
use crate::string_matcher::{patterns, StringMatcher, StringPattern};

/// Creates a [StringMatcher] from the given [StringPattern].
/// Approximately a shorthand for `&string_pattern!(pattern).matcher()`.
///
/// Matchers are self-referential, so the output can't be moved after it's created.
/// All predefined patterns in the [patterns] module are brought into scope for the pattern expression.
///
/// # Example
///
/// ```
/// let quotes_matcher = gramma::string_matcher!(
///     char('"') + char(..).repeat(..).lazy() + !follows(char('\\')) + char('"')
/// );
///
/// assert!(quotes_matcher.match_string(4, r#"s = "hello, \"world\"!";"#).is_some());
/// ```
#[macro_export]
macro_rules! string_matcher {
    ($expr:expr $(,)?) => {
        &$crate::string_matcher::StringPattern::matcher({
            #[allow(unused)]
            use $crate::string_matcher::patterns::*;
            $expr
        }) as &$crate::string_matcher::StringMatcher<_>
    };
}

/// Compose a [StringPattern].
/// All predefined patterns in the [patterns] module are brought into scope.
///
/// # Example
///
/// ```
/// let quotes_pattern = gramma::string_pattern!(
///     char('"') + char(..).repeat(..).lazy() + !follows(char('\\')) + char('"')
/// );
///
/// assert!(quotes_pattern.matcher().match_string(4, r#"s = "hello, \"world\"!";"#).is_some());
/// ```
#[macro_export]
macro_rules! string_pattern {
    ($expr:expr $(,)?) => {
        $crate::string_matcher::StringPattern::_validate_string_pattern({
            #[allow(unused)]
            use $crate::string_matcher::patterns::*;
            $expr
        })
    };
}

///
/// ```
/// # use gramma::{define_string_pattern, string_matcher};
/// define_string_pattern!(
///     fn identifier(max_len: impl Into<Option<u32>>) {
///         !precedes(ascii_digit())
///             + word().repeat(1..=max_len.into().unwrap_or(u32::MAX)).simple()
///             + word_boundary()
///     }
/// );
///
/// assert_eq!(string_matcher!(identifier(4)).match_string(0, "foo"), Some(0..3));
/// assert_eq!(string_matcher!(identifier(4)).match_string(0, "foobar"), None);
/// assert_eq!(string_matcher!(identifier(None)).match_string(0, "foobarbaz"), Some(0..9));
/// ```
#[macro_export]
macro_rules! define_string_pattern {
    { $(
        $vis:vis fn $Name:ident (
            $($arg:tt $($arg1:ident)* $(: $Arg:ty)?),* $(,)?
        ) $body:block
    )* } => { $(
        $vis fn $Name ( $($arg $($arg1)* $(: $Arg)?),* ) ->
            $crate::string_matcher::StringPattern<impl $crate::string_matcher::IntoMatchString>
            { $crate::string_pattern! ($body) }
    )* };
}