Implement branching

This commit is contained in:
Alexander Luzgarev 2020-07-19 09:02:45 +02:00
parent c8025ff723
commit 3f494cef6d
4 changed files with 129 additions and 56 deletions

View File

@ -4,34 +4,18 @@ using Mono.Cecil.Rocks;
using Parser.Binding;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
namespace Parser.Emitting
{
public class MethodInterfaceDescription
{
public MethodInterfaceDescription(ImmutableArray<ParameterSymbol> inputDescription, ImmutableArray<ParameterSymbol> outputDescription, MethodDefinition method)
{
InputDescription = inputDescription;
OutputDescription = outputDescription;
Method = method;
}
public ImmutableArray<ParameterSymbol> InputDescription { get; }
public ImmutableArray<ParameterSymbol> OutputDescription { get; }
public MethodDefinition Method { get; }
}
public class Emitter
{
private Dictionary<string, TypeReference> _knownTypes = new Dictionary<string, TypeReference>();
private Dictionary<string, MethodInterfaceDescription> _functions = new Dictionary<string, MethodInterfaceDescription>();
private MethodReference? _consoleWriteLineReference;
private MethodReference? _dispReference;
private MethodReference? _mObjectToBool;
private MethodReference? _stringToMObject;
private MethodReference? _doubleToMObject;
private MethodReference? _getItemFromDictionary;
@ -44,6 +28,8 @@ namespace Parser.Emitting
private MethodReference? _dictionaryCtorReference = null;
private Dictionary<BoundBinaryOperatorKind, MethodReference> _binaryOperations = new Dictionary<BoundBinaryOperatorKind, MethodReference>();
private Dictionary<BoundUnaryOperatorKind, MethodReference> _unaryOperations = new Dictionary<BoundUnaryOperatorKind, MethodReference>();
private Dictionary<BoundLabel, int> _labels = new Dictionary<BoundLabel, int>();
private Dictionary<int, BoundLabel> _forwardLabelsToFix = new Dictionary<int, BoundLabel>();
private static TypeReference ResolveAndImportType(
string typeName,
@ -206,6 +192,13 @@ namespace Parser.Emitting
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
_mObjectToBool = ResolveAndImportMethod(
typeName: "Parser.MFunctions.MHelpers",
methodName: "ToBool",
parameterTypeNames: new[] { "Parser.Objects.MObject" },
assemblies: assemblies,
assemblyDefinition: assemblyDefinition);
_stringToMObject = ResolveAndImportMethod(
typeName: "Parser.Objects.MObject",
methodName: "CreateCharArray",
@ -315,6 +308,9 @@ namespace Parser.Emitting
{
var ilProcessor = methodDefinition.Body.GetILProcessor();
_labels.Clear();
_forwardLabelsToFix.Clear();
// Local #0 is the dictionary with actual local variables.
_currentLocals = new VariableDefinition(_stringMObjectDictionary);
ilProcessor.Body.Variables.Add(_currentLocals);
@ -331,6 +327,7 @@ namespace Parser.Emitting
counter++;
}
// The following locals are "output variables".
_currentOutputVariables.Clear();
if (function.OutputDescription.Length > 0)
@ -359,50 +356,89 @@ namespace Parser.Emitting
}
}
foreach (var (index, target) in _forwardLabelsToFix)
{
var targetIndex = _labels[target];
ilProcessor.Body.Instructions[index].Operand = ilProcessor.Body.Instructions[targetIndex];
}
ilProcessor.Emit(OpCodes.Ret);
}
private void EmitBlockStatement(BoundBlockStatement block, MethodDefinition methodDefinition)
{
var index = 0;
while (index < block.Statements.Length)
var ilProcessor = methodDefinition.Body.GetILProcessor();
foreach (var statement in block.Statements)
{
var statement = block.Statements[index];
switch (statement.Kind)
{
case BoundNodeKind.GotoStatement:
throw new NotImplementedException("Gotos are not supported.");
EmitGoto((BoundGotoStatement)statement, ilProcessor);
break;
case BoundNodeKind.ConditionalGotoStatement:
throw new NotImplementedException("Conditional gotos are not supported.");
EmitConditionalGoto((BoundConditionalGotoStatement)statement, ilProcessor);
break;
case BoundNodeKind.LabelStatement:
throw new NotImplementedException("Labels are not supported.");
EmitLabelStatement((BoundLabelStatement)statement, ilProcessor);
break;
default:
EmitStatement(statement, methodDefinition);
index++;
EmitStatement(statement, ilProcessor);
break;
}
}
}
private void EmitStatement(BoundStatement node, MethodDefinition methodDefinition)
private void EmitLabelStatement(BoundLabelStatement node, ILProcessor ilProcessor)
{
_labels[node.Label] = ilProcessor.Body.Instructions.Count;
}
private void EmitGoto(BoundGotoStatement node, ILProcessor ilProcessor)
{
if (_labels.TryGetValue(node.Label, out var target))
{
ilProcessor.Emit(OpCodes.Br, ilProcessor.Body.Instructions[target]);
}
else
{
_forwardLabelsToFix.Add(ilProcessor.Body.Instructions.Count, node.Label);
ilProcessor.Emit(OpCodes.Br, Instruction.Create(OpCodes.Nop));
}
}
private void EmitConditionalGoto(BoundConditionalGotoStatement node, ILProcessor ilProcessor)
{
EmitExpression(node.Condition, ilProcessor);
ilProcessor.Emit(OpCodes.Call, _mObjectToBool);
var instruction = node.GotoIfTrue ? OpCodes.Brtrue : OpCodes.Brfalse;
if (_labels.TryGetValue(node.Label, out var target))
{
ilProcessor.Emit(instruction, ilProcessor.Body.Instructions[target]);
}
else
{
_forwardLabelsToFix.Add(ilProcessor.Body.Instructions.Count, node.Label);
ilProcessor.Emit(instruction, Instruction.Create(OpCodes.Nop));
}
}
private void EmitStatement(BoundStatement node, ILProcessor ilProcessor)
{
switch (node.Kind)
{
case BoundNodeKind.EmptyStatement:
break;
case BoundNodeKind.ExpressionStatement:
EmitExpressionStatement((BoundExpressionStatement)node, methodDefinition);
EmitExpressionStatement((BoundExpressionStatement)node, ilProcessor);
break;
default:
throw new NotImplementedException($"Invalid statement kind '{node.Kind}'.");
};
}
private void EmitExpressionStatement(BoundExpressionStatement node, MethodDefinition methodDefinition)
private void EmitExpressionStatement(BoundExpressionStatement node, ILProcessor ilProcessor)
{
var ilProcessor = methodDefinition.Body.GetILProcessor();
EmitExpression(node.Expression, methodDefinition);
EmitExpression(node.Expression, ilProcessor);
if (node.DiscardResult)
{
ilProcessor.Emit(OpCodes.Pop);
@ -413,44 +449,42 @@ namespace Parser.Emitting
}
}
private void EmitExpression(BoundExpression node, MethodDefinition methodDefinition)
private void EmitExpression(BoundExpression node, ILProcessor ilProcessor)
{
switch (node.Kind) {
case BoundNodeKind.AssignmentExpression:
EmitAssignmentExpression((BoundAssignmentExpression)node, methodDefinition);
EmitAssignmentExpression((BoundAssignmentExpression)node, ilProcessor);
break;
case BoundNodeKind.BinaryOperationExpression:
EmitBinaryOperationExpression((BoundBinaryOperationExpression)node, methodDefinition);
EmitBinaryOperationExpression((BoundBinaryOperationExpression)node, ilProcessor);
break;
case BoundNodeKind.FunctionCallExpression:
EmitFunctionCallExpression((BoundFunctionCallExpression)node, methodDefinition);
EmitFunctionCallExpression((BoundFunctionCallExpression)node, ilProcessor);
break;
case BoundNodeKind.IdentifierNameExpression:
EmitIdentifierNameExpression((BoundIdentifierNameExpression)node, methodDefinition);
EmitIdentifierNameExpression((BoundIdentifierNameExpression)node, ilProcessor);
break;
case BoundNodeKind.NumberLiteralExpression:
EmitNumberLiteralExpression((BoundNumberLiteralExpression)node, methodDefinition);
EmitNumberLiteralExpression((BoundNumberLiteralExpression)node, ilProcessor);
break;
case BoundNodeKind.StringLiteralExpression:
EmitStringLiteralExpression((BoundStringLiteralExpression)node, methodDefinition);
EmitStringLiteralExpression((BoundStringLiteralExpression)node, ilProcessor);
break;
default:
throw new NotImplementedException($"Invalid node kind '{node.Kind}'.");
}
}
private void EmitBinaryOperationExpression(BoundBinaryOperationExpression node, MethodDefinition methodDefinition)
private void EmitBinaryOperationExpression(BoundBinaryOperationExpression node, ILProcessor ilProcessor)
{
var ilProcessor = methodDefinition.Body.GetILProcessor();
var method = _binaryOperations[node.Op.Kind];
EmitExpression(node.Left, methodDefinition);
EmitExpression(node.Right, methodDefinition);
EmitExpression(node.Left, ilProcessor);
EmitExpression(node.Right, ilProcessor);
ilProcessor.Emit(OpCodes.Call, method);
}
private void EmitAssignmentExpression(BoundAssignmentExpression node, MethodDefinition methodDefinition)
private void EmitAssignmentExpression(BoundAssignmentExpression node, ILProcessor ilProcessor)
{
var ilProcessor = methodDefinition.Body.GetILProcessor();
if (node.Left.Kind != BoundNodeKind.IdentifierNameExpression)
{
throw new Exception("Assignment to complex lvalues is not supported.");
@ -458,42 +492,38 @@ namespace Parser.Emitting
var left = ((BoundIdentifierNameExpression)node.Left);
ilProcessor.Emit(OpCodes.Ldloc_0);
ilProcessor.Emit(OpCodes.Ldstr, left.Name);
EmitExpression(node.Right, methodDefinition);
EmitExpression(node.Right, ilProcessor);
ilProcessor.Emit(OpCodes.Callvirt, _putItemIntoDictionary);
ilProcessor.Emit(OpCodes.Ldloc_0);
ilProcessor.Emit(OpCodes.Ldstr, left.Name);
ilProcessor.Emit(OpCodes.Callvirt, _getItemFromDictionary);
}
private void EmitIdentifierNameExpression(BoundIdentifierNameExpression node, MethodDefinition methodDefinition)
private void EmitIdentifierNameExpression(BoundIdentifierNameExpression node, ILProcessor ilProcessor)
{
var ilProcessor = methodDefinition.Body.GetILProcessor();
ilProcessor.Emit(OpCodes.Ldloc_0);
ilProcessor.Emit(OpCodes.Ldstr, node.Name);
ilProcessor.Emit(OpCodes.Callvirt, _getItemFromDictionary);
}
private void EmitNumberLiteralExpression(BoundNumberLiteralExpression node, MethodDefinition methodDefinition)
private void EmitNumberLiteralExpression(BoundNumberLiteralExpression node, ILProcessor ilProcessor)
{
var ilProcessor = methodDefinition.Body.GetILProcessor();
ilProcessor.Emit(OpCodes.Ldc_R8, node.Value);
ilProcessor.Emit(OpCodes.Call, _doubleToMObject);
}
private void EmitStringLiteralExpression(BoundStringLiteralExpression node, MethodDefinition methodDefinition)
private void EmitStringLiteralExpression(BoundStringLiteralExpression node, ILProcessor ilProcessor)
{
var ilProcessor = methodDefinition.Body.GetILProcessor();
ilProcessor.Emit(OpCodes.Ldstr, node.Value);
ilProcessor.Emit(OpCodes.Call, _stringToMObject);
}
private void EmitFunctionCallExpression(BoundFunctionCallExpression node, MethodDefinition methodDefinition)
private void EmitFunctionCallExpression(BoundFunctionCallExpression node, ILProcessor ilProcessor)
{
var ilProcessor = methodDefinition.Body.GetILProcessor();
if (node.Name.Kind == BoundNodeKind.IdentifierNameExpression
&& ((BoundIdentifierNameExpression)node.Name).Name == "disp")
{
EmitExpression(node.Arguments[0], methodDefinition);
EmitExpression(node.Arguments[0], ilProcessor);
ilProcessor.Emit(OpCodes.Call, _dispReference);
ilProcessor.Emit(OpCodes.Ldnull);
}
@ -504,7 +534,7 @@ namespace Parser.Emitting
{
if (i < node.Arguments.Length)
{
EmitExpression(node.Arguments[i], methodDefinition);
EmitExpression(node.Arguments[i], ilProcessor);
}
else
{

View File

@ -0,0 +1,22 @@
using Mono.Cecil;
using Parser.Binding;
using System.Collections.Immutable;
namespace Parser.Emitting
{
public class MethodInterfaceDescription
{
public MethodInterfaceDescription(ImmutableArray<ParameterSymbol> inputDescription, ImmutableArray<ParameterSymbol> outputDescription, MethodDefinition method)
{
InputDescription = inputDescription;
OutputDescription = outputDescription;
Method = method;
}
public ImmutableArray<ParameterSymbol> InputDescription { get; }
public ImmutableArray<ParameterSymbol> OutputDescription { get; }
public MethodDefinition Method { get; }
}
}

View File

@ -12,5 +12,16 @@ namespace Parser.MFunctions
Console.WriteLine(obj);
}
}
public static bool ToBool(MObject operand)
{
return operand switch
{
MDoubleNumber { Value: var value } => value != 0.0,
MLogical { Value: var value } => value,
MCharArray { Chars: var value } => value.Length > 0,
_ => throw new System.Exception($"Unknown MObject type {operand.GetType()}"),
};
}
}
}

View File

@ -1,11 +1,21 @@
x = 2;
f(x);
x = 5;
f(x);
x = 3;
f(x);
function f(x)
disp('X was');
disp(x);
if x > 3
disp('greater than 3!');
elseif x < 3
disp('less than 3!');
else
disp('exactly 3!');
end
x = x + 1;
disp('X is');
disp('X + 1 is');
disp(x);
end