packages feed

language-lua-0.9.0: src/Language/Lua/Annotated/Lexer.x

{

{-# OPTIONS_GHC -w #-}
{-# LANGUAGE BangPatterns #-}

module Language.Lua.Annotated.Lexer
  ( llex
  , llexNamed
  , llexNamedWithWhiteSpace
  , llexFile
  , LexToken(..)
  , SourcePos(..)
  , dropWhiteSpace
  ) where

import           Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.IO as Text

import           Language.Lua.Token
import           Language.Lua.LexerUtils

}

$letter      = [a-zA-Z_]                -- first letter of variables
$identletter = [a-zA-Z_0-9]             -- letters for rest of variables

$digit    = 0-9                         -- decimal digits
$hexdigit = [0-9a-fA-F]                 -- hexadecimal digits

$dqstr    = . # [ \" \\ ]               -- valid character in a string literal with dquotes
$sqstr    = . # [ \' \\ ]               -- valid character in a string literal with quotes
$longstr  = [ . \n ]                    -- valid character in a long string

-- escape characters
@charesc   = \\ ([ntvbrfa\\"'] | $digit{1,3} | x$hexdigit{2} | u\{$hexdigit+\} | \n | z $white*)

@digits    = $digit+
@hexdigits = $hexdigit+

@mantpart = @digits | @digits \. @digits | @digits \. | \. @digits
@exppart  = [eE][\+\-]? @digits

@hexprefix   = 0x | 0X
@mantparthex = @hexdigits | @hexdigits \. @hexdigits | @hexdigits \. | \. @hexdigits
@expparthex  = [pP][\+\-]? @hexdigits

tokens :-

    <0> $white+    { tok TokWhiteSpace }

    -- keywords
    <0> "and"      { tok TokAnd }
    <0> "break"    { tok TokBreak }
    <0> "do"       { tok TokDo }
    <0> "else"     { tok TokElse }
    <0> "elseif"   { tok TokElseIf }
    <0> "end"      { tok TokEnd }
    <0> "false"    { tok TokFalse }
    <0> "for"      { tok TokFor }
    <0> "function" { tok TokFunction }
    <0> "goto"     { tok TokGoto }
    <0> "if"       { tok TokIf }
    <0> "in"       { tok TokIn }
    <0> "local"    { tok TokLocal }
    <0> "nil"      { tok TokNil }
    <0> "not"      { tok TokNot }
    <0> "or"       { tok TokOr }
    <0> "repeat"   { tok TokRepeat }
    <0> "return"   { tok TokReturn }
    <0> "then"     { tok TokThen }
    <0> "true"     { tok TokTrue }
    <0> "until"    { tok TokUntil }
    <0> "while"    { tok TokWhile }

    -- identifiers
    <0> $letter $identletter* { tok TokIdent }

    -- number literals
    <0> @mantpart @exppart?                  { tok TokNum }
    <0> @hexprefix @mantparthex @expparthex? { tok TokNum }

    <0> \'                   { enterSingleString }
    <state_sstring> @charesc ;
    <state_sstring> $sqstr   ;
    <state_sstring> \'       { endMode }

    -- string literals
    <0> \"                   { enterDoubleString }
    <state_dstring> @charesc ;
    <state_dstring> $dqstr   ;
    <state_dstring> \"       { endMode }

    -- long strings
    <0> \[ =* \[              { enterLongString }
    <state_lstring> \] =* \] / { endStringPredicate } { endMode }
    <state_lstring> $longstr  ;

    -- comments
    <0> "--" \[ =* \[       { enterLongComment }
    <0> "--"                { enterComment }
    <state_comment> .*      { endMode }

    -- operators
    <0> "+"   { tok TokPlus }
    <0> "-"   { tok TokMinus }
    <0> "*"   { tok TokStar }
    <0> "/"   { tok TokSlash }
    <0> "//"  { tok TokDSlash }
    <0> "%"   { tok TokPercent }
    <0> "^"   { tok TokExp }
    <0> "#"   { tok TokSh }
    <0> "=="  { tok TokEqual }
    <0> "~="  { tok TokNotequal }
    <0> "<="  { tok TokLEq }
    <0> ">="  { tok TokGEq }
    <0> "<"   { tok TokLT }
    <0> ">"   { tok TokGT }
    <0> "="   { tok TokAssign }
    <0> "("   { tok TokLParen }
    <0> ")"   { tok TokRParen }
    <0> "{"   { tok TokLBrace }
    <0> "}"   { tok TokRBrace }
    <0> "["   { tok TokLBracket }
    <0> "]"   { tok TokRBracket }
    <0> "::"  { tok TokDColon }
    <0> ";"   { tok TokSemic }
    <0> ":"   { tok TokColon }
    <0> ","   { tok TokComma }
    <0> "."   { tok TokDot }
    <0> ".."  { tok TokDDot }
    <0> "..." { tok TokEllipsis }
    <0> "&"   { tok TokAmpersand }
    <0> "|"   { tok TokPipe }
    <0> "~"   { tok TokTilde }
    <0> "<<"  { tok TokDLT }
    <0> ">>"  { tok TokDGT }

    <0,state_sstring,state_dstring,state_lstring>
       . | \n    { unexpectedChar }

{

-- | Map a lexer 'Mode' an an alex state code.
modeCode :: Mode -> Int
modeCode mode =
  case mode of
    NormalMode        -> 0
    CommentMode    {} -> state_comment
    QuoteMode      {} -> state_lstring
    SingleQuoteMode{} -> state_sstring
    DoubleQuoteMode{} -> state_dstring

scanner' :: AlexInput -> Mode -> [LexToken SourcePos]
scanner' inp mode =
  case alexScanUser mode inp (modeCode mode) of
    AlexEOF                   -> abortMode Nothing mode
    AlexError _               -> error "language-lua lexer internal error"
    AlexSkip inp' _           -> scanner' inp' mode
    AlexToken inp' len action ->
       case action len inp mode of
         (mode', ts) -> ts ++ scanner' inp' mode'

scanner :: String -> Text -> [LexToken SourcePos]
scanner name str = scanner' (AlexInput (startPos name) str) NormalMode

-- | Lua lexer with default @=<string>@ name.
llex :: Text {- ^ chunk -} -> [LexToken SourcePos]
llex = llexNamed "=<string>"


-- | Lua lexer with explicit name.
llexNamed ::
  String {- ^ name -} ->
  Text   {- ^ chunk -} ->
  [LexToken SourcePos]
llexNamed name chunk = dropWhiteSpace (llexNamedWithWhiteSpace name chunk)


-- | Lua lexer with explicit name, preseves white space and comments.
llexNamedWithWhiteSpace ::
  String {- ^ name -} ->
  Text   {- ^ chunk -} ->
  [LexToken SourcePos]
llexNamedWithWhiteSpace name chunk = scanner name (dropSpecialComment chunk)


-- | Run Lua lexer on a file.
llexFile :: FilePath -> IO [LexToken SourcePos]
llexFile fp = fmap (llexNamed fp) (Text.readFile fp)

}