Current section

Files

Jump to
perch lib perch.ex
Raw

lib/perch.ex

defmodule Perch do
@moduledoc """
Documentation for `Perch`.
"""
defstruct tables: %{},
output: nil
def parse(str) when is_binary(str) do
parsed = str
|> extract_groups()
|> Enum.reduce(%__MODULE__{}, &do_parse_thing/2)
{:ok, parsed}
end
def generate_output(perch) do
perch
|> replace_roll_macros()
|> replace_inline_macros()
|> replace_inline_ranges()
end
def roll(%__MODULE__{} = perch, tab) do
perch
|> Map.get(:tables)
|> Map.get(tab)
|> Enum.random()
end
### Private
defp replace_roll_macros(perch) do
Regex.replace(~r/\[(\w*)\]/, perch.output, fn _, match ->
match
|> String.to_atom()
|> then(& Perch.roll(perch, &1))
end)
end
defp replace_inline_macros(output) do
Regex.replace(~r/\{(.*\|.*)\}/, output, fn _full_match, match ->
match
|> String.split("|")
|> Enum.random()
end)
end
defp replace_inline_ranges(output) do
regex = ~r/\{(\d*)-(\d*)\}/
Regex.replace(regex, output, fn _match, a, b ->
Enum.random(String.to_integer(a)..String.to_integer(b))
|> Integer.to_string()
end)
end
defp extract_groups(tab_string) do
tab_string
|> String.split("\n", trim: true)
|> Enum.chunk_by(& String.starts_with?(&1, " "))
|> Enum.chunk_every(2)
end
# handles the special "output" group
defp do_parse_thing([["output"], [output]], acc) do
%{acc | output: String.trim(output)}
end
# handles a table
defp do_parse_thing([[a], b], acc) do
new_key = String.to_atom(a)
new_tab = Enum.map(b, &String.trim/1)
%{acc | tables: Map.put(acc.tables, new_key, new_tab)}
end
end