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