package ch.usi.pl.hw6;
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 ParsersHW {
/**
* 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 or(final Parser pb) {
final Parser pa = this;
return new AltParser(pa, pb);
}
Parser> then(final Parser pb) {
final Parser pa = this;
return new SequenceParser(pa, pb);
}
/** Just like then, but delays evaluating the argument until needed. */
Parser> lazyThen(final Thunk> lazyPb) {
// TODO
throw new UnsupportedOperationException("implement 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(ParsersHW. cons()).or(empty(Collections. emptyList()));
}
Parser> zeroOrMore() {
// TODO
throw new UnsupportedOperationException("implement this");
}
Parser> oneOrMore() {
// TODO
throw new UnsupportedOperationException("implement this");
}
Parser> zeroOrMore(Parser> sep) {
// TODO
throw new UnsupportedOperationException("implement this");
}
Parser> oneOrMore(Parser> sep) {
// TODO
throw new UnsupportedOperationException("implement this");
}
Parser map(Function1 f) {
return new MapParser(this, f);
}
}
static abstract class Thunk {
abstract A compute();
A force() {
// TODO
throw new UnsupportedOperationException("implement this");
}
}
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 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.isEmpty()) {
char ch = in.charAt(0);
if (set.indexOf(ch) >= 0)
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 interface Function1 {
R apply(A arg);
}
static class Pair {
final A fst;
final B snd;
Pair(A fst, B snd) {
this.fst = fst;
this.snd = snd;
}
}
/** 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) {
// TODO: add your test cases here.
// Remember you may need to enable assertions (run with java -ea) to
// make assert statements do anything.
throw new RuntimeException("unimplemented test cases");
}
}