Skip to content

Commit

Permalink
Expand API for consistency with popular libraries
Browse files Browse the repository at this point in the history
  • Loading branch information
albertprz committed Nov 18, 2023
1 parent a7c1e9b commit 3c753d6
Show file tree
Hide file tree
Showing 8 changed files with 146 additions and 75 deletions.
37 changes: 31 additions & 6 deletions src/Bookhound/Parser.purs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ module Bookhound.Parser
, runParser
, andThen
, exactly
, eof
, lookAhead
, notFollowedBy
, both
, choice
, anyOf
, allOf
, anyChar
Expand Down Expand Up @@ -134,12 +138,33 @@ andThen p1 (p2@(P { transform: (t :: Transform), errors: e })) =
mkParser (\i -> parse p2 $ fromRight i $ runParser p1 i)

exactly :: forall a. Parser a -> Parser a
exactly (P { parse: p, transform: (t :: Transform), errors: e }) =
applyTransformError t e $ mkParser
\x -> case p x of
result@(Result i _) | i == mempty -> result
Result i _ -> Error $ ExpectedEof i
err -> err
exactly p = p <* eof

eof :: Parser Unit
eof = mkParser
\i -> if i == mempty then
Result i unit
else
Error $ ExpectedEof i

lookAhead :: forall a. Parser a -> Parser a
lookAhead (P { parse: p, transform: (t :: Transform), errors: e }) =
applyTransformError t e $
mkParser
\x -> case p x of
Result _ a -> Result x a
err -> err

notFollowedBy :: forall a. Parser a -> Parser Unit
notFollowedBy (P { parse: p, transform: (t :: Transform), errors: e }) =
applyTransformError t e $
mkParser
\x -> case p x of
Result _ _ -> Error UnexpectedEof
_ -> Result x unit

choice :: forall f a. Foldable f => f (Parser a) -> Parser a
choice = anyOf

anyOf :: forall f a. Foldable f => f (Parser a) -> Parser a
anyOf = foldl alt empty
Expand Down
53 changes: 38 additions & 15 deletions src/Bookhound/ParserCombinators.purs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module Bookhound.ParserCombinators
, is
, isNot
, inverse
, char
, string
, oneOf
, noneOf
, times
Expand All @@ -12,17 +14,20 @@ module Bookhound.ParserCombinators
, manyChar
, someChar
, multipleChar
, within
, between
, applyCons
, applyTuple
, maybeWithin
, withinBoth
, maybeWithinBoth
, maybeBetween
, surroundedBy
, maybeSurroundedBy
, manySepBy
, someSepBy
, multipleSepBy
, sepByOp
, sepByOps
, manyEndBy
, someEndBy
, multipleEndBy
, parseAppend
, withErrorFlipped
, timesFlipped
Expand All @@ -41,7 +46,7 @@ module Bookhound.ParserCombinators
, (||++)
) where

import Prelude
import Prelude hiding (between)

import Bookhound.Parser (Parser, allOf, anyOf, both, anyChar, except, satisfy, withError)
import Bookhound.ParserCombinators.List as List
Expand Down Expand Up @@ -76,6 +81,12 @@ else instance IsMatch String where
isMatch :: forall a. (a -> a -> Boolean) -> Parser a -> a -> Parser a
isMatch cond ma c1 = satisfy (cond c1) ma

char :: Char -> Parser Char
char = is

string :: String -> Parser String
string = is

oneOf :: forall a. IsMatch a => Array a -> Parser a
oneOf = anyOf <<< map is

Expand Down Expand Up @@ -109,7 +120,7 @@ times n p
| n < 1 = pure []
| otherwise = sequence $ p <$ (1 .. n)

-- Separated by combinators
-- Sep by combinators
manySepBy :: forall a b. Parser a -> Parser b -> Parser (Array b)
manySepBy sep p = Array.fromFoldable <$> List.manySepBy sep p

Expand All @@ -119,6 +130,7 @@ someSepBy sep p = Array.fromFoldable <$> List.someSepBy sep p
multipleSepBy :: forall a b. Parser a -> Parser b -> Parser (Array b)
multipleSepBy sep p = Array.fromFoldable <$> List.multipleSepBy sep p

-- Sep by ops combinators
sepByOps :: forall a b. Parser a -> Parser b -> Parser (Array a /\ Array b)
sepByOps sep p = bimap Array.fromFoldable Array.fromFoldable
<$> List.sepByOps sep p
Expand All @@ -127,18 +139,29 @@ sepByOp :: forall a b. Parser a -> Parser b -> Parser (a /\ Array b)
sepByOp sep p = rmap Array.fromFoldable
<$> List.sepByOp sep p

-- Within combinators
withinBoth :: forall a b c. Parser a -> Parser b -> Parser c -> Parser c
withinBoth start end p = start *> p <* end
-- End by combinators
manyEndBy :: forall a b. Parser a -> Parser b -> Parser (Array b)
manyEndBy end p = Array.fromFoldable <$> List.manyEndBy end p

someEndBy :: forall a b. Parser a -> Parser b -> Parser (Array b)
someEndBy end p = Array.fromFoldable <$> List.someEndBy end p

multipleEndBy :: forall a b. Parser a -> Parser b -> Parser (Array b)
multipleEndBy end p = Array.fromFoldable <$> List.multipleEndBy end p


-- Between combinators
surroundedBy :: forall a b c. Parser a -> Parser b -> Parser c -> Parser c
surroundedBy start end p = start *> p <* end

maybeWithinBoth :: forall a b c. Parser a -> Parser b -> Parser c -> Parser c
maybeWithinBoth p p' = withinBoth (optional p) (optional p')
maybeSurroundedBy :: forall a b c. Parser a -> Parser b -> Parser c -> Parser c
maybeSurroundedBy p p' = surroundedBy (optional p) (optional p')

within :: forall a b. Parser a -> Parser b -> Parser b
within p = withinBoth p p
between :: forall a b. Parser a -> Parser b -> Parser b
between p = surroundedBy p p

maybeWithin :: forall a b. Parser a -> Parser b -> Parser b
maybeWithin = within <<< optional
maybeBetween :: forall a b. Parser a -> Parser b -> Parser b
maybeBetween = between <<< optional

parseAppend
:: forall a b
Expand Down
27 changes: 26 additions & 1 deletion src/Bookhound/ParserCombinators/List.purs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ module Bookhound.ParserCombinators.List
, manySepBy
, someSepBy
, multipleSepBy
, manyEndBy
, someEndBy
, multipleEndBy
, sepByOp
, sepByOps
, (|?)
Expand Down Expand Up @@ -35,7 +38,7 @@ some = satisfy List.hasSome <<< many
multiple :: forall a. Parser a -> Parser (List a)
multiple = satisfy List.hasMultiple <<< many

-- Separated by combinators
-- Sep by combinators
sepBy
:: forall a b
. (Parser b -> Parser (Maybe b))
Expand All @@ -58,6 +61,7 @@ someSepBy = sepBy (map Just) many
multipleSepBy :: forall a b. Parser a -> Parser b -> Parser (List b)
multipleSepBy = sepBy (map Just) some

-- Sep by ops combinators
sepByOps :: forall a b. Parser a -> Parser b -> Parser (List a /\ List b)
sepByOps sepP p = do
x <- p
Expand All @@ -72,6 +76,27 @@ sepByOp sepP p = do
xs <- (|*) (sepP *> p)
pure (sep /\ x1 : x2 : xs)

-- End by combinators
endBy
:: forall a b
. (Parser b -> Parser (Maybe b))
-> (Parser b -> Parser (List b))
-> Parser a
-> Parser b
-> Parser (List b)
endBy freq1 freq2 sep p =
sepBy freq1 freq2 sep p <* sep

manyEndBy :: forall a b. Parser a -> Parser b -> Parser (List b)
manyEndBy = endBy optional many

someEndBy :: forall a b. Parser a -> Parser b -> Parser (List b)
someEndBy = endBy (map Just) many

multipleEndBy :: forall a b. Parser a -> Parser b -> Parser (List b)
multipleEndBy = endBy (map Just) some


applyCons :: forall f a. Apply f => f a -> f (List a) -> f (List a)
applyCons = lift2 Cons

