Import Upstream version 20180207
[hcoop/debian/mlton.git] / mlton / xml / parse-sxml.fun
CommitLineData
7f918cf1
CE
1(* Copyright (C) 2017 Jason Carr.
2 *
3 * MLton is released under a BSD-style license.
4 * See the file MLton-LICENSE for details.
5 *)
6
7functor ParseSxml (S: PARSE_SXML_STRUCTS): PARSE_SXML =
8struct
9 open S
10 open XmlTree
11 structure P = Parse
12 open P.Ops
13 infix 1 <|> >>=
14 infix 3 <*> <* *>
15 infixr 4 <$> <$$> <$$$> <$ <$?>
16
17 fun isInfixChar b = case List.index
18 (String.explode "!%&$#+-/:<=>?@\\~'^|*",
19 fn c => b = c) of
20 SOME _ => true
21 | NONE => false
22
23 fun isIdentFirst b = Char.isAlpha b orelse b = #"'"
24 fun isIdentRest b = Char.isAlphaNum b orelse b = #"'" orelse b = #"_" orelse b = #"."
25
26 val stringToken = (fn (x, y) => [x, y]) <$$> (P.char #"\\", P.next) <|>
27 (fn x => [x] ) <$> P.next
28 fun skipComments () = P.any
29 [P.str "(*" *> P.cut (P.manyCharsFailing(P.str "*)" <|> P.str "(*") *>
30 ((P.str "*)" <|> "" <$ P.delay skipComments) *> P.each [P.next]
31 <|> P.failCut "Closing comment")),
32 List.concat <$> P.each
33 [P.each [P.char #"\""],
34 List.concat <$> P.manyFailing(stringToken, P.char #"\""),
35 P.each [P.char #"\""]],
36 P.each [P.next]]
37
38 fun token s = P.notFollowedBy
39 (P.str s,
40 (P.char #"_") <|> (P.nextSat Char.isAlphaNum)) <* P.spaces
41 fun symbol s = P.notFollowedBy
42 (P.str s,
43 (P.nextSat (fn b => isInfixChar b orelse b = #"_"))) <* P.spaces
44
45 val clOptions = P.manyCharsFailing(P.str "Datatypes:")
46
47 fun 'a makeNameResolver(f: string -> 'a): string -> 'a =
48 let
49 val hash = String.hash
50 val map = HashSet.new{hash= hash o #1}
51 fun eq x (a: string * 'a) = String.equals(x, #1 a)
52 in
53 fn x => (#2 o HashSet.lookupOrInsert)(map, hash x, eq x, fn () => (x, f x))
54 end
55
56
57 val ident = (
58 String.implode <$> (P.any
59 [(op ::) <$$>
60 (P.nextSat isIdentFirst,
61 P.many (P.nextSat isIdentRest)),
62 List.append <$$>
63 (P.many1 (P.nextSat isInfixChar),
64 (op ::) <$$> (P.char #"_", P.many (P.nextSat Char.isDigit)) <|> P.pure []
65 (* just for collecting _0 *)
66 )])) <|> P.failCut "identifier"
67
68 fun doneRecord' () = P.char #"{" <* P.many(P.delay doneRecord' <|> P.failing(P.char #"}") *> P.next) <* P.char #"}"
69 val doneRecord = doneRecord' ()
70 fun fromRecord name p = P.peek
71 (P.char #"{" *> P.many (() <$ P.delay doneRecord' <|> () <$ P.failing (token name <* symbol "=") <* P.next)
72 *> token name *> symbol "=" *> p)
73
74 fun casesOf(con, left, right) = Vector.fromList <$> P.sepBy1
75 (left <* P.spaces <* token "=>" >>= (fn l =>
76 right >>= (fn r => con (l, r))),
77 P.spaces)
78
79 fun optionOf p = SOME <$> (token "Some" *> P.cut(p)) <|> NONE <$ token "None"
80
81
82 (* too many arguments for the maps, curried to use <*> instead *)
83 fun makeTyp resolveTycon (args, ident) = Type.con (resolveTycon ident, args)
84
85 local
86 fun typ' resolveTycon () = (makeTyp resolveTycon) <$$>
87 (((P.tuple (P.delay (typ' resolveTycon))) <|> P.pure (Vector.new0 ())),
88 (P.spaces *> ident <* P.spaces))
89 in
90 fun typ resolveTycon = typ' resolveTycon ()
91 end
92
93 val ctype = (P.any o List.map)
94 (CType.all, fn ct =>
95 ct <$ token (CType.toString ct))
96
97 fun makeCon resolveCon (name, arg) = {con = resolveCon name, arg = arg}
98
99 (* parse in a constructor (to Con.t) *)
100 fun constructor resolveCon resolveTycon = (makeCon resolveCon) <$$>
101 (ident <* P.spaces,
102 P.optional (token "of" *> (typ resolveTycon) <* P.spaces) )
103
104
105 fun makeDt resolveTycon (tycon, cons) =
106 {tyvars = Vector.new0 (),
107 tycon = resolveTycon tycon, cons = cons}
108
109 fun datatyp resolveCon resolveTycon = (makeDt resolveTycon) <$$>
110 ((P.spaces *> ident <* P.spaces <* symbol "="),
111 (Vector.fromList <$> P.sepBy1
112 ((constructor resolveCon resolveTycon) <* P.spaces,
113 P.char #"|" *> P.spaces)))
114
115 fun datatypes resolveCon resolveTycon =
116 token "Datatypes:" *> Vector.fromList <$> P.many (datatyp resolveCon resolveTycon)
117
118 fun overflow resolveVar = P.str "Overflow:" *> P.spaces *> (
119 (P.str "Some" *> P.spaces *> SOME <$> resolveVar <$> ident <* P.spaces) <|>
120 (NONE <$ P.str "None" <* P.spaces))
121
122
123 val digits = P.many (P.nextSat (fn c => Char.isDigit c orelse c = #"~"))
124 val parseIntInf = ((IntInf.fromString o String.implode) <$?> digits) <|> P.failCut "integer"
125 val parseString = ((String.fromString o String.implode o List.concat) <$?>
126 (P.char #"\"" *> (P.manyFailing(stringToken, P.char #"\"")) <* P.char #"\""))
127 fun makeReal s = (case (RealX.make s) of NONE => NONE | x => x) handle Fail _ => NONE
128 fun parseReal sz = (makeReal <$?> (fn p => p) <$$> (String.implode <$>
129 List.concat <$> P.each
130 [P.many (P.nextSat (fn c => Char.isDigit c orelse c = #"~")),
131 P.char #"." *> P.pure [#"."] <|> P.pure [],
132 P.many (P.nextSat (fn c => Char.isDigit c orelse c = #"E" orelse c = #"~"))],
133 P.pure sz))
134 val parseHex = P.fromReader (IntInf.scan(StringCvt.HEX, P.toReader P.next))
135 val parseBool = true <$ token "true" <|> false <$ token "false"
136
137 fun makeWord typ int =
138 if Tycon.isWordX typ
139 then P.pure (WordX.fromIntInf(int, (Tycon.deWordX typ)))
140 else P.fail "Invalid word"
141 val parseWord8Vector = WordXVector.fromVector <$$>
142 (P.pure {elementSize=WordSize.word8},
143 P.char #"#" *> P.vector (parseHex >>= makeWord (Tycon.word WordSize.word8)))
144
145 fun exp resolveCon resolveTycon resolveVar =
146 let
147 fun makeLet(decs, result) = Exp.make {decs=decs, result=result}
148 fun makeExnDec(ca) = Dec.Exception ca
149 fun makeValDec(var, ty, exp) = Dec.MonoVal {exp=exp, ty=ty, var=var}
150 fun makeFunDecs(lambdas) = Dec.Fun {decs=Vector.fromList lambdas, tyvars=Vector.new0 ()}
151 fun makeFunDec((var, ty), lambda) = {lambda=lambda, ty=ty, var=var}
152 val var = resolveVar <$> ident <* P.spaces
153 val typedvar = (fn (x,y) => (x,y)) <$$>
154 (var,
155 symbol ":" *> (typ resolveTycon) <* P.spaces)
156 fun makeVarExp var = VarExp.T {var=var, targs = Vector.new0 ()}
157 val varExp =
158 P.failing (token "in" <|> token "exception" <|> token "val") *>
159 makeVarExp <$> var
160 fun makeApp(func, arg) = {arg=arg, func=func}
161 fun makeConApp(con, targs, arg) = {arg=arg, con=con, targs=targs}
162 fun conApp v = makeConApp <$$$>
163 (resolveCon <$> ident <* P.spaces,
164 P.pure (Vector.new0 ()),
165 P.optional v)
166 val conAppExp = token "new" *> P.cut (conApp varExp)
167 fun constExp typ =
168 if Tycon.isWordX typ then
169 Const.Word <$> (P.str "0x" *> parseHex >>= makeWord typ) <|> P.failCut "word"
170 else if Tycon.isRealX typ then
171 Const.Real <$> parseReal (Tycon.deRealX typ) <|> P.failCut "real"
172 else if Tycon.isIntX typ then
173 Const.IntInf <$> parseIntInf <|> P.failCut "integer"
174 else if Tycon.equals(typ, Tycon.vector) then
175 (* assume it's a word8 vector *)
176 P.any
177 [Const.string <$> parseString,
178 Const.wordVector <$> parseWord8Vector,
179 P.failCut "string constant"]
180
181 else
182 P.fail "constant"
183 fun makeRaise(NONE, exn) = {exn=exn, extend=false}
184 | makeRaise(SOME _, exn) = {exn=exn, extend=true}
185 val raiseExp = token "raise" *> P.cut (makeRaise <$$> (P.optional (token "extend"), varExp <* P.spaces))
186 fun makeHandle(try, catch, handler) = {catch=catch, handler=handler, try=try}
187 fun makeLambda(mayInline, (var, typ), exp) = Lambda.make {arg=var, argType=typ, body=exp, mayInline=mayInline}
188
189 val parseConvention = CFunction.Convention.Cdecl <$ token "cdecl" <|>
190 CFunction.Convention.Stdcall <$ token "stdcall"
191 fun makeRuntimeTarget bytes ens mayGC maySwitch modifies readsSt writesSt =
192 CFunction.Kind.Runtime ({bytesNeeded=bytes, ensuresBytesFree=ens,
193 mayGC=mayGC, maySwitchThreads=maySwitch, modifiesFrontier=modifies,
194 readsStackTop=readsSt, writesStackTop=writesSt})
195 val parseRuntimeTarget = makeRuntimeTarget
196 <$> fromRecord "bytesNeeded" (optionOf P.uint)
197 <*> fromRecord "ensuresBytesFree" parseBool
198 <*> fromRecord "mayGC" parseBool
199 <*> fromRecord "maySwitchThreads" parseBool
200 <*> fromRecord "modifiesFrontier" parseBool
201 <*> fromRecord "readsStackTop" parseBool
202 <*> fromRecord "writesStackTop" parseBool
203 <* doneRecord
204 val parseKind = CFunction.Kind.Impure <$ token "Impure" <|>
205 CFunction.Kind.Pure <$ token "Pure" <|>
206 token "Runtime" *> P.cut parseRuntimeTarget
207
208 val parsePrototype = (fn x => x) <$$>
209 (fromRecord "args" (P.tuple ctype),
210 fromRecord "res" (optionOf ctype)) <* doneRecord
211 val parseSymbolScope = P.any
212 [CFunction.SymbolScope.External <$ token "external",
213 CFunction.SymbolScope.Private <$ token "private",
214 CFunction.SymbolScope.Public <$ token "public"]
215
216
217 val parseTarget = CFunction.Target.Indirect <$ symbol "<*>" <|>
218 CFunction.Target.Direct <$> ident
219 fun makeFFI args conv kind prototype return symbolScope target =
220 Prim.ffi (CFunction.T
221 {args=args, convention=conv, kind=kind,
222 prototype=prototype, return=return, symbolScope=symbolScope,
223 target = target})
224 val resolveFFI = token "FFI" *> P.cut(
225 makeFFI
226 <$> fromRecord "args" (P.tuple (typ resolveTycon))
227 <*> fromRecord "convention" parseConvention
228 <*> fromRecord "kind" parseKind
229 <*> fromRecord "prototype" parsePrototype
230 <*> fromRecord "return" (typ resolveTycon)
231 <*> fromRecord "symbolScope" parseSymbolScope
232 <*> fromRecord "target" parseTarget
233 <* doneRecord)
234 fun makeFFISym name cty symbolScope = Prim.ffiSymbol {name=name, cty=cty, symbolScope=symbolScope}
235 val resolveFFISym = token "FFI_Symbol" *> P.cut(
236 makeFFISym
237 <$> fromRecord "name" ident
238 <*> fromRecord "cty" (optionOf ctype)
239 <*> fromRecord "symbolScope" parseSymbolScope
240 <* doneRecord)
241
242 fun resolvePrim p = case Prim.fromString p
243 of SOME p' => P.pure p'
244 | NONE => P.fail ("valid primitive, got " ^ p)
245 fun makePrimApp(prim, targs, args) = {args=args, prim=prim, targs=targs}
246 val primAppExp = token "prim" *> P.cut (makePrimApp <$$$>
247 (P.any [
248 resolveFFI,
249 resolveFFISym,
250 (ident <* P.spaces >>= resolvePrim)],
251 (P.vector (typ resolveTycon) <|> P.pure (Vector.new0 ())) <* P.spaces,
252 P.tuple varExp <* P.spaces))
253 fun makeSelect(offset, var) = {offset=offset, tuple=var}
254 val selectExp = symbol "#" *> P.cut(makeSelect <$$>
255 (P.uint <* P.spaces,
256 varExp))
257 val profileExp = (ProfileExp.Enter <$ token "Enter" <|>
258 ProfileExp.Leave <$ token "Leave") <*>
259 P.cut ((SourceInfo.fromC o String.implode) <$>
260 P.manyCharsFailing(P.char #"\n") <* P.char #"\n" <* P.spaces)
261 fun makeConCases var (cons, def) =
262 {test=var,
263 cases=Cases.Con cons,
264 default=Option.map(def, fn x => (x, Region.bogus))}
265 fun makeWordCases var s (wds, def) =
266 {test=var,
267 cases=Cases.Word (case s of
268 8 => Type.WordSize.word8
269 | 16 => Type.WordSize.word16
270 | 32 => Type.WordSize.word32
271 | 64 => Type.WordSize.word64
272 | _ => raise Fail "makeWordCases" (* can't happen *)
273 , wds),
274 default=Option.map(def, fn x => (x, Region.bogus))}
275 fun makePat(con, exp) = P.pure (Pat.T con, exp)
276 fun makeCaseWord size (int, exp) = case size of
277 (* this is repetetive, but it's a bit awkward to rework around the fail *)
278 8 => P.pure ((WordX.fromIntInf(int, Type.WordSize.word8)), exp)
279 | 16 => P.pure ((WordX.fromIntInf(int, Type.WordSize.word16)), exp)
280 | 32 => P.pure ((WordX.fromIntInf(int, Type.WordSize.word32)), exp)
281 | 64 => P.pure ((WordX.fromIntInf(int, Type.WordSize.word64)), exp)
282 | _ => P.fail "valid word size for cases (8, 16, 32 or 64)"
283
284
285 fun exp' () = makeLet <$$>
286 (token "let" *>
287 P.many (dec ()) <* token "in", varExp <* P.str "end")
288 and dec () = P.any
289 [token "exception" *>
290 P.cut (makeExnDec <$> (constructor resolveCon resolveTycon)),
291 token "val" *> token "rec" *>
292 P.cut (makeFunDecs <$>
293 P.many (makeFunDec <$$> (typedvar <* symbol "=" <* P.spaces, lambdaExp ()))),
294 token "val" *>
295 P.cut (makeValDec <$>
296 (typedvar >>= (fn (var, ty) =>
297 (symbol "=" *> primexp ty <* P.spaces) >>= (fn primexp =>
298 P.pure (var, ty, primexp)))))]
299 and primexp typ = P.any
300 [PrimExp.Case <$> casesExp (),
301 PrimExp.ConApp <$> conAppExp,
302 PrimExp.Lambda <$> lambdaExp (),
303 (* const must come before select due to vector constants *)
304 PrimExp.Const <$> constExp (Type.tycon typ),
305 PrimExp.Handle <$> handleExp (),
306 PrimExp.PrimApp <$> primAppExp,
307 PrimExp.Profile <$> profileExp,
308 PrimExp.Raise <$> raiseExp,
309 PrimExp.Select <$> selectExp,
310 PrimExp.Tuple <$> (P.tuple varExp) <* P.spaces,
311 (* put these last, they just take identifiers so they're pretty greedy *)
312 (* App *must* procede var, due to greediness *)
313 PrimExp.App <$> makeApp <$$> (varExp, varExp),
314 PrimExp.Var <$> varExp]
315 and handleExp () = makeHandle <$$$>
316 (P.delay exp' <* P.spaces,
317 token "handle" *> P.cut typedvar,
318 P.cut (token "=>" *> P.delay exp' <* P.spaces)
319 )
320 and lambdaExp () = token "fn" *> P.cut(makeLambda <$$$>
321 (false <$ (token "noinline") <|> P.pure true,
322 typedvar,
323 token "=>" *> P.delay exp' <* P.spaces))
324 and casesExp () = P.str "case" *>
325 P.optional P.uint <* P.many1 P.space >>= (fn size => P.cut(
326 varExp <* token "of" <* P.spaces >>= (fn var =>
327 case size of
328 NONE => makeConCases var <$$>
329 (casesOf(makePat, conApp typedvar, P.delay exp'),
330 P.spaces *> P.optional(token "_" *> token "=>" *> P.delay exp'))
331 | SOME s => makeWordCases var s <$$>
332 (casesOf(makeCaseWord s, P.str "0x" *> parseHex, P.delay exp'),
333 P.spaces *> P.optional(token "_" *> token "=>" *> P.delay exp'))
334 )))
335 in
336 exp' ()
337 end
338
339 fun body resolveCon resolveTycon resolveVar = P.str "Body:" *> P.spaces
340 *> exp resolveCon resolveTycon resolveVar
341 (*pure (Exp.fromPrimExp (PrimExp.Tuple (Vector.new0 ()),
342 Type.tuple (Vector.new0 ())))*)
343
344 fun makeProgram(datatypes, overflow, body) =
345 Program.T
346 {body = body,
347 datatypes = datatypes,
348 overflow = overflow}
349
350
351 val program : Program.t Parse.t =
352 let
353 fun strip_unique s = case P.parseString
354 (String.implode <$> P.manyCharsFailing(
355 P.char #"_" *> P.many1 (P.nextSat Char.isDigit) *> P.failing P.next),
356 s) of Result.Yes s' => s'
357 | Result.No _ => s
358 val resolveCon0 = makeNameResolver(Con.newString o strip_unique)
359 fun resolveCon ident =
360 case List.peek ([Con.falsee, Con.truee, Con.overflow, Con.reff], fn con =>
361 ident = Con.toString con) of
362 SOME con => con
363 | NONE => resolveCon0 ident
364 val resolveTycon0 = makeNameResolver(Tycon.newString o strip_unique)
365 fun resolveTycon ident =
366 case List.peekMap (Tycon.prims, fn {name, tycon, ...} =>
367 if ident = name then SOME tycon else NONE) of
368 SOME con => con
369 | NONE => if ident = "unit"
370 then Tycon.tuple
371 else resolveTycon0 ident
372 val resolveVar = makeNameResolver(Var.newString o strip_unique)
373 in
374 P.compose(skipComments (),
375 clOptions *>
376 (makeProgram <$$$>
377 (datatypes resolveCon resolveTycon,
378 overflow resolveVar,
379 body resolveCon resolveTycon resolveVar <* P.spaces <* (P.failing P.next <|> P.failCut "End of file"))))
380 (* failing next to check for end of file *)
381 end
382
383end