为了提高对我以前的brainfuck问题的认识,这里还提供了brainfuck解释器。
这是用Java 8编写的
类摘要(298 4个文件行,总共7409字节)
BrainF.java:表示Brainfuck程序/执行
BrainFCommand.java:各种Brainfuck命令的枚举
代码
也可以在github上找到此代码
BrainF.java:(198行,4575字节)
public class BrainF {
public static final int DEFAULT_MEMORY_SIZE = 0x1000;
public BrainF(int memorySize, String code, Stream<Byte> in) {
this(memorySize, in);
addCommands(code);
}
public BrainF(int memorySize, Stream<Byte> in) {
memory = new byte[memorySize];
input = in.iterator();
}
private final List<BrainFCommand> commands = new ArrayList<>();
private final Iterator<Byte> input;
private int commandIndex;
private final byte[] memory;
private final StringBuilder output = new StringBuilder();
private int memoryIndex;
public void addCommands(String string) {
string.chars().mapToObj(i -> BrainFCommand.getCommand((char) i)).filter(obj -> obj != null).forEachOrdered(commands::add);
}
private void changeMemory(int i) {
checkMemoryIndex();
memory[memoryIndex] += i;
}
private void findMatching(BrainFCommand decrease, BrainFCommand increase, int direction) {
int matching = 1;
while (true) {
commandIndex += direction;
BrainFCommand current = commands.get(commandIndex);
if (current == decrease) {
matching--;
if (matching == 0) {
break;
}
}
else if (current == increase) {
matching++;
}
}
}
public byte getMemory() {
return memory[memoryIndex];
}
public void runToEnd() {
while (commandIndex < commands.size()) {
step();
}
}
public BrainFCommand step() {
if (commandIndex >= commands.size()) {
return null;
}
BrainFCommand command = commands.get(commandIndex);
perform(command);
commandIndex++;
return command;
}
public void setMemory(byte value) {
memory[memoryIndex] = value;
}
public String getOutput() {
return output.toString();
}
public int getMemoryIndex() {
return memoryIndex;
}
public int getCommandIndex() {
return commandIndex;
}
public void perform(BrainFCommand command) {
switch (command) {
case ADD:
changeMemory(1);
break;
case END_WHILE:
if (getMemory() != 0) {
findMatching(BrainFCommand.WHILE, BrainFCommand.END_WHILE, -1);
}
break;
case NEXT:
memoryIndex++;
checkMemoryIndex();
break;
case PREVIOUS:
memoryIndex--;
checkMemoryIndex();
break;
case READ:
byte value = input.next();
setMemory(value);
break;
case SUBSTRACT:
changeMemory(-1);
break;
case WHILE:
if (getMemory() == 0) {
findMatching(BrainFCommand.END_WHILE, BrainFCommand.WHILE, 1);
}
break;
case WRITE:
char write = (char) getMemory();
output.append(write);
break;
case NONE:
default:
break;
}
}
private void checkMemoryIndex() {
if (memoryIndex < 0) {
memoryIndex += memory.length;
}
if (memoryIndex >= memory.length) {
memoryIndex -= memory.length;
}
}
public byte[] getMemoryArray(int fromIndex, int length) {
return Arrays.copyOfRange(memory, fromIndex, fromIndex + length);
}
public void setCommands(String text) {
commands.clear();
addCommands(text);
}
public void reset() {
Arrays.fill(memory, (byte) 0);
commandIndex = 0;
memoryIndex = 0;
output.setLength(0);
}
public int getMemorySize() {
return memory.length;
}
public byte getMemory(int index) {
return memory[index];
}
public static BrainF createFromCodeAndInput(int memorySize, String code, String input) {
return createFromCodeAndInput(memorySize, code, input.chars().mapToObj(i -> (byte) i ));
}
public static BrainF createFromCodeAndInput(int memorySize, String code, Stream<Byte> inputStream) {
return new BrainF(DEFAULT_MEMORY_SIZE, code, inputStream);
}
public static BrainF createFromCode(String code) {
return createFromCodeAndInput(DEFAULT_MEMORY_SIZE, code, "");
}
public static BrainF createFromCode(int memorySize, String code) {
return createFromCodeAndInput(memorySize, code, "");
}
public static BrainF createWithDefaultSize() {
return createUsingSystemInputWithMemorySize(DEFAULT_MEMORY_SIZE);
}
public static BrainF createUsingSystemInputWithMemorySize(int memorySize) {
Stream<Byte> in = Stream.generate(() -> {
try {
return (byte) System.in.read();
}
catch (Exception e) {
throw new RuntimeException(e);
}
});
return new BrainF(memorySize, in);
}
}
BrainFCommand.java:(27行,619字节)
public enum BrainFCommand {
NONE((char) 0), NEXT('>'), PREVIOUS('<'), WRITE('.'), READ(','), ADD('+'), SUBSTRACT('-'), WHILE('['), END_WHILE(']');
private final char ch;
private static final Map<Character, BrainFCommand> commands = new HashMap<>();
static {
for (BrainFCommand comm : BrainFCommand.values()) {
commands.put(comm.ch, comm);
}
}
private BrainFCommand(char ch) {
this.ch = ch;
}
public static BrainFCommand getCommand(char ch) {
return commands.getOrDefault(ch, NONE);
}
}
用法/测试
简单测试即可证明其有效。在Github上可以找到一个简单的GUI(如果使用它,请确保单击“保存代码”按钮)
BrainTest.java :( 67行,1992字节)
public class BrainTest {
@Test
public void gotoCorrectEndWhile() {
BrainF brain = BrainF.createWithDefaultSize();
brain.addCommands(">+>[-]+ ");
brain.addCommands("++[-->++]--> Find next 254 and go one step beyond it");
brain.addCommands(" Loop through all 254s");
brain.addCommands("+++[--- Make sure that we are not at 253 (end)");
brain.addCommands("++[--<++]-- ");
assertEquals(BrainFCommand.NEXT, brain.step());
assertEquals(BrainFCommand.ADD, brain.step());
assertEquals(BrainFCommand.NEXT, brain.step());
assertEquals(BrainFCommand.WHILE, brain.step());
assertEquals(6, brain.getCommandIndex());
assertEquals(BrainFCommand.ADD, brain.step());
}
@Test
public void simpleLoopMultiplication() {
BrainF brain = BrainF.createWithDefaultSize();
brain.addCommands("++[>+++<-]>>>");
brain.runToEnd();
assertArrayEquals(new byte[] { 0, 6, 0, 0, 0, 0, 0, 0, 0, 0 },
brain.getMemoryArray(0, 10));
}
@Test
public void printAlphabet() {
BrainF brain = BrainF.createWithDefaultSize();
brain.addCommands("++++++[>++++++++++>++++<<-]>+++++>++[-<.+>]");
brain.runToEnd();
assertEquals("ABCDEFGHIJKLMNOPQRSTUVWXYZ", brain.getOutput());
}
@Test
public void input() {
BrainF brain = BrainF.createFromCodeAndInput(BrainF.DEFAULT_MEMORY_SIZE, "+++,.", "a");
brain.runToEnd();
assertEquals("a", brain.getOutput());
}
@Test
public void simpleCommands() {
BrainF abc = BrainF.createWithDefaultSize();
abc.addCommands("+>++>+++<");
abc.runToEnd();
assertEquals(9, abc.getCommandIndex());
assertEquals(1, abc.getMemoryIndex());
assertEquals(2, abc.getMemory());
abc.perform(BrainFCommand.PREVIOUS);
assertEquals(1, abc.getMemory());
abc.perform(BrainFCommand.NEXT);
abc.perform(BrainFCommand.NEXT);
assertEquals(3, abc.getMemory());
}
}
问题
Stream<Byte>
是输入的不错选择吗?我正在考虑几个问题,但是在我看来,这似乎是最好的选择。欢迎任何其他评论,但是我希望更多的“高级”评论胜于风格和命名细节。我认为我没有犯任何样式或命名错误,但是如果您真的认为我也可以对此发表评论。
#1 楼
将内存移到其自己的类中。 BF程序正在操作的磁带的概念可以通过有限的接口明确地定义为自己的类。对于BF解释器,实际上只有先设置一个特定的程序然后运行它才有意义。对于解释器来说,操作或检查超出此范围的状态实际上是没有意义的。就目前而言,您的接口有太多选项。不要将输入传递给构造函数。如果这完全是一个认真的解释器,那么您可能希望能够针对不同的输入运行同一程序。因此,将输入作为
run
函数的参数确实更有意义。 checkMemoryIndex
和findMatching
名称不正确。两者都更改了BF解释器的状态,但是没有一个名称使我认为会。我希望check*
断言状态是否变坏,find*
返回结果。 #2 楼
可用性f。[<不带
main()
函数时如何使用此解释器?这是我使用java.nio.file.*
提出的最简单的实现:即使显示了一些内部可用性问题,也是如此。我本来希望public static void main(String[] args) throws IOException {
String code = new String(Files.readAllBytes(Paths.get(args[0])));
// TODO: Implement InputStreamToByteIteratorAdaptor
BrainF interp = createFromCodeAndInput(DEFAULT_MEMORY_SIZE, code, new InputStreamToByteIteratorAdaptor());
interp.runToEnd();
System.out.println(interp.getOutput());
}
工作。值得注意的是,
工厂方法名称不需要提及“默认值” —
BrainF.create()
或new BrainF()
也可以正常工作。当我要担心尺寸时,让我担心尺寸。解释器还可以根据需要自动扩展内存。通过
Iterator<Byte>
指定输入并将输出缓冲到StringBuilder
非常麻烦。将输入和输出分别连接到System.in
和System.out
应该很容易-实际上,除非故意将I / O重定向,否则这应该是容易的。我不必编写InputStreamToByteIteratorAdaptor
来运行交互式程序。runToEnd()
可能只是run()
。此外,此工厂方法有问题:
public static void main(String[] args) throws IOException {
(new BrainF(new File(args[0]))).run();
}
它有很多错误,因为它放弃了其
memorySize
参数。另外,它的作用与BrainF(memorySize, code, in)
构造函数相同。如果提供工厂方法,则将构造函数设为私有,以避免使接口提供的内容混乱。或者,只需提供多个构造函数。命令
switch
中的巨型BrainF.perform()
是一种代码味道。当您可以直接打开字符文字时,为什么还要费心创建一个枚举呢?或者,如果您确实有一个枚举,则每个命令的行为都应在命令本身中实现。SUBSTRACT
应该重命名为SUBTRACT
。 (就我个人而言,我会选择“增加” /“减少”。)