[Nitra] Параметризованные правила
От: VladD2 Российская Империя www.nemerle.org
Дата: 03.11.14 16:39
Оценка: 18 (1)
Мы тут продумываем дизайн параметризованных правил. В двух словах параметризованные правила — это правила которые имеют нечто вроде параметра типов, но вместо типа в него подставляется другое правило. Получается что-то вроде обобщенных правил.

Вот пример (из реальной грамматики JSON) демонстрирующий проблему решаемую параметризованными правилами:
regex HexDigit = ['0'..'9', 'a'..'f', 'A'..'F'];
regex EscChar  = '\\' | '/' | 'b' | 'f' | 'n' | 'r'| 't' | 'u' HexDigit{4,4}
 
[SpanClass(String)]
token StringLiteral1 = Quote StringPart* Quote
{
  regex Quote   = '\"';
  regex Esc     = '\\' (Quote | EscChar);
  regex Escs    = Esc+;
  regex NotEscs = ~(Any* (Quote | '\\') Any*) - "";
 
  token StringPart
  {
    | Escs;
    | NotEscs;
  }
}
 
[SpanClass(String)]
token StringLiteral2 = Quote StringPart* Quote
{
  regex Quote   = '\'';
  token StringPart
  {
    regex Esc = '\\' (Quote | EscChar);
    | Escs    { regex Escs    = Esc+; }
    | NotEscs { regex NotEscs = ~(Any* (Quote | '\\') Any*) - ""; }
  }
}
 
syntax Value
{
  | Identifier
  | StringLiteral1
  | StringLiteral2
  ...


Выделенное жирным правило StringLiteral2 практически дублирует правило StringLiteral1. Разница заключается только в типе кавычки заданной в подправиле Quote.

Бывают случае когда такое дублирование принимает угрожающие размеры. Например, в грамматике нового форматера РСДН таких мест довольно много Paragraph.nitra, Content.nitra. Правило Part, в каждом своем вхождении, дублирует один и тот же код.

Так вот возникла идея ввести параметризованные правила, чтобы избавиться от этого дублирования.
Вот пример приведенной выше грамматики литералов JSON-а переписанный с гипотетическими параметризованными правилами:

regex HexDigit = ['0'..'9', 'a'..'f', 'A'..'F'];
regex EscChar  = '\\' | '/' | 'b' | 'f' | 'n' | 'r'| 't' | 'u' HexDigit{4,4}
 
[SpanClass(String)]
token StringLiteral<Quote> = Quote StringPart* Quote
{
  regex Esc     = '\\' (Quote | EscChar);
  regex Escs    = Esc+;
  regex NotEscs = ~(Any* (Quote | '\\') Any*) - "";
 
  token StringPart
  {
    | Escs;
    | NotEscs;
  }
}
 
syntax Value
{
  | Identifier
  | StringLiteral1 = StringLiteral<'\"'>
  | StringLiteral2 = StringLiteral<'\''>
  ...


Думаю все очевидно.

Теперь, собственно, вопрос!

Так как Нитра является статически типизируемым языком, на параметры правил налагаются определенные ограничения. В приведенном примере в качестве параметра Quote можно подставить исключительно regex-правило, так как оно применяется в теле другого regex-правила. Могут быть следующие ограничения:
1. Параметр может быть исключительно regex-правилом.
2. Параметр может быть regex-правилом или token-правилом.
3. Параметр может быть любым правилом (т.е. regex, token или syntax).

В приведенном примере тип параметра выводится из использования, т.е. при компиляции компилятор узнает тип Quote только после того как проведет анализ всех (или части) подправил. Так как параметры правил это часть публичного интерфейса, то резонно возникают сомнения относительно целесообразности его вывода.

Альтернативой выводу типа параметров может быть явная его задание при объявлении параметра.
Например, так:
[SpanClass(String)]
token StringLiteral<regex Quote> = Quote StringPart* Quote
...


Собственно вопрос в том насколько интуитивным являются следующие соглашения
* regex — означает что правило может быть только regex-ом.
* token — правило может быть token-правилом или regex-ом.
* syntax — правило может быть syntax-правилом, token-правилом или regex-ом.

Не введет ли это пользователей в заблуждение?

Если это плохое решение, то предлагайте свои варианты, которые кажутся вам более интуитивными.

ЗЫ

Еще, возможно, будет разумно различать аннотации для расширяемых правил (или-правила) и для обычных. Тут уже совсем не ясно как это описать синтаксически. Если есть идеи, милости просим.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.