在Perl中编写简单的解析器:有lexer输出,下一步去哪里?

时间:2015-11-05 16:17:32

标签: perl parsing dsl

我试图在Perl中编写一个简单的数据操作语言(只读,它意味着将SQL启发的查询转换为用于vSphere Perl API的过滤器和属性:{{3} })

如果我理解正确的话,我目前有类似lexer输出的东西 - 像这样的标记列表(Data :: Dumper打印哈希数组):

$VAR1 = {
      'word' => 'SHOW',
      'part' => 'verb',
      'position' => 0
    };
$VAR2 = {
      'part' => 'bareword',
      'word' => 'name,',
      'position' => 1
    };
$VAR3 = {
      'word' => 'cpu,',
      'part' => 'bareword',
      'position' => 2
    };
$VAR4 = {
      'word' => 'ram',
      'part' => 'bareword',
      'position' => 3
    };

现在我要做的是构建一个语法树。我到目前为止看到的文档主要是使用模块和生成BNF的语法,但目前我无法绕过它。

我想修补相对简单的程序代码,可能是递归的,以便自己做一些丑陋的实现。

我目前正在考虑的是构建一串$token->{'part'}s这样的字符串:

my $parts = 'verb bareword bareword ... terminator';

然后针对它运行一个大而丑的正则表达式,(ab)使用Perl的功能将代码嵌入到正则表达式中:http://pubs.vmware.com/vsphere-60/topic/com.vmware.perlsdk.pg.doc/viperl_advancedtopics.5.1.html_

$parts =~ /
     ^verb(?{ do_something_smart })\s       # Statement always starts with a verb
     (bareword\s(?{ do_something_smart }))+ # Followed by one or more barewords
     | # Or
     # Other rules duct taped here
/x;

无论我到目前为止发现了什么,都需要扎实的CS和/或语言学知识,而且我甚至都不理解它。

我应该如何处理词法分析器输出以便通过正确的解析来开始理解和修补?类似于'构建一组临时哈希,代表声明的较小部分'或者'删除子字符串,直到字符串为空,然后验证你得到的内容'。

我知道龙书和SICP​​,但此时我还想要更轻松的东西。

谢谢!

1 个答案:

答案 0 :(得分:0)

正如上面几条评论所述,但这里又作为一个真正的答案:

您可能会喜欢Parser::MGC。 (免责声明:我是Parser::MGC

的作者

首先考虑各种令牌的现有(regexp?)定义,并将它们转换为"令牌_..."方法使用generic_token方法。

从这里开始,您可以开始构建方法,通过使用结构构建方法来解析更大和更大的语法结构。

至于实际构建AST - 它可能最简单的方法是使用包含结构命名部分的键简单地发出HASH引用。从你在问题中给出的例子中讲出一个语法结构很难,但你可能会有一个"命令的概念"这是一个"动词"其次是一些"名词"。您可以使用以下方法解析它:

sub parse_command
{
  my $self = shift;

  my $verb = $self->token_verb;

  my $nouns = $self->sequence_of( sub { $self->token_noun } );
      # $nouns here will be an ARRAYref

  return { type => "command", verb => $verb, nouns => $nouns };
}

通常在这一点上编写一个解析器,我决定我想要一些实际类型的对象,而不仅仅是哈希引用。一种简单的方法是通过我的另一个模块Struct::Dumb

use Struct::Dumb qw( -named_constructors );
struct Command => [qw( verb nouns )];

...
return Command( verb => $verb, nouns => $nouns );