started on pattern matching

This commit is contained in:
Brian Picciano 2013-07-23 12:04:11 -04:00
parent 67cb7c7a30
commit 2efde48331
3 changed files with 140 additions and 1 deletions

View File

@ -13,3 +13,4 @@ with the language.
itself. There's very few data structures, meaning there's minimal syntax. itself. There's very few data structures, meaning there's minimal syntax.
* [runtime](/doc/runtime.md) - How to structure your ginger data to be runnable by the ginger interpreter. * [runtime](/doc/runtime.md) - How to structure your ginger data to be runnable by the ginger interpreter.
Includes execution, variable definition, scope, and function definition. Includes execution, variable definition, scope, and function definition.
* [pattern matching](/doc/pattern.md) - Deconstruction of data structures and case statements on them.

138
doc/pattern.md Normal file
View File

@ -0,0 +1,138 @@
# Pattern Matching
Pattern matching in ginger is used both to assign variables and test for the contents of them.
## Assignment
The `=` function's job is to assign values to variables. In the simplest case:
```
(= Foo 5)
```
Assigns the value `5` to the variable named `Foo`. We can assign more complicated values to variables
as well:
```
(= Foo [1 2 3 4])
```
### Deconstruction
With pattern matching, we can deconstruct the value on the right side and assign its individual parts
to their own individual variables:
```
[
(= [Foo Bar [Baz Box] Bus] [1 a [3 4] [5 6]])
(println Foo)
(println Bar)
(println Baz)
(println Box)
(println Bus)
]
```
The above would print out:
```
1
a
3
4
[5 6]
```
### No Match
What happens if we try to pattern match a value that doesn't match the left side?:
```
(= [Foo Bar Baz Box] [1 2 3]) ; => [error nomatch]
```
The value itself is a vector with three items, but we want a vector with four! This returns an error
and none of the variables were assigned.
### Blanks
If there's a variable we expect the right side to have, but we don't actually want to do anything with
it, we can use `_`:
```
(= [Foo _ Baz] [1 2 3])
```
The above would assign `1` to `Foo` and `2` to `Baz`.
### Left-side checks
The left side isn't exclusively used for assignment, it can also be used to check certain values on
the right:
```
(= [1 Foo 2 Bar] [1 a 2 b]) ; => success
(= [1 Foo 2 Bar] [1 a 3 b]) ; => [error nomatch]
```
If a variable is already defined it can be used as a check as well:
```
[
(= Foo 1)
(= [Foo Foo Bar] [1 1 2]) ; => success
(= [Foo Foo Baz] [1 2 3]) ; => [error nomatch]
]
```
Finally, undefined variables on the left side that are used more than once are ensured that all
instances have the same value:
```
[
(= [Foo Foo Foo] [foo foo foo]) ; => success
(= [Bar Bar Bar] [foo bar bar]) ; => [error nomatch]
]
```
### Vectors
What if we don't know the full length of the vector, but we only want to retrieve the first two values
from it?:
```
(= [Foo Bar -] [1 2 3 4 5])
```
The above would assign `1` to `Foo` and `2` to `Bar`. `-` is treated to mean "zero or more items in the sequence
that we don't care about".
What if you want to match on the tail end of the vector?:
```
(= [- Bar Foo] [1 2 3 4 5])
```
The above would assign `4` to Bar and `5` to `Foo`.
Can we do both?:
```
(= [Foo - Bar] [1 2 3 4 5])
```
The above would assign `1` to `Foo` and `5` to `Bar`.
### Lists
Everything that applies to vectors applies to lists, as far as pattern matching goes:
```
(= (Foo Bar Baz -) (l (foo bar baz)))
```
In the above `foo` is assigned to `Foo`, `bar` to `Bar`, and `baz` to `Baz`.
Note that the right side needs the `l` literal wrapper to prevent evaluation, while
the left side does not. Nothing on the left will ever be evaluated, if you want it
to be dynamically defined you'll have to use a macro.
### Maps
Pattern matching can be performed on maps as well:
```
(= { Foo Bar } { a b }) ; => success
(= { Foo Bar } { a b c d }) ; => [error nomatch]
(= { Foo Bar - - } { a b c d }) ; => success
```
In the last case, the important thing is that the key is `-`. This tells the matcher
that you don't care about any other keys that may or may not exist. The value can be
anything, but `-` seems semantically more correct imo.