Поддержка шаблонов в инструментах авторинга

Issue #312 closed
Oleg Sychev repo owner created an issue

Originally reported on Google Code with ID 312

Нужно реализовать следующую функциональность:
Храним (в БД, а пока можно захардкодить) наборы соответствий типа 
number  - \d*\.\d* . 
Соответственно некоторая запись в регексе с использованием комментариев (или другой
вариант - например flex использовал фигурные скобки, но тогда их эскейпить надо) будет
преобразовываться в содержимое шаблона - например (?#$$word$$)

Шаблоны могут содержать нумерованные плейсхолдеры - место, в котором может стоять любое
корректное регулярное выражение. Например в словаре шаблонов может быть описано типа

parens - ((?#$$1$$)|\((?R)\)) 
будет использовать плейсхолдер с номером 1.
В регулярном выражении при задании такого шаблона необходимо указать значение плейсхолдера.
Значение должно быть вне комментария, т.к. коммент закрывается любой скобкой. Здесь
вопрос как лучше. Вариант (но длинновато)
(?#$$parens$$)(?#$$1)abc(?#1$$)

Ваши предложения? Кто короче предложит вариант записи шаблона с плейсхолдером в регексе?

Нам необходима будет новая опция в режиме парсинга - учитывать ли шаблоны. И возможность
сгенерировать текст регекса при подстановке шаблона, а также возможность показать их
как узлы в инструментах авторинга - т.е. будут два новых типа preg-узла - preg_template_leaf
и preg_template_operator (они используются инструментами авторинга для отображения
шаблонов, матчер же их заменяет на реальные значения).

Решать надо срочно, т.к. желательно к релизу сделать хотя бы для захардкоденных шаблонов
(включая плейсхолдеры!) возможность вводить их и получать результирующий регекс.

Reported by oasychev on 2014-12-03 21:51:31

Comments (39)

  1. Valeriy Streltsov
    У меня пока что вопрос - что если нам надо подряд несколько шаблонов parens? Они же
    будет иметь разные подстановки для плейсхолдера. Я так понимаю, что плейсхолдер $$1$$
    надо тоже уметь задавать во время редактирования.
    

    Reported by vostreltsov on 2014-12-06 10:17:53

  2. Valeriy Streltsov
    И чуть более короткий вариант для задания значения плейсхолдера - (?#$$1abc). То есть
    если видим запись (?#$$n то всё что до конца коммента - это регекс.
    

    Reported by vostreltsov on 2014-12-06 10:19:30

  3. Oleg Sychev reporter
    Смысл плейсхолдера - это та часть шаблона, которая задается не в описании шаблона, а
    в конкретном регексе. Параметризованный шаблон.
    
    Укорачивать и уточнять синтаксис плейсхолдеров нужно. Возможно по принципу функций
    в Си - круглая скобка вместе с названием, потом разделители-комменты (а не имена плейсхолдеров),
    потом закрывающая скобка. Причем именно скобки и разделители (запятые) должны быть
    комментами, значения плейсхолдеров между ними должны быть обычными регексами.
    
    Писать значение плейсхолдера в комменте нельзя категорически, т.к. коммент идет до
    первой закрывающей скобки, а значение - произвольный регекс. Более того - ваша мысль
    о подрядидущих шаблонах недостаточно страшна - они еще и вложенными будут (причем в
    случае с рекурсией и скобками это вполне рядовая юзерская ситуация) - внутри первого
    плейсхолдера parens может встретится снова шаблон parens со своим плейсхолдером и т.д.
    Синтаксис должен это выдерживать.
    

    Reported by oasychev on 2014-12-07 22:51:49

  4. Former user Account Deleted
    оба синтаксиса какието очень нечитаемые.
    Есть 2 предложения:
    1) 
    Простые шаблоны:
    (?#<pattern_name>)
    
    Шаблоны с плейсхолдерами:
    (?#<pattern_name>inner_regex)
    
    Описание плейсхолдера при определении шаблона
    (?#<placeholder>)
    2) json
    Простые шаблоны:
    (?#{name: "patternname"})
    
    Шаблоны с плейсхолдерами:
    (?#{name: "patternname", placeholder1: "blablabla"})
    
    Описание плейсхолдера при определении шаблона
    (?#{placeholder: "placeholder1"})
    

    Reported by TOPT.iiiii on 2014-12-09 14:45:34

  5. Oleg Sychev reporter
    Я уже писал почему нельзя размещать значение плейсхолдера (по сути - параметра функции)
    внутри коммента - оно нарушит синтаксис.
    
    Пока я вижу самое простое - использовать синтаксис функции - нумеровать плейсхолдеры
    и в скобках писать:
    (?#$$templatename()placeholder1_value(?#$$,)placehodler2_value(?#$$)
    * примечание - закрывающая круглая скобка я так понимаю не может быть частью коммента
    регекса, поэтому в последнем просто $$. Доллары нужны чтобы отличить специальные комменты
    шаблонов от обычных комментов.
    Плейсхолдер - это по сути параметр шаблона, т.е. при определении шаблона задается его
    номер (название) в нужных местах шаблона; а при вставке шаблона в регекс задается значение
    плейсхолдера в виде произвольного регулярного выражения.
    

    Reported by oasychev on 2014-12-09 14:59:50

  6. Oleg Sychev reporter
    Да, можно еще квадратные или фигурные скобки вместо круглых, тогда закрывающая тоже
    поместиться. Круглые скобки и запятые я взял по аналогиями с функциями С++, это универсальный
    синтаксис позволяющий внутри значения плейсхолдера вставлять новые шаблоны со своими
    плейсхолдерами.
    

    Reported by oasychev on 2014-12-09 15:01:02

  7. Former user Account Deleted
    есть еще предложение - использовать квадратные скобки.
    То есть, в режиме использования шаблоном, мы говорим, что если первый символ символьного
    класса, например, # - то это уже не символьный класс а шаблон.
    
    Например
    
    [#patternname]
    
    [#patternname#placeholder1#placeholder2] или
    [#patternname[placeholder1][placeholder2]]
    
    это позволит избавиться от странностей вроде той, что в конце комментария может не
    быть круглой скобки. И это уменьшит размер шаблонов...
      ***
    Еще предложение в варианте
    (?#$$templatename()placeholder1_value(?#$$,)placehodler2_value(?#$$)
    не использовать доллары:
    (?##templatename()placeholder1_value(?##,)placehodler2_value(?##)
    
    (мне не нравятся доллары, кажется, они усложняют читаемость в миллиард раз)
    

    Reported by TOPT.iiiii on 2014-12-10 16:11:55

  8. Oleg Sychev reporter
    Манером с квадратными скобками вы нарушаете обратную совместимость - то, что раньше
    было корректными символьными классами становится чем-то непонятным; к тому же совершенно
    неясно какие правила эскейпинга применять к значениям плейсхолдеров - как внутри симв.
    класса (потому что внутри) или как обычно (потому что это обычный регекс, а вовсе не
    часть симв. класса). Очень проблемный подход.
    
    Использовать вместо доллара дублирование решетки можно, может быть три раза ее повторить.
    Общий смысл долларов или лишних решеток - сделать крайне маловероятным совпадение обычного
    коммента с шаблоном. 
    

    Reported by oasychev on 2014-12-10 21:00:12

  9. Oleg Sychev reporter
    Новые узлы шаблонов для авторинга создаются, просьба всем оперативно написать их отображение
    в инструментах авторинга.
    
    Сначала каждый пишет здесь свои предложения по внешнему виду (граф/дерево) и необходимым
    строкам (описание, а также надписи в графе и тултипы в дереве); потом реализуем.
    
    Поскольку для релиза то надо сделать быстро - в течении недели...
    

    Reported by oasychev on 2015-01-04 19:23:52

  10. Oleg Sychev reporter
    Появился новый класс шаблона, который позволяет хранить их захардкоденными.
    Пахомову после общего обсуждения строк добавить туда поля для хранения строк с описаниями
    в естественном падеже. Посмотрите есть ли в Moodle filter/multilang или что-то подобное,
    там может быть способ записи строки с текстами на нескольких языках. Не использовать
    get_string здесь логично, ибо в будущем эти строки будет вводить пользователь. Только
    сначала согласовать строки, чтобы не повторяться а по максимум использовать те же для
    графа, тултипа дерева и описания.
    

    Reported by oasychev on 2015-01-05 00:17:03

  11. Valeriy Streltsov
    Еще пара моментов, которые надо уточнить.
    
    1) Шаблонам нужны свои личные опции, потому что можно писать хоть в обычной, хоть в
    extended нотации, и так далее. Предлагаю строковое поле $options рядом с $regex.
    
    2) Нужно ли уметь использовать шаблоны внутри описания шаблона? Именно описания, а
    не вложенных вызовов. Там тогда получается нужно кучу дополнительных проверок городить,
    и пока не совсем понятно как это вообще парсить.
    

    Reported by vostreltsov on 2015-01-06 11:30:46

  12. Valeriy Streltsov
    1) реализовано, 2) в первом приближении тоже
    
    Вот такой вопрос нумерации подвыражений. Допустим, есть:
    
    'parens_req' => new qtype_preg\template('parens_req', '(   \(    (?:$$1|(?-1))   \)
     )', 'x', 1),
    'parens_opt' => new qtype_preg\template('parens_opt', '$$1|(?###parens_req<)$$1(?###>)',
    '', 1),
    
    И простое использование:
    
    (?###parens_opt<)(a)(?###>)
    
    Правильно ли, номер 1 идет на левый операнд альтернативы, 2 - это обертка parens_req,
    3 - это исходный (a) уже в качестве операнда вместо $$1?
    Просто получается, что записанное в строке в одном месте выражение размножается на
    несколько поддеревьев в разными номерами. Можно, конечно, решить этот конкретный случай
    вызовом подвыражения но как-то странно это всё равно.
    

    Reported by vostreltsov on 2015-01-08 12:32:59

  13. Oleg Sychev reporter
    Ну по логике вещей правильно. Другое дело что лучше в операндах таких шаблонов не использовать
    подвыражения без надобности. А использовать двойную нумерацию подвыражений тут невозможно?
    Если нет, то для именованных подвыражений можно точно добиться чтобы совпадение было
    с одинаковым ключом.
    

    Reported by oasychev on 2015-01-09 18:17:36

  14. Former user Account Deleted
    добавил описания к шаблонам, и получение описания для текущего языка.
    
    Описания задаются в виде:
    array('код языка' => 'описание $$1', 'код языка' => 'описание $$1')
    
    $$1 выбрано для единообразия с синтаксисом шаблонов.
    
    Между $$ и цифрой можно указывать форму.
    
    Пример:
    new qtype_preg\template(
        'parens_req', 
        '(   \(    (?:$$1|(?-1))   \)  )', 
        'x', 
        array('en' => '$$1 in parens', 'ru' => '$$1 в скобках'), 
        1
    )
    

    Reported by TOPT.iiiii on 2015-01-11 09:42:20

  15. Valeriy Streltsov
    Допустим, шаблон (x)(y)$$1|($$1)
    
    (?###template<)(a)(?###>)
    
    Допустим, мы проходим подряд по всем токенам, которые встречаем, и если это шаблон
    - сразу парсим его.
    Тогда у шаблона есть подвыражения 1,2,3. Операнд (a) получает номер 4 во время парсинга.
    Затем мы доходим до правила свёртки, и нужно подставить (a) вместо $$1. Вот этот момент
    мне и не ясен до конца. (x)(y) нужно оставить как есть, потом $$1 сделать номер 3,
    потом увеличить номер подвыражения из правой альтернативы, и снова увеличить номер
    правого $$1. И после всего этого лексеру нужно тоже увеличить счетчик для последующих
    скобок.
    
    Я пока не вижу четкого алгоритма, как оно должно работать. Может попробуем текстовый
    препроцессор сделать?
    

    Reported by vostreltsov on 2015-01-12 18:17:23

  16. Valeriy Streltsov
    Вот, предлагаю гораздо более простое решение с препроцессингом. Это будет одна-единственная
    функция, принимающая все токены из регекса и возвращающая строку, теперь уже без шаблонов.
    
    У нее есть стек открытых шаблонов - как при проверке открытых-закрытых скобок. Проходим
    по всем токенам, если встретили не-шаблон, дописываем к результату userinscription
    токена. Если шаблон, то добавляем его на стек. И так далее. Синтаксические ошибки обнаруживаются
    здесь же.
    
    По производительности я не думаю, что это будет медленее (а может и быстрее), чем каждый
    раз клонирование всех закэшированных шаблонов + многочисленные обходы для перенумерации.
    

    Reported by vostreltsov on 2015-01-12 21:52:13

  17. Valeriy Streltsov
    Еще забыл уточнить, что учет опций шаблона делается через (?x: ... ) в случае расширенной
    нотации, и т. п. Так что объект парсера будет создаваться всего один, только для финального
    парсинга. А лексера будет два - для первоначального и финального лексинга.
    

    Reported by vostreltsov on 2015-01-12 21:58:00

  18. Oleg Sychev reporter
    Честно говоря если подставлять прямо при лексинге основного регекса шаблоны, не понимаю
    какие могли бы быть проблемы - просто прибавлять количество подвыражений внутри шаблона
    и все, оно же слева направо работает.
    
    Но можно и препроцессором, надо только чтобы он режим preserveallnodes не испортил.
    

    Reported by oasychev on 2015-01-15 18:46:33

  19. Oleg Sychev reporter
    Пахомову вопрос - там слоформы описаний аргументов как-то поддерживаются? Или только
    именительный падеж везде?
    

    Reported by oasychev on 2015-01-15 19:16:27

  20. Former user Account Deleted
    сейчас всё что есть - в именительном падеже. В описании шаблонов можно вместо $$1 использовать
    $$form1 для указания требуемой формы. 
    

    Reported by TOPT.iiiii on 2015-01-15 20:33:31

  21. Former user Account Deleted
    предлагаю в дереве отображать следующим образом - т.к. все стили форм уже были использованы
    ( http://www.graphviz.org/doc/info/attrs.html ), то выделить края новых узлов зеленым
    цветом. Для параметризованного шаблона так:
    1) http://prntscr.com/5tp9lk
    2) тултип на русском: шаблон с параметрами
    3) тултип на английском: template with parameters
    Для шаблона без параметров так:
    1) http://prntscr.com/5tozca
    2) тултип на русском: шаблон без параметров
    3) тултип на английском: template without parameters
    

    Reported by grvlter on 2015-01-17 20:22:41

  22. Oleg Sychev reporter
    Во-первых, если выделять цветом, то тогда уже форму делать для шаблона без параметров
    идентичную с листом-символом, прямоугольную. А с параметрами - как у операций. Как
    простые и сложные ассерты (мы, кстати и dashed и dotted ухитрились использовать?)
    
    Во-вторых, зеленый цвет вроде используется для выделения, может синий или оранжевый?
    
    В третьих, я уже писал выше - в качестве тултипа должно быть описание смысла шаблона
    на русском/английском языке, которое вводится в класс шаблона. Что этот конкретный
    шаблон означает, т.е. условно (над фразой надо подумать) "любое количество правильно
    открытых и закрытых скобок" а не просто "шаблон с параметрами". Использовать строки
    от описания Пахомова может получиться для шаблонов с параметрами если правильно продумать
    слова, которые в общем смысле характеризуют параметры.
    

    Reported by oasychev on 2015-01-18 15:02:18

  23. Former user Account Deleted
    1) Формы для шаблонов без параметров/с параметрами соответствуют формам операндов/операторов
    (прямоугольные/овалы)
    2) синий цвет:
    http://prntscr.com/5ujazb
    оранжевый цвет:
    http://prntscr.com/5ujai8
    Я за синий, он на вид заметнее. Прошу высказать остальных своё мнение.
    

    Reported by grvlter on 2015-01-19 21:19:02

  24. Oleg Sychev reporter
    Синий неплох, только у нас некоторые надписи внутри синим/голубым делались в других
    узлах, мешать не будет? В шаблонах вроде таких надписей быть не должно. И таки может
    их как-нибудь dotted/dashed - не могли же мы оба эти стиля использовать на ассерты?
    Тоже прошу всех высказаться...
    

    Reported by oasychev on 2015-01-19 21:46:34

  25. Valeriy Streltsov
    Я за синий. Кстати, если мне не изменяет память - в графе, дереве и описании выделения
    разными цветами были сделаны? Может стоит унифицировать?
    

    Reported by vostreltsov on 2015-01-19 21:49:50

  26. Oleg Sychev reporter
    В графе и дереве вроде одинаково - зеленым. В описании это фон (а не прямоугольник),
    так что унифицировать едва ли разумно.
    

    Reported by oasychev on 2015-01-19 21:50:53

  27. Former user Account Deleted
    Значит оставляем синий.
    
    3) тултипы:
    для word: 
     "любое слово"
     "any word"
    для integer:
     "любое число, включая + или -"
     "any number including + or -"
    для parens_req:
     "подвыражение в скобках"
     "subexpression in parentheses"
    для parens_opt:
     "подвыражение в скобках или без"
     "subexpression in parentheses or without"
    для brackets_req:
     "подвыражение в квадратных скобках"
     "subexpression in square brackets"
    для brackets_opt:
     "подвыражение в квадратных скобках или без"
     "subexpression in square brackets or without"
    
    Что касается тултипов для пользовательских шаблонов: их должен описывать сам пользователь
    при создании шаблона так, как он хочет и на нужном ему языке или нужно генерировать
    краткое описание, подобно описаю самого регулярного выражения?
    

    Reported by grvlter on 2015-01-20 21:45:36

  28. Oleg Sychev reporter
    Я вот думаю, может быть к тултипам добавлять на первой строке слово "шаблон" а на второй
    уже описание? Мы так делали с символьными классами вроде.
    
    По скобкам - не надо пугать людей словом "подвыражение" - может "строка в скобках"
    или "текст в скобках"? Для круглых скобок - писать явно "в круглых скобках". Я бы еще
    и фигурные сделал.
    Может быть даже, для унификации с описанием, типа "в любом количестве круглых скобок"
    - а что именно будет в описании добавлено.
    
    Пользователь будет сам вводить для своих шаблонов описания, но это уже следующая версия.
    

    Reported by oasychev on 2015-01-23 13:39:41

  29. Former user Account Deleted
    Добавил поддержку шаблонов в графе (репозиторий zlumyo-preg-28). Приму предложения по
    внешнему виду. На данный момент внешний вид такой:
     - простой шаблон выглядит как обычный узел в форме гексагона;
     - сложный как подмаска с подписью, что это собственного говоря шаблон типа "такого-то".
    

    Reported by ZluMYO on 2015-01-25 11:28:32

  30. Former user Account Deleted
    3) тултипы:
    для word: 
     "любое слово"
     "any word"
    для integer:
     "любое число, включая + или -"
     "any number including + or -"
    для parens_req:
     "текст в круглых скобках"
     "text in round parentheses"
    для parens_opt:
     "текст в круглых скобках или без"
     "text in round parentheses or without"
    для brackets_req:
     "текст в квадратных скобках"
     "text in square brackets"
    для brackets_opt:
     "тект в квадратных скобках или без"
     "text in square brackets or without"
    
    "Пользователь будет сам вводить для своих шаблонов описания, но это уже следующая версия."
    - в таком случае предлагаю хранить тултипы на данном этапе в lang-файле
    

    Reported by grvlter on 2015-01-25 18:33:58

  31. Oleg Sychev reporter
    а) в графе сложный шаблон желательно отделить от подмасок/квантификаторов цветом или
    видом линии еще
    б) в lang файле можно, но с пользовательским это не пройдет.
    Есть еще такая штука для этих целей - в отличие от get_string работает и на пользовательских
    данных тоже
     https://github.com/moodle/moodle/tree/master/filter/multilang
    
    Вопрос только что ее надо проверять, если выключена - выдавать текст на одном каком-то
    языке, чтобы ужасов юзеру не показывать.
    

    Reported by oasychev on 2015-01-25 21:43:55

  32. Former user Account Deleted
    Внешний вид сложного шаблона в графе довольно проблематично отделить от подмасок/квантификатором.
    Вот такие есть варианты отрисовки границ кластера: http://prntscr.com/5xcuam
    Если думать о цветовом выделении, то наиболее очевидные (и ярко выраженные) цвета уже
    используются в других местах графа. Или можно повториться?
    

    Reported by ZluMYO on 2015-01-26 18:03:39

  33. Former user Account Deleted
    Для дерева вроде все сделал по шаблонам, прошу проверить
    

    Reported by grvlter on 2015-01-26 21:38:13

  34. Valeriy Streltsov
    Считаю, что и для дерева, и для описания, строки нужно делать прямо полями в самом классе
    шаблона. Юзер их будет вводить на своем родном языке сам, а сейчас пока захардкодим
    на английском. Не будет же он менять lang/en/qtype_preg.php!
    

    Reported by vostreltsov on 2015-01-29 11:59:21

  35. Former user Account Deleted
    они сейчас (у меня по крайней мере) берут описания не из lang-файла а как параметр конструктора
    
    new qtype_preg\template(
        'parens_req', 
        '(   \(    (?:$$1|(?-1))   \)  )', 
        'x', 
    >>> array('en' => '$$1 in parens', 'ru' => '$$1 в скобках'), <<<<<<<<<<<<<<<<<<<<<<
        1
    )
    
    расчет был на то, что в будущем появится форма вида:
    http://prntscr.com/5yhior
    и вся эта радость будет храниться в бд
    

    Reported by TOPT.iiiii on 2015-01-29 12:13:47

  36. Valeriy Streltsov
    Зачем мне вводить описание на английском, если я его всё равно не буду использовать?
    

    Reported by vostreltsov on 2015-01-29 12:24:51

  37. Oleg Sychev reporter
    По языкам и описаниям еще раз (см 31-й коммент) - "Есть еще такая штука для этих целей
    - в отличие от get_string работает и на пользовательских данных тоже
     https://github.com/moodle/moodle/tree/master/filter/multilang "
    
    Предлагаю проверять, включен ли - если проверять то вываливать описания на всех языках
    в соотв. тегах, если нет - на английском.
    Вообще по идее функция, возвращающая текущий язык интерфейса тоже есть.
    
    Валерий - про описание на английском не понял, хардкодить его точно надо.
    

    Reported by oasychev on 2015-02-08 22:07:49

  38. Former user Account Deleted
    >> по идее функция, возвращающая текущий язык интерфейса тоже есть.
    Есть. Она используется уже
    

    Reported by TOPT.iiiii on 2015-02-09 12:52:09

  39. Log in to comment