Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我可以用Perl编写DSL吗?_Perl_Dsl - Fatal编程技术网

我可以用Perl编写DSL吗?

我可以用Perl编写DSL吗?,perl,dsl,Perl,Dsl,我们使用Perl实现GUI测试自动化。它非常成功。我们已经为GUI测试编写了一种非常轻量级的DSL语言。DSL与对象模型非常相似 例如,我们在根目录下有一个应用程序对象。应用程序中的每个属性页都是一个视图对象。页面下的每个页面称为页面对象本身。 我们从Perl向GUI应用程序发送命令,GUI解释命令并很好地响应命令。要发送命令,请执行以下操作: socket_object->send_command("App.View2.Page2.Activate()") socket_object-&

我们使用Perl实现GUI测试自动化。它非常成功。我们已经为GUI测试编写了一种非常轻量级的DSL语言。DSL与对象模型非常相似

例如,我们在根目录下有一个应用程序对象。应用程序中的每个属性页都是一个视图对象。页面下的每个页面称为页面对象本身。 我们从Perl向GUI应用程序发送命令,GUI解释命令并很好地响应命令。要发送命令,请执行以下操作:

socket_object->send_command("App.View2.Page2.Activate()")
socket_object->send_command("App.View1.Page3.OKBtn.Click()")
这不是很可读。相反,我想为App、View和Page编写一个perldsl。 Perl是否提供了某种DSL结构,我可以在其中执行以下操作

App.View2.Page2.Activate();
App.View1.Page2.Click();
其中App应为应用程序类的实例。我必须在运行时获取View2的对象


如何使用这样的东西?

perl5中的方法调用使用
->
而不是
,所以它看起来像
App->View2->Page2->Activate()
$App->View2->Page2->Active()
,除非您做了一些真正有趣的事情(例如,源代码过滤器)。假设没有问题,您可以使用普通的PerlOO内容

现在,您需要的下一部分是在运行时创建方法。这其实相当简单:

sub _new_view {
    my ($view, $view_num);

    # ...
    # ... (code to create $view object)
    # ...

    my $sym = "App::View$view_num";
    *$sym = sub { return $view }; # can also use Symbol package
}
或者,如果您希望仅在调用方法时创建方法,则
AUTOLOAD
就是这样做的。您还可以滥用autoload使所有方法调用成功(尽管要注意具有特殊含义的方法,如DESTROY)

这将为您提供语法。让您的对象生成一个字符串以传递给
send\u命令
,应该不会那么困难


另外,我对它不太熟悉,但你可能想看看。它可能有更简单的方法来实现这一点。

您几乎可以用Perl做任何事情。但您必须做一些奇怪的事情,才能让Perl使用非Perl的语法执行

  • 要准确地处理您所拥有的内容,您必须使用许多高级技巧,根据定义,这些技巧是不可维护的。你必须:

    • 连接运算符“.”(需要一个连接引用)
    • 关闭或创建一个subs来允许那些简单的单词-当然,你可以为你想要使用的所有单词(或使用模块)编写subs
    • 可能的话,创建多个包,并使用多个
      自动加载
      s
  • 另一种方式是,我可能会因为提到这个功能而遭到否决。所以我不推荐这种方法给那些寻求帮助的人。但它就在那里。源代码过滤器(我也做了我的工作)只是其中一个领域,你可能会认为你太聪明了,不利于自己

    尽管如此,如果您对Perl作为DSL“主机”语言感兴趣,那么它也不是完全禁止的。然而,将其限制在你想做的事情上,可能会马上做你需要做的大部分事情。它将把
    转换成Perl能够理解的“->”。但您仍然可以查看源过滤器,以了解幕后的情况

    我也不想在离开这个话题时不建议使用Damian Conway的方法来缓解生成自己的源代码过滤器(我建议不要这样做)可能带来的许多挫折

  • 最简单的方法是放弃“.”运算符,而只希望看到Perl代码

    App->View2->Page2->Activate(); 
    App->View1->Page2->Click();
    
    App
    可能是一个包或一个子包。在当前包中定义或导入,它返回一个对象,该对象被祝福到一个包中,并带有一个
    View2
    sub(可能是一个子包),该子包返回一个包的名称或祝福到一个包中的引用,该子包可以理解
    Page2
    ,最后,返回的代码将理解
    激活
    单击
    。(如果需要,请参阅。)


我建议您停止尝试做奇怪的“DSL”工作,只编写Perl类来处理您想要管理的对象。我建议您考虑为此使用新的MoosePerl对象系统,尽管传统的PerlOO可以。深入阅读面向对象教程的Perl文档;它们很棒。

DSL源过滤器 这是另一个尝试。Skipoppy说得有道理,但再看一眼,我注意到(到目前为止)你问得不多,这太复杂了。您只需要接受每个命令并告诉远程服务器执行它。必须理解命令的不是perl,而是服务器

因此,我删除了一些关于源过滤器的警告,并决定向您展示 如何写一个简单的。再说一次,你所做的事情并没有那么复杂,我下面的“过滤”也很简单

