using System.Collections.Generic;
using System.Text;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using System.IO;
using System;
namespace BrainfuckCompiler
{
/// <summary>
/// Brainfuck compiler and transpiler
/// </summary>
public class Brainfuck
{
/// <summary>
/// Main frame for the output
/// </summary>
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;
}
}
";
/// <summary>
/// Memory size in bytes
/// </summary>
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<char, string> Commands;
private Dictionary<char, string> MultiCommands;
/// <summary>
/// Creates a new Brainfuck instance
/// </summary>
public Brainfuck()
{
Init();
}
/// <summary>
/// Resets all codes
/// </summary>
private void Init()
{
Commands = new Dictionary<char, string>();
MultiCommands = new Dictionary<char, string>();
//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");
}
/// <summary>
/// Removes all non-code data
/// </summary>
/// <param name="Source">Source code</param>
/// <returns>Cleaned brainfuck</returns>
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();
}
/// <summary>
/// Parses a brainfuck file and returns optimized C# code
/// </summary>
/// <param name="SourceCode">Source code</param>
/// <returns>C# code</returns>
public string Parse(string SourceCode)
{
return Parse(SourceCode, true);
}
/// <summary>
/// Parses a brainfuck file and returns C# code
/// </summary>
/// <param name="SourceCode">Source code</param>
/// <param name="optimize">true, to optimize commands</param>
/// <returns>C# code</returns>
public string Parse(string SourceCode, bool optimize)
{
Init();
//CurrentIndent = 2;
List<string> Parts = new List<string>();
//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());
}
/// <summary>
/// Compresses code using simple Replacements (still valid C#)
/// </summary>
/// <param name="Code">Code</param>
/// <returns>Compressed code</returns>
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", "");
}
/// <summary>
/// gets the shortest version of a multicommand
/// </summary>
/// <param name="Command">Command</param>
/// <returns>multicommand</returns>
private string MultiCommand(string Command)
{
return MultiCommand(Command[0], Command.Length);
}
/// <summary>
/// gets the shortest version of a multicommand
/// </summary>
/// <param name="Command">Command</param>
/// <param name="Count">How often it is repeated</param>
/// <returns>multicommand</returns>
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();
}
}
/// <summary>
/// Compiles C# source code
/// </summary>
/// <param name="SourceCode">Source code (not the file name)</param>
/// <param name="TargetFile">Target file</param>
/// <returns>true, if successful</returns>
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;
}
}
}
}
Comments
0 B
|👍
/👎