brainf*ckらめぇぇぇぇ

激しく今さら感漂いまくりだけど、brainf*ck作ってみた。まあいろいろ突っ込み所はあると思いますが勘弁してね。

using System;
using System.IO;
using System.Text;
namespace Brainf_ckProcessor
{
  class Program
  {
    static void Main(string[] args)
    {
      // 処理系オブジェクト
      Processor processor = new Processor(Console.Read, Console.Write);

      // コマンドラインでソースが指定されればソースから実行
      if (args.Length <= 0) InteractiveMode(processor);
      else if (File.Exists(args[0])) FromSourceFile(args[0], processor);
      else Console.WriteLine("file not found.");
    }

    /// <summary>ソースコードから実行</summary>
    static void FromSourceFile(string filePath, Processor processor)
    {
      StreamReader sr = new StreamReader(filePath, Encoding.Default);
      string source = sr.ReadToEnd();
      sr.Close();

      try
      {
        processor.Eval(source);
      }
      catch (Exception e)
      {
        Console.Write(e.Message);
      }
      finally
      {
        Console.WriteLine(string.Empty);
      }
    }

    /// <summary>対話形式</summary>
    static void InteractiveMode(Processor processor)
    {
      string source = string.Empty;
      while (null != (source = Prompt("brainf*ck> ")))
      {
        try
        {
          processor.Eval(source);
        }
        catch (Exception e)
        {
          Console.Write(e.Message);
          break;
        }
        finally
        {
          Console.WriteLine(string.Empty);
        }
      }
    }

    /// <summary>対話形式のプロンプト</summary>
    static string Prompt(string prompt)
    {
      Console.Write(prompt);
      return Console.ReadLine();
    }
  }
}
using System;
using System.Collections.Generic;
namespace Brainf_ckProcessor
{
  /// <summary>処理系オブジェクト</summary>
  class Processor
  {
    /// <summary>字句解析器</summary>
    Lexer _lexer;

    /// <summary>データメモリ領域</summary>
    char[] _memData;

    /// <summary>現在見ているデータメモリの番地</summary>
    int _ptrData;

    /// <summary>この処理系が扱えるメモリ領域の大きさ</summary>
    const int MEMORY_SIZE = 1024;

    /// <summary>入力</summary>
    Func<int> _read;

    /// <summary>出力</summary>
    Action<char> _write;

    /// <summary>Brainf*ck処理系を作成</summary>
    public Processor(Func<int> read, Action<char> write)
      : this(read, write, ">", "<", "+", "-", ".", ",", "[", "]") { }

    /// <summary>字句をカスタマイズしたBrainf*ck処理系を作成</summary>
    public Processor(Func<int> read, Action<char> write,
      string moveForward, string moveBackward,
      string incrementPointee, string decrementPointee,
      string output, string input,
      string entryLoop, string exitLoop)
    {
      _read = read;
      _write = write;
      _lexer =
        new Lexer(moveForward, moveBackward,
          incrementPointee, decrementPointee,
          input, output, entryLoop, exitLoop);
      SetupDataMemory();
    }

    /// <summary>メモリ関係を初期化</summary>
    void SetupDataMemory()
    {
      _ptrData = 0;
      _memData = new char[MEMORY_SIZE];
      for (int i = 0; i < MEMORY_SIZE; i++)
      {
        _memData[i] = (char)0;
      }
    }

    /// <summary>ソースコードを渡して実行</summary>
    public void Eval(string source)
    {
      List<LexicalToken> memInst = _lexer.ReadAndTokenize(source);
      //if (MEMORY_SIZE < memInst.Count)
      //  throw new OutOfMemoryException("source code too long");

      int ptrInst = 0;
      while (ptrInst != memInst.Count)
      {
        if (memInst[ptrInst] == LexicalToken.MoveForward)
        {
          if (_memData.Length - 1 == _ptrData)
            throw new OutOfMemoryException("out of memory");
          ++_ptrData;
        }
        else if (memInst[ptrInst] == LexicalToken.MoveBackward)
        {
          if (_ptrData == 0)
            throw new OutOfMemoryException("out of memory");
          --_ptrData;
        }
        else if (memInst[ptrInst] == LexicalToken.IncrementPointee)
        {
          ++_memData[_ptrData];
        }
        else if (memInst[ptrInst] == LexicalToken.DecrementPointee)
        {
          --_memData[_ptrData];
        }
        else if (memInst[ptrInst] == LexicalToken.Input)
        {
          _memData[_ptrData] = (char)_read();
        }
        else if (memInst[ptrInst] == LexicalToken.Output)
        {
          _write((char)_memData[_ptrData]);
        }

        else if (memInst[ptrInst] == LexicalToken.EntryLoop)
        {
          if (_memData[_ptrData] == 0)
          {
            int index =
              memInst.FindIndex(ptrInst + 1, t => t == LexicalToken.ExitLoop);
            if (index < 0) throw new SyntaxException("syntax error");
            ptrInst = index;
          }
        }
        else
        {
          bool found = false;
          for (int i = ptrInst - 1; 0 <= i; i--)
          {
            if (memInst[i] == LexicalToken.EntryLoop)
            {
              found = true;
              ptrInst = i - 1;
              break;
            }
          }
          if (!found) throw new SyntaxException("syntax error");
        }

        ptrInst++;
      }
    }
  }
}
using System.Collections.Generic;
namespace Brainf_ckProcessor
{
  /// <summary>字句解析器的な何か</summary>
  class Lexer
  {
    /// <summary>ソースとトークンの対応表</summary>
    Dictionary<string, LexicalToken> _tokenTable;

    /// <summary>字句解析器を作成</summary>
    public Lexer(string moveForward, string moveBackward,
      string incrementPointee, string decrementPointee,
      string input, string output, string entryLoop, string exitLoop)
    {
      _tokenTable = new Dictionary<string, LexicalToken>();
      _tokenTable.Add(moveForward, LexicalToken.MoveForward);
      _tokenTable.Add(moveBackward, LexicalToken.MoveBackward);
      _tokenTable.Add(incrementPointee, LexicalToken.IncrementPointee);
      _tokenTable.Add(decrementPointee, LexicalToken.DecrementPointee);
      _tokenTable.Add(input, LexicalToken.Input);
      _tokenTable.Add(output, LexicalToken.Output);
      _tokenTable.Add(entryLoop, LexicalToken.EntryLoop);
      _tokenTable.Add(exitLoop, LexicalToken.ExitLoop);
    }

    /// <summary>ソースからトークンへの変換</summary>
    public List<LexicalToken> ReadAndTokenize(string source)
    {
      List<LexicalToken> tokens = new List<LexicalToken>();

      for (; !string.IsNullOrEmpty(source); )
      {
        string key = string.Empty;
        foreach (string k in _tokenTable.Keys)
        {
          if (source.StartsWith(k))
          {
            key = k;
            break;
          }
        }

        // 現在の先頭文字は不要な文字
        if (string.IsNullOrEmpty(key))
        {
          source = source.Substring(1);
          continue;
        }

        tokens.Add(_tokenTable[key]);
        source = source.Substring(key.Length);
      }
      return tokens;
    }
  }

  /// <summary>トークン</summary>
  public enum LexicalToken
  {
    /// <summary>ポインタを1つ進める</summary>
    MoveForward,
    /// <summary>ポインタを1つ戻す</summary>
    MoveBackward,
    /// <summary>ポインタが指す要素の値を1増やす</summary>
    IncrementPointee,
    /// <summary>ポインタが指す要素の値を1減らす</summary>
    DecrementPointee,
    /// <summary>標準入力からの入力をポインタの指す場所へ格納</summary>
    Input,
    /// <summary>ポインタの指す値を標準出力へ出力</summary>
    Output,
    /// <summary>ポインタが指す値が0ならば、対応するExitLoopまでジャンプ</summary>
    EntryLoop,
    /// <summary>対応するEntryLoopへジャンプ</summary>
    ExitLoop
  }
}
using System;
namespace Brainf_ckProcessor
{
  class OutOfMemoryException : Exception
  {
    public OutOfMemoryException() { }
    public OutOfMemoryException(string msg) : base(msg) { }
  }
  class SyntaxException : Exception
  {
    public SyntaxException() { }
    public SyntaxException(string msg) : base(msg) { }
  }
}

HelloWorldのソースはこうなる

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

で、だ。

8つの命令を変更できるようにしたので、

Processor processor =
  new Processor(Console.Read, Console.Write,
    "らめぇ", "ひぎぃ",
    "ぇ", "ぁ",
    "んあっ", "やんっ", "ビクンッ", "!");

とでもやると、HelloWorldのソースはこう書ける。

ぇぇぇぇぇぇぇぇぇ
ビクンッらめぇぇぇぇぇぇぇぇぇらめぇぇぇぇぇぇぇぇぇぇぇぇらめぇぇぇぇぇぇひぎぃひぎぃひぎぃぁ!
らめぇんあっ
らめぇぇぇんあっ
ぇぇぇぇぇぇぇんあっ
んあっ
ぇぇぇんあっ
らめぇぁんあっ
ぁぁぁぁぁぁぁぁぁぁぁぁんあっ
ひぎぃぇぇぇぇぇぇぇぇんあっ
ぁぁぁぁぁぁぁぁんあっ
ぇぇぇんあっ
ぁぁぁぁぁぁんあっ
ぁぁぁぁぁぁぁぁんあっ
らめぇぇんあっ

僕はあんまり変態ではないので、イマイチな出来だなぁ。