defmodule Rollex do # NOTE: Order Matters! If an item matches in Rollex.Lexer._do_tokenize/0, the lexer will skip the rest of the tokens # This is why number is after dice tokens, since number will very frequently match on what should be dice input @default_tokens [%Rollex.Tokens.Division{}, %Rollex.Tokens.Multiplication{}, %Rollex.Tokens.Subtraction{}, %Rollex.Tokens.Addition{}, %Rollex.Tokens.ParanoiaDice{}, %Rollex.Tokens.FudgeDice{}, # %Rollex.Tokens.TakeTop{}, # %Rollex.Tokens.TakeTopDice{}, # %Rollex.Tokens.TakeBottomDice{}, %Rollex.Tokens.RegularDice{}, %Rollex.Tokens.Number{}, %Rollex.Tokens.LeftParenthesis{}, %Rollex.Tokens.RightParenthesis{}] # Use built-in defaults def roll(roll_expr, options \\ []) def roll(roll_expr, []) do default_options = [token_list: @default_tokens, lexer: Rollex.Lexer, validator: Rollex.Validator, evaluator: Rollex.Evaluator] _roll(roll_expr, default_options) end def roll(roll_expr, options) when is_list(options) do _roll(roll_expr, options) end defp _roll(roll_expr, options) do {:ok, roll_expr} |> _apply_roll_step(options[:lexer], :tokenize, options[:token_list]) |> _apply_roll_step(options[:validator], :validate) |> _apply_roll_step(options[:evaluator], :evaluate) |> _build_final_output(roll_expr) end defp _apply_roll_step(input, module, function_atom, additional_options \\ []) defp _apply_roll_step(input, module, function_atom, additional_options) when is_atom(function_atom) and is_list(additional_options) do apply(module, function_atom, [input | [additional_options]]) end defp _apply_roll_step({:error, _error_msg} = input, _module, _function_atom, _additional_options) do input end defp _build_final_output({:error, %{} = result}, input_roll_string) do %{status: :error, error_msg: result.error_msg, raw_input: input_roll_string} end defp _build_final_output({:ok, %{} = result}, input_roll_string) do %{status: :ok, total: result.total, tokenized_input: result.tokenized_input, raw_input: input_roll_string} end defp _build_final_output({:error, msg}, input_roll_string) when is_binary(msg) do %{status: :error, error_msg: msg, tokenized_input: [], raw_input: input_roll_string} end end