Как выразить контекстно-свободную грамматику дизайна как внутренний DSL в Python?

[Примечание: перечитав это перед отправкой, я понял, что этот вопрос стал чем-то вроде эпопеи. Спасибо за то, что потворствовали моему длинному объяснению причин этого стремления. Я чувствую, что если бы я был в состоянии помочь другому исполнителю аналогичного проекта, я бы с большей вероятностью присоединился к нему, если бы знал мотивацию этого вопроса.]

Я начал заниматься Structure Synth Микаэлем Хвидтфельдтом Кристенсеном. Это инструмент для создания 3D-геометрии из (в основном) контекстно-свободной грамматики под названием Eisenscript. Structure Synth сам по себе вдохновлен Context Free Art. Контекстно-свободные грамматики могут создавать потрясающие результаты из удивительно простых наборов правил.

Мой текущий рабочий процесс Structure Synth включает в себя экспорт файла OBJ из Structure Synth, импорт его в Blender, настройку источников света, материалов и так далее, а затем рендеринг с Luxrender. К сожалению, импорт этих файлов OBJ часто приводит к остановке работы Blender, поскольку могут быть тысячи объектов с довольно сложной геометрией. Я говорю «справедливо», потому что Structure Synth генерирует только базовые формы, но сфера, представленная треугольниками, по-прежнему имеет много граней.

Таким образом, генерация структур непосредственно в Blender будет предпочтительнее текущего процесса (глубокая поддержка Blender скриптов Python должна сделать это возможным). Интеллектуальная библиотека Python может использовать возможности Blender по созданию экземпляров, чтобы использовать одну сетку для создания множества объектов, тем самым экономя память. Плюс Blender - это полнофункциональный 3D-пакет, и его способность интерпретировать CFDG предоставит творческие возможности, выходящие далеко за рамки того, что может предложить Structure Synth.

И поэтому мой вопрос в том, как лучше всего перевести грамматику Eisenscript в Python DSL. Вот как выглядит простой Eisenscript:

set maxdepth 2000
{ a 0.9 hue 30 } R1 

rule R1 { 
  { x 1  rz 3 ry 5  } R1
  { s 1 1 0.1 sat 0.9 } box
}

rule R1 { 
  { x 1  rz -3 ry 5  } R1
  { s 1 1 0.1 } box
}

Чтобы объяснить, первый вызов R1 (строка 2) случайным образом вызовет одно из двух определений R1. Каждое определение R1 рекурсивно вызывает R1 (случайным образом вызывая одно из двух определений), а также создает блок. Первая строка уничтожает генерацию после того, как рекурсия перешла на 2000 уровней.

Джереми Ашкенас (из известного CoffeeScript) успешно реализовал контекстно-свободный DSL на Ruby с использованием блоков. Внутренне он работает путем создания хеш-ключа для каждого «имени» правила и сохраняет блоки для каждого определения этого правила в массиве, который будет случайным образом выбран при вызове правила.

Предыдущие определения правил Eisenscript будут переводить к Ruby DSL следующим образом:

rule :r1 do
  r1 :x => 1, :rz => 3, :ry => 5
  box :s => [1, 1, 0.1], :sat => 0.9
end

rule :r1 do
  r1 :x => 1, :rz => -3, :ry => 5
  box :s => [1, 1, 0.1]
end

Я начинающий пользователь Python и поэтому проводил некоторые исследования возможностей функционального программирования Python. Кажется, лямбда слишком ограничена, чтобы создать что-то похожее на Джереми Ruby DSL, и, насколько я могу судить, лямбда - единственный вариант для анонимных функций?

Как опытный Pythonista может подойти к дизайну?

5
задан Jedidiah Hurt 1 July 2011 в 19:22
поделиться