Initial Commit

This commit is contained in:
Sebastian Cabrera 2021-08-02 05:44:37 -04:00
parent 53eb92e9af
commit 270ab7d11f
15341 changed files with 700234 additions and 0 deletions

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 5029d678f341c40e888b037abf7f6672
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fa1a96c0a3f154fbba933b74f7414a76
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b71fe733d92c1489cb3c89d5841ea222
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: dc809ec21d2bf44b983265840a5ed2f4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,9 @@
namespace Unity.VisualScripting
{
public interface IUnitConnectionDebugData : IGraphElementDebugData
{
int lastInvokeFrame { get; set; }
float lastInvokeTime { get; set; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9239efc28f45f4c848294b936db9bfa0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,4 @@
namespace Unity.VisualScripting
{
public interface IUnitRelation : IConnection<IUnitPort, IUnitPort> { }
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 20177830b472f40faa7374d0861ddad0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f564c98f800f2453595d4c4e261215ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1a8cc9b6e18b84c82a16ca1d5ae11643
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5b33449365cf844e986fad5c887be657
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f3617e416d65b4e44842280c042df8a5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b74709ab7c4674a84903b44120fcb9fb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e688b34379ad4462b284ae6a0cc3cdc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fac4026eb26ea42c29c85fec8007face
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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:

View file

@ -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
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ace4e9bf94df345f183dcb34a039e1d8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,4 @@
namespace Unity.VisualScripting.Dependencies.NCalc
{
public delegate void EvaluateFunctionHandler(Flow flow, string name, FunctionArgs args);
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f8b54261ed2a8497ebdd20f7ddbe1e6f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,4 @@
namespace Unity.VisualScripting.Dependencies.NCalc
{
public delegate void EvaluateParameterHandler(Flow flow, string name, ParameterArgs args);
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6180c9bbe40b44d45980f2b1a0325e58
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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) { }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6499d38cb236f4d369ab2ce0e0585ecc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b546fea2e0eea4ab98c47948cb30b03c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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>();
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b0e70958412a3493888a351443ad8866
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3dd474c9747c7444d9ca4b191b48301d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 55514a8464191433785154307e445337
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9e4f622436c3c477bb484bd0114b74cb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ccf41f828829d4eafa25be821b8e2a60
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2d630dc61d2194bf28114c04393228bb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6a2c35661d97146b2b302a94d1120f35
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;}
;

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 1d65eede9661f46a88d6e1949070eb9f
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d81700b95c8e3471584ffa94fd06b03f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 87e1dcf4a9a154ad291d02cad1d920ca
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b211073028ea4467ad1c7f48e0b3530
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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(") ");
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 207ce2879c26b4ef0a891282aa1dfb27
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1e339c45a25164bda8fb4ef97ff8d67e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c614751e0f58c477cb0e9658be0ebdc4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b7338a5f262ac4ecea97462e8eb78178
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0f417b240b9ac4d80a5526339bbe758a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b61cb47a7912b4a0ab155cee34717187
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8b277cd84c6a344269542f249c6feafc
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
using System;
namespace Unity.VisualScripting
{
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
public class PortLabelHiddenAttribute : Attribute
{ }
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 57ded228646794ea2a6e7b966f8d9880
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
using System;
namespace Unity.VisualScripting
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)]
public sealed class SpecialUnitAttribute : Attribute
{ }
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b2a4736f47f7048e0babf1936b9f5ced
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f4fd0e2acdd2492ca24c2cc723baf81
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b6e9e78d67fd046868b0433247b23fad
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d3e4ca0c5150d447abfbdc84355362b9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 99ce26428a6ff4f419011392de515c3a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bc18ef3f025fa46629333873a5c1e2a5
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e03bac02ee3494a1184f0544b7f3afe1
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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; }
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 37d3ce74ab2f0451db3e7823e0305c2a
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1ad5fed5d057548d88cf8bf03518c101
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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) }
}
};
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e0e14a597602d4c3cb2960f771330b86
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6d70db210652542ea80e2220dd47df74
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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();
}
}
}

View file

@ -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: ''
...

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6316762f06a0f4f66aed09829f59eb8c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 61c823205c2384c13b4d38c05194c1df
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}

View file

@ -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