
import maybe.*;



//
// TokenInput?, TokenType
//

[TokenInput]
TokenInput?(static TokenInput) = Iterator?(TokenInput);

[TokenInput]
TokenType(static TokenInput) = IteratorTargetType(TokenInput);



//
// Parser?, Parsers?
//

[TokenInput]
Parser?(static TokenInput, x) = 
    SimpleParser?(TokenInput, x) or ConvertibleParser?(TokenInput, x);


define Parsers?;

[TokenInput]
overload Parsers?(static TokenInput, first, ..rest) =
    Parser?(TokenInput, first) and Parsers?(TokenInput, ..rest);

[TokenInput]
overload Parsers?(static TokenInput) = true;



//
// SimpleParser?
//

define SimpleParser?;

[TokenInput, Parser]
overload SimpleParser?(static TokenInput, static Parser) = false;

[TokenInput, Parser | CallDefined?(call, Parser, TokenInput)]
overload SimpleParser?(static TokenInput, static Parser) {
    alias parser = typeToLValue(Parser);
    alias input = typeToLValue(TokenInput);
    return MaybeType?(..ReturnType(..parser(input)));
}

private MaybeType?(..args) = false;
[T] overload MaybeType?(static Maybe[T]) = true;



//
// ConvertibleParser?
//

define ConvertibleParser?;

[TokenInput]
overload ConvertibleParser?(static TokenInput, x) = false;

[TokenInput, Token | TokenType(TokenInput) == Token]
overload ConvertibleParser?(static TokenInput, static Token) = true;

[TokenInput, A | Sequence?(A) and (TokenType(TokenInput) == SequenceElementType(A))]
overload ConvertibleParser?(static TokenInput, static A) = true;

[TokenInput, ..T | Parsers?(TokenInput, ..T)]
overload ConvertibleParser?(static TokenInput, static Tuple[..T]) = true;



//
// convertParser
//

define convertParser;

[TokenInput, Token | TokenType(TokenInput) == Token]
overload convertParser(static TokenInput, token:Token) =
    literal(TokenInput, token);

[TokenInput, A | Sequence?(A) and (SequenceElementType(A) == TokenType(TokenInput))]
overload convertParser(static TokenInput, forward a:A) =
    literalString(TokenInput, a);

[TokenInput, ..T | Parsers?(TokenInput, ..T)]
overload convertParser(static TokenInput, parsers:Tuple[..T]) =
    sequence(TokenInput, ..convertParsers(TokenInput, ..unpackRef(parsers)));

[TokenInput, Parser | SimpleParser?(TokenInput, Parser)]
overload convertParser(static TokenInput, ref parser:Parser) =
    parser;

[TokenInput, Parser | SimpleParser?(TokenInput, Parser)]
overload convertParser(static TokenInput, rvalue parser:Parser) =
    move(parser);



//
// convertParsers
//

private record Converter[TokenInput]();
[TokenInput] overload call(x:Converter[TokenInput], forward p) =
    forward convertParser(TokenInput, p);

[TokenInput, ..T | Parsers?(TokenInput, ..T)]
convertParsers(static TokenInput, forward ..parsers:T) =
    forward ..mapValues(Converter[TokenInput](), ..parsers);



//
// ParseResult
//

[TokenInput, Parser | Parser?(TokenInput, Parser)]
ParseResult(static TokenInput, static Parser) {
    alias parser = convertParser(TokenInput, typeToLValue(Parser));
    alias input = typeToLValue(TokenInput);
    return UnwrapMaybe(ReturnType(parser(input)));
}

[T] private UnwrapMaybe(static Maybe[T]) = T;



//
// nextToken
//

[TokenInput]
nextToken(static TokenInput, input:TokenInput) {
    if (not hasNext?(input))
        return nothing(TokenType(TokenInput));
    return Maybe(next(input));
}



//
// condition
//

[TokenInput, Predicate]
TokenPredicate?(static TokenInput, static Predicate) {
    alias predicate = typeToLValue(Predicate);
    alias Token = TokenType(TokenInput);
    alias token = typeToLValue(Token);
    return Tuple[Bool] == Tuple[..Type(..predicate(token))];
}

