From 1bc674f6b696d96e12cc5e6b1c01cbc41f94261b Mon Sep 17 00:00:00 2001 From: Yuri Tatishchev Date: Fri, 13 Mar 2026 15:01:31 -0700 Subject: [PATCH] hw3: init --- hw3/WhileInterp.hs | 196 ++++++++++++++++++++++++++++++++++++++++ hw3/abs.imp | 6 ++ hw3/error.imp | 7 ++ hw3/extra.imp | 4 + hw3/fact.imp | 11 +++ hw3/output_expected.txt | 19 ++++ hw3/test.hs | 17 ++++ hw3/test.imp | 6 ++ hw3/times.imp | 7 ++ 9 files changed, 273 insertions(+) create mode 100644 hw3/WhileInterp.hs create mode 100644 hw3/abs.imp create mode 100644 hw3/error.imp create mode 100644 hw3/extra.imp create mode 100644 hw3/fact.imp create mode 100644 hw3/output_expected.txt create mode 100644 hw3/test.hs create mode 100644 hw3/test.imp create mode 100644 hw3/times.imp diff --git a/hw3/WhileInterp.hs b/hw3/WhileInterp.hs new file mode 100644 index 0000000..c322700 --- /dev/null +++ b/hw3/WhileInterp.hs @@ -0,0 +1,196 @@ +{- + Name: Yuri Tatishchev + Class: CS 252 + Assigment: HW3 + Date: 2026-03-27 + Description: Parser/interpreter for the WHILE language +-} + + +module WhileInterp ( + Expression(..), + Binop(..), + Value(..), + runFile, + showParsedExp, + run +) where + +import Data.Map (Map) +import qualified Data.Map as Map +import Text.ParserCombinators.Parsec +import Control.Monad.Except + +-- We represent variables as strings. +type Variable = String + +--We also represent error messages as strings. +type ErrorMsg = String + +-- The store is an associative map from variables to values. +-- (The store roughly corresponds with the heap in a language like Java). +type Store = Map Variable Value + +data Expression = + Var Variable -- x + | Val Value -- v + | Assign Variable Expression -- x := e + | Sequence Expression Expression -- e1; e2 + | Op Binop Expression Expression + | If Expression Expression Expression -- if e1 then e2 else e3 endif + | While Expression Expression -- while e1 do e2 endwhile + deriving (Show) + +data Binop = + Plus -- + :: Int -> Int -> Int + | Minus -- - :: Int -> Int -> Int + | Times -- * :: Int -> Int -> Int + | Divide -- / :: Int -> Int -> Int + | Gt -- > :: Int -> Int -> Bool + | Ge -- >= :: Int -> Int -> Bool + | Lt -- < :: Int -> Int -> Bool + | Le -- <= :: Int -> Int -> Bool + deriving (Show) + +data Value = + IntVal Int + | BoolVal Bool + deriving (Show) + + +fileP :: GenParser Char st Expression +fileP = do + prog <- exprP + eof + return prog + +exprP = do + e <- exprP' + rest <- optionMaybe restSeqP + return (case rest of + Nothing -> e + Just e' -> Sequence e e') + +-- Expressions are divided into terms and expressions for the sake of +-- parsing. Note that binary operators **DO NOT** follow the expected +-- presidence rules. +-- +-- ***FOR 2pts EXTRA CREDIT (hard, no partial credit)*** +-- Correct the precedence of the binary operators. +exprP' = do + spaces + t <- termP + spaces + rest <- optionMaybe restP + spaces + return (case rest of + Nothing -> t + Just (":=", t') -> (case t of + Var varName -> Assign varName t' + _ -> error "Expected var") + Just (op, t') -> Op (transOp op) t t') + +restSeqP = do + char ';' + exprP + +transOp s = case s of + "+" -> Plus + "-" -> Minus + "*" -> Times + "/" -> Divide + ">=" -> Ge + ">" -> Gt + "<=" -> Le + "<" -> Lt + o -> error $ "Unexpected operator " ++ o + +-- Some string, followed by an expression +restP = do + ch <- string "+" + <|> string "-" + <|> string "*" + <|> string "/" + <|> try (string "<=") + <|> string "<" + <|> try (string ">=") + <|> string ">" + <|> string ":=" -- not really a binary operator, but it fits in nicely here. + "binary operator" + e <- exprP' + return (ch, e) + +-- All terms can be distinguished by looking at the first character +termP = valP + <|> ifP + <|> whileP + <|> parenP + <|> varP + "value, variable, 'if', 'while', or '('" + + +valP = do + v <- boolP <|> numberP + return $ Val v + +boolP = do + bStr <- string "true" <|> string "false" <|> string "skip" + return $ case bStr of + "true" -> BoolVal True + "false" -> BoolVal False + "skip" -> BoolVal False -- Treating the command 'skip' as a synonym for false, for ease of parsing + +numberP = error "TBD" + +varP = error "TBD" + +ifP = error "TBD" + +whileP = error "TBD" + +-- An expression in parens, e.g. (9-5)*2 +parenP = error "TBD" + + +-- This function will be useful for defining binary operations. +-- Unlike in the previous assignment, this function returns an "Either value". +-- The right side represents a successful computaton. +-- The left side is an error message indicating a problem with the program. +-- The first case is done for you. +applyOp :: Binop -> Value -> Value -> Either ErrorMsg Value +applyOp Plus (IntVal i) (IntVal j) = Right $ IntVal $ i + j +applyOp _ _ _ = error "TBD" + + +-- As with the applyOp method, the semantics for this function +-- should return Either values. Left indicates an error, +-- whereas Right indicates a successful execution. +evaluate :: Expression -> Store -> Either ErrorMsg (Value, Store) +evaluate (Op o e1 e2) s = do + (v1,s1) <- evaluate e1 s + (v2,s') <- evaluate e2 s1 + v <- applyOp o v1 v2 + return (v, s') +evaluate _ _ = error "TBD" + + +-- Evaluates a program with an initially empty state +run :: Expression -> Either ErrorMsg (Value, Store) +run prog = evaluate prog Map.empty + +showParsedExp fileName = do + p <- parseFromFile fileP fileName + case p of + Left parseErr -> print parseErr + Right exp -> print exp + +runFile fileName = do + p <- parseFromFile fileP fileName + case p of + Left parseErr -> print parseErr + Right exp -> + case (run exp) of + Left msg -> print msg + Right (v,s) -> print $ show s + + diff --git a/hw3/abs.imp b/hw3/abs.imp new file mode 100644 index 0000000..17c8498 --- /dev/null +++ b/hw3/abs.imp @@ -0,0 +1,6 @@ +X := 0 - 3; +if X < 0 then + X := 0 - X +else + skip +endif \ No newline at end of file diff --git a/hw3/error.imp b/hw3/error.imp new file mode 100644 index 0000000..abe04e1 --- /dev/null +++ b/hw3/error.imp @@ -0,0 +1,7 @@ +x := 7 + 3; +if x then + true +else + false +endif + diff --git a/hw3/extra.imp b/hw3/extra.imp new file mode 100644 index 0000000..0e570b8 --- /dev/null +++ b/hw3/extra.imp @@ -0,0 +1,4 @@ +X := 3 + 9 * 4; +Y := 5 * 2 + 7; +Z := 360 / 12 / 3 + diff --git a/hw3/fact.imp b/hw3/fact.imp new file mode 100644 index 0000000..4589cf8 --- /dev/null +++ b/hw3/fact.imp @@ -0,0 +1,11 @@ +N := 2; +F := 1; +while N > 0 do + X := N; + Z := F; + while X > 1 do + F := Z + F; + X := X - 1 + endwhile; + N := N - 1 +endwhile diff --git a/hw3/output_expected.txt b/hw3/output_expected.txt new file mode 100644 index 0000000..0dcf7bb --- /dev/null +++ b/hw3/output_expected.txt @@ -0,0 +1,19 @@ +***Testing abs.imp +Sequence (Assign "X" (Op Minus (Val (IntVal 0)) (Val (IntVal 3)))) (If (Op Lt (Var "X") (Val (IntVal 0))) (Assign "X" (Op Minus (Val (IntVal 0)) (Var "X"))) (Val (BoolVal False))) +"fromList [(\"X\",IntVal 3)]" + +***Testing fact.imp +Sequence (Assign "N" (Val (IntVal 2))) (Sequence (Assign "F" (Val (IntVal 1))) (While (Op Gt (Var "N") (Val (IntVal 0))) (Sequence (Assign "X" (Var "N")) (Sequence (Assign "Z" (Var "F")) (Sequence (While (Op Gt (Var "X") (Val (IntVal 1))) (Sequence (Assign "F" (Op Plus (Var "Z") (Var "F"))) (Assign "X" (Op Minus (Var "X") (Val (IntVal 1)))))) (Assign "N" (Op Minus (Var "N") (Val (IntVal 1))))))))) +"fromList [(\"F\",IntVal 2),(\"N\",IntVal 0),(\"X\",IntVal 1),(\"Z\",IntVal 2)]" + +***Testing times.imp +Sequence (Assign "X" (Val (IntVal 10))) (Sequence (Assign "Y" (Val (IntVal 3))) (Sequence (Assign "Z" (Val (IntVal 0))) (While (Op Gt (Var "X") (Val (IntVal 0))) (Sequence (Assign "Z" (Op Plus (Var "Z") (Var "Y"))) (Assign "X" (Op Minus (Var "X") (Val (IntVal 1)))))))) +"fromList [(\"X\",IntVal 0),(\"Y\",IntVal 3),(\"Z\",IntVal 30)]" + +***Testing test.imp +Sequence (Assign "X" (Op Plus (Op Minus (Op Plus (Val (IntVal 1)) (Val (IntVal 2))) (Val (IntVal 3))) (Op Plus (Val (IntVal 1)) (Val (IntVal 3))))) (Sequence (Assign "Y" (Val (IntVal 0))) (While (Op Gt (Var "X") (Val (IntVal 0))) (Sequence (Assign "Y" (Op Plus (Var "Y") (Var "X"))) (Assign "X" (Op Minus (Var "X") (Val (IntVal 1))))))) +"fromList [(\"X\",IntVal 0),(\"Y\",IntVal 10)]" + +***Testing error.imp +Sequence (Assign "x" (Op Plus (Val (IntVal 7)) (Val (IntVal 3)))) (If (Var "x") (Val (BoolVal True)) (Val (BoolVal False))) +test.hs: Non-boolean value '10' used as a conditional diff --git a/hw3/test.hs b/hw3/test.hs new file mode 100644 index 0000000..9ec9e46 --- /dev/null +++ b/hw3/test.hs @@ -0,0 +1,17 @@ +import WhileInterp + +test f = do + putStrLn $ "***Testing " ++ f + --showParsedExp f + runFile f + putStrLn "" + +main :: IO () +main = do + test "abs.imp" + --test "extra.imp" + test "fact.imp" + test "times.imp" + test "test.imp" + test "error.imp" + diff --git a/hw3/test.imp b/hw3/test.imp new file mode 100644 index 0000000..248ba6a --- /dev/null +++ b/hw3/test.imp @@ -0,0 +1,6 @@ +X := ( ( 1 + 2 ) - 3 ) + ( 1 + 3 ); +Y := 0; +while X>0 do + Y := Y+X; + X := X-1 +endwhile diff --git a/hw3/times.imp b/hw3/times.imp new file mode 100644 index 0000000..a14cf0b --- /dev/null +++ b/hw3/times.imp @@ -0,0 +1,7 @@ +X := 10; +Y := 3; +Z := 0; +while X > 0 do + Z := Z + Y; + X := X - 1 +endwhile \ No newline at end of file