# Syntax In Malachi, commands consist of literal text and variable captures/groups. These are separated by at least one whitespace character; any number of any whitespace can be used and they all mean the same thing. In contrast to regex, whitespace is not matched literally but used only as a syntactic separator. Just like in regex, in order for a match to succeed, all the syntactic segments in it must be satisfied. Unlike regex, every command starts with an implicit starting anchor (`^` in regex.) A terminating anchor (`$` in regex) is currently not a part of the language but will be added in a future version. Here's a Malachi command that will match the word "dog" literally:\ `dog` The above command has only one segment and it is a literal. This will match: - "dog" - "dog says woof" - "dogs are nice!" It will not match: - "where is the dog?" - "Dog" - "do" ## Captures Matching literal text can only get you so far. You most probably also want to extract values from your inputs. `Captures` let you do exactly that; you can think of them like named capturing groups in regex. The syntax is as follows:\ `` That is, a capture is surrounded by angle brackets `<>`, must have a name, can have a quantifier symbol and can be followed by patterns after a colon. Before I show you some examples, these are the quantifiers: - (no quantifier): The capture must match once. - `?`: The capture can match once but is satisfied with no matches. - `+`: The capture must match at least once. - `*`: The capture can match any number of times including 0. Simple enough? Lets see some examples! #### Example Captures - ``\ This is the simplest capture. It matches a word and stores it as "name". (A `word` is a whitespace delimited string.) - ``\ This is the same as above except it won't fail if it doesn't match because of the `?` quantifier. - ``\ This capture will match 1 or more words and store every one of them as "names". > In Malachi, you can access these as a list! So it won't just overwrite like in regex. - ``\ This is the same as the previous example except it is satisfied with 0 matches. > Ok. That's cool and all, but what else can we do? Patterns! It would be a shame to call Malachi a pattern matching language without them! ## Patterns Patterns let you customize a capture. They are specified after the name and any quantifier, preceded by a colon (`:`). They consist of filters separated by commas. A capture can have any number of patterns. Each pattern is separated by a semicolon (`;`). In order for a capture to succeed, any of its patterns must match. ## Filters Filters do the matching. They look like function calls in a programming language. They can have arguments -- quoted strings separated by commas. Basically: `filter_name("arg1", "arg2", "...argN")` There are shorthands for some of the filters: They can be specified with an alternate syntax (the [eq][] filter and the [regex][] filter). Filters' main purpose is to limit what the pattern can match. Two special filters also modify the captured text; we'll talk about it in a bit. The most basic filter is a filter that matches some text literally. This is the `eq` filter. > Note: This may look pointless but there's more to it. Stick with me. Here's an example:\ `` As you can see, we use the filter after the name, the quantifier and a `:`. This command has only one segment and it is a capture named "haha". The "haha" capture has only one pattern and that pattern has only one filter, [eq][]. The quantifier is of course optional but the name and the `:` are not. This capture now will only match any amount of `haha`s, space-delimited or stuck together like "hahahaha". It will match: - `haha` - `hahahaha` - `haha haha haha` - `haha hahahaha haha` The [eq][] filter accepts multiple arguments; in that case it will match any of the provided strings. This command matches any number of "dog" or "cat":\ `` You can specify multiple filters if you separate them with commas. If a filter is specified more than once, its arguments will be combined into one filter. The previous command can be specified like below:\ `` ## List of Filters - [eq][]: Matches any of its arguments exactly. - [starts][]: Matches a word starting with any of its arguments. - [ends][]: Matches a word ending with any of its arguments. - [nocase][]: Makes the [eq][] and [starts][] filters case insensitive. - [notrim][]: Makes the [starts][] and the [ends][] filters not trim their matches. - [regex][]: Validates a match with a regular expression. ### The `eq` Filter We've seen this one in the previous examples but there's more to this filter. Most importantly, it has a special syntax if you want: it can be used without the `eq()`. That is, just put a string. These commands are equivalent: - `` - `` - `` Second, this filter works with the [nocase][] filter, making the comparison case insensitive. ### The `starts` Filter This filter will match any text having any of its arguments as a prefix. By default, it will trim the prefix from the matched text; you can prevent it by using the [notrim][] filter. This filter also works with the [nocase][] filter, making prefix matching case insensitive. #### Examples - ``\ Matches: - `-foo -bar` (values: `["-foo", "-bar"]`) - `--foo --bar` (values: `["--foo", "--bar"]`) - ` This filter takes no arguments. The `nocase()` filter makes the [eq][] and [starts][] filters case insensitive. ### The `notrim` Filter > This filter takes no arguments. The `notrim()` filter prevents the [starts][] and [ends][] filters from trimming their matches. ### The `regex` Filter The `regex` filter validates the match with a regular expression. The syntax of these regular expressions are of the [regex crate's](https://crates.io/crates/regex). In order to avoid the [escape hell](https://github.com/Hamz-a/php-regex-best-practices/blob/master/06 Escaping%20a backslash%20hell.md), Malachi defines an alternate syntax for this filter. It is the same as the Javascript's regex literals: a regular expression wrapped in a pair of forward slashes (`/`). You can of course use the normal filter syntax: `regex("\\d+")`, however you will need to escape every backslash twice because Malachi strings recognize some of the escape sequences. There are notable "gotchas" when dealing with this filter you should be aware of: - The [nocase][] filter has no effect on the regex. - This filter gets the string it will run against only after every other filter has done its job. This for example means that the [starts][] without [notrim][] will first trim its match, then the regex will be run against the trimmed string. - The regex is not anchored, use `^` and `$` inside it if you want that behaviour. - If you use `^` or `$` as anchors inside the regex, those will refer to the start and the end of the match respectively; not to start and end of the whole input. #### Examples of Regex Validation So far we've only seen single-segment commands; however a more realistic use case would involve sequences of patterns. So, lets write a command that takes any amount of numbers and sums them. We want the command to have valid inputs so each argument should be valid numbers. We could accomplish this through a regular expression such as `\d+`. The command:\ `?add ` Will do the trick!\ Notice that we've used `^` and `$` anchors inside the regex. The reason for this is without it, an input like `"?add not1number"` would have matched. The above command will now match: - `?add 1 2 3` (values: `["1", "2", "3"]`) - `?add -1 0 22` (values: `["-1", "0", "22"]`) But won't match: - `?add 2 books` - `?add -9C` Now to demonstrate when the regex gets its input, lets write a command that asks for a year and wants you to prefix it with `year=`. The command:\ `!year In this example a priority group and a normal group will have the same behaviour. We'll also see an example where it makes a difference. Here's the `?divine` command: ```text ?divine {