package ch.usi.pl.parser;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
/** Demostration of a parser combinator library in Java. */
public class ParserMain {
/**
* 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);
}
}
/** 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;
}
}
public static void main(String[] args) throws IOException {
Parser ws = oneOf(" \t\n");
Parser digit = oneOf("0123456789");
assert (digit.parse("0").result == '0');
assert (ws.parse(" ").result == ' ');
// Parse a list of integers separated by whitespace.
//
// Parser digits = digit.oneOrMore();
// Parser n = digits.convertStringToInteger();
// Parser wss = ws.zeroOrMore();
//
// Parser> L = wss.then(n.then(wss).zeroOrMore());
//
// assert (L.parse("123 456").result.contains(123));
String s = slurp(System.in);
// parse two digits in a row.
Result> r = digit.then(digit).parse(s);
if (r.success) {
System.out.println(r.result.fst);
System.out.println(r.result.snd);
}
else {
System.out.println("fail");
}
}
/** 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() : "";
}
}