diff --git a/README.md b/README.md index 93fffd3..fb50273 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,36 @@ # parz -> Learning about Parser Combinators using [The YouTube Series by Low Byte Productions](https://www.youtube.com/playlist?list=PLP29wDx6QmW5yfO1LAgO8kU3aQEj8SIrU) +A simple parser combinator library + +> Trying to learn [Gleam]() while also Learning about Parser Combinators using +> [The YouTube Series by Low Byte Productions](https://www.youtube.com/playlist?list=PLP29wDx6QmW5yfO1LAgO8kU3aQEj8SIrU) +> and [Understanding Parser Combinators](https://fsharpforfunandprofit.com/posts/understanding-parser-combinators/) [![Package Version](https://img.shields.io/hexpm/v/parz)](https://hex.pm/packages/parz) [![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/parz/) +The project exposes the following modules: + +1. `parz.` - contains the `run` method which is used for executing a parser +2. `parz/combinators` - the parser combinator library +3. `pars/parsers` - some simple, prmiitive parsers + +## Usage + ```sh gleam add parz ``` ```gleam -import parz +import parz.{run} +import pars/combinators +import pars/parsers pub fn main() { - // TODO: An example of the project in use + // For an example usage look at the `tests/sample_parser_test.gleam` file + let parser = // .. define a parser + + let result = run(parser, content_to_parse) } ``` diff --git a/src/parz/combinators.gleam b/src/parz/combinators.gleam index 573a3c7..4183812 100644 --- a/src/parz/combinators.gleam +++ b/src/parz/combinators.gleam @@ -1,7 +1,6 @@ import gleam/list import gleam/string import parz/types.{type Parser, type ParserState, ParserState} -import parz/util.{tap} fn sequence_rec( parsers: List(Parser(a)), diff --git a/src/parz/util.gleam b/src/parz/util.gleam deleted file mode 100644 index 69e60f1..0000000 --- a/src/parz/util.gleam +++ /dev/null @@ -1,15 +0,0 @@ -import gleam/io - -pub fn tap(msg) { - fn(a) { - io.debug(#(msg, a)) - a - } -} - -pub fn do(f) { - fn(a) { - f(a) - a - } -} diff --git a/test/simple_parser_test.gleam b/test/simple_parser_test.gleam new file mode 100644 index 0000000..6a0eb3f --- /dev/null +++ b/test/simple_parser_test.gleam @@ -0,0 +1,89 @@ +import gleeunit/should +import parz.{run} +import parz/combinators.{label_error, left, map, separator1, sequence} +import parz/parsers.{letters, regex, str} +import parz/types.{ParserState} + +type Kind { + StringKind + BooleanKind + NumberKind + UnknownKind +} + +type Identifier { + Identifier(name: String) +} + +type Node { + UnknownNode + Node(name: Identifier, kind: Kind) +} + +type NodePart { + K(kind: Kind) + I(identifier: Identifier) +} + +type AST { + AST(List(Node)) +} + +const input = "name:string; +age:number; +active:boolean;" + +const custom_error = "Expected : but found something else" + +fn parser() { + let name = + left(letters(), str(":") |> label_error(custom_error)) + |> map(Identifier) + |> map(I) + + let kind = + left(letters(), str(";")) + |> map(fn(ok) { + case ok { + "string" -> StringKind + "number" -> NumberKind + "boolean" -> BooleanKind + _ -> UnknownKind + } + }) + |> map(K) + + let node = + sequence([name, kind]) + |> map(fn(ok) { + case ok { + [I(i), K(k)] -> Node(i, k) + _ -> UnknownNode + } + }) + + let whitespace = regex("\\s*") + + let parser = separator1(node, whitespace) |> map(AST) + + parser +} + +pub fn simple_parser_test() { + run(parser(), input) + |> should.be_ok + |> should.equal(ParserState( + AST([ + Node(Identifier("name"), StringKind), + Node(Identifier("age"), NumberKind), + Node(Identifier("active"), BooleanKind), + ]), + "", + )) +} + +pub fn simple_parser_error_test() { + run(parser(), "name;number") + |> should.be_error + |> should.equal(custom_error) +}