patterncsharpMinor
BF#: Round 2 (Fight!)
Viewed 0 times
fightroundstackoverflow
Problem
So I've completely changed how BrainfuckSharp does things, and added some features. This is obviously a follow up to: Interpreting Brainfuck code to C#, then compiling to a .exe
It now features an
Just as well, it's now much more like a "true" compiler in that it treats everything like a token.
Let's start with the
This represents every possible token in Brainfuck, and a default "None" token.
Next,
This is self-explanatory. It represents the (currently two) possible optimization levels of BrainfuckSharp. Specifying
On to the
```
public class Interpreter
{
public OptimizationLevel OptimizationLevel { get; set; } = OptimizationLevel.None;
public List Lines { get; } = new List();
private List _symbols = new List();
private List _validTokens = new List { '', ',', '.', '+', '-', '[', ']' };
private Dictionary _tokenMap = new Dictionary
{
{ '', TokenSymbol.MoveRight },
{ ',', TokenSymbol.InputCharacter },
{
It now features an
OptimizationLevel (currently hardcoded, will fix for the future) that allows you to indicate how far you want the interpreted code optimized. The OptimizationLevel.Level1 level will smash subsequent calls to the increment/decrement and move left/right tokens together into one call. (Instead of >> turning into index++; index++; it now turns into index += 2;.)Just as well, it's now much more like a "true" compiler in that it treats everything like a token.
Let's start with the
TokenSymbol enum:public enum TokenSymbol
{
None,
MoveLeft,
MoveRight,
LoopBegin,
LoopEnd,
Increment,
Decrement,
OutputValue,
InputCharacter
}This represents every possible token in Brainfuck, and a default "None" token.
Next,
OptimizationLevel:///
/// Determines how the will optimize Brainfuck code.
///
public enum OptimizationLevel
{
///
/// Do not apply any optimizations.
///
None,
///
/// Apply first level optimizations (combining sequences of increment/decrement characters into one increment/decrement per sequence).
///
Level1,
}This is self-explanatory. It represents the (currently two) possible optimization levels of BrainfuckSharp. Specifying
None will not optimize the code, specifying Level1 will apply basic optimizations.On to the
Interpreter:```
public class Interpreter
{
public OptimizationLevel OptimizationLevel { get; set; } = OptimizationLevel.None;
public List Lines { get; } = new List();
private List _symbols = new List();
private List _validTokens = new List { '', ',', '.', '+', '-', '[', ']' };
private Dictionary _tokenMap = new Dictionary
{
{ '', TokenSymbol.MoveRight },
{ ',', TokenSymbol.InputCharacter },
{
Solution
if ((OptimizationLevel & OptimizationLevel.Level1) >= OptimizationLevel.Level1)
{
if (lastSymbol != symbol && lastSymbol != TokenSymbol.None && (lastSymbol == TokenSymbol.Decrement || lastSymbol == TokenSymbol.Increment || lastSymbol == TokenSymbol.MoveLeft || lastSymbol == TokenSymbol.MoveRight))
{
Lines.AddRange(SymbolToLines(lastSymbol, symbolRepeated, indents));
symbolRepeated = 0;
}
else
{
lastSymbol = symbol;
symbolRepeated++;
}
}These lines are duplicated* 5 times in
void Interpret(string): once in every single case block.. not ideal.Start by copying it again a 6th time, here:
foreach (var symbol in _symbols)
{
if (lastSymbol != symbol && lastSymbol != TokenSymbol.None && (lastSymbol == TokenSymbol.Decrement || lastSymbol == TokenSymbol.Increment || lastSymbol == TokenSymbol.MoveLeft || lastSymbol == TokenSymbol.MoveRight))
{
}
switch (symbol)
{
...Now remove it from every single case block, except the last one* - and then turn this:
if (lastSymbol != symbol && lastSymbol != TokenSymbol.None && (lastSymbol == TokenSymbol.Decrement || lastSymbol == TokenSymbol.Increment || lastSymbol == TokenSymbol.MoveLeft || lastSymbol == TokenSymbol.MoveRight))Into this:
if (lastSymbol != symbol && lastSymbol != TokenSymbol.None
&& (lastSymbol == TokenSymbol.Decrement
|| lastSymbol == TokenSymbol.Increment
|| lastSymbol == TokenSymbol.MoveLeft
|| lastSymbol == TokenSymbol.MoveRight))Ah, now we can read it! Or can we? What's it saying anyway? What do the
Decrement, Increment, MoveLeft and MoveRight symbols have in common?Could we say that they're the instructions you consider optimizable?
private static readonly TokenSymbol[] OptimizableInstructions =
{
TokenSymbol.Decrement,
TokenSymbol.Increment,
TokenSymbol.MoveLeft,
TokenSymbol.MoveRight
};And that condition is verifying what exactly, whether the current symbol ends a sequence of repeating previous symbol?
private static bool IsRepeatedSymbolSequenceEnding(TokenSymbol previous, TokenSymbol current)
=> previous != current
&& previous != TokenSymbol.None
&& OptimizableInstructions.Contains(previous);That little static function is just enough abstraction to know what's going on without being bothered with the whole 12-operator logic:
if (IsRepeatedSymbolSequenceEnding(lastSymbol, symbol))
{
Lines.AddRange(SymbolToLines(lastSymbol, symbolRepeated, indents));
symbolRepeated = 0;
}
else
{
lastSymbol = symbol;
symbolRepeated++;
}Not sure I like the naming - I mean,
IsRepeatedSymbolSequenceEnding is kinda awful, but I'm thinking more of lastSymbol versus symbolRepeated - "last symbol" reads well. "repeated symbol" would read better than symbolRepeated.. but the variable is an int and is actually counting repetitions - wouldn't repetitions be a much better name for it?Anyway, I skipped something:
if ((OptimizationLevel & OptimizationLevel.Level1) >= OptimizationLevel.Level1)Why the bitwise operation on a non-flag enum here? What's wrong with simplicity here?
if (OptimizationLevel >= OptimizationLevel.Level1)Per C# spec. 7.9.5, enum comparison operators operate like their underlying type:
In other words, the enumeration type comparison operators simply compare the underlying integral values of the two operands.
KISS dude.
*The last block is slightly different.
So, that last block.
case TokenSymbol.Decrement:
case TokenSymbol.Increment:
case TokenSymbol.MoveLeft:
case TokenSymbol.MoveRight:Where have I seen those...
Ah, yes!
private static readonly TokenSymbol[] OptimizableInstructions =
{
TokenSymbol.Decrement,
TokenSymbol.Increment,
TokenSymbol.MoveLeft,
TokenSymbol.MoveRight
};I think you could turn that
foreach loop into this then:```
foreach (var symbol in _symbols)
{
if (OptimizationLevel >= OptimizationLevel.Level1)
{
var isOptimizableSymbol = OptimizableInstructions.Contains(symbol);
if (IsRepeatedSymbolSequenceEnding(lastSymbol, symbol))
{
Lines.AddRange(SymbolToLines(lastSymbol, repetitions, indents));
repetitions = isOptimizableSymbol ? 1 : 0;
}
else
{
if (isOptimizableSymbol)
{
repetitions++;
}
else
{
repetitions = 0;
}
}
}
switch (symbol)
{
case TokenSymbol.LoopBegin:
Lines.AddRange(SymbolToLines(symbol, repetitions, indents));
indents += 1;
break;
case TokenSymbol.LoopEnd:
indents -= 1;
Lines.AddRange(SymbolToLines(symbol, repetitions, indents));
break;
case TokenSymbol.InputCharacter:
case TokenSymbo
Code Snippets
if ((OptimizationLevel & OptimizationLevel.Level1) >= OptimizationLevel.Level1)
{
if (lastSymbol != symbol && lastSymbol != TokenSymbol.None && (lastSymbol == TokenSymbol.Decrement || lastSymbol == TokenSymbol.Increment || lastSymbol == TokenSymbol.MoveLeft || lastSymbol == TokenSymbol.MoveRight))
{
Lines.AddRange(SymbolToLines(lastSymbol, symbolRepeated, indents));
symbolRepeated = 0;
}
else
{
lastSymbol = symbol;
symbolRepeated++;
}
}foreach (var symbol in _symbols)
{
if (lastSymbol != symbol && lastSymbol != TokenSymbol.None && (lastSymbol == TokenSymbol.Decrement || lastSymbol == TokenSymbol.Increment || lastSymbol == TokenSymbol.MoveLeft || lastSymbol == TokenSymbol.MoveRight))
{
}
switch (symbol)
{
...if (lastSymbol != symbol && lastSymbol != TokenSymbol.None && (lastSymbol == TokenSymbol.Decrement || lastSymbol == TokenSymbol.Increment || lastSymbol == TokenSymbol.MoveLeft || lastSymbol == TokenSymbol.MoveRight))if (lastSymbol != symbol && lastSymbol != TokenSymbol.None
&& (lastSymbol == TokenSymbol.Decrement
|| lastSymbol == TokenSymbol.Increment
|| lastSymbol == TokenSymbol.MoveLeft
|| lastSymbol == TokenSymbol.MoveRight))private static readonly TokenSymbol[] OptimizableInstructions =
{
TokenSymbol.Decrement,
TokenSymbol.Increment,
TokenSymbol.MoveLeft,
TokenSymbol.MoveRight
};Context
StackExchange Code Review Q#111660, answer score: 8
Revisions (0)
No revisions yet.