Перевод декларативного DSL в вызовы вложенных функций - PullRequest
4 голосов
/ 09 сентября 2011

У меня есть библиотека Python, которая создает специальные итераторы (дерево поведения) из вызовов вложенных функций . Хотя API имеет довольно приятный и легкий синтаксис (поскольку он является python), он действительно может использовать декларативный DSL.

Вот примерный набросок того, что я представляю:

DSL (с использованием YAML):

tree:
  - sequence:
    - do_action1
    - do_action2
    - select:
      - do_action3
      - sequence:
        - do_action4
        - do_action5
      - do_action6

приведет к следующим вызовам вложенных функций:

visit(
    sequence(
        do_action1(),
        do_action2(),
        select(
            do_action3(),
            sequence(
                do_action4(),
                do_action5(),
                ),
            do_action6(),
            )
        )
    )

У меня проблемы с визуализацией, как именно это сделать. Поскольку DSL должен представлять дерево, простой обход в глубину представляется целесообразным. Но чтобы построить вызовы вложенных функций, я должен как-то вывернуть это наизнанку. Возможно, в нем есть что-то умное с промежуточным стеком или что-то в этом роде, но я не совсем понимаю. Как правильно выполнить это преобразование?

1 Ответ

3 голосов
/ 09 сентября 2011

Я думаю, вы могли бы позволить python отслеживать вызовы функций и параметры вместо того, чтобы делать это самостоятельно со стеком.

Предположим, у вас есть дерево разбора YAML, в котором каждый узел представляет вызов функции, а каждый дочерний элементэтого узла является параметром (который также является вызовом функции, поэтому он потенциально может иметь свои собственные параметры).

Затем определите функцию evaluate, которая оценивает узел этого дерева, следующим образом (псевдокод):

def evaluate(node):
    # evaluate parameters of the call
    params = []
    for child in node:
        params.append(evaluate(child))

    # now make the call to whatever function this node represents,
    # passing the parameters
    return node.function.call(*params)

Наконец, вызовите evaluate, передавая корень дерева YAML в качестве параметра, и вы должны получить желаемое поведение.


Несколько иной eval-applyструктура

def evaluate(node):
    # evaluate parameters of the call
    params = [ evaluate(child) for child in node ]

    # apply whatever function this node represents
    return node.function.call(*params)
...