lhae-0.0.3: src/Model/CellExpression/Parser.hs
module Model.CellExpression.Parser (parse)
where
import Data.List (find,intercalate,nub)
import Control.Applicative ((<$>))
import Text.ParserCombinators.Parsec ((<|>),(<?>),Parser,try)
import qualified Text.ParserCombinators.Parsec as P
import qualified Text.ParserCombinators.Parsec.Error as Error
import qualified Text.ParserCombinators.Parsec.Expr as E
import qualified Text.ParserCombinators.Parsec.Token as T
import qualified Text.ParserCombinators.Parsec.Language as L
import Model.CellContent (CellExpr (..),CellReference(..)
,NamedReference(..),CompileReason(..))
import qualified Model.CellContent as CC
import I18n (__)
program,topLevelFormula,topLevelLiteral :: Parser CellExpr
expression,term,functionCall,constant :: Parser CellExpr
number,stringExpr,subExpression :: Parser CellExpr
list,topLevelList :: Parser CellExpr
ifThenElse,reference,miscRef,cellRangeRef :: Parser CellExpr
program = topLevelFormula <|> topLevelLiteral
topLevelLiteral = (try ( topLevelParser number
<|> topLevelList))
<|> (StringExpr <$> (P.many1 P.anyChar))
topLevelParser :: Parser CellExpr -> Parser CellExpr
topLevelParser parser = do
result <- whitespace >> parser
P.eof >> return result
topLevelFormula = do
f <- P.char '=' >> whitespace >> expression
P.eof >> return f
expression = E.buildExpressionParser table term <?> __ "formula"
table :: E.OperatorTable Char () CellExpr
table = [[ prefix "-" CC.UnaryOp]
,[ binary "^" CC.BinaryOp E.AssocRight]
,[ binary "*" CC.BinaryOp E.AssocLeft
, binary "/" CC.BinaryOp E.AssocLeft]
,[ binary "+" CC.BinaryOp E.AssocLeft
, binary "-" CC.BinaryOp E.AssocLeft]
,[ binary "==" CC.BinaryOp E.AssocLeft
, binary "/=" CC.BinaryOp E.AssocLeft
, binary ">" CC.BinaryOp E.AssocLeft
, binary ">=" CC.BinaryOp E.AssocLeft
, binary "<" CC.BinaryOp E.AssocLeft
, binary "<=" CC.BinaryOp E.AssocLeft]
,[ binary "&&" CC.BinaryOp E.AssocLeft]
,[ binary "||" CC.BinaryOp E.AssocLeft]
]
where
binary sign f = E.Infix $ do {reservedOp sign; return $ f sign}
prefix sign f = E.Prefix $ do {reservedOp sign; return $ f sign}
term = subExpression <|> ifThenElse <|> try functionCall <|> constant
<|> number <|> list <|> stringExpr <|> reference <?> __ "formula"
functionCall = do { name <- identifier
; arg <- number <|> stringExpr <|> list
<|> reference <|> subExpression
<|> constant <?> __ "argument"
; return $ Call name arg
} <?> __ "function call"
constant = (Constant <$> identifier) <?> __ "constant"
number = (NumberExpr <$> signedDouble) <?> __ "number"
stringExpr = (StringExpr <$> stringLiteral) <?> __ "string"
listParser :: Parser CellExpr -> Parser CellExpr
listParser parser =
do { reservedOp "["
; l <- P.sepBy1 parser $ reservedOp ","
; reservedOp "]"
; return $ ListExpr l
} <?> "list"
list = listParser expression
topLevelList = listParser $ constant <|> number
reference = try cellRangeRef <|> try miscRef <?> __ "reference"
miscRef = do { string "$"
; row <- P.many1 (P.noneOf [','])
; string ","
; column <- P.many1 (P.noneOf ['$'])
; string "$"
; whitespace
; return $ NamedReference $
case (row,column) of
("_",c) -> NamedColumn c
(r,"_") -> NamedRow r
("=",c) -> NamedCell $ SameRow c
(r,"=") -> NamedCell $ SameColumn r
_ -> NamedCell $ Named (row,column)
} <?> __ "cell reference"
cellRangeRef = do { NamedReference (NamedCell from) <- miscRef
; symbol ":"
; NamedReference (NamedCell to) <- miscRef
; return $ NamedReference $ NamedRange (from,to)
} <?> __ "cell range reference"
subExpression = do { subExpr <- P.between (symbol "(") (symbol ")") expression
; return $ Sub subExpr
} <?> __ "subexpression"
ifThenElse = do { condition <- reserved "if" >> expression
; thenBranch <- reserved "then" >> expression
; elseBranch <- P.option EmptyExpr
$ reserved "else" >> expression
; return $ IfThenElse condition thenBranch elseBranch
} <?> __ "if-then-else expression"
lexer :: T.TokenParser ()
lexer =
let def = L.emptyDef
{ L.opStart = P.oneOf "+-*/~^:[,]=<>&|"
, L.opLetter = L.opStart def
, L.reservedOpNames = ["+","-","*","/","~","^",":","[",",","]"
,"=","==","/=","<","<=",">",">=","&&","||"]
, L.reservedNames = ["if","then","else"]}
in
T.makeTokenParser def
signedDouble :: Parser Double
signedDouble =
let double = do f <- T.naturalOrFloat lexer
case f of
Left i -> return $ realToFrac i
Right d -> return d
signed = symbol "-" >> ((*) (-1)) <$> double
in
P.choice [signed,double]
reserved :: String -> Parser ()
reserved = T.reserved lexer
reservedOp :: String -> Parser ()
reservedOp = T.reservedOp lexer
whitespace :: Parser ()
whitespace = T.whiteSpace lexer
symbol :: String -> Parser ()
symbol s = T.symbol lexer s >> return ()
identifier :: Parser String
identifier = T.identifier lexer
stringLiteral :: Parser String
stringLiteral = T.stringLiteral lexer
string :: String -> Parser ()
string s = P.string s >> return ()
parse :: String -> CellExpr
parse "" = EmptyExpr
parse input =
case (P.parse program "" input) of
Left e -> CompileErrorExpr $ ParseError $ errorMessage e
Right x -> x
errorMessage :: Error.ParseError -> String
errorMessage parseError =
let errors = Error.errorMessages parseError
Just unexpected = find (\e -> case e of
Error.SysUnExpect _ -> True
Error.UnExpect _ -> True
_ -> False) errors
expected = filter (\e -> case e of
Error.Expect "" -> False
Error.Expect _ -> True
_ -> False
) errors
message e =
case e of
Error.SysUnExpect "" -> __ "end of input"
Error.SysUnExpect a -> a
Error.UnExpect "" -> __ "end of input"
Error.UnExpect a -> a
a -> Error.messageString a
unexpectedMessage = unwords [ (__ "unexpected") ++ ":"
, message unexpected]
expectedMessage =
case expected of
[] -> ""
_ -> (__ "expecting") ++ " " ++
(intercalate " or " $ nub $ map message expected)
in
case expectedMessage of
"" -> unexpectedMessage
_ -> unexpectedMessage ++ ", " ++ expectedMessage
-- ++ "#####" ++ show parseError