0

我正在测试 Ragel 中状态动作的功能。我有以下 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。它们在转换中的动作之后但在当前字符前进并针对输入块的末尾进行测试之前执行。

但是其中没有关于执行两次操作的内容。我非常感谢您对此事的任何帮助或澄清。

提前致谢。

4

2 回答 2

1

问题在于$运算符的使用,它们在所有状态上执行,这意味着动作将在每个标签的起始状态以及控制转移到的状态上运行。在这种情况下,您应该使用>运算符,它们仅在每个标签进入开始状态时执行。这确保每个标签只调用一次每个动作。所以,机器看起来像这样:

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

这是上面的状态图:

上述机器的状态图

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

于 2016-01-05T02:14:11.453 回答
0

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

这是 Ragel 文件的 Graphviz:

在此处输入图像描述

在对您的问题进行了一些反思之后,我认为 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 现在看起来像这样:

在此处输入图像描述

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

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。

于 2015-12-25T16:52:43.733 回答