{
module QueryParser (ParseResult, parseCommands, parseFile) where
import Prelude hiding (catch)
import qualified Control.Exception as C
import QueryLexer
import QueryAST
}
%name queryParser
%tokentype { ConstraintToken }
%error { parseError }
%monad { E }
%token
'>' { TL p }
'<' { TG p }
'<=' { TLE p }
'>=' { TGE p }
'!=' { TNEqual p }
'==' { TEqual p }
'(' { TParenOpen p }
')' { TParenClose p }
',' { TComma p }
'*' { TProduct p }
':=' { TAssign p }
';' { TSemiColon p }
as { TAs p }
extension { TExtension p }
language { TLanguage p }
project { TProject p }
filename { TFilename p }
year { TYear p }
month { TMonth p }
day { TDay p }
dow { TDow p }
doy { TDoy p }
group { TGroup p }
nogroup { TDontGroup p }
limit { TLimit p }
asc { TAscending p }
desc { TDescending p }
integer { TInteger p $$ }
string { TString p $$ }
ident { TIdent p $$ }
%%
COMMANDS : MULTICOMMAND { $1 }
| MULTICOMMAND COMMAND { $1 ++ [$2] }
| COMMAND { [$1] }
MULTICOMMAND: MULTICOMMAND SINGLECOMMAND { $1 ++ [$2] }
| SINGLECOMMAND { [$1] }
SINGLECOMMAND : COMMAND ';' { $1 }
COMMAND : QUERY { Left $1 }
| ASSIGNMENT { Right $1 }
ASSIGNMENT : ident ':=' QUERY { QAssign $1 $3 }
QUERY : SUBQUERY { [$1] }
| QUERY '*' SUBQUERY { $1 ++ [$3] }
SUBQUERY : GROUP INDEX CONSTRAINTS AS ORDER LIMIT {% if typeCheckConstraints $2 $3
then return $ QSubQuery $1 $2 $3 $4 $5 $6
else fail "Parse error in constraints: expecting an integer"}
| ident { QCall $1 }
GROUP : group { True }
| nogroup { False }
| { True }
INDEX : extension { Ext }
| language { Lang }
| project { Proj }
| filename { File }
| year { Year }
| month { Month }
| day { Day }
| dow { Dow }
| doy { Doy }
CONSTRAINTS : '(' CONS ')' { $2 }
| CONS { $1 }
| { [] }
CONS : CONSTRAINT { [$1] }
| CONS ',' CONSTRAINT { $1 ++ [$3] }
CONSTRAINT : INDEX OPERATOR EXPR {% typeCheckQC $1 $2 $3 }
| OPERATOR EXPR { QCOE $1 $2 }
| EXPR { QCE $1 }
AS : as string { As $2 }
| { DefaultAs }
OPERATOR : '<' { QL }
| '>' { QG }
| '<=' { QLE }
| '>=' { QGE }
| '==' { QE }
| '!=' { QNE }
EXPR : integer { QInt $1 }
| string { QString $1 }
ORDER : asc { Asc }
| desc { Desc }
| { NoOrder }
LIMIT: limit integer { Limit $2 }
| { NoLimit }
{
typeCheckConstraints i cs = all typeCheck cs
where typeCheck (QC _ _ _) = True
typeCheck (QCOE _ e) = check e
typeCheck (QCE e) = check e
check e = if elem i [Year, Day, Doy]
then case e of { QInt _ -> True ; _ -> False }
else True
typeCheckQC a b c = if elem a [Year, Day, Doy]
then case c of
QInt _ -> return $ QC a b c
QString s -> fail $ "Expecting an integer, but got string \"" ++ s ++ "\""
else (return $ QC a b c)
type ParseResult = E QCommands
parseError :: [ConstraintToken] -> E a
parseCommands :: String -> ParseResult
parseFile :: FilePath -> IO ParseResult
parseError s = fail "Parse error."
parseCommands s = case alexScanTokens' s of
Left err -> fail err
Right a -> queryParser a
parseFile f = readFile f >>= return . parseCommands
}