using System.Collections.Generic; using System.Text; using System.CodeDom.Compiler; using Microsoft.CSharp; using System.IO; using System; namespace BrainfuckCompiler { /// /// Brainfuck compiler and transpiler /// public class Brainfuck { /// /// Main frame for the output /// public const string MAIN = @"using System; static class Program { static int Main(string[] Args) { int Index=0; char[] Values=new char[/*MEMSIZE*/]; /*COMPILED*/ return (int)Values[Index]; } static void Put(char C) { Console.Write(C); } static char Get() { Put('>'); char C=Console.ReadKey(true).KeyChar; return C==27?'\0':C; } } "; /// /// Memory size in bytes /// public const int MEMSIZE = 32000; //no changes needed below public const string REPLACE = "/*COMPILED*/"; public const string CODE = "/*SOURCE*/"; public const string MEM = "/*MEMSIZE*/"; private const string VALUE = "VAL"; private Dictionary Commands; private Dictionary MultiCommands; /// /// Creates a new Brainfuck instance /// public Brainfuck() { Init(); } /// /// Resets all codes /// private void Init() { Commands = new Dictionary(); MultiCommands = new Dictionary(); //add strings Commands.Add('>', "++Index;\r\n"); Commands.Add('<', "--Index;\r\n"); Commands.Add('+', "++Values[Index];\r\n"); Commands.Add('-', "--Values[Index];\r\n"); Commands.Add('[', "while(Values[Index]!=0)\r\n{\r\n"); Commands.Add(']', "}\r\n"); Commands.Add('.', "Put(Values[Index]);\r\n"); Commands.Add(',', "Values[Index]=Get();\r\n"); //add multi-commands MultiCommands.Add('>', "Index+=VAL;\r\n"); MultiCommands.Add('<', "Index-=VAL;\r\n"); MultiCommands.Add('+', "Values[Index]+=(char)VAL;\r\n"); MultiCommands.Add('-', "Values[Index]-=(char)VAL;\r\n"); } /// /// Removes all non-code data /// /// Source code /// Cleaned brainfuck public string Filter(string SourceCode) { Init(); StringBuilder SB = new StringBuilder(); foreach (char C in SourceCode) { if (Commands.ContainsKey(C)) { SB.Append(C); } } return SB.ToString(); } /// /// Parses a brainfuck file and returns optimized C# code /// /// Source code /// C# code public string Parse(string SourceCode) { return Parse(SourceCode, true); } /// /// Parses a brainfuck file and returns C# code /// /// Source code /// true, to optimize commands /// C# code public string Parse(string SourceCode, bool optimize) { Init(); //CurrentIndent = 2; List Parts = new List(); //add dummy item Parts.Add("YAY"); SourceCode = Filter(SourceCode); //add optimizations here if (optimize) { //[-] sets the current value to 0 ([+] does too) Commands.Add('!', "Values[Index]='\\0';\r\n"); SourceCode = SourceCode .Replace("[-]", "!") .Replace("[+]", "!"); } foreach (char c in SourceCode) { if (Commands.ContainsKey(c)) { if (optimize && Parts[Parts.Count - 1][0] == c) { Parts[Parts.Count - 1] += c; } else { Parts.Add(c.ToString()); } } } //remove dummy item Parts.RemoveAt(0); StringBuilder SB = new StringBuilder(); foreach (string s in Parts) { SB.Append(MultiCommand(s)); } return MAIN .Replace(REPLACE, SB.ToString()) .Replace(CODE, SourceCode) .Replace(MEM, MEMSIZE.ToString()); } /// /// Compresses code using simple Replacements (still valid C#) /// /// Code /// Compressed code public string Compress(string SourceCode) { return SourceCode .Replace("Put", "W") .Replace("Get", "R") //.Replace("32000", "1<<15") //not actually shorter, just obfuscated .Replace("Values", "V") .Replace("Index", "I") .Replace('\t', '\n') .Replace('\r', '\n') .Replace("\n", ""); } /// /// gets the shortest version of a multicommand /// /// Command /// multicommand private string MultiCommand(string Command) { return MultiCommand(Command[0], Command.Length); } /// /// gets the shortest version of a multicommand /// /// Command /// How often it is repeated /// multicommand public string MultiCommand(char Command, int Count) { if (Count == 1) { return Commands[Command]; } if (MultiCommands.ContainsKey(Command)) { return MultiCommands[Command].Replace(VALUE, Count.ToString()); } else { StringBuilder SB = new StringBuilder(Commands[Command].Length * Count); for (int i = 0; i < Count; i++) { SB.Append(Commands[Command]); } return SB.ToString(); } } /// /// Compiles C# source code /// /// Source code (not the file name) /// Target file /// true, if successful public bool Compile(string SourceCode,string TargetFile) { using (CodeDomProvider codeProvider = new CSharpCodeProvider()) { CompilerParameters compilerParams = new CompilerParameters(); compilerParams.MainClass = "Program"; compilerParams.OutputAssembly = TargetFile; compilerParams.CompilerOptions = " /optimize"; compilerParams.GenerateExecutable = true; compilerParams.IncludeDebugInformation = false; compilerParams.ReferencedAssemblies.Add("mscorlib.dll"); compilerParams.ReferencedAssemblies.Add("System.dll"); CompilerResults results = codeProvider.CompileAssemblyFromSource(compilerParams, new string[] { SourceCode }); if (results.Errors.Count > 0) { Console.ForegroundColor = ConsoleColor.Red; foreach (CompilerError E in results.Errors) { Console.ForegroundColor = E.IsWarning ? ConsoleColor.Yellow : ConsoleColor.Red; Console.WriteLine("<{0}:{1}> [{2}]: {3}", E.Line, E.Column, E.ErrorNumber, E.ErrorText); } } return results.Errors.Count > 0; } } } }