Mercurial > urweb
comparison src/tutorial.sml @ 1493:9cb923efea4d
Generated pretty-printed HTML for a simple tutorial source file
author | Adam Chlipala <adam@chlipala.net> |
---|---|
date | Fri, 15 Jul 2011 16:50:55 -0400 |
parents | |
children | 9ef6dd0df7a0 |
comparison
equal
deleted
inserted
replaced
1492:175b6d52252d | 1493:9cb923efea4d |
---|---|
1 (* Copyright (c) 2011, Adam Chlipala | |
2 * All rights reserved. | |
3 * | |
4 * Redistribution and use in source and binary forms, with or without | |
5 * modification, are permitted provided that the following conditions are met: | |
6 * | |
7 * - Redistributions of source code must retain the above copyright notice, | |
8 * this list of conditions and the following disclaimer. | |
9 * - Redistributions in binary form must reproduce the above copyright notice, | |
10 * this list of conditions and the following disclaimer in the documentation | |
11 * and/or other materials provided with the distribution. | |
12 * - The names of contributors may not be used to endorse or promote products | |
13 * derived from this software without specific prior written permission. | |
14 * | |
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
25 * POSSIBILITY OF SUCH DAMAGE. | |
26 *) | |
27 | |
28 structure Tutorial :> TUTORIAL = struct | |
29 | |
30 fun readAll' inf = | |
31 let | |
32 fun loop acc = | |
33 case TextIO.inputLine inf of | |
34 NONE => Substring.full (String.concat (rev acc)) | |
35 | SOME line => loop (line :: acc) | |
36 in | |
37 loop [] | |
38 before TextIO.closeIn inf | |
39 end | |
40 | |
41 fun readAll fname = readAll' (TextIO.openIn fname) | |
42 | |
43 fun doUr fname = | |
44 let | |
45 val eval = TextIO.openOut "/tmp/eval.ur" | |
46 val gen = TextIO.openOut "/tmp/gen.ur" | |
47 | |
48 fun untilEnd source = | |
49 let | |
50 val (befor, after) = Substring.position "(* end *)" source | |
51 in | |
52 if Substring.isEmpty after then | |
53 (source, Substring.full "") | |
54 else | |
55 (befor, Substring.slice (after, 9, NONE)) | |
56 end | |
57 | |
58 fun doDirectives (count, source) = | |
59 let | |
60 val safe = String.translate (fn #"<" => "<" | |
61 | #"&" => "&" | |
62 | #"{" => "{" | |
63 | #"(" => "(" | |
64 | #"\n" => "(*NL*)\n" | |
65 | ch => str ch) o Substring.string | |
66 | |
67 val (befor, after) = Substring.position "(* begin " source | |
68 | |
69 fun default () = (TextIO.outputSubstr (eval, source); | |
70 TextIO.output (gen, safe source)) | |
71 in | |
72 if Substring.isEmpty after then | |
73 default () | |
74 else | |
75 let | |
76 val (command, after) = Substring.splitl (not o Char.isSpace) (Substring.slice (after, 9, NONE)) | |
77 in | |
78 if Substring.isEmpty after then | |
79 default () | |
80 else | |
81 let | |
82 val (_, rest) = Substring.position "*)" after | |
83 in | |
84 if Substring.isEmpty rest then | |
85 default () | |
86 else | |
87 let | |
88 val (arg, source) = untilEnd (Substring.slice (rest, 3, NONE)) | |
89 val () = (TextIO.outputSubstr (eval, befor); | |
90 TextIO.output (gen, safe befor)) | |
91 val (count, skip) = | |
92 case Substring.string command of | |
93 "hide" => (TextIO.outputSubstr (eval, arg); | |
94 (count, true)) | |
95 | "eval" => (TextIO.output (eval, "val _eval"); | |
96 TextIO.output (eval, Int.toString count); | |
97 TextIO.output (eval, " = "); | |
98 TextIO.outputSubstr (eval, arg); | |
99 TextIO.output (eval, "\n\n"); | |
100 | |
101 TextIO.output (gen, safe arg); | |
102 TextIO.output (gen, "== {[_eval"); | |
103 TextIO.output (gen, Int.toString count); | |
104 TextIO.output (gen, "]}"); | |
105 | |
106 (count + 1, false)) | |
107 | s => raise Fail ("Unknown tutorial directive: " ^ s) | |
108 in | |
109 doDirectives (count, if skip then | |
110 #2 (Substring.splitl Char.isSpace source) | |
111 else | |
112 source) | |
113 end | |
114 end | |
115 end | |
116 end | |
117 in | |
118 doDirectives (0, readAll fname); | |
119 TextIO.closeOut gen; | |
120 | |
121 TextIO.output (eval, "\n\nfun main () : transaction page =\nreturn <xml><body>"); | |
122 TextIO.outputSubstr (eval, readAll "/tmp/gen.ur"); | |
123 TextIO.output (eval, "</body></xml>"); | |
124 TextIO.closeOut eval; | |
125 | |
126 if Compiler.compile "/tmp/eval" then | |
127 let | |
128 val proc = Unix.execute ("/bin/sh", ["-c", "/tmp/eval.exe /main"]) | |
129 val inf = Unix.textInstreamOf proc | |
130 val s = readAll' inf | |
131 val _ = Unix.reap proc | |
132 | |
133 val (befor, after) = Substring.position "<sc>" s | |
134 in | |
135 if Substring.isEmpty after then | |
136 print ("Bad output for " ^ fname ^ "! [1]\n") | |
137 else | |
138 let | |
139 val after = Substring.slice (after, 4, NONE) | |
140 val (befor, after) = Substring.position "</body>" after | |
141 in | |
142 if Substring.isEmpty after then | |
143 print ("Bad output for " ^ fname ^ "! [2]\n") | |
144 else | |
145 let | |
146 val outf = TextIO.openOut "/tmp/final.ur" | |
147 | |
148 fun eatNls source = | |
149 let | |
150 val (befor, after) = Substring.position "(*NL*)" source | |
151 in | |
152 if Substring.isEmpty after then | |
153 TextIO.outputSubstr (outf, source) | |
154 else | |
155 (TextIO.outputSubstr (outf, befor); | |
156 eatNls (Substring.slice (after, 6, NONE))) | |
157 end | |
158 | |
159 val cmd = "emacs --eval \"(progn " | |
160 ^ "(global-font-lock-mode t) " | |
161 ^ "(add-to-list 'load-path \\\"" | |
162 ^ Config.sitelisp | |
163 ^ "/\\\") " | |
164 ^ "(load \\\"urweb-mode-startup\\\") " | |
165 ^ "(urweb-mode) " | |
166 ^ "(find-file \\\"/tmp/final.ur\\\") " | |
167 ^ "(switch-to-buffer (htmlize-buffer)) " | |
168 ^ "(write-file \\\"" | |
169 ^ OS.Path.mkAbsolute {relativeTo = OS.FileSys.getDir (), | |
170 path = OS.Path.joinBaseExt {base = OS.Path.base fname, ext = SOME "html"}} | |
171 ^ "\\\") " | |
172 ^ "(kill-emacs))\"" | |
173 in | |
174 eatNls befor; | |
175 TextIO.closeOut outf; | |
176 ignore (OS.Process.system cmd) | |
177 end | |
178 end | |
179 end | |
180 else | |
181 () | |
182 end | |
183 | |
184 fun make dirname = | |
185 let | |
186 val dir = OS.FileSys.openDir dirname | |
187 | |
188 fun doDir () = | |
189 case OS.FileSys.readDir dir of | |
190 NONE => OS.FileSys.closeDir dir | |
191 | SOME fname => | |
192 (if OS.Path.ext fname = SOME "ur" then | |
193 doUr (OS.Path.joinDirFile {dir = dirname, file = fname}) | |
194 else | |
195 (); | |
196 doDir ()) | |
197 in | |
198 Settings.setProtocol "static"; | |
199 doDir () | |
200 end | |
201 | |
202 end |