18 releases (8 breaking)
Uses new Rust 2021
new 0.9.0 | May 16, 2022 |
---|---|
0.7.3 | May 6, 2022 |
0.7.0 | Feb 17, 2022 |
0.6.2 | Nov 22, 2021 |
#142 in Parser implementations
580 downloads per month
56KB
2K
SLoC
malachi
A domain specific pattern matching language made mainly for defining bot commands.
Crate Examples
Please check out the examples subdirectory of this repository.
Notes
- While declaring commands, any kind of whitespace (space, tab, newline, crlf) are treated the same.
<>
defines a capture.[]
defines a list of captures to be matched out of order.- You can include a space literal by escaping it with
\\
. I.e..foo\ bar
will match the literal"foo bar"
but not"foo bar"
.
Capture Syntax
You can define captures in 3 ways:
<NAME[QUANTIFIER]>
<NAME[QUANTIFIER]: FILTERS>
<NAME[QUANTIFIER]: FILTERS1; FILTERS2; FILTERS3...>
Quantifiers:
?
: Match this capture 1 or 0 times.*
: Match this capture 0 or more times. The captures will be returned in aVec
.+
: Match this capture at least once. The values are returned in aVec
.- None: Match this exactly once.
If you use the first form, the default is to match on words (whitespace separated). Otherwise you must use a filter.
Filter Syntax
Filters are exactly like a function call in any mainstream language. They may take arguments. Arguments are always quoted strings.
Some examples:
starts("--")
foo()
Strings
Strings are always quoted with one of the "
, '
or ```.
You can escape the quotation to include it in the string.
Some more escape patterns are recognized:
\n
: newline.\t
: tab.\r
: carriage return.
Capture Group Syntax
A capture group defines a list of patterns that will be matched out of order. There are two forms of capture groups:
- Priority groups: These are delimited with
[]
and the match priority of the captures enclosed are unchanged. - Normal group: These groups are enclosed in
{}
and the enclosed captures may be re-ordered for potentially more matches.
The syntax is as follows:
Priority groups:
[ CAPTURE1 CAPTURE2 ...CAPTURE_N ]
Normal groups:
{ CAPTURE1 CAPTURE2 ...CAPTURE_N }
Example:
[<first> <second> <maybe_third?>]
Some defaults
- Patterns have an implicit starting anchor (
^
in regex). - Whitespace is only used as a terminator while matching.
starts
andends
filters trim the match.
Examples
Run code
This example demonstrates a command for running code.
The flags are optional but must start with --
The code block is not optional and must either start and end with:
- "```"
- "`" Or,
- Starts with ```rust or ```rs and ends with ```.
.run
<flags*: starts("--")>
<code:
starts("```rust", "```rs", "```"), ends("```");
starts("`"), ends("`");
>
Get a Bible Verse
This example command has 3 arguments:
book
: required, must start with"book=
chapter
: Optional, must start with eitherchapter=
orch=
, case is not important.verse
: optional, must start withverse=
.
Since the patterns are wrapped in []
, they can be matched out of order.
.bible
[
<book: starts("book=")>
<chapter?: starts("chapter=", "ch="), nocase()>
<verse?: starts("verse=")>
]
Bet some credits
This basic example command lets you bet some of your discord credits. The only required argument is the amount.
.bet <amount>
See a tag
This command takes 1 or more space separated arguments. It does not specify any pattern so the defaults apply:
- Arguments are whitespace separated.
.tag <tags+:>
Filters
Every filter can be used any number of times, the arguments are combined into one filter.
starts(prefix1, prefix2, ...prefixN)
: Any of the given prefixes must match.ends(suffix1, suffix2, ...suffixN)
: Any of the suffixes must match.eq(s1, s2, ...sN)
: Must match any string given as an argument. Only other filter that can occur ifeq
is specified isnocase
."foo"
is shorthand foreq("foo")
.nocase()
: Ignore case while matching. Not always accurate and ignored while matching suffixes.notrim()
: Do not trim the prefix/suffix while usingstarts()
orends()
. Only valid in those two filters.
Dependencies
~2MB
~55K SLoC