gdritter repos apicius / master Apicius / Lexer.x
master

Tree @master (Download .tar.gz)

Lexer.x @masterraw · history · blame

{
{-# OPTIONS -w #-}
{-# LANGUAGE OverloadedStrings #-}

module Apicius.Lexer where

import           Control.Monad (liftM)
import           Data.Text (Text)
import qualified Data.Text as T

import           Prelude hiding (lex)
}

%wrapper "monadUserState"

$special = [ \{ \} \[ \] \( \) \; \, \+ \& \- \$ ]
$idchar = $printable # $special

tokens :-
  $white+ ;
  "#".* ;

  DONE  { lex' TkDone }

  \{  { lex' TkLCurl }
  \}  { lex' TkRCurl }
  \[  { lex' TkLBrac }
  \]  { lex' TkRBrac }
  \;  { lex' TkSemi  }
  \+  { lex' TkPlus }
  \&  { lex' TkAnd }

  \-\> { lex' TkArrow }

  $idchar + { lex (TkText . T.strip) }

  \$ $idchar + { lex (TkJoin . T.strip) }

{
data Token = Token
  { tkPosn :: AlexPosn
  , tkType :: TkType
  } deriving (Eq, Show)

data TkType
  = TkLCurl
  | TkRCurl
  | TkLBrac
  | TkRBrac
  | TkArrow
  | TkAnd
  | TkSemi
  | TkDone
  | TkPlus
  | TkText Text
  | TkJoin Text
  | TkEOF
    deriving (Eq, Show)

data AlexUserState = AlexUserState
  { filePath  :: FilePath
  , lastToken :: Maybe Token
  , thisToken :: Maybe Token
  } deriving (Eq, Show)

alexInitUserState :: AlexUserState
alexInitUserState = AlexUserState "<unknown>" Nothing Nothing

getFilePath :: Alex FilePath
getFilePath = liftM filePath alexGetUserState

setFilePath :: FilePath -> Alex ()
setFilePath f = do
  userState <- alexGetUserState
  alexSetUserState userState { filePath = f }

getLastToken :: Alex (Maybe Token)
getLastToken = liftM lastToken alexGetUserState

setLastToken :: Token -> Alex ()
setLastToken t = do
  userState <- alexGetUserState
  alexSetUserState userState
    { lastToken = thisToken userState
    , thisToken = Just t
    }

alexMonadScan' :: Alex Token
alexMonadScan' = do
  inp <- alexGetInput
  sc <- alexGetStartCode
  case alexScan inp sc of
    AlexEOF -> alexEOF
    AlexError (p, _, _, s) ->
        alexError' p ("lexical error at character '" ++ take 1 s ++ "'")
    AlexSkip  inp' len -> do
        alexSetInput inp'
        alexMonadScan'
    AlexToken inp' len action -> do
        alexSetInput inp'
        tok <- action (ignorePendingBytes inp) len
        setLastToken tok
        return tok

alexError' :: AlexPosn -> String -> Alex a
alexError' (AlexPn _ l c) msg = do
  fp <- getFilePath
  alexError (fp ++ ":" ++ show l ++ ":" ++ show c ++ ": " ++ msg)

alexEOF :: Alex Token
alexEOF = do
  (p,_,_,_) <- alexGetInput
  return (Token p TkEOF)


lex :: (Text -> TkType) -> AlexAction Token
lex f = \(p,_,_,s) i -> return (Token p (f (T.pack (take i s))))

lex' :: TkType -> AlexAction Token
lex' = lex . const

runAlex' :: Alex a -> FilePath -> String -> Either String a
runAlex' mote fp input = runAlex input (setFilePath fp >> mote)
}