← Архив: Common Lisp

Новая версия cl-closure-template

Author: · 09.12.2012 10:15
· original author: archimag
Привет,
Я в последнее время на CL практически не писал, но всё же у меня есть план и я ему следую (медленно). Первый шаг - привести cl-closure-template в более божеский вид. Я делал это время от времени и вот закончил важную стадию:
  • Переписан парсер, теперь для представления AST используются не s-выражения, а настоящие объекты
  • Соответственно, пришлось переписать практически всё (хоть работа и была не очень сложной)
  • Полностью переписан JavaScript-backend без использования parenscript.
Новая версия уже на github. У меня большая просьба, кто использует cl-closure-template - протестировать на своих проектах. В принципе, разницы в поведении быть не должно, всё работает также (за исключением небольшого отличия в обработке whitespaces, теперь она ближе к оригиналу, в большинстве случаев это не критично), но мало ли.
P.S. Недавно в библиотеку был добавлен python-backend, его пока пришлось отключить - необходимо адаптировать под новый формат AST.
· original author: motopeh
LOR'ский комментатор новостей:
Ссылку надо бы добавить.
· original author: andy128k
Есть пара проблем.
Вот тут сломалось:
<div class="row{if not $category} no-toggle{/if}">
И не работает
(defmethod compile-template ((backend javascript-backend) (templates list))
Прозреваю что вместо namespace-map написан (namespace-name namespace).
· original author: andy128k
Ещё
Error: TypeError: Math.length is not a function
Для кода
  {if $item.shared_by and length($item.shared_by)}
сгенерировало
    if (($env$.item["shared_by"] && Math.length($env$.item["shared_by"]))) {
· original author: archimag
> <div class="row{if not $category} no-toggle{/if}">
Хм, не могу воспроизвести:
(defvar *tmpl*
  "{namespace test} {template hello}<div class=\"row{if not $category} no-toggle{/if}\">{/template}"
)

(closure-template:compile-template :javascript-backend *tmpl*)
Получаю:
if (typeof test === 'undefined') { test = {}; }
test.$isEmpty$ = function (obj) {
    for (var prop in obj) if (obj.hasOwnProperty(prop)) return false;
    return true;
}
;
test.$escapeHTML$ = function (obj) {
    if (typeof obj == 'string') return String(obj).split('&').join('&amp;').split( '<').join('&lt;').split('>').join('&gt;').split('\"').join('&quot;').split('\'').join('&#039;');
    else return obj;
}
;
test.$round$ = function (number, ndigits) {
    if (ndigits) {
        var factor = Math.pow(10.0, ndigits);
        return Math.round(number * factor) / factor;
    
}

    else return Math.round(number)
}
;
test.$objectFromPrototype$ = function (obj) {
    function C () {}
    C.prototype = obj;
    return new C;
}
;
test.hello = function($env$, $target$) {
    if (!$env$) { $env$ = {}; }
    var $result$ = $target$ || [];
    $result$.push("<div class=\"row");
    if (!$env$.category) {
        $result$.push("no-toggle");
    
}

    $result$.push("\">");
    if (!$target$) return $result$.join('');
    else return null;
}
;
· original author: andy128k
Можешь :)
У тебя тоже получается class="rowno-toggle" вместо class="row no-toggle"
· original author: archimag
> У тебя тоже получается class="rowno-toggle" вместо class="row no-toggle"
А, ну это как раз связано с изменение обработки whitespaces. Так и задумано. Вставь {sp} перед no-toggle.
· original author: archimag
> Error: TypeError: Math.length is not a function
Fixed.
· original author: orivej
Спасибо; CL бэкенд в трёх проектах работает как раньше, JS не использовал, Python бы пригодился.
· original author: archimag
> Python бы пригодился.
Вообще, есть ещё такая вещь: https://github.com/archimag/python-closure-templates, парсер там только медленный, приходиться кэшировать (этого в либе нет).
· original author: orivej
> python-closure-templates
На тестовом шаблоне, с которым легко справляется версия на CL, parseNamespace (вызов lepl) уходит видимо в бесконечную рекурсию. Могу подготовить минимальный пример. Ещё версия на Python не поддерживает дефисы в именах шаблонов. И называется pyclosuretempALtes.
· original author: archimag
> parseNamespace (вызов lepl) уходит видимо в бесконечную рекурсию> Могу подготовить минимальный пример
Ага, можно. Вообще эта либа боевая, я ей сейчас активно использую. На моих шаблонах работает. Правда, если в шаблоне ошибка, то да, может уйти куда-то глубоко. Ну вообще, некоторые шаблоны она у меня по минуте парсит, поэтому я их после разбора кэширую на диск с помощью pickle кэширую.
> Ещё версия на Python не поддерживает дефисы в именах шаблонов. 
Ну новая версия для JavaScript-backend-а тоже не поддерживает :) Вообще эта была такая вольность, по сравнению с оригинальной спецификацией. Для либы на Python в ней особого смысла нет. Я вообще подумал, что надо уходить от этого и делать всё строго как сказали, т.е. никаких дефисов.
· original author: orivej
Хотя нет: если убрать дефисы из всех имён, то парсер справляется и дальше шаблоны работают.
· original author: orivej
Я вообще подумал, что надо уходить от этого и делать всё строго как сказали, т.е. никаких дефисов.

Тогда ещё в CL-шаблонах нужно начинать все имена шаблонов с точки: "You must create a unique name for each template and begin the template name with a dot, which indicates that the template name is relative to the file's namespace."
· original author: archimag
> which indicates that the template name is relative to the file's namespace
Мне это сразу показалось каким-то дибилизмом.
· original author: orivej
Мне это сразу показалось каким-то дибилизмом.
Точно :)  Ну ладно, буду знать, что если python-closure-templates долго парсит шаблон, ошибка наверняка в шаблоне, и что для Python (и JS) нужно убирать дефисы.
· original author: amartynov
Неожиданно :)
Мы тут вточили:
1. поддержку плагиновых директив для {print}.
2. поддержку генерации RequireJS модуля
3. Компиляцию через ASDF для Common Lisp backend,
Придется подождать готовности  и переделать.
Вопрос - а почему отказался от Parenscript?
· original author: archimag
> Неожиданно :)
Ну, скажем, это было нужно сделать.  Разбирать код перемалывающий s-выражения удовольствие не из приятных.
> Мы тут вточили
Это интересно.
> Вопрос - а почему отказался от Parenscript?
Перестал видеть смысл. Код фактически не упрощается (а может быть даже усложняется). Плюс изменения в parenscript порой порождают ошибки в cl-closure-templates. Меньше зависимостей и больше контроля.
· original author: amartynov
>Разбирать код перемалывающий s-выражения удовольствие не из приятных.
Молодежь прекрасно это сделала :) Но тоже не без затрат.

>> Мы тут вточили

> Это интересно.
Можно посмотреть на 2 и 3 пункт в https://github.com/alexey-martynov/cl-closure-template, я просто не успел прислать реквест.
ASDF должен примениться как есть, а вот с RequireJS могут быть проблемы. Я домой приду, посмотрю, что можно сделать, скорее всего, там все просто.
PS. А сходу квотить тут можно при ответе?
· original author: archimag
> Можно посмотреть на 2 и 3 пункт в https://github.com/alexey-martynov/cl-closure-template
Лучше дождусь реквеста, времени не хватает :) Это обновление делал несколько месяцев по часу в неделю.
> Молодежь прекрасно это сделала :)
Где такая молодежь, если не секрет?
· original author: archimag
> PS. А сходу квотить тут можно при ответе?
Нет. Вообще у меня есть большой план переписать весь lisper.ru, можно будет и форум подремонтировать ;)
· original author: amartynov
> Лучше дождусь реквеста, времени не хватает
Тогда будет сразу 2 - для нового и для старого вариантов
> Где такая молодежь, если не секрет?
Да есть тут у нас в Питере такие интерны :) Учим потихоньку.
· original author: amartynov
Заслал реквест относительно зарефакторенного варианта.
В заначке есть еще одна вещь для Emacs - компиляция через Common Lisp backend через Slime, но это чуть позже.
PS. Клевая засада - редактирование сообщений в Firefox нереально глючит :( Косячит фокус контрола редактирования, из-под Linux совсем не смог редактировать.
· original author: archimag
> редактирование сообщений в Firefox нереально глючит
Какой Firefox? У меня 10 в Gentoo всё вроде нормально :(
Ну вот когда-нибудь приведу всё в божеский вид, буду тогда патчи просить :)
· original author: amartynov
17-й
В Linux просто невозможно получить курсор и ввод. Под Windows легче, но он все время теряется при нажатии любой кнопочки в тулбаре. В районе 13-го, кстати, все работало. Сегодня еще попрошу потестить в Chrome.
· original author: amartynov
Предложение (даже 2): имена в camel case совершенно неестественно выглядят в Common Lisp. Может добавить name converter, который для Common Lisp backend превратит их в имена с дефисами?
Более того, было бы прикольно иметь его настраиваемым, через параметр compile-template или через динамическую переменную.
Могу сделать такую штуку и предложить на рассмотрение, если идея нравится. Как минимум, хотелось бы иметь настройки.
· original author: orivej
17-й Firefox под Gentoo, никаких проблем нет. В safe-mode тоже.
· original author: amartynov
Полагаю, что привет надо передавать в Debian. :( Ну и черт с ним пока. Есть резервные варианты.
· original author: archimag
> Предложение (даже 2): имена в camel case совершенно неестественно выглядят в Common Lisp.> Может добавить name converter, который для Common Lisp backend превратит их в имена с дефисами?
Не совсем понял о чём речь, ведь так и происходит? 
Хотелось бы пример увидеть.
· original author: archimag
> одна вещь для Emacs - компиляция через Common Lisp backend через Slime
О, заценил :) Удобно.
· original author: amartynov
Формально, если мы следуем стандартам, должно быть так:
{namespace ns}
{template .sampleTemplate}
...
{/template}
В результате, будет функция ns:sampletemplate, что довольно несимпатично. Гораздо лучше ns:sample-template.
Потому как сейчас, если
{template samplte-template}
{/template}
То и в Javascript она так и будет называться - sample-template :)
· original author: andy128k
> Компиляцию через ASDF для Common Lisp backend
Поясните, как этим пользоваться? Если у меня в системе есть шаблон и она зависит от closure-template, то получается рекурсия.
Система не загружается, потому как ASDF не знает как обработать шаблон. И closure-template не грузится потому как syntax error в asd-файле и до depends-on дело не доходит.
· original author: archimag
> Поясните, как этим пользоваться?
Вот пример:
(defsystem #:test
  :defsystem-depends-on (#:closure-template)
  :components ((:closure-template "test"))
)

Обрати внимание, что вместо :depends-on используется :defsystem-depends-on. :depends-on может быть свойством любого компонента, а :defsystem-depends-on это только свойство всей системы и используется для загрузки зависимостей ещё до того, как начнётся обрабатываться определение системы. Наиболее очевидное применение - загрузка расширений asdf, хотя можно и просты зависимости так грузить. Подробности смотри здесь, раздел "5.3.4 Defsystem depends on".
· original author: andy128k
Спасибо!
(Теперь ещё больше поводов ждать обновления quicklisp)
· original author: amartynov
С формированием имен честно протупил :(
Итого, сейчас для шаблона у которого:
1. namespace - "closureTemplate.Example"
2. имя - "helloName"
Будут сгенерированы следующие имена:
1. Common Lisp: "closuretemplate.example:hello-name"
2. "closureTemplate.Example.helloName
3. RequireJS: пространство имен будет пропущено (оно появится при загрузке по выбору клиента), имя функции - "helloName".
Как видно, есть некоторая неконсистентность - имя пакета не "лиспофицируется".
Есть предложение его лиспофицировать тоже. Какие-либо возражения?
PS. Работа над кастомными директовами для print пока еще идет.
· original author: archimag
> Есть предложение его лиспофицировать тоже. Какие-либо возражения?
Возражений нет. Собственно, там же есть функция #'closure-template::lispify-name, но
CL-USER> (closure-template::lispify-name "closureTemplate.Example")
:CLOSURE-TEMPLATE.-EXAMPLE
Т.е. перед EXAMPLE добавляется ненужный "-". 
· original author: amartynov
А это не проблема. К концу праздников сделаю и заодно исправлю ведущий дефис.