package ch.usi.pl.parser;
import static ch.usi.pl.parser.Parsers.oneOf;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
/** Demostration of a parser combinator library in Java. */
public class Parsers {
/**
* A parser combinator. This is mostly just a function from an input String
* to a Result, which is either an A and the unconsumed tail of the input
* String, or a failure.
*/
static abstract class Parser {
/** The parser function proper. */
abstract Result parse(String in);
Parser> then(final Parser pb) {
final Parser pa = this;
return new SequenceParser(pa, pb);
}
Parser thenWithConstructor(final Parser pb, Function2 f) {
throw new UnsupportedOperationException("probably a bad idea to implement this; bind will be more useful");
}
Parser bind(Function1> f) {
throw new UnsupportedOperationException("unimplemented: we'll do this Tuesday");
}
// Alternative version of 'then', using 'bind'.
// Note, I don't need to deal with parser success or failure or the
// input string.
Parser> thenWithBind(final Parser pb) {
return bind(new Function1>>() {
public Parser> apply(final A fst) {
return pb.map(new Function1>() {
public Pair apply(B snd) {
return new Pair(fst, snd);
}
});
}
});
}
Parser or(final Parser pb) {
final Parser pa = this;
return new AltParser(pa, pb);
}
Parser> zeroOrMore() {
return new StarParser(this);
}
// Alternative version of zeroOrMore.
// This would work except for the recursive call!
// It goes into an infinite loop building the parser.
Parser> zeroOrMore_Broken() {
return this.then(this.zeroOrMore_Broken()).map(Parsers. cons()).or(empty(Collections. emptyList()));
}
// Alternative version of zeroOrMore_Broken where we push the
// construction into the parse method.
// Unsatisfying.
Parser> zeroOrMore_Hack() {
return new AtLeastZeroParser(this);
}
// Version of atLeastZero, using bind rather than then.
Parser> zeroOrMore_Bind() {
return this.bind(new Function1>>() {
@Override
public Parser> apply(final A head) {
Function1, List> prepend = new Function1, List>() {
public List apply(List tail) {
List xs = new ArrayList();
xs.add(head);
xs.addAll(tail);
return xs;
}
};
return zeroOrMore_Bind().map(prepend);
}
}).or(empty(Collections. emptyList()));
}
Parser map(Function1 f) {
return new MapParser(this, f);
}
}
/**
* Slightly nicer version of StarParser, where I don't need to mess with the
* parser plumbing as much. We still define a subclass of Parser to avoid
* the infinite loop in zeroOrMore, above.
*/
static class AtLeastZeroParser extends Parser> {
Parser p;
AtLeastZeroParser(Parser p) {
this.p = p;
}
@Override
Result> parse(String in) {
return p.then(this).map(Parsers. cons()).or(empty(Collections. emptyList())).parse(in);
}
}
static Function1>, List> cons() {
return new Function1>, List>() {
@Override
public List apply(Pair> arg) {
List xs = new ArrayList();
xs.add(arg.fst);
xs.addAll(arg.snd);
return xs;
}
};
}
static class MapParser extends Parser {
private Parser p;
private Function1 f;
MapParser(Parser p, Function1 f) {
this.p = p;
this.f = f;
}
@Override
Result parse(String in) {
Result a = p.parse(in);
if (a.success) {
return new Result(f.apply(a.result), true, a.tail);
}
else {
return fail(in);
}
}
}
/** Return a parser that consumes no input, but returns a value of type A. */
static Parser empty(final A value) {
return new Parser() {
@Override
Result parse(String in) {
return new Result(value, true, in);
}
};
}
static class StarParser extends Parser> {
private final Parser p;
StarParser(Parser p) {
this.p = p;
}
@Override
Result> parse(String in) {
Result r = p.parse(in);
if (!r.success) {
return new Result>(Collections. emptyList(), true, in);
}
else {
Result> t = this.parse(r.tail);
if (t.success) {
List xs = new ArrayList();
xs.add(r.result);
xs.addAll(t.result);
return new Result>(xs, true, t.tail());
}
else {
return new Result>(Collections.singletonList(r.result), true, r.tail);
}
}
}
}
static class AltParser extends Parser {
private final Parser pa;
private final Parser pb;
AltParser(Parser pa, Parser pb) {
this.pa = pa;
this.pb = pb;
}
Result parse(String in) {
Result ra = pa.parse(in);
if (ra.success()) {
return ra;
}
else {
return pb.parse(in);
}
}
}
/** A parser that parses first an A, then a B, returning both. */
static class SequenceParser extends Parser> {
private final Parser pa;
private final Parser pb;
SequenceParser(Parser pa, Parser pb) {
this.pa = pa;
this.pb = pb;
}
@Override
Result> parse(String in) {
Result ra = pa.parse(in);
if (ra.success) {
Result rb = pb.parse(ra.tail);
if (rb.success) {
return new Result>(new Pair(ra.result, rb.result), true, rb.tail);
}
}
return fail(in);
}
}
/**
* A parser that parses a single character in a String of possible matching
* characters.
*/
static Parser oneOf(final String set) {
return new OneOfParser(set);
}
/**
* A parser that parses a single character in a String of possible matching
* characters.
*/
static class OneOfParser extends Parser {
private final String set;
OneOfParser(String set) {
this.set = set;
}
@Override
Result parse(String in) {
if (in.length() >= 1) {
for (int i = 0; i < set.length(); i++) {
char ch = set.charAt(i);
if (in.charAt(0) == ch) {
return new Result(ch, true, in.substring(1));
}
}
}
return fail(in);
}
}
/** A failed parse result. */
static Result fail(String in) {
return new Result(null, false, in);
}
/** Parser result */
static class Result {
/** Whether the parse succeeded or not. */
private final boolean success;
/** The actual result if the parser was successful. */
private final A result;
/** The uncomsumed input. */
private final String tail;
Result(A result, boolean success, String tail) {
this.result = result;
this.success = success;
this.tail = tail;
}
boolean success() {
return success;
}
A result() {
if (success)
return result;
else
throw new RuntimeException("parse failed");
}
String tail() {
if (success)
return tail;
else
throw new RuntimeException("parse failed");
}
}
static class Pair {
final A fst;
final B snd;
Pair(A fst, B snd) {
this.fst = fst;
this.snd = snd;
}
}
interface Either {
}
class Left implements Either {
A a;
void match(Visitor m) {
m.handleA(a);
}
}
class Right implements Either {
B b;
void match(Visitor m) {
m.handleB(b);
}
}
interface Visitor {
void handleA(A a);
void handleB(B b);
}
/** Read an InputStream into a String. */
static String slurp(InputStream is) throws IOException {
Scanner s = new Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
public static void main(String[] args) throws IOException {
Parser ws = oneOf(" \t\n");
Parser digit = oneOf("0123456789");
Parser lower = oneOf("abcdefghijklmnopqrstuvwxyz");
Parser upper = oneOf("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
Parser letter = lower.or(upper);
assert (digit.parse("0").result() == '0');
assert (ws.parse(" ").result() == ' ');
String s = Parsers.slurp(System.in);
Result> r = letter.or(digit).zeroOrMore_Hack().parse(s);
if (r.success()) {
System.out.println(r.result());
}
else {
System.out.println("fail");
}
}
}