inchworm-1.1.1.2: Text/Lexer/Inchworm.hs
-- | Parser combinator framework specialized to lexical analysis.
-- Tokens can be specified via simple fold functions,
-- and we include baked in source location handling.
--
-- Matchers for standard tokens like comments and strings
-- are in the "Text.Lexer.Inchworm.Char" module.
--
-- No dependencies other than the Haskell 'base' library.
--
-- If you want to parse expressions instead of performing lexical
-- analysis then try the @parsec@ or @attoparsec@ packages, which
-- have more general purpose combinators.
--
-- __ Minimal example __
--
-- The following code demonstrates how to perform lexical analysis
-- of a simple LISP-like language. We use two separate name classes,
-- one for variables that start with a lower-case letter,
-- and one for constructors that start with an upper case letter.
--
-- Integers are scanned using the `scanInteger` function from the
-- "Text.Lexer.Inchworm.Char" module.
--
-- The result of @scanStringIO@ contains the list of leftover input
-- characters that could not be parsed. In a real lexer you should
-- check that this is empty to ensure there has not been a lexical
-- error.
--
-- @
-- import Text.Lexer.Inchworm.Char
-- import qualified Data.Char as Char
--
-- -- | A source token.
-- data Token
-- = KBra | KKet | KVar String | KCon String | KInt Integer
-- deriving Show
--
-- -- | A thing with attached location information.
-- data Located a
-- = Located FilePath Location a
-- deriving Show
--
-- -- | Scanner for a lispy language.
-- scanner :: FilePath
-- -> Scanner IO Location [Char] (Located Token)
-- scanner fileName
-- = skip Char.isSpace
-- $ alts [ fmap (stamp id) $ accept '(' KBra
-- , fmap (stamp id) $ accept ')' KKet
-- , fmap (stamp KInt) $ scanInteger
-- , fmap (stamp KVar)
-- $ munchWord (\\ix c -> if ix == 0 then Char.isLower c
-- else Char.isAlpha c)
-- , fmap (stamp KCon)
-- $ munchWord (\\ix c -> if ix == 0 then Char.isUpper c
-- else Char.isAlpha c)
-- ]
-- where -- Stamp a token with source location information.
-- stamp k (l, t)
-- = Located fileName l (k t)
--
-- main
-- = do let fileName = "Source.lispy"
-- let source = "(some (Lispy like) 26 Program 93 (for you))"
-- toks <- scanStringIO source (scanner fileName)
-- print toks
-- @
--
module Text.Lexer.Inchworm
( -- * Basic Types
Source
, Scanner
-- * Generic Scanning
, scanList
, scanListIO
-- ** Source Construction
, makeListSourceIO
-- ** Scanner Evaluation
, scanSourceToList
-- * Combinators
-- ** Basic
, satisfies, skip
-- ** Accept
, accept, accepts
-- ** From
, from, froms
-- ** Alternation
, alt, alts
-- ** Munching
, munchPred, munchWord, munchFold)
where
import Text.Lexer.Inchworm.Source
import Text.Lexer.Inchworm.Scanner
import Text.Lexer.Inchworm.Combinator
import System.IO.Unsafe
-- | Scan a list of generic input tokens in the IO monad,
-- returning the source location of the final input token,
-- along with the remaining input.
--
-- NOTE: If you just want to scan a `String` of characters
-- use @scanString@ from "Text.Lexer.Inchworm.Char"
--
scanList
:: Eq i
=> loc -- ^ Starting source location.
-> (i -> loc -> loc) -- ^ Function to bump the current location by one input token.
-> [i] -- ^ List of input tokens.
-> Scanner IO loc [i] a -- ^ Scanner to apply.
-> ([a], loc, [i])
scanList loc bump input scanner
= unsafePerformIO $ scanListIO loc bump input scanner
-- | Implementation for `scanList`,
-- that uses the IO monad to manage its state.
scanListIO
:: Eq i
=> loc -- ^ Starting source location.
-> (i -> loc -> loc) -- ^ Function to bump the current location by one input token.
-> [i] -- ^ List of input tokens.
-> Scanner IO loc [i] a -- ^ Scanner to apply.
-> IO ([a], loc, [i])
scanListIO loc bump input scanner
= do src <- makeListSourceIO loc bump input
scanSourceToList src scanner