Numeric constants for ConditionExpression

This commit is contained in:
atlimit8
2017-02-06 12:13:00 -06:00
parent 65725efd04
commit 73895d07e2
7 changed files with 178 additions and 92 deletions

View File

@@ -61,6 +61,13 @@ namespace OpenRA.Support
: base(symbol, index, Associativity.Left, 0) { }
}
class NumberToken : Token
{
public readonly int Value;
public NumberToken(int index, string symbol)
: base(symbol, index, Associativity.Left, 0) { Value = int.Parse(symbol); }
}
class AndToken : BinaryOperationToken { public AndToken(int index) : base("&&", index) { } }
class OrToken : BinaryOperationToken { public OrToken(int index) : base("||", index) { } }
class EqualsToken : BinaryOperationToken { public EqualsToken(int index) : base("==", index) { } }
@@ -120,17 +127,17 @@ namespace OpenRA.Support
for (var i = 0; i < tokens.Count - 1; i++)
{
// Unary tokens must be followed by a variable, another unary token, or an opening parenthesis
if (tokens[i] is UnaryOperationToken && !(tokens[i + 1] is VariableToken || tokens[i + 1] is UnaryOperationToken
|| tokens[i + 1] is OpenParenToken))
// Unary tokens must be followed by a variable, number, another unary token, or an opening parenthesis
if (tokens[i] is UnaryOperationToken && !(tokens[i + 1] is VariableToken || tokens[i + 1] is NumberToken
|| tokens[i + 1] is UnaryOperationToken || tokens[i + 1] is OpenParenToken))
throw new InvalidDataException("Unexpected token `{0}` at index {1}".F(tokens[i].Symbol, tokens[i].Index));
// Disallow empty parentheses
if (tokens[i] is OpenParenToken && tokens[i + 1] is CloseParenToken)
throw new InvalidDataException("Empty parenthesis at index {0}".F(tokens[i].Index));
// A variable must be followed by a binary operation or by a closing parenthesis
if (tokens[i] is VariableToken && !(tokens[i + 1] is BinaryOperationToken || tokens[i + 1] is CloseParenToken))
// A variable or number must be followed by a binary operation or by a closing parenthesis
if ((tokens[i] is VariableToken || tokens[i] is NumberToken) && !(tokens[i + 1] is BinaryOperationToken || tokens[i + 1] is CloseParenToken))
throw new InvalidDataException("Missing binary operation at index {0}".F(tokens[i + 1].Index));
}
@@ -147,8 +154,8 @@ namespace OpenRA.Support
for (var i = 1; i < tokens.Count - 1; i++)
{
if (tokens[i] is BinaryOperationToken && (
!(tokens[i - 1] is CloseParenToken || tokens[i - 1] is VariableToken) ||
!(tokens[i + 1] is OpenParenToken || tokens[i + 1] is VariableToken || tokens[i + 1] is UnaryOperationToken)))
!(tokens[i - 1] is CloseParenToken || tokens[i - 1] is VariableToken || tokens[i - 1] is NumberToken) ||
!(tokens[i + 1] is OpenParenToken || tokens[i + 1] is VariableToken || tokens[i + 1] is NumberToken || tokens[i + 1] is UnaryOperationToken)))
throw new InvalidDataException("Unexpected token `{0}` at index `{1}`".F(tokens[i].Symbol, tokens[i].Index));
}
@@ -206,6 +213,27 @@ namespace OpenRA.Support
throw new InvalidDataException("Unexpected character '|' at index {0}".F(start));
}
// Scan forwards until we find an non-digit character
if (c == '-' || char.IsDigit(expression[i]))
{
i++;
for (; i < expression.Length; i++)
{
c = expression[i];
if (!char.IsDigit(c))
{
if (!char.IsWhiteSpace(c) && c != '(' && c != ')' && c != '!' && c != '&' && c != '|' && c != '=' && c != '+')
throw new InvalidDataException("Number and variable merged at index {0}".F(start));
// Put the bad character back for the next parse attempt
i--;
return new NumberToken(start, expression.Substring(start, i - start + 1));
}
}
return new NumberToken(start, expression.Substring(start));
}
// Scan forwards until we find an invalid name character
for (; i < expression.Length; i++)
{
@@ -255,7 +283,7 @@ namespace OpenRA.Support
while (!((temp = s.Pop()) is OpenParenToken))
yield return temp;
}
else if (t is VariableToken)
else if (t is VariableToken || t is NumberToken)
yield return t;
else
{
@@ -286,6 +314,8 @@ namespace OpenRA.Support
ApplyBinaryOperation(s, (x, y) => (y == x) ? 1 : 0);
else if (t is NotToken)
ApplyUnaryOperation(s, x => (x > 0) ? 0 : 1);
else if (t is NumberToken)
s.Push(((NumberToken)t).Value);
else if (t is VariableToken)
s.Push(ParseSymbol((VariableToken)t, symbols));
}

View File

