dtab-1.0: src/Data/DTA/Lex.x
{
-- | Generated lexer for text @.dta@ files.
{-# OPTIONS_GHC -w #-}
module Data.DTA.Lex (scan, Token(..), AlexPosn(..)) where
import qualified Data.ByteString.Char8 as B8
import Data.Int (Int32)
}
%wrapper "posn"
$digit = 0-9
$alpha = [a-zA-Z]
tokens :-
-- Whitespace and line comments.
$white+ ;
\; [^\n]* ;
-- Numbers. Longest match rule means N.N is float, not int.
\-? $digit+ { \pn str -> (pn, Int $ read str) }
\-? $digit+ (\. $digit+)? (e \-? $digit+)? { \pn str -> (pn, Float $ read str) }
-- Variable names.
\$ ($alpha | $digit | _)+ { \pn str -> (pn, Var $ B8.pack $ tail str) }
-- This reserved word needs to come before the general keyword rule.
"kDataUnhandled" { \pn _ -> (pn, Unhandled) }
-- Raw keywords. Note: these can start with digits, like "3sand7s", as long as
-- they also have letters in them.
($alpha | $digit | _ | \/ | \.)+ { \pn str -> (pn, Key $ B8.pack str) }
-- Quoted keywords.
' ([^'] | \\')* ' { \pn str -> (pn, Key $ B8.pack $ getKeyword str) }
-- Quoted strings.
\" ([^\"] | \\\")* \" { \pn str -> (pn, String $ B8.pack $ read str) }
-- Preprocessor commands.
\#ifdef { \pn _ -> (pn, IfDef) }
\#else { \pn _ -> (pn, Else) }
\#endif { \pn _ -> (pn, EndIf) }
\#define { \pn _ -> (pn, Define) }
\#include { \pn _ -> (pn, Include) }
\#merge { \pn _ -> (pn, Merge) }
\#ifndef { \pn _ -> (pn, IfNDef) }
-- Subtrees.
\( { \pn _ -> (pn, LParen) }
\) { \pn _ -> (pn, RParen) }
\{ { \pn _ -> (pn, LBrace) }
\} { \pn _ -> (pn, RBrace) }
\[ { \pn _ -> (pn, LBracket) }
\] { \pn _ -> (pn, RBracket) }
{
data Token
= Int Int32
| Float Float
| Var B8.ByteString
| Key B8.ByteString
| Unhandled
| IfDef
| Else
| EndIf
| LParen
| RParen
| LBrace
| RBrace
| String B8.ByteString
| LBracket
| RBracket
| Define
| Include
| Merge
| IfNDef
deriving (Eq, Ord, Show, Read)
-- | Reads a single-quoted string, by converting it to a double-quoted one.
getKeyword :: String -> String
getKeyword = read . go where
go ('\'':xs) = '"' : go xs -- string begin/end -> double-quote
go ('"':xs) = '\\' : '"' : go xs -- double-quote gets escaped
go ('\\':x:xs) = '\\' : x : go xs -- any escaped char can remain escaped
go (x:xs) = x : go xs -- all other chars are unchanged
go [] = []
scan :: String -> [(AlexPosn, Token)]
scan = alexScanTokens
}