-
Notifications
You must be signed in to change notification settings - Fork 3
/
CalcInpt.hs
158 lines (125 loc) · 5.62 KB
/
CalcInpt.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
module CalcInpt where
import CalcInptStoreModule
import CalcInptType (Expr (..), Ops (..), Parse)
import Data.Char
import System.IO
import System.IO.Unsafe (unsafePerformIO)
none :: Parse a b
none _ = []
spot :: (a -> Bool) -> Parse a a
spot f (x : xs) | f x = [(x, xs)]
| otherwise = []
spot _ [] = []
token :: Eq a => a -> Parse a a
token t = spot (== t)
-- tc is testcase and tp is testpass
succeed :: b -> Parse a b
succeed tp tc = [(tp, tc)]
alt :: Parse a b -> Parse a b -> Parse a b
alt p1 p2 tc = p1 tc ++ p2 tc
-- actually a combinator with originally: [a] to b, c, [a], then to [((b, c), [a])]
infixr 5 >*>
(>*>) :: Parse a b -> Parse a c -> Parse a (b, c)
(parse1 >*> parse2) testcase = [ ((get1, get2), rem2) | (get1, rem1) <- parse1 testcase
, (get2, rem2) <- parse2 rem1 ]
-- change the form of parsed val, [(val, rem)] to [(f val, rem)]
build :: Parse a b -> (b -> c) -> Parse a c
build p f t = map (\(v, r) -> (f v, r)) (p t)
-- list all the parsed until not valid: [a] to [([b], [a])]
list :: Parse a b -> Parse a [b]
list p = succeed [] `alt` ((p >*> list p) `build` uncurry (:))
bracketL :: Parse Char Char
bracketL = token '('
digit :: Parse Char Char
digit = spot isDigit
digList :: Parse Char String
digList = list digit
-- optional is something that get one parse at the top
-- like optional (token '~') "~123123" is [("~", "123123")]
-- then optional (token '~') "123123" is [("", "123123")]
optional :: Parse a b -> Parse a [b]
optional p val | null subres = [([], val)]
| otherwise = (p `build` (: [])) val
where subres = p val
-- neList digit "" is [("", "")]
-- neList digit "123" is [("123", "")]
-- neList digit "123a213" is [("123", "a213")]
-- neList digit "a123" is [("", "a123")]
neList :: Parse a b -> Parse a [b]
neList _ [] = [([], [])]
neList p val | null res = succeed [] val
| otherwise = ((p >*> neList p) `build` uncurry (:)) val
where res = p val
-- since the number restrict has been applied
-- we consider only the problem about parser's recognization
-- since nTimes for nTimes 5 digit "a1234", then digit "p" is []
-- thus it is []
nTimes :: Int -> Parse a b -> Parse a [b]
nTimes 0 _ c = succeed [] c
nTimes t p c | length c < t || t < 0 = []
| otherwise = ((p >*> nTimes (t - 1) p) `build` uncurry (:)) c
parser :: Parse Char Expr
parser = _parser . filter (not . flip elem " \n\t")
_parser :: Parse Char Expr
_parser = varParse `alt` litParse `alt` opExprParser
varParse :: Parse Char Expr
varParse = neList (spot (\x -> x `elem` ['a' .. 'z'])) `build` Var
litParse :: Parse Char Expr
litParse = (optional (token '~') >*> neList digit) `build` (charlistToLit . uncurry (++))
-- considered about how to change the charlist into a Int then to Lit
-- first the form might be "~123123" or "123123"
-- thus no null charlist
-- then if charlist is one, then no "~"
-- since all the variable are ['a' .. 'z']
-- thus if anything is wrong, then use Var "Fail" to show that it is error
charlistToLit :: String -> Expr
charlistToLit [] = Var "Fail"
charlistToLit (x : xs) | null xs && x == '~' = Var "Fail"
| x == '~' = Lit ((-1) * (read xs :: Int))
| otherwise = Lit (read (x : xs) :: Int)
isOperator :: Char -> Bool
isOperator = flip elem "-+*/%:."
opExprParser :: Parse Char Expr
opExprParser = (token '(' >*> _parser >*> spot isOperator >*> _parser >*> token ')')
`build` makeExpr
makeExpr :: (a1, (Expr, (Char, (Expr, a2)))) -> Expr
makeExpr (_, (e1, (op, (e2, _)))) = Op (symbToOper op) e1 e2
symbToOper :: Char -> Ops
symbToOper ch | ch == '-' = Sub
| ch == '+' = Add
| ch == '*' = Mul
| ch == '/' = Div
| ch == '%' = Mod
| ch == ':' = Def
| ch == '.' = Frc
| otherwise = error "wrong symbol"
topLevel :: Parse a b -> b -> [a] -> b
topLevel p defaultVal inp = case results of
[] -> defaultVal
_ -> head results
where results = [ f | (f, []) <- p inp ]
exprParser :: String -> Expr
exprParser = topLevel parser (Var "Fail")
data Command = Eval { interpreted :: Expr }
| Assign { varName :: String
, varValu :: Expr }
| Exit { }
| Null { }
deriving (Show, Eq)
commandParse :: Parse Char Command
commandParse [] = [(Null, [])]
commandParse xs | subres == Var "Fail" = [(Null, [])]
| subres == Var "exit" = [(Exit, [])]
| otherwise = [(defExprCommandTrans subres, [])]
where subres = exprParser xs :: Expr
defExprCommandTrans :: Expr -> Command
defExprCommandTrans (Op Def (Var e1) e2) = Assign e1 e2
defExprCommandTrans other = Eval other
commandLine :: String -> Command
commandLine = topLevel commandParse Null
-- the next task is to workout how to create a enduring data type
-- how to make the Datatype store keep the previous value?
-- how to filter the unused varName?
-- how about using the Expr -> Maybe Float
-- if returns nothing, then we can recursively put all the varName into the store.
-- how to recursively remember the store before the exit of the calculator?