Upsilon/Upsilon/Binder/BoundStatements/BoundNumericForStatement.cs

147 lines
5.5 KiB
C#

using System.Collections;
using System.Collections.Generic;
using Upsilon.BaseTypes.Number;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Evaluator;
using Upsilon.Text;
namespace Upsilon.Binder
{
public class BoundNumericForStatement : BoundStatement
{
public VariableSymbol Variable { get; }
public BoundExpression BoundStart { get; }
public BoundExpression BoundStop { get; }
public BoundExpression BoundStep { get; }
public BoundBlockStatement Block { get; }
public BoundNumericForStatement(VariableSymbol variable, BoundExpression boundStart,
BoundExpression boundStop, BoundExpression boundStep, BoundBlockStatement block, TextSpan span) : base(span)
{
Variable = variable;
BoundStart = boundStart;
BoundStop = boundStop;
BoundStep = boundStep;
Block = block;
}
public override BoundKind Kind => BoundKind.BoundNumericForStatement;
public override IEnumerable<BoundNode> GetChildren()
{
yield return BoundStart;
yield return BoundStop;
yield return BoundStep;
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 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)
{
Block.Evaluate(innerScope, diagnostics, ref innerState);
if (innerState.HasBroken)
break;
if (innerState.Returned)
{
outerState.Returned = true;
outerState.ReturnValue = innerState.ReturnValue;
return;
}
}
}
else if (step < 0)
{
for (; startVal.Value >= stopVal.Value; startVal.Value = startVal.Value + step)
{
Block.Evaluate(innerScope, diagnostics, ref innerState);
if (innerState.HasBroken)
break;
if (innerState.Returned)
{
outerState.Returned = true;
outerState.ReturnValue = innerState.ReturnValue;
return;
}
}
}
}
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;
}
}
}
}
}
}