Added support for coroutines
This commit is contained in:
parent
237f2fefd9
commit
b475bd4495
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using Upsilon.Evaluator;
|
||||
using Upsilon.Text;
|
||||
|
||||
|
@ -17,5 +18,6 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
|||
}
|
||||
|
||||
public abstract ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span);
|
||||
public abstract IEnumerator RunCoroutine(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
@ -47,7 +48,31 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
|||
|
||||
public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span)
|
||||
{
|
||||
var objects = new List<object>();
|
||||
var result = Execute(diagnostics, variables, script, scope, span);
|
||||
if (_directTypeManipulation)
|
||||
return (ScriptType)result;
|
||||
return result.ToScriptType();
|
||||
|
||||
}
|
||||
|
||||
public override IEnumerator RunCoroutine(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope,
|
||||
TextSpan span)
|
||||
{
|
||||
var result = Execute(diagnostics, variables, script, scope, span);
|
||||
if (result is IEnumerator enumerator)
|
||||
{
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
yield return enumerator.Current;
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
yield return result;
|
||||
}
|
||||
|
||||
private object Execute(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span)
|
||||
{
|
||||
var objects = new List<object>();
|
||||
if (_passScriptReference)
|
||||
objects.Add(script);
|
||||
if (_passScopeReference)
|
||||
|
@ -129,10 +154,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
|||
if (e.InnerException != null) throw e.InnerException;
|
||||
throw;
|
||||
}
|
||||
if (_directTypeManipulation)
|
||||
return (ScriptType)result;
|
||||
return result.ToScriptType();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
|
@ -33,6 +33,22 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
|||
return option.Run(diagnostics, variables, script, scope, span);
|
||||
}
|
||||
|
||||
public override IEnumerator RunCoroutine(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope,
|
||||
TextSpan span)
|
||||
{
|
||||
var option = GetValidOption(variables);
|
||||
if (option == null)
|
||||
{
|
||||
throw new EvaluationException(script.FileName,
|
||||
$"No valid function found", span);
|
||||
}
|
||||
var coroutine = option.RunCoroutine(diagnostics, variables, script, scope, span);
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
yield return coroutine.Current;
|
||||
}
|
||||
}
|
||||
|
||||
public ScriptRuntimeFunctionOption GetValidOption(object[] variables)
|
||||
{
|
||||
if (variables == null)
|
||||
|
@ -163,7 +179,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
|||
EvaluationScope = scope;
|
||||
}
|
||||
|
||||
public EvaluationScope EvaluationScope { get; set; }
|
||||
public EvaluationScope EvaluationScope { get; private set; }
|
||||
|
||||
public BoundBlockStatement Block { get; }
|
||||
public ImmutableArray<BoundVariableSymbol> Parameters { get; }
|
||||
|
@ -188,6 +204,28 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
|||
Block.Evaluate(innerScope, diagnostics, ref state);
|
||||
return state.ReturnValue;
|
||||
}
|
||||
|
||||
public IEnumerator RunCoroutine(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope outerScope, TextSpan span)
|
||||
{
|
||||
var innerScope = new EvaluationScope(EvaluationScope);
|
||||
var state = new EvaluationState()
|
||||
{
|
||||
Script = script,
|
||||
};
|
||||
if (Parameters != null)
|
||||
{
|
||||
for (var i = 0; i < Parameters.Length; i++)
|
||||
{
|
||||
var parameterVariable = Parameters[i];
|
||||
var parameterValue = variables[i];
|
||||
innerScope.CreateLocal(parameterVariable.VariableSymbol, parameterValue);
|
||||
}
|
||||
}
|
||||
|
||||
return Block.EvaluateCoroutine(innerScope, diagnostics, state);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -92,7 +92,8 @@ namespace Upsilon.Binder
|
|||
return BindGenericForStatement((GenericForStatementSyntax) s);
|
||||
case SyntaxKind.WhileStatement:
|
||||
return BindWhileStatement((WhileStatementSyntax) s);
|
||||
|
||||
case SyntaxKind.YieldStatement:
|
||||
return BindYieldStatement((YieldStatementSyntax) s);
|
||||
case SyntaxKind.BreakStatement:
|
||||
return new BoundBreakStatement(s.Span);
|
||||
}
|
||||
|
@ -685,7 +686,7 @@ namespace Upsilon.Binder
|
|||
var returnType = Scope.ReturnType;
|
||||
Scope = Scope.ParentScope;
|
||||
var func = new BoundFunctionExpression(parameters.ToImmutable(), (BoundBlockStatement) block, e.Span,
|
||||
innerScope, returnType);
|
||||
innerScope, returnType, e.IsCoroutine);
|
||||
return func;
|
||||
}
|
||||
else
|
||||
|
@ -719,7 +720,8 @@ namespace Upsilon.Binder
|
|||
|
||||
if (!Scope.TryGetVariable(name, !isLocal, out var variable))
|
||||
{
|
||||
var functionVariable = new ScriptFunctionVariableSymbol(name, isLocal, parameters.ToImmutable(), func.ReturnType)
|
||||
var functionVariable = new ScriptFunctionVariableSymbol(name, isLocal, parameters.ToImmutable(), func.ReturnType
|
||||
, func.IsCoroutine)
|
||||
{
|
||||
CommentValue = commentData.ToArray()
|
||||
};
|
||||
|
@ -1072,5 +1074,10 @@ namespace Upsilon.Binder
|
|||
return new BoundWhileStatement(condition, block, e.Span);
|
||||
}
|
||||
|
||||
private BoundYieldStatement BindYieldStatement(YieldStatementSyntax yieldStatementSyntax)
|
||||
{
|
||||
var expression = BindExpression(yieldStatementSyntax.Expression);
|
||||
return new BoundYieldStatement(expression, yieldStatementSyntax.Span);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,14 +13,16 @@ namespace Upsilon.Binder
|
|||
{
|
||||
public ImmutableArray<BoundVariableSymbol> Parameters { get; }
|
||||
public BoundBlockStatement Block { get; set; }
|
||||
public bool IsCoroutine { get; }
|
||||
|
||||
public BoundFunctionExpression(ImmutableArray<BoundVariableSymbol> parameters, BoundBlockStatement block,
|
||||
TextSpan span, BoundScope scope, Type returnType) : base(span)
|
||||
TextSpan span, BoundScope scope, Type returnType, bool isCoroutine) : base(span)
|
||||
{
|
||||
Parameters = parameters;
|
||||
Block = block;
|
||||
Scope = scope;
|
||||
ReturnType = returnType;
|
||||
IsCoroutine = isCoroutine;
|
||||
}
|
||||
|
||||
public override BoundKind Kind => BoundKind.BoundFunctionExpression;
|
||||
|
|
|
@ -31,5 +31,6 @@ namespace Upsilon.Binder
|
|||
BoundGenericForStatement,
|
||||
BoundBreakStatement,
|
||||
BoundWhileStatement,
|
||||
BoundYieldStatement
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Upsilon.Evaluator;
|
||||
|
@ -32,5 +33,21 @@ namespace Upsilon.Binder
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
|
||||
{
|
||||
foreach (var statement in Statements)
|
||||
{
|
||||
var coroutine = statement.EvaluateCoroutine(scope, diagnostics, state);
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
yield return coroutine.Current;
|
||||
}
|
||||
if (state.Returned)
|
||||
yield break;
|
||||
if (state.HasBroken)
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.Evaluator;
|
||||
using Upsilon.Text;
|
||||
|
@ -21,5 +22,11 @@ namespace Upsilon.Binder
|
|||
{
|
||||
state.HasBroken = true;
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
|
||||
{
|
||||
state.HasBroken = true;
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.Evaluator;
|
||||
using Upsilon.Text;
|
||||
|
@ -24,5 +25,12 @@ namespace Upsilon.Binder
|
|||
var value = Expression.Evaluate(scope, diagnostics, ref state);
|
||||
state.LastValue = value;
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
|
||||
{
|
||||
var value = Expression.Evaluate(scope, diagnostics, ref state);
|
||||
state.LastValue = value;
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.BaseTypes.ScriptFunction;
|
||||
using Upsilon.Binder.VariableSymbols;
|
||||
|
@ -51,5 +52,11 @@ namespace Upsilon.Binder
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
|
||||
{
|
||||
Evaluate(scope, diagnostics, ref state);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Upsilon.BaseTypes;
|
||||
|
@ -81,5 +82,57 @@ namespace Upsilon.Binder
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope outerScope, Diagnostics diagnostics, EvaluationState outerState)
|
||||
{
|
||||
var innerScope = new EvaluationScope(outerScope);
|
||||
var innerState = new EvaluationState()
|
||||
{
|
||||
Script = outerState.Script,
|
||||
};
|
||||
|
||||
var enumeratorObject = BoundEnumerableExpression.Evaluate(outerScope, diagnostics, ref innerState);
|
||||
if (!(enumeratorObject is IIterable iterable))
|
||||
{
|
||||
throw new Exception($"Can't iterate over object with type '{enumeratorObject.Type}'");
|
||||
}
|
||||
|
||||
using (var enumerator = iterable.GetScriptEnumerator())
|
||||
{
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
var current = enumerator.Current;
|
||||
if (current == null)
|
||||
{
|
||||
throw new Exception($"Can't assign result value of nothing to multiple values");
|
||||
}
|
||||
if (current.Type != BaseTypes.Type.Table)
|
||||
{
|
||||
throw new Exception($"Can't assign result value with type '{current.Type}' to multiple values");
|
||||
}
|
||||
|
||||
var table = (SimpleScriptTable)current;
|
||||
if (Variables[0].VariableSymbol.Name != "_")
|
||||
innerScope.CreateLocal(Variables[0].VariableSymbol, table[0].ToScriptType());
|
||||
if (Variables[1].VariableSymbol.Name != "_")
|
||||
innerScope.CreateLocal(Variables[1].VariableSymbol, table[1]);
|
||||
|
||||
var coroutine = Block.EvaluateCoroutine(innerScope, diagnostics, innerState);
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
yield return coroutine.Current;
|
||||
}
|
||||
if (innerState.HasBroken || innerState.Returned)
|
||||
{
|
||||
if (innerState.Returned)
|
||||
{
|
||||
outerState.Returned = true;
|
||||
outerState.ReturnValue = innerState.ReturnValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.BaseTypes;
|
||||
using Upsilon.Evaluator;
|
||||
|
@ -57,6 +58,21 @@ namespace Upsilon.Binder
|
|||
ElseStatement?.Evaluate(scope, diagnostics, ref state);
|
||||
}
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
|
||||
{
|
||||
var condition = Condition.Expression.Evaluate(scope, diagnostics, ref state);
|
||||
if ((ScriptBoolean) condition)
|
||||
{
|
||||
return Block.EvaluateCoroutine(scope, diagnostics, state);
|
||||
}
|
||||
if (NextElseIf != null)
|
||||
{
|
||||
return NextElseIf.EvaluateCoroutine(scope, diagnostics, state);
|
||||
}
|
||||
|
||||
return ElseStatement?.EvaluateCoroutine(scope, diagnostics, state);
|
||||
}
|
||||
}
|
||||
|
||||
public class BoundElseStatement : BoundStatement
|
||||
|
@ -79,5 +95,10 @@ namespace Upsilon.Binder
|
|||
{
|
||||
Block.Evaluate(scope, diagnostics, ref state);
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
|
||||
{
|
||||
return Block.EvaluateCoroutine(scope, diagnostics, state);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Upsilon.BaseTypes.Number;
|
||||
|
@ -52,5 +53,11 @@ namespace Upsilon.Binder
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
|
||||
{
|
||||
Evaluate(scope, diagnostics, ref state);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.BaseTypes.Number;
|
||||
using Upsilon.Binder.VariableSymbols;
|
||||
|
@ -84,5 +85,63 @@ namespace Upsilon.Binder
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope outerScope, Diagnostics diagnostics, EvaluationState outerState)
|
||||
{
|
||||
var innerScope = new EvaluationScope(outerScope);
|
||||
var innerState = new EvaluationState()
|
||||
{
|
||||
Script = outerState.Script,
|
||||
};
|
||||
|
||||
var startVal = (ScriptNumberLong) BoundStart.Evaluate(innerScope, diagnostics, ref innerState);
|
||||
innerScope.CreateLocal(Variable, startVal);
|
||||
var stopVal = (ScriptNumberLong)BoundStop.Evaluate(innerScope, diagnostics, ref innerState);
|
||||
long step = 1;
|
||||
if (BoundStep != null)
|
||||
{
|
||||
var stepVal = (ScriptNumberLong) BoundStep.Evaluate(innerScope, diagnostics, ref innerState);
|
||||
step = stepVal.Value;
|
||||
}
|
||||
|
||||
if (step > 0)
|
||||
{
|
||||
for (; startVal.Value <= stopVal.Value; startVal.Value = startVal.Value + step)
|
||||
{
|
||||
var coroutine = Block.EvaluateCoroutine(innerScope, diagnostics, innerState);
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
yield return coroutine.Current;
|
||||
}
|
||||
if (innerState.HasBroken)
|
||||
break;
|
||||
if (innerState.Returned)
|
||||
{
|
||||
outerState.Returned = true;
|
||||
outerState.ReturnValue = innerState.ReturnValue;
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (step < 0)
|
||||
{
|
||||
for (; startVal.Value >= stopVal.Value; startVal.Value = startVal.Value + step)
|
||||
{
|
||||
var coroutine = Block.EvaluateCoroutine(innerScope, diagnostics, innerState);
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
yield return coroutine.Current;
|
||||
}
|
||||
if (innerState.HasBroken)
|
||||
break;
|
||||
if (innerState.Returned)
|
||||
{
|
||||
outerState.Returned = true;
|
||||
outerState.ReturnValue = innerState.ReturnValue;
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.Evaluator;
|
||||
using Upsilon.Text;
|
||||
|
@ -25,5 +26,12 @@ namespace Upsilon.Binder
|
|||
state.Returned = true;
|
||||
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
|
||||
{
|
||||
state.ReturnValue = Expression?.Evaluate(scope, diagnostics, ref state);
|
||||
state.Returned = true;
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.Evaluator;
|
||||
using Upsilon.Text;
|
||||
|
@ -31,5 +32,10 @@ namespace Upsilon.Binder
|
|||
{
|
||||
Statement.Evaluate(scope, diagnostics, ref state);
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
|
||||
{
|
||||
return Statement.EvaluateCoroutine(scope, diagnostics, state);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using Upsilon.Evaluator;
|
||||
using Upsilon.Text;
|
||||
|
||||
|
@ -12,5 +13,8 @@ namespace Upsilon.Binder
|
|||
internal bool HasBreakpoint { get; set; }
|
||||
|
||||
internal abstract void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state);
|
||||
|
||||
internal abstract IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics,
|
||||
EvaluationState state);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.BaseTypes;
|
||||
using Upsilon.BaseTypes.ScriptTypeInterfaces;
|
||||
|
@ -51,5 +52,11 @@ namespace Upsilon.Binder
|
|||
}
|
||||
indexable.Set(diagnostics, Span, index.ToString().ToScriptType(), value);
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
|
||||
{
|
||||
Evaluate(scope, diagnostics, ref state);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.Evaluator;
|
||||
using Upsilon.Text;
|
||||
|
@ -38,5 +39,11 @@ namespace Upsilon.Binder
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
|
||||
{
|
||||
Evaluate(scope, diagnostics, ref state);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.BaseTypes;
|
||||
using Upsilon.Evaluator;
|
||||
|
@ -49,5 +50,33 @@ namespace Upsilon.Binder
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope outerScope, Diagnostics diagnostics, EvaluationState outerState)
|
||||
{
|
||||
var innerScope = new EvaluationScope(outerScope);
|
||||
var innerState = new EvaluationState()
|
||||
{
|
||||
Script = outerState.Script
|
||||
};
|
||||
|
||||
|
||||
while ((ScriptBoolean) Condition.Evaluate(innerScope, diagnostics, ref innerState))
|
||||
{
|
||||
var coroutine = Block.EvaluateCoroutine(innerScope, diagnostics, innerState);
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
yield return coroutine.Current;
|
||||
}
|
||||
if (innerState.HasBroken || innerState.Returned)
|
||||
{
|
||||
if (innerState.Returned)
|
||||
{
|
||||
outerState.Returned = true;
|
||||
outerState.ReturnValue = innerState.ReturnValue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.BaseTypes;
|
||||
using Upsilon.BaseTypes.ScriptFunction;
|
||||
using Upsilon.Evaluator;
|
||||
using Upsilon.Exceptions;
|
||||
using Upsilon.Text;
|
||||
using Type = Upsilon.BaseTypes.Type;
|
||||
|
||||
namespace Upsilon.Binder
|
||||
{
|
||||
public class BoundYieldStatement : BoundStatement
|
||||
{
|
||||
public BoundExpression Expression { get; }
|
||||
|
||||
public BoundYieldStatement(BoundExpression expression, TextSpan span) : base(span)
|
||||
{
|
||||
Expression = expression;
|
||||
}
|
||||
|
||||
public override BoundKind Kind => BoundKind.BoundYieldStatement;
|
||||
public override IEnumerable<BoundNode> GetChildren()
|
||||
{
|
||||
yield return Expression;
|
||||
}
|
||||
|
||||
internal override void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state)
|
||||
{
|
||||
throw new ScriptRuntimeException(state.Script.FileName,
|
||||
"Yielding in a function that's not executed as a coroutine is not possible.", Span,
|
||||
diagnostics.ScriptString.GetLine(Span.StartLine));
|
||||
}
|
||||
|
||||
internal override IEnumerator EvaluateCoroutine(EvaluationScope scope, Diagnostics diagnostics, EvaluationState state)
|
||||
{
|
||||
if (Expression.Kind == BoundKind.BoundFunctionCallExpression)
|
||||
{
|
||||
var functionCall = (BoundFunctionCallExpression) Expression;
|
||||
var variable = functionCall.Identifier.Evaluate(scope, diagnostics, ref state);
|
||||
if (!(variable is ScriptFunction function))
|
||||
{
|
||||
throw new EvaluationException(state.Script.FileName, $"Variable is not a function.", functionCall.Identifier.Span);
|
||||
}
|
||||
var ls = new List<ScriptType>();
|
||||
foreach (var t in functionCall.Parameters)
|
||||
{
|
||||
var evaluate = t.Evaluate(scope, diagnostics, ref state);
|
||||
ls.Add(evaluate);
|
||||
}
|
||||
var coroutine = function.RunCoroutine(diagnostics, ls.ToArray(), state.Script, scope, Span);
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
yield return coroutine.Current;
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
var value = Expression.Evaluate(scope, diagnostics, ref state);
|
||||
yield return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ namespace Upsilon.Binder
|
|||
{
|
||||
public UnboundFunctionExpression(ImmutableArray<BoundVariableSymbol> parameters,
|
||||
BlockStatementSyntax unboundBlock, TextSpan span, BoundScope scope, string name)
|
||||
: base(parameters, null, span, scope, BaseTypes.Type.Unknown)
|
||||
: base(parameters, null, span, scope, BaseTypes.Type.Unknown, false)
|
||||
{
|
||||
UnboundBlock = unboundBlock;
|
||||
Name = name;
|
||||
|
|
|
@ -5,13 +5,15 @@ using Upsilon.BaseTypes;
|
|||
|
||||
namespace Upsilon.Binder.VariableSymbols
|
||||
{
|
||||
public abstract class FunctionVariableSymbol : VariableSymbol
|
||||
public abstract class FunctionVariableSymbol : VariableSymbol
|
||||
{
|
||||
public List<FunctionVariableSymbolOption> FunctionOption { get; protected set; } = new List<FunctionVariableSymbolOption>();
|
||||
public bool IsCoroutine { get; }
|
||||
|
||||
public FunctionVariableSymbol(string name, bool local, Type resultType)
|
||||
public FunctionVariableSymbol(string name, bool local, bool isCoroutine)
|
||||
: base(name, Type.Function, local)
|
||||
{
|
||||
IsCoroutine = isCoroutine;
|
||||
}
|
||||
|
||||
public abstract bool ValidateParameters(
|
||||
|
|
|
@ -11,14 +11,15 @@ namespace Upsilon.Binder.VariableSymbols
|
|||
public class InternalFunctionVariableSymbol : FunctionVariableSymbol
|
||||
{
|
||||
public InternalFunctionVariableSymbol(string name, bool local, TypeContainer resultType,
|
||||
InternalFunctionParameter[] functionParameters, MethodInfo overrideResultType)
|
||||
: base(name, local, resultType)
|
||||
InternalFunctionParameter[] functionParameters, MethodInfo overrideResultType, bool isCoroutine)
|
||||
: base(name, local, isCoroutine)
|
||||
{
|
||||
FunctionOption.Add(new InternalFunctionVariableOption(resultType, functionParameters, overrideResultType));
|
||||
}
|
||||
|
||||
public InternalFunctionVariableSymbol(string name, bool local, Type resultType, List<FunctionVariableSymbolOption> options)
|
||||
: base(name, local, resultType)
|
||||
public InternalFunctionVariableSymbol(string name, bool local, List<FunctionVariableSymbolOption> options,
|
||||
bool isCoroutine)
|
||||
: base(name, local, isCoroutine)
|
||||
{
|
||||
FunctionOption = options;
|
||||
}
|
||||
|
|
|
@ -7,8 +7,9 @@ namespace Upsilon.Binder.VariableSymbols
|
|||
{
|
||||
public class ScriptFunctionVariableSymbol : FunctionVariableSymbol
|
||||
{
|
||||
public ScriptFunctionVariableSymbol(string name, bool local, ImmutableArray<VariableSymbol> parameters, Type resultType)
|
||||
: base(name, local, resultType)
|
||||
public ScriptFunctionVariableSymbol(string name, bool local, ImmutableArray<VariableSymbol> parameters, Type resultType,
|
||||
bool isCoroutine)
|
||||
: base(name, local, isCoroutine)
|
||||
{
|
||||
FunctionOption.Add(new ScriptFunctionVariableOption(resultType, parameters));
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ using Upsilon.BaseTypes;
|
|||
|
||||
namespace Upsilon.Evaluator
|
||||
{
|
||||
internal struct EvaluationState
|
||||
internal class EvaluationState
|
||||
{
|
||||
public Script Script;
|
||||
public bool Returned;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
using Upsilon.BaseTypes;
|
||||
using Upsilon.BaseTypes.ScriptFunction;
|
||||
|
@ -82,5 +83,28 @@ namespace Upsilon.Evaluator
|
|||
var result = option.Run(_diagnostics, parameters?.Select(x => x.ToScriptType()).ToArray(), _script, Scope, new TextSpan());
|
||||
return result;
|
||||
}
|
||||
|
||||
public IEnumerator EvaluateCoroutine(BoundScript e, string functionName, object[] parameters)
|
||||
{
|
||||
if (!Scope.TryGet(functionName, out var statement) || statement.Type != Type.Function)
|
||||
{
|
||||
throw new ArgumentException(($"Function '{functionName}' could not be found"));
|
||||
}
|
||||
var function = (ScriptRuntimeFunction) statement;
|
||||
var option = function.GetValidOption(parameters);
|
||||
if (option == null)
|
||||
{
|
||||
if (parameters == null)
|
||||
{
|
||||
throw new EvaluationException(_script.FileName,
|
||||
$"No function found with name '{functionName}' and no parameters.",
|
||||
e.Span);
|
||||
}
|
||||
throw new EvaluationException(_script.FileName,
|
||||
$"No function found with name '{functionName}' and available parameters {string.Join(", ", parameters.Select(x => $"{x.GetType().Name}"))}",
|
||||
e.Span);
|
||||
}
|
||||
return option.RunCoroutine(_diagnostics, parameters?.Select(x => x.ToScriptType()).ToArray(), _script, Scope, new TextSpan());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Upsilon.BaseTypes;
|
||||
|
@ -114,6 +115,18 @@ namespace Upsilon.Evaluator
|
|||
return Convert(Evaluator.Evaluate(Bind(), functionName, parameters));
|
||||
}
|
||||
|
||||
public IEnumerator EvaluateFunctionCoroutine(string functionName, object[] parameters = null)
|
||||
{
|
||||
var coroutine = Evaluator.EvaluateCoroutine(Bind(), functionName, parameters);
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
var current = coroutine.Current;
|
||||
if (current is ScriptType scriptType)
|
||||
yield return scriptType.ToCSharpObject();
|
||||
yield return current;
|
||||
}
|
||||
}
|
||||
|
||||
private static T Convert<T>(ScriptType t)
|
||||
{
|
||||
var result = UpsilonBinder.Default.ChangeType(t, typeof(T), CultureInfo.InvariantCulture);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections;
|
||||
using Upsilon.Binder;
|
||||
using Upsilon.Evaluator;
|
||||
|
||||
|
@ -89,5 +90,11 @@ namespace Upsilon
|
|||
var script = ParseInputAndEvaluate(input, options);
|
||||
return script.EvaluateFunction(function, parameters);
|
||||
}
|
||||
|
||||
public static IEnumerator EvaluateFunctionCoroutine(string input, string function, object[] parameters = null, ScriptOptions options = null)
|
||||
{
|
||||
var script = ParseInputAndEvaluate(input, options);
|
||||
return script.EvaluateFunctionCoroutine(function, parameters);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,10 +12,11 @@ namespace Upsilon.Parser
|
|||
public SyntaxToken CloseParenthesis { get; }
|
||||
public BlockStatementSyntax Block { get; }
|
||||
public SyntaxToken EndToken { get; }
|
||||
public bool IsCoroutine { get; }
|
||||
|
||||
public FunctionExpressionSyntax(SyntaxToken functionToken,
|
||||
SyntaxToken openParenthesis, ImmutableArray<ParameterToken> parameters, SyntaxToken closeParenthesis,
|
||||
BlockStatementSyntax block, SyntaxToken endToken)
|
||||
BlockStatementSyntax block, SyntaxToken endToken, bool isCoroutine)
|
||||
{
|
||||
FunctionToken = functionToken;
|
||||
OpenParenthesis = openParenthesis;
|
||||
|
@ -23,6 +24,7 @@ namespace Upsilon.Parser
|
|||
CloseParenthesis = closeParenthesis;
|
||||
Block = block;
|
||||
EndToken = endToken;
|
||||
IsCoroutine = isCoroutine;
|
||||
Span = TextSpan.Between(FunctionToken.Span, endToken.Span);
|
||||
}
|
||||
|
||||
|
|
|
@ -79,11 +79,19 @@ namespace Upsilon.Parser
|
|||
}
|
||||
if (Current.Kind == SyntaxKind.FunctionKeyword && Next.Kind != SyntaxKind.OpenParenthesis)
|
||||
{
|
||||
return ParseFunctionAssignmentStatement();
|
||||
return ParseFunctionAssignmentStatement(SyntaxKind.FunctionKeyword);
|
||||
}
|
||||
if (Current.Kind == SyntaxKind.LocalKeyword && Next.Kind == SyntaxKind.FunctionKeyword)
|
||||
{
|
||||
return ParseFunctionAssignmentStatement();
|
||||
return ParseFunctionAssignmentStatement(SyntaxKind.FunctionKeyword);
|
||||
}
|
||||
if (Current.Kind == SyntaxKind.CoroutineKeyword && Next.Kind != SyntaxKind.OpenParenthesis)
|
||||
{
|
||||
return ParseFunctionAssignmentStatement(SyntaxKind.CoroutineKeyword);
|
||||
}
|
||||
if (Current.Kind == SyntaxKind.LocalKeyword && Next.Kind == SyntaxKind.CoroutineKeyword)
|
||||
{
|
||||
return ParseFunctionAssignmentStatement(SyntaxKind.CoroutineKeyword);
|
||||
}
|
||||
if (Current.Kind == SyntaxKind.ForKeyword)
|
||||
{
|
||||
|
@ -97,6 +105,10 @@ namespace Upsilon.Parser
|
|||
{
|
||||
return new BreakStatementSyntax(NextToken());
|
||||
}
|
||||
if (Current.Kind == SyntaxKind.YieldKeyword)
|
||||
{
|
||||
return ParseYieldStatement();
|
||||
}
|
||||
|
||||
return ParseExpressionStatement();
|
||||
}
|
||||
|
@ -210,9 +222,9 @@ namespace Upsilon.Parser
|
|||
}
|
||||
|
||||
|
||||
private ExpressionSyntax ParseFunctionExpression()
|
||||
private ExpressionSyntax ParseFunctionExpression(SyntaxKind openKeyword)
|
||||
{
|
||||
var functionToken = MatchToken(SyntaxKind.FunctionKeyword);
|
||||
var functionToken = MatchToken(openKeyword);
|
||||
var openParenthesis = MatchToken(SyntaxKind.OpenParenthesis);
|
||||
var variableBuilder = ImmutableArray.CreateBuilder<ParameterToken>();
|
||||
SyntaxToken current = null;
|
||||
|
@ -239,11 +251,13 @@ namespace Upsilon.Parser
|
|||
var closeParenthesis = MatchToken(SyntaxKind.CloseParenthesis);
|
||||
var block = ParseBlockStatement(new[] {SyntaxKind.EndKeyword});
|
||||
var endToken = MatchToken(SyntaxKind.EndKeyword);
|
||||
|
||||
var isCoroutine = openKeyword == SyntaxKind.CoroutineKeyword;
|
||||
return new FunctionExpressionSyntax(functionToken, openParenthesis,
|
||||
variableBuilder.ToImmutable(), closeParenthesis, (BlockStatementSyntax) block, endToken);
|
||||
variableBuilder.ToImmutable(), closeParenthesis, (BlockStatementSyntax) block, endToken, isCoroutine);
|
||||
}
|
||||
|
||||
private StatementSyntax ParseFunctionAssignmentStatement()
|
||||
private StatementSyntax ParseFunctionAssignmentStatement(SyntaxKind identifyingKeyword)
|
||||
{
|
||||
SyntaxToken localToken = null;
|
||||
string[] commentData = null;
|
||||
|
@ -252,7 +266,7 @@ namespace Upsilon.Parser
|
|||
localToken = NextToken();
|
||||
commentData = localToken.CommentData;
|
||||
}
|
||||
var functionToken = MatchToken(SyntaxKind.FunctionKeyword);
|
||||
var functionToken = MatchToken(identifyingKeyword);
|
||||
if (commentData == null)
|
||||
{
|
||||
commentData = functionToken.CommentData;
|
||||
|
@ -278,8 +292,10 @@ namespace Upsilon.Parser
|
|||
var closeParenthesis = MatchToken(SyntaxKind.CloseParenthesis);
|
||||
var block = ParseBlockStatement(new[] {SyntaxKind.EndKeyword});
|
||||
var endToken = MatchToken(SyntaxKind.EndKeyword);
|
||||
|
||||
var isCoroutine = identifyingKeyword == SyntaxKind.CoroutineKeyword;
|
||||
var functionExpression = new FunctionExpressionSyntax(functionToken, openParenthesis,
|
||||
variableBuilder.ToImmutable(), closeParenthesis, (BlockStatementSyntax) block, endToken);
|
||||
variableBuilder.ToImmutable(), closeParenthesis, (BlockStatementSyntax) block, endToken, isCoroutine);
|
||||
return new FunctionAssignmentStatementSyntax(localToken, (IdentifierToken) identifier, functionExpression)
|
||||
{
|
||||
CommentData = commentData
|
||||
|
@ -309,12 +325,24 @@ namespace Upsilon.Parser
|
|||
return new ReturnStatementSyntax(returnToken, expression);
|
||||
}
|
||||
|
||||
private StatementSyntax ParseYieldStatement()
|
||||
{
|
||||
var yieldToken = MatchToken(SyntaxKind.YieldKeyword);
|
||||
var expression = ParseExpression();
|
||||
return new YieldStatementSyntax(yieldToken, expression);
|
||||
}
|
||||
|
||||
|
||||
private ExpressionSyntax ParseExpression()
|
||||
{
|
||||
ExpressionSyntax expression;
|
||||
if (Current.Kind == SyntaxKind.FunctionKeyword && Next.Kind == SyntaxKind.OpenParenthesis)
|
||||
{
|
||||
expression = ParseFunctionExpression();
|
||||
expression = ParseFunctionExpression(SyntaxKind.FunctionKeyword);
|
||||
}
|
||||
else if (Current.Kind == SyntaxKind.CoroutineKeyword && Next.Kind == SyntaxKind.OpenParenthesis)
|
||||
{
|
||||
expression = ParseFunctionExpression(SyntaxKind.FunctionKeyword);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Upsilon.Parser
|
||||
{
|
||||
public class YieldStatementSyntax : StatementSyntax
|
||||
{
|
||||
public SyntaxToken YieldToken { get; }
|
||||
public ExpressionSyntax Expression { get; }
|
||||
|
||||
public YieldStatementSyntax(SyntaxToken yieldToken, ExpressionSyntax expression)
|
||||
{
|
||||
YieldToken = yieldToken;
|
||||
Expression = expression;
|
||||
}
|
||||
|
||||
public override SyntaxKind Kind => SyntaxKind.YieldStatement;
|
||||
public override IEnumerable<SyntaxNode> ChildNodes()
|
||||
{
|
||||
yield return YieldToken;
|
||||
yield return Expression;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,6 +44,10 @@ namespace Upsilon.Parser
|
|||
return SyntaxKind.DoKeyword;
|
||||
case "break":
|
||||
return SyntaxKind.BreakKeyword;
|
||||
case "yield":
|
||||
return SyntaxKind.YieldKeyword;
|
||||
case "coroutine":
|
||||
return SyntaxKind.CoroutineKeyword;
|
||||
default:
|
||||
return SyntaxKind.Identifier;
|
||||
}
|
||||
|
|
|
@ -56,6 +56,8 @@ namespace Upsilon.Parser
|
|||
InKeyword,
|
||||
DoKeyword,
|
||||
BreakKeyword,
|
||||
YieldKeyword,
|
||||
CoroutineKeyword,
|
||||
|
||||
Identifier,
|
||||
Parameter,
|
||||
|
@ -90,6 +92,7 @@ namespace Upsilon.Parser
|
|||
NumericForStatement,
|
||||
BreakStatement,
|
||||
GenericForStatement,
|
||||
WhileStatement
|
||||
WhileStatement,
|
||||
YieldStatement,
|
||||
}
|
||||
}
|
|
@ -64,7 +64,7 @@ namespace Upsilon.StandardLibraries
|
|||
var derivedType = DeriveValidTypes(typeInfo.Type);
|
||||
return new InternalFunctionVariableSymbol.InternalFunctionParameter(func.Key, derivedType,
|
||||
typeInfo.IsOptional);
|
||||
}).ToArray(), func.Value.MethodInfoFunction.Method.GetMethods()[0].Attribute.OverrideReturnType)
|
||||
}).ToArray(), func.Value.MethodInfoFunction.Method.GetMethods()[0].Attribute.OverrideReturnType, false)
|
||||
{
|
||||
CommentValue = func.Value.CommentValue?.Split('\n')
|
||||
};
|
||||
|
@ -139,7 +139,7 @@ namespace Upsilon.StandardLibraries
|
|||
}
|
||||
|
||||
var result = genericParameters[genericParameters.Length - 1].GetScriptType();
|
||||
return new InternalFunctionVariableSymbol(name, true, result, parameters.ToArray(), null);
|
||||
return new InternalFunctionVariableSymbol(name, true, Type.Nil, parameters.ToArray(), null, false);
|
||||
}
|
||||
|
||||
private static VariableSymbol BuildActionVariableSymbol(string name, System.Type type)
|
||||
|
@ -147,7 +147,8 @@ namespace Upsilon.StandardLibraries
|
|||
var genericParameters = type.GetGenericArguments();
|
||||
return new InternalFunctionVariableSymbol(name, true, Type.Nil,
|
||||
genericParameters.Select(DeriveValidTypes).Select(t =>
|
||||
new InternalFunctionVariableSymbol.InternalFunctionParameter(name, t, false)).ToArray(), null);
|
||||
new InternalFunctionVariableSymbol.InternalFunctionParameter(name, t, false)).ToArray(), null,
|
||||
false);
|
||||
}
|
||||
|
||||
public static TypeContainer DeriveValidTypes(System.Type type)
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon;
|
||||
using Upsilon.BaseTypes.UserData;
|
||||
using Xunit;
|
||||
|
||||
namespace UpsilonTests.GeneralTests
|
||||
{
|
||||
public class CoroutineTests
|
||||
{
|
||||
[Fact]
|
||||
public void BasicCoroutineTest()
|
||||
{
|
||||
const string input = @"
|
||||
coroutine testCoroutine()
|
||||
yield 5
|
||||
yield 1000
|
||||
yield ""test""
|
||||
yield nil
|
||||
end
|
||||
";
|
||||
var coroutine = Executor.EvaluateFunctionCoroutine(input, "testCoroutine");
|
||||
var count = 0;
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
var value = coroutine.Current;
|
||||
switch (count)
|
||||
{
|
||||
case 0:
|
||||
Assert.Equal(5L, value);
|
||||
break;
|
||||
case 1:
|
||||
Assert.Equal(1000L, value);
|
||||
break;
|
||||
case 2:
|
||||
Assert.Equal("test", value);
|
||||
break;
|
||||
case 3:
|
||||
Assert.Null(value);
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
Assert.Equal(4, count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyCoroutineTest()
|
||||
{
|
||||
const string input = @"
|
||||
coroutine testCoroutine()
|
||||
end
|
||||
";
|
||||
var coroutine = Executor.EvaluateFunctionCoroutine(input, "testCoroutine");
|
||||
var count = 0;
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
count++;
|
||||
}
|
||||
Assert.Equal(0, count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedCoroutineTest()
|
||||
{
|
||||
const string input = @"
|
||||
coroutine testCoroutine()
|
||||
for i = 0, 9 do
|
||||
yield nil
|
||||
end
|
||||
end
|
||||
";
|
||||
var coroutine = Executor.EvaluateFunctionCoroutine(input, "testCoroutine");
|
||||
var count = 0;
|
||||
var objArray = new List<object>();
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
objArray.Add(coroutine.Current);
|
||||
count++;
|
||||
}
|
||||
Assert.Equal(10, count);
|
||||
Assert.All(objArray, Assert.Null);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(9)]
|
||||
[InlineData(20)]
|
||||
[InlineData(2)]
|
||||
public void FunctionNestedCoroutine(int max)
|
||||
{
|
||||
var input = @"
|
||||
coroutine lowerCoroutine(number max)
|
||||
for i = 0, max do
|
||||
yield nil
|
||||
end
|
||||
end
|
||||
|
||||
coroutine testCoroutine()
|
||||
yield lowerCoroutine(" + max + @")
|
||||
end
|
||||
";
|
||||
var coroutine = Executor.EvaluateFunctionCoroutine(input, "testCoroutine");
|
||||
var count = 0;
|
||||
var objArray = new List<object>();
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
objArray.Add(coroutine.Current);
|
||||
count++;
|
||||
}
|
||||
Assert.Equal(max + 1, count);
|
||||
Assert.All(objArray, Assert.Null);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CoroutineReturnTest()
|
||||
{
|
||||
const string input = @"
|
||||
coroutine testCoroutine()
|
||||
yield 5
|
||||
yield 1000
|
||||
return
|
||||
yield ""test""
|
||||
yield nil
|
||||
end
|
||||
";
|
||||
var coroutine = Executor.EvaluateFunctionCoroutine(input, "testCoroutine");
|
||||
var count = 0;
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
var value = coroutine.Current;
|
||||
switch (count)
|
||||
{
|
||||
case 0:
|
||||
Assert.Equal(5L, value);
|
||||
break;
|
||||
case 1:
|
||||
Assert.Equal(1000L, value);
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
Assert.Equal(2, count);
|
||||
}
|
||||
|
||||
public class TestClass
|
||||
{
|
||||
public IEnumerator TestCoroutine()
|
||||
{
|
||||
yield return 5L;
|
||||
yield return 1000L;
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CSharpCoroutineCallTest()
|
||||
{
|
||||
UserDataTypeHandler.LoadType<TestClass>();
|
||||
|
||||
const string input = @"
|
||||
coroutine testCoroutine(enumerator)
|
||||
yield enumerator.TestCoroutine()
|
||||
end
|
||||
";
|
||||
var coroutine = Executor.EvaluateFunctionCoroutine(input, "testCoroutine", new[] {new TestClass()});
|
||||
var count = 0;
|
||||
while (coroutine.MoveNext())
|
||||
{
|
||||
var value = coroutine.Current;
|
||||
switch (count)
|
||||
{
|
||||
case 0:
|
||||
Assert.Equal(5L, value);
|
||||
break;
|
||||
case 1:
|
||||
Assert.Equal(1000L, value);
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
Assert.Equal(2, count);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue