zeolite-lang-0.7.0.0: example/parser/README.md
# Zeolite Parser Example
*Also see
[a highlighted version of the example code](https://ta0kira.github.io/zeolite/example/parser).*
This example demonstrates parsing text using a parser-combinator approach
inspired by [`parsec`][parsec]. (The Haskell library that the `zeolite` compiler
uses to parse source and config files.)
## Notes
This example highlights several distinguishing features of the
[Zeolite language][zeolite]:
- **Encapsulation.** Data encapsulation is mandatory in Zeolite. This forces the
code author to think about usage patterns before data representation.
- **Factory Functions.** There is no "default construction" in Zeolite; the code
author must explicitly expose factory functions if needed. In this example,
most of the factory functions return an interface, rather than the actual
`concrete` type being constructed.
- **Internal Inheritance.** `ParseState` inherits `ParseContext` internally
(see `parser.0rp` and `parser.0rx`), which allows `ParseState` to
conditionally expose the `ParseContext` interface to other functions.
- **Parameter Variance.** Several categories used in this example (e.g.,
`ParseState` and `ParseContext`) have a *covariant* parameter, which allows
their parameters to be converted to other types. Specifically:
- Any `ParseContext<#x>` can be converted to `ParseContext<any>` when calling
`Parser.run`. (In C++ this would require a `template` and in Java it would
require using `<?>`.)
- The `ParseState<all>` returned from `ParseContext.setBrokenInput` can be
converted to all other `ParseState<#x>`. (In C++ or Java this would require
the caller to explicitly pass a type parameter to `setBrokenInput`.)
- **Multiple Returns.** Many of the functions in this example return more than
one value to the caller. (In C++ and Java this would require a data structure
for grouping objects together.)
- **Test Sources.** The `parser-test.0rt` source file contains a `testcase` that
the compiler itself can execute.
- **Expression Pragmas.** `parser-test.0rt` uses the `$ExprLookup[MODULE_PATH]$`
pragma to get the absolute path to its own module in order to locate a data
file. This might otherwise require hard-coding a path or relying on the tests
being executed from the module path.
- **Module Config.** The `.zeolite-module` file contains the build configuration
for this example. This means that building and running the example does not
require special instructions, scripting, or `Makefile`s.
- **Type Inference.** Several of the function definitions in the `.0rx` files
request parameter type-inference using `?`, e.g., `ErrorOr$$value<?>(match)`.
This is useful when the parameter value can be easily guessed by the reader of
the code from its context.
## Running
(Included with compiler package starting with version `0.6.0.0`.)
To run the example:
```shell
# This is just to locate the example code. Not for normal use!
ZEOLITE_PATH=$(zeolite --get-path)
# Compile the example.
zeolite -p "$ZEOLITE_PATH" -r example/parser
# Run the unit tests.
zeolite -p "$ZEOLITE_PATH" -t example/parser
```
(There is currently no binary target in this example.)
[parsec]: https://hackage.haskell.org/package/parsec
[zeolite]: https://github.com/ta0kira/zeolite