@@ -37,11 +37,27 @@ namespace OpenRA.Test
Assert.True(new ConditionExpression(expression).Evaluate(testValues) > 0, expression);
}
void AssertValue(string expression, int value)
{
Assert.AreEqual(value, new ConditionExpression(expression).Evaluate(testValues), expression);
}
void AssertParseFailure(string expression)
{
Assert.Throws(typeof(InvalidDataException), () => new ConditionExpression(expression).Evaluate(testValues), expression);
}
[TestCase(TestName = "Numbers")]
public void TestNumbers()
{
AssertParseFailure("1a");
AssertValue("0", 0);
AssertValue("1", 1);
AssertValue("12", 12);
AssertValue("-1", -1);
AssertValue("-12", -12);
}
[TestCase(TestName = "AND operation")]
public void TestAnd()
{
@@ -49,6 +65,10 @@ namespace OpenRA.Test
AssertFalse("false && false");
AssertFalse("true && false");
AssertFalse("false && true");
AssertValue("2 && false", 0);
AssertValue("false && 2", 0);
AssertValue("3 && 2", 2);
AssertValue("2 && 3", 3);
}
[TestCase(TestName = "OR operation")]
@@ -58,6 +78,10 @@ namespace OpenRA.Test
AssertFalse("false || false");
AssertTrue("true || false");
AssertTrue("false || true");
AssertValue("2 || false", 2);
AssertValue("false || 2", 2);
AssertValue("3 || 2", 3);
AssertValue("2 || 3", 2);
}
[TestCase(TestName = "Equals operation")]
@@ -67,6 +91,15 @@ namespace OpenRA.Test
AssertTrue("false == false");
AssertFalse("true == false");
AssertFalse("false == true");
AssertTrue("1 == 1");
AssertTrue("0 == 0");
AssertFalse("1 == 0");
AssertTrue("1 == true");
AssertFalse("1 == false");
AssertTrue("0 == false");
AssertFalse("0 == true");
AssertValue("12 == 12", 1);
AssertValue("1 == 12", 0);
}
[TestCase(TestName = "Not-equals operation")]
@@ -76,15 +109,26 @@ namespace OpenRA.Test
AssertFalse("false != false");
AssertTrue("true != false");
AssertTrue("false != true");
AssertValue("1 != 2", 1);
AssertValue("1 != 1", 0);
AssertFalse("1 != true");
AssertFalse("0 != false");
AssertTrue("1 != false");
AssertTrue("0 != true");
}
[TestCase(TestName = "NOT operation")]
public void TestNOT()
{
AssertFalse("!true");
AssertTrue("!false");
AssertTrue("!!true");
AssertFalse("!!false");
AssertValue("!true", 0);
AssertValue("!false", 1);
AssertValue("!!true", 1);
AssertValue("!!false", 0);
AssertValue("!0", 1);
AssertValue("!1", 0);
AssertValue("!5", 0);
AssertValue("!!5", 1);
AssertValue("!-5", 1);
}
[TestCase(TestName = "Precedence")]
@@ -134,7 +178,7 @@ namespace OpenRA.Test
AssertParseFailure("true false");
AssertParseFailure("true & false");
AssertParseFailure("true | false");
AssertParseFailure("true / false");
AssertParseFailure("true : false");
AssertParseFailure("true & false && !");
AssertParseFailure("(true && !)");
AssertParseFailure("&& false");

View File

@@ -17,66 +17,69 @@
^GainsExperience:
GainsExperience:
Conditions:
200: rank-veteran-1
400: rank-veteran-2
800: rank-veteran-3
1600: rank-elite
200: rank-veteran
400: rank-veteran
800: rank-veteran
1600: rank-veteran
GrantCondition@RANK-ELITE:
RequiresCondition: rank-veteran == 4
Condition: rank-elite
DamageMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 95
DamageMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 90
DamageMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 85
DamageMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 75
FirepowerMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 105
FirepowerMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 110
FirepowerMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 120
FirepowerMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 130
SpeedMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 105
SpeedMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 110
SpeedMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 120
SpeedMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 140
ReloadDelayMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 95
ReloadDelayMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 90
ReloadDelayMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 85
ReloadDelayMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 75
InaccuracyMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 90
InaccuracyMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 80
InaccuracyMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 70
InaccuracyMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
@@ -92,21 +95,21 @@
Sequence: rank-veteran-1
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
ZOffset: 256
WithDecoration@RANK-2:
Image: rank
Sequence: rank-veteran-2
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
ZOffset: 256
WithDecoration@RANK-3:
Image: rank
Sequence: rank-veteran-3
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
ZOffset: 256
WithDecoration@RANK-ELITE:
Image: rank

View File

@@ -17,66 +17,69 @@
^GainsExperience:
GainsExperience:
Conditions:
200: rank-veteran-1
400: rank-veteran-2
800: rank-veteran-3
1600: rank-elite
200: rank-veteran
400: rank-veteran
800: rank-veteran
1600: rank-veteran
GrantCondition@RANK-ELITE:
RequiresCondition: rank-veteran == 4
Condition: rank-elite
DamageMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 96
DamageMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 92
DamageMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 88
DamageMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 80
FirepowerMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 105
FirepowerMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 110
FirepowerMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 115
FirepowerMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 125
SpeedMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 105
SpeedMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 110
SpeedMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 115
SpeedMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 125
ReloadDelayMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 96
ReloadDelayMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 92
ReloadDelayMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 88
ReloadDelayMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 80
InaccuracyMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 90
InaccuracyMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 80
InaccuracyMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 70
InaccuracyMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
@@ -93,21 +96,21 @@
Sequence: rank-veteran-1
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
ZOffset: 256
WithDecoration@RANK-2:
Image: rank
Sequence: rank-veteran-2
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
ZOffset: 256
WithDecoration@RANK-3:
Image: rank
Sequence: rank-veteran-3
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
ZOffset: 256
WithDecoration@RANK-ELITE:
Image: rank

View File

@@ -59,14 +59,11 @@ V05:
DOG:
# HACK: Disable experience without killing the linter
-GainsExperience:
ExternalCondition@RANK-VETERAN-1:
Condition: rank-veteran-1
ExternalCondition@RANK-VETERAN-2:
Condition: rank-veteran-2
ExternalCondition@RANK-VETERAN-3:
Condition: rank-veteran-3
ExternalCondition@RANK-VETERAN:
Condition: rank-veteran
ExternalCondition@RANK-ELITE:
Condition: rank-elite
-GrantCondition@RANK-ELITE:
SPY:
Mobile:

View File

@@ -16,66 +16,69 @@
^GainsExperience:
GainsExperience:
Conditions:
200: rank-veteran-1
400: rank-veteran-2
800: rank-veteran-3
1600: rank-elite
200: rank-veteran
400: rank-veteran
800: rank-veteran
1600: rank-veteran
GrantCondition@RANK-ELITE:
RequiresCondition: rank-veteran == 4
Condition: rank-elite
DamageMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 95
DamageMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 90
DamageMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 85
DamageMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 75
FirepowerMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 105
FirepowerMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 110
FirepowerMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 120
FirepowerMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 130
SpeedMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 105
SpeedMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 110
SpeedMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 120
SpeedMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 140
ReloadDelayMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 95
ReloadDelayMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 90
ReloadDelayMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 85
ReloadDelayMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
Modifier: 75
InaccuracyMultiplier@RANK-1:
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
Modifier: 90
InaccuracyMultiplier@RANK-2:
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
Modifier: 80
InaccuracyMultiplier@RANK-3:
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
Modifier: 70
InaccuracyMultiplier@RANK-ELITE:
RequiresCondition: rank-elite
@@ -91,21 +94,21 @@
Sequence: rank-veteran-1
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-1 && !rank-veteran-2
RequiresCondition: rank-veteran == 1
ZOffset: 256
WithDecoration@RANK-2:
Image: rank
Sequence: rank-veteran-2
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-2 && !rank-veteran-3
RequiresCondition: rank-veteran == 2
ZOffset: 256
WithDecoration@RANK-3:
Image: rank
Sequence: rank-veteran-3
Palette: effect
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran-3 && !rank-elite
RequiresCondition: rank-veteran == 3
ZOffset: 256
WithDecoration@RANK-ELITE:
Image: rank

View File

@@ -18,28 +18,34 @@
^GainsExperience:
GainsExperience:
Conditions:
500: rank-veteran
1000: rank-elite
500: rank
1000: rank
GrantCondition@RANK-VETERAN:
RequiresCondition: rank == 1
Condition: rank-veteran
GrantCondition@RANK-ELITE:
RequiresCondition: rank == 2
Condition: rank-elite
FirepowerMultiplier@VETERAN:
RequiresCondition: rank-veteran && !rank-elite
RequiresCondition: rank-veteran
Modifier: 110
FirepowerMultiplier@ELITE:
RequiresCondition: rank-elite
Modifier: 130
DamageMultiplier@VETERAN:
RequiresCondition: rank-veteran && !rank-elite
RequiresCondition: rank-veteran
Modifier: 90
DamageMultiplier@ELITE:
RequiresCondition: rank-elite
Modifier: 75
SpeedMultiplier@VETERAN:
RequiresCondition: rank-veteran && !rank-elite
RequiresCondition: rank-veteran
Modifier: 120
SpeedMultiplier@ELITE:
RequiresCondition: rank-elite
Modifier: 140
ReloadDelayMultiplier@VETERAN:
RequiresCondition: rank-veteran && !rank-elite
RequiresCondition: rank-veteran
Modifier: 90
ReloadDelayMultiplier@ELITE:
RequiresCondition: rank-elite
@@ -55,7 +61,7 @@
Sequence: veteran
Palette: ra
ReferencePoint: Bottom, Right
RequiresCondition: rank-veteran && !rank-elite
RequiresCondition: rank-veteran
ZOffset: 256
WithDecoration@ELITE:
Image: rank