PowershellでBrainf*ck

Powershellじゃなくて、C#だけど。
なんとなく書いてみた。

$source = @"
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

    public class Brainf_ck
    {
        private const char WHILE = '[';
        private const char LOOP = ']';

        // ま、Powershell用なので、利便性から public に
        public List<char> _mem = new List<char>();
        public int _index = 0;
        public Stack<StringBuilder> _while = new Stack<StringBuilder>();
        public int _do = 0;

        public Dictionary<string, char> _dic = new Dictionary<string, char>();


        public Brainf_ck()
        {
            _mem.Add((char)0);
            // ジョジョ言語用 http://d.hatena.ne.jp/toyoshi/20100208/1265587511
            _dic.Add("スターフィンガ", '>');
            _dic.Add("ロードローラ", '<');
            _dic.Add("オラ", '+');
            _dic.Add("無駄", '-');
            _dic.Add("ハーミットパープル", '.');
            _dic.Add("新手のスタンド使いか", ',');
            _dic.Add("あ・・・ありのまま今起こったことを話すぜ", '[');
            _dic.Add("ザ・ワールド", ']');
            _dic.Add("やれやれだぜ", '>');
            _dic.Add("貧弱", '<');
        }

        private void Move(int d)
        {
            if (_do == 0)
            {
                int index = _index + d;
                if (index < 0)
                {
                    Log("Error(decrement) " + index);
                    index = 0;
                }
                while (index >= _mem.Count) _mem.Add((char)0);
                _index = index;
            }
        }
        private void Add(int d)
        {
            if (_do == 0)
            {
                char c = _mem[_index];
                c += (char)d;
                _mem[_index] = c;
            }
        }
        private void Put()
        {
            if (_do == 0)
            {
                Console.Out.Write(_mem[_index]);
            }
        }
        private void Get()
        {
            if (_do == 0)
            {
                _mem[_index] = (char)Console.In.Read();
            }
        }
        private void While()
        {
            if (_do == 0 && _mem[_index] != 0)
            {
                _while.Push(new StringBuilder());
            }
            else
            {
                _do++;
            }
        }
        private string Loop(string code)
        {
            string ret = code;
            if (_do == 0)
            {
                StringBuilder part = _while.Pop();
                if (_mem[_index] != 0)
                {
                    ret = part.Append(LOOP).Append(code).ToString();
                    _while.Push(new StringBuilder());
                }
                else
                {
                    if (_while.Count > 0) _while.Peek().Append(WHILE).Append(part).Append(LOOP);
                }
            }
            else
            {
                _do--;
                if (_do < 0)
                {
                    Log("Error(While - Loop)");
                    _do = 0;
                }
            }
            return ret;
        }
        private void Log(string msg)
        {
            Console.Out.WriteLine(msg);
        }
        private char Trans(ref string code)
        {
            bool found = false;
            char ret = '\0';
            int len = 0;
            foreach (KeyValuePair<string, char> kv in _dic)
            {
                if (code.StartsWith(kv.Key))
                {
                    ret = kv.Value;
                    len = kv.Key.Length;
                    found = true;
                    break;
                }
            }
            // 見つからない場合、先頭の1文字
            if (!found)
            {
                ret = code[0];
                len++;
            }
            code = code.Substring(len);
            return ret;
        }

        public void Run(string code)
        {
            string part = "" + code;
            while (part.Length > 0)
            {
                char c = Trans(ref part);
                switch (c)
                {
                    case '>':
                        Move(1);
                        break;
                    case '<':
                        Move(-1);
                        break;
                    case '+':
                        Add(1);
                        break;
                    case '-':
                        Add(-1);
                        break;
                    case '.':
                        Put();
                        break;
                    case ',':
                        Get();
                        break;
                    case WHILE:
                        While();
                        c = '\0';
                        break;
                    case LOOP:
                        part = Loop(part);
                        c = '\0';
                        break;
                    default:
                        // nop
                        c = '\0';
                        break;
                }
                if (_while.Count > 0 && c != '\0')
                {
                    _while.Peek().Append(c);
                }
            }
        }
        

        public static void Main(string[] args)
        {
            for (int i = 0; i < args.Length; i++)
            {
                Brainf_ck pg = new Brainf_ck();
                if (File.Exists(args[i]))
                {
                    using(FileStream fs = new FileStream(args[i], FileMode.Open))
                    using (StreamReader sr = new StreamReader(fs))
                    {
                        string code = "";
                        while (code != null)
                        {
                            code = sr.ReadLine();
                            pg.Run(code);
                        }
                        sr.Close();
                        fs.Close();
                    }
                }
                else
                {
                    pg.Run(args[i]);
                }
            }
        }
    }
"@
# コンパイルする
Add-Type -TypeDefinition $source
$code = @"
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+++.>-.------------.<++++++++.--------.+++.------.--------.>+.
--------------------.---.
"@
[Brainf_ck]::Main($code)


サンプルは、Powershell_iseだと上手く動かない。
Console.Out.Writeが効かない模様。
ただのPowershellで実行のこと。
データがcharなので、本家とは少し異なる。


$codeは、Hello, world!なんだけど、二行目を付与。
改行がつかないと、Powershellで読みにくいので。


あと、コード中にも書いたけど、ジョジョ言語にも対応してみた。
(ハーミットパープル呼び過ぎじゃない?)
コードはUnicodeの必要あり。
ジョジョ言語用と書いたDictionaryへのAddをやめて、他の言語を入れれば、その言語になります。(けど制約あり)
A言語的なのは無理。


ちょっとだけ工夫したのは、ループの処理かな。
全コードを読み込まないと動かないようには、したくなかったので。
といっても、巨大ループを書かれたら、全コード読むのと変わらないけど。
ま、ただのBrainf*ckコードになるので、ましですが。
もっとちゃんと考えれば、もう少しスマートに出来そう。


これで気軽にネタ言語作りたい放題、、、。
意味、ないけど。


ネタ言語作りたい放題、と言うのであれば、、、逆を作るべきな気がしてきた。
文章を入力すると、これをHello, world!にするには、
この辺とこの辺に、この単語を入れれば、こういうマッピングで、
Brainf*ck派生言語が作成できます、みたいな。
ふむ、苦手分野か。