(* Table-based character classes *)

structure CharClass
    : sig
	  val isLegal : char -> bool
	  val isSpace : char -> bool
	  val isPrint : char -> bool
      end =
struct

    (* Slow versions *)
    
    fun inStr (c,s) = List.exists (fn c' => c' = c) (String.explode s)
    fun isSpace c = inStr (c, " \^M\n\t")
    fun isPrint c = let val i = ord c in i >= 0x21 andalso i <= 0x7E end
    fun isLegal c = isSpace c orelse isPrint c


    (* Fast versions *)
	
    structure W = Word8
    structure A = Word8Array
    structure V = Word8Vector

    (* setMask : A.array -> (char -> bool) * W.word -> unit *)
    (* Assumes Char.maxOrd <= size buf *)
    fun setMask buf (p, mask) =
	let
	    fun mark i = A.update (buf, i, W.orb (A.sub (buf, i), mask))

	    fun loop i = if i = Char.maxOrd
			     then ()
			 else (if p (chr i) then mark i else ();
			       loop (i+1))
	in
	    loop 0
	end

    (* speedUp : (char -> bool) list -> (char -> bool) list *)
    exception SpeedUp
    fun speedUp (predicates : (char -> bool) list) =
	let
	    val arr = if Char.maxOrd > 255 then raise SpeedUp
		      else A.array (Char.maxOrd, 0w0)
	    val masks : W.word list =
		if length predicates > 8 then raise SpeedUp
		else List.take ([0w1, 0w2, 0w4, 0w8,
				 0w16, 0w32, 0w64, 0w128], length predicates)
	    val _ = ListPair.app (setMask arr) (predicates, masks)
	    val vec = A.extract (arr, 0, NONE)
	    fun lookup mask c = W.andb (mask, V.sub (vec, ord c)) <> 0w0
	in
	    List.map lookup masks
	end

    val [isLegal,isSpace,isPrint] =
	speedUp [isLegal,isSpace,isPrint]

end
