From dde78b5d49418989c0a5c6565d75a88d0eb5b87d Mon Sep 17 00:00:00 2001 From: Ryan Brewer Date: Wed, 31 Jul 2024 16:22:31 +0800 Subject: [PATCH] add stateful combinators --- CHANGELOG.md | 6 ++++++ gleam.toml | 2 +- src/party.gleam | 32 ++++++++++++++++++++++++++++++++ test/party_test.gleam | 17 ++++++++++++++--- 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a74ffa4..4f17031 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.3] - 2024-07-31 + +### Added + + - The `stateful_many` and `stateful_many1` combinators. + ## [1.0.2] - 2024-04-05 ### Added diff --git a/gleam.toml b/gleam.toml index 9fb4a23..7d02b10 100644 --- a/gleam.toml +++ b/gleam.toml @@ -1,5 +1,5 @@ name = "party" -version = "1.0.2" +version = "1.0.3" description = "A little parser combinator library in Gleam." licences = ["MPL-2.0"] diff --git a/src/party.gleam b/src/party.gleam index 3147536..0a09bbf 100644 --- a/src/party.gleam +++ b/src/party.gleam @@ -372,3 +372,35 @@ pub fn until( }, ) } + +/// A `many` parser that also gets to update some state with each success +pub fn stateful_many(state: s, p: Parser(fn(s)->#(a, s), e)) -> Parser(#(List(a), s), e) { + Parser(fn(source, pos) { + case run(p, source, pos) { + Error(_) -> Ok(#(#([], state), source, pos)) + Ok(#(f, r, pos2)) -> { + let #(x, s) = f(state) + result.map(run(stateful_many(s, p), r, pos2), fn(res) { + let #(#(rest, s2), r2, pos3) = res + #(#([x, ..rest], s2), r2, pos3) + }) + } + } + }) +} + +/// A `many1` parser that also gets to update some state with each success +pub fn stateful_many1(state: s, p: Parser(fn(s)->#(a, s), e)) -> Parser(#(List(a), s), e) { + Parser(fn(source, pos) { + case run(p, source, pos) { + Error(e) -> Error(e) + Ok(#(f, r, pos2)) -> { + let #(x, s) = f(state) + result.map(run(stateful_many(s, p), r, pos2), fn(res) { + let #(#(rest, s), r2, pos3) = res + #(#([x, ..rest], s), r2, pos3) + }) + } + } + }) +} \ No newline at end of file diff --git a/test/party_test.gleam b/test/party_test.gleam index aca366b..a5ee7d4 100644 --- a/test/party_test.gleam +++ b/test/party_test.gleam @@ -2,12 +2,12 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -import gleeunit -import gleeunit/should -import party import gleam/int import gleam/list import gleam/string +import gleeunit +import gleeunit/should +import party pub fn main() { gleeunit.main() @@ -298,3 +298,14 @@ pub fn multiline_comment_test() { ) |> should.equal(Ok("hello! * ")) } + +pub fn stateful_many_test() { + let stateful_char = fn(c) { + party.char(c) + |> party.map(fn(x) { fn(counter) { #(x, counter /. 2.0) } }) + } + party.go(party.stateful_many(100.0, stateful_char("a")), "aaab") + |> should.equal(Ok(#(["a", "a", "a"], 12.5))) + party.go(party.stateful_many(100.0, stateful_char("b")), "aaab") + |> should.equal(Ok(#([], 100.0))) +}