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"); } }