Expand Down
24 changes: 11 additions & 13 deletions src/Bookhound/Parsers/Char.purs
Original file line number Diff line number Diff line change
@@ -1,36 +1,34 @@
module Bookhound.Parsers.Char where
module Bookhound.Parsers.Char (module AnyChar, digit, hexDigit, upper, lower, alpha, alphaNum, space, tab, newLine, spaceOrTab, whiteSpace, comma, dot, colon, quote, doubleQuote, dash, plus, equal, underscore, hashTag, question, openParens, closeParens, openSquare, closeSquare, openCurly, closeCurly, openAngle, closeAngle) where

import Prelude

import Bookhound.Parser (Parser, satisfy)
import Bookhound.Parser (Parser, anyChar)
import Bookhound.Parser as Parser
import Bookhound.Parser (anyChar) as AnyChar
import Bookhound.ParserCombinators (is)
import Bookhound.Utils.Char (isAlpha, isAlphaNum, isDigit, isHexDigit, isLower, isUpper)
import Control.Alt ((<|>))

anyChar :: Parser Char
anyChar = Parser.anyChar

satisfyChar :: (Char -> Boolean) -> Parser Char
satisfyChar = flip satisfy anyChar
satisfy :: (Char -> Boolean) -> Parser Char
satisfy = flip Parser.satisfy anyChar

digit :: Parser Char
digit = satisfyChar isDigit
digit = satisfy isDigit

hexDigit :: Parser Char
hexDigit = satisfyChar isHexDigit
hexDigit = satisfy isHexDigit

upper :: Parser Char
upper = satisfyChar isUpper
upper = satisfy isUpper

lower :: Parser Char
lower = satisfyChar isLower
lower = satisfy isLower

alpha :: Parser Char
alpha = satisfyChar isAlpha
alpha = satisfy isAlpha

alphaNum :: Parser Char
alphaNum = satisfyChar isAlphaNum
alphaNum = satisfy isAlphaNum

space :: Parser Char
space = is ' '
Expand Down
6 changes: 3 additions & 3 deletions src/Bookhound/Parsers/Collections.purs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module Bookhound.Parsers.Collections (collOf, listOf, tupleOf, mapOf) where
import Prelude

import Bookhound.Parser (Parser, satisfy, withErrorN)
import Bookhound.ParserCombinators (manySepBy, maybeWithin, (</\>))
import Bookhound.ParserCombinators (manySepBy, maybeBetween, (</\>))
import Bookhound.Parsers.Char (closeCurly, closeParens, closeSquare, comma, openCurly, openParens, openSquare)
import Bookhound.Parsers.String (spacing)
import Bookhound.Utils.Array (hasMultiple)
Expand All @@ -13,7 +13,7 @@ import Data.Map as Map
collOf :: forall a b c d. Parser a -> Parser b -> Parser c -> Parser d -> Parser (Array d)
collOf start end sep elemParser = start *> elemsParser <* end
where
elemsParser = manySepBy sep $ maybeWithin spacing elemParser
elemsParser = manySepBy sep $ maybeBetween spacing elemParser

listOf :: forall a. Parser a -> Parser (Array a)
listOf = withErrorN (-1) "List"
Expand All @@ -29,4 +29,4 @@ mapOf sep p1 p2 = withErrorN (-1) "Map "
$ Map.fromFoldable
<$> collOf openCurly closeCurly comma mapEntry
where
mapEntry = (p1 <* maybeWithin spacing sep) </\> p2
mapEntry = (p1 <* maybeBetween spacing sep) </\> p2
54 changes: 27 additions & 27 deletions src/Bookhound/Parsers/String.purs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
module Bookhound.Parsers.String where
module Bookhound.Parsers.String (anyString, word, digits, uppers, lowers, alphas, alphaNums, spaces, tabs, newLines, spacesOrTabs, spacing, blankLine, blankLines, betweenQuotes, betweenDoubleQuotes, betweenParens, betweenSquare, betweenCurly, betweenAngle, maybeBetweenQuotes, maybeBetweenDoubleQuotes, maybeBetweenParens, maybeBetweenSquare, maybeBetweenCurly, maybeBetweenAngle) where

