test
This commit is contained in:
@@ -0,0 +1 @@
|
||||
TARGET_INCLUDE_ALL
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5029d678f341c40e888b037abf7f6672
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fa1a96c0a3f154fbba933b74f7414a76
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,38 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class ControlConnection : UnitConnection<ControlOutput, ControlInput>, IUnitConnection
|
||||
{
|
||||
[Obsolete(Serialization.ConstructorWarning)]
|
||||
public ControlConnection() : base() { }
|
||||
|
||||
public ControlConnection(ControlOutput source, ControlInput destination) : base(source, destination)
|
||||
{
|
||||
if (source.hasValidConnection)
|
||||
{
|
||||
throw new InvalidConnectionException("Control output ports do not support multiple connections.");
|
||||
}
|
||||
}
|
||||
|
||||
#region Ports
|
||||
|
||||
public override ControlOutput source => sourceUnit.controlOutputs[sourceKey];
|
||||
|
||||
public override ControlInput destination => destinationUnit.controlInputs[destinationKey];
|
||||
|
||||
IUnitOutputPort IConnection<IUnitOutputPort, IUnitInputPort>.source => source;
|
||||
|
||||
IUnitInputPort IConnection<IUnitOutputPort, IUnitInputPort>.destination => destination;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dependencies
|
||||
|
||||
public override bool sourceExists => sourceUnit.controlOutputs.Contains(sourceKey);
|
||||
|
||||
public override bool destinationExists => destinationUnit.controlInputs.Contains(destinationKey);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b71fe733d92c1489cb3c89d5841ea222
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,24 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/* Implementation notes:
|
||||
*
|
||||
* IUnitConnection cannot implement IConnection<IUnitOutputPort, IUnitInputPort> because
|
||||
* the compiler will be overly strict and complain that types may unify.
|
||||
* https://stackoverflow.com/questions/7664790
|
||||
*
|
||||
* Additionally, using contravariance for the type parameters will compile but
|
||||
* fail at runtime, because Unity's Mono version does not properly support variance,
|
||||
* even though it's supposed to be a CLR feature since .NET 1.1.
|
||||
* https://forum.unity3d.com/threads/398665/
|
||||
* https://github.com/jacobdufault/fullinspector/issues/9
|
||||
*
|
||||
* Therefore, the only remaining solution is to re-implement source and destination
|
||||
* manually. This introduces ambiguity, as the compiler will warn, but it's fine
|
||||
* if the implementations point both members to the same actual object.
|
||||
*/
|
||||
|
||||
public interface IUnitConnection : IConnection<IUnitOutputPort, IUnitInputPort>, IGraphElementWithDebugData
|
||||
{
|
||||
new FlowGraph graph { get; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dc809ec21d2bf44b983265840a5ed2f4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,9 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IUnitConnectionDebugData : IGraphElementDebugData
|
||||
{
|
||||
int lastInvokeFrame { get; set; }
|
||||
|
||||
float lastInvokeTime { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9239efc28f45f4c848294b936db9bfa0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,4 @@
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public interface IUnitRelation : IConnection<IUnitPort, IUnitPort> { }
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 20177830b472f40faa7374d0861ddad0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class InvalidConnection : UnitConnection<IUnitOutputPort, IUnitInputPort>, IUnitConnection
|
||||
{
|
||||
[Obsolete(Serialization.ConstructorWarning)]
|
||||
public InvalidConnection() : base() { }
|
||||
|
||||
public InvalidConnection(IUnitOutputPort source, IUnitInputPort destination) : base(source, destination) { }
|
||||
|
||||
public override void AfterRemove()
|
||||
{
|
||||
base.AfterRemove();
|
||||
source.unit.RemoveUnconnectedInvalidPorts();
|
||||
destination.unit.RemoveUnconnectedInvalidPorts();
|
||||
}
|
||||
|
||||
#region Ports
|
||||
|
||||
public override IUnitOutputPort source => sourceUnit.outputs.Single(p => p.key == sourceKey);
|
||||
|
||||
public override IUnitInputPort destination => destinationUnit.inputs.Single(p => p.key == destinationKey);
|
||||
|
||||
public IUnitOutputPort validSource => sourceUnit.validOutputs.Single(p => p.key == sourceKey);
|
||||
|
||||
public IUnitInputPort validDestination => destinationUnit.validInputs.Single(p => p.key == destinationKey);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dependencies
|
||||
|
||||
public override bool sourceExists => sourceUnit.outputs.Any(p => p.key == sourceKey);
|
||||
|
||||
public override bool destinationExists => destinationUnit.inputs.Any(p => p.key == destinationKey);
|
||||
|
||||
public bool validSourceExists => sourceUnit.validOutputs.Any(p => p.key == sourceKey);
|
||||
|
||||
public bool validDestinationExists => destinationUnit.validInputs.Any(p => p.key == destinationKey);
|
||||
|
||||
public override bool HandleDependencies()
|
||||
{
|
||||
// Replace the invalid connection with a valid connection if it can be created instead.
|
||||
if (validSourceExists && validDestinationExists && validSource.CanValidlyConnectTo(validDestination))
|
||||
{
|
||||
validSource.ValidlyConnectTo(validDestination);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add the invalid ports to the nodes if need be
|
||||
if (!sourceExists)
|
||||
{
|
||||
sourceUnit.invalidOutputs.Add(new InvalidOutput(sourceKey));
|
||||
}
|
||||
|
||||
if (!destinationExists)
|
||||
{
|
||||
destinationUnit.invalidInputs.Add(new InvalidInput(destinationKey));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f564c98f800f2453595d4c4e261215ab
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,165 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/* Implementation note:
|
||||
* Using an abstract base class works as a type unification workaround.
|
||||
* https://stackoverflow.com/questions/22721763
|
||||
* https://stackoverflow.com/a/7664919
|
||||
*
|
||||
* However, this forces us to use concrete classes for connections
|
||||
* instead of interfaces. In other words, no IControlConnection / IValueConnection.
|
||||
* If we did use interfaces, there would be ambiguity that needs to be resolved
|
||||
* at every reference to the source or destination.
|
||||
*
|
||||
* However, using a disambiguator hack seems to confuse even recent Mono runtime versions of Unity
|
||||
* and breaks its vtable. Sometimes, method pointers are just plain wrong.
|
||||
* I'm guessing this is specifically due to InvalidConnection, which actually
|
||||
* does unify the types; what the C# warning warned about.
|
||||
* https://stackoverflow.com/q/50051657/154502
|
||||
*
|
||||
* THEREFORE, IUnitConnection has to be implemented at the concrete class level,
|
||||
* because at that point the type unification warning is moot, because the type arguments are
|
||||
* provided.
|
||||
*/
|
||||
|
||||
public abstract class UnitConnection<TSourcePort, TDestinationPort> : GraphElement<FlowGraph>, IConnection<TSourcePort, TDestinationPort>
|
||||
where TSourcePort : class, IUnitOutputPort
|
||||
where TDestinationPort : class, IUnitInputPort
|
||||
{
|
||||
[Obsolete(Serialization.ConstructorWarning)]
|
||||
protected UnitConnection() { }
|
||||
|
||||
protected UnitConnection(TSourcePort source, TDestinationPort destination)
|
||||
{
|
||||
Ensure.That(nameof(source)).IsNotNull(source);
|
||||
Ensure.That(nameof(destination)).IsNotNull(destination);
|
||||
|
||||
if (source.unit.graph != destination.unit.graph)
|
||||
{
|
||||
throw new NotSupportedException("Cannot create connections across graphs.");
|
||||
}
|
||||
|
||||
if (source.unit == destination.unit)
|
||||
{
|
||||
throw new InvalidConnectionException("Cannot create connections on the same unit.");
|
||||
}
|
||||
|
||||
sourceUnit = source.unit;
|
||||
sourceKey = source.key;
|
||||
destinationUnit = destination.unit;
|
||||
destinationKey = destination.key;
|
||||
}
|
||||
|
||||
public virtual IGraphElementDebugData CreateDebugData()
|
||||
{
|
||||
return new UnitConnectionDebugData();
|
||||
}
|
||||
|
||||
#region Ports
|
||||
|
||||
[Serialize]
|
||||
protected IUnit sourceUnit { get; private set; }
|
||||
|
||||
[Serialize]
|
||||
protected string sourceKey { get; private set; }
|
||||
|
||||
[Serialize]
|
||||
protected IUnit destinationUnit { get; private set; }
|
||||
|
||||
[Serialize]
|
||||
protected string destinationKey { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
public abstract TSourcePort source { get; }
|
||||
|
||||
[DoNotSerialize]
|
||||
public abstract TDestinationPort destination { get; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dependencies
|
||||
|
||||
public override int dependencyOrder => 1;
|
||||
|
||||
public abstract bool sourceExists { get; }
|
||||
|
||||
public abstract bool destinationExists { get; }
|
||||
|
||||
protected void CopyFrom(UnitConnection<TSourcePort, TDestinationPort> source)
|
||||
{
|
||||
base.CopyFrom(source);
|
||||
}
|
||||
|
||||
public override bool HandleDependencies()
|
||||
{
|
||||
// Replace the connection with an invalid connection if the ports are either missing or incompatible.
|
||||
// If the ports are missing, create invalid ports if required.
|
||||
|
||||
var valid = true;
|
||||
IUnitOutputPort source;
|
||||
IUnitInputPort destination;
|
||||
|
||||
if (!sourceExists)
|
||||
{
|
||||
if (!sourceUnit.invalidOutputs.Contains(sourceKey))
|
||||
{
|
||||
sourceUnit.invalidOutputs.Add(new InvalidOutput(sourceKey));
|
||||
}
|
||||
|
||||
source = sourceUnit.invalidOutputs[sourceKey];
|
||||
valid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
source = this.source;
|
||||
}
|
||||
|
||||
if (!destinationExists)
|
||||
{
|
||||
if (!destinationUnit.invalidInputs.Contains(destinationKey))
|
||||
{
|
||||
destinationUnit.invalidInputs.Add(new InvalidInput(destinationKey));
|
||||
}
|
||||
|
||||
destination = destinationUnit.invalidInputs[destinationKey];
|
||||
valid = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
destination = this.destination;
|
||||
}
|
||||
|
||||
if (!source.CanValidlyConnectTo(destination))
|
||||
{
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (!valid && source.CanInvalidlyConnectTo(destination))
|
||||
{
|
||||
source.InvalidlyConnectTo(destination);
|
||||
|
||||
// Silence this warning if a unit with a missing type is involved (as it will not have any defined ports).
|
||||
// This is to avoid drowning users in warning and error messages if a unit's script goes missing.
|
||||
if (source.unit.GetType() != typeof(MissingType) && destination.unit.GetType() != typeof(MissingType))
|
||||
{
|
||||
Debug.LogWarning($"Could not load connection between '{source.key}' of '{sourceUnit}' and '{destination.key}' of '{destinationUnit}'.");
|
||||
}
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Analytics
|
||||
|
||||
public override AnalyticsIdentifier GetAnalyticsIdentifier()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a8cc9b6e18b84c82a16ca1d5ae11643
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public class UnitConnectionDebugData : IUnitConnectionDebugData
|
||||
{
|
||||
public int lastInvokeFrame { get; set; }
|
||||
|
||||
public float lastInvokeTime { get; set; }
|
||||
|
||||
public Exception runtimeException { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5b33449365cf844e986fad5c887be657
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class UnitRelation : IUnitRelation
|
||||
{
|
||||
public UnitRelation(IUnitPort source, IUnitPort destination)
|
||||
{
|
||||
Ensure.That(nameof(source)).IsNotNull(source);
|
||||
Ensure.That(nameof(destination)).IsNotNull(destination);
|
||||
|
||||
if (source.unit != destination.unit)
|
||||
{
|
||||
throw new NotSupportedException("Cannot create relations across nodes.");
|
||||
}
|
||||
|
||||
this.source = source;
|
||||
this.destination = destination;
|
||||
}
|
||||
|
||||
public IUnitPort source { get; }
|
||||
|
||||
public IUnitPort destination { get; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3617e416d65b4e44842280c042df8a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class ValueConnection : UnitConnection<ValueOutput, ValueInput>, IUnitConnection
|
||||
{
|
||||
public class DebugData : UnitConnectionDebugData
|
||||
{
|
||||
public object lastValue { get; set; }
|
||||
|
||||
public bool assignedLastValue { get; set; }
|
||||
}
|
||||
|
||||
public override IGraphElementDebugData CreateDebugData()
|
||||
{
|
||||
return new DebugData();
|
||||
}
|
||||
|
||||
[Obsolete(Serialization.ConstructorWarning)]
|
||||
public ValueConnection() : base() { }
|
||||
|
||||
public ValueConnection(ValueOutput source, ValueInput destination) : base(source, destination)
|
||||
{
|
||||
if (destination.hasValidConnection)
|
||||
{
|
||||
throw new InvalidConnectionException("Value input ports do not support multiple connections.");
|
||||
}
|
||||
|
||||
if (!source.type.IsConvertibleTo(destination.type, false))
|
||||
{
|
||||
throw new InvalidConnectionException($"Cannot convert from '{source.type}' to '{destination.type}'.");
|
||||
}
|
||||
}
|
||||
|
||||
#region Ports
|
||||
|
||||
public override ValueOutput source => sourceUnit.valueOutputs[sourceKey];
|
||||
|
||||
public override ValueInput destination => destinationUnit.valueInputs[destinationKey];
|
||||
|
||||
IUnitOutputPort IConnection<IUnitOutputPort, IUnitInputPort>.source => source;
|
||||
|
||||
IUnitInputPort IConnection<IUnitOutputPort, IUnitInputPort>.destination => destination;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Dependencies
|
||||
|
||||
public override bool sourceExists => sourceUnit.valueOutputs.Contains(sourceKey);
|
||||
|
||||
public override bool destinationExists => destinationUnit.valueInputs.Contains(destinationKey);
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b74709ab7c4674a84903b44120fcb9fb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e688b34379ad4462b284ae6a0cc3cdc
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fac4026eb26ea42c29c85fec8007face
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,46 @@
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public class BinaryExpression : LogicalExpression
|
||||
{
|
||||
public BinaryExpression(BinaryExpressionType type, LogicalExpression leftExpression, LogicalExpression rightExpression)
|
||||
{
|
||||
Type = type;
|
||||
LeftExpression = leftExpression;
|
||||
RightExpression = rightExpression;
|
||||
}
|
||||
|
||||
public LogicalExpression LeftExpression { get; set; }
|
||||
|
||||
public LogicalExpression RightExpression { get; set; }
|
||||
|
||||
public BinaryExpressionType Type { get; set; }
|
||||
|
||||
public override void Accept(LogicalExpressionVisitor visitor)
|
||||
{
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
public enum BinaryExpressionType
|
||||
{
|
||||
And,
|
||||
Or,
|
||||
NotEqual,
|
||||
LesserOrEqual,
|
||||
GreaterOrEqual,
|
||||
Lesser,
|
||||
Greater,
|
||||
Equal,
|
||||
Minus,
|
||||
Plus,
|
||||
Modulo,
|
||||
Div,
|
||||
Times,
|
||||
BitwiseOr,
|
||||
BitwiseAnd,
|
||||
BitwiseXOr,
|
||||
LeftShift,
|
||||
RightShift,
|
||||
Unknown
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ace4e9bf94df345f183dcb34a039e1d8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,4 @@
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public delegate void EvaluateFunctionHandler(Flow flow, string name, FunctionArgs args);
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8b54261ed2a8497ebdd20f7ddbe1e6f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,4 @@
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public delegate void EvaluateParameterHandler(Flow flow, string name, ParameterArgs args);
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6180c9bbe40b44d45980f2b1a0325e58
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public sealed class EvaluationException : ApplicationException
|
||||
{
|
||||
public EvaluationException(string message) : base(message) { }
|
||||
|
||||
public EvaluationException(string message, Exception innerException) : base(message, innerException) { }
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6499d38cb236f4d369ab2ce0e0585ecc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,37 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides enumerated values to use to set evaluation options.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum EvaluateOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that no options are set.
|
||||
/// </summary>
|
||||
None = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Specifies case-insensitive matching.
|
||||
/// </summary>
|
||||
IgnoreCase = 2,
|
||||
|
||||
/// <summary>
|
||||
/// No-cache mode. Ingores any pre-compiled expression in the cache.
|
||||
/// </summary>
|
||||
NoCache = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Treats parameters as arrays and result a set of results.
|
||||
/// </summary>
|
||||
IterateParameters = 8,
|
||||
|
||||
/// <summary>
|
||||
/// When using Round(), if a number is halfway between two others, it is rounded toward the nearest number that is away
|
||||
/// from zero.
|
||||
/// </summary>
|
||||
RoundAwayFromZero = 16
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b546fea2e0eea4ab98c47948cb30b03c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,450 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public class EvaluationVisitor : LogicalExpressionVisitor
|
||||
{
|
||||
public EvaluationVisitor(Flow flow, EvaluateOptions options)
|
||||
{
|
||||
this.flow = flow;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public event EvaluateFunctionHandler EvaluateFunction;
|
||||
|
||||
public event EvaluateParameterHandler EvaluateParameter;
|
||||
|
||||
private readonly Flow flow;
|
||||
|
||||
private readonly EvaluateOptions options;
|
||||
|
||||
private bool IgnoreCase => options.HasFlag(EvaluateOptions.IgnoreCase);
|
||||
|
||||
public object Result { get; private set; }
|
||||
|
||||
public Dictionary<string, object> Parameters { get; set; }
|
||||
|
||||
private object Evaluate(LogicalExpression expression)
|
||||
{
|
||||
expression.Accept(this);
|
||||
return Result;
|
||||
}
|
||||
|
||||
public override void Visit(TernaryExpression ternary)
|
||||
{
|
||||
// Evaluates the left expression and saves the value
|
||||
ternary.LeftExpression.Accept(this);
|
||||
|
||||
var left = ConversionUtility.Convert<bool>(Result);
|
||||
|
||||
if (left)
|
||||
{
|
||||
ternary.MiddleExpression.Accept(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
ternary.RightExpression.Accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Visit(BinaryExpression binary)
|
||||
{
|
||||
// Simulate Lazy<Func<>> behavior for late evaluation
|
||||
object leftValue = null;
|
||||
Func<object> left = () =>
|
||||
{
|
||||
if (leftValue == null)
|
||||
{
|
||||
binary.LeftExpression.Accept(this);
|
||||
leftValue = Result;
|
||||
}
|
||||
return leftValue;
|
||||
};
|
||||
|
||||
// Simulate Lazy<Func<>> behavior for late evaluation
|
||||
object rightValue = null;
|
||||
Func<object> right = () =>
|
||||
{
|
||||
if (rightValue == null)
|
||||
{
|
||||
binary.RightExpression.Accept(this);
|
||||
rightValue = Result;
|
||||
}
|
||||
return rightValue;
|
||||
};
|
||||
|
||||
switch (binary.Type)
|
||||
{
|
||||
case BinaryExpressionType.And:
|
||||
Result = ConversionUtility.Convert<bool>(left()) && ConversionUtility.Convert<bool>(right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Or:
|
||||
Result = ConversionUtility.Convert<bool>(left()) || ConversionUtility.Convert<bool>(right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Div:
|
||||
Result = OperatorUtility.Divide(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Equal:
|
||||
Result = OperatorUtility.Equal(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Greater:
|
||||
Result = OperatorUtility.GreaterThan(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.GreaterOrEqual:
|
||||
Result = OperatorUtility.GreaterThanOrEqual(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Lesser:
|
||||
Result = OperatorUtility.LessThan(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.LesserOrEqual:
|
||||
Result = OperatorUtility.LessThanOrEqual(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Minus:
|
||||
Result = OperatorUtility.Subtract(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Modulo:
|
||||
Result = OperatorUtility.Modulo(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.NotEqual:
|
||||
Result = OperatorUtility.NotEqual(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Plus:
|
||||
Result = OperatorUtility.Add(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Times:
|
||||
Result = OperatorUtility.Multiply(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.BitwiseAnd:
|
||||
Result = OperatorUtility.And(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.BitwiseOr:
|
||||
Result = OperatorUtility.Or(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.BitwiseXOr:
|
||||
Result = OperatorUtility.ExclusiveOr(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.LeftShift:
|
||||
Result = OperatorUtility.LeftShift(left(), right());
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.RightShift:
|
||||
Result = OperatorUtility.RightShift(left(), right());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Visit(UnaryExpression unary)
|
||||
{
|
||||
// Recursively evaluates the underlying expression
|
||||
unary.Expression.Accept(this);
|
||||
|
||||
switch (unary.Type)
|
||||
{
|
||||
case UnaryExpressionType.Not:
|
||||
Result = !ConversionUtility.Convert<bool>(Result);
|
||||
break;
|
||||
|
||||
case UnaryExpressionType.Negate:
|
||||
Result = OperatorUtility.Negate(Result);
|
||||
break;
|
||||
|
||||
case UnaryExpressionType.BitwiseNot:
|
||||
Result = OperatorUtility.Not(Result);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Visit(ValueExpression value)
|
||||
{
|
||||
Result = value.Value;
|
||||
}
|
||||
|
||||
public override void Visit(FunctionExpression function)
|
||||
{
|
||||
var args = new FunctionArgs
|
||||
{
|
||||
Parameters = new Expression[function.Expressions.Length]
|
||||
};
|
||||
|
||||
// Don't call parameters right now, instead let the function do it as needed.
|
||||
// Some parameters shouldn't be called, for instance, in a if(), the "not" value might be a division by zero
|
||||
// Evaluating every value could produce unexpected behaviour
|
||||
for (var i = 0; i < function.Expressions.Length; i++)
|
||||
{
|
||||
args.Parameters[i] = new Expression(function.Expressions[i], options);
|
||||
args.Parameters[i].EvaluateFunction += EvaluateFunction;
|
||||
args.Parameters[i].EvaluateParameter += EvaluateParameter;
|
||||
|
||||
// Assign the parameters of the Expression to the arguments so that custom Functions and Parameters can use them
|
||||
args.Parameters[i].Parameters = Parameters;
|
||||
}
|
||||
|
||||
// Calls external implementation
|
||||
OnEvaluateFunction(IgnoreCase ? function.Identifier.Name.ToLower() : function.Identifier.Name, args);
|
||||
|
||||
// If an external implementation was found get the result back
|
||||
if (args.HasResult)
|
||||
{
|
||||
Result = args.Result;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (function.Identifier.Name.ToLower(CultureInfo.InvariantCulture))
|
||||
{
|
||||
case "abs":
|
||||
CheckCase(function, "Abs");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Abs(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
break;
|
||||
|
||||
case "acos":
|
||||
CheckCase(function, "Acos");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Acos(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
break;
|
||||
|
||||
case "asin":
|
||||
CheckCase(function, "Asin");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Asin(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
break;
|
||||
|
||||
case "atan":
|
||||
CheckCase(function, "Atan");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Atan(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
break;
|
||||
|
||||
case "ceil":
|
||||
CheckCase(function, "Ceil");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Ceil(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
break;
|
||||
|
||||
case "cos":
|
||||
CheckCase(function, "Cos");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Cos(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
break;
|
||||
|
||||
case "exp":
|
||||
CheckCase(function, "Exp");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Exp(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
break;
|
||||
|
||||
case "floor":
|
||||
CheckCase(function, "Floor");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Floor(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
break;
|
||||
|
||||
case "truncate":
|
||||
CheckCase(function, "Truncate");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.FloorToInt(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
break;
|
||||
|
||||
case "log":
|
||||
CheckCase(function, "Log");
|
||||
CheckExactArgumentCount(function, 2);
|
||||
Result = Mathf.Log(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])), ConversionUtility.Convert<float>(Evaluate(function.Expressions[1])));
|
||||
break;
|
||||
|
||||
case "log10":
|
||||
CheckCase(function, "Log10");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Log10(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
break;
|
||||
|
||||
case "pow":
|
||||
CheckCase(function, "Pow");
|
||||
CheckExactArgumentCount(function, 2);
|
||||
Result = Mathf.Pow(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])), ConversionUtility.Convert<float>(Evaluate(function.Expressions[1])));
|
||||
break;
|
||||
|
||||
case "round":
|
||||
CheckCase(function, "Round");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
//var rounding = (options & EvaluateOptions.RoundAwayFromZero) == EvaluateOptions.RoundAwayFromZero ? MidpointRounding.AwayFromZero : MidpointRounding.ToEven;
|
||||
Result = Mathf.Round(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
break;
|
||||
|
||||
case "sign":
|
||||
CheckCase(function, "Sign");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Sign(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
|
||||
break;
|
||||
|
||||
case "sin":
|
||||
CheckCase(function, "Sin");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Sin(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
break;
|
||||
|
||||
case "sqrt":
|
||||
CheckCase(function, "Sqrt");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Sqrt(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
|
||||
break;
|
||||
|
||||
case "tan":
|
||||
CheckCase(function, "Tan");
|
||||
CheckExactArgumentCount(function, 1);
|
||||
Result = Mathf.Tan(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])));
|
||||
|
||||
break;
|
||||
|
||||
case "max":
|
||||
CheckCase(function, "Max");
|
||||
CheckExactArgumentCount(function, 2);
|
||||
Result = Mathf.Max(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])), ConversionUtility.Convert<float>(Evaluate(function.Expressions[1])));
|
||||
break;
|
||||
|
||||
case "min":
|
||||
CheckCase(function, "Min");
|
||||
CheckExactArgumentCount(function, 2);
|
||||
Result = Mathf.Min(ConversionUtility.Convert<float>(Evaluate(function.Expressions[0])), ConversionUtility.Convert<float>(Evaluate(function.Expressions[1])));
|
||||
break;
|
||||
|
||||
case "in":
|
||||
CheckCase(function, "In");
|
||||
CheckExactArgumentCount(function, 2);
|
||||
|
||||
var parameter = Evaluate(function.Expressions[0]);
|
||||
|
||||
var evaluation = false;
|
||||
|
||||
// Goes through any values, and stop whe one is found
|
||||
for (var i = 1; i < function.Expressions.Length; i++)
|
||||
{
|
||||
var argument = Evaluate(function.Expressions[i]);
|
||||
|
||||
if (Equals(parameter, argument))
|
||||
{
|
||||
evaluation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Result = evaluation;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Function not found", function.Identifier.Name);
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckCase(FunctionExpression function, string reference)
|
||||
{
|
||||
var called = function.Identifier.Name;
|
||||
|
||||
if (IgnoreCase)
|
||||
{
|
||||
if (string.Equals(called, reference, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
throw new ArgumentException("Function not found.", called);
|
||||
}
|
||||
|
||||
if (called != reference)
|
||||
{
|
||||
throw new ArgumentException($"Function not found: '{called}'. Try '{reference}' instead.");
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEvaluateFunction(string name, FunctionArgs args)
|
||||
{
|
||||
EvaluateFunction?.Invoke(flow, name, args);
|
||||
}
|
||||
|
||||
public override void Visit(IdentifierExpression identifier)
|
||||
{
|
||||
if (Parameters.ContainsKey(identifier.Name))
|
||||
{
|
||||
// The parameter is defined in the dictionary
|
||||
if (Parameters[identifier.Name] is Expression)
|
||||
{
|
||||
// The parameter is itself another Expression
|
||||
var expression = (Expression)Parameters[identifier.Name];
|
||||
|
||||
// Overloads parameters
|
||||
foreach (var p in Parameters)
|
||||
{
|
||||
expression.Parameters[p.Key] = p.Value;
|
||||
}
|
||||
|
||||
expression.EvaluateFunction += EvaluateFunction;
|
||||
expression.EvaluateParameter += EvaluateParameter;
|
||||
|
||||
Result = ((Expression)Parameters[identifier.Name]).Evaluate(flow);
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = Parameters[identifier.Name];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The parameter should be defined in a callback method
|
||||
var args = new ParameterArgs();
|
||||
|
||||
// Calls external implementation
|
||||
OnEvaluateParameter(identifier.Name, args);
|
||||
|
||||
if (!args.HasResult)
|
||||
{
|
||||
throw new ArgumentException("Parameter was not defined", identifier.Name);
|
||||
}
|
||||
|
||||
Result = args.Result;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEvaluateParameter(string name, ParameterArgs args)
|
||||
{
|
||||
EvaluateParameter?.Invoke(flow, name, args);
|
||||
}
|
||||
|
||||
public static void CheckExactArgumentCount(FunctionExpression function, int count)
|
||||
{
|
||||
if (function.Expressions.Length != count)
|
||||
{
|
||||
throw new ArgumentException($"{function.Identifier.Name}() takes at exactly {count} arguments. {function.Expressions.Length} provided.");
|
||||
}
|
||||
}
|
||||
|
||||
public static void CheckMinArgumentCount(FunctionExpression function, int count)
|
||||
{
|
||||
if (function.Expressions.Length < count)
|
||||
{
|
||||
throw new ArgumentException($"{function.Identifier.Name}() takes at at least {count} arguments. {function.Expressions.Length} provided.");
|
||||
}
|
||||
}
|
||||
|
||||
private delegate T Func<T>();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0e70958412a3493888a351443ad8866
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,301 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using Unity.VisualScripting.Antlr3.Runtime;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public class Expression
|
||||
{
|
||||
private Expression()
|
||||
{
|
||||
// Fix: the original grammar doesn't include a null identifier.
|
||||
Parameters["null"] = Parameters["NULL"] = null;
|
||||
}
|
||||
|
||||
public Expression(string expression, EvaluateOptions options = EvaluateOptions.None) : this()
|
||||
{
|
||||
if (string.IsNullOrEmpty(expression))
|
||||
{
|
||||
throw new ArgumentException("Expression can't be empty", nameof(expression));
|
||||
}
|
||||
|
||||
// Fix: The original grammar doesn't allow double quotes for strings.
|
||||
expression = expression.Replace('\"', '\'');
|
||||
|
||||
OriginalExpression = expression;
|
||||
Options = options;
|
||||
}
|
||||
|
||||
public Expression(LogicalExpression expression, EvaluateOptions options = EvaluateOptions.None) : this()
|
||||
{
|
||||
if (expression == null)
|
||||
{
|
||||
throw new ArgumentException("Expression can't be null", nameof(expression));
|
||||
}
|
||||
|
||||
ParsedExpression = expression;
|
||||
Options = options;
|
||||
}
|
||||
|
||||
public event EvaluateFunctionHandler EvaluateFunction;
|
||||
public event EvaluateParameterHandler EvaluateParameter;
|
||||
|
||||
/// <summary>
|
||||
/// Textual representation of the expression to evaluate.
|
||||
/// </summary>
|
||||
protected readonly string OriginalExpression;
|
||||
|
||||
protected Dictionary<string, IEnumerator> ParameterEnumerators;
|
||||
|
||||
private Dictionary<string, object> _parameters;
|
||||
|
||||
public EvaluateOptions Options { get; set; }
|
||||
|
||||
public string Error { get; private set; }
|
||||
|
||||
public LogicalExpression ParsedExpression { get; private set; }
|
||||
|
||||
public Dictionary<string, object> Parameters
|
||||
{
|
||||
get
|
||||
{
|
||||
return _parameters ?? (_parameters = new Dictionary<string, object>());
|
||||
}
|
||||
set
|
||||
{
|
||||
_parameters = value;
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateUnityTimeParameters()
|
||||
{
|
||||
Parameters["dt"] = Parameters["DT"] = Time.deltaTime;
|
||||
Parameters["second"] = Parameters["Second"] = 1 / Time.deltaTime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pre-compiles the expression in order to check syntax errors.
|
||||
/// If errors are detected, the Error property contains the message.
|
||||
/// </summary>
|
||||
/// <returns>True if the expression syntax is correct, otherwise false</returns>
|
||||
public bool HasErrors()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ParsedExpression == null)
|
||||
{
|
||||
ParsedExpression = Compile(OriginalExpression, (Options & EvaluateOptions.NoCache) == EvaluateOptions.NoCache);
|
||||
}
|
||||
|
||||
// In case HasErrors() is called multiple times for the same expression
|
||||
return ParsedExpression != null && Error != null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Error = e.Message;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public object Evaluate(Flow flow)
|
||||
{
|
||||
if (HasErrors())
|
||||
{
|
||||
throw new EvaluationException(Error);
|
||||
}
|
||||
|
||||
if (ParsedExpression == null)
|
||||
{
|
||||
ParsedExpression = Compile(OriginalExpression, (Options & EvaluateOptions.NoCache) == EvaluateOptions.NoCache);
|
||||
}
|
||||
|
||||
var visitor = new EvaluationVisitor(flow, Options);
|
||||
visitor.EvaluateFunction += EvaluateFunction;
|
||||
visitor.EvaluateParameter += EvaluateParameter;
|
||||
visitor.Parameters = Parameters;
|
||||
|
||||
// If array evaluation, execute the same expression multiple times
|
||||
if ((Options & EvaluateOptions.IterateParameters) == EvaluateOptions.IterateParameters)
|
||||
{
|
||||
var size = -1;
|
||||
|
||||
ParameterEnumerators = new Dictionary<string, IEnumerator>();
|
||||
|
||||
foreach (var parameter in Parameters.Values)
|
||||
{
|
||||
if (parameter is IEnumerable enumerable)
|
||||
{
|
||||
var localsize = 0;
|
||||
|
||||
foreach (var o in enumerable)
|
||||
{
|
||||
localsize++;
|
||||
}
|
||||
|
||||
if (size == -1)
|
||||
{
|
||||
size = localsize;
|
||||
}
|
||||
else if (localsize != size)
|
||||
{
|
||||
throw new EvaluationException("When IterateParameters option is used, IEnumerable parameters must have the same number of items.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var key in Parameters.Keys)
|
||||
{
|
||||
var parameter = Parameters[key] as IEnumerable;
|
||||
if (parameter != null)
|
||||
{
|
||||
ParameterEnumerators.Add(key, parameter.GetEnumerator());
|
||||
}
|
||||
}
|
||||
|
||||
var results = new List<object>();
|
||||
|
||||
for (var i = 0; i < size; i++)
|
||||
{
|
||||
foreach (var key in ParameterEnumerators.Keys)
|
||||
{
|
||||
var enumerator = ParameterEnumerators[key];
|
||||
enumerator.MoveNext();
|
||||
Parameters[key] = enumerator.Current;
|
||||
}
|
||||
|
||||
ParsedExpression.Accept(visitor);
|
||||
results.Add(visitor.Result);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParsedExpression.Accept(visitor);
|
||||
return visitor.Result;
|
||||
}
|
||||
}
|
||||
|
||||
public static LogicalExpression Compile(string expression, bool noCache)
|
||||
{
|
||||
LogicalExpression logicalExpression = null;
|
||||
|
||||
if (_cacheEnabled && !noCache)
|
||||
{
|
||||
try
|
||||
{
|
||||
Rwl.AcquireReaderLock(Timeout.Infinite);
|
||||
|
||||
if (_compiledExpressions.ContainsKey(expression))
|
||||
{
|
||||
Trace.TraceInformation("Expression retrieved from cache: " + expression);
|
||||
var wr = _compiledExpressions[expression];
|
||||
logicalExpression = wr.Target as LogicalExpression;
|
||||
|
||||
if (wr.IsAlive && logicalExpression != null)
|
||||
{
|
||||
return logicalExpression;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Rwl.ReleaseReaderLock();
|
||||
}
|
||||
}
|
||||
|
||||
if (logicalExpression == null)
|
||||
{
|
||||
var lexer = new NCalcLexer(new ANTLRStringStream(expression));
|
||||
var parser = new NCalcParser(new CommonTokenStream(lexer));
|
||||
|
||||
logicalExpression = parser.ncalcExpression().value;
|
||||
|
||||
if (parser.Errors != null && parser.Errors.Count > 0)
|
||||
{
|
||||
throw new EvaluationException(String.Join(Environment.NewLine, parser.Errors.ToArray()));
|
||||
}
|
||||
|
||||
if (_cacheEnabled && !noCache)
|
||||
{
|
||||
try
|
||||
{
|
||||
Rwl.AcquireWriterLock(Timeout.Infinite);
|
||||
_compiledExpressions[expression] = new WeakReference(logicalExpression);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Rwl.ReleaseWriterLock();
|
||||
}
|
||||
|
||||
CleanCache();
|
||||
|
||||
Trace.TraceInformation("Expression added to cache: " + expression);
|
||||
}
|
||||
}
|
||||
|
||||
return logicalExpression;
|
||||
}
|
||||
|
||||
#region Cache management
|
||||
|
||||
private static bool _cacheEnabled = true;
|
||||
private static Dictionary<string, WeakReference> _compiledExpressions = new Dictionary<string, WeakReference>();
|
||||
private static readonly ReaderWriterLock Rwl = new ReaderWriterLock();
|
||||
|
||||
public static bool CacheEnabled
|
||||
{
|
||||
get
|
||||
{
|
||||
return _cacheEnabled;
|
||||
}
|
||||
set
|
||||
{
|
||||
_cacheEnabled = value;
|
||||
|
||||
if (!CacheEnabled)
|
||||
{
|
||||
// Clears cache
|
||||
_compiledExpressions = new Dictionary<string, WeakReference>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes unused entries from cached compiled expression.
|
||||
/// </summary>
|
||||
private static void CleanCache()
|
||||
{
|
||||
var keysToRemove = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
Rwl.AcquireWriterLock(Timeout.Infinite);
|
||||
|
||||
foreach (var de in _compiledExpressions)
|
||||
{
|
||||
if (!de.Value.IsAlive)
|
||||
{
|
||||
keysToRemove.Add(de.Key);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var key in keysToRemove)
|
||||
{
|
||||
_compiledExpressions.Remove(key);
|
||||
Trace.TraceInformation("Cache entry released: " + key);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
Rwl.ReleaseReaderLock();
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3dd474c9747c7444d9ca4b191b48301d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public class FunctionArgs : EventArgs
|
||||
{
|
||||
private object _result;
|
||||
|
||||
private Expression[] _parameters = new Expression[0];
|
||||
|
||||
public object Result
|
||||
{
|
||||
get
|
||||
{
|
||||
return _result;
|
||||
}
|
||||
set
|
||||
{
|
||||
_result = value;
|
||||
HasResult = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasResult { get; set; }
|
||||
|
||||
public Expression[] Parameters
|
||||
{
|
||||
get
|
||||
{
|
||||
return _parameters;
|
||||
}
|
||||
set
|
||||
{
|
||||
_parameters = value;
|
||||
}
|
||||
}
|
||||
|
||||
public object[] EvaluateParameters(Flow flow)
|
||||
{
|
||||
var values = new object[_parameters.Length];
|
||||
for (var i = 0; i < values.Length; i++)
|
||||
{
|
||||
values[i] = _parameters[i].Evaluate(flow);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55514a8464191433785154307e445337
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,20 @@
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public class FunctionExpression : LogicalExpression
|
||||
{
|
||||
public FunctionExpression(IdentifierExpression identifier, LogicalExpression[] expressions)
|
||||
{
|
||||
Identifier = identifier;
|
||||
Expressions = expressions;
|
||||
}
|
||||
|
||||
public IdentifierExpression Identifier { get; set; }
|
||||
|
||||
public LogicalExpression[] Expressions { get; set; }
|
||||
|
||||
public override void Accept(LogicalExpressionVisitor visitor)
|
||||
{
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9e4f622436c3c477bb484bd0114b74cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,17 @@
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public class IdentifierExpression : LogicalExpression
|
||||
{
|
||||
public IdentifierExpression(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public override void Accept(LogicalExpressionVisitor visitor)
|
||||
{
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ccf41f828829d4eafa25be821b8e2a60
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,258 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public abstract class LogicalExpression
|
||||
{
|
||||
public BinaryExpression And(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.And, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression And(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.And, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression DividedBy(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Div, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression DividedBy(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Div, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression EqualsTo(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Equal, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression EqualsTo(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Equal, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression GreaterThan(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Greater, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression GreaterThan(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Greater, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression GreaterOrEqualThan(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.GreaterOrEqual, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression GreaterOrEqualThan(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.GreaterOrEqual, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression LesserThan(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Lesser, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression LesserThan(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Lesser, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression LesserOrEqualThan(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.LesserOrEqual, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression LesserOrEqualThan(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.LesserOrEqual, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression Minus(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Minus, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression Minus(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Minus, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression Modulo(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Modulo, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression Modulo(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Modulo, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression NotEqual(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.NotEqual, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression NotEqual(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.NotEqual, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression Or(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Or, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression Or(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Or, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression Plus(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Plus, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression Plus(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Plus, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression Mult(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Times, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression Mult(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.Times, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression BitwiseOr(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.BitwiseOr, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression BitwiseOr(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.BitwiseOr, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression BitwiseAnd(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.BitwiseAnd, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression BitwiseAnd(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.BitwiseAnd, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression BitwiseXOr(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.BitwiseXOr, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression BitwiseXOr(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.BitwiseXOr, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression LeftShift(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.LeftShift, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression LeftShift(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.LeftShift, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public BinaryExpression RightShift(LogicalExpression operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.RightShift, this, operand);
|
||||
}
|
||||
|
||||
public BinaryExpression RightShift(object operand)
|
||||
{
|
||||
return new BinaryExpression(BinaryExpressionType.RightShift, this, new ValueExpression(operand));
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var serializer = new SerializationVisitor();
|
||||
|
||||
Accept(serializer);
|
||||
|
||||
return serializer.Result.ToString().TrimEnd(' ');
|
||||
}
|
||||
|
||||
// This method is not just syntactic sugar.
|
||||
// It chooses the proper overload of Visit() based on the current type.
|
||||
public virtual void Accept(LogicalExpressionVisitor visitor)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private const char BS = '\\';
|
||||
|
||||
private static string ExtractString(string text)
|
||||
{
|
||||
var sb = new StringBuilder(text);
|
||||
var startIndex = 1; // Skip initial quote
|
||||
var slashIndex = -1;
|
||||
|
||||
while ((slashIndex = sb.ToString().IndexOf(BS, startIndex)) != -1)
|
||||
{
|
||||
var escapeType = sb[slashIndex + 1];
|
||||
|
||||
switch (escapeType)
|
||||
{
|
||||
case 'u':
|
||||
var hcode = String.Concat(sb[slashIndex + 4], sb[slashIndex + 5]);
|
||||
var lcode = String.Concat(sb[slashIndex + 2], sb[slashIndex + 3]);
|
||||
var unicodeChar = Encoding.Unicode.GetChars(new[] { Convert.ToByte(hcode, 16), Convert.ToByte(lcode, 16) })[0];
|
||||
sb.Remove(slashIndex, 6).Insert(slashIndex, unicodeChar);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
sb.Remove(slashIndex, 2).Insert(slashIndex, '\n');
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
sb.Remove(slashIndex, 2).Insert(slashIndex, '\r');
|
||||
break;
|
||||
|
||||
case 't':
|
||||
sb.Remove(slashIndex, 2).Insert(slashIndex, '\t');
|
||||
break;
|
||||
|
||||
case '\'':
|
||||
sb.Remove(slashIndex, 2).Insert(slashIndex, '\'');
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
sb.Remove(slashIndex, 2).Insert(slashIndex, '\\');
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ApplicationException("Unvalid escape sequence: \\" + escapeType);
|
||||
}
|
||||
|
||||
startIndex = slashIndex + 1;
|
||||
}
|
||||
|
||||
sb.Remove(0, 1);
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2d630dc61d2194bf28114c04393228bb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,12 @@
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public abstract class LogicalExpressionVisitor
|
||||
{
|
||||
public abstract void Visit(TernaryExpression ternary);
|
||||
public abstract void Visit(BinaryExpression binary);
|
||||
public abstract void Visit(UnaryExpression unary);
|
||||
public abstract void Visit(ValueExpression value);
|
||||
public abstract void Visit(FunctionExpression function);
|
||||
public abstract void Visit(IdentifierExpression identifier);
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a2c35661d97146b2b302a94d1120f35
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,303 @@
|
||||
grammar NCalc;
|
||||
|
||||
options
|
||||
{
|
||||
output=AST;
|
||||
ASTLabelType=CommonTree;
|
||||
language=CSharp;
|
||||
}
|
||||
|
||||
@header {
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System.Collections.Generic;
|
||||
using NCalc.Domain;
|
||||
}
|
||||
|
||||
@members {
|
||||
private const char BS = '\\';
|
||||
private static NumberFormatInfo numberFormatInfo = new NumberFormatInfo();
|
||||
|
||||
private string extractString(string text) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(text);
|
||||
int startIndex = 1; // Skip initial quote
|
||||
int slashIndex = -1;
|
||||
|
||||
while ((slashIndex = sb.ToString().IndexOf(BS, startIndex)) != -1)
|
||||
{
|
||||
char escapeType = sb[slashIndex + 1];
|
||||
switch (escapeType)
|
||||
{
|
||||
case 'u':
|
||||
string hcode = String.Concat(sb[slashIndex+4], sb[slashIndex+5]);
|
||||
string lcode = String.Concat(sb[slashIndex+2], sb[slashIndex+3]);
|
||||
char unicodeChar = Encoding.Unicode.GetChars(new byte[] { System.Convert.ToByte(hcode, 16), System.Convert.ToByte(lcode, 16)} )[0];
|
||||
sb.Remove(slashIndex, 6).Insert(slashIndex, unicodeChar);
|
||||
break;
|
||||
case 'n': sb.Remove(slashIndex, 2).Insert(slashIndex, '\n'); break;
|
||||
case 'r': sb.Remove(slashIndex, 2).Insert(slashIndex, '\r'); break;
|
||||
case 't': sb.Remove(slashIndex, 2).Insert(slashIndex, '\t'); break;
|
||||
case '\'': sb.Remove(slashIndex, 2).Insert(slashIndex, '\''); break;
|
||||
case '\\': sb.Remove(slashIndex, 2).Insert(slashIndex, '\\'); break;
|
||||
default: throw new RecognitionException("Unvalid escape sequence: \\" + escapeType);
|
||||
}
|
||||
|
||||
startIndex = slashIndex + 1;
|
||||
|
||||
}
|
||||
|
||||
sb.Remove(0, 1);
|
||||
sb.Remove(sb.Length - 1, 1);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public List<string> Errors { get; private set; }
|
||||
|
||||
public override void DisplayRecognitionError(String[] tokenNames, RecognitionException e) {
|
||||
|
||||
base.DisplayRecognitionError(tokenNames, e);
|
||||
|
||||
if(Errors == null)
|
||||
{
|
||||
Errors = new List<string>();
|
||||
}
|
||||
|
||||
String hdr = GetErrorHeader(e);
|
||||
String msg = GetErrorMessage(e, tokenNames);
|
||||
Errors.Add(msg + " at " + hdr);
|
||||
}
|
||||
}
|
||||
|
||||
@init {
|
||||
numberFormatInfo.NumberDecimalSeparator = ".";
|
||||
}
|
||||
|
||||
ncalcExpression returns [LogicalExpression value]
|
||||
: logicalExpression EOF! {$value = $logicalExpression.value; }
|
||||
;
|
||||
|
||||
logicalExpression returns [LogicalExpression value]
|
||||
: left=conditionalExpression { $value = $left.value; } ( '?' middle=conditionalExpression ':' right=conditionalExpression { $value = new TernaryExpression($left.value, $middle.value, $right.value); })?
|
||||
;
|
||||
|
||||
conditionalExpression returns [LogicalExpression value]
|
||||
@init {
|
||||
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||||
}
|
||||
: left=booleanAndExpression { $value = $left.value; } (
|
||||
('||' | 'or') { type = BinaryExpressionType.Or; }
|
||||
right=conditionalExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||||
)*
|
||||
;
|
||||
|
||||
booleanAndExpression returns [LogicalExpression value]
|
||||
@init {
|
||||
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||||
}
|
||||
: left=bitwiseOrExpression { $value = $left.value; } (
|
||||
('&&' | 'and') { type = BinaryExpressionType.And; }
|
||||
right=bitwiseOrExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||||
)*
|
||||
;
|
||||
|
||||
bitwiseOrExpression returns [LogicalExpression value]
|
||||
@init {
|
||||
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||||
}
|
||||
: left=bitwiseXOrExpression { $value = $left.value; } (
|
||||
'|' { type = BinaryExpressionType.BitwiseOr; }
|
||||
right=bitwiseOrExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||||
)*
|
||||
;
|
||||
|
||||
bitwiseXOrExpression returns [LogicalExpression value]
|
||||
@init {
|
||||
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||||
}
|
||||
: left=bitwiseAndExpression { $value = $left.value; } (
|
||||
'^' { type = BinaryExpressionType.BitwiseXOr; }
|
||||
right=bitwiseAndExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||||
)*
|
||||
;
|
||||
|
||||
bitwiseAndExpression returns [LogicalExpression value]
|
||||
@init {
|
||||
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||||
}
|
||||
: left=equalityExpression { $value = $left.value; } (
|
||||
'&' { type = BinaryExpressionType.BitwiseAnd; }
|
||||
right=equalityExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||||
)*
|
||||
;
|
||||
|
||||
equalityExpression returns [LogicalExpression value]
|
||||
@init {
|
||||
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||||
}
|
||||
: left=relationalExpression { $value = $left.value; } (
|
||||
( ('==' | '=' ) { type = BinaryExpressionType.Equal; }
|
||||
| ('!=' | '<>' ) { type = BinaryExpressionType.NotEqual; } )
|
||||
right=relationalExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||||
)*
|
||||
;
|
||||
|
||||
relationalExpression returns [LogicalExpression value]
|
||||
@init {
|
||||
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||||
}
|
||||
: left=shiftExpression { $value = $left.value; } (
|
||||
( '<' { type = BinaryExpressionType.Lesser; }
|
||||
| '<=' { type = BinaryExpressionType.LesserOrEqual; }
|
||||
| '>' { type = BinaryExpressionType.Greater; }
|
||||
| '>=' { type = BinaryExpressionType.GreaterOrEqual; } )
|
||||
right=shiftExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||||
)*
|
||||
;
|
||||
|
||||
shiftExpression returns [LogicalExpression value]
|
||||
@init {
|
||||
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||||
}
|
||||
: left=additiveExpression { $value = $left.value; } (
|
||||
( '<<' { type = BinaryExpressionType.LeftShift; }
|
||||
| '>>' { type = BinaryExpressionType.RightShift; } )
|
||||
right=additiveExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||||
)*
|
||||
;
|
||||
|
||||
additiveExpression returns [LogicalExpression value]
|
||||
@init {
|
||||
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||||
}
|
||||
: left=multiplicativeExpression { $value = $left.value; } (
|
||||
( '+' { type = BinaryExpressionType.Plus; }
|
||||
| '-' { type = BinaryExpressionType.Minus; } )
|
||||
right=multiplicativeExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||||
)*
|
||||
;
|
||||
|
||||
multiplicativeExpression returns [LogicalExpression value]
|
||||
@init {
|
||||
BinaryExpressionType type = BinaryExpressionType.Unknown;
|
||||
}
|
||||
: left=unaryExpression { $value = $left.value); } (
|
||||
( '*' { type = BinaryExpressionType.Times; }
|
||||
| '/' { type = BinaryExpressionType.Div; }
|
||||
| '%' { type = BinaryExpressionType.Modulo; } )
|
||||
right=unaryExpression { $value = new BinaryExpression(type, $value, $right.value); }
|
||||
)*
|
||||
;
|
||||
|
||||
|
||||
unaryExpression returns [LogicalExpression value]
|
||||
: primaryExpression { $value = $primaryExpression.value; }
|
||||
| ('!' | 'not') primaryExpression { $value = new UnaryExpression(UnaryExpressionType.Not, $primaryExpression.value); }
|
||||
| ('~') primaryExpression { $value = new UnaryExpression(UnaryExpressionType.BitwiseNot, $primaryExpression.value); }
|
||||
| '-' primaryExpression { $value = new UnaryExpression(UnaryExpressionType.Negate, $primaryExpression.value); }
|
||||
;
|
||||
|
||||
primaryExpression returns [LogicalExpression value]
|
||||
: '(' logicalExpression ')' { $value = $logicalExpression.value; }
|
||||
| expr=value { $value = $expr.value; }
|
||||
| identifier {$value = (LogicalExpression) $identifier.value; } (arguments {$value = new Function($identifier.value, ($arguments.value).ToArray()); })?
|
||||
;
|
||||
|
||||
value returns [ValueExpression value]
|
||||
: INTEGER { try { $value = new ValueExpression(int.Parse($INTEGER.text)); } catch(System.OverflowException) { $value = new ValueExpression(long.Parse($INTEGER.text)); } }
|
||||
| FLOAT { $value = new ValueExpression(double.Parse($FLOAT.text, NumberStyles.Float, numberFormatInfo)); }
|
||||
| STRING { $value = new ValueExpression(extractString($STRING.text)); }
|
||||
| DATETIME { $value = new ValueExpression(DateTime.Parse($DATETIME.text.Substring(1, $DATETIME.text.Length-2))); }
|
||||
| TRUE { $value = new ValueExpression(true); }
|
||||
| FALSE { $value = new ValueExpression(false); }
|
||||
;
|
||||
|
||||
identifier returns[Identifier value]
|
||||
: ID { $value = new Identifier($ID.text); }
|
||||
| NAME { $value = new Identifier($NAME.text.Substring(1, $NAME.text.Length-2)); }
|
||||
;
|
||||
|
||||
expressionList returns [List<LogicalExpression> value]
|
||||
@init {
|
||||
List<LogicalExpression> expressions = new List<LogicalExpression>();
|
||||
}
|
||||
: first=logicalExpression {expressions.Add($first.value);} ( ',' follow=logicalExpression {expressions.Add($follow.value);})*
|
||||
{ $value = expressions; }
|
||||
;
|
||||
|
||||
arguments returns [List<LogicalExpression> value]
|
||||
@init {
|
||||
$value = new List<LogicalExpression>();
|
||||
}
|
||||
: '(' ( expressionList {$value = $expressionList.value;} )? ')'
|
||||
;
|
||||
|
||||
TRUE
|
||||
: 'true'
|
||||
;
|
||||
|
||||
FALSE
|
||||
: 'false'
|
||||
;
|
||||
|
||||
ID
|
||||
: LETTER (LETTER | DIGIT)*
|
||||
;
|
||||
|
||||
INTEGER
|
||||
: DIGIT+
|
||||
;
|
||||
|
||||
FLOAT
|
||||
: DIGIT* '.' DIGIT+ E?
|
||||
| DIGIT+ E
|
||||
;
|
||||
|
||||
STRING
|
||||
: '\'' ( EscapeSequence | (options {greedy=false;} : ~('\u0000'..'\u001f' | '\\' | '\'' ) ) )* '\''
|
||||
;
|
||||
|
||||
DATETIME
|
||||
: '#' (options {greedy=false;} : ~('#')*) '#'
|
||||
;
|
||||
|
||||
NAME : '[' (options {greedy=false;} : ~(']')*) ']'
|
||||
;
|
||||
|
||||
E : ('E'|'e') ('+'|'-')? DIGIT+
|
||||
;
|
||||
|
||||
fragment LETTER
|
||||
: 'a'..'z'
|
||||
| 'A'..'Z'
|
||||
| '_'
|
||||
;
|
||||
|
||||
fragment DIGIT
|
||||
: '0'..'9'
|
||||
;
|
||||
|
||||
fragment EscapeSequence
|
||||
: '\\'
|
||||
(
|
||||
'n'
|
||||
| 'r'
|
||||
| 't'
|
||||
| '\''
|
||||
| '\\'
|
||||
| UnicodeEscape
|
||||
)
|
||||
;
|
||||
|
||||
fragment HexDigit
|
||||
: ('0'..'9'|'a'..'f'|'A'..'F') ;
|
||||
|
||||
|
||||
fragment UnicodeEscape
|
||||
: 'u' HexDigit HexDigit HexDigit HexDigit
|
||||
;
|
||||
|
||||
/* Ignore white spaces */
|
||||
WS : (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;}
|
||||
;
|
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d65eede9661f46a88d6e1949070eb9f
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user