Изучаем макросы Nemerle
От: VladD2 Российская Империя www.nemerle.org
Дата: 18.02.06 16:45
Оценка: 180 (16)
Единственный способ развеить скепсис — это попробовать.

Я решил попробовать макросы Nemerle в более-менее реальной задаче. Цель две.
1. Изучить собственно Nemerle и его макросы.
2. Продемонстрировать его возможности на этом форуме.

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

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

Итак, для экспериментов вам понадобится:
1. .NET Framework 2.0 (и возможно SDK). Лучше конечно если на машине просто установлена VS 2005.
2. Компилятор Nemerle и сопутствующая ему байда. Все это можно взять на их сайте http://nemerle.org. Так здесь http://nemerle.org/download/msi/nemerle-0.9.2.msi лежит инсталлятор для Виндовс. А здесь http://nemerle.org/download/ можно взять более свежую рабочую версию причем вместе с исходниками. Очень советую это сделать даже если вы выберете инсталляцию msi-я, так как поглядеть на исходники макросво и вообще на исподники созданные авторами языка очень полезно. Полноценной документации ведь пока нет, а исходники говорят о многом.
2. Сцинтила или другой редактор который упростить ввод и тестирование кода. Сцентилу можно взять здесь: http://prdownloads.sourceforge.net/scintilla/wscite167.zip?use_mirror=heanet
Файлы для обеспечения подсветки синтаксиса Nemerle для Сцинтылы можно взять здесь:
http://nemerle.org/svn/nemerle/trunk/misc/scintilla/SciTEGlobal.properties
http://nemerle.org/svn/nemerle/trunk/misc/scintilla/nemerle.properties
их нужно скопировать в каталог где лежат другие properties-файлы Сцинтилы.
3. Для упрощения компиляции и запуска файлов я написал простенький cmd-файлик для ХРюши:
@echo off

SET FileName=%1

SET MacroOutFileName=%FileName%-macro.dll
SET MacroSourceFileName=%FileName%-macro.n

SET SourceFileName=%FileName%.n
SET OutFileName=%FileName%.exe

if exist %MacroSourceFileName% (
  @echo --# Compiling Macro-assembly #---
  @echo ---------------------------------
  ncc.exe -no-color -debug+ -r Nemerle.Compiler.dll -t:dll %MacroSourceFileName% -out %MacroOutFileName%
  if not errorlevel 1 (
    @echo --- Compiling target assembly ---
    @echo ---------------------------------
    ncc.exe -no-color  -debug+ -r %MacroOutFileName% -t exe %SourceFileName% -o %OutFileName%
    rem -r System.Windows.Forms -r System.Data 
    if errorlevel 1 (
      @echo -------------------------------------
      @echo !!! Error detected in targer code !!!
    ) else (
      @echo --------------- OK -----------------
      @echo --Run: %OutFileName%
      @echo .
      %OutFileName%
    )
  ) else (
    @echo ------------------------------------
    @echo !!! Error detected in macro-code !!!
  )
) else (
  @echo ----- No Macro-assembly found ------
  @echo ------------------------------------
  if not errorlevel 1 (
    @echo --- Compiling target assembly ---
    @echo ---------------------------------
    ncc.exe -no-color  -debug+ -t exe %SourceFileName% -o %OutFileName%
    rem -r System.Windows.Forms -r System.Data 
    if errorlevel 1 (
      @echo -------------------------------------
      @echo !!! Error detected in targer code !!!
    ) else (
      @echo --------------- OK -----------------
      @echo --- Run: %OutFileName%
      @echo .
      %OutFileName%
    )
  ) else (
    @echo ------------------------------------
    @echo --! Error detected in macro-code !--
  )
)

Его нужно сохранить под именеме build-and-run.cmd в каталог где лежат бинарники Нэмерла (файл ncc.exe и т.п.).
Далее нужно отредактировать файл nemerle.properties добавив в него примерно такие строчки:
#Extra tool: Build With Macro and Run
#Build macro and target for console application"
command.name.6.*.n=Build With Macro and Run
command.6.*.n=build-and-run.cmd $(FileName)
command.6.subsystem.*.n=0
command.save.before.6.*.n=1
command.shortcut.6.*.n=F5

При этом, если что, нужно или изменить F5 на удобную для себя кнопку, или убрать/изменить аналогичный шорткат у имеющегся в nemerle.properties пункта.

Теперь открыв в Сцинтиле файл с расширением .n вы можете просто нажатием на F5 скомпилировать его и запустить на выполнение.
Если при этом рядом с компилируемым файлом будет лежать файл начальная часть имени которого совпадает с компилируемым, и оно оканчивается на «-macro», то сначала будет произведена компиляция этого файла. А потом, вслучае успеха будет откомплирован вайл без суффикса «–macro». Причем при его компиляции будет подключаться макросборка получившаяся на первом этапе. Например, если имеются файлы:
test-001.n
test-001-macro.n

если активизировать в Сцинтиле файл test-001.n и нажать F5, то будет сначала произвдена компиляция макросборки test-001-macro.dll (на базе файла test-001-macro.n), а потом будет произведена компиляция сборки test-001.exe (на базе файла test-001.n). Причем при компиляции test-001.exe будет использована макро-сборк полученная на первом этапе.
Если все скомпилируется, то будет произведен запуск test-001.exe. Иначе будет выданы сообщения об ошибке.

Для того, чтобы убедиться, что все работает создайте простенькую тестовую пару файлов:
Test-001.n – содержащий следующий код:
// abstract, and, array, as, base, catch, class, def, delegate, do, else, enum, event, extern, false, finally, for, foreach, fun, if, implements, in, interface, internal, lock, macro, match, module, mutable, namespace, new, null, out, override, params,  
// WriteLine public private protected internal

TestMacro();

Примечание: Коментарии с лабудой нужны для того, чтобы можно было пользоваться недо-интелисенсом. Если в Сцинтиле написат первые буквы слова и нажать Ctrl+Enter, то она выдаст список слов начинающихся на эти буквы.


И файл test-001-macro.n содержащий следующий код:
using System.Console;
// abstract, and, array, as, base, catch, class, def, delegate, do, else, enum, event, extern, false, finally, for, foreach, fun, if, implements, in, interface, internal, lock, macro, match, module, mutable, namespace, new, null, out, override, params,  
// WriteLine public private protected internal

macro TestMacro()
{
  WriteLine("compile-time\n");
  <[ WriteLine("run-time\n") ]>;
}

Теперь откройте Сцинтилой файл Test-001.n и нажмите F5.
Если в ответ вы увидите следующее:
>build-and-run.cmd test-001
--# Compiling Macro-assembly #---
---------------------------------
--- Compiling target assembly ---
---------------------------------
compile-time

--------------- OK -----------------
--Run: test-001.exe
.
run-time

>Exit code: 0

Значит вы все сделали верно и первый этап знакомства с Нэмерлом и его макросами пройдет.

Если вам сказали, что мол файл не найдет, то пропишите путь к каталогу с бинарниками Нэмерла в переменную PATH окружения.

Теперь прокомментирую, что вы видите на экране. Вывод:
---------------------------------
--- Compiling target assembly ---
---------------------------------
compile-time

Означает что началась компиляция основной сборки (сборки которую мы будем запускать на исполнение) и вызвался макрос. Который вывел информацию о себе на констоль. Надпись:
run-time

Ввел код сгенерированный макросом. Если вы теперь откроете полученный исполнимый файл Рефлектором, то увидите, что в функции Main() присутствует вызов:
Console.WriteLine("run-time\n");

а от макроса не осталось и следа. Это и есть чудесное действие макросов.

Продолжение следует.
... << RSDN@Home 1.2.0 alpha rev. 637>>

14.03.06 11:09: Перенесено из 'Философия программирования'
30.01.07 18:23: Перенесено модератором из 'Декларативное программирование' — IT
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
01 Изучаем макросы Nemerle - Квази-цитирование
От: VladD2 Российская Империя www.nemerle.org
Дата: 18.02.06 20:14
Оценка: 156 (19)
Итак, сейчас я расскажу, что такое квази-циритвание (quasi-quotation).

Квази-циритвание — это очень простая идея, но именно она делает макросы Nemerle-а мощнейшим и в тоже время относительно безопасным инструментом по сравнению с текстуальными макросами С/С++.

Прежде чем разбираться с тем, что такое квази-цитирование давайте сначала разберемся, что вообще означает термин «цитирование».

Цитирование (quotation)

Компилятор преобразует текст программы сначала в список токенов (умные ученые мужи еще называют их терминалами), а потом в так называемое абстрактное синтаксическое дерево (Abstract Syntax Tree, AST). AST – это дерево состоящее из объектов описывающих код.
В AST каждый элемент синтаксиса представлен веткой AST-дерева. В общем-то когда программист воспринимает код, то он тоже мысленно строит подобное дерево.
Я не специалист в AST Nemerle-a, так что продемонстрирую AST на базе AST R#-а. Вот как выглядит вот такой кусок кода:
if (a == 1)
  a = 2;

в AST R#-а:
RStatement statement = new RConditionStatement(new RBinaryOperatorExpression(
    new RUnresolvedReferenceExpression("a"), 
    RBinaryOperator.IdentityEquality, new RPrimitiveExpression("1")),
    new RExpressionStatement(new RAssignmentExpression(
    new RUnresolvedReferenceExpression("a"), RAssignmentOperator.Assign,
    new RPrimitiveExpression("1"))));

Переменная statement будет содержать AST-векту описывающую if.
Если теперь вызвать у statement метод ToString():
Console.WriteLine(statement.ToString())

то на консоль выведется текст:
if (a == 1)
  a = 2;

«Зачем такие сложности?» – спросят многие. А затем, что AST является объектной моделью с которой намного удобнее работать программно нежели с плоским текстом.
Преобразовав текст программы в AST компилятор выполняет над AST множество действий. Так он может преобразовать AST некоторой высокоуровневой конструкции вроде foreach в более низкоуровневую (например, в while). Так же компилятор вычисляет множество семантических атрибутов. Так он вычисляет типы переменных и выражений. Производит их сопоставление и контроль. В общем, творит с AST все что хочет. В итоге компилятор преобразует AST в промежуточное представление (например, в MSIL), а затем и в машинный код.

Зачем я рассказал обо все этом, спросите вы?
А затем, что цитирование (quotation) – это способ заставить компилятор преобразовать некоторый код в это самое AST. Мы конечно могли бы просто написать код создающий AST (если бы компилятор предоставлял бы нам доступ хотя бы к нему), но это, как вы могли заметить не очень приятное занятие.
С использованием цитирования мы можем резко упростить данный процесс.
Вот как будет выглядеть получение AST приведенного выше выражения (if) с использованием цитирования:
RStatement statement = <[
    if (a == 1)
        a = 2; ]>;

Не правда ли, значительно проще? Это синтаксис C#. Вернее гипотетического C#. C# в котором поддерживается цитирование. А вот как тоже самое выглядит на Nemerle:
def expr = <[
    when (a == 1)
        a = 2; ]>;

Как видите, на Nemerle это выглядит даже проще (что не может не радовать ).
Я использовал имя expr, так как в Nemerle if-это не предложение (statement) как в C#, а выражение (expression).
В отличии от C# мне не пришлось указывать тип переменной. Это можно благодаря тому, что Nemerle обладает мощнейшей функцией вывода типов.
Фактически тип переменной expr является PExpr. Это можно указать явно:
def expr : Nemerle.Compiler.Parsetree.PExpr = <[
  when (a == 1)
      a = 2; ]>;

Примечание: Естественно, что как и в C# указывать полный путь к типу не обязательно. Nemerle.Compiler.Parsetree можно было задать в using-е.

Теперь вы достаточно знаете чтобы понять суть макроса из первого сообщения этой темы. Напомню его код:
using System.Console;

macro TestMacro()
{
  WriteLine("compile-time");
  <[ WriteLine("run-time") ]>;
}

Что же мы видим в этом макросе?
А видим мы два вызова метода WriteLine(). Один просто внутри тела макроса, а второй внутри «кавычек».
Так как макрос вызывается во время компиляции, то первый вызов WriteLine():
WriteLine("compile-time");

выведет строку "compile-time" в окно вывода компилятора. Важно понят, что этот код, выполняемый во время компиляции. Я всего лишь вывел строчку в окно, но с тем же успехом я мог бы подсоединиться к БД и считать из нее метаинформацию. В общем, я волен делать в нем все что угодно.
Вторая строка кода не выполняется во время компиляции. Они заключена в «ковычки». Это приводит к тому, что компилятор преобразует ее в AST и его нам.
Так как Nemerle ориентирован на выражения, в нем не нужно писать «return», чтобы возвратить значение функции. Последнее выражение в последовательности автоматически становится возвращаемым значением функции (в данном случае макроса TestMacro()). Если бы в C# поддерживались макросы и квази-цитирование, то этот код записывался бы на C# примерно так:
macro RExpr TestMacro()
{
  WriteLine("compile-time");
  return <[ WriteLine("run-time") ]>;
}

думаю, вы уже без проблем должны проводить аналогии между C# и Nemerle-ом. Так что оставлю вам самим задачу угадать откуда взялся RExpr после ключевого слова macro .

Квази-цитирование (quasi-quotation)

Теперь настала пора понять, что же означает приставка «квази».
Изменим немного наш пример:
macro TestMacro(myName)
{
  WriteLine("Compile-time. myName = " + myName.ToString());
  <[ WriteLine("Run-time.\n Hallo, " + $myName) ]>;
}

За одно придется изменить и код применения макроса на следующий:
TestMacro("Vlad");
def a = "VladD2";
TestMacro(a);

Нажмем F5 находясь в файле содержащем последний код... Вот что мы получим при этом в окне вывода:
>build-and-run.cmd test-001
--# Compiling Macro-assembly #---
---------------------------------
--- Compiling target assembly ---
---------------------------------
Compile-time. myName = "Vlad"
Compile-time. myName = a
--------------- OK -----------------
--Run: test-001.exe
.
Run-time.
 Hallo, Vlad
Run-time.
 Hallo, VladD2
>Exit code: 0

Теперь разберемся с каждой строкой макроса по отедльности.
Первое что изменилось в макросе – это то, что мы добавили параметр «myName».
Опять же не думайте, что он не типизированный. Его тип Nemerle выведет из дальнейшего использования. Чуть позже мы вернемся к вопросу о типе параметра этого макроса а сейчас пока что разберем остальные строки.
В строке:
WriteLine("Compile-time. myName = " + myName.ToString());

Мы выводим в окно компилятора значение содержащееся в параметре myName преобразовывая его в строку.
Примечание: Все объекты Nemerle – это автоматически объекты .NET-а, так что не странно, что у них реализованы методы класса object.
Теперь давайте поглядим, что же выводят эти строки?
При вызове макроса следующим абазом:
TestMacro("Vlad");

указанная строчка выводит:
Compile-time. myName = "Vlad"

А при вызове его так:
def a = "VladD2";
TestMacro(a);

мы получаем:
Compile-time. myName = a

Почему же мы во втором случае не получили текстовое значение помещенное в переменную «a»? Дело в том, что myName – это не строковая переменная, а переменная содержащая AST-ветку описывающую передаваемое в макрос выражение. В первом случае нам был передан литерал, а во втором переменная. Именно по этому, мы видим то что видим.
Попробуем проверить мое предположение. Для этого добавим в макрос перед строчкой:
WriteLine("Compile-time. myName = " + myName.ToString());

еще одну стрчку:
WriteLine(myName.GetType());

она выведет реальный тип выражения. Выполним компиляцию и поглядим на результат:
---------------------------------
--- Compiling target assembly ---
---------------------------------
Nemerle.Compiler.Parsetree.PExpr+Literal
Compile-time. myName = "Vlad"
Nemerle.Compiler.Parsetree.PExpr+Ref
Compile-time. myName = a
...

Результат полностью подтверждает мои ожидания.
Остается разобраться, что же такое PExpr+Literal и PExpr+Ref.
Лезем в Рефлектор и видим, что это классы вложенные в класс PExpr являющиеся его наследниками. Таким образом мы можем сделать четкий вывод, что в макрос переданы AST-ветки описывающие выражения Nemerle. Причем в первом случае это строковый литерал, а во втором – ссылка на переменную.
Теперь перейдем к строке макроса:
  <[ WriteLine("Run-time.\n Hallo, " + $myName) ]>;

Эта строка и является конечной целью повествования этого рздела.
Она тоже изменилась. Как и в предыдущей строке в ней я попытался прибавить к константной строке строку, содержащуюся в параметре.
Но в данном случае присутствуют два важных отличия.
1. Я не вызываю метод ToString().
2. Перед именем параметра макроса стоит символ «$».
Возникает резонный вопрос, а почему собсвенно?
Дело в том, что для предыдущих строк myName являлся переменной. А для цитируемого кода myName переменной являться не будет. Если я напишу:
  <[ WriteLine("Run-time.\n Hallo, " + myName.ToString()) ]>;

то компиляция макроса пройдет «на ура», но при его применении я получу сообщения об ошибке:
...
test-001.n:4:1:4:10: error: unbound name `myName.ToString'
Nemerle.Compiler.Parsetree.PExpr+Ref
Compile-time. myName = a
test-001.n:6:1:6:10: error: unbound name `myName.ToString'
...

Причем, заметьте! Код макроса не смотря на ошибки выполняется. А сообщения об ошибках я получаю уже от компилятора компилирующего сгенерированный макросом код.
Так происходит потому, что области видимости имен макросов и области видимости кода из которых эти макросы вызываются не пересекаются. Макросы Nemerle-a являются так называемыми гигиеническими. Использовать имена из вызывающего кода можно, но для этого придется постараться. Это уберегает от ошибок связанных с перекрытием имен. Так что даже если мы в взывающем коде напишем:
def myName = "xxx";
TestMacro("Vlad");

то ничего не изменится и мы получим все то же сообщение:
test-001.n:5:1:5:10: error: unbound name `myName.ToString'

Что же делать? Как получить доступ к внешним именам?
Ответ – никак! Точенее этого не нужно делать.
Мы получили в качестве параметра выражение. И мы можем внедрить его внутрь возвращаемого макросом выражения.
В принципе, произвести такое внедрение можно написав код просматривающий формируемое AST и модифицирующий его. Но это довольно сложно. Чтобы упростить подобные операции и было придумано квази-цитирование!
Внутри цитируемого кода мы можем вставлять, скажем так, активный контент. То есть нечто, что будет вычисляться перед возвратом из макроса.
Именно этой возможностью я и воспользовался чтобы внедрить ссылку на параметр макроса внутрь генерируемого кода. Символ «$» перед именем «myName» в строке:
<[ WriteLine("Run-time.\n Hallo, " + $myName) ]>;

Как раз и указывает компилятору Nemerle-а, что имя myName нужно рассматривать как этот самый активный кнтент.
Получается, что это уже не просто цитирование. Вот по этому его и назвали квази-цитированием.

Наверно вам интересно, что же в итоге попадает в код исполняемого модуля? Мне тоже это интересно (хотя я и догадываюсь – что). По этому я декомпилировал полученный исполняемый файл. Вот что я увидил:
Console.WriteLine("Run-time.\n Hallo, Vlad");
string text1 = "VladD2";
Console.WriteLine("Run-time.\n Hallo, " + text1);

Обратите внимание, что в первом случае получилась константная строка, а во втором код конкатинации переменной со строкой. Несомненно первый случай является результатом оптимизаций компилятора Nemerle-a.
Не трудно догадаться, что если в качестве параметра макроса передать выражение, то компилятор подставит его вместо параметра.
А что будет если тип выражения окажется несовместимым со строкой?
Проведем смелый научный эксперимент и передадим макросу в качестве параметра выражение «1 + 2»:
TestMacro(1 + 2);

Компилятор отреагировал довольно ожидаемым образом:
test-001.n:5:1:5:10: error: each overload has an error during call:
test-001.n:5:1:5:10: error:   overload #1, method System.String.op_Addition(left : string, right : string) : string
test-001.n:5:1:5:10: error: in argument #2 (right), needed a string, got int: System.Int32 is not a subtype of System.String [simple require]
...

С нашими знаниями мы без труда определим, что макрос просто хочет строку в качестве параметра. Можно сказать, что вывод типов в Nemerle-е опять «рулит», но откровенно говоря за такую «рулежку» можно и в бубен схлопотать если нашим макросом воспользуется кто-то довольно здоровый и невыдержанный (как я) .
Что же делать?
Выхода есть два. И оба они хорошие. Самый простой четко указать в месте применения, что мы хотим иметь дело со строкой. Для этого обсуждаемую строку макроса нужно переписать следующим образом:
<[ WriteLine("Run-time.\n Hallo, " + ($myName : string)) ]>;

Если после этого скомпилировать код, то мы получим совершенно вменяемое сообщение об ошибке:
test-001.n:5:1:5:10: error: expected string, got int in type-enforced expression: System.Int32 is not a subtype of System.String [simple require]

В принципе, этого достаточно в большинстве случаев, но душа как всегда хочет большего. Можно ли попытаться добиться этого большего?
А как же? Мы же обладаем всей мощью компиляторостроителей! Просто изменим код следующим образом:
  <[ WriteLine("Run-time.\n Hallo, " + System.Convert.ToString($myName)) ]>;

и вуаля...
Теперь наш макрос стал значительно более гибким. Компилируем:
TestMacro(1 + 2);
TestMacro(1 + 2098098);
TestMacro("Vlad");
def a = "VladD2";
TestMacro(a);

и получаем:
>build-and-run.cmd test-001
--# Compiling Macro-assembly #---
---------------------------------
--- Compiling target assembly ---
---------------------------------
Nemerle.Compiler.Parsetree.PExpr+Call
Compile-time. myName = 1 + 2
Nemerle.Compiler.Parsetree.PExpr+Call
Compile-time. myName = 1 + 2098098
Nemerle.Compiler.Parsetree.PExpr+Literal
Compile-time. myName = "Vlad"
Nemerle.Compiler.Parsetree.PExpr+Ref
Compile-time. myName = a
--------------- OK -----------------
--Run: test-001.exe
.
Run-time.
 Hallo, 3
Run-time.
 Hallo, 2098099
Run-time.
 Hallo, Vlad
Run-time.
 Hallo, VladD2
>Exit code: 0

А что же у нес теперь «под копотом»? Сейчас декомпильнем :
Console.WriteLine("Run-time.\n Hallo, " + Convert.ToString((sbyte) 3));
Console.WriteLine("Run-time.\n Hallo, " + Convert.ToString(0x2003b3));
Console.WriteLine("Run-time.\n Hallo, " + Convert.ToString("Vlad"));
string text1 = "VladD2";
Console.WriteLine("Run-time.\n Hallo, " + Convert.ToString(text1));

А что если скормить нечто, что Convert-ору не по зубам:
TestMacro(System.Console);

Все в порядке! Мы получаем довольно разумное сообщение:
test-001.n:5:11:5:25: error: none of the meanings of `System.Console' meets the type ?:

И это мы еще не использовали таких возможностей макросов как обращение к информации о типах выражений и анализу AST!

В общем, даже на таком простом примере видно насколько мощной вещью являются макросы Nemerle.

Продолжение следует.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Изучаем макросы Nemerle
От: VladGalkin Украина  
Дата: 19.02.06 18:55
Оценка: :)
Здравствуйте, VladD2, Вы писали:

Влад, помогите пожалуйста в священной борьбе в ЖЖ!
... << RSDN@Home 1.1.4 stable rev. 510>>
ДЭ!
Re[2]: Изучаем макросы Nemerle
От: WolfHound  
Дата: 19.02.06 19:36
Оценка:
Здравствуйте, VladGalkin, Вы писали:

VG>Влад, помогите пожалуйста в священной борьбе в ЖЖ!

ЖЖ в сад! Нам тут священнгой войны хватает.
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[3]: Изучаем макросы Nemerle
От: VladGalkin Украина  
Дата: 19.02.06 19:47
Оценка:
Здравствуйте, WolfHound, Вы писали:

WH>ЖЖ в сад! Нам тут священнгой войны хватает.

Не смею спорить с господином Модератором, предложение было скорее шуточное, чем серьезное.
... << RSDN@Home 1.1.4 stable rev. 510>>
ДЭ!
Re[2]: Изучаем макросы Nemerle
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.02.06 16:20
Оценка:
Здравствуйте, VladGalkin, Вы писали:

VG>Влад, помогите пожалуйста в священной борьбе в ЖЖ!


Зимы мы уже дождались. Так что зазывай всех врагов (оппонентов) сюда. И будем их мочить в с... ээ... на РСДН.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Изучаем макросы Nemerle
От: VladGalkin Украина  
Дата: 20.02.06 17:34
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Зимы мы уже дождались. Так что зазывай всех врагов (оппонентов) сюда. И будем их мочить в с... ээ... на РСДН.


"Самое главное не спугнуть. Будем брать по одному" Зазываю, линки даю, не идут.
... << RSDN@Home 1.1.4 stable rev. 510>>
ДЭ!
Re[4]: Изучаем макросы Nemerle
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.02.06 18:35
Оценка:
Здравствуйте, VladGalkin, Вы писали:

VG>"Самое главное не спугнуть. Будем брать по одному" Зазываю, линки даю, не идут.


Значит боятся. Ведь так проще жить.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Изучаем макросы Nemerle
От: Vermicious Knid  
Дата: 20.02.06 20:40
Оценка:
VG>>"Самое главное не спугнуть. Будем брать по одному" Зазываю, линки даю, не идут.
VD>Значит боятся. Ведь так проще жить.

Кое кто из них уже здесь был и успел "отличиться". Я уверен, что некий товарищ mauhuur это никто иной как забаненный(?) WinniePoh.

Такую манеру общения ни с чем не спутаешь, цитирую:

> Еще-бы все они писаны на старом добром С.
Нет, ламеришко.
....
....
Так что, глупое вы ламерьё, идите читать http://www.paulgraham.com/

И не позорьтесь, и так вы слишком уж сильно на ничтожество похожи.


Вообще хорошо, что не идут. ЖЖ-шной манеры общения здесь только и не хватало.
Re[2]: Изучаем макросы Nemerle
От: CrazyPit  
Дата: 20.02.06 20:47
Оценка: +1 :)
Здравствуйте, VladGalkin, Вы писали:

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


VG>Влад, помогите пожалуйста в священной борьбе в ЖЖ! :)


Спасибо за ссылку! Весёлый топик — Маухур, как обычно, зажигает коллектив:))
Re[6]: Изучаем макросы Nemerle
От: CrazyPit  
Дата: 20.02.06 20:51
Оценка:
Здравствуйте, Vermicious Knid, Вы писали:

VG>>>"Самое главное не спугнуть. Будем брать по одному" :)) Зазываю, линки даю, не идут. :xz:

VD>>Значит боятся. Ведь так проще жить.

VK>Кое кто из них уже здесь был и успел "отличиться". :))) Я уверен, что некий товарищ mauhuur это никто иной как забаненный(?) WinniePoh.


Похож конечно, но он в одном топики поставил Гвидо на ряду с Кнутом и Грэмом, что от Маухура слышать весьма странно.

VK>Такую манеру общения ни с чем не спутаешь, цитирую:

VK>

>> Еще-бы все они писаны на старом добром С.
VK>Нет, ламеришко.
VK>....
VK>....
VK>Так что, глупое вы ламерьё, идите читать http://www.paulgraham.com/

VK>И не позорьтесь, и так вы слишком уж сильно на ничтожество похожи.


Хотя может и он.

VK>Вообще хорошо, что не идут. ЖЖ-шной манеры общения здесь только и не хватало. :-\
Re[6]: Изучаем макросы Nemerle
От: WolfHound  
Дата: 20.02.06 22:54
Оценка: :))
Здравствуйте, Vermicious Knid, Вы писали:

VK>Вообще хорошо, что не идут. ЖЖ-шной манеры общения здесь только и не хватало.

Не волнуйся... на RSDN баня большая... на всех хватит...
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Пусть это будет просто:
просто, как только можно,
но не проще.
(C) А. Эйнштейн
Re[6]: Изучаем макросы Nemerle
От: Cyberax Марс  
Дата: 21.02.06 07:17
Оценка: :)
Vermicious Knid wrote:
> Вообще хорошо, что не идут. ЖЖ-шной манеры общения здесь только и не
> хватало.
Это не ЖЖ, это известный FIDOшник — Виталий Луговской.
Posted via RSDN NNTP Server 2.0
Sapienti sat!
Re[7]: Изучаем макросы Nemerle
От: Dog  
Дата: 21.02.06 17:50
Оценка:
>> Вообще хорошо, что не идут. ЖЖ-шной манеры общения здесь только и не
>> хватало.
C>Это не ЖЖ, это известный FIDOшник — Виталий Луговской.
это диагноз
... << RSDN@Home 1.2.0 alpha rev. 631>>
Re[6]: Изучаем макросы Nemerle
От: vvotan Россия  
Дата: 22.02.06 09:41
Оценка:
Здравствуйте, Vermicious Knid, Вы писали:

VK>Такую манеру общения ни с чем не спутаешь, цитирую:

VK>

>> Еще-бы все они писаны на старом добром С.
VK>Нет, ламеришко.
VK>....
VK>....
VK>Так что, глупое вы ламерьё, идите читать http://www.paulgraham.com/

VK>И не позорьтесь, и так вы слишком уж сильно на ничтожество похожи.


Луговский?
--
Sergey Chadov

... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Re: Микро-аддин для VS 2005
От: Oyster Украина https://github.com/devoyster
Дата: 06.03.06 18:32
Оценка: 95 (11) +1
Здравствуйте, VladD2, Вы писали:

Я всё время пользовался исключительно SciTE для работы с Nemerle, но для серьёзных проектов он как-то слабо подходит. Мне как минимум не хватает solutions/проектов, не хватает дебага и, наконец, не хватает VS 2005 IDE Я, конечно, надеюсь, что плагин для VS когда-нибудь доделают, но ждать как-то неохота.

Сегодня я прочитал вот это вот сообщение — Re[5]: Снова о Nemerle или профанация не пройдет :)
Автор: xhalt
Дата: 06.03.06
— и сделал парочку project/item templates для VS 2005. Шаблоны вполне стандартные:

Их можно установить, распаковав файлик Nemerle-VS2005-MicroAddin.zip и скопировав папочку "Templates" прямо в "My Documents\Visual Studio 2005". После этого в студии появятся проекты с префиксом "Nemerle" (во вкладке "Visual C#" под "My Templates" — это в окошке создания проекта) и project items с таким же префиксом (при добавлении нового айтема).

Для того, чтобы студия не ругалась на проекты (мол, targets не те) и для Build Action="Compile" по умолчанию для файлов с расширением .n надо ещё запустить файлик "NemerleImport.reg" из того же архива.

Самый приятный результат всех этих действий — наличие проектов и возможность дебага. Самый огромный недостаток — полное отсутствие подсветки синтаксиса (впрочем, какую-никакую подсветку можно организовать, связав расширение .n с одним из стандартных редакторов, как писал xtile
Автор: xhalt
Дата: 06.03.06
) и какого бы ни было Intellisense.

Надеюсь, будет полезно.
Re: Изучаем макросы Nemerle
От: xhalt Украина  
Дата: 07.03.06 11:40
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Единственный способ развеить скепсис — это попробовать.

+1

Мне вот (как и многим другим) понравилась э... "микроядерная архитектура"
компилятора Nemerle..

Интересно, но что помешало авторам Memerle,
реализовать ключевое слово "macro" не как часть компилятора а тоже макросом?

либо на самом себе же себе:
macro @macro( ... )
syntax ( "macro", ... )
...


либо, если сложности с self-hosting'ом, напрямую:
public class Macro : IMacro
{
  public Run (...) : PExpr  { /* здесь уже имеющимися средствами генерируем AST */}
  public SyntaxExpression (...) : ... { /* здесь описываем синтаксис*/}
    //... и т.п.
}


Или это излишнее усложнение?
... << RSDN@Home 1.2.0 alpha rev. 0>>


Предлагаю работу в Киеве
Автор:
Дата: 04.05.06
Re[2]: Изучаем макросы Nemerle
От: VladD2 Российская Империя www.nemerle.org
Дата: 07.03.06 15:04
Оценка:
Здравствуйте, xhalt, Вы писали:

X>Интересно, но что помешало авторам Memerle,

X>реализовать ключевое слово "macro" не как часть компилятора а тоже макросом?

А ты погляди исходники компилятора. Там почти все так и есть.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Изучаем макросы Nemerle
От: xhalt Украина  
Дата: 07.03.06 15:44
Оценка:
Здравствуйте, VladD2, Вы писали:
X>>реализовать ключевое слово "macro" не как часть компилятора а тоже макросом?
VD>А ты погляди исходники компилятора. Там почти все так и есть.
Дык смотрел уже. Реализация в "основном ядре". К слову, достаточно компактная и легко читаемая.
Показалось что можно ещё упростить... Впрочем, возможно только лишь показалось
... << RSDN@Home 1.2.0 alpha rev. 0>>


Предлагаю работу в Киеве
Автор:
Дата: 04.05.06
Re[4]: Изучаем макросы Nemerle
От: VladD2 Российская Империя www.nemerle.org
Дата: 07.03.06 18:01
Оценка:
Здравствуйте, xhalt, Вы писали:

X>Дык смотрел уже. Реализация в "основном ядре".


Ну, мало ли где. По сути сделано так же.

X>К слову, достаточно компактная и легко читаемая.


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

X>Показалось что можно ещё упростить... Впрочем, возможно только лишь показалось


Совершенство не достижимо, но стремиться к нему стоит.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Интеграция с IDE
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.03.06 10:44
Оценка: 34 (2)
Здравствуйте, VladD2, Вы писали:

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

Хотя имеется расширение для VS 2005, но он явно находится в очень зачаточном состоянии.

Между тем Oyster создал набор визардов
Автор: Oyster
Дата: 06.03.06
позволяющих получить минимальную поддержку VS 2005 без сложной установки расширения к студии.

От себя добавлю, что чтобы получить более менее приличную подсветку синтаксиса расширение ".n" лучше ассоциировать с С++. После этого можно создать файл "usertype.dat" в дирректории VS\VS2005\Common7\IDE\. В этот файл нужно поместить список ключевых слов разделенных концами строк:
_
abstract
and
array
as
base
catch
class
def
delegate
do
else
enum
event
extern
false
finally
for
foreach
fun
if
implements
in
interface
internal
lock
macro
match
module
mutable
namespace
new
null
out
override
params
private
protected
public
ref
sealed
static
struct
syntax
this
throw
true
try
type
typeof
unless
using
variant
virtual
void
when
where
while
assert
ignore

После перезапуска IDE будет доступна подсветка синтаксиса Nemerlr. Правда, к сожалению, она же будет доступна и в С++.

Шалоны описанные Oyster-ом лучше поместить в каталог:
%USERPROFILE%\My Documents\Visual Studio 2005\Templates\ProjectTemplates\Nemerle\

Каталог "Nemerle" при этом прийдется создать вручную.

При этом визарды появятся в ветке "Visual C#\Nemerle".

В общем-то можно сделать так чтобы в одном проекте можно было держать cs-файлы и n-файлы. При этом первые будут при компиляции конвертироваться в n-файлы с помощью утилиты cs2n.exe. Незнаю насколько это нужно, но в принципе это позволило бы, например, ползоваться некоторыми дизайнерами вроде дизайнера форм.

К сожалению cs2n.exe пользуется довольно кривым парсером C#. Так что не все файлы можно преобрзовать в Немерловские.

Если эта фича действительно нужна, могу попробовать на досуге смастерить подправленный файл проекта.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Изучаем макросы Nemerle
От: mrozov  
Дата: 10.03.06 11:41
Оценка:
Здравствуйте, VladD2, Вы писали:

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

Т.е. споткнувшись обо что-то, отметишь это как недостаток, а не будешь заниматься медитацией, как отдельные комрады?
Re[2]: Изучаем макросы Nemerle
От: Oyster Украина https://github.com/devoyster
Дата: 10.03.06 11:52
Оценка:
Здравствуйте, mrozov, Вы писали:

M>Влад, а можно надеяться, что обнаружив в Nemerle концептуальные грабли, ты возвестишь об этом общественность не менее громко и эмоционально, чем о многочисленных его достоинствах?


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


Думаю, это относится ко всем, не только к Владу. Уже спотыкались и уже сообщали — см. Снова о Nemerle или профанация не пройдет :)
Автор: IT
Дата: 15.02.06
(например, про возможность указания неймспейса или задания алиаса для first token, про { .. $list } в quoted type declaration и др.).

Поэтому не понимаю твоих опасений
Re: Интеграция с IDE
От: mogadanez Чехия  
Дата: 10.03.06 14:21
Оценка:
Здравствуйте, VladD2, Вы писали:

<skip>

что то падает оно при компиляции

в OutPut пишет

------ Rebuild All started: Project: ConsoleApplication2, Configuration: Debug Any CPU ------
C:\Program Files\Nemerle\ncc.exe /no-color /define:DEBUG;TRACE /debug+ /out:obj\Debug\ConsoleApplication2.exe /target:exe Main.n

Unhandled Exception: OutOfMemoryException.
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========
... << RSDN@Home 1.1.4 beta 6a rev. 436>>
Re[2]: Интеграция с IDE
От: Oyster Украина https://github.com/devoyster
Дата: 10.03.06 15:14
Оценка:
Здравствуйте, mogadanez, Вы писали:

M>Unhandled Exception: OutOfMemoryException.
M>========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========


Ты не первый: http://nemerle.org/bugs/view.php?id=613. Но почему это происходит, я понятия не имею — у меня всё ОК.
Re[2]: Интеграция с IDE
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.03.06 16:33
Оценка:
Здравствуйте, mogadanez, Вы писали:

M>что то падает оно при компиляции


Скорее всего у тебя компилятор более новой версии чем библиотеки. Ты или все бери из SVN-а и компилируй сам, или пользуйся бинарниками из инсталлятора.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Изучаем макросы Nemerle
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.03.06 16:33
Оценка: 1 (1)
Здравствуйте, mrozov, Вы писали:

M>Влад, а можно надеяться, что обнаружив в Nemerle концептуальные грабли, ты возвестишь об этом общественность не менее громко и эмоционально, чем о многочисленных его достоинствах?


Я чертовски не люблю проблем. И о том, что обнаруживаю сразу же сообщаю их разработчикам. Пока что они идут на уступки и устраняют проблемы:
http://nemerle.org/forum/viewforum.php?f=2&amp;sid=6254c08ffd33039c6230ad0a1cb72e40

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


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

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

Но для этого нужно получить чуть больше опыта и убедиться, что недостаток концептуален, а не является обнаруженым багом компилятора.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Изучаем макросы Nemerle
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.03.06 16:33
Оценка:
Здравствуйте, Oyster, Вы писали:

O>Думаю, это относится ко всем, не только к Владу. Уже спотыкались и уже сообщали — см. Снова о Nemerle или профанация не пройдет :)
Автор: IT
Дата: 15.02.06
(например, про возможность указания неймспейса или задания алиаса для first token, про { .. $list } в quoted type declaration и др.).


Все это недоработки в компиляторе. Мня больше смущают не всегда внятные сообщения компилятора, отсуствие комплита и т.п.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Интеграция с IDE
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.03.06 17:39
Оценка:
Здравствуйте, mogadanez, Вы писали:

M>
M>------ Rebuild All started: Project: ConsoleApplication2, Configuration: Debug Any CPU ------
M>C:\Program Files\Nemerle\ncc.exe /no-color /define:DEBUG;TRACE /debug+ /out:obj\Debug\ConsoleApplication2.exe /target:exe Main.n

M>Unhandled Exception: OutOfMemoryException.
M>========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========
M>


Опиши, плиз, конфигурацию (процессор... ОС...).
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Интеграция с IDE
От: mogadanez Чехия  
Дата: 10.03.06 20:12
Оценка:
Здравствуйте, VladD2, Вы писали:

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


M>>
M>>------ Rebuild All started: Project: ConsoleApplication2, Configuration: Debug Any CPU ------
M>>C:\Program Files\Nemerle\ncc.exe /no-color /define:DEBUG;TRACE /debug+ /out:obj\Debug\ConsoleApplication2.exe /target:exe Main.n

M>>Unhandled Exception: OutOfMemoryException.
M>>========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========
M>>


VD>Опиши, плиз, конфигурацию (процессор... ОС...).


Проблема была на работе — Dell P4 2800, wk2 Server — стоит две студии. Nemerle просто скачал и поставил, откуда другие библиотеки непонятно.

Пришел домой, все запработало с первого раза. машинка старенькая celeron1800 — w2k3 — студия только 2005
Re: Интеграция с IDE
От: mogadanez Чехия  
Дата: 10.03.06 20:36
Оценка:
Здравствуйте, VladD2, Вы писали:

<skip>

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

Если макрос поместить в проект ConsoleApp то дает ошибку
Error 1 unbound name `TestMacro2'
Re: Интеграция с IDE
От: mogadanez Чехия  
Дата: 10.03.06 22:08
Оценка:
Здравствуйте, VladD2, Вы писали:

<skip>

еще момент. при компиляции в студии не видно в Output окошке сообщений типа Compile-time.
myName = "Vlad"
Compile-time. myName = a


я нашел только один способ их там увидеть
Options->Projects and Solutions -> Build and Run-> MSBuild....verbosity
установить не менее Detailed. но тогда еще куча ненужной ботвы выводится.

может есть другие ваианты?
Re[4]: Интеграция с IDE
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.03.06 22:28
Оценка:
Здравствуйте, mogadanez, Вы писали:

M>Проблема была на работе — Dell P4 2800, wk2 Server — стоит две студии. Nemerle просто скачал и поставил, откуда другие библиотеки непонятно.


В том баг-репорте тоже wk2 фигурировала. Надо бы им об этом сообщить. А еще ничего особенного на работчей машине не было?
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Интеграция с IDE
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.03.06 22:54
Оценка:
Здравствуйте, mogadanez, Вы писали:

M>я правильно понимаю что макросы нужно всегда выносить в проект типа MacroLibrary?


Да. Макросы должны быть оформлены в виде библиотеки и подключаться к основному проекту как обычная библиотека.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Интеграция с IDE
От: VladD2 Российская Империя www.nemerle.org
Дата: 10.03.06 22:54
Оценка:
Здравствуйте, mogadanez, Вы писали:

M>я нашел только один способ их там увидеть

M>Options->Projects and Solutions -> Build and Run-> MSBuild....verbosity
M>установить не менее Detailed. но тогда еще куча ненужной ботвы выводится.

M>может есть другие ваианты?


С этим я пока не разбирался. Но думаю, что только если подправить код MSBuildTask.n увеличив приоритет вывода.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: Интеграция с IDE
От: mogadanez Чехия  
Дата: 11.03.06 05:28
Оценка:
Здравствуйте, VladD2, Вы писали:

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


M>>Проблема была на работе — Dell P4 2800, wk2 Server — стоит две студии. Nemerle просто скачал и поставил, откуда другие библиотеки непонятно.


VD>В том баг-репорте тоже wk2 фигурировала. Надо бы им об этом сообщить. А еще ничего особенного на работчей машине не было?


да там дофига всякого хлама стоит.
Re[3]: Интеграция с IDE
От: VladGalkin Украина  
Дата: 11.03.06 07:54
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Да. Макросы должны быть оформлены в виде библиотеки и подключаться к основному проекту как обычная библиотека.


От себя добавлю, что это необходимо потому, что макросы в Nemerle нельзя компилировать и использовать в одной компиляции, т.к. это связано с рядом проблем возникающих при компиляции транзитивных замыканий типов, испоьзуемых макросом. Но Nemerl'овцы пишут, что изучают подход компиляции и использования, одновременно, в одной компиляции.
... << RSDN@Home 1.1.4 stable rev. 510>>
ДЭ!
Re[4]: Интеграция с IDE
От: VladD2 Российская Империя www.nemerle.org
Дата: 11.03.06 22:40
Оценка:
Здравствуйте, VladGalkin, Вы писали:

VG>От себя добавлю, что это необходимо потому, что макросы в Nemerle нельзя компилировать и использовать в одной компиляции, т.к. это связано с рядом проблем возникающих при компиляции транзитивных замыканий типов, испоьзуемых макросом.


В языках вроде Схемы эти проблемы решены. Только вот только крышу рвать начинает.

VG>Но Nemerl'овцы пишут, что изучают подход компиляции и использования, одновременно, в одной компиляции.


Мне кажется это лишнее. Лучше бы развили идею рантайм-кодогенерации.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Интеграция с IDE
От: Аноним  
Дата: 17.03.06 08:51
Оценка:
Здравствуйте, VladD2, Вы писали:

M>>Unhandled Exception: OutOfMemoryException.


VD>Опиши, плиз, конфигурацию (процессор... ОС...).


А для Немерла нет чего-нибудь отладочного, типа Jedi CodeLib или Эврики для Delphi?

Чтобы при исключении раскручивал стек и версии подключенных библиотек выводил ?
Re[4]: Интеграция с IDE
От: VladD2 Российская Империя www.nemerle.org
Дата: 17.03.06 19:38
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>А для Немерла нет чего-нибудь отладочного, типа Jedi CodeLib или Эврики для Delphi?


А>Чтобы при исключении раскручивал стек и версии подключенных библиотек выводил ?


Это и так делается. Приложение то менеджед.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Изучаем макросы Nemerle
От: ie Россия http://ziez.blogspot.com/
Дата: 31.03.06 19:39
Оценка:
В процессе изучения этих самых макросов, столкнулся с задачей. Хочется понять есть ли у класса, к которому применяется макрос, тот или иной атрибут. И если есть, выдернуть из него значения. Проблема в том, что для модифицируемого класса не могу получить Type:

    [Nemerle.MacroUsage(Nemerle.MacroPhase.BeforeInheritance,
                        Nemerle.MacroTargets.Class)]
    macro TestMacro(tb : TypeBuilder)
    {
        def c = tb.SystemType;
        System.Console.WriteLine(c.ToString()); // выпадаем с NullReference
    }


Может уже сталкивались с проблемой вытаскивания атрибутов? Как она решается? И почему все-таки не пашет такой код — баг?
... << RSDN@Home 1.2.0 alpha rev. 0>>
Превратим окружающую нас среду в воскресенье.
Re[2]: Изучаем макросы Nemerle
От: Vermicious Knid  
Дата: 31.03.06 22:05
Оценка: 51 (3)
Здравствуйте, ie, Вы писали:

ie> Проблема в том, что для модифицируемого класса не могу получить Type:


Естественно. Тип же еще не существует в скомпилированном виде.

ie>Может уже сталкивались с проблемой вытаскивания атрибутов? Как она решается? И почему все-таки не пашет такой код — баг?


Да легко решается:

    def attributes = tb.GetModifiers().GetCustomAttributes();
    foreach (attr in attributes)
        Console.WriteLine(attr.ToString());

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

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

using System;
using Nemerle.Macros;
using Nemerle.Compiler;

macro DissectAttributes(typeName)
{
    def typer = ImplicitCTX();

    def lookupType(tn)
    {
        match(Macros.GetIfIsType(typer.Env, tn))
        {
            | Some(typeInfo) => typeInfo
            | None => null
        }        
    }

    def getAttrType(attrName)
    {
        def attrType = lookupType(<[ $(attrName : name) ]>);
        if (attrType == null)
        {
            def altName = attrName.NewName(attrName.ToString() + "Attribute");
            lookupType(<[ $(altName : name) ]>)
        }
        else
            attrType
    }

    def attributes = lookupType(typeName).GetModifiers().GetCustomAttributes();
    def serializable = lookupType(<[ System.SerializableAttribute ]>);
    foreach (attr in attributes)
    {
        | <[ $(attrName : name) ]> =>
            def attrType = getAttrType(attrName);
            Console.WriteLine("expr: {0}, type: {1}",
                              attr, attrType.ToString());

            when (serializable.Equals(attrType))
                Console.WriteLine("yay! it's serializable");

        | <[ $(attrName : name)(.. $attrParams) ]> =>
            def attrType = getAttrType(attrName);
            Console.WriteLine("expr: {0}, type: {1}, params: {2}", 
                              attr, attrType.ToString(), attrParams);
        | _ => Console.WriteLine("should not happen, attr was {0}", attr);
    }
    <[ ]>
}

Пример использования:
using System;

[AttributeUsage(AttributeTargets.Class, Inherited=true, AllowMultiple=true)]
public class FooAttribute : Attribute
{
    public this(_name : string)  {}
}


[AttributeUsage(AttributeTargets.Class, Inherited=true, AllowMultiple=true)]
public class Bar : Attribute
{
    public this(_foo : string, _bar : int)  {}
}

[Foo("foobar"), Bar("hellothere!", 2), Serializable]
public class Test
{
    static Main() : void
    {
        DissectAttributes(Test);
    }
}

Результат:
expr: Serializable, type: System.SerializableAttribute
yay! it's serializable
expr: Bar ("hellothere!", 2), type: Bar, params: ["hellothere!", 2]
expr: Foo ("foobar"), type: FooAttribute, params: ["foobar"]

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

Сама концепция макросов и квази-цитирования очень хороша, но вот api компилятора весьма убогое на мой взгляд. В любом случае если язык приобретет популярность, то api поменяется(или наооборот ). И переписывать код макросов будет гораздо проще, если он будет более читабелен.
Re[2]: Изучаем макросы Nemerle
От: VladD2 Российская Империя www.nemerle.org
Дата: 31.03.06 22:22
Оценка:
Здравствуйте, ie, Вы писали:

ie>В процессе изучения этих самых макросов, столкнулся с задачей. Хочется понять есть ли у класса, к которому применяется макрос, тот или иной атрибут. И если есть, выдернуть из него значения. Проблема в том, что для модифицируемого класса не могу получить Type:


Сейчас разбираться нет времени, но настараживает стадия BeforeInheritance. В этот момент типы еще не должны быть доступны в полном объеме. Нужно попробовать более поздние стадии. Поищи описание на сайте Немерла.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Изучаем макросы Nemerle
От: VladD2 Российская Империя www.nemerle.org
Дата: 02.04.06 17:02
Оценка:
Здравствуйте, Vermicious Knid, Вы писали:

VK> Еще невозможно получить таким образом макро-атрибуты(у меня кстати вообще были сомнения, что это возможно).


Погади. Ведь метаатрибуты просто вызываются компилятором. Причем происходит это на разных стадиях. Так?

Раз так, то можно на ранних стадиях читать информацию переданную в метаатрибуты и складывать ее, например, в хеш-таблицу. На следующей стадии можно эту информацию использовать.
... << RSDN@Home 1.2.0 alpha rev. 637>>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Изучаем макросы Nemerle
От: ie Россия http://ziez.blogspot.com/
Дата: 03.04.06 10:29
Оценка: 33 (2)
Здравствуйте, Vermicious Knid, Вы писали:

ie>>Может уже сталкивались с проблемой вытаскивания атрибутов? Как она решается? И почему все-таки не пашет такой код — баг?


VK>Да легко решается:


VK>
VK>    def attributes = tb.GetModifiers().GetCustomAttributes();
VK>    foreach (attr in attributes)
VK>        Console.WriteLine(attr.ToString());
VK>

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

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

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


У класса Modifiers есть публичное поле macro_attrs, которое по всей видимости и есть список макро-атрибутов. Я не проверял.

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


Мне все же захотелось работать с атрибутами как с атрибутами, поэтому доработал твой пример и вот что собственно получилось:

Хэлпер, инстанциирующий атрибуты и пара тестовых атрибутов:
    [Record]
    public class MyAttrAttribute : Attribute
    {
        [Accessor]
        mutable _name : string = "(Default)";
        public this(){}
    }
    [Record]
    public class MyAttr2Attribute : Attribute
    {
        [Accessor]
        mutable _name : string;
        [Accessor]
        mutable _number : int;
        [Accessor]
        mutable _char : char;
        [Accessor]
        mutable _money : decimal;
        public this(){}
    }

    internal module AttributeHelper
    {
        public GetAttributes(tb : TypeBuilder) : array[Attribute]
        {
            def env = tb.GlobalEnv;

            def lookupType(tn)
            {
                match(Macros.GetIfIsType(env, tn))
                {
                    | Some(typeInfo) => typeInfo
                    | None => null
                }
            }
            def getAttrType(attrName)
            {
                def attrType = lookupType(<[ $(attrName : name) ]>);
                if (attrType == null)
                {
                    def altName = attrName.NewName(attrName.ToString() + "Attribute");
                    lookupType(<[ $(altName : name) ]>)
                }
                else
                    attrType
            }
            def getParamValue(param)
            {
                def resolveLiteralInteger(lit : Literal.Integer)
                {
                    def val = lit.val;
                    def is_negative = lit.is_negative;
                    def treat_as = lit.treat_as;


                    def mul = if (is_negative) -1 else 1;
                    
                    // смотриться убого, надо понять как это все переписать на match
                    if (treat_as.Equals(InternalType.Byte))
                        (val :> Byte) : object;
                    else if (treat_as.Equals(InternalType.UInt16))
                        (val :> UInt16) : object
                    else if (treat_as.Equals(InternalType.UInt32))
                        (val :> UInt32) : object
                    else if (treat_as.Equals(InternalType.UInt64))
                        val : object
                    else if (treat_as.Equals(InternalType.SByte))
                        ((val :> SByte)*mul) : object
                    else if (treat_as.Equals(InternalType.Int16))
                        ((val :> Int16)*mul) : object
                    else if (treat_as.Equals(InternalType.Int32))
                        ((val :> Int32)*mul) : object
                    else if (treat_as.Equals(InternalType.Int64))
                        ((val :> Int64)*mul) : object
                    else
                        throw Exception("Should never be throwed...")
                }
                def resolveLiteral(param)
                {
                    match (param)
                    {
                        | lit is PExpr.Literal =>
                            match (lit.val)
                            {
                                | Literal.Void => null : object
                                | Literal.Null => null : object
                                | Literal.String (val) => val : object
                                | Literal.Float (val) => val : object
                                | Literal.Double (val) => val : object
                                | Literal.Decimal (val) => val : object
                                
                                | Literal.Integer (_, _, _) => 
                                    resolveLiteralInteger(lit.val :> Literal.Integer)
                                
                                | Literal.Bool (val) => val : object
                                | Literal.Char (val) => val : object
                                | Literal.Enum (val, _) => val : object

                                | _ => throw Exception("This literal is not supported yet...")
                            }
                        | _ => throw Exception("Should never be throwed...")
                    }
                }

                resolveLiteral(param);
            }

            def attributes = tb.GetModifiers().GetCustomAttributes();
            mutable resolvedAttributes = [];

            foreach (attr in attributes)
            {
                | <[ $(attrName : name) ]> =>
                    def attrTypeName = getAttrType(attrName).ToString();
                    def attrObj = 
                        Activator.CreateInstance(null, attrTypeName)
                            .Unwrap() :> Attribute;
                    resolvedAttributes = attrObj :: resolvedAttributes;

                | <[ $(attrName : name)(.. $attrParams) ]> =>
                    def attrTypeName = getAttrType(attrName).ToString();
                    def cctorParams = attrParams.Map(getParamValue).ToArray();
                    def attrObj =
                        Activator
                            .CreateInstance(null, attrTypeName, false, 
                                            BindingFlags.CreateInstance, null,
                                            cctorParams, null, null, null)
                            .Unwrap() :> Attribute;
                    resolvedAttributes = attrObj :: resolvedAttributes;

                | _ => throw Exception("Should not happen");
            }

            resolvedAttributes.ToArray();
        }

    }


И собственно употребление этого хэлпера:
    [Nemerle.MacroUsage(Nemerle.MacroPhase.BeforeInheritance,
                        Nemerle.MacroTargets.Class,
                        Inherited = true)]
    macro TestMacro(tb : TypeBuilder)
    {
        def attrs = AttributeHelper.GetAttributes(tb);
        foreach (attr in attrs)
        {
            | a is MyAttrAttribute => 
                Console.WriteLine("MyAttrAttribute(\"{0}\")", a.Name);
            | a is MyAttr2Attribute => 
                Console.WriteLine("MyAttr2Attribute(\"{0}\", {1}, '{2}', {3})",
                                  a.Name, a.Number, a.Char, a.Money);
            | _ => 
                Console.WriteLine("This attribute isn't supported...");
        }
    }


После применения к классу:
    [TestMacro]
    [MyAttr("TestName")]
    [MyAttr2("TestName2", 23, 't', 34.3434m)]
    class TestMacroTest
    {}


Получаем:

MyAttrAttribute("TestName")
MyAttr2Attribute("TestName2", 23, 't', 34.3434)


Однако, скомпилировать такой пример полностью не удалось. Макросы выполнились нормально, но атрибут MyAttr2 компилятору не понравился, упали с исключением:

error : internal compiler error: got ArgumentException (An invalid type was used as a custom attribute constructor argument, field or property.)
at System.Reflection.Emit.CustomAttributeBuilder.InitCustomAttributeBuilder(ConstructorInfo con, Object[] constructorArgs, PropertyInfo[] namedProperties, Object[] propertyValues, FieldInfo[] namedFields, Object[] fieldValues)
at Nemerle.Compiler.AttributeCompiler.do_compile(GlobalEnv env, TypeBuilder ti, TypeInfo attr, list`1 parms)
at Nemerle.Compiler.AttributeCompiler.CompileAttribute(GlobalEnv env, TypeBuilder ti, PExpr expr)
at Nemerle.Compiler.Modifiers.SaveCustomAttributes(TypeBuilder ti, Function`3 adder)
at Nemerle.Compiler.TypeBuilder.EmitImplementation()
at Nemerle.Compiler.TypesManager._N_emit_impl_23651.apply_void(TypeBuilder _N_23650)
at Nemerle.Compiler.TypesManager._N_maybe_f_24088.apply_void(TypeBuilder _N_24087)
at Nemerle.Collections.List.Iter['a](list`1 l, FunctionVoid`1 f)
at Nemerle.Core.list`1.Iter(FunctionVoid`1 f)
at Nemerle.Compiler.TypesManager.Iter(list`1 builders, FunctionVoid`1 f)
at Nemerle.Compiler.TypesManager.compile_all_tyinfos(Boolean aux_phase)
at Nemerle.Compiler.TypesManager._N__N_l23232_23319.apply_void()
at Nemerle.Compiler.Solver.Enqueue(FunctionVoid action)
at Nemerle.Compiler.Passes.Run()
at Nemerle.CommandlineCompiler.MainClass.main_with_catching()


По выделенному я понял, что компилятор сам компилит атрибуты, но как он это делает еще не глянул, пока нет времени

VK>Вообще этот пример и другие примеры из моего личного опыта показывают, что для разработки серьезных макросов неизбежно придется писать удобоваримую обертку над api компилятора. Может быть даже макро-библиотеку для написания макросов.


+1

VK>Сама концепция макросов и квази-цитирования очень хороша, но вот api компилятора весьма убогое на мой взгляд. В любом случае если язык приобретет популярность, то api поменяется(или наооборот ). И переписывать код макросов будет гораздо проще, если он будет более читабелен.


+1
... << RSDN@Home 1.1.4 beta 7 rev. 447>>
Превратим окружающую нас среду в воскресенье.
Re[4]: Изучаем макросы Nemerle
От: Vermicious Knid  
Дата: 04.04.06 23:07
Оценка: 15 (1)
Здравствуйте, ie, Вы писали:

ie>У меня как-раз было огромное желание работать с атрибутами штатным способом. Идея была в том, чтобы использовать атрибуты, как управляющую состовляющую макросов.


Понятно. Посмотрел твой код. Странно как-то ты работаешь с литералами. Нужно что-то вроде этого:
match(lit) // где lit это один из подтипов PExpr, т.е. выражение
{
  // void
  <[ () ]> => // ...
  <[ null ]> => // ...
  <[ $(n : int) ]> => // ...
  <[ $(str : string) ]> => // ...
}

ie>У класса Modifiers есть публичное поле macro_attrs, которое по всей видимости и есть список макро-атрибутов. Я не проверял.

Это по документации. На деле macro_attrs это внутреннее(aka internal) поле. Вообще документация кроме того, что она слабая, она еще и устаревшая. Так что лучшая документация на сегодняшний день это исходники компилятора.

ie>Мне все же захотелось работать с атрибутами как с атрибутами, поэтому доработал твой пример и вот что собственно получилось:


Ясно. Рекомендую отправить баг-репорт. По поводу работы с атрибутами как с атрибутами — на самом деле если не не брать в расчет сложности с пространствами имен, то работа с атрибутами как с выражениями не намного сложнее. Приведу пример:

foreach(attr in attributes)
{
  | <[ Serializable ]> => //...
  | <[ MyAttr($(money : decimal), $(name : string)) ]> => Console.WriteLine($"$money $name");
}

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

1) если мы линкуем обычные типы из макро-библиотеки, то притягиваем к компилируемой сборке зависимость в виде компилятора

2) невозможно будет работать с атрибутами определяемыми в компилируемых в данный момент исходников

Можно конечно использовать и совершенно иной подход, который частенько используется(мною и в стандартных макросах) для целей схожих с твоими. Вот примитивный пример(не очень полезный правда ):
using System;
using Nemerle.Macros;
using Nemerle.Compiler;
using Nemerle.Collections;

namespace PlainWeird
{
    internal variant WeirdAttribute
    {
        | Bad  { money : decimal }
        | Good { name : string   }
        | Ugly { name : string; number : int; money : decimal }
    }

    internal module WeirdCache
    {
        // можно и по TypeBuilder кэшировать, но:
        // 1) TypeInfo.FullName обязано обеспечивать уникальное имя типа
        // 2) я не уверен, что у нас всегда будет доступ 
        //    к TypeBuilder классов(в отличие от TypeInfo)
        private AttributeCache : Hashtable[string, list[WeirdAttribute]] = Hashtable();

        public AddAttribute(typeInfo : TypeInfo, attribute : WeirdAttribute) : void
        {
            def fullName = typeInfo.FullName;
            AttributeCache[fullName] = attribute :: AttributeCache[fullName];
        }

        public ContainsAttributes(typeInfo : TypeInfo) : bool
        {
            AttributeCache.Contains(typeInfo.FullName)
        }

        public GetAttributes(typeInfo : TypeInfo) : list[WeirdAttribute]
        {
            AttributeCache[typeInfo.FullName]
        }
    }
    
    [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance, 
                         Nemerle.MacroTargets.Class)]
    macro WeirdlyBad(tb : TypeBuilder, money : decimal)
    {
        WeirdCache.AddAttribute(tb, WeirdAttribute.Bad(money));
    }

    [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance, 
                         Nemerle.MacroTargets.Class)]
    macro WeirdlyGood(tb : TypeBuilder, name : string)
    {
        WeirdCache.AddAttribute(tb, WeirdAttribute.Good(name));    
    }

    [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeInheritance, 
                         Nemerle.MacroTargets.Class)]
    macro WeirdAndUgly(tb : TypeBuilder, name : string, number : int, money : decimal)
    {
        WeirdCache.AddAttribute(tb, WeirdAttribute.Ugly(name, number, money));    
    }

    // если я не ошибаюсь можно определять несколько макросов с одним названием,
    // если они отличаются фазами и/или целями применения
    [Nemerle.MacroUsage (Nemerle.MacroPhase.BeforeTypedMembers, 
                         Nemerle.MacroTargets.Class)]
    macro WeirdlyBad(_tb : TypeBuilder, _money : decimal)
    {
        // сделать что-нибудь нехорошее на другой стадии
    }

    macro IsItWeird(typeName)
    {
        def typer = ImplicitCTX();
        def lookupType(tn)
        {
            match(Macros.GetIfIsType(typer.Env, tn))
            {
                | Some(typeInfo) => typeInfo
                | None => null
            }        
        }
        def t = lookupType(typeName);
        when (t == null)
            Message.FatalError($"Expected user-defined type name, got '$typeName'");
        when (WeirdCache.ContainsAttributes(t))
        {
            Console.WriteLine($"$(t.FullName):");
            foreach(attr in WeirdCache.GetAttributes(t))
            {
                | Bad(money) => Console.WriteLine($"    WeirdlyBad($money)")
                | Good(name) => Console.WriteLine($"    WeirdlyGood($name)")
                | Ugly(name, number, money) =>
                    Console.WriteLine($"    WeirdAndUgly($name,$number,$money)")
            }
            Console.WriteLine();
        }
        <[ ]>
    }
}


Пример использования:
using PlainWeird;

namespace Kansas
{
    [WeirdAndUgly("Billy The Kid", 1, 50000.0m), WeirdlyBad(20000.0m)]
    class BillyTheKid
    {
        public this()
        {
            IsItWeird(Sheriff);
            IsItWeird(Texas.Sheriff);
            IsItWeird(Texas.UnknownBandit);
        }
    }

    [WeirdlyGood("Tony Smith")]
    class Sheriff
    {
    }
}

namespace Texas
{
    [WeirdAndUgly("Joe Hulligan", 1, 35000.0m), WeirdlyBad(35000.0m)]
    class JoeHulligan
    {
    }

    [WeirdAndUgly("Billy The Kid #2", 2, 30000.0m)]
    class BillyTheKid
    {
    }

    [WeirdlyBad(3000.0m)]
    class UnknownBandit
    {
    }

    [WeirdlyGood("John Smith")]
    class Sheriff
    {
    }

}

IsItWeird(Kansas.BillyTheKid);
IsItWeird(Texas.BillyTheKid);
IsItWeird(Texas.JoeHulligan);
IsItWeird(IncomprehensibleIdiot);


А вот что компилятор выдаст в консоль:
Kansas.Sheriff:
    WeirdlyGood(Tony Smith)

Texas.Sheriff:
    WeirdlyGood(John Smith)

Texas.UnknownBandit:
    WeirdlyBad(3000.0)

Kansas.BillyTheKid:
    WeirdlyBad(20000.0)
    WeirdAndUgly(Billy The Kid,1,50000.0)

Texas.BillyTheKid:
    WeirdAndUgly(Billy The Kid #2,2,30000.0)

Texas.JoeHulligan:
    WeirdlyBad(35000.0)
    WeirdAndUgly(Joe Hulligan,1,35000.0)

attrtest.n:49:1:49:10: Expected user-defined type name, got 'IncomprehensibleIdiot'
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.