packages feed

toml-parser-1.0.0.0: src/Toml/Lexer/Utils.hs

{-|
Module      : Toml.Lexer.Utils
Description : Wrapper and actions for generated lexer
Copyright   : (c) Eric Mertens, 2023
License     : ISC
Maintainer  : emertens@gmail.com

This module provides a custom engine for the Alex generated
lexer. This lexer drive provides nested states, unicode support,
and file location tracking.

-}
module Toml.Lexer.Utils (
    
    -- * Types
    M, Action,
    Context(..),

    -- * Actions
    value,
    value_,
    token,
    token_,
    enterList,
    exitList,
    enterTable,
    exitTable,
    equals,
    timeValue,

    -- * Alex extension points
    AlexInput,
    alexGetByte,

    ) where

import Control.Monad.Trans.State.Strict (State, modify, state)
import Data.Char (ord, isAscii)
import Data.Foldable (asum)
import Data.Time.Format (parseTimeM, defaultTimeLocale, ParseTime)

import Toml.Located (Located(..))
import Toml.Position (move)
import Toml.Lexer.Token (Token(..))

type M a = State [Context] a

type Action = String -> M Token

data Context
  = ListContext  -- ^ processing an inline list, lex values
  | TableContext -- ^ processing an inline table, don't lex values
  | ValueContext -- ^ processing after an equals, lex one value
  deriving Show

pushContext :: Context -> M ()
pushContext cxt = modify \st ->
  case st of
    ValueContext : st' -> cxt : st'
    _                  -> cxt : st

equals :: Action
equals _ = TokEquals <$ pushContext ValueContext

enterList :: Action
enterList _ = TokSquareO <$ pushContext ListContext

enterTable :: Action
enterTable _ = TokCurlyO <$ pushContext TableContext

exitTable :: Action
exitTable _ = state \case
  TableContext : st -> (TokCurlyC             , st)
  st                -> (TokError "Unmatched }", st)

exitList :: Action
exitList _ = state \case
  ListContext : st -> (TokSquareC            , st)
  []               -> (TokSquareC            , [])
  st               -> (TokError "Unmatched ]", st)

token_ :: Token -> Action
token_ t _ = pure t

token :: (String -> Token) -> Action
token f x = pure (f x)

value_ :: Token -> Action
value_ t _ = emitValue t

value :: (String -> Token) -> Action
value f x = emitValue (f x)

emitValue :: a -> M a
emitValue v = state \st ->
  case st of
    ValueContext:st' -> (v, st')
    _                -> (v, st )

timeValue :: ParseTime a => String -> [String] -> (a -> Token) -> Action
timeValue description patterns constructor = value \str ->
  case asum [parseTimeM False defaultTimeLocale pattern str | pattern <- patterns] of
    Nothing -> TokError ("Malformed " ++ description)
    Just t  -> constructor t

type AlexInput = Located String

alexGetByte :: AlexInput -> Maybe (Int, AlexInput)
alexGetByte Located { locPosition = p, locThing = str } =
  case str of
    "" -> Nothing
    x:xs
      | x == '\1' -> Just (0,     rest)
      | isAscii x -> Just (ord x, rest)
      | otherwise -> Just (1,     rest)
      where
        rest = Located { locPosition = move x p, locThing = xs }