    native class StringTokens
        (str, separator, discardSeparators, groupSeparators, limit)
            satisfies {String+} {

        String str;
        Boolean separator(Character ch);
        Boolean discardSeparators;
        Boolean groupSeparators;
        Integer? limit;

        shared actual native Iterator<String> iterator();
        shared actual native Boolean empty;

    }

    native("js") class StringTokens
        (str, separator, discardSeparators, groupSeparators, limit)
            satisfies {String+} {

        String str;
        Boolean separator(Character ch);
        Boolean discardSeparators;
        Boolean groupSeparators;
        Integer? limit;

        shared actual native("js") Boolean empty => false;

        shared actual native("js") Iterator<String> iterator() {
            dynamic str = this.str;

            return object satisfies Iterator<String> {

                variable value first = true;
                variable value lastTokenWasSeparator = false;
                variable value count = 0;
                variable value index = 0;

                Integer currentCodePoint {
                    dynamic {
                        return str.codePointAt(index);
                    }
                }

                Integer currentCharCode {
                    dynamic {
                        return str.codePointAt(index);
                    }
                }

                void advanceToEnd() {
                    dynamic {
                        index = str.length;
                    }
                }

                void advance() {
                    index += #d800 <= currentCharCode <= #dbff
                                        then 2 else 1;
                }

                value eof {
                    dynamic {
                        return index >= str.length;
                    }
                }

                value separator
                        => !eof
                        && outer.separator(currentCodePoint.character);

                value regular
                        => !eof
                        && !outer.separator(currentCodePoint.character);

                String token(Integer start, Boolean separator) {
                    if (!separator) {
                        count ++;
                    }
                    first = false;
                    lastTokenWasSeparator = separator;
                    dynamic {
                        return str.substring(start, index);
                    }
                }

                function emptyToken() {
                    count ++;
                    first = false;
                    lastTokenWasSeparator = false;
                    return "";
                }

                shared actual String|Finished next() {
                    if (!eof) {
                        if (first || lastTokenWasSeparator,
                            separator) {
                            return emptyToken();
                        }

                        if (separator) {
                            value start = index;
                            advance();
                            if (groupSeparators) {
                                while (separator) {
                                    advance();
                                }
                            }
                            if (!discardSeparators) {
                                return token(start, true);
                            }
                        }

                        value start = index;
                        if (exists limit, count>=limit) {
                            advanceToEnd();
                        }
                        else {
                            while (regular) {
                                advance();
                            }
                        }
                        return token(start, true);
                    }
                    else if (lastTokenWasSeparator) {
                        return emptyToken();
                    }
                    else {
                        return finished;
                    }
                }

            };
        }

    }

    native("jvm") class StringTokens
        (str, separator, discardSeparators, groupSeparators, limit)
            satisfies {String+} {

        import java.lang {
            JString=String,
            JCharacter=Character
        }

        String str;
        Boolean separator(Character ch);
        Boolean discardSeparators;
        Boolean groupSeparators;
        Integer? limit;

        shared actual native("jvm") Boolean empty => false;

        shared actual native("jvm") Iterator<String> iterator()
                => object satisfies Iterator<String> {

            value string = JString(str);

            variable value first = true;
            variable value lastTokenWasSeparator = false;
            variable value count = 0;
            variable value index = 0;

            void advanceToEnd() {
                index = string.length();
            }

            void advance() {
                index += JCharacter.charCount(string.codePointAt(index));
            }

            value eof => index >= string.length();

            value separator
                    => !eof
                    && outer.separator(string.codePointAt(index).character);

            value regular
                    => !eof
                    && !outer.separator(string.codePointAt(index).character);

            String token(Integer start, Boolean separator) {
                if (!separator) {
                    count ++;
                }
                first = false;
                lastTokenWasSeparator = separator;
                return string.substring(start, index);
            }

            String emptyToken() {
                count ++;
                first = false;
                lastTokenWasSeparator = false;
                return "";
            }

            shared actual String|Finished next() {
                if (!eof) {
                    if (first || lastTokenWasSeparator,
                        separator) {
                        return emptyToken();
                    }

                    if (separator) {
                        value start = index;
                        advance();
                        if (groupSeparators) {
                            while (separator) {
                                advance();
                            }
                        }
                        if (!discardSeparators) {
                            return token(start, true);
                        }
                    }

                    value start = index;
                    if (exists limit, count>=limit) {
                        advanceToEnd();
                    }
                    else {
                        while (regular) {
                            advance();
                        }
                    }
                    return token(start, true);
                }
                else if (lastTokenWasSeparator) {
                    return emptyToken();
                }
                else {
                    return finished;
                }
            }

        };

    }