65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 XML/XSLT 自动生成有限状态自动机的源代码

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.36/5 (5投票s)

2005年9月1日

CPOL

1分钟阅读

viewsIcon

40002

downloadIcon

9

展示了如何通过应用 XSLT 生成由 XML 描述的有限状态自动机的源代码。

引言

有限状态自动机在一些情况下非常有用,例如:解析协议、跟踪用户输入等。但它有一个很大的缺点:自动机的源代码非常庞大。它包含大量的行,并且难以创建和维护。本文介绍如何使用 XML 描述自动机,并使用 XSLT 生成其源代码。我使用了 C#,但您可以使用任何其他 .NET 语言。作为示例,我将创建一个简单的解析器,用于解析仅包含两个文本命令:“show message”(显示消息)和“show error”(显示错误)。

描述自动机

有限状态自动机是一组状态和状态之间的转换。因此,我将此内容描述在以下 XML 文件中

<?xml version="1.0" ?>
<parser-descriptor namespace="CommandParser">
  <class-descriptor classname="Parser">
    <states varname="_state" initstate="NOTHING" >
      <state name="NOTHING">
        <input val="sS" newstate="S" />
      </state>
            
      <!-- show error or message -->
      <state name="S">
        <input val="hH" newstate="SH" />
      </state>
      <state name="SH">
        <input val="oO" newstate="SHO" />
      </state>
      <state name="SHO">
        <input val="wW" newstate="SHOW" />
      </state>
      <state name="SHOW">
        <input val=" " newstate="SHOW_space" />
      </state>
            
      <state name="SHOW_space">
        <input val="eE" newstate="SHOW_E" />
        <input val="mM" newstate="SHOW_M" />
      </state>
            
      <!-- show error -->
      <state name="SHOW_E">
        <input val="rR" newstate="SHOW_ER" />
      </state>
      <state name="SHOW_ER">
        <input val="rR" newstate="SHOW_ERR" />
      </state>
      <state name="SHOW_ERR">
        <input val="oO" newstate="SHOW_ERRO" />
      </state>
      <state name="SHOW_ERRO">
        <input val="rR" newstate="SHOW_ERROR" />
      </state>
      <state name="SHOW_ERROR">
      </state>
            
      <!-- show message -->
      <state name="SHOW_M">
        <input val="eE" newstate="SHOW_ME" />
      </state>
      <state name="SHOW_ME">
        <input val="sS" newstate="SHOW_MES" />
      </state>
      <state name="SHOW_MES">
        <input val="sS" newstate="SHOW_MESS" />
      </state>
      <state name="SHOW_MESS">
        <input val="aA" newstate="SHOW_MESSA" />
      </state>
      <state name="SHOW_MESSA">
        <input val="gG" newstate="SHOW_MESSAG" />
      </state>
      <state name="SHOW_MESSAG">
        <input val="eE" newstate="SHOW_MESSAGE" />
      </state>
      <state name="SHOW_MESSAGE">
      </state>
            
    </states>
  </class-descriptor>
</parser-descriptor>

其中

  • <parser-descriptor> 是 .NET 命名空间的描述
  • <class-descriptor> 是类的描述
  • <states> 是用于存储自动机状态的类字段的描述
  • <state> 是状态的描述
  • <input> 是转换的描述(输入消息和新状态)

生成源代码

我创建了这个简单的 XSLT 样式表,用于将 XML 文件转换为 C# 源代码

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html"/>
  <!--  -->
<xsl:template match="/parser-descriptor">
<html>
<body>
<pre>
namespace <xsl:value-of select="@namespace"/>
{
<xsl:apply-templates select="class-descriptor"/>
}
</pre>
</body>
</html>
</xsl:template>

<!-- create class -->
<xsl:template match="class-descriptor">
  public class <xsl:value-of select="@classname" />
  {
    public enum States
    {
<xsl:for-each select=".//states//state" >
      <xsl:value-of select="@name" />,
</xsl:for-each>
    }
    States <xsl:value-of select=".//states//@varname" /> = 
	States.<xsl:value-of select=".//states//@initstate" />;
    
    public void Reset()
    {
      <xsl:value-of select=".//states//@varname" /> = 
	States.<xsl:value-of select=".//states//@initstate" />;
    }
    
    public States State
    {
      get
      {
        return <xsl:value-of select=".//states//@varname" />;
      }
    }

    public void InputChar(char c)
    {
      switch(<xsl:value-of select=".//states//@varname" />)
      {
<xsl:for-each select=".//states//state" >
      case States.<xsl:value-of select="@name" />:
<xsl:apply-templates select="input"/>
        throw new Exception("Invalid input character");
</xsl:for-each>
      default:
        throw new Exception("Invalid state value");
      }
    }
  }
</xsl:template>

<!-- processing of input character -->
<xsl:template match="input" >
       if (-1 != ("<xsl:value-of select="@val" />").IndexOf(c))
       {
         <xsl:value-of select="..//..//@varname" /> = 
		States.<xsl:value-of select="@newstate" />;
         return;
       }       
</xsl:template>

</xsl:stylesheet>

我创建了一个简单的 C# 程序来使用此 XSLT 样式表

using(Stream xsltData = new FileStream(_xsltFileName, FileMode.Open))
{
  XslTransform tr = new XslTransform();
  tr.Load(new XmlTextReader(xsltData));

  tr.Transform(_xmlFileName, _resultFileName);
}

转换后,我获得了自动机的源代码

namespace CommandParser
{
  public class Parser
  {
    public enum States
    {
      NOTHING,
      S,
      SH,
      ... and so on
    }
    States _state = States.NOTHING;
    
    public void Reset()
    {
      _state = States.NOTHING;
    }
    
    public States State
    {
      get
      {
        return _state;
      }
    }

    public void InputChar(char c)
    {
      switch(_state)
      {

        case States.NOTHING:

          if (-1 != ("sS").IndexOf(c))
          {
            _state = States.S;
            return;
          }       

          throw new Exception("Invalid input character");

        case States.S:

          if (-1 != ("hH").IndexOf(c))
          {
            _state = States.SH;
            return;
          }       

          throw new Exception("Invalid input character");

...等等。

最后,这是用于检查我的自动机工作是否正常的代码

private void _textBoxCommands_TextChanged(object sender, System.EventArgs e)
{      
  Parser parser = new Parser();
  string msg = _textBoxCommands.Text;
  int length = msg.Length;

  for (int i = 0; i < length; ++i)
  {
    char c = msg[i];
    try
    {
      parser.InputChar(c);
    }
    catch(Exception ex)
    {
      return;
    }
  }
  switch(parser.State)
  {
    case Parser.States.SHOW_ERROR:
      MessageBox.Show("Error Message");
      break;
    case Parser.States.SHOW_MESSAGE:
      MessageBox.Show("Info Message");
      break;
  }
}

结论

现在,我可以以简单的方式更改此自动机解析的一组命令。源代码将自动生成。我曾在创建 GUI 控件中用于跟踪用户输入的有限状态自动机时使用过这种方法。

祝你好运。

历史

  • 2005 年 9 月 1 日:初始发布
© . All rights reserved.