/*---------------------------------------------------------------------------
  Copyright 2012-2016 Microsoft Corporation.
 
  This is free software; you can redistribute it and/or modify it under the
  terms of the Apache License, Version 2.0. A copy of the License can be
  found in the file "license.txt" at the root of this distribution.
---------------------------------------------------------------------------*/

/* Regular expressions.

   The regular expressions conform to the regular expressions of JavaScript
   as described at <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions>
*/  
module regex

import int32

extern include {
  cs file "regex-inline.cs"
  js file "regex-inline.js"
}

// Abstract type of a regular expression object
abstract struct regexstd/text/regex/regex: V( objobj: any: anystd/core/any: V )

// The result of a regular expression match, where `matched` is the content that
// was matched, and `groups` is a vector that contains the content for each 
// capture group (where `groups[0] == matched`).
// The `slice` field contains the `:sslice` of the string that was matched.
public struct matchedstd/text/regex/matched: V( sliceslice: sslice : sslicestd/core/sslice: V, matchedmatched: string :stringstd/core/string: V, groupsgroups: groups : groupsstd/text/regex/groups: V )

abstract struct groupsstd/text/regex/groups: V( grpgrp: any : anystd/core/any: V )

extern regex-sourcestd/text/regex/regex-source: (r : any) -> string( rr: any : anystd/core/any: V ) : stringstd/core/string: V {
  cs "RegEx.Source"
  js inline "#1.regex.source"
}

extern groups-indexstd/text/regex/groups-index: (groups : any, index : int32) -> string( groupsgroups: any : anystd/core/any: V, indexindex: int32 : int32std/core/int32: V ) : stringstd/core/string: V {
  cs "RegEx.GroupsIndex"
  js inline "((#1)[#2] != null ? (#1)[#2] : '')"
} 

extern inline groups-matched-onstd/text/regex/groups-matched-on: (groups : any, index : int32) -> int32( groups : anystd/core/any: V, index : int32std/core/int32: V ) : int32std/core/int32: V {
  cs "RegEx.GroupsMatchedOn"
  js inline "(#1[#2] ? 1 : 0)"  // mmm, doesn't catch empty group match.. but FireFox does not distinguish those :-(
} 

// For alternative regular expressions, return the alternative that was matched.
// If this was not an alternative regex, returns `-1`
public fun alternativestd/text/regex/alternative: (groups : groups) -> int( groupsgroups: groups : groupsstd/text/regex/groups: V ) : intstd/core/int: V {
  xalternativestd/text/regex/xalternative: (groups : groups) -> int32(groupsgroups: groups).intstd/core/int.1: (i : int32) -> int
}

extern xalternativestd/text/regex/xalternative: (groups : groups) -> int32( groupsgroups: groups : groupsstd/text/regex/groups: V ) : int32std/core/int32: V {
  cs inline "-1"
  js inline "(#1.alternative!=null ? #1.alternative : -1)"
}

extern regex-createstd/text/regex/regex-create: (string, int32, int32) -> any : (stringstd/core/string: V,int32std/core/int32: V,int32std/core/int32: V) -> anystd/core/any: V {
  cs "RegEx.Create"
  js "$regexCreate"
}

extern regex-create-altstd/text/regex/regex-create-alt: (vector<string>, int32, int32) -> any : (vectorstd/core/vector: V -> V<stringstd/core/string: V>,int32std/core/int32: V,int32std/core/int32: V) -> anystd/core/any: V {
  cs "RegEx.CreateAlt"
  js "$regexCreateAlt"
}

extern regex-execstd/text/regex/regex-exec: (any, string, int32) -> maybe<matched> : (anystd/core/any: V,stringstd/core/string: V,int32std/core/int32: V) -> maybestd/core/maybe: V -> V<matchedstd/text/regex/matched: V> {
  cs "RegEx.Exec"
  js "$regexExec"
}

extern regex-exec-allstd/text/regex/regex-exec-all: (any, string, int32) -> vector<matched> : (anystd/core/any: V,stringstd/core/string: V,int32std/core/int32: V) -> vectorstd/core/vector: V -> V<matchedstd/text/regex/matched: V> {
  cs "RegEx.ExecAll"
  js "$regexExecAll"
}

