用于动态创建带范围的函数的宏魔术

时间:2016-09-16 07:46:35

标签: erlang elixir sax

我正在使用xmerl_sax_parser并且我使用的XML结构是深度嵌套的,所以我最终得到了很多回调函数,它们只适用于作用域堆栈,如下所示:

defmodule Parser do
  defstruct acc: [], scope: []

  def parse(xml_string) do
    event_state = %Parser{}
    event_fun = &callback/3

    {:ok, state, _} = :xmerl_sax_parser.stream(xml_string, event_fun: event_fun, event_state: event_state)

    state
  end

  defp callback({:startElement, _, 'car', _, _}, _, state) do
    Map.update!(state, :scope, &([:car | &1]))
  end

  defp callback({:endElement, _, 'car', _}, _, %{scope: [:car]} = state) do
    %{state | scope: []}
  end

  defp callback({:startElement, _, 'detail', _, _}, _, %{scope: [:car] = scope} = state) do
    %{state | scope: [:detail | scope]}
  end

  defp callback({:endElement, _, 'detail', _}, _, %{scope: [:detail | [:car] = scope_tail]} = state) do
    %{state | scope: scope_tail}
  end

  defp callback({:startElement, _, 'engine', _, _}, _, %{scope: [:detail, :car] = scope} = state) do
    %{state | scope: [:engine | scope]}
  end

  defp callback({:endElement, _, 'engine', _}, _, %{scope: [:engine | [:detail, :car] = scope_tail]} = state) do
    %{state | scope: scope_tail}
  end

  defp callback({:characters, engine}, _, %{scope: [:engine, :detail, :car], acc: acc} = state) do
    %{state | acc: [engine | acc]}
  end
end

因此,为了获得car -> detail -> engine范围内的值,我必须编写7个函数,并且我需要一遍又一遍地重复我需要获得的每个值。因此,显而易见的决定是提出一些宏观魔法以避免所有这些,它可能看起来像这样:

scope [:car, :detail] do
  text :engine, fn(state, value) -> #some logic end
end

但是当调用text时,我找不到一种方法来访问定义它的范围,所以我不知道如何去做。

所以问题是,实现这种适用于范围的DSL的方法是什么?或者有更好的方法来解决这个问题吗?

谢谢

0 个答案:

没有答案