-- | A parser for Sifflet input values. -- This is not a parser for all Sifflet expressions, -- but just those that might be input in textual form -- through the function call dialog that asks for the argument values. -- So, it is limited (deliberately) to "data" types of expressions: -- that is, Exprs using the constructors: -- ELit -- EList -- That means excluding Exprs constructed with EUndefined, -- ESymbol, EIf, and ECall. module Sifflet.Language.Parser (parseExpr , parseValue , parseLiteral , parseTest , parseSuccFail , parseTypedInput2, parseTypedInputs2 , parseTypedInput3, parseTypedInputs3 , nothingBut , expr, list , value, typedValue , bool, qchar, qstring, integer, double , number ) where import Text.ParserCombinators.Parsec import Data.Number.Sifflet import Sifflet.Language.Expr import Sifflet.Util -- | Parse a Sifflet data literal (number, string, char, bool, or list), -- returning an Expr parseExpr :: String -> SuccFail Expr parseExpr = parseSuccFail expr -- | Parse a Sifflet literal expression and return its Value parseValue :: String -> SuccFail Value parseValue s = -- take a shortcut here? -- case parseExpr s of -- stringToExpr s of -- Succ expr -> exprToValue expr -- Fail errmsg -> Fail errmsg parseLiteral s >>= exprToValue parseLiteral :: String -> SuccFail Expr parseLiteral s = -- parseValue s >>= valueToLiteral case parseExpr s of Succ e -> if exprIsLiteral e then Succ e else Fail $ "parseLiteral: expr is non-literal" ++ show e Fail errmsg -> Fail errmsg parseSuccFail :: Parser a -> String -> SuccFail a parseSuccFail p s = case parse p "user input" s of Left perr -> Fail (show perr) Right v -> Succ v -- | Try to parse an input value of a specific type parseTypedInput2 :: (String, VpType) -> SuccFail Value parseTypedInput2 (str, vartype) = parseSuccFail (nothingBut (typedValue vartype)) str -- | Try to parse input values of specific types parseTypedInputs2 :: [String] -- ^ input strings -> [VpType] -- ^ expected types -> SuccFail [Value] parseTypedInputs2 strs vartypes = mapM parseTypedInput2 (zip strs vartypes) -- | Try to parse an input value for a named variable of a specific type parseTypedInput3 :: (String, String, VpType) -> SuccFail Value parseTypedInput3 (s, varname, vartype) = case parseSuccFail (nothingBut (typedValue vartype)) s of Fail msg -> Fail ("For variable " ++ varname ++ ":\n" ++ msg) Succ v -> Succ v -- | Try to parse input values for named variables of specific types parseTypedInputs3 :: [String] -- ^ inputs -> [String] -- ^ variable names -> [VpType] -- ^ variable types -> SuccFail [Value] parseTypedInputs3 strs varnames vartypes = mapM parseTypedInput3 (zip3 strs varnames vartypes) -- | Like expr, but consumes the entire input, -- so there must not be any extraneous characters after the Expr. input :: Parser Expr input = nothingBut expr -- | 'nothingBut p is like 'p', but consumes the entire input, -- so there must be no extraneous characters (except space) -- after whatever 'p' parses. nothingBut :: Parser a -> Parser a nothingBut p = (many space >> p) `prog1` (many space >> eof) prog1 :: (Monad m) => m a -> m b -> m a prog1 m1 m2 = m1 >>= (\ r -> m2 >> return r) -- | Parse a Sifflet data expression -- actually only a literal -- or a list of literals. expr :: Parser Expr expr = -- (try (list expr >>= return . EList)) <|> (bool >>= return . EBool) <|> (qchar >>= return . EChar) <|> (qstring >>= return . EString) <|> try (double >>= return . ENumber . Inexact) <|> (integer >>= return . ENumber . Exact) <|> (list expr >>= return . EList) list :: Parser a -> Parser [a] list element = let sep = try (skipMany space >> char ',' >> skipMany space) in (char '[' >> many space >> sepBy element sep) `prog1` (many space >> char ']') -- do I need (...) above? "list" -- ??? -- | Parser for a Value of any type (any VpType), -- except that we cannot parse as VpTypeVar or VpTypeFunction. value :: Parser Value value = (bool >>= return . VBool) <|> (qchar >>= return .VChar) <|> (qstring >>= return . VString) <|> try (double >>= return . VNumber . Inexact) <|> (integer >>= return . VNumber . Exact) <|> (list value >>= return . VList) -- | Parser for a value with a specific VpType expected. -- Again, we cannot do this for VpTypeVar (why not?) -- or VpTypeFunction typedValue :: VpType -> Parser Value typedValue t = (case t of VpTypeBool -> bool >>= return . VBool VpTypeChar -> qchar >>= return . VChar VpTypeString -> qstring >>= return . VString VpTypeNum -> number >>= return . VNumber VpTypeList e -> list (typedValue e) >>= return . VList VpTypeVar _ -> value -- can't check, so just accept anything VpTypeFunction _ _ -> error "typedValue: not implemented for VpTypeFunction" ) typeName t -- | A name for the type, for use in parser error reporting typeName :: VpType -> String typeName t = case t of VpTypeBool -> "boolean" -- "boolean (True or False)" VpTypeChar -> "character" -- "character (in single quotes)" VpTypeNum -> "number" VpTypeString -> "string" -- "string (in double quotes)" VpTypeList e -> "list" ++ -- "list (in brackets)" ++ case e of VpTypeVar _ -> "" _ -> " of " ++ typeName e VpTypeVar _ -> "anything" -- could be more specific! VpTypeFunction _ _ -> "function" -- ??? bool :: Parser Bool bool = (try (string "True" >> return True) <|> (string "False" >> return False)) typeName VpTypeBool -- quoted character 'c' qchar :: Parser Char qchar = let sq = '\'' -- single quote character in (((char sq "opening single quote") >> (try escapedChar <|> noneOf [sq])) `prog1` (char sq "closing single quote") ) typeName VpTypeChar -- quoted string "c..." qstring :: Parser String qstring = let dq = '\"' -- double quote character in (char dq >> many (escapedChar <|> noneOf [dq] "")) `prog1` (char dq "close of quotation") -- Do I need (...) above? typeName VpTypeString -- escapedChar recognizes the following escape sequences: -- \t = tab -- \n = newline -- \r = carriage return -- \\ = backslash -- Anything else that begins with \ is an error. escapedChar :: Parser Char escapedChar = let bs = '\\' -- backslash character in char bs >> (oneOf "ntr\\" "n, t, r, or \\ to follow \\") >>= (\ c -> return (case c of 'n' -> '\n' 't' -> '\t' 'r' -> '\r' '\\' -> '\\' _ -> error "escapedChar: c MUST be n, t, r, or \\" ) ) data Sign = Minus | Plus -- Integer ::= (+|-)? digit+ integer :: Parser Integer -- sign, digits integer = do { s <- optSign; u <- unsignedInteger; return (applySign s u) } "integer" unsignedInteger :: Parser Integer unsignedInteger = many1 digit >>= return . read -- An optional + or - defaulting to + optSign :: Parser Sign -- 1: negative; 0: non-negative optSign = try ( char '-' >> return Minus ) <|> try ( char '+' >> return Plus ) <|> return Plus applySign :: (Num n) => Sign -> n -> n applySign s x = case s of Minus -> (- x) Plus -> x -- A double (float) may begin with a sign (+ or -) and must contain -- a decimal point along with at least one digit before and/or after -- the decimal point. -- So there are three cases: -- [sign] digits '.' digits -- [sign] digits '.' -- [sign] '.' digits double :: Parser Double -- Double FAILS if there is a decimal point. -- It succeeds in the following cases: double = let digits1 = many1 digit point = char '.' -- wpf: whole-part point fraction-part wpf = do { dd <- digits1; dd' <- point >> digits1; return (dd, dd') } -- wp: whole-part point wp = do { dd <- digits1 `prog1` point; return (dd, "0") } -- pf: point fraction-part pf = do { dd' <- point >> digits1; return ("0", dd') } -- optional trailing exponent notation e.g. e-4 scale = do { i <- oneOf "eE" >> integer; return (10 ** fromIntegral i) } <|> return 1 in do { sign <- optSign ; (whole, frac) <- (try wpf <|> try wp <|> try pf) ; m <- scale; ; let w = read (whole ++ ".0") -- whole part as number f = read ("0." ++ frac) -- frac part as number ; return (m * applySign sign (w + f)) } "real number" -- A number is a Sifflet Number, which is exact unless it contains -- a decimal point. -- To avoid consuming "123" from "123." and interpreting it as an exact -- number, we MUST try to parse double before integer. number :: Parser Number number = (try (double >>= return . Inexact) <|> (integer >>= return . Exact)) typeName VpTypeNum