<br>
<p align="center">
<a href="https://shortishly.github.io/scran/cover/">
<img alt="Test Coverage" src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fshortishly.github.io%2Fscran%2Fcover%2Fcoverage.json&query=%24.total&suffix=%25&style=flat-square&label=Test%20Coverage&color=green">
</a>
<a href="https://shortishly.github.io/scran/edoc/">
<img alt="edoc" src="https://img.shields.io/badge/Documentation-edoc-green?style=flat-square">
</a>
<a href="https://erlang.org/">
<img alt="Erlang/OTP 25+" src="https://img.shields.io/badge/Erlang%2FOTP-25%2B-green?style=flat-square">
</a>
<a href="https://www.apache.org/licenses/LICENSE-2.0">
<img alt="Apache-2.0" src="https://img.shields.io/github/license/shortishly/scran?style=flat-square">
</a>
</p>
## What is scran?
scran is a parser combinator library heavily influenced by
[nom][nom-parser-combinators].
Unlike nom, scran isn't primarily intended for binary parsing because
we already have [bit syntax expressions][erlang-bit-syntax] in
Erlang. However, it does have a small number of combinators for length
encoded, or null terminated byte sequences (typically "strings"),
big/little integers with common bit sizes.
Internally, scran uses the [maybe keyword, which is supported by OTP
25+][maybe-expression].
## Example
Each parser returns a function that takes Unicode input, returning a
tuple of the unmatched and matched input.
```erlang
1> (scran_character_complete:tag("Hello"))("Hello, World!").
{", World!","Hello"}
```
Where `"Hello"` has been matched by `tag("Hello")` and `", World!"' is
the remaining input.
```erlang
2> (scran_character_complete:tag("Hello"))("hello, world!").
nomatch
```
The `tag("Hello")` is case sensitive so there is `nomatch` for "hello,
world!".
```erlang
3> (scran_character_complete:tag_no_case("Hello"))("hello, world!").
{", world!","hello"}
4> (scran_character_complete:tag_no_case("Hello"))("hellO, wOrlD!").
{", wOrlD!","hellO"}
```
`tag_no_case` can be used for case insensitive matching.
## Character Complete
The `scran_character_complete` module has various parsers used match
different character classes or combinations.
### alpha0
The `alpha0` parser will match zero or more alphabetic characters
in the range of `[a-zA-Z]`.
```erlang
12> (scran_character_complete:alpha0())("abc").
{[], "abc"}
13> (scran_character_complete:alpha0())("").
{[], []}
14> (scran_character_complete:alpha0())("abc123").
{"123", "abc"}
15> (scran_character_complete:alpha0())("123abc").
{"123abc", []}
```
### alpha1
The `alpha1` parser will match one or more alphabetic characters
in the range of `[a-zA-Z]`.
```erlang
8> (scran_character_complete:alpha1())("abc").
{[], "abc"}
9> (scran_character_complete:alpha1())("").
nomatch
10> (scran_character_complete:alpha1())("abc123").
{"123","abc"}
11> (scran_character_complete:alpha1())("123abc").
nomatch
```
### alphanumeric0
The `alphanumeric0` parser will match zero or more alpha numeric
characters in the range of `[a-zA-Z0-9]`.
```erlang
16> (scran_character_complete:alphanumeric0())("abc").
{[], "abc"}
17> (scran_character_complete:alphanumeric0())("").
{[], []}
18> (scran_character_complete:alphanumeric0())("abc123").
{[], "abc123"}
19> (scran_character_complete:alphanumeric0())("123abc").
{[], "123abc"}
20> (scran_character_complete:alphanumeric0())("123abc!%^").
{"!%^","123abc"}
21> (scran_character_complete:alphanumeric0())("!%^abc123").
{"!%^abc123", []}
```
### alphanumeric1
The `alphanumeric1` parser will match one or more alpha numeric
characters in the range of `[a-zA-Z0-9]`.
```erlang
24> (scran_character_complete:alphanumeric1())("abc").
{[], "abc"}
25> (scran_character_complete:alphanumeric1())("").
nomatch
26> (scran_character_complete:alphanumeric1())("abc123").
{[], "abc123"}
27> (scran_character_complete:alphanumeric1())("123abc").
{[], "123abc"}
28> (scran_character_complete:alphanumeric1())("123abc!%^").
{"!%^", "123abc"}
29> (scran_character_complete:alphanumeric1())("!%^").
nomatch
30> (scran_character_complete:alphanumeric1())("!%^abc123").
nomatch
```
### digit0
The `digit0` parser will match zero or more numeric
characters in the range of `[0-9]`.
### digit1
The `digit1` parser will match one or more numeric
characters in the range of `[0-9]`.
### multispace0
The `multispace0` parser will match zero or more numeric
characters in the range of `[\\s\\t\\n\\r]`.
### multispace1
The `multispace1` parser will match one or more numeric
characters in the range of `[\\s\\t\\n\\r]`.
### none_of
The `none_of` parser will match if the next character of the input is
none of the supplied characters.
### one_of
The `one_of` parser will match if the next character of the input is
one of the supplied characters.
### re
The `re` parser will match if the input satisfies the supplied case
sensitive regular expression.
### re\_no\_case
The `re\_no\_case` parser will match if the input satisfies the supplied case
insensitive regular expression.
### tag
The `tag` parser will match if the input matches the supplied case
sensitive string.
### tag\_no\_case
The `tag\_no\_case` parser will match if the input matches the supplied case
insensitive string.
### take
The `take` parser will match if it can take the specified number of
characters from the input.
## Branches
The `scan_branch` module is used to specify different branches of
parsing behaviour.
### alt
With `scran_branch:alt/1` you can specify alternate parsers to
use. Each parser is tried in turn until `nomatch` is returned.
```erlang
1> (scran_branch:alt([scran_character_complete:alpha1(),
scran_character_complete:digit1()]))("abc123").
{"123","abc"}
2> (scran_branch:alt([scran_character_complete:alpha1(),
scran_character_complete:digit1()]))("123456").
{[],"123456"}
3> (scran_branch:alt([scran_character_complete:alpha1(),
scran_character_complete:digit1()]))("123456abc").
{"abc","123456"}
4> (scran_branch:alt([scran_character_complete:alpha1(),
scran_character_complete:digit1()]))("!@£$").
nomatch
```
### permutation
## scran_combinator
The `scan_combinator` module is used to specify parsers that are
combined into ones that can exhibit complex behaviours.
### all_comsuming
This parser succeeds if all the input has been consumed by its child parser.
### map_result
This parser maps a function on the result of a parser.
### ignore_result
This parser ignores the result of a parser.
### map_parser
This parser applies a parser over the result of another one.
### opt
An optional parser, will return none if the option is not taken.
### value
This parser returns the provided value if the child parser succeeds.
### condition
This parser calls the supplied parser if the condition is met.
### peek
This parser tries to apply its parser without consuming the input.
### eof
This parser returns its input if it is at the end of input data.
### is_not
This parser succeeds if the child parser returns an error.
## Additional
The test cases are currently the best place to look at simple examples
of the combinators. There is also a more complex example that is used
to [parse part of the PostgreSQL grammar][github-com-pgsqlp].
### coverage report
Coverage report [is available here][scran-cover].
### edoc
edoc [is available here][scran-edoc].
[erlang-bit-syntax]: https://www.erlang.org/doc/reference_manual/expressions.html#bit_syntax
[github-com-pgsqlp]: https://github.com/shortishly/pgsqlp
[maybe-expression]: https://www.erlang.org/doc/reference_manual/expressions.html#maybe
[nom-parser-combinators]: https://github.com/rust-bakery/nom
[scran-cover]: https://shortishly.github.io/scran/cover
[scran-edoc]: https://shortishly.github.io/scran/edoc