Tracer { public static Parser on(Parser source, Consumer<TraceEvent> consumer) { TraceEvent[] parentClosure = new TraceEvent[1]; return Mirror.of(source) .transform(parser -> parser.callCC((continuation, context) -> { TraceEvent parent = parentClosure[0]; TraceEvent enter = new TraceEvent(TraceEventType.ENTER, parent, parser, context); consumer.accept(enter); parentClosure[0] = enter; Result result = continuation.apply(context); parentClosure[0] = parent; TraceEvent exit = new TraceEvent(TraceEventType.EXIT, parent, parser, result); consumer.accept(exit); return result; })); }  static Parser on(Parser source, Consumer<TraceEvent> consumer);  }
@Test public void testSuccessfulTrace() { List<String> expected = Arrays.asList( "FlattenParser", " SequenceParser", " CharacterParser[letter expected]", " Success[1:2]: a", " PossessiveRepeatingParser[0..*]", " CharacterParser[letter or digit expected]", " Failure[1:2]: letter or digit expected", " Success[1:2]: []", " Success[1:2]: [a, []]", "Success[1:2]: a" ); List<Tracer.TraceEvent> actual = new ArrayList<>(); Result result = Tracer.on(ExamplesTest.IDENTIFIER, actual::add).parse("a"); assertTrue(result.isSuccess()); assertEquals(expected, actual.stream().map(Tracer.TraceEvent::toString) .collect(Collectors.toList())); }
@Test public void testFailingTrace() { List<String> expected = Arrays.asList( "FlattenParser", " SequenceParser", " CharacterParser[letter expected]", " Failure[1:1]: letter expected", " Failure[1:1]: letter expected", "Failure[1:1]: letter expected" ); List<Tracer.TraceEvent> actual = new ArrayList<>(); Result result = Tracer.on(ExamplesTest.IDENTIFIER, actual::add).parse("1"); assertFalse(result.isSuccess()); assertEquals(expected, actual.stream().map(Tracer.TraceEvent::toString) .collect(Collectors.toList())); }