読者です 読者をやめる 読者になる 読者になる

エロと研究と日々の徒然

淫夢語でプログラミング

こんにちは。プルさわです。

最近私の周りに淫夢好きなよく分からない人が目に着くようになりました。たまげたなあ。
私は正直淫夢のことは良くわからないのですが、最近ハマってる変なプログラミング言語シリーズと絡めてなんかしてみようということで、今回新しいプログラミング言語を作ってみました。

ということで、できたのがIceTea(アイスティー)という言語です。

Rubyで作りました。

命令とか使い方とか

とは言っても内容はBrainf_ckに毛が生えた程度のものなので、とりあえず知らない人は↓の記事でも読んでいただけると良いかと思います。

Brainfuck - Wikipedia

以下のように使ってください。

命令文字列 機能
n回だよ、n回あくしろよ ポインタの指す値をn増やす
n回だよ、n回お手だよ ポインタの指す値をn減らす
なんか足んねえよなぁもうn回鳴いてみろ ポインタをn進める
この野郎もうn回鳴いてみろ ポインタをn戻す
こ↑こ↓ ポインタの指す値をUTF-8のコードとみなして出力する
(小学生並みの感想) 1文字入力を読み込んで、ポインタの指す値に設定します
ハイ、よーいスタート ポインタの指す値が0以外なら何もせず、0ならループを終了して次へ進む。
2人は幸せなキスをして終了 ループの終了を意味します。この位置まで来ると、対応するループの先頭へ戻ります

Hello, World!

サンプルプログラムです。
以下のプログラムを実行すると「Hello, World!」が出力されます。

9回だよ、9回あくしろよ

ハイ、よーいスタート
  なんか足んねえよなぁもう1回鳴いてみろ
  8回だよ、8回あくしろよ
  なんか足んねえよなぁもう1回鳴いてみろ
  11回だよ、11回あくしろよ
  なんか足んねえよなぁもう1回鳴いてみろ
  5回だよ、5回あくしろよ
  この野郎もう3回鳴いてみろ
  1回だよ、1回お手だよ
2人は幸せなキスをして終了

なんか足んねえよなぁもう1回鳴いてみろ こ↑こ↓
なんか足んねえよなぁもう1回鳴いてみろ
2回だよ、2回あくしろよ こ↑こ↓
7回だよ、7回あくしろよ こ↑こ↓ こ↑こ↓
3回だよ、3回あくしろよ こ↑こ↓
なんか足んねえよなぁもう1回鳴いてみろ
1回だよ、1回お手だよ こ↑こ↓
12回だよ、12回お手だよ こ↑こ↓
この野郎もう1回鳴いてみろ
8回だよ、8回あくしろよ こ↑こ↓
8回だよ、8回お手だよ こ↑こ↓
3回だよ、3回あくしろよ こ↑こ↓
6回だよ、6回お手だよ こ↑こ↓
8回だよ、8回お手だよ こ↑こ↓
なんか足んねえよなぁもう1回鳴いてみろ
1回だよ、1回あくしろよ こ↑こ↓

その他実装とかの細かい話

  • ポインタの移動とポインタの指す値の増減を数値でできるようにしました
  • 日本語を使っているのでもちろんUTF-8です
  • 数値部分は全角でも半角でもオッケイです
  • メモリの増減・ポインタの移動に関する数値は正規表現で抜き出してます
  • ポインタ系の命令では、2回数値を記述しますが同じだろうが違おうが始めにでてきた方の数値を使うようにしています
  • Regexp#unionなんていう便利なメソッドがあったので、それを使って命令をまとめました

参考資料(偉大な先人達)

以下の本はBrainf_ckの実装で大変お世話になりました。
それから、Rubyで言語実装系を作る際にとても勉強になります。


以下のサイトはBrainf_ckを元に変な言語を作った人たちです。

brainf*ckでジョジョ言語 - toyoshiの日記

おっぱい言語

masarakki/nyaruko_lang · GitHub



ソースコード

class Icetea
  # エラークラスを継承する
  class ProgramError < StandardError; end
  
  ORDERS = [
    /\d+回だよ、\d+回あくしろよ/,
    /\d+回だよ、\d+回お手だよ/,
    /なんか足んねえよなぁもう\d+回鳴いてみろ/,
    /この野郎もう\d+回鳴いてみろ/,
    /こ↑こ↓/,
    /\(小学生並みの感想\)/,
    /ハイ、よーいスタート/,
    /2人は幸せなキスをして終了/
  ]

  def initialize(src)
    # トークンを入れる配列
    @tokens = src.scan(Regexp.union(ORDERS))
    @jumps = analyze_jumps(@tokens)
  end

  # 実行
  def run
    # テープを表した配列
    tape = Array.new()

    # 何番目の命令を実行しているか
    pc = 0
    
    # ポインタが現在どこを指しているか
    cur = 0
    
    while pc < @tokens.size

      case @tokens[pc]
      when /(\d+)回だよ、\d+回あくしろよ/
        tape[cur] = tape[cur].to_i + $1.to_i
      when /(\d+)回だよ、\d+回お手だよ/
        tape[cur] = tape[cur].to_i - $1.to_i
      when /なんか足んねえよなぁもう(\d+)回鳴いてみろ/
        cur += $1.to_i
      when /この野郎もう(\d+)回鳴いてみろ/
        cur -= $1.to_i
        raise ProgramError, "You can't move lefft from start point" if cur < 0
      when /ハイ、よーいスタート/
        pc = @jumps[pc] if tape[cur].to_i == 0
      when /2人は幸せなキスをして終了/
        pc = @jumps[pc] if tape[cur].to_i != 0
      when /こ↑こ↓/
        n = tape[cur].to_i
        print n.chr
      when /\(小学生並みの感想\)/
        tape[cur] = $stdin.getc.ord
      end
      pc += 1
    end
  end

  private
  # ジャンプ命令を解析する
  def analyze_jumps(tokens)
    jumps = {}
    starts =[]
    startword = "ハイ、よーいスタート"
    endword = "2人は幸せなキスをして終了"

    tokens.each_with_index do |e, i|
      if e == startword
        starts << i
      elsif e == endword
        raise ProgramError, "total number of #{startword} is more than #{endword}" if starts.empty?
        from = starts.pop
        to = i
        jumps[from] = to
        jumps[to] = from
      end
    end
    raise ProgramError, "total number of #{endword} is more than #{startword}" unless starts.empty?
    return jumps
  end
end

begin 
  Icetea.new(ARGF.read).run
rescue Icetea::ProgramError => e
  puts e.message
end


エンジョイ!プログラミングライフ!