git.iko.soy/ikoHSE/sc-project
on master help sign in
๐ŸŽ‰
commit300eab9e582feb8f39c8d10fbd71e6d6029b1c19 authoriko <ilyakooo0@gmail.com> dateSun 24 May 2020, 23:41 (6 years ago) parent8310bc5
5 files changed+223โˆ’3
README.md+155 package.yaml+1 src/Task.hs+11โˆ’2 task.cabal+41 test/Tests.hs+15โˆ’1
README.mdnew file+155
@@ -0,0 +1,155 @@
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+### Exmaples
39+
40+These all represent valid expressions:
41+
42+- `((1 + 2) * 3)`
43+- `1`
44+- `(((1 + 2) *3) +(1 + 1) )`
45+
46+## The proposed approach
47+
48+The proposed approach is to use the following monad for parsing
49+
50+```haskell
51+type Parser a = StateT String [] a
52+```
53+
54+`StateT String` -- the state contains the String being currently parsed.
55+
56+`[]` -- the base monad should be a list. This will provide us with the context of trying different combinations of alternatives, and will also provide a failure context (the empty list).
57+
58+The result of the parser would be a (possible infinity) list of possible parsed values. You should take the first one.
59+
60+You can provide a different definition of the `Parser` monad, but you can not use a monad, which was intended for parsing.
61+
62+### Minimal required combinators
63+
64+This combinator should read and return exactly one character from the input.
65+If there are no characters to read from the input, then it should fail.
66+
67+```haskell
68+anyToken :: Parser Char
69+```
70+
71+```haskell
72+token :: Char -> Parser ()
73+```
74+
75+```haskell
76+tokens :: String -> Parser ()
77+```
78+
79+```haskell
80+tokensWhile :: (Char -> Bool) -> Parser String
81+```
82+
83+```haskell
84+tokensWhile1 :: (Char -> Bool) -> Parser String
85+```
86+
87+### Simple example
88+
89+A sample parser might look something like this
90+
91+```haskell
92+number :: Parser Int
93+number = do
94+ sign <- signParser
95+ digits <- digitsParser
96+ return (read (sign <> digits))
97+```
98+
99+### Control
100+
101+The proposed approach of handling control is using the `Alternative` typeclass.
102+
103+The instance for `[]` already provides the following behavior:
104+
105+`empty` represents a failure (no values).
106+
107+`<|>` represents parsing alternative. For example an expression can be either an operation or a number.
108+
109+## Complex expressions
110+
111+This is a slightly more complex version of expressions -- it includes conditional expressions
112+
113+This represents the new parts:
114+
115+```bnf
116+<expression> ::= <number> | <operation> | <conditional>
117+
118+<conditional> ::= "if " <spaces> <bool_expression> <spaces> " then " <spaces> <expression> <spaces> " else " <spaces> <expression>
119+
120+<bool_expression> ::= "(" <spaces> <expression> <spaces> <bool_operator> <spaces> <expression> <spaces> ")"
121+
122+<bool_operator> = "=" | "<" | ">" | "<=" | ">="
123+```
124+
125+### Examples
126+
127+These all represent valid expressions:
128+
129+- `if ( 1 = 2 ) then (2 + 4) else 2`
130+- `(((1 + 2) *3) +( if ( 1 = 2 ) then (2 + 4) else 2 + 1) )`
131+
132+## Artifacts
133+
134+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`.
135+
136+```haskell
137+calculate :: String -> Maybe Int
138+```
139+
140+## Libraries you can use
141+
142+You can use these two libraries:
143+- `base`
144+- `mtl`
145+
146+## Grading
147+
148+| Requirement | Score |
149+| -------------------------------------------------------- | ----- |
150+| Definition of the structure, representing the expression | 1 |
151+| Definition of the minimal monadic parser combinators | 2 |
152+| Definition of the expression parser | 2 |
153+| Definition of the expression interpreter | 2 |
154+| All of the above for complex expressions | 2 |
155+| Proper decomposition of functionality and coding style | 1 |
package.yaml+1
@@ -4,6 +4,7 @@ license: Unlicense
44
55dependencies:
66 - base >= 4.7 && < 5
7+ - mtl
78
89library:
910 source-dirs: src
src/Task.hs+11โˆ’2
@@ -1,3 +1,12 @@
11module Task
2โˆ’ (
3โˆ’ ) where
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"
task.cabalnew file+41
@@ -0,0 +1,41 @@
1+cabal-version: 2.2
2+
3+-- This file has been generated from package.yaml by hpack version 0.33.0.
4+--
5+-- see: https://github.com/sol/hpack
6+--
7+-- hash: b1f4ab7a3e6ea31d3a641f90b944f1c91d42ea2980c970ad21e0210599d7c284
8+
9+name: task
10+version: 0.1.0.0
11+license: Unlicense
12+license-file: LICENSE
13+build-type: Simple
14+
15+library
16+ exposed-modules:
17+ Task
18+ other-modules:
19+ Paths_task
20+ hs-source-dirs:
21+ src
22+ build-depends:
23+ base >=4.7 && <5
24+ , mtl
25+ default-language: Haskell2010
26+
27+test-suite task-tests
28+ type: exitcode-stdio-1.0
29+ main-is: Main.hs
30+ other-modules:
31+ Tests
32+ Paths_task
33+ hs-source-dirs:
34+ test
35+ build-depends:
36+ base >=4.7 && <5
37+ , mtl
38+ , task
39+ , tasty
40+ , tasty-hspec
41+ default-language: Haskell2010
test/Tests.hs+15โˆ’1
@@ -8,4 +8,18 @@ import Test.Tasty.Hspec
88
99tests :: Spec
1010tests = do
11โˆ’ return ()
11+ it "Basic expression" $ do
12+ calculate "((1 + 2) * 3)" `shouldBe` Just 9
13+ calculate "1" `shouldBe` Just 1
14+ calculate "(((1 + 2) *3) +(1 + 1) )" `shouldBe` Just 11
15+ calculate "(((1 + 2) *3) +(1 + 1) " `shouldBe` Nothing
16+ calculate "1 + " `shouldBe` Nothing
17+ calculate "(1 / (8 - 8))" `shouldBe` Nothing
18+ calculate "(4 / 2)" `shouldBe` Just 2
19+ it "Complex expression" $ do
20+ calculate "if ( 1 = 2 ) then (2 + 4) else 2" `shouldBe` Just 2
21+ calculate "(((1 + 2) *3) +( if ( 1 = 2 ) then (2 + 4) else 2 + 1) )" `shouldBe` Just 12
22+ calculate "(((1 + 2) *3) +( if 1 = 2 ) then (2 + 4) else 2 + 1) )" `shouldBe` Nothing
23+ calculate "(((1 + 2) *3) +( if ( 1 = 2 ) then (2 + 4) else 2 + 1)" `shouldBe` Nothing
24+ calculate "(((1 + 2) *3) +( f ( 1 = 2 ) then (2 + 4) else 2 + 1) )" `shouldBe` Nothing
25+ calculate "(((1 + 2) *3) +( f ( 1 = 2 ) then (2 + 4) + 1) )" `shouldBe` Nothing