是否有内置状态机构造的编程语言?

Is there a programming language with built-in state machine construct?

我只是好奇是否有一种编程语言以状态机(类似于boost::statechart)作为主要语言结构。

类比-C代表有Java使用观测器模式和C有回调的委托。Perl和Python有内置的散列,而C++和Java需要一个库。

更新:

这应该是C++语言、C语言、Java语言、LISP语言的通用编程语言。

我的意思是"成熟的"状态机,在harel形式主义或UML状态图或boost::statechart级别上拥有所有的铃声和口哨。


Ragel是一种状态机语言。噢,它不是一种支持状态机的语言,而是一种只支持状态机的语言。这显然意味着它不是图灵完备的,但谁需要呢?

更确切地说,Ragel是一个状态机编译器,它用ReXEX-Apple语言描述状态机,并在C、C++、Objul-C、D、Java或Ruby中生成该状态机的实现。(想想yacc,但是对于状态机,而不是LALR(1)表解析器。)RAGEL的主要目的是解析二进制协议(如网络协议或磁盘文件格式),但它也可以用于文本。

使用Ragel的一个著名例子是MongrelWebServerforRuby:它的HTTP内核是用Ragel编写的,这使得它非常快速和安全。事实上,HTTP内核非常好,在不同的应用程序中被多次重复使用:瘦的、独角兽和彩虹也是网络服务器,事实上,它们是Mongrel的直接竞争对手。EBB是反向HTTP代理。Rfuzz是一个用于Web应用程序的模糊测试工具。另外,一些安全工具也会使用它。

Ragel还允许将主机语言中的代码嵌入到状态机中,从而使其图灵完整,不仅能够识别协议,还能够解释协议。

一般来说,支持高级用户定义控制流的每种语言都可以通过协程(例如lua)或延续(例如scala)或GOTO(例如php)或适当的尾部调用(例如scheme)轻松实现状态机。(generators(python)aka iterators(c),基本上是"蹩脚的协同程序",可能工作,也可能不工作,这取决于您对"work"的定义),任何具有灵活语法(如ruby)或支持元语法抽象(如clojure)的语言都可以用来描述状态机。(对非ASCII标识符的支持也有帮助,这样您就可以为状态机使用实际的箭头。)

这意味着,如果将这两种语言结合起来,并使用同时支持尾部调用和元语法抽象的语言,就可以得到非常好的状态机,而不需要本机语言支持。在首届轻量级语言会议上,Krishnamurthi先生发表了一篇名为"Perl之前的猪"的著名演讲,在演讲中他演示了FSM在方案中的实现。(这是幻灯片、录音和解释代码的纸)。代码本身是一个26行(实际上是非常短的行)宏,它允许您这样编写代码:

1
2
3
4
5
6
7
(define my-regex
  (automaton init
             [init : (c → more)]
             [more : (a → more)
                     (d → more)
                     (r → end)]
             [end : accept]))

这是与正则表达式c(a|d)*r相对应的状态机规范。它不仅是一个规范,也是一个实现状态机的可运行程序。

我可以这样称呼它:

1
(my-regex '(c a d a d d r))

在这种情况下,得到的结果是#t(也就是说,计划代表true)。


有一种新的基于W3CXML的状态机语言称为SCXML,它基于DavidHarel的状态图形式主义(它支持分层和并行状态机)。

Apache公有一个基于Java的SCXML实现:

Commons SCXML is an implementation aimed at creating and maintaining a Java SCXML engine capable of executing a state machine defined using a SCXML document, while abstracting out the environment interfaces.


SMC是一种针对特定领域的简单语言的编译器,它将为许多流行语言生成状态机。我已经使用它为各种各样的事情生成了可维护的状态机,比如复杂的用户界面和定制的网络协议。


Plaid编程语言引入了"面向类型状态的编程,这是一种用类型状态扩展面向对象编程的范例。"

这是医生:http://www.cs.cmu.edu/~aldrich/plaid/

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
state File {
    public final String filename;
}

state OpenFile extends File {
    private CFilePtr filePtr;
    public int read() { ... }
    public void close() [OpenFile>>ClosedFile]
        { ... }
}

state ClosedFile extends File {
    public void open() [ClosedFile>>OpenFile]
        { ... }
}

不完全是这样,但是有一个用于python的状态机模块,它允许您使用修饰器来支持实现harel样式的状态图,包括具有多个状态的上下文、嵌套具有或不具有历史的子状态。代码最终看起来像下面这样。模块位于http://wiki.python.org/moin/state%20machine%20via%20decorators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
 #!/bin/env/python
"""
This example now works. The state pattern module
allows defining states which are their their own context for
implementing substates.  Substate Medium (class Medium) shows this here.
"""
"""
Example with 5 buttons. Two ,'up','down' cause state to rotate among the
several states.  The other three, bx,by,bz, invoke state dependent behavior.

Switching into a state causes the labels of the three buttons bx,by,bz to
change.  Pressing one of the buttons causes associated text to appear in
corresponding static text box. An 'onEnter' method changes the text.
"""
import wx
import DecoratorStateMachine as dsm

class MyFrame(wx.Frame, dsm.ContextBase):

   xtable = dsm.TransitionTable('pstate')


   def __init__(self):
      MyFrame.xtable.initialize(self)

      wx.Frame.__init__(self, None, -1,"My Frame", size=(470,220))

      family = wx.SWISS
      style = wx.NORMAL
      weight = wx.BOLD
      font = wx.Font(11,family,style,weight, False,"Verdana")
      self.SetFont(font)

      panel = wx.Panel(self, -1)

      b = wx.Button(panel, -1,"Up", pos=(50,20), size=(80,35))
      self.Bind(wx.EVT_BUTTON, self.OnUp, b)
      b.SetDefault()

      b = wx.Button(panel, -1,"Down", pos=(50,60), size=(80,35))
      self.Bind(wx.EVT_BUTTON, self.OnDown, b)

      self.bx = wx.Button(panel, -1,"xxx", pos=(50,100), size=(110,35))
      self.Bind(wx.EVT_BUTTON, self.OnBA, self.bx)
      self.tx = wx.StaticText(panel, -1,"", pos=(50,140), size=(110,35))

      self.by = wx.Button(panel, -1,"yyy", pos=(180,100), size=(110,35))
      self.Bind(wx.EVT_BUTTON, self.OnBB, self.by)
      self.ty = wx.StaticText(panel, -1,"", pos=(180,140), size=(110,35))

      self.bz = wx.Button(panel, -1,"zzz", pos=(310,100), size=(110,35))
      self.Bind(wx.EVT_BUTTON, self.OnBC, self.bz )
      self.tz = wx.StaticText(panel, -1,"", pos=(310,140), size=(110,35))


   @dsm.transition(xtable)
   def OnUp(self, event):
      pass

   @dsm.transition(xtable)
   def OnDown(self, event):
      pass

   @dsm.event(xtable)
   def OnBA(self, event):
      pass

   @dsm.event(xtable)
   def OnBB(self, event):
      pass

   @dsm.event(xtable)
   def OnBC(self, event):
      self.tz.SetLabel("Bossy")


class Off(MyFrame):
  "This is state Off"

   def onEnter(self):
      self.bx.SetLabel("Chase")
      self.by.SetLabel("Onry")
      self.bz.SetLabel("Cow")

   def OnBA(self, event):
      self.tx.SetLabel("Chase the")

   def OnBB(self, event):
      self.ty.SetLabel("Onry")


class Low(MyFrame):
  "This is state Low"
   items = ["Walk","Green","Llama"]

    def onEnter(self):
      self.bx.SetLabel(self.items[0])
      self.by.SetLabel(self.items[1])
      self.bz.SetLabel(self.items[2])

   def OnBA(self, event):
      self.tx.SetLabel("Walk the")

   def OnBB(self, event):
      self.ty.SetLabel(self.items[1])

   def OnBC(self, event):
      self.tz.SetLabel(self.items[2])


class Medium(MyFrame):
  "This is state Medium"
   ytable = dsm.TransitionTable('qstate')

   def onEnter(self):
      if not hasattr(self, 'qstate'):    #unconditionally initialize for no history
         self.ytable.initialize(self)
      self.doEnter()

   @dsm.event(ytable)
   def doEnter(): pass

   @dsm.transitionevent(ytable)
   def OnBA(self, event):
      pass

   @dsm.transitionevent(ytable)
   def OnBB(self, event):
      pass

   @dsm.transitionevent(ytable)
   def OnBC(self, event):
      pass


class High(Low):
  "This is state High"

   items = ["Pet","Tame","Dog"]

   def OnBA(self, event):
      self.tx.SetLabel("Pet his")

class MedBlue(Medium):
  """State med blu"""

   items = ["Med BLue","Checkered","Tractor"]

   def onEnter(self):
      self.bx.SetLabel(self.items[0])
      self.by.SetLabel(self.items[1])
      self.bz.SetLabel(self.items[2])

   def doEnter(self):
      self.onEnter()

   def OnBA(self, event):
      self.tx.SetLabel("Med Blue")
   def OnBB(self, event):
      self.ty.SetLabel("Chekered")
   def OnBC(self, event):
      self.tz.SetLabel("Tractor")


class MedRed(Medium):
  """State med red"""

   items = ["Med Red","Striped","Combine"]

   def onEnter(self):
      self.bx.SetLabel(self.items[0])
      self.by.SetLabel(self.items[1])
      self.bz.SetLabel(self.items[2])

   def doEnter(self):
      self.onEnter()

   def OnBA(self, event):
      self.tx.SetLabel("Med Red")
   def OnBB(self, event):
      self.ty.SetLabel("Striped")
   def OnBC(self, event):
      self.tz.SetLabel("Combine")


MyFrame.xtable.nextStates(Low, (Medium,Off))
MyFrame.xtable.nextStates(Medium, (High,Low))
MyFrame.xtable.nextStates(High, (Off,Medium))
MyFrame.xtable.nextStates(Off, (Low,High))
MyFrame.xtable.initialstate = Off

Medium.ytable.nextStates(MedBlue, (MedBlue, MedRed, MedRed))
Medium.ytable.nextStates(MedRed,  (MedBlue, MedBlue, MedRed))
Medium.ytable.initialstate = MedBlue


if __name__=='__main__':
   app = wx.PySimpleApp()
   frame = MyFrame()
   frame.Show(True)
   app.MainLoop()

Erlang的OTP支持通过"gen_fsm"构造状态机。我上次看它已经有几年了,所以我有点生疏了,但是你可以在谷歌上搜索"Erlang Gen_-fsm",然后找到大量参考资料。


我参加晚会已经晚了将近十年,但最近我偶然发现了一种晦涩的语言,它借用了金融服务管理局所谓的休谟的观点。

我不确定它是否仍然是主动维护的,但是您至少可以下载编译器并使用它。信息是很难得到的,但是网上有一些论文和文章显示了这些要点。


微软研究院最近在Github上发布了P语言。它们还具有psharp框架,该框架提供了C扩展库和高级语法以及该语言的编译器。

我期待着尝试一下。

以下是其中一个C扩展示例的一部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
internal class Server : Machine
{
    MachineId Client;

    [Start]
    [OnEntry(nameof(InitOnEntry))]
    class Init : MachineState { }

    void InitOnEntry()
    {
        ...
        this.Goto(typeof(Active));
    }

    ...

下面是它们的高级语法的一部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
?using System;

namespace TheStateMachine
{
  internal machine Client
  {
    private machine Server;
    private start state Init
    {
      entry
      {
        this.Server = (trigger as Config).target;
        jump(Playing);
      }
    }

    private state Playing
    {
      entry
      {
        //execute logic
      }
      on AnotherEvent goto AnotherState;
      on SomeEvent do ProcessSomeLogic;
    }

  ...

Krishnamurthi先生有一个演讲和一篇论文,关于如何使用宏来为自动机添加嵌入式子语言来进行设计。不过,我不确定是否有任何方案将他的宏作为标准库包括在内。


除了Ragel之外,还有一种技术上很有趣但相当晦涩的语言,叫做sl1。请参阅http://ieeexplore.ieee.org/xpl/freeabsu all.jsp?Arnumber=1095580。它是由斯洛文尼亚的Iskratel创建的,目的是开发电信系统,其中状态机是基本块。


在C中,迭代器(带有"yield return"和"yield break")是直接转换为状态机的语言构造。我从来没有这样使用过它,但实际上我认为它可以在实践中使用。

这里有一个关于它的stackoverflow问题。但投票最高的答案却不鼓励…


我刚刚找到一个:ASML(抽象状态机语言)。这是在codeplex上有更多信息的页面。

有趣的是,它是由微软开发的。