正则表达式的虚拟机

时间:2015-05-22 13:13:49

标签: regex vm-implementation

我读了Regular Expression Matching: the Virtual Machine Approach,现在我尝试解析正则表达式并从中创建虚拟机。 标记器工作并创建其标记。在那一步之后,我从令牌流创建反向波兰表示法,所以最后我得到

a b c | |
来自正则表达式a|(b|c)

。 那么,现在是我卡住的一步:我想要一个数组

0: split 1, 3
1: match 'a'
2: jump 7
3: split 4, 6
4: match 'b'
5: jump 7
6: match 'c'
7: noop

来自上面的流。我没有把它弄好......我使用输出数组和堆栈作为每个令牌的起始位置。首先,将3个值添加到输出中(并且它是堆栈的起始位置)。

output              stack
------------------- ------
0: match 'a'        0: 0
1: match 'b'        1: 1
2: match 'c'        2: 2

使用|,我会从堆叠中弹出最后2个位置,并在特定位置插入splitjump。根据当前堆栈长度和我添加的元素数量计算这些值。 最后,我将最后一个元素的新起始位置添加到堆栈中(在这种情况下保持不变)。

output              stack
------------------- ------
0: match 'a'        0: 0
1: split 2, 4       1: 1
2: match 'b'
3: jump 5
4: match 'c'

这似乎没问题。现在,弹出下一个| ......

output              stack
------------------- ------
0: split 1, 3       0: 0
1: match 'a'
2: jump 7
3: split 2, 4
4: match 'b'
5: jump 5
6: match 'c'

这就是问题所在。我必须更新之前计算的所有地址(第3行和第5行)。那不是我想要的。 我猜,相对地址有同样的问题(至少如果值为负)。

所以我的问题是,如何从正则表达式创建一个vm。 我是在正确的轨道上(使用rpn形式)还是有另一种(和/或更简单的)方式?

输出数组存储为整数数组。 split - 命令实际上需要3个条目,jump需要两个,......

4 个答案:

答案 0 :(得分:0)

我认为在处理过程中不是设置地址,而是可以将引用存储到要跳转的命令,在输出数组中也可以存储引用(或指针)。完成所有处理后,您将继续生成的输出,并根据结果数组中引用命令的实际位置分配索引。

答案 1 :(得分:0)

RPN不是构建所需输出的最佳方式。如果您构建了一个AST,那么使用递归遍历生成代码会很容易。

假设您有一个OR节点,例如,有两个孩子“左”和“右”。每个节点实现一个方法generate(OutputBuffer),OR节点的实现如下所示:

void generate(OutputBuffer out)
{
int splitpos = out.length();
out.append(SPLIT);
out.append(splitpos+3); //L1
out.append(0); //reservation 1
//L1
left.generate(out);
int jumppos = out.length();
out.append("JUMP");
out.append(0); //reservation 2
//L2
out.set(splitpos+2, out.length()); //reservation 1 = L2
right.generate(out);
//L3
out.set(jumppos+1, out.length()); //reservation 2 = L3
}

答案 2 :(得分:0)

使用相对跳转和拆分会更容易。

  • a-将match推入堆栈

    0: match 'a'
    
  • b-将match推入堆栈

    0: match 'a'
    --
    0: match 'b'
    
  • c-将match推入堆栈

    0: match 'a'
    --
    0: match 'b'
    --
    0: match 'c'
    
  • | —从堆栈中弹出两帧,然后按split <frame1> jump <frame2>

    0: match 'a'
    --
    0: split +1, +3
    1: match 'b'
    2: jump +2
    3: match 'c'
    
  • | —从堆栈中弹出两帧,然后按split <frame1> jump <frame2>

    0: split +1, +3
    1: match 'a'
    2: jump +5
    3: split +1, +3
    4: match 'b'
    5: jump +2
    6: match 'c'
    

如果确实需要绝对跳跃,则可以轻松地迭代并调整所有偏移量。

答案 3 :(得分:0)

FWIW,Cox发布了他的方法here的实现。您可能会发现它对您有所帮助。