ex_spirit v0.1.2 ExSpirit.Parser.Text
ExSpirit.Parser.Text is a set of parser specifically for parsing out utf-8 text from a binary.
Parsers
lit
The literal parser matches out a specific string or character, entirely
ignoring the result and returning nil
.
Examples
# `lit` matches a specific string or character
iex> import ExSpirit.Tests.Parser
iex> context = parse("Test 42", lit("Test"))
iex> {context.error, context.result, context.rest}
{nil, nil, " 42"}
# `lit` matches a specific string or character
iex> import ExSpirit.Tests.Parser
iex> context = parse("Test 42", lit(?T))
iex> {context.error, context.result, context.rest}
{nil, nil, "est 42"}
uint
The unsigned integer parser parses a plain number from the input with a few options.
- The first argument is the radix, defaults to 10, everything from 2 to 36 is supported.
- The second argument is the minimum character count, defaults 1, valid at 1+. If the characters able to be parsed as a number is less than this value then the parser fails.
- The third argument is the maximum character count, defaults -1 (unlimited), valid values are -1, or 1+. It stops parsing at this amount of characters and returns what it has parsed so far, if there are more number characters still to be parsed then they will be handled by the next parser.
Examples
# `uint` parses out an unsigned integer, default radix of 10 with a min size of 1 and max of unlimited
iex> import ExSpirit.Tests.Parser
iex> context = parse("42", uint())
iex> {context.error, context.result, context.rest}
{nil, 42, ""}
# `uint` parsing out base-2
iex> import ExSpirit.Tests.Parser
iex> context = parse("101", uint(2))
iex> {context.error, context.result, context.rest}
{nil, 5, ""}
# `uint` parsing out base-16 lower-case, can be mixed too
iex> import ExSpirit.Tests.Parser
iex> context = parse("ff", uint(16))
iex> {context.error, context.result, context.rest}
{nil, 255, ""}
# `uint` parsing out base-16 upper-case, can be mixed too
iex> import ExSpirit.Tests.Parser
iex> context = parse("FFF", uint(16))
iex> {context.error, context.result, context.rest}
{nil, 4095, ""}
Text (UTF-8 Binaries) parsing
# `char` can parse out any single character
iex> import ExSpirit.Tests.Parser
iex> context = parse("Test", char())
iex> {context.error, context.result, context.rest}
{nil, ?T, "est"}
# `char` can parse out any 'specific' single character as well
iex> import ExSpirit.Tests.Parser
iex> context = parse("Test", char(?T))
iex> {context.error, context.result, context.rest}
{nil, ?T, "est"}
# `char` can parse out anything 'but' a 'specific' single character too,
# just negate it, don't mix positive and negative matchers in the same set
# unless there is only one negative matcher and it is at the end of the list
iex> import ExSpirit.Tests.Parser
iex> context = parse("Nope", char(-?T))
iex> {context.error, context.result, context.rest}
{nil, ?N, "ope"}
# `char` can parse out any 'specific' single character from a range too
iex> import ExSpirit.Tests.Parser
iex> context = parse("Test", char(?A..?Z))
iex> {context.error, context.result, context.rest}
{nil, ?T, "est"}
# `char` can parse out any but a 'specific' single character from a range
iex> import ExSpirit.Tests.Parser
iex> context = parse("42", char(-?A..-?Z))
iex> {context.error, context.result, context.rest}
{nil, ?4, "2"}
# `char` can parse out any 'specific' single character from a list of
# characters or ranges too
iex> import ExSpirit.Tests.Parser
iex> context = parse("Test", char([?a..?z, ?T]))
iex> {context.error, context.result, context.rest}
{nil, ?T, "est"}
# `char` can parse out any but a 'specific' single character from a list of
# characters or ranges too
iex> import ExSpirit.Tests.Parser
iex> context = parse("42", char([?a..?z, -?T]))
iex> {context.error, context.result, context.rest}
{nil, ?4, "2"}
# a mixed list is fine if the negated ones are at the end of it, only
iex> import ExSpirit.Tests.Parser
iex> context = parse("Test", char([?a..?z, ?T, -?A..-?Z]))
iex> {context.error, context.result, context.rest}
{nil, ?T, "est"}
# a mixed list is fine if the negated ones are at the end of it, only,
# here is how a failure looks
iex> import ExSpirit.Tests.Parser
iex> context = parse("Rest", char([?a..?z, ?T, -?A..-?Z]))
iex> {String.starts_with?(context.error.message, "Tried parsing out any of the the characters of"), context.result, context.rest}
{true, nil, "Rest"}
# `chars` parser is like char but it parses all matching as a binary
iex> import ExSpirit.Tests.Parser
iex> context = parse("TEST42", chars(?A..?Z))
iex> {context.error, context.result, context.rest}
{nil, "TEST", "42"}
# `chars` parser is like char but it parses all matching as a binary, can
# also take an initial single-char matcher
iex> import ExSpirit.Tests.Parser
iex> context = parse("_TEST42", chars(?_, ?A..?Z))
iex> {context.error, context.result, context.rest}
{nil, "_TEST", "42"}
# `symbols` takes a ExSpirit.TreeMap, which is a structure designed for fast
# lookups, though slow insertions, so please cache the data structure at
# compile-time if possible. This `symbols` parser will take the text input
# stream and match it on the TreeMap to find the longest-matching string,
# then it will take the return value, if a function then it will run it as
# as parserFn, else it will return it as a value
iex> import ExSpirit.Tests.Parser
iex> alias ExSpirit.TreeMap, as: TreeMap
iex> symbol_TreeMap = TreeMap.new() |> TreeMap.add_text("int", &uint(&1)) |> TreeMap.add_text("char", &char(&1))
iex> context = parse("int42", symbols(symbol_TreeMap))
iex> {context.error, context.result, context.rest}
{nil, 42, ""}
iex> context = parse("charT", symbols(symbol_TreeMap))
iex> {context.error, context.result, context.rest}
{nil, ?T, ""}
iex> context = parse("in", symbols(symbol_TreeMap))
iex> {String.starts_with?(context.error.message, "Tried matching out symbols and got to `i` but failed"), context.result, context.rest}
{true, nil, "in"}
iex> context = parse("", symbols(symbol_TreeMap))
iex> {String.starts_with?(context.error.message, "Tried matching out symbols and got the end of the line but failed to find a value"), context.result, context.rest}
{true, nil, ""}
iex> import ExSpirit.Tests.Parser
iex> alias ExSpirit.TreeMap, as: TreeMap
iex> symbol_TreeMap = TreeMap.new() |> TreeMap.add_text("let", 1) |> TreeMap.add_text("letmap", 2) |> TreeMap.add_text("", 0)
iex> context = parse("", symbols(symbol_TreeMap))
iex> {context.error, context.result, context.rest}
{nil, 0, ""}
iex> context = parse("A", symbols(symbol_TreeMap))
iex> {context.error, context.result, context.rest}
{nil, 0, "A"}
iex> context = parse("let", symbols(symbol_TreeMap))
iex> {context.error, context.result, context.rest}
{nil, 1, ""}
iex> context = parse("letmap", symbols(symbol_TreeMap))
iex> {context.error, context.result, context.rest}
{nil, 2, ""}
iex> context = parse("letma", symbols(symbol_TreeMap))
iex> {context.error, context.result, context.rest}
{nil, 1, "ma"}