Skip to content

Commit

Permalink
🎨 make sequence work more generically
Browse files Browse the repository at this point in the history
  • Loading branch information
nabeelvalley committed Jul 19, 2024
1 parent 22da7ea commit b155520
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 19 deletions.
27 changes: 16 additions & 11 deletions src/parz/combinators.gleam
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import gleam/string
import parz/types.{type Parser, type ParserState, ParserState}
import parz/util.{tap}

fn sequence_rec(parsers: List(Parser(a)), input, acc) {
fn sequence_rec(
parsers: List(Parser(a)),
input: String,
acc: ParserState(List(a)),
) -> Result(ParserState(List(a)), String) {
case parsers {
[] -> Ok(#([], input))
[] -> Ok(ParserState([], input))
[first, ..rest] ->
case first(input) {
Error(err) -> Error(err)
Ok(ok) ->
case sequence_rec(rest, ok.remaining, acc) {
Error(err) -> Error(err)
Ok(rec) -> {
let #(matches, remaining) = rec
Ok(#([ok.matched, ..matches], remaining))
}
Ok(rec) ->
Ok(ParserState([ok.matched, ..rec.matched], rec.remaining))
}
}
}
}

pub fn sequence(parsers: List(Parser(a))) {
fn(input) { sequence_rec(parsers, input, []) }
fn(input) { sequence_rec(parsers, input, ParserState([], input)) }
}

pub fn choice(parsers: List(Parser(a))) {
Expand Down Expand Up @@ -116,19 +119,21 @@ pub fn many(parser: Parser(a)) {
}
}

pub fn concat(parser) {
pub fn concat(parser: Parser(List(a)), merge) {
fn(input) {
case parser(input) {
Error(err) -> Error(err)
Ok(ok) -> {
let #(parts, remainder) = ok

Ok(ParserState(string.concat(parts), remainder))
Ok(ParserState(merge(ok.matched), ok.remaining))
}
}
}
}

pub fn concat_str(parser) {
concat(parser, string.concat)
}

pub fn label_error(parser, message) {
fn(input) {
case parser(input) {
Expand Down
5 changes: 4 additions & 1 deletion src/parz/parsers.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ pub fn regex(regex) {
Error(_) -> Error("Invalid Regex Provided " <> regex)
Ok(re) -> {
case regex.scan(re, input) {
[] -> Error("String does not match regex " <> regex)
[] ->
Error(
"String does not match Regex: " <> regex <> "String: " <> input,
)
[match, ..] -> {
let remaining =
string.drop_left(input, string.length(match.content))
Expand Down
15 changes: 8 additions & 7 deletions test/combinators_test.gleam
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import gleeunit/should
import parz.{run}
import parz/combinators.{
between, choice, concat, label_error, left, many, many1, map, maybe, right,
sequence,
between, choice, concat, concat_str, label_error, left, many, many1, map,
maybe, right, sequence,
}
import parz/parsers.{letters, str}
import parz/types.{ParserState}
Expand Down Expand Up @@ -122,18 +122,18 @@ pub fn sequence_test() {

run(parser, "[hello]")
|> should.be_ok
|> should.equal(#(["[", "hello", "]"], ""))
|> should.equal(ParserState(["[", "hello", "]"], ""))

run(parser, "[hello]!")
|> should.be_ok
|> should.equal(#(["[", "hello", "]"], "!"))
|> should.equal(ParserState(["[", "hello", "]"], "!"))

run(parser, "[hello")
|> should.be_error
}

pub fn concat_test() {
let parser = concat(sequence([str("["), letters(), str("]")]))
let parser = concat_str(sequence([str("["), letters(), str("]")]))

run(parser, "[hello]")
|> should.be_ok
Expand Down Expand Up @@ -166,7 +166,8 @@ pub fn maybe_test() {
pub fn label_error_test() {
let message = "Expected [letters]"
let parser =
concat(sequence([str("["), letters(), str("]")])) |> label_error(message)
concat_str(sequence([str("["), letters(), str("]")]))
|> label_error(message)

run(parser, "[hello]")
|> should.be_ok
Expand All @@ -184,7 +185,7 @@ type Transformed {

pub fn map_test() {
let parser =
concat(sequence([str("["), letters(), str("]")]))
concat_str(sequence([str("["), letters(), str("]")]))
|> map(fn(ok) {
case ok {
"" -> NoContent
Expand Down

0 comments on commit b155520

Please sign in to comment.