git.iko.soy/production-haskell
on master help sign in
Added seminar 2
commit14c9909fcb1525502fc6f3f059b7b13878bf1b51 authoriko <mail@iko.soy> dateWed 21 Feb 2024, 13:07 (3 years ago) parentdd85609
8 files changed+285
seminars/2/.gitignore+28 seminars/2/README.md+138 seminars/2/package.yaml+19 seminars/2/src/Task.hs+12 seminars/2/stack.yaml+7 seminars/2/task.cabal+44 seminars/2/test/Main.hs+10 seminars/2/test/Tests.hs+27
seminars/2/.gitignorenew file+28
@@ -0,0 +1,28 @@
1+dist
2+dist-*
3+cabal-dev
4+*.o
5+*.hi
6+*.hie
7+*.chi
8+*.chs.h
9+*.dyn_o
10+*.dyn_hi
11+.hpc
12+.hsenv
13+.cabal-sandbox/
14+cabal.sandbox.config
15+*.prof
16+*.aux
17+*.hp
18+*.eventlog
19+.stack-work/
20+cabal.project.local
21+cabal.project.local~
22+.HTF/
23+.ghc.environment.*
24+.DS_Store
25+.idea/
26+*.dump-hi
27+*.lock
28+*.iml
seminars/2/README.mdnew file+138
@@ -0,0 +1,138 @@
1+# Mathematical expression interpreter
2+
3+Your task is to:
4+1. Write monadic parsing combinators
5+2. Define a structure to represent a parsed mathematical expression in Haskell
6+3. Write a parser, which parses a string into the structure
7+4. Write an interpreter for the structure, which performs the calculation and returns the result.
8+
9+**NOTE: the parser and interpreter should be independent.**
10+
11+You will have to handle division by zero errors.
12+
13+## The basic expression
14+
15+### Formal Definition
16+
17+Note that the expressions can be nested arbitrarily deeply.
18+
19+Also note, that a number can only be an integer.
20+
21+The `/` operator is integer division truncated towards negative infinity (`div` in Haskell).
22+
23+```bnf
24+<expression> ::= <number> | <operation>
25+
26+<number> ::= "-" <digits> | <digits>
27+
28+<digits> ::= <digit> | <digit> <digits>
29+<digit> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
30+
31+<operation> ::= "(" <spaces> <expression> <spaces> <operator> <spaces> <expression> <spaces> ")"
32+
33+<operator> ::= "+" | "-" | "*" | "/"
34+
35+<spaces> ::= "" | " " <spaces>
36+```
37+
38+### Examples
39+
40+These all represent valid expressions:
41+
42+- `((1 + 2) * 3)`
43+- `1`
44+- `(((1 + 2) *3) +(1 + 1) )`
45+
46+### Minimal required combinators
47+
48+This combinator should read and return exactly one character from the input.
49+If there are no characters to read from the input, then it should fail.
50+
51+```haskell
52+anyToken :: Parser Char
53+```
54+
55+This combinator should read and return exactly one character from the input.
56+If the character does not match the given character, then it should fail.
57+
58+```haskell
59+token :: Char -> Parser ()
60+```
61+
62+This combinator should read a string of charracters. If the string does not match the given string then fail.
63+
64+```haskell
65+tokens :: String -> Parser ()
66+```
67+
68+This character reads any number of consecutive characters, which stisfy the predicate. So this can never fail.
69+
70+```haskell
71+tokensWhile :: (Char -> Bool) -> Parser String
72+```
73+
74+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.
75+
76+```haskell
77+tokensWhile1 :: (Char -> Bool) -> Parser String
78+```
79+
80+### Simple example
81+
82+A sample parser might look something like this
83+
84+```haskell
85+number :: Parser Int
86+number = do
87+ sign <- signParser
88+ digits <- digitsParser
89+ return (read (sign <> digits))
90+```
91+
92+### Control
93+
94+The proposed approach of handling control is using the `Alternative` typeclass.
95+
96+The instance for `[]` already provides the following behavior:
97+
98+`empty` represents a failure (no values).
99+
100+`<|>` represents parsing alternative. For example an expression can be either an operation or a number.
101+
102+## Complex expressions
103+
104+This is a slightly more complex version of expressions -- it includes conditional expressions
105+
106+This represents the new parts:
107+
108+```bnf
109+<expression> ::= <number> | <operation> | <conditional>
110+
111+<conditional> ::= "if " <spaces> <bool_expression> <spaces> " then " <spaces> <expression> <spaces> " else " <spaces> <expression>
112+
113+<bool_expression> ::= "(" <spaces> <expression> <spaces> <bool_operator> <spaces> <expression> <spaces> ")"
114+
115+<bool_operator> = "=" | "<" | ">" | "<=" | ">="
116+```
117+
118+### Examples
119+
120+These all represent valid expressions:
121+
122+- `if ( 1 = 2 ) then (2 + 4) else 2`
123+- `(((1 + 2) *3) +( if ( 1 = 2 ) then (2 + 4) else 2 + 1))`
124+
125+## Artifacts
126+
127+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`.
128+
129+```haskell
130+calculate :: String -> Maybe Int
131+```
132+
133+## Libraries you can use
134+
135+You can use these two libraries:
136+- `base`
137+- `mtl`
138+
seminars/2/package.yamlnew file+19
@@ -0,0 +1,19 @@
1+name: task
2+version: 0.1.0.0
3+license: Unlicense
4+
5+dependencies:
6+ - base >= 4.7 && < 5
7+ - mtl
8+
9+library:
10+ source-dirs: src
11+
12+tests:
13+ task-tests:
14+ source-dirs: test
15+ main: Main.hs
16+ dependencies:
17+ - tasty
18+ - task
19+ - tasty-hunit
seminars/2/src/Task.hsnew file+12
@@ -0,0 +1,12 @@
1+module Task
2+ ( calculate,
3+ )
4+where
5+
6+import Control.Applicative
7+import Control.Monad.State.Lazy
8+import Data.Char
9+import Data.Maybe
10+
11+calculate :: String -> Maybe Int
12+calculate = error "TODO: calculate"
seminars/2/stack.yamlnew file+7
@@ -0,0 +1,7 @@
1+resolver: lts-22.6
2+
3+packages:
4+- .
5+
6+ghc-options:
7+ "$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
seminars/2/task.cabalnew file+44
@@ -0,0 +1,44 @@
1+cabal-version: 2.2
2+
3+-- This file has been generated from package.yaml by hpack version 0.36.0.
4+--
5+-- see: https://github.com/sol/hpack
6+--
7+-- hash: 20a39a4ec02d6e75694b336bd8631bf5d630fcaacb76de84c7263c8abe1c45b3
8+
9+name: task
10+version: 0.1.0.0
11+license: Unlicense
12+build-type: Simple
13+
14+library
15+ exposed-modules:
16+ Task
17+ other-modules:
18+ Paths_task
19+ autogen-modules:
20+ Paths_task
21+ hs-source-dirs:
22+ src
23+ build-depends:
24+ base >=4.7 && <5
25+ , mtl
26+ default-language: Haskell2010
27+
28+test-suite task-tests
29+ type: exitcode-stdio-1.0
30+ main-is: Main.hs
31+ other-modules:
32+ Tests
33+ Paths_task
34+ autogen-modules:
35+ Paths_task
36+ hs-source-dirs:
37+ test
38+ build-depends:
39+ base >=4.7 && <5
40+ , mtl
41+ , task
42+ , tasty
43+ , tasty-hunit
44+ default-language: Haskell2010
seminars/2/test/Main.hsnew file+10
@@ -0,0 +1,10 @@
1+module Main
2+ ( main,
3+ )
4+where
5+
6+import Test.Tasty
7+import Tests
8+
9+main :: IO ()
10+main = defaultMain tests
seminars/2/test/Tests.hsnew file+27
@@ -0,0 +1,27 @@
1+module Tests
2+ ( tests,
3+ )
4+where
5+
6+import Task
7+import Test.Tasty
8+import Test.Tasty.HUnit
9+
10+tests :: TestTree
11+tests = testGroup "Tests"
12+ [ testCase "Basic expression" $ do
13+ calculate "((1 + 2) * 3)" @=? Just 9
14+ calculate "1" @=? Just 1
15+ calculate "(((1 + 2) *3) +(1 + 1) )" @=? Just 11
16+ calculate "(((1 + 2) *3) +(1 + 1) " @=? Nothing
17+ calculate "1 + " @=? Nothing
18+ calculate "(1 / (8 - 8))" @=? Nothing
19+ calculate "(4 / 2)" @=? Just 2
20+ , testCase "Complex expression" $ do
21+ calculate "if ( 1 = 2 ) then (2 + 4) else 2" @=? Just 2
22+ calculate "(((1 + 2) *3) +( if ( 1 = 2 ) then (2 + 4) else 2 + 1) )" @=? Just 12
23+ calculate "(((1 + 2) *3) +( if 1 = 2 ) then (2 + 4) else 2 + 1) )" @=? Nothing
24+ calculate "(((1 + 2) *3) +( if ( 1 = 2 ) then (2 + 4) else 2 + 1)" @=? Nothing
25+ calculate "(((1 + 2) *3) +( f ( 1 = 2 ) then (2 + 4) else 2 + 1) )" @=? Nothing
26+ calculate "(((1 + 2) *3) +( f ( 1 = 2 ) then (2 + 4) + 1) )" @=? Nothing
27+ ]