hw3: init

This commit is contained in:
2026-03-13 15:01:31 -07:00
parent 7541f30384
commit 1bc674f6b6
9 changed files with 273 additions and 0 deletions

196
hw3/WhileInterp.hs Normal file
View File

@@ -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 <error msg> indicates an error,
-- whereas Right <something> 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

6
hw3/abs.imp Normal file
View File

@@ -0,0 +1,6 @@
X := 0 - 3;
if X < 0 then
X := 0 - X
else
skip
endif

7
hw3/error.imp Normal file
View File

@@ -0,0 +1,7 @@
x := 7 + 3;
if x then
true
else
false
endif

4
hw3/extra.imp Normal file
View File

@@ -0,0 +1,4 @@
X := 3 + 9 * 4;
Y := 5 * 2 + 7;
Z := 360 / 12 / 3

11
hw3/fact.imp Normal file
View File

@@ -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

19
hw3/output_expected.txt Normal file
View File

@@ -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

17
hw3/test.hs Normal file
View File

@@ -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"

6
hw3/test.imp Normal file
View File

@@ -0,0 +1,6 @@
X := ( ( 1 + 2 ) - 3 ) + ( 1 + 3 );
Y := 0;
while X>0 do
Y := Y+X;
X := X-1
endwhile

7
hw3/times.imp Normal file
View File

@@ -0,0 +1,7 @@
X := 10;
Y := 3;
Z := 0;
while X > 0 do
Z := Z + Y;
X := X - 1
endwhile