extern regex-replace-funstd/text/regex/regex-replace-fun: forall<e> (any, string, (matched) -> e string, int32, int32) -> e string : forall<ee: E> (anystd/core/any: V, stringstd/core/string: V, (matchedstd/text/regex/matched: V) -> ee: E stringstd/core/string: V, int32std/core/int32: V, int32std/core/int32: V ) -> ee: E stringstd/core/string: V {
  cs "RegEx.ReplaceFun"
  js "$regexReplaceFun"
}

extern regex-replacestd/text/regex/regex-replace: (any, string, string, int32, int32) -> string : (anystd/core/any: V, stringstd/core/string: V, stringstd/core/string: V, int32std/core/int32: V, int32std/core/int32: V ) -> stringstd/core/string: V {
  cs "RegEx.Replace"
  js "$regexReplace"
}

extern regex-splitstd/text/regex/regex-split: (any, string, int32, int32) -> vector<string> : (anystd/core/any: V, stringstd/core/string: V, int32std/core/int32: V, int32std/core/int32: V ) -> vectorstd/core/vector: V -> V<stringstd/core/string: V> {
  cs "RegEx.Split"
  js "$regexSplit"
}

// Return the string following match 
public fun afterstd/text/regex/after: (cap : matched) -> string( capcap: matched : matchedstd/text/regex/matched: V ) : stringstd/core/string: V {
  capcap: matched.slicestd/text/regex/slice: (matched : matched) -> sslice.afterstd/core/after: (slice : sslice) -> sslice.stringstd/core/string.3: (slice : sslice) -> string
}

// Return the string following match but ensure we
// advance by at least 1 character (to ensure termination with empty matches). 
public fun after1std/text/regex/after1: (cap : matched) -> string( capcap: matched : matchedstd/text/regex/matched: V ) : stringstd/core/string: V {
  val ss: sslice = capcap: matched.slicestd/text/regex/slice: (matched : matched) -> sslice
  if (sstd/core/True: bool.empty?std/core/empty?: (slice : sslice) -> bool) then ss: sslice.advancestd/core/advance: (slice : sslice, count : int) -> sslice(1).afterstd/core/after: (slice : sslice) -> sslice.stringstd/core/string.3: (slice : sslice) -> string else ss: sslice.afterstd/core/after: (slice : sslice) -> sslice.stringstd/core/string.3: (slice : sslice) -> string
}

// Return the string before a match 
public fun beforestd/text/regex/before: (cap : matched) -> string( capcap: matched : matchedstd/text/regex/matched: V ) : stringstd/core/string: V {
  capcap: matched.slicestd/text/regex/slice: (matched : matched) -> sslice.beforestd/core/before: (slice : sslice) -> sslice.stringstd/core/string.3: (slice : sslice) -> string
}


// Return the pattern as a string
public fun sourcestd/text/regex/source: (r : regex) -> string( rr: regex : regexstd/text/regex/regex: V ) : stringstd/core/string: V {
  regex-sourcestd/text/regex/regex-source: (r : any) -> string(rr: regex.objstd/text/regex/obj: (regex : regex) -> any)
}

// How many groups are captured by this regex?
public fun groups-countstd/text/regex/groups-count: (r : regex) -> int( rr: regex : regexstd/text/regex/regex: V ) : intstd/core/int: V 
{
  rr: regex.sourcestd/text/regex/source: (r : regex) -> string.replace-allstd/text/regex/replace-all.1: (s : string, regex : regex, repl : string) -> string(rxNonGroupstd/text/regex/rxNonGroup: regex,"").countstd/core/count.1: (s : string) -> int;
}
val rxNonGroupstd/text/regex/rxNonGroup: regex = regexstd/text/regex/regex: (regex : string, ignorecase : ?bool, multiline : ?bool) -> regex("[^\\\\\\[(]+|\\\\[\\s\\S]?|\\(\\?|\\[(?:[^\\\\\\]]|\\\\.)*\\]")

// Return first group that was matched (or -1 if nothing was matched) 
public fun first-matched?std/text/regex/first-matched?: (groups : groups, start : ?int, end : ?int) -> int( groupsgroups: groups : groupsstd/text/regex/groups: V, startstart: ?int : intstd/core/optional: V -> V = 1, endend: ?int : intstd/core/optional: V -> V = 10 ) : intstd/core/int: V
{
  match (liststd/core/list: (lo : int, hi : int) -> total list<int>(startstart: int,endend: int).findstd/core/find: forall<a> (xs : list<a>, pred : (a) -> bool) -> maybe<a>(fun(ii: int) { groupsgroups: groups.matched?std/text/regex/matched?: (groups : groups, index : int) -> bool(ii: int) })) {
    Nothingstd/core/Nothing: forall<a> maybe<a> -> ~std/core/(~): (i : int) -> int1
    Juststd/core/Just: forall<a> (value : a) -> maybe<a>(ii: int) -> ii: int
  }
}

