paiza.ioでelixir その317

# 概要
paiza.ioでelixirやってみた。
練習問題、やってみた。

# 練習問題
俺cpuのアセンブラからROMコードを生成するコンパイラを書け。

# 俺CPUの仕様
- スタックマシン
- 命令長　１２ビット
- レジスタ　８ビット４本
- 命令メモリー　８ビット２５６バイト

- 命令セット

|命令コード|ニーモニック	|意味	|stackの状態	|pcの状態|
|:--|:--|:--|:--|:--|
|000|nop	|何もしない。	|変化無し	|1つ増える
|1nn|jp i	|無条件でi番地へ飛ぶ。	|変化無し	|i番地になる。
|2nn|push 1	|stackに整数を積む。	|1個増える	|1つ増える
|3nn|get a	|stackに変数aの値を入れる。	|1個増える	|1つ増える
|4nn|set b	|変数bにstackを入れる。	|1個減る	|1つ増える
|500|out	|stackをそのまま出力する。	|1個減る	|1つ増える
|501|out1	|stackの100の位を出力する。	|変化無し	|1つ増える
|502|out2	|stackの10の位を出力する。	|変化無し	|1つ増える
|503|out3	|stackの1の位を出力する。	|変化無し	|1つ増える
|6nn|if i	|stackが真ならi番地へ飛ぶ。	|1個減る	|場合によりi番地になる
|7nn|if! i	|stackが偽ならi番地へ飛ぶ。	|1個減る	|場合によりi番地になる
|800|dup	|stackにコピーを積む。	|1個増える	|1つ増える
|801|rnd	|stackに乱数を積む。	|1個増える	|1つ増える
|900|drop	|stackを一個減らす。	|1個減る |1つ増える
|ann|call i	|stackへ今の番地を積んでi番地へ飛ぶ。	|1個増える	|i番地になる。
|001|ret	|stack番地へ飛ぶ。	|1個減る	|stack番地になる。
|f00|+	|stack-1,stackの加算を求める。	|1個減る	|1つ増える
|f01|-	|stack-1,stackの減算を求める。	|1個減る	|1つ増える
|f02|*	|stack-1,stackの乗算を求める。	|1個減る	|1つ増える
|f03|/	|stack-1,stackの除算を求める。	|1個減る	|1つ増える
|f04|%	|stack-1,stackの剰余を求める。	|1個減る	|1つ増える
|f05|=	|stack-1,stackの等しいを求める。	|1個減る	|1つ増える
|f06|>	|stack-1,stackの大小を求める。	|1個減る	|1つ増える

# 投入するソースコード
1-100までプリント
```
  1
loop  set  a
  get  a
  100
  >
  if  tugi
  get  a
  out3
  out2
  out1
  ' '
  out
  drop
  get  a
  1
  +
  jmp  loop
tugi  jmp  0

```

# 期待するROMコード

```
//  1
rom[0] <= 12'h2_01;
//loop  set  a
rom[1] <= 12'h4_00;
//  get  a
rom[2] <= 12'h3_00;
//  100
rom[3] <= 12'h2_64;
//  >
rom[4] <= 12'hf_06;
//  if  tugi
rom[5] <= 12'h6_11;
//  get  a
rom[6] <= 12'h3_00;
//  out3
rom[7] <= 12'h5_03;
//  out2
rom[8] <= 12'h5_02;
//  out1
rom[9] <= 12'h5_01;
//  ' '
rom[10] <= 12'h2_20;
//  out
rom[11] <= 12'h5_00;
//  drop
rom[12] <= 12'h9_00;
//  get  a
rom[13] <= 12'h3_00;
//  1
rom[14] <= 12'h2_01;
//  +
rom[15] <= 12'hf_00;
//  jmp  loop
rom[16] <= 12'h1_01;
//tugi  jmp  0
rom[17] <= 12'h1_01;
//
rom[18] <= 12'h2_00;
```

# サンプルコード

```

defmodule Va do
	use Agent
	def start_link() do
		Agent.start_link(fn ->
			0
		end, name: __MODULE__)
	end
	def get() do
		Agent.get(__MODULE__, fn v ->
			v
		end)
	end
	def set(x) do
		Agent.update(__MODULE__, fn v ->
			x
		end)
	end
end

defmodule Src do
	use Agent
	def start_link() do
		Agent.start_link(fn -> 
            ""
		end, name: __MODULE__)
	end
	def set(x) do
		Agent.update(__MODULE__, fn v ->
	        x
		end)
	end
	def get() do
		Agent.get(__MODULE__, fn v ->
			v
		end)
	end
	def getn(n) do
		Agent.get(__MODULE__, fn v ->
	    	Enum.at(String.split(v, "\n", trim: true), n - 1)
	    end)
    end
    def getl("end") do
        0
    end
	def getl(a) do
		Agent.get(__MODULE__, fn v ->
            Enum.reduce(String.split(v, "\n", trim: true), 1, fn l, n ->
	    	    if String.starts_with?(l, a) do
	    	        Va.set(n)
	    	    end
	    	    n = n + 1
	        end)
	        Va.get - 1
	    end)
    end
end

defmodule Main do
    def val(nil) do
        0
    end
    def val(s) do
        if String.starts_with?(s, "'") do
            String.at(s, 1)
            |> String.to_charlist
            |> List.first
        else
            String.to_integer(s)
        end
    end
    def add(n) do
	    IO.puts "rom[#{n}] <= 12'hf_00;"
	end
	def sub(n) do
	    IO.puts "rom[#{n}] <= 12'hf_01;"
	end
	def mul(n) do
	    IO.puts "rom[#{n}] <= 12'hf_02;"
	end
	def div(n) do
	    IO.puts "rom[#{n}] <= 12'hf_03;"
	end
	def rem(n) do
	    IO.puts "rom[#{n}] <= 12'hf_03;"
	end
	def eq(n) do
	    IO.puts "rom[#{n}] <= 12'hf_03;"
	end
	def rnd(n) do
	    IO.puts "rom[#{n}] <= 12'hf_07;"
	end
	def dup(n) do
	    IO.puts "rom[#{n}] <= 12'h8_00;"
	end
	def drop(n) do
	    IO.puts "rom[#{n}] <= 12'h9_00;"
	end
	def gt(n) do
	    IO.puts "rom[#{n}] <= 12'hf_06;"
	end
	def out3(n) do
	    IO.puts "rom[#{n}] <= 12'h5_03;"
	end
	def out2(n) do
	    IO.puts "rom[#{n}] <= 12'h5_02;"
	end
	def out1(n) do
	    IO.puts "rom[#{n}] <= 12'h5_01;"
	end
	def out(n) do
	    IO.puts "rom[#{n}] <= 12'h5_00;"
	end
	def push(n, s) do
	    i = val(s)
	    j = Integer.floor_div(i, 16)
	    i = rem(i, 16)
	    i = Integer.to_string(i, 16)
	    j = Integer.to_string(j, 16)
	    IO.puts "rom[#{n}] <= 12'h2_#{j}#{i};"
	end	
	def if_(n, s) do
	    i = Src.getl(s)
	    j = Integer.floor_div(i, 16)
	    i = rem(i, 16)
	    i = Integer.to_string(i, 16)
	    j = Integer.to_string(j, 16)
	    IO.puts "rom[#{n}] <= 12'h6_#{j}#{i};"
	end
	def jmp(n, s) do
	    i = Src.getl(s)
	    j = Integer.floor_div(i, 16)
	    i = rem(i, 16)
	    i = Integer.to_string(i, 16)
	    j = Integer.to_string(j, 16)
	    IO.puts "rom[#{n}] <= 12'h1_#{j}#{i};"
	end
	def set(n, s) do
	    cond do
	    s == "a" ->
	        IO.puts "rom[#{n}] <= 12'h4_00;"
	    s == "b" ->
	        IO.puts "rom[#{n}] <= 12'h4_01;"
	    s == "c" ->
	        IO.puts "rom[#{n}] <= 12'h4_02;"
	    end
	end
	def get(n, s) do
	    cond do
	    s == "a" ->
	        IO.puts "rom[#{n}] <= 12'h3_00;"
	    s == "b" ->
	        IO.puts "rom[#{n}] <= 12'h3_01;"
	    s == "c" ->
	        IO.puts "rom[#{n}] <= 12'h3_02;"
	    end
	end
    def asm(str) do
        Enum.reduce(String.split(str, "\n"), 0, fn st, n ->
            IO.puts "//#{st}"
            s = String.split(st, "  ")
            cond do
	        Enum.at(s, 1) == "+" ->
		        add(n)
    	    Enum.at(s, 1) == "-" ->
	    	    sub(n)
	        Enum.at(s, 1) == "*" ->
		        mul(n)
    	    Enum.at(s, 1) == "/" ->
	    	    div(n)
    	    Enum.at(s, 1) == "%" ->
	    	    rem(n)
    	    Enum.at(s, 1) == "=" ->
	    	    eq(n)
    	    Enum.at(s, 1) == "rnd" ->
	    	    rnd(n)
    	    Enum.at(s, 1) == "out3" ->
	    	    out3(n)
    	    Enum.at(s, 1) == "out2" ->
	    	    out2(n)
    	    Enum.at(s, 1) == "out1" ->
	    	    out1(n)
    	    Enum.at(s, 1) == "out" ->
	    	    out(n)
	        Enum.at(s, 1) == "dup" ->
		        dup(n)
    	    Enum.at(s, 1) == "drop" ->
	    	    drop(n)
	        Enum.at(s, 1) == ">" ->
		        gt(n)
	        Enum.at(s, 1) == "jmp" ->
		        jmp(n, Enum.at(s, 2))
	        Enum.at(s, 1) == "if" ->
		        if_(n, Enum.at(s, 2))
	        Enum.at(s, 1) == "set" ->
		        set(n, Enum.at(s, 2))
    	    Enum.at(s, 1) == "get" ->
	    	    get(n, Enum.at(s, 2))
    	    true ->
                push(n, Enum.at(s, 1))
            end
            n = n + 1
        end)
    end
    def start(str) do
        Src.set(str)
        asm(str)
    end
end

Va.start_link
Src.start_link

str = """
  1
loop  set  a
  get  a
  100
  >
  if  tugi
  get  a
  out3
  out2
  out1
  ' '
  out
  drop
  get  a
  1
  +
  jmp  loop
tugi  jmp  0
"""

Main.start(str)



```

成果物

https://paiza.io/projects/YTyi7Ae9N90wKswKCcQHTQ

以上。