packages feed

egison-3.9.1: hs-src/Language/Egison/Lexer.x

{
{-# OPTIONS -w #-}

{- |
Module      : Language.Egison.Lexer
Copyright   : Satoshi Egi
Licence     : MIT

This module provides new Egison lexer.
-}

module Language.Egison.Lexer
       ( Token(..)
       , AlexPosn(..)
       , TokenClass(..)
       , Alex(..)
       , runAlex'
       , alexMonadScan'
       , alexError
       , alexError'
       ) where

import           Prelude               hiding (lex)
import           Control.Monad         (liftM)

import           Language.Egison.Types

}

%wrapper "monadUserState"
$digit = 0-9
$alpha = [A-Za-z]
tokens :-
  $white+                        ;

  -- Keywords
  True                           { lex' TokenTrue           }
  False                          { lex' TokenFalse          }
  test                           { lex' TokenTest           }
  match                          { lex' TokenMatch          }
  matchDFS                       { lex' TokenMatchDFS       }
  matchAll                       { lex' TokenMatchAll       }
  matchAllDFS                    { lex' TokenMatchAllDFS    }
  matchLambda                    { lex' TokenMatchLambda    }
  matchAllLambda                 { lex' TokenMatchAllLambda }
  as                             { lex' TokenAs             }
  with                           { lex' TokenWith           }
  something                      { lex' TokenSomething      }
  if                             { lex' TokenIf             }
  then                           { lex' TokenThen           }
  else                           { lex' TokenElse           }

  -- Data
  $digit+                        { lex (TokenInt . read)    }
  $alpha [$alpha $digit \']*     { lex  TokenVar            }

  -- Operators
  \=\=                           { lex' TokenEqEq           }
  \<                             { lex' TokenLT             }
  \>                             { lex' TokenGT             }
  \<\=                           { lex' TokenLE             }
  \>\=                           { lex' TokenGE             }
  \+                             { lex' TokenPlus           }
  \-                             { lex' TokenMinus          }
  \%                             { lex' TokenPercent        }
  \*                             { lex' TokenAsterisk       }
  \/                             { lex' TokenDiv            }
  \^                             { lex' TokenCaret          }
  \&\&                           { lex' TokenAndAnd         }
  \|\|                           { lex' TokenBarBar         }
  \:                             { lex' TokenColon          }
  \.\.                           { lex' TokenDotDot         }
  \+\+                           { lex' TokenPlusPlus       }

  \|                             { lex' TokenBar            }
  \-\>                           { lex' TokenArrow          }
  \$                             { lex' TokenDollar         }
  \_                             { lex' TokenUnderscore     }
  \#                             { lex' TokenSharp          }
  \,                             { lex' TokenComma          }
  \\                             { lex' TokenBackSlash      }
  \*\$                           { lex' TokenAstDollar      }
  \=                             { lex' TokenEq             }

  \(                             { lex' TokenLParen         }
  \)                             { lex' TokenRParen         }
  \[                             { lex' TokenLBracket       }
  \]                             { lex' TokenRBracket       }

{
-- To improve error messages, We keep the path of the file we are
-- lexing in our own state.
data AlexUserState = AlexUserState { filePath :: FilePath }

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

getFilePath :: Alex FilePath
getFilePath = liftM filePath alexGetUserState

setFilePath :: FilePath -> Alex ()
setFilePath = alexSetUserState . AlexUserState

-- The token type, consisting of the source code position and a token class.
data Token = Token AlexPosn TokenClass
  deriving ( Show )

data TokenClass
  -- Keywords
  = TokenTrue
  | TokenFalse
  | TokenTest
  | TokenMatch
  | TokenMatchDFS
  | TokenMatchAll
  | TokenMatchAllDFS
  | TokenMatchLambda
  | TokenMatchAllLambda
  | TokenAs
  | TokenWith
  | TokenSomething
  | TokenIf
  | TokenThen
  | TokenElse

  -- Data and Variables
  | TokenInt Integer
  | TokenVar String

  | TokenEqEq
  | TokenLT
  | TokenGT
  | TokenLE
  | TokenGE
  | TokenPlus
  | TokenMinus
  | TokenPercent
  | TokenAsterisk
  | TokenDiv
  | TokenCaret
  | TokenAndAnd
  | TokenBarBar
  | TokenColon
  | TokenDotDot
  | TokenPlusPlus

  | TokenBar
  | TokenArrow
  | TokenDollar
  | TokenUnderscore
  | TokenSharp
  | TokenComma
  | TokenBackSlash
  | TokenAstDollar
  | TokenEq

  | TokenLParen
  | TokenRParen
  | TokenLBracket
  | TokenRBracket
  | TokenEOF

-- For nice parser error messages.
instance Show TokenClass where
  show TokenTrue = "True"
  show TokenFalse = "False"
  show TokenTest = "test"
  show TokenMatch = "match"
  show TokenMatchDFS = "matchDFS"
  show TokenMatchAll = "matchAll"
  show TokenMatchAllDFS = "matchAllDFS"
  show TokenMatchLambda = "matchLambda"
  show TokenMatchAllLambda = "matchAllLambda"
  show TokenAs = "as"
  show TokenWith = "with"
  show TokenSomething = "something"
  show TokenIf = "if"
  show TokenThen = "then"
  show TokenElse = "else"

  show (TokenInt i) = show i
  show (TokenVar s) = show s

  show TokenEqEq = "=="
  show TokenLT = "<"
  show TokenGT = ">"
  show TokenLE = "<="
  show TokenGE = ">="
  show TokenPlus = "+"
  show TokenMinus = "-"
  show TokenPercent = "%"
  show TokenAsterisk = "*"
  show TokenDiv = "/"
  show TokenCaret = "^"
  show TokenAndAnd = "&&"
  show TokenBarBar = "||"
  show TokenColon = ":"
  show TokenDotDot = ".."
  show TokenPlusPlus = "++"

  show TokenBar = "|"
  show TokenArrow = "->"
  show TokenDollar = "$"
  show TokenUnderscore = "_"
  show TokenSharp = "#"
  show TokenComma = ","
  show TokenBackSlash = "\\"
  show TokenAstDollar = "*$"
  show TokenEq = "="

  show TokenLParen = "("
  show TokenRParen = ")"
  show TokenLBracket = "["
  show TokenRBracket = "]"
  show TokenEOF = "<EOF>"

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

-- Unfortunately, we have to extract the matching bit of string
-- ourselves...
lex :: (String -> TokenClass) -> AlexAction Token
lex f = \(p,_,_,s) i -> return $ Token p (f (take i s))

-- For constructing tokens that do not depend on
-- the input
lex' :: TokenClass -> AlexAction Token
lex' = lex . const

-- We rewrite alexMonadScan' to delegate to alexError' when lexing fails
-- (the default implementation just returns an error message).
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'
        action (ignorePendingBytes inp) len

-- Signal an error, including a commonly accepted source code position.
alexError' :: AlexPosn -> String -> Alex a
alexError' (AlexPn _ l c) msg = do
  fp <- getFilePath
  alexError (fp ++ ":" ++ show l ++ ":" ++ show c ++ ": " ++ msg)

-- A variant of runAlex, keeping track of the path of the file we are lexing.
runAlex' :: Alex a -> String -> Either EgisonError a
runAlex' a input =
  case runAlex input (setFilePath "<stdin>" >> a) of
    Left msg -> Left $ Parser msg
    Right r  -> Right r
}