为什么Ragel两次执行州和州内行动?

时间:2015-12-01 01:39:11

标签: c ragel

我正在测试Ragel中的to-and-state动作的功能。我有以下Ragel计划:

ragelScaffolding.rl:

#include <stdio.h>
#include <stdbool.h>
#include <string.h>

char *p, *pe;
int cs;

void runRagelMachine(char instructions[], int instructionLen){
p = instructions; 
pe = p + instructionLen;
%%{
    machine test;
    action testToAction1{
        puts("1");
    }

    action testFromAction1{
        puts("f1");
    }

    action testToAction2{
        puts("2");
    }

    test = (
        start: (
            any -> s1
        ),
        s1: (
            any -> s2
        )$to(testToAction1) $from(testFromAction1),
        s2: (
            any -> final
        )$to(testToAction2)
    );

    main := test;
    write data;
    write init;
    write exec;
}%%
}

int main(){

char buf[1024];
runRagelMachine(buf, 1024);
}

我希望这会输出以下内容:

1
f1
2

但它输出:

1
f1
1
2
f1
2

告诉我它会两次运行这些操作。我一直在考虑为什么会出现这种情况并阅读文档,但我似乎无法弄清楚为什么会这样。当使用Ragel 6.9和7进行编译(并使用gcc编译C)时会发生这种情况。文档说明如下:

  

只要状态机进入指定状态,就会执行到状态操作   通过过渡的自然运动或基于动作的控制转移,例如fgoto。   它们在转换后的操作之后但在当前字符前进之前执行   并针对输入块的末尾进行测试。

但是没有任何关于两次执行动作的内容。我真的很感谢对此事的任何帮助或澄清。

提前致谢。

2 个答案:

答案 0 :(得分:1)

问题是使用在所有状态下执行的$运算符,这意味着操作将在每个标签的起始状态以及控制转移到<的状态下运行/ em>的。在这种情况下,您应该使用>运算符,只有在每个标签处输入开始状态时才会执行。这可确保每个标签仅调用一次每个操作。所以,机器看起来像这样:

test = (
    start: (
        any -> s1
    ),
    s1: (
        any -> s2
    )>to(testToAction1) >from(testFromAction1),
    s2: (
        any -> final
    )>to(testToAction2)
);

以下是上述状态图:

State diagram for the above machine

如您所见,每个动作只被调用一次。

答案 1 :(得分:0)

在尝试了解Ragel的工作原理时,您可以使用-V选项生成Graphviz点文件。

以下是您的Ragel文件的Graphviz:

enter image description here

在你的问题上有一些反思之后,我认为Ragel的运作方式如下:我使它与精确的东西相匹配,而不是任何东西,它使事情更容易理解。

我将您的代码更改为:

    test = (
      start: (
          '1' -> s1
      )$to(testFromAction1),
      s1: (
          '2' -> s2
      )$to(testToAction1),
      s2: (
          '3' -> final
      )$to(testToAction2)
    );

并将其命名为:

int main()
{
  char buf[5];

  strcpy(buf, "1341");
  runRagelMachine(buf, 5);
}

这不应该完全匹配。

graphvis现在看起来像这样:

enter image description here

几乎相同,但如果我运行它,这是输出:

f1
1

它匹配'1',它触发了启动状态并调用了testFromAction1。在状态s1中没有任何匹配,这不会阻止testToAction1的调用。

如果我们称之为:

  strcpy(buf, "1234");
  runRagelMachine(buf, 5);

它应该与所有州相匹配。 我们得到了这个输出:

f1
1
1
2
2

Ragel正在以树步骤解析我们的字符串。

  • 首先,它在输入中查找“0”启动器状态。它调用testToAction1(打印f1)并评估s1。在这种情况下,它打印第一个'1'。
  • 然后,解析器的光标移动到输入的“1”。它验证当前状态(s1)匹配。为此,它再次评估s1,这就是打印第二个'1'的原因。由于s1调用s2状态,它也会计算s2,这是第一个'2'打印的原因。由于光标下的当前数据与'2'匹配,光标移动到'3'。
  • 现在,我们再次评估s2步骤,打印一个'2',因为我们执行它以进入最终状态。

如果我们使用最后一个伪输入来运行它,则确认了这个逻辑:

  strcpy(buf, "1243");
  runRagelMachine(buf, 5);

这次打印:

f1
1
1
2

我们可以在这里看到它停止了'4'数字的解析,仍然我们打印了4行。与前面描述的逻辑相同。

我不确定它完全回答你的问题,但我希望它有助于理解一点Ragel。