mirror of
https://github.com/ilyakooo0/production-haskell.git
synced 2024-10-03 19:57:17 +03:00
Added seminar 2
This commit is contained in:
parent
dd8560938a
commit
14c9909fcb
28
seminars/2/.gitignore
vendored
Normal file
28
seminars/2/.gitignore
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
dist
|
||||
dist-*
|
||||
cabal-dev
|
||||
*.o
|
||||
*.hi
|
||||
*.hie
|
||||
*.chi
|
||||
*.chs.h
|
||||
*.dyn_o
|
||||
*.dyn_hi
|
||||
.hpc
|
||||
.hsenv
|
||||
.cabal-sandbox/
|
||||
cabal.sandbox.config
|
||||
*.prof
|
||||
*.aux
|
||||
*.hp
|
||||
*.eventlog
|
||||
.stack-work/
|
||||
cabal.project.local
|
||||
cabal.project.local~
|
||||
.HTF/
|
||||
.ghc.environment.*
|
||||
.DS_Store
|
||||
.idea/
|
||||
*.dump-hi
|
||||
*.lock
|
||||
*.iml
|
138
seminars/2/README.md
Normal file
138
seminars/2/README.md
Normal file
@ -0,0 +1,138 @@
|
||||
# Mathematical expression interpreter
|
||||
|
||||
Your task is to:
|
||||
1. Write monadic parsing combinators
|
||||
2. Define a structure to represent a parsed mathematical expression in Haskell
|
||||
3. Write a parser, which parses a string into the structure
|
||||
4. Write an interpreter for the structure, which performs the calculation and returns the result.
|
||||
|
||||
**NOTE: the parser and interpreter should be independent.**
|
||||
|
||||
You will have to handle division by zero errors.
|
||||
|
||||
## The basic expression
|
||||
|
||||
### Formal Definition
|
||||
|
||||
Note that the expressions can be nested arbitrarily deeply.
|
||||
|
||||
Also note, that a number can only be an integer.
|
||||
|
||||
The `/` operator is integer division truncated towards negative infinity (`div` in Haskell).
|
||||
|
||||
```bnf
|
||||
<expression> ::= <number> | <operation>
|
||||
|
||||
<number> ::= "-" <digits> | <digits>
|
||||
|
||||
<digits> ::= <digit> | <digit> <digits>
|
||||
<digit> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
||||
|
||||
<operation> ::= "(" <spaces> <expression> <spaces> <operator> <spaces> <expression> <spaces> ")"
|
||||
|
||||
<operator> ::= "+" | "-" | "*" | "/"
|
||||
|
||||
<spaces> ::= "" | " " <spaces>
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
These all represent valid expressions:
|
||||
|
||||
- `((1 + 2) * 3)`
|
||||
- `1`
|
||||
- `(((1 + 2) *3) +(1 + 1) )`
|
||||
|
||||
### Minimal required combinators
|
||||
|
||||
This combinator should read and return exactly one character from the input.
|
||||
If there are no characters to read from the input, then it should fail.
|
||||
|
||||
```haskell
|
||||
anyToken :: Parser Char
|
||||
```
|
||||
|
||||
This combinator should read and return exactly one character from the input.
|
||||
If the character does not match the given character, then it should fail.
|
||||
|
||||
```haskell
|
||||
token :: Char -> Parser ()
|
||||
```
|
||||
|
||||
This combinator should read a string of charracters. If the string does not match the given string then fail.
|
||||
|
||||
```haskell
|
||||
tokens :: String -> Parser ()
|
||||
```
|
||||
|
||||
This character reads any number of consecutive characters, which stisfy the predicate. So this can never fail.
|
||||
|
||||
```haskell
|
||||
tokensWhile :: (Char -> Bool) -> Parser String
|
||||
```
|
||||
|
||||
This character reads any number (but at least one) of consecutive characters, which stisfy the predicate. This can only fail if the first character does not satisfy the predicate.
|
||||
|
||||
```haskell
|
||||
tokensWhile1 :: (Char -> Bool) -> Parser String
|
||||
```
|
||||
|
||||
### Simple example
|
||||
|
||||
A sample parser might look something like this
|
||||
|
||||
```haskell
|
||||
number :: Parser Int
|
||||
number = do
|
||||
sign <- signParser
|
||||
digits <- digitsParser
|
||||
return (read (sign <> digits))
|
||||
```
|
||||
|
||||
### Control
|
||||
|
||||
The proposed approach of handling control is using the `Alternative` typeclass.
|
||||
|
||||
The instance for `[]` already provides the following behavior:
|
||||
|
||||
`empty` represents a failure (no values).
|
||||
|
||||
`<|>` represents parsing alternative. For example an expression can be either an operation or a number.
|
||||
|
||||
## Complex expressions
|
||||
|
||||
This is a slightly more complex version of expressions -- it includes conditional expressions
|
||||
|
||||
This represents the new parts:
|
||||
|
||||
```bnf
|
||||
<expression> ::= <number> | <operation> | <conditional>
|
||||
|
||||
<conditional> ::= "if " <spaces> <bool_expression> <spaces> " then " <spaces> <expression> <spaces> " else " <spaces> <expression>
|
||||
|
||||
<bool_expression> ::= "(" <spaces> <expression> <spaces> <bool_operator> <spaces> <expression> <spaces> ")"
|
||||
|
||||
<bool_operator> = "=" | "<" | ">" | "<=" | ">="
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
These all represent valid expressions:
|
||||
|
||||
- `if ( 1 = 2 ) then (2 + 4) else 2`
|
||||
- `(((1 + 2) *3) +( if ( 1 = 2 ) then (2 + 4) else 2 + 1))`
|
||||
|
||||
## Artifacts
|
||||
|
||||
You should export the `calculate` function, which should parse the given string and return the computed result. If any of the steps involved fail (couldn't parse the string or there was a divide by zero error), you should return `Nothing`.
|
||||
|
||||
```haskell
|
||||
calculate :: String -> Maybe Int
|
||||
```
|
||||
|
||||
## Libraries you can use
|
||||
|
||||
You can use these two libraries:
|
||||
- `base`
|
||||
- `mtl`
|
||||
|
19
seminars/2/package.yaml
Normal file
19
seminars/2/package.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
name: task
|
||||
version: 0.1.0.0
|
||||
license: Unlicense
|
||||
|
||||
dependencies:
|
||||
- base >= 4.7 && < 5
|
||||
- mtl
|
||||
|
||||
library:
|
||||
source-dirs: src
|
||||
|
||||
tests:
|
||||
task-tests:
|
||||
source-dirs: test
|
||||
main: Main.hs
|
||||
dependencies:
|
||||
- tasty
|
||||
- task
|
||||
- tasty-hunit
|
12
seminars/2/src/Task.hs
Normal file
12
seminars/2/src/Task.hs
Normal file
@ -0,0 +1,12 @@
|
||||
module Task
|
||||
( calculate,
|
||||
)
|
||||
where
|
||||
|
||||
import Control.Applicative
|
||||
import Control.Monad.State.Lazy
|
||||
import Data.Char
|
||||
import Data.Maybe
|
||||
|
||||
calculate :: String -> Maybe Int
|
||||
calculate = error "TODO: calculate"
|
7
seminars/2/stack.yaml
Normal file
7
seminars/2/stack.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
resolver: lts-22.6
|
||||
|
||||
packages:
|
||||
- .
|
||||
|
||||
ghc-options:
|
||||
"$locals": -ddump-to-file -ddump-hi -fwarn-unused-binds -fwarn-unused-imports -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates -Werror=missing-home-modules -Wmissing-home-modules -Widentities -Wredundant-constraints -Wmissing-export-lists
|
44
seminars/2/task.cabal
Normal file
44
seminars/2/task.cabal
Normal file
@ -0,0 +1,44 @@
|
||||
cabal-version: 2.2
|
||||
|
||||
-- This file has been generated from package.yaml by hpack version 0.36.0.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
--
|
||||
-- hash: 20a39a4ec02d6e75694b336bd8631bf5d630fcaacb76de84c7263c8abe1c45b3
|
||||
|
||||
name: task
|
||||
version: 0.1.0.0
|
||||
license: Unlicense
|
||||
build-type: Simple
|
||||
|
||||
library
|
||||
exposed-modules:
|
||||
Task
|
||||
other-modules:
|
||||
Paths_task
|
||||
autogen-modules:
|
||||
Paths_task
|
||||
hs-source-dirs:
|
||||
src
|
||||
build-depends:
|
||||
base >=4.7 && <5
|
||||
, mtl
|
||||
default-language: Haskell2010
|
||||
|
||||
test-suite task-tests
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Main.hs
|
||||
other-modules:
|
||||
Tests
|
||||
Paths_task
|
||||
autogen-modules:
|
||||
Paths_task
|
||||
hs-source-dirs:
|
||||
test
|
||||
build-depends:
|
||||
base >=4.7 && <5
|
||||
, mtl
|
||||
, task
|
||||
, tasty
|
||||
, tasty-hunit
|
||||
default-language: Haskell2010
|
10
seminars/2/test/Main.hs
Normal file
10
seminars/2/test/Main.hs
Normal file
@ -0,0 +1,10 @@
|
||||
module Main
|
||||
( main,
|
||||
)
|
||||
where
|
||||
|
||||
import Test.Tasty
|
||||
import Tests
|
||||
|
||||
main :: IO ()
|
||||
main = defaultMain tests
|
27
seminars/2/test/Tests.hs
Normal file
27
seminars/2/test/Tests.hs
Normal file
@ -0,0 +1,27 @@
|
||||
module Tests
|
||||
( tests,
|
||||
)
|
||||
where
|
||||
|
||||
import Task
|
||||
import Test.Tasty
|
||||
import Test.Tasty.HUnit
|
||||
|
||||
tests :: TestTree
|
||||
tests = testGroup "Tests"
|
||||
[ testCase "Basic expression" $ do
|
||||
calculate "((1 + 2) * 3)" @=? Just 9
|
||||
calculate "1" @=? Just 1
|
||||
calculate "(((1 + 2) *3) +(1 + 1) )" @=? Just 11
|
||||
calculate "(((1 + 2) *3) +(1 + 1) " @=? Nothing
|
||||
calculate "1 + " @=? Nothing
|
||||
calculate "(1 / (8 - 8))" @=? Nothing
|
||||
calculate "(4 / 2)" @=? Just 2
|
||||
, testCase "Complex expression" $ do
|
||||
calculate "if ( 1 = 2 ) then (2 + 4) else 2" @=? Just 2
|
||||
calculate "(((1 + 2) *3) +( if ( 1 = 2 ) then (2 + 4) else 2 + 1) )" @=? Just 12
|
||||
calculate "(((1 + 2) *3) +( if 1 = 2 ) then (2 + 4) else 2 + 1) )" @=? Nothing
|
||||
calculate "(((1 + 2) *3) +( if ( 1 = 2 ) then (2 + 4) else 2 + 1)" @=? Nothing
|
||||
calculate "(((1 + 2) *3) +( f ( 1 = 2 ) then (2 + 4) else 2 + 1) )" @=? Nothing
|
||||
calculate "(((1 + 2) *3) +( f ( 1 = 2 ) then (2 + 4) + 1) )" @=? Nothing
|
||||
]
|
Loading…
Reference in New Issue
Block a user