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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
|
module FormatParser(parse) where
import Parser
import Char
import ReadNumeric
import AllSolutions
import ParseTypes
type Expression = ([Either String Specifier],[Variable])
data Specifier = Spec (Maybe Flags) (Maybe Width) (Maybe Precision) Type
data SpecifierWVar =
SpecV
(Maybe Flags)
(Maybe WidthV)
(Maybe PrecisionV)
Type
Variable
type Flags = String
type Width = Either Int Char
type WidthV = Int
type Precision = Either Int Char
type PrecisionV = Int
type Type = Char
type Variable = String
flags :: String
flags = ['-','+','0',' ','#']
types :: String
types = ['c','d','i','o','x','X','e','E','f','s']
escapable :: String
escapable = ['a','b','f','n','r','t','v','"' ,'\'','?','\\','0','x']
isVarStartLetter :: Char -> Bool
isVarStartLetter c = c /= ','
isVarInnerLetter :: Char -> Bool
isVarInnerLetter c = c /= ','
mapTypes :: Char -> String
mapTypes c = "Format." ++ case c of
'c' -> "showChar"
'd' -> "showInt"
'i' -> "showInt"
'o' -> "showInt"
'x' -> "showInt"
'X' -> "showInt"
'e' -> "showFloat"
'E' -> "showFloat"
'f' -> "showFloat"
'g' -> "showFloat"
'G' -> "showFloat"
's' -> "showString"
_ -> error "mapTypes: unknown character"
parse :: String -> LangParser
parse showfun p s = do
x <- addVarsToSpecs p $ readExpression p s
return (bindPM (liftPM genString x)
(\y -> cleanPM ((++) (showfun++" (") y)))
genString :: [Either String SpecifierWVar] -> String
genString [] = []
genString [x] = case x of
(Left stri) -> "\"" ++ stri ++ "\")"
(Right sp) -> specToString sp ++ ")"
genString (x1:x2:xs) = case x1 of
(Left stri) -> "\"" ++ stri ++ "\" ++ " ++ genString (x2:xs)
(Right sp) -> specToString sp ++ " ++ " ++ genString (x2:xs)
specToString :: SpecifierWVar -> String
specToString (SpecV f w p t v) =
"(" ++ mapTypes t
++ " " ++ showBr t
++ " " ++ showBr f
++ " " ++ showBr w
++ " " ++ showBr p
++ " " ++ (if all isAlphaNum v then v else '(':v++")")
++ ")"
where showBr x = '(' : show x ++ ")"
addVarsToSpecs :: Pos -> IO (PM Expression)
-> IO (PM [Either String SpecifierWVar])
addVarsToSpecs p ioprexp =
do prexp <- ioprexp
let prfs = fstPM prexp
prsn = sndPM prexp
in do return $ bindPM prfs $ \fs ->
bindPM prsn $ \sn ->
addVarsToSpecifiers p fs sn
addVarsToSpecifiers :: Pos -> [Either String Specifier] -> [Variable]
-> PM [Either String SpecifierWVar]
addVarsToSpecifiers _ [] [] = cleanPM []
addVarsToSpecifiers po [] (_:_) =
throwPM po "Too many variables in format expression"
addVarsToSpecifiers po ((Right _):_) [] =
throwPM po "Too few variables in format expression"
addVarsToSpecifiers po ((Left x):xs) [] =
liftPM ((:) (Left x)) (addVarsToSpecifiers po xs [])
addVarsToSpecifiers po (q:qs) varis@(v:vs) = case q of
(Left stri) -> liftPM ((:) (Left stri))
(addVarsToSpecifiers po qs varis)
(Right (Spec f w p t)) -> if (isStar w)
then
let iv = Just (Left (fst $ maybe failed id $ readNat v))
in addVarsToSpecifiers po (Right (Spec f iv p t):qs) vs
else if (isStar p)
then
let iv = Just (Left (fst $ maybe failed id $ readNat v))
in addVarsToSpecifiers po (Right (Spec f w iv t):qs) vs
else liftPM ((:) (Right (SpecV f (eE w) (eE p) t v)))
(addVarsToSpecifiers po qs vs)
where
isStar :: Maybe (Either Int Char) -> Bool
isStar = maybe
False
(\e -> case e of
(Right '*') -> True
_ -> False)
eE :: Maybe (Either Int Char) -> Maybe Int
eE = maybe Nothing (\x -> Just (either id failed x))
readExpression :: Pos -> String -> IO (PM Expression)
readExpression p st =
do x <- getOneSolution (\a -> expression a st =:= "")
return $ maybe (throwPM p "Parse error in format expression.") cleanPM x
expression :: ([Either (String) Specifier],[String]) -> String -> String
expression = quoted q <*> vars v >>> (q,v) where q,v free
quoted :: [Either (String) Specifier] -> String -> String
quoted = terminal '"' <*> strsAndSpecs s <*> terminal '"' >>> s where s free
strsAndSpecs :: [Either (String) Specifier] -> String -> String
strsAndSpecs = empty >>> []
<||> str st >>> [Left st]
<||> spec sp <*> strsAndSpecs stsps >>> (Right sp:stsps)
<||> str st <*> spec sp <*> strsAndSpecs stsps >>> (Left st:Right sp:stsps) where st,sp,stsps free
str :: String -> String -> String
str = noescp c <*> eorstr st >>> (c:st)
<||> terminal '\\' <*> escp e <*> eorstr st >>> ('\\':e:st) where c,e,st free
eorstr :: String -> String -> String
eorstr = empty >>> ""
<||> str s >>> s where s free
noescp :: Char -> String -> String
noescp = satisfy (\d -> d /= '\\' && d /= '%' && d /= '"' ) c >>> c where c free
escp :: Char -> String -> String
escp = satisfy (\c -> elem c escapable) e >>> e where e free
spec :: Specifier -> String -> String
spec = terminal '%' <*> flgs f <*> wid w <*> prc p <*> typ t >>> (Spec f w p t)
<||> terminal '%' <*> terminal '%'
>>> (Spec Nothing Nothing Nothing '%') where f,w,p,t free
flgs :: Maybe (String) -> String -> String
flgs = empty >>> Nothing
<||> flag f <*> someflags fl >>> (Just (f:fl)) where f,fl free
someflags :: String -> String -> String
someflags = empty >>> ""
<||> flag f <*> someflags fl >>> (f:fl) where f,fl free
flag :: Char -> String -> String
flag = satisfy (\x -> elem x flags) c >>> c where c free
wid :: Maybe (Either Int Char) -> String -> String
wid = empty >>> Nothing
<||> posInt s >>> (Just (Left (extractNat s)))
<||> terminal '*' >>> (Just (Right '*')) where s free
posInt :: String -> String -> String
posInt = nonzerodigit d <*> digits ds >>> (d:ds) where d,ds free
nonzerodigit :: Char -> String -> String
nonzerodigit = satisfy (\c -> isDigit c && c /= '0') d >>> d where d free
digits :: String -> String -> String
digits = empty >>> ""
<||> digit d <*> digits ds >>> (d:ds) where d,ds free
digit :: Char -> String -> String
digit = satisfy (\c -> isDigit c) ch >>> ch where ch free
prc :: Maybe (Either Int Char) -> String -> String
prc = empty >>> Nothing
<||> terminal '.' <*> posZeroInt p >>> (Just (Left (extractNat p)))
<||> terminal '.' >>> (Just (Left 0))
<||> terminal '*' <*> terminal '*' >>> (Just (Right '*')) where p free
posZeroInt :: String -> String -> String
posZeroInt = digit d <*> digits ds >>> (d:ds) where d,ds free
typ :: Char -> String -> String
typ = satisfy (\c -> elem c types) t >>> t where t free
vars :: [String] -> String -> String
vars = empty >>> []
<||> terminal ',' <*> var v <*> vars vs >>> (v:vs) where v,vs free
var :: String -> String -> String
var = posInt i >>> i
<||> varStart s <*> varInners i >>> (s:i) where s,i free
varStart :: Char -> String -> String
varStart = satisfy (\c -> isVarStartLetter c) s >>> s where s free
varInners :: String -> String -> String
varInners = empty >>> ""
<||> varInner v <*> varInners i >>> (v:i) where v,i free
varInner :: Char -> String -> String
varInner = satisfy (\c -> isVarInnerLetter c) i >>> i where i free
extractNat :: String -> Int
s = maybe failed (fst . id) (readNat s)
|