Upsilon/Upsilon/Binder/BoundStatements/BoundGenericForStatement.cs

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