2017-04-21 50 views
2

我正在尝试创建一个语法。这是我的代码到目前为止:如何用perl6语法创建一个分析树?

use Text::Table::Simple; # zef install Text::Table::Simple 

my $desc = q:to"FIN"; 
record person 
     name string; 
     age int; 
end-record 
FIN 

grammar rec { 
     token TOP { <ws>* 'record' \s+ <rec-name> <field-descriptors> <ws> 'end-record' <ws> } 
     token rec-name { \S+ } 
     token field-descriptors { <field-descriptor>* } 
     token field-descriptor { <ws>* <field-name> <ws>+ <field-type> <ws>* ';' } 
     token field-name { \S+ } 
     token field-type { <[a..z]>+ } 
     token ws { <[\r\n\t\ ]> } 
} 


class recActions { 
     method field-descriptors($/) { $/.make: $/; } 
     method field-descriptor($/) { $/.make: $/; } 
     method field-name($/) { $/.make: $/ } 
     method field-type($/) { $/.make: $/ } 
} 

my $r = rec.parse($desc, :actions(recActions)); 
#say $r; 

my $inp = q:to"FIN"; 
adam 26 
joe  23 
mark 51 
FIN 

sub splitter($line) { 
     my @lst = split /\s+/, $line; 
} 


sub matrixify(&splitter, $data) 
{ 
     my @d = (split /\n/, (trim-trailing $data)).map(-> $x { splitter $x ; }); 
     #@d.say; 
     #my @cols = <name age>; 
     #say lol2table(@cols, @d).join("\n"); 
     @d; 
} 

#my @cols =<A B>; 
#my @rows = ([1,2], [3,4]); 
#say lol2table(@cols, @rows).join("\n"); 

my @m = matrixify &splitter, $inp; 

sub tabulate($rec-desc, @matrix) 
{ 
     my $fds = $rec-desc<field-descriptors>; 
     #say %fds<field-name>; 
     say $fds; 
     my @cols = $rec-desc.<field-descriptors>.map(-> $fd { say $fd; $fd.<field-name> ; 1;}); 
     #say $rec-desc.<field-descriptors>; 
     #say @cols; 
} 
tabulate $r, @m ; 

我真的只是想让语法从输入创建一个列表/散列表的树。从代码的输出是:

​​

这看起来相当不错。 perl6似乎解码了这样的事实,即field-descriptors由多个field-descriptor组成,但实际上它似乎并未将它们放入列表中。我可以做say $fds;,但我不能做say $fds[0];。为什么前者“工作”,但后者却不行?

我必须承认对发生的事情掌握得相当薄弱。我会更好地使用规则而不是令牌吗?我真的需要一个动作类吗?我不能只是让perl自动为我填充分析树,而不必指定一类操作?

更新:可能的解决方案

假设我们只想解析:

my $desc = q:to"FIN"; 
record person 
    name string; 
    age int; 
end-record 
FIN 

和字段名和类型,我们发现报告。我打算做一个小的简化到我上面写的语法:

grammar rec { 
    token TOP { <ws>* 'record' \s+ <rec-name> <field-descriptor>+ <ws> 'end-record' <ws> } 
    token rec-name { \S+ } 
    token field-descriptor { <ws>* <field-name> <ws>+ <field-type> <ws>* ';' } 
    token field-name { \S+ } 
    token field-type { <[a..z]>+ } 
    token ws { <[\r\n\t\ ]> } 
} 

让我们完全回避动作,只是解析成一棵树:

my $r1 = rec.parse($desc); 

现在让我们来检查我们的手艺,和打印出来的名称和类型的,我们已经解析了每个字段:

for $r1<field-descriptor> -> $fd { say "Name: $fd<field-name>, Type: $fd<field-type>"; } 

我们的产量是因为我们预计:

Name: name, Type: string 
Name: age, Type: int 
+0

我注意到,下面的工作,虽然:'$ fds [0]'所以我想我必须越来越近。 – blippy

+0

http://stackoverflow.com/a/40799024/1077672有帮助吗? – raiph

+0

'$ /。make:$ /'实际上是一个无操作。你还需要对'.parse'的结果调用'.made'或'.ast'来获取操作类的结果。 –

回答

2

我知道你现在已经全部设置好了,但是这里有一个答案,可以为以后的其他人阅读事情做好准备。

如何用perl6语法创建一个分析树?

它很简单,只要使用调用其中一个内置解析例程的返回值即可。

(提供解析成功parse和表兄弟返回parse tree

从代码的输出...看起来还算不错。 perl6似乎正在解释这样一个事实,即字段描述符是由多个字段描述符组成的,但实际上并没有将它们放入列表中。我可以这样说$ fds;但我不能说$ fds [0] ;.为什么前者“工作”,但后者却不行?

看到我对SO问题的回答"How do I access the captures within a match?"

我会更好地使用规则而不是令牌吗?

标记和规则之间的唯一区别是解释标记/规则中包含的裸空白的默认值。

(一令牌内裸空格完全被忽略。一个规则内裸空白表示“有可能是在输入了这一点空白”。)

我真的需要一个行动类[?]

只有一个动作类麻烦,如果你想系统地进行后期处理解析ŧ稀土元素。

我不能只是让Perl自动为我填充分析树而无需指定一类操作?

是的。无论何时您致电parse并且解析成功,其返回值都是解析树。

更新:可能的解决方案

让我们完全回避动作,只是解析成一棵树:

权。如果你想要的只是解析树,那么你不需要动作类,你不需要拨打makemade。相反,如果你想要另一棵树,比如Abstract Syntax Tree,那么你可能会发现使用内置的makemade例程很方便。如果您使用makemade,您可能会发现将它们与单独的操作类结合使用,而不是直接将它们直接嵌入到语法规则/令牌/正则表达式中是适当的。