// Return the first matched group (or "" if nothing matched) starting at `start`
// and returning at most group `end`
public fun first-matchedstd/text/regex/first-matched: (groups : groups, start : ?int, end : ?int) -> string(groupsgroups: groups : groupsstd/text/regex/groups: V, startstart: ?int : intstd/core/optional: V -> V = 1, endend: ?int : intstd/core/optional: V -> V = 10 ) : stringstd/core/string: V
{
  val ii: int = groupsgroups: groups.first-matched?std/text/regex/first-matched?: (groups : groups, start : ?int, end : ?int) -> int(startstart: int,endend: int)
  if (istd/core/True: bool >=std/core/(>=).1: (int, int) -> bool 0) then groupsgroups: groups[std/text/regex/[]: (groups : groups, index : int) -> stringii: int] else ""
}

// Return the string captured by a particular group or the empty string.
public fun [std/text/regex/[]: (groups : groups, index : int) -> string]( groupsgroups: groups : groupsstd/text/regex/groups: V, indexindex: int : intstd/core/int: V ) : stringstd/core/string: V {
  groups-indexstd/text/regex/groups-index: (groups : any, index : int32) -> string(groupsgroups: groups.grpstd/text/regex/grp: (groups : groups) -> any, indexindex: int.int32std/core/int32: (int) -> int32)
}

// Returns `True` if a particular capture group actually matched.
// This is used if the group can match, but may capture the empty string.
public fun matched?std/text/regex/matched?: (groups : groups, index : int) -> bool( groupsgroups: groups : groupsstd/text/regex/groups: V, indexindex: int : intstd/core/int: V ) : boolstd/core/bool: V {
  groups-matched-onstd/text/regex/groups-matched-on: (groups : any, index : int32) -> int32(groupsgroups: groups.grpstd/text/regex/grp: (groups : groups) -> any, indexindex: int.int32std/core/int32: (int) -> int32).boolstd/num/int32/bool: (i : int32) -> bool
}

// Create a new regular expression. Takes two optional parameters. Set `ignoreCase` to `True`
// to ignore uppercase/lowercase distinction. If  `multiline` is set to `True`, then `^` and `$`
// match also the beginning and end of every line (instead of the entire input).
public fun regexstd/text/regex/regex: (regex : string, ignorecase : ?bool, multiline : ?bool) -> regex( regexregex: string :stringstd/core/string: V, ignorecaseignorecase: ?bool :boolstd/core/optional: V -> V = Falsestd/core/False: bool, multilinemultiline: ?bool : boolstd/core/optional: V -> V = Falsestd/core/False: bool ) : regexstd/text/regex/regex: V
{
  Regexstd/text/regex/Regex: (obj : any) -> regex(regex-createstd/text/regex/regex-create: (string, int32, int32) -> any(regexregex: string,if (ignorecaseignorecase: bool) then onestd/num/int32/one: int32 else zerostd/num/int32/zero: int32, if (multilinemultiline: bool) then onestd/num/int32/one: int32 else zerostd/num/int32/zero: int32));
}

// Create a new _alternative_ regular expression. 
// Matches any of the given patterns but the groups are local to each alternative.
// See `alternative` to find out which alternative was matched. Contains an optimization
// where a common prefix of all patterns is lifted out of the alternative to increase efficiency.
// Takes two optional parameters. Set `ignoreCase` to `True`
// to ignore uppercase/lowercase distinction. If  `multiline` is set to `True`, then `^` and `$`
// match also the beginning and end of every line (instead of the entire input).  
// Note: currently only supported in the javascript backend.
public fun regex-altstd/text/regex/regex-alt: (regexs : list<string>, ignorecase : ?bool, multiline : ?bool) -> regex( regexsregexs: list<string> :liststd/core/list: V -> V<stringstd/core/string: V>, ignorecaseignorecase: ?bool :boolstd/core/optional: V -> V = Falsestd/core/False: bool, multilinemultiline: ?bool : boolstd/core/optional: V -> V = Falsestd/core/False: bool ) : regexstd/text/regex/regex: V
{
  Regexstd/text/regex/Regex: (obj : any) -> regex(regex-create-altstd/text/regex/regex-create-alt: (vector<string>, int32, int32) -> any(regexsregexs: list<string>.vectorstd/core/vector.3: forall<a> (xs : list<a>) -> vector<a>,if (ignorecaseignorecase: bool) then onestd/num/int32/one: int32 else zerostd/num/int32/zero: int32, if (multilinemultiline: bool) then onestd/num/int32/one: int32 else zerostd/num/int32/zero: int32));
}