[TokenInput, Predicate | TokenPredicate?(TokenInput, Predicate)]
condition(static TokenInput, predicate:Predicate) =
    input => {
        var saved = input;
        return maybe(nextToken(TokenInput, input),
            x -> {
                if (predicate(x))
                    return Maybe(move(x));
                input = move(saved);
                return nothing(TokenType(TokenInput));
            },
            () -> nothing(TokenType(TokenInput)),
        );
    };



//
// literal
//

[TokenInput, Token | TokenType(TokenInput) == Token]
literal(static TokenInput, expected:Token) =
    condition(TokenInput, x => (x == expected));



//
// literalString
//

[TokenInput, A | Sequence?(A) and (SequenceElementType(A) == TokenType(TokenInput))]
literalString(static TokenInput, forward a:A) {
    return input => {
        var saved = input;
        for (item in a) {
            var parsed? = maybe(nextToken(TokenInput, input),
                (x) -> (x == item),
                () => false,
            );
            if (not parsed?) {
                input = move(saved);
                return nothing(A);
            }
        }
        return Maybe(a);
    };
}



//
// optional
//

[TokenInput, Parser | Parser?(TokenInput, Parser)]
optional(static TokenInput, forward _parser:Parser) {
    var parser = convertParser(TokenInput, _parser);
    return input => Maybe(parser(input));
}



//
// choice
//

UniformParsers?(TokenInput, ..Parsers) =
    Parsers?(TokenInput, ..Parsers) and
    equalValues?(..mapValues(P => ParseResult(TokenInput, P), ..Parsers));

[TokenInput, First, ..Rest | UniformParsers?(TokenInput, First, ..Rest)]
choice(static TokenInput, forward first:First, forward ..rest:Rest) {
    var parsers = Tuple(..convertParsers(TokenInput, first, ..rest));
    return input => {
        ..for (parser in unpackRef(parsers)) {
            var result = parser(input);
            if (not nothing?(result))
                return move(result);
        }
        return nothing(ParseResult(TokenInput, First));
    };
}



//
// sequence, sequenceAs
//

[TokenInput, First, ..Rest | Parsers?(TokenInput, First, ..Rest)]
sequence(static TokenInput, forward first:First, forward ..rest:Rest) {
    var parsers = Tuple(..convertParsers(TokenInput, first, ..rest));
    return input => {
        alias f = (parser -> require(parser(input)));
        alias result = Tuple(..mapValues(f, ..unpackRef(parsers)));
        var saved = input;
        try {
            return Maybe(result);
        }
        catch (e:ValueMissing) {
            input = saved;
            return nothing(Type(result));
        }
    };
}

[TokenInput, Result, ..Parsers | Parsers?(TokenInput, ..Parsers)]
sequenceAs(static TokenInput, result:Result, forward .._parsers:Parsers) {
    var parsers = Tuple(..convertParsers(TokenInput, .._parsers));
    return input => {
        var saved = input;
        ..for (parser in unpackRef(parsers)) {
            var result = parser(input);
            if (nothing?(result)) {
                input = saved;
                return nothing(Result);
            }
        }
        return Maybe(result);
    };
}



//
// modify
//

Modifier?(TokenInput, Modifier, Parser) {
    alias M = typeToLValue(Modifier);
    alias T = ParseResult(TokenInput, Parser);
    return (Parser?(TokenInput, Parser) and CallDefined?(M, T));
}

[TokenInput, Modifier, Parser | Modifier?(TokenInput, Modifier, Parser)]
modify(static TokenInput, modifier:Modifier, forward _parser:Parser) {
    var parser = convertParser(TokenInput, _parser);
    return input => {
        alias T = Type(modifier(require(parser(input))));
        return maybe(parser(input),
            x -> Maybe(modifier(move(x))),
            () -> nothing(T),
        );
    };
}



//
// repeated, repeatedAs
//

[TokenInput, Parser | Parser?(TokenInput, Parser)]
repeated(static TokenInput, min:Int, max:Int, forward _parser:Parser) {
    var parser = convertParser(TokenInput, _parser);
    return input => {
        alias T = ParseResult(TokenInput, Parser);
        var saved = input;
        var v = Vector[T]();
        while (size(v) < max) {
            var parsed? = maybe(parser(input),
                x -> { push(v, move(x)); return true; },
                () => false,
            );
            if (not parsed?)
                break;
        }
        if (size(v) < min) {
            input = saved;
            return nothing(Vector[T]);
        }
        return Maybe(move(v));
    };
}

[TokenInput, Result, Parser | Parser?(TokenInput, Parser)]
repeatedAs(static TokenInput, result:Result, min:Int, max:Int, forward _parser:Parser) {
    var parser = convertParser(TokenInput, _parser);
    return input => {
        var saved = input;
        var count = 0;
        while (count < max) {
            var result = parser(input);
            if (nothing?(result))
                break;
            count += 1;
        }
        if (count < min) {
            input = saved;
            return nothing(Result);
        }
        return Maybe(result);
    };
}



//
// zeroOrMore, oneOrMore
// zeroOrMoreAs, oneOrMoreAs
//

[TokenInput, Parser | Parser?(TokenInput, Parser)]
zeroOrMore(static TokenInput, forward parser:Parser) =
    repeated(TokenInput, 0, Greatest(Int), parser);

[TokenInput, Parser | Parser?(TokenInput, Parser)]
oneOrMore(static TokenInput, forward parser:Parser) =
    repeated(TokenInput, 1, Greatest(Int), parser);

[TokenInput, Result, Parser | Parser?(TokenInput, Parser)]
zeroOrMoreAs(static TokenInput, result:Result, forward parser:Parser) =
    repeatedAs(TokenInput, result, 0, Greatest(Int), parser);

[TokenInput, Result, Parser | Parser?(TokenInput, Parser)]
oneOrMoreAs(static TokenInput, result:Result, forward parser:Parser) =
    repeatedAs(TokenInput, result, 1, Greatest(Int), parser);



//
// separatedList, optSeparatedList,
// separatedListStrict, optSeparatedListStrict,
//

[TokenInput, Separator, Element | Parsers?(TokenInput, Separator, Element)]
separatedList(static TokenInput, forward _separator:Separator, forward _element:Element) {
    var separator = convertParser(TokenInput, _separator);
    var element = convertParser(TokenInput, _element);
    return input => {
        alias T = ParseResult(TokenInput, Element);
        var v = Vector[T]();
        while (true) {
            var parsed? = maybe(element(input),
                x -> { push(v, move(x)); return true; },
                () => false,
            );
            if (not parsed?)
                break;
            var sep = separator(input);
            if (nothing?(sep))
                break;
        }
        if (empty?(v))
            return nothing(Vector[T]);
        return Maybe(move(v));
    };
}

[TokenInput, Separator, Element | Parsers?(TokenInput, Separator, Element)]
optSeparatedList(static TokenInput, forward _separator:Separator, forward _element:Element) {
    alias T = ParseResult(TokenInput, Element);
    return modify(
        TokenInput,
        x => maybe(move(x), y => move(y), () => Vector[T]()),
        optional(TokenInput, separatedList(TokenInput, _separator, _element)),
    );
}

[TokenInput, Separator, Element | Parsers?(TokenInput, Separator, Element)]
separatedListStrict(static TokenInput, forward _separator:Separator, forward _element:Element) {
    var separator = convertParser(TokenInput, _separator);
    var element = convertParser(TokenInput, _element);
    return input => {
        alias T = ParseResult(TokenInput, Element);
        var v = Vector[T]();
        var saved = input;
        while (true) {
            var parsed? = maybe(element(input),
                x -> { push(v, move(x)); return true; },
                () => false,
            );
            if (not parsed?)
                break;
            saved = input;
            var sep = separator(input);
            if (nothing?(sep))
                break;
        }
        input = saved;
        if (empty?(v))
            return nothing(Vector[T]);
        return Maybe(move(v));
    };
}

[TokenInput, Separator, Element | Parsers?(TokenInput, Separator, Element)]
optSeparatedListStrict(static TokenInput, forward _separator:Separator, forward _element:Element) {
    alias T = ParseResult(TokenInput, Element);
    return modify(
        x => maybe(move(x), y => move(y), () => Vector[T]()),
        optional(separatedListStrict(_separator, _element)),
    );
}
