hw3: init
This commit is contained in:
196
hw3/WhileInterp.hs
Normal file
196
hw3/WhileInterp.hs
Normal 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
6
hw3/abs.imp
Normal file
@@ -0,0 +1,6 @@
|
||||
X := 0 - 3;
|
||||
if X < 0 then
|
||||
X := 0 - X
|
||||
else
|
||||
skip
|
||||
endif
|
||||
7
hw3/error.imp
Normal file
7
hw3/error.imp
Normal file
@@ -0,0 +1,7 @@
|
||||
x := 7 + 3;
|
||||
if x then
|
||||
true
|
||||
else
|
||||
false
|
||||
endif
|
||||
|
||||
4
hw3/extra.imp
Normal file
4
hw3/extra.imp
Normal file
@@ -0,0 +1,4 @@
|
||||
X := 3 + 9 * 4;
|
||||
Y := 5 * 2 + 7;
|
||||
Z := 360 / 12 / 3
|
||||
|
||||
11
hw3/fact.imp
Normal file
11
hw3/fact.imp
Normal 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
19
hw3/output_expected.txt
Normal 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
17
hw3/test.hs
Normal 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
6
hw3/test.imp
Normal 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
7
hw3/times.imp
Normal file
@@ -0,0 +1,7 @@
|
||||
X := 10;
|
||||
Y := 3;
|
||||
Z := 0;
|
||||
while X > 0 do
|
||||
Z := Z + Y;
|
||||
X := X - 1
|
||||
endwhile
|
||||
Reference in New Issue
Block a user