免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2628 | 回复: 9
打印 上一主题 下一主题

求Perl语法解析库及工具推荐 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-11-08 11:49 |只看该作者 |倒序浏览
我最近需要完成几个和语法解析相关的工具,包括一个简单的代码自动生成工具和一个配置文件语法解析工具。

代码自动生成工具:
场景1:
hello,q{1-3}c{1-5}:m,l{0-2,99}
m->MUSTERNTER
l->LEGAL

生成:
hello
    MUSTERNTER
    LEGAL
        0
        1
        2
        99
    END
END

q1
    MUSTERENTER
END

q1c1
    MUSTERENTER
    LEGAL
        0
        1
        2
        99
    END
END

q1c2
    MUSTERENTER
    LEGAL
        0
        1
        2
        99
    END
END

...

我现在有一个手写的脚本可以完成简单的语法解析,但是复杂的很容易出错;
有没有什么工具或库可以自动解析像c{1-3} -> c1,c2,c3的语法解析工具?

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
2 [报告]
发表于 2010-11-08 11:55 |只看该作者
cpan Parse::RecDescent

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
3 [报告]
发表于 2010-11-08 12:09 |只看该作者
cpan Parse::RecDescent
zhlong8 发表于 2010-11-08 11:55

你错了,楼主要的不是这个。楼主要的是一个组合枚举的东西。

论坛徽章:
0
4 [报告]
发表于 2010-11-08 13:11 |只看该作者
回复 2# zhlong8


    先谢了,我再在cpan上扒扒看。

其实我现在最需要的是一个用于展开{}内内容的示例或者一个可以解析很弱的正则表达式的示例。

我现在在做的是一个自动生成语法校验的辅助工具,手工敲一堆的验证命令很是麻烦,我想先把常用的命令通过表达式展开,但是不管是perl还是bash都没有合适的工具

我现在已经做了一个枚举语法的小工具,能够自用,但是已经不能再扩展了,所以需要一个成熟的工具/库去整合这一堆语法

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
5 [报告]
发表于 2010-11-08 13:21 |只看该作者
感觉写一个不是很难嘛。不知道有没有这方面的 CPAN 模块。

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
6 [报告]
发表于 2010-11-08 13:23 |只看该作者
的确,只看后面配置文件语法解析了。楼主这个代码生成看不太懂规则,楼主有设计语法和转换规则那不就是个编译器了

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
7 [报告]
发表于 2010-11-08 13:30 |只看该作者
本帖最后由 zhlong8 于 2010-11-08 13:34 编辑

回复 4# feiliang1983

这样能展开{}里面的内容,不过要看情况可能有bug
  1. sub trans;
  2. my $s = 'hello,q{1-3}c{1-5}:m,l{0-2,99}';

  3. $s =~ s/(\w\{.*?\})/trans $1/ge; #看你的转换应该是前面一个字符跟 {} 的形式

  4. sub trans {
  5.     my $s = shift;
  6.     # 你的展开规则
  7. }
复制代码
总之感觉自己写一个应该也复杂不到哪里吧,用模块什么的还要看一堆文档,看完都搞定了估计

论坛徽章:
0
8 [报告]
发表于 2010-11-08 14:59 |只看该作者
回复 7# zhlong8

我需要的是一个代码自动生成工具。

实际上我现在是在帮人写一堆简单的验证规则;验证规则中有%90以上的内容就是:
key
    RANGE start end
    LEGAL
       1
       2
       99
   END
END

key
   LEGAL
       1
       0
   END
END
之类的东西

key的格式为q{1-25}[c{1-9}[c{1-5}]],但是如果想做的更好一点,我想将key的格式作成更通用的模式。

我现在已经完成的东西在通用性上做的很差,第一个文件可以采用
hello,world,q{1-10}c{1-5},q{11-12}c{0,1,2},q{13-19}c{0,1},q{20-25}:m
q{1-15}:l{0,1,99}
q{16-20}:l{0-7,99}
...
方式直接替换
但是来了一个新的文件,我保留的关键字q/c都变成了非关键字,结果导致替换完全失败;如果进一步扩张关键字集的话代码量将超过我要写的语法验证文件,从成本上讲非常不划算;如果我将新文件中的冲突给手动移除的话,之后的操作比用vi直接写然后进行字符串替换的效率只低不高;而且新的脚本能够复用的可能性非常小,我不想去做。

