Commit | Line | Data |
---|---|---|
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 | ||
8 | structure Sexp: SEXP = | |
9 | struct | |
10 | ||
11 | datatype t = | |
12 | Atom of string | |
13 | | List of t list | |
14 | | String of string | |
15 | ||
16 | fun 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 | ||
33 | val toString = Layout.toString o layout | |
34 | ||
35 | datatype parseResult = | |
36 | Eof | |
37 | | Error of string | |
38 | | Sexp of t | |
39 | ||
40 | fun 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 | ||
113 | fun input ins = | |
114 | parse (fn () => In.peekChar ins, | |
115 | fn () => In.inputChar ins) | |
116 | ||
117 | fun 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 | ||
140 | end |