我正在尝试为一个简单的DSL编写一个解析器,它在表单< statementName>中有十几个语句. <参数1> < param2的> ……;,参数数量变化的地方.由于语句的结构非常相似(所有匹配的语句名称字符串后跟一系列由名称给出的标记),并且结果的结构非常相似(所有存储语句名称和参数的哈希值),我想知道如何指定想要的结果结构,而不必为每个语句操作重复自己.
动作类的伪代码,可以帮助我指定这样的结果结构:
class FooActions { method *_stmt ($/) { @result[0] = make string of statement name $/[0]; @result[1] = make hash of $/[1..] with the keys being the name of the rule at index (i.e. '"var"' for `<var=identifier>` and `"type"` for `<type>`,etc.) and values being the `.made` results for the rules at index (see below); return @result; } method identifier ($/) { return ~$/ } method number ($/) { return +$/ } method type ($/) { return ~$/ } }
测试文件:
use v6; use Test; use Foo; my $s; $s = 'GoTo 2 ;'; is_deeply Foo::FooGrammar.parse($s).made,('GoTo',{pos => 2}); $s = 'Set foo 3 ;'; is_deeply Foo::FooGrammar.parse($s).made,('Set',{var => 'foo',target => 3}); $s = 'Get bar Long ;'; is_deeply Foo::FooGrammar.parse($s).made,('Get',{var => 'bar',type => 'Long'}); $s = 'Set foo bar ;'; is_deeply Foo::FooGrammar.parse($s).made,target => 'bar'});
语法:
use v6; unit package Foo; grammar FooGrammar is export { rule TOP { <stmt> ';' } rule type { 'Long' | 'Int' } rule number { \d+ } rule identifier { <alpha> \w* } rule numberOrIdentifier { <number> || <identifier> } rule goto_stmt { 'GoTo' <pos=number> } rule set_stmt { 'Set' <var=identifier> <target=numberOrIdentifier> } rule get_stmt { 'Get' <var=identifier> <type> } rule stmt { <goto_stmt> || <set_stmt> || <get_stmt> } }
解决方法
此方法将每个语句类型表示为
Proto-regex,并使用
syms来避免重复语句关键字(GoTo等).
个别陈述没有行动方法.这些是在下一级(TOP)处理的,它在匹配时使用caps方法将其转换为哈希.
< sym> capture用于额外关键字.行的其余部分转换为哈希.解决方法如下
语法和行动:
use v6; unit package Foo; grammar Grammar is export { rule TOP { <stmt> ';' } token type { 'Long' | 'Int' } token number { \d+ } token identifier { <alpha>\w* } rule numberOrIdentifier { <number> || <identifier> } proto rule stmt {*} rule stmt:sym<GoTo> { <sym> <pos=.number> } rule stmt:sym<Set> { <sym> <var=.identifier> <target=.numberOrIdentifier> } rule stmt:sym<Get> { <sym> <var=.identifier> <type> } } class Actions { method number($/) { make +$/ } method identifier($/) { make ~$/ } method type($/) { make ~$/ } method numberOrIdentifier($/) { make ($<number> // $<identifier>).made } method TOP($/) { my %caps = $<stmt>.caps; my $keyw = .Str given %caps<sym>:delete; my %args = %caps.pairs.map: {.key => .value.made}; make ($keyw,%args,); } }
测试:
use v6; use Test; use Foo; my $actions = Foo::Actions.new; my $s; $s = 'GoTo 2 ;'; is-deeply Foo::Grammar.parse($s,:$actions).made,{pos => 2}); $s = 'Set foo 3;'; is-deeply Foo::Grammar.parse($s,target => 3}); $s = 'Get bar Long ;'; is-deeply Foo::Grammar.parse($s,type => 'Long'}); $s = 'Set foo bar ;'; is-deeply Foo::Grammar.parse($s,target => 'bar'});