/IronBrainFuck/JIT/BrainCompiler.cs
C# | 172 lines | 126 code | 25 blank | 21 comment | 5 complexity | 8849af26cb81443ffd8c98337143b251 MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Reflection;
- using System.Reflection.Emit;
- using System.Text;
- using ZackFlame.IronBrainFuck.Parser;
-
- namespace ZackFlame.IronBrainFuck.JIT
- {
- /// <summary>
- /// Представляет компилятор BrainFuck программы.
- /// </summary>
- public sealed class BrainCompiler
- {
- static readonly MethodInfo writeChar, writeCell, readChar, readCell;
-
- static BrainCompiler()
- {
- writeChar = typeof(IOHelper).GetMethod(
- "WriteChar",
- BindingFlags.Static | BindingFlags.Public);
-
- writeCell = typeof(IOHelper).GetMethod(
- "WriteCell",
- BindingFlags.Static | BindingFlags.Public);
-
- readChar = typeof(IOHelper).GetMethod(
- "ReadChar",
- BindingFlags.Static | BindingFlags.Public);
-
- readCell = typeof(IOHelper).GetMethod(
- "ReadCell",
- BindingFlags.Static | BindingFlags.Public);
- }
-
- /// <summary>
- /// Генерирует IL для заданной BrainFuck программы.
- /// </summary>
- /// <param name="il">
- /// Генератор IL кода. Обычно берётся из DynamicMethod.GetILGenerator()
- /// или MethodBuilder.GetILGenerator().
- /// </param>
- /// <param name="brainFuckProgram">BrainFuck программа.</param>
- public static void GenerateIL(ILGenerator il, string brainFuckProgram)
- {
- // Стек циклов [[[...]]]
- var labels = new Stack<KeyValuePair<Label, Label>>();
-
- // Объявляем локальную переменную с CellIndex
- il.DeclareLocal(typeof(UInt16));
- il.Emit(OpCodes.Ldarg_1);
- il.Emit(OpCodes.Stloc_0);
-
- BrainAction[] program = BrainParser.Parse(brainFuckProgram);
-
- for (int i = 0; i < program.Length; i++)
- {
- switch (program[i].Operation)
- {
- case BrainOperation.Add:
- EmitHelper.EmitAdd(il, program[i].Count);
- break;
-
- case BrainOperation.Sub:
- EmitHelper.EmitSub(il, program[i].Count);
- break;
-
- case BrainOperation.Right:
- EmitHelper.EmitRight(il, program[i].Count);
- break;
-
- case BrainOperation.Left:
- EmitHelper.EmitLeft(il, program[i].Count);
- break;
-
- case BrainOperation.CycleOpen:
- Label label1 = il.DefineLabel();
- Label label2 = il.DefineLabel();
- labels.Push(new KeyValuePair<Label, Label>(label1, label2));
- il.MarkLabel(label1);
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldloc_0);
- il.Emit(OpCodes.Ldelem_U2);
- il.Emit(OpCodes.Brfalse, label2);
- break;
-
- case BrainOperation.CycleClose:
- if (labels.Count == 0)
- throw new InvalidProgramException("Opening bracket expected");
-
- var labelPair = labels.Pop();
- il.Emit(OpCodes.Br, labelPair.Key);
- il.MarkLabel(labelPair.Value);
- break;
-
- case BrainOperation.Write:
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldloc_0);
- il.Emit(OpCodes.Ldelem_U2);
- il.Emit(OpCodes.Ldarg_3);
- il.EmitCall(OpCodes.Call, writeChar, null);
- break;
-
- case BrainOperation.Read:
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldloc_0);
- il.Emit(OpCodes.Ldarg_2);
- il.EmitCall(OpCodes.Call, readChar, null);
- il.Emit(OpCodes.Stelem_I2);
- break;
-
- case BrainOperation.SpecialWrite:
- il.Emit(OpCodes.Ldloc_0);
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldloc_0);
- il.Emit(OpCodes.Ldelem_U2);
- il.Emit(OpCodes.Ldarg_3);
- il.EmitCall(OpCodes.Call, writeCell, null);
- break;
-
- case BrainOperation.SpecialRead:
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldloc_0);
- il.Emit(OpCodes.Ldarg_2);
- il.EmitCall(OpCodes.Call, readCell, null);
- il.Emit(OpCodes.Stelem_I2);
- break;
-
- case BrainOperation.Zero:
- il.Emit(OpCodes.Ldarg_0);
- il.Emit(OpCodes.Ldloc_0);
- il.Emit(OpCodes.Ldc_I4_0);
- il.Emit(OpCodes.Stelem_I2);
- break;
- }
- }
-
- // Возвращаем CellIndex
- il.Emit(OpCodes.Ldloc_0);
- il.Emit(OpCodes.Ret);
-
- if (labels.Count > 0)
- throw new InvalidProgramException("Closing bracket expected");
- }
-
- /// <summary>
- /// Компилирует BrainFuck программу в IL (только для исполнения в памяти,
- /// для компиляции в сборку используйте BrainFuckCompiler).
- /// </summary>
- /// <param name="brainFuckProgram">BrainFuck программа.</param>
- /// <returns>Скомпилированная в IL программа.</returns>
- public BrainMethod Compile(string brainFuckProgram)
- {
- DynamicMethod method = new DynamicMethod(
- "Execute",
- typeof(UInt16),
- new Type[]
- {
- typeof(UInt16[]), typeof(UInt16), // Cells, CellIndex
- typeof(TextReader), typeof(TextWriter) // Input, Output
- });
-
- ILGenerator il = method.GetILGenerator();
- GenerateIL(il, brainFuckProgram);
-
- // Возвращаем готовый метод
- return new BrainMethod(brainFuckProgram, method);
- }
- }
- }