138 lines
5.5 KiB
C#
138 lines
5.5 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using Upsilon.BaseTypes;
|
|
using Upsilon.BaseTypes.ScriptTypeInterfaces;
|
|
using Upsilon.Evaluator;
|
|
using Upsilon.Text;
|
|
using Type = System.Type;
|
|
|
|
namespace Upsilon.Binder
|
|
{
|
|
public class BoundGenericForStatement : BoundStatement
|
|
{
|
|
public ImmutableArray<BoundVariableSymbol> Variables { get; }
|
|
public BoundExpression BoundEnumerableExpression { get; }
|
|
public BoundStatement Block { get; }
|
|
|
|
public BoundGenericForStatement(ImmutableArray<BoundVariableSymbol> variables,
|
|
BoundExpression boundEnumerableExpression, BoundStatement block, TextSpan span) : base(span)
|
|
{
|
|
Variables = variables;
|
|
BoundEnumerableExpression = boundEnumerableExpression;
|
|
Block = block;
|
|
}
|
|
|
|
public override BoundKind Kind => BoundKind.BoundGenericForStatement;
|
|
|
|
public override IEnumerable<BoundNode> GetChildren()
|
|
{
|
|
foreach (var variable in Variables)
|
|
{
|
|
yield return variable;
|
|
}
|
|
yield return BoundEnumerableExpression;
|
|
yield return Block;
|
|
}
|
|
|
|
internal override void Evaluate(EvaluationScope outerScope, Diagnostics diagnostics, ref 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]);
|
|
Block.Evaluate(innerScope, diagnostics, ref innerState);
|
|
if (innerState.HasBroken || innerState.Returned)
|
|
{
|
|
if (innerState.Returned)
|
|
{
|
|
outerState.Returned = true;
|
|
outerState.ReturnValue = innerState.ReturnValue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |