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派生言語が作成できます、みたいな。
ふむ、苦手分野か。