MParser/Parser/Lowering/Lowerer.cs

117 lines
3.6 KiB
C#

using Parser.Binding;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using static Parser.Binding.BoundNodeFactory;
namespace Parser.Lowering
{
internal class Lowerer : BoundTreeRewriter
{
private int _labelNumber = 0;
private Lowerer()
{
}
private BoundLabel GenerateLabel()
{
var name = $"Label{++_labelNumber}";
return new BoundLabel(name);
}
public override BoundStatement RewriteIfStatement(BoundIfStatement node)
{
// if cond
// body
// elseif cond1
// body1
// elseif cond2
// body2
// else
// body3
//
// gotoFalse cond Label1
// body
// goto LabelEnd
// Label1:
// gotoFalse cond1 Label2
// body1
// goto LabelEnd
// Label2:
// gotoFalse cond2 Label3
// body2
// goto LabelEnd
// Label3:
// body3
// LabelEnd:
var builder = ImmutableArray.CreateBuilder<BoundStatement>();
var numberOfLabelsNeeded = 1 + node.ElseifClauses.Length + (node.ElseClause is null ? 0 : 1);
var labels = new BoundLabel[numberOfLabelsNeeded];
for (var i = 0; i < numberOfLabelsNeeded; i++)
{
labels[i] = GenerateLabel();
}
builder.Add(GotoIfFalse(node.Syntax, node.Condition, labels[0]));
builder.Add(node.Body);
if (numberOfLabelsNeeded >= 2)
{
var counter = 0;
foreach (var elseifClause in node.ElseifClauses)
{
builder.Add(Goto(node.Syntax, labels[^1]));
builder.Add(LabelStatement(node.Syntax, labels[counter]));
counter++;
builder.Add(GotoIfFalse(node.Syntax, elseifClause.Condition, labels[counter]));
builder.Add(elseifClause.Body);
}
if (node.ElseClause is { } elseClause)
{
builder.Add(Goto(node.Syntax, labels[^1]));
builder.Add(LabelStatement(node.Syntax, labels[counter]));
builder.Add(elseClause);
}
}
builder.Add(LabelStatement(node.Syntax, labels[^1]));
return RewriteBlockStatement(Block(node.Syntax, builder.ToArray()));
}
public static BoundBlockStatement Lower(BoundStatement statement)
{
var lowerer = new Lowerer();
var result = lowerer.RewriteStatement(statement);
var flatResult = lowerer.Flatten(result);
return flatResult;
}
private BoundBlockStatement Flatten(BoundStatement statement)
{
var builder = ImmutableArray.CreateBuilder<BoundStatement>();
var stack = new Stack<BoundStatement>();
stack.Push(statement);
while (stack.Count() > 0)
{
var current = stack.Pop();
if (current is BoundBlockStatement block)
{
foreach (var s in block.Statements.Reverse())
{
stack.Push(s);
}
}
else
{
builder.Add(current);
}
}
return Block(statement.Syntax, builder.ToImmutable());
}
}
}