1val _ = use "../../tools-poly/prelude.ML"; 2val _ = use "../../tools-poly/prelude2.ML"; 3val _ = PolyML.print_depth 0; 4 5fun exnMessage (e:exn) = 6 let 7 fun p (e:exn) = PolyML.prettyRepresentation (e, 10000) 8 in 9 PP.pp_to_string 75 p e 10 end 11 12fun die s = (TextIO.output(TextIO.stdErr, s ^ "\n"); 13 OS.Process.exit OS.Process.failure) 14fun lnumdie linenum extra exn = 15 die ("Exception raised on line " ^ Int.toString linenum ^ ": "^ 16 extra ^ exnMessage exn) 17 18val outputPrompt = ref "> " 19 20val quote = QFRead.fromString 21val default_linewidth = 77 22 23fun quoteFile lnum fname = 24 QFRead.inputFile fname handle e => lnumdie lnum "" e 25 26datatype lbuf = 27 LB of { 28 currentOpt : string option ref, 29 strm : TextIO.instream , 30 line : int ref 31 } 32 33fun current (LB {currentOpt, ...}) = !currentOpt 34fun linenum (LB {line, ...}) = !line 35fun advance (LB {strm, currentOpt, line}) = 36 (currentOpt := TextIO.inputLine strm ; 37 line := !line + 1) 38 39fun mklbuf strm = 40 let 41 val lb = LB {currentOpt = ref NONE, strm = strm, line = ref 0} 42 val _ = advance lb 43 in 44 lb 45 end 46 47fun mkLex s = let 48 val i = ref 0 49 val sz = size s 50 fun doit () = 51 if !i < sz then SOME (String.sub(s,!i)) before i := !i + 1 52 else NONE 53in 54 doit 55end 56 57fun compiler {push = obufPush, read = obufRD, reset = obufRST} handler infn = 58 let 59 fun pushback c infn = 60 let val used = ref false 61 in 62 fn () => if !used then infn() else (used := true; SOME c) 63 end 64 fun record_error {message,...} = PolyML.prettyPrint(obufPush,70) message 65 fun rpt infn acc = 66 (obufRST(); 67 PolyML.compiler(infn, 68 [PolyML.Compiler.CPErrorMessageProc record_error, 69 PolyML.Compiler.CPOutStream obufPush]) () 70 handle e => handler (obufRD()) e; 71 case infn() of 72 NONE => String.concat (List.rev (obufRD() :: acc)) 73 | SOME c => rpt (pushback c infn) (obufRD() :: acc) 74 ) 75 in 76 rpt infn [] 77 end 78 79fun silentUse lnum s = 80 let 81 val filecontents = quoteFile lnum s 82 val buf = HM_SimpleBuffer.mkBuffer() 83 in 84 compiler buf (lnumdie 1) (mkLex filecontents) 85 end 86 87 88fun umunge umap s = 89 let 90 fun recurse acc s = 91 case UTF8.getChar s of 92 NONE => String.concat (List.rev acc) 93 | SOME ((c, _), rest) => 94 case Binarymap.peek(umap, c) of 95 NONE => recurse (c :: acc) rest 96 | SOME s => recurse (s :: acc) rest 97 in 98 recurse [] s 99 end 100 101val elision_string1 = 102 ref "\\quad\\textit{\\small\\dots{}output elided\\dots{}}\n" 103 104fun deleteTrailingWhiteSpace s = 105 let 106 open Substring 107 val (pfx, sfx) = splitr Char.isSpace (full s) 108 val noNLsfx = dropl (not o equal #"\n") sfx 109 val term = if size noNLsfx = 0 then "" else "\n" 110 in 111 string pfx ^ term 112 end 113 114fun remove_multi_goalproved s = 115 let 116 val ss = Substring.full s 117 val (l,r) = Substring.position "\n\nGoal proved." ss 118 fun recurse ss = 119 let 120 val ss' = Substring.slice(ss, 1, NONE) 121 val (l,r) = Substring.position "\n\nGoal proved." ss' 122 in 123 if Substring.size r <> 0 then 124 case recurse r of NONE => SOME r | x => x 125 else NONE 126 end 127 in 128 if Substring.size r <> 0 then 129 case recurse r of 130 NONE => NONE 131 | SOME r' => 132 SOME (Substring.string l ^ !elision_string1 ^ 133 Substring.string (Substring.triml 1 r')) 134 else 135 NONE 136 end 137 138fun cruftSuffix sfxs s = 139 case List.find (fn (sfx,_) => String.isSuffix sfx s) sfxs of 140 NONE => NONE 141 | SOME (sfx,rep) => SOME (String.substring(s, 0, size s - size sfx) ^ rep) 142 143val cruftySuffixes = ref [ 144 ("\n : proofs\n", "\n"), 145 ("\n : proof\n", "\n"), 146 (":\n proof\n", "\n") 147 ] 148 149fun try _ [] s = s 150 | try restart (f::fs) s = case f s of 151 SOME s' => restart s' 152 | NONE => try restart fs s 153 154fun remove_nsubgoals s = 155 let 156 open Substring 157 val ss0 = full s 158 val (ss,_) = splitr Char.isSpace ss0 159 in 160 if isSuffix "subgoals" ss then 161 let 162 val (p,s) = splitr (fn c => c <> #"\n") ss 163 val (sn, rest) = splitl Char.isDigit s 164 in 165 if size sn <> 0 andalso string rest = " subgoals" then SOME (string p) 166 else NONE 167 end 168 else NONE 169 end 170 171fun delete_suffixNL sfx s = 172 if String.isSuffix (sfx ^ "\n") s then 173 String.extract(s, 0, SOME (size s - (size sfx + 1))) ^ "\n" 174 |> deleteTrailingWhiteSpace 175 else s 176 177fun remove_colonthm s = 178 s |> delete_suffixNL "thm" |> delete_suffixNL ":" 179 180fun shorten_longmetis s = 181 let 182 open Substring 183 val ss = full s 184 val (pfx, sfx) = position "\nmetis: " ss 185 fun ismetis_guff c = 186 case c of 187 #"r" => true | #"+" => true | #"[" => true | #"]" => true 188 | _ => Char.isDigit c 189 fun recurse csfx = 190 if size csfx <> 0 then 191 let 192 val (guff, rest) = splitl ismetis_guff (triml 8 csfx) 193 in 194 if size guff > 55 then 195 SOME (string (span (pfx, slice(guff, 0, SOME 50))) ^ " .... " ^ 196 string rest) 197 else 198 let 199 val (_, sfx') = position "\nmetis: " (triml 1 csfx) 200 in 201 recurse sfx' 202 end 203 end 204 else NONE 205 in 206 recurse sfx 207 end 208 209fun removeCruft s = 210 try removeCruft [cruftSuffix (!cruftySuffixes), 211 remove_multi_goalproved, 212 shorten_longmetis, 213 remove_nsubgoals] 214 s 215 216fun addIndent ws = String.translate(fn #"\n" => "\n"^ws | c => str c) 217 218val linelen_limit = ref (NONE : int option) 219 220fun trunc lim s = 221 if UTF8.size s <= lim then s 222 else if lim > 3 then UTF8.substring(s,0, lim - 3) ^ "..." 223 else UTF8.substring(s,0,lim) 224 225fun impose_linelen_limit s = 226 case !linelen_limit of 227 NONE => s 228 | SOME lim => 229 let 230 val lines = String.fields (fn c => c = #"\n") s 231 in 232 String.concatWith "\n" (map (trunc lim) lines) 233 end 234 235fun transformOutput umap ws s = 236 s |> umunge umap 237 |> removeCruft 238 |> addIndent ws 239 |> deleteTrailingWhiteSpace 240 |> (fn s => ws ^ s) 241 |> impose_linelen_limit 242 243fun spaceNotNL c = Char.isSpace c andalso c <> #"\n" 244 245val getIndent = 246 (Substring.string ## Substring.string) 247 o Substring.splitl spaceNotNL o Substring.full 248 249fun dropLWS s = 250 s |> Substring.full |> Substring.dropl Char.isSpace |> Substring.string 251 252fun strip_for_thm s = 253 let 254 val s0 = if String.isPrefix "val it =" s then 255 String.extract(s, 9, NONE) |> dropLWS 256 else s 257 in 258 remove_colonthm s0 259 end 260 261fun tailmap f [] = [] 262 | tailmap f (h::t) = h :: map f t 263 264fun poss_space_extract n s = 265 let val s = String.extract(s,n,NONE) 266 in 267 if s <> "" andalso String.sub(s,0) = #" " then 268 (String.extract(s, 1, NONE), n + 1) 269 else (s, n) 270 end 271 272fun strcat s1 s2 = s1 ^ s2 273 274fun dropWhile0 P a [] = (List.rev a,[]) 275 | dropWhile0 P a (l as h::t) = if P h then dropWhile0 P (h::a) t 276 else (List.rev a, l) 277fun dropWhile P l = dropWhile0 P [] l 278 279fun process_line debugp umap obuf origline lbuf = let 280 val {reset = obRST, ...} = obuf 281 val (ws,line) = getIndent origline 282 val indent = String.size ws 283 val oPsize = size (!outputPrompt) 284 val oPws = CharVector.tabulate(oPsize, fn _ => #" ") 285 fun getRest userPromptSize acc = 286 let 287 val _ = advance lbuf 288 (* know that we're being called on something with > indent many space 289 characters at the start of the line; that number is wssz *) 290 fun handlePromptSize wssz line = 291 (* strip indent many characters from line first *) 292 let 293 val line = String.extract(line, indent, NONE) 294 val remws = wssz - indent 295 val tostrip = Int.min(remws, userPromptSize) 296 in 297 String.extract(line, tostrip, NONE) 298 end 299 fun extract_trailing_blanklines l = 300 let 301 val (blanks, rest) = dropWhile (CharVector.all Char.isSpace) l 302 in 303 (List.rev rest, blanks |> List.rev |> String.concat) 304 end 305 in 306 case current lbuf of 307 NONE => extract_trailing_blanklines acc 308 | SOME s => 309 let 310 val (ws',_) = getIndent s 311 val wssz = String.size ws' 312 in 313 if indent < wssz then 314 getRest userPromptSize (handlePromptSize wssz s::acc) 315 else if CharVector.all Char.isSpace s then 316 getRest userPromptSize ("\n" :: acc) 317 else extract_trailing_blanklines acc 318 end 319 end 320 val assertcmd = "##assert " 321 val assertcmdsz = size assertcmd 322 val stringCReader = #read o QFRead.stringToReader true 323 fun compile exnhandle input = 324 (if debugp then 325 TextIO.output(TextIO.stdErr, input) 326 else (); 327 compiler obuf exnhandle (stringCReader input)) 328in 329 if String.isPrefix ">>>" line then 330 (advance lbuf; (ws ^ String.extract(line, 1, NONE), "")) 331 else if String.isPrefix "###" line then 332 (advance lbuf; (ws ^ String.extract(line, 1, NONE), "")) 333 else if String.isPrefix assertcmd line then 334 let 335 val e = String.substring(line, assertcmdsz, size line - assertcmdsz - 1) 336 (* for \n at end *) 337 val _ = compile (lnumdie (linenum lbuf)) 338 ("val _ = if (" ^ e ^ ") then () " ^ 339 "else die \"Assertion failed: line " ^ 340 Int.toString (linenum lbuf) ^ "\";\n") 341 val _ = advance lbuf 342 in 343 ("", "") 344 end 345 else if String.isPrefix "##thm" line then 346 let 347 val suffix = String.extract(line, 5, NONE) 348 val ((guffp,guffs), thm_name) = 349 if Char.isDigit (String.sub(suffix,0)) then 350 let 351 val (w,nm) = 352 case String.tokens Char.isSpace suffix of 353 [ds,nm] => ((valOf (Int.fromString ds), nm) 354 handle Option => 355 lnumdie (linenum lbuf) 356 "Bad integer for linewidth" 357 Option) 358 | _ => lnumdie (linenum lbuf) 359 "Malformed ##thm line" 360 (Fail "") 361 in 362 (("val _ = linewidth := " ^ Int.toString w ^"; ", 363 "val _ = linewidth := " ^ Int.toString default_linewidth ^ ";"), 364 nm ^ "\n") 365 end 366 else (("",""), dropLWS suffix) 367 val raw_output = 368 compile (lnumdie (linenum lbuf)) 369 (guffp ^ thm_name ^ " :Thm.thm;" ^ guffs) 370 val output = transformOutput umap ws (strip_for_thm raw_output) 371 |> deleteTrailingWhiteSpace 372 |> (fn s => " " ^ s) 373 val _ = advance lbuf 374 in 375 (ws ^ umunge umap thm_name, output) 376 end 377 else if String.isPrefix "##eval" line then 378 let 379 val line = String.extract(line, 6, NONE) 380 val e = Fail "" 381 val (inputpfx, firstline, indent) = 382 if String.isPrefix "[" line then 383 let 384 val ss = Substring.full line 385 val (p,s) = Substring.position "] " ss 386 val _ = Substring.size s <> 0 orelse 387 lnumdie (linenum lbuf) "Mal-formed ##eval directive" e 388 val vname = String.extract(Substring.string p, 1, NONE) 389 val firstline = String.extract(Substring.string s, 2, NONE) 390 in 391 ("val "^vname^" = ", firstline, 9 + size vname) 392 end 393 else 394 ("", String.extract(line, 1, NONE), 7) 395 handle Subscript => 396 lnumdie (linenum lbuf) "Mal-formed ##eval directive" e 397 val (input, blankstr) = getRest indent [firstline] 398 val input = input |> map (fn s => ws ^ s) |> String.concat 399 val _ = compile (lnumdie (linenum lbuf)) (inputpfx ^ input) 400 in 401 (umunge umap input, blankstr) 402 end 403 else if String.isPrefix "##parse" line then 404 let 405 val line = String.extract(line, 7, NONE) 406 val pfx = 407 if String.isPrefix "tm " line then "" 408 else if String.isPrefix "ty " line then ":" 409 else lnumdie (linenum lbuf) "Mal-formed ##parse directive" (Fail "") 410 val (firstline, indent) = (String.extract(line, 3, NONE), 10) 411 val (input, blankstr) = getRest indent [firstline] 412 val input = input |> map (fn s => ws ^ s) |> String.concat 413 val _ = compile (lnumdie (linenum lbuf)) ("``" ^ pfx ^ input ^ "``") 414 in 415 (umunge umap input, blankstr) 416 end 417 else if String.isPrefix "##use " line then 418 let 419 val fname = String.substring(line, 6, size line - 7) (* for \n at end *) 420 val _ = silentUse (linenum lbuf) fname 421 val _ = advance lbuf 422 in 423 ("", "") 424 end 425 else if String.isPrefix "##linelen_limit " line then 426 case Int.fromString (String.extract(line, 16, NONE)) of 427 NONE => lnumdie (linenum lbuf) "Mal-formed ##linelen_limit directive" 428 (Fail "") 429 | SOME i => if i >= 10 then 430 (advance lbuf; linelen_limit := SOME i; ("", "")) 431 else 432 lnumdie (linenum lbuf) 433 "Unreasonable (<10) ##linelen_limit directive" 434 (Fail "") 435 else if String.isPrefix "##nolinelen_limit" line then 436 (advance lbuf; linelen_limit := NONE; ("", "")) 437 else if String.isPrefix ">>-" line then 438 let 439 val (firstline,d) = poss_space_extract 3 line 440 val (input, blankstr) = getRest d [firstline] 441 val raw_output = compile (lnumdie (linenum lbuf)) (String.concat input) 442 in 443 ("", transformOutput umap ws raw_output ^ blankstr) 444 end 445 else if String.isPrefix ">>__" line then 446 let 447 val (firstline,d) = poss_space_extract 4 line 448 val (input, blankstr) = getRest d [firstline] 449 val _ = compile (lnumdie (linenum lbuf)) (String.concat input) 450 in 451 ("", blankstr) 452 end 453 else if String.isPrefix ">>_" line then 454 let 455 val (firstline,d) = poss_space_extract 3 line 456 val (input, blankstr) = getRest d [firstline] 457 val inp_to_print = input |> tailmap (strcat (oPws ^ ws)) |> String.concat 458 val _ = compile (lnumdie (linenum lbuf)) (String.concat input) 459 fun removeNL s = String.substring(s, 0, size s - 1) 460 in 461 (ws ^ !outputPrompt ^ removeNL (umunge umap inp_to_print), 462 !elision_string1 ^ blankstr) 463 end 464 else if String.isPrefix ">>+" line then 465 let 466 val (firstline,d) = poss_space_extract 3 line 467 val (input,blankstr) = getRest d [firstline] 468 val inp_to_print = input |> tailmap (strcat (oPws ^ ws)) |> String.concat 469 fun handle_exn extra exn = raise Fail (extra ^ exnMessage exn) 470 val raw_output = compile handle_exn (String.concat input) 471 handle Fail s => "Exception- " ^ s ^ " raised\n" 472 in 473 (ws ^ !outputPrompt ^ umunge umap inp_to_print, 474 transformOutput umap ws raw_output ^ blankstr) 475 end 476 else if String.isPrefix ">>" line then 477 let 478 val _ = obRST() 479 val (firstline, d) = poss_space_extract 2 line 480 val (input,blankstr) = getRest d [firstline] 481 val inp_to_print = input |> tailmap (strcat (oPws ^ ws)) |> String.concat 482 val raw_output = compile (lnumdie (linenum lbuf)) (String.concat input) 483 in 484 (ws ^ !outputPrompt ^ umunge umap inp_to_print, 485 transformOutput umap ws raw_output ^ blankstr) 486 end 487 else 488 (advance lbuf; (origline, "")) 489end 490 491fun read_umap fname = 492 let 493 val instrm = TextIO.openIn fname handle _ => die ("Couldn't open "^fname) 494 fun recurse (i, acc) = 495 case TextIO.inputLine instrm of 496 SOME line => 497 (if size line <= 1 then recurse (i + 1, acc) 498 else 499 case UTF8.getChar line of 500 NONE => die ("read_umap: should never happen") 501 | SOME ((cstr, _), rest) => 502 let 503 val restss = Substring.full rest 504 val range = 505 restss |> Substring.dropl Char.isSpace 506 |> Substring.dropr Char.isSpace 507 |> Substring.string 508 in 509 recurse(i + 1, Binarymap.insert(acc, cstr, range)) 510 end) 511 | NONE => acc 512 in 513 recurse(1, Binarymap.mkDict String.compare) 514 end 515 516 517fun usage() = 518 "Usage:\n "^ CommandLine.name() ^ " [-d] [umapfile]" 519 520fun main () = 521 let 522 val _ = PolyML.print_depth 100; 523 val _ = Parse.current_backend := PPBackEnd.raw_terminal 524 val (debugp,umap) = 525 case CommandLine.arguments() of 526 [] => (false, Binarymap.mkDict String.compare) 527 | ["-d"] => (true, Binarymap.mkDict String.compare) 528 | [name] => (false, read_umap name) 529 | ["-d", name] => (true, read_umap name) 530 | _ => die (usage()) 531 val (obuf as {push = obPush, ...}) = HM_SimpleBuffer.mkBuffer() 532 val _ = ReadHMF.extend_path_with_includes {verbosity = 0, lpref = loadPath} 533 val _ = Feedback.ERR_outstream := obPush 534 val _ = Feedback.WARNING_outstream := obPush 535 val _ = Feedback.MESG_outstream := obPush 536 val cvn = PolyML.Compiler.compilerVersionNumber 537 val _ = if cvn = 551 orelse cvn = 552 then 538 ignore (TextIO.inputLine TextIO.stdIn) 539 else () 540 val lb = mklbuf TextIO.stdIn 541 fun recurse lb : unit= 542 case current lb of 543 NONE => () 544 | SOME line => 545 let 546 val (i, output) = process_line debugp umap obuf line lb 547 handle e => die ("Untrapped exception: line "^ 548 Int.toString (linenum lb) ^ ": " ^ 549 exnMessage e) 550 in 551 print (i ^ output); 552 recurse lb 553 end 554 in 555 recurse lb 556 end 557