package com.github.kmizu.jcombinator;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import static com.github.kmizu.jcombinator.Parser.*;
import static com.github.kmizu.jcombinator.core.Functions.*;

@RunWith(JUnit4.class)
public class ChainlTest {
	private Rule<Integer> expression() {
	    return rule(() ->
			additive().cat(eof()).map(t -> t.item1())
		);
	}
	private Rule<Integer> additive() {
		return rule(() -> {
			final Parser<Fn2<Integer, Integer, Integer>> Q = string("+").map(op -> (Integer lhs, Integer rhs) -> lhs + rhs);
			final Parser<Fn2<Integer, Integer, Integer>> R = string("-").map(op -> (Integer lhs, Integer rhs) -> lhs - rhs);
			return multitive().chain(Q.or(R));
		});
	}
	private Rule<Integer> multitive() {
		return rule(() -> {
			final Parser<Fn2<Integer, Integer, Integer>> Q = string("*").map(op -> (Integer lhs, Integer rhs) -> lhs * rhs);
			final Parser<Fn2<Integer, Integer, Integer>> R = string("/").map(op -> (Integer lhs, Integer rhs) -> lhs / rhs);
			return primary().chain(Q.or(R));
		});
	}

	private final Rule<Integer> primary() {
		return rule(() ->
			number().or((string("(").cat(expression())).cat(string(")")).map(t -> t.item1().item2()))
		);
	}

	private final Rule<Integer> number() {
		return rule(() ->
			digit().many1().map(digits -> Integer.parseInt(join(digits, "")))
		);
	}

	@Test
    public void testExpression() {
	    Parser<Integer> arithmetic = expression();
    	arithmetic.invoke("100").onSuccess(s -> {
    		assertEquals((Integer)100, s.value());
    	});
    	arithmetic.invoke("100+200").onSuccess(s -> {
    		assertEquals((Integer)300, s.value());
    	});
    	arithmetic.invoke("(1+2)*(3+4)").onSuccess(s -> {
    		assertEquals((Integer)21, s.value());
    	});
    }
}

@@ -1852,20 +1852,136 @@
 );%0A    %09%7D);%0A
+        arithmetic.invoke(%221+2*3+4%22).onSuccess(s -%3E %7B%0A            assertEquals((Integer)11, s.value());%0A        %7D);%0A
     %7D%0A%7D%0A

Correct indent