PageRenderTime 26ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/IronBrainFuck/JIT/BrainCompiler.cs

http://ironbrainfuck.codeplex.com
C# | 172 lines | 126 code | 25 blank | 21 comment | 5 complexity | 8849af26cb81443ffd8c98337143b251 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Reflection;
  6. using System.Reflection.Emit;
  7. using System.Text;
  8. using ZackFlame.IronBrainFuck.Parser;
  9. namespace ZackFlame.IronBrainFuck.JIT
  10. {
  11. /// <summary>
  12. /// Представляет компилятор BrainFuck программы.
  13. /// </summary>
  14. public sealed class BrainCompiler
  15. {
  16. static readonly MethodInfo writeChar, writeCell, readChar, readCell;
  17. static BrainCompiler()
  18. {
  19. writeChar = typeof(IOHelper).GetMethod(
  20. "WriteChar",
  21. BindingFlags.Static | BindingFlags.Public);
  22. writeCell = typeof(IOHelper).GetMethod(
  23. "WriteCell",
  24. BindingFlags.Static | BindingFlags.Public);
  25. readChar = typeof(IOHelper).GetMethod(
  26. "ReadChar",
  27. BindingFlags.Static | BindingFlags.Public);
  28. readCell = typeof(IOHelper).GetMethod(
  29. "ReadCell",
  30. BindingFlags.Static | BindingFlags.Public);
  31. }
  32. /// <summary>
  33. /// Генерирует IL для заданной BrainFuck программы.
  34. /// </summary>
  35. /// <param name="il">
  36. /// Генератор IL кода. Обычно берётся из DynamicMethod.GetILGenerator()
  37. /// или MethodBuilder.GetILGenerator().
  38. /// </param>
  39. /// <param name="brainFuckProgram">BrainFuck программа.</param>
  40. public static void GenerateIL(ILGenerator il, string brainFuckProgram)
  41. {
  42. // Стек циклов [[[...]]]
  43. var labels = new Stack<KeyValuePair<Label, Label>>();
  44. // Объявляем локальную переменную с CellIndex
  45. il.DeclareLocal(typeof(UInt16));
  46. il.Emit(OpCodes.Ldarg_1);
  47. il.Emit(OpCodes.Stloc_0);
  48. BrainAction[] program = BrainParser.Parse(brainFuckProgram);
  49. for (int i = 0; i < program.Length; i++)
  50. {
  51. switch (program[i].Operation)
  52. {
  53. case BrainOperation.Add:
  54. EmitHelper.EmitAdd(il, program[i].Count);
  55. break;
  56. case BrainOperation.Sub:
  57. EmitHelper.EmitSub(il, program[i].Count);
  58. break;
  59. case BrainOperation.Right:
  60. EmitHelper.EmitRight(il, program[i].Count);
  61. break;
  62. case BrainOperation.Left:
  63. EmitHelper.EmitLeft(il, program[i].Count);
  64. break;
  65. case BrainOperation.CycleOpen:
  66. Label label1 = il.DefineLabel();
  67. Label label2 = il.DefineLabel();
  68. labels.Push(new KeyValuePair<Label, Label>(label1, label2));
  69. il.MarkLabel(label1);
  70. il.Emit(OpCodes.Ldarg_0);
  71. il.Emit(OpCodes.Ldloc_0);
  72. il.Emit(OpCodes.Ldelem_U2);
  73. il.Emit(OpCodes.Brfalse, label2);
  74. break;
  75. case BrainOperation.CycleClose:
  76. if (labels.Count == 0)
  77. throw new InvalidProgramException("Opening bracket expected");
  78. var labelPair = labels.Pop();
  79. il.Emit(OpCodes.Br, labelPair.Key);
  80. il.MarkLabel(labelPair.Value);
  81. break;
  82. case BrainOperation.Write:
  83. il.Emit(OpCodes.Ldarg_0);
  84. il.Emit(OpCodes.Ldloc_0);
  85. il.Emit(OpCodes.Ldelem_U2);
  86. il.Emit(OpCodes.Ldarg_3);
  87. il.EmitCall(OpCodes.Call, writeChar, null);
  88. break;
  89. case BrainOperation.Read:
  90. il.Emit(OpCodes.Ldarg_0);
  91. il.Emit(OpCodes.Ldloc_0);
  92. il.Emit(OpCodes.Ldarg_2);
  93. il.EmitCall(OpCodes.Call, readChar, null);
  94. il.Emit(OpCodes.Stelem_I2);
  95. break;
  96. case BrainOperation.SpecialWrite:
  97. il.Emit(OpCodes.Ldloc_0);
  98. il.Emit(OpCodes.Ldarg_0);
  99. il.Emit(OpCodes.Ldloc_0);
  100. il.Emit(OpCodes.Ldelem_U2);
  101. il.Emit(OpCodes.Ldarg_3);
  102. il.EmitCall(OpCodes.Call, writeCell, null);
  103. break;
  104. case BrainOperation.SpecialRead:
  105. il.Emit(OpCodes.Ldarg_0);
  106. il.Emit(OpCodes.Ldloc_0);
  107. il.Emit(OpCodes.Ldarg_2);
  108. il.EmitCall(OpCodes.Call, readCell, null);
  109. il.Emit(OpCodes.Stelem_I2);
  110. break;
  111. case BrainOperation.Zero:
  112. il.Emit(OpCodes.Ldarg_0);
  113. il.Emit(OpCodes.Ldloc_0);
  114. il.Emit(OpCodes.Ldc_I4_0);
  115. il.Emit(OpCodes.Stelem_I2);
  116. break;
  117. }
  118. }
  119. // Возвращаем CellIndex
  120. il.Emit(OpCodes.Ldloc_0);
  121. il.Emit(OpCodes.Ret);
  122. if (labels.Count > 0)
  123. throw new InvalidProgramException("Closing bracket expected");
  124. }
  125. /// <summary>
  126. /// Компилирует BrainFuck программу в IL (только для исполнения в памяти,
  127. /// для компиляции в сборку используйте BrainFuckCompiler).
  128. /// </summary>
  129. /// <param name="brainFuckProgram">BrainFuck программа.</param>
  130. /// <returns>Скомпилированная в IL программа.</returns>
  131. public BrainMethod Compile(string brainFuckProgram)
  132. {
  133. DynamicMethod method = new DynamicMethod(
  134. "Execute",
  135. typeof(UInt16),
  136. new Type[]
  137. {
  138. typeof(UInt16[]), typeof(UInt16), // Cells, CellIndex
  139. typeof(TextReader), typeof(TextWriter) // Input, Output
  140. });
  141. ILGenerator il = method.GetILGenerator();
  142. GenerateIL(il, brainFuckProgram);
  143. // Возвращаем готовый метод
  144. return new BrainMethod(brainFuckProgram, method);
  145. }
  146. }
  147. }