Здравствуйте, VladD2, Вы писали:
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>