package openmods.calc;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.util.List;
import openmods.calc.parsing.DefaultPostfixCompiler;
import openmods.calc.parsing.IPostfixCompilerState;
import openmods.calc.parsing.Token;
import openmods.calc.parsing.TokenType;
import openmods.utils.OptionalInt;
import openmods.utils.StackUnderflowException;
import org.junit.Test;
public class PostfixCompilerTest extends CalcTestUtils {
public final OperatorDictionary<String> operators = new OperatorDictionary<String>();
{
operators.registerBinaryOperator(PLUS);
operators.registerUnaryOperator(UNARY_MINUS);
}
private static final String TEST_MODIFIER = "!!!";
private static String rawTokenString(Token token) {
return TEST_MODIFIER + ":" + token.type + ":" + token.value;
}
private static class TestModifierState implements IPostfixCompilerState<String> {
private IExecutable<String> result;
@Override
public Result acceptToken(Token token) {
Preconditions.checkState(result == null);
result = Value.create(rawTokenString(token));
return Result.ACCEPTED_AND_FINISHED;
}
@Override
public Result acceptExecutable(IExecutable<String> executable) {
return Result.REJECTED;
}
@Override
public IExecutable<String> exit() {
Preconditions.checkState(result != null);
return result;
}
}
private static final IExecutable<String> OPEN_BRACKET = marker("((");
private static final IExecutable<String> CLOSE_BRACKET = marker("))");
private static class TestBracketState implements IPostfixCompilerState<String> {
private final List<IExecutable<String>> result = Lists.newArrayList();
{
result.add(OPEN_BRACKET);
}
@Override
public Result acceptToken(Token token) {
if (token.type == TokenType.RIGHT_BRACKET) return Result.ACCEPTED_AND_FINISHED;
result.add(Value.create(token.value));
return Result.ACCEPTED;
}
@Override
public Result acceptExecutable(IExecutable<String> executable) {
result.add(executable);
return Result.ACCEPTED;
}
@Override
public IExecutable<String> exit() {
result.add(CLOSE_BRACKET);
return new ExecutableList<String>(result);
}
}
private CompilerResultTester given(Token... inputs) {
return new CompilerResultTester(new DefaultPostfixCompiler<String>(VALUE_PARSER, operators) {
@Override
protected IPostfixCompilerState<String> createStateForModifier(String modifier) {
if (modifier.equals(TEST_MODIFIER))
return new TestModifierState();
else
return super.createStateForModifier(modifier);
}
@Override
protected IPostfixCompilerState<String> createStateForBracket(String modifier) {
return new TestBracketState();
}
}, inputs);
}
@Test
public void testSinglValue() {
given(dec("1")).expect(c("1"));
given(oct("2")).expect(c("2"));
given(hex("3")).expect(c("3"));
given(bin("10")).expect(c("10"));
given(quoted("10")).expect(c("10"));
}
@Test
public void testSingleOp() {
given(OP_PLUS).expect(PLUS);
}
@Test
public void testSymbol() {
given(symbol("a")).expect(call("a"));
}
@Test
public void testModifier() {
given(mod(TEST_MODIFIER), dec("3")).expect(c(rawTokenString(dec("3"))));
given(mod(TEST_MODIFIER), symbol("a")).expect(c(rawTokenString(symbol("a"))));
given(dec("1"), mod(TEST_MODIFIER), symbol("a"), symbol("b")).expect(c("1"), c(rawTokenString(symbol("a"))), call("b"));
}
@Test
public void testBrackets() {
given(LEFT_BRACKET, RIGHT_BRACKET).expect(OPEN_BRACKET, CLOSE_BRACKET);
given(LEFT_BRACKET, dec("3"), RIGHT_BRACKET).expect(OPEN_BRACKET, c("3"), CLOSE_BRACKET);
given(LEFT_BRACKET, dec("3"), dec("4"), RIGHT_BRACKET).expect(OPEN_BRACKET, c("3"), c("4"), CLOSE_BRACKET);
given(dec("1"), LEFT_BRACKET, dec("2"), RIGHT_BRACKET, dec("3")).expect(c("1"), OPEN_BRACKET, c("2"), CLOSE_BRACKET, c("3"));
given(dec("1"), LEFT_BRACKET, dec("2"), dec("3"), RIGHT_BRACKET, dec("4")).expect(c("1"), OPEN_BRACKET, c("2"), c("3"), CLOSE_BRACKET, c("4"));
}
@Test
public void testNestedBracketFlattening() {
given(LEFT_BRACKET, LEFT_BRACKET, dec("4"), RIGHT_BRACKET, RIGHT_BRACKET).expect(OPEN_BRACKET, OPEN_BRACKET, c("4"), CLOSE_BRACKET, CLOSE_BRACKET);
given(LEFT_BRACKET, LEFT_BRACKET, LEFT_BRACKET, dec("4"), RIGHT_BRACKET, RIGHT_BRACKET, RIGHT_BRACKET).expect(OPEN_BRACKET, OPEN_BRACKET, OPEN_BRACKET, c("4"), CLOSE_BRACKET, CLOSE_BRACKET, CLOSE_BRACKET);
given(LEFT_BRACKET, dec("3"), LEFT_BRACKET, dec("4"), RIGHT_BRACKET, RIGHT_BRACKET).expect(OPEN_BRACKET, c("3"), OPEN_BRACKET, c("4"), CLOSE_BRACKET, CLOSE_BRACKET);
}
@Test(expected = StackUnderflowException.class)
public void testUnclosedBrackers() {
given(LEFT_BRACKET, dec("4"));
}
@Test
public void testSingleExpr() {
given(symbol("a"), dec("3"), OP_PLUS).expect(call("a"), c("3"), PLUS);
}
@Test
public void testSymbolWithArgs() {
given(symbol_args("a$2")).expect(call("a", OptionalInt.of(2), OptionalInt.absent()));
given(symbol_args("a$2,")).expect(call("a", OptionalInt.of(2), OptionalInt.absent()));
given(symbol_args("a$,3")).expect(call("a", OptionalInt.absent(), OptionalInt.of(3)));
given(symbol_args("a$,")).expect(call("a", OptionalInt.absent(), OptionalInt.absent()));
given(symbol_args("b$3,4")).expect(call("b", OptionalInt.of(3), OptionalInt.of(4)));
given(symbol_args("b$35,45")).expect(call("b", OptionalInt.of(35), OptionalInt.of(45)));
}
}