private extern inline lengthstd/text/regex/length: (s : string) -> int32( s : stringstd/core/string: V ) : int32std/core/int32: V {
  cs inline "#1.Length"
  js inline "#1.length"
}

private extern inline substrstd/text/regex/substr: (s : string, start : int32) -> string( s : stringstd/core/string: V, start : int32std/core/int32: V ) : stringstd/core/string: V {
  js inline "(#1).substr(#2)"
}
private extern inline substrstd/text/regex/substr: (s : string, start : int32, len : int32) -> string( s : stringstd/core/string: V, start : int32std/core/int32: V, len : int32std/core/int32: V ) : stringstd/core/string: V {
  js inline "(#1).substr(#2,#3)"
}

// Find a match for a regular expression.
// See also `contains`
// (note: this fun is called |exec| in JavaScript).
public fun findstd/text/regex/find: (s : string, regex : regex) -> maybe<matched>( ss: string :stringstd/core/string: V,  regexregex: regex :regexstd/text/regex/regex: V ) : maybestd/core/maybe: V -> V<matchedstd/text/regex/matched: V>
{  
  regex-execstd/text/regex/regex-exec: (any, string, int32) -> maybe<matched>(regexregex: regex.objstd/text/regex/obj: (regex : regex) -> any,ss: string,0.int32std/core/int32: (int) -> int32)
  //return (if (matched.index < 0) then Nothing else Just(matched))
}

// Find all matches for a regular expression in a string.
public fun find-allstd/text/regex/find-all: (s : string, regex : regex) -> list<matched>( ss: string :stringstd/core/string: V,  regexregex: regex :regexstd/text/regex/regex: V ) : liststd/core/list: V -> V<matchedstd/text/regex/matched: V>
{
  returnreturn: list<matched> regex-exec-allstd/text/regex/regex-exec-all: (any, string, int32) -> vector<matched>(regexregex: regex.objstd/text/regex/obj: (regex : regex) -> any,ss: string,zerostd/num/int32/zero: int32).liststd/core/list.5: forall<a> (v : vector<a>) -> list<a>
}

fun replace-exstd/text/regex/replace-ex: forall<e> (s : string, regex : regex, repl : (matched : matched) -> e string, all : ?bool) -> e string( ss: string :stringstd/core/string: V, regexregex: regex :regexstd/text/regex/regex: V, replrepl: (matched : matched) -> _1082 string :(matched :matchedstd/text/regex/matched: V) -> ee: E stringstd/core/string: V, allall: ?bool :boolstd/core/optional: V -> V = Falsestd/core/False: bool) : ee: E stringstd/core/string: V
{
  returnreturn: string regex-replace-funstd/text/regex/regex-replace-fun: forall<e> (any, string, (matched) -> e string, int32, int32) -> e string(regexregex: regex.objstd/text/regex/obj: (regex : regex) -> any, ss: string, replrepl: (matched : matched) -> _1082 string, if (allall: bool) then onestd/num/int32/one: int32 else zerostd/num/int32/zero: int32, zerostd/num/int32/zero: int32);
}

fun replace-exstd/text/regex/replace-ex.1: (s : string, regex : regex, repl : string, all : ?bool) -> string( ss: string :stringstd/core/string: V, regexregex: regex :regexstd/text/regex/regex: V, replrepl: string :stringstd/core/string: V, allall: ?bool :boolstd/core/optional: V -> V = Falsestd/core/False: bool) : stringstd/core/string: V
{
  returnreturn: string regex-replacestd/text/regex/regex-replace: (any, string, string, int32, int32) -> string(regexregex: regex.objstd/text/regex/obj: (regex : regex) -> any, ss: string, replrepl: string, if (allall: bool) then onestd/num/int32/one: int32 else zerostd/num/int32/zero: int32, zerostd/num/int32/zero: int32);
}

// Split a string `s` in at most `n` parts using a regular expression `r` as separator.
public fun splitstd/text/regex/split: (s : string, r : regex, n : ?int) -> list<string>( ss: string :stringstd/core/string: V, rr: regex :regexstd/text/regex/regex: V, nn: ?int :intstd/core/optional: V -> V = 0 ) : liststd/core/list: V -> V<stringstd/core/string: V>
{
  if (nstd/core/True: bool<std/core/(<).1: (int, int) -> bool0) returnreturn: list<string> [std/core/Nil: forall<a> list<a>]std/core/Nil: forall<a> list<a>;
  returnreturn: list<string> regex-splitstd/text/regex/regex-split: (any, string, int32, int32) -> vector<string>( rr: regex.objstd/text/regex/obj: (regex : regex) -> any, ss: string, if (nstd/core/True: bool.pos?std/core/pos?.1: (i : int) -> bool) then nn: int.int32std/core/int32: (int) -> int32 else zerostd/num/int32/zero: int32, zerostd/num/int32/zero: int32 ).liststd/core/list.5: forall<a> (v : vector<a>) -> list<a>;
}

// Replace the first occurrence of `regex` by the result of the replacement fun `repl` in a string `s`.
public fun replacestd/text/regex/replace: forall<e> (s : string, regex : regex, repl : (matched : matched) -> e string) -> e string( ss: string : stringstd/core/string: V, regexregex: regex: regexstd/text/regex/regex: V, replrepl: (matched : matched) -> _1403 string : (matched :matchedstd/text/regex/matched: V) -> ee: E stringstd/core/string: V) : ee: E stringstd/core/string: V {
  replace-exstd/text/regex/replace-ex: forall<e> (s : string, regex : regex, repl : (matched : matched) -> e string, all : ?bool) -> e string( ss: string, regexregex: regex, replrepl: (matched : matched) -> _1403 string, all = Falsestd/core/False: bool )
}

// Replace the all occurrences of `regex` by the result of the replacement fun `repl` in a string `s`.
public fun replace-allstd/text/regex/replace-all: forall<e> (s : string, regex : regex, repl : (matched : matched) -> e string) -> e string( ss: string : stringstd/core/string: V, regexregex: regex: regexstd/text/regex/regex: V, replrepl: (matched : matched) -> _1133 string : (matched :matchedstd/text/regex/matched: V) -> ee: E stringstd/core/string: V) : ee: E stringstd/core/string: V {
  replace-exstd/text/regex/replace-ex: forall<e> (s : string, regex : regex, repl : (matched : matched) -> e string, all : ?bool) -> e string( ss: string, regexregex: regex, replrepl: (matched : matched) -> _1133 string, all = Truestd/core/True: bool)
}

// Replace the first occurrence of `regex` by the result a replacement string `repl` in a string `s`.
// The replacement string can contain `$$` for a `$` sign, `$n` for a capture group,
// `$&` for the entire match (`==$0`).
public fun replacestd/text/regex/replace.1: (s : string, regex : regex, repl : string) -> string( ss: string : stringstd/core/string: V, regexregex: regex: regexstd/text/regex/regex: V, replrepl: string :stringstd/core/string: V) : stringstd/core/string: V {
  replace-exstd/text/regex/replace-ex.1: (s : string, regex : regex, repl : string, all : ?bool) -> string( ss: string, regexregex: regex, replrepl: string, all = Falsestd/core/False: bool)
}

// Replace all ocurrences of `regex` by the result of a replacement string `repl` in a string `s`.
public fun replace-allstd/text/regex/replace-all.1: (s : string, regex : regex, repl : string) -> string( ss: string : stringstd/core/string: V, regexregex: regex: regexstd/text/regex/regex: V, replrepl: string :stringstd/core/string: V) : stringstd/core/string: V {
  replace-exstd/text/regex/replace-ex.1: (s : string, regex : regex, repl : string, all : ?bool) -> string( ss: string, regexregex: regex, replrepl: string, all = Truestd/core/True: bool)
}

// Does a regular expression pattern occur in a string `s`?
// (note: called `test` in javascript)
public fun containsstd/text/regex/contains: (s : string, r : regex) -> bool( ss: string :stringstd/core/string: V, rr: regex :regexstd/text/regex/regex: V ) : boolstd/core/bool: V 
{
  returnreturn: bool ss: string.findstd/text/regex/find: (s : string, regex : regex) -> maybe<matched>(rr: regex).boolstd/core/bool.1: forall<a> (m : maybe<a>) -> bool
}


// Split a string `s` over separator `sep` where `sep` does not occur in 
// _tokens_ matching `exclude`.
// For example: ``split-exclude("comma,'sep,arated',values", regex(","),regex("'[^']*'|[^',]"))``
public fun split-excludestd/text/regex/split-exclude: (s : string, sep : regex, exclude : regex) -> list<string>( ss: string : stringstd/core/string: V, sepsep: regex : regexstd/text/regex/regex: V, excludeexclude: regex : regexstd/text/regex/regex: V ) : liststd/core/list: V -> V<stringstd/core/string: V>
{
  if (sstd/core/True: bool==std/core/(==).3: (string, string) -> bool"") then [std/core/Nil: forall<a> list<a>]std/core/Nil: forall<a> list<a> else {
    val splitrsplitr: regex = regexstd/text/regex/regex: (regex : string, ignorecase : ?bool, multiline : ?bool) -> regex( "^(?:((?:" +std/core/(+).3: (string, string) -> string excludeexclude: regex.sourcestd/text/regex/source: (r : regex) -> string +std/core/(+).3: (string, string) -> string ")+)|(" +std/core/(+).3: (string, string) -> string sepsep: regex.sourcestd/text/regex/source: (r : regex) -> string +std/core/(+).3: (string, string) -> string "))")
    ss: string.split-excludexstd/text/regex/split-excludex: (s : string, splitr : regex, acc : string) -> list<string>(splitrsplitr: regex,"")
  }
}

fun split-excludexstd/text/regex/split-excludex: (s : string, splitr : regex, acc : string) -> list<string>( ss: string : stringstd/core/string: V, splitrsplitr: regex : regexstd/text/regex/regex: V, accacc: string : stringstd/core/string: V ) : liststd/core/list: V -> V<stringstd/core/string: V>
{
  if (sstd/core/True: bool==std/core/(==).3: (string, string) -> bool"") returnreturn: list<string> [std/core/Cons: forall<a> (head : a, tail : list<a>) -> list<a>accacc: string]std/core/Nil: forall<a> list<a>
  match( ss: string.findstd/text/regex/find: (s : string, regex : regex) -> maybe<matched>(splitrsplitr: regex) ) {
    Nothingstd/core/Nothing: forall<a> maybe<a> -> split-excludexstd/text/regex/split-excludex: (s : string, splitr : regex, acc : string) -> list<string>( unsafe-decreasingstd/core/unsafe-decreasing: forall<a> (x : a) -> a(ss: string.tailstd/core/tail.2: (s : string) -> string), splitrsplitr: regex, accacc: string +std/core/(+).3: (string, string) -> string ss: string.headstd/core/head.3: (s : string) -> string) // todo: improve efficiency?
    Juststd/core/Just: forall<a> (value : a) -> maybe<a>(capcap: matched) -> {
      if (capstd/core/True: bool.groupsstd/text/regex/groups: (matched : matched) -> groups.matched?std/text/regex/matched?: (groups : groups, index : int) -> bool(1)) {
        split-excludexstd/text/regex/split-excludex: (s : string, splitr : regex, acc : string) -> list<string>( unsafe-decreasingstd/core/unsafe-decreasing: forall<a> (x : a) -> a(capcap: matched.after1std/text/regex/after1: (cap : matched) -> string), splitrsplitr: regex, accacc: string +std/core/(+).3: (string, string) -> string capcap: matched.matchedstd/text/regex/matched: (matched : matched) -> string) 
      }
      else {
        Consstd/core/Cons: forall<a> (head : a, tail : list<a>) -> list<a>(accacc: string, split-excludexstd/text/regex/split-excludex: (s : string, splitr : regex, acc : string) -> list<string>( unsafe-decreasingstd/core/unsafe-decreasing: forall<a> (x : a) -> a(capcap: matched.after1std/text/regex/after1: (cap : matched) -> string), splitrsplitr: regex, "")) // todo: make tail recursive
      }
    }
  }
}