From 62b829a11d25c2c861186590e212d46e7fff3200 Mon Sep 17 00:00:00 2001 From: That-One-Nerd Date: Tue, 4 Feb 2025 18:05:51 -0500 Subject: [PATCH] New BF interpreter. --- .gitignore | 4 +- BrRun.sln => BfRun.sln | 12 +- BrRun/BrRun.csproj => BfRun/BfRun.csproj | 3 +- BfRun/Program.cs | 10 + BrRun/BrInterpretContext.cs | 11 - BrRun/BrInterpreterBase.cs | 15 - BrRun/InterpretMode.cs | 8 - BrRun/Interpreters/StandardBrInterpreter.cs | 325 -------------------- BrRun/Program.cs | 85 ----- 9 files changed, 19 insertions(+), 454 deletions(-) rename BrRun.sln => BfRun.sln (60%) rename BrRun/BrRun.csproj => BfRun/BfRun.csproj (70%) create mode 100644 BfRun/Program.cs delete mode 100644 BrRun/BrInterpretContext.cs delete mode 100644 BrRun/BrInterpreterBase.cs delete mode 100644 BrRun/InterpretMode.cs delete mode 100644 BrRun/Interpreters/StandardBrInterpreter.cs delete mode 100644 BrRun/Program.cs diff --git a/.gitignore b/.gitignore index 9a18cb0..ef96fea 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ .vs/ # Compilation Files -BrRun/bin/ -BrRun/obj/ +*/bin/ +*/obj/ diff --git a/BrRun.sln b/BfRun.sln similarity index 60% rename from BrRun.sln rename to BfRun.sln index 5ba05bd..efb7766 100644 --- a/BrRun.sln +++ b/BfRun.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34309.116 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BrRun", "BrRun\BrRun.csproj", "{2F98A27C-2F74-4377-BC63-88E1DF52558D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BfRun", "BfRun\BfRun.csproj", "{5FDEA610-020B-4D09-8950-491E551DA9A6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,15 +11,15 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {2F98A27C-2F74-4377-BC63-88E1DF52558D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2F98A27C-2F74-4377-BC63-88E1DF52558D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2F98A27C-2F74-4377-BC63-88E1DF52558D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2F98A27C-2F74-4377-BC63-88E1DF52558D}.Release|Any CPU.Build.0 = Release|Any CPU + {5FDEA610-020B-4D09-8950-491E551DA9A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5FDEA610-020B-4D09-8950-491E551DA9A6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5FDEA610-020B-4D09-8950-491E551DA9A6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5FDEA610-020B-4D09-8950-491E551DA9A6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {777E29D0-1222-4B40-BCB3-BD0489E5F38D} + SolutionGuid = {22B5EFF7-3B38-4012-A9E7-ABD601F01ADC} EndGlobalSection EndGlobal diff --git a/BrRun/BrRun.csproj b/BfRun/BfRun.csproj similarity index 70% rename from BrRun/BrRun.csproj rename to BfRun/BfRun.csproj index 0f7d180..2150e37 100644 --- a/BrRun/BrRun.csproj +++ b/BfRun/BfRun.csproj @@ -3,9 +3,8 @@ Exe net8.0 - disable + enable enable - bfrun diff --git a/BfRun/Program.cs b/BfRun/Program.cs new file mode 100644 index 0000000..6b56f5f --- /dev/null +++ b/BfRun/Program.cs @@ -0,0 +1,10 @@ +namespace BfRun +{ + internal class Program + { + static void Main(string[] args) + { + Console.WriteLine("Hello, World!"); + } + } +} diff --git a/BrRun/BrInterpretContext.cs b/BrRun/BrInterpretContext.cs deleted file mode 100644 index 832b173..0000000 --- a/BrRun/BrInterpretContext.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace BrRun; - -public class BrInterpretContext -{ - public required string filePath; - public required bool stepFlag; - public required bool usefulFlag; - public required InterpretMode mode; - - internal BrInterpretContext() { } -} diff --git a/BrRun/BrInterpreterBase.cs b/BrRun/BrInterpreterBase.cs deleted file mode 100644 index 031a1d4..0000000 --- a/BrRun/BrInterpreterBase.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace BrRun; - -public abstract class BrInterpreterBase -{ - public BrInterpretContext Context { get; set; } - public string FilePath { get; set; } - - public BrInterpreterBase(string filePath, BrInterpretContext context) - { - FilePath = filePath; - Context = context; - } - - public abstract void Interpret(); -} diff --git a/BrRun/InterpretMode.cs b/BrRun/InterpretMode.cs deleted file mode 100644 index f3a5782..0000000 --- a/BrRun/InterpretMode.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace BrRun; - -public enum InterpretMode -{ - StandardBr, - BrPlusPlus, - UsefulBr, -} diff --git a/BrRun/Interpreters/StandardBrInterpreter.cs b/BrRun/Interpreters/StandardBrInterpreter.cs deleted file mode 100644 index 6b2bb23..0000000 --- a/BrRun/Interpreters/StandardBrInterpreter.cs +++ /dev/null @@ -1,325 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; - -namespace BrRun.Interpreters; - -public class StandardBrInterpreter : BrInterpreterBase -{ - // Can technically be changed, but it should remain the same to match convention. - public static readonly short TapeMinValue = 0, TapeMaxValue = 255; - public static readonly int TapeLength = 30_000; - - protected FileStream? reader; - protected readonly short[] tape; - - protected int dataPointer; - protected Stack stack; - protected Stack<(int, int)> debugOpeningStack; - - private int lineNumber; - private int charNumber; - private char curChar; - private int awakeTop = 0; - - public StandardBrInterpreter(string filePath, BrInterpretContext context) : base(filePath, context) - { - reader = null; - tape = new short[TapeLength]; - dataPointer = 0; - stack = []; - debugOpeningStack = []; - - lineNumber = 1; - charNumber = 0; - } - - public override void Interpret() - { - awakeTop = Console.CursorTop; - if (Context.stepFlag) - { - for (int i = 0; i < 10; i++) Console.WriteLine(); - awakeTop = Console.CursorTop - 10; - } - reader = new(FilePath, FileMode.Open); - - IntentionKind intent; - while ((intent = StepProgram()) != IntentionKind.EndOfFile) - { - if (Context.stepFlag) ShowDebugScreen(intent); - HandleIntent(intent); - } - - reader.Close(); - Console.CursorVisible = true; - } - - protected void HandleIntent(IntentionKind intent) - { - switch (intent) - { - case IntentionKind.IncrementPointer: - dataPointer++; - if (dataPointer >= TapeLength) - { - Console.WriteLine($"warn L{lineNumber} C{charNumber}: data pointer has overflowed! (length {TapeLength})"); - dataPointer = 0; - } - break; - - case IntentionKind.DecrementPointer: - dataPointer--; - if (dataPointer < 0) - { - Console.WriteLine($"warn L{lineNumber} C{charNumber}: data pointer has underflowed! (length {TapeLength})"); - dataPointer = TapeLength - 1; - } - break; - - case IntentionKind.IncrementValue: - if (tape[dataPointer] == TapeMaxValue) tape[dataPointer] = TapeMinValue; - else tape[dataPointer]++; - break; - - case IntentionKind.DecrementValue: - if (tape[dataPointer] == TapeMinValue) tape[dataPointer] = TapeMaxValue; - else tape[dataPointer]--; - break; - - case IntentionKind.OutputValue: - Console.Write((char)tape[dataPointer]); - break; - - case IntentionKind.InputValue: - Console.CursorVisible = true; - tape[dataPointer] = (byte)Console.ReadKey().KeyChar; - break; - - case IntentionKind.BeginGroup: - if (reader is null) Console.WriteLine($"error L{lineNumber} C{charNumber}: file hasn't been opened yet! how did this happen?"); - else - { - stack.Push(reader.Position); - debugOpeningStack.Push((lineNumber, charNumber)); - if (tape[dataPointer] == 0) - { - // Look for closing brace. - IntentionKind newIntent; - while ((newIntent = StepProgram()) != IntentionKind.EndOfFile) - { - if (newIntent == IntentionKind.EndGroup) break; // Found closing bracket. - } - if (newIntent == IntentionKind.EndOfFile) - { - Console.WriteLine($"error L{lineNumber} C{charNumber}: no closing bracket to match opening bracket."); - return; - } - } - } - break; - - case IntentionKind.EndGroup: - if (stack.Count == 0) - { - Console.WriteLine($"error L{lineNumber} C{charNumber}: no opening bracket to match closing bracket."); - return; - } - - if (tape[dataPointer] == 0) - { - // Exit loop. - stack.Pop(); - debugOpeningStack.Pop(); - } - else - { - // Restart. - if (reader is null) Console.WriteLine($"error L{lineNumber} C{charNumber}: file hasn't been opened yet! how did this happen?"); - else reader.Seek(stack.Peek(), SeekOrigin.Begin); - - (int newL, int newC) = debugOpeningStack.Peek(); - lineNumber = newL; - charNumber = newC; - } - break; - - default: - Console.WriteLine($"warn L{lineNumber} C{charNumber}: unknown intent! how did this happen?"); - break; - } - } - - private int remainingSkips = 0; - protected void ShowDebugScreen(IntentionKind currentIntent) - { - const int numSpace = 3; - - Console.CursorVisible = false; - int initialTop = Console.CursorTop, initialLeft = Console.CursorLeft; - int totalCanFit = (Console.WindowWidth - 1) / (numSpace + 3); - - int startIndex = int.Max(0, (int)(dataPointer - totalCanFit * 0.5)), - endIndex = int.Min(startIndex + totalCanFit - 1, TapeLength - 1); - - int consolePos = 0; - for (int i = startIndex; i <= endIndex; i++) - { - Console.SetCursorPosition(consolePos, awakeTop + 1); - Console.Write($" {i,numSpace + 2}"); - - Console.SetCursorPosition(consolePos, awakeTop + 2); - if (i == startIndex) Console.Write("╔═════"); - else Console.Write("╦═════"); - - Console.SetCursorPosition(consolePos, awakeTop + 3); - Console.Write($"║ {tape[i],numSpace} "); - - Console.SetCursorPosition(consolePos, awakeTop + 4); - if (i == startIndex) Console.Write("╚═════"); - else Console.Write("╩═════"); - - Console.SetCursorPosition(consolePos, awakeTop + 5); - if (i == dataPointer) Console.Write(" ^ "); - else Console.Write(" "); - - Console.SetCursorPosition(consolePos, awakeTop + 9); - Console.Write($"──────"); - - consolePos += numSpace + 3; - } - Console.SetCursorPosition(consolePos, awakeTop + 2); - Console.Write('╗'); - Console.SetCursorPosition(consolePos, awakeTop + 3); - Console.Write('║'); - Console.SetCursorPosition(consolePos, awakeTop + 4); - Console.Write('╝'); - Console.SetCursorPosition(consolePos, awakeTop + 9); - Console.Write('─'); - - Console.SetCursorPosition(0, awakeTop + numSpace + 3); - string message = $"L{lineNumber} C{charNumber}: {curChar} {GetDebugDescriptionOfOperator(currentIntent)} "; - Console.Write(message + new string(' ', int.Max(Console.WindowWidth - message.Length - 1, 0))); - - if (remainingSkips < int.MaxValue - 1) remainingSkips--; - if (remainingSkips == int.MaxValue - 1 && currentIntent == IntentionKind.EndGroup && tape[dataPointer] == 0) remainingSkips = 0; - - Console.SetCursorPosition(0, awakeTop + 7); - if (currentIntent == IntentionKind.InputValue) - { - Console.Write("Enter one character to step the program." + new string(' ', 70)); - } - else - { - if (remainingSkips > 0) - { - string message2; - if (remainingSkips == int.MaxValue) message2 = $"Continuing program to completion..."; - else if (remainingSkips == int.MaxValue - 1) message2 = $"Waiting for loop exit..."; - else message2 = $"Skipping {remainingSkips} steps..."; - Console.Write(message2 + new string(' ', 110 - message2.Length)); - } - else - { - Console.Write("Press space to step the system. D = +5 steps, F = +25 steps, G = +100 steps, J = until loop ends, K = continuous"); - - _readKey: - ConsoleKeyInfo stepKey = Console.ReadKey(true); - switch (stepKey.Key) - { - case ConsoleKey.Spacebar: - remainingSkips++; - break; - case ConsoleKey.D: - remainingSkips += 5; - break; - case ConsoleKey.F: - remainingSkips += 25; - break; - case ConsoleKey.G: - remainingSkips += 100; - break; - case ConsoleKey.J: - remainingSkips = int.MaxValue - 1; - break; - case ConsoleKey.K: - remainingSkips = int.MaxValue; - break; - default: goto _readKey; - } - } - } - - Console.SetCursorPosition(initialLeft, initialTop); - } - private string GetDebugDescriptionOfOperator(IntentionKind intent) => intent switch - { - IntentionKind.IncrementPointer => $"Move pointer to right ({dataPointer} -> {dataPointer + 1})", - IntentionKind.DecrementPointer => $"Move pointer to left ({dataPointer} -> {dataPointer - 1})", - IntentionKind.IncrementValue => $"Increase value at position {dataPointer} ({tape[dataPointer]} -> {tape[dataPointer] + 1})", - IntentionKind.DecrementValue => $"Decrease value at position {dataPointer} ({tape[dataPointer]} -> {tape[dataPointer] - 1})", - IntentionKind.OutputValue => $"Print out current value as character (value {tape[dataPointer]})", - IntentionKind.InputValue => $"Input next character input into position {dataPointer}", - IntentionKind.BeginGroup => tape[dataPointer] == 0 - ? "Skipping loop. Moving execution forward to closing bracket." - : "Beginning a loop", - IntentionKind.EndGroup => tape[dataPointer] == 0 - ? $"Breaking out of a loop" - : $"Moving execution back to L{debugOpeningStack.Peek().Item1} C{debugOpeningStack.Peek().Item2} until value at position {dataPointer} is zero (currently {tape[dataPointer]})", - _ => "?? unknown intent ??" - }; - - protected IntentionKind StepProgram() - { - if (reader is null) - { - if (reader is null) Console.WriteLine("error: file hasn't been opened yet! how did this happen?"); - return IntentionKind.EndOfFile; - } - - int cI = reader.ReadByte(); - if (cI == -1) return IntentionKind.EndOfFile; - - char c = (char)cI; - if (c == '\n') - { - lineNumber++; - charNumber = 0; - } - else if (c != '\r') charNumber++; - - - curChar = c; - switch (c) - { - case '\r' or '\n' or ' ' or '\t': return StepProgram(); // Skip newlines. - case '#': - Console.WriteLine("error: comments are not supported in standard brainfuck."); - return StepProgram(); - case '>': return IntentionKind.IncrementPointer; - case '<': return IntentionKind.DecrementPointer; - case '+': return IntentionKind.IncrementValue; - case '-': return IntentionKind.DecrementValue; - case '.': return IntentionKind.OutputValue; - case ',': return IntentionKind.InputValue; - case '[': return IntentionKind.BeginGroup; - case ']': return IntentionKind.EndGroup; - default: - Console.WriteLine($"error: unsupported operator {c}"); - return StepProgram(); - } - } - - protected enum IntentionKind - { - EndOfFile, - IncrementPointer, - DecrementPointer, - IncrementValue, - DecrementValue, - OutputValue, - InputValue, - BeginGroup, - EndGroup - } -} diff --git a/BrRun/Program.cs b/BrRun/Program.cs deleted file mode 100644 index f152f4f..0000000 --- a/BrRun/Program.cs +++ /dev/null @@ -1,85 +0,0 @@ -using BrRun.Interpreters; -using System; -using System.IO; - -namespace BrRun; - -public static class Program -{ - public static void Main(string[] args) - { - if (args.Length == 0) - { - Console.WriteLine("fatal: no file provided."); - return; - } - - string path = args[0]; - bool stepFlag = false, usefulFlag = false; - for (int i = 1; i < args.Length; i++) - { - switch (args[i]) - { - case "--step": - if (stepFlag) Console.WriteLine("warn: duplicate --step flag."); - stepFlag = true; - break; - - case "--useful": - if (usefulFlag) Console.WriteLine("warn: duplicate --useful flag."); - usefulFlag = true; - break; - - default: - Console.WriteLine($"warn: unknown {args[i]} argument."); - break; - } - } - - if (!File.Exists(path)) - { - Console.WriteLine($"fatal: file does not exist at {path}"); - return; - } - - InterpretMode mode; - if (path.EndsWith(".bf") || path.EndsWith(".br") || path.EndsWith(".b")) - { - mode = InterpretMode.StandardBr; - if (usefulFlag) Console.WriteLine("warn: --useful flag is not applicable to standard brainfuck."); - } - else if (path.EndsWith(".bpp") || path.EndsWith(".b++") || path.EndsWith(".bfpp") || - path.EndsWith(".bf++") || path.EndsWith(".brpp") || path.EndsWith(".br++")) - { - mode = InterpretMode.BrPlusPlus; - if (usefulFlag) mode = InterpretMode.UsefulBr; - } - else - { - Console.WriteLine($"fatal: unsupported file type {path[path.LastIndexOf('.')..]}."); - return; - } - - BrInterpretContext context = new() - { - filePath = path, - stepFlag = stepFlag, - usefulFlag = usefulFlag, - mode = mode - }; - - BrInterpreterBase interpreter; - switch (mode) - { - case InterpretMode.StandardBr: - interpreter = new StandardBrInterpreter(path, context); - break; - - default: - Console.WriteLine("fatal: unknown interpreter mode. how did this happen?"); - return; - } - - interpreter.Interpret(); - } -}