概要
ilasmでstack machineやってみた。
練習問題やってみた。
練習問題
Elixirでilasmを実行する、インタープリタを書け。
fizzbuzzを実行せよ。
サンプルコード
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 ->
[n: 0, i: 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 == "n" ->
v = List.keyreplace(v, :n, 0, {:n, top})
pc = v[:pc] + 1
List.keyreplace(v, :pc, 0, {:pc, pc})
d == "i" ->
v = List.keyreplace(v, :i, 0, {:i, 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 == "n" ->
n = v[:n]
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})
d == "i" ->
i = v[:i]
stack = v[:stack]
stack = Enum.concat(stack, [i])
v = List.keyreplace(v, :stack, 0, {:stack, stack})
pc = v[:pc] + 1
List.keyreplace(v, :pc, 0, {:pc, pc})
end
end)
end
def bgt(t) 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})
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 brfalse(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 == 0 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 outn() do
Agent.update(__MODULE__, fn v ->
stack = v[:stack]
po = Enum.count(stack) - 1
top = Enum.at(stack, po)
IO.puts(top)
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 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 pushs(s) do
Agent.update(__MODULE__, fn v ->
stack = v[:stack]
g = s
v = List.keyreplace(v, :g, 0, {:g, g})
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.update(__MODULE__, fn v ->
g = v[:g]
IO.puts g
pc = v[:pc] + 1
List.keyreplace(v, :pc, 0, {:pc, pc})
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
def up() do
Agent.update(__MODULE__, fn v ->
pc = v[:pc] + 1
List.keyreplace(v, :pc, 0, {:pc, pc})
end)
end
def ret() do
Agent.update(__MODULE__, fn v ->
pc = 0
List.keyreplace(v, :pc, 0, {:pc, pc})
end)
end
end
defmodule Main do
def run(0) do
IO.puts("ok")
end
def run(pc) do
m = Src.getn(pc)
#IO.puts(m)
s = String.split(m, " ")
#IO.inspect(s)
cond do
Enum.at(s, 1) == "ldc.i4" ->
Sim.push(Enum.at(s, 2))
Enum.at(s, 1) == "ldstr" ->
Sim.pushs(Enum.at(s, 2))
Enum.at(s, 1) == "stloc" ->
Sim.set(Enum.at(s, 2))
Enum.at(s, 1) == "ldloc" ->
Sim.get(Enum.at(s, 2))
Enum.at(s, 1) == "ret" ->
Sim.ret()
Enum.at(s, 1) == "pop" ->
Sim.drop()
Enum.at(s, 1) == "call" && Enum.at(s, 4) == "System.Console::WriteLine(int32)"->
Sim.outn()
Enum.at(s, 1) == "call" && Enum.at(s, 4) == "System.Console::WriteLine(string)"->
Sim.print()
Enum.at(s, 1) == "add" ->
Sim.add()
Enum.at(s, 1) == "rem" ->
Sim.rem()
Enum.at(s, 1) == "mul" ->
Sim.mul()
Enum.at(s, 1) == "bgt" ->
Sim.bgt(Enum.at(s, 2))
Enum.at(s, 1) == "brfalse" ->
Sim.brfalse(Enum.at(s, 2))
Enum.at(s, 1) == "br" ->
Sim.jmp(Enum.at(s, 2))
true ->
Sim.up
#IO.puts(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 = """
ldc.i4 100
stloc n
ldc.i4 1
stloc i
loop:
ldloc i
ldloc n
bgt bye
ldloc i
ldc.i4 15
rem
brfalse fb
ldloc i
ldc.i4 5
rem
brfalse lb
ldloc i
ldc.i4 3
rem
brfalse fc
ldloc i
call void [mscorlib] System.Console::WriteLine(int32)
br tugi
fb:
ldstr \"fizzbuzz\"
br print
lb:
ldstr \"buzz\"
br print
fc:
ldstr \"fizz\"
print:
call void [mscorlib] System.Console::WriteLine(string)
tugi:
ldloc i
ldc.i4 1
add
stloc i
br loop
bye:
ret
"""
Main.start(str)
実行結果
1
2
"fizz"
4
"buzz"
"fizz"
7
8
"fizz"
"buzz"
11
"fizz"
13
14
"fizzbuzz"
16
17
"fizz"
19
"buzz"
"fizz"
22
23
"fizz"
"buzz"
26
"fizz"
28
29
"fizzbuzz"
31
32
"fizz"
34
"buzz"
"fizz"
37
38
"fizz"
"buzz"
41
"fizz"
43
44
"fizzbuzz"
46
47
"fizz"
49
"buzz"
"fizz"
52
53
"fizz"
"buzz"
56
"fizz"
58
59
"fizzbuzz"
61
62
"fizz"
64
"buzz"
"fizz"
67
68
"fizz"
"buzz"
71
"fizz"
73
74
"fizzbuzz"
76
77
"fizz"
79
"buzz"
"fizz"
82
83
"fizz"
"buzz"
86
"fizz"
88
89
"fizzbuzz"
91
92
"fizz"
94
"buzz"
"fizz"
97
98
"fizz"
"buzz"
ok
成果物
以上。