import Prelude
import Prelude hiding (between)

import Bookhound.Parser (Parser)
import Bookhound.ParserCombinators (inverse, maybeWithin, maybeWithinBoth, within, withinBoth, (->>-), (|+), (|?), (||*), (||+))
import Bookhound.ParserCombinators (inverse, maybeBetween, maybeSurroundedBy, between, surroundedBy, (->>-), (|+), (|?), (||*), (||+))
import Bookhound.Parsers.Char (alpha, alphaNum, anyChar, closeAngle, closeCurly, closeParens, closeSquare, digit, doubleQuote, lower, newLine, openAngle, openCurly, openParens, openSquare, quote, space, spaceOrTab, tab, upper, whiteSpace)
import Data.Foldable (fold)

Expand Down Expand Up @@ -49,38 +49,38 @@ blankLine = (|?) spacesOrTabs ->>- newLine
blankLines :: Parser String
blankLines = fold <$> (|+) blankLine

withinQuotes :: forall b. Parser b -> Parser b
withinQuotes = within quote
betweenQuotes :: forall b. Parser b -> Parser b
betweenQuotes = between quote

withinDoubleQuotes :: forall b. Parser b -> Parser b
withinDoubleQuotes = within doubleQuote
betweenDoubleQuotes :: forall b. Parser b -> Parser b
betweenDoubleQuotes = between doubleQuote

withinParens :: forall b. Parser b -> Parser b
withinParens = withinBoth openParens closeParens
betweenParens :: forall b. Parser b -> Parser b
betweenParens = surroundedBy openParens closeParens

withinSquareBrackets :: forall b. Parser b -> Parser b
withinSquareBrackets = withinBoth openSquare closeSquare
betweenSquare :: forall b. Parser b -> Parser b
betweenSquare = surroundedBy openSquare closeSquare

withinCurlyBrackets :: forall b. Parser b -> Parser b
withinCurlyBrackets = withinBoth openCurly closeCurly
betweenCurly :: forall b. Parser b -> Parser b
betweenCurly = surroundedBy openCurly closeCurly

withinAngleBrackets :: forall b. Parser b -> Parser b
withinAngleBrackets = withinBoth openAngle closeAngle
betweenAngle :: forall b. Parser b -> Parser b
betweenAngle = surroundedBy openAngle closeAngle

maybeWithinQuotes :: forall b. Parser b -> Parser b
maybeWithinQuotes = maybeWithin quote
maybeBetweenQuotes :: forall b. Parser b -> Parser b
maybeBetweenQuotes = maybeBetween quote

maybeWithinDoubleQuotes :: forall b. Parser b -> Parser b
maybeWithinDoubleQuotes = maybeWithin doubleQuote
maybeBetweenDoubleQuotes :: forall b. Parser b -> Parser b
maybeBetweenDoubleQuotes = maybeBetween doubleQuote

maybeWithinParens :: forall b. Parser b -> Parser b
maybeWithinParens = maybeWithinBoth openParens closeParens
maybeBetweenParens :: forall b. Parser b -> Parser b
maybeBetweenParens = maybeSurroundedBy openParens closeParens

maybeWithinSquareBrackets :: forall b. Parser b -> Parser b
maybeWithinSquareBrackets = maybeWithinBoth openSquare closeSquare
maybeBetweenSquare :: forall b. Parser b -> Parser b
maybeBetweenSquare = maybeSurroundedBy openSquare closeSquare

maybeWithinCurlyBrackets :: forall b. Parser b -> Parser b
maybeWithinCurlyBrackets = maybeWithinBoth openCurly closeCurly
maybeBetweenCurly :: forall b. Parser b -> Parser b
maybeBetweenCurly = maybeSurroundedBy openCurly closeCurly

maybeWithinAngleBrackets :: forall b. Parser b -> Parser b
maybeWithinAngleBrackets = maybeWithinBoth openAngle closeAngle
maybeBetweenAngle :: forall b. Parser b -> Parser b
maybeBetweenAngle = maybeSurroundedBy openAngle closeAngle
Loading

0 comments on commit 3c753d6

Please sign in to comment.