现在我想做的是找一个能够进行宏解析的工具,通过定义自己的宏语法规则,完成一个类似编译器的工具。
我不是计算机专业出身,对编译原理不是很了解。想找一个合适的库帮助解析语法,最好只需要填充对一个个token的回调函数。这样就算我一个解释脚本写的不是很好,我也能保证在不需要对脚本做大的变动的基础上完善脚本的目的。

我现在的问题是在cpan上我除了扒拉parser相关的库之外还有什么方式快速找到一个适用的库的方法。
我在cpan上找了好几页,目前没有发现适用的package

论坛徽章:
46
15-16赛季CBA联赛之四川
日期:2018-03-27 11:59:132015年亚洲杯之沙特阿拉伯
日期:2015-04-11 17:31:45天蝎座
日期:2015-03-25 16:56:49双鱼座
日期:2015-03-25 16:56:30摩羯座
日期:2015-03-25 16:56:09巳蛇
日期:2015-03-25 16:55:30卯兔
日期:2015-03-25 16:54:29子鼠
日期:2015-03-25 16:53:59申猴
日期:2015-03-25 16:53:29寅虎
日期:2015-03-25 16:52:29羊年新春福章
日期:2015-03-25 16:51:212015亚冠之布里斯班狮吼
日期:2015-07-13 10:44:56
9 [报告]
发表于 2010-11-08 16:00 |只看该作者
回复 8# feiliang1983


    宏展开我只知道有个 m4,而且不会用

论坛徽章:
0
10 [报告]
发表于 2010-11-09 11:00 |只看该作者
回复 9# zhlong8

我的代码如下:
我目前只定义了四个宏规则:
n -> 删除MUSTENTER规则
s\d+e\d+ -> 输出 RANGE start end规则
l{[\d+-\d+]|\d+} -> 输出 LEGAL规则
j -> 输出JUMPS模板

这个脚本的主要作用是在需要大量手工输入简单语法规则检查(epidata)时,通过使用宏替换规则生成一个简单的模板,然后使用一个高效的编辑器完成剩余的工作。

一个简单的验证用模板输入如下:
hello,world,s{1-5|7|9},s{good-bye|hello}:l{0|1|99}
haha,q{1-6}c{1-9|12|17|99}c{1-7}:s1e9,l{0|1}
q{7-9}c{1-9}:s1e8,j

