Whitespace Language Compiler for .NET
February 16, 2017 ยท View on GitHub
using System; using System.IO; using System.Collections.Generic; using System.Reflection; using System.Reflection.Emit;
using Whitespace.Utils;
namespace Whitespace { public delegate void CompiledMethod(TextReader input, TextWriter output);
public sealed class Compiler {
#region Prefetched Reflection Info
private static readonly Type stackType = typeof(Stack<int>);
private static readonly Type heapType = typeof(Heap<int>);
private static readonly Type helperType = typeof(Helper);
private static readonly Type textReaderType = typeof(TextReader);
private static readonly Type textWriterType = typeof(TextWriter);
private static readonly Type[] intParams = new[] { typeof(int) };
private static readonly Type[] charParams = new[] { typeof(char) };
private static readonly Type[] textReaderParams = new[] { typeof(TextReader) };
private static readonly MethodInfo stackPopFn = stackType.GetMethod("Pop", Type.EmptyTypes);
private static readonly MethodInfo stackPushFn = stackType.GetMethod("Push", intParams);
private static readonly PropertyInfo heapIndexer = heapType.GetProperty("Item", intParams);
private static readonly MethodInfo heapStoreFn = heapType.GetMethod("Store", intParams);
private static readonly MethodInfo heapStoreSpecificFn = heapIndexer.GetSetMethod();
private static readonly MethodInfo heapRestoreFn = heapIndexer.GetGetMethod();
private static readonly MethodInfo readCharFn = textReaderType.GetMethod("Read", Type.EmptyTypes);
private static readonly MethodInfo readIntFn = helperType.GetMethod("ReadInteger", textReaderParams);
private static readonly MethodInfo writeCharFn = textWriterType.GetMethod("Write", charParams);
private static readonly MethodInfo writeIntFn = textWriterType.GetMethod("Write", intParams);
#endregion
#region Local Variables
private readonly Parser parser;
private readonly ILGenerator il;
private readonly Dictionary<int, Label> labels = new Dictionary<int, Label>();
private readonly List<Label> returnLabels = new List<Label>();
private LocalBuilder swap1, swap2, cbStack, heap;
private Label returnSelLabel, defaultReturnLabel;
#endregion
#region Constructors
private Compiler() { }
public Compiler(Parser parser, ILGenerator il) {
this.il = il;
this.parser = parser;
}
#endregion
#region Compiler
private void Compile() {
il.Emit(OpCodes.Nop);
swap1 = il.DeclareLocal(typeof(int));
if(parser.hasSwap)
swap2 = il.DeclareLocal(typeof(int));
if(parser.hasCbStack) {
cbStack = il.DeclareLocal(stackType);
returnSelLabel = il.DefineLabel();
defaultReturnLabel = il.DefineLabel();
il.Emit(OpCodes.Newobj, stackType.GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Stloc, cbStack);
}
heap = il.DeclareLocal(heapType);
il.Emit(OpCodes.Newobj, heapType.GetConstructor(Type.EmptyTypes));
il.Emit(OpCodes.Stloc, heap);
il.Emit(OpCodes.Nop);
foreach(OpParam op in parser.Instructions)
Emit(op.op, op.parameter);
if(parser.hasCbStack) {
il.Emit(OpCodes.Br, defaultReturnLabel);
il.MarkLabel(returnSelLabel);
il.Emit(OpCodes.Ldloc, cbStack);
il.Emit(OpCodes.Call, stackPopFn);
returnLabels.Add(defaultReturnLabel);
il.Emit(OpCodes.Switch, returnLabels.ToArray());
il.MarkLabel(defaultReturnLabel);
il.Emit(OpCodes.Nop);
}
il.Emit(OpCodes.Ret);
}
private void Emit(InstOpCode op, int parameter) {
switch(op) {
case InstOpCode.Pus:
il.Emit(OpCodes.Ldc_I4, parameter);
break;
case InstOpCode.Dup:
il.Emit(OpCodes.Dup);
break;
case InstOpCode.Swp:
il.Emit(OpCodes.Stloc, swap2);
EmitSwap(swap2);
break;
case InstOpCode.Pop:
il.Emit(OpCodes.Pop);
break;
case InstOpCode.Add:
il.Emit(OpCodes.Add);
break;
case InstOpCode.Sub:
il.Emit(OpCodes.Sub);
break;
case InstOpCode.Mul:
il.Emit(OpCodes.Mul);
break;
case InstOpCode.Div:
il.Emit(OpCodes.Div);
break;
case InstOpCode.Mod:
il.Emit(OpCodes.Rem);
break;
case InstOpCode.Sto:
EmitHeapStoreFn();
break;
case InstOpCode.Ret:
EmitSwap(heap);
il.Emit(OpCodes.Call, heapRestoreFn);
break;
case InstOpCode.Mrk:
il.MarkLabel(GetLabel(parameter));
break;
case InstOpCode.Cas:
int returnId;
Label returnLabel = GetReturnLabel(out returnId);
il.Emit(OpCodes.Ldloc, cbStack);
il.Emit(OpCodes.Ldc_I4, returnId);
il.Emit(OpCodes.Call, stackPushFn);
il.Emit(OpCodes.Br, GetLabel(parameter));
il.MarkLabel(returnLabel);
break;
case InstOpCode.Jmp:
il.Emit(OpCodes.Br, GetLabel(parameter));
break;
case InstOpCode.Jmz:
il.Emit(OpCodes.Brfalse, GetLabel(parameter));
break;
case InstOpCode.Jmn:
il.Emit(OpCodes.Ldc_I4_0);
il.Emit(OpCodes.Clt);
il.Emit(OpCodes.Brtrue, GetLabel(parameter));
break;
case InstOpCode.Ens:
il.Emit(OpCodes.Br, returnSelLabel);
break;
case InstOpCode.End:
il.Emit(OpCodes.Ret);
break;
case InstOpCode.Wrc:
EmitSwap(OpCodes.Ldarg_1);
il.Emit(OpCodes.Callvirt, writeCharFn);
break;
case InstOpCode.Wrn:
EmitSwap(OpCodes.Ldarg_1);
il.Emit(OpCodes.Callvirt, writeIntFn);
break;
case InstOpCode.Rec:
EmitSwap(heap);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Callvirt, readCharFn);
il.Emit(OpCodes.Call, heapStoreSpecificFn);
break;
case InstOpCode.Ren:
EmitSwap(heap);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, readIntFn);
il.Emit(OpCodes.Call, heapStoreSpecificFn);
break;
}
}
private void EmitHeapStoreFn() {
EmitSwap(heap);
il.Emit(OpCodes.Call, heapStoreFn);
}
private void EmitSwap(OpCode ldOpcode) {
il.Emit(OpCodes.Stloc, swap1);
il.Emit(ldOpcode);
il.Emit(OpCodes.Ldloc, swap1);
}
private void EmitSwap(LocalBuilder loc) {
il.Emit(OpCodes.Stloc, swap1);
il.Emit(OpCodes.Ldloc, loc);
il.Emit(OpCodes.Ldloc, swap1);
}
#endregion
#region Helpers
private Label GetLabel(int id) {
Label label;
if(!labels.TryGetValue(id, out label)) {
label = il.DefineLabel();
labels.Add(id, label);
}
return label;
}
private Label GetReturnLabel(out int returnId) {
Label label = il.DefineLabel();
returnId = returnLabels.Count;
returnLabels.Add(label);
return label;
}
#endregion
#region Public API
// Generated method's parameters: Compiled(TextReader reader, TextWriter writer);
public static void Compile(string source, ILGenerator il) {
if(source == null)
throw new ArgumentNullException("source");
if(il == null)
throw new ArgumentNullException("il");
Parser parser = new Parser(source);
Compiler compiler = new Compiler(parser, il);
parser.Parse();
compiler.Compile();
}
public static CompiledMethod Compile(string source) {
if(source == null)
throw new ArgumentNullException("source");
DynamicMethod method = new DynamicMethod("_Compiled_", null, new[] {
textReaderType, textWriterType
});
method.DefineParameter(0, ParameterAttributes.In, "input");
method.DefineParameter(1, ParameterAttributes.In, "output");
Parser parser = new Parser(source);
Compiler compiler = new Compiler(parser, method.GetILGenerator());
parser.Parse();
compiler.Compile();
return method.CreateDelegate(typeof(CompiledMethod)) as CompiledMethod;
}
#endregion
}
}