package org.reasm.m68k.expressions.internal;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
import org.hamcrest.Matcher;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.reasm.AssemblyMessage;
import org.reasm.FloatValue;
import org.reasm.StringValue;
import org.reasm.UnsignedIntValue;
import org.reasm.expressions.*;
import org.reasm.messages.OverflowInLiteralWarningMessage;
import org.reasm.testhelpers.AssemblyMessageCollector;
import org.reasm.testhelpers.DummySymbolLookup;
import org.reasm.testhelpers.EquivalentAssemblyMessage;
import ca.fragag.Consumer;
/**
* Parameterized test class for {@link ExpressionParser#parse(Tokenizer, SymbolLookup, Consumer)}.
*
* @author Francis Gagné
*/
@RunWith(Parameterized.class)
public class ExpressionParserTest {
/**
* Stores the parameters and expected result for a test.
*
* @author Francis Gagné
*/
@Immutable
public static class DataItem {
@Nonnull
final String expressionText;
final int finalPosition;
@CheckForNull
final Expression expectedExpression;
@CheckForNull
final List<Matcher<? super AssemblyMessage>> expectedAssemblyMessageMatchers;
/**
* Initializes a new DataItem.
*
* @param expressionText
* the text of the expression to parse
* @param finalPosition
* the expected final position. Specify -1 to indicate that the parse is expected to fail with an
* {@link InvalidTokenException}.
* @param expectedExpression
* the expected expression
* @param expectedAssemblyMessages
* the expected assembly messages that should be generated when parsing the expression
*/
public DataItem(@Nonnull String expressionText, int finalPosition, @CheckForNull Expression expectedExpression,
@CheckForNull AssemblyMessage... expectedAssemblyMessages) {
this.expressionText = expressionText;
this.finalPosition = finalPosition;
this.expectedExpression = expectedExpression;
if (expectedAssemblyMessages == null) {
this.expectedAssemblyMessageMatchers = null;
} else {
final ArrayList<Matcher<? super AssemblyMessage>> matchers = new ArrayList<>(expectedAssemblyMessages.length);
for (AssemblyMessage expectedAssemblyMessage : expectedAssemblyMessages) {
matchers.add(new EquivalentAssemblyMessage(expectedAssemblyMessage));
}
this.expectedAssemblyMessageMatchers = matchers;
}
}
@Nonnull
@Override
public String toString() {
return this.expressionText;
}
}
@Nonnull
private static final ValueExpression VALUE_UINT_0 = new ValueExpression(new UnsignedIntValue(0));
@Nonnull
private static final ValueExpression VALUE_UINT_3 = new ValueExpression(new UnsignedIntValue(3));
@Nonnull
private static final ValueExpression VALUE_UINT_123456789 = new ValueExpression(new UnsignedIntValue(123456789));
@Nonnull
private static final ValueExpression VALUE_UINT_1234567890123456789 = new ValueExpression(new UnsignedIntValue(
1234567890123456789L));
@Nonnull
private static final ValueExpression VALUE_UINT_18446744073709551615 = new ValueExpression(new UnsignedIntValue(-1));
@Nonnull
private static final ValueExpression VALUE_UINT_5097733592125636885 = new ValueExpression(new UnsignedIntValue(
5097733592125636885L));
@Nonnull
private static final ValueExpression VALUE_FLOAT_3_POINT_5 = new ValueExpression(new FloatValue(3.5));
@Nonnull
private static final ValueExpression VALUE_STRING_EMPTY = new ValueExpression(new StringValue(""));
@Nonnull
private static final ValueExpression VALUE_STRING_ABCDEF = new ValueExpression(new StringValue("abcdef"));
@Nonnull
private static final ValueExpression VALUE_STRING_BACKSLASH = new ValueExpression(new StringValue("\\"));
@Nonnull
private static final IdentifierExpression IDENTIFIER_A = new IdentifierExpression("a", DummySymbolLookup.DEFAULT);
@Nonnull
private static final IdentifierExpression IDENTIFIER_B = new IdentifierExpression("b", DummySymbolLookup.DEFAULT);
@Nonnull
private static final IdentifierExpression IDENTIFIER_C = new IdentifierExpression("c", DummySymbolLookup.DEFAULT);
@Nonnull
private static final IdentifierExpression IDENTIFIER_D = new IdentifierExpression("d", DummySymbolLookup.DEFAULT);
@Nonnull
private static final IdentifierExpression IDENTIFIER_E = new IdentifierExpression("e", DummySymbolLookup.DEFAULT);
@Nonnull
private static final IdentifierExpression IDENTIFIER_FOO = new IdentifierExpression("foo", DummySymbolLookup.DEFAULT);
@Nonnull
private static final AssemblyMessage[] DONT_CHECK_ASSEMBLY_MESSAGES = null;
@Nonnull
private static final Object[][] TEST_DATA_ARRAY = new Object[][] {
// Empty string
{ new DataItem("", 0, null) },
// A single space
{ new DataItem(" ", 1, null) },
// An anonymous symbol
{ new DataItem("+++", 3, new IdentifierExpression("+++", DummySymbolLookup.DEFAULT)) },
// An anonymous symbol
{ new DataItem("-----", 5, new IdentifierExpression("-----", DummySymbolLookup.DEFAULT)) },
// An anonymous symbol followed by a closing parenthesis
{ new DataItem("+)", 1, new IdentifierExpression("+", DummySymbolLookup.DEFAULT)) },
// A short decimal integer literal
{ new DataItem("0", 1, VALUE_UINT_0) },
// A short decimal integer literal followed by a closing parenthesis
{ new DataItem("0)", 1, VALUE_UINT_0) },
// A long decimal integer literal followed by a closing parenthesis
{ new DataItem("123456789)", 9, VALUE_UINT_123456789) },
// A long decimal integer literal
{ new DataItem("123456789", 9, VALUE_UINT_123456789) },
// A longer decimal integer literal
{ new DataItem("1234567890123456789", 19, VALUE_UINT_1234567890123456789) },
// An almost-overflowing decimal integer literal
{ new DataItem("18446744073709551615", 20, VALUE_UINT_18446744073709551615) },
// An overflowing decimal integer literal
{ new DataItem("12345678901234567890123456789", 29, VALUE_UINT_5097733592125636885,
new OverflowInLiteralWarningMessage("12345678901234567890123456789")) },
// An overflowing decimal integer literal (parse with assemblyMessageConsumer == null)
{ new DataItem("12345678901234567890123456789", 29, VALUE_UINT_5097733592125636885, DONT_CHECK_ASSEMBLY_MESSAGES) },
// An overflowing decimal integer literal that fails a too simplistic overflow check
{ new DataItem("26000000000000000000", 20, new ValueExpression(new UnsignedIntValue(7553255926290448384L)),
new OverflowInLiteralWarningMessage("26000000000000000000")) },
// An overflowing decimal integer literal that overflows when summing r and s
{ new DataItem("18446744073709551620", 20, new ValueExpression(new UnsignedIntValue(4L)),
new OverflowInLiteralWarningMessage("18446744073709551620")) },
// An overflowing decimal integer literal that overflows when adding the last digit to the result
{ new DataItem("18446744073709551619", 20, VALUE_UINT_3, new OverflowInLiteralWarningMessage("18446744073709551619")) },
// A float literal with no digits after the decimal point
{ new DataItem("3.", 2, new ValueExpression(new FloatValue(3.))) },
// A float literal with no digits after the decimal point followed by "?"
{ new DataItem("3.?", 2, new ValueExpression(new FloatValue(3.))) },
// A short float literal
{ new DataItem("3.5", 3, VALUE_FLOAT_3_POINT_5) },
// An invalid float literal
{ new DataItem("3.5e", -1, null) },
// An invalid float literal
{ new DataItem("3.5e?", -1, null) },
// A short float literal with scientific E notation (upper-case E)
{ new DataItem("3.5E3", 5, new ValueExpression(new FloatValue(3.5e3))) },
// A short float literal with scientific E notation (lower-case E)
{ new DataItem("3.5e3", 5, new ValueExpression(new FloatValue(3.5e3))) },
// A short float literal with scientific E notation with a long exponent
{ new DataItem("3.5e120", 7, new ValueExpression(new FloatValue(3.5e120))) },
// A short float literal with scientific E notation with a too big exponent
{ new DataItem("3.5e400", 7, new ValueExpression(new FloatValue(Double.POSITIVE_INFINITY))) },
// An invalid float literal
{ new DataItem("3.5e+", -1, null) },
// An invalid float literal
{ new DataItem("3.5e+?", -1, null) },
// A short float literal with scientific E notation with prefix + and a short exponent
{ new DataItem("3.5e+3", 6, new ValueExpression(new FloatValue(3.5e+3))) },
// A short float literal with scientific E notation with prefix + and a long exponent
{ new DataItem("3.5e+120", 8, new ValueExpression(new FloatValue(3.5e+120))) },
// An invalid float literal
{ new DataItem("3.5e-", -1, null) },
// An invalid float literal
{ new DataItem("3.5e-?", -1, null) },
// A short float literal with scientific E notation with prefix - and a short exponent
{ new DataItem("3.5e-3", 6, new ValueExpression(new FloatValue(3.5e-3))) },
// A short float literal with scientific E notation with prefix - and a long exponent
{ new DataItem("3.5e-120", 8, new ValueExpression(new FloatValue(3.5e-120))) },
// A float literal with a long integral part
{ new DataItem(
"10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000.0", 103,
new ValueExpression(new FloatValue(1e100))) },
// A float literal with a long fractional part
{ new DataItem("3.99609375", 10, new ValueExpression(new FloatValue(3.99609375))) },
// A short float literal with scientific E notation but no fractional part
{ new DataItem("3e3", 3, new ValueExpression(new FloatValue(3e3))) },
// A long float literal with scientific E notation but no fractional part
{ new DataItem("987654321e123", 13, new ValueExpression(new FloatValue(987654321e123))) },
// An invalid expression
{ new DataItem("%", -1, null) },
// A short binary integer literal
{ new DataItem("%0", 2, VALUE_UINT_0) },
// A short binary integer literal followed by a closing parenthesis
{ new DataItem("%0)", 2, VALUE_UINT_0) },
// A long binary integer literal
{ new DataItem("%111010110111100110100010101", 28, VALUE_UINT_123456789) },
// A long binary integer literal followed by a closing parenthesis
{ new DataItem("%111010110111100110100010101)", 28, VALUE_UINT_123456789) },
// A longer binary integer literal
{ new DataItem("%1000100100010000100001111010001111101111010011000000100010101", 62, VALUE_UINT_1234567890123456789) },
// An almost-overflowing binary integer literal
{ new DataItem("%1111111111111111111111111111111111111111111111111111111111111111", 65, VALUE_UINT_18446744073709551615) },
// An overflowing binary integer literal
{ new DataItem("%1001111110010000011011001100100100011010111110110010011011000101101110001110011000000100010101", 95,
VALUE_UINT_5097733592125636885, new OverflowInLiteralWarningMessage(
"1001111110010000011011001100100100011010111110110010011011000101101110001110011000000100010101")) },
// An overflowing binary integer literal (parse with assemblyMessageConsumer == null)
{ new DataItem("%1001111110010000011011001100100100011010111110110010011011000101101110001110011000000100010101", 95,
VALUE_UINT_5097733592125636885, DONT_CHECK_ASSEMBLY_MESSAGES) },
// An invalid expression
{ new DataItem("$", -1, null) },
// A short hexadecimal integer literal
{ new DataItem("$0", 2, VALUE_UINT_0) },
// A short hexadecimal integer literal followed by a closing parenthesis
{ new DataItem("$0)", 2, VALUE_UINT_0) },
// A long hexadecimal integer literal (upper-case letters)
{ new DataItem("$75BCD15", 8, VALUE_UINT_123456789) },
// A long hexadecimal integer literal (lower-case letters)
{ new DataItem("$75bcd15", 8, VALUE_UINT_123456789) },
// A long hexadecimal integer literal followed by a closing parenthesis
{ new DataItem("$75BCD15)", 8, VALUE_UINT_123456789) },
// A longer hexadecimal integer literal
{ new DataItem("$112210F47DE98115", 17, VALUE_UINT_1234567890123456789) },
// An almost-overflowing hexadecimal integer literal
{ new DataItem("$FFFFFFFFFFFFFFFF", 17, VALUE_UINT_18446744073709551615) },
// An overflowing hexadecimal integer literal
{ new DataItem("$27E41B3246BEC9B16E398115", 25, VALUE_UINT_5097733592125636885, new OverflowInLiteralWarningMessage(
"27E41B3246BEC9B16E398115")) },
// An overflowing hexadecimal integer literal (parse with assemblyMessageConsumer == null)
{ new DataItem("$27E41B3246BEC9B16E398115", 25, VALUE_UINT_5097733592125636885, DONT_CHECK_ASSEMBLY_MESSAGES) },
// An invalid hexadecimal integer literal
{ new DataItem("$0g", -1, null) },
// The program counter
{ new DataItem("*", 1, ProgramCounterExpression.INSTANCE) },
// A string delimited by apostrophes (single quotes)
{ new DataItem("'abcdef'", 8, VALUE_STRING_ABCDEF) },
// A string delimited by double quotes
{ new DataItem("\"abcdef\"", 8, VALUE_STRING_ABCDEF) },
// An unterminated string starting with an apostrophe (single quote)
{ new DataItem("'abcdef", -1, null) },
// An unterminated string starting with a double quote
{ new DataItem("\"abcdef", -1, null) },
// An unterminated string starting with an apostrophe (single quote) and containing an escaped apostrophe
{ new DataItem("'\\'", -1, null) },
// An unterminated string starting with a double quote and containing an escaped double quote
{ new DataItem("\"\\\"", -1, null) },
// A short identifier
{ new DataItem("a", 1, IDENTIFIER_A) },
// A long identifier
{ new DataItem("abcdef", 6, new IdentifierExpression("abcdef", DummySymbolLookup.DEFAULT)) },
// A long identifier followed by a closing parenthesis
{ new DataItem("abcdef)", 6, new IdentifierExpression("abcdef", DummySymbolLookup.DEFAULT)) },
// An identifier containing a period
{ new DataItem("abc.def", 7, new IdentifierExpression("abc.def", DummySymbolLookup.DEFAULT)) },
// The unary period operator
{ new DataItem(".a", 2, new PeriodExpression(new IdentifierExpression("", null), IDENTIFIER_A,
DummySymbolLookup.DEFAULT)) },
// The unary plus operator
{ new DataItem("+a", 2, new UnaryOperatorExpression(UnaryOperator.UNARY_PLUS, IDENTIFIER_A)) },
// The negation operator
{ new DataItem("-a", 2, new UnaryOperatorExpression(UnaryOperator.NEGATION, IDENTIFIER_A)) },
// The bitwise NOT operator
{ new DataItem("~a", 2, new UnaryOperatorExpression(UnaryOperator.BITWISE_NOT, IDENTIFIER_A)) },
// The logical NOT operator
{ new DataItem("!a", 2, new UnaryOperatorExpression(UnaryOperator.LOGICAL_NOT, IDENTIFIER_A)) },
// An invalid expression
{ new DataItem("^a", 0, null) },
// The unary plus operator surrounded with spaces
{ new DataItem(" + a", 4, new UnaryOperatorExpression(UnaryOperator.UNARY_PLUS, IDENTIFIER_A)) },
// An invalid expression
{ new DataItem(".", 0, null) },
// An invalid expression
{ new DataItem("~", 0, null) },
// An invalid expression
{ new DataItem("~)", 0, null) },
// An invalid expression
{ new DataItem("?", 0, null) },
// An invalid expression
{ new DataItem("!=a", 0, null) },
// An invalid expression
{ new DataItem("(", 0, null) },
// An invalid expression
{ new DataItem("(0", 0, null) },
// A decimal integer literal surrounded with parentheses
{ new DataItem("(0)", 3, new GroupingExpression(VALUE_UINT_0)) },
// A decimal integer literal surrounded with parentheses, with spaces added
{ new DataItem(" ( 0 ) ", 7, new GroupingExpression(VALUE_UINT_0)) },
// An identifier followed by a question mark
{ new DataItem("a?", 1, IDENTIFIER_A) },
// An identifier followed by "?b"
{ new DataItem("a?b", 1, IDENTIFIER_A) },
// An identifier followed by "?b:"
{ new DataItem("a?b:", 1, IDENTIFIER_A) },
// The conditional operator
{ new DataItem("a?b:c", 5, new ConditionalExpression(IDENTIFIER_A, IDENTIFIER_B, IDENTIFIER_C)) },
// An identifier followed by an opening parenthesis
{ new DataItem("foo(", 3, IDENTIFIER_FOO) },
// An identifier followed by "(0"
{ new DataItem("foo(0", 3, IDENTIFIER_FOO) },
// An identifier followed by "(0,"
{ new DataItem("foo(0,", 3, IDENTIFIER_FOO) },
// An identifier followed by "(0, "
{ new DataItem("foo(0, ", 3, IDENTIFIER_FOO) },
// An identifier followed by an opening parenthesis and an opening bracket
{ new DataItem("foo([", 3, IDENTIFIER_FOO) },
// A function call with no arguments
{ new DataItem("foo()", 5, new FunctionCallExpression(IDENTIFIER_FOO)) },
// A function call with one argument
{ new DataItem("foo(0)", 6, new FunctionCallExpression(IDENTIFIER_FOO, VALUE_UINT_0)) },
// A function call with three arguments
{ new DataItem("foo(0,'a', i)", 13, new FunctionCallExpression(IDENTIFIER_FOO, VALUE_UINT_0, new ValueExpression(
new StringValue("a")), new IdentifierExpression("i", DummySymbolLookup.DEFAULT))) },
// A function call with three arguments and a bunch of spaces and tabs all around
{ new DataItem("foo \t( \t0 \t, \t'a' \t, \ti \t) \t", 28, new FunctionCallExpression(IDENTIFIER_FOO, VALUE_UINT_0,
new ValueExpression(new StringValue("a")), new IdentifierExpression("i", DummySymbolLookup.DEFAULT))) },
// A chained function call
{ new DataItem("foo(0)(0)", 9, new FunctionCallExpression(new FunctionCallExpression(IDENTIFIER_FOO, VALUE_UINT_0),
VALUE_UINT_0)) },
// An identifier followed by an opening bracket
{ new DataItem("a[", 1, IDENTIFIER_A) },
// An identifier followed by "[b"
{ new DataItem("a[b", 1, IDENTIFIER_A) },
// The indexer operator
{ new DataItem("a[b]", 4, new IndexerExpression(IDENTIFIER_A, IDENTIFIER_B, DummySymbolLookup.DEFAULT)) },
// The period operator
{ new DataItem("3.a", 3, new PeriodExpression(VALUE_UINT_3, IDENTIFIER_A, DummySymbolLookup.DEFAULT)) },
// An identifier followed by a period
{ new DataItem("a .", 2, IDENTIFIER_A) },
// The period operator used twice
{ new DataItem("a . b . c", 9, new PeriodExpression(new PeriodExpression(IDENTIFIER_A, IDENTIFIER_B,
DummySymbolLookup.DEFAULT), IDENTIFIER_C, DummySymbolLookup.DEFAULT)) },
// The period operator followed by a function call
{ new DataItem("a . b ( c )", 11, new FunctionCallExpression(new PeriodExpression(IDENTIFIER_A, IDENTIFIER_B,
DummySymbolLookup.DEFAULT), IDENTIFIER_C)) },
// An integer literal followed by an asterisk
{ new DataItem("3*", 1, VALUE_UINT_3) },
// The multiplication operator
{ new DataItem("3*a", 3, new BinaryOperatorExpression(BinaryOperator.MULTIPLICATION, VALUE_UINT_3, IDENTIFIER_A)) },
// The division operator
{ new DataItem("3/a", 3, new BinaryOperatorExpression(BinaryOperator.DIVISION, VALUE_UINT_3, IDENTIFIER_A)) },
// The modulus operator
{ new DataItem("3%a", 3, new BinaryOperatorExpression(BinaryOperator.MODULUS, VALUE_UINT_3, IDENTIFIER_A)) },
// The addition operator
{ new DataItem("3+a", 3, new BinaryOperatorExpression(BinaryOperator.ADDITION, VALUE_UINT_3, IDENTIFIER_A)) },
// The subtraction operator
{ new DataItem("3-a", 3, new BinaryOperatorExpression(BinaryOperator.SUBTRACTION, VALUE_UINT_3, IDENTIFIER_A)) },
// The bitwise XOR operator
{ new DataItem("3^a", 3, new BinaryOperatorExpression(BinaryOperator.BITWISE_XOR, VALUE_UINT_3, IDENTIFIER_A)) },
// The bit shift left operator
{ new DataItem("3<<a", 4, new BinaryOperatorExpression(BinaryOperator.BIT_SHIFT_LEFT, VALUE_UINT_3, IDENTIFIER_A)) },
// The less than or equal to operator
{ new DataItem("3<=a", 4,
new BinaryOperatorExpression(BinaryOperator.LESS_THAN_OR_EQUAL_TO, VALUE_UINT_3, IDENTIFIER_A)) },
// The different from operator
{ new DataItem("3<>a", 4, new BinaryOperatorExpression(BinaryOperator.DIFFERENT_FROM, VALUE_UINT_3, IDENTIFIER_A)) },
// The less than operator
{ new DataItem("3<a", 3, new BinaryOperatorExpression(BinaryOperator.LESS_THAN, VALUE_UINT_3, IDENTIFIER_A)) },
// The greater than or equal to operator
{ new DataItem("3>=a", 4, new BinaryOperatorExpression(BinaryOperator.GREATER_THAN_OR_EQUAL_TO, VALUE_UINT_3,
IDENTIFIER_A)) },
// The bit shift right operator
{ new DataItem("3>>a", 4, new BinaryOperatorExpression(BinaryOperator.BIT_SHIFT_RIGHT, VALUE_UINT_3, IDENTIFIER_A)) },
// The greater than operator
{ new DataItem("3>a", 3, new BinaryOperatorExpression(BinaryOperator.GREATER_THAN, VALUE_UINT_3, IDENTIFIER_A)) },
// The strictly equal to operator
{ new DataItem("3==a", 4, new BinaryOperatorExpression(BinaryOperator.STRICTLY_EQUAL_TO, VALUE_UINT_3, IDENTIFIER_A)) },
// The equal to operator
{ new DataItem("3=a", 3, new BinaryOperatorExpression(BinaryOperator.EQUAL_TO, VALUE_UINT_3, IDENTIFIER_A)) },
// The logical AND operator
{ new DataItem("3&&a", 4, new BinaryOperatorExpression(BinaryOperator.LOGICAL_AND, VALUE_UINT_3, IDENTIFIER_A)) },
// The bitwise AND operator
{ new DataItem("3&a", 3, new BinaryOperatorExpression(BinaryOperator.BITWISE_AND, VALUE_UINT_3, IDENTIFIER_A)) },
// The logical OR operator
{ new DataItem("3||a", 4, new BinaryOperatorExpression(BinaryOperator.LOGICAL_OR, VALUE_UINT_3, IDENTIFIER_A)) },
// The bitwise OR operator
{ new DataItem("3|a", 3, new BinaryOperatorExpression(BinaryOperator.BITWISE_OR, VALUE_UINT_3, IDENTIFIER_A)) },
// The strictly different from operator
{ new DataItem("3!=a", 4, new BinaryOperatorExpression(BinaryOperator.STRICTLY_DIFFERENT_FROM, VALUE_UINT_3,
IDENTIFIER_A)) },
// The multiplication operator surrounded with spaces
{ new DataItem("3 * a", 5, new BinaryOperatorExpression(BinaryOperator.MULTIPLICATION, VALUE_UINT_3, IDENTIFIER_A)) },
// The addition operator used twice
{ new DataItem("a+b+c", 5, new BinaryOperatorExpression(BinaryOperator.ADDITION, new BinaryOperatorExpression(
BinaryOperator.ADDITION, IDENTIFIER_A, IDENTIFIER_B), IDENTIFIER_C)) },
// The addition operator followed by the subtraction operator
{ new DataItem("a+b-c", 5, new BinaryOperatorExpression(BinaryOperator.SUBTRACTION, new BinaryOperatorExpression(
BinaryOperator.ADDITION, IDENTIFIER_A, IDENTIFIER_B), IDENTIFIER_C)) },
// The subtraction operator followed by the addition operator
{ new DataItem("a-b+c", 5, new BinaryOperatorExpression(BinaryOperator.ADDITION, new BinaryOperatorExpression(
BinaryOperator.SUBTRACTION, IDENTIFIER_A, IDENTIFIER_B), IDENTIFIER_C)) },
// The addition operator followed by the multiplication operator
{ new DataItem("a+b*c", 5, new BinaryOperatorExpression(BinaryOperator.ADDITION, IDENTIFIER_A,
new BinaryOperatorExpression(BinaryOperator.MULTIPLICATION, IDENTIFIER_B, IDENTIFIER_C))) },
// The multiplication operator followed by the addition operator
{ new DataItem("a*b+c", 5, new BinaryOperatorExpression(BinaryOperator.ADDITION, new BinaryOperatorExpression(
BinaryOperator.MULTIPLICATION, IDENTIFIER_A, IDENTIFIER_B), IDENTIFIER_C)) },
// A decimal integer literal followed by an exclamation mark and a letter
{ new DataItem("3!a", 1, VALUE_UINT_3) },
// A decimal integer literal followed by a tilde and a letter
{ new DataItem("3~a", 1, VALUE_UINT_3) },
// The program counter multiplied by the program counter
{ new DataItem("***", 3, new BinaryOperatorExpression(BinaryOperator.MULTIPLICATION, ProgramCounterExpression.INSTANCE,
ProgramCounterExpression.INSTANCE)) },
// The period operator with the program counter on both sides
{ new DataItem("*.*", 3, new PeriodExpression(ProgramCounterExpression.INSTANCE, ProgramCounterExpression.INSTANCE,
DummySymbolLookup.DEFAULT)) },
// A binary integer literal modulo another binary integer literal
{ new DataItem("%11%%11", 7, new BinaryOperatorExpression(BinaryOperator.MODULUS, VALUE_UINT_3, VALUE_UINT_3)) },
// The negation operator applied to the result of a function call
{ new DataItem("-a(b)", 5, new UnaryOperatorExpression(UnaryOperator.NEGATION, new FunctionCallExpression(IDENTIFIER_A,
IDENTIFIER_B))) },
// An unary operator after a period operator
{ new DataItem("a . !b", 2, IDENTIFIER_A) },
// Grouping parentheses after a period operator
{ new DataItem("a . (b)", 7, new PeriodExpression(IDENTIFIER_A, new GroupingExpression(IDENTIFIER_B),
DummySymbolLookup.DEFAULT)) },
// A conditional expression mixed with binary operators
{ new DataItem("a + b = c ? d : e", 17, new ConditionalExpression(new BinaryOperatorExpression(BinaryOperator.EQUAL_TO,
new BinaryOperatorExpression(BinaryOperator.ADDITION, IDENTIFIER_A, IDENTIFIER_B), IDENTIFIER_C), IDENTIFIER_D,
IDENTIFIER_E)) },
// Nested conditional expressions
{ new DataItem("a ? b ? c : d : c ? b : a", 25, new ConditionalExpression(IDENTIFIER_A, new ConditionalExpression(
IDENTIFIER_B, IDENTIFIER_C, IDENTIFIER_D), new ConditionalExpression(IDENTIFIER_C, IDENTIFIER_B, IDENTIFIER_A))) },
};
@Nonnull
private static final List<Object[]> TEST_DATA_LIST = Arrays.asList(TEST_DATA_ARRAY);
/**
* Gets the test data for this parameterized test.
*
* @return the test data
*/
@Nonnull
@Parameters
public static List<Object[]> data() {
return TEST_DATA_LIST;
}
@Nonnull
private final DataItem data;
/**
* Initializes a new ExpressionParserTest.
*
* @param data
* the data to test with
*/
public ExpressionParserTest(@Nonnull DataItem data) {
this.data = data;
}
/**
* Asserts that {@link ExpressionParser#parse(Tokenizer, SymbolLookup, Consumer)} parses the correct expression, leaves the
* tokenizer in the expected state and emits the expected {@link AssemblyMessage AssemblyMessages}.
*/
@Test
public void parse() {
try {
final Tokenizer tokenizer = new Tokenizer();
final ArrayList<AssemblyMessage> assemblyMessages = new ArrayList<>();
final Consumer<AssemblyMessage> assemblyMessageConsumer = this.data.expectedAssemblyMessageMatchers == null ? null
: new AssemblyMessageCollector(assemblyMessages);
final Expression expression;
tokenizer.setCharSequence(this.data.expressionText);
try {
expression = ExpressionParser.parse(tokenizer, DummySymbolLookup.DEFAULT, assemblyMessageConsumer);
} catch (InvalidTokenException e) {
assertThat(-1, is(this.data.finalPosition));
return;
}
assertThat(expression, is(this.data.expectedExpression));
if (tokenizer.getTokenType() == TokenType.END) {
assertThat(this.data.expressionText.length(), is(this.data.finalPosition));
} else {
assertThat(tokenizer.getTokenStart(), is(this.data.finalPosition));
}
if (this.data.expectedAssemblyMessageMatchers != null) {
if (this.data.expectedAssemblyMessageMatchers.isEmpty()) {
assertThat(assemblyMessages, is(empty()));
} else {
assertThat(assemblyMessages, contains(this.data.expectedAssemblyMessageMatchers));
}
}
} catch (AssertionError e) {
throw new AssertionError(this.data.expressionText + e.getMessage(), e);
}
}
}