目前简单的替换规则语法简单,工作良好;当输入行数超过1000行时比vi/notepad手工编辑效率高50%以上(脚本完成基本内容的输入,手工调整一下几个跳转规则即可)。
脚本长相相当丑陋,继续求可用的库或成熟工具,最好能够在100行以内可以定义10组以下的宏替换规则,完成基本的宏扩展功能;或者能够在1000行以内,可以定义约30个关键字完成一个小的语法生成工具。
  1. #!/usr/bin/perl
  2. #

  3. use strict;
  4. use warnings;

  5. if (@ARGV < 2) {
  6.     print "Usage:
  7. $0 inputfile outputfile
  8.   inputfile: formated template file for qes file
  9.   outputfile: .chk file generated from inputfile\n";

  10.   exit 1;
  11. }

  12. my ($inp, $outp) = @ARGV;

  13. print "input file: $inp, output file: $outp\n";

  14. open IN, "<$inp" or die "open $inp for read failed: $!\n";
  15. open OUT, ">$outp" or die "open $outp for write failed: $!\n";

  16. sub do_second_parse(@)
  17. {
  18.     my @origs = @_;
  19.     my @keys;

  20.     foreach my $orig (@origs) {
  21.         if ($orig =~ /^([^{]*){([^}]*)}(.*)$/) {
  22.             my ($p, $m, $a) = ($1, $2, $3);
  23.             $a = "" if not defined $a;

  24.             my @sps = split(/\|/, $m);
  25.             foreach my $sp (@sps) {
  26.                 my ($start, $end) = split(/-/, $sp);
  27.                 if (not defined $end) {
  28.                     push @keys, "$p$sp$a";
  29.                 }
  30.                 else {
  31.                     if (int($end) > int($start)) {
  32.                         push @keys, map { "$p$_$a" } ($start .. $end);
  33.                     }
  34.                     else {
  35.                         push @keys, "$p$sp$a";
  36.                     }
  37.                 }
  38.             }
  39.         }
  40.         else {
  41.             push @keys, $orig;
  42.         }
  43.     }

  44.     return @keys;
  45. }

  46. sub second_parse($)
  47. {
  48.     my $fp = shift;
  49.     my @keys;

  50.     push @keys, $fp;
  51.     while ( grep {/{.*}/} @keys ) {
  52.         @keys = do_second_parse(@keys);
  53.     }

  54.     return @keys;
  55. }

  56. sub parse_key($)
  57. {
  58.     my $orig = shift;
  59.     my @first_parse = split(/,/, $orig);
  60.     my @keys = ();

  61.     foreach my $fp (@first_parse) {
  62.         push @keys, second_parse($fp);
  63.     }

  64.     return @keys;
  65. }

  66. sub parse_rules ($)
  67. {
  68.     my $orig_rule = shift;
  69.     my %rules = (
  70.         enter => "    MUSTENTER\n",
  71.         range => "",
  72.         legal => "",
  73.         jumps => "",
  74.     );

  75.     my @items = split(/,/, $orig_rule);
  76.     if (grep { /^n$/ } @items) {
  77.         enter => "";
  78.     }

  79.     my @ranges = grep { /^s\d+e\d+$/ } @items;
  80.     if (@ranges) {
  81.         my $range = $ranges[0];
  82.         my ($start, $end) = ($range =~ /^s(\d+)e(\d+)$/);
  83.         $rules{range} = "    RANGE $start $end\n";
  84.     }

  85.     my @legals = grep { /^l{.*}$/ } @items;
  86.     if (@legals) {
  87.         my $legal = $legals[0];
  88.         my ($lr) = ($legal =~ /^l{(.*)}$/);
  89.         my @bps = split(/\|/, $lr);
  90.         my @aps;

  91.         foreach my $bp (@bps) {
  92.             my ($start, $end) = split(/-/, $bp);
  93.             if (not defined $end) {
  94.                 push @aps, $bp;
  95.             }
  96.             else {
  97.                 if (int($end) > int($start)) {
  98.                     push @aps, ($start .. $end);
  99.                 }
  100.                 else {
  101.                     push @aps, $bp;
  102.                 }
  103.             }
  104.         }

  105.         $rules{legal} = "    LEGAL\n";
  106.         foreach my $ap (@aps) {
  107.             $rules{legal} .= "        $ap\n";
  108.         }
  109.         $rules{legal} .= "    END\n";
  110.     }

  111.     if (grep { /^j$/ } @items) {
  112.         $rules{jumps} .= "    JUMPS\n";
  113.         $rules{jumps} .= "        ###TODO###\n";
  114.         $rules{jumps} .= "    END\n";
  115.     }

  116.     my $res = $rules{enter};
  117.     if ($rules{range}) {
  118.         $res .= $rules{range};
  119.     }

  120.     if ($rules{legal}) {
  121.         $res .= $rules{legal};
  122.     }

  123.     if ($rules{jumps}) {
  124.         $res .= $rules{jumps};
  125.     }

  126.     return $res;
  127. }

  128. while (<IN>) {
  129.     chomp;

  130.     s/#.*$//;
  131.     s/\s*//;
  132.     next if /^$/;

  133.     my ($key, $rule) = split(/:/, $_);
  134.     my @keys  = parse_key($key);
  135.     #use Data::Dumper;
  136.     #print Dumper(@keys);
  137.     my $fmt = parse_rules($rule);
  138.    
  139.     foreach my $_key (@keys) {
  140.         print OUT "$_key\n${fmt}END\n\n";
  141.     }
  142. }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP