Current section

Files

Jump to
perch lib perch.ex
Raw

lib/perch.ex

defmodule Perch do
@moduledoc """
Documentation for `Perch`.
"""
defstruct tables: %{},
output: nil
require Logger
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, output_template \\ nil, recursions \\ 10) when recursions > 0 do
input = output_template || perch.output
output =
perch
|> replace_roll_macros(input)
|> replace_inline_macros()
|> replace_inline_ranges()
if has_macro?(output) do
generate_output(perch, output, recursions - 1)
else
output
end
end
def generate_output(_, _, _), do: raise("too many recursions")
def roll(%__MODULE__{} = perch, tab) do
perch
|> Map.get(:tables)
|> Map.get(tab)
|> Enum.random()
end
### Private
@section_regex ~r/\[(\w*)\]/
@inline_macro_regex ~r/\{(.*\|.*)\}/
@inline_range_regex ~r/\{(\d*)-(\d*)\}/
def macro_patterns() do
[
@section_regex,
@inline_macro_regex,
@inline_range_regex
]
end
def has_macro?(str) do
Enum.any?(macro_patterns(), fn rx ->
String.match?(str, rx)
end)
end
defp replace_roll_macros(perch, input) do
Regex.replace(@section_regex, input, fn _, match ->
match
|> String.to_atom()
|> then(&Perch.roll(perch, &1))
end)
end
defp replace_inline_macros(output) do
Regex.replace(@inline_macro_regex, output, fn _full_match, match ->
match
|> String.split("|")
|> Enum.random()
end)
end
defp replace_inline_ranges(output) do
Regex.replace(@inline_range_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