https://github.com/keean/applicative_parser
parser combinators applicative TypeScript deno module
https://github.com/keean/applicative_parser
parser parser-combinators typescript
Last synced: 14 days ago
JSON representation
parser combinators applicative TypeScript deno module
- Host: GitHub
- URL: https://github.com/keean/applicative_parser
- Owner: keean
- License: mit
- Created: 2021-07-12T18:15:51.000Z (about 5 years ago)
- Default Branch: main
- Last Pushed: 2021-08-14T13:22:22.000Z (almost 5 years ago)
- Last Synced: 2025-10-20T18:00:51.233Z (9 months ago)
- Topics: parser, parser-combinators, typescript
- Language: TypeScript
- Homepage:
- Size: 112 KB
- Stars: 3
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# applicative_parser
*parser combinators applicative TypeScript deno module*
The applicative approach to parsing has benefits compared to the monadic approach,
it separates the structure of the parser from the implementation. This allows different
implementations to be used (for example with or without backtracking, or error recovery)
without changing the use of the application of the parser. It also allows other algorithms
over the parser applications such as pretty-printing or symbol extraction.
These features cannot be implemented with a monadic parser because the second argument of
monadic-bind is a function, and hence opaque.
Key features:
- no external dependencies
- able to extract all valid symbols from parser
- allows pretty printing
- supports multiple evaluation strategies (backtracking vs non-backtracking etc)
Based on "Notes on Computing", "Applicative Parsing" in ML :
Using existential encoding from "Existential Quantification in TypeScript"
to encode the GADT used in "Applicative Parsing"
Here is an example parser for floating point numbers:
```ts
const digit = OneOf('0123456789');
const float1 = seqMap((a, b) => [...a, b], many1(digit), OneOf('.'));
const float2 = seqMap((a, b) => [...a, ...b], float1, many1(digit));
const float3 = seqMap((a, b, c) => [...a, b, ...c], choice(float2, many1(digit)), OneOf('e'), many1(digit));
export const float: Parser = FMap(a => parseFloat(a.join('')), choice(float3, float2, float1));
}
```
This parser can be compiled into a function:
```ts
const floatParser = parse(float);
```
`parse` is just one possible compiler, as the static parser definition (in this case float) does
not depend on the implementation of `parse`, you can replace `parse` with a different implementation
of the parser (say with no backtracking, or better error reporting) _without_ chaning the definiton
of `float` at all.
The supplied `parse` compiler produces a parser with the following type:
```ts
type Parse = (_: {cs: string, pos: number, args: A}) => {result: B, cs: string, pos: number}|null;
```
So the parser can be applied to an input string as follows:
```ts
const parsed = floatParser({cs: '123.456e2', pos: 0});
```
The output `parsed` would then be:
```ts
{cs: '123.456e2', pos: 9, result: 12345.6}
```
`cs` is the complete input string, `pos` is the final position of the parser, and these can be used as the input to further parsers, alhough it would be better to build all the logic into a single parser as any needed logic can be
expressed using the parser combinators. A `null` will be returned if the parser failed.
Below is the result of `show(float)` but with the functions in the maps replaced with `?`, to show that the
whole structure of the parser is static, and transparent (unlike with the monadic approach). The helper functions like
`choice` and `seqMap` are all represented by the combinators in the initial algebra.
```ts
FMap(?, (Either(FMap(?, (FMap(?, (Product(FMap(?, (Either(FMap(?, (FMap(?, (Product(FMap(?, (FMap(?, (FMap(?, (Product(FMap(?, (FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fix(Either(FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fail(''))), Return()))))), FMap(?, (Product(FMap(?, (OneOf('.')), Return())))))), FMap(?, (Product(FMap(?, (FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fix(Either(FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fail(''))), Return()))))), Return()))))), Either(FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fix(Either(FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fail(''))), Return())))), Fail('')))), FMap(?, (Product(FMap(?, (OneOf('e')), FMap(?, (Product(FMap(?, (FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fix(Either(FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fail(''))), Return()))))), Return()))))))), Either(FMap(?, (FMap(?, (Product(FMap(?, (FMap(?, (FMap(?, (Product(FMap(?, (FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fix(Either(FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fail(''))), Return()))))), FMap(?, (Product(FMap(?, (OneOf('.')), Return())))))), FMap(?, (Product(FMap(?, (FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fix(Either(FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fail(''))), Return()))))), Return()))))), Either(FMap(?, (FMap(?, (Product(FMap(?, (FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fix(Either(FMap(?, (Product(FMap(?, (OneOf('0123456789')), Fail(''))), Return()))))), FMap(?, (Product(FMap(?, (OneOf('.')), Return()))))), Fail('')))))
```