Added (Boolean|Integer)Expression subclasses of VariableExpression
This commit is contained in:
@@ -398,13 +398,29 @@ namespace OpenRA
|
|||||||
|
|
||||||
return InvalidValueAction(value, fieldType, fieldName);
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
}
|
}
|
||||||
else if (fieldType == typeof(VariableExpression))
|
else if (fieldType == typeof(BooleanExpression))
|
||||||
{
|
{
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new VariableExpression(value);
|
return new BooleanExpression(value);
|
||||||
|
}
|
||||||
|
catch (InvalidDataException e)
|
||||||
|
{
|
||||||
|
throw new YamlException(e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return InvalidValueAction(value, fieldType, fieldName);
|
||||||
|
}
|
||||||
|
else if (fieldType == typeof(IntegerExpression))
|
||||||
|
{
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new IntegerExpression(value);
|
||||||
}
|
}
|
||||||
catch (InvalidDataException e)
|
catch (InvalidDataException e)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,14 +18,12 @@ using Expressions = System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace OpenRA.Support
|
namespace OpenRA.Support
|
||||||
{
|
{
|
||||||
public class VariableExpression
|
public abstract class VariableExpression
|
||||||
{
|
{
|
||||||
public readonly string Expression;
|
public readonly string Expression;
|
||||||
readonly HashSet<string> variables = new HashSet<string>();
|
readonly HashSet<string> variables = new HashSet<string>();
|
||||||
public IEnumerable<string> Variables { get { return variables; } }
|
public IEnumerable<string> Variables { get { return variables; } }
|
||||||
|
|
||||||
readonly Func<IReadOnlyDictionary<string, int>, int> asFunction;
|
|
||||||
|
|
||||||
enum CharClass { Whitespace, Operator, Mixed, Id, Digit }
|
enum CharClass { Whitespace, Operator, Mixed, Id, Digit }
|
||||||
|
|
||||||
static CharClass CharClassOf(char c)
|
static CharClass CharClassOf(char c)
|
||||||
@@ -528,12 +526,16 @@ namespace OpenRA.Support
|
|||||||
public VariableExpression(string expression)
|
public VariableExpression(string expression)
|
||||||
{
|
{
|
||||||
Expression = expression;
|
Expression = expression;
|
||||||
|
}
|
||||||
|
|
||||||
|
Expression Build(ExpressionType resultType)
|
||||||
|
{
|
||||||
var tokens = new List<Token>();
|
var tokens = new List<Token>();
|
||||||
var currentOpeners = new Stack<Token>();
|
var currentOpeners = new Stack<Token>();
|
||||||
Token lastToken = null;
|
Token lastToken = null;
|
||||||
for (var i = 0;;)
|
for (var i = 0;;)
|
||||||
{
|
{
|
||||||
var token = Token.GetNext(expression, ref i, lastToken != null ? lastToken.Type : TokenType.Invalid);
|
var token = Token.GetNext(Expression, ref i, lastToken != null ? lastToken.Type : TokenType.Invalid);
|
||||||
if (token == null)
|
if (token == null)
|
||||||
{
|
{
|
||||||
// Sanity check parsed tree
|
// Sanity check parsed tree
|
||||||
@@ -591,7 +593,20 @@ namespace OpenRA.Support
|
|||||||
if (currentOpeners.Count > 0)
|
if (currentOpeners.Count > 0)
|
||||||
throw new InvalidDataException("Unclosed opening parenthesis at index {0}".F(currentOpeners.Peek().Index));
|
throw new InvalidDataException("Unclosed opening parenthesis at index {0}".F(currentOpeners.Peek().Index));
|
||||||
|
|
||||||
asFunction = new Compiler().Compile(ToPostfix(tokens).ToArray());
|
return new Compiler().Build(ToPostfix(tokens).ToArray(), resultType);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Func<IReadOnlyDictionary<string, int>, T> Compile<T>()
|
||||||
|
{
|
||||||
|
ExpressionType resultType;
|
||||||
|
if (typeof(T) == typeof(int))
|
||||||
|
resultType = ExpressionType.Int;
|
||||||
|
else if (typeof(T) == typeof(bool))
|
||||||
|
resultType = ExpressionType.Bool;
|
||||||
|
else
|
||||||
|
throw new InvalidCastException("Variable expressions can only be int or bool.");
|
||||||
|
|
||||||
|
return Expressions.Expression.Lambda<Func<IReadOnlyDictionary<string, int>, T>>(Build(resultType), SymbolsParam).Compile();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ParseSymbol(string symbol, IReadOnlyDictionary<string, int> symbols)
|
static int ParseSymbol(string symbol, IReadOnlyDictionary<string, int> symbols)
|
||||||
@@ -712,7 +727,7 @@ namespace OpenRA.Support
|
|||||||
{
|
{
|
||||||
readonly AstStack ast = new AstStack();
|
readonly AstStack ast = new AstStack();
|
||||||
|
|
||||||
public Func<IReadOnlyDictionary<string, int>, int> Compile(Token[] postfix)
|
public Expression Build(Token[] postfix, ExpressionType resultType)
|
||||||
{
|
{
|
||||||
foreach (var t in postfix)
|
foreach (var t in postfix)
|
||||||
{
|
{
|
||||||
@@ -877,10 +892,34 @@ namespace OpenRA.Support
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Expressions.Expression.Lambda<Func<IReadOnlyDictionary<string, int>, int>>(
|
return ast.Pop(resultType);
|
||||||
ast.Pop(ExpressionType.Int), SymbolsParam).Compile();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BooleanExpression : VariableExpression
|
||||||
|
{
|
||||||
|
readonly Func<IReadOnlyDictionary<string, int>, bool> asFunction;
|
||||||
|
|
||||||
|
public BooleanExpression(string expression) : base(expression)
|
||||||
|
{
|
||||||
|
asFunction = Compile<bool>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Evaluate(IReadOnlyDictionary<string, int> symbols)
|
||||||
|
{
|
||||||
|
return asFunction(symbols);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class IntegerExpression : VariableExpression
|
||||||
|
{
|
||||||
|
readonly Func<IReadOnlyDictionary<string, int>, int> asFunction;
|
||||||
|
|
||||||
|
public IntegerExpression(string expression) : base(expression)
|
||||||
|
{
|
||||||
|
asFunction = Compile<int>();
|
||||||
|
}
|
||||||
|
|
||||||
public int Evaluate(IReadOnlyDictionary<string, int> symbols)
|
public int Evaluate(IReadOnlyDictionary<string, int> symbols)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,13 +29,13 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
if (typeof(IEnumerable<string>).IsAssignableFrom(type))
|
if (typeof(IEnumerable<string>).IsAssignableFrom(type))
|
||||||
return fieldInfo.GetValue(ruleInfo) as IEnumerable<string>;
|
return fieldInfo.GetValue(ruleInfo) as IEnumerable<string>;
|
||||||
|
|
||||||
if (type == typeof(VariableExpression))
|
if (type == typeof(BooleanExpression) || type == typeof(IntegerExpression))
|
||||||
{
|
{
|
||||||
var expr = (VariableExpression)fieldInfo.GetValue(ruleInfo);
|
var expr = (VariableExpression)fieldInfo.GetValue(ruleInfo);
|
||||||
return expr != null ? expr.Variables : Enumerable.Empty<string>();
|
return expr != null ? expr.Variables : Enumerable.Empty<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException("Bad type for reference on {0}.{1}. Supported types: string, IEnumerable<string>, BooleanExpression"
|
throw new InvalidOperationException("Bad type for reference on {0}.{1}. Supported types: string, IEnumerable<string>, BooleanExpression, IntegerExpression"
|
||||||
.F(ruleInfo.GetType().Name, fieldInfo.Name));
|
.F(ruleInfo.GetType().Name, fieldInfo.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,13 +48,13 @@ namespace OpenRA.Mods.Common.Lint
|
|||||||
if (typeof(IEnumerable).IsAssignableFrom(type))
|
if (typeof(IEnumerable).IsAssignableFrom(type))
|
||||||
return (IEnumerable<string>)propertyInfo.GetValue(ruleInfo);
|
return (IEnumerable<string>)propertyInfo.GetValue(ruleInfo);
|
||||||
|
|
||||||
if (type == typeof(VariableExpression))
|
if (type == typeof(BooleanExpression) || type == typeof(IntegerExpression))
|
||||||
{
|
{
|
||||||
var expr = (VariableExpression)propertyInfo.GetValue(ruleInfo);
|
var expr = (VariableExpression)propertyInfo.GetValue(ruleInfo);
|
||||||
return expr != null ? expr.Variables : Enumerable.Empty<string>();
|
return expr != null ? expr.Variables : Enumerable.Empty<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidOperationException("Bad type for reference on {0}.{1}. Supported types: string, IEnumerable<string>, BooleanExpression"
|
throw new InvalidOperationException("Bad type for reference on {0}.{1}. Supported types: string, IEnumerable<string>, BooleanExpression, IntegerExpression"
|
||||||
.F(ruleInfo.GetType().Name, propertyInfo.Name));
|
.F(ruleInfo.GetType().Name, propertyInfo.Name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
[ConsumedConditionReference]
|
[ConsumedConditionReference]
|
||||||
[Desc("Boolean expression defining the condition to enable this trait.")]
|
[Desc("Boolean expression defining the condition to enable this trait.")]
|
||||||
public readonly VariableExpression RequiresCondition = null;
|
public readonly BooleanExpression RequiresCondition = null;
|
||||||
|
|
||||||
public abstract object Create(ActorInitializer init);
|
public abstract object Create(ActorInitializer init);
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
|
|
||||||
public virtual void RulesetLoaded(Ruleset rules, ActorInfo ai)
|
public virtual void RulesetLoaded(Ruleset rules, ActorInfo ai)
|
||||||
{
|
{
|
||||||
EnabledByDefault = RequiresCondition != null ? RequiresCondition.Evaluate(NoConditions) > 0 : true;
|
EnabledByDefault = RequiresCondition == null || RequiresCondition.Evaluate(NoConditions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var wasDisabled = IsTraitDisabled;
|
var wasDisabled = IsTraitDisabled;
|
||||||
IsTraitDisabled = Info.RequiresCondition.Evaluate(conditions) <= 0;
|
IsTraitDisabled = !Info.RequiresCondition.Evaluate(conditions);
|
||||||
|
|
||||||
if (IsTraitDisabled != wasDisabled)
|
if (IsTraitDisabled != wasDisabled)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
[Desc("Requirements for accepting a plug type.",
|
[Desc("Requirements for accepting a plug type.",
|
||||||
"Key is the plug type that the requirements applies to.",
|
"Key is the plug type that the requirements applies to.",
|
||||||
"Value is the condition expression defining the requirements to place the plug.")]
|
"Value is the condition expression defining the requirements to place the plug.")]
|
||||||
public readonly Dictionary<string, VariableExpression> Requirements = new Dictionary<string, VariableExpression>();
|
public readonly Dictionary<string, BooleanExpression> Requirements = new Dictionary<string, BooleanExpression>();
|
||||||
|
|
||||||
[GrantedConditionReference]
|
[GrantedConditionReference]
|
||||||
public IEnumerable<string> LinterConditions { get { return Conditions.Values; } }
|
public IEnumerable<string> LinterConditions { get { return Conditions.Values; } }
|
||||||
@@ -119,7 +119,7 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
void IConditionConsumer.ConditionsChanged(Actor self, IReadOnlyDictionary<string, int> conditions)
|
void IConditionConsumer.ConditionsChanged(Actor self, IReadOnlyDictionary<string, int> conditions)
|
||||||
{
|
{
|
||||||
foreach (var req in Info.Requirements)
|
foreach (var req in Info.Requirements)
|
||||||
plugTypesAvailability[req.Key] = req.Value.Evaluate(conditions) != 0;
|
plugTypesAvailability[req.Key] = req.Value.Evaluate(conditions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,28 +29,28 @@ namespace OpenRA.Test
|
|||||||
|
|
||||||
void AssertFalse(string expression)
|
void AssertFalse(string expression)
|
||||||
{
|
{
|
||||||
Assert.False(new VariableExpression(expression).Evaluate(testValues) > 0, expression);
|
Assert.False(new BooleanExpression(expression).Evaluate(testValues), expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssertTrue(string expression)
|
void AssertTrue(string expression)
|
||||||
{
|
{
|
||||||
Assert.True(new VariableExpression(expression).Evaluate(testValues) > 0, expression);
|
Assert.True(new BooleanExpression(expression).Evaluate(testValues), expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssertValue(string expression, int value)
|
void AssertValue(string expression, int value)
|
||||||
{
|
{
|
||||||
Assert.AreEqual(value, new VariableExpression(expression).Evaluate(testValues), expression);
|
Assert.AreEqual(value, new IntegerExpression(expression).Evaluate(testValues), expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssertParseFailure(string expression)
|
void AssertParseFailure(string expression)
|
||||||
{
|
{
|
||||||
Assert.Throws(typeof(InvalidDataException), () => new VariableExpression(expression).Evaluate(testValues), expression);
|
Assert.Throws(typeof(InvalidDataException), () => new IntegerExpression(expression).Evaluate(testValues), expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssertParseFailure(string expression, string errorMessage)
|
void AssertParseFailure(string expression, string errorMessage)
|
||||||
{
|
{
|
||||||
var actualErrorMessage = Assert.Throws(typeof(InvalidDataException),
|
var actualErrorMessage = Assert.Throws(typeof(InvalidDataException),
|
||||||
() => new VariableExpression(expression).Evaluate(testValues),
|
() => new IntegerExpression(expression).Evaluate(testValues),
|
||||||
expression).Message;
|
expression).Message;
|
||||||
Assert.AreEqual(errorMessage, actualErrorMessage, expression + " ===> " + actualErrorMessage);
|
Assert.AreEqual(errorMessage, actualErrorMessage, expression + " ===> " + actualErrorMessage);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user