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.Evaluator;
|
||||||
using Upsilon.Text;
|
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 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;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -46,6 +47,30 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
||||||
}
|
}
|
||||||
|
|
||||||
public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span)
|
public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span)
|
||||||
|
{
|
||||||
|
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>();
|
var objects = new List<object>();
|
||||||
if (_passScriptReference)
|
if (_passScriptReference)
|
||||||
|
@ -129,10 +154,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
||||||
if (e.InnerException != null) throw e.InnerException;
|
if (e.InnerException != null) throw e.InnerException;
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
if (_directTypeManipulation)
|
return result;
|
||||||
return (ScriptType)result;
|
|
||||||
return result.ToScriptType();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
using System;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -33,6 +33,22 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
||||||
return option.Run(diagnostics, variables, script, scope, span);
|
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)
|
public ScriptRuntimeFunctionOption GetValidOption(object[] variables)
|
||||||
{
|
{
|
||||||
if (variables == null)
|
if (variables == null)
|
||||||
|
@ -163,7 +179,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
||||||
EvaluationScope = scope;
|
EvaluationScope = scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EvaluationScope EvaluationScope { get; set; }
|
public EvaluationScope EvaluationScope { get; private set; }
|
||||||
|
|
||||||
public BoundBlockStatement Block { get; }
|
public BoundBlockStatement Block { get; }
|
||||||
public ImmutableArray<BoundVariableSymbol> Parameters { get; }
|
public ImmutableArray<BoundVariableSymbol> Parameters { get; }
|
||||||
|
@ -188,6 +204,28 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
||||||
Block.Evaluate(innerScope, diagnostics, ref state);
|
Block.Evaluate(innerScope, diagnostics, ref state);
|
||||||
return state.ReturnValue;
|
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);
|
return BindGenericForStatement((GenericForStatementSyntax) s);
|
||||||
case SyntaxKind.WhileStatement:
|
case SyntaxKind.WhileStatement:
|
||||||
return BindWhileStatement((WhileStatementSyntax) s);
|
return BindWhileStatement((WhileStatementSyntax) s);
|
||||||
|
case SyntaxKind.YieldStatement:
|
||||||
|
return BindYieldStatement((YieldStatementSyntax) s);
|
||||||
case SyntaxKind.BreakStatement:
|
case SyntaxKind.BreakStatement:
|
||||||
return new BoundBreakStatement(s.Span);
|
return new BoundBreakStatement(s.Span);
|
||||||
}
|
}
|
||||||
|
@ -685,7 +686,7 @@ namespace Upsilon.Binder
|
||||||
var returnType = Scope.ReturnType;
|
var returnType = Scope.ReturnType;
|
||||||
Scope = Scope.ParentScope;
|
Scope = Scope.ParentScope;
|
||||||
var func = new BoundFunctionExpression(parameters.ToImmutable(), (BoundBlockStatement) block, e.Span,
|
var func = new BoundFunctionExpression(parameters.ToImmutable(), (BoundBlockStatement) block, e.Span,
|
||||||
innerScope, returnType);
|
innerScope, returnType, e.IsCoroutine);
|
||||||
return func;
|
return func;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -719,7 +720,8 @@ namespace Upsilon.Binder
|
||||||
|
|
||||||
if (!Scope.TryGetVariable(name, !isLocal, out var variable))
|
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()
|
CommentValue = commentData.ToArray()
|
||||||
};
|
};
|
||||||
|
@ -1072,5 +1074,10 @@ namespace Upsilon.Binder
|
||||||
return new BoundWhileStatement(condition, block, e.Span);
|
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 ImmutableArray<BoundVariableSymbol> Parameters { get; }
|
||||||
public BoundBlockStatement Block { get; set; }
|
public BoundBlockStatement Block { get; set; }
|
||||||
|
public bool IsCoroutine { get; }
|
||||||
|
|
||||||
public BoundFunctionExpression(ImmutableArray<BoundVariableSymbol> parameters, BoundBlockStatement block,
|
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;
|
Parameters = parameters;
|
||||||
Block = block;
|
Block = block;
|
||||||
Scope = scope;
|
Scope = scope;
|
||||||
ReturnType = returnType;
|
ReturnType = returnType;
|
||||||
|
IsCoroutine = isCoroutine;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override BoundKind Kind => BoundKind.BoundFunctionExpression;
|
public override BoundKind Kind => BoundKind.BoundFunctionExpression;
|
||||||
|
|
|
@ -31,5 +31,6 @@ namespace Upsilon.Binder
|
||||||
BoundGenericForStatement,
|
BoundGenericForStatement,
|
||||||
BoundBreakStatement,
|
BoundBreakStatement,
|
||||||
BoundWhileStatement,
|
BoundWhileStatement,
|
||||||
|
BoundYieldStatement
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
|
@ -32,5 +33,21 @@ namespace Upsilon.Binder
|
||||||
return;
|
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 System.Collections.Generic;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Upsilon.Text;
|
using Upsilon.Text;
|
||||||
|
@ -21,5 +22,11 @@ namespace Upsilon.Binder
|
||||||
{
|
{
|
||||||
state.HasBroken = true;
|
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 System.Collections.Generic;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Upsilon.Text;
|
using Upsilon.Text;
|
||||||
|
@ -24,5 +25,12 @@ namespace Upsilon.Binder
|
||||||
var value = Expression.Evaluate(scope, diagnostics, ref state);
|
var value = Expression.Evaluate(scope, diagnostics, ref state);
|
||||||
state.LastValue = value;
|
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 System.Collections.Generic;
|
||||||
using Upsilon.BaseTypes.ScriptFunction;
|
using Upsilon.BaseTypes.ScriptFunction;
|
||||||
using Upsilon.Binder.VariableSymbols;
|
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;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using Upsilon.BaseTypes;
|
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 System.Collections.Generic;
|
||||||
using Upsilon.BaseTypes;
|
using Upsilon.BaseTypes;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
|
@ -57,6 +58,21 @@ namespace Upsilon.Binder
|
||||||
ElseStatement?.Evaluate(scope, diagnostics, ref state);
|
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
|
public class BoundElseStatement : BoundStatement
|
||||||
|
@ -79,5 +95,10 @@ namespace Upsilon.Binder
|
||||||
{
|
{
|
||||||
Block.Evaluate(scope, diagnostics, ref state);
|
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.Generic;
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using Upsilon.BaseTypes.Number;
|
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 System.Collections.Generic;
|
||||||
using Upsilon.BaseTypes.Number;
|
using Upsilon.BaseTypes.Number;
|
||||||
using Upsilon.Binder.VariableSymbols;
|
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 System.Collections.Generic;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Upsilon.Text;
|
using Upsilon.Text;
|
||||||
|
@ -25,5 +26,12 @@ namespace Upsilon.Binder
|
||||||
state.Returned = true;
|
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 System.Collections.Generic;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Upsilon.Text;
|
using Upsilon.Text;
|
||||||
|
@ -31,5 +32,10 @@ namespace Upsilon.Binder
|
||||||
{
|
{
|
||||||
Statement.Evaluate(scope, diagnostics, ref state);
|
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.Evaluator;
|
||||||
using Upsilon.Text;
|
using Upsilon.Text;
|
||||||
|
|
||||||
|
@ -12,5 +13,8 @@ namespace Upsilon.Binder
|
||||||
internal bool HasBreakpoint { get; set; }
|
internal bool HasBreakpoint { get; set; }
|
||||||
|
|
||||||
internal abstract void Evaluate(EvaluationScope scope, Diagnostics diagnostics, ref EvaluationState state);
|
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;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Upsilon.BaseTypes;
|
using Upsilon.BaseTypes;
|
||||||
using Upsilon.BaseTypes.ScriptTypeInterfaces;
|
using Upsilon.BaseTypes.ScriptTypeInterfaces;
|
||||||
|
@ -51,5 +52,11 @@ namespace Upsilon.Binder
|
||||||
}
|
}
|
||||||
indexable.Set(diagnostics, Span, index.ToString().ToScriptType(), value);
|
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 System.Collections.Generic;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
using Upsilon.Text;
|
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 System.Collections.Generic;
|
||||||
using Upsilon.BaseTypes;
|
using Upsilon.BaseTypes;
|
||||||
using Upsilon.Evaluator;
|
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,
|
public UnboundFunctionExpression(ImmutableArray<BoundVariableSymbol> parameters,
|
||||||
BlockStatementSyntax unboundBlock, TextSpan span, BoundScope scope, string name)
|
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;
|
UnboundBlock = unboundBlock;
|
||||||
Name = name;
|
Name = name;
|
||||||
|
|
|
@ -8,10 +8,12 @@ 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 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)
|
: base(name, Type.Function, local)
|
||||||
{
|
{
|
||||||
|
IsCoroutine = isCoroutine;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract bool ValidateParameters(
|
public abstract bool ValidateParameters(
|
||||||
|
|
|
@ -11,14 +11,15 @@ namespace Upsilon.Binder.VariableSymbols
|
||||||
public class InternalFunctionVariableSymbol : FunctionVariableSymbol
|
public class InternalFunctionVariableSymbol : FunctionVariableSymbol
|
||||||
{
|
{
|
||||||
public InternalFunctionVariableSymbol(string name, bool local, TypeContainer resultType,
|
public InternalFunctionVariableSymbol(string name, bool local, TypeContainer resultType,
|
||||||
InternalFunctionParameter[] functionParameters, MethodInfo overrideResultType)
|
InternalFunctionParameter[] functionParameters, MethodInfo overrideResultType, bool isCoroutine)
|
||||||
: base(name, local, resultType)
|
: base(name, local, isCoroutine)
|
||||||
{
|
{
|
||||||
FunctionOption.Add(new InternalFunctionVariableOption(resultType, functionParameters, overrideResultType));
|
FunctionOption.Add(new InternalFunctionVariableOption(resultType, functionParameters, overrideResultType));
|
||||||
}
|
}
|
||||||
|
|
||||||
public InternalFunctionVariableSymbol(string name, bool local, Type resultType, List<FunctionVariableSymbolOption> options)
|
public InternalFunctionVariableSymbol(string name, bool local, List<FunctionVariableSymbolOption> options,
|
||||||
: base(name, local, resultType)
|
bool isCoroutine)
|
||||||
|
: base(name, local, isCoroutine)
|
||||||
{
|
{
|
||||||
FunctionOption = options;
|
FunctionOption = options;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,9 @@ namespace Upsilon.Binder.VariableSymbols
|
||||||
{
|
{
|
||||||
public class ScriptFunctionVariableSymbol : FunctionVariableSymbol
|
public class ScriptFunctionVariableSymbol : FunctionVariableSymbol
|
||||||
{
|
{
|
||||||
public ScriptFunctionVariableSymbol(string name, bool local, ImmutableArray<VariableSymbol> parameters, Type resultType)
|
public ScriptFunctionVariableSymbol(string name, bool local, ImmutableArray<VariableSymbol> parameters, Type resultType,
|
||||||
: base(name, local, resultType)
|
bool isCoroutine)
|
||||||
|
: base(name, local, isCoroutine)
|
||||||
{
|
{
|
||||||
FunctionOption.Add(new ScriptFunctionVariableOption(resultType, parameters));
|
FunctionOption.Add(new ScriptFunctionVariableOption(resultType, parameters));
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ using Upsilon.BaseTypes;
|
||||||
|
|
||||||
namespace Upsilon.Evaluator
|
namespace Upsilon.Evaluator
|
||||||
{
|
{
|
||||||
internal struct EvaluationState
|
internal class EvaluationState
|
||||||
{
|
{
|
||||||
public Script Script;
|
public Script Script;
|
||||||
public bool Returned;
|
public bool Returned;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Upsilon.BaseTypes;
|
using Upsilon.BaseTypes;
|
||||||
using Upsilon.BaseTypes.ScriptFunction;
|
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());
|
var result = option.Run(_diagnostics, parameters?.Select(x => x.ToScriptType()).ToArray(), _script, Scope, new TextSpan());
|
||||||
return result;
|
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;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Upsilon.BaseTypes;
|
using Upsilon.BaseTypes;
|
||||||
|
@ -114,6 +115,18 @@ namespace Upsilon.Evaluator
|
||||||
return Convert(Evaluator.Evaluate(Bind(), functionName, parameters));
|
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)
|
private static T Convert<T>(ScriptType t)
|
||||||
{
|
{
|
||||||
var result = UpsilonBinder.Default.ChangeType(t, typeof(T), CultureInfo.InvariantCulture);
|
var result = UpsilonBinder.Default.ChangeType(t, typeof(T), CultureInfo.InvariantCulture);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System.Collections;
|
||||||
using Upsilon.Binder;
|
using Upsilon.Binder;
|
||||||
using Upsilon.Evaluator;
|
using Upsilon.Evaluator;
|
||||||
|
|
||||||
|
@ -89,5 +90,11 @@ namespace Upsilon
|
||||||
var script = ParseInputAndEvaluate(input, options);
|
var script = ParseInputAndEvaluate(input, options);
|
||||||
return script.EvaluateFunction(function, parameters);
|
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 SyntaxToken CloseParenthesis { get; }
|
||||||
public BlockStatementSyntax Block { get; }
|
public BlockStatementSyntax Block { get; }
|
||||||
public SyntaxToken EndToken { get; }
|
public SyntaxToken EndToken { get; }
|
||||||
|
public bool IsCoroutine { get; }
|
||||||
|
|
||||||
public FunctionExpressionSyntax(SyntaxToken functionToken,
|
public FunctionExpressionSyntax(SyntaxToken functionToken,
|
||||||
SyntaxToken openParenthesis, ImmutableArray<ParameterToken> parameters, SyntaxToken closeParenthesis,
|
SyntaxToken openParenthesis, ImmutableArray<ParameterToken> parameters, SyntaxToken closeParenthesis,
|
||||||
BlockStatementSyntax block, SyntaxToken endToken)
|
BlockStatementSyntax block, SyntaxToken endToken, bool isCoroutine)
|
||||||
{
|
{
|
||||||
FunctionToken = functionToken;
|
FunctionToken = functionToken;
|
||||||
OpenParenthesis = openParenthesis;
|
OpenParenthesis = openParenthesis;
|
||||||
|
@ -23,6 +24,7 @@ namespace Upsilon.Parser
|
||||||
CloseParenthesis = closeParenthesis;
|
CloseParenthesis = closeParenthesis;
|
||||||
Block = block;
|
Block = block;
|
||||||
EndToken = endToken;
|
EndToken = endToken;
|
||||||
|
IsCoroutine = isCoroutine;
|
||||||
Span = TextSpan.Between(FunctionToken.Span, endToken.Span);
|
Span = TextSpan.Between(FunctionToken.Span, endToken.Span);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,11 +79,19 @@ namespace Upsilon.Parser
|
||||||
}
|
}
|
||||||
if (Current.Kind == SyntaxKind.FunctionKeyword && Next.Kind != SyntaxKind.OpenParenthesis)
|
if (Current.Kind == SyntaxKind.FunctionKeyword && Next.Kind != SyntaxKind.OpenParenthesis)
|
||||||
{
|
{
|
||||||
return ParseFunctionAssignmentStatement();
|
return ParseFunctionAssignmentStatement(SyntaxKind.FunctionKeyword);
|
||||||
}
|
}
|
||||||
if (Current.Kind == SyntaxKind.LocalKeyword && Next.Kind == 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)
|
if (Current.Kind == SyntaxKind.ForKeyword)
|
||||||
{
|
{
|
||||||
|
@ -97,6 +105,10 @@ namespace Upsilon.Parser
|
||||||
{
|
{
|
||||||
return new BreakStatementSyntax(NextToken());
|
return new BreakStatementSyntax(NextToken());
|
||||||
}
|
}
|
||||||
|
if (Current.Kind == SyntaxKind.YieldKeyword)
|
||||||
|
{
|
||||||
|
return ParseYieldStatement();
|
||||||
|
}
|
||||||
|
|
||||||
return ParseExpressionStatement();
|
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 openParenthesis = MatchToken(SyntaxKind.OpenParenthesis);
|
||||||
var variableBuilder = ImmutableArray.CreateBuilder<ParameterToken>();
|
var variableBuilder = ImmutableArray.CreateBuilder<ParameterToken>();
|
||||||
SyntaxToken current = null;
|
SyntaxToken current = null;
|
||||||
|
@ -239,11 +251,13 @@ namespace Upsilon.Parser
|
||||||
var closeParenthesis = MatchToken(SyntaxKind.CloseParenthesis);
|
var closeParenthesis = MatchToken(SyntaxKind.CloseParenthesis);
|
||||||
var block = ParseBlockStatement(new[] {SyntaxKind.EndKeyword});
|
var block = ParseBlockStatement(new[] {SyntaxKind.EndKeyword});
|
||||||
var endToken = MatchToken(SyntaxKind.EndKeyword);
|
var endToken = MatchToken(SyntaxKind.EndKeyword);
|
||||||
|
|
||||||
|
var isCoroutine = openKeyword == SyntaxKind.CoroutineKeyword;
|
||||||
return new FunctionExpressionSyntax(functionToken, openParenthesis,
|
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;
|
SyntaxToken localToken = null;
|
||||||
string[] commentData = null;
|
string[] commentData = null;
|
||||||
|
@ -252,7 +266,7 @@ namespace Upsilon.Parser
|
||||||
localToken = NextToken();
|
localToken = NextToken();
|
||||||
commentData = localToken.CommentData;
|
commentData = localToken.CommentData;
|
||||||
}
|
}
|
||||||
var functionToken = MatchToken(SyntaxKind.FunctionKeyword);
|
var functionToken = MatchToken(identifyingKeyword);
|
||||||
if (commentData == null)
|
if (commentData == null)
|
||||||
{
|
{
|
||||||
commentData = functionToken.CommentData;
|
commentData = functionToken.CommentData;
|
||||||
|
@ -278,8 +292,10 @@ namespace Upsilon.Parser
|
||||||
var closeParenthesis = MatchToken(SyntaxKind.CloseParenthesis);
|
var closeParenthesis = MatchToken(SyntaxKind.CloseParenthesis);
|
||||||
var block = ParseBlockStatement(new[] {SyntaxKind.EndKeyword});
|
var block = ParseBlockStatement(new[] {SyntaxKind.EndKeyword});
|
||||||
var endToken = MatchToken(SyntaxKind.EndKeyword);
|
var endToken = MatchToken(SyntaxKind.EndKeyword);
|
||||||
|
|
||||||
|
var isCoroutine = identifyingKeyword == SyntaxKind.CoroutineKeyword;
|
||||||
var functionExpression = new FunctionExpressionSyntax(functionToken, openParenthesis,
|
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)
|
return new FunctionAssignmentStatementSyntax(localToken, (IdentifierToken) identifier, functionExpression)
|
||||||
{
|
{
|
||||||
CommentData = commentData
|
CommentData = commentData
|
||||||
|
@ -309,12 +325,24 @@ namespace Upsilon.Parser
|
||||||
return new ReturnStatementSyntax(returnToken, expression);
|
return new ReturnStatementSyntax(returnToken, expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private StatementSyntax ParseYieldStatement()
|
||||||
|
{
|
||||||
|
var yieldToken = MatchToken(SyntaxKind.YieldKeyword);
|
||||||
|
var expression = ParseExpression();
|
||||||
|
return new YieldStatementSyntax(yieldToken, expression);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private ExpressionSyntax ParseExpression()
|
private ExpressionSyntax ParseExpression()
|
||||||
{
|
{
|
||||||
ExpressionSyntax expression;
|
ExpressionSyntax expression;
|
||||||
if (Current.Kind == SyntaxKind.FunctionKeyword && Next.Kind == SyntaxKind.OpenParenthesis)
|
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
|
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;
|
return SyntaxKind.DoKeyword;
|
||||||
case "break":
|
case "break":
|
||||||
return SyntaxKind.BreakKeyword;
|
return SyntaxKind.BreakKeyword;
|
||||||
|
case "yield":
|
||||||
|
return SyntaxKind.YieldKeyword;
|
||||||
|
case "coroutine":
|
||||||
|
return SyntaxKind.CoroutineKeyword;
|
||||||
default:
|
default:
|
||||||
return SyntaxKind.Identifier;
|
return SyntaxKind.Identifier;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,8 @@ namespace Upsilon.Parser
|
||||||
InKeyword,
|
InKeyword,
|
||||||
DoKeyword,
|
DoKeyword,
|
||||||
BreakKeyword,
|
BreakKeyword,
|
||||||
|
YieldKeyword,
|
||||||
|
CoroutineKeyword,
|
||||||
|
|
||||||
Identifier,
|
Identifier,
|
||||||
Parameter,
|
Parameter,
|
||||||
|
@ -90,6 +92,7 @@ namespace Upsilon.Parser
|
||||||
NumericForStatement,
|
NumericForStatement,
|
||||||
BreakStatement,
|
BreakStatement,
|
||||||
GenericForStatement,
|
GenericForStatement,
|
||||||
WhileStatement
|
WhileStatement,
|
||||||
|
YieldStatement,
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -64,7 +64,7 @@ namespace Upsilon.StandardLibraries
|
||||||
var derivedType = DeriveValidTypes(typeInfo.Type);
|
var derivedType = DeriveValidTypes(typeInfo.Type);
|
||||||
return new InternalFunctionVariableSymbol.InternalFunctionParameter(func.Key, derivedType,
|
return new InternalFunctionVariableSymbol.InternalFunctionParameter(func.Key, derivedType,
|
||||||
typeInfo.IsOptional);
|
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')
|
CommentValue = func.Value.CommentValue?.Split('\n')
|
||||||
};
|
};
|
||||||
|
@ -139,7 +139,7 @@ namespace Upsilon.StandardLibraries
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = genericParameters[genericParameters.Length - 1].GetScriptType();
|
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)
|
private static VariableSymbol BuildActionVariableSymbol(string name, System.Type type)
|
||||||
|
@ -147,7 +147,8 @@ namespace Upsilon.StandardLibraries
|
||||||
var genericParameters = type.GetGenericArguments();
|
var genericParameters = type.GetGenericArguments();
|
||||||
return new InternalFunctionVariableSymbol(name, true, Type.Nil,
|
return new InternalFunctionVariableSymbol(name, true, Type.Nil,
|
||||||
genericParameters.Select(DeriveValidTypes).Select(t =>
|
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)
|
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