package RemoteAppScript;
use Filter::Simple;    # The basis of many a sane source filter
use Smart::Comments;   # treat yourself and install this if you don't have 
                       # it... or just comment it out.

# Simple test sub
sub send_command { 
    my $cmd = shift;
    print qq(Command "$cmd" sent.\n);
    return;
}

# The list of commands
my @script_list;

# The interface to Filter::Simple's method of source filters.
FILTER { 
    # Save $_, because Filter::Simple doesn't like you reading more than once.
    my $mod = $_;

    # v-- Here a Smart::Comment.
    ### $mod

    # Allow for whole-line perl style comments in the script
    $mod =~ s/^\s*#.*$//m;

    # 1. Break the package up into commands by split
    # 2. Trim the strings, if needed
    # 3. lose the entries that are just blank strings.
    @script_list 
        = grep { length } 
          map  { s/^\s+|\s+$//g; $_ } 
          split /;/, $mod
        ;
    ### @script_list

    # Replace the whole script with a command to run the steps.
    $_ = __PACKAGE__ . '::run_script();';
    # PBP.
    return;
};

# Here is the sub that performs each action.
sub run_script { 
    ### @script_list
    foreach my $command ( @script_list ) {
        #send_command( $command );
        socket_object->send_command( $command );
    }
}

1;
您需要将其保存在perl可以找到的地方。(如果需要知道位置,请尝试使用
perl-MData::Dumper-e'打印转储程序(\@INC),“\n”

然后,您可以创建一个“perl”文件,该文件包含以下内容:

use RemoteAppScript;
App.View2.Page2.Activate();
App.View1.Page2.Click();
然而 没有真正的理由不能读取包含服务器命令的文件。这将抛出
过滤器
调用。你会的

App.View2.Page2.Activate();
App.View1.Page2.Click();
在脚本文件中,perl文件看起来更像:

#!/bin/perl -w 

my $script = do { 
    local $/;
    <ARGV>;
};

$script =~ s/^\s*#.*$//m;

foreach my $command ( 
    grep { length() } map  { s/^\s+|\s+$//g; $_ } split /;/, $script 
) { 
    socket_object->send_command( $command );
}

是直接集成到perl解析器中的源过滤器的现代替代品,值得一看。

替代覆盖
'。
或使用
->
语法可能使用包语法(:),即在创建View2/Page 2时创建像App::View2和App::View2::Page2这样的包,将自动加载的sub添加到包中,该包将委托给App::View::Page或App::View方法,如下所示:

#!/bin/perl -w 

my $script = do { 
    local $/;
    <ARGV>;
};

$script =~ s/^\s*#.*$//m;

foreach my $command ( 
    grep { length() } map  { s/^\s+|\s+$//g; $_ } split /;/, $script 
) { 
    socket_object->send_command( $command );
}
在您的应用程序/DSL.pm中:

package App::DSL;
use strict; 
use warnings;
# use to avoid *{"App::View::$view::method"} = \&sub and friends
use Package::Stash;

sub new_view(%);
our %views;

# use App::DSL (View1 => {attr1 => 'foo', attr2 => 'bar'}); 
sub import {
    my $class = shift;
    my %new_views = @_ or die 'No view specified';

    foreach my $view (keys %new_views) {
            my $stash = Package::Stash->new("App::View::$view");
        # In our AUTOLOAD we create a closure over the right
        # App::View object and call the right method on it
        # for this example I just used _api_\L$method as the
        # internal method name (Activate => _api_activate)
        $stash->add_package_symbol('&AUTOLOAD' =>  
            sub {  
                our $AUTOLOAD;
                my ($method) = 
                   $AUTOLOAD =~ m{App::View::\Q$view\E::(.*)};
                my $api_method = "_api_\L$method";
                die "Invalid method $method on App::View::$view"
                   unless my $view_sub = App::View->can($api_method);
                my $view_obj = $views{$view}
                    or die "Invalid View $view";
                my $sub = sub {
                        $view_obj->$view_sub();
                };
                     # add the function to the package, so that AUTOLOAD
                     # won't need to be called for this method again
                $stash->add_package_symbol("\&$method" => $sub);
                goto $sub;
            });
        $views{$view} = bless $new_views{$view}, 'App::View';
    }
}

package App::View;

# API Method App::View::ViewName::Activate;
sub _api_activate {
    my $self = shift;
    # do something with $self here, which is the view 
    # object created by App::DSL
    warn $self->{attr1};
}

1;
在你的剧本中:

use strict;
use warnings;
# Create App::View::View1 and App::View::View2
use App::DSL (View1 => {attr1 => 'hello'}, View2 => {attr1 => 'bye'});
App::View::View1::Activate();
App::View::View2::Activate();
App::View::View1::Activate();

我对insideout一无所知,所以我无法推测它会为您带来什么价值。仅仅编程传统的OO类来处理这些问题有什么不对?第二种解决方案是+1。简单的fo