[Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 02.12.15 07:31
Оценка: 21 (2)
#Имя: wiki.nitra.SimpleLanguage
В качестве примера опишу создание простого (но тем не менее не тривиального) языка.

Этот язык создан в целях демонстрации того чтобы на простом примере продемонстрировать как использовать AST, декларации, символы, области видимости (Scope's) и зависимые свойства для типизации. Для понимания проходящего сначала рекомендую прочесть Описание подсистемы сбора информации «Nitra»
Автор(ы):
.

Для начала пример кода на этом языке:
var x = y + 2 * z;
var z = y + 3;
var y = 1;


Данный язык состоит из объявлений переменных которые видны глобально. "Глобально" значит что из выражений можно обращаться как к переменным объявленным выше текущей, так и ниже (а возможно и в другом файле).
Переменные можно инициализировать произвольным выражением. Причем выражения вычисляются не в порядке их написания, а в порядке зависимостей. Зацикленным выражения не вычисляются.

Особенность данного примере в том, что он не порождает ничего. Он только демонстрирует расчеты на зависимых свойствах. Аналогом этому примеру является свертка констант (constant folding) в языках программирования общего назначения.

Весь код (без комментариев) этого примера доступен здесь.

Для начала опишем синтаксис:
[cut]
syntax module SampleSyntax
{
  using Nitra.Core; // https://github.com/JetBrains/Nitra/blob/master/Nitra/Nitra.Runtime/Core.nitra
  using Nitra.CStyleComments; // https://github.com/JetBrains/Nitra/blob/master/Nitra/Nitra.Runtime/CStyleComments.nitra

  // директива keyword regex приказывает генератору парсеров вставлять правило S за литералами
  // соответствующими регулярной грамматике ['a'..'z', '_'..'_']+. Это нужно, чтобы парсер не
  // распознавал ключевое слово "var" в конструкции вида "varx= 42", а считал varx единым идентификатором.
  keyword regex ['a'..'z', '_'..'_']+ rule S;

  regex Keyword = "var";

  // это и последующее правило описывает идентификатор языка.
  // В Нитре поддерживается автоматическое связывание имен, по этому идентификатор 
  // разделяется на правило объявляющее имя символа (в данном случае "Name")
  // и правило описывающее ссылку на имя (в данном случае "Reference").
  // Атрибут "Name" производит автоматическое отображение идентификатора на предопределенный
  // в библиотеке Nitra.Runtime.dll ast типа Name: 
  // https://github.com/JetBrains/Nitra/blob/master/Nitra/Nitra.Runtime/Declarations/Name.nitra.
  [Name]
  token Name = !Keyword IdentifierBody;

  // Атрибут "Reference" производит автоматическое отображение идентификатора на предопределенный
  // в библиотеке Nitra.Runtime.dll ast типа Reference:
  // https://github.com/JetBrains/Nitra/blob/master/Nitra/Nitra.Runtime/Declarations/Reference.nitra.
  [Reference]
  token Reference = !Keyword IdentifierBody;

  // Стартовое правило. Разбирает 0 или более переменных (описанных в правиле VariableDeclaration).
  // nl - это маркер указывающий претипринтеру, что переменные нужно разделять переносом строки.
  [StartRule]
  syntax TopRule = (VariableDeclaration nl)*;

  // Правило описывающее переменную. Обратите внимание, что для указания имени мы 
  // использовали правило Name - это нужно для автоматического порождения символа для переменной.
  syntax VariableDeclaration = "var" sm Name sm "=" sm Expression ";";

  // Expression - Выражение нашего языка. В Expression описывается несколько альтернатив.
  // В Nitra единственный способ сделать набор альтернатив (т.е. связать правила по ИЛИ)
  // это описать расширяемое правило. Каждая альтернатива отделяется знаком "|" (шпалой, как мы ее называем).
  syntax Expression
  {
    | [SpanClass(Number)] Num = Digits // альтернатива разбирающая число
      {
        regex Digits = ['0'..'9']+; // вложенное правило типа regex разбирающее последовательность цифр - число
      }

    | Braces = "(" Expression ")"; // очевидно - скобки

    | Variable = Reference; // ссылка на другие переменные. 

  precedence Sum: // группа приоритетов для операторов "+" и "-"
    | Sum = Expression sm Operator="+" sm Expression; // оператор "+". 
    | Sub = Expression sm Operator="-" sm Expression; // sm - это маркер заставляющий претипринтер напечатать пробел

  precedence Mul: // Mul имеет более высокий приоритет нежели Sum но более низкий чем Unary
    | Mul = Expression sm Operator="*" sm Expression; // конструкция "Operator=" задает имя образуемому подправилом полю
    | Div = Expression sm Operator="/" sm Expression;

  precedence Unary:
    | Plus  = Operator="+" Expression
    | Minus = Operator="-" Expression
  }
}

[/cut]
Скормив этот файл nitra и получив dll мы уже получим возможность забирать код на нашем языке, подсветку и претипринт.

Чтобы наш можно было использовать из IDE или из тестовой утилиты нужно написать еще один файл Sample-Language.nitra с описанием языка:
language Sample
{
  syntax module SampleSyntax start rule TopRule;
}

Здесь SampleSyntax — это имя описанного выше синтаксического модуля, а TopRule имя стартового правила. Еще в этом файле может быть множество информации: расширение файла, информация об авторе, информация о стилях подсветки и т.п. Как это выглядит для более серьезного языка можно посмотреть здесь. Описание языка для самой Nitra.

Теперь нам нужно писать AST нашего языка в которых будут располагаться вычисления необходимые нам. AST находится в файле Sample-Ast.nitra:
[cut]
using Nitra;
using Nitra.Declarations;
using Nitra.Runtime.Binding;

// Стартовый AST. Обычно он называется CompileUnit, так 
// как описывает код отдельного файла.
ast Top
{
  // Зависимое свойство хранящее TableScope. TableScope находится в библиотеке 
  // и предоставляет функциональность таблицы имен. По совместительству TableScope
  // является областью видимости, и может использоваться в формировании более 
  // сложных областей видимости.
  // Значение этого свойства задается ивзне. В реализации интерфейса IProjectSupport (см. ниже)
  in ContainingTable : TableScope;

  // Данное присваивание присваевает таблицу имен на которую ссылается ContainingTable
  // всем AST-ам переменных (их свойствам ContainingTable). Variables - это структурная 
  // коллекция, а у них в Nitre автоматически генерируются свойства имеющие тот же тип и имя, 
  // что свойство у элемента коллекции. Подробнее см.
  // http://rsdn.ru/article/nitra/Nitra-Ast-and-Symbols-doc.xml#EAEAC
  Variables.ContainingTable = ContainingTable;

  // Структурное поле Variables хратит список AST-ов переменных.
  // Значение этого поля заполняется путем отображения на него дерева разбора.
  // Как это делается можно будет увидеть ниже.
  Variables : Variable*;
}

// Variable описывает декларацию переменной в нашем языке.
// Декларация - это особый вид AST, который порождает новый символ.
// Для описания декларации используем синтаксис "declaration", а не "ast".
// Декларации неявно унаследованы от Declaration:
// https://github.com/JetBrains/Nitra/blob/master/Nitra/Nitra.Runtime/Declarations/Declaration.n
// В Declaration объявлено структурное свойство Name и зависимое свойство ContainingTable.
// Для каждой декларации автоматически создается соответствующий ей символ.
declaration Variable
{
  // Декларация может содержать секцию symbol, в которой описываются зависимые 
  // свойства символа поражаемого деклараций. Собственно задача типизации вычислить
  // и собрать данные с AST и поместить их в свойства символов. Набор символов и будет
  // детальным описанием нашей программы.
  symbol
  {
    Kind = "var"; // Kind - это предопределенное свойство используемое при визуализации.

  // Директива stage позволяет задать стадию на которой могут быть вычисленные зависимые 
  // свойства объявленные после нее. Стадии нумеруются с нуля.
  stage 1: 
    // зависимое свойство которое будет хранить результат вычисления 
    // инициализирующего выражения.
    in Result : double; 
  }

  // Зависимое свойство ContainingTable унаследовано от неявного предка Declaration.
  // По этому нам не надо объявлять его явно.
  //in ContainingTable : TableScope;
  // Это свойство используется Nitra для размещения символа автоматически создаваемого 
  //по декларации. Символ будет создан как только будет задано значение этого свойства.

  // Передаем область видимости выражению. Она будет использоваться для автоматического
  // связывания имен.
  Expression.Scope = ContainingTable;
  // Помещаем результат вычисления выражения в свойство Result символа порождаемого для переменной.
  Symbol.Result    = Expression.Result;

  // Структурное свойство содержащее AST выражения инициализирующего переменную. 
  // Заполняется при отображении (см. отображение ниже).
  Expression : Expression;
}

// Абстрактный базовый AST описывающий выражение.
abstract ast Expression
{
stage 1:
  // Обратите внимание, на то, что Scope выражения задан на стадии 1. Это приводит к тому, что к
  // моменту начала его использования в Scope попадут все символы переменных имеющиеся в программе.
  // Так же обратите внимание на то, что тип данного свойства "Scope". Scope - это абстрактный тип
  // объявленный в библиотеке. Все области видимости обязаны наследоваться от него. Это поволяет
  // формировать сложную структуру областей видимости. Подробнее см. раздел 
  // "Области видимости, таблицы имен и связывание"
  // http://rsdn.ru/article/nitra/Nitra-Ast-and-Symbols-doc.xml#EPFAE
  in  Scope  : Scope;
  out Result : double;
}

// AST хранящий значение числа (числового литерала) в нашем языке.
// Он унаследован от Expression и не является абстрактным (может быть создан его экземпляр).
ast Number : Expression
{
  // Берем значение числа или 0, если его не удалось разобрать.
  Result = Value.ValueOrDefault;

  // Value - это структурно поле заполняемое при отображении. Его реальный тип ParsedValue<double>.
  // ParsedValue<> оборачивает значения простых типов решая две задачи:
  // 1. Добавляет к значению координаты откуда оно было разобрано.
  // 2. Позволяет хранить информацию о том, что значение не удалось разобрать.
  Value : double;
}

// Так как в нашем языке есть несколько типов выражений с похожей структурой AST,
// объявляем для них абстрактный базовый AST с именем "Binary".
abstract ast Binary : Expression
{
  // назначаем область видимости обоим подвыражениям.
  Expression1.Scope = Scope;
  Expression2.Scope = Scope;

  // структурные свойства описывающие два подвыражения.
  Expression1 : Expression;
  Expression2 : Expression;
}

// теперь в конкретных AST нам остается описать только вычисление зависимого
// свойства Result (описанного в Expression). Структурные свойства Expression1 и Expression2 у нас уже есть.
ast Sum : Binary { Result = Expression1.Result + Expression2.Result; }
ast Sub : Binary { Result = Expression1.Result - Expression2.Result; }
ast Mul : Binary { Result = Expression1.Result * Expression2.Result; }
ast Div : Binary { Result = Expression1.Result / Expression2.Result; }

// аналогично поступаем для выражений с одним подвыражением...
abstract ast Unary : Expression
{
  Expression.Scope = Scope;
  
  Expression : Expression;
}

ast Plus  : Unary { Result = Expression.Result; }
ast Minus : Unary { Result = -Expression.Result; }

// VariableRef - пожалуй это самая интересная часть описания AST. 
// Это описание ссылки на значение другой переменной в выражении.
ast VariableRef : Expression
{
  // Ref<> - это специальный тип позволяющий выразить результат разрешения имен или связывания.
  // Этот типа позволят хранить 
  // 1. Обычную ссылку на символа (при удачном связывании/разрешении имен).
  // 2. Информацию о том, что связывание/разрешение имен прошли ну дачно (символ не определен, Unresolved).
  // 3. При связывании или разрешении имен было найдено более одного символа с тем же именем (неоднозначность, ambigouty).
  // Метод Ref<>.Resolve() позволяет произвести разрешение имер (обычно выражающееся в фильтрации 
  // информации некоторым алгоритмом.
  // Reference.Ref (объявленный в библиотеке) имеет тип Ref[DeclarationSymbol]. См.
  // https://github.com/JetBrains/Nitra/blob/master/Nitra/Nitra.Runtime/Declarations/Reference.nitra 
  // Нам же нужно убедиться, что связывание произошло не с каким-то любым символом, а с символом 
  // типа VariableSymbol (образованного декларацией Variable, см. выше).
  // Метод Resolve производит эту операцию и в случае успеха помещает в зависимое свойство Ref результат.
  out Ref : Ref[VariableSymbol] = Reference.Ref.Resolve();

  // передает область видимости Reference-у. Это приведет к тому, что в нем автоматически произойдет
  // связывание ссылки на имя с символом. При этом результат помещается в свойство Reference.Ref.
  Reference.Scope = Scope;
  // Обращаемся к значению свойства Result символа с которым было связано имя. 
  // Обратите внимание, что этот код выполнится только при успешном разрешении имени
  // (при выполнении Reference.Ref.Resolve()). В ином случае значение свойства Ref.Symbol останется не вычисленным,
  // соответственно не будет вычесано и значение зависимого свойства Result (и так по цепочке вверх).
  Result     = Ref.Symbol.Result;

  // Структурное свойство значение которого получается при отображении.
  // Отображение для AST Reference генерируется автоматически, так как
  // на правило Reference был повешен атрибут "Reference".
  Reference : Reference;
}

[/cut]

AST и расчеты на нем готовы, но AST не появится сам собой. Нам нужно проецировать дерево разбора на него, чтобы Nitra автоматически строила AST для кода на нашем языке.
Отображение (оно же проекция, оно же маппинг) находится в файле Sample-Mapping.nitra.
[cut]
using Nitra;
using Nitra.Runtime;
using Nitra.Declarations;
using Nitra.Runtime.Binding;

// Отображаем дерево разбора правила TopRule из синтаксического модуля SampleSyntax на AST типа Top (см. их объявления выше).
map syntax SampleSyntax.TopRule -> Top
{
  // отображаем поле TopRule.VariableDeclarations (дерева разбора) на структурное свойство Top.Variables
  VariableDeclarations -> Variables;
}

// здесь все аналогично...
map syntax SampleSyntax.VariableDeclaration -> Variable
{
  // в AST Variable нет объявления поле Name. Оно неявно унаследовано от базового типа 
  // Declaration описанного в библиотеке. Но проецировать значения нужно и на свойства
  // базовых типов.
  Name       -> Name;
  Expression -> Expression;
}

// Отображение для расширяемого правила (описывающего ИЛИ в грамматике Nitra).
// Здесь опять же все похоже на самое первое отражение. Сначала указывается тип дерева 
// разбора (имя правил), а затем имя AST-типа.
map syntax SampleSyntax.Expression -> Expression
{
  // описание отображения для каждого расширения правила SampleSyntax.Expression
  // Num - это имя расширяемого правила (SampleSyntax.Expression.Num) 
  // Number - это имя AST-типа в который происходит отображение.
  | Num -> Number
    {
      // для вычисления значение структурного свойства Value используется выражение на Nemrle.
      // Мы это называем ручное или программное отображение. Оно нужно потому что Nitra не умеет
      // отображать распарсенные значения в double.
      // Значение типов не являющихся AST в Nitra должны быть обернуты в тип ParsedValue<>.
      // При этом ему передается позиция значения в коде (поле Digits дерева разбора имеет тип NSpan),
      // а так же самое значение.
      Value = ParsedValue(Digits, double.Parse(GetText(Digits)));
    }

  // остальное отображение тривиально и аналогично описанному выше, так что я не буду его описывать.

  | Braces -> Expression
  | Variable -> VariableRef
    {
      Reference -> Name;
    }
  | Sum
    {
      Expression1 -> Expression1;
      Expression2 -> Expression2;
    }
  | Sub
    {
      Expression1 -> Expression1;
      Expression2 -> Expression2;
    }
  | Mul
    {
      Expression1 -> Expression1;
      Expression2 -> Expression2;
    }
  | Div
    {
      Expression1 -> Expression1;
      Expression2 -> Expression2;
    }
  | Plus
    {
      Expression -> Expression;
    }
  | Minus
    {
      Expression -> Expression;
    }
}

[/cut]

Последний штрих... Для того чтобы написанные нами вычисления работали нам нужно написать небольшой обвязочный код, который запустит их и проинициализирует ContainingTable у корневых элементов AST-а (т.е. у Top). Пока что в Nitra это делается так. Нужно создать паршал-класс корневого элемент AST и реализовать в нем интерфейс IProjectSupport. Вот как это сделано в данном примере (файл Main.n):

[cut]
public partial class Top : AstBase, IProjectSupport
{
  public RefreshProject(project : Project) : void
  {
    // получаем файлы проекта
    def files   = project.Files.ToArray(); 
    // неведанная хфигня :) необходимая для вычисления зависимых свойств
    def context = DependentPropertyEvalContext(); 
    // Таблица имен, она же глобальная область видимости (Scope).
    // В ней будут объявлены все наши ременные. 
    def scope   = TableScope("Variables", null);

    foreach (file in files)
      when (file.Ast is Top as top)
        // задаем глобальную область видимости каждому Top AST нашего проекта.
        top.ContainingTable = scope;

    // Прогоняем первую стадию вычислений. На ней будут сформированы 
    // и помещены в таблицу имен символы переменных.
    AstUtils.EvalProperties(context, files, "Collect variables", 0);
    // Прогоняем вторую стадию. На ней будут произведены вычисления и получен результат.
    AstUtils.EvalProperties(context, files, "Compute variables", 1);
  }
}

[/cut]

Заключение

Данный пример аналогичен свертке констант (constant folding) применяемому в большинстве языков программирования. Он изначально игрушечный и не предполагает компиляции в машинный код и т.п. Его цель показать как производятся расчеты на зависимых свойствах.

Интересно то, что данные вычисления не могут зациклиться. Если в выражениях двух или более переменных создать циклическую зависимость, то все переменные входящие в эту зависимость не будут вычислены. Это особенность вычислительной модели зависимых свойств. Если надо ее можно обойти. Но это уже отдельная тема.

Посмотреть на результат можно поместив полученную dll в нашу тестовую утилиту (Nitra.Visualizer.exe). Так же пример будет работать и в IDE, но там будет доступна только подсветка и навигация, а результат расчетов увидеть не удастся.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Отредактировано 14.01.2017 23:36 VladD2 . Предыдущая версия . Еще …
Отредактировано 14.01.2017 23:12 VladD2 . Предыдущая версия .
Отредактировано 17.12.2015 20:58 VladD2 . Предыдущая версия .
Отредактировано 04.12.2015 16:03 VladD2 . Предыдущая версия .
Отредактировано 02.12.2015 9:35 VladD2 . Предыдущая версия .
Отредактировано 02.12.2015 9:28 VladD2 . Предыдущая версия .
Отредактировано 02.12.2015 9:21 VladD2 . Предыдущая версия .
Отредактировано 02.12.2015 9:07 VladD2 . Предыдущая версия .
Re: [Nitra] Пример простого языка вычисляющего выражения
От: _NN_ www.nemerleweb.com
Дата: 02.12.15 07:32
Оценка:
Здравствуйте, VladD2, Вы писали:

Вот такие вот статьи надо ставить не только сюда, но и в блог плюс ссылку в твиттер.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re: [Nitra] Пример простого языка вычисляющего выражения
От: _NN_ www.nemerleweb.com
Дата: 02.12.15 07:35
Оценка:
Здравствуйте, VladD2, Вы писали:

Тут тоже стоит добавить комментарии
// Это понятно
syntax module SampleSyntax
{
  // Это понятно, но неясно что получаем
  // Только ниже по коду догадываемся, что nl , sm оттуда .
  using Nitra.Core;
  // А это зачем в данном примере ?
  using Nitra.CStyleComments;

  // Вот это совсем неясно для чего
  keyword regex ['a'..'z', '_'..'_']+ rule S;
http://rsdn.nemerleweb.com
http://nemerleweb.com
Отредактировано 14.01.2017 23:15 VladD2 . Предыдущая версия .
Re[2]: [Nitra] Пример простого языка вычисляющего выражения
От: hardcase Пират http://nemerle.org
Дата: 02.12.15 08:45
Оценка: 9 (1)
Здравствуйте, _NN_, Вы писали:

_NN>
_NN>  // Вот это совсем неясно для чего
_NN>  keyword regex ['a'..'z', '_'..'_']+ rule S;
_NN>


Так как парсеры у нас безлексерные, мы вынуждены расставлять в пользовательский код так называемые пробельные правила — s и S. s правило опционально съедает пробелы, концы строк и еще ряд специальных символов. S правило вызывает s, но сперва проверяет, что на текущей позиции конец идентификатора (предикат !IdentifierPartCharacters).
keyword regex — это директива генератору парсеров, которая сообщает какие строковые литералы, используемые в правилах идущих ниже по тексту в текущем синтаксическом модуле, являются ключевыми словами. После таких строковых литералов будет вставлено правило S. Вероятно ее нужно назвать как-то понятнее, либо вообще избавиться от нее.
Эти приседания нужны чтобы парсер не принимал текст вида:
varx = 10;

Без этой директивы он благополучно распознает ключевое слово var и идентификатор x.
/* иЗвиНите зА неРовнЫй поЧерК */
Re[2]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 02.12.15 09:38
Оценка:
Здравствуйте, _NN_, Вы писали:

Нечаянно запостил тему раньше времени (не дописав). Ты как-то очень быстро отреагировал. Посмотри тему еще раз, так как я ее дописал.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 02.12.15 09:48
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN> // А это зачем в данном примере ?

_NN> using Nitra.CStyleComments;

Это добавляет поддержку комментариев. В прочем штука для данного примера не особо нужная.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: [Nitra] Пример простого языка вычисляющего выражения
От: Kolesiki  
Дата: 04.12.15 00:59
Оценка: +4
Здравствуйте, VladD2, Вы писали:

VD>В качестве примера опишу создание простого (но тем не менее не тривиального) языка.


Влад, откровенно, "простой язычок" выливается в весьма непростой набор описаний, выходящих далеко за рамки PEG. Понятно, что каждая строчка там — что-то полезное, без чего волшебство не работает, но не окажется ли так, что уже средней сложности язык утонет в умопомрачительном количестве директив и подсказок Нитре?
Когда Нитра начиналась, мне казалось, что она наоборот — должна быть проще Немерли.
Конструктор-всемогутер (в силу своей идеи) не может не захватывать всё, что только может переварить, но мне кажется работа "в лоб" (с простым деревом разбора) может оказаться проще, чем указывание Нитре в каждый аспект.
АСТ — да, полезная вещь, но если даже имеется просто дерево разбора, можно написать всяких вспомогательных методов, дающих ту же инфу, что и АСТ, но без плясок с мэппингом.
Область компилеростроения и так сложная вещь, так что если её ещё больше усложнять, есть риск никогда не добраться до финиша.

"Близе к делю" (ц) Шакал
Можно ли как-то работать с Нитрой, но без АСТ маппингов? (но чтобы расширять грамматику тоже можно было)
Re: [Nitra] Пример простого языка вычисляющего выражения
От: jazzer Россия Skype: enerjazzer
Дата: 04.12.15 01:12
Оценка:
Здравствуйте, VladD2, Вы писали:

Влад, а напиши, сколько строк реального кода в каждом примере (без комментариев). А то с комментариями выглядит устрашающе, а на самом деле кода там мало.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[2]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.12.15 12:38
Оценка:
Здравствуйте, jazzer, Вы писали:

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


J>Влад, а напиши, сколько строк реального кода в каждом примере (без комментариев). А то с комментариями выглядит устрашающе, а на самом деле кода там мало.


Там же рядом ссылки на файлы в которых комментариев нет.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.12.15 15:42
Оценка:
Здравствуйте, Kolesiki, Вы писали:

K>Влад, откровенно, "простой язычок" выливается в весьма непростой набор описаний, выходящих далеко за рамки PEG. Понятно, что каждая строчка там — что-то полезное, без чего волшебство не работает, но не окажется ли так, что уже средней сложности язык утонет в умопомрачительном количестве директив и подсказок Нитре?


Боюсь ты совсем не верно оцениваешь Нитру. Главное что тебе нужно понять — Нитра это не Немерл 2.0. Нитра — это средство для создания Нимерл 2.0 (или расширяемого C#).

Ее нельзя сравнивать с Немерлом просто потому, что в Немерле нет средств позволяющих делать то что и на Нитре.

Если разобраться в том как устроен Немерл, то окажется, что буквально все до тел методов и атрибутов в Немерле захардкожено. Немерл поддерживает только символы для типов (классов, структур, интерфейсов), членов типов и локальных переменных. По сути он поддерживает только подотчетные символы. С синтаксисом дела обстаят так. Для выражений мы еще можем объявить макросы (переписывающие выражения в другие), но для каких-то конструкций верхнего уровня синтаксис почти не расширяется (есть костыль, но очень неудобный и кривой).

Нитра же предоставляет возможность создавать собственные констуркции верхнего уровня.

В Немерле вообще невозможно управлять областями видимости. Нитра же предоставляет решение для этого. Причем решение очень декларативное и простое в использовании.

В Немерле почти нет возможностей для описания какие-то собственные стадии компиляции и вмешаться в стадии компилятора. Нитра же рассчитана на эти сценарии изначально.

K>Когда Нитра начиналась, мне казалось, что она наоборот — должна быть проще Немерли.


Они не проще. Она позволяет решать сложные задачи проще чем на Немерле. Написав компилятор Немерла на ней мы получим существенно более простое решение. Его будет в сотни раз проще развивать и поддерживать. При этом из коробки поддерживаются плюшки IDE и снимаются всякие ограничения на расширяемость синтаксиса.

K>Конструктор-всемогутер (в силу своей идеи) не может не захватывать всё, что только может переварить, но мне кажется работа "в лоб" (с простым деревом разбора) может оказаться проще, чем указывание Нитре в каждый аспект.


Это только на первый взгляд кажется. В прочем, если тебе этого достаточно, то есть методы на правилах, в которых можно (как в макрах) проанализировать что разобрано в правиле и вычислить какой-то результат.

Вот только поверь на слово — это закат солнца вручную. Сложные вещи — это связывание имен и типизация. Их проще делать специальными средствами.

Мы к пришли к этому эволюционным путем. Текущая версия Найтры написана "в лоб" и решение получилось довольно сложным, хотя мы использовали методы на правилах (довольно высокоуровневое решение для анализа дерева разбора).

K>АСТ — да, полезная вещь, но если даже имеется просто дерево разбора, можно написать всяких вспомогательных методов, дающих ту же инфу, что и АСТ, но без плясок с мэппингом.


Можно. Но чем сложнее задача, тем сложнее будет выглядеть решение на дереве разбора.

За AST есть следующие аргументы:
1. Все что делается с кодом делается ради создания модели кода в виде символов. AST это промежуточное (между деревом разбора и символами) представление позволяющее упростить трансформацию информации. При трансформации прямо из дерева разбора в символы трасформация получается очень нетривиальной. Структура AST-а намного ближе к символам. Это позволяет порождать символы прямо по AST.
2. AST позволяет описать вычисления один раз, а не дублировать их.
3. AST поддерживает полиморфизм, что позволяет произвести нормальную декомпозицию AST/деклараций/символов.
4. AST компактный и его можно легко сериализовать. Это позволит оптимизировать работу плагина к IDE (не парсить весь проект при открытии, а десиреализвоать символы и AST, а так же занимать меньше памяти).
5. В AST все члены именованы. Это позволяет упросить описание вычислений.

K>Область компилеростроения и так сложная вещь, так что если её ещё больше усложнять, есть риск никогда не добраться до финиша.


Она ложная только потому что в ней пытаются решать задача "в лоб". Вот Нитра и делается для того, чтобы решать задачи компиляторостроения не в лоб, а с умом.

K>Можно ли как-то работать с Нитрой, но без АСТ маппингов? (но чтобы расширять грамматику тоже можно было)


Можно. Но глупо. Это будет в разы сложнее.

А так, можно получить парстри и анализировать его вручную или через методы на правилах. Естественно, при этом IDE не будет знать о твоих действиях и тебе придется еще дописать код всего что выходит за подсветку ключевых слов и фолдинг.

Неужели маппинг настолько сложно выглядит?

Код вычисления на AST понятен?

ЗЫ

Спасибо за отклик!

2All: не стесняйтесь задавать вопросы. Только в обсуждении можно найти лучшие решения.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.12.15 15:59
Оценка:
Здравствуйте, Kolesiki, Вы писали:

K>но не окажется ли так, что уже средней сложности язык утонет в умопомрачительном количестве директив и подсказок Нитре?


Опиши, плиз, что тебе кажется излишним? Что не понятно? В общем, по больше деталей.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: [Nitra] Пример простого языка вычисляющего выражения
От: novitk США  
Дата: 04.12.15 16:08
Оценка: :)
Здравствуйте, jazzer, Вы писали:

J>а на самом деле кода там мало.


Мало? Я уверен, что реализация этой задачи на скажем ANTLR/Скале будет не больше.

Влад впрочем скажет, что этот пример такой игрушечный, а вот для Немерле 2 абсолютно необходим весь этот зоопарк ДСЛей. Тут я не уверен, но думаю что ребят занесло.
Re[3]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.12.15 17:50
Оценка:
Здравствуйте, novitk, Вы писали:

N>Мало? Я уверен, что реализация этой задачи на скажем ANTLR/Скале будет не больше.


Я рад за твою уверенность. Сделай, сравним.

N>Влад впрочем скажет, что этот пример такой игрушечный, а вот для Немерле 2 абсолютно необходим весь этот зоопарк ДСЛей. Тут я не уверен, но думаю что ребят занесло.


А ты не думай. Ты сделай аналог и сравни.

У меня четкие критерии. Один и тот же код переписывается с использованием разных технологий и далее сравнивается объем кода и простота его поддержки/развития. При этом вопросов не возникает. У тебя же "уверенность" высосанная из пальца.

И таки — да, чем сложнее язык, тем более явно проявляются преимущества. Калькулятор по фигу на чем писать. Язык со сложными областями видимости уже нет. Язык с выводом типов и расширяемым синтаксисом уже и обсуждать нечего.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: [Nitra] Пример простого языка вычисляющего выражения
От: Философ Ад http://vk.com/id10256428
Дата: 04.12.15 18:30
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>В качестве примера опишу создание простого (но тем не менее не тривиального) языка.

VD>Для начала пример кода на этом языке:
VD>
VD>var x = y + 2 * z;
VD>var z = y + 3;
VD>var y = 1;
VD>


Код не читал, просто любопытствую:
а вот так:
var x = y + 2 * z;
var z = y + 3;
var y = x*z;


будет работать?

т.е. когд y неоднозначно, и решается через квадратное уравнение.
Всё сказанное выше — личное мнение, если не указано обратное.
Re[2]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 04.12.15 18:48
Оценка:
Здравствуйте, Философ, Вы писали:

Ф>Код не читал, просто любопытствую:

Ф>а вот так:
Ф>
Ф>var x = y + 2 * z;
Ф>var z = y + 3;
Ф>var y = x*z;
Ф>

Ф>будет работать?

Так все переменные входящие в цикл останутся не вычисленными.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: [Nitra] Пример простого языка вычисляющего выражения
От: Kolesiki  
Дата: 05.12.15 20:33
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Главное что тебе нужно понять — Нитра это не Немерл 2.0.


Это-то я как раз понимаю! Тут сложно перейти от мышления "реализатора языка" к "юзеру конструктора языка" (улавливаешь?). Вы-то там, в джетбрэйнсах, варитесь во всём этом и всё кажется простым и логичным но тут, по другую сторону джакузи, всё выглядит как-то переусложнённо.

Про АСТ вот чего-то не догнал:

VD> AST это промежуточное (между деревом разбора и символами)


Я думал, АСТ — это и есть сияние чистой программы, где дерево описывает все её сущности и по которым уже можно выдавать код. А символы — это просто узлы АСТ — классы, переменные... т.е. символы не являются чем-то отдельным.

VD> Опиши, плиз, что тебе кажется излишним? Что не понятно?


Ну вот представь, ты слесарь и чётко разбираешься в гайках, поршнях, можешь даже собрать двигатель, знаешь структуру всех деталей, а потом тебе предлагают собрать F1 используя "3Д-печатный конструктор болидов" — умом-то ты понимаешь, что там тоже будут известные тебе детали и узлы, но вся кухня в целом тебе абсолютно чужеродна. Вот так я (умея работать с PEG) смотрю на Нитру — всё знакомо, но ничего непонятно.

Сейчас пойдёт поток несвязных мыслей, так что можно отвечать так же несвязно

1. После ПЕГ пугает введение излишеств вроде "keyword regex". Я боюсь, что подобный "неявный помощник" заложит таймбомбу, что потом не разберёшься, почему один токен работает, а другой — нет.

2. "...по_этому идентификатор разделяется на правило объявляющее имя символа (в данном случае "Name") и правило описывающее ссылку на имя" — когда и зачем это применять? Почему я не могу просто поставить какой-нибудь крючочек и конструктор сам догадается что нужно делать?

3. Каша из keyword, regex, token, syntax — у меня уже голова кругом. Далее идёт отдельно три объявления syntax, а потом внутри syntax Expression ещё куча подправил. Как всё это разруливать? Почему все они не сделаны как syntax? Откуда столько неоднородности?
ПЕГ предлагает простую модель: терминалы и правила. Можно в Нитре оставаться в рамках модели ПЕГ?

4. Ладно, синтаксис описали — зачем ещё один Sample-Language.nitra? (причём в примере с NitraLanguage.nitra там ещё и цвета зачем-то захардкожены) Вроде б мы уже имеем "подсветку и претипринт", так мы ещё раз приседаем для какой-то другой утилиты?

5. Далее АСТ: вообще весь файл ни о чём, кроме знакомых строк, которые я уже как бы описал в SampleSyntax:
"ast Sum : Binary { Result = Expression1.Result + Expression2.Result; }"
Все эти ContainingTable, stage 1, Reference — это, вероятно, какие-то чертовски важные детали, но которые со стороны выглядят как лапша, торчащая из коллайдера. У нас простой язык: список присвоений, переменные, арифм.выражения, числа, операторы, скобки. Они структурированы в дерево. Всё. Но никакого дерева в Sample-Ast.nitra я не вижу — тону в дебрях.

6. Отображение — тут выглядит всё просто, но опять же — из-за простоты и дублирования кажется, будто делаешь одну и ту же работу трижды. Здесь всё понятно, кроме одного — что, все эти пляски с АСТ создавались из-за одной единственной фигни под названием partial class? Повторюсь, мне кажется можно взять простое дерево разбора и запрашивать у него инфу через хелперы (которые, никто не запрещает, могут построить некий внутренний АСТ для ускорения работы).

7. Ну и довершает пасквиль на могиле моего понимания "последний штрих" обвязочного кода, потому что мы и так уже предостаточно наописывали для АСТ, чтоб теперь ещё и приседать вокруг него.

Громоздко, загромождено деталями, непонятно — вот так для меня выглядит ещё простой! язык арифметики. Видимо, Нитра будет инструментом для ооочень избранных головастиков, я такой "упрощающий инструмент" едва ли постигну.
Re[3]: [Nitra] Пример простого языка вычисляющего выражения
От: Kolesiki  
Дата: 05.12.15 21:51
Оценка:
Влад, чисто политический момент: Нитра — не какая-то там саморасширяющаяся Немерля, а ажно целый конструктор таких языков! Не кажется ли тебе, что это прыжок через ступеньку? Может, сначала саму Немерлю-2 стоило начать с чистого листа, реализовать почти так же, но с учётом всего того, что вам нехватало? Ведь я так понимаю, конечная цель — не облака компилеростроения, а реальный практический язык для ДотНЕТа. Так не всё ли равно, как другие компании будут делать другие языки?
И "конструктор" тогда бы не понадобился (со всей обязательной в таких случаях универсализацией подо все языки).
Re[4]: [Nitra] Пример простого языка вычисляющего выражения
От: WolfHound  
Дата: 05.12.15 22:37
Оценка:
Здравствуйте, Kolesiki, Вы писали:

K>Влад, чисто политический момент: Нитра — не какая-то там саморасширяющаяся Немерля, а ажно целый конструктор таких языков! Не кажется ли тебе, что это прыжок через ступеньку? Может, сначала саму Немерлю-2 стоило начать с чистого листа, реализовать почти так же, но с учётом всего того, что вам нехватало?

Там не хватает примерно всей нитры.
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[4]: [Nitra] Пример простого языка вычисляющего выражения
От: hi_octane Беларусь  
Дата: 06.12.15 02:55
Оценка: +1
K>Влад, чисто политический момент: Нитра — не какая-то там саморасширяющаяся Немерля, а ажно целый конструктор таких языков! Не кажется ли тебе, что это прыжок через ступеньку? Может, сначала саму Немерлю-2 стоило начать с чистого листа, реализовать почти так же, но с учётом всего того, что вам нехватало? Ведь я так понимаю, конечная цель — не облака компилеростроения, а реальный практический язык для ДотНЕТа. Так не всё ли равно, как другие компании будут делать другие языки?
K>И "конструктор" тогда бы не понадобился (со всей обязательной в таких случаях универсализацией подо все языки).

Nitra это не через ступеньку — это удаление из Nemerle всего что было захардкожено, выпрямление всех косяков и ограничений, и т.п. Nemerle — язык который можно раширять под задачу очень-очень сильно, но некоторые ограничения захардкожены и требуют очень серьёзного усилия для поддержки как в языке так и в интеграции с VS. Но как только ты хорошо подумаешь над задачей "хочу язык который можно расширять не просто очень сильно а как угодно", сразу получится что "возможность сделать из исходного языка совсем-совсем другой" — это часть определения "как угодно". Ну и отсюда сразу становится очевидно что раз уровень абстракции решения и самой задачи совпадают — значит чисто политически всё делается правильно.
Nemerle — power of metaprogramming, functional, object-oriented and imperative features in a statically-typed .NET language
Re[4]: [Nitra] Пример простого языка вычисляющего выражения
От: _NN_ www.nemerleweb.com
Дата: 06.12.15 05:37
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Я рад за твою уверенность. Сделай, сравним.


Мне кажется будет хорошей идеей привести сравнение Нитры с другими решениями.
Это сразу поставит все точки над ё.
И так будет ясно когда лучше поменять код на использование Нитри, а где можно и оставить как есть.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[4]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 06.12.15 11:38
Оценка:
Здравствуйте, Kolesiki, Вы писали:

K>Влад, чисто политический момент: Нитра — не какая-то там саморасширяющаяся Немерля, а ажно целый конструктор таких языков! Не кажется ли тебе, что это прыжок через ступеньку?


Нет. Не кажется.

Чтобы решить сложную задзадачу нужен инструмент подходящей мощности. Или очень большие ресурсы. Их нам никто не предоставит.

У немерла куча проблем:
1. Его иерархия типов захардкожена и не расширяется. Релизация чего–то сложного приводит к созданию параллельной системы типов.
2. Сам язык типизируется горой кода в которой не могут разобраться даже авторы.
3. Изза итсутствия аналога зависимых свойств в коде очень трудно контроллировать правильность последовательности вычислений. Это приводит к огромным трудностями в отладке багов связанных с невердой последовательностью вычислений.
3. С расширяемостью синтаксиса у немерла проблемы. Тот же query comprehension (синтаксис Linq) пришлось засовывать в кавычки. На уровне типов вообще хрен расширишь.
4. Есть проблемы с интеграцией с IDE.

Подход же использованый с Nitra позволяет нам решить все проблемыиразом и за одно продемонстрировать все преимущества DSL.

Что касается кажущейся, на первый взгляд, сложности, то это ложное ощущение. Если сравнить это решение с рукапашным, то окажется, что оно очь компактное и ничего лишнего в нем нет. Одна только поддержка в IDE выльется в гору кода. А тут она будет из коробки. Ну, а чем боллее сложен будет язык, тем очевиднее будет разница.

Но пожалуй самое важное это, что DSL–подход позволяет держать сложность в рамках разумного и легко менять реализацию, если это необходимо.

K>Может, сначала саму Немерлю-2 стоило начать с чистого листа, реализовать почти так же, но с учётом всего того, что вам нехватало?


Поверь, это не намного проще получилось бы и большой шанс получить большинство проблем немерла. Причину некоторых его проблем я понял только работая над Нитрой.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: [Nitra] Пример простого языка вычисляющего выражения
От: Kolesiki  
Дата: 06.12.15 19:14
Оценка:
Здравствуйте, VladD2, Вы писали:

Влад, я даже осознать не могу, насколько Нитра мощнее Немерли (ибо не знаю трудоёмкости и насколько Нитра упростит жизнь), но попробую снова высказаться за Немерле-1.5 что ли

VD>У немерла куча проблем:

VD>1. Его иерархия типов захардкожена и не расширяется.

При переписывании это легко устранить, опираясь только на внутренние типы .NET; Касательно вообще типов, мы все знаем, что .NET "объектно натянут" по самые хэдеры, т.е. "язык под .НЕТ" уже должен быть ООП, с классами и методами.
Это я к тому, что нет смысла закладываться на "язык черепашки с вкраплениями SQL", т.к. это ортогональное дотнету решение. А раз мы в рамках ООП, то и "конструктор языков" сразу можно упростить до понятия "у нас есть классы и их члены" — далее можно плясать от этой модели.

VD>2. Сам язык типизируется горой кода в которой не могут разобраться даже авторы.


Очевидно, что студенческий код рано или поздно пришлось бы выкинуть, но для этого вы и нужны — люди, которые знают старое и видят, как правильно создавать новое!

VD>3. Из-за итсутствия аналога зависимых свойств в коде очень трудно контроллировать правильность последовательности вычислений. Это приводит к огромным трудностями в отладке багов связанных с невердой последовательностью вычислений.


эээ.... даже не понял, про что это. Думаю, Туьукду(Nemerle ) не первый компилятор, построенный без них — другие же как-то справляются?

VD>3. С расширяемостью синтаксиса у немерла проблемы. Тот же query comprehension (синтаксис Linq) пришлось засовывать в кавычки. На уровне типов вообще хрен расширишь.


Ну вот! Это и есть точка улучшения. В смысле взять то, что есть и расширить.

VD>4. Есть проблемы с интеграцией с IDE.


Утилитарные мелочи, MS и сам признаёт, что нагородил фигни (потому и стал запиливать Roslyn). Опять же, кто сказал, что нужна именно VS? Сред для расширения навалом, можно и своё написать.

Но я в целом что хотел сказать: пилить "конструктор" и пилить "расширяемый язык" — две задачи, отличающиеся на порядок (понятно, последняя проще). Потому мне и кажется, что запиливание Немерли-1.5 "с нуля" (и постепенной переброской полезностей из Немерли-1) было бы проще — вы знаете слабые места и знаете, как это решить. Конструктор сразу надевает вам на голову "шлем всемогутерства", в котором... нет дырок! Вы не знаете, куда и как люди захотят расширять язык. Или наоборот — бросите кучу сил в направлении, которое окажется невостребованным.
По ср. с конструктором, Немерля-1.5 была бы проще, т.к. опиралась бы на устоявшуюся схему: классы, члены классов, точки расширения и макросы.

VD>Поверь, это не намного проще получилось бы и большой шанс получить большинство проблем немерла. Причину некоторых его проблем я понял только работая над Нитрой.


Никто ж не просит тащить старые проблемы в новый язык! Зато Немерля-1.5 — это почти вся идеология старой Немерли (по которой мы уже имеем багаж понятных доков) минус ограничения студенческой реализации.
Надеюсь, я донёс свою мысль. Время у вас может и не самый решающий фактор, но даже Немерля-1.5 отняла бы приличную долю усилий, которая за год уже дала бы ощутимый результат. Ощутимый нами, людьми извне — вот что важно.
Отредактировано 06.12.2015 19:35 Kolesiki . Предыдущая версия .
Re[4]: [Nitra] Пример простого языка вычисляющего выражения
От: novitk США  
Дата: 08.12.15 20:34
Оценка:
Здравствуйте, VladD2, Вы писали:

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


N>>Мало? Я уверен, что реализация этой задачи на скажем ANTLR/Скале будет не больше.


VD>Я рад за твою уверенность. Сделай, сравним.


Думал оно будет очевидно. Впрочем мне не сложно набросать полсотни строчек.

[scala]
import scala.util.parsing.combinator._

object Main extends JavaTokenParsers {
class Expr;
case class NumberExpr(num: Int) extends Expr;
case class MultExpr(expr: Expr) extends Expr;
case class DivExpr(expr: Expr) extends Expr;
case class AddExpr(expr: Expr) extends Expr;
case class SubtractExpr(expr: Expr) extends Expr;
case class VarExpr(name: String) extends Expr;
case class OpExpr(expr: Expr, subExprs: List[Expr]) extends Expr;
class VarDef(val name: String, val expr: Expr)

class EvalContext(val vars: Map[String, VarDef]) {
val results = scala.collection.mutable.Map[String, Int]()
def evalVar(name: String): Int =
{
if (!results.contains(name))
results(name) = evalExpr(vars(name).expr)
results(name)
}
def evalExpr(expr: Expr): Int = expr match {
case NumberExpr(num) => num
case VarExpr(name: String) => evalVar(name)
case OpExpr(expr, subExprs: List[Expr]) =>
subExprs.foldLeft(evalExpr(expr)) {
(a, subExpr) =>
subExpr match {
case MultExpr(exprInner) => a * evalExpr(exprInner)
case DivExpr(exprInner) => a / evalExpr(exprInner)
case AddExpr(exprInner) => a + evalExpr(exprInner)
case SubtractExpr(exprInner) => a — evalExpr(exprInner)
}
}
}
}

def makeOpExpr(p: ~[Expr, List[Expr]]): Expr = p match {
case f ~ Nil => f
case f ~ l => OpExpr(f, l)
}
def factor: Parser[Expr] = wholeNumber ^^ (s => NumberExpr(s.toInt)) | ident ^^ VarExpr | "(" ~> expr <~ ")"
def term: Parser[Expr] = (factor ~ rep("*" ~> factor ^^ MultExpr | "/" ~> factor ^^ DivExpr)) ^^ makeOpExpr
def expr: Parser[Expr] = (term ~ rep("+" ~> term ^^ AddExpr | "-" ~> term ^^ SubtractExpr)) ^^ makeOpExpr
def varDef: Parser[VarDef] = ("var" ~> ident) ~ ("=" ~> expr) ^^ { case n ~ e => new VarDef(n, e) }
def varDefParseAll(in: CharSequence): ParseResult[VarDef] = parseAll(varDef, in)

def main(args: Array[String]) = {
val vars: Map[String, VarDef] = Map(args.toSeq.map(varDefParseAll).filter(_.successful).map {
pr =>
{
val vd = pr.get
vd.name -> vd
}
}: _*)
val evalCtx = new EvalContext(vars)
println(evalCtx.evalVar("b"))
}
}
[/scala]
Отредактировано 09.12.2015 21:28 VladD2 . Предыдущая версия .
Re[5]: [Nitra] Пример простого языка вычисляющего выражения
От: WolfHound  
Дата: 08.12.15 22:58
Оценка:
Здравствуйте, novitk, Вы писали:

N>Думал оно будет очевидно. Впрочем мне не сложно набросать полсотни строчек.

1)Это ужос:
  def makeOpExpr(p: ~[Expr, List[Expr]]): Expr = p match {
    case f ~ Nil => f
    case f ~ l => OpExpr(f, l)
  }
  def factor: Parser[Expr] = wholeNumber ^^ (s => NumberExpr(s.toInt)) | ident ^^ VarExpr | "(" ~> expr <~ ")"
  def term: Parser[Expr] = (factor ~ rep("*" ~> factor ^^ MultExpr | "/" ~> factor ^^ DivExpr)) ^^ makeOpExpr
  def expr: Parser[Expr] = (term ~ rep("+" ~> term ^^ AddExpr | "-" ~> term ^^ SubtractExpr)) ^^ makeOpExpr
  def varDef: Parser[VarDef] = ("var" ~> ident) ~ ("=" ~> expr) ^^ { case n ~ e => new VarDef(n, e) }
  def varDefParseAll(in: CharSequence): ParseResult[VarDef] = parseAll(varDef, in)

2)Поддержка ИДЕ где?
3)Вывод сообщений об ошибках парсинга где?
4)Вывод сообщений о неизвестных переменных где?
5)Проверка на зацикливание вычислений где?
6)Расширяемость где?
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[6]: [Nitra] Пример простого языка вычисляющего выражения
От: novitk США  
Дата: 08.12.15 23:26
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>1)Это ужос:

Это обычные, известные даже детям, парсековские комбинаторы на Скале. Что в них ужасного?

WH>2)Поддержка ИДЕ где?

У меня нет Нитры. Что такого волшебного делает поддержка ИДЕ у вас для этого примера? Раскрашивает текст?

WH>3)Вывод сообщений об ошибках парсинга где?

WH>4)Вывод сообщений о неизвестных переменных где?
WH>5)Проверка на зацикливание вычислений где?
Ты ведь отлично знаешь что это три строчки. Зачем придуриваешься?

WH>6)Расширяемость где?

Расширяемость куда? Если пример неудачный, сделайте наконец нормальный, который покажет мне нафиг мне сдался все эти ваши мэппинги и прочие прелести. Пока оно не видно.
Re[7]: [Nitra] Пример простого языка вычисляющего выражения
От: WolfHound  
Дата: 08.12.15 23:53
Оценка:
Здравствуйте, novitk, Вы писали:

N>Это обычные, известные даже детям, парсековские комбинаторы на Скале. Что в них ужасного?

Примерно всё. И про парсер комбинаторы я знаю ещё с тех времен, когда скалы в проекте не было.

N>У меня нет Нитры. Что такого волшебного делает поддержка ИДЕ у вас для этого примера? Раскрашивает текст?

Раскрашивает текст.
Подсвечивает ошибки.
Работает автодополнение.

WH>>3)Вывод сообщений об ошибках парсинга где?

WH>>4)Вывод сообщений о неизвестных переменных где?
WH>>5)Проверка на зацикливание вычислений где?
N>Ты ведь отлично знаешь что это три строчки. Зачем придуриваешься?
Я отлично знаю, что ты это не сделал.

WH>>6)Расширяемость где?

N>Расширяемость куда?
Куда угодно.
Попробуй добавить новый оператор с новым приоритетом так чтобы твой код не трогать.
Например, он лежит в ДЛЛ, исходники которой ты менять не можешь.

N>Если пример неудачный, сделайте наконец нормальный, который покажет мне нафиг мне сдался все эти ваши мэппинги и прочие прелести. Пока оно не видно.

Сначала этот пример повтори.
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[8]: [Nitra] Пример простого языка вычисляющего выражения
От: _NN_ www.nemerleweb.com
Дата: 09.12.15 11:23
Оценка: +2
Здравствуйте, WolfHound, Вы писали:

N>>Если пример неудачный, сделайте наконец нормальный, который покажет мне нафиг мне сдался все эти ваши мэппинги и прочие прелести. Пока оно не видно.

WH>Сначала этот пример повтори.

Мне кажется , что нужны как раз примеры , где все эти достоинства показываются.
В простом примере вроде этого конечно трудно осознать.

Скажем, можно сделать два проекта , чтобы показать расширяемость.
Вместе с этим снимки экранов , где показана подсветка , сообщения об ошибках и автодополнения.

Тогда будет легче объяснить почему Nitra делает намного больше чем просто парсит.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[5]: [Nitra] Пример простого языка вычисляющего выражения
От: _NN_ www.nemerleweb.com
Дата: 09.12.15 11:25
Оценка:
Здравствуйте, novitk, Вы писали:

N>Думал оно будет очевидно. Впрочем мне не сложно набросать полсотни строчек.


Можно объяснить что означает "~" в ~[Expr] и в f ~ Nil в Scala ?
  def makeOpExpr(p: ~[Expr, List[Expr]]): Expr = p match {
    case f ~ Nil => f
    case f ~ l => OpExpr(f, l)
  }
http://rsdn.nemerleweb.com
http://nemerleweb.com
Отредактировано 09.12.2015 17:52 VladD2 . Предыдущая версия .
Re[6]: [Nitra] Пример простого языка вычисляющего выражения
От: novitk США  
Дата: 09.12.15 15:16
Оценка:
Здравствуйте, _NN_, Вы писали:

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


N>>Думал оно будет очевидно. Впрочем мне не сложно набросать полсотни строчек.


_NN>Можно объяснить что означает "~" в ~[Expr] и в f ~ Nil в Scala ?

_NN>
_NN>  def makeOpExpr(p: ~[Expr, List[Expr]]): Expr = p match {
_NN>    case f ~ Nil => f
_NN>    case f ~ l => OpExpr(f, l)
_NN>  }
_NN>


def ~[U](q: ⇒ Parser[U]): Parser[~[T, U]]
A parser combinator for sequential composition.

Грубо говоря есть две сущности: "def ~" и "case class ~". Первое возвращает парсер для второго. Второе по сути обычный двойной тупл, ака пара. Код наверху оптимизация сокращающая уровень вложености.
Re[9]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.12.15 17:45
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Скажем, можно сделать два проекта , чтобы показать расширяемость.


Ты объем работы по ручной реализации не тривиального языка представляешь?

Вот доделаем типизацию Шарпа и Немерла, можно будет сравнить первый с кодом Розлина, а второй с кодом Немерлового компилятора. Заранее могу сказать, что разница будет огромной.

_NN>Вместе с этим снимки экранов , где показана подсветка , сообщения об ошибках и автодополнения.


Может проще прочесть закрепленную в топе статью:
http://rsdn.ru/forum/nemerle/6217398.flat
Автор: VladD2
Дата: 17.10.15

?

Там в конце скриншоты есть.

_NN>Тогда будет легче объяснить почему Nitra делает намного больше чем просто парсит.


Тут вопрос в том пробовал ли человек что-то сам сделать или нет. Если упереться рогом как novitk, то можно постараться не заметить все что угодно.

Если же ты попытался реализовать хотя бы самый маленький язычок, но целиком (с выводом вменяемой диагностики, с реализацией поддержки IDE, с заботой о поддержке и развитии), то объяснять особо и не придется ничего.

С другой стороны в чем-то он прав. Маппинг напрягает. Но предложение жить на парстри (без отдельного АСТ-а) на практике не канает.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.12.15 17:51
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Раскрашивает текст.


Не просто раскрашивает, а раскрашивает в зависимости от типов символов. В данном случае ссылки на переменные и переменные можно сделать отдельными цветами.

WH>Подсвечивает ошибки.


Во время правки кода, что немаловажно.

WH>Работает автодополнение.


Еще навигация к определению (к переменным по ссылкам на них) и поиск вхождений.

Так же автоматически поддерживается работа в проекте. Переменные можно разнести по отдельным файлам проекта и все будет работать.

Ну, и главное что ты забыл — это автоматическое восстановление после ошибок. Уверен, что в сколовском варианте этого нет и в помине.

N>>Ты ведь отлично знаешь что это три строчки. Зачем придуриваешься?

WH>Я отлично знаю, что ты это не сделал.

Надеюсь мы товарища дожмем и он сделает свой ужастик по истине монструозным .

WH>>>6)Расширяемость где?

N>>Расширяемость куда?
WH>Куда угодно.
WH>Попробуй добавить новый оператор с новым приоритетом так чтобы твой код не трогать.
WH>Например, он лежит в ДЛЛ, исходники которой ты менять не можешь.

Ага. Это приакольно. У них даже приоритетов операторов нет. Так что вставка любого нового правила создающего новый приоритет приведет к переписыванию грамматики.

N>>Если пример неудачный, сделайте наконец нормальный, который покажет мне нафиг мне сдался все эти ваши мэппинги и прочие прелести. Пока оно не видно.

WH>Сначала этот пример повтори.

+1

Но придумать удачный пример, чтобы он был не примитивным, но и не очень сложным тоже надо.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.12.15 18:12
Оценка:
Здравствуйте, novitk, Вы писали:

N>Ты ведь отлично знаешь что это три строчки. Зачем придуриваешься?


Придуриваешься, как раз, ты. Раз три строчки, приведи их, а не придуривайся.

За одно скорми своему решению следующие примеры:
1.
var a = b;
var b = a;


2.
var a = b;
var b = c;


3.
var a = b // нет закрывающей ";"
var b = 1;


и расскажи нам, что произошло.

Далее можно добавить в язык еще один оператор и посмотреть на то как легко это сделать. У нас это делается без изменения исходного примера.
syntax module MyExtension
{
  extend syntax Expression
  {
  precedence Sum:
  precedence Pow: // добавляем новую группу приоритетов большую чем Sum и меньшую чем Mul
    | Power = Expression "^" Expression
  precedence Mul: 
  }
}

ast Power : Binary { Result = Math.Pow(Expression1.Result, Expression2.Result); }

map syntax MyExtension.Expression -> Expression
{
  | Power
    {
      Expression1 -> Expression1;
      Expression2 -> Expression2;
    }
}


Можно поместить это дело в тот же проект или в отдельную dll. Таким образом можно расширять другие языки. Например, прикрутить к Скале человеческий синтаксис для грамматик, а не этого уродца, что ты показал.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[7]: [Nitra] Пример простого языка вычисляющего выражения
От: _NN_ www.nemerleweb.com
Дата: 09.12.15 19:13
Оценка:
Здравствуйте, novitk, Вы писали:

N>

N>def ~[U](q: ⇒ Parser[U]): Parser[~[T, U]]
N>A parser combinator for sequential composition.

N>Грубо говоря есть две сущности: "def ~" и "case class ~". Первое возвращает парсер для второго. Второе по сути обычный двойной тупл, ака пара. Код наверху оптимизация сокращающая уровень вложености.

Ясно.
Жаль нельзя указывать имена p: ~[left Expr, right List[Expr]] , т.е. нужно понимать контекст что есть Expr , а что List[Expr].
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[10]: [Nitra] Пример простого языка вычисляющего выражения
От: _NN_ www.nemerleweb.com
Дата: 09.12.15 19:22
Оценка: +2
Здравствуйте, VladD2, Вы писали:

VD>Ты объем работы по ручной реализации не тривиального языка представляешь?

VD>Вот доделаем типизацию Шарпа и Немерла, можно будет сравнить первый с кодом Розлина, а второй с кодом Немерлового компилятора. Заранее могу сказать, что разница будет огромной.
Это конечно интересно , однако людям нужны простые примеры.
Да даже в этом примере можно вынести операции 'Sum' и прочее в отдельный файл и показать расширяемость.

_NN>>Вместе с этим снимки экранов , где показана подсветка , сообщения об ошибках и автодополнения.


VD>Может проще прочесть закрепленную в топе статью:

VD>http://rsdn.ru/forum/nemerle/6217398.flat
Автор: VladD2
Дата: 17.10.15

VD>?
Я имел ввиду это в студии.
Желательно даже с анимацией.

_NN>>Тогда будет легче объяснить почему Nitra делает намного больше чем просто парсит.


VD>Тут вопрос в том пробовал ли человек что-то сам сделать или нет. Если упереться рогом как novitk, то можно постараться не заметить все что угодно.

Человек пытается понять почему он должен учить что-то новое.
Мне кажется , что не хватает сравнений которые покажут, что Nitra таки да умеет то, что другим не под силу или сложно реализовать.

VD>Если же ты попытался реализовать хотя бы самый маленький язычок, но целиком (с выводом вменяемой диагностики, с реализацией поддержки IDE, с заботой о поддержке и развитии), то объяснять особо и не придется ничего.

С таким подходом боюсь ничего не выйдет.
Можно сто раз рассказывать, что Nemerle лучше чем C# , однако человек выберет C# потому что не видит всей картины.
А просто тратить время на проверки ни у кого нет.

Нужно всё разжёвывать и разложить по полочкам.
Тогда и вопросы будут по существу , а не в стиле "чем это лучше".

VD>С другой стороны в чем-то он прав. Маппинг напрягает. Но предложение жить на парстри (без отдельного АСТ-а) на практике не канает.
http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[8]: [Nitra] Пример простого языка вычисляющего выражения
От: novitk США  
Дата: 09.12.15 19:45
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Придуриваешься, как раз, ты. Раз три строчки, приведи их, а не придуривайся.

VD>За одно скорми своему решению следующие примеры:

твои примеры, расширяемость операторов и прочие шлюхи.
  Скрытый текст
import scala.util.parsing.combinator._

object Main extends JavaTokenParsers {
  class Expr;
  class BinOp(val evalF: (Int, Int) => Int, val expr: Expr);
  def mkBinOp = (new BinOp(_, _)).curried
  val binOpGroups = Seq(
      Seq("*" -> mkBinOp(_*_), "/" -> mkBinOp(_/_)),
      Seq("^^" -> mkBinOp(Math.pow(_, _).toInt)),
      Seq("+" -> mkBinOp(_+_), "+" -> mkBinOp(_+_)))

  case class NumberExpr(num: Int) extends Expr;
  case class VarExpr(name: String) extends Expr;
  case class OpExpr(expr: Expr, subExprs: List[BinOp]) extends Expr;
  class VarDef(val name: String, val expr: Expr)
  
  def makeOpExpr(p: ~[Expr, List[BinOp]]): Expr = p match {
    case f ~ Nil => f
    case f ~ l => OpExpr(f, l)
  }
  def binOpGroupParser(termAbove: Parser[Expr], opGroup: Seq[(String, Expr => BinOp)]): Parser[Expr] = {
    val opGroupParsers = opGroup.map { case (opCode, constr) => opCode ~> termAbove ^^ constr }.reduce(_|_)
    (termAbove ~ rep(opGroupParsers)) ^^ makeOpExpr
  }  

  def factor: Parser[Expr] = wholeNumber ^^ (s => NumberExpr(s.toInt)) | ident ^^ VarExpr | "(" ~> expr <~ ")"
  def expr: Parser[Expr] = binOpGroups.foldLeft(factor)(binOpGroupParser(_, _))
  def varDef: Parser[VarDef] = ("var" ~> ident) ~ ("=" ~> expr <~ ";") ^^ { case n ~ e => new VarDef(n, e) }
  def varDefParseAll(in: CharSequence): ParseResult[VarDef] = parseAll(varDef, in)

  class EvalContext(val vars: Map[String, VarDef]) {
    val results = scala.collection.mutable.Map[String, Int]()
    val evalStack = scala.collection.mutable.Stack[String]()
    def evalVar(name: String): Int =
      {
        if (!results.contains(name)) {
          if (evalStack.contains(name))
            throw new RuntimeException(s"looped $name")
          if (!vars.contains(name))
            throw new RuntimeException(s"undefined $name")
          evalStack.push(name)
          results(name) = evalExpr(vars(name).expr)
        }
        results(name)
      }
    def evalExpr(expr: Expr): Int = expr match {
      case NumberExpr(num) => num
      case VarExpr(name) => evalVar(name)
      case OpExpr(expr, subExprs) => subExprs.foldLeft(evalExpr(expr)) {
        (a, subExpr: BinOp) => subExpr.evalF(a, evalExpr(subExpr.expr))
        }
    }
  }

  def main(args: Array[String]) = {
    val vars: Map[String, VarDef] = Map(args.toSeq.map(varDefParseAll).map {
      pr =>
        {
          val vd = pr.get
          vd.name -> vd
        }
    }: _*)
    val evalCtx = new EvalContext(vars)
    println(evalCtx.evalVar("b"))
  }
}

VD>Например, прикрутить к Скале человеческий синтаксис для грамматик, а не этого уродца, что ты показал.
"Уродец" это субъективно, а вот 50 строчек, Карл, нет.
Re[8]: [Nitra] Пример простого языка вычисляющего выражения
От: novitk США  
Дата: 09.12.15 19:54
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Сначала этот пример повтори.


В соседней ветке.

По остальным пунктам поясню. Я это фрагмент сделал лишь потому, что Влад усомнился в возможности сделать его пример сравнимым количеством кода без Нитры, а не потому что я тебя не уважаю или считаю что Нитра не нужна. Все, горячится не надо.
Re[9]: [Nitra] Пример простого языка вычисляющего выражения
От: WolfHound  
Дата: 09.12.15 20:05
Оценка: +1
Здравствуйте, novitk, Вы писали:

WH>>Сначала этот пример повтори.

N>В соседней ветке.
Часть замечаний ты замазал.
Но большая часть осталась.

А главное код стало понять ещё сложнее.

N>По остальным пунктам поясню. Я это фрагмент сделал лишь потому, что Влад усомнился в возможности сделать его пример сравнимым количеством кода без Нитры, а не потому что я тебя не уважаю или считаю что Нитра не нужна. Все, горячится не надо.

Если тут кто и горячится то это ты.
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[11]: [Nitra] Пример простого языка вычисляющего выражения
От: novitk США  
Дата: 09.12.15 20:17
Оценка:
Здравствуйте, _NN_, Вы писали:

VD>>Тут вопрос в том пробовал ли человек что-то сам сделать или нет. Если упереться рогом как novitk, то можно постараться не заметить все что угодно.

_NN>Человек пытается понять почему он должен учить что-то новое.
_NN>Мне кажется , что не хватает сравнений которые покажут, что Nitra таки да умеет то, что другим не под силу или сложно реализовать.

Не совсем так. Я как раз неплохо понимаю, что они делают и как. Просто они претендуют на некоторую универсальность и революционность, которой я не наблюдаю. А как конструктор для Немерле 2 или как оно там теперь называется, проект имеет право на жизнь. На мой вкус переборщили с DSL-ами, но я списываю это на их фанатизм и мой субъективизм.
Re[10]: [Nitra] Пример простого языка вычисляющего выражения
От: novitk США  
Дата: 09.12.15 20:44
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>Часть замечаний ты замазал.

Не "замазал", а "исправил". Что за хамская манера?

WH>Но большая часть осталась.

Перечисли. Типизации в этом примере нет. Раскраска и прочая гуйня выведет размер за разумные рамки, но что тут интересного?

WH>А главное код стало понять ещё сложнее.

Перестань. Любой ФПишник сможет поддерживать этот код, тем более что у него сейчас есть нормальная IDE.
Re[12]: [Nitra] Пример простого языка вычисляющего выражения
От: _NN_ www.nemerleweb.com
Дата: 09.12.15 20:45
Оценка:
Здравствуйте, novitk, Вы писали:


N>Не совсем так. Я как раз неплохо понимаю, что они делают и как. Просто они претендуют на некоторую универсальность и революционность, которой я не наблюдаю. А как конструктор для Немерле 2 или как оно там теперь называется, проект имеет право на жизнь. На мой вкус переборщили с DSL-ами, но я списываю это на их фанатизм и мой субъективизм.


А как хочется наблюдать ?
Неужели визуализатора и работы в VS недостаточно ?



http://rsdn.nemerleweb.com
http://nemerleweb.com
Re[4]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.12.15 21:05
Оценка:
Здравствуйте, Kolesiki, Вы писали:

K>1. После ПЕГ пугает введение излишеств вроде "keyword regex". Я боюсь, что подобный "неявный помощник" заложит таймбомбу, что потом не разберёшься, почему один токен работает, а другой — нет.


Для "keyword regex" можно ввести дефолтную реализацию, которая будет использоваться, если он не задан явно.
В общем, учтем критику.

K>2. "...по_этому идентификатор разделяется на правило объявляющее имя символа (в данном случае "Name") и правило описывающее ссылку на имя" — когда и зачем это применять?


Для связывания имен. Нитра же не просто парсер. Она предоставляет законченное решение по типизации. Этот пример довольно простой, но и в нем есть объявление переменных и ссылки на них. Вот Name надо использовать там где вводится имя переменной, а Reference там где на это имя ссылаются из выражения.

K>Почему я не могу просто поставить какой-нибудь крючочек и конструктор сам догадается что нужно делать?


Нитре нужно показать где идентификатор используется как имя, а где как ссылка на него.

Почти любой компьютерный язык — это нечто вроде реляционной БД или объектной модели где есть записи/объекты и ссылки на них.

K>3. Каша из keyword, regex, token, syntax — у меня уже голова кругом.


Вот это уже не конструктивная критика. Любой серьезный генератор парсеров отличает лексерные правила (лексемы или токены) и рекурсивные. У нас есть всего лишь одно расширение лексемы могут быть token или regex. Причем regex — это оптимизация позволяющая уменьшить расход памяти и ускорить парсин. Освоить это можно за пять минут. "keyword" — это наверно от keyword regex. Это как раз средство упростить работу с PEG-ом. Вместо того чтобы в каждом правиле расставлять пробельные подправила нужно всего лишь один раз описать keyword regex и Нитра сама расставит пробельные правила во всей грамматике (где не указано, что этого не нужно делать). Я уже понял, что это дело по началу пугает. Мы спрячем это дело, так чтобы его нужно было использовать только в особых случаях. Все равно он в 99% случаев получается одинаковым.

K>Далее идёт отдельно три объявления syntax, а потом внутри syntax Expression ещё куча подправил. Как всё это разруливать?


Прочесть описание.

K>ПЕГ предлагает простую модель: терминалы и правила. Можно в Нитре оставаться в рамках модели ПЕГ?


ПЕГ довольно убог и ПЕГ это не более чем нотация. Алгоритм там Пакратом называется. Для применения в реальном мире его пришлось сильно докрутить. Для того же восстановления после ошибок нужно рабтать на уровне лексем.

K>4. Ладно, синтаксис описали — зачем ещё один Sample-Language.nitra? (причём в примере с NitraLanguage.nitra там ещё и цвета зачем-то захардкожены) Вроде б мы уже имеем "подсветку и претипринт", так мы ещё раз приседаем для какой-то другой утилиты?


А как ты предлагаешь сказать Нитре, что, скажем, переменные и ссылки на них нужно отдельными цветами выделять?

Сам language нужен чтобы определить данные для плагина к IDE и указать какое расширение будут иметь файлы твоего языка. Если ты расширяешь язык, то language вообще не обязателен. Описывать цвета тоже не обязательно. Но это же пример! Он демонстрирует разные аспекты.

K>5. Далее АСТ: вообще весь файл ни о чём, кроме знакомых строк, которые я уже как бы описал в SampleSyntax:

K>"ast Sum : Binary { Result = Expression1.Result + Expression2.Result; }"

Как раз этот весь файл о самом важном. Там декларативно описана семантика языка. Этот синтаксис не так важен, на самом деле. А семантика — это соль языка. Куда важнее какого-то там сложения то, что объявляется область видимости и в ней декларируются переменные. В более серьезном языке и области видимости будут намного сложнее и деклараций больше.

Весь смысл примера как раз дать примитивный пример областей видимости и деклараций.

K>Все эти ContainingTable, stage 1, Reference — это, вероятно, какие-то чертовски важные детали, но которые со стороны выглядят как лапша, торчащая из коллайдера.


Ну, дык может стоит сначала прочитать описание
Автор(ы):
?

K>У нас простой язык: список присвоений, переменные, арифм.выражения, числа, операторы, скобки. Они структурированы в дерево. Всё. Но никакого дерева в Sample-Ast.nitra я не вижу — тону в дебрях.


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

K>6. Отображение — тут выглядит всё просто, но опять же — из-за простоты и дублирования кажется, будто делаешь одну и ту же работу трижды.


Вот тут согласен. Более того в сложных случаях отображение еще и не так очевидно как кажется на первый взгляд. Возможно стоит покапать в сторону выражения синтаксиса через АСТ. Но это серьезное исследование которое придется оставить на будущее.

K>Здесь всё понятно, кроме одного — что, все эти пляски с АСТ создавались из-за одной единственной фигни под названием partial class? Повторюсь, мне кажется можно взять простое дерево разбора и запрашивать у него инфу через хелперы (которые, никто не запрещает, могут построить некий внутренний АСТ для ускорения работы).


Не "из-за", а "в том числе из-за". За АСТ много аргументов. Просто для их понимания опять таки требуется погружение в детали.

K>7. Ну и довершает пасквиль на могиле моего понимания "последний штрих" обвязочного кода, потому что мы и так уже предостаточно наописывали для АСТ, чтоб теперь ещё и приседать вокруг него.


Этот код не айс и позже мы его задслим. Но он необходим, если мы хотим рабоать не с одним файлом, а с целым проектом (где таких файлов может быть много). Опять таки это пример. Нам важно показать что нужно сделать для поддержки реального языка. Этот код будет почти идентичен как для вот такой малютки, так и для огромного C#-а. В нем нужно связать асты всех файлов в единое пространство вычислений.

K>Громоздко, загромождено деталями, непонятно — вот так для меня выглядит ещё простой! язык арифметики. Видимо, Нитра будет инструментом для ооочень избранных головастиков, я такой "упрощающий инструмент" едва ли постигну.


Ну, вот тут рядом novitk попытался реализовать этот же простой пример на самом продвинутом в мире (по его мнению) языке — Скале. Можешь поглядеть как у него все просто получилось. И это при условии, что он не реализовал тучу всего, что предоставляет Нитра из коробки за бесплатно.

Будем шлифовать. Что-то можно сделать по умолчанию. Что-то запихнуть в библиотеки. С маппингом нужно серьезно подумать. Но в целом, скорее всего, сказывается просто непонимание синтаксиса и того, что получается в результате. Попробуй сначала прочесть описание, а потом взглянуть еще раз.

Ну, и нужно понимать, что Нитра не создавалась для подобных простых языков. Она рассчитана на создание расширяемых C# и Nemerle 2. Код их компиляторов сложнее в тысячи раз, а поддержка IDE захардкожена и содержит много букв. Тут же ты предоставляешь описание и получаешь все в автомате (и поддержку IDE, и связывание/резолв имен, и произвольные расчеты независящие от порядка их описания).

Я понимаю, что это трудно оценить по достоинству без погружения. Но оно того стоит. Так же надо понимать, что это примеры создания языка с нуля. А для реального использования это не особо нужно. Большинству людей будет нужно создать мелкое расширение с нуля или тупо взять за основу язык и на его основе написать свой, похожий.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: [Nitra] Пример простого языка вычисляющего выражения
От: xvost Германия http://www.jetbrains.com/company/people/Pasynkov_Eugene.html
Дата: 09.12.15 21:08
Оценка:
Здравствуйте, VladD2, Вы писали:

А когда парсер С# в РеШарпере будет написан на Nitra вместо PsiGen'а?
С уважением, Евгений
JetBrains, Inc. "Develop with pleasure!"
Re[13]: [Nitra] Пример простого языка вычисляющего выражения
От: novitk США  
Дата: 09.12.15 21:19
Оценка: :)
Здравствуйте, _NN_, Вы писали:

_NN>А как хочется наблюдать ?

_NN>Неужели визуализатора и работы в VS недостаточно ?

Визуализатор? Полезная штука для отладки. В ANTLR он тоже есть, но это всего лишь один из аспектов. Лично я не готов платить за его наличие громоздкостью комбайна сделанного исключительно под C#-подобный язык для крошечного ДСЛ в нашем примере.

Обычный unix-way — дайте мне лучшие компоненты, я сам соберу из них, что мне нужно.
Re[2]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.12.15 21:37
Оценка:
Здравствуйте, xvost, Вы писали:

X>А когда парсер С# в РеШарпере будет написан на Nitra вместо PsiGen'а?


Это не от нас зависит, как ты понимаешь. Он ведь и так работает и каши не просит. Вокруг все скептики. Те кто исследователи по натуре переключились на Котлин. Найтра никому не интересна. Путь даже код Решарпера сжался бы в 10 раз будучи переписанным на Найтре и стал бы в 100 раз понятнее и стабильнее, но никто на это не решится.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.12.15 21:51
Оценка:
Здравствуйте, novitk, Вы писали:

N>твои примеры, расширяемость операторов и прочие шлюхи.


Шлюх не заметил. В прочем, теперь там черт ногу сломит.

Что там у тебя с восстановлением после ошибок? Как организовать подсветку и переходы к определению переменных в редакторе? Сколько там еще нужно "строчек"?

Обработка ошибок на исключениях — это вообще прелесно! О востановлении после ошибок можно сразу забыть. Еще нужно незабыть написать код обработки этих исключений и вывода людям в человеческой форме.

N>"Уродец" это субъективно, а вот 50 строчек, Карл, нет.


Уродец это объективно. Как бы язык там можно разглядеть только очень напрягшись. То что ты его упаковал в 50 строк не исправляет того факта, что это 50 строк говнокода, которые при росте сложности языка превратится в вечную попоболь. Да и более 50 строк там уже.

Еще прибавь сюда тормоза, так тавои комбинаторы это треш с точки зрения производительности.

В итоге получается унылое говно которое совершенно невозможно читать, сложно поддерживать, которое тормозит и кидается исключениями вместо диагностики. И это на примитивном примере.

На Нитре тоже можно написать в подобном стиле. Не в том смысле что можно получить такой говнокод, а в том смысле, что можно обойтись без АСТ, символов, и зависимых вычислений. В Нитре есть rule-методы, которые так же позовляют размазать код вычислегний по дереву разбора (см., например, пример калькулятора
Автор: VladD2
Дата: 09.12.15
). Суть примера в том, чтобы показать как и для чего используются средства Нитры. Конечно, если хочешь увидеть выигрыш от применения Нитры, лучше смотреть код типизации Шарпа или самой Нитры. Именно ради таких задач и создавались все эти навороты.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[9]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.12.15 22:00
Оценка:
Здравствуйте, novitk, Вы писали:

N>По остальным пунктам поясню. Я это фрагмент сделал лишь потому, что Влад усомнился в возможности сделать его пример сравнимым количеством кода без Нитры, а не потому что я тебя не уважаю или считаю что Нитра не нужна. Все, горячится не надо.


Так ты до сих пор и не убидел. Я же тебе сразу сказал, что у тебя нет:
1. Восстановления после ошибок.
2. Нормальной диагностики ошибок.
3. Поддержки IDE (навигация, подсветка, реалтаймная подсветка ошибок).
4. Да что уж там? Просто читабельного решения нет. Ты этим примером людей Скалы реально отпугнул.

Это уже будет не 50 и даже не 500 строк. Это ты вообще не потянешь. А тут из коробки. За здорово живешь.

Ну, и комбинаторы твои — тормоза. В Скле вроде как появлися вариант на макрах. Это аналог нашего PegGrammar-а
Автор(ы): Чистяков Владислав Юрьевич
Дата: 07.06.2011
Макрос PegGrammar – это макрос Nemerle, позволяющий добавлять в приложения парсеры, описываемые в нотации PEG.
, только с более убогим синтаксисом (патамушта расширение синтаксиса нам не нужно (ц)). Попробуй на нем, что ли, а не на этих убоги комбинаторых.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[11]: [Nitra] Пример простого языка вычисляющего выражения
От: WolfHound  
Дата: 09.12.15 22:01
Оценка: +1
Здравствуйте, novitk, Вы писали:

WH>>Часть замечаний ты замазал.

N>Не "замазал", а "исправил". Что за хамская манера?
Это именно что замазал. Исправил, это когда ты можешь показать все сообщения об ошибках.

WH>>Но большая часть осталась.

N>Перечисли. Типизации в этом примере нет. Раскраска и прочая гуйня выведет размер за разумные рамки, но что тут интересного?
То что нитра даёт это всё бесплатно. Причём качественно.
А с твоим подходом это всё за разумными рамками.

WH>>А главное код стало понять ещё сложнее.

N>Перестань. Любой ФПишник сможет поддерживать этот код, тем более что у него сейчас есть нормальная IDE.
Давай спросим у ФПшников, какой код им будет проще поддерживать?

Пойми, тут никто не сомневался, что можно как-то написать код, который будет делать что-то похожее.
Разговор был про качественное воспроизведение всего функционала.
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[12]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.12.15 22:04
Оценка:
Здравствуйте, novitk, Вы писали:

N>Не совсем так. Я как раз неплохо понимаю, что они делают и как. Просто они претендуют на некоторую универсальность и революционность, которой я не наблюдаю. А как конструктор для Немерле 2 или как оно там теперь называется, проект имеет право на жизнь. На мой вкус переборщили с DSL-ами, но я списываю это на их фанатизм и мой субъективизм.


Мне кажется ты отлично показал, что наши ДСЛ-и намного читабельнее чем, то что можно выжать даже из очень продвинутой Скалы (без расширения синтаксиса).

Влад сразу сказал: "сделай — сравним". Вот теперь есть что сравнивать и есть на что указывать. Можно спросить у окружающих. Считают ли они твой вариант человеко-читаемым и считают ли они достоинством компактность в ущерб понятности.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[11]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.12.15 22:09
Оценка:
Здравствуйте, novitk, Вы писали:

N>Перечисли. Типизации в этом примере нет. Раскраска и прочая гуйня выведет размер за разумные рамки, но что тут интересного?


Ну, дык интересно то, что в нашем то примере все это есть, так как Нитра именно для этого и нужна.

WH>>А главное код стало понять ещё сложнее.

N>Перестань. Любой ФПишник сможет поддерживать этот код, тем более что у него сейчас есть нормальная IDE.

Перестань. Любой разумный программист перепишет этот код. И пусть он станет в 10 раз длинне, но его можно будет читать, а не только писать.

Это форменный говнокод. Вычисления перемешаны с правилами грамматики так, что нужно пол часа вчитываться, чтобы понять что где.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[14]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 09.12.15 22:17
Оценка:
Здравствуйте, novitk, Вы писали:

N>Визуализатор? Полезная штука для отладки. В ANTLR он тоже есть, но это всего лишь один из аспектов.


В ANTLR нет ничего кроме парсера. Их ГУЙ о том как отлаживать грамматики. Это у них сделано отлично. Но сравнивать их невозможно. Разные задачи и разные возможности.

Закладку что показал NN используют для отладки типизации, а не парсинга. Как раз средств отладки парсинга у нас мало.

N>Лично я не готов платить за его наличие громоздкостью комбайна сделанного исключительно под C#-подобный язык для крошечного ДСЛ в нашем примере.


Ну, ты же понимаешь, что пример крошечный специально, чтобы людей не грузить? Естественно Нитра не для подобных игрушек сделана. Проблема в том, что если я покажу реально мозговышибательные вещи типа вычисления областей видимости и связывания для C#, то понять это без подготовки не сможет никто. Вот мы и придумали (за 20 минут до презентации) язычок в котором все просто, но есть на чем показать связывание и таблицы имен.

N>Обычный unix-way — дайте мне лучшие компоненты, я сам соберу из них, что мне нужно.


Ты уже скала-вей показал. Собрал страшилку.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Отредактировано 09.12.2015 22:30 VladD2 . Предыдущая версия .
Re[12]: [Nitra] Пример простого языка вычисляющего выражения
От: novitk США  
Дата: 09.12.15 22:36
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Это форменный говнокод.

Это абсолютно обычный FP-код. Вам parsec не нравиться что-ли? Его там всего 4 строчки. Предположи, что я взял ANTLR или что тебе там нравится, и забудь.

VD>Вычисления перемешаны с правилами грамматики так, что нужно пол часа вчитываться, чтобы понять что где.

O чем ты? EvalContext абсолютно независим от парсера. Я поместил его в один файл только для удобства оформления.
Re[10]: [Nitra] Пример простого языка вычисляющего выражения
От: novitk США  
Дата: 09.12.15 23:59
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Шлюх не заметил. В прочем, теперь там черт ногу сломит.

VD>Что там у тебя с восстановлением после ошибок? Как организовать подсветку и переходы к определению переменных в редакторе? Сколько там еще нужно "строчек"?
А почему у вас нет автоматической трансляцией под CUDА или генерации документации по ГОСТ#123.б ?!
Если серьезно, то с подсветкой у меня все ровно так же как и у тебя — надо писать код. Так как этот код из Нитры ты не привел с чем мне мой сравнивать?
Безусловно "из коробки" у вас много и это прекрасно. Меня только цена не устраивает для моих целей.

VD>Обработка ошибок на исключениях — это вообще прелесно! О востановлении после ошибок можно сразу забыть. Еще нужно незабыть написать код обработки этих исключений и вывода людям в человеческой форме.

Дак, а откуда ты знаешь какая семантика обработки ошибок будет нужна. Может мне будет надо спустить унитаз на "a=b;b=a"?
Дефолтная семантика нитры делает конечно больше чем скалы, но это потому, что кто-то написал код, см выше.

VD>Еще прибавь сюда тормоза, так тавои комбинаторы это треш с точки зрения производительности.

Правильно, поэтому завтра я могу выкинуть эти комбинаторы и взять ваш крутой парсер, но при этом оставить свой "крутой" RP-pull граф с ассинхронностью и паралеллизацией, а не ваш детский сад под названием "зависимые свойства". Под "свою" имеется ввиду, то что мы используем в продакшене, а не в моем примере. Unix way, brother! Нет я конечно понимаю, что можно использовать только ваш парсер, но клеить-то придется на N, а значит ваш парсер тоже идет в топку.

VD>В итоге получается унылое говно которое совершенно невозможно читать, сложно поддерживать, которое тормозит и кидается исключениями вместо диагностики. И это на примитивном примере.

за час изобразить все что вы придумывали и писали пятилетку? Вопрос стоял не так. Я всего лишь сказал, что опишу синтакс и семантику вычислений компактней.
Re[11]: [Nitra] Пример простого языка вычисляющего выражения
От: WolfHound  
Дата: 10.12.15 00:14
Оценка:
Здравствуйте, novitk, Вы писали:

N>Если серьезно, то с подсветкой у меня все ровно так же как и у тебя — надо писать код. Так как этот код из Нитры ты не привел с чем мне мой сравнивать?

Это вообще к делу не относится.
Мы же у тебя не требуем код скалы и парсеркомбинаторов.

N>Безусловно "из коробки" у вас много и это прекрасно. Меня только цена не устраивает для моих целей.

А что с ценой?
Даже на игрушечном примере она намного меньше, чем с твоими подходами.

N>Дак, а откуда ты знаешь какая семантика обработки ошибок будет нужна. Может мне будет надо спустить унитаз на "a=b;b=a"?

1)Получи сообщение об ошибки и спускай.
2)Единственная разумная реакция компилятора на ошибку это показать её пользователю.

N>Правильно, поэтому завтра я могу выкинуть эти комбинаторы и взять ваш крутой парсер, но при этом оставить свой "крутой" RP-pull граф с ассинхронностью и паралеллизацией, а не ваш детский сад под названием "зависимые свойства".

И где можно на него посмотреть?
После чего мы посмотрим, где детский сад.

N>за час изобразить все что вы придумывали и писали пятилетку? Вопрос стоял не так. Я всего лишь сказал, что опишу синтакс и семантику вычислений компактней.

Хватит уже выкручиваться.
Замахнулся на то, что не можешь осилить. Бывает.
Признай, что был не прав, и закончим на этом.
... << RSDN@Home 1.2.0 alpha 5 rev. 62>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[3]: [Nitra] Пример простого языка вычисляющего выражения
От: _someuser  
Дата: 10.12.15 09:36
Оценка:
Здравствуйте, VladD2, Вы писали:

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


X>>А когда парсер С# в РеШарпере будет написан на Nitra вместо PsiGen'а?


VD>Это не от нас зависит, как ты понимаешь. Он ведь и так работает и каши не просит. Вокруг все скептики. Те кто исследователи по натуре переключились на Котлин. Найтра никому не интересна. Путь даже код Решарпера сжался бы в 10 раз будучи переписанным на Найтре и стал бы в 100 раз понятнее и стабильнее, но никто на это не решится.


В этом плане интересен вопрос, как будет отличаться производительность реализации парсера и типизации на найтре от решарпера или рослина. Скажем, насколько знаю, в рослине поддерживается инкрементальный парсинг, а в найтре пока нет (насколько понял планируется, но пока не в приоритете). Интересно узнать, у вас, как разработчиков найтры, какие мысли по поводу производительности? Т.е. почему ваша реализация будет работать не медленнее или быстрее?
Re[13]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.12.15 10:57
Оценка:
Здравствуйте, novitk, Вы писали:

N>Это абсолютно обычный FP-код.


Это ты рассказываешь человеку который каждый день ФП применяет. ФП разное бывает. Бывает по делу, а бывает набор спермитазойдов и смайликов, как в твоем случае.

И это не мудрено. Для описания парсеров нужна грамматика, а не ее кодирование с помощью смайликов.

Вот это:
syntax module SampleSyntax
{
  using Nitra.Core;
  using Nitra.CStyleComments;

  regex Keyword = "var";

  [Reference] token Reference = !Keyword IdentifierBody;
  [Name]      token Name    = !Keyword IdentifierBody;

  [StartRule] syntax TopRule = (VariableDeclaration nl)*;

  syntax VariableDeclaration = "var" sm Name sm "=" sm Expression ";";

  syntax Expression
  {
    | [SpanClass(Number)] Num = Digits {  Digits = ['0'..'9']+; }
    | Braces = "(" Expression ")";
    | Variable = Reference;
  precedence Sum:
    | Sum = Expression sm Operator="+" sm Expression;
    | Sub = Expression sm Operator="-" sm Expression;
  precedence Mul:
    | Mul = Expression sm Operator="*" sm Expression;
    | Div = Expression sm Operator="/" sm Expression;
  precedence Unary:
    | Plus  = Operator="+" Expression
    | Minus = Operator="-" Expression
  }
}

грамматика.
А это:
  class Expr;
  class BinOp(val evalF: (Int, Int) => Int, val expr: Expr);
  def mkBinOp = (new BinOp(_, _)).curried
  val binOpGroups = Seq(
      Seq("*" -> mkBinOp(_*_), "/" -> mkBinOp(_/_)),
      Seq("^^" -> mkBinOp(Math.pow(_, _).toInt)),
      Seq("+" -> mkBinOp(_+_), "+" -> mkBinOp(_+_)))

  case class NumberExpr(num: Int) extends Expr;
  case class VarExpr(name: String) extends Expr;
  case class OpExpr(expr: Expr, subExprs: List[BinOp]) extends Expr;
  class VarDef(val name: String, val expr: Expr)
  
  def makeOpExpr(p: ~[Expr, List[BinOp]]): Expr = p match {
    case f ~ Nil => f
    case f ~ l => OpExpr(f, l)
  }
  def binOpGroupParser(termAbove: Parser[Expr], opGroup: Seq[(String, Expr => BinOp)]): Parser[Expr] = {
    val opGroupParsers = opGroup.map { case (opCode, constr) => opCode ~> termAbove ^^ constr }.reduce(_|_)
    (termAbove ~ rep(opGroupParsers)) ^^ makeOpExpr
  }  

  def factor: Parser[Expr] = wholeNumber ^^ (s => NumberExpr(s.toInt)) | ident ^^ VarExpr | "(" ~> expr <~ ")"
  def expr: Parser[Expr] = binOpGroups.foldLeft(factor)(binOpGroupParser(_, _))
  def varDef: Parser[VarDef] = ("var" ~> ident) ~ ("=" ~> expr <~ ";") ^^ { case n ~ e => new VarDef(n, e) }
  def varDefParseAll(in: CharSequence): ParseResult[VarDef] = parseAll(varDef, in)

закарючки.

N>Вам parsec не нравиться что-ли?


Конечно, нет. Грамматика ANTLR мне нравится, а закарчки на Хаскеле эмулирующие грамматику — нет.

N>Его там всего 4 строчки. Предположи, что я взял ANTLR или что тебе там нравится, и забудь.


Зачем мне что-то предполагать? Ты обещал, что на Скале будет проще. Я вижу что получился какой-то не читаемый пипец с кучей упрощений, не поддерживающий IDE ни в каком виде и в добавок еще и потенциально тормозной из-за комбинаторов.

VD>>Вычисления перемешаны с правилами грамматики так, что нужно пол часа вчитываться, чтобы понять что где.

N>O чем ты?

Вот об этом "(s => NumberExpr(s.toInt))", об это "{ case n ~ e => new VarDef(n, e) }" и о другой грязи.

Я уже говорил, о том, что это мы проходили уже очень давно. У нас есть в разы более чистый путь — rule-методы:
  syntax Expr
  {
    Value() : double;

   | [SpanClass(Number)] Number
      {
        regex Digit = ['0'..'9'];
        regex Number = Digit+ ('.' Digit+)?;
 
        override Value = double.Parse(GetText(Number));
      }

    | ParenthesesParentheses = '(' Expr ')' { override Value = Expr.Value(); }
  precedence Additive:
    | Add         = Expr '+' Expr { override Value = Expr1.Value() + Expr2.Value(); }
    | Sub         = Expr '-' Expr { override Value = Expr1.Value() - Expr2.Value(); }
  precedence Multiplicative:
    | Mul         = Expr '*' Expr { override Value = Expr1.Value() * Expr2.Value(); }
    | Div         = Expr '/' Expr { override Value = Expr1.Value() / Expr2.Value(); }
  precedence Power:
    | Pow      = Expr '^' Expr precedence 30 right-associative { override Value = Math.Pow(Expr1.Value(), Expr2.Value()); }
  precedence Unary:
    | Neg         = '-' Expr { override Value = -Expr.Value(); }
  }

это выглядит не плохо только на примитивных примерах. Смешение кода и грамматики — плохая идея. Да и вычисления прямо на дереве разбора — тоже.

В серьезных языках нужны такие вещи как полмиморфизм, разделение грамматики и семантики, неявное задание последовательности вычислений и т.п.

Так же есть и чисто технические аспекты которые на практике так же важны.

N>EvalContext абсолютно независим от парсера. Я поместил его в один файл только для удобства оформления.


Сомнительное удобство, кстати. Погоня за минимизацией символов. Людям это не надо. Людям нужно минимизировать число сущностей которые они обязаны одновременно держать в голове. А количество строк их абсолютно не волнует. Иначе все писали бы даже не на Хаскеле, а на каком нибудь J или K.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Отредактировано 10.12.2015 13:00 VladD2 . Предыдущая версия .
Re[11]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.12.15 11:48
Оценка:
Здравствуйте, novitk, Вы писали:

VD>>Что там у тебя с восстановлением после ошибок? Как организовать подсветку и переходы к определению переменных в редакторе? Сколько там еще нужно "строчек"?

N>А почему у вас нет автоматической трансляцией под CUDА

Почему нет? Есть. В Nemerle есть макросик поддерживающий CUDА.

N>или генерации документации по ГОСТ#123.б ?!


За ГОСТ не скажу, но о том чтобы добавить поддержку автоматизации документирования я давно подумываю. Ее отстутствие это недостаток.

Просто это не так критично как возможность удобно редактировать код на своем языке. И это не так сложно реализовать.

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

N>Если серьезно, то с подсветкой у меня все ровно так же как и у тебя — надо писать код.


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

Так что вое решение не только менее читабельно, но и намного менее функционально. Чтобы его приблизить к нашему тебе придется сделать все что сделали мы. А это годы работы. 99% просто не осилят ее никогда.

N>Так как этот код из Нитры ты не привел с чем мне мой сравнивать?


Я привел весь необходимые код. Все остальное делает Нитра. Ах, да. Забыл. Еще нужен скрипт для генерации плагина. Он выглядит примерно так:
Nitra.LanguageCompiler.exe MyLang.dll

На выходе будет клагин к Решарперу. Потенциально можно генерировать плагины к любой IDE. Так же можно сделать генерацию исполняемых файлов под любую платформу. И все это потому что у нас DSL-и, а не хардкод на Скале или Немрле. Мы просто читаем исчерпывающее описание и генерируем по нему нужный код.

N>Безусловно "из коробки" у вас много и это прекрасно. Меня только цена не устраивает для моих целей.


А что с ценой то не так? Все объективная критика пока что сводится к тому, что многим кажется, что АСТ и мапинг лишние. Ну, да на простых примерах они кажутся лишними. Ну, да можно как-то оптимизировать это дело. Но по сути там ничего лишнего. Каждое решение взвешивалось по сто раз и является обоснованным.

VD>>Обработка ошибок на исключениях — это вообще прелесно! О востановлении после ошибок можно сразу забыть. Еще нужно незабыть написать код обработки этих исключений и вывода людям в человеческой форме.

N>Дак, а откуда ты знаешь какая семантика обработки ошибок будет нужна. Может мне будет надо спустить унитаз на "a=b;b=a"?

Я не знаю. Я проставляю средства для этого. Я знаю только, что ошибки люди хотят видеть не виде трапов IDE и компилятора, а в виде списка выведенного на экран (на консоль в компиляторе и в GUI-список в IDE).

Ошибки парсинга вообще не должны заботить пишущего код человека. Мы обеспечиваем восстановление после ошибок и формирование внятных сообщениях о них. И это все на основании той самой грамматики. Никаких дополнительных приседаний. Ошибки связывания так же выводятся автоматически. А если нужны какие-то семантические проверки и сообщения, то у нас есть простой механизм выдать их. Примерно так:
ast Xxxx
{
  ...
  when (делаем проверку обращаясь к зависимым свойствам)
    Error(сообщаем об ошибке);
}


Этот код вычислится кода станут доступны данные используемые в проверке и коде формирования сообщения и выведется ровно один раз. В твоем же случае ты все это будешь вынужден делать самостоятельно и, скорее всего, в императивном виде.

N>Дефолтная семантика нитры делает конечно больше чем скалы, но это потому, что кто-то написал код, см выше.


Естественно! И еще потому что мы пишем на не Немерле, а на языке (точнее языках) специально предназначенном для решения этой задачи. Когда его проектировали, то все это предусмотрели.

Конечно же ты ты тоже можешь на Скале написать такой же язык, но это и будет аналог Нитры.

Вот именно по этому решение на Скале никогда не будет конкурентом решению на Нитре. Чтобы сделать аналогичное решение ты будешь вынужден написать на Скале аналог Нитры.

N>Правильно, поэтому завтра я могу выкинуть эти комбинаторы и взять ваш крутой парсер, но при этом оставить свой "крутой" RP-pull граф с ассинхронностью и паралеллизацией, а не ваш детский сад под названием "зависимые свойства".


Надеюсь, что когда ты поймешь что такое зависимые свойства и для чего они нужны тыне будешь говорить такие глупости. Зависимые свойства — это возможность произвести вычисления по разобранному коду при этом не задавая их порядок явно и имея возможность в любой момент расширить их новыми конструкциями не меняя реализацию базового языка.

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

N>Под "свою" имеется ввиду, то что мы используем в продакшене, а не в моем примере.


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

Пойми ЗС — это средство сделать типизацию декларативной и независимой от последовательности вычислений.

N>Unix way, brother! Нет я конечно понимаю, что можно использовать только ваш парсер, но клеить-то придется на N, а значит ваш парсер тоже идет в топку.


Если ты о зависимости от платформы, то мы проектировали все так, чтобы можно было относительно легко портировать языки написанные на Нитри (а значит и саму Нитру) на другие платформы. Это не тривиальное занятие и у нас нет на него сил/времени, но технически это возможно.

N>за час изобразить все что вы придумывали и писали пятилетку? Вопрос стоял не так. Я всего лишь сказал, что опишу синтакс и семантику вычислений компактней.


Ты сказал, что реализация будет не больше. Но твоя реализация не равноценна. Она много чего не умеет и срезает углы. При этом она получилась очень запутана.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[4]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.12.15 12:40
Оценка:
Здравствуйте, _someuser, Вы писали:

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


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

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

_>Интересно узнать, у вас, как разработчиков найтры, какие мысли по поводу производительности? Т.е. почему ваша реализация будет работать не медленнее или быстрее?


Мы не гарантируем, что она будет быстрее любой другой. Это физически невозможно, так как конкретную реализацию всегда можно заоптимизировать сильнее, чем общую. Мы делаем так, чтобы производительности хватало для комфортного использования. Скажем, если мы печатаем в редакторе, то не должно возникать тормозов. То же инкрементальное редактирование нужно именно для того, чтобы комфортно печатать было и в больших файлах.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Отредактировано 10.12.2015 13:10 VladD2 . Предыдущая версия .
Re[11]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.12.15 12:49
Оценка:
Здравствуйте, _NN_, Вы писали:

_NN>Это конечно интересно , однако людям нужны простые примеры.


Вот видишь? Сделали простой пример, так сразу нашлись любители докопаться до радио, которые вместо того, чтобы на их основе понять как что работает в Найтре, начали обсуждать объемы кода и другую "важную" пенесометрию.

Возможно надо просто разбирать каждую особенность по отдельности и приводить отдельные примеры.

Если есть идеи примеров — подкидывайте.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[6]: [Nitra] Пример простого языка вычисляющего выражения
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.12.15 13:52
Оценка:
Здравствуйте, Kolesiki, Вы писали:

VD>>1. Его иерархия типов захардкожена и не расширяется.


K>При переписывании это легко устранить, опираясь только на внутренние типы .NET; Касательно вообще типов, мы все знаем, что .NET "объектно натянут" по самые хэдеры, т.е. "язык под .НЕТ" уже должен быть ООП, с классами и методами.


Язык выполняющийся на донтнете вовсе не бязан быть ООП. Примером могут служить регулярные выражения.

Для поддержки ООП можно сделать библиотечное решение (что мы и сделали). Но при этом должна быть возможность вводить сущности присущие предметной области на которую нацелен язык. Например, в той же Найтре у нас есть символы и типы правил, типы АСТа и т.п. Оно в итоге переписывается в классы, но работать с ними нужно как правилами, АСТом и т.п. Иначе код становится слишком сложным.

В Немерле у тебя нет средств выразить твои сущности и ты вынужден самостоятельно писать АСТ и символы для своего языка. Хорошо если ты имеешь подготовку и сразу понимаешь, что это надо делать. Большинство же людей начинают решать проблему в лоб и упираются в то, что это очень сложно.

K>Это я к тому, что нет смысла закладываться на "язык черепашки с вкраплениями SQL", т.к. это ортогональное дотнету решение.


Ну, вот Linq встроили в C#. Он к ООП никаким боком. Скорее это ФП. Мы без проблем можем оформить Linq в виде отдельного расширения. А это значит, что любой может создать подобное решение. При этом оно будет интегрировано в зык и IDE не хуже чем "родное".

K>А раз мы в рамках ООП, то и "конструктор языков" сразу можно упростить до понятия "у нас есть классы и их члены" — далее можно плясать от этой модели.


Дело в том, что мы то ведь пробовали делать свои языки. Потому и пришли к Нитре.

hi_octane очень правильно сказал
Автор: hi_octane
Дата: 06.12.15
про выпрямление косяов и убирание хардкода. Мы сняли ограничения на расширение грамматики и семантики. Нужный тебе язык становится родным. И совершенно все равно является ли он простеньким встроенным в другой язык ДСЛ-ем, или отдельным полнофункциональным языком. Мы потому и работали так долго над Найтрой, что это не тривиальное решение.

VD>>2. Сам язык типизируется горой кода в которой не могут разобраться даже авторы.


K>Очевидно, что студенческий код рано или поздно пришлось бы выкинуть, но для этого вы и нужны — люди, которые знают старое и видят, как правильно создавать новое!


Гы. Эти студенты дадут фору большинству профессионалов из МС и Гугля. Они конечно гнали вперед срезая угля, но очевидно, что проблемы не только в их косяках. Проблемы в самом подходе. В современном языке программирования (воде Немерла, C# или даже VB) области видимости переплетены хитрейшим образом. Такие языки невозможно типизировать в один проход. Явное описание зависимостей выливается в гору кода и, как следствие, к необходимости его поддерживать в дальнейшем.

Чтобы написать и поддерживать Немерл нужно не 2.5 человека, как есть у нас, а человек 20 профессионалов на полной зарплате. Таких ресурсов нам никто не даст. По сему мы пишем средство которое сокращает сложность задачи во много раз. Поддерживать это средство и реализацию на нем таки проще, чем реализацию без него.

K>эээ.... даже не понял, про что это. Думаю, Туьукду(Nemerle ) не первый компилятор, построенный без них — другие же как-то справляются?


Справляются. Одни городят архитектуры и нанимают кучу программистов. Другие довольствуются компилятором командной строки, а каждое его зименение выливается в попоболь.

Так для справки, в Решарпере гигабайты кода.

K>Опять же, кто сказал, что нужна именно VS? Сред для расширения навалом, можно и своё написать.


Начинать лучше с ОС. Это более традиционный для России подход.

K>Но я в целом что хотел сказать: пилить "конструктор" и пилить "расширяемый язык" — две задачи, отличающиеся на порядок (понятно, последняя проще).


Да не особо.

Опять же для справки. Немерл пилили 10 лет в общей сложности более 20 программистов. Правда, по очереди. Одновременно до 4 программистов трудилось. Мы над найтрой работаем 3 года. Еще пол года нужно на язык уровня немерла.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.