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