Initial Commit
This commit is contained in:
parent
53eb92e9af
commit
270ab7d11f
15341 changed files with 700234 additions and 0 deletions
|
@ -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 units 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,151 @@
|
|||
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);
|
||||
|
||||
Debug.LogWarning($"Could not load connection between '{source.key}' of '{sourceUnit}' and '{destination.key}' of '{destinationUnit}'.");
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
#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 units.");
|
||||
}
|
||||
|
||||
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:
|
Binary file not shown.
|
@ -0,0 +1,33 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f9e773f6742c84fa28f8820bd00cce30
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 0
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
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,444 @@
|
|||
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 "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 Antlr.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
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d81700b95c8e3471584ffa94fd06b03f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 87e1dcf4a9a154ad291d02cad1d920ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public class ParameterArgs : EventArgs
|
||||
{
|
||||
private object _result;
|
||||
|
||||
public object Result
|
||||
{
|
||||
get
|
||||
{
|
||||
return _result;
|
||||
}
|
||||
set
|
||||
{
|
||||
_result = value;
|
||||
HasResult = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasResult { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1b211073028ea4467ad1c7f48e0b3530
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,211 @@
|
|||
using System.Globalization;
|
||||
using System.Text;
|
||||
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public class SerializationVisitor : LogicalExpressionVisitor
|
||||
{
|
||||
public SerializationVisitor()
|
||||
{
|
||||
Result = new StringBuilder();
|
||||
_numberFormatInfo = new NumberFormatInfo { NumberDecimalSeparator = "." };
|
||||
}
|
||||
|
||||
private readonly NumberFormatInfo _numberFormatInfo;
|
||||
|
||||
public StringBuilder Result { get; protected set; }
|
||||
|
||||
public override void Visit(TernaryExpression ternary)
|
||||
{
|
||||
EncapsulateNoValue(ternary.LeftExpression);
|
||||
|
||||
Result.Append("? ");
|
||||
|
||||
EncapsulateNoValue(ternary.MiddleExpression);
|
||||
|
||||
Result.Append(": ");
|
||||
|
||||
EncapsulateNoValue(ternary.RightExpression);
|
||||
}
|
||||
|
||||
public override void Visit(BinaryExpression binary)
|
||||
{
|
||||
EncapsulateNoValue(binary.LeftExpression);
|
||||
|
||||
switch (binary.Type)
|
||||
{
|
||||
case BinaryExpressionType.And:
|
||||
Result.Append("and ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Or:
|
||||
Result.Append("or ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Div:
|
||||
Result.Append("/ ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Equal:
|
||||
Result.Append("= ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Greater:
|
||||
Result.Append("> ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.GreaterOrEqual:
|
||||
Result.Append(">= ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Lesser:
|
||||
Result.Append("< ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.LesserOrEqual:
|
||||
Result.Append("<= ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Minus:
|
||||
Result.Append("- ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Modulo:
|
||||
Result.Append("% ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.NotEqual:
|
||||
Result.Append("!= ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Plus:
|
||||
Result.Append("+ ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.Times:
|
||||
Result.Append("* ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.BitwiseAnd:
|
||||
Result.Append("& ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.BitwiseOr:
|
||||
Result.Append("| ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.BitwiseXOr:
|
||||
Result.Append("~ ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.LeftShift:
|
||||
Result.Append("<< ");
|
||||
break;
|
||||
|
||||
case BinaryExpressionType.RightShift:
|
||||
Result.Append(">> ");
|
||||
break;
|
||||
}
|
||||
|
||||
EncapsulateNoValue(binary.RightExpression);
|
||||
}
|
||||
|
||||
public override void Visit(UnaryExpression unary)
|
||||
{
|
||||
switch (unary.Type)
|
||||
{
|
||||
case UnaryExpressionType.Not:
|
||||
Result.Append("!");
|
||||
break;
|
||||
|
||||
case UnaryExpressionType.Negate:
|
||||
Result.Append("-");
|
||||
break;
|
||||
|
||||
case UnaryExpressionType.BitwiseNot:
|
||||
Result.Append("~");
|
||||
break;
|
||||
}
|
||||
|
||||
EncapsulateNoValue(unary.Expression);
|
||||
}
|
||||
|
||||
public override void Visit(ValueExpression value)
|
||||
{
|
||||
switch (value.Type)
|
||||
{
|
||||
case ValueType.Boolean:
|
||||
Result.Append(value.Value).Append(" ");
|
||||
break;
|
||||
|
||||
case ValueType.DateTime:
|
||||
Result.Append("#").Append(value.Value).Append("#").Append(" ");
|
||||
break;
|
||||
|
||||
case ValueType.Float:
|
||||
Result.Append(decimal.Parse(value.Value.ToString()).ToString(_numberFormatInfo)).Append(" ");
|
||||
break;
|
||||
|
||||
case ValueType.Integer:
|
||||
Result.Append(value.Value).Append(" ");
|
||||
break;
|
||||
|
||||
case ValueType.String:
|
||||
Result.Append("'").Append(value.Value).Append("'").Append(" ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Visit(FunctionExpression function)
|
||||
{
|
||||
Result.Append(function.Identifier.Name);
|
||||
|
||||
Result.Append("(");
|
||||
|
||||
for (var i = 0; i < function.Expressions.Length; i++)
|
||||
{
|
||||
function.Expressions[i].Accept(this);
|
||||
|
||||
if (i < function.Expressions.Length - 1)
|
||||
{
|
||||
Result.Remove(Result.Length - 1, 1);
|
||||
Result.Append(", ");
|
||||
}
|
||||
}
|
||||
|
||||
// Trim spaces before adding a closing parenthesis
|
||||
while (Result[Result.Length - 1] == ' ')
|
||||
{
|
||||
Result.Remove(Result.Length - 1, 1);
|
||||
}
|
||||
|
||||
Result.Append(") ");
|
||||
}
|
||||
|
||||
public override void Visit(IdentifierExpression identifier)
|
||||
{
|
||||
Result.Append("[").Append(identifier.Name).Append("] ");
|
||||
}
|
||||
|
||||
protected void EncapsulateNoValue(LogicalExpression expression)
|
||||
{
|
||||
if (expression is ValueExpression)
|
||||
{
|
||||
expression.Accept(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Result.Append("(");
|
||||
expression.Accept(this);
|
||||
|
||||
// Trim spaces before adding a closing parenthesis
|
||||
while (Result[Result.Length - 1] == ' ')
|
||||
{
|
||||
Result.Remove(Result.Length - 1, 1);
|
||||
}
|
||||
|
||||
Result.Append(") ");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 207ce2879c26b4ef0a891282aa1dfb27
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,23 @@
|
|||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public class TernaryExpression : LogicalExpression
|
||||
{
|
||||
public TernaryExpression(LogicalExpression leftExpression, LogicalExpression middleExpression, LogicalExpression rightExpression)
|
||||
{
|
||||
LeftExpression = leftExpression;
|
||||
MiddleExpression = middleExpression;
|
||||
RightExpression = rightExpression;
|
||||
}
|
||||
|
||||
public LogicalExpression LeftExpression { get; set; }
|
||||
|
||||
public LogicalExpression MiddleExpression { get; set; }
|
||||
|
||||
public LogicalExpression RightExpression { get; set; }
|
||||
|
||||
public override void Accept(LogicalExpressionVisitor visitor)
|
||||
{
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1e339c45a25164bda8fb4ef97ff8d67e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,27 @@
|
|||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public class UnaryExpression : LogicalExpression
|
||||
{
|
||||
public UnaryExpression(UnaryExpressionType type, LogicalExpression expression)
|
||||
{
|
||||
Type = type;
|
||||
Expression = expression;
|
||||
}
|
||||
|
||||
public LogicalExpression Expression { get; set; }
|
||||
|
||||
public UnaryExpressionType Type { get; set; }
|
||||
|
||||
public override void Accept(LogicalExpressionVisitor visitor)
|
||||
{
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
public enum UnaryExpressionType
|
||||
{
|
||||
Not,
|
||||
Negate,
|
||||
BitwiseNot
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c614751e0f58c477cb0e9658be0ebdc4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,100 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting.Dependencies.NCalc
|
||||
{
|
||||
public class ValueExpression : LogicalExpression
|
||||
{
|
||||
public ValueExpression(object value, ValueType type)
|
||||
{
|
||||
Value = value;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public ValueExpression(object value)
|
||||
{
|
||||
switch (System.Type.GetTypeCode(value.GetType()))
|
||||
{
|
||||
case TypeCode.Boolean:
|
||||
Type = ValueType.Boolean;
|
||||
break;
|
||||
|
||||
case TypeCode.DateTime:
|
||||
Type = ValueType.DateTime;
|
||||
break;
|
||||
|
||||
case TypeCode.Decimal:
|
||||
case TypeCode.Double:
|
||||
case TypeCode.Single:
|
||||
Type = ValueType.Float;
|
||||
break;
|
||||
|
||||
case TypeCode.Byte:
|
||||
case TypeCode.SByte:
|
||||
case TypeCode.Int16:
|
||||
case TypeCode.Int32:
|
||||
case TypeCode.Int64:
|
||||
case TypeCode.UInt16:
|
||||
case TypeCode.UInt32:
|
||||
case TypeCode.UInt64:
|
||||
Type = ValueType.Integer;
|
||||
break;
|
||||
|
||||
case TypeCode.String:
|
||||
Type = ValueType.String;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new EvaluationException("This value could not be handled: " + value);
|
||||
}
|
||||
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public ValueExpression(string value)
|
||||
{
|
||||
Value = value;
|
||||
Type = ValueType.String;
|
||||
}
|
||||
|
||||
public ValueExpression(int value)
|
||||
{
|
||||
Value = value;
|
||||
Type = ValueType.Integer;
|
||||
}
|
||||
|
||||
public ValueExpression(float value)
|
||||
{
|
||||
Value = value;
|
||||
Type = ValueType.Float;
|
||||
}
|
||||
|
||||
public ValueExpression(DateTime value)
|
||||
{
|
||||
Value = value;
|
||||
Type = ValueType.DateTime;
|
||||
}
|
||||
|
||||
public ValueExpression(bool value)
|
||||
{
|
||||
Value = value;
|
||||
Type = ValueType.Boolean;
|
||||
}
|
||||
|
||||
public object Value { get; set; }
|
||||
public ValueType Type { get; set; }
|
||||
|
||||
public override void Accept(LogicalExpressionVisitor visitor)
|
||||
{
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ValueType
|
||||
{
|
||||
Integer,
|
||||
String,
|
||||
DateTime,
|
||||
Float,
|
||||
Boolean
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b7338a5f262ac4ecea97462e8eb78178
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0f417b240b9ac4d80a5526339bbe758a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
|
||||
public class PortKeyAttribute : Attribute
|
||||
{
|
||||
public PortKeyAttribute(string key)
|
||||
{
|
||||
Ensure.That(nameof(key)).IsNotNull(key);
|
||||
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public string key { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b61cb47a7912b4a0ab155cee34717187
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,16 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
|
||||
public class PortLabelAttribute : Attribute
|
||||
{
|
||||
public PortLabelAttribute(string label)
|
||||
{
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public string label { get; private set; }
|
||||
public bool hidden { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8b277cd84c6a344269542f249c6feafc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
|
||||
public class PortLabelHiddenAttribute : Attribute
|
||||
{ }
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 57ded228646794ea2a6e7b966f8d9880
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class SpecialUnitAttribute : Attribute
|
||||
{ }
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b2a4736f47f7048e0babf1936b9f5ced
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,13 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class UnitFooterPortsAttribute : Attribute
|
||||
{
|
||||
public bool ControlInputs { get; set; } = false;
|
||||
public bool ControlOutputs { get; set; } = false;
|
||||
public bool ValueInputs { get; set; } = true;
|
||||
public bool ValueOutputs { get; set; } = true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4f4fd0e2acdd2492ca24c2cc723baf81
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class UnitHeaderInspectableAttribute : Attribute
|
||||
{
|
||||
public UnitHeaderInspectableAttribute() { }
|
||||
|
||||
public UnitHeaderInspectableAttribute(string label)
|
||||
{
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public string label { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b6e9e78d67fd046868b0433247b23fad
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class UnitOrderAttribute : Attribute
|
||||
{
|
||||
public UnitOrderAttribute(int order)
|
||||
{
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
public int order { get; private set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d3e4ca0c5150d447abfbdc84355362b9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class UnitShortTitleAttribute : Attribute
|
||||
{
|
||||
public UnitShortTitleAttribute(string title)
|
||||
{
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public string title { get; private set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 99ce26428a6ff4f419011392de515c3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class UnitSubtitleAttribute : Attribute
|
||||
{
|
||||
public UnitSubtitleAttribute(string subtitle)
|
||||
{
|
||||
this.subtitle = subtitle;
|
||||
}
|
||||
|
||||
public string subtitle { get; private set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: bc18ef3f025fa46629333873a5c1e2a5
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class UnitSurtitleAttribute : Attribute
|
||||
{
|
||||
public UnitSurtitleAttribute(string surtitle)
|
||||
{
|
||||
this.surtitle = surtitle;
|
||||
}
|
||||
|
||||
public string surtitle { get; private set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e03bac02ee3494a1184f0544b7f3afe1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,15 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
|
||||
public sealed class UnitTitleAttribute : Attribute
|
||||
{
|
||||
public UnitTitleAttribute(string title)
|
||||
{
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public string title { get; private set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 37d3ce74ab2f0451db3e7823e0305c2a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,838 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class Flow : IPoolable, IDisposable
|
||||
{
|
||||
// We need to check for recursion by passing some additional
|
||||
// context information to avoid the same port in multiple different
|
||||
// nested flow graphs to count as the same item. Naively,
|
||||
// we're using the parent as the context, which seems to work;
|
||||
// it won't theoretically catch recursive nesting, but then recursive
|
||||
// nesting already isn't supported anyway, so this way we avoid hashing
|
||||
// or turning the stack into a reference.
|
||||
// https://support.ludiq.io/communities/5/topics/2122-r
|
||||
// We make this an equatable struct to avoid any allocation.
|
||||
private struct RecursionNode : IEquatable<RecursionNode>
|
||||
{
|
||||
public IUnitPort port { get; }
|
||||
|
||||
public IGraphParent context { get; }
|
||||
|
||||
public RecursionNode(IUnitPort port, GraphPointer pointer)
|
||||
{
|
||||
this.port = port;
|
||||
this.context = pointer.parent;
|
||||
}
|
||||
|
||||
public bool Equals(RecursionNode other)
|
||||
{
|
||||
return other.port == port && other.context == context;
|
||||
}
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is RecursionNode other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return HashUtility.GetHashCode(port, context);
|
||||
}
|
||||
}
|
||||
|
||||
public GraphStack stack { get; private set; }
|
||||
|
||||
private Recursion<RecursionNode> recursion;
|
||||
|
||||
private readonly Dictionary<IUnitValuePort, object> locals = new Dictionary<IUnitValuePort, object>();
|
||||
|
||||
public readonly VariableDeclarations variables = new VariableDeclarations();
|
||||
|
||||
private readonly Stack<int> loops = new Stack<int>();
|
||||
|
||||
private readonly HashSet<GraphStack> preservedStacks = new HashSet<GraphStack>();
|
||||
|
||||
public MonoBehaviour coroutineRunner { get; private set; }
|
||||
|
||||
private ICollection<Flow> activeCoroutinesRegistry;
|
||||
|
||||
private bool coroutineStopRequested;
|
||||
|
||||
public bool isCoroutine { get; private set; }
|
||||
|
||||
private IEnumerator coroutineEnumerator;
|
||||
|
||||
public bool isPrediction { get; private set; }
|
||||
|
||||
private bool disposed;
|
||||
|
||||
public bool enableDebug
|
||||
{
|
||||
get
|
||||
{
|
||||
if (isPrediction)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stack.hasDebugData)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static Func<GraphPointer, bool> isInspectedBinding { get; set; }
|
||||
|
||||
public bool isInspected => isInspectedBinding?.Invoke(stack) ?? false;
|
||||
|
||||
|
||||
#region Lifecycle
|
||||
|
||||
private Flow() { }
|
||||
|
||||
public static Flow New(GraphReference reference)
|
||||
{
|
||||
Ensure.That(nameof(reference)).IsNotNull(reference);
|
||||
|
||||
var flow = GenericPool<Flow>.New(() => new Flow()); ;
|
||||
flow.stack = reference.ToStackPooled();
|
||||
|
||||
return flow;
|
||||
}
|
||||
|
||||
void IPoolable.New()
|
||||
{
|
||||
disposed = false;
|
||||
|
||||
recursion = Recursion<RecursionNode>.New();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (disposed)
|
||||
{
|
||||
throw new ObjectDisposedException(ToString());
|
||||
}
|
||||
|
||||
GenericPool<Flow>.Free(this);
|
||||
}
|
||||
|
||||
void IPoolable.Free()
|
||||
{
|
||||
stack?.Dispose();
|
||||
recursion?.Dispose();
|
||||
locals.Clear();
|
||||
loops.Clear();
|
||||
variables.Clear();
|
||||
|
||||
// Preserved stacks could remain if coroutine was interrupted
|
||||
foreach (var preservedStack in preservedStacks)
|
||||
{
|
||||
preservedStack.Dispose();
|
||||
}
|
||||
|
||||
preservedStacks.Clear();
|
||||
|
||||
loopIdentifier = -1;
|
||||
stack = null;
|
||||
recursion = null;
|
||||
isCoroutine = false;
|
||||
coroutineEnumerator = null;
|
||||
coroutineRunner = null;
|
||||
activeCoroutinesRegistry?.Remove(this);
|
||||
activeCoroutinesRegistry = null;
|
||||
coroutineStopRequested = false;
|
||||
isPrediction = false;
|
||||
|
||||
disposed = true;
|
||||
}
|
||||
|
||||
public GraphStack PreserveStack()
|
||||
{
|
||||
var preservedStack = stack.Clone();
|
||||
preservedStacks.Add(preservedStack);
|
||||
return preservedStack;
|
||||
}
|
||||
|
||||
public void RestoreStack(GraphStack stack)
|
||||
{
|
||||
this.stack.CopyFrom(stack);
|
||||
}
|
||||
|
||||
public void DisposePreservedStack(GraphStack stack)
|
||||
{
|
||||
stack.Dispose();
|
||||
preservedStacks.Remove(stack);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Loops
|
||||
|
||||
public int loopIdentifier = -1;
|
||||
|
||||
public int currentLoop
|
||||
{
|
||||
get
|
||||
{
|
||||
if (loops.Count > 0)
|
||||
{
|
||||
return loops.Peek();
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool LoopIsNotBroken(int loop)
|
||||
{
|
||||
return currentLoop == loop;
|
||||
}
|
||||
|
||||
public int EnterLoop()
|
||||
{
|
||||
var loop = ++loopIdentifier;
|
||||
|
||||
loops.Push(loop);
|
||||
|
||||
return loop;
|
||||
}
|
||||
|
||||
public void BreakLoop()
|
||||
{
|
||||
if (currentLoop < 0)
|
||||
{
|
||||
throw new InvalidOperationException("No active loop to break.");
|
||||
}
|
||||
|
||||
loops.Pop();
|
||||
}
|
||||
|
||||
public void ExitLoop(int loop)
|
||||
{
|
||||
if (loop != currentLoop)
|
||||
{
|
||||
// Already exited through break
|
||||
return;
|
||||
}
|
||||
|
||||
loops.Pop();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Control
|
||||
|
||||
public void Run(ControlOutput port)
|
||||
{
|
||||
Invoke(port);
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void StartCoroutine(ControlOutput port, ICollection<Flow> registry = null)
|
||||
{
|
||||
isCoroutine = true;
|
||||
|
||||
coroutineRunner = stack.component;
|
||||
|
||||
if (coroutineRunner == null)
|
||||
{
|
||||
coroutineRunner = CoroutineRunner.instance;
|
||||
}
|
||||
|
||||
activeCoroutinesRegistry = registry;
|
||||
|
||||
activeCoroutinesRegistry?.Add(this);
|
||||
|
||||
// We have to store the enumerator because Coroutine itself
|
||||
// can't be cast to IDisposable, which we'll need when stopping.
|
||||
coroutineEnumerator = Coroutine(port);
|
||||
|
||||
coroutineRunner.StartCoroutine(coroutineEnumerator);
|
||||
}
|
||||
|
||||
public void StopCoroutine(bool disposeInstantly)
|
||||
{
|
||||
if (!isCoroutine)
|
||||
{
|
||||
throw new NotSupportedException("Stop may only be called on coroutines.");
|
||||
}
|
||||
|
||||
if (disposeInstantly)
|
||||
{
|
||||
StopCoroutineImmediate();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We prefer a soft coroutine stop here that will happen at the *next frame*,
|
||||
// because we don't want the flow to be disposed just yet when the event unit stops
|
||||
// listening, as we still need it for clean up operations.
|
||||
coroutineStopRequested = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal void StopCoroutineImmediate()
|
||||
{
|
||||
if (coroutineRunner && coroutineEnumerator != null)
|
||||
{
|
||||
coroutineRunner.StopCoroutine(coroutineEnumerator);
|
||||
|
||||
// Unity doesn't dispose coroutines enumerators when calling StopCoroutine, so we have to do it manually:
|
||||
// https://forum.unity.com/threads/finally-block-not-executing-in-a-stopped-coroutine.320611/
|
||||
((IDisposable)coroutineEnumerator).Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerator Coroutine(ControlOutput startPort)
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var instruction in InvokeCoroutine(startPort))
|
||||
{
|
||||
if (coroutineStopRequested)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return instruction;
|
||||
|
||||
if (coroutineStopRequested)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Manual disposal might have already occurred from StopCoroutine,
|
||||
// so we have to avoid double disposal, which would throw.
|
||||
if (!disposed)
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Invoke(ControlOutput output)
|
||||
{
|
||||
Ensure.That(nameof(output)).IsNotNull(output);
|
||||
|
||||
var connection = output.connection;
|
||||
|
||||
if (connection == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var input = connection.destination;
|
||||
|
||||
var recursionNode = new RecursionNode(output, stack);
|
||||
|
||||
BeforeInvoke(output, recursionNode);
|
||||
|
||||
var nextPort = InvokeDelegate(input);
|
||||
|
||||
if (nextPort != null)
|
||||
{
|
||||
Invoke(nextPort);
|
||||
}
|
||||
|
||||
AfterInvoke(output, recursionNode);
|
||||
}
|
||||
|
||||
private IEnumerable InvokeCoroutine(ControlOutput output)
|
||||
{
|
||||
var connection = output.connection;
|
||||
|
||||
if (connection == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
var input = connection.destination;
|
||||
|
||||
var recursionNode = new RecursionNode(output, stack);
|
||||
|
||||
BeforeInvoke(output, recursionNode);
|
||||
|
||||
if (input.supportsCoroutine)
|
||||
{
|
||||
foreach (var instruction in InvokeCoroutineDelegate(input))
|
||||
{
|
||||
if (instruction is ControlOutput)
|
||||
{
|
||||
foreach (var unwrappedInstruction in InvokeCoroutine((ControlOutput)instruction))
|
||||
{
|
||||
yield return unwrappedInstruction;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return instruction;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ControlOutput nextPort = InvokeDelegate(input);
|
||||
|
||||
if (nextPort != null)
|
||||
{
|
||||
foreach (var instruction in InvokeCoroutine(nextPort))
|
||||
{
|
||||
yield return instruction;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AfterInvoke(output, recursionNode);
|
||||
}
|
||||
|
||||
private RecursionNode BeforeInvoke(ControlOutput output, RecursionNode recursionNode)
|
||||
{
|
||||
try
|
||||
{
|
||||
recursion?.Enter(recursionNode);
|
||||
}
|
||||
catch (StackOverflowException ex)
|
||||
{
|
||||
output.unit.HandleException(stack, ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
var connection = output.connection;
|
||||
var input = connection.destination;
|
||||
|
||||
if (enableDebug)
|
||||
{
|
||||
var connectionEditorData = stack.GetElementDebugData<IUnitConnectionDebugData>(connection);
|
||||
var inputUnitEditorData = stack.GetElementDebugData<IUnitDebugData>(input.unit);
|
||||
|
||||
connectionEditorData.lastInvokeFrame = EditorTimeBinding.frame;
|
||||
connectionEditorData.lastInvokeTime = EditorTimeBinding.time;
|
||||
inputUnitEditorData.lastInvokeFrame = EditorTimeBinding.frame;
|
||||
inputUnitEditorData.lastInvokeTime = EditorTimeBinding.time;
|
||||
}
|
||||
|
||||
return recursionNode;
|
||||
}
|
||||
|
||||
private void AfterInvoke(ControlOutput output, RecursionNode recursionNode)
|
||||
{
|
||||
recursion?.Exit(recursionNode);
|
||||
}
|
||||
|
||||
private ControlOutput InvokeDelegate(ControlInput input)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (input.requiresCoroutine)
|
||||
{
|
||||
throw new InvalidOperationException($"Port '{input.key}' on '{input.unit}' can only be triggered in a coroutine.");
|
||||
}
|
||||
|
||||
return input.action(this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
input.unit.HandleException(stack, ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable InvokeCoroutineDelegate(ControlInput input)
|
||||
{
|
||||
var instructions = input.coroutineAction(this);
|
||||
|
||||
while (true)
|
||||
{
|
||||
object instruction;
|
||||
|
||||
try
|
||||
{
|
||||
if (!instructions.MoveNext())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
instruction = instructions.Current;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
input.unit.HandleException(stack, ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
yield return instruction;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Values
|
||||
|
||||
public bool IsLocal(IUnitValuePort port)
|
||||
{
|
||||
Ensure.That(nameof(port)).IsNotNull(port);
|
||||
|
||||
return locals.ContainsKey(port);
|
||||
}
|
||||
|
||||
public void SetValue(IUnitValuePort port, object value)
|
||||
{
|
||||
Ensure.That(nameof(port)).IsNotNull(port);
|
||||
Ensure.That(nameof(value)).IsOfType(value, port.type);
|
||||
|
||||
if (locals.ContainsKey(port))
|
||||
{
|
||||
locals[port] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
locals.Add(port, value);
|
||||
}
|
||||
}
|
||||
|
||||
public object GetValue(ValueInput input)
|
||||
{
|
||||
if (locals.TryGetValue(input, out var local))
|
||||
{
|
||||
return local;
|
||||
}
|
||||
|
||||
var connection = input.connection;
|
||||
|
||||
if (connection != null)
|
||||
{
|
||||
if (enableDebug)
|
||||
{
|
||||
var connectionEditorData = stack.GetElementDebugData<IUnitConnectionDebugData>(connection);
|
||||
|
||||
connectionEditorData.lastInvokeFrame = EditorTimeBinding.frame;
|
||||
connectionEditorData.lastInvokeTime = EditorTimeBinding.time;
|
||||
}
|
||||
|
||||
var output = connection.source;
|
||||
|
||||
var value = GetValue(output);
|
||||
|
||||
if (enableDebug)
|
||||
{
|
||||
var connectionEditorData = stack.GetElementDebugData<ValueConnection.DebugData>(connection);
|
||||
|
||||
connectionEditorData.lastValue = value;
|
||||
connectionEditorData.assignedLastValue = true;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
else if (TryGetDefaultValue(input, out var defaultValue))
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new MissingValuePortInputException(input.key);
|
||||
}
|
||||
}
|
||||
|
||||
private object GetValue(ValueOutput output)
|
||||
{
|
||||
if (locals.TryGetValue(output, out var local))
|
||||
{
|
||||
return local;
|
||||
}
|
||||
|
||||
if (!output.supportsFetch)
|
||||
{
|
||||
throw new InvalidOperationException($"The value of '{output.key}' on '{output.unit}' cannot be fetched dynamically, it must be assigned.");
|
||||
}
|
||||
|
||||
var recursionNode = new RecursionNode(output, stack);
|
||||
|
||||
try
|
||||
{
|
||||
recursion?.Enter(recursionNode);
|
||||
}
|
||||
catch (StackOverflowException ex)
|
||||
{
|
||||
output.unit.HandleException(stack, ex);
|
||||
throw;
|
||||
}
|
||||
|
||||
if (enableDebug)
|
||||
{
|
||||
var outputUnitEditorData = stack.GetElementDebugData<IUnitDebugData>(output.unit);
|
||||
|
||||
outputUnitEditorData.lastInvokeFrame = EditorTimeBinding.frame;
|
||||
outputUnitEditorData.lastInvokeTime = EditorTimeBinding.time;
|
||||
}
|
||||
|
||||
var value = GetValueDelegate(output);
|
||||
|
||||
recursion?.Exit(recursionNode);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public object GetValue(ValueInput input, Type type)
|
||||
{
|
||||
return ConversionUtility.Convert(GetValue(input), type);
|
||||
}
|
||||
|
||||
public T GetValue<T>(ValueInput input)
|
||||
{
|
||||
return (T)GetValue(input, typeof(T));
|
||||
}
|
||||
|
||||
public object GetConvertedValue(ValueInput input)
|
||||
{
|
||||
return GetValue(input, input.type);
|
||||
}
|
||||
|
||||
private object GetDefaultValue(ValueInput input)
|
||||
{
|
||||
if (!TryGetDefaultValue(input, out var defaultValue))
|
||||
{
|
||||
throw new InvalidOperationException("Value input port does not have a default value.");
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public bool TryGetDefaultValue(ValueInput input, out object defaultValue)
|
||||
{
|
||||
if (!input.unit.defaultValues.TryGetValue(input.key, out defaultValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (input.nullMeansSelf && defaultValue == null)
|
||||
{
|
||||
defaultValue = stack.self;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private object GetValueDelegate(ValueOutput output)
|
||||
{
|
||||
try
|
||||
{
|
||||
return output.getValue(this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
output.unit.HandleException(stack, ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static object FetchValue(ValueInput input, GraphReference reference)
|
||||
{
|
||||
var flow = New(reference);
|
||||
|
||||
var result = flow.GetValue(input);
|
||||
|
||||
flow.Dispose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static object FetchValue(ValueInput input, Type type, GraphReference reference)
|
||||
{
|
||||
return ConversionUtility.Convert(FetchValue(input, reference), type);
|
||||
}
|
||||
|
||||
public static T FetchValue<T>(ValueInput input, GraphReference reference)
|
||||
{
|
||||
return (T)FetchValue(input, typeof(T), reference);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Value Prediction
|
||||
|
||||
public static bool CanPredict(IUnitValuePort port, GraphReference reference)
|
||||
{
|
||||
Ensure.That(nameof(port)).IsNotNull(port);
|
||||
|
||||
var flow = New(reference);
|
||||
|
||||
flow.isPrediction = true;
|
||||
|
||||
bool canPredict;
|
||||
|
||||
if (port is ValueInput)
|
||||
{
|
||||
canPredict = flow.CanPredict((ValueInput)port);
|
||||
}
|
||||
else if (port is ValueOutput)
|
||||
{
|
||||
canPredict = flow.CanPredict((ValueOutput)port);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
flow.Dispose();
|
||||
|
||||
return canPredict;
|
||||
}
|
||||
|
||||
private bool CanPredict(ValueInput input)
|
||||
{
|
||||
if (!input.hasValidConnection)
|
||||
{
|
||||
if (!TryGetDefaultValue(input, out var defaultValue))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof(Component).IsAssignableFrom(input.type))
|
||||
{
|
||||
defaultValue = defaultValue?.ConvertTo(input.type);
|
||||
}
|
||||
|
||||
if (!input.allowsNull && defaultValue == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
var output = input.validConnectedPorts.Single();
|
||||
|
||||
if (!CanPredict(output))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var connectedValue = GetValue(output);
|
||||
|
||||
if (!ConversionUtility.CanConvert(connectedValue, input.type, false))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof(Component).IsAssignableFrom(input.type))
|
||||
{
|
||||
connectedValue = connectedValue?.ConvertTo(input.type);
|
||||
}
|
||||
|
||||
if (!input.allowsNull && connectedValue == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CanPredict(ValueOutput output)
|
||||
{
|
||||
// Shortcircuit the expensive check if the port isn't marked as predictable
|
||||
if (!output.supportsPrediction)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var recursionNode = new RecursionNode(output, stack);
|
||||
|
||||
if (!recursion?.TryEnter(recursionNode) ?? false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check each value dependency
|
||||
foreach (var relation in output.unit.relations.WithDestination(output))
|
||||
{
|
||||
if (relation.source is ValueInput)
|
||||
{
|
||||
var source = (ValueInput)relation.source;
|
||||
|
||||
if (!CanPredict(source))
|
||||
{
|
||||
recursion?.Exit(recursionNode);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var value = CanPredictDelegate(output);
|
||||
|
||||
recursion?.Exit(recursionNode);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
private bool CanPredictDelegate(ValueOutput output)
|
||||
{
|
||||
try
|
||||
{
|
||||
return output.canPredictValue(this);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogWarning($"Prediction check failed for '{output.key}' on '{output.unit}':\n{ex}");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static object Predict(IUnitValuePort port, GraphReference reference)
|
||||
{
|
||||
Ensure.That(nameof(port)).IsNotNull(port);
|
||||
|
||||
var flow = New(reference);
|
||||
|
||||
flow.isPrediction = true;
|
||||
|
||||
object value;
|
||||
|
||||
if (port is ValueInput)
|
||||
{
|
||||
value = flow.GetValue((ValueInput)port);
|
||||
}
|
||||
else if (port is ValueOutput)
|
||||
{
|
||||
value = flow.GetValue((ValueOutput)port);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
flow.Dispose();
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public static object Predict(IUnitValuePort port, GraphReference reference, Type type)
|
||||
{
|
||||
return ConversionUtility.Convert(Predict(port, reference), type);
|
||||
}
|
||||
|
||||
public static T Predict<T>(IUnitValuePort port, GraphReference pointer)
|
||||
{
|
||||
return (T)Predict(port, pointer, typeof(T));
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 1ad5fed5d057548d88cf8bf03518c101
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,174 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[SerializationVersion("A")]
|
||||
[DisplayName("Script Graph")]
|
||||
public sealed class FlowGraph : Graph, IGraphWithVariables, IGraphEventListener
|
||||
{
|
||||
public FlowGraph()
|
||||
{
|
||||
units = new GraphElementCollection<IUnit>(this);
|
||||
controlConnections = new GraphConnectionCollection<ControlConnection, ControlOutput, ControlInput>(this);
|
||||
valueConnections = new GraphConnectionCollection<ValueConnection, ValueOutput, ValueInput>(this);
|
||||
invalidConnections = new GraphConnectionCollection<InvalidConnection, IUnitOutputPort, IUnitInputPort>(this);
|
||||
groups = new GraphElementCollection<GraphGroup>(this);
|
||||
|
||||
elements.Include(units);
|
||||
elements.Include(controlConnections);
|
||||
elements.Include(valueConnections);
|
||||
elements.Include(invalidConnections);
|
||||
elements.Include(groups);
|
||||
|
||||
controlInputDefinitions = new UnitPortDefinitionCollection<ControlInputDefinition>();
|
||||
controlOutputDefinitions = new UnitPortDefinitionCollection<ControlOutputDefinition>();
|
||||
valueInputDefinitions = new UnitPortDefinitionCollection<ValueInputDefinition>();
|
||||
valueOutputDefinitions = new UnitPortDefinitionCollection<ValueOutputDefinition>();
|
||||
|
||||
variables = new VariableDeclarations();
|
||||
}
|
||||
|
||||
public override IGraphData CreateData()
|
||||
{
|
||||
return new FlowGraphData(this);
|
||||
}
|
||||
|
||||
public void StartListening(GraphStack stack)
|
||||
{
|
||||
stack.GetGraphData<FlowGraphData>().isListening = true;
|
||||
|
||||
foreach (var unit in units)
|
||||
{
|
||||
(unit as IGraphEventListener)?.StartListening(stack);
|
||||
}
|
||||
}
|
||||
|
||||
public void StopListening(GraphStack stack)
|
||||
{
|
||||
foreach (var unit in units)
|
||||
{
|
||||
(unit as IGraphEventListener)?.StopListening(stack);
|
||||
}
|
||||
|
||||
stack.GetGraphData<FlowGraphData>().isListening = false;
|
||||
}
|
||||
|
||||
public bool IsListening(GraphPointer pointer)
|
||||
{
|
||||
return pointer.GetGraphData<FlowGraphData>().isListening;
|
||||
}
|
||||
|
||||
#region Variables
|
||||
|
||||
[Serialize]
|
||||
public VariableDeclarations variables { get; private set; }
|
||||
|
||||
public IEnumerable<string> GetDynamicVariableNames(VariableKind kind, GraphReference reference)
|
||||
{
|
||||
return units.OfType<IUnifiedVariableUnit>()
|
||||
.Where(v => v.kind == kind && Flow.CanPredict(v.name, reference))
|
||||
.Select(v => Flow.Predict<string>(v.name, reference))
|
||||
.Where(name => !StringUtility.IsNullOrWhiteSpace(name))
|
||||
.Distinct()
|
||||
.OrderBy(name => name);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Elements
|
||||
|
||||
[DoNotSerialize]
|
||||
public GraphElementCollection<IUnit> units { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
public GraphConnectionCollection<ControlConnection, ControlOutput, ControlInput> controlConnections { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
public GraphConnectionCollection<ValueConnection, ValueOutput, ValueInput> valueConnections { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
public GraphConnectionCollection<InvalidConnection, IUnitOutputPort, IUnitInputPort> invalidConnections { get; private set; }
|
||||
|
||||
[DoNotSerialize]
|
||||
public GraphElementCollection<GraphGroup> groups { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Definition
|
||||
|
||||
private const string DefinitionRemoveWarningTitle = "Remove Port Definition";
|
||||
|
||||
private const string DefinitionRemoveWarningMessage = "Removing this definition will break any existing connection to this port. Are you sure you want to continue?";
|
||||
|
||||
[Serialize]
|
||||
[InspectorLabel("Trigger Inputs")]
|
||||
[InspectorWide(true)]
|
||||
[WarnBeforeRemoving(DefinitionRemoveWarningTitle, DefinitionRemoveWarningMessage)]
|
||||
public UnitPortDefinitionCollection<ControlInputDefinition> controlInputDefinitions { get; private set; }
|
||||
|
||||
[Serialize]
|
||||
[InspectorLabel("Trigger Outputs")]
|
||||
[InspectorWide(true)]
|
||||
[WarnBeforeRemoving(DefinitionRemoveWarningTitle, DefinitionRemoveWarningMessage)]
|
||||
public UnitPortDefinitionCollection<ControlOutputDefinition> controlOutputDefinitions { get; private set; }
|
||||
|
||||
[Serialize]
|
||||
[InspectorLabel("Data Inputs")]
|
||||
[InspectorWide(true)]
|
||||
[WarnBeforeRemoving(DefinitionRemoveWarningTitle, DefinitionRemoveWarningMessage)]
|
||||
public UnitPortDefinitionCollection<ValueInputDefinition> valueInputDefinitions { get; private set; }
|
||||
|
||||
[Serialize]
|
||||
[InspectorLabel("Data Outputs")]
|
||||
[InspectorWide(true)]
|
||||
[WarnBeforeRemoving(DefinitionRemoveWarningTitle, DefinitionRemoveWarningMessage)]
|
||||
public UnitPortDefinitionCollection<ValueOutputDefinition> valueOutputDefinitions { get; private set; }
|
||||
|
||||
public IEnumerable<IUnitPortDefinition> validPortDefinitions =>
|
||||
LinqUtility.Concat<IUnitPortDefinition>(controlInputDefinitions,
|
||||
controlOutputDefinitions,
|
||||
valueInputDefinitions,
|
||||
valueOutputDefinitions)
|
||||
.Where(upd => upd.isValid)
|
||||
.DistinctBy(upd => upd.key);
|
||||
|
||||
public event Action onPortDefinitionsChanged;
|
||||
|
||||
public void PortDefinitionsChanged()
|
||||
{
|
||||
onPortDefinitionsChanged?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static FlowGraph WithInputOutput()
|
||||
{
|
||||
return new FlowGraph()
|
||||
{
|
||||
units =
|
||||
{
|
||||
new GraphInput() { position = new Vector2(-250, -30) },
|
||||
new GraphOutput() { position = new Vector2(105, -30) }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static FlowGraph WithStartUpdate()
|
||||
{
|
||||
return new FlowGraph()
|
||||
{
|
||||
units =
|
||||
{
|
||||
new Start() { position = new Vector2(-204, -144) },
|
||||
new Update() { position = new Vector2(-204, 60) }
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e0e14a597602d4c3cb2960f771330b86
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,14 @@
|
|||
namespace Unity.VisualScripting
|
||||
{
|
||||
public sealed class FlowGraphData : GraphData<FlowGraph>, IGraphDataWithVariables, IGraphEventListenerData
|
||||
{
|
||||
public VariableDeclarations variables { get; }
|
||||
|
||||
public bool isListening { get; set; }
|
||||
|
||||
public FlowGraphData(FlowGraph definition) : base(definition)
|
||||
{
|
||||
variables = definition.variables.CloneViaFakeSerialization();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6d70db210652542ea80e2220dd47df74
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,64 @@
|
|||
using System.ComponentModel;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
[AddComponentMenu("Visual Scripting/Script Machine")]
|
||||
[RequireComponent(typeof(Variables))]
|
||||
[DisableAnnotation]
|
||||
[HelpURL("https://docs.unity3d.com/Packages/com.unity.visualscripting@latest/index.html?subfolder=/manual/vs-graphs-machines-macros.html")]
|
||||
[DisplayName("Script Machine")]
|
||||
public sealed class FlowMachine : EventMachine<FlowGraph, ScriptGraphAsset>
|
||||
{
|
||||
public override FlowGraph DefaultGraph()
|
||||
{
|
||||
return FlowGraph.WithStartUpdate();
|
||||
}
|
||||
|
||||
protected override void OnEnable()
|
||||
{
|
||||
if (hasGraph)
|
||||
{
|
||||
graph.StartListening(reference);
|
||||
}
|
||||
|
||||
base.OnEnable();
|
||||
}
|
||||
|
||||
protected override void OnInstantiateWhileEnabled()
|
||||
{
|
||||
if (hasGraph)
|
||||
{
|
||||
graph.StartListening(reference);
|
||||
}
|
||||
|
||||
base.OnInstantiateWhileEnabled();
|
||||
}
|
||||
|
||||
protected override void OnUninstantiateWhileEnabled()
|
||||
{
|
||||
base.OnUninstantiateWhileEnabled();
|
||||
|
||||
if (hasGraph)
|
||||
{
|
||||
graph.StopListening(reference);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnDisable()
|
||||
{
|
||||
base.OnDisable();
|
||||
|
||||
if (hasGraph)
|
||||
{
|
||||
graph.StopListening(reference);
|
||||
}
|
||||
}
|
||||
|
||||
[ContextMenu("Show Data...")]
|
||||
protected override void ShowData()
|
||||
{
|
||||
base.ShowData();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d2fe5546f775c47d2820aa5e8a715f53
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon:
|
||||
fileID: 2800000
|
||||
guid: fe5b77214c748b64ba5033f2af1b4c34
|
||||
type: 3
|
||||
userData: ''
|
||||
assetBundleName: ''
|
||||
assetBundleVariant: ''
|
||||
...
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6316762f06a0f4f66aed09829f59eb8c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 61c823205c2384c13b4d38c05194c1df
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
|
||||
namespace Unity.VisualScripting
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a struct with its default initializer.
|
||||
/// </summary>
|
||||
[SpecialUnit]
|
||||
public sealed class CreateStruct : Unit
|
||||
{
|
||||
[Obsolete(Serialization.ConstructorWarning)]
|
||||
public CreateStruct() : base() { }
|
||||
|
||||
public CreateStruct(Type type) : base()
|
||||
{
|
||||
Ensure.That(nameof(type)).IsNotNull(type);
|
||||
|
||||
if (!type.IsStruct())
|
||||
{
|
||||
throw new ArgumentException($"Type {type} must be a struct.", nameof(type));
|
||||
}
|
||||
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
[Serialize]
|
||||
public Type type { get; internal set; }
|
||||
|
||||
// Shouldn't happen through normal use, but can happen
|
||||
// if deserialization fails to find the type
|
||||
// https://support.ludiq.io/communities/5/topics/1661-x
|
||||
public override bool canDefine => type != null;
|
||||
|
||||
/// <summary>
|
||||
/// The entry point to create the struct. You can
|
||||
/// still get the return value without connecting this port.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlInput enter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The action to call once the struct has been created.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ControlOutput exit { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The created struct.
|
||||
/// </summary>
|
||||
[DoNotSerialize]
|
||||
[PortLabelHidden]
|
||||
public ValueOutput output { get; private set; }
|
||||
|
||||
protected override void Definition()
|
||||
{
|
||||
enter = ControlInput(nameof(enter), Enter);
|
||||
exit = ControlOutput(nameof(exit));
|
||||
output = ValueOutput(type, nameof(output), Create);
|
||||
|
||||
Succession(enter, exit);
|
||||
}
|
||||
|
||||
private ControlOutput Enter(Flow flow)
|
||||
{
|
||||
flow.SetValue(output, Activator.CreateInstance(type));
|
||||
|
||||
return exit;
|
||||
}
|
||||
|
||||
private object Create(Flow flow)
|
||||
{
|
||||
return Activator.CreateInstance(type);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: af2d94135152d4d9f8cbc7d76430b106
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue