All Downloads are FREE. Search and download functionalities are using the official Maven repository.

in.ashwanthkumar.utils.parser.Parser Maven / Gradle / Ivy

The newest version!
package in.ashwanthkumar.utils.parser;

import in.ashwanthkumar.utils.func.Function;
import in.ashwanthkumar.utils.lang.tuple.Tuple2;

public abstract class Parser {
    private String name = "";

    abstract public ParserResult parse(String input);

    public Parser named(String name) {
        this.name = name;
        return this;
    }

    /**
     * {@code this.map(transform)} succeeds if {@code this} succeeds and {@code transform} is used to convert the result of {@code this}
     *
     * @param transform Function to transform the result from T -> U
     * @return a `Parser` that -- on success -- returns the result after applying {@code transform}.
     */
    public  Parser map(final Function transform) {
        final Parser me = this;
        return new Parser() {
            @Override
            public ParserResult parse(String input) {
                return me.parse(input).map(transform);
            }
        };
    }

    /**
     * {@code this.thenR(another)} succeeds if {@code this} succeeds and {@code another} succeeds on the input left over by {@code this}
     *
     * @param another Another Parser
     * @return a `Parser` that -- on success -- returns the result of `another`.
     */
    public  Parser thenR(final Parser another) {
        final Parser me = this;
        return new Parser() {
            @Override
            public ParserResult parse(String input) {
                ParserResult myResult = me.parse(input);
                return myResult.successful() ?
                        another.parse(myResult.getRemainingInput()) :
                        Failure.of(((Failure) myResult).getMessage(), myResult.getRemainingInput());
            }
        }.named(this.name + " ~> " + another.name);
    }

    /**
     * {@code this.thenL(another)} succeeds if {@code this} succeeds and {@code another} succeeds on the input left over by {@code this}
     *
     * @param another Another Parser
     * @return a `Parser` that -- on success -- returns the result of `another`.
     */
    public  Parser thenL(final Parser another) {
        final Parser me = this;
        return new Parser() {
            @Override
            public ParserResult parse(String input) {
                ParserResult myResult = me.parse(input);
                if (myResult.successful()) {
                    ParserResult anotherResult = another.parse(myResult.getRemainingInput());
                    return myResult.successful() && anotherResult.successful() ?
                            myResult.setRemainingInput(anotherResult.getRemainingInput()) :
                            Failure.of(((Failure) anotherResult).getMessage(), myResult.getRemainingInput());
                } else {
                    return myResult;
                }
            }
        }.named(this.name + " <~ " + another.name);
    }

    /**
     * {@code this.thenL(another)} succeeds if {@code this} succeeds and {@code another} succeeds on the input left over by {@code this}
     *
     * @param another Another Parser
     * @return a `Parser` that -- on success -- returns the result of both `this` and `another`
     */
    public  Parser> then(final Parser another) {
        final Parser me = this;
        return new Parser>() {
            @Override
            public ParserResult> parse(String input) {
                ParserResult myResult = me.parse(input);
                if (myResult.successful()) {
                    ParserResult anotherResult = another.parse(myResult.getRemainingInput());
                    return myResult.successful() && anotherResult.successful() ?
                            Success.of(Tuple2.tuple2(myResult.get(), anotherResult.get()), anotherResult.getRemainingInput()) :
                            Failure.>of(((Failure) anotherResult).getMessage(), anotherResult.getRemainingInput());
                } else {
                    return Failure.of(((Failure) myResult).getMessage(), myResult.getRemainingInput());
                }
            }
        }.named(this.name + " ~ " + another.name);
    }

    /**
     * {@code this.or(another)} succeeds if {@code this} succeeds or {@code another} succeeds on the given input.
     * {@code another} parser is not evaluated if {@code this} succeeds.
     *
     * @param another Another Parser
     * @return a `Parser` that -- on success -- returns the result of either `this` or `another`
     */
    public Parser or(final Parser another) {
        final Parser me = this;
        return new Parser() {
            @Override
            public ParserResult parse(String input) {
                ParserResult myResult = me.parse(input);
                return myResult.successful() ? myResult : another.parse(input);
            }
        };
    }

    public Parser debug() {
        final Parser me = this;
        return new Parser() {
            @Override
            public ParserResult parse(String input) {
                System.out.println("Trying " + me.name + " at '" + input + "'");
                ParserResult result = me.parse(input);
                System.out.println(me.name + " --> " + result);
                return result;
            }
        };
    }

    public Parser skip(final int nChars) {
        assert nChars > 0;
        final Parser me = this;
        return new Parser() {
            @Override
            public ParserResult parse(String input) {
                return me.parse(input.substring(nChars - 1));
            }
        };
    }
}