--
--
-- String
--
--

string :=
  matcher
    | regexCg #$regexpr $ $ $ as (string, list string, string) with
      | $tgt -> regexCg regexpr tgt
    | regex #$regexpr $ $ $ as (string, string, string) with
      | $tgt -> regex regexpr tgt
    | [] as () with
      | $tgt -> if "" = tgt then [()] else []
    | $ :: $ as (char, string) with
      | $tgt -> if "" = tgt then [] else [unconsString tgt]
    | $ ++ #$px :: $ as (string, string) with
      | $tgt ->
        matchAll S.split (pack [px]) tgt as list string with
          | (![] & $xs) ++ ![] & $ys ->
            (S.intercalate (pack [px]) xs, S.intercalate (pack [px]) ys)
    | $ ++ #$pxs ++ $ as (string, string) with
      | $tgt ->
        matchAll S.split pxs tgt as list string with
          | (![] & $xs) ++ ![] & $ys ->
            (S.intercalate pxs xs, S.intercalate pxs ys)
    | $ ++ $ as (string, string) with
      | $tgt ->
        matchAll tgt as string with
          | loop $i (1, $n)
              ($xa_i :: ...)
              $rs -> (pack (map (\i -> xa_i) (between 1 n)), rs)
    | #$val as () with
      | $tgt -> if val = tgt then [()] else []
    | $ as (something) with
      | $tgt -> [tgt]

--
-- String as collection
--
S.isEmpty xs := xs = ""

S.cons x xs := appendString (pack [x]) xs

S.head xs :=
  match xs as string with
    | $x :: _ -> x

S.tail xs :=
  match xs as string with
    | _ :: $r -> r

S.last str :=
  match str as string with
    | _ ++ $c :: [] -> c

S.map f xs := pack (map f (unpack xs))

S.length xs := lengthString xs

S.split sep ls := splitString sep ls

S.append xs ys := appendString xs ys

S.concat xss := foldr (\xs rs -> S.append xs rs) "" xss

S.intercalate := compose intersperse S.concat

S.replace before after str := S.intercalate after (S.split before str)

--
-- Alphabet
--
C.between c1 c2 := map itoc (between (ctoi c1) (ctoi c2))

C.isBetween c1 c2 c := ctoi c >= ctoi c1 && ctoi c <= ctoi c2

isAlphabet c := C.isBetween 'a' 'z' c || C.isBetween 'A' 'Z' c

isAlphabetString s := all isAlphabet (unpack s)

upperCase c := if C.isBetween 'a' 'z' c then itoc (ctoi c - 32) else c

lowerCase c := if C.isBetween 'A' 'Z' c then itoc (ctoi c + 32) else c
