Skip to content

Instantly share code, notes, and snippets.

@righ1113
Last active February 28, 2025 13:54
Show Gist options
  • Save righ1113/7805bc5ca88da23d2616a13c6a5ba016 to your computer and use it in GitHub Desktop.
Save righ1113/7805bc5ca88da23d2616a13c6a5ba016 to your computer and use it in GitHub Desktop.
Brainf*ck in Egison
# $ iex -S mix
# > Bf.hello |> Bf.parse([]) |> Bf.run()
defmodule Bf do
# data
# Module.register_attribute(__MODULE__, :hello, persist: true)
# @hello "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."
def hello do
'++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.'
end
# parse
def parse(data, acc) do
case data do
'' -> acc
[93 | xs] -> {xs, acc} # ]
[62 | xs] -> parse(xs, acc ++ [:Next]) # >
[60 | xs] -> parse(xs, acc ++ [:Prev]) # <
[43 | xs] -> parse(xs, acc ++ [:Inc]) # +
[45 | xs] -> parse(xs, acc ++ [:Dec]) # -
[46 | xs] -> parse(xs, acc ++ [:Write]) # .
[44 | xs] -> parse(xs, acc ++ [:Read]) # ,
[91 | xs] -> {xs2, acc2} = parse(xs, []); parse(xs2, acc ++ [acc2]) # [
[_ | xs] -> parse(xs, acc)
end
end
# run
def run(code), do: (run_sub({List.duplicate(0, 30), 0, List.duplicate(0, 30)}, code); true)
defp run_sub(d, code) do
case {code, d} do
{[], _ } -> d
{[x |xs], {_, 0, _ } } when is_list(x) -> run_sub(d, xs)
{[x |_ ], _ } when is_list(x) -> d |> run_sub(x) |> run_sub(code) # loop
{[:Next |xs], {ls, c, [rx | rs2]}} -> run_sub({[c | ls], rx, rs2 }, xs)
{[:Prev |xs], {[lx | ls2], c, rs}} -> run_sub({ls2, lx, [c | rs]}, xs)
{[:Inc |xs], {ls, c, rs} } -> run_sub({ls, c + 1, rs }, xs)
{[:Dec |xs], {ls, c, rs} } -> run_sub({ls, c - 1, rs }, xs)
{[:Write|xs], {_, c, _ } } -> List.to_string([c]) |> IO.puts() ||| run_sub(d, xs)
{[:Read |xs], _ } -> run_sub(d, xs) # omitted
{[_ |xs], _ } -> run_sub(d, xs)
end
end
defp left ||| right, do: (_ = left; right)
end
# ############################################################################################################
# Claude 3.5 でコメント作成
# このBrainf_ckインタプリタのアルゴリズムを説明します:
# クラス構造
# ・Brainf_ckクラスを定義し、メモリと現在のメモリ位置(MAR: Memory Address Register)を管理します
# 初期化
# ・initializeメソッドで:
#  ・メモリ配列(@mem)を30,000要素、初期値0で作成
#  ・メモリポインタ(@mar)を0に初期化
# 実行エンジン(execメソッド)
# 主要な命令の処理:
# 1.>: メモリポインタを1つ進める
# 2.<: メモリポインタを1つ戻す
# 3.+: 現在のメモリ位置の値を1増やす
# 4.-: 現在のメモリ位置の値を1減らす
# 5..: 現在のメモリ位置の値を文字として出力
# 6.[:
#  ・現在のメモリ値が0の場合、対応する]まで移動
#  ・move_pcメソッドを使用して適切なジャンプ先を計算
# 7.]:
#  ・現在のメモリ値が0でない場合、対応する[まで戻る
#  ・同じくmove_pcメソッドを使用
# 括弧処理(move_pcメソッド)
# ・再帰的に対応する括弧を検索
# ・パラメータ:
#  ・pro: プログラムテキスト
#  ・i: 現在の位置
#  ・stop: 探索の終点
#  ・sign: 探索方向(+1 or -1)
#  ・bracket: 探している括弧の種類
#  ・d: 括弧の深さカウンタ
# このインタプリタは再帰的な実装を採用しており、
# 各命令を順次処理しながら プログラムカウンタpc を管理し、プログラムの実行を制御しています。
# ############################################################################################################
class Brainf_ck
def initialize = (@mar = 0; @mem = Array.new(30_000, 0))
def exec(pro, pc = 0, max = pro.length - 1)
case pro[pc]
when '>' then @mar += 1
when '<' then @mar -= 1
when '+' then @mem[@mar] += 1
when '-' then @mem[@mar] -= 1
when '.' then print @mem[@mar].chr
when '[' then pc = move_pc(pro, pc + 1, max, 1, ']') if @mem[@mar].zero?
when ']' then pc = move_pc(pro, pc - 1, 0, -1, '[') unless @mem[@mar].zero?
end
pc == max ? :true_end : exec(pro, pc + 1)
end
private
def move_pc(pro, i, stop, sign, bracket, d = 0, d2 = pro[i] == '[' ? d + sign : (pro[i] == ']' ? d - sign : d))
if d.zero? && pro[i] == bracket then i # 対応する括弧発見
elsif i == stop then :pro_last
else move_pc pro, i + sign, stop, sign, bracket, d2
end
end
end
bf = Brainf_ck.new
bf.exec '++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.'
--
-- Brainf*ck
--
-- $ egison 4.1.3
-- > loadFile "bf2.egi"
--
-- Pattern Function
--
def bfLoopPatt := \pat1 => #'[' :: $x :: ~pat1 ++ #(pack [']', x])
--
-- Codes
--
def parse code := parse' code 1 [] where
parse' code mark acc := match code as string with
| [] -> pack $ reverse acc
| #'[' :: $xs -> parse' xs (mark + 1) ((itoc (mark + 48 )) :: '[' :: acc)
| #']' :: $xs -> parse' xs (mark - 1) ((itoc (mark + 48 - 1)) :: ']' :: acc)
| $x :: $xs -> parse' xs mark (x :: acc)
def bf code := do{ run code (take 10 $ repeat1 0, 0, take 10 $ repeat1 0); write "" } where
run code d := match (code, d) as (string, (list integer, integer, list integer)) with
| ([], _ ) -> return d
| ((bfLoopPatt _) ++ $xs, (_, #0, _) ) -> run xs d
| ((bfLoopPatt $x) ++ $xs, _ ) -> run code $ io $ run x d -- loop
| (#'>' :: $xs, ($ls, $c, $rx :: $rs2)) -> run xs (c :: ls, rx, rs2)
| (#'<' :: $xs, ($lx :: $ls2, $c, $rs)) -> run xs (ls2, lx, c :: rs)
| (#'+' :: $xs, ($ls, $c, $rs) ) -> run xs (ls, (c + 1), rs)
| (#'-' :: $xs, ($ls, $c, $rs) ) -> run xs (ls, (c - 1), rs)
| (#'.' :: $xs, (_, $c, _) ) -> do{ write $ pack [itoc c]; run xs d }
| (#',' :: $xs, _ ) -> run xs d -- omitted
| (_ :: $xs, _ ) -> run xs d
--
-- Data
--
-- 1m35s
def hello :=
"++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++."
--
-- Tests
--
-- $ egison -t bf2.egi
io $ bf $ parse hello
def test5 := matchAll "[1ii[2abc]2xxx[2y[3]3]2df]1ggg" as string with
| (bfLoopPatt $x) ++ $xs -> (x, xs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment