exs_loader.ex (2948B)
1 defmodule Credo.ExsLoader do 2 @moduledoc false 3 4 def parse(exs_string, filename, exec, safe \\ false) 5 6 def parse(exs_string, filename, _exec, true) do 7 case Code.string_to_quoted(exs_string, file: filename) do 8 {:ok, ast} -> 9 {:ok, process_exs(ast)} 10 11 {:error, {line_meta, message, trigger}} when is_list(line_meta) -> 12 {:error, {line_meta[:line], message, trigger}} 13 14 {:error, value} -> 15 {:error, value} 16 end 17 end 18 19 def parse(exs_string, filename, exec, false) when is_atom(filename) do 20 parse(exs_string, "mod:#{filename}", exec, false) 21 end 22 23 def parse(exs_string, filename, exec, false) when is_binary(filename) do 24 {result, _binding} = 25 Code.eval_string(exs_string, [exec: exec], file: to_string(filename) || "nofile") 26 27 {:ok, result} 28 rescue 29 error -> 30 case error do 31 %SyntaxError{description: "syntax error before: " <> trigger, line: line_meta} 32 when is_list(line_meta) -> 33 {:error, {line_meta[:line], "syntax error before: ", trigger}} 34 35 %SyntaxError{description: "syntax error before: " <> trigger, line: line_no} -> 36 {:error, {line_no, "syntax error before: ", trigger}} 37 38 error -> 39 {:error, error} 40 end 41 end 42 43 @doc false 44 def parse_safe(exs_string) do 45 case Code.string_to_quoted(exs_string) do 46 {:ok, ast} -> 47 process_exs(ast) 48 49 _ -> 50 %{} 51 end 52 end 53 54 defp process_exs(v) 55 when is_atom(v) or is_binary(v) or is_float(v) or is_integer(v), 56 do: v 57 58 defp process_exs(list) when is_list(list) do 59 Enum.map(list, &process_exs/1) 60 end 61 62 defp process_exs({:sigil_w, _, [{:<<>>, _, [list_as_string]}, []]}) do 63 String.split(list_as_string, ~r/\s+/) 64 end 65 66 # TODO: support regex modifiers 67 defp process_exs({:sigil_r, _, [{:<<>>, _, [regex_as_string]}, []]}) do 68 Regex.compile!(regex_as_string) 69 end 70 71 defp process_exs({:%{}, _meta, body}) do 72 process_map(body, %{}) 73 end 74 75 defp process_exs({:{}, _meta, body}) do 76 process_tuple(body, {}) 77 end 78 79 defp process_exs({:__aliases__, _meta, name_list}) do 80 Module.safe_concat(name_list) 81 end 82 83 defp process_exs({{:__aliases__, _meta, name_list}, options}) do 84 {Module.safe_concat(name_list), process_exs(options)} 85 end 86 87 defp process_exs({key, value}) when is_atom(key) or is_binary(key) do 88 {process_exs(key), process_exs(value)} 89 end 90 91 defp process_tuple([], acc), do: acc 92 93 defp process_tuple([head | tail], acc) do 94 acc = process_tuple_item(head, acc) 95 process_tuple(tail, acc) 96 end 97 98 defp process_tuple_item(value, acc) do 99 Tuple.append(acc, process_exs(value)) 100 end 101 102 defp process_map([], acc), do: acc 103 104 defp process_map([head | tail], acc) do 105 acc = process_map_item(head, acc) 106 process_map(tail, acc) 107 end 108 109 defp process_map_item({key, value}, acc) 110 when is_atom(key) or is_binary(key) do 111 Map.put(acc, key, process_exs(value)) 112 end 113 end