Brainf*ck

| # Comments | 1 Trackback
昨日に続きまして、 Rubyで作る奇妙なプログラミング言語 ~Esoteric Language~にて紹介した本の中から、「Brainf*ck」言語のJava実装です。

Brainfuckの言語仕様

実行可能な命令は「8つ」のみである。

  1. > ポインタをインクリメントする。ポインタをptrとすると、C言語の「ptr++;」に相当する。
  2. < ポインタをデクリメントする。C言語の「ptr--;」に相当。
  3. + ポインタが指す値をインクリメントする。C言語の「(*ptr)++;」に相当。
  4. - ポインタが指す値をデクリメントする。C言語の「(*ptr)--;」に相当。
  5. . ポインタが指す値を出力する。C言語の「putchar(*ptr);」に相当。
  6. , 1バイトを入力してポインタが指す値に代入する。C言語の「*ptr=getchar();」に相当。
  7. [ ポインタが指す値が0なら、対応する ] の直後までジャンプする。C言語の「while(*ptr){」に相当。
  8. ] ポインタが指す値が0でないなら、対応する [ にジャンプする。C言語の「}」に相当。
Wikipediaより

今日は初めから独自実装。


package st.happy_camper.esoteric.brainfuck;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import st.happy_camper.esoteric.util.Argf;

/**
 * @author ueshin
 */
public class Brainfuck {

    private interface CommandProcessor {

        void process();
    }

    private final Map<Character, CommandProcessor> processors = new HashMap<Character, CommandProcessor>();

    {
        processors.put('>', new PointerToRightProcessor());
        processors.put('<', new PointerToLeftProcessor());
        processors.put('+', new IncrementProcessor());
        processors.put('-', new DecrementProcessor());
        processors.put('.', new WriteProcessor());
        processors.put(',', new ReadProcessor());
        processors.put('[', new ForwardProcessor());
        processors.put(']', new BackProcessor());
    }

    private final String src;

    private List<Integer> tape;

    private int pointer;

    private int current;

    /**
     * @param src
     */
    public Brainfuck(String src) {
        this.src = src;
    }

    /**
     * 
     */
    public synchronized void run() {
        tape = new ArrayList<Integer>();
        tape.add(0);
        for(pointer = 0, current = 0; current < src.length(); current++) {
            char c = src.charAt(current);
            CommandProcessor processor = processors.get(c);
            if(processor != null) {
                processor.process();
            }
        }
    }

    /**
     * @param args
     * @throws IOException
     */
    public static void main(String... args) throws IOException {
        new Brainfuck(Argf.read(args)).run();
    }

    private class PointerToRightProcessor implements CommandProcessor {

        @Override
        public void process() {
            pointer++;
            while(tape.size() <= pointer) {
                tape.add(0);
            }
        }
    }

    private class PointerToLeftProcessor implements CommandProcessor {

        @Override
        public void process() {
            pointer--;
            if(pointer < 0) {
                throw new IndexOutOfBoundsException();
            }
        }
    }

    private class IncrementProcessor implements CommandProcessor {

        @Override
        public void process() {
            tape.set(pointer, tape.get(pointer) + 1);
        }
    }

    private class DecrementProcessor implements CommandProcessor {

        @Override
        public void process() {
            tape.set(pointer, tape.get(pointer) - 1);
        }
    }

    private class WriteProcessor implements CommandProcessor {

        @Override
        public void process() {
            System.out.print((char)tape.get(pointer).intValue());
        }
    }

    private class ReadProcessor implements CommandProcessor {

        @Override
        public void process() {
            try {
                tape.set(pointer, System.in.read());
            }
            catch(IOException e) {
            }
        }
    }

    private class ForwardProcessor implements CommandProcessor {

        @Override
        public void process() {
            if(tape.get(pointer) == 0) {
                current++;
                for(int brackets = 1; brackets > 0; current++) {
                    char c = src.charAt(current);
                    if(c == '[') {
                        brackets++;
                    }
                    else if(c == ']') {
                        brackets--;
                    }
                }
                current--;
            }
        }
    }

    private class BackProcessor implements CommandProcessor {

        @Override
        public void process() {
            if(tape.get(pointer) != 0) {
                current--;
                for(int brackets = 1; brackets > 0; current--) {
                    char c = src.charAt(current);
                    if(c == '[') {
                        brackets--;
                    }
                    else if(c == ']') {
                        brackets++;
                    }
                }
                current++;
            }
        }
    }

}

Hello World

入力ファイルとして以下のファイルを準備します。

++++++++[>+++++++++<-]>.
<+++++[>++++++<-]>-.
<++[>+++<-]>+.
.
+++.
<++++++[>----<-]>.
<++++++[>++++<-]>.
+++.
------.
--------.

もしくは

+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.
------------.<++++++++.--------.+++.------.--------.>+.

helloworld.txtとして保存して、

$ java st.happy_camper.esoteric.brainfuck.Brainfuck helloworld.txt
HelloWorld

Argfクラス

引数で渡されたファイルをすべて読み込む部分をユーティリティとして切り出しています。
引数がなければ標準入力から読み込みます。

package st.happy_camper.esoteric.util;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;

/**
 * @author ueshin
 */
public class Argf {

    /**
     * 
     */
    private Argf() {
    }

    /**
     * @param args
     * @return
     * @throws IOException
     */
    public static String read(String... args) throws IOException {
        StringBuilder src = new StringBuilder();
        if(args != null && args.length > 0) {
            for(String file : args) {
                read(src, new FileReader(file));
            }
        }
        else {
            read(src, new InputStreamReader(System.in));
        }
        return src.toString();
    }

    /**
     * @param src
     * @param reader
     * @throws IOException
     */
    private static void read(StringBuilder src, Reader reader)
            throws IOException {
        BufferedReader BufferedReader = null;
        try {
            BufferedReader = new BufferedReader(reader);

            String line;
            while((line = BufferedReader.readLine()) != null) {
                src.append(line);
                src.append("\n");
            }
        }
        finally {
            if(BufferedReader != null) {
                BufferedReader.close();
                BufferedReader = null;
            }
        }
    }

}

トラックバック(1)

Happy-Camper Street - Whitespace (2009年1月22日 02:10)

Rubyで作る奇妙なプログラミング言語 ~Esoteric Language~... 続きを読む

comments powered by Disqus

Twitter Icon

AdSense

Creative Commons License
このブログはクリエイティブ・コモンズでライセンスされています。
Powered by Movable Type 5.14-ja

Google検索

カスタム検索

2013年10月

    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31