-1

I am trying to write an evaluator which executes a simple if-block of code. The only two conditions that I'm interested in at the moment is when the condition to enter the block is true or false or if the condition is a relational expression.

The former pattern works fine and it makes sense to me in the implementation of it.

evalCond (Bool cond) = if cond then True else False

I consider my condition to be one of my HenryVal booleans value and then I return the correct Boolean value after the check.

When it comes to the relational expression evaluation I'm not so sure where it's going wrong. An arithmetic expression is given by <x op y> (without spaces) and that returns the correct binary expression piece of data. For example <5<10> would return RBinary Less (Integer 5) (Integer 10). So when I go to the evaluation function of every different expression, the condition is passed into it's own function which evaluates it to True or False. I consider this condition at that moment of time a String because it's the only way in which it can be passed to the condition evaluation function. If I were to pass RBinary op x y, I am left with an error because RBinary expects three arguments, not one.

Furthermore I know the evaluation works because in ghci, if I were to enter the following I would be left with the correct evaluation:

cond = "4 Less 5"
(henryBool2Bool (evalRBinOp (int2HenryInt (str2Int ((words cond) !! 0))) (evalROp ((words cond) !! 1)) (int2HenryInt (str2Int ((words cond) !! 2))) ))

Through command line arguments, if I enter the following I get the error:

./hask "if <5<10>then x:=5 else x:=10"
hask: hask.hs:(336,1)-(338,137): Non-exhaustive patterns in function evalCond

So I thought perhaps a function which translates a string into an RBinary expression would be better as then if it's being passed in as a string then I can evaluate the string as an RBinary expression but that gave me the same non-exhaustive pattern error.

Below is my code which includes my data types and my evaluator functions.

data HenryVal = Atom String
              | String String 
              | Integer Integer
              | Bool Bool
              | Not HenryVal
              | Neg HenryVal
              | List [HenryVal]
              | Seq [HenryVal]
              | Assign String HenryVal
              | If HenryVal HenryVal HenryVal
              | While HenryVal HenryVal
              | Skip
              | ABinOp ABinOp
              | RBinOp RBinOp
              | ABinary ABinOp HenryVal HenryVal
              | BBinary BBinOp HenryVal HenryVal
              | RBinary RBinOp HenryVal HenryVal

data BBinOp = And | Or deriving (Show)

data RBinOp = Greater
            | GEqual
            | Less
            | LEqual
             deriving (Show)

data ABinOp = Add
            | Subtract
            | Multiply
            | Divide
              deriving (Show)

evalRBinOp :: HenryVal -> RBinOp -> HenryVal -> HenryVal
evalRBinOp (Integer a) Greater (Integer b) = Bool (a > b)
evalRBinOp (Integer a) Less (Integer b) = Bool (a < b)
evalRBinOp (Integer a) GEqual (Integer b) = Bool (a >= b)
evalRBinOp (Integer a) LEqual (Integer b) = Bool (a <= b)

evalCond :: HenryVal -> Bool
evalCond (Bool cond) = if cond then True else False
evalCond (String cond) = if  (henryBool2Bool (eval (str2rbinary cond))) then True else False
--evalCond (String cond) = if (henryBool2Bool (evalRBinOp (int2HenryInt (str2Int ((words cond) !! 0))) (evalROp ((words cond) !! 1)) (int2HenryInt (str2Int ((words cond) !! 2))) )) == True then True else False

henryVal2Rop :: HenryVal -> RBinOp
henryVal2Rop (RBinOp Less) = Less
henryVal2Rop (RBinOp Greater) = Greater

str2Int :: String -> Integer
str2Int str = read (str) :: Integer

int2HenryInt :: Integer -> HenryVal
int2HenryInt num = Integer num

henryBool2Bool :: HenryVal -> Bool
henryBool2Bool (Bool True) = True
henryBool2Bool (Bool False) = False
henryBool2Bool (String "True") = True
henryBool2Bool (String "False") = False

str2rbinary :: String -> HenryVal
str2rbinary string = RBinary (evalROp ((words string) !! 1)) (int2HenryInt (str2Int ((words string) !! 0))) (int2HenryInt (str2Int ((words string) !! 2)))

evalROp :: String -> RBinOp
evalROp "Less" = Less
evalROp "Greater" = Greater
evalROp "GEqual" = GEqual
evalROp "LEqual" = LEqual

eval :: HenryVal -> HenryVal
eval val@(Atom _) = val
eval val@(String _) = val
eval val@(Integer _) = val
eval val@(Bool _) = val
eval val@(Neg _) = val
eval val@(Not _) = val
eval (List [Atom "quote", val]) = val
eval val@(List _) = val
eval val@(Seq _) = val
eval (If cond a b) = if (evalCond cond) then (eval a) else (eval b)
eval (While cond a) = a
eval (Assign var val) = val
eval val@(ABinOp _) = val
eval val@(RBinOp _) = val
eval (Skip) = Skip
eval (ABinary op x y) = evalABinOp (eval x) op (eval y)
eval (BBinary op x y) = evalBBinOp (eval x) op (eval y)
eval (RBinary op x y) = evalRBinOp (eval x) op (eval y)
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
Mr.Bloom
  • 343
  • 1
  • 11
  • Your `evalCond` function explicitly only works with string or bool literals, but not with relational expressions. You are passing the expression `RBinary Less (Integer 5) (Integer 10)` to `evalCond` which has no pattern for `RBinary`. – user2407038 May 13 '20 at 20:39
  • The thing that is confusing me is a representation of `RBinary` in `evalCond` would result in an error because RBinary expects three arguments for both it's declaration and evaluation in `evalRBinOp`. I'm unsure how to proceed from this. – Mr.Bloom May 13 '20 at 21:00
  • I'm not sure I understand what you mean. What have you tried regarding `RBinary` in `evalCond` and what error does it give? – user2407038 May 13 '20 at 21:16
  • @user2407038 I've tried to think more simply with a function such as `evalCond (RBinary cond) = ...` The issue with this function is that it expects three arguments but I have only supplied it one argument. – Mr.Bloom May 13 '20 at 21:29
  • @user2407038 That is why I thought of thinking it as a string and then converting it into an RBinary expression that could then be evaluated which didn't work. Regarding the number of arguments issues, I went back to `eval` and included the following `eval (If cond a b) = if (evalRBinOp (cond!!0) (cond!!1) (cond!!2)) then (eval a) else (eval b)`. That gave me the following the error : `Couldn't match expected type ‘[HenryVal]’ with actual type ‘HenryVal’ `. – Mr.Bloom May 13 '20 at 21:35
  • This to me makes it seem like I could treat `cond` as a list containing the RBinary expression and then execute the RBinary expression using list indexing – Mr.Bloom May 13 '20 at 21:35
  • I actually just solved it! – Mr.Bloom May 13 '20 at 21:50
  • `if cond then True else False` can be written as just `cond` – 4castle May 14 '20 at 02:59

1 Answers1

2

So my issue was not knowing how to evaluate RBinary because it requires three arguments but we are only given one, cond. I played around in GHCI and figured out that I could just treat cond as a three argument expression if I pass it to eval instead of evalCond. This evaluates correctly and as an added benefit, I can remove evalCond though I will keep it if it becomes necessary later on. The code to fix it was:

henryBool2Bool :: HenryVal -> Bool
henryBool2Bool (Bool True) = True
henryBool2Bool (Bool False) = False
henryBool2Bool (String "True") = True
henryBool2Bool (String "False") = False

eval (If cond a b) = if (henryBool2Bool (eval cond)) then (eval a) else (eval b)
Mr.Bloom
  • 343
  • 1
  • 11
  • The cases for `(Bool True)` and `(Bool False)` can be collapsed to `henryBool2Bool (Bool bool) = bool` – 4castle May 14 '20 at 03:05