packages feed

Kawaii-Parser 2.0.0 → 3.0.0

raw patch · 8 files changed

+510/−497 lines, 8 filesdep +directorydep +generic-lensdep +lensdep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: directory, generic-lens, lens

Dependency ranges changed: base

API changes (from Hackage documentation)

- Parser.Errors: Ambiguity :: Error
- Parser.Errors: Attempt_to_recover_a_filter_error :: Error
- Parser.Errors: Incomplete_tokenisation :: Error
- Parser.Errors: data Error
- Parser.Errors: instance GHC.Show.Show Parser.Errors.Error
- Parser.Line_and_char: L :: Line_and_char -> t -> L t
- Parser.Line_and_char: data L t
- Parser.Line_and_char: data Line_and_char
- Parser.Line_and_char: init_line_and_char :: Line_and_char
- Parser.Line_and_char: instance GHC.Classes.Eq Parser.Line_and_char.Line_and_char
- Parser.Line_and_char: instance GHC.Classes.Ord Parser.Line_and_char.Line_and_char
- Parser.Line_and_char: instance GHC.Show.Show Parser.Line_and_char.Line_and_char
- Parser.Line_and_char: instance GHC.Show.Show t => GHC.Show.Show (Parser.Line_and_char.L t)
- Parser.Line_and_char: next_char :: Line_and_char -> Line_and_char
- Parser.Line_and_char: next_line :: Line_and_char -> Line_and_char
- Parser.Line_and_char: write_file_name_and_line_and_char :: String -> Line_and_char -> String
- Parser.Line_and_char: write_line_and_char :: Line_and_char -> String
- Parser.Parser: data Parse_error' token
- Parser.Parser: data Parser' token t
- Parser.Parser: filter_Parser :: Ord token => String -> (t -> Bool) -> Parser' token t -> Parser' token t
- Parser.Parser: fmap_filter_Parser :: Ord token => String -> (t -> Maybe u) -> Parser' token t -> Parser' token u
- Parser.Parser: instance GHC.Base.Applicative (Parser.Parser.Parser' token)
- Parser.Parser: instance GHC.Base.Functor (Parser.Parser.Parser' token)
- Parser.Parser: instance GHC.Base.Monad (Parser.Parser.Parser' token)
- Parser.Parser: instance GHC.Show.Show token => GHC.Show.Show (Parser.Parser.Lookahead token)
- Parser.Parser: instance GHC.Show.Show token => GHC.Show.Show (Parser.Parser.Parse_error' token)
- Parser.Parser: parse :: Eq token => (Char -> char_class) -> (char_class -> Line_and_char -> Line_and_char) -> Tokeniser' char_class token err () -> Parser' token t -> (Parse_error' token -> err) -> String -> Either Error (Either err t)
- Parser.Parser: parse_empty_list :: Parser' token [t]
- Parser.Parser: parse_line_and_char :: Parser' token Line_and_char
- Parser.Parser: write_parse_error :: (token -> String) -> String -> Parse_error' token -> String
- Parser.Tokeniser: add_token :: token -> Tokeniser' char_class token err ()
- Parser.Tokeniser: current_line_and_char :: Tokens' token -> Line_and_char
- Parser.Tokeniser: data Tokeniser' char_class token err t
- Parser.Tokeniser: data Tokens' token
- Parser.Tokeniser: delete_char :: Tokeniser' char_class token err ()
- Parser.Tokeniser: gather_token :: (char_class -> Maybe Char) -> (String -> token) -> Tokeniser' char_class token err ()
- Parser.Tokeniser: get_char :: Int -> Tokeniser' char_class token err (Maybe char_class)
- Parser.Tokeniser: get_token :: Tokens' token -> Maybe token
- Parser.Tokeniser: instance GHC.Base.Applicative (Parser.Tokeniser.Tokeniser' char_class token err)
- Parser.Tokeniser: instance GHC.Base.Functor (Parser.Tokeniser.Tokeniser' char_class token err)
- Parser.Tokeniser: instance GHC.Base.Monad (Parser.Tokeniser.Tokeniser' char_class token err)
- Parser.Tokeniser: instance GHC.Show.Show char_class => GHC.Show.Show (Parser.Tokeniser.State char_class)
- Parser.Tokeniser: instance GHC.Show.Show token => GHC.Show.Show (Parser.Tokeniser.Tokens' token)
- Parser.Tokeniser: take_token :: (token -> Maybe t) -> Tokens' token -> Maybe (t, Tokens' token)
- Parser.Tokeniser: tokenisation_error :: (Line_and_char -> err) -> Tokeniser' char_class token err t
- Parser.Tokeniser: tokenise :: (Char -> char_class) -> (char_class -> Line_and_char -> Line_and_char) -> Tokeniser' char_class token err () -> String -> Either Error (Either err (Tokens' token))
- Parser.Tokeniser: tokens_ended :: Tokens' token -> Bool
- Parser.Transf: evalRST :: Functor f => RST env state f t -> env -> state -> f t
- Parser.Transf: evalWST :: Functor f => WST res state f t -> state -> f (t, res)
- Parser.Transf: execRST :: Functor f => RST env state f t -> env -> state -> f state
- Parser.Transf: execRWT :: Functor f => RWT env res f t -> env -> f res
- Parser.Transf: execWST :: Functor f => WST res state f t -> state -> f (state, res)
- Parser.Transf: runRST :: Functor f => RST env state f t -> env -> state -> f (t, state)
- Parser.Transf: runRWT :: Functor f => RWT env res f t -> env -> f (t, res)
- Parser.Transf: runWST :: WST res state f t -> state -> f (t, state, res)
- Parser.Transf: type RST env = RWST env ()
- Parser.Transf: type RWT env res = RWST env res ()
- Parser.Transf: type WST = RWST ()
+ Parser.Locations: File_path_and_location :: File_path -> Location -> File_path_and_location
+ Parser.Locations: With_location :: Location -> t -> With_location t
+ Parser.Locations: data File_path_and_location
+ Parser.Locations: data Location
+ Parser.Locations: data With_location t
+ Parser.Locations: init_location :: Location
+ Parser.Locations: instance GHC.Classes.Eq Parser.Locations.Location
+ Parser.Locations: instance GHC.Classes.Ord Parser.Locations.Location
+ Parser.Locations: instance GHC.Show.Show Parser.Locations.File_path_and_location
+ Parser.Locations: instance GHC.Show.Show Parser.Locations.Location
+ Parser.Locations: instance GHC.Show.Show t => GHC.Show.Show (Parser.Locations.With_location t)
+ Parser.Locations: next_char :: Location -> Location
+ Parser.Locations: next_line :: Location -> Location
+ Parser.Locations: write_file_path_and_location :: File_path -> Location -> String
+ Parser.Locations: write_location :: Location -> String
+ Parser.Parser: Ambiguity :: Usage_error
+ Parser.Parser: Attempt_to_recover_a_filter_error :: Usage_error
+ Parser.Parser: Filter_error :: error -> Parse_error error
+ Parser.Parser: Filter_error_encountered_in_parse_not :: Usage_error
+ Parser.Parser: Parse_error' :: Location -> Parse_error error
+ Parser.Parser: add_token :: Tokeniser' char_class token error token -> Tokeniser' char_class token error ()
+ Parser.Parser: data Parse_error error
+ Parser.Parser: data Usage_error
+ Parser.Parser: filter_parser :: (Eq token, Monoid output) => (t -> Bool) -> (Location -> error) -> Parser token output error t -> Parser token output error t
+ Parser.Parser: fmap_filter_parser :: (Eq token, Monoid output) => (t -> Either (Location -> error) u) -> Parser token output error t -> Parser token output error u
+ Parser.Parser: instance GHC.Base.Functor (Parser.Parser.Parser token output error)
+ Parser.Parser: instance GHC.Base.Monoid output => GHC.Base.Applicative (Parser.Parser.Parser token output error)
+ Parser.Parser: instance GHC.Base.Monoid output => GHC.Base.Monad (Parser.Parser.Parser token output error)
+ Parser.Parser: instance GHC.Generics.Generic (Parser.Parser.State token)
+ Parser.Parser: instance GHC.Show.Show Parser.Parser.Usage_error
+ Parser.Parser: instance GHC.Show.Show error => GHC.Show.Show (Parser.Parser.Parse_error error)
+ Parser.Parser: instance GHC.Show.Show token => GHC.Show.Show (Parser.Parser.Tokens token)
+ Parser.Parser: parse' :: (Char -> char_class) -> (char_class -> Location -> Location) -> Tokeniser' char_class token error () -> Parser' token error t -> (Location -> error) -> String -> Either Usage_error (Either error t)
+ Parser.Parser: parse_default :: Monoid output => Parser token output error t -> t -> Parser token output error t
+ Parser.Parser: parse_file_path' :: Eq token => Parser' token error [Back] -> Parser' token error String -> token -> token -> (String -> token) -> String -> Parser' token error File_path
+ Parser.Parser: parse_location :: Monoid output => Parser token output error Location
+ Parser.Parser: parse_not :: Monoid output => Parser token output error t -> Parser token output error ()
+ Parser.Parser: parse_with_location :: Monoid output => Parser token output error t -> Parser token output error (With_location t)
+ Parser.Parser: type Parser' token error = Parser token () error
+ Parser.Parser: type Tokeniser' char_class token error = Parser char_class [With_location token] error
+ Parser.Utilities: (<->) :: String -> String -> String
+ Parser.Utilities: (<//>) :: Directory -> File_path -> File_path
+ Parser.Utilities: Back :: Back
+ Parser.Utilities: Directory :: [Back] -> [String] -> Directory
+ Parser.Utilities: File_path :: Directory -> String -> String -> File_path
+ Parser.Utilities: all_equal :: Eq t => [t] -> Maybe (Maybe t)
+ Parser.Utilities: between :: Ord t => t -> t -> t -> Bool
+ Parser.Utilities: check :: MonadError error f => error -> Bool -> f ()
+ Parser.Utilities: check_ext :: String -> File_path -> Bool
+ Parser.Utilities: construct_map :: Ord t => [(t, u)] -> Maybe (Map t u)
+ Parser.Utilities: construct_set :: Ord t => [t] -> Maybe (Set t)
+ Parser.Utilities: data Back
+ Parser.Utilities: data Directory
+ Parser.Utilities: data File_path
+ Parser.Utilities: drop_file_name :: File_path -> Directory
+ Parser.Utilities: element_at :: Int -> [t] -> Maybe t
+ Parser.Utilities: infixr 5 <->
+ Parser.Utilities: infixr 6 <//>
+ Parser.Utilities: instance GHC.Base.Monoid Parser.Utilities.Directory
+ Parser.Utilities: instance GHC.Base.Semigroup Parser.Utilities.Directory
+ Parser.Utilities: instance GHC.Classes.Eq Parser.Utilities.Back
+ Parser.Utilities: instance GHC.Classes.Eq Parser.Utilities.Directory
+ Parser.Utilities: instance GHC.Classes.Eq Parser.Utilities.File_path
+ Parser.Utilities: instance GHC.Classes.Ord Parser.Utilities.Back
+ Parser.Utilities: instance GHC.Classes.Ord Parser.Utilities.Directory
+ Parser.Utilities: instance GHC.Classes.Ord Parser.Utilities.File_path
+ Parser.Utilities: instance GHC.Show.Show Parser.Utilities.Back
+ Parser.Utilities: instance GHC.Show.Show Parser.Utilities.Directory
+ Parser.Utilities: instance GHC.Show.Show Parser.Utilities.File_path
+ Parser.Utilities: lcm_all :: (Foldable f, Integral t) => f t -> t
+ Parser.Utilities: read_file :: File_path -> IO (Maybe String)
+ Parser.Utilities: swap_either :: Either t u -> Either u t
+ Parser.Utilities: write_file_path :: File_path -> FilePath
- Parser.Parser: (<+>) :: Eq token => Parser' token t -> Parser' token t -> Parser' token t
+ Parser.Parser: (<+>) :: Parser token output error t -> Parser token output error t -> Parser token output error t
- Parser.Parser: add_location :: Parser' token t -> Parser' token (L t)
+ Parser.Parser: add_location :: Monoid output => Parser token output error t -> Parser token output error (With_location t)
- Parser.Parser: parse_brackets :: Parser' token () -> Parser' token () -> Parser' token t -> Parser' token t
+ Parser.Parser: parse_brackets :: (Eq token, Monoid output) => token -> token -> Parser token output error t -> Parser token output error t
- Parser.Parser: parse_list :: Eq token => Parser' token () -> Parser' token t -> Parser' token [t]
+ Parser.Parser: parse_list :: (Eq token, Monoid output) => token -> Parser token output error t -> Parser token output error [t]
- Parser.Parser: parse_many :: Eq token => Parser' token t -> Parser' token [t]
+ Parser.Parser: parse_many :: Monoid output => Parser token output error t -> Parser token output error [t]
- Parser.Parser: parse_non_empty_list :: Eq token => Parser' token () -> Parser' token t -> Parser' token [t]
+ Parser.Parser: parse_non_empty_list :: (Eq token, Monoid output) => token -> Parser token output error t -> Parser token output error [t]
- Parser.Parser: parse_some :: Eq token => Parser' token t -> Parser' token [t]
+ Parser.Parser: parse_some :: Monoid output => Parser token output error t -> Parser token output error [t]
- Parser.Parser: parse_token :: Eq token => token -> String -> Parser' token ()
+ Parser.Parser: parse_token :: (Eq token, Monoid output) => token -> Parser token output error ()
- Parser.Parser: parse_token' :: Eq token => (token -> Maybe t) -> String -> Parser' token t
+ Parser.Parser: parse_token' :: Monoid output => (token -> Maybe t) -> Parser token output error t

Files

Kawaii-Parser.cabal view
@@ -1,25 +1,30 @@-author: Liisi Kerik
-build-type: Simple
-cabal-version: >= 1.10
-category: Monad transformers, Parsing, Tokenisation
-description:
-  This library provides a simple tokeniser and parser. Its main focus is not efficiency but simplicity of implementation and
-  use. The choice operator for parsers is symmetric, avoiding the need to think about the order in which the alternatives are
-  listed. The library supports adding locations to the parse tree and aims to provide reasonably detailed information about
-  parse errors with minimal user involvement. It also contains a module with type synonyms for some compositions of monad
-  transformers.
-homepage: https://github.com/liisikerik/kawaii_parser
-license: BSD3
-license-file: LICENSE
-maintainer: liisikerik@hotmail.com
-name: Kawaii-Parser
-synopsis: A simple parsing library.
-version: 2.0.0
-library
-  build-depends: base < 4.15, containers, mtl
-  default-language: Haskell2010
-  exposed-modules: Parser.Errors, Parser.Line_and_char, Parser.Parser, Parser.Tokeniser, Parser.Transf
-  other-extensions: StandaloneDeriving
-source-repository head
-  location: https://github.com/liisikerik/kawaii_parser.git
+cabal-version: 3.8+author: Liisi Kerik+category: Parsing, Tokenisation, Utilities+description:+  The library provides a tiny general utilities module, and a simple combinator-based tokeniser and parser. Its main focus is+  not efficiency but simplicity of implementation and use. The choice operator for parsers is symmetric, avoiding the need to+  think about the order in which the alternatives are listed. The library provides error locations and supports adding locations+  to the parse tree.+license: BSD-3-Clause+license-file: LICENSE+maintainer: liisikerik@hotmail.com+name: Kawaii-Parser+synopsis: A simple parsing library and some additional utilities.+version: 3.0.0+library+  build-depends:+    base >= 3.0.3.1 && < 4.22,+    containers >= 0.1.0.0 && < 0.9,+    directory >= 1.0.0.0 && < 1.4,+    generic-lens >= 0.1.0.0 && < 2.3,+    lens >= 0.1 && < 5.4,+    mtl >= 1.0 && < 2.4+  default-extensions:+    DeriveGeneric, FlexibleInstances, MultiParamTypeClasses, OverloadedLabels, ScopedTypeVariables, StandaloneDeriving+  default-language: Haskell2010+  exposed-modules: Parser.Locations, Parser.Parser, Parser.Utilities+  ghc-options: -Wall+source-repository head+  location: https://github.com/liisikerik/kawaii_parser.git   type: git
− Parser/Errors.hs
@@ -1,11 +0,0 @@-{-# OPTIONS_GHC -Wall #-}
-{-# LANGUAGE StandaloneDeriving #-}
-{-|
-Description: Errors.
-
-* Errors
--}
-module Parser.Errors (Error (..)) where
-  -- | Errors that indicate that the issue is not with the text but with incorrect use of the parsing library.
-  data Error = Ambiguity | Attempt_to_recover_a_filter_error | Incomplete_tokenisation
-  deriving instance Show Error
− Parser/Line_and_char.hs
@@ -1,39 +0,0 @@-{-# OPTIONS_GHC -Wall #-}
-{-# LANGUAGE StandaloneDeriving #-}
-{-|
-Description: Locations.
-
-* Locations
-* Writing locations
--}
-module Parser.Line_and_char (
-  L (..),
-  Line_and_char,
-  init_line_and_char,
-  next_char,
-  next_line,
-  write_file_name_and_line_and_char,
-  write_line_and_char) where
-  -- | Add a location to any type.
-  data L t = L Line_and_char t
-  -- | Locations.
-  data Line_and_char = Line_and_char Integer Integer
-  deriving instance Eq Line_and_char
-  deriving instance Ord Line_and_char
-  deriving instance Show t => Show (L t)
-  deriving instance Show Line_and_char
-  -- | First line, first character.
-  init_line_and_char :: Line_and_char
-  init_line_and_char = Line_and_char 1 1
-  -- | Move one character to the right.
-  next_char :: Line_and_char -> Line_and_char
-  next_char (Line_and_char line char) = Line_and_char line (1 + char)
-  -- | Move to the beginning of the next line.
-  next_line :: Line_and_char -> Line_and_char
-  next_line (Line_and_char line _) = Line_and_char (1 + line) 1
-  -- | Write the location with the file name.
-  write_file_name_and_line_and_char :: String -> Line_and_char -> String
-  write_file_name_and_line_and_char file_name (Line_and_char line char) = file_name ++ ":" ++ show line ++ ":" ++ show char
-  -- | Write the location without a file name.
-  write_line_and_char :: Line_and_char -> String
-  write_line_and_char (Line_and_char line char) = show line ++ ":" ++ show char
+ Parser/Locations.hs view
@@ -0,0 +1,41 @@+{-|+Description: Locations.++* Locations.+-}+module Parser.Locations (+  File_path_and_location (..),+  Location,+  With_location (..),+  init_location,+  next_char,+  next_line,+  write_file_path_and_location,+  write_location) where+  import Parser.Utilities+  deriving instance Eq Location+  deriving instance Ord Location+  deriving instance Show File_path_and_location+  deriving instance Show Location+  deriving instance Show t => Show (With_location t)+  -- | Locations with a file path.+  data File_path_and_location = File_path_and_location File_path Location+  -- | Locations.+  data Location = Location Integer Integer+  -- | Add a location to any type.+  data With_location t = With_location Location t+  -- | First line, first character.+  init_location :: Location+  init_location = Location 1 1+  -- | Move one character to the right.+  next_char :: Location -> Location+  next_char (Location line char) = Location line (1 + char)+  -- | Move to the next line.+  next_line :: Location -> Location+  next_line (Location line _) = Location (1 + line) 1+  -- | Write the location with the file path.+  write_file_path_and_location :: File_path -> Location -> String+  write_file_path_and_location file_path location = write_file_path file_path <> ":" <> write_location location+  -- | Write the location.+  write_location :: Location -> String+  write_location (Location line char) = show line <> ":" <> show char
Parser/Parser.hs view
@@ -1,242 +1,309 @@-{-# OPTIONS_GHC -Wall #-}
-{-# LANGUAGE StandaloneDeriving #-}
-{-|
-Description: An unambiguous parser with symmetric choice and location tracking.
-
-* Parser
-* Filter
-* Parsing locations
-* Parsing brackets
-* Parsing lists
--}
-module Parser.Parser (
-  (<+>),
-  Parse_error',
-  Parser',
-  add_location,
-  filter_Parser,
-  fmap_filter_Parser,
-  parse,
-  parse_brackets,
-  parse_empty_list,
-  parse_line_and_char,
-  parse_list,
-  parse_many,
-  parse_non_empty_list,
-  parse_some,
-  parse_token,
-  parse_token',
-  write_parse_error) where
-  import Control.Monad.Except (ExceptT (..), MonadError (..), runExceptT, withExceptT)
-  import Control.Monad.State.Strict (MonadState (..), StateT (..), evalStateT, modify)
-  import Data.List (intercalate)
-  import Data.Set (Set, elems, empty, singleton, union)
-  import Parser.Errors (Error (..))
-  import Parser.Line_and_char (L (..), Line_and_char, write_file_name_and_line_and_char)
-  import Parser.Tokeniser (Tokeniser', Tokens', current_line_and_char, get_token, take_token, tokenise, tokens_ended)
-  data Lookahead token = Lookahead Line_and_char (Set String) (Maybe token)
-  -- | Parse errors.
-  data Parse_error' token = Filter_error Line_and_char String | Match_error (Lookahead token)
-  -- | A parser that works on any kind of tokens.
-  newtype Parser' token t = Parser {run_Parser :: StateT (State token) (ExceptT (Parse_error' token) (Either Error)) t}
-  data State token = State {state_tokens :: Tokens' token, state_lookahead :: Lookahead token}
-  infixr 4 ===
-  (===) :: Eq t => t -> t -> t
-  x === y =
-    case x == y of
-      False -> undefined
-      True -> x
-  -- | Symmetric choice between two parsers that selects the longest match. Note that if both parsers successfully reach the
-  -- same location it will result in an ambiguity error that, unlike a normal parse error, is not recoverable by backtracking.
-  -- Also note that you should not attempt to recover a filter error. While this operator is normally associative this property
-  -- does not hold when ambiguity errors are involved.
-  infixr 3 <+>
-  (<+>) :: Eq token => Parser' token t -> Parser' token t -> Parser' token t
-  Parser (StateT parse_0) <+> Parser (StateT parse_1) =
-    Parser
-      (StateT
-        (\st ->
-          ExceptT
-            (do
-              (lookahead_0, result_0) <- runExceptT (parse_0 st) >>= deconstruct_result
-              (lookahead_1, result_1) <- runExceptT (parse_1 st) >>= deconstruct_result
-              (
-                construct_result (add_lookaheads lookahead_0 lookahead_1) <$>
-                case (result_0, result_1) of
-                  (Nothing, Nothing) -> Right Nothing
-                  (Nothing, Just _) -> Right result_1
-                  (Just _, Nothing) -> Right result_0
-                  (Just (_, tokens_0), Just (_, tokens_1)) ->
-                    case compare (current_line_and_char tokens_0) (current_line_and_char tokens_1) of
-                      LT -> Right result_1
-                      EQ -> Left Ambiguity
-                      GT -> Right result_0))))
-  instance Applicative (Parser' token) where
-    Parser parse_0 <*> Parser parse_1 = Parser (parse_0 <*> parse_1)
-    pure x = Parser (return x)
-  instance Functor (Parser' token) where
-    fmap f (Parser parse') = Parser (f <$> parse')
-  instance Monad (Parser' token) where
-    Parser parse' >>= f = Parser (parse' >>= run_Parser <$> f)
-  deriving instance Show token => Show (Lookahead token)
-  deriving instance Show token => Show (Parse_error' token)
-  deriving instance Show token => Show (State token)
-  -- | Parse something with an added location from the first token.
-  add_location :: Parser' token t -> Parser' token (L t)
-  add_location parse_t = L <$> parse_line_and_char <*> parse_t
-  add_lookaheads :: Eq token => Lookahead token -> Lookahead token -> Lookahead token
-  add_lookaheads
-    (lookahead_0 @ (Lookahead line_and_char_0 expected_0 found_0))
-    (lookahead_1 @ (Lookahead line_and_char_1 expected_1 found_1)) =
-      case compare line_and_char_0 line_and_char_1 of
-        LT -> lookahead_1
-        EQ -> Lookahead (line_and_char_0 === line_and_char_1) (union expected_0 expected_1) (found_0 === found_1)
-        GT -> lookahead_0
-  certain_token :: Eq token => token -> token -> Maybe ()
-  certain_token token token' =
-    case token == token' of
-      False -> Nothing
-      True -> Just ()
-  construct_result :: Lookahead token -> Maybe (t, Tokens' token) -> Either (Parse_error' token) (t, State token)
-  construct_result lookahead result =
-    case result of
-      Nothing -> Left (Match_error lookahead)
-      Just (x, tokens) -> Right (x, State tokens lookahead)
-  deconstruct_result :: Either (Parse_error' token) (t, State token) -> Either Error (Lookahead token, Maybe (t, Tokens' token))
-  deconstruct_result result =
-    case result of
-      Left err ->
-        case err of
-          Filter_error _ _ -> Left Attempt_to_recover_a_filter_error
-          Match_error lookahead -> Right (lookahead, Nothing)
-      Right (x, State tokens lookahead) -> Right (lookahead, Just (x, tokens))
-  -- | Filter the parse results - for example, restrict an integer parser to positive numbers. You also have to provide an error
-  -- string. Note that filter errors, unlike token matching errors, are non-recoverable.
-  filter_Parser :: Ord token => String -> (t -> Bool) -> Parser' token t -> Parser' token t
-  filter_Parser err f =
-    fmap_filter_Parser
-      err
-      (\x ->
-        case f x of
-          False -> Nothing
-          True -> Just x)
-  -- | Filter and transform the parse results. You also have to provide an error string. Note that filter errors, unlike token
-  -- matching errors, are non-recoverable.
-  fmap_filter_Parser :: Ord token => String -> (t -> Maybe u) -> Parser' token t -> Parser' token u
-  fmap_filter_Parser err f parse_t =
-    do
-      line_and_char <- parse_line_and_char
-      x <- parse_t
-      case f x of
-        Nothing -> Parser (throwError (Filter_error line_and_char err))
-        Just y -> return y
-  match_error :: Eq token => String -> Parser' token t
-  match_error expected =
-    do
-      State tokens lookahead <- Parser get
-      Parser
-        (throwError
-          (Match_error
-            (add_lookaheads lookahead (Lookahead (current_line_and_char tokens) (singleton expected) (get_token tokens)))))
-  -- | Parse the text. You have to provide a function that classifies characters, a function that tells how to update the
-  -- location depending on the character, a tokeniser, a parser and a function that converts parse errors to your preferred
-  -- type.
-  parse ::
-    (
-      Eq token => 
-      (Char -> char_class) ->
-      (char_class -> Line_and_char -> Line_and_char) ->
-      Tokeniser' char_class token err () ->
-      Parser' token t ->
-      (Parse_error' token -> err) ->
-      String ->
-      Either Error (Either err t))
-  parse classify_char next_line_and_char tokenise_t parse_t transform_parse_error text =
-    runExceptT
-      (do
-        tokens <- ExceptT (tokenise classify_char next_line_and_char tokenise_t text)
-        withExceptT
-          transform_parse_error
-          (evalStateT
-            (run_Parser (parse_end parse_t))
-            (State tokens (Lookahead (current_line_and_char tokens) empty (get_token tokens)))))
-  -- | Parse a term in brackets.
-  parse_brackets :: Parser' token () -> Parser' token () -> Parser' token t -> Parser' token t
-  parse_brackets parse_left_bracket parse_right_bracket parse_t =
-    do
-      parse_left_bracket
-      x <- parse_t
-      parse_right_bracket
-      return x
-  parse_end :: Eq token => Parser' token t -> Parser' token t
-  parse_end parse_t =
-    do
-      x <- parse_t
-      parse_end'
-      return x
-  parse_end' :: Eq token => Parser' token ()
-  parse_end' =
-    do
-      tokens <- state_tokens <$> Parser get
-      case tokens_ended tokens of
-        False -> match_error "end of text"
-        True -> return ()
-  parse_element :: Parser' token () -> Parser' token t -> Parser' token t
-  parse_element parse_separator parse_t =
-    do
-      parse_separator
-      parse_t
-  -- | Returns an empty list.
-  parse_empty_list :: Parser' token [t]
-  parse_empty_list = pure []
-  -- | Get the current location.
-  parse_line_and_char :: Parser' token Line_and_char
-  parse_line_and_char = current_line_and_char <$> state_tokens <$> Parser get
-  -- | Parse a (possibly empty) list with separators.
-  parse_list :: Eq token => Parser' token () -> Parser' token t -> Parser' token [t]
-  parse_list parse_separator parse_t = parse_empty_list <+> parse_non_empty_list parse_separator parse_t
-  -- | Parse a (possibly empty) list without separators.
-  parse_many :: Eq token => Parser' token t -> Parser' token [t]
-  parse_many parse_t = parse_empty_list <+> parse_some parse_t
-  -- | Parse a non-empty list with separators.
-  parse_non_empty_list :: Eq token => Parser' token () -> Parser' token t -> Parser' token [t]
-  parse_non_empty_list parse_separator parse_t = (:) <$> parse_t <*> parse_many (parse_element parse_separator parse_t)
-  -- | Parse a non-empty list without separators.
-  parse_some :: Eq token => Parser' token t -> Parser' token [t]
-  parse_some parse_t = (:) <$> parse_t <*> parse_many parse_t
-  -- | Parse a certain token (for example, a delimiter or a keyword) without returning any results. You also have to provide a
-  -- string that briefly describes the kind of token that is expected - it is used to provide detailed parse errors.
-  parse_token :: Eq token => token -> String -> Parser' token ()
-  parse_token token = parse_token' (certain_token token)
-  -- | Parses tokens that fit a certain pattern and transforms them into something more useful - for example, a string or an
-  -- integer. You also have to provide a string that briefly describes the kind of token that is expected - it is used to
-  -- provide detailed parse errors.
-  parse_token' :: Eq token => (token -> Maybe t) -> String -> Parser' token t
-  parse_token' f expected =
-    do
-      tokens <- state_tokens <$> Parser get
-      case take_token f tokens of
-        Nothing -> match_error expected
-        Just (x, tokens') ->
-          do
-            Parser (modify (\st -> st {state_tokens = tokens'}))
-            return x
-  write_maybe_token :: (token -> String) -> Maybe token -> String
-  write_maybe_token write_token maybe_token =
-    case maybe_token of
-      Nothing -> "end of file"
-      Just token -> write_token token
-  -- | Write parse errors. You have to provide a function that converts tokens to strings and the file name.
-  write_parse_error :: (token -> String) -> String -> Parse_error' token -> String
-  write_parse_error write_token file_name err =
-    case err of
-      Filter_error line_and_char err' -> err' ++ " at " ++ write_file_name_and_line_and_char file_name line_and_char ++ "."
-      Match_error (Lookahead line_and_char expected found) ->
-        (
-          "Parse error at " ++
-          write_file_name_and_line_and_char file_name line_and_char ++
-          ". Expected [" ++
-          intercalate ", " (elems expected) ++
-          "], found " ++
-          write_maybe_token write_token found ++
-          ".")+{-|+Description: Tokenising and parsing.++* A combinator-based tokeniser with symmetric choice and location tracking.+* An unambiguous parser with symmetric choice and location tracking.+-}+module Parser.Parser (+  Parse_error (..),+  Parser',+  Tokeniser',+  Usage_error (..),+  (<+>),+  add_location,+  add_token,+  filter_parser,+  fmap_filter_parser,+  parse',+  parse_brackets,+  parse_default,+  parse_file_path',+  parse_list,+  parse_location,+  parse_many,+  parse_non_empty_list,+  parse_not,+  parse_some,+  parse_token,+  parse_token',+  parse_with_location) where+  import Control.Lens.Combinators+  import Control.Lens.Operators+  import Control.Monad.Except+  import Control.Monad.RWS.Strict+  import Data.Generics.Labels ()+  import GHC.Generics+  import Parser.Locations+  import Parser.Utilities+  newtype Parser token output error t =+    Parser {run_parser' :: RWST () output (Parser.Parser.State token) (ExceptT (Parse_error error) (Either Usage_error)) t}+  -- | A parser that works on any kind of tokens.+  type Parser' token error = Parser token () error+  -- | Parse errors.+  data Parse_error error = Filter_error error | Parse_error' Location+  data State token = State {state_tokens :: Tokens token, state_lookahead :: Location}+  -- | A tokeniser that works on any kind of custom characters and tokens. The custom character type is useful if you need to+  -- classify characters before tokenisation to simplify patternmatching.+  type Tokeniser' char_class token error = Parser char_class [With_location token] error+  -- | A sequence of tokens with locations. For internal use in the parser.+  data Tokens token = Tokens [With_location token] Location+  -- | Errors that indicate that the parsing library has been used incorrectly.+  data Usage_error = Ambiguity | Attempt_to_recover_a_filter_error | Filter_error_encountered_in_parse_not+  -- | Symmetric choice between two parsers that selects the longest match. Note that if both parsers successfully reach the+  -- same location it will result in an ambiguity error. Also note that you should not attempt to recover a filter error. This+  -- operator is normally associative unless you make a mistake and write an ambiguous parser.+  infixr 3 <+>+  (<+>) :: Parser token output error t -> Parser token output error t -> Parser token output error t+  Parser (RWST parse_0) <+> Parser (RWST parse_1) =+    Parser+      (RWST+        (\ () st ->+          ExceptT+            (do+              (lookahead_0, result_0) <- deconstruct_result (parse_0 () st)+              (lookahead_1, result_1) <- deconstruct_result (parse_1 () st)+              (+                construct_result (max lookahead_0 lookahead_1) <$>+                case (result_0, result_1) of+                  (Nothing, Nothing) -> Right Nothing+                  (Nothing, Just _) -> Right result_1+                  (Just _, Nothing) -> Right result_0+                  (Just (_, tokens_0, _), Just (_, tokens_1, _)) ->+                    case compare (current_location tokens_0) (current_location tokens_1) of+                      LT -> Right result_1+                      EQ -> Left Ambiguity+                      GT -> Right result_0))))+  instance Monoid output => Applicative (Parser token output error) where+    Parser parse_0 <*> Parser parse_1 = Parser (parse_0 <*> parse_1)+    pure x = Parser (return x)+  instance Functor (Parser token output error) where+    fmap f (Parser parse_t) = Parser (f <$> parse_t)+  deriving instance Generic (Parser.Parser.State token)+  instance Monoid output => Monad (Parser token output error) where+    Parser parse_t >>= f = Parser (parse_t >>= run_parser' <$> f)+  deriving instance Show error => Show (Parse_error error)+  deriving instance Show token => Show (Parser.Parser.State token)+  deriving instance Show token => Show (Tokens token)+  deriving instance Show Usage_error+  -- | Parse with location.+  add_location :: Monoid output => Parser token output error t -> Parser token output error (With_location t)+  add_location parse_t = With_location <$> parse_location <*> parse_t+  -- | Add the token to the output.+  add_token :: Tokeniser' char_class token error token -> Tokeniser' char_class token error ()+  add_token tokenise =+    do+      location <- parse_location+      token <- tokenise+      Parser (tell [With_location location token])+  certain_token :: Eq token => token -> token -> Maybe ()+  certain_token token token' =+    do+      check () (token == token')+      return ()+  construct_result ::+    Location -> Maybe (t, Tokens token, output) -> Either (Parse_error error) (t, Parser.Parser.State token, output)+  construct_result lookahead result =+    case result of+      Nothing -> Left (Parse_error' lookahead)+      Just (x, tokens, output) -> Right (x, State tokens lookahead, output)+  -- | Get the location of first token or, if there are none, the end of file. For internal use in the parser.+  current_location :: Tokens token -> Location+  current_location (Tokens tokens end_location) =+    case tokens of+      [] -> end_location+      With_location location _ : _ -> location+  deconstruct_result ::+    (+      ExceptT (Parse_error error) (Either Usage_error) (t, Parser.Parser.State token, output) ->+      Either Usage_error (Location, Maybe (t, Tokens token, output)))+  deconstruct_result (ExceptT maybe_result) =+    do+      result <- maybe_result+      case result of+        Left err ->+          case err of+            Filter_error _ -> Left Attempt_to_recover_a_filter_error+            Parse_error' lookahead -> Right (lookahead, Nothing)+        Right (x, State tokens lookahead, output) -> Right (lookahead, Just (x, tokens, output))+  -- | Filter the parse results - for example, restrict an integer parser to positive numbers. You also have to provide an+  -- error. Note that filter errors, unlike token matching errors, are non-recoverable.+  filter_parser :: (Eq token, Monoid output) =>+    (t -> Bool) -> (Location -> error) -> Parser token output error t -> Parser token output error t+  filter_parser f err =+    fmap_filter_parser+      (\ x ->+        do+          check err (f x)+          Right x)+  -- | Filter and transform the parse results in one operation. You also have to provide an error. Note that filter errors,+  -- unlike matching errors, are non-recoverable.+  fmap_filter_parser :: (Eq token, Monoid output) =>+    (t -> Either (Location -> error) u) -> Parser token output error t -> Parser token output error u+  fmap_filter_parser f parse_t =+    do+      location <- parse_location+      x <- parse_t+      case f x of+        Left err -> Parser (throwError (Filter_error (err location)))+        Right y -> return y+  get_tokens :: Monoid output => Parser token output error (Tokens token)+  get_tokens =+    do+      tokens <- Parser (use #state_tokens)+      Parser (#state_lookahead %= max (current_location tokens))+      return tokens+  -- | Parse the text. You have to provide a function that classifies characters, a function that updates the location after+  -- each character, a tokeniser, a parser and a function that converts parse errors to your preferred type.+  parse' :: forall char_class token error t .+    (+      (Char -> char_class) ->+      (char_class -> Location -> Location) ->+      Tokeniser' char_class token error () ->+      Parser' token error t ->+      (Location -> error) ->+      String ->+      Either Usage_error (Either error t))+  parse' classify_char next_location tokenise_t parse_t parse_error text =+    runExceptT+      (withExceptT+        transform_error+        (do+          let (end_location, text') = execRWS (classify_chars text) () init_location+          ((), tokens) <- run_parser tokenise_t (Tokens text' end_location)+          fst <$> run_parser parse_t (Tokens tokens end_location)))+    where+      classify_chars :: String -> RWS () [With_location char_class] Location ()+      classify_chars text' =+        case text' of+          [] -> return ()+          c : text'' ->+            do+              location <- get+              let char_class = classify_char c+              tell [With_location location char_class]+              modify (next_location char_class)+              classify_chars text''+      transform_error :: Parse_error error -> error+      transform_error err =+        case err of+          Filter_error err' -> err'+          Parse_error' location -> parse_error location+  -- | Parse a term in brackets.+  parse_brackets :: (Eq token, Monoid output) => token -> token -> Parser token output error t -> Parser token output error t+  parse_brackets left_bracket right_bracket parse_t =+    do+      parse_token left_bracket+      x <- parse_t+      parse_token right_bracket+      return x+  -- | Parse something optional or return the default value.+  parse_default :: Monoid output => Parser token output error t -> t -> Parser token output error t+  parse_default parse_t x = return x <+> parse_t+  parse_directory ::+    Eq token => Parser' token error [Back] -> Parser' token error String -> token -> Parser' token error Directory+  parse_directory parse_back parse_name slash_token =+    Directory <$> parse_back <*> parse_many (parse_one_directory parse_name slash_token)+  parse_element :: (Eq token, Monoid output) => token -> Parser token output error t -> Parser token output error t+  parse_element separator parse_t =+    do+      parse_token separator+      parse_t+  parse_end :: Monoid output => Parser token output error t -> Parser token output error t+  parse_end parse_t =+    do+      x <- parse_t+      parse_end'+      return x+  parse_end' :: Monoid output => Parser token output error ()+  parse_end' =+    do+      tokens <- get_tokens+      case tokens_ended tokens of+        False -> parse_error'+        True -> return ()+  parse_error' :: Monoid output => Parser token output error t+  parse_error' =+    do+      lookahead <- Parser (use #state_lookahead)+      Parser (throwError (Parse_error' lookahead))+  -- | Parse a file path. You have to provide a parent directory counter parser, a name parser, the slash token, the dot token,+  -- a function that converts strings to name tokens and the file extension.+  parse_file_path' :: Eq token =>+    (+      Parser' token error [Back] ->+      Parser' token error String ->+      token ->+      token ->+      (String -> token) ->+      String ->+      Parser' token error File_path)+  parse_file_path' parse_back parse_name slash_token dot_token name_token ext =+    do+      directory <- parse_directory parse_back parse_name slash_token+      file_name <- parse_name+      parse_token dot_token+      parse_token (name_token ext)+      return (File_path directory file_name ext)+  -- | Parse a (possibly empty) list with separators.+  parse_list :: (Eq token, Monoid output) => token -> Parser token output error t -> Parser token output error [t]+  parse_list separator parse_t = parse_default (parse_non_empty_list separator parse_t) []+  -- | Get the current location.+  parse_location :: Monoid output => Parser token output error Location+  parse_location = current_location <$> Parser (use #state_tokens)+  -- | Parse a (possibly empty) list without separators.+  parse_many :: Monoid output => Parser token output error t -> Parser token output error [t]+  parse_many parse_t = parse_default (parse_some parse_t) []+  -- | Parse a non-empty list with separators.+  parse_non_empty_list :: (Eq token, Monoid output) => token -> Parser token output error t -> Parser token output error [t]+  parse_non_empty_list separator parse_t = (:) <$> parse_t <*> parse_many (parse_element separator parse_t)+  -- | Succeed if the parser fails.+  parse_not :: Monoid output => Parser token output error t -> Parser token output error ()+  parse_not (Parser (RWST parse_t)) =+    Parser+      (RWST+        (\ () st ->+          ExceptT+            (do+              result <- runExceptT (parse_t () st)+              case result of+                Left err ->+                  case err of+                    Filter_error _ -> Left Filter_error_encountered_in_parse_not+                    Parse_error' location -> Right (Right ((), st {state_lookahead = location}, mempty))+                Right (_, State _ lookahead', _) -> Right (Left (Parse_error' lookahead')))))+  parse_one_directory :: Eq token => Parser' token error t -> token -> Parser' token error t+  parse_one_directory parse_name slash_token =+    do+      directory <- parse_name+      parse_token slash_token+      return directory+  -- | Parse a non-empty list without separators.+  parse_some :: Monoid output => Parser token output error t -> Parser token output error [t]+  parse_some parse_t = (:) <$> parse_t <*> parse_many parse_t+  -- | Parse a certain token (for example, a delimiter or a keyword) without returning a result.+  parse_token :: (Eq token, Monoid output) => token -> Parser token output error ()+  parse_token token = parse_token' (certain_token token)+  -- | Parse tokens that fit a certain pattern and transform them into something more useful - for example, an int or a string.+  parse_token' :: Monoid output => (token -> Maybe t) -> Parser token output error t+  parse_token' f =+    do+      tokens <- get_tokens+      case take_token f tokens of+        Nothing -> parse_error'+        Just (x, tokens') ->+          do+            Parser (#state_tokens .= tokens')+            return x+  -- | Parse data with location.+  parse_with_location :: Monoid output => Parser token output error t -> Parser token output error (With_location t)+  parse_with_location parse_t = With_location <$> parse_location <*> parse_t+  run_parser :: Monoid output =>+    Parser token output error t -> Tokens token -> ExceptT (Parse_error error) (Either Usage_error) (t, output)+  run_parser parse_t tokens = evalRWST (run_parser' (parse_end parse_t)) () (Parser.Parser.State tokens init_location)+  take_token :: (token -> Maybe t) -> Tokens token -> Maybe (t, Tokens token)+  take_token f (Tokens tokens end_location) =+    case tokens of+      [] -> Nothing+      With_location _ token : tokens' -> flip (,) (Tokens tokens' end_location) <$> f token+  tokens_ended :: Tokens token -> Bool+  tokens_ended (Tokens tokens _) = null tokens
− Parser/Tokeniser.hs
@@ -1,141 +0,0 @@-{-# OPTIONS_GHC -Wall #-}
-{-# LANGUAGE StandaloneDeriving #-}
-{-|
-Description: A simple state- and transition-based tokeniser with location tracking.
-
-* Tokeniser
--}
-module Parser.Tokeniser (
-  Tokeniser',
-  Tokens',
-  add_token,
-  current_line_and_char,
-  delete_char,
-  gather_token,
-  get_char,
-  get_token,
-  take_token,
-  tokenisation_error,
-  tokenise,
-  tokens_ended) where
-  import Control.Monad.Except (MonadError (..))
-  import Control.Monad.RWS.Strict (RWST, execRWST)
-  import Control.Monad.Reader (MonadReader (..))
-  import Control.Monad.State.Strict (MonadState (..))
-  import Control.Monad.Writer.Strict (MonadWriter (..))
-  import Parser.Errors (Error (..))
-  import Parser.Line_and_char (L (..), Line_and_char, init_line_and_char)
-  data State char_class = State {state_line_and_char :: Line_and_char, state_text :: [char_class]}
-  -- | A tokeniser that works with any kind of custom characters, tokens and errors. The custom character type is useful if you
-  -- need to classify characters according to their behavior before tokenisation - for example, wrap all operators, letters,
-  -- delimiters or digits in the same constructor to simplify pattern matching.
-  data Tokeniser' char_class token err t =
-    Tokeniser {run_tokeniser :: RWST (char_class -> Line_and_char -> Line_and_char) [L token] (State char_class) (Either err) t}
-  -- | A sequence of tokens with locations. For internal use in the parser.
-  data Tokens' token = Tokens [L token] Line_and_char
-  instance Applicative (Tokeniser' char_class token err) where
-    Tokeniser tokenise_0 <*> Tokeniser tokenise_1 = Tokeniser (tokenise_0 <*> tokenise_1)
-    pure x = Tokeniser (return x)
-  instance Functor (Tokeniser' char_class token err) where
-    fmap f (Tokeniser tokenise') = Tokeniser (f <$> tokenise')
-  instance Monad (Tokeniser' char_class token err) where
-    Tokeniser tokenise' >>= f = Tokeniser (tokenise' >>= run_tokeniser <$> f)
-  deriving instance Show char_class => Show (State char_class)
-  deriving instance Show token => Show (Tokens' token)
-  -- | Add the token to the output. Note that the order of adding tokens is important and you have to add the token before
-  -- deleting the respective characters to get the correct location.
-  add_token :: token -> Tokeniser' char_class token err ()
-  add_token token =
-    do
-      line_and_char <- state_line_and_char <$> Tokeniser get
-      add_token' line_and_char token
-  add_token' :: Line_and_char -> token -> Tokeniser' char_class token err ()
-  add_token' line_and_char token = Tokeniser (tell [L line_and_char token])
-  -- | Get the location of the first token or, if there are none, the end of file. For internal use in the parser.
-  current_line_and_char :: Tokens' token -> Line_and_char
-  current_line_and_char (Tokens tokens end_line_and_char) =
-    case tokens of
-      [] -> end_line_and_char
-      L line_and_char _ : _ -> line_and_char
-  -- | Delete the first character from the remaining text. Automatically updates the location.
-  delete_char :: Tokeniser' char_class token err ()
-  delete_char =
-    do
-      next_line_and_char <- Tokeniser ask
-      State line_and_char text <- Tokeniser get
-      case text of
-        [] -> return ()
-        char_class : text' -> Tokeniser (put (State (next_line_and_char char_class line_and_char) text'))
-  -- | Add a token that consists of several characters - for example, an operator, a word or a number. You have to provide a
-  -- function that recognises suitable characters and a function that transforms the resulting string into a token.
-  gather_token :: (char_class -> Maybe Char) -> (String -> token) -> Tokeniser' char_class token err ()
-  gather_token recognise_char string_to_token =
-    do
-      line_and_char <- state_line_and_char <$> Tokeniser get
-      token <- gather_token' recognise_char
-      add_token' line_and_char (string_to_token token)
-  gather_token' :: (char_class -> Maybe Char) -> Tokeniser' char_class token err String
-  gather_token' recognise_char =
-    let
-      f = gather_token' recognise_char
-    in
-      do
-        maybe_char_class <- get_char 0
-        case maybe_char_class >>= recognise_char of
-          Nothing -> return ""
-          Just char ->
-            do
-              delete_char
-              token <- f
-              return (char : token)
-  -- | Take a look at a character without deleting it. Returns @Nothing@ if the index is negative or if the remaining text is
-  -- too short.
-  get_char :: Int -> Tokeniser' char_class token err (Maybe char_class)
-  get_char i =
-    do
-      text <- state_text <$> Tokeniser get
-      return
-        (case 0 <= i && i < length text of
-          False -> Nothing
-          True -> Just (text !! i))
-  -- | Get the first token without deleting it. For internal use in the parser.
-  get_token :: Tokens' token -> Maybe token
-  get_token (Tokens tokens _) =
-    case tokens of
-      [] -> Nothing
-      L _ token : _ -> Just token
-  -- | Recognises tokens that fit a certain pattern and transforms them into something more useful - for example, a string or an
-  -- integer. Returns @Nothing@ if the first token does not fit the pattern, and returns the transformed token and the rest of
-  -- the sequence if it does fit. For internal use in the parser.
-  take_token :: (token -> Maybe t) -> Tokens' token -> Maybe (t, Tokens' token)
-  take_token f (Tokens tokens end_line_and_char) =
-    case tokens of
-      [] -> Nothing
-      L _ token : tokens' ->
-        do
-          x <- f token
-          return (x, Tokens tokens' end_line_and_char)
-  -- | Throw a tokenisation error at the current location.
-  tokenisation_error :: (Line_and_char -> err) -> Tokeniser' char_class token err t
-  tokenisation_error err =
-    do
-      line_and_char <- state_line_and_char <$> Tokeniser get
-      Tokeniser (throwError (err line_and_char))
-  -- | Tokenise the text. For internal use in the parser.
-  tokenise ::
-    (
-      (Char -> char_class) ->
-      (char_class -> Line_and_char -> Line_and_char) ->
-      Tokeniser' char_class token err () ->
-      String ->
-      Either Error (Either err (Tokens' token)))
-  tokenise classify_char next_line_and_char (Tokeniser tokenise') text =
-    case execRWST tokenise' next_line_and_char (State init_line_and_char (classify_char <$> text)) of
-      Left err -> Right (Left err)
-      Right (State line_and_char text', tokens) ->
-        case text' of
-          [] -> Right (Right (Tokens tokens line_and_char))
-          _ -> Left Incomplete_tokenisation
-  -- | Check whether the sequence of tokens has ended. For internal use in the parser.
-  tokens_ended :: Tokens' token -> Bool
-  tokens_ended (Tokens tokens _) = null tokens
− Parser/Transf.hs
@@ -1,40 +0,0 @@-{-# OPTIONS_GHC -Wall #-}
-{-|
-Description: Type synonyms and run functions for pairwise compositions of reader, writer and state transformers.
-
-* RST
-* RWT
-* WST
--}
-module Parser.Transf (RST, RWT, WST, evalRST, evalWST, execRST, execRWT, execWST, runRST, runRWT, runWST) where
-  import Control.Monad.RWS.Strict (RWST (..))
-  -- | The composition of reader and state transformers.
-  type RST env = RWST env ()
-  -- | The composition of reader and writer transformers.
-  type RWT env res = RWST env res ()
-  -- | The composition of writer and state transformers.
-  type WST = RWST ()
-  -- | Discards the end state.
-  evalRST :: Functor f => RST env state f t -> env -> state -> f t
-  evalRST (RWST f) env st = (\(x, _, _) -> x) <$> f env st
-  -- | Discards the end state.
-  evalWST :: Functor f => WST res state f t -> state -> f (t, res)
-  evalWST (RWST f) st = (\(x, _, res) -> (x, res)) <$> f () st
-  -- | Discards the output.
-  execRST :: Functor f => RST env state f t -> env -> state -> f state
-  execRST (RWST f) env st = (\(_, st', _) -> st') <$> f env st
-  -- | Discards the output.
-  execRWT :: Functor f => RWT env res f t -> env -> f res
-  execRWT (RWST f) env = (\(_, (), res) -> res) <$> f env ()
-  -- | Discards the output.
-  execWST :: Functor f => WST res state f t -> state -> f (state, res)
-  execWST (RWST f) st = (\(_, st', res) -> (st', res)) <$> f () st
-  -- | Runs the RS transformer.
-  runRST :: Functor f => RST env state f t -> env -> state -> f (t, state)
-  runRST (RWST f) env st = (\(x, st', ()) -> (x, st')) <$> f env st
-  -- | Runs the RW transformer.
-  runRWT :: Functor f => RWT env res f t -> env -> f (t, res)
-  runRWT (RWST f) env = (\(x, (), res) -> (x, res)) <$> f env ()
-  -- | Runs the WS transformer.
-  runWST :: WST res state f t -> state -> f (t, state, res)
-  runWST (RWST f) = f ()
+ Parser/Utilities.hs view
@@ -0,0 +1,131 @@+{-|+Description: Assorted utilities.++* Assorted utilities.+* Relative directories and file paths.+-}+module Parser.Utilities (+  Back (..),+  Directory (..),+  File_path (..),+  (<->),+  (<//>),+  all_equal,+  between,+  check,+  check_ext,+  construct_map,+  construct_set,+  drop_file_name,+  element_at,+  lcm_all,+  read_file,+  swap_either,+  write_file_path) where+  import Control.Monad.Except+  import Data.Foldable as Foldable+  import Data.List as List+  import Data.Map as Map+  import Data.Set as Set+  import System.Directory+  -- | Parent directory.+  data Back = Back+  -- | Relative directory.+  data Directory = Directory [Back] [String]+  -- | Relative file path.+  data File_path = File_path Directory String String+  -- | Concatenate strings with a whitespace in between.+  infixr 5 <->+  (<->) :: String -> String -> String+  s <-> t = s <> " " <> t+  -- | Prepend a directory to file path.+  infixr 6 <//>+  (<//>) :: Directory -> File_path -> File_path+  directory_0 <//> File_path directory_1 file_name ext = File_path (directory_0 <> directory_1) file_name ext+  deriving instance Eq Back+  deriving instance Eq Directory+  deriving instance Eq File_path+  instance Monoid Directory where+    mempty = Directory [] []+  deriving instance Ord Back+  deriving instance Ord Directory+  deriving instance Ord File_path+  instance Semigroup Directory where+    Directory back_0 directories_0 <> Directory back_1 directories_1 =+      case (Foldable.length directories_0, Foldable.length back_1) of+        (0, 0) -> Directory back_0 directories_1+        (0, _) -> Directory (back_0 <> back_1) directories_1+        (_, 0) -> Directory back_0 (directories_0 <> directories_1)+        (_, _) -> Directory back_0 (init directories_0) <> Directory (List.drop 1 back_1) directories_1+  deriving instance Show Back+  deriving instance Show Directory+  deriving instance Show File_path+  -- | Checks if all elements of a list are equal. Returns @Nothing@ if all elements aren't equal, @Just Nothing@ if the list is+  -- empty and @Just (Just x)@ if all elements are equal to @x@.+  all_equal :: Eq t => [t] -> Maybe (Maybe t)+  all_equal x =+    case x of+      [] -> Just Nothing+      y : x' ->+        do+          check () (all ((==) y) x')+          Just (Just y)+  -- | Check if the value is in bounds.+  between :: Ord t => t -> t -> t -> Bool+  between lowest highest x = lowest <= x && highest >= x+  -- | Throw an error if the condition is not satisfied.+  check :: MonadError error f => error -> Bool -> f ()+  check err condition =+    case condition of+      False -> throwError err+      True -> return ()+  -- | Check file path extension.+  check_ext :: String -> File_path -> Bool+  check_ext ext (File_path _ _ ext') = ext == ext'+  -- | Construct a map if all keys are different. Otherwise return @Nothing@.+  construct_map :: Ord t => [(t, u)] -> Maybe (Map t u)+  construct_map x =+    do+      let y = Map.fromList x+      check () (Foldable.length x == Map.size y)+      Just y+  -- | Construct a set if all elements are different. Otherwise return @Nothing@.+  construct_set :: Ord t => [t] -> Maybe (Set t)+  construct_set x =+    do+      let y = Set.fromList x+      check () (Foldable.length x == Set.size y)+      Just y+  -- | Get the directory part of a file path.+  drop_file_name :: File_path -> Directory+  drop_file_name (File_path directory _ _) = directory+  -- | Safe list indexation.+  element_at :: Int -> [t] -> Maybe t+  element_at j x =+    do+      check () (between 0 (Foldable.length x - 1) j)+      return (x !! j)+  -- | Aggregate least common denominator.+  lcm_all :: (Foldable f, Integral t) => f t -> t+  lcm_all = Foldable.foldr lcm 1+  -- | Read a file.+  read_file :: File_path -> IO (Maybe String)+  read_file file_path =+    do+      let file_path' = write_file_path file_path+      pathExists <- doesPathExist file_path'+      case pathExists of+        False -> return Nothing+        True -> Just <$> readFile file_path'+  -- | Swap Left and Right.+  swap_either :: Either t u -> Either u t+  swap_either x =+    case x of+      Left y -> Right y+      Right y -> Left y+  write_back :: Back -> String+  write_back Back = ".."+  -- | Convert a file path to text.+  write_file_path :: File_path -> FilePath+  write_file_path (File_path (Directory back directories) file_name ext) =+    intercalate "/" ((write_back <$> back) <> directories <> [file_name <> "." <> ext])