为不同元数的规则指定共同的语法动作
Specify common grammar actions for rules of different arity
我正在尝试为一个简单的 DSL 编写一个解析器,它有许多 <statementName> <param1> <param2> ... ;
形式的语句,其中参数的数量各不相同。由于语句的结构非常相似(都匹配语句名称字符串后跟一系列由名称给出的标记)并且生成的结果的结构非常相似(都存储语句名称和参数的散列),我想知道如何指定想要的结果结构,而不必为每个语句操作重复自己。
一个动作的伪代码 class 可以帮助我指定这样一个结果结构:
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, ('Set', {var => 'foo', 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 and uses syms 以避免重复语句关键字(GoTo
等)。
单个语句没有操作方法。这些在下一级 (TOP
) 处理,它在匹配上使用 caps 方法,将其转换为散列。
<sym>
抓包用于额外关键字。行的其余部分转换为散列。解决方案如下:
语法和动作:
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, ('GoTo', {pos => 2});
$s = 'Set foo 3;';
is-deeply Foo::Grammar.parse($s, :$actions).made, ('Set', {var => 'foo', target => 3});
$s = 'Get bar Long ;';
is-deeply Foo::Grammar.parse($s, :$actions).made, ('Get', {var => 'bar', type => 'Long'});
$s = 'Set foo bar ;';
is-deeply Foo::Grammar.parse($s, :$actions).made, ('Set', {var => 'foo', target => 'bar'});
我正在尝试为一个简单的 DSL 编写一个解析器,它有许多 <statementName> <param1> <param2> ... ;
形式的语句,其中参数的数量各不相同。由于语句的结构非常相似(都匹配语句名称字符串后跟一系列由名称给出的标记)并且生成的结果的结构非常相似(都存储语句名称和参数的散列),我想知道如何指定想要的结果结构,而不必为每个语句操作重复自己。
一个动作的伪代码 class 可以帮助我指定这样一个结果结构:
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, ('Set', {var => 'foo', 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 and uses syms 以避免重复语句关键字(GoTo
等)。
单个语句没有操作方法。这些在下一级 (TOP
) 处理,它在匹配上使用 caps 方法,将其转换为散列。
<sym>
抓包用于额外关键字。行的其余部分转换为散列。解决方案如下:
语法和动作:
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, ('GoTo', {pos => 2});
$s = 'Set foo 3;';
is-deeply Foo::Grammar.parse($s, :$actions).made, ('Set', {var => 'foo', target => 3});
$s = 'Get bar Long ;';
is-deeply Foo::Grammar.parse($s, :$actions).made, ('Get', {var => 'bar', type => 'Long'});
$s = 'Set foo bar ;';
is-deeply Foo::Grammar.parse($s, :$actions).made, ('Set', {var => 'foo', target => 'bar'});