(*--------------------------------------------------------------------------*) (* Structure: UtilString *) (*--------------------------------------------------------------------------*) signature UtilString = sig val quoteString : char -> string -> string val numberNth : int -> string val prependAnA : string -> string val nBlanks : int -> string val padxLeft : char -> string * int -> string val padxRight : char -> string * int -> string val breakLines : int -> string -> string list val toUpperFirst : string -> string val toUpperString : string -> string val Int2String : int -> string val Bool2xString : string * string -> bool -> string val Bool2String : bool -> string val Option2xString : string * (('a -> string) -> 'a -> string) -> ('a -> string) -> 'a option -> string val Option2String0 : ('a -> string) -> 'a option -> string val Option2String : ('a -> string) -> 'a option -> string val List2xString : string * string * string -> ('a -> string) -> 'a list -> string val List2String0 : ('a -> string) -> 'a list -> string val List2String : ('a -> string) -> 'a list -> string val Vector2xString : string * string * string -> ('a -> string) -> 'a vector -> string val Vector2String : ('a -> string) -> 'a vector -> string end structure UtilString : UtilString = struct fun quoteString q s = let val quote = String.implode [q] in quote^s^quote end (*--------------------------------------------------------------------*) (* generate a string with the ordinal number of n, by appending *) (* "st", "nd", "rd" or "th" to the number. *) (*--------------------------------------------------------------------*) fun numberNth n = let val suffix = case n mod 9 of 1 => "st" | 2 => "nd" | 3 => "rd" | _ => "th" in Int.toString n^suffix end (*--------------------------------------------------------------------*) (* is the single character c represented by a word starting with a *) (* vocal in the alphabet? (l~ell->true, k~kay->false) *) (*--------------------------------------------------------------------*) fun vocalLetter c = case Char.toLower c of #"a" => true | #"f" => true | #"h" => true | #"i" => true | #"l" => true | #"m" => true | #"n" => true | #"o" => true | #"r" => true | #"s" => true | #"x" => true | #"8" => true | _ => false (*--------------------------------------------------------------------*) (* is character c a vocal? *) (*--------------------------------------------------------------------*) fun isVocal c = case Char.toLower c of #"a" => true | #"e" => true | #"i" => true | #"o" => true | #"u" => true | _ => false (*--------------------------------------------------------------------*) (* does a word require "an" as undefinite article? true if: *) (* - it is a single letter that starts with a vocal in the alphabet *) (* - its first two letters are capitals, i.e. it is an abbreviation, *) (* and its first letter starts with a vocal in the alphabet *) (* - it has more than one letter, is not an abbreviation, and either *) (* + it starts with a, i or o *) (* + it starts with e and the second letter is not a u (europe) *) (* + it starts with a u and continues with a vocal (very unlikely, *) (* only in c.s., like uuencoded or uid *) (* + it starts with a u, continues with a consonant not followed by *) (* an i (like in unicode); that is something like un-... *) (* This ruleset is not complete since it does not cover, e.g., the *) (* word uninvented, but sufficient for most cases. *) (* (Is english pronounciation decidable at all?) *) (*--------------------------------------------------------------------*) fun extendsAtoAn word = case String.explode word of nil => false | [c] => vocalLetter c | c1::c2::cs => if not (Char.isLower c1 orelse Char.isLower c2) then vocalLetter c1 else case Char.toLower c1 of #"a" => true | #"i" => true | #"o" => true | #"e" => Char.toLower c2 <> #"u" | #"u" => if isVocal c2 then false else (case cs of nil => true | c3::_ => Char.toLower c3 <> #"i") | _ => false (*--------------------------------------------------------------------*) (* add an undefinite article to a word. *) (*--------------------------------------------------------------------*) fun prependAnA word = if extendsAtoAn word then "an "^word else "a "^word (*--------------------------------------------------------------------*) (* generate a list/string of n times character c. *) (*--------------------------------------------------------------------*) fun nCharsC c n = if n>0 then c::nCharsC c (n-1) else nil fun nChars c n = String.implode (nCharsC c n) val nBlanks = nChars #" " (*--------------------------------------------------------------------*) (* add a minimal number of characters c to the left/right of a string *) (* in order to make its length at least n. *) (*--------------------------------------------------------------------*) fun padxLeft c (s,n) = (nChars c (n-String.size s))^s fun padxRight c (s,n) = s^(nChars c (n-String.size s)) val padLeft = padxLeft #" " val padRight = padxRight #" " (*--------------------------------------------------------------------*) (* break a string into several lines of length width. *) (*--------------------------------------------------------------------*) fun breakLines width str = let val tokens = String.tokens (fn c => #" "=c) str fun makeLine(toks,lines) = if null toks then lines else (String.concat (rev toks))::lines fun doit w (toks,lines) nil = makeLine(toks,lines) | doit w (toks,lines) (one::rest) = let val l = String.size one val w1 = w+l in if w1=width then doit 0 (nil,one::makeLine(toks,lines)) rest else doit (l+1) ([" ",one],makeLine(toks,lines)) rest end in List.rev (doit 0 (nil,nil) tokens) end (*--------------------------------------------------------------------*) (* convert the first/all characters of a string to upper case *) (*--------------------------------------------------------------------*) fun toUpperFirst str = case String.explode str of nil => "" | c::cs => String.implode (Char.toUpper c::cs) fun toUpperString str = String.implode(map Char.toUpper (String.explode str)) (*--------------------------------------------------------------------*) (* return a string representation of an int, char or unit. *) (*--------------------------------------------------------------------*) val Int2String = Int.toString val Char2String = Char.toString fun Unit2String() = "()" (*--------------------------------------------------------------------*) (* return a string representation of a boolean. *) (*--------------------------------------------------------------------*) fun Bool2xString (t,f) b = if b then t else f val Bool2String = Bool2xString ("true","false") (*--------------------------------------------------------------------*) (* return a string representation of an option. *) (* the first arg is a string for the NONE case, the second a function *) (* that converts x to a string, given a function for doing so. *) (*--------------------------------------------------------------------*) fun Option2xString (none,Some2String) x2String opt = case opt of NONE => none | SOME x => Some2String x2String x fun Option2String0 x2String = Option2xString ("",fn f => fn x => f x) x2String fun Option2String x2String = Option2xString ("NONE",fn f => fn x => "SOME "^f x) x2String (*--------------------------------------------------------------------*) (* return a string representation of list; start with pre, separate *) (* with sep and finish with post; use X2String for each element. *) (*--------------------------------------------------------------------*) fun List2xString (pre,sep,post) X2String nil = pre^post | List2xString (pre,sep,post) X2String l = let fun doit nil _ = [post] | doit (x::r) str = str::X2String x::doit r sep in String.concat (doit l pre) end fun List2String X2String nil = "[]" | List2String X2String l = let fun doit nil _ = ["]"] | doit (x::r) str = str::X2String x::doit r "," in String.concat (doit l "[") end fun List2String0 X2String nil = "" | List2String0 X2String l = let fun doit nil _ = nil | doit (x::r) str = str::X2String x::doit r " " in String.concat (doit l "") end (* a compiler bug in smlnj 110 makes the following uncompilable: *) (* fun List2String X2String xs = List2xString ("[",",","]") X2String xs *) (* fun List2String0 X2String xs = List2xString (""," ","") X2String xs *) (*--------------------------------------------------------------------*) (* return a string representation of list; start with pre, separate *) (* with sep and finish with post; use X2String for each element. *) (*--------------------------------------------------------------------*) fun Vector2xString (pre,sep,post) X2String vec = if Vector.length vec=0 then pre^post else String.concat (pre::X2String(Vector.sub(vec,0)):: VectorSlice.foldri (fn (_,x,yet) => sep::X2String x::yet) [post] (VectorSlice.slice (vec,1,NONE))) fun Vector2String X2String vec = Vector2xString ("#[",",","]") X2String vec end