paiza.ioでelixir その319

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

# 練習問題
俺cpuで1-10の総和を実行するインタープリターを書け。

# 投入するソース

```
  1
  set  a
  0
  set  b
loop  get  b
  get  a
  +
  set  b
  get  a
  1
  +
  set  a
  get  a
  11
  <
  if  loop
  get  b
  outn
  jmp  end
```

# サンプルコード

```

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
	    end)
    end
end

defmodule Sim do
    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 start_link do
		Agent.start_link(fn ->
			[a: 0, b: 0, c: 0, pc: 1, stack: [], g: ""]
		end, name: __MODULE__)
	end
	def push(s) do
		Agent.update(__MODULE__, fn v ->
    	    n = val(s)
		    stack = v[:stack]
			stack = Enum.concat(stack, [n])
			v = List.keyreplace(v, :stack, 0, {:stack, stack})
			pc = v[:pc] + 1
			List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
	end
    def set(d) do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
		    po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
		    stack = Enum.drop(stack, -1) 
			v = List.keyreplace(v, :stack, 0, {:stack, stack})
			cond do
			d == "a" ->
    			v = List.keyreplace(v, :a, 0, {:a, top})
	    		pc = v[:pc] + 1
		    	List.keyreplace(v, :pc, 0, {:pc, pc})
			d == "b" ->
    			v = List.keyreplace(v, :b, 0, {:b, top})
	    		pc = v[:pc] + 1
		    	List.keyreplace(v, :pc, 0, {:pc, pc})
			d == "c" ->
    			v = List.keyreplace(v, :c, 0, {:c, top})
	    		pc = v[:pc] + 1
		    	List.keyreplace(v, :pc, 0, {:pc, pc})
			end
		end)
    end
    def get(d) do
		Agent.update(__MODULE__, fn v ->
		    cond do
		    d == "a" ->
    		    a = v[:a]
	    	    stack = v[:stack]
		        stack = Enum.concat(stack, [a])
			    v = List.keyreplace(v, :stack, 0, {:stack, stack})
		      	pc = v[:pc] + 1
		    	List.keyreplace(v, :pc, 0, {:pc, pc})
		    d == "b" ->
    		    b = v[:b]
	    	    stack = v[:stack]
		        stack = Enum.concat(stack, [b])
			    v = List.keyreplace(v, :stack, 0, {:stack, stack})
		      	pc = v[:pc] + 1
		    	List.keyreplace(v, :pc, 0, {:pc, pc})
		    d == "c" ->
    		    c = v[:c]
	    	    stack = v[:stack]
		        stack = Enum.concat(stack, [c])
			    v = List.keyreplace(v, :stack, 0, {:stack, stack})
		      	pc = v[:pc] + 1
		    	List.keyreplace(v, :pc, 0, {:pc, pc})
			end
		end)
    end
    def rnd() do
		Agent.update(__MODULE__, fn v ->
		    rnd = Enum.random(0..255)
		    stack = v[:stack]
		    stack = Enum.concat(stack, [rnd])
			v = List.keyreplace(v, :stack, 0, {:stack, stack})
			pc = v[:pc] + 1
			List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
    def qt() do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
			po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
			tmp = Enum.with_index(stack)
			po = po - 1
			stack = Enum.map(tmp, fn {val, ind} ->
				cond do
				ind == po ->
					val < top
				true ->
					val
				end
			end)
			stack = Enum.drop(stack, -1)
			v = List.keyreplace(v, :stack, 0, {:stack, stack})
			pc = v[:pc] + 1
			List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
    def gt() do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
			po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
			tmp = Enum.with_index(stack)
			po = po - 1
			stack = Enum.map(tmp, fn {val, ind} ->
				cond do
				ind == po ->
					val > top
				true ->
					val
				end
			end)
			stack = Enum.drop(stack, -1)
			v = List.keyreplace(v, :stack, 0, {:stack, stack})
			pc = v[:pc] + 1
			List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
    def eq() do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
			po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
			tmp = Enum.with_index(stack)
			po = po - 1
			stack = Enum.map(tmp, fn {val, ind} ->
				cond do
				ind == po ->
					val == top
				true ->
					val
				end
			end)
			stack = Enum.drop(stack, -1)
			v = List.keyreplace(v, :stack, 0, {:stack, stack})
			pc = v[:pc] + 1
			List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
    def if_(t) do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
		    po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
		    stack = Enum.drop(stack, -1) 
			v = List.keyreplace(v, :stack, 0, {:stack, stack})
			if top do
			    pc = Src.getl(t)
	    		List.keyreplace(v, :pc, 0, {:pc, pc})
			else
			    pc = v[:pc] + 1
	    		List.keyreplace(v, :pc, 0, {:pc, pc})
			end
		end)
    end
    def jmp(t) do
		Agent.update(__MODULE__, fn v ->
		    pc = Src.getl(t)
    		List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
	end
    def out3() do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
		    g = v[:g]
		    po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
		    o = Integer.floor_div(top, 100)
		    g = g <> to_string(o)
            v = List.keyreplace(v, :g, 0, {:g, g})
		    pc = v[:pc] + 1
		    List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
	def out2() do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
		    g = v[:g]
		    po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
			o = rem(top, 100)
		    o = Integer.floor_div(o, 10)
		    g = g <> to_string(o)
            v = List.keyreplace(v, :g, 0, {:g, g})
		    pc = v[:pc] + 1
		    List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
    def out1() do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
		    g = v[:g]
		    po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
		    o = rem(top, 10)
		    g = g <> to_string(o)
            v = List.keyreplace(v, :g, 0, {:g, g})
		    pc = v[:pc] + 1
		    List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
    def outn() do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
		    g = v[:g]
		    po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
		    o = rem(top, 1000)
		    g = g <> to_string(o)
            v = List.keyreplace(v, :g, 0, {:g, g})
		    pc = v[:pc] + 1
		    List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
    def drop() do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
		    stack = Enum.drop(stack, -1) 
			v = List.keyreplace(v, :stack, 0, {:stack, stack})
		    pc = v[:pc] + 1
		    List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
    def out() do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
		    g = v[:g]
		    po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
		    g = g <> <<top ::utf8>>
            v = List.keyreplace(v, :g, 0, {:g, g})
		    stack = Enum.drop(stack, -1) 
			v = List.keyreplace(v, :stack, 0, {:stack, stack})
		    pc = v[:pc] + 1
		    List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
    def add() do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
			po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
			tmp = Enum.with_index(stack)
			po = po - 1
			stack = Enum.map(tmp, fn {val, ind} ->
				cond do
				ind == po ->
					val + top
				true ->
					val
				end
			end)
			stack = Enum.drop(stack, -1)
			v = List.keyreplace(v, :stack, 0, {:stack, stack})
			pc = v[:pc] + 1
			List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
    def mul() do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
			po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
			tmp = Enum.with_index(stack)
			po = po - 1
			stack = Enum.map(tmp, fn {val, ind} ->
				cond do
				ind == po ->
					val * top
				true ->
					val
				end
			end)
			stack = Enum.drop(stack, -1)
			v = List.keyreplace(v, :stack, 0, {:stack, stack})
			pc = v[:pc] + 1
			List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
    def rem() do
		Agent.update(__MODULE__, fn v ->
		    stack = v[:stack]
			po = Enum.count(stack) - 1
			top = Enum.at(stack, po)
			tmp = Enum.with_index(stack)
			po = po - 1
			stack = Enum.map(tmp, fn {val, ind} ->
				cond do
				ind == po ->
					rem(val, top)
				true ->
					val
				end
			end)
			stack = Enum.drop(stack, -1)
			v = List.keyreplace(v, :stack, 0, {:stack, stack})
			pc = v[:pc] + 1
			List.keyreplace(v, :pc, 0, {:pc, pc})
		end)
    end
	def print() do
		Agent.get(__MODULE__, fn v ->
            g = v[:g]
            IO.puts g
        end)
    end
	def pc() do
		Agent.get(__MODULE__, fn v ->
            v[:pc]
        end)
    end
    def test() do
		Agent.get(__MODULE__, fn v ->
            v
        end)
    end
end

defmodule Main do
    def run(0) do
        Sim.print()
    end
    def run(pc) do
        m = Src.getn(pc)
        #IO.puts(m)
        s = String.split(m, "  ")
        cond do
	    Enum.at(s, 1) == "set" ->
    	    Sim.set(Enum.at(s, 2))
	    Enum.at(s, 1) == "get" ->
	   	    Sim.get(Enum.at(s, 2))
	    Enum.at(s, 1) == "rnd" ->
	   	    Sim.rnd()
	    Enum.at(s, 1) == "<" ->
		    Sim.qt()
	    Enum.at(s, 1) == ">" ->
		    Sim.gt()
	    Enum.at(s, 1) == "=" ->
		    Sim.eq()
	    Enum.at(s, 1) == "if" ->
		    Sim.if_(Enum.at(s, 2))
	    Enum.at(s, 1) == "outn" ->
		    Sim.outn()
	    Enum.at(s, 1) == "out3" ->
		    Sim.out3()
	    Enum.at(s, 1) == "out2" ->
		    Sim.out2()
	    Enum.at(s, 1) == "out1" ->
		    Sim.out1()
	    Enum.at(s, 1) == "drop" ->
		    Sim.drop()
	    Enum.at(s, 1) == "out" ->
		    Sim.out()
	    Enum.at(s, 1) == "+" ->
		    Sim.add()
	    Enum.at(s, 1) == "%" ->
		    Sim.rem()
	    Enum.at(s, 1) == "*" ->
		    Sim.mul()
   	    Enum.at(s, 1) == "jmp" ->
		    Sim.jmp(Enum.at(s, 2))
	    true ->
		    Sim.push(Enum.at(s, 1))
	    end
	    #IO.inspect Sim.test
        run(Sim.pc)
	end
	def start(str) do
	    Src.set(str)
	    run(1)
	end
end
Va.start_link
Sim.start_link
Src.start_link

str = """
  1
  set  a
  0
  set  b
loop  get  b
  get  a
  +
  set  b
  get  a
  1
  +
  set  a
  get  a
  11
  <
  if  loop
  get  b
  outn
  jmp  end
"""
Main.start(str)




```

# 実行結果

```
55
```

# 成果物

https://paiza.io/projects/oOb4tXtPpNqcl54WixKCCg


以上。