zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

codegen.ex (3488B)


      1 defmodule Jason.Codegen do
      2   @moduledoc false
      3 
      4   alias Jason.{Encode, EncodeError}
      5 
      6   def jump_table(ranges, default) do
      7     ranges
      8     |> ranges_to_orddict()
      9     |> :array.from_orddict(default)
     10     |> :array.to_orddict()
     11   end
     12 
     13   def jump_table(ranges, default, max) do
     14     ranges
     15     |> ranges_to_orddict()
     16     |> :array.from_orddict(default)
     17     |> resize(max)
     18     |> :array.to_orddict()
     19   end
     20 
     21   defmacro bytecase(var, do: clauses) do
     22     {ranges, default, literals} = clauses_to_ranges(clauses, [])
     23 
     24     jump_table = jump_table(ranges, default)
     25 
     26     quote do
     27       case unquote(var) do
     28         unquote(jump_table_to_clauses(jump_table, literals))
     29       end
     30     end
     31   end
     32 
     33   defmacro bytecase(var, max, do: clauses) do
     34     {ranges, default, empty} = clauses_to_ranges(clauses, [])
     35 
     36     jump_table = jump_table(ranges, default, max)
     37 
     38     quote do
     39       case unquote(var) do
     40         unquote(jump_table_to_clauses(jump_table, empty))
     41       end
     42     end
     43   end
     44 
     45   def build_kv_iodata(kv, encode_args) do
     46     elements =
     47       kv
     48       |> Enum.map(&encode_pair(&1, encode_args))
     49       |> Enum.intersperse(",")
     50 
     51     collapse_static(List.flatten(["{", elements] ++ '}'))
     52   end
     53 
     54   defp clauses_to_ranges([{:->, _, [[{:in, _, [byte, range]}, rest], action]} | tail], acc) do
     55     clauses_to_ranges(tail, [{range, {byte, rest, action}} | acc])
     56   end
     57 
     58   defp clauses_to_ranges([{:->, _, [[default, rest], action]} | tail], acc) do
     59     {Enum.reverse(acc), {default, rest, action}, literal_clauses(tail)}
     60   end
     61 
     62   defp literal_clauses(clauses) do
     63     Enum.map(clauses, fn {:->, _, [[literal], action]} ->
     64       {literal, action}
     65     end)
     66   end
     67 
     68   defp jump_table_to_clauses([{val, {{:_, _, _}, rest, action}} | tail], empty) do
     69     quote do
     70       <<unquote(val), unquote(rest)::bits>> ->
     71         unquote(action)
     72     end ++ jump_table_to_clauses(tail, empty)
     73   end
     74 
     75   defp jump_table_to_clauses([{val, {byte, rest, action}} | tail], empty) do
     76     quote do
     77       <<unquote(byte), unquote(rest)::bits>> when unquote(byte) === unquote(val) ->
     78         unquote(action)
     79     end ++ jump_table_to_clauses(tail, empty)
     80   end
     81 
     82   defp jump_table_to_clauses([], literals) do
     83     Enum.flat_map(literals, fn {pattern, action} ->
     84       quote do
     85         unquote(pattern) ->
     86           unquote(action)
     87       end
     88     end)
     89   end
     90 
     91   defp resize(array, size), do: :array.resize(size, array)
     92 
     93   defp ranges_to_orddict(ranges) do
     94     ranges
     95     |> Enum.flat_map(fn
     96          {int, value} when is_integer(int) ->
     97            [{int, value}]
     98 
     99          {enum, value} ->
    100            Enum.map(enum, &{&1, value})
    101        end)
    102     |> :orddict.from_list()
    103   end
    104 
    105   defp encode_pair({key, value}, encode_args) do
    106     key = IO.iodata_to_binary(Encode.key(key, &escape_key/3))
    107     key = "\"" <> key <> "\":"
    108     [key, quote(do: Encode.value(unquote(value), unquote_splicing(encode_args)))]
    109   end
    110 
    111   defp escape_key(binary, _original, _skip) do
    112     check_safe_key!(binary)
    113     binary
    114   end
    115 
    116   defp check_safe_key!(binary) do
    117     for <<(<<byte>> <- binary)>> do
    118       if byte > 0x7F or byte < 0x1F or byte in '"\\/' do
    119         raise EncodeError,
    120               "invalid byte #{inspect(byte, base: :hex)} in literal key: #{inspect(binary)}"
    121       end
    122     end
    123 
    124     :ok
    125   end
    126 
    127   defp collapse_static([bin1, bin2 | rest]) when is_binary(bin1) and is_binary(bin2) do
    128     collapse_static([bin1 <> bin2 | rest])
    129   end
    130 
    131   defp collapse_static([other | rest]) do
    132     [other | collapse_static(rest)]
    133   end
    134 
    135   defp collapse_static([]) do
    136     []
    137   end
    138 end