Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, para, Вы писали:
P>>списком состояний, начальным состоянием и функции перехода, заданной: P>>например,
VD>Для людей важно просто и наглядно задать описание автомата. По сему совершенно не важно чем там в теории определяется автомат. Важно иметь язык который наиболее интуитивно позволит описать этот автомат. Я привел один из возможных языков. Описывать автоматы в виде таблиц очень неудобно. Таблицей можно описать только ДКА. А люди мысленно обычно создают НКА, а НКА не возможно описать с помощью простой матрицы. Спорить на эту тему я не хочу (это не тот форум). Но в принципе такие языки как регулярные выражения и грамматики (BNF, PEG) — это идилальные высокооуровневые языки. Почти для любой задачи можно придумать некоторый высокоуровневый язык который будет лучше любого описания КА. Именно по то этому людям обычно не интересны ДСЛ-и описывающие КА. Но если нужно формировать именно КА, то язык вроде приведенного мной (или использованный в БУ) очень удобен для этого. А разные статистические данные можно генерировать в процессе компиляции макросов. Скажем тот же список переходов в некоторое состояние построить не проблема. Можно даже диаграмму нарисовать.
Вообще комплексную логику на иерархических автоматах можно легко нарисовать, тут таблицы наверное усложнять декларируемость описания автомата, вопрос стоит только как проще и быстрее сделать автомат из описания, без лишней работы, вышеприведенный язык DSL мне кажется более подходит для прикладного применения, с помощью макросов на этапе компиляции можно делать и преобразования и анализ как сказал Владислав, мне кажется очень здесь замечательное применение это анализ автомата и генерация, можно сделать компилируемый автомат который генерирует только последовательность вызовов действий с помощью делегатов на основании иерархии и получает наиболее оптимизированный код, тогда никакие внешние библиотеки не будут нужны, описание автомата просто преобразуется в последовательность вызовов функций комплексной логики, как реакцию на события. Само же описание наверное можно получить из диаграммы, в UML редакторах есть функция экспорта XML, которое можно преобразовать в DSL и так далее.
Здравствуйте, CodingUnit, Вы писали:
CU>Задача такова, есть библиотека описания автоматов состояний в коде аналогичным в UML, проблема в том что описание структуры автомата сложно читаемое и создаваемое вручную, хотелось бы автоматизировать этот процессс за счет макросов и DSL, я уже видел решение подобной задачи в Boo, но хотелось бы попробовать сделать такое решение на Nemerle, под нужную библиотеку, там имеются разные процедуры для описания иерархии автомата, хотелось бы например сделать иерархическое визуальное описание иерархии, например:
CU>
CU>[statemachine]
CU>class TestFSM
CU>{
CU> [state]
CU> class Top
CU> {
CU> [state]
CU> class Start
CU> {
CU> [state]
CU> class Sub
CU> {
CU> entry() : void
CU> {
CU> // действие при входе
CU> }
CU> exit() : void
CU> {
CU> // действие при выходе
CU> }
CU> }
CU> }
CU> [state]
CU> class Next
CU> {
CU> transition("Sub",UserEvt) // описание перехода
CU> }
CU> }
CU>}
CU>
Примерно ясно. А почему решено именно классами их описывать?
CU>как то так, на Boo там вообще абстрактно описали автомат, типа
CU>
CU>state @Parked:
CU> when @EngineStarted >> @IdleInNeutral
CU>state @IdleInNeutral:
CU> when @EngineKilled >> @Parked
CU> when @GearEngaged >> @IdleInGear
CU>state @IdleInGear:
CU> when @GearDisengaged >> @IdleInNeutral
CU> when @GasApplied >> @RacingAlong
CU>state @RacingAlong:
CU> when @BrakeApplied >> @IdleInGear
CU> when @CarCrashedIntoTree >> @CarDestroyed
CU>state @CarDestroyed
CU>
Такое описание лично мне намного больше нравится. Ну, может быть можно выбрать немного другой синтаксис, чтобы урпостить себе жизнь и сделать его более похожим на основной синтаксис Немерла, но что-то вроде того.
Если бы я решал подобную задачу, то скорее всего я выбрал бы следующий подход:
1. Каждый автомат я описывал бы как отдельный класс.
2. Список состояний я описывал бы в метаатрибуте которым помечал бы этот класс.
3. Синтаксис сделал бы похожим на описание локальных функций.
4. Реакции на переходы описывал бы как методы внутри этого класса.
Вот как мог бы выглядеть приведенный выше автомат:
[FsmDef(
{
state Parked
{
| EngineStarted => IdleInNeutral
}
state IdleInNeutral
{
| EngineKilled => Parked
| GearEngaged => IdleInGear
}
state IdleInGear
{
| GearDisengaged => IdleInNeutral
| GasApplied => RacingAlong
}
state RacingAlong
{
| BrakeApplied => IdleInGear
| CarCrashedIntoTree => CarDestroyed
}
state CarDestroyed { }
}
)]
class MyFsm
{
// обарботчик события перехода
EnterIn_IdleInGear_State(previosState : MyFsmState) : void
{
... код обработки
}
...
}
CU>хотелось бы что то подобное создать и переводить все это на этапе компиляции в вызовы соответствующих методов в библиотеке автомата для инициализации, главная проблема в легком описании на каком то DSL или псевдоязыке, метааттрибутов, какие могут быть возможнные решения?
Выше я описал возможный вариант.
Для реализации данного подхода нужно описать и реализовать два макроса:
1. FsmDef — метаатрибут. В нем должна производиться основная работа по построению конечного автомата.
2. state — макрос уровня выражения. Нужен только для того, чтобы немерле мог воспринимать синтаксис описания состояния — "state имяСостояния список состояний".
Вот как могут выглядеть заглушки (т.е. без реализации) этих макросов:
[Nemerle.MacroUsage(Nemerle.MacroPhase.BeforeTypedMembers, Nemerle.MacroTargets.Class)]
macro FsmDef(type_builder : TypeBuilder, body)
{
}
macro State(stateName, body)
syntax("state", stateName, body)
{
<[ () ]> // В этом макросе нам ничего делать не надо. Он нужет только чтобы немерле "пропустил" наш синтаксис.
}
Понимаю, что без опыта создания макросов распознать столь не тривиальную структуру будет не просто. По этому я потратил час, чтобы набросать примерную реализацию макросов. Вот что у меня получилось...
Сами макросы:
using Nemerle;
using Nemerle.Compiler;
using Nemerle.Compiler.Parsetree;
using System.Diagnostics;
namespace MacroLibrary2
{
[Nemerle.MacroUsage(Nemerle.MacroPhase.BeforeTypedMembers, Nemerle.MacroTargets.Class)]
macro FsmDef(type_builder : TypeBuilder, body)
{
Helper.MakeFsm(type_builder, body);
}
macro State(stateName, body)
syntax("state", stateName, body)
{
_ = stateName; // говорим компилятору, что мы намеренно не желаем использовать параметр
_ = body; // тоже самое
<[ () ]> // В этом макросе нам ничего делать не надо. Он нужен только чтобы немерле "пропустил" наш синтаксис.
}
module Helper
{
public MakeFsm(ty : TypeBuilder, body : PExpr) : void
{
def makeTransition(transitionDef : MatchCase) : void
{
Message.Hint(transitionDef.Location, $" Events=$(transitionDef.patterns) Transition=$(transitionDef.body)")
}
def makeSate(stateDef : PExpr) : void
{ // ncc заворачивает обращение к макросу в конструкцию PExpr.MacroCall...
| PExpr.MacroCall(name, _, parms) when name.Id == "state" =>
match (parms)
{ // параметры (код передаваемый в них) макроса заворачиваются в SyntaxElement.Expression.
// Пользователь должен передать два параметра...
// имя и пустую группу...
| [Expression(<[ $stateName ]>), Expression(<[ { } ]>)] =>
Message.Hint(stateName.Location, $"Распознано описание состояния '$stateName' (без переходов!)");
// или имя и список вхождений оператора match который компилятор превращает в полноценный оператор match...
| [Expression(<[ $stateName ]>), Expression(<[ match ($_) { ..$transitions } ]>)] =>
Message.Hint(stateName.Location, $"Распознано описание состояния '$stateName'. Переходы:");
foreach (transitionDef in transitions)
makeTransition(transitionDef);
| [Expression(<[ $_ ]>), Expression(<[ { $x } ]>)] =>
Message.Error(x.Location, "Ожидается описание переходов в формате: { | Event => State | Event => State ... }");
| _ =>
Message.Error(stateDef.Location,
"Ожидается описание состояния в формате: state StateName { transitions }");
}
| _ =>
Message.Error(stateDef.Location,
"Ожидается описание состояния в формате: state StateName { transitions }");
}
match (body)
{
// описания состояний заключены в группу (фигурные скобки)
| <[ { ..$states } ]> => // матчим список выражений описывающих состоянияforeach (state in states)
makeSate(state);
| _ =>
Message.Error(body.Location,
"Ожидается список состоянии в формате { state1 state2 ... }");
}
}
}
}
Это только код распознавания. В нем нет построения самого КА и генерации кода его реализующего.
Вместо этого я просто выдаю подсказки (которые можно увидеть в окне Output в VS). Так что просто ищи код "Message.Hint" и думай как заменить его на нечто действительно полезное.
Пример использования:
[FsmDef(
{
state Parked
{
| EngineStarted => IdleInNeutral
}
state IdleInNeutral
{
| EngineKilled => Parked
| GearEngaged => IdleInGear
}
state IdleInGear
{
| GearDisengaged => IdleInNeutral
| GasApplied => RacingAlong
}
state RacingAlong
{
| BrakeApplied => IdleInGear
| CarCrashedIntoTree => CarDestroyed
}
state CarDestroyed { }
}
)]
class MyFsm
{
// обработчик события перехода
EnterIn_IdleInGear_State() : void
{
}
}
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, тут встала проблема написания сложного кода автоматически, за основу взяты макросы Nemerle, при это когда создаются новые ключевые слова их нельзя использовать в пространстве имен или в классе, видимо только в методе, иначе пишется сообщение об ошибке, так ли это?:
namespace TestFSM
{
// ошибка пишет что требуется тип, макрос определен правильно
statemachine Test
{
}
class TestClass
{
// тоже самое
statemachine Test
{
}
}
}
Еще встала другая проблема, надо определить во время компиляции в макросе перечисление на основе кода, например так:
[MacroUsage(MacroPhase.BeforeTypedMembers,MacroTargets.Class,Inherited=true)]
macro statemachine(ty : TypeBuilder)
{
// определяем перечисление, нормально
def stateid=ty.DefineNestedType(<[ decl:
public enum StateID
{
}
]>);
..
def states : list[TypeBuilder]= ty.DeclaredNestedTypes; // получаем лист TypeBuilder-ов для внутренних типов
// для каждого типа добавляем имя в качестве константы к перечислению
states.Iter( x => stateid.Define(<[ $(x.Name)]>)); // не компилируется
// также просто если попробовать
stateid.Define(<[decl: | ABC ]>); // компилируется, но при выполнении ничего не происходит, новый член не добавляется
}
stateid.Compile(); // компилируем тип
// при этом если написать сразу список членов, то он определяется правильно:
def stateid=ty.DefineNestedType(<[ decl:
public enum StateID
{
| ABC
| BCD
}
]>); // если бы была возможность как то в цитату вставить строки с описанием членов
Вопрос такой, как определить перечисление в макросе и добавить как подтип в классе? Как добавить динамически в макросе элементы в список перечисления, есть ли такая возможность?
И последний вопрос, если писать DSL то потребуется много ключевых слов, при этом они как то должны взаимодействовать между собой, проходит синтаксические и семантические проверки, для этого надо чтобы текст разбирался и получалось некое описание на DSL, для этого нужен какой то объект который хранит в себе все состояние описанной структуры, то есть должен быть доступен на этапе компиляции и при вызове макроса через помеченные объекты или описание в методе, добавлять информацию в этот объект, то есть должен создаваться один раз и жить в остальное время. Еще может потребоваться некоторые члены удалять, которые не несут в себе никакой информации для приложения, например классы для описания вложенной иерархии нужные макросу, но ненужные в рантайме, было бы хорошо удалить чтобы они были не видны, но TypeBuilder допускает объявлять члены, но не допускает удалять и многие внутренние поля компилятора read-only, можно ли решить как то эту задачу?
Re: Написание DSL на Nemerle и создание Enum в макросах
CU>namespace TestFSM
CU>{
CU>// ошибка пишет что требуется тип, макрос определен правильно
CU>statemachine Test
CU>{
CU>}
Так, действительно, в текущей версии делать нельзя.
Но можно делать например так:
class Test statemachine
{
}
Ну, или в виде атрибута.
В обоих случаях нужно создавать макроатрибут, только в первом кроме того нужно объявить свой синтаксис.
В качестве примерами я написал небольшой макрос "test" который добавляет реализацию метода к классу. Собственно код макроса:
using Nemerle;
using Nemerle.Compiler;
namespace MacroLibrary2
{
[Nemerle.MacroUsage(
Nemerle.MacroPhase.BeforeInheritance,
Nemerle.MacroTargets.Class // указываем, что макрос - это макроатрибут уровня класса
)]
macro @MyTest(type_builder : TypeBuilder)
syntax("test") // задаем синтаксис
{
Helper.MyTest(type_builder);
}
module Helper
{
public MyTest(type_builder : TypeBuilder) : void
{
// добавляем в класс которому применен этот макрос реализацию метода "Test() : void"
_ = type_builder.DefineWithSource(
<[ decl:
public Test() : void
{
System.Console.WriteLine("The test happen! :)");
}
]>);
}
}
}
А вот использование этого макроса:
using System;
using System.Console;
using Nemerle.Utility;
using MacroLibrary2;
class A test // собственно вот оно
{
}
module Program
{
Main() : void
{
def a = A();
a.Test(); // а это использование сгенерированного метода.
_ = ReadLine();
}
}
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Написание DSL на Nemerle и создание Enum в макросах
Да генерировать методы легко, хорошо что синтаксис можно добавить после класса, но не могли бы Вы Владислав написать есть ли возможность создать в классе перечисление и добавить в него имена констант на основе динамических данных, как я описал здесь, почему не компилируется определение перечисления и не добавляется даже статическое константное поле в перечисление, есть какие то ограничения у компилятора?:
MacroUsage(MacroPhase.BeforeTypedMembers,MacroTargets.Class,Inherited=true)]
macro statemachine(ty : TypeBuilder)
{
// определяем перечисление, нормально
def stateid=ty.DefineNestedType(<[ decl:
public enum StateID
{
}
]>);
..
def states : list[TypeBuilder]= ty.DeclaredNestedTypes; // получаем лист TypeBuilder-ов для внутренних типов
// для каждого типа добавляем имя в качестве константы к перечислению
states.Iter( x => stateid.Define(<[ | $(x.Name)]>)); // не компилируется
// также просто если попробовать
stateid.Define(<[decl: | ABC ]>); // компилируется, но при выполнении ничего не происходит, новый член не добавляется
}
stateid.Compile(); // компилируем тип
// при этом если написать сразу список членов, то он определяется правильно:
def stateid=ty.DefineNestedType(<[ decl:
public enum StateID
{
| ABC
| BCD
}
]>); // если бы была возможность как то в цитату вставить строки с описанием членов
И еще вопрос можно ли удалять объявления например подклассов из класса? например убрать объявление SubTest, чтобы он присутствовал во время работы макроса и удалялся из конечного кода?
class Test
{
class SubTest
{
}
}
Re: Написание DSL на Nemerle и создание Enum в макросах
Здравствуйте, CodingUnit, Вы писали:
CU>Еще встала другая проблема, надо определить во время компиляции в макросе перечисление на основе кода, например так: CU>
CU>[MacroUsage(MacroPhase.BeforeTypedMembers,MacroTargets.Class,Inherited=true)]
CU>macro statemachine(ty : TypeBuilder)
CU>{
CU>// определяем перечисление, нормально
CU>def stateid=ty.DefineNestedType(<[ decl:
CU> public enum StateID
CU> {
CU> }
CU> ]>);
CU>..
CU>def states : list[TypeBuilder]= ty.DeclaredNestedTypes; // получаем лист TypeBuilder-ов для внутренних типов
CU>// для каждого типа добавляем имя в качестве константы к перечислению
CU>states.Iter( x => stateid.Define(<[ $(x.Name)]>)); // не компилируется
CU>// также просто если попробовать
CU>stateid.Define(<[decl: | ABC ]>); // компилируется, но при выполнении ничего не происходит, новый член не добавляется
CU>}
CU>stateid.Compile(); // компилируем тип
CU>
CU>
CU>// при этом если написать сразу список членов, то он определяется правильно:
CU>def stateid=ty.DefineNestedType(<[ decl:
CU> public enum StateID
CU> {
CU> | ABC
CU> | BCD
CU> }
CU> ]>); // если бы была возможность как то в цитату вставить строки с описанием членов
CU>
CU>Вопрос такой, как определить перечисление в макросе и добавить как подтип в классе?
Что-то вроде этого:
[MacroUsage(MacroPhase.BeforeTypedMembers,MacroTargets.Class,Inherited=true)]
macro statemachine(ty : TypeBuilder)
{
// генерируем список определений полей перечисления
def cases = ty.DeclaredNestedTypes.Map(t => <[ decl: | $(t.ParsedName : name); ]>);
def enumTypeBuilder = ty.DefineNestedType( // вложенные типы нужно добавлять только с помощью DefineNestedType
<[ decl:
public enum StateID
{
..$cases
}
]>, true);
enumTypeBuilder.Compile();
}
CU> Как добавить динамически в макросе элементы в список перечисления, есть ли такая возможность?
Или как показал я, или с помощью методов DefineXxx() класса TypeBuilder.
CU>И последний вопрос, если писать DSL то потребуется много ключевых слов, при этом они как то должны взаимодействовать между собой, проходит синтаксические и семантические проверки, для этого надо чтобы текст разбирался и получалось некое описание на DSL, для этого нужен какой то объект который хранит в себе все состояние описанной структуры, то есть должен быть доступен на этапе компиляции и при вызове макроса через помеченные объекты или описание в методе, добавлять информацию в этот объект, то есть должен создаваться один раз и жить в остальное время.
Это все очень сильно зависит от задачи говорить о чем-то в ообщем невозможно.
CU>Еще может потребоваться некоторые члены удалять, которые не несут в себе никакой информации для приложения, например классы для описания вложенной иерархии нужные макросу, но ненужные в рантайме, было бы хорошо удалить чтобы они были не видны, но TypeBuilder допускает объявлять члены, но не допускает удалять и многие внутренние поля компилятора read-only, можно ли решить как то эту задачу?
Удалаять нельзя. Но нужную метаинформацию можно описать в другом виде. Например в виде доп-синтаксиса, или в виде мета-атрибутов.
В общем, лучше опиши стоящую задачу (без ее реализации), а мы попробуем как ее лучше реализовать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: Написание DSL на Nemerle и создание Enum в макросах
Здравствуйте, CodingUnit, Вы писали:
CU>Да генерировать методы легко, хорошо что синтаксис можно добавить после класса, но не могли бы Вы Владислав написать есть ли возможность создать в классе перечисление и добавить в него имена констант на основе динамических данных, как я описал здесь, почему не компилируется определение перечисления и не добавляется даже статическое константное поле в перечисление, есть какие то ограничения у компилятора?
Здравствуйте, VladD2, Вы писали:
CU>>Вопрос такой, как определить перечисление в макросе и добавить как подтип в классе?
VD>Что-то вроде этого: VD>
VD>[MacroUsage(MacroPhase.BeforeTypedMembers,MacroTargets.Class,Inherited=true)]
VD>macro statemachine(ty : TypeBuilder)
VD>{
VD> // генерируем список определений полей перечисления
VD> def cases = ty.DeclaredNestedTypes.Map(t => <[ decl: | $(t.ParsedName : name); ]>);
VD> def enumTypeBuilder = ty.DefineNestedType( // вложенные типы нужно добавлять только с помощью DefineNestedType
VD> <[ decl:
VD> public enum StateID
VD> {
VD> ..$cases
VD> }
VD> ]>, true);
VD> enumTypeBuilder.Compile();
VD>}
VD>
спасибо, работает!
CU>>И последний вопрос, если писать DSL то потребуется много ключевых слов, при этом они как то должны взаимодействовать между собой, проходит синтаксические и семантические проверки, для этого надо чтобы текст разбирался и получалось некое описание на DSL, для этого нужен какой то объект который хранит в себе все состояние описанной структуры, то есть должен быть доступен на этапе компиляции и при вызове макроса через помеченные объекты или описание в методе, добавлять информацию в этот объект, то есть должен создаваться один раз и жить в остальное время.
VD>Это все очень сильно зависит от задачи говорить о чем-то в ообщем невозможно.
CU>>Еще может потребоваться некоторые члены удалять, которые не несут в себе никакой информации для приложения, например классы для описания вложенной иерархии нужные макросу, но ненужные в рантайме, было бы хорошо удалить чтобы они были не видны, но TypeBuilder допускает объявлять члены, но не допускает удалять и многие внутренние поля компилятора read-only, можно ли решить как то эту задачу?
VD>Удалаять нельзя. Но нужную метаинформацию можно описать в другом виде. Например в виде доп-синтаксиса, или в виде мета-атрибутов.
VD>В общем, лучше опиши стоящую задачу (без ее реализации), а мы попробуем как ее лучше реализовать.
Задача такова, есть библиотека описания автоматов состояний в коде аналогичным в UML, проблема в том что описание структуры автомата сложно читаемое и создаваемое вручную, хотелось бы автоматизировать этот процессс за счет макросов и DSL, я уже видел решение подобной задачи в Boo, но хотелось бы попробовать сделать такое решение на Nemerle, под нужную библиотеку, там имеются разные процедуры для описания иерархии автомата, хотелось бы например сделать иерархическое визуальное описание иерархии, например:
[statemachine]
class TestFSM
{
[state]
class Top
{
[state]
class Start
{
[state]
class Sub
{
entry() : void
{
// действие при входе
}
exit() : void
{
// действие при выходе
}
}
}
[state]
class Next
{
transition("Sub",UserEvt) // описание перехода
}
}
}
как то так, на Boo там вообще абстрактно описали автомат, типа
state @Parked:
when @EngineStarted >> @IdleInNeutral
state @IdleInNeutral:
when @EngineKilled >> @Parked
when @GearEngaged >> @IdleInGear
state @IdleInGear:
when @GearDisengaged >> @IdleInNeutral
when @GasApplied >> @RacingAlong
state @RacingAlong:
when @BrakeApplied >> @IdleInGear
when @CarCrashedIntoTree >> @CarDestroyed
state @CarDestroyed
хотелось бы что то подобное создать и переводить все это на этапе компиляции в вызовы соответствующих методов в библиотеке автомата для инициализации, главная проблема в легком описании на каком то DSL или псевдоязыке, метааттрибутов, какие могут быть возможнные решения?
VD>Такое описание лично мне намного больше нравится. Ну, может быть можно выбрать немного другой синтаксис, чтобы урпостить себе жизнь и сделать его более похожим на основной синтаксис Немерла, но что-то вроде того.
VD>Если бы я решал подобную задачу, то скорее всего я выбрал бы следующий подход: VD>1. Каждый автомат я описывал бы как отдельный класс. VD>2. Список состояний я описывал бы в метаатрибуте которым помечал бы этот класс. VD>3. Синтаксис сделал бы похожим на описание локальных функций. VD>4. Реакции на переходы описывал бы как методы внутри этого класса.
VD>Вот как мог бы выглядеть приведенный выше автомат: VD>
VD>
Спасибо за ответ Владислав и подробное описание, хотел заметить что автомат иерархический, в Boo был описан плоский автомат, на практике чаще автоматы нужны сложные, то есть когда состояния могут быть вложенными друг в друга, как я описал здесь:
CU>>
CU>>[statemachine]
CU>>class TestFSM
CU>>{
CU>> [state]
CU>> class Top // самое верхнее состояние
CU>> {
CU>> [state]
CU>> class Start // подсостояние Top
CU>> {
CU>> [state]
CU>> class Sub1 // подсостояние Start
CU>> {
[state]
class Sub2 // подсостояние Sub1
{
}
CU>> }
CU>> }
CU>> [state]
CU>> class Next // подсостояние Top
CU>> {
CU>> }
CU>> }
CU>>}
CU>>
графически они выглядят примерно так
В принципе с метаатрибутом описать весь автомат можно, лишь бы это не сложно было сделать, тогда описание будет выглядеть примерно так:
[FsmDef(
{
state Top
{
state Start
{
| Event1 => Next // переход по событию Event1
// подостояние 1
state Sub1
{
| entry => {
Action1 // действие при входе
}
| exit => Action2 // действие при выходе
// подсостояние 2
state Sub2
{
}
}
}
state Next
{
| Event2 => Start
}
}
})]
class MyFsm
{
// обарботчик события перехода
EnterIn_IdleInGear_State(previosState : MyFsmState) : void
{
... код обработки
}
сложно ли такую структуру считать в макросе? Там видимо понадобится дополнительный рекурсивный разбор каждого вложенного состояния и создание иерархии, при этом надо как то выделить части | name, и вложенные state. Hадо еще чтобы какой то класс хранил состояние структуру, то есть создавался однократно и использовался как синглтон в дальнейшем. Сложность пока только в том чтобы распознать конструкции | и state на одном уровне, остальное дело техники, не могли бы вы Владислав описать примерно как такое можно сделать, и что такое ..$ в цитате, в прошлом посте оно позволило развернуть лист выражений в цитате? В этих конструкциях match с чем конкретно он сравнивает, после switch такая конструкция выглядит странно, как я понимаю там списки из Expression, то есть он сравнивает списки экземпляров Expression?
VD> VD> ... VD>}
VD VD>Выше я описал возможный вариант. VD>Для реализации данного подхода нужно описать и реализовать два макроса: VD>1. FsmDef — метаатрибут. В нем должна производиться основная работа по построению конечного автомата. VD>2. state — макрос уровня выражения. Нужен только для того, чтобы немерле мог воспринимать синтаксис описания состояния — "state имяСостояния список состояний".
VD>Вот как могут выглядеть заглушки (т.е. без реализации) этих макросов: VD>
VD> [Nemerle.MacroUsage(Nemerle.MacroPhase.BeforeTypedMembers, Nemerle.MacroTargets.Class)]
VD> macro FsmDef(type_builder : TypeBuilder, body)
VD> {
VD> }
VD> macro State(stateName, body)
VD> syntax("state", stateName, body)
VD> {
VD> <[ () ]> // В этом макросе нам ничего делать не надо. Он нужет только чтобы немерле "пропустил" наш синтаксис.
VD> }
VD>
VD>Понимаю, что без опыта создания макросов распознать столь не тривиальную структуру будет не просто. По этому я потратил час, чтобы набросать примерную реализацию макросов. Вот что у меня получилось... VD>Сами макросы: VD>
VD>using Nemerle;
VD>using Nemerle.Compiler;
VD>using Nemerle.Compiler.Parsetree;
VD>using System.Diagnostics;
VD>namespace MacroLibrary2
VD>{
VD> [Nemerle.MacroUsage(Nemerle.MacroPhase.BeforeTypedMembers, Nemerle.MacroTargets.Class)]
VD> macro FsmDef(type_builder : TypeBuilder, body)
VD> {
VD> Helper.MakeFsm(type_builder, body);
VD> }
VD> macro State(stateName, body)
VD> syntax("state", stateName, body)
VD> {
VD> _ = stateName; // говорим компилятору, что мы намеренно не желаем использовать параметр
VD> _ = body; // тоже самое
VD> <[ () ]> // В этом макросе нам ничего делать не надо. Он нужен только чтобы немерле "пропустил" наш синтаксис.
VD> }
VD> module Helper
VD> {
VD> public MakeFsm(ty : TypeBuilder, body : PExpr) : void
VD> {
VD> def makeTransition(transitionDef : MatchCase) : void
VD> {
VD> Message.Hint(transitionDef.Location, $" Events=$(transitionDef.patterns) Transition=$(transitionDef.body)")
VD> }
VD> def makeSate(stateDef : PExpr) : void
VD> { // ncc заворачивает обращение к макросу в конструкцию PExpr.MacroCall...
VD> | PExpr.MacroCall(name, _, parms) when name.Id == "state" =>
VD> match (parms)
VD> { // параметры (код передаваемый в них) макроса заворачиваются в SyntaxElement.Expression.
VD> // Пользователь должен передать два параметра...
VD> // имя и пустую группу...
VD> | [Expression(<[ $stateName ]>), Expression(<[ { } ]>)] =>
VD> Message.Hint(stateName.Location, $"Распознано описание состояния '$stateName' (без переходов!)");
VD> // или имя и список вхождений оператора match который компилятор превращает в полноценный оператор match...
VD> | [Expression(<[ $stateName ]>), Expression(<[ match ($_) { ..$transitions } ]>)] =>
VD> Message.Hint(stateName.Location, $"Распознано описание состояния '$stateName'. Переходы:");
VD> foreach (transitionDef in transitions)
VD> makeTransition(transitionDef);
VD> | [Expression(<[ $_ ]>), Expression(<[ { $x } ]>)] =>
VD> Message.Error(x.Location, "Ожидается описание переходов в формате: { | Event => State | Event => State ... }");
VD> | _ =>
VD> Message.Error(stateDef.Location,
VD> "Ожидается описание состояния в формате: state StateName { transitions }");
VD> }
VD> | _ =>
VD> Message.Error(stateDef.Location,
VD> "Ожидается описание состояния в формате: state StateName { transitions }");
VD> }
VD> match (body)
VD> {
VD> // описания состояний заключены в группу (фигурные скобки)
VD> | <[ { ..$states } ]> => // матчим список выражений описывающих состояния
VD> foreach (state in states)
VD> makeSate(state);
VD> | _ =>
VD> Message.Error(body.Location,
VD> "Ожидается список состоянии в формате { state1 state2 ... }");
VD> }
VD> }
VD> }
VD>}
VD>
Здравствуйте, CodingUnit, Вы писали:
CU>Спасибо за ответ Владислав и подробное описание, хотел заметить что автомат иерархический, в Boo был описан плоский автомат, на практике чаще автоматы нужны сложные, то есть когда состояния могут быть вложенными друг в друга, как я описал здесь:...
Я показал как можно спроектировать синтаксис и как его разбирать. Думаю, что основные моменты я показал, так что будет не сложно переделать его в иерархический вариант.
CU>графически они выглядят примерно так CU>здесь
CU>В принципе с метаатрибутом описать весь автомат можно, лишь бы это не сложно было сделать, тогда описание будет выглядеть примерно так:
CU>
...
CU> state Sub1
CU> {
CU> | entry => {
CU> Action1 // действие при входе
CU> }
CU> | exit => Action2 // действие при выходе
CU>})]
CU>
Так выглядит красивее. Только одно замечание. Вот эти вот "entry" и "exit" ведь к самому автомату отношения не имеют, да? Это ведь реакция?
На мой взгляд лучше не забивать описание автомата действиями которые должны осуществляться при переходах. Действия лучше помещать в методы внутри класса. Метод EnterIn_IdleInGear_State(), который я изобразил в своем примере как раз и был таким действием. Их можно помечать атрибутами или распознавать по имени. В моем случае подразумевалось, что он будет распознаваться оп имени. Можно их описывать и на основании атрибутов. Например, для приведенного выше фрагмента можно описать реакции так:
[OnEnter(State=Sub1)]
{
Action1 // действие при входе
}
[OnExit(State=Sub1)]
{
Action2 // действие при выходе
}
Это позволит разгрузить описание самого автомат и иметь интеллисенс (дополнение при вводе, подсветку, хинты, ...) внутри методов.
CU>сложно ли такую структуру считать в макросе?
Макросам без разницы. Они разбирают абстрактный код. Принцип я показал. Если будут какие-то непонимания или вопросы, то можно обращаться сюда или прямо ко мне на Skype (имя в скапе совпадает с ником на форуме).
Естественно, имеет смысл почитать статьи посвященные написанию макросов.
CU> Там видимо понадобится дополнительный рекурсивный разбор каждого вложенного состояния и создание иерархии, при этом надо как то выделить части | name, и вложенные state.
Да, конечно. Вам нужно расширить функцию makeTransition (Ее, собственно, по любому нужно развивать.):
В transitionDef.body может находиться произвольный Nemerle-код. Так как мы объявили макрос state, то в transitionDef.body может находиться и он (или даже несколько обращений к нему).
В ваши задачи будет выходить рекурсивный разбор transitionDef.body, проверка корректности его содержимого и выявление вложенных состояний.
CU>Hадо еще чтобы какой то класс хранил состояние структуру, то есть создавался однократно и использовался как синглтон в дальнейшем.
Это предложение я не понял.
Речь идет о создании объектной модели автомата?
Или о том, что описание одного автомата может использоваться для разных задач?
CU> Сложность пока только в том чтобы распознать конструкции | и state на одном уровне,
Что значит на одном уровне?
Как распознавать state и его вхождения я показал. Вам нужно только немного расширить это распознавание сделав его рекурсивным. Сейчас распознование конструкции "| X => Y" выливается в банальный вывод текста в вывод компилятора:
Вам же нужно развить это дело.
CU> остальное дело техники, не могли бы вы Владислав описать примерно как такое можно сделать, и что такое ..$ в цитате, в прошлом посте оно позволило развернуть лист выражений в цитате?
Это синтаксис квази-цирирования. Оно работает в две стороны (на распознавание и на композицию кода). Конкретно "..$x" позволяет распознать набор повторяющихся участов кода в список или сгенерировать код из списока. Такая конструкция может применятся только в скобках (в данном случсе в фигурных). Более подробно о квази-цитировании написано во второй статье
Перед тем как браться за серьезные макросы, а данный тип макроса несомненно не самый простой, следует прочитать этот курс. По крайней мере первые две статьи.
CU>В этих конструкциях match с чем конкретно он сравнивает, после switch такая конструкция выглядит странно, как я понимаю там списки из Expression, то есть он сравнивает списки экземпляров Expression?
Там список так называемых PExpr. PExpr — это вариантный тип описывающих код немерла. Грубо говоря — это АСТ немерла. Чтобы не нужно было знать все типы данных в него входящий и писать сложный и громоздкий код была придумана система квази-цитирования. В общем, в указанном курсе все это описано.
ЗЫ
Один полезный совет... Чтобы лучше понимать происходящее на работу макросов лучше смотреть из под отладчика. Просто добавьте в некоторый фрагмент кода макроса:
assert2(false);
и скомпилируйте тестовый проект. Когда появится окно ассерат нажмите Retry и вы окажетесь в отладчике (нужно открыть его в отдельной ссесси, не той в котрой идет компиляция). Далее можно будет идти по коду макроса отладчиком и смоетреть значения и тип переменных. Это резко упрощает понимание (особенно чужого кода).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, CodingUnit, Вы писали:
CU>>Спасибо за ответ Владислав и подробное описание, хотел заметить что автомат иерархический, в Boo был описан плоский автомат, на практике чаще автоматы нужны сложные, то есть когда состояния могут быть вложенными друг в друга, как я описал здесь:...
VD>Я показал как можно спроектировать синтаксис и как его разбирать. Думаю, что основные моменты я показал, так что будет не сложно переделать его в иерархический вариант.
CU>>графически они выглядят примерно так CU>>здесь
CU>>В принципе с метаатрибутом описать весь автомат можно, лишь бы это не сложно было сделать, тогда описание будет выглядеть примерно так:
CU>>
VD>...
CU>> state Sub1
CU>> {
CU>> | entry => {
CU>> Action1 // действие при входе
CU>> }
CU>> | exit => Action2 // действие при выходе
CU>>})]
CU>>
VD>Так выглядит красивее. Только одно замечание. Вот эти вот "entry" и "exit" ведь к самому автомату отношения не имеют, да? Это ведь реакция? VD>На мой взгляд лучше не забивать описание автомата действиями которые должны осуществляться при переходах. Действия лучше помещать в методы внутри класса. Метод EnterIn_IdleInGear_State(), который я изобразил в своем примере как раз и был таким действием. Их можно помечать атрибутами или распознавать по имени. В моем случае подразумевалось, что он будет распознаваться оп имени. Можно их описывать и на основании атрибутов. Например, для приведенного выше фрагмента можно описать реакции так: VD>[c#]
Все работает замечательно, на Nemerle можно строить неплохие DSL, но вот беда, почему то не хотят создаваться в цикле объявления событий, то есть создаются правильные PExpr объявления члена класса события, но когда начинается конечная компиляция оказывается что члены поля и методов add,remove генерируются с одинаковыми именами, соответствующих названию в той строчке в которой производилось объявление, например если делаешь так:
// функция которая создает события из описания где то списка событий
def create_events(model)
{
model.Events.Map(x =>
{
def ename : string =x.Name; // это имя члена
<[ decl: public event $(ename : usesite) : EventHandler; ]> // на выходе объявление члена события
});
}
def defs=create_events(model); // создаем список объявлений
defs.Iter(x => typebuilder.Define(x)); // добавляем в typebuilder
при этом возникает ошибка в духе: _N2_ename_usesite_field duplicated identifier, тоже самое и с методами add,remove, исследовав этот вопрос оказывается когда создается с помощью цитаты объявление события оно всегда использует имя объявления в данном случае текст $(ename : usesite), и с помощью него создает имя членов, видимо подразумевая что событие должно объявлятся с константным именем и всегда разным, но что делать если надо объявить на основе динамических данных? Я решил эту задачу только с помощью специального метода, который берет объявление с помощью цитаты, которое преобразуется в ClassMember.Event и создает новый класс ClassMember.Event заменяя имена на новые и оставляя тела такие же как в старом созданном с помощью цитаты, тогда сообщение об ошибке не возникает, но это не выход, надо видимо чтобы имя полей и методов add,remove, при объявлении квази цитаты использовалось уникальное на основе имени которое берется из выражения, а не на основе текста выражения, это надо править компилятор, где бы найти файл где вся эта логика распознавания цитат реализуется? Может ли помочь наш всемогущий Владислав?
Здравствуйте, CodingUnit, Вы писали:
CU>при этом возникает ошибка в духе: _N2_ename_usesite_field duplicated identifier, тоже самое и с методами add,remove
Здравствуйте, CodingUnit, Вы писали:
CU>при этом возникает ошибка в духе: _N2_ename_usesite_field duplicated identifier, тоже самое и с методами add,remove, исследовав этот вопрос оказывается когда создается с помощью цитаты объявление события оно всегда использует имя объявления в данном случае текст $(ename : usesite), и с помощью него создает имя членов, видимо подразумевая что событие должно объявлятся с константным именем и всегда разным, но что делать если надо объявить на основе динамических данных? Я решил эту задачу только с помощью специального метода, который берет объявление с помощью цитаты, которое преобразуется в ClassMember.Event и создает новый класс ClassMember.Event заменяя имена на новые и оставляя тела такие же как в старом созданном с помощью цитаты, тогда сообщение об ошибке не возникает, но это не выход, надо видимо чтобы имя полей и методов add,remove, при объявлении квази цитаты использовалось уникальное на основе имени которое берется из выражения, а не на основе текста выражения, это надо править компилятор, где бы найти файл где вся эта логика распознавания цитат реализуется? Может ли помочь наш всемогущий Владислав?
А можно прислать мне проект воспроизводящий ошибку? Желательно, чтобы в нем не было ничего лишнего, только описанная проблема. Тогда я постараюсь посмотреть, что происходит, и если получится, поправить баг в компиляторе или объяснить как нужно действовать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, CodingUnit, Вы писали:
CU>>при этом возникает ошибка в духе: _N2_ename_usesite_field duplicated identifier, тоже самое и с методами add,remove, исследовав этот вопрос оказывается когда создается с помощью цитаты объявление события оно всегда использует имя объявления в данном случае текст $(ename : usesite), и с помощью него создает имя членов, видимо подразумевая что событие должно объявлятся с константным именем и всегда разным, но что делать если надо объявить на основе динамических данных? Я решил эту задачу только с помощью специального метода, который берет объявление с помощью цитаты, которое преобразуется в ClassMember.Event и создает новый класс ClassMember.Event заменяя имена на новые и оставляя тела такие же как в старом созданном с помощью цитаты, тогда сообщение об ошибке не возникает, но это не выход, надо видимо чтобы имя полей и методов add,remove, при объявлении квази цитаты использовалось уникальное на основе имени которое берется из выражения, а не на основе текста выражения, это надо править компилятор, где бы найти файл где вся эта логика распознавания цитат реализуется? Может ли помочь наш всемогущий Владислав?
VD>А можно прислать мне проект воспроизводящий ошибку? Желательно, чтобы в нем не было ничего лишнего, только описанная проблема. Тогда я постараюсь посмотреть, что происходит, и если получится, поправить баг в компиляторе или объяснить как нужно действовать.
У меня сейчас интеграция вылетела, не хочет переставлятся, нужно будет студию переставлять, сложно будет собрать внятный проект, но описанную проблему легко воспроизвести, достаточно создать макроаттрибут который добавляет члены к классу, далее сделать массив строк имен эвентов и преобразовать в цитатное объявление события так:
void MacroHelper(typebuilder : TypeBuilder)
{
def create_events(names : list[string])
{
names.Map(x =>
{
def ename : string =x; // это имя члена
<[ decl: public event $(ename : usesite) : EventHandler; ]> // на выходе объявление члена события
});
}
def names="abc" :: "cda" :: []; // cоздаем массив хотя бы двух имен
def defs=create_events(names); // создаем список объявлений
defs.Iter(x => typebuilder.Define(x)); // добавляем в typebuilder
}
потом добавляем макроаттрибут к какому нибудь классу, и собираем проект с применением макроса и проблема себя проявит сообщением о сдублированных идентификаторов генерируемых компилятором для закрытого поля события и методов add,remove которые все будут именоваться одним именем имеющим в себе название "ename_usesite" по объявлению, кроме как хаком пока такая проблема не решается, хорошо бы глянуть поглубже, если че то не будет работать, то я завтра сделаю проект.
Здравствуйте, VladD2, Вы писали:
VD>А можно прислать мне проект воспроизводящий ошибку? Желательно, чтобы в нем не было ничего лишнего, только описанная проблема. Тогда я постараюсь посмотреть, что происходит, и если получится, поправить баг в компиляторе или объяснить как нужно действовать.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, CodingUnit, Вы писали:
VD>Да... Пока я (или кто-то еще) не разобрался в ошибке можно обойти проблему описывая свойство явно. Что-то вроде: VD>
К соответствующему свойству для события Foo из класса можно обращаться через подчеркивание: _Foo, операторы += и -= не работают — нужно явно обращаться к add_Foo и remove_Foo.
Здравствуйте, hardcase, Вы писали:
H>Текст (name : usesite) наводит на определенные мысли...
Ага. Вот какой код генерируется в итоге (C# by Reflector):
tb.Define(
new ClassMember.Event(
new Splicable.Name(new Name(name, ManagerClass.Instance.MacroColors.UseColor, ManagerClass.Instance.MacroColors.UseContext)),
new Modifiers(NemerleAttributes.Public, list<PExpr>.Nil._N_constant_object),
evt_type,
new ClassMember.Field(new Splicable.Name(Name.NameInCurrentColor("_N__N_event_field_of__name___usesite__3262", _N_MacroContexts.Get(1, ManagerClass.Instance))), new Modifiers(NemerleAttributes.Mutable | NemerleAttributes.Private, list<PExpr>.Nil._N_constant_object), evt_type),
new ClassMember.Function(new Splicable.Name(Name.NameInCurrentColor("add_(name : usesite)", _N_MacroContexts.Get(1, ManagerClass.Instance))), new Modifiers(NemerleAttributes.Public, list<PExpr>.Nil._N_constant_object), new PFunHeader(new Typarms(list<Splicable>.Nil._N_constant_object, list<Constraint>.Nil._N_constant_object), new Splicable.Name(Name.NameInCurrentColor("add_(name : usesite)", _N_MacroContexts.Get(1, ManagerClass.Instance))), new PExpr.Void(), new list<PParameter>.Cons(new PParameter(new Splicable.Name(Name.NameInCurrentColor("value", _N_MacroContexts.Get(1, ManagerClass.Instance))), new Modifiers(NemerleAttributes.None, list<PExpr>.Nil._N_constant_object), evt_type), list<PParameter>.Nil._N_constant_object)), list<PExpr>.Nil._N_constant_object, FunBody.Abstract._N_constant_object),
new ClassMember.Function(new Splicable.Name(Name.NameInCurrentColor("remove_(name : usesite)", _N_MacroContexts.Get(1, ManagerClass.Instance))), new Modifiers(NemerleAttributes.Public, list<PExpr>.Nil._N_constant_object), new PFunHeader(new Typarms(list<Splicable>.Nil._N_constant_object, list<Constraint>.Nil._N_constant_object), new Splicable.Name(Name.NameInCurrentColor("remove_(name : usesite)", _N_MacroContexts.Get(1, ManagerClass.Instance))), new PExpr.Void(), new list<PParameter>.Cons(new PParameter(new Splicable.Name(Name.NameInCurrentColor("value", _N_MacroContexts.Get(1, ManagerClass.Instance))), new Modifiers(NemerleAttributes.None, list<PExpr>.Nil._N_constant_object), evt_type), list<PParameter>.Nil._N_constant_object)), list<PExpr>.Nil._N_constant_object, FunBody.Abstract._N_constant_object)
)
Таким образом еще при формировании цитаты формируется некорректный код.
К сожалению искать место в компиляторе которое занимается лифтингом квази-цитат событий не так просто. На это уйдет время.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
В случае использования сплайсинга в имени свойства в id находится не имя, а выражение (для приведенного примера — это выражение "(name : usesite)". Оно тупо приводится к строке и далее используется для формирования имени поля, методов add_Xxx и remove_Xxx. Естественно, что в результате получается чушь.
Этот код нужно переписывать, так чтобы вместо генерации имени во время парсига, генерировался бы код который формировал бы имена во время компиляции исходника (т.е. раскрытия макроса в нашем случае).
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, VladD2, Вы писали:
VD>В общем, проблема возникает еще при парсинге квази цитаты (см. файл MainParser.n) строка 1619 и далее со строки 1696 и далее:
Я правильно думаю что plain_name должен получаться также как и в parse_property (строка 1340):
"plain_name" вообще не должно быть. Вместо этого нужно генерировать код который сформирует этот самый plain_name динамически (во время компиляции).
Но что вот как это сделать я пока не понял. Уже устал. Да и задачка получается слишком высшего порядка.
По уму имя поля, add-ера и remove-ера нужно формировать динамически.
Splicable — это вариант который описан следующим образом:
public variant Splicable : Located
{
| Name { body : Parsetree.Name; }
| Expression
{
expr : PExpr;
[RecordIgnore] public mutable env : GlobalEnv;
public this(expr : PExpr, env : GlobalEnv) { this.expr = expr; this.env = env; }
public this(loc : Location, expr : PExpr, env : GlobalEnv)
{
this.loc = loc; this.expr = expr; this.env = env;
}
}
Expression как раз и предназначен для формирования имени динамически.
Посему для формирования имен поля, add-ера и remove-ера нужно сформировать экзепляры Splicable.Expression() которые будут содержать код из имени свойства плюс дополнительный код модифицирующий имя так чтобы из него получались требуемые имена.
Сплайс вида "$(name : usesite)" на этапе компиляции квази-цитаты преобразуется в:
Splicable.Expression(<[ name : usesite ]>, GlobalEnv(...))
Думается, что нужно разобрать выражение <[ name : usesite ]> "на запчасти" и вцепить из него выржение (в данном случае "name"), тип (в данном случае "usesite") и сформировать новые выражение которое будут выглядеть как-то так:
<[ ("_N_event_field_of_" + name) : usesite ]> // для имени поля
<[ ("add_" + name) : usesite ]> // для имени add-ера
<[ ("remove_" + name) : usesite ]> // для имени remove-ра
ну, и далее сформировать на их основе новые варианты Splicable.Expression() которые и использовать для формирования имен соответствующих сущностный.
ЗЫ
Откровенно говоря у меня сейчас ужасный цейтнот, так что заняться в ближайшее время я этим наверно не смогу. Буду рад, если кто-то попытается исправить этот баг.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Ну, то есть... в двух словах parse_property тоже не рассчитан на сплайснутые имена. В этом случае имя просто становится пустой строкой, что как ты понимаешь, так же не приемлемо.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Довольно красиво)
Однако ещё можно задавать автомат(граф) матрицей смежности
Примерно так:
[StateMachine]
class Sm
{
public States {A, B, C}; //like enumpublic AdjacencyMatrix : bool[,] // извините не знаю, как на немерле двумерный массив объявить
{
{0,1,0};
{0,1,1};
{1,0,0};
};
public DefaultInitialState = State.A;
}
такое можно даже на шарпе написать через дженерики))
Здравствуйте, hardcase, Вы писали:
H>Можно, но только это совершенно не очевидно. H>Для 3-4 состояний может еще и можно разобраться, но вот потом......
Это зависит от контекста использования.
Я в принципе то же самое хотел сказать про список смежных вершин.
Более наглядным является диаграмма (Мура), но и она имеет предел понятности.
Матричная запись, как и диаграмма имеют преимущество перед списком:
Видно не только исходящие, но и входящие дуги.
У нас а инсте был один препод (довольно противный), он говорил так:
Матрицы экономят бумагу, но не экономят мозги.
Я придерживаюсь аналогичного мнения про ФП.
Было бы безобразно, зато однообразно)))
Здравствуйте, hardcase, Вы писали:
H>Можно, но только это совершенно не очевидно. H>Для 3-4 состояний может еще и можно разобраться, но вот потом......
Здравствуйте, para, Вы писали:
P>списком состояний, начальным состоянием и функции перехода, заданной: P>например,
Для людей важно просто и наглядно задать описание автомата. По сему совершенно не важно чем там в теории определяется автомат. Важно иметь язык который наиболее интуитивно позволит описать этот автомат. Я привел один из возможных языков. Описывать автоматы в виде таблиц очень неудобно. Таблицей можно описать только ДКА. А люди мысленно обычно создают НКА, а НКА не возможно описать с помощью простой матрицы. Спорить на эту тему я не хочу (это не тот форум). Но в принципе такие языки как регулярные выражения и грамматики (BNF, PEG) — это идилальные высокооуровневые языки. Почти для любой задачи можно придумать некоторый высокоуровневый язык который будет лучше любого описания КА. Именно по то этому людям обычно не интересны ДСЛ-и описывающие КА. Но если нужно формировать именно КА, то язык вроде приведенного мной (или использованный в БУ) очень удобен для этого. А разные статистические данные можно генерировать в процессе компиляции макросов. Скажем тот же список переходов в некоторое состояние построить не проблема. Можно даже диаграмму нарисовать.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, CodingUnit, Вы писали:
CU>Само же описание наверное можно получить из диаграммы, в UML редакторах есть функция экспорта XML, которое можно преобразовать в DSL и так далее.
Скажу больше. Можно сделать так, чтобы макрос кроме ДСЛ-я мог бы читать и описание из ХМЛ-я (и писать его в ХМЛ). Надо только грамотно спроектировать такой макрос. Тогда можно будет даже сделать весьма навороченное решение которое будет позволять редактировать автомат визуально и по нему генерировать эффективный код.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>"plain_name" вообще не должно быть. Вместо этого нужно генерировать код который сформирует этот самый plain_name динамически (во время компиляции). VD>Сплайс вида "$(name : usesite)" на этапе компиляции квази-цитаты преобразуется в: VD>
VD>Splicable.Expression(<[ name : usesite ]>, GlobalEnv(...))
VD>
VD>Думается, что нужно разобрать выражение <[ name : usesite ]> "на запчасти" и вцепить из него выржение (в данном случае "name"), тип (в данном случае "usesite") и сформировать новые выражение которое будут выглядеть как-то так: VD>
VD><[ ("_N_event_field_of_" + name) : usesite ]> // для имени поля
VD><[ ("add_" + name) : usesite ]> // для имени add-ера
VD><[ ("remove_" + name) : usesite ]> // для имени remove-ра
VD>
VD>ну, и далее сформировать на их основе новые варианты Splicable.Expression() которые и использовать для формирования имен соответствующих сущностный.
1) На данный момент проблема не исправлена, может кто-нибудь из гуру Nemerle ей заняться? Хочется в production коде использовать Nemerle, но с такой багой страшно.
2) Поделитесь, пожалуйста, опытные товарищи наиболее критичными проблемами в компиляторе/интеграции для релизной версии 1.0, которые встречались при использовании Nemerle. Интересуют прежде всего проблемы при написании бизнес-логики, DSL и метапрограммирования (это то, для чего я сейчас пытаюсь использовать Nemerle). Проблемы с GUI (asp, WinForms, WPF) пока меня не волнуют, т.к. для этого буду использовать только C#.
Здравствуйте, humanist-TPV-, Вы писали:
HT>1) На данный момент проблема не исправлена, может кто-нибудь из гуру Nemerle ей заняться? Хочется в production коде использовать Nemerle, но с такой багой страшно.
Какая версия компилятора? Проблему с генерированием евентов еще зимой решили.
VD>><[ ("_N_event_field_of_" + name) : usesite ]> // для имени поля
VD>><[ ("add_" + name) : usesite ]> // для имени add-ера
VD>><[ ("remove_" + name) : usesite ]> // для имени remove-ра
VD>>
VD>>ну, и далее сформировать на их основе новые варианты Splicable.Expression() которые и использовать для формирования имен соответствующих сущностный.
HT>1) На данный момент проблема не исправлена, может кто-нибудь из гуру Nemerle ей заняться? Хочется в production коде использовать Nemerle, но с такой багой страшно.
Вообще-то баг исправляли. Подробностей уже не помню, но точно помню, что исправляли. По позже погляжу в списке тестов.
HT>2) Поделитесь, пожалуйста, опытные товарищи наиболее критичными проблемами в компиляторе/интеграции для релизной версии 1.0, которые встречались при использовании Nemerle. HT>Интересуют прежде всего проблемы при написании бизнес-логики, DSL и метапрограммирования (это то, для чего я сейчас пытаюсь использовать Nemerle). Проблемы с GUI (asp, WinForms, WPF) пока меня не волнуют, т.к. для этого буду использовать только C#.
В основном проблемы есть как раз с указанными дизайнерами ASP и WinForms работают, но глючат, а WPF дизайнеры вообще не поддерживается.
Сам компилятор весьма стабилен. Баги конечно есть, но не критичные. По мере сил они исправляются. Если будет что-то стопорящее работу, то всегда можно обратиться на этот форум. Или предложим обходной маневр, или поправим.
Что касается интеграции, то мелких багов хватает, но они скорее из разряда мелких раздражителей. Опять же если что-то сильно достает, то можно обратиться сюда и мы постараемся поправить. Ну, или (что вообще замечательно) можно поправить самостоятельно и прислать патч.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
HT>2) Поделитесь, пожалуйста, опытные товарищи наиболее критичными проблемами в компиляторе/интеграции для релизной версии 1.0, которые встречались при использовании Nemerle. Интересуют прежде всего проблемы при написании бизнес-логики, DSL и метапрограммирования (это то, для чего я сейчас пытаюсь использовать Nemerle). Проблемы с GUI (asp, WinForms, WPF) пока меня не волнуют, т.к. для этого буду использовать только C#.
Нижеследующий баг мы тщательно обходили , причём он очень многолик, встречается и в присваивании и в подстановке аргумента в функцию, а один раз лочились из-за него на совсем другом обьекте (со всеми вытекающими в рантайме последствиями): 0001052: Compiler loose a generic type, causing a program to crash.
workaround — параноидально указывать точный тип через ':' при использовании подобных иерархий и внимательно разглядывать тултип при передаче в функцию.
Здравствуйте, hardcase, Вы писали:
H>Здравствуйте, humanist-TPV-, Вы писали:
HT>>1) На данный момент проблема не исправлена, может кто-нибудь из гуру Nemerle ей заняться? Хочется в production коде использовать Nemerle, но с такой багой страшно.
H>Какая версия компилятора? Проблему с генерированием евентов еще зимой решили.
Спасибо, оказывается, стояло 2 версии компилятора Снес обе и инсталлировал билд 8879, проблема не проявляется.
Здравствуйте, hi_octane, Вы писали:
_>Нижеследующий баг мы тщательно обходили , причём он очень многолик, встречается и в присваивании и в подстановке аргумента в функцию, а один раз лочились из-за него на совсем другом обьекте (со всеми вытекающими в рантайме последствиями): _>0001052: Compiler loose a generic type, causing a program to crash. _>workaround — параноидально указывать точный тип через ':' при использовании подобных иерархий и внимательно разглядывать тултип при передаче в функцию.
А можно пример воспроизводящий баг?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, humanist-TPV-, Вы писали:
VD>>>
VD>>><[ ("_N_event_field_of_" + name) : usesite ]> // для имени поля
VD>>><[ ("add_" + name) : usesite ]> // для имени add-ера
VD>>><[ ("remove_" + name) : usesite ]> // для имени remove-ра
VD>>>
VD>>>ну, и далее сформировать на их основе новые варианты Splicable.Expression() которые и использовать для формирования имен соответствующих сущностный.
HT>>1) На данный момент проблема не исправлена, может кто-нибудь из гуру Nemerle ей заняться? Хочется в production коде использовать Nemerle, но с такой багой страшно.
VD>Вообще-то баг исправляли. Подробностей уже не помню, но точно помню, что исправляли. По позже погляжу в списке тестов.
Спасибо, оказывается, стояло 2 версии компилятора Снес обе и инсталлировал билд 8879, проблема не проявляется.
HT>>2) Поделитесь, пожалуйста, опытные товарищи наиболее критичными проблемами в компиляторе/интеграции для релизной версии 1.0, которые встречались при использовании Nemerle. VD>Интересуют прежде всего проблемы при написании бизнес-логики, DSL и метапрограммирования (это то, для чего я сейчас пытаюсь использовать Nemerle). Проблемы с GUI (asp, WinForms, WPF) пока меня не волнуют, т.к. для этого буду использовать только C#.
VD>В основном проблемы есть как раз с указанными дизайнерами ASP и WinForms работают, но глючат, а WPF дизайнеры вообще не поддерживается.
VD>Сам компилятор весьма стабилен. Баги конечно есть, но не критичные. По мере сил они исправляются. Если будет что-то стопорящее работу, то всегда можно обратиться на этот форум. Или предложим обходной маневр, или поправим.
VD>Что касается интеграции, то мелких багов хватает, но они скорее из разряда мелких раздражителей. Опять же если что-то сильно достает, то можно обратиться сюда и мы постараемся поправить. Ну, или (что вообще замечательно) можно поправить самостоятельно и прислать патч.
Спасибо за оперативную реакцию, пойду дальше немерлить Если дело пойдет, попробую баги постить.
Еще пару вопросов до кучи:
1) Какие на данный момент планы по выходу релиза? В форуме ты как-то говорил, что к лету планируешь зарелизиться.
2) Еще раз хочу актуализировать для себя и сочуствующих:
2.1) Баги лучше куда постить на данный момент?
— https://nemerle.org/bugs — этот часто в дауне
— http://nemerle.rsdn.ru/bugs/ —
— http://code.google.com/p/nemerle/issues/list
2.2) Доступ на заведение багов как проще всего получить? Можно центролизовано сразу на всех указанных ресурсах (если конечно все реально используются) ?
Здравствуйте, humanist-TPV-, Вы писали:
HT>Еще пару вопросов до кучи: HT>1) Какие на данный момент планы по выходу релиза? В форуме ты как-то говорил, что к лету планируешь зарелизиться.
Народ только только сделал поддержку ASP и там багов хватает. Так что в ближайшее время, переведем в статус Бэты 2, а там видно будет.
Как я уже говорил компилятор уже более чем устойчив. Так что если дизайнеры не требуются, то использовать его можно смело.
HT>2) Еще раз хочу актуализировать для себя и сочуствующих: HT>2.1) Баги лучше куда постить на данный момент? HT>- https://nemerle.org/bugs — этот часто в дауне
Именно сюда. Только без "s" в протоколе: http://nemerle.org/bugs Возможно в нем и дело.
В дауне он не находился с тех пор как переехал на железо РСДН, то есть не меньше двух месяцев.
Если баг критичный, то имеет смысл еще и сюда сообщение добавить со ссылкой на баг и объяснением на русском.
А вообще, чтобы баг устранить очень важно иметь хороший тест легко повторяемый и воспроизводящий ошибку. Тогда они правятся легко.
HT>- http://code.google.com/p/nemerle/issues/list
Сюда не надо пока. Это вообще какая-то прямая ссылка. По идее на странице Issues стоит редирект на http://nemerle.org/bugs.
HT>2.2) Доступ на заведение багов как проще всего получить? Можно центролизовано сразу на всех указанных ресурсах (если конечно все реально используются) ?
В багтреккере нужно завести логин. Вот если захочется самому баги править, то нужно обратиться ко мне, прислать гуглевый экаунт и я добавляю и поменяю все как надо.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
_>>Нижеследующий баг мы тщательно обходили , причём он очень многолик, встречается и в присваивании и в подстановке аргумента в функцию, а один раз лочились из-за него на совсем другом обьекте (со всеми вытекающими в рантайме последствиями): _>>0001052: Compiler loose a generic type, causing a program to crash. _>>workaround — параноидально указывать точный тип через ':' при использовании подобных иерархий и внимательно разглядывать тултип при передаче в функцию.
VD>А можно пример воспроизводящий баг?
, в том обсуждении есть пара примеров. Но тогда Дмитрий Иванков почему-то заподозрил в нём фичу и оставил на баге пометку — "фича?".
Самые часто встречающиеся проявления этого бага — из-за того что компилятор некорректно выбирает тип AnyCar[CarBase] вместо AnyCar[TDerived], даже там где пользователь указывает желаемый тип. Ну а от этой тайной подмены уже странности пляшут, да такие, что даже с отладчиком часто нихрена непонятно.
Завтра закину в SVN тесты на его проявления, вот например два из них:
using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;
using System;
using System.Collections.Generic;
using System.Console;
using System.Linq;
module Program
{
public class CarBase //базовый класс
{
private static mutable globalIndex = 0;
public index : int;
public this() { globalIndex++; index = globalIndex; }
}
public class AnyCar[TDerived] : CarBase
where TDerived : CarBase, new()
{
public static Instance : TDerived = TDerived();//типа синглтонpublic static Impossible() : void
{//тут вообще для неискушённого программиста странное творится - судя по коду используем статический item, но в рантайме он почему-то типа CarBase и один на два разных наследника
WriteLine(Instance.GetType().Name + " index = " + Instance.index.ToString());//в этой строке отламывается автодополнение, но компилятор работает
}
}
public class SmallCar : AnyCar[SmallCar] { } //первый наследникpublic class BigCar : AnyCar[BigCar] { } //ещё один
Write(car : CarBase) : void
{
WriteLine(car.GetType().Name);
}
Main() : void
{
//первый баг, хорошо тут write, а у нас lock был :)
Write(SmallCar.Instance);//ожидается SmallCar, получается почему-то CarBase
Write(BigCar.Instance);//ожидается BigCar, получается опять CarBase
//очень странное поведение - вызывается наследник, но реально управление передаётся в метод AnyCar[CarBase].Impossible()
SmallCar.Impossible();
BigCar.Impossible();
}
}