[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-ом.

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

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

ЗЫ

Еще, возможно, будет разумно различать аннотации для расширяемых правил (или-правила) и для обычных. Тут уже совсем не ясно как это описать синтаксически. Если есть идеи, милости просим.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: [Nitra] Параметризованные правила
От: btn1  
Дата: 03.11.14 18:50
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>3. Параметр может быть любым правилом (т.е. regex, token или syntax).


С этого момента непонятно PEG — он же вроде однородный, описал правило — получи соответствие! Зачем нужны типы для правил?
Re: [Nitra] Параметризованные правила
От: s22  
Дата: 03.11.14 19:41
Оценка:
Здравствуйте, VladD2, Вы писали:


Почему тогда не сделать параметризацией и константами?
[SpanClass(String)]
token StringLiteral<Count> = Strings
{
  regex Strings= Digit(Count,Count);
}
Re[2]: [Nitra] Параметризованные правила
От: VladD2 Российская Империя www.nemerle.org
Дата: 03.11.14 20:05
Оценка:
Здравствуйте, btn1, Вы писали:

B>С этого момента непонятно PEG — он же вроде однородный, описал правило — получи соответствие! Зачем нужны типы для правил?


В Найтре давно не совсем PEG. На нижнем уровне используются классические регулярные выражения. Они быстрее и экономичнее по памяти.
Так же делается разделение на обычные правила и токены. Токены — это по сути обычные правила (в них доступна рекурсия), но в них не производится подстановка пробельных правил и они иначе интерпретируются при восстановлении и в IDE.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: [Nitra] Параметризованные правила
От: VladD2 Российская Империя www.nemerle.org
Дата: 03.11.14 20:06
Оценка:
Здравствуйте, s22, Вы писали:

s22>Почему тогда не сделать параметризацией и константами?


Зачем?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: [Nitra] Параметризованные правила
От: WolfHound  
Дата: 03.11.14 21:18
Оценка:
Здравствуйте, VladD2, Вы писали:

s22>>Почему тогда не сделать параметризацией и константами?

VD>Зачем?
За тем, что у нас вот такая хрень есть.
    | UpperBound = "{" sm        "," sm Number sm "}" { override Bounds = (0,               Some(Number.Value()));  }
    | LowerBound = "{" sm Number ","           sm "}" { override Bounds = (Number.Value(),  None());                }
    | FullBounds = "{" sm Number "," sm Number sm "}" { override Bounds = (Number1.Value(), Some(Number2.Value())); }
    | Exact      = "{" sm Number               sm "}" { override Bounds { def x = Number.Value(); (x, Some(x)); }   }
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[4]: [Nitra] Параметризованные правила
От: VladD2 Российская Империя www.nemerle.org
Дата: 03.11.14 22:35
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>За тем, что у нас вот такая хрень есть.


Не думаю, что в этом есть особый смысл.

В прочем, можно и числа поддержать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: [Nitra] Параметризованные правила
От: WolfHound  
Дата: 03.11.14 23:42
Оценка:
Здравствуйте, VladD2, Вы писали:

WH>>За тем, что у нас вот такая хрень есть.

VD>Не думаю, что в этом есть особый смысл.
VD>В прочем, можно и числа поддержать.
Нам это почти ничего не стоит.
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[6]: [Nitra] Параметризованные правила
От: s22  
Дата: 04.11.14 10:04
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Нам это почти ничего не стоит.


А пробрасывать функции? (в методы обработки)
Re[7]: [Nitra] Параметризованные правила
От: WolfHound  
Дата: 04.11.14 11:46
Оценка:
Здравствуйте, s22, Вы писали:

WH>>Нам это почти ничего не стоит.

s22>А пробрасывать функции? (в методы обработки)
Не понял.
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re: [Nitra] Параметризованные правила
От: WolfHound  
Дата: 04.11.14 11:50
Оценка:
Здравствуйте, VladD2, Вы писали:

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

Моё мнение задавать тип параметра правила нужно явно.
Ибо это публичный контракт правила.
Даже в языках, где вывод типов суров и глобален, правилом хорошего тона является явное задание типов функций, которые экспортируются из модуля.
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[2]: [Nitra] Параметризованные правила
От: s22  
Дата: 04.11.14 11:54
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, VladD2, Вы писали:


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

WH>Моё мнение задавать тип параметра правила нужно явно.
WH>Ибо это публичный контракт правила.
WH>Даже в языках, где вывод типов суров и глобален, правилом хорошего тона является явное задание типов функций, которые экспортируются из модуля.

Согласен.
Но как быть с тем, что синтаксис, токен и регексп не являются наследниками друг друга?
А есл метод может включать снтаксис то может включать регексп
Единственное сделать что то типа иерархии.
Re[8]: [Nitra] Параметризованные правила
От: s22  
Дата: 04.11.14 11:58
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Здравствуйте, s22, Вы писали:


WH>>>Нам это почти ничего не стоит.

s22>>А пробрасывать функции? (в методы обработки)
WH>Не понял.
[SpanClass(String)]
token StringLiteral<Count, Digit, F> = Strings1
{
  regex Strings= Digit(Count,Count);
  syntax Strings =  (s:Strings sm) { F(s)}
}

Нечто вроде, как я понимаю там все равно работают макросы, так что много переделывать не потредуется
Re[2]: [Nitra] Параметризованные правила
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.11.14 13:41
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Моё мнение задавать тип параметра правила нужно явно.


Да я же не против. Цель этой темы придумать внятный синтаксис или подтвердить, что люди понимают предложенный. Пока что (судя по отсутствию осмысленных ответов) люди его не очень понимают. Даже наличие 3 сущностей кое-кого смущает. А уж указание token за regex явно будет смущать.

Народ! Высказывайтесь, плиз.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: [Nitra] Параметризованные правила
От: WolfHound  
Дата: 04.11.14 15:13
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Да я же не против. Цель этой темы придумать внятный синтаксис или подтвердить, что люди понимают предложенный. Пока что (судя по отсутствию осмысленных ответов) люди его не очень понимают. Даже наличие 3 сущностей кое-кого смущает. А уж указание token за regex явно будет смущать.

У тебя будут ровно эти же сущности. Только они будут выводиться.
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[3]: [Nitra] Параметризованные правила
От: CodingUnit Россия  
Дата: 05.11.14 12:19
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, WolfHound, Вы писали:


WH>>Моё мнение задавать тип параметра правила нужно явно.


VD>Да я же не против. Цель этой темы придумать внятный синтаксис или подтвердить, что люди понимают предложенный. Пока что (судя по отсутствию осмысленных ответов) люди его не очень понимают. Даже наличие 3 сущностей кое-кого смущает. А уж указание token за regex явно будет смущать.


VD>Народ! Высказывайтесь, плиз.


Параметризированные правила хорошая вещь, но мое мнение, параметры типов не надо указывать, притом что это не параметры типов, они не говорят о конкретном правиле или типе, поэтому о типизации здесь речь не идет, сравнивая с обычными языками где есть контракт интерфейса на входные типы, существует всего три варианта подстановки и неужели эту работу выбрать токен, режекс или синтакс не может сделать компилятор за пользователя, которому эти лишние сложности, ключевые слова и ограничения не нужны, он просто пишет быстро грамматику, в наиболее короткой форме, компилятор все остальное делает сам. Параметры правил больше похожи на обобщенные типы где T в большинстве не описывается как определенная сущность, часто там может быть что угодно и при связывании уже метод вызывается со своим T, ограничения на T where T : class и тп делается редко и далеко не всегда, чаще используется обычный <T>. Можно сделать два синтаксиса как и есть в Н и C#, обобщенный <T> которому подставляется что угодно и компилятор найтры смотрит сопоставления при компиляции правила, также можно указать явно токен, режекс или синтакс правило которое может подставляться туда, и можно как публичный интерфейс задавать ограничения на правило, но для обычных проектов где краткость кода и удобство использования языка на главном месте, никакие лишние ключевые слова не нужны, в любом случае если правило переопределяется или используется из внешней библиотеки оно может проверяться при компиляции на соответствие также, как аналогично и с С# и остальными языками с обобщенными типами.
Re: [Nitra] Параметризованные правила
От: kaa_t Россия  
Дата: 05.11.14 14:42
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Мы тут продумываем дизайн параметризованных правил. В двух словах параметризованные правила — это правила которые имеют нечто вроде параметра типов, но вместо типа в него подставляется другое правило. Получается что-то вроде обобщенных правил.


VD>Собственно вопрос в том насколько интуитивным являются следующие соглашения

VD>* regex — означает что правило может быть только regex-ом.
VD>* token — правило может быть token-правилом или regex-ом.
VD>* syntax — правило может быть syntax-правилом, token-правилом или regex-ом.

Мне кажется, это будет сбивать с толку. Лучше ввести другие ключевые слова.

Еще мне не очень нравятся угловые скобки. Лучше обычные. Оно как то логичнее на мой взгляд, получится.

token StringLiteral ( Quote : token ) = Quote StringPart* Quote



А вообще с введением параметров, напрашиваются управляющие конструкции обрабатывающие входные параметры, что то типа :

syntax Value(Param)
{
    | Identifier
 
 #if Param == 1
    | StringLiteral = StringLiteral('\"');
 #else if Param == 2
    | StringLiteral = StringLiteral('\'');
 #else
    | StringLiteral1 = StringLiteral('\"');
    | StringLiteral2 = StringLiteral('\'');
 #endif
Re[2]: [Nitra] Параметризованные правила
От: VladD2 Российская Империя www.nemerle.org
Дата: 05.11.14 15:34
Оценка:
Здравствуйте, kaa_t, Вы писали:

_>Мне кажется, это будет сбивать с толку. Лучше ввести другие ключевые слова.


Дык, какие?

_>Еще мне не очень нравятся угловые скобки. Лучше обычные. Оно как то логичнее на мой взгляд, получится.


_>
_>token StringLiteral ( Quote : token ) = Quote StringPart* Quote
_>


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

_>А вообще с введением параметров, напрашиваются управляющие конструкции обрабатывающие входные параметры, что то типа :


Эти параметры не препроцессорные символы. Скорее их можно сравнить со ссылками на функции. Параметризованные правила (скорее всего) будут доступны в бинарном виде из внешних сборок (как дженерики или функции).

Так что управлять генерацией кода в них будет нельзя.

Зато у нас и так есть средства расширения — extend.

Список правил можно даже в рантайме изменять. Как минимум мы хотим воспроивести поведение Немерла в котором можно расширять грамматику с помощью using-а (импорта).

Параметризованные же правила — это некий вариант обобщений. Но они будут не шаблонного (шаблонов С++) плана, а скорее более похожими на дженерики дотнета или даже на функции высшего порядка. По сути правила — это функции. Так что для того чтобы передать правилу ссылку на другое правило, нужно просто передать ему ссылку на функцию или некий интерфейс.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: [Nitra] Параметризованные правила
От: Рамиль Вахитов  
Дата: 10.11.14 19:01
Оценка:
Если я правильно понял, тип параметра определяется описанием правила, а не объявлением параметра. Для меня важным является описание правила. И если объявление типа конфликтует с описанием правила, тогда я не хочу заморачиваться с объявлением типа.
Re[2]: [Nitra] Параметризованные правила
От: WolfHound  
Дата: 10.11.14 23:42
Оценка:
Здравствуйте, Рамиль Вахитов, Вы писали:

РВ>Если я правильно понял, тип параметра определяется описанием правила, а не объявлением параметра. Для меня важным является описание правила. И если объявление типа конфликтует с описанием правила, тогда я не хочу заморачиваться с объявлением типа.

Не правильно.
Тип у параметра будет и так и так.
Вопрос в том указывать явно или выводить.
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re: [Nitra] Параметризованные правила
От: STDray http://stdray.livejournal.com
Дата: 12.11.14 14:46
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Мы тут продумываем дизайн параметризованных правил. В двух словах параметризованные правила — это правила которые имеют нечто вроде параметра типов, но вместо типа в него подставляется другое правило. Получается что-то вроде обобщенных правил.


VD>Собственно вопрос в том насколько интуитивным являются следующие соглашения

VD>* regex — означает что правило может быть только regex-ом.
VD>* token — правило может быть token-правилом или regex-ом.
VD>* syntax — правило может быть syntax-правилом, token-правилом или regex-ом.
Я не знаю про интуитивность.

Пусть у нас есть множество типов правил X = { regex, token, syntax } и бинарное отношение R = 'правило x можно подставить в правило y'.
Тогда, мы имеем
— Рефлексивность (можно подставить x в x)
— Антисимметричность(если x можно подставить в y и y можно подставить в x, то x равен y)
— Транзитивность (если x можно подставить в y и y можно подставить в z, то x можно подставить в z)
— Линейность (x можно подставить в y или y можно подставить в x).
Выходит, что отношение отношение R = 'правило x можно подставить в правило y' является нестрогим линейным порядком. А выбранные соглашения вполне логичны.


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

Целесообразность состоит в том, что для определения типа правила-параметра человек вынужден проанализировать все его использования, а затем вывести корректный тип. Это машинная работа. При том, как я понимаю, процедура вывода достаточно проста — поиск правила с "наименьшим" типом, куда подставляется правило-параметр. Таким образом будет выведен "наибольший" из возможных типов для правила-параметра.

Если говорить про публичный интерфейс правила, то лучше разобрать на примере. Пусть у нас есть некоторый параметр ruleparam, который используется в правилах типа token и syntax. Для ruleparam будет выведен тип token.
Если мы избавимся от использования в правилах типа token, то тип ruleparam может быть "увеличен" до syntax, тогда весь вызывающий код останется рабочим.
Если же мы, наоборот, воспользуемся ruleparam в правилах типа regex, то тип ruleparam будет "уменьшен" до regex, вызывающий код может сломаться. Но в этом случает он сломается вне зависимости от того, указывали мы тип явно или он был выведен.
Другое дело, что может возникнуть желание указать "меньший" тип правила, чем выводимый. Полагаю, это может быть нужно для генерации более эффективного автомата, либо если "уменьшение" типа правила параметра планируется заранее (но мне сложно представить, как это возможно).
В итоге особой пользы от публичного интерфейса не видно, а удобство использование страдает, плюс бойлерплейт. Лучшим вариантом выглядит вывод по умолчанию (по вышеописанной схеме) с возможностью явно указать тип.


VD>Альтернативой выводу типа параметров может быть явная его задание при объявлении параметра.

Я полагаю, что выбрать синтаксис для обозначения типа правила-параметра нужно в любом случае. Поскольку он будет использоваться не только при возможно явном задании типа, но так же в документации и, в перспективе, в интеграции с IDE. Как я уже писал отношение подстановки правил является нестрогим линейным порядком, а для его обозначения в математике применяется символ '<='. Соответственно
token StringLiteral< Quote <= regex > = Quote StringPart* Quote

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

С другой стороны, при практическом использовании нет необходимости каждый раз пытаться как-то подчеркнуть то, что можно один раз прочитать в документации и запомнить. Так что исходный вариант
VD>
token StringLiteral<regex Quote> = Quote StringPart* Quote

представляется вполне нормальным.


VD>ЗЫ

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