Import Upstream version 20180207
[hcoop/debian/mlton.git] / lib / mlton / basic / sexp.sml
CommitLineData
7f918cf1
CE
1(* Copyright (C) 1999-2005 Henry Cejtin, Matthew Fluet, Suresh
2 * Jagannathan, and Stephen Weeks.
3 *
4 * MLton is released under a BSD-style license.
5 * See the file MLton-LICENSE for details.
6 *)
7
8structure Sexp: SEXP =
9struct
10
11datatype t =
12 Atom of string
13 | List of t list
14 | String of string
15
16fun layout sexp =
17 let
18 open Layout
19 in
20 case sexp of
21 Atom s => str s
22 | List sexps => paren (align (List.map (sexps, layout)))
23 | String s =>
24 str (concat ["\"",
25 String.translate (s, fn c =>
26 case c of
27 #"\"" => "\"\""
28 | #"\\" => "\\\\"
29 | _ => String.fromChar c),
30 "\""])
31 end
32
33val toString = Layout.toString o layout
34
35datatype parseResult =
36 Eof
37 | Error of string
38 | Sexp of t
39
40fun parse (peekChar: unit -> char option,
41 getChar: unit -> char option): parseResult =
42 let
43 exception Err of string
44 fun error s = raise (Err s)
45 fun atom (cs: char list): t =
46 let
47 fun done () = Atom (String.fromListRev cs)
48 in
49 case peekChar () of
50 NONE => done ()
51 | SOME c =>
52 if Char.isSpace c
53 orelse c = #"(" orelse c = #")" orelse c = #"\""
54 orelse c = #";"
55 then done ()
56 else
57 case getChar () of
58 NONE => done ()
59 | SOME c => atom (c :: cs)
60 end
61 fun string (cs: char list): t =
62 case getChar () of
63 NONE => error "eof in middle of string"
64 | SOME c =>
65 (case c of
66 #"\"" => String (String.fromListRev cs)
67 | #"\\" => (case getChar () of
68 NONE => error "eof in middle of string"
69 | SOME c => string (c :: cs))
70 | _ => string (c :: cs))
71 fun ignoreLine (): bool =
72 case getChar () of
73 NONE => false
74 | SOME c => c = #"\n" orelse ignoreLine ()
75 fun sexp (): t option =
76 case getChar () of
77 NONE => NONE
78 | SOME c => sexpChar c
79 and sexpChar (c: char): t option =
80 case c of
81 #"(" => SOME (List (finishList []))
82 | #")" => error "unmatched )"
83 | #"\"" => SOME (string [])
84 | #";" => if ignoreLine ()
85 then sexp ()
86 else NONE
87 | _ => if Char.isSpace c
88 then sexp ()
89 else SOME (atom [c])
90 and finishList (elts: t list): t list =
91 case getChar () of
92 NONE => error "unmatched ("
93 | SOME c =>
94 (case c of
95 #")" => rev elts
96 | #";" =>
97 if ignoreLine ()
98 then finishList elts
99 else error "unmatched ("
100 | _ =>
101 if Char.isSpace c
102 then finishList elts
103 else
104 case sexpChar c of
105 NONE => error "unmatched ("
106 | SOME s => finishList (s :: elts))
107 in
108 (case sexp () of
109 NONE => Eof
110 | SOME s => Sexp s) handle Err s => Error s
111 end
112
113fun input ins =
114 parse (fn () => In.peekChar ins,
115 fn () => In.inputChar ins)
116
117fun fromString s =
118 let
119 val n = String.size s
120 val r = ref 0
121 fun peekChar () =
122 let
123 val i = !r
124 in
125 if i = n
126 then NONE
127 else SOME (String.sub (s, i))
128 end
129 fun getChar () =
130 let
131 val res = peekChar ()
132 val _ = if isSome res then r := 1 + !r else ()
133 in
134 res
135 end
136 in
137 parse (peekChar, getChar)
138 end
139
140end