|
-- Tato možnost zapne všechny warningy: |
|
{-# OPTIONS_GHC -Wall #-} |
|
|
|
module Simpl where |
|
|
|
-- Modul pro mapu/slovník |
|
-- Dokumentaci najdete zde: https://hackage.haskell.org/package/containers-0.6.4.1/docs/Data-Map-Lazy.html |
|
-- |
|
-- Modul je importován tzv. kvalifikovaně. |
|
-- Pokud chcete použít např. funkci 'lookup', musíte napsat 'Map.lookup' |
|
import qualified Data.Map as Map |
|
|
|
-- Modul pro State (víceméně stejný jako ze cvičení až na funkce 'modify' a 'evalState') |
|
import State |
|
|
|
-- | Proměnná je řetězec |
|
type Var = String -- <var> |
|
|
|
-- | Příkazy samy o sobě nemají hodnotu, ale manipulují s proměnnými |
|
data Statement |
|
= Assign Var Expression -- <var> = <expr> |
|
| AssignOp Op Var Expression -- <var> <op>= <expr> |
|
deriving (Eq, Show) |
|
|
|
-- | Skupina příkazů je blok |
|
type Block = [Statement] |
|
|
|
-- | Výrazy mají hodnotu samy o sobě |
|
data Expression -- <expr> |
|
= Number Int -- 42 |
|
| Variable Var -- <var> |
|
| BinaryOp Op Expression Expression -- <expr> <op> <expr> |
|
deriving (Eq, Show) |
|
|
|
-- | Reprezentuje druh binární operace |
|
data Op -- <expr> |
|
= Add -- (+) |
|
| Sub -- (-) |
|
| Mul -- (*) |
|
| Div -- (/) |
|
deriving (Eq, Show) |
|
|
|
-- | Hodnoty jsou pouze čísla. |
|
type Value = Int |
|
|
|
-- | 'Store' je mapa, která proměnným přiřazuje hodnoty. |
|
type Store = Map.Map Var Value |
|
|
|
{- Příkladný program si můžete představit například následovně: |
|
|
|
> x = 4 + 5 + 3 |
|
> y = (x * 42) + 1 |
|
> z = x + y |
|
> x += z |
|
> result = (x * x * x * x) / 4 |
|
|
|
-} |
|
|
|
-- | Vyhodnocení výrazu probíhá následovně: |
|
-- |
|
-- Pokud je výraz číslo, tak jej vrátí. |
|
-- Pokud je výraz proměnná, tak se podívá do 'Store'. |
|
-- Pokud má proměnná přiřazenou hodnotu, pak ji vrátí. |
|
-- Pokud proměnná nemá přiřazenou hodnotu, vrátí nulu. |
|
-- Pokud je výraz nějaká binární operace, zavolá 'evalBinaryOp' |
|
-- |
|
-- Pokud se výraz skládá z více částí, je nejprve vyhodnocena levá část a až potom pravá část |
|
evalExpr :: Expression -> State Store Value |
|
evalExpr expr = undefined -- <- FIXME |
|
|
|
-- | Vyhodnocení binární operace |
|
evalBinaryOp :: Op -> Expression -> Expression -> State Store Value |
|
evalBinaryOp = apply . getOp |
|
where |
|
-- | Převede 'Op' na funkci, která vezme dvě hodnoty a vrátí novou hodnotu |
|
getOp :: Op -> (Value -> Value -> Value) |
|
getOp op = undefined -- <- FIXME |
|
|
|
-- | Vezme funkci získanou z 'getOp' výše, |
|
-- dva výrazy, které vyhodnotí pomocí 'evalExpr' |
|
-- a aplikuje na ně kombinující funkci. |
|
-- |
|
-- Pokuste se o co nejelegantnější zápis. |
|
-- |
|
-- Hint: Tady budete potřebovat 'do'-notaci. |
|
apply |
|
:: (Value -> Value -> Value) |
|
-> Expression |
|
-> Expression |
|
-> State Store Value |
|
apply opFn e1 e2 = undefined -- <- FIXME |
|
|
|
-- | Vyhodnocení příkazu |
|
-- |
|
-- Přiřazení (Assign) je vyhodnoceno tak, že je vyhodnocen výraz na pravé straně |
|
-- a potom je výsledná hodnota přiřazena do proměnné (vložena do 'Store'). |
|
-- |
|
-- Přiřazení s operací (AssignOp) je vyhodnoceno podobně jako výše uvedené přiřazení. |
|
-- Využijte funkci 'evalBinaryOp', abyste si ulehčili práci. |
|
-- [Nápověda: x += y je to samé jako x = (x + y)] |
|
evalStatement :: Statement -> State Store () |
|
evalStatement stmt = undefined -- <- FIXME |
|
|
|
-- | Vyhodnocení bloku příkazů probíhá tak, |
|
-- že je postupně vyhodnocen každý příkaz |
|
evalBlock :: Block -> State Store () |
|
evalBlock = mapM_ evalStatement |
|
|
|
-- | Celkové vyhodnocení znamená spustit stavový výpočet 'evalBlock' s prázdným Storem |
|
eval :: Block -> Store |
|
eval = evalState Map.empty . evalBlock |
|
|
|
-- Pokud chcete bonusové body: |
|
-- |
|
-- * Vyrobte sadu testů, která pokryje většinu situací, které mohou nastat |
|
-- * Přidejte `==`, které vrací 0/1, `if` příkaz [(If Expr Block Block)] a napište pro něj vyhodnocení |
|
-- * Přidejte hezkou `Show` instanci, která vše hezky vypíše |
|
-- * Napište parser pomocí monády `Parser` ze cvičení! |