package openmods.calc;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.PeekingIterator;
import java.util.Arrays;
import java.util.List;
import openmods.calc.BinaryOperator.Associativity;
import openmods.calc.parsing.ContainerNode;
import openmods.calc.parsing.DummyNode;
import openmods.calc.parsing.IAstParser;
import openmods.calc.parsing.ICompilerState;
import openmods.calc.parsing.IExprNode;
import openmods.calc.parsing.IModifierStateTransition;
import openmods.calc.parsing.ISymbolCallStateTransition;
import openmods.calc.parsing.ITokenStreamCompiler;
import openmods.calc.parsing.IValueParser;
import openmods.calc.parsing.QuotedParser;
import openmods.calc.parsing.QuotedParser.IQuotedExprNodeFactory;
import openmods.calc.parsing.SymbolCallNode;
import openmods.calc.parsing.Token;
import openmods.calc.parsing.TokenType;
import openmods.calc.types.multi.TypedCalcConstants;
import openmods.utils.OptionalInt;
import openmods.utils.Stack;
import org.junit.Assert;
public class CalcTestUtils {
public static class ValueParserHelper<E> {
public final IValueParser<E> parser;
public ValueParserHelper(IValueParser<E> parser) {
this.parser = parser;
}
private E parse(TokenType type, String value) {
return parser.parseToken(new Token(type, value));
}
public E bin(String value) {
return parse(TokenType.BIN_NUMBER, value);
}
public void testBin(E expected, String input) {
Assert.assertEquals(expected, bin(input));
}
public E oct(String value) {
return parse(TokenType.OCT_NUMBER, value);
}
public void testOct(E expected, String input) {
Assert.assertEquals(expected, oct(input));
}
public E dec(String value) {
return parse(TokenType.DEC_NUMBER, value);
}
public void testDec(E expected, String input) {
Assert.assertEquals(expected, dec(input));
}
public E hex(String value) {
return parse(TokenType.HEX_NUMBER, value);
}
public void testHex(E expected, String input) {
Assert.assertEquals(expected, hex(input));
}
public E quoted(String value) {
return parse(TokenType.QUOTED_NUMBER, value);
}
public void testQuoted(E expected, String input) {
Assert.assertEquals(expected, quoted(input));
}
}
public static Token t(TokenType type, String value) {
return new Token(type, value);
}
public static Token dec(String value) {
return t(TokenType.DEC_NUMBER, value);
}
public static Token oct(String value) {
return t(TokenType.OCT_NUMBER, value);
}
public static Token hex(String value) {
return t(TokenType.HEX_NUMBER, value);
}
public static Token bin(String value) {
return t(TokenType.BIN_NUMBER, value);
}
public static Token quoted(String value) {
return t(TokenType.QUOTED_NUMBER, value);
}
public static Token string(String value) {
return t(TokenType.STRING, value);
}
public static Token symbol(String value) {
return t(TokenType.SYMBOL, value);
}
public static Token symbol_args(String value) {
return t(TokenType.SYMBOL_WITH_ARGS, value);
}
public static Token op(String value) {
return t(TokenType.OPERATOR, value);
}
public static Token mod(String value) {
return t(TokenType.MODIFIER, value);
}
public static Token leftBracket(String value) {
return t(TokenType.LEFT_BRACKET, value);
}
public static Token rightBracket(String value) {
return t(TokenType.RIGHT_BRACKET, value);
}
public static final Token COMMA = t(TokenType.SEPARATOR, ",");
public static final Token RIGHT_BRACKET = rightBracket(")");
public static final Token LEFT_BRACKET = leftBracket("(");
public static final Token QUOTE_MODIFIER = mod(TypedCalcConstants.MODIFIER_QUOTE);
public static final Token QUOTE_SYMBOL = symbol(TypedCalcConstants.SYMBOL_QUOTE);
public static class DummyBinaryOperator<E> extends BinaryOperator.Direct<E> {
public DummyBinaryOperator(int precendence, String id) {
super(id, precendence);
}
public DummyBinaryOperator(int precendence, String id, Associativity associativity) {
super(id, precendence, associativity);
}
@Override
public E execute(E left, E right) {
return null;
}
}
public static class DummyUnaryOperator<E> extends UnaryOperator.Direct<E> {
public DummyUnaryOperator(String id) {
super(id);
}
@Override
public E execute(E value) {
return null;
}
}
public static final BinaryOperator<String> PLUS = new DummyBinaryOperator<String>(1, "+");
public static final Token OP_PLUS = op("+");
public static final UnaryOperator<String> UNARY_PLUS = new DummyUnaryOperator<String>("+");
public static final BinaryOperator<String> MINUS = new DummyBinaryOperator<String>(1, "-");
public static final Token OP_MINUS = op("-");
public static final UnaryOperator<String> UNARY_MINUS = new DummyUnaryOperator<String>("-");
public static final UnaryOperator<String> UNARY_NEG = new DummyUnaryOperator<String>("!");
public static final Token OP_NEG = op("!");
public static final BinaryOperator<String> MULTIPLY = new DummyBinaryOperator<String>(2, "*");
public static final Token OP_MULTIPLY = op("*");
public static final BinaryOperator<String> DEFAULT = new DummyBinaryOperator<String>(2, "<*>");
public static final Token OP_DEFAULT = op("<*>");
public static final BinaryOperator<String> ASSIGN = new DummyBinaryOperator<String>(1, "=", Associativity.RIGHT);
public static final Token OP_ASSIGN = op("=");
public static IExecutable<String> c(String value) {
return new Value<String>(value);
}
public static SymbolGet<String> get(String value) {
return new SymbolGet<String>(value);
}
public static SymbolCall<String> call(String value) {
return new SymbolCall<String>(value);
}
public static SymbolCall<String> call(String value, int args) {
return new SymbolCall<String>(value, args, 1);
}
public static SymbolCall<String> call(String value, int args, int rets) {
return new SymbolCall<String>(value, args, rets);
}
public static SymbolCall<String> call(String value, OptionalInt args, OptionalInt rets) {
return new SymbolCall<String>(value, args, rets);
}
public static ExecutableList<String> list(IExecutable<String>... elements) {
return new ExecutableList<String>(elements);
}
public static class MarkerExecutable<E> implements IExecutable<E> {
public String tag;
public MarkerExecutable(String tag) {
this.tag = Strings.nullToEmpty(tag);
}
@Override
public void execute(Frame<E> frame) {
throw new UnsupportedOperationException();
}
@Override
public int hashCode() {
return tag.hashCode();
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
return obj instanceof MarkerExecutable
&& ((MarkerExecutable<?>)obj).tag.equals(this.tag);
}
@Override
public String toString() {
return "!!!" + tag;
}
}
public static IExecutable<String> marker(String tag) {
return new MarkerExecutable<String>(tag);
}
public static final IValueParser<String> VALUE_PARSER = new IValueParser<String>() {
@Override
public String parseToken(Token token) {
return token.value;
}
};
public interface Acceptor<E> {
public void accept(E value);
}
public static class StackCheck<E> {
private final Calculator<E, ExprType> sut;
public StackCheck(Calculator<E, ExprType> sut) {
this.sut = sut;
}
public StackCheck<E> expectStack(E... values) {
Assert.assertEquals(Arrays.asList(values), Lists.newArrayList(sut.environment.topFrame().stack()));
return this;
}
public StackCheck<E> expectEmptyStack() {
Assert.assertTrue(sut.environment.topFrame().stack().isEmpty());
return this;
}
}
public static class CalcCheck<E> {
private final Calculator<E, ExprType> sut;
private final IExecutable<E> expr;
public CalcCheck(Calculator<E, ExprType> sut, IExecutable<E> expr) {
this.expr = expr;
this.sut = sut;
}
public CalcCheck<E> expectResult(E value) {
final Frame<E> frame = sut.environment.executeIsolated(expr);
final E top = frame.stack().pop();
Assert.assertEquals(value, top);
if (!frame.stack().isEmpty())
Assert.fail("Extra values on stack: " + Lists.newArrayList(frame.stack()));
return this;
}
public CalcCheck<E> expectResults(E... values) {
final Frame<E> frame = sut.environment.executeIsolated(expr);
Assert.assertEquals(Arrays.asList(values), Lists.newArrayList(frame.stack()));
return this;
}
public StackCheck<E> execute() {
sut.environment.execute(expr);
return new StackCheck<E>(sut);
}
public E executeAndPop() {
final Frame<E> frame = sut.environment.executeIsolated(expr);
final E top = frame.stack().pop();
if (!frame.stack().isEmpty())
Assert.fail("Extra values on stack: " + Lists.newArrayList(frame.stack()));
return top;
}
public Stack<E> executeAndGetStack() {
final Frame<E> frame = sut.environment.executeIsolated(expr);
return frame.stack();
}
public void expectThrow(Class<? extends Throwable> cls) {
try {
sut.environment.execute(expr);
} catch (Throwable t) {
if (!cls.isInstance(t)) {
final AssertionError assertionError = new AssertionError("Expected " + cls);
assertionError.initCause(t);
throw assertionError;
}
return;
}
throw new AssertionError("Expected exception " + cls + ", got nothing");
}
public void expectThrow(Class<? extends Throwable> cls, String message) {
try {
sut.environment.execute(expr);
} catch (Throwable t) {
Assert.assertTrue("Expected " + cls + " got " + t, cls.isInstance(t));
Assert.assertEquals(message, t.getMessage());
return;
}
throw new AssertionError("Expected exception " + cls + ", got nothing");
}
public static <E> CalcCheck<E> create(Calculator<E, ExprType> sut, String value, ExprType exprType) {
final IExecutable<E> expr = sut.compilers.compile(exprType, value);
return new CalcCheck<E>(sut, expr);
}
public static <E> CalcCheck<E> create(Calculator<E, ExprType> sut, IExecutable<E> expr) {
return new CalcCheck<E>(sut, expr);
}
public CalcCheck<E> expectSameAs(CalcCheck<E> other) {
Assert.assertEquals(this.expr, other.expr);
return this;
}
}
public static PeekingIterator<Token> tokenIterator(Token... inputs) {
return Iterators.peekingIterator(Iterators.forArray(inputs));
}
public static class CompilerResultTester {
private final List<IExecutable<String>> actual;
private final ITokenStreamCompiler<String> compiler;
@SuppressWarnings("unchecked")
public CompilerResultTester(ITokenStreamCompiler<String> compiler, Token... inputs) {
this.compiler = compiler;
final IExecutable<String> result = compiler.compile(tokenIterator(inputs));
if (result instanceof NoopExecutable) {
this.actual = Lists.newArrayList();
} else if (result instanceof ExecutableList) {
this.actual = ((ExecutableList<String>)result).getCommands();
} else {
this.actual = Arrays.asList(result);
}
}
public CompilerResultTester expectSameAs(Token... inputs) {
final IExecutable<?> result = compiler.compile(tokenIterator(inputs));
Assert.assertTrue(result instanceof ExecutableList);
Assert.assertEquals(((ExecutableList<?>)result).getCommands(), actual);
return this;
}
public CompilerResultTester expect(IExecutable<?>... expected) {
Assert.assertEquals(Arrays.asList(expected), actual);
return this;
}
}
public static final IExecutable<String> CLOSE_QUOTE = rightBracketMarker(")");
public static final IExecutable<String> OPEN_QUOTE = leftBracketMarker("(");
public static final IExecutable<String> OPEN_ROOT_QUOTE_M = marker("<<" + TypedCalcConstants.MODIFIER_QUOTE);
public static final IExecutable<String> CLOSE_ROOT_QUOTE_M = marker(TypedCalcConstants.MODIFIER_QUOTE + ">>");
public static final IExecutable<String> OPEN_ROOT_QUOTE_S = marker("((" + TypedCalcConstants.SYMBOL_QUOTE);
public static final IExecutable<String> CLOSE_ROOT_QUOTE_S = marker(TypedCalcConstants.SYMBOL_QUOTE + "))");
public static IExecutable<String> leftBracketMarker(String value) {
return marker("<" + value);
}
public static IExecutable<String> rightBracketMarker(String value) {
return marker(value + ">");
}
public static IExecutable<String> valueMarker(String value) {
return marker("value:" + value);
}
public static IExecutable<String> rawValueMarker(Token token) {
return rawValueMarker(token.type, token.value);
}
public static IExecutable<String> rawValueMarker(TokenType type, String value) {
return marker("raw:" + type + ":" + value);
}
public static class MarkerNode implements IExprNode<String> {
private final IExecutable<String> value;
public MarkerNode(IExecutable<String> value) {
this.value = value;
}
@Override
public void flatten(List<IExecutable<String>> output) {
output.add(value);
}
@Override
public Iterable<IExprNode<String>> getChildren() {
return ImmutableList.of();
}
}
public static class QuoteNodeTestFactory implements IQuotedExprNodeFactory<String> {
@Override
public IExprNode<String> createValueNode(String value) {
return new MarkerNode(valueMarker(value));
}
@Override
public IExprNode<String> createValueNode(Token token) {
return new MarkerNode(token.type.isValue()? valueMarker(token.value) : rawValueMarker(token));
}
@Override
public IExprNode<String> createBracketNode(final String openingBracket, final String closingBracket, final List<IExprNode<String>> children) {
return new IExprNode<String>() {
@Override
public void flatten(List<IExecutable<String>> output) {
output.add(leftBracketMarker(openingBracket));
for (IExprNode<String> child : children)
child.flatten(output);
output.add(rightBracketMarker(closingBracket));
}
@Override
public Iterable<IExprNode<String>> getChildren() {
return children;
}
};
}
}
public static class QuotedCompilerState implements ICompilerState<String> {
private final QuotedParser<String> quotedParser = new QuotedParser<String>(VALUE_PARSER, new QuoteNodeTestFactory());
@Override
public IAstParser<String> getParser() {
return quotedParser;
}
@Override
public ISymbolCallStateTransition<String> getStateForSymbolCall(String symbol) {
throw new UnsupportedOperationException();
}
@Override
public IModifierStateTransition<String> getStateForModifier(String modifier) {
throw new UnsupportedOperationException();
}
}
public static class ModifierQuoteTransition implements IModifierStateTransition<String> {
private final String modifier;
public ModifierQuoteTransition(String modifier) {
this.modifier = modifier;
}
@Override
public IExprNode<String> createRootNode(IExprNode<String> child) {
return new DummyNode<String>(child) {
@Override
public void flatten(List<IExecutable<String>> output) {
output.add(marker("<<" + modifier));
super.flatten(output);
output.add(marker(modifier + ">>"));
}
};
}
@Override
public ICompilerState<String> getState() {
return new QuotedCompilerState();
}
}
public static class SymbolQuoteTransition implements ISymbolCallStateTransition<String> {
private final String symbol;
public SymbolQuoteTransition(String symbol) {
this.symbol = symbol;
}
@Override
public IExprNode<String> createRootNode(List<IExprNode<String>> children) {
return new ContainerNode<String>(children) {
@Override
public void flatten(List<IExecutable<String>> output) {
output.add(marker("((" + symbol));
for (IExprNode<String> child : args)
child.flatten(output);
output.add(marker(symbol + "))"));
}
};
}
@Override
public ICompilerState<String> getState() {
return new QuotedCompilerState();
}
}
public abstract static class TestCompilerState implements ICompilerState<String> {
@Override
public IModifierStateTransition<String> getStateForModifier(String modifier) {
if (modifier.equals(QUOTE_MODIFIER.value)) return new ModifierQuoteTransition(modifier);
throw new UnsupportedOperationException(modifier);
}
@Override
public ISymbolCallStateTransition<String> getStateForSymbolCall(final String symbol) {
if (symbol.equals(QUOTE_SYMBOL.value)) return new SymbolQuoteTransition(symbol);
return new ISymbolCallStateTransition<String>() {
@Override
public ICompilerState<String> getState() {
return TestCompilerState.this;
}
@Override
public IExprNode<String> createRootNode(List<IExprNode<String>> children) {
return new SymbolCallNode<String>(symbol, children);
}
};
}
}
public static class SymbolStub<E> implements ISymbol<E> {
private int callCount;
private int getCount;
private List<E> expectedArgs = Lists.newArrayList();
private boolean exactArgCount = false;
private List<E> returns = Lists.newArrayList();
private E getValue;
private boolean exactReturnCount = false;
private boolean allowCalls = false;
private boolean allowGets = false;
public SymbolStub<E> expectArgs(E... args) {
expectedArgs = Lists.reverse(Arrays.asList(args));
return this;
}
public SymbolStub<E> verifyArgCount() {
this.exactArgCount = true;
return this;
}
public SymbolStub<E> setReturns(E... rets) {
returns = Arrays.asList(rets);
return this;
}
public SymbolStub<E> setGetValue(E value) {
this.getValue = value;
return this;
}
public SymbolStub<E> verifyReturnCount() {
this.exactReturnCount = true;
return this;
}
@Override
public void call(Frame<E> frame, OptionalInt argumentsCount, OptionalInt returnsCount) {
Assert.assertTrue(allowCalls);
if (exactArgCount) Assert.assertEquals(OptionalInt.of(expectedArgs.size()), argumentsCount);
if (exactReturnCount) Assert.assertEquals(OptionalInt.of(returns.size()), returnsCount);
for (E expectedArg : expectedArgs)
Assert.assertEquals(frame.stack().pop(), expectedArg);
frame.stack().pushAll(returns);
callCount++;
}
@Override
public E get() {
Assert.assertTrue(allowGets);
getCount++;
return getValue;
}
public SymbolStub<E> checkCallCount(int expectedCallCount) {
Assert.assertEquals(expectedCallCount, this.callCount);
return this;
}
public SymbolStub<E> resetCallCount() {
this.callCount = 0;
return this;
}
public SymbolStub<E> allowCalls() {
this.allowCalls = true;
return this;
}
public SymbolStub<E> checkGetCount(int expectedGetCount) {
Assert.assertEquals(expectedGetCount, this.getCount);
return this;
}
public SymbolStub<E> resetGetCount() {
this.getCount = 0;
return this;
}
public SymbolStub<E> allowGets() {
this.allowGets = true;
return this;
}
}
public static class CallableStub<E> implements ICallable<E> {
private int callCount;
private List<E> expectedArgs = Lists.newArrayList();
private boolean exactArgCount = false;
private List<E> returns = Lists.newArrayList();
private boolean exactReturnCount = false;
public CallableStub<E> expectArgs(E... args) {
expectedArgs = Lists.reverse(Arrays.asList(args));
return this;
}
public CallableStub<E> verifyArgCount() {
this.exactArgCount = true;
return this;
}
public CallableStub<E> setReturns(E... rets) {
returns = Arrays.asList(rets);
return this;
}
public CallableStub<E> verifyReturnCount() {
this.exactReturnCount = true;
return this;
}
@Override
public void call(Frame<E> frame, OptionalInt argumentsCount, OptionalInt returnsCount) {
if (exactArgCount) Assert.assertEquals(OptionalInt.of(expectedArgs.size()), argumentsCount);
if (exactReturnCount) Assert.assertEquals(OptionalInt.of(returns.size()), returnsCount);
for (E expectedArg : expectedArgs)
Assert.assertEquals(frame.stack().pop(), expectedArg);
frame.stack().pushAll(returns);
callCount++;
}
public CallableStub<E> checkCallCount(int expectedCallCount) {
Assert.assertEquals(expectedCallCount, this.callCount);
return this;
}
public CallableStub<E> resetCallCount() {
this.callCount = 0;
return this;
}
}
}