I want to create parser-like behaviour in Haskell such that I can assign an expression to a variable based on a string. I have difficulties doing so.
If I have types with the following definitions:
data Expr =
Numb Int
| Add Expr Expr
| Let {var :: PVariable, definition, body :: Expr}
type PVariable = String
And want to create a function 'eval' that would be able to handle different operations such as Add, Subtract, Multiply etc... but also the Let binding, sucht that 'eval' would be subject to the following definition:
eval :: Exp -> Integer
eval (Number expr) = expr
eval (Add expr1 expr2) = eval(expr1) + eval(expr2)
...
eval (Let v expr1 body) = ...
How could I then create eval such that it would assign an expr1 to the string v, that would then be expressed in the body, such that the parser-like behaviour could accomplish for instance something similar to the conversion from:
Let {var = "Var1", definition = expr1, body = (Add (Var "Var1") (Var "Var1"))}
where expr1 would be a chosen expression such that the above could be expressed as
let Var1 = expr1 in expr1+expr1
That could then have different Expr assigned to expr1 such as (Numb 2), so that we would get something similar to the following in Haskell:
let Var1 = 2 in Var1 + Var1
So far I have tried to deal with isolating fields of the record 'Let' so that I can evaluate each of these considering that I want to stay with the function type declarations. But I don't think that this is the easiest way, and it would probably require that I create a whole function to extract these, as far as I can see from : How to generically extract field names and values in Haskell records
Is there a smarter way to go about it?
You'll need the function eval to have extra argument that would contain the variable bindings and pass it to subexpressions recursively. You also need a special case to evaluate Var-expressions:
module Main where
import qualified Data.Map as M
data Expr =
Numb Int
| Add Expr Expr
| Let {var :: PVariable, definition, body :: Expr}
| Var PVariable
type PVariable = String
type Env = M.Map PVariable Int
eval :: Env -> Expr -> Int
eval _ (Numb a) = a
eval env (Add e1 e2) = (eval env e1) + (eval env e2)
eval env (Var v) = M.findWithDefault (error $ "undefined variable: " ++ v) v env
eval env (Let v expr body) = let
val = eval env expr
env' = M.insert v val env
in eval env' body
main = print $ eval M.empty $ Let "a" (Numb 1) (Add (Var "a") (Numb 2))
Related
Lambdas have the form: (lambda (var) expr) and represent an anonymous function that takes a single argument. There are no multi-argument functions in MiniRacket.
data Expr
= LambdaExpr String Expr
| LetExpr String Expr Expr
deriving (Show, Eq)
Evaluating a lambda should result in a closure, which is the function body, the name of the argument to it, and the current environment when the function was defined. This environment is then used when applying the function because it should let us know which bindings were available up until that point.
Here is my evalLambdaExpr
-- evaluate lambdas, which requires storing the current environment as the closure,
-- this should result in a ClosureVal, which can later be used for apply, if it's
-- a totally anonymous function, then "" is the function name, otherwise if it was
-- built with a let expression, then the let name is its name.
evalLambdaExpr :: Evaluator Value
evalLambdaExpr = do
(env, LambdaExpr letName valexpr) <- next
case getValue (eval evalExpr (env, valexpr)) of
Right (ClosureVal "" argName funBody cenv) ->
let env = Env.bind "" (ClosureVal "" argName funBody cenv) cenv
in case getValue (eval evalExpr (env, funBody)) of
Right v -> return v
Left err -> evalError err
Right nameval ->
case getValue (eval evalExpr (bind letName nameval env, valexpr)) of
Right letval -> return letval
Left err -> evalError err
Left err -> evalError err
-- and an Alternative ...
instance Alternative Evaluator where
empty = failEval "no matching evaluation"
-- define the type for values, which in our mini language
-- can be integers, bools, pairs, or closures
data Value
= IntVal Integer
| BoolVal Bool
| PairVal (Value, Value)
| ClosureVal String String Expr ValueEnv
deriving (Show, Eq)
newtype Evaluator a = E {eval :: (ValueEnv, Expr) -> Either ErrorT (a, (ValueEnv, Expr))}
-- this is the basic evaluator, it ends when ast is an EmptyExpr, but otherwise,
-- we get the the environment and expr returned
next :: Evaluator (ValueEnv, Expr)
next =
E
( \parserState -> case parserState of
(_, EmptyExpr) -> Left $ EvalError "no more expressions"
(env, x) -> Right ((env, x), (env, EmptyExpr))
)
evalError :: ErrorT -> Evaluator a
evalError err = E (\_ -> Left err)
My evalLambdaExpr is always returning a Left; why is that?
As an example, my evalLetExpr is working correctly and this is what I have:
-- Evaluate a let expression. This first evaluates the
-- argument to the identifier. Once that is evaluated, I
-- bind the value to the name in a new environment, then
-- I evaluate the body with this new environment
evalLetExpr :: Evaluator Value
evalLetExpr = do
(env, LetExpr letName valexpr body) <- next
case getValue (eval evalExpr (env, valexpr)) of
-- we got a closure from it, but it doesn't have a name,
-- so let's add that to the closure as its 'funname'
Right (ClosureVal "" argName funBody cenv) ->
let env' = Env.bind letName (ClosureVal letName argName funBody cenv) cenv
in case getValue (eval evalExpr (env', body)) of
Right v -> return v
Left err -> evalError err
Right nameval ->
case getValue (eval evalExpr (bind letName nameval env, body)) of
Right letval -> return letval
Left err -> evalError err
Left err -> evalError err
For both evaluators, I commented what they do on above them. Why is evalStr "(lambda (x) true)" returning Left (EvalError "no matching evaluation")?
I know it's not my parsing because that works.
evalLambdaExpr :: Evaluator Value
evalLambdaExpr = do
(env, LambdaExpr letName valexpr) <- next
case getValue (eval evalExpr (env, valexpr)) of
Hmm. As far as I understand, this says that in order to evaluate (lambda (x) (+ x 1)), we should evaluate (+ x 1) immediately. That is not what should happen -- the body of the lambda should only be evaluated when the closure is applied -- after all, we don't even know what the value of x should be in that expression.
I am not sure if this is what is causing your error, but I would expect this to cause all sorts of weird things to happen as you try to evaluate expressions at the wrong time with unbound variables.
One typically needs an Expr constructor for applying a lambda to an argument.
In the language I'm writing at the moment, I'm trying to implement a function which evaluates the entire program based on what I have written already as I can only execute one statement at a time. The function allows me to parse and evaluate files from a file.
The function evalString is the problem. The function executes perfectly if it's last line were runIOThrows $ liftM show $ evalStatement env (x!!0) for example. I felt like the natural step to take was to use map but that just gives me [IO String] rather than IO String.
If I make the return of the function [IO String] however there exists an error with the readStatement function and evalAndPrint function:
----- readStatement -----
Couldn't match type ‘IO’ with ‘[]’
Expected type: [[HStatement]]
Actual type: IO [HStatement]
----- evalAndPrint -----
Couldn't match type ‘[]’ with ‘IO’
Expected type: IO ()
Actual type: [()]
Couldn't match type ‘IO’ with ‘[]’
Expected type: IO String -> [()]
Actual type: String -> IO ()
I get the impression that there's a much easier way to achieve the desired effect in using map. If I executed each statement sequentially then everything works perfectly so perhaps I could use map to evaluate n-1 statements then execute the nth one manually?
parseProgram :: Parser [HStatement]
parseProgram = spaces *> many (parseEvalHVal <* spaces)
readStatement :: String -> IO [HStatement]
readStatement input = do
program <- readFile input
case parse parseProgram "fyp" program of
Left err -> fail $ show err
Right parsed -> return $ parsed
evalAndPrint :: Env -> String -> IO ()
evalAndPrint env expr = evalString env expr >>= putStrLn
evalString :: Env -> String -> IO String
evalString env expr = do
x <- readStatement expr
putStrLn $ show x
map (\exprs -> runIOThrows $ liftM show $ evalStatement env exprs) x
run :: String -> IO ()
run expr = nullEnv >>= flip evalAndPrint expr
main :: IO ()
main = do
args <- getArgs
run $ args !! 0
runIOThrows :: IOThrowsError String -> IO String
runIOThrows action = runExceptT (trapError action) >>= return . extractValue
You can use mapM to perform the steps of an IO and then retrieve the list of strings:
evalString :: Env -> String -> IO [String]
evalString env expr = do
x <- readStatement expr
putStrLn (show x)
mapM (runIOThrows . liftM show . evalStatement env) x
This of course gives us a list of strings. If you want to post-process that list, for example concatenating the strings, you can fmap it:
evalString :: Env -> String -> IO String
evalString env expr = do
x <- readStatement expr
putStrLn (show x)
concat <$> mapM (runIOThrows . liftM show . evalStatement env) x
I'm trying to write a simple language and at the moment I'm trying to implement a loop but every time I run the program, I get an error that there's a non-exhaustive pattern in the evalStatement_ function.
----- Main -----
readStatement :: String -> IO [HStatement]
readStatement input = do
program <- readFile input
case parse parseProgram "Olivia" program of
Left err -> fail $ show err
Right parsed -> return $ parsed
evalString :: Env -> String -> IO String
evalString env expr = do
x <- readStatement expr
concat <$> mapM (runIOThrows . liftM show . evalStatement_ env) x
--mapM (runIOThrows . liftM show . evalStatement env) x
-- evalStatement env x
--map (\exprs -> runIOThrows $ liftM show $ evalStatement env exprs) x
--map (runIOThrows $ liftM show $ evalStatement env) x
--runIOThrows $ liftM show $ (evalStatement env x) -- >>= runIOThrows $ liftM show $ evalStatement env
evalAndPrint :: Env -> String -> IO ()
evalAndPrint env expr = do
evalString env expr
return ()
run :: String -> IO ()
run expr = nullEnv >>= flip evalAndPrint expr
main :: IO ()
main = do
args <- getArgs
run $ args !! 0
----- Error -----
Main: Expr.hs:(82,1)-(85,34): Non-exhaustive patterns in function evalStatement_
-----------------
evalStatement_ :: Env -> HStatement -> IOThrowsError ()
evalStatement_ env (Do cond expr) = evalDo env (Do cond expr)
evalStatement_ env (Print val) = do
x <- evalVal env val
liftIO $ putStrLn $ show x
evalDo :: Env -> HStatement -> IOThrowsError ()
evalDo env (Do cond expr) = evalVal env cond >>= \x -> case x of
HBool False -> return ()
HBool True -> do
traverse_ (evalVal env) expr
evalStatement_ env $ Do cond expr
evalVal :: Env -> HVal -> IOThrowsError HVal
evalVal env val #(HInteger _) = return $ val
evalVal env val #(HBool _) = return $ val
evalVal env val #(HString _) = return $ val
evalVal env val #(HList _) = return $ val
evalVal env (Arith x op y) = evalArithmetic env x op y
evalVal env (Assign var val) = evalVal env val >>= defineVar env var
I isolated the error down to these functions. I use evalStatement_ to evaluate the Do and Print functions. I have tested print and it does work but I don't understand why evalDo doesn't. evalVal works as intended so I'm at a loss as to where the non-exhaustive problem resides. I ran the compile command with -Wall -Wextra and it gave me the following based on the Statement data type.
Expr.hs:82:1: warning: [-Wincomplete-patterns]
Pattern match(es) are non-exhaustive
In an equation for ‘evalStatement_’:
Patterns not matched:
_ (Eval _)
_ (Program _)
|
82 | evalStatement_ env (Do cond expr) = evalDo env (Do cond expr)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...
data HStatement
= Eval HVal
| Print HVal
| Do HVal [HVal]
| Program [HVal]
deriving (Eq, Read)
I'm not sure why Eval and Program would have bearing on something which doesn't actually used them or am I missing something completely?
Your main program appears to run evalStatement_ sequentially on the full list of statements parsed from your source file:
evalString :: Env -> String -> IO String
evalString env expr = do
x <- readStatement expr
concat <$> mapM (runIOThrows . liftM show . evalStatement_ env) x
^^^^ runs on every statement in the list `x`
If the statement list x :: [HStatement] contains any Program or Eval statements, then this will cause a run-time error, since evalStatement_ only handles the Do and Print constructors.
So, as #jpmarinier comments, if you expect to call evalStatement_ on any valid statement, then you need to handle all cases that the compiler warns you about when you turn on -Wall.
The following definitions for the missing cases might work for you to get things running:
evalStatement_ env (Program pgm) = mapM_ (evalStatement_ env) pgm
evalStatement_ env (Eval val) = do
result <- evalVal env val
return ()
Note that the case for Eval is pretty useless. Since evalStatement_ can't return anything other than (), we end up calculating a result and then throwing it away. It's going to be almost the same as just doing:
evalStatement_ _ (Eval _) = return () -- do nothing
I'm practicing writing parsers. I'm using Tsodings JSON Parser video as reference. I'm trying to add to it by being able to parse arithmetic of arbitrary length and I have come up with the following AST.
data HVal
= HInteger Integer -- No Support For Floats
| HBool Bool
| HNull
| HString String
| HChar Char
| HList [HVal]
| HObj [(String, HVal)]
deriving (Show, Eq, Read)
data Op -- There's only one operator for the sake of brevity at the moment.
= Add
deriving (Show, Read)
newtype Parser a = Parser {
runParser :: String -> Maybe (String, a)
}
The following functions is my attempt of implementing the operator parser.
ops :: [Char]
ops = ['+']
isOp :: Char -> Bool
isOp c = elem c ops
spanP :: (Char -> Bool) -> Parser String
spanP f = Parser $ \input -> let (token, rest) = span f input
in Just (rest, token)
opLiteral :: Parser String
opLiteral = spanP isOp
sOp :: String -> Op
sOp "+" = Add
sOp _ = undefined
parseOp :: Parser Op
parseOp = sOp <$> (charP '"' *> opLiteral <* charP '"')
The logic above is similar to how strings are parsed therefore my assumption was that the only difference was looking specifically for an operator rather than anything that's not a number between quotation marks. It does seemingly begin to parse correctly but it then gives me the following error:
λ > runParser parseOp "\"+\""
Just ("+\"",*** Exception: Prelude.undefined
CallStack (from HasCallStack):
error, called at libraries/base/GHC/Err.hs:80:14 in base:GHC.Err
undefined, called at /DIRECTORY/parser.hs:110:11 in main:Main
I'm confused as to where the error is occurring. I'm assuming it's to do with sOp mainly due to how the other functions work as intended as the rest of parseOp being a translation of the parseString function:
stringLiteral :: Parser String
stringLiteral = spanP (/= '"')
parseString :: Parser HVal
parseString = HString <$> (charP '"' *> stringLiteral <* charP '"')
The only reason why I have sOp however is that if it was replaced with say Op, I would get the error that the following doesn't exist Op :: String -> Op. When I say this my inclination was that the string coming from the parsed expression would be passed into this function wherein I could return the appropriate operator. This however is incorrect and I'm not sure how to proceed.
charP and Applicative Instance
charP :: Char -> Parser Char
charP x = Parser $ f
where f (y:ys)
| y == x = Just (ys, x)
| otherwise = Nothing
f [] = Nothing
instance Applicative Parser where
pure x = Parser $ \input -> Just (input, x)
(Parser p) <*> (Parser q) = Parser $ \input -> do
(input', f) <- p input
(input', a) <- q input
Just (input', f a)
The implementation of (<*>) is the culprit. You did not use input' in the next call to q, but used input instead. As a result you pass the string to the next parser without "eating" characters. You can fix this with:
instance Applicative Parser where
pure x = Parser $ \input -> Just (input, x)
(Parser p) <*> (Parser q) = Parser $ \input -> do
(input', f) <- p input
(input'', a) <- q input'
Just (input'', f a)
With the updated instance for Applicative, we get:
*Main> runParser parseOp "\"+\""
Just ("",Add)
I'm working on a Happy math expressions and variables parser. The problem is that I don't know how to save the value for a variable and use it later. Any ideas?
This is how I recognize expressions and variables assignment:
genExp : exp { $1 }
| variable '=' exp { //here I want to save the value of the variable; something like this: insert variables $1 $3, where 'variables' is a Data.Map }
A expression can contain a variable. For example:
a = 2 + 1
a + 2 (now the parser must print 5)
I need to save the value of the variable 'a' when the parser is parsing the line 'a = 2 + 1' and to get the value of the variable 'a' when the parser is parsing the line 'a + 2'
What you want to do is to keep track of the value of variables during evaluation of expressions, not during parsing. Let's assume you parse your expressions into the following types:
data Expr = Literal Int | Variable Var | Assign Var Expr | Add Expr Expr | ...
newtype Var = Var String deriving (Ord, Eq, Show)
Then you could simply pass a Map around your evaluation function with the current value of all variables:
import qualified Data.Map as M
import Control.Monad.State
data Expr = Literal Int | Variable Var | Assign Var Expr | Add Expr Expr
newtype Var = Var String deriving (Ord, Eq, Show)
-- Each Expr corresponds to a single line in your language, so
-- a = 2+1
-- a + 2
-- corresponds to
-- [Assign (Var "a") (Add (Literal 2) (Literal 1)),
-- Add (Variable (Var "a")) (Literal 2)]
eval :: [Expr] -> Int
eval es = last $ evalState (mapM eval' es) M.empty -- M.empty :: M.Map Var Int
where
eval' (Literal n) = return n
eval' (Variable v) = do
vs <- get
case M.lookup v vs of
Just x -> return x
_ -> error $ "variable " ++ show v ++ " is undefined!"
eval' (Assign v ex) = do
x <- eval' ex
modify (M.insert v x)
return x
eval' (Add a b) = do
x <- eval' a
y <- eval' b
return (x+y)
Of course, there's nothing to prevent you from evaluating expressions as you parse them, eliminating the need for an abstract syntax tree such as this. The general idea is the same there; you'll need to keep some state with you during the entire parsing, that keeps track of the current value of all your variables.