(* lowercase-word.sml *) (* Copyright (C) 2008 Alley Stoughton This file is part of crypto, a cryptogram encoder/decoder. See the file COPYING.txt for copying and usage restrictions *) structure LowercaseWord :> LOWERCASE_WORD = struct (* val isWord : string -> bool isWord s tests whether each character of s is a lowercase letter *) fun isWord s = let val sz = size s (* val all : int -> bool in a call all i, we will have that 0 <= i <= sz all i tests whether all characters of s at positions that are >= i and < sz are lowercase letters *) fun all i = i = sz orelse Char.isLower(String.sub(s, i)) andalso all(i + 1) in all 0 end datatype line = EOFLine | AbortLine | TokensLine of string list (* val getLine : bool -> TextIO.instream -> line *) fun getLine inter stm = case TextIO.inputLine stm of NONE => EOFLine | SOME x => if inter then if x = ".\n" then EOFLine else if x = "!\n" then AbortLine else TokensLine(String.tokens Char.isSpace x) else TokensLine(String.tokens Char.isSpace x) exception Abort (* inter stands for interactive; if inter is true, then lines consisting of ".\n" (end of file) and "!\n" (abort) have their special meanings *) fun processStream (inter, initNormal, wordNormal, newlineNormal, eofNormal, initError, wordError, newlineError, eofError) stm = let fun fold(a, c, nil) = (case getLine inter stm of EOFLine => (eofNormal a, eofError c) | AbortLine => raise Abort | TokensLine xs => fold(newlineNormal a, newlineError c, xs)) | fold(a, c, x :: xs) = if isWord x then fold(wordNormal(x, a), c, xs) else fold(a, wordError(x, c), xs) in fold(initNormal, initError, nil) end exception UnableToOpenFile fun processFile (initNormal, wordNormal, newlineNormal, eofNormal, initError, wordError, newlineError, eofError) file = let val stm = TextIO.openIn file handle _ => raise UnableToOpenFile val x = processStream (false, initNormal, wordNormal, newlineNormal, eofNormal, initError, wordError, newlineError, eofError) stm (* AVOIDING CML BUG: val _ = TextIO.closeIn stm *) in x end fun processString (initNormal, wordNormal, newlineNormal, eofNormal, initError, wordError, newlineError, eofError) str = let val stm = TextIO.openString str val x = processStream (false, initNormal, wordNormal, newlineNormal, eofNormal, initError, wordError, newlineError, eofError) stm (* AVOIDING CML BUG: val _ = TextIO.closeIn stm *) in x end (* a value of type normal represents the reversal of the reversal of its elements *) type normal = char list list list exception Normal val initNormal = nil fun wordNormal (y, (xs :: xss)) = (explode y :: xs) :: xss | wordNormal _ = raise Normal fun newlineNormal xs = nil :: xs fun eofNormal xs = rev(map rev xs) (* a value (n, ps) : error represents (n, rev ps) *) type error = int * (int * string)list val initError = (0, nil) fun wordError(x, (n, ps)) = (n, (n, x) :: ps) fun newlineError(n, ps) = (n + 1, ps) fun eofError(_, ps) = rev ps end;