(*
  author: cks


  whitespace elimination.
*)

structure WhiteSpace :> WHITESPACE =
  struct

    structure M = Meaning

    fun optimize e =
      let
        val sp = ref NONE

        fun elim (p, Il.Tag (tl, e)) = Il.Tag (tl, elim (M.addTags (p, tl), e))
          | elim (p, Il.Seq el) = Il.Seq (map (fn x => elim (p, x)) el)
          | elim (p, e as (Il.Text str)) =
            if (M.isSetAttribute (p, M.Tt)) then
              if String.size str > 0 then
                (sp := NONE; e)
              else
                e
            else
              let
                val src = Byte.stringToBytes str
                val len = Word8Vector.length src
                val dst = Word8Array.array (len, 0wx20)
                val sp' = SOME (M.prop2SpaceProp p)

                fun isSPC 0wx20 = true
                  | isSPC 0wxa = true
                  | isSPC 0wxd = true
                  | isSPC 0wx9 = true
                  | isSPC _ = false

                fun cal (i, j, k, sp) =
                    if (j = len) then
                      (if (j > i) then
                        Word8Array.copyVec {  dst=dst,
                                              di=k,
                                              src=src,
                                              si=i,
                                              len=SOME(j-i) }
                      else
                        (); (k + j - i, sp))
                    else
                      if isSPC (Word8Vector.sub (src, j)) then
                        case (sp', sp) of
                          (SOME a, SOME b) =>
                            if M.propertyEqual (a, b) then
                              (if (j > i) then
                                Word8Array.copyVec {  dst=dst,
                                                      di=k,
                                                      src=src,
                                                      si=i,
                                                      len=SOME(j-i) }
                              else
                                (); cal (j + 1, j + 1, k + j - i, sp'))
                            else
                              cal (i, j + 1, k, sp')
                        | _ => cal (i, j + 1, k, sp')
                      else
                        cal (i, j + 1, k, NONE)

                val (k, sp') = cal (0, 0, 0, !sp)

                val _ = sp := sp'
              in
                Il.Text (Byte.unpackString (dst, 0, SOME k))
              end
      in
        elim (M.null, e)
      end

  end
