- 名前
- バージョン
- 概要
- 説明
- 概観
Parse::RecDescent
の利用- 規則 (Rules)
- サブ規則 (Subrules)
- トークン (Tokens)
- 終端記号の区切り (Terminal Separators)
- アクション (Actions)
- 開始アクション (Start-up Actions)
- オートアクション (Autoactions)
- オートツリー (Autotrees)
- オートスタブ (Autostubbing)
- 先読み (Look-ahead)
- ディレクティブ (Directives)
- サブ規則の引数リスト (Subrule argument lists)
- オルタネーション (Alternations)
- 増加解析 (Incremental Parsing)
- パーサのプリコンパイル (Precompiling parsers)
Parse::RecDescent
のメタ文法 (A Metagrammar forParse::RecDescent
)
- おや? (GOTCHAS)
- 診断 (DIAGNOSTICS)
- 作者
- バグやイライラ (BUGS AND IRRITATIONS)
- 進行中の問題と今後の方向性 (ON-GOING ISSUES AND FUTURE DIRECTIONS)
- 著作権
名前¶
Parse::RecDescent - 再帰下降パーサの生成 (Generate Recursive-Descent Parsers)
バージョン¶
This document describes version 1.94 of Parse::RecDescent, released April 9, 2003.
このドキュメントは、2003年4月9日にリリースされた Parse::RecDescentのバージョン1.94について述べたものである。
概要¶
use Parse::RecDescent;
# $grammarの仕様書からパーサを生成
$parser = new Parse::RecDescent ($grammar);
# $othergrammarの仕様書からパーサを生成
$anotherparser = new Parse::RecDescent ($othergrammar);
# 規則'startrule'($grammar内で定義されなければならない)
# を用いて$textを解析:
$parser->startrule($text);
# 規則'otherrule'(これも$grammar内で定義されなければならない)
# を用いて$textを解析:
$parser->otherrule($text);
# 全般的なトークン前置子のパターンを変更
# (デフォルトは'\s*'):
$Parse::RecDescent::skip = '[ \t]+';
# $newgrammarで定義されたプロダクションを使って既存規則の
# プロダクションを置き換える(あるいは新しいものを作成):
$parser->Replace($newgrammar);
# $moregrammarで定義されたプロダクションを加えて
# 既存規則を拡張する(あるいは新しいものを作成)
$parser->Extend($moregrammar);
# グローバルなフラグ(-sでのコマンドライン引数として便利):
$::RD_ERRORS # 未定義でなければ、致命的エラーを報告
$::RD_WARN # 未定義でなければ、致命的でない問題も報告
$::RD_HINT # 定義されていれば、回復方法も示唆
$::RD_TRACE # 定義されていれば、パーサの振る舞いもトレース
$::RD_AUTOSTUB # 定義されていれば、未定義の規則用に"スタブ"を生成
$::RD_AUTOACTION # 定義されていれば、プロダクションに指定されたアクション追加
説明¶
概観¶
Parse::RecDescent incrementally generates top-down recursive-descent text parsers from simple yacc-like grammar specifications. It provides:
Parse::RecDescentは、単純なyacc風の文法仕様から、 下向きの再帰下降パーサを段階的に生成する。提供される機能は:
Regular expressions or literal strings as terminals (tokens),
終端記号(トークン)は正規表現かリテラルな文字列
Multiple (non-contiguous) productions for any rule,
任意の規則に対して複数(不連続)のプロダクション(生成規則)
Repeated and optional subrules within productions,
プロダクション内においてサブ規則は繰り返しと省略が可能
Full access to Perl within actions specified as part of the grammar,
文法の一部として指定されたアクション内でPerlにフルアクセス
Simple automated error reporting during parser generation and parsing,
パーサの生成及び解析時における簡単な自動エラーレポート
The ability to commit to, uncommit to, or reject particular productions during a parse,
パース中に特定のプロダクションにcommit、uncommit、rejectが可能
The ability to pass data up and down the parse tree ("down" via subrule argument lists, "up" via subrule return values)
解析木に対してデータを上げ渡したり、下げ渡したり可能 (下げるのはサブ規則引数リストを通じて、上げはサブ規則の戻り値を通じて)
Incremental extension of the parsing grammar (even during a parse),
文法解析の段階的な拡張(解析中でさえも)
Precompilation of parser objects,
パーサオブジェクトのプリコンパイル(precompilation)
User-definable reduce-reduce conflict resolution via "scoring" of matching productions.
プロダクションのマッチに"点数ををつける"ことで ユーザー定義可能な還元-還元衝突の解決
Parse::RecDescent
の利用¶
Parser objects are created by calling Parse::RecDescent::new
, passing in a grammar specification (see the following subsections). If the grammar is correct, new
returns a blessed reference which can then be used to initiate parsing through any rule specified in the original grammar. A typical sequence looks like this:
Parse::RecDescent::new
を呼び出すと、文法仕様を 通してパーサオブジェクトが生成される(続くサブセクションを参照)。 文法が正しいものであれば、new
はblessされたリファレンスを返す。 その後、このリファレンスは元の文法で指定された規則を通じて解析を開始 するのに利用できる。典型的な流れは次のようになる:
$grammar = q {
# ここに文法仕様
};
$parser = new Parse::RecDescent ($grammar) or die "Bad grammar!\n";
# $textの獲得
defined $parser->startrule($text) or print "Bad text!\n";
The rule through which parsing is initiated must be explicitly defined in the grammar (i.e. for the above example, the grammar must include a rule of the form: "startrule: <subrules>".
解析開始の際に用いられる規則は、文法において明示的に定義されなければ ならない(つまり上の例でいうと、この文法は次のような形式の規則を 含まなければならない:"startrule: <subrules>")。
If the starting rule succeeds, its value (see below) is returned. Failure to generate the original parser or failure to match a text is indicated by returning undef
. Note that it's easy to set up grammars that can succeed, but which return a value of 0, "0", or "". So don't be tempted to write:
開始の規則が成功すると、その値(下記参照)が返される。 元になるパーサの生成に失敗したり、テキスト照合に失敗すると、 undef
が返されることによって示される。成功する文法を用意するのは 簡単だが、それは0、"0"、あるいは""という値を返すことに注意。だから、 このような書き方をしてはいけない:
$parser->startrule($text) or print "Bad text!\n";
Normally, the parser has no effect on the original text. So in the previous example the value of $text would be unchanged after having been parsed.
通常、パーサは元のテキストに何ら影響を与えない。それゆえ、先の 例では、$textの値は解析後も変化しない。
If, however, the text to be matched is passed by reference:
しかしながら、照合させるテキストをリファレンスで渡した場合:
$parser->startrule(\$text)
then any text which was consumed during the match will be removed from the start of $text.
照合時に消費されたテキストは$textのはじめから取り除かれるだろう。
規則 (Rules)¶
In the grammar from which the parser is built, rules are specified by giving an identifier (which must satisfy /[A-Za-z]\w*/), followed by a colon on the same line, followed by one or more productions, separated by single vertical bars. The layout of the productions is entirely free-format:
パーサ構築に用いられる文法中で、識別子(/[A-Za-z]\w*/を満たさなければ ならない)、次いで同一行にコロン、次いで単一の縦棒で区切られた 一つ以上のプロダクション(production)を与えることによって規則は 設定される。プロダクションのレイアウトは完全に自由形式である:
rule1: production1
| production2 |
production3 | production4
At any point in the grammar previously defined rules may be extended with additional productions. This is achieved by redeclaring the rule with the new productions. Thus:
文法の任意の場所で、前に定義した規則を追加のプロダクションでもって 拡張することができる。これは新しいプロダクションの規則を再宣言する ことによって達成される。すなわち:
rule1: a | b | c
rule2: d | e | f
rule1: g | h
is exactly equivalent to:
は次のものと正確に等価である:
rule1: a | b | c | g | h
rule2: d | e | f
Each production in a rule consists of zero or more items, each of which may be either: the name of another rule to be matched (a "subrule"), a pattern or string literal to be matched directly (a "token"), a block of Perl code to be executed (an "action"), a special instruction to the parser (a "directive"), or a standard Perl comment (which is ignored).
規則内のプロダクションはそれぞれ、0個以上のアイテムを構成する。 アイテムは次のいずれかである:照合されるべき別規則の名前("サブ規則")、 直接照合される正規表現パターンあるいは文字列リテラル("トークン")、 実行されるPerlコードのブロック("アクション")、パーサに対する特別な 指示("ディレクティブ")、あるいは標準的なPerlコメント(無視される)。
A rule matches a text if one of its productions matches. A production matches if each of its items match consecutive substrings of the text. The productions of a rule being matched are tried in the same order that they appear in the original grammar, and the first matching production terminates the match attempt (successfully). If all productions are tried and none matches, the match attempt fails.
ある規則のプロダクションのどれか一つにマッチすれば、あるテキストは その規則にマッチしたことになる。あるプロダクションのアイテム全てが、 そのテキストの連続する文字列にマッチすれば、そのプロダクションは マッチしたことになる。マッチした規則のプロダクションは、元の文法に 現れたのと同じ順番で試される。そして最初にマッチしたプロダクションに よって照合の試みは終了する(成功)。もし全てのプロダクションが試みられ、 かつ、マッチしなければ、照合の試みは失敗となる。
Note that this behaviour is quite different from the "prefer the longer match" behaviour of yacc. For example, if yacc were parsing the rule:
この振る舞いは、yaccの”最長マッチ指向”(prefer the longer match) とは全く違うので注意すること。
seq : 'A' 'B'
| 'A' 'B' 'C'
upon matching "AB" it would look ahead to see if a 'C' is next and, if so, will match the second production in preference to the first. In other words, yacc effectively tries all the productions of a rule breadth-first in parallel, and selects the "best" match, where "best" means longest (note that this is a gross simplification of the true behaviour of yacc but it will do for our purposes).
"AB"にマッチした時、yaccは'C'が次に来るかどうか調べるために先読みをする。 そしてその通りであったら、最初のプロダクションではなく二番目の プロダクションにマッチすることになる。言い換えると、yaccは 幅優先で並列に規則のプロダクションを全て試みる。そして"best"な マッチを選択する。ここで"best"とは、最長を意味する(これはyaccの 振る舞いをあまりに単純化してしまっているが、我々の目的には適っている)。
In contrast, Parse::RecDescent
tries each production depth-first in sequence, and selects the "best" match, where "best" means first. This is the fundamental difference between "bottom-up" and "recursive descent" parsing.
対照的に、Parse::RecDescent
は深さ優先で連続的にそれぞれの プロダクションを試みる。そして"best"なマッチを選択する。ここで "best"とは、最初のプロダクションということである。これが、 ”上向き”("bottom-up")パーサと”再帰下降”("recursive descent") パーサの根本的な違いである。
Each successfully matched item in a production is assigned a value, which can be accessed in subsequent actions within the same production (or, in some cases, as the return value of a successful subrule call). Unsuccessful items don't have an associated value, since the failure of an item causes the entire surrounding production to immediately fail. The following sections describe the various types of items and their success values.
プロダクション内で照合に成功したアイテムはそれぞれ、ある値が割り当て られる。この値は同じプロダクション内の(場合によっては成功したサブ規則 の戻り値として)その後に続くアクションからアクセス可能である。 照合に成功しなかったアイテムは関連づけられた値を持たない。というのも、 アイテムの失敗は、それを取り囲んでいるプロダクション全体を直ちに失敗 させるためである。続くセクションでは、様々なアイテムのタイプと、その 成功時の値について記述する。
サブ規則 (Subrules)¶
A subrule which appears in a production is an instruction to the parser to attempt to match the named rule at that point in the text being parsed. If the named subrule is not defined when requested the production containing it immediately fails (unless it was "autostubbed" - see Autostubbing).
プロダクション内で現れるサブ規則は、その時点で解析されているテキストに 対して、その名前が付けられた規則を照合するようパーサを導く。 要求があったのにその名前のサブ規則が定義されていない場合、そのサブ規則を 含むプロダクションは直ちに失敗する("オートスタブされて"いない限り - オートスタブ (Autostubbing)を参照)。
A rule may (recursively) call itself as a subrule, but not as the left-most item in any of its productions (since such recursions are usually non-terminating).
ある規則は自分自身をサブ規則として(再帰的に)呼び出すかも知れない。 しかし、プロダクション内の最左端のアイテムとして呼ばれるのではない (なぜならそのような再帰は通常、非終端であるから)。
The value associated with a subrule is the value associated with its $return
variable (see "Actions" below), or with the last successfully matched item in the subrule match.
サブ規則に関連づけられた値とは、その$return
変数に関連づけられた 値のことである(後述の"アクション (Actions)"を参照)。あるいは、サブ規則の照合 において、最後に成功したアイテムと関連づけられた値である。
Subrules may also be specified with a trailing repetition specifier, indicating that they are to be (greedily) matched the specified number of times. The available specifiers are:
また、サブ規則はその後ろに、反復指定子が設定されているかもしれない。 この指定子は、指定した回数だけサブ規則が(貪欲に)マッチされる必要が あることを示している。利用可能な指定子は:
subrule(?) # 1あるいは0回マッチ
subrule(s) # 1回以上マッチ
subrule(s?) # 0回以上マッチ
subrule(N) # 正確にN回マッチ(Nは整数で N > 0)
subrule(N..M) # N〜M回マッチ
subrule(..M) # 1〜M回マッチ
subrule(N..) # 少なくともN回はマッチ
Repeated subrules keep matching until either the subrule fails to match, or it has matched the minimal number of times but fails to consume any of the parsed text (this second condition prevents the subrule matching forever in some cases).
反復サブ規則は、そのサブ規則が照合に失敗するか、最小回数は マッチしたがパースされたテキストのどこかで消費(consume)に 失敗するかのどちらかになるまで、照合を続ける(この二番目の条件によ り、ある場合で生じるサブルールが永久に照合を行うことを防いでいる)。
Since a repeated subrule may match many instances of the subrule itself, the value associated with it is not a simple scalar, but rather a reference to a list of scalars, each of which is the value associated with one of the individual subrule matches. In other words in the rule:
反復サブ規則は、サブ規則それ自身のインスタンスの多くを照合するかも しれないので、そのサブ規則に関連する値は単純なスカラーではなく、 リストのリファレンスとなる。このリストはそれぞれ、個々のサブ規則の マッチ一つひとつと関連する値である。つまり規則において:
program: statement(s)
the value associated with the repeated subrule "statement(s)" is a reference to an array containing the values matched by each call to the individual subrule "statement".
反復サブ規則"statement(s)"に関連づけられた値は、個々のサブ規則"statement" の呼び出し毎にマッチした値を含んだ配列へのリファレンスである。
Repetition modifieres may include a separator pattern:
反復修飾子は区切りパターンを含むかも知れない。
program: statement(s /;/)
specifying some sequence of characters to be skipped between each repetition. This is really just a shorthand for the <leftop:...> directive (see below).
これは反復するサブ規則間でスキップされるキャラクタの連なりを指定している。 これはまさに<leftop:...>ディレクティブ(後述を参照)の略記に 他ならない。
トークン (Tokens)¶
If a quote-delimited string or a Perl regex appears in a production, the parser attempts to match that string or pattern at that point in the text. For example:
もしプロダクション内にクォートで区切られた文字列か、Perlの正規表現が 現れたなら、パーサはテキストのその時点で、その文字列ないしは 正規表現パターンを照合しようと試みる:
typedef: "typedef" typename identifier ';'
identifier: /[A-Za-z_][A-Za-z0-9_]*/
As in regular Perl, a single quoted string is uninterpolated, whilst a double-quoted string or a pattern is interpolated (at the time of matching, not when the parser is constructed). Hence, it is possible to define rules in which tokens can be set at run-time:
正規のPerlでは、シングルクォートで囲まれた文字列は展開されない。他方、 ダブルクォートで囲まれた文字列や正規表現パターンは展開される(これは 照合時の話であって、パーサが構築される時の話ではない)。それゆえ、 実行時にトークンをセットしながら、規則を定義することが可能である。
typedef: "$::typedefkeyword" typename identifier ';'
identifier: /$::identpat/
Note that, since each rule is implemented inside a special namespace belonging to its parser, it is necessary to explicitly quantify variables from the main package.
注意して欲しいのだが、全ての規則は、パーサに属する固有の名前空間の内部で 実装されているので、mainパッケージの変数であることを明示的に指定する 必要がある。
Regex tokens can be specified using just slashes as delimiters or with the explicit m<delimiter>......<delimiter>
syntax:
正規表現トークンは、デリミタとしてスラッシュを使って指定することができる。 あるいは明示的にm<delimiter>......<delimiter>
構文が使える:
typedef: "typedef" typename identifier ';'
typename: /[A-Za-z_][A-Za-z0-9_]*/
identifier: m{[A-Za-z_][A-Za-z0-9_]*}
A regex of either type can also have any valid trailing parameter(s) (that is, any of [cgimsox]):
どちらのタイプの正規表現も、その後ろに妥当なパラメータ([cgimsox]で複数可) を持つことができる:
typedef: "typedef" typename identifier ';'
identifier: / [a-z_] # 最初はアルファベットかアンダーバー
[a-z0-9_]* # 次からは数字もOK
/ix # 大文字小文字の区別/スペース/コメント を無視
The value associated with any successfully matched token is a string containing the actual text which was matched by the token.
マッチに成功したトークンに関連づけられる値とは、トークンによって照合された 実際のテキストを含んだ文字列である。
It is important to remember that, since each grammar is specified in a Perl string, all instances of the universal escape character '\' within a grammar must be "doubled", so that they interpolate to single '\'s when the string is compiled. For example, to use the grammar:
次のことを覚えておくべきである。それぞれの文法はPerlの文字列で指定されて いるので、文法に含まれる一般的エスケープキャラクタ'\'の表記は全て"二重"に しなければならない。こうすれば、その文字列がコンパイルされる時に、単一の '\'に展開される。例えば次のような文法を利用するなら:
word: /\S+/ | backslash
line: prefix word(s) "\n"
backslash: '\\'
the following code is required:
以下のコードにする必要がある:
$parser = new Parse::RecDescent (q{
word: /\\S+/ | backslash
line: prefix word(s) "\\n"
backslash: '\\\\'
});
終端記号の区切り (Terminal Separators)¶
For the purpose of matching, each terminal in a production is considered to be preceded by a "prefix" - a pattern which must be matched before a token match is attempted. By default, the prefix is optional whitespace (which always matches, at least trivially), but this default may be reset in any production.
照合の目的からすると、プロダクション内の全ての終端記号は"前置子"が 前に来なければならないと考えられる。- すなわちトークンに対する照合が 試みられる前にマッチしなければならないパターンである。デフォルトでは この前置子は任意の空白である(少なくとも自明であるが、これは常に マッチする)。しかしこの既定値は任意のプロダクションで再設定できる。
The variable $Parse::RecDescent::skip
stores the universal prefix, which is the default for all terminal matches in all parsers built with Parse::RecDescent
.
変数$Parse::RecDescent::skip
は全般的な前置子を保持する。 Parse::RecDescent
で構築された全てのパーサにおいて、 全終端記号に対する照合のデフォルトはこの前置子になる。
The prefix for an individual production can be altered by using the <skip:...>
directive (see below).
個々のプロダクションに対する前置子は、<skip:...>
ディレクティブを利用して変更することができる(後述箇所を参考)。
アクション (Actions)¶
An action is a block of Perl code which is to be executed (as the block of a do
statement) when the parser reaches that point in a production. The action executes within a special namespace belonging to the active parser, so care must be taken in correctly qualifying variable names (see also "Start-up Actions" below).
アクションはPerlコードのブロックである。プロダクション内でパーサが この部分に到達すると、このPerlコードは(do
ステートメントブッロク の如く)実行される。このアクションは、実際に動作しているパーサに 属する固有の名前空間内で実行される。よって、正しく変数名を指定する ように注意しなければならない(後述の"開始アクション (Start-up Actions)"も参照)。
The action is considered to succeed if the final value of the block is defined (that is, if the implied do
statement evaluates to a defined value - even one which would be treated as "false"). Note that the value associated with a successful action is also the final value in the block.
アクションは、そのブロックの最終的な値が定義されたとき(すなわち内包 されたdo
ステートメントがある定義値 -- 偽として扱われたとしても -- を評価した場合)、成功したと考えられる。注意しなければならないのは、 成功したアクションに関連付けられる値もまた、そのブロック内の最終値で あるということだ。
An action will fail if its last evaluated value is undef
. This is surprisingly easy to accomplish by accident. For instance, here's an infuriating case of an action that makes its production fail, but only when debugging isn't activated:
最後に評価された値がundef
の場合、アクションは失敗に終わる。これは ふとした弾みで驚くほど簡単に起こる。例えばこれは、このアクションのある プロダクションを失敗させる劇的なケースである。ただしデバッグモードが 有効いなっていない時にのみ生じるのだが:
description: name rank serial_number
{ print "Got $item[2] $item[1] ($item[3])\n"
if $::debugging
}
If $debugging
is false, no statement in the block is executed, so the final value is undef
, and the entire production fails. The solution is:
もし$debugging
が偽であるなら、ブロック内のステートメントは実行されない。 その結果、最終的な値はundef
となり、プロダクション全体が失敗する。解決策は:
description: name rank serial_number
{ print "Got $item[2] $item[1] ($item[3])\n"
if $::debugging;
1;
}
Within an action, a number of useful parse-time variables are available in the special parser namespace (there are other variables also accessible, but meddling with them will probably just break your parser. As a general rule, if you avoid referring to unqualified variables - especially those starting with an underscore - inside an action, things should be okay):
アクション内では、パース実行時の有用な変数の多くは、そのパーサの特別な名前 空間を使って利用できる(他にもアクセス可能な変数はある。だがそれらの変数を いじくりまわすとパーサを壊してしまいかねない。一般的なルールとして、 アクション内では修飾されていない変数 - 殊に、アンダーバーで始まるもの - を 参照するのを回避しておけば、万事うまくいくであろう)。:
@item
と%item
-
The array slice
@item[1..$#item]
stores the value associated with each item (that is, each subrule, token, or action) in the current production. The analogy is to$1
,$2
, etc. in a yacc grammar. Note that, for obvious reasons,@item
only contains the values of items before the current point in the production.配列のスライス
@item[1..$#item]
は、現在のプロダクション内の各アイテム (サブ規則、トークン、アクション)に関連付けられた値を保持する。類似の ものとしてはyaccの文法内における$1
、$2
等があげられよう。 注意して欲しいのだが、明確な理由により、@item
はプロダクション内の 現在の地点より前のアイテムの値を含むだけである。The first element (
$item[0]
) stores the name of the current rule being matched.最初の要素(
$item[0]
)はマッチした現在の規則名を保持する。@item
is a standard Perl array, so it can also be indexed with negative numbers, representing the number of items back from the current position in the parse:@item
は標準的なPerlの配列なので、負の数もインデックスに利用できる。 これは、その構文解析における現在の場所から後ろのアイテム数を表す:stuff: /various/ bits 'and' pieces "then" data 'end' { print $item[-2] } # data をプリント # ($item[6]より簡単)
The
%item
hash complements the <@item> array, providing named access to the same item values:ハッシュ
%item
は、アイテムの値に対して名前でアクセスを可能にする ことにより、配列@item
を補完する:stuff: /various/ bits 'and' pieces "then" data 'end' { print $item{data} # data をプリント # (@itemを使うよりずっと簡単)
The results of named subrules are stored in the hash under each subrule's name (including the repetition specifier, if any), whilst all other items are stored under a "named positional" key that indictates their ordinal position within their item type: __STRINGn__, __PATTERNn__, __DIRECTIVEn__, __ACTIONn__:
名前付きサブ規則の結果は、それぞれサブ規則の名前のついたハッシュに 保持される(もしあれば反復指定子も含む)。一方、それ以外の全ての アイテムは、そのアイテムのタイプ別に順番を表す "名前+ポジション"キー(named positional key)に基づいて保持される: __STRINGn__、__PATTERNn__、__DIRECTIVEn__、__ACTIONn__:
stuff: /various/ bits 'and' pieces "then" data 'end' { save } { print $item{__PATTERN1__}, # 'various' をプリント $item{__STRING2__}, # 'then' をプリント $item{__ACTION1__}, # saveの戻り値を # プリント }
If you want proper named access to patterns or literals, you need to turn them into separate rules:
もし正規表現パターンやリテラルに対して適切な名前によるアクセスを望むなら、 それらの名前を個々別々の規則に分離してやらなければならない:
stuff: various bits 'and' pieces "then" data 'end' { print $item{various} # various をプリント } various: /various/
The special entry
$item{__RULE__}
stores the name of the current rule (i.e. the same value as$item[0]
.特別なエントリー
$item{__RULE__}
は現在の規則名を保持する(すなわち$item[0]
と同じ値ということ)。The advantage of using
%item
, instead of@items
is that it removes the need to track items positions that may change as a grammar evolves. For example, adding an interim<skip>
directive of action can silently ruin a trailing action, by moving an@item
element "down" the array one place. In contrast, the named entry of%item
is unaffected by such an insertion.@item
の代わりに%item
を使うメリットは、文法の進展に伴って アイテムの位置が変わってしまうのを追いかけなくて済むという所にある。 例えば、アクション内で<skip>
ディレクティブを追加すると、@item
の要素を配列一つ分下げてしまって、いつの間にかひき続く アクションを滅茶苦茶にしてしまいかねない。反対に、%item
の名前による エントリーは、そのような挿入による影響を受けない。A limitation of the
%item
hash is that it only records the last value of a particular subrule. For example:%item
には一つ制約があって、このハッシュは件のサブ規則における 最後の値を記録するだけなのである。例えば:range: '(' number '..' number )' { $return = $item{number} }
will return only the value corresponding to the second match of the
number
subrule. In other words, successive calls to a subrule overwrite the corresponding entry in%item
. Once again, the solution is to rename each subrule in its own rule:これは、
number
というサブ規則のうち、二番目のマッチに対応した値 だけを返す。つまり、成功したサブ規則の呼び出しは、%item
の対応して いるエントリーを上書きしてしまう。もう一度繰り返すと、この 解決方法は、サブ規則をそれぞれ独自の名前に変えてやることだ:range: '(' from_num '..' to_num )' { $return = $item{from_num} } from_num: number to_num: number
@arg
と%arg
-
The array
@arg
and the hash%arg
store any arguments passed to the rule from some other rule (see ""Subrule argument lists"). Changes to the elements of either variable do not propagate back to the calling rule (data can be passed back from a subrule via the$return
variable - see next item).配列
@arg
とハッシュ%arg
は規則から規則へと渡される引数を保持する ("サブ規則引数リスト (Subrule argument lists)"を参照)。どちらの 変数に対して要素を変更しても、それが呼び出し側の規則にまで伝搬すること はない(データは$return
変数を通じてサブ規則へ送り戻すことが可能で ある - 次項を参照)。 $return
-
If a value is assigned to
$return
within an action, that value is returned if the production containing the action eventually matches successfully. Note that setting$return
doesn't cause the current production to succeed. It merely tells it what to return if it does succeed. Hence$return
is analogous to$$
in a yacc grammar.アクション内である値を
$return
に代入した場合、そのアクションを含んでいる プロダクションが最終的にマッチに成功すれば、その値が返される。$return
をセットすることが現在のプロダクションを成功させるわけではない ことに注意。単に成功した場合に何が返されるのかを教えるだけである。 それゆえ、$return
はyacc文法における$$
に似ているといえよう。If
$return
is not assigned within a production, the value of the last component of the production (namely:$item[$#item]
) is returned if the production succeeds.プロダクション内で
$return
に値が代入されないで、そのプロダクションが 成功した場合は、プロダクションの最後の構成要素の値(つまり、$item[$#item]
) が返される。 $commit
-
The current state of commitment to the current production (see "Directives" below).
現在のプロダクションに対するcommitの状態(下の"ディレクティブ (Directives)"を参照)。
$skip
-
The current terminal prefix (see "Directives" below).
現在の終端記号の前置子(下の"ディレクティブ (Directives)"を参照)。
$text
-
The remaining (unparsed) text. Changes to
$text
do not propagate out of unsuccessful productions, but do survive successful productions. Hence it is possible to dynamically alter the text being parsed - for example, to provide a#include
-like facility:残っている(パースされていない)テキスト。
$text
に対する変更は、マッチに 失敗したプロダクションの外にまで伝播しない。だがマッチに成功した プロダクションでは残る。よって、パースされたテキストをダイナミックに 変更することが可能である。例えば、#include
のような機能を提供できる:hash_include: '#include' filename { $text = ::loadfile($item[2]) . $text } filename: '<' /[a-z0-9._-]+/i '>' { $return = $item[2] } | '"' /[a-z0-9._-]+/i '"' { $return = $item[2] }
$thisline
と$prevline
-
$thisline
stores the current line number within the current parse (starting from 1).$prevline
stores the line number for the last character which was already successfully parsed (this will be different from$thisline
at the end of each line).$thisline
は現在パースしているテキストの行番号(1から始まる)を保持する。$prevline
は既にパースに成功した最新のキャラクタの行番号を保持する (これは各行の終端で$thisline
と異なるだろう)。For efficiency,
$thisline
and$prevline
are actually tied hashes, and only recompute the required line number when the variable's value is used.効率を良くするために、
$thisline
と$prevline
はハッシュにtieされていて、 その変数の値が使われたときにだけ行番号を再計算している。Assignment to
$thisline
adjusts the line number calculator, so that it believes that the current line number is the value being assigned. Note that this adjustment will be reflected in all subsequent line numbers calculations.$thisline
への代入は行番号計算機を調整する。そのため、それは現在の 行番号は代入された値であると信じる。この調整は全ての連続する行番号計算 で反映されることに注意すること。Modifying the value of the variable
$text
(as in the previoushash_include
example, for instance) will confuse the line counting mechanism. To prevent this, you should callParse::RecDescent::LineCounter::resync($thisline)
immediately after any assignment to the variable$text
(or, at least, before the next attempt to use$thisline
).変数
$text
の値を変更すると(例えば先のhash_include
の例のように)、 行番号計算のメカニズムが混乱する。これを防ぐには、変数$text
に何かを 代入した直後に(あるいは少なくとも次に$thisline
の利用を試みる 前に)Parse::RecDescent::LineCounter::resync($thisline)
を呼び出す 必要がある。Note that if a production fails after assigning to or resync'ing
$thisline
, the parser's line counter mechanism will usually be corrupted.もし
$thisline
に代入、あるいは再同期(resync)した後、プロダクションの マッチが失敗した場合、通常、パーサの行番号計算メカニズムは壊れてしまう ことに注意。Also see the entry for
@itempos
.@itempos
の記載事項も参照のこと。The line number can be set to values other than 1, by calling the start rule with a second argument. For example:
行番号は1以外の値をセットすることができる。これは第二引数を与えて 開始規則を呼び出すことによって行なう。例えば:
$parser = new Parse::RecDescent ($grammar); $parser->input($text, 10); # 開始の行番号は10
$thiscolumn
と$prevcolumn
-
$thiscolumn
stores the current column number within the current line being parsed (starting from 1).$prevcolumn
stores the column number of the last character which was actually successfully parsed. Usually$prevcolumn == $thiscolumn-1
, but not at the end of lines.$thiscolumn
は、パースされている現在の行(1から始まる)内における カラム数を保持する。$prevcolumn
は、実際にパースに成功した最新の キャラクタのカラム数を保持する。通常、$prevcolumn == $thiscolumn-1
であるが、行末では一致しない。For efficiency,
$thiscolumn
and$prevcolumn
are actually tied hashes, and only recompute the required column number when the variable's value is used.効率を良くするために、
$thiscolumn
と$prevcolumn
はハッシュに tieされていて、その変数の値が使われたときにだけカラム数を再計算する。Assignment to
$thiscolumn
or$prevcolumn
is a fatal error.$thiscolumn
、$prevcolumn
への代入は致命的エラーとなる。Modifying the value of the variable
$text
(as in the previoushash_include
example, for instance) may confuse the column counting mechanism.変数
$text
の値を変更すると(例えば先のhash_include
の例のように)、 カラム計算のメカニズムが混乱するかもしれない。Note that
$thiscolumn
reports the column number before any whitespace that might be skipped before reading a token. Hence if you wish to know where a token started (and ended) use something like this:$thiscolumn
は、トークンを読み込む前にスキップされた空白の前の カラム数を報告することに注意すること。よって、どこからトークンがスタート したか(そして終わったか)を知りたければ、次のようにする:rule: token1 token2 startcol token3 endcol token4 { print "token3: columns $item[3] to $item[5]"; } startcol: '' { $thiscolumn } # NEED THE '' TO STEP PAST TOKEN SEP endcol: { $prevcolumn }
Also see the entry for
@itempos
.@itempos
の記載事項も参照のこと。 $thisoffset
と$prevoffset
-
$thisoffset
stores the offset of the current parsing position within the complete text being parsed (starting from 0).$prevoffset
stores the offset of the last character which was actually successfully parsed. In all cases$prevoffset == $thisoffset-1
.$thisoffset
は、パース中の全テキスト(0から始まる)内における、 現在のパース位置のオフセットを保持する。$prevoffset
は、実際に パースに成功した最新のキャラクタのオフセットを保持する。どんな場合 でも$prevoffset == $thisoffset-1
になる。For efficiency,
$thisoffset
and$prevoffset
are actually tied hashes, and only recompute the required offset when the variable's value is used.効率を良くするため、
$thisoffset
と$prevoffset
はハッシュにtie されていて、その変数の値が使われたときにだけオフセット値を再計算する。Assignment to
$thisoffset
or <$prevoffset> is a fatal error.$thisoffset
、<$prevoffset>への代入は致命的エラーとなる。Modifying the value of the variable
$text
will not affect the offset counting mechanism.変数
$text
の値を変更しても、オフセット計算のメカニズムに影響は 与えないだろう。Also see the entry for
@itempos
.@itempos
の記載事項も参照のこと。 @itempos
-
The array
@itempos
stores a hash reference corresponding to each element of@item
. The elements of the hash provide the following:配列
@itempos
は、@item
の各要素に対応するハッシュリファレンスを 保持する。提供されるハッシュ要素は:$itempos[$n]{offset}{from} # VALUE OF $thisoffset BEFORE $item[$n] $itempos[$n]{offset}{to} # VALUE OF $prevoffset AFTER $item[$n] $itempos[$n]{line}{from} # VALUE OF $thisline BEFORE $item[$n] $itempos[$n]{line}{to} # VALUE OF $prevline AFTER $item[$n] $itempos[$n]{column}{from} # VALUE OF $thiscolumn BEFORE $item[$n] $itempos[$n]{column}{to} # VALUE OF $prevcolumn AFTER $item[$n]
Note that the various
$itempos[$n]...{from}
values record the appropriate value after any token prefix has been skipped.$itempos[$n]...{from}
は、トークン前置子がスキップされた後の 適正な値を記録していることに注意。Hence, instead of the somewhat tedious and error-prone:
それゆえ、次のような少々単調でエラーの生じやすい方法に対して:
rule: startcol token1 endcol startcol token2 endcol startcol token3 endcol { print "token1: columns $item[1] to $item[3] token2: columns $item[4] to $item[6] token3: columns $item[7] to $item[9]" } startcol: '' { $thiscolumn } # NEED THE '' TO STEP PAST TOKEN SEP endcol: { $prevcolumn }
it is possible to write:
次のような書き方が可能である:
rule: token1 token2 token3 { print "token1: columns $itempos[1]{column}{from} to $itempos[1]{column}{to} token2: columns $itempos[2]{column}{from} to $itempos[2]{column}{to} token3: columns $itempos[3]{column}{from} to $itempos[3]{column}{to}" }
Note however that (in the current implementation) the use of
@itempos
anywhere in a grammar implies that item positioning information is collected everywhere during the parse. Depending on the grammar and the size of the text to be parsed, this may be prohibitively expensive and the explicit use of$thisline
,$thiscolumn
, etc. may be a better choice.だが注意してもらいたいのは、(現在の実装では)文法のどこであろうと
@itempos
を使用すると、パース中のあらゆる場所でアイテムの ポジショニング情報が収集されるということだ。文法及びパースされるテキスト のサイズによっては、これはひどくコストが高くつくので、$thisline
や$thiscolumn
等を明示的に利用する方が良い選択肢の場合がある。 $thisparser
-
A reference to the
Parse::RecDescent
object through which parsing was initiated.Parse::RecDescent
オブジェクトへのリファレンス。パースの開始は このオブジェクトを通じてなされている。The value of
$thisparser
propagates down the subrules of a parse but not back up. Hence, you can invoke subrules from another parser for the scope of the current rule as follows:$thisparser
の値は、パース対象のサブ規則に伝わるが、バックアップ しない。よって、次のようにして現在の規則のスコープ用に別のパーサから サブ規則を呼び出すことができる:rule: subrule1 subrule2 | { $thisparser = $::otherparser } <reject> | subrule3 subrule4 | subrule5
The result is that the production calls "subrule1" and "subrule2" of the current parser, and the remaining productions call the named subrules from
$::otherparser
. Note, however that "Bad Things" will happen if::otherparser
isn't a blessed reference and/or doesn't have methods with the same names as the required subrules!結果は次のようになる。プロダクションが現在のパーサの"subrule1"と "subrule2"を呼び出し、残りのプロダクションは
$::otherparser
から 指定された名前のサブルールを呼び出す。しかし、::otherparser
が blessされたリファレンスでない、かつ/または、必要とされるサブ規則 と同じ名前のメソッドを持っていない場合、”良くないこと”が起きる ことに注意して欲しい。 $thisrule
-
A reference to the
Parse::RecDescent::Rule
object corresponding to the rule currently being matched.現在照合中の規則に対応した
Parse::RecDescent::Rule
オブジェクトへの リファレンス。 $thisprod
-
A reference to the
Parse::RecDescent::Production
object corresponding to the production currently being matched.現在照合中のプロダクションに対応した
Parse::RecDescent::Production
オブジェクトへのリファレンス。 $score
と$score_return
-
$score stores the best production score to date, as specified by an earlier
<score:...>
directive. $score_return stores the corresponding return value for the successful production.$scoreは、データに対する最も良いプロダクションスコアを保持する。 これはより早く
<score:...>
ディレクティブで指定された スコアである。$score_returnは、マッチに成功したプロダクションに 対応した戻り値を保持する。See "Scored productions".
Warning: the parser relies on the information in the various this...
objects in some non-obvious ways. Tinkering with the other members of these objects will probably cause Bad Things to happen, unless you really know what you're doing. The only exception to this advice is that the use of $this...->{local}
is always safe.
警告:パーサは、不明瞭ではるが、様々なthis...
オブジェクトの 情報に依存している。これらオブジェクトの他のメンバをへたに弄ると、 十中八九、良くないことが起こるだろう。自分のやっていることを本当に 理解していない限り。この忠告に対する唯一の例外は、 $this...->{local}
の利用が常に安全であるということだ。
開始アクション (Start-up Actions)¶
Any actions which appear before the first rule definition in a grammar are treated as "start-up" actions. Each such action is stripped of its outermost brackets and then evaluated (in the parser's special namespace) just before the rules of the grammar are first compiled.
文法内において最初の規則の定義の前に現れたアクションは、 "開始"アクションとして扱われる。このようなアクションは全て、 文法の規則が最初にコンパイルされる前に(パーサ固有の名前空間で) 外側のブラケットが取り除かれてから評価される。
The main use of start-up actions is to declare local variables within the parser's special namespace:
開始アクションの主要な使い道は、パーサの固有の名前空間内で ローカル変数を宣言することにある:
{ my $lastitem = '???'; }
list: item(s) { $return = $lastitem }
item: book { $lastitem = 'book'; }
bell { $lastitem = 'bell'; }
candle { $lastitem = 'candle'; }
but start-up actions can be used to execute any valid Perl code within a parser's special namespace.
しかし開始アクションは、パーサ固有の名前空間内で、どんな正しい Perlコードでも実行することが出来る。
Start-up actions can appear within a grammar extension or replacement (that is, a partial grammar installed via Parse::RecDescent::Extend()
or Parse::RecDescent::Replace()
- see "Incremental Parsing"), and will be executed before the new grammar is installed. Note, however, that a particular start-up action is only ever executed once.
開始アクションは、文法の拡張や置換(Parse::RecDescent::Extend()
や Parse::RecDescent::Replace()
を通じて一部の文法をインストールすること。 "増加解析 (Incremental Parsing)"を参照)内に出現することができる。そして、その 新しい文法がインストールされる前に実行される。しかし、注意して欲しいのは、 その開始アクションは一度だけ実行されるということだ。
オートアクション (Autoactions)¶
It is sometimes desirable to be able to specify a default action to be taken at the end of every production (for example, in order to easily build a parse tree). If the variable $::RD_AUTOACTION
is defined when Parse::RecDescent::new()
is called, the contents of that variable are treated as a specification of an action which is to appended to each production in the corresponding grammar. So, for example, to construct a simple parse tree:
プロダクションの終わり毎に、デフォルトで実行されるアクションを 指定したいと思うことがある(例えば、解析木を簡単に構築するためとか)。 Parse::RecDescent::new()
が呼び出される際、変数$::RD_AUTOACTION
が 定義されていると、その変数の内容は対応する文法の各プロダクションに 追加されるアクションの指定として扱われる。だから例えば、単純な構文木を 構築するために:
$::RD_AUTOACTION = q { [@item] };
parser = new Parse::RecDescent (q{
expression: and_expr '||' expression | and_expr
and_expr: not_expr '&&' and_expr | not_expr
not_expr: '!' brack_expr | brack_expr
brack_expr: '(' expression ')' | identifier
identifier: /[a-z]+/i
});
which is equivalent to:
これは次と等価だ:
parser = new Parse::RecDescent (q{
expression: and_expr '||' expression
{ [@item] }
| and_expr
{ [@item] }
and_expr: not_expr '&&' and_expr
{ [@item] }
| not_expr
{ [@item] }
not_expr: '!' brack_expr
{ [@item] }
| brack_expr
{ [@item] }
brack_expr: '(' expression ')'
{ [@item] }
| identifier
{ [@item] }
identifier: /[a-z]+/i
{ [@item] }
});
Alternatively, we could take an object-oriented approach, use different classes for each node (and also eliminating redundant intermediate nodes):
これとは別に、オブジェクト指向なアプローチとして、それぞれのノードに 対して様々なクラスを利用することができる(そしてまた、余計な中間ノード を選り分けを行うことも):
$::RD_AUTOACTION = q
{ $#item==1 ? $item[1] : new ${"$item[0]_node"} (@item[1..$#item]) };
parser = new Parse::RecDescent (q{
expression: and_expr '||' expression | and_expr
and_expr: not_expr '&&' and_expr | not_expr
not_expr: '!' brack_expr | brack_expr
brack_expr: '(' expression ')' | identifier
identifier: /[a-z]+/i
});
which is equivalent to:
これは次と等価だ:
parser = new Parse::RecDescent (q{
expression: and_expr '||' expression
{ new expression_node (@item[1..3]) }
| and_expr
and_expr: not_expr '&&' and_expr
{ new and_expr_node (@item[1..3]) }
| not_expr
not_expr: '!' brack_expr
{ new not_expr_node (@item[1..2]) }
| brack_expr
brack_expr: '(' expression ')'
{ new brack_expr_node (@item[1..3]) }
| identifier
identifier: /[a-z]+/i
{ new identifer_node (@item[1]) }
});
Note that, if a production already ends in an action, no autoaction is appended to it. For example, in this version:
注意として、既にプロダクションの終わりがアクションである場合、 オートアクションは追加されない。例えば:
$::RD_AUTOACTION = q
{ $#item==1 ? $item[1] : new ${"$item[0]_node"} (@item[1..$#item]) };
parser = new Parse::RecDescent (q{
expression: and_expr '&&' expression | and_expr
and_expr: not_expr '&&' and_expr | not_expr
not_expr: '!' brack_expr | brack_expr
brack_expr: '(' expression ')' | identifier
identifier: /[a-z]+/i
{ new terminal_node($item[1]) }
});
each identifier
match produces a terminal_node
object, not an identifier_node
object.
identifier
に対するマッチはそれぞれterminal_node
オブジェクトを 生成するのであって、identifier_node
オブジェクトではない。
A level 1 warning is issued each time an "autoaction" is added to some production.
アートアクションがプロダクションに追加されるときに、レベル1の警告が 発生する。
オートツリー (Autotrees)¶
A commonly needed autoaction is one that builds a parse-tree. It is moderately tricky to set up such an action (which must treat terminals differently from non-terminals), so Parse::RecDescent simplifies the process by providing the <autotree>
directive.
一般に、オートアクションが必要となるのは解析木を構築する時である。 そのようなアクション(終端記号と非終端記号を分けて扱わなければならない) を用意するには少々手の込んだ手法が入り用だ。そこで、Parse::RecDescentは、 <autotree>
ディレクティブによって、このプロセスを簡単にしている。
If this directive appears at the start of grammar, it causes Parse::RecDescent to insert autoactions at the end of any rule except those which already end in an action. The action inserted depends on whether the production is an intermediate rule (two or more items), or a terminal of the grammar (i.e. a single pattern or string item).
このディレクティブが文法の開始時に現れると、Parse::RecDescentは、既に アクションで終わるようになっているものを除いて、規則の最後に オートアクションを挿入する。挿入されるアクションは、そのプロダクションが 中間規則(2つ以上のアイテム)か、文法の終端記号(パターン、なしいは文字列 による単一アイテム)かによって変わる。
So, for example, the following grammar:
よって、例えば次のような文法では:
<autotree>
file : command(s)
command : get | set | vet
get : 'get' ident ';'
set : 'set' ident 'to' value ';'
vet : 'check' ident 'is' value ';'
ident : /\w+/
value : /\d+/
is equivalent to:
以下と等価になる:
file : command(s) { bless \%item, $item[0] }
command : get { bless \%item, $item[0] }
| set { bless \%item, $item[0] }
| vet { bless \%item, $item[0] }
get : 'get' ident ';' { bless \%item, $item[0] }
set : 'set' ident 'to' value ';' { bless \%item, $item[0] }
vet : 'check' ident 'is' value ';' { bless \%item, $item[0] }
ident : /\w+/ { bless {__VALUE__=>$item[1]}, $item[0] }
value : /\d+/ { bless {__VALUE__=>$item[1]}, $item[0] }
Note that each node in the tree is blessed into a class of the same name as the rule itself. This makes it easy to build object-oriented processors for the parse-trees that the grammar produces. Note too that the last two rules produce special objects with the single attribute '__VALUE__'. This is because they consist solely of a single terminal.
ツリー内の各ノードは、規則自身と同じ名前のクラスにblessされている ことに注意。これによって簡単に、文法が生み出した構文解析木用の オブジェクト指向なプロセッサが構築できる。また、後二つの規則は 一つの属性'__VALUE__'を持った特殊なオブジェクトを生成することにも 注意。これは、それらが単一の終端記号で構成されているためである。
This autoaction-ed grammar would then produce a parse tree in a data structure like this:
このオートアクションを指定された文法は、以下のようなデータ構造の 構文解析木を生成する:
{
file => {
command => {
[ get => {
identifier => { __VALUE__ => 'a' },
},
set => {
identifier => { __VALUE__ => 'b' },
value => { __VALUE__ => '7' },
},
vet => {
identifier => { __VALUE__ => 'b' },
value => { __VALUE__ => '7' },
},
],
},
}
}
(except, of course, that each nested hash would also be blessed into the appropriate class).
(もちろん、ネストされたハッシュがそれぞれ、適切なクラスにblessされても いることは除く。)
オートスタブ (Autostubbing)¶
Normally, if a subrule appears in some production, but no rule of that name is ever defined in the grammar, the production which refers to the non-existent subrule fails immediately. This typically occurs as a result of misspellings, and is a sufficiently common occurance that a warning is generated for such situations.
通常、あるプロダクション内でサブ規則が現れた場合(ただし、文法内で その名前が定義されているものを除く)、存在しないサブ規則を参照する プロダクションは直ちに照合に失敗する。これは典型的には、スペルミスで 発生する。そのような状況に対し警告が発生するのは、ごく一般的な ことである。
However, when prototyping a grammar it is sometimes useful to be able to use subrules before a proper specification of them is really possible. For example, a grammar might include a section like:
しかしながら、文法をプロトタイプする際、サブ規則を適切に宣言する前に 利用することができれば便利なことがある。そしてこれは実際可能である。 例えば、次のようなセクションを含んだ文法において:
function_call: identifier '(' arg(s?) ')'
identifier: /[a-z]\w*/i
where the possible format of an argument is sufficiently complex that it is not worth specifying in full until the general function call syntax has been debugged. In this situation it is convenient to leave the real rule arg
undefined and just slip in a placeholder (or "stub"):
このような場合、引数のとり得るフォーマットは非常に複雑なため、一般的な 関数コールシンタックスがデバッグされてしまわない限り、仕様を完全に 記述するのは無意味である。こういう状況では、本当の規則arg
は未定義の ままにしておいて、単にプレースホルダー(スタブ (stub) )に入れておく のが便利だ。
arg: 'arg'
so that the function call syntax can be tested with dummy input such as:
そうしておいて、ダミー入力を使って関数コールシンタックスをテストする:
f0()
f1(arg)
f2(arg arg)
f3(arg arg arg)
et cetera.
等々。
Early in prototyping, many such "stubs" may be required, so Parse::RecDescent
provides a means of automating their definition. If the variable $::RD_AUTOSTUB
is defined when a parser is built, a subrule reference to any non-existent rule (say, sr
), causes a "stub" rule of the form:
プロトタイプの初期では、このようなスタブが大量に必要となる。そこで Parse::RecDescent
は、それらの定義を自動化する手段を提供している。 パーサが構築される際、変数$::RD_AUTOSTUB
が定義されていれば、 存在しない規則(例としてsr
)を参照するサブ規則は、次のような 形式のスタブ規則を発生させる:
sr: 'sr'
to be automatically defined in the generated parser. A level 1 warning is issued for each such "autostubbed" rule.
これは生成されたパーサにおいて自動的に定義される。このような オートスタブされた規則ができる度に、レベル1警告が発生する。
Hence, with $::AUTOSTUB
defined, it is possible to only partially specify a grammar, and then "fake" matches of the unspecified (sub)rules by just typing in their name.
$::AUTOSTUB
を定義することによって、部分的に文法を定義することが 可能になり、名前をタイプするだけで未指定の(サブ)規則を擬似的に 照合することが可能になる。
先読み (Look-ahead)¶
If a subrule, token, or action is prefixed by "...", then it is treated as a "look-ahead" request. That means that the current production can (as usual) only succeed if the specified item is matched, but that the matching does not consume any of the text being parsed. This is very similar to the /(?=...)/
look-ahead construct in Perl patterns. Thus, the rule:
サブ規則、トークン、あるいはアクションの前に"..."が置かれた場合、これは "先読み要求"(look-ahead)として扱われる。どういうことかというと、現在の プロダクションは(通常)、指定されたアイテムがマッチした場合にのみ成功するが、 この照合の場合、パースされるテキストが消費されない。これはPerlの 正規表現パターンの先読み構造/(?=...)/
とよく似ている。よって、規則:
inner_word: word ...word
will match whatever the subrule "word" matches, provided that match is followed by some more text which subrule "word" would also match (although this second substring is not actually consumed by "inner_word")
は、サブ規則"word"にマッチするものは何でもマッチする。このとき、 このマッチに続いてサブ規則"word"にマッチするさらなるテキストが続く(ただし、 この二番目の文字列は実際には"inner_word"によって消費されない)。
Likewise, a "...!" prefix, causes the following item to succeed (without consuming any text) if and only if it would normally fail. Hence, a rule such as:
同様に、"...!" 前置子は、続くアイテムがマッチに失敗したときだけ、 (テキストの消費無しに)マッチに成功したことになる。それゆえ、 次のような規則:
identifier: ...!keyword ...!'_' /[A-Za-z_]\w*/
matches a string of characters which satisfies the pattern /[A-Za-z_]\w*/
, but only if the same sequence of characters would not match either subrule "keyword" or the literal token '_'.
は、正規表現パターン/[A-Za-z_]\w*/
を満たすキャラクタ文字列にマッチ する。しかしこれは、その同じキャラクタの連なりが、サブ規則"keyword"か リテラルトークン'_'のいずれかにマッチしない場合に限る。
Sequences of look-ahead prefixes accumulate, multiplying their positive and/or negative senses. Hence:
先読み前置子を繋げていくと、その肯定・否定の意味が掛け合わされていく。 よって:
inner_word: word ...!......!word
is exactly equivalent the the original example above (a warning is issued in cases like these, since they often indicate something left out, or misunderstood).
これは、先にあげた最初の例と正確に等しい(このようなケースでは警告が 発生する。というのも、これらはしばしば、[先読み前置子を]忘れていたり、 間違った理解をしている兆候といえるからである)。
Note that actions can also be treated as look-aheads. In such cases, the state of the parser text (in the local variable $text
) after the look-ahead action is guaranteed to be identical to its state before the action, regardless of how it's changed within the action (unless you actually undefine $text
, in which case you get the disaster you deserve :-).
アクションもまた、先読みとして扱われうることに注意。そのような場合、 先読みアクションの後ろのテキストの状態(ローカル変数$text
)は、 そのアクションの前の状態と全く同じであることが保証される。これは そのアクション内でいかに状態が変化しようとも関係ない($text
を 本当にundefしない限りは。って、そんなことをしたら、あなたに災いが 降りかかるだろうが(笑))。
ディレクティブ (Directives)¶
Directives are special pre-defined actions which may be used to alter the behaviour of the parser. There are currently eighteen directives:
ディレクティブ(directive)とは、パーサの振る舞いを変更するために利用する 予約された特別なアクションである。現在、18のディレクティブがある:
<commit>
, <uncommit>
, <reject>
, <score>
, <autoscore>
, <skip>
, <resync>
, <error>
, <rulevar>
, <matchrule>
, <leftop>
, <rightop>
, <defer>
, <nocheck>
, <perl_quotelike>
, <perl_codeblock>
, <perl_variable>
, そして <token>
.
- commit と uncommit (Committing and uncommitting)
-
The
<commit>
and<uncommit>
directives permit the recursive descent of the parse tree to be pruned (or "cut") for efficiency. Within a rule, a<commit>
directive instructs the rule to ignore subsequent productions if the current production fails. For example:<commit>
及び<uncommit>
ディレクティブは、解析木(parse tree) の再帰下降を効率よく刈り込める(切る)ようにする。規則内に対して<commit>
ディレクティブは、現在のプロダクションが失敗したさいに、続くプロダクションを 無視するように導く。例えば:command: 'find' <commit> filename | 'open' <commit> filename | 'move' filename filename
Clearly, if the leading token 'find' is matched in the first production but that production fails for some other reason, then the remaining productions cannot possibly match. The presence of the
<commit>
causes the "command" rule to fail immediately if an invalid "find" command is found, and likewise if an invalid "open" command is encountered.最初のプロダクションにおいて、先行するトークン'find'はマッチしたものの、その プロダクションが別の理由で照合に失敗した場合、明らかに残りのプロダクション もマッチしないだろう。
<commit>
があれば、"command"規則は、"find" コマンドが妥当でない時点で直ちに失敗となる。妥当でない"open"コマンドに 出くわした場合も同様である。It is also possible to revoke a previous commitment. For example:
先に指定したcommitを解除することも可能だ。例えば:
if_statement: 'if' <commit> condition 'then' block <uncommit> 'else' block | 'if' <commit> condition 'then' block
In this case, a failure to find an "else" block in the first production shouldn't preclude trying the second production, but a failure to find a "condition" certainly should.
このケースでは、最初のプロダクションにおいて"else"ブロックを発見でき なくても、二番目のプロダクションを試すことを排除しない。しかし、 "condition"を発見できない場合、二番目のプロダクションは試行されない。
As a special case, any production in which the first item is an
<uncommit>
immediately revokes a preceding<commit>
(even though the production would not otherwise have been tried). For example, in the rule:特殊なケースとして、最初のアイテムが
<uncommit>
である プロダクションは、先行する<commit>
を直ちに無効にする (そのプロダクションが照合を試みられないときでさえ)。 例えば、次の規則で:request: 'explain' expression | 'explain' <commit> keyword | 'save' | 'quit' | <uncommit> term '?'
if the text being matched was "explain?", and the first two productions failed, then the
<commit>
in production two would cause productions three and four to be skipped, but the leading<uncommit>
in the production five would allow that production to attempt a match.もし照合されるテキストが"explain?"で、最初の二つのプロダクションの 照合に失敗した場合、2番目のプロダクションの
<commit>
によって 3、4番目のプロダクションがスキップされる。しかし、5番目のプロダクションの <<uncommit>>によって、このプロダクションへの照合が試みられる。Note in the preceding example, that the
<commit>
was only placed in production two. If production one had been:先の例では、
<commit>
が2番目のプロダクションにだけ置いてある ことに注意。もし1番目のプロダクションが:request: 'explain' <commit> expression
then production two would be (inappropriately) skipped if a leading "explain..." was encountered.
の場合、先導する"explain..."に出くわしたら2番目のプロダクションは (不適切にも)スキップしてしまうだろう。
Both
<commit>
and<uncommit>
directives always succeed, and their value is always 1.<commit>
及び<uncommit>
ディレクティブは常に成功する。 そしてその値は常に1である。 - プロダクションのreject (Rejecting a production)
-
The
<reject>
directive immediately causes the current production to fail (it is exactly equivalent to, but more obvious than, the action{undef}
). A<reject>
is useful when it is desirable to get the side effects of the actions in one production, without prejudicing a match by some other production later in the rule. For example, to insert tracing code into the parse:<reject>
ディレクティブは、直ちに現在のプロダクションの照合を 失敗させる(これは{undef}
アクションと全く等価だが、より明瞭だ)。 規則の後の方に出てくるプロダクションによって照合が邪魔されることなく、 あるプロダクション内のアクションによって副次効果を得たいような時に、<reject>
は便利だ。例えば、トレースするコードをパーサに 追加するときなど:complex_rule: { print "In complex rule...\n"; } <reject> complex_rule: simple_rule '+' 'i' '*' simple_rule | 'i' '*' simple_rule | simple_rule
It is also possible to specify a conditional rejection, using the form
<reject:condition>
, which only rejects if the specified condition is true. This form of rejection is exactly equivalent to the action{(condition)?undef:1}>
. For example:また、
<reject:condition>
を使うことで、条件付きrejectを 設定することも可能だ。これは指定された条件が真の時にのみrejectされる。 このrejectの形式は、アクション{(condition)?undef:1}>
と 正確に等しい。例えば:command: save_command | restore_command | <reject: defined $::tolerant> { exit } | <error: Unknown command. Ignored.>
A
<reject>
directive never succeeds (and hence has no associated value). A conditional rejection may succeed (if its condition is not satisfied), in which case its value is 1.<reject>
ディレクティブが照合に成功することはない(それゆえ、 関連付けられる値もない)。条件付きrejectは(条件を満たさなければ) 成功する場合があり、このとき、関連付けられる値は1である。As an extra optimization,
Parse::RecDescent
ignores any production which begins with an unconditional<reject>
directive, since any such production can never successfully match or have any useful side-effects. A level 1 warning is issued in all such cases.最適化のために、
Parse::RecDescent
は、条件付きでない<reject>
ディレクティブで始まるプロダクションを無視する。なぜなら、そのような プロダクションは決してマッチしないし、何ら有益な副次効果をもたらさない からである。このようなケースでは全て、レベル1警告が発生する。Note that productions beginning with conditional
<reject:...>
directives are never "optimized away" in this manner, even if they are always guaranteed to fail (for example:<reject:1>
)例え常に照合に失敗することが保証されていても、条件付き
<reject:...>
ディレクティブで始まるプロダクションは、 この方法によっては"最適化"されないことに注意 (例えば<reject:1>
)。Due to the way grammars are parsed, there is a minor restriction on the condition of a conditional
<reject:...>
: it cannot contain any raw '<' or '>' characters. For example:パースされる文法の方法により、条件付き
<reject:...>
の 条件にはマイナーな制限がある:すなわち、生の'<'や''>'を含むことが できない。例えば:line: cmd <reject: $thiscolumn > max> data
results in an error when a parser is built from this grammar (since the grammar parser has no way of knowing whether the first > is a "less than" or the end of the
<reject:...>
.この文法からパーサが構築される際、エラーが起きる(なぜなら、 この文法に基づくパーサは、最初の > が"〜より小さい"なのか、
<reject:...>
の終端なのかを知る術がないからだ)。To overcome this problem, put the condition inside a do{} block:
この問題を乗り越えるには、 do{} ブロックの中に条件を置けばよい:
line: cmd <reject: do{$thiscolumn > max}> data
Note that the same problem may occur in other directives that take arguments. The same solution will work in all cases.
他の引数を取るディレクティブでも同じ問題が生じることに注意。 どのケースでも同様の解決策が使える。
- 終端記号間のスキップ (Skipping between terminals)
-
The
<skip>
directive enables the terminal prefix used in a production to be changed. For example:<skip>
ディレクティブによって、プロダクション内で使われている 終端記号前置子を変更することができる。例として:OneLiner: Command <skip:'[ \t]*'> Arg(s) /;/
causes only blanks and tabs to be skipped before terminals in the
Arg
subrule (and any of its subrules>, and also before the final/;/
terminal. Once the production is complete, the previous terminal prefix is reinstated. Note that this implies that distinct productions of a rule must reset their terminal prefixes individually.これは
Arg
サブ規則(と、そのサブ規則)の終端記号の前、及び、最後の/;/
終端記号の前で、空白とタブだけをスキップ可能にする。 ひとたびそのプロダクションの照合が完了すると、以前の終端記号前置子が 復帰する。つまり、一つの規則の異なるプロダクションで終端記号前置子が それぞれリセットされるということに注意して欲しい。The
<skip>
directive evaluates to the previous terminal prefix, so it's easy to reinstate a prefix later in a production:<skip>
ディレクティブは以前の終端記号前置子を評価する。 だから、プロダクションの後ろで前置子を復帰することが容易だ:Command: <skip:","> CSV(s) <skip:$item[1]> Modifier
The value specified after the colon is interpolated into a pattern, so all of the following are equivalent (though their efficiency increases down the list):
コロンの後に指定された値は、正規表現パターンに展開される。よって、次のものは 全て同じことになる:
<skip: "$colon|$comma"> # ASSUMING THE VARS HOLD THE OBVIOUS VALUES <skip: ':|,'> <skip: q{[:,]}> <skip: qr/[:,]/>
There is no way of directly setting the prefix for an entire rule, except as follows:
次のやり方以外に、規則全体に直接前置子を設定する方法はない:
Rule: <skip: '[ \t]*'> Prod1 | <skip: '[ \t]*'> Prod2a Prod2b | <skip: '[ \t]*'> Prod3
or, better:
よりうまいやり方:
Rule: <skip: '[ \t]*'> ( Prod1 | Prod2a Prod2b | Prod3 )
Note: Up to release 1.51 of Parse::RecDescent, an entirely different mechanism was used for specifying terminal prefixes. The current method is not backwards-compatible with that early approach. The current approach is stable and will not to change again.
注意:Parse::RecDescentのリリース1.51以降、終端記号前置子の指定には 全く違うメカニズムが使用されている。現在の方法には、初期アプローチとの 後方互換はない。現在のアプローチ方法は安定しているので、再び変更する ことはないだろう。
- 再同期 (Resynchronization)
-
The
<resync>
directive provides a visually distinctive means of consuming some of the text being parsed, usually to skip an erroneous input. In its simplest form<resync>
simply consumes text up to and including the next newline ("\n"
) character, succeeding only if the newline is found, in which case it causes its surrounding rule to return zero on success.<resync>
ディレクティブは、エラーの発生するような入力を スキップすることで、パース中のテキストを消費する視覚的に際だった手段を 提供する。最も単純な形式では、<resync>
は単にテキストを 次の改行コード("\n"
)まで含めて消費する。改行が見つかった場合に のみマッチは成功し、取り囲んでいる規則は0を返す。In other words, a
<resync>
is exactly equivalent to the token/[^\n]*\n/
followed by the action{ $return = 0 }
(except that productions beginning with a<resync>
are ignored when generating error messages). A typical use might be:つまり、
<resync>
は、アクション{ $return = 0 }
が後ろに続く トークン/[^\n]*\n/
と正確に等しい(例外として、<resync>
で 始まるプロダクションは、エラーメッセージが発生したとき無視される)。 典型的な使用例は:script : command(s) command: save_command | restore_command | <resync> # 可能なら次の行を試す
It is also possible to explicitly specify a resynchronization pattern, using the
<resync:pattern>
variant. This version succeeds only if the specified pattern matches (and consumes) the parsed text. In other words,<resync:pattern>
is exactly equivalent to the token/pattern/
(followed by a{ $return = 0 }
action). For example, if commands were terminated by newlines or semi-colons:また、再同期のための正規表現パターンを明示的に指定することも可能である。 これは
<resync:pattern>
というふうに変形して使う。この方法 では、設定したパターンがパースされているテキストにマッチ(そして消費) した場合にのみ成功となる。つまり、<resync:pattern>
は、 トークン/pattern/
(後ろにアクション{ $return = 0 }
が続く)と 正確に等しい。例として改行かセミコロンで終了するコマンドがあったとして:command: save_command | restore_command | <resync:[^;\n]*[;\n]>
The value of a successfully matched
<resync>
directive (of either type) is the text that it consumed. Note, however, that since the directive also sets$return
, a production consisting of a lone<resync>
succeeds but returns the value zero (which a calling rule may find useful to distinguish between "true" matches and "tolerant" matches). Remember that returning a zero value indicates that the rule succeeded (since only anundef
denotes failure withinParse::RecDescent
parsers.マッチに成功した
<resync>
ディレクティブの値は、それが消費した テキストとなる。しかし、このディレクティブは$return
をセットするので、 単独の<resync>
を構成するプロダクションは成功しても、値0を 返すことに注意(呼び出し側の規則にとっては"true"マッチと"tolerant"マッチ を区別するのに便利だ)。0を返した時、その規則は成功したのだという ことを覚えておいて欲しい(Parse::RecDescent
パーサにとって、失敗を 意味するのは、undef
だけだから)。 - エラー処理 (Error handling)
-
The
<error>
directive provides automatic or user-defined generation of error messages during a parse. In its simplest form<error>
prepares an error message based on the mismatch between the last item expected and the text which cause it to fail. For example, given the rule:<error>
ディレクティブは、パース中のエラーメッセージを 自動ないしはユーザー定義で提供する。最も単純な形式において、<error>
は、予想された最終アイテムと、失敗を引き起こす テキストとの間の不整合に基づいてエラーメッセージを用意する。 例えば、次のような規則が与えられていて:McCoy: curse ',' name ', I'm a doctor, not a' a_profession '!' | pronoun 'dead,' name '!' | <error>
the following strings would produce the following messages:
以下の文字列は、次のようメッセージを発生させる:
- "Amen, Jim!"
-
ERROR (line 1): Invalid McCoy: Expected curse or pronoun not found
- "Dammit, Jim, I'm a doctor!"
-
ERROR (line 1): Invalid McCoy: Expected ", I'm a doctor, not a" but found ", I'm a doctor!" instead
- "He's dead,\n"
-
ERROR (line 2): Invalid McCoy: Expected name not found
- "He's alive!"
-
ERROR (line 1): Invalid McCoy: Expected 'dead,' but found "alive!" instead
- "Dammit, Jim, I'm a doctor, not a pointy-eared Vulcan!"
-
ERROR (line 1): Invalid McCoy: Expected a profession but found "pointy-eared Vulcan!" instead
Note that, when autogenerating error messages, all underscores in any rule name used in a message are replaced by single spaces (for example "a_production" becomes "a production"). Judicious choice of rule names can therefore considerably improve the readability of automatic error messages (as well as the maintainability of the original grammar).
注意して欲しいのだが、エラーメッセージが自動生成されると、メッセージ 内に登場する規則名のアンダーバーは全て一つのスペースに置き換えら れる(例えば"a_production"は"a production"という具合に)。それゆえ、 規則名に対して賢明な選択をすれば、自動エラーメッセージの可読性が 向上するだろう(同時に元の文法の維持管理もしやすくなる)。
If the automatically generated error is not sufficient, it is possible to provide an explicit message as part of the error directive. For example:
自動エラー生成で不十分なら、エラーディレクティブの一部として明示的に メッセージを与えることが可能だ。例えば:
Spock: "Fascinating ',' (name | 'Captain') '.' | "Highly illogical, doctor." | <error: He never said that!>
which would result in all failures to parse a "Spock" subrule printing the following message:
この規則は、"Spock"のサブ規則に対するパースに失敗した全ての場合において 次のメッセージをプリントする:
ERROR (line <N>): Invalid Spock: He never said that!
The error message is treated as a "qq{...}" string and interpolated when the error is generated (not when the directive is specified!). Hence:
エラーメッセージは"qq{...}"文字列として扱われ、エラーが発生したときに 展開される(ディレクティブを指定したときではない!)。よって:
<error: Mystical error near "$text">
would correctly insert the ambient text string which caused the error.
これはエラーを引き起こした周囲のテキスト文字列が正しく挿入される。
There are two other forms of error directive:
<error?>
and<error?: msg>
. These behave just like<error>
and<error: msg>
respectively, except that they are only triggered if the rule is "committed" at the time they are encountered. For example:エラーディレクティブにはあと二つの違った形式がある:
<error?>
と<error?: msg>
だ。これらはそれぞれ<error>
、<error: msg>
と全く同じように振舞う。ただし、この ディレクティブに出くわしたときに、規則が"commit"されている場合にのみ エラーが引き起こされるという点が違う。例えば:Scotty: "Ya kenna change the Laws of Phusics," <commit> name | name <commit> ',' 'she's goanta blaw!' | <error?>
will only generate an error for a string beginning with "Ya kenna change the Laws o' Phusics," or a valid name, but which still fails to match the corresponding production. That is,
$parser->Scotty("Aye, Cap'ain")
will fail silently (since neither production will "commit" the rule on that input), whereas$parser->Scotty("Mr Spock, ah jest kenna do'ut!")
will fail with the error message:これは、"Ya kenna change the Laws o' Phusics,"で始まる文字列、あるいは 妥当な名前だが対応するプロダクションのマッチにまだ失敗するようなものに 対してのみエラーを発生させる。つまり、
$parser->Scotty("Aye, Cap'ain")
は黙って失敗する(なぜならどちらのプロダクションも入力に対する規則にcommit しないから)。一方、$parser->Scotty("Mr Spock, ah jest kenna do'ut!")
はエラーメッセージを出して失敗する:ERROR (line 1): Invalid Scotty: expected 'she's goanta blaw!' but found 'I jest kenna do'ut!' instead.
since in that case the second production would commit after matching the leading name.
このようになるのは、先行する名前にマッチした後に、2番目のプロダクションが commitするからである。
Note that to allow this behaviour, all
<error>
directives which are the first item in a production automatically uncommit the rule just long enough to allow their production to be attempted (that is, when their production fails, the commitment is reinstated so that subsequent productions are skipped).この振る舞いを可能にするために、プロダクションの最初のアイテムとなって いる
<error>
ディレクティブは全て、そのプロダクションが試みら れるのに十分な長さの規則を自動でuncommitする(つまり、プロダクションの 照合が失敗したとき、commitが復帰して続くプロダクションがスキップされる)。In order to permanently uncommit the rule before an error message, it is necessary to put an explicit
<uncommit>
before the<error>
. For example:エラーメッセージの前の規則を永続的にuncommitするために、
<error>
の前に明示的に<uncommit>
を置く必要が ある。例えば:line: 'Kirk:' <commit> Kirk | 'Spock:' <commit> Spock | 'McCoy:' <commit> McCoy | <uncommit> <error?> <reject> | <resync>
Error messages generated by the various
<error...>
directives are not displayed immediately. Instead, they are "queued" in a buffer and are only displayed once parsing ultimately fails. Moreover,<error...>
directives that cause one production of a rule to fail are automatically removed from the message queue if another production subsequently causes the entire rule to succeed. This means that you can put<error...>
directives wherever useful diagnosis can be done, and only those associated with actual parser failure will ever be displayed. Also see "Gotchas".<error...>
ディレクティブによって吐き出される種々のエラー メッセージは、直ちに表示されるわけではない。一度バッファに"溜め"られて から、パース処理が最終的に失敗したときにだけ表示される。加えて、ある規則 のプロダクションを失敗させる<error...>
ディレクティブは、 別のプロダクションが結果的に規則全体のマッチを成功させた場合には自動的に メッセージから取り除かれる。この意味するところは、有益な診断が可能な ところならどこでも<error...>
ディレクティブをおくことができる ということと、実際にパーサを失敗させた事柄と関連するメッセージだけが 表示されるということだ。"おや? (Gotchas)"も参照のこと。As a general rule, the most useful diagnostics are usually generated either at the very lowest level within the grammar, or at the very highest. A good rule of thumb is to identify those subrules which consist mainly (or entirely) of terminals, and then put an
<error...>
directive at the end of any other rule which calls one or more of those subrules.一般的にいって、最も有益な診断メッセージは、いつも文法内の 最も低レベルか最も高レベルのどちらかで生成されるものだ。 善き経験則とは、主として(あるいは完全に)終端記号で構成された サブ規則を同定し、その上で、一つ以上のサブ規則を呼び出す他の規則の 終わりに
<error...>
ディレクティブを置くことである。There is one other situation in which the output of the various types of error directive is suppressed; namely, when the rule containing them is being parsed as part of a "look-ahead" (see "Look-ahead"). In this case, the error directive will still cause the rule to fail, but will do so silently.
エラーディレクティブの出力を抑制するもう一つの状況がある;すなわち、 ディレクティブを含む規則が"先読み"の一部としてパーサされる場合である( "先読み (Look-ahead)"を参照)。このようなケースでは、エラーディレクティブが その規則を失敗させるとしても、それはこっそりとなされるだろう。
An unconditional
<error>
directive always fails (and hence has no associated value). This means that encountering such a directive always causes the production containing it to fail. Hence an<error>
directive will inevitably be the last (useful) item of a rule (a level 3 warning is issued if a production contains items after an unconditional<error>
directive).条件のない
<error>
ディレクティブは常に照合に失敗する(それゆえ 関連付けられる値もない)。この意味するところは、そのようなディレクティブに 出会うと、それらを含むプロダクションは常に照合に失敗するということだ。よって、<error>
ディレクティブは必然的に規則の最後の(有益な)アイテムに なる(プロダクションが無条件<error>
ディレクティブの後に アイテムを含んでいると、レベル3警告が発生する)。An
<error?>
directive will succeed (that is: fail to fail :-), if the current rule is uncommitted when the directive is encountered. In that case the directive's associated value is zero. Hence, this type of error directive can be used before the end of a production. For example:もし現在の規則が、
<error?>
ディレクティブに出くわしたときに uncommitされていたら、<error?>
は成功する(つまり、失敗の 失敗というわけ(笑))。このときディレクティブに関連付けられる値は 0となる。それゆえ、このタイプのエラーディレクティブは、プロダクション 終端の前に使うことができる。例えば:command: 'do' <commit> something | 'report' <commit> something | <error?: Syntax error> <error: Unknown command>
Warning: The
<error?>
directive does not mean "always fail (but do so silently unless committed)". It actually means "only fail (and report) if committed, otherwise succeed". To achieve the "fail silently if uncommitted" semantics, it is necessary to use:警告:
<error?>
ディレクティブは"常に失敗する(ただしcommitされない 場合は暗黙のうちに)"ということを意味するわけではない。実際のところ、それは "commitされたときだけ失敗(そして報告)、そうでなければ成功"を意味している。 "uncommitされたらこっそり失敗"を実現するには、次のようにする必要がある:rule: item <commit> item(s) | <error?> <reject> # COMMITされない場合、黙って失敗
However, because people seem to expect a lone
<error?>
directive to work like this:だが、
<error?>
ディレクティブ単独で以下のようなことを望む 人々のために:rule: item <commit> item(s) | <error?: Error message if committed> | <error: Error message if uncommitted>
Parse::RecDescent automatically appends a
<reject>
directive if the<error?>
directive is the only item in a production. A level 2 warning (see below) is issued when this happens.Parse::RecDescentは、プロダクションの唯一のアイテムが
<error?>
のとき、自動的に<reject>
ディレクティブを追加する。このような 場合、レベル2警告が発生する(下を見よ)。The level of error reporting during both parser construction and parsing is controlled by the presence or absence of four global variables:
$::RD_ERRORS
,$::RD_WARN
,$::RD_HINT
, and <$::RD_TRACE>. If$::RD_ERRORS
is defined (and, by default, it is) then fatal errors are reported.パーサ構築中、およびパース中のエラーレポートのレベルは、四つの グローバル変数の有無によってコントロールされる:
$::RD_ERRORS
、$::RD_WARN
、$::RD_HINT
、そして<$::RD_TRACE>。$::RD_ERRORS
が定義されている場合(デフォルト)、致命的エラーは 報告される。Whenever
$::RD_WARN
is defined, certain non-fatal problems are also reported. Warnings have an associated "level": 1, 2, or 3. The higher the level, the more serious the warning. The value of the corresponding global variable ($::RD_WARN
) determines the lowest level of warning to be displayed. Hence, to see all warnings, set$::RD_WARN
to 1. To see only the most serious warnings set$::RD_WARN
to 3. By default$::RD_WARN
is initialized to 3, ensuring that serious but non-fatal errors are automatically reported.$::RD_WARN
が定義されていれば常に、致命的でない問題もある程度報告される。 警告は関連付けられたレベル(1、2、3)を持つ。より高いレベルに応じて より深刻な警告となる。グローバル変数($::RD_WARN
)の対応する値によって、 表示される最低限の警告レベルが決まる。つまり、全警告をみるなら$::RD_WARN
に1をセットし、最も深刻な警告だけみるなら、3をセットする。 デフォルトでは、$::RD_WARN
は3で初期化されている。これにより、深刻だが 致命的ではないエラーが自動的に報告される。See "DIAGNOSTICS" for a list of the varous error and warning messages that Parse::RecDescent generates when these two variables are defined.
これら二つの変数が定義されたときの、Parse::RecDescentが生成する 種々のエラー及び警告メッセージのリストについては"DIAGNOSTICS"参照。
Defining any of the remaining variables (which are not defined by default) further increases the amount of information reported. Defining
$::RD_HINT
causes the parser generator to offer more detailed analyses and hints on both errors and warnings. Note that setting$::RD_HINT
at any point automagically sets$::RD_WARN
to 1.残りの変数を定義すると(デフォルトでは定義されていない)、 レポートされる情報量がさらに増える。
$::RD_HINT
を定義すれば、 パーサジェネレータは、より詳細な分析とエラー・警告に対するヒントを 与えてくれる。注意して欲しいのだが、どの時点で$::RD_HINT
を設定 しても、自動的に$::RD_WARN
に1がセットされる。Defining
$::RD_TRACE
causes the parser generator and the parser to report their progress to STDERR in excruciating detail (although, without hints unless $::RD_HINT is separately defined). This detail can be moderated in only one respect: if$::RD_TRACE
has an integer value (N) greater than 1, only the N characters of the "current parsing context" (that is, where in the input string we are at any point in the parse) is reported at any time. >$::RD_TRACE
を定義すると、パーサジェネレータとパーサは、 進捗具合を嫌と言うほど詳細にSTDERRにレポートする(ただし、別個に $::RD_HINTを定義していなければヒントは報告されない)。ただ一点、 この詳細な報告を緩めることができる:$::RD_TRACE
に1以上の整数 (N)を与えると、どんなときでも"現在パース中のテキスト"(つまり パーサ内のどの時点においても、入力文字列内で)におけるN個の キャラクタだけが報告される。$::RD_TRACE
is mainly useful for debugging a grammar that isn't behaving as you expected it to. To this end, if$::RD_TRACE
is defined when a parser is built, any actual parser code which is generated is also written to a file named "RD_TRACE" in the local directory.$::RD_TRACE
は主として、文法が予想通りに振る舞わないときの デバッグとして役に立つ。そのため、パーサが構築されている際に$::RD_TRACE
を定義すると、生成される実際のパーサコードは、 ローカルディレクトリの"RD_TRACE"という名前のファイルにも 書き出される。Note that the four variables belong to the "main" package, which makes them easier to refer to in the code controlling the parser, and also makes it easy to turn them into command line flags ("-RD_ERRORS", "-RD_WARN", "-RD_HINT", "-RD_TRACE") under perl -s.
これら四つの変数は"main"パッケージに所属していることに注目して欲しい。 これにより、パーサを制御するコードを参照しやすくなるし、 変数をコマンドラインのフラグに利用することも簡単になっている (perl -s下で"-RD_ERRORS"、"-RD_WARN"、"-RD_HINT"、"-RD_TRACE")。
- 局所変数の指定 (Specifying local variables)
-
It is occasionally convenient to specify variables which are local to a single rule. This may be achieved by including a
<rulevar:...>
directive anywhere in the rule. For example:一つの規則に対して局所化された変数を指定できたら便利な場合がある。 これは規則の任意の場所に
<rulevar:...>
ディレクティブを 含めることで達成できる。例えば:markup: <rulevar: $tag> markup: tag {($tag=$item[1]) =~ s/^<|>$//g} body[$tag]
The example
<rulevar: $tag>
directive causes a "my" variable named$tag
to be declared at the start of the subroutine implementing themarkup
rule (that is, before the first production, regardless of where in the rule it is specified).<rulevar: $tag>
ディレクティブは、markup
規則を実装する サブルーチンのはじめの部分で、$tag
という名前の"my"変数を宣言する (つまり、規則内のどこでディレクティブが指定されるかに関わらず、最初の プロダクションの前で宣言される)。Specifically, any directive of the form:
<rulevar:text>
causes a line of the formmy text;
to be added at the beginning of the rule subroutine, immediately after the definitions of the following local variables:とりわけ、次の形式のディレクティブ:
<rulevar:text>
は、下記ローカル変数が定義された直後、 規則サブルーチンの最初にmy text;
という形式の一行を付け加える。$thisparser $commit $thisrule @item $thisline @arg $text %arg
This means that the following
<rulevar>
directives work as expected:これは、次の
<rulevar>
ディレクティブが期待通りに動作する ことを意味する:<rulevar: $count = 0 > <rulevar: $firstarg = $arg[0] || '' > <rulevar: $myItems = \@item > <rulevar: @context = ( $thisline, $text, @arg ) > <rulevar: ($name,$age) = $arg{"name","age"} >
If a variable that is also visible to subrules is required, it needs to be
local
'd, notmy
'd.rulevar
defaults tomy
, but iflocal
is explicitly specified:サブ規則からも見える変数が必要なら、その変数を
my
するのではなく、local
しなければならない。rulevar
のデフォルトはmy
である。 しかしlocal
を明示的に指定することはできる:<rulevar: local $count = 0 >
then a
local
-ized variable is declared instead, and will be available within subrules.こうすれば代わりに
local
変数が宣言され、サブ規則内からも利用できる。Note however that, because all such variables are "my" variables, their values do not persist between match attempts on a given rule. To preserve values between match attempts, values can be stored within the "local" member of the
$thisrule
object:このような変数は全て"my"変数なので、与えられた規則に対し照合が試み られている間、その値が永続的に存在するわけではないことに注意。 照合の間値を維持するために、
$thisrule
オブジェクトの"local" メンバに値を保持しておくことができる:countedrule: { $thisrule->{"local"}{"count"}++ } <reject> | subrule1 | subrule2 | <reject: $thisrule->{"local"}{"count"} == 1> subrule3
When matching a rule, each
<rulevar>
directive is matched as if it were an unconditional<reject>
directive (that is, it causes any production in which it appears to immediately fail to match). For this reason (and to improve readability) it is usual to specify any<rulevar>
directive in a separate production at the start of the rule (this has the added advantage that it enablesParse::RecDescent
to optimize away such productions, just as it does for the<reject>
directive).規則を照合する際、
<rulevar>
ディレクティブは、無条件<reject>
であるかのようにマッチする(つまり、それが現れた プロダクションは直ちに照合に失敗する)。この理由から(そして可読性向上 のために)、規則のはじめに個別のプロダクションで<rulevar>
ディレクティブを設定するのは有益である(さらにParse::RecDescent
が<reject>
ディレクティブに対してそうするのと同様に、 このようなプロダクションを最適化するという効果ももたらす)。 - 動的にマッチされた規則 (Dynamically matched rules)
-
Because regexes and double-quoted strings are interpolated, it is relatively easy to specify productions with "context sensitive" tokens. For example:
正規表現とダブルクォート文字列は展開されるので、"文脈感度の高い" トークンを持ったプロダクションを指定するのが比較的容易である。例えば:
command: keyword body "end $item[1]"
which ensures that a command block is bounded by a "<keyword>...end <same keyword>" pair.
commandブロックは"<keyword>...end <same keyword>" というペアに結び付けられることが確かなものとなる。
Building productions in which subrules are context sensitive is also possible, via the
<matchrule:...>
directive. This directive behaves identically to a subrule item, except that the rule which is invoked to match it is determined by the string specified after the colon. For example, we could rewrite thecommand
rule like this:文脈感度の高いサブ規則を含んだプロダクションを構築することも可能だ。 これは
<matchrule:...>
ディレクティブを通じてなされる。この ディレクティブは、照合を行なう規則がコロンの後に指定された文字列によって 決定される場合を除いて、サブ規則アイテムと全く同様に振舞う。例えば、 先のcommand
規則を次のよう書きなおすことができる:command: keyword <matchrule:body> "end $item[1]"
Whatever appears after the colon in the directive is treated as an interpolated string (that is, as if it appeared in
qq{...}
operator) and the value of that interpolated string is the name of the subrule to be matched.ディレクティブ中のコロンの後に現れたものは何であれ、展開された文字列( つまり
qq{...}
演算子内で現れたもの)として扱われる。そしてその展開された 文字列の値は、マッチされるべきサブ規則の名前である。Of course, just putting a constant string like
body
in a<matchrule:...>
directive is of little interest or benefit. The power of directive is seen when we use a string that interpolates to something interesting. For example:もちろん、単に
body
のような定数文字列を<matchrule:...>
ディレクティブ内に置いても、ほとんど面白くも無いし、益のないことだ。 このディレクティブの真価は、面白いものに展開されるような文字列を使った ときにこそ発揮される。例えば:command: keyword <matchrule:$item[1]_body> "end $item[1]" keyword: 'while' | 'if' | 'function' while_body: condition block if_body: condition block ('else' block)(?) function_body: arglist block
Now the
command
rule selects how to proceed on the basis of the keyword that is found. It is as ifcommand
were declared:今や
command
規則は、見つかったkeywordに基づいて、どのように先に進むかを 選ぶことになる。command
は以下のように宣言されたかのようにみえる:command: 'while' while_body "end while" | 'if' if_body "end if" | 'function' function_body "end function"
When a
<matchrule:...>
directive is used as a repeated subrule, the rule name expression is "late-bound". That is, the name of the rule to be called is re-evaluated each time a match attempt is made. Hence, the following grammar:<matchrule:...>
ディレクティブが、繰り返しサブ規則で使われた 場合、その規則名は"遅延バインディング"(late-bound)になる。つまり、 呼び出される規則の名前は、照合がなされる度に再評価される。 よって、以下の文法は:{ $::species = 'dogs' } pair: 'two' <matchrule:$::species>(s) dogs: /dogs/ { $::species = 'cats' } cats: /cats/
will match the string "two dogs cats cats" completely, whereas it will only match the string "two dogs dogs dogs" up to the eighth letter. If the rule name were "early bound" (that is, evaluated only the first time the directive is encountered in a production), the reverse behaviour would be expected.
"two dogs cats cats"には完全にマッチする。一方、"two dogs dogs dogs"は 8文字目までしかマッチしない。もし、規則名が"事前バインディング" (early bound)だったなら(すなわちプロダクション内で遭遇したディレクティブ が最初の一回目だけ評価されるなら)、逆の振る舞いが予想されただろう。
Note that the
matchrule
directive takes a string that is to be treated as a rule name, not as a rule invocation. That is, it's like a Perl symbolic reference, not aneval
. Just as you can say:matchrule
ディレクティブは文字列を規則名として取り扱うのであって、 規則の呼び出しをするのではない。つまり、Perlのシンボリックリファレンス のようなものであって、eval
ではないとうことだ。下のようにできる:$subname = 'foo'; # and later... &{$foo}(@args);
but not:
だが、こうではない:
$subname = 'foo(@args)'; # and later... &{$foo};
likewise you can say:
同様にして、このようにできる:
$rulename = 'foo'; # and in the grammar... <matchrule:$rulename>[@args]
but not:
だが、これはできない:
$rulename = 'foo[@args]'; # and in the grammar... <matchrule:$rulename>
- 遅延アクション (Deferred actions)
-
The
<defer:...>
directive is used to specify an action to be performed when (and only if!) the current production ultimately succeeds.<defer:...>
ディレクティブは、現在のプロダクションが究極的に 成功したとき(そして、その場合にのみ!)実行されるアクションを指定する のに利用される。Whenever a
<defer:...>
directive appears, the code it specifies is converted to a closure (an anonymous subroutine reference) which is queued within the active parser object. Note that, because the deferred code is converted to a closure, the values of any "local" variable (such as$text
, <@item>, etc.) are preserved until the deferred code is actually executed.<defer:...>
ディレクティブが現れたときはいつでも、そこで指定 されているコードはクロージャ(無名サブルーチンのリファレンス)に変換 される。このクロージャは実行中のパーサオブジェクトのキューに溜められる。 注意して欲しいのは、この遅延コードはクロージャなので、どの"局所"変数 ($text
、@item
等々)も、コードが実際に実行されるまで保存されている ということだ。If the parse ultimately succeeds and the production in which the
<defer:...>
directive was evaluated formed part of the successful parse, then the deferred code is executed immediately before the parse returns. If however the production which queued a deferred action fails, or one of the higher-level rules which called that production fails, then the deferred action is removed from the queue, and hence is never executed.パーサが最終的に成功し、かつ、
<defer:...>
ディレクティブが 評価されたプロダクションが成功したパーサの一部を形成しているなら、 遅延コードはパーサが戻る直前に実行される。しかし、遅延アクションをキュー に溜めているプロダクションが失敗した場合、あるいは、そのプロダクションを 呼び出すより高レベルの規則の一つが失敗した場合、遅延アクションはキュー から取り除かれる。よって、実行されることもない。For example, given the grammar:
例えば、次のような文法があるとして:
sentence: noun trans noun | noun intrans noun: 'the dog' { print "$item[1]\t(noun)\n" } | 'the meat' { print "$item[1]\t(noun)\n" } trans: 'ate' { print "$item[1]\t(transitive)\n" } intrans: 'ate' { print "$item[1]\t(intransitive)\n" } | 'barked' { print "$item[1]\t(intransitive)\n" }
then parsing the sentence
"the dog ate"
would produce the output:その後、
"the dog ate"
という文をパースすると、吐き出される出力は:the dog (noun) ate (transitive) the dog (noun) ate (intransitive)
This is because, even though the first production of
sentence
ultimately fails, its initial subrulesnoun
andtrans
do match, and hence they execute their associated actions. Then the second production ofsentence
succeeds, causing the actions of the subrulesnoun
andintrans
to be executed as well.どうしてこうなるかというと、、
sentence
の最初のプロダクションが最終的に 失敗してさえも、冒頭のサブ規則noun
とtrans
がマッチし、関連づけられた アクションが実行される。それからsentence
の二番目のプロダクションが成功 するのでサブ規則noun
とintrans
のアクションも実行されるからである。On the other hand, if the actions were replaced by
<defer:...>
directives:他方、アクションを
<defer:...>
ディレクティブに置き換えたなら:sentence: noun trans noun | noun intrans noun: 'the dog' <defer: print "$item[1]\t(noun)\n" > | 'the meat' <defer: print "$item[1]\t(noun)\n" > trans: 'ate' <defer: print "$item[1]\t(transitive)\n" > intrans: 'ate' <defer: print "$item[1]\t(intransitive)\n" > | 'barked' <defer: print "$item[1]\t(intransitive)\n" >
the output would be:
出力は:
the dog (noun) ate (intransitive)
since deferred actions are only executed if they were evaluated in a production which ultimately contributes to the successful parse.
これは、遅延アクションが、パースを最終的に成功させるにいたった プロダクション内で評価された場合にのみ実行されるからである。
In this case, even though the first production of
sentence
caused the subrulesnoun
andtrans
to match, that production ultimately failed and so the deferred actions queued by those subrules were subsequently disgarded. The second production then succeeded, causing the entire parse to succeed, and so the deferred actions queued by the (second) match of thenoun
subrule and the subsequent match ofintrans
are preserved and eventually executed.この場合、
sentence
の最初のプロダクションがサブ規則noun
とtrans
とを マッチさせているにも関わらず、最終的にはプロダクションは失敗する。結果、 サブ規則のキューに溜められている遅延アクションは破棄される。二番目の プロダクションは成功するので、パース全体は成功になる。だから(二番目に) マッチしたnoun
サブ規則と次にマッチしたintrans
のキューに溜まっていた 遅延アクションは保持され、最後に実行される。Deferred actions provide a means of improving the performance of a parser, by only executing those actions which are part of the final parse-tree for the input data.
遅延アクションは、入力データに対する最終的な解析木の一部であるアクション だけを実行することによって、パーサのパフォーマンスを向上させる手段を 提供している。
Alternatively, deferred actions can be viewed as a mechanism for building (and executing) a customized subroutine corresponding to the given input data, much in the same way that autoactions (see "Autoactions") can be used to build a customized data structure for specific input.
他にも、遅延アクションは、与えられた入力データに対応してカスタマイズ されたサブルーチンを構築(及び実行)するメカニズムとして見ることが できる。これは、オートアクション("オートアクション (Autoactions)"参照)を、 指定された入力に対してカスタマイズされたデータ構造を構築するのに用いることが できたのと同じ手法であるといえる。
Whether or not the action it specifies is ever executed, a
<defer:...>
directive always succeeds, returning the number of deferred actions currently queued at that point.指定されたアクションが実行されようがされまいが、
<defer:...>
は常に成功し、その時点でキューに溜められている 遅延アクションの数を返す。 - Perlの解析 (Parsing Perl)
-
Parse::RecDescent provides limited support for parsing subsets of Perl, namely: quote-like operators, Perl variables, and complete code blocks.
Parse::RecDescentは、パース用のPerlサブセットを限定的にサポートしている: クォート風の演算子、Perl変数、そして完全なコードブロックだ。
The
<perl_quotelike>
directive can be used to parse any Perl quote-like operator:'a string'
,m/a pattern/
,tr{ans}{lation}
, etc. It does this by calling Text::Balanced::quotelike().<perl_quotelike>
ディレクティブは、Perlのクォート風の演算子を パースするのに用いられる:'a string'
、m/a pattern/
、tr{ans}{lation}
等々。これはText::Balanced::quotelike()を呼び出す ことによって実現される。If a quote-like operator is found, a reference to an array of eight elements is returned. Those elements are identical to the last eight elements returned by Text::Balanced::extract_quotelike() in an array context, namely:
クォート風の演算子が見つかると、8要素の配列のリファレンスが返される。これら の要素は、リストコンテキストにおいてText::Balanced::extract_quotelike()が 返す後ろからの8要素と同じものだ。すなわち:
- [0]
-
the name of the quotelike operator -- 'q', 'qq', 'm', 's', 'tr' -- if the operator was named; otherwise
undef
,もし名前が付いていれば、そのクォート風演算子の名前 -- 'q', 'qq', 'm', 's', 'tr' -- 。それ以外は
undef
。 - [1]
-
the left delimiter of the first block of the operation,
その演算子の第一ブロック左側のデリミタ。
- [2]
-
the text of the first block of the operation (that is, the contents of a quote, the regex of a match, or substitution or the target list of a translation),
その演算子の第一ブロックのテキスト(クォートの中身、マッチないしは 置換の正規表現、あるいは変換用の対象リスト)。
- [3]
-
the right delimiter of the first block of the operation,
その演算子の第一ブロック右側のデリミタ。
- [4]
-
the left delimiter of the second block of the operation if there is one (that is, if it is a
s
,tr
, ory
); otherwiseundef
,もしあれば(つまり
s
、tr
、あるいはy
の場合)、その演算子の 第二ブロック左側のデリミタ。そうでなければundef
。 - [5]
-
the text of the second block of the operation if there is one (that is, the replacement of a substitution or the translation list of a translation); otherwise
undef
,もしあれば(置換における置き換え文、あるいは変換における変換リスト)、 その演算子の第二ブロックのテキスト。それ以外は
undef
。 - [6]
-
the right delimiter of the second block of the operation (if any); otherwise
undef
,(あれば)その演算子の第二ブロック右側のデリミタ。なければ
undef
。 - [7]
-
the trailing modifiers on the operation (if any); otherwise
undef
.(あれば)その演算子の後ろに続く修飾子。なければ
undef
。
If a quote-like expression is not found, the directive fails with the usual
undef
value.もしクォート風の式が見つからなければ、このディレクティブは通常の
undef
値を伴って失敗する。The
<perl_variable>
directive can be used to parse any Perl variable: $scalar, @array, %hash, $ref->{field}[$index], etc. It does this by calling Text::Balanced::extract_variable().<perl_variable>
はPerlの変数をパースするのに用いられる: $scalar、@array、%hash、$ref->{field}[$index]等々。これは Text::Balanced::extract_variable()を呼び出すことで実現される。If the directive matches text representing a valid Perl variable specification, it returns that text. Otherwise it fails with the usual
undef
value.このディレクティブは、正しいPerlの変数指定を表すテキストにマッチすると そのテキストを返す。そうでない場合は通常の
undef
値を伴って失敗する。The
<perl_codeblock>
directive can be used to parse curly-brace-delimited block of Perl code, such as: { $a = 1; f() =~ m/pat/; }. It does this by calling Text::Balanced::extract_codeblock().<perl_codeblock>
ディレクティブは、ブレースで区切られた Perlのコードブロックをパースするのに用いられる。例えば: { $a = 1; f() =~ m/pat/; }。これは、Text::Balanced::extract_codeblock()を 呼び出すことによってなされる。If the directive matches text representing a valid Perl code block, it returns that text. Otherwise it fails with the usual
undef
value.このディレクティブは、正しいPerlのコードブロックを表すテキストにマッチする と、そのテキストを返す。そうでなければ通常の
undef
値を伴って失敗する。You can also tell it what kind of brackets to use as the outermost delimiters. For example:
また、外側のデリミタとして使うブラケットの種類をディレクティブに伝える こともできる。例えば:
arglist: <perl_codeblock ()>
causes an arglist to match a perl code block whose outermost delimiters are
(...)
(rather than the default{...}
).上では、arglistが
(...)
を外側のデリミタに持つコードブロックにマッチ するようになる。 - トークンの構築 (Constructing tokens)
-
Eventually, Parse::RecDescent will be able to parse tokenized input, as well as ordinary strings. In preparation for this joyous day, the
<token:...>
directive has been provided. This directive creates a token which will be suitable for input to a Parse::RecDescent parser (when it eventually supports tokenized input).いずれ、Parse::RecDescentは通常の文字列同様、トークン化された入力を パースすることができるようになる。この喜ばしき日を準備するために、
<token:...>
ディレクティブが提供される。このディレクティブは Parse::RecDescentパーサへの入力に適したトークンを生成する( トークン化された入力を最終的にサポートしたときに)。The text of the token is the value of the immediately preceding item in the production. A
<token:...>
directive always succeeds with a return value which is the hash reference that is the new token. It also sets the return value for the production to that hash ref.トークンのテキストは、プロダクション内の直前に先行する アイテムの値となる。
<token:...>
ディレクティブは、新しい トークンのハッシュリファレンスを戻り値にして常に成功する。また、 プロダクションへの戻り値にそのハッシュリファレンスをセットする。The
<token:...>
directive makes it easy to build a Parse::RecDescent-compatible lexer in Parse::RecDescent:<token:...>
によって、Parse::RecDescent互換の字句解析器 (lexer)を構築するのが簡単になる:my $lexer = new Parse::RecDescent q { lex: token(s) token: /a\b/ <token:INDEF> | /the\b/ <token:DEF> | /fly\b/ <token:NOUN,VERB> | /[a-z]+/i { lc $item[1] } <token:ALPHA> | <error: Unknown token> };
which will eventually be able to be used with a regular Parse::RecDescent grammar:
これは最終的には、正規のParse::RecDescent文法と一緒に使うことができる:
my $parser = new Parse::RecDescent q { startrule: subrule1 subrule 2 # 等々... };
either with a pre-lexing phase:
そして前-字句解析フェーズか:
$parser->startrule( $lexer->lex($data) );
or with a lex-on-demand approach:
字句解析要求型アプローチと一緒に使う:
$parser->startrule( sub{$lexer->token(\$data)} );
But at present, only the
<token:...>
directive is actually implemented. The rest is vapourware.しかし現在の所、
<token:...>
ディレクティブは実際に 実装されただけである。残りの部分は実体がない。 - 演算子の指定 (Specifying operations)
-
One of the commonest requirements when building a parser is to specify binary operators. Unfortunately, in a normal grammar, the rules for such things are awkward:
パーサ構築時に最も共通して必要となることの一つは、二項演算子の指定だ。 不幸なことに、通常の文法では、そのようなことのために利用できる規則は 不格好なものになる:
disjunction: conjunction ('or' conjunction)(s?) { $return = [ $item[1], @{$item[2]} ] } conjunction: atom ('and' atom)(s?) { $return = [ $item[1], @{$item[2]} ] }
or inefficient:
あるいは効率の悪い方法:
disjunction: conjunction 'or' disjunction { $return = [ $item[1], @{$item[2]} ] } | conjunction { $return = [ $item[1] ] } conjunction: atom 'and' conjunction { $return = [ $item[1], @{$item[2]} ] } | atom { $return = [ $item[1] ] }
and either way is ugly and hard to get right.
いずれにしても、醜し、正しく扱うのが困難だ。
The
<leftop:...>
and<rightop:...>
directives provide an easier way of specifying such operations. Using<leftop:...>
the above examples become:<leftop:...>
及び<rightop:...>
ディレクティブは、 そのような演算子を指定する、より簡単な方法を提供してくれる。<leftop:...>
を使うと、上の例はこうなる:disjunction: <leftop: conjunction 'or' conjunction> conjunction: <leftop: atom 'and' atom>
The
<leftop:...>
directive specifies a left-associative binary operator. It is specified around three other grammar elements (typically subrules or terminals), which match the left operand, the operator itself, and the right operand respectively.<leftop:...>
ディレクティブは、左結合の二項演算子を指定する。 これは他の三つの文法要素(典型的にはサブ規則と終端記号)の周辺に指定される。 このディレクティブは、左のオペランド、演算子それ自身、右のオペランドに それぞれマッチする。A
<leftop:...>
directive such as:このような
<leftop:...>
ディレクティブは:disjunction: <leftop: conjunction 'or' conjunction>
is converted to the following:
次のように変換される:
disjunction: ( conjunction ('or' conjunction)(s?) { $return = [ $item[1], @{$item[2]} ] } )
In other words, a
<leftop:...>
directive matches the left operand followed by zero or more repetitions of both the operator and the right operand. It then flattens the matched items into an anonymous array which becomes the (single) value of the entire<leftop:...>
directive.つまり、
<leftop:...>
ディレクティブは、0個以上の演算子 または右オペランドの繰り返しが続く左オペランドにマッチする。それから マッチしたアイテムは無名配列に入れられて、<leftop:...>
ディレクティブ丸ごとの(単一の)値になる。For example, an
<leftop:...>
directive such as:例えば、このような
<leftop:...>
ディレクティブがあって:output: <leftop: ident '<<' expr >
when given a string such as:
このような文字列が与えられたとき:
cout << var << "str" << 3
would match, and
$item[1]
would be set to:これはマッチして
$item[1]
には以下がセットされる:[ 'cout', 'var', '"str"', '3' ]
In other words:
言い換えると:
output: <leftop: ident '<<' expr >
is equivalent to a left-associative operator:
は、左結合の演算子に等しい:
output: ident { $return = [$item[1]] } | ident '<<' expr { $return = [@item[1,3]] } | ident '<<' expr '<<' expr { $return = [@item[1,3,5]] } | ident '<<' expr '<<' expr '<<' expr { $return = [@item[1,3,5,7]] } # ...等々...
Similarly, the
<rightop:...>
directive takes a left operand, an operator, and a right operand:同様に、
<rightop:...>
ディレクティブは左側のオペランド、 演算子、右側のオペランドをとる:assign: <rightop: var '=' expr >
and converts them to:
これは次のように変換される:
assign: ( (var '=' {$return=$item[1]})(s?) expr { $return = [ @{$item[1]}, $item[2] ] } )
which is equivalent to a right-associative operator:
つまり右結合の演算子に等しい:
assign: var { $return = [$item[1]] } | var '=' expr { $return = [@item[1,3]] } | var '=' var '=' expr { $return = [@item[1,3,5]] } | var '=' var '=' var '=' expr { $return = [@item[1,3,5,7]] } # ...etc...
Note that for both the
<leftop:...>
and<rightop:...>
directives, the directive does not normally return the operator itself, just a list of the operands involved. This is particularly handy for specifying lists:<leftop:...>
と<rightop:...>
ディレクティブは どちらも通常、演算子それ自体を返すわけではなく、演算子に関わるオペランドの リストを返すことに注意。これは部分的にはリストを指定するのに便利だ:list: '(' <leftop: list_item ',' list_item> ')' { $return = $item[2] }
There is, however, a problem: sometimes the operator is itself significant. For example, in a Perl list a comma and a
=>
are both valid separators, but the=>
has additional stringification semantics. Hence it's important to know which was used in each case.だが問題がある:ときに演算子それ自体が重要なことがあるからだ。例えば、 Perlのリストにおいて、カンマと
=>
はどちらも正しい区切り文字だが、=>
には追加的な意味がある。だからどちらのケースとして使われているかを 知ることが重要になる。To solve this problem the
<leftop:...>
and<rightop:...>
directives do return the operator(s) as well, under two circumstances. The first case is where the operator is specified as a subrule. In that instance, whatever the operator matches is returned (on the assumption that if the operator is important enough to have its own subrule, then it's important enough to return).この問題を解決するには、
<leftop:...>
と<rightop:...>
ディレクティブが、二つの状況下では演算子も返すようにすればいい。 最初のケースは、演算子がサブ規則として指定されている場合。そのような例では、 マッチした演算子は何であれ返される(その演算子が自分自身のサブ規則を持つ ぐらいに重要であるなら、返すに値するくらい重要であろうという仮定に基づく)。The second case is where the operator is specified as a regular expression. In that case, if the first bracketed subpattern of the regular expression matches, that matching value is returned (this is analogous to the behaviour of the Perl
split
function, except that only the first subpattern is returned).二番目のケースは、演算子が正規表現として指定されてる場合。このケースでは 正規表現のブラケットがついた最初のサブパターンにマッチすると、マッチした 値が返される(これはPerlの
split
関数の振る舞いに似ている。ただし最初の サブパターンが返される点を除いて)。In other words, given the input:
つまり、次の入力が与えられるなら:
( a=>1, b=>2 )
the specifications:
指定は:
list: '(' <leftop: list_item separator list_item> ')' separator: ',' | '=>'
or:
あるいは:
list: '(' <leftop: list_item /(,|=>)/ list_item> ')'
cause the list separators to be interleaved with the operands in the anonymous array in
$item[2]
:こうすると、そのリスト区切りは、オペランドに挟まれて
$item[2]
の無名配列の中に収まる:[ 'a', '=>', '1', ',', 'b', '=>', '2' ]
But the following version:
しかし次のようなやり方だと:
list: '(' <leftop: list_item /,|=>/ list_item> ')'
returns only the operators:
演算子だけが返される: [訳者注:オペランドでは?]
[ 'a', '1', 'b', '2' ]
Of course, none of the above specifications handle the case of an empty list, since the
<leftop:...>
and<rightop:...>
directives require at least a single right or left operand to match. To specify that the operator can match "trivially", it's necessary to add a(?)
qualifier to the directive:もちろん、上の指定は空リストのケースを扱わない。なぜなら、
<leftop:...>
と<rightop:...>
ディレクティブは少なくとも 一つの右ないしは左オペランドにマッチする必要があるからだ。演算子を "些細なこと"にマッチさせるには、ディレクティブに(?)
を加える必要がある:list: '(' <leftop: list_item /(,|=>)/ list_item>(?) ')'
Note that in almost all the above examples, the first and third arguments of the
<leftop:...>
directive were the same subrule. That is because<leftop:...>
's are frequently used to specify "separated" lists of the same type of item. To make such lists easier to specify, the following syntax:上の例のほとんどで、
<leftop:...>
ディレクティブの一番目と 三番目の引数が同じサブ規則であることに注意して欲しい。 これは、<leftop:...>
が、しばしば同じタイプのアイテムが"分離"された リストを指定するのに使われるためである。そのようなリストを簡単に指定 するために、次の構文:list: element(s /,/)
is exactly equivalent to:
は、以下と正確に等しい:
list: <leftop: element /,/ element>
Note that the separator must be specified as a raw pattern (i.e. not a string or subrule).
区切りは生のパターンとして指定されるべきであることに注意(つまり 文字列やサブ規則ではなく)。
- 点数付きプロダクション (Scored productions)
-
By default, Parse::RecDescent grammar rules always accept the first production that matches the input. But if two or more productions may potentially match the same input, choosing the first that does so may not be optimal.
デフォルトでは、Parse::RecDescentの文法規則は常に、入力にマッチした 最初のプロダクションを受け入れる。だが、2つ以上のプロダクションが 同じ入力に対してマッチする可能性があるなら、最初のプロダクションを 選択することが最適とはいえないかもしれない。
For example, if you were parsing the sentence "time flies like an arrow", you might use a rule like this:
例えば、"time flies like an arrow"という文をパースしたとき、次のような 規則を使っただろう:
sentence: verb noun preposition article noun { [@item] } | adjective noun verb article noun { [@item] } | noun verb preposition article noun { [@item] }
Each of these productions matches the sentence, but the third one is the most likely interpretation. However, if the sentence had been "fruit flies like a banana", then the second production is probably the right match.
これらのプロダクションはいずれも文とマッチするが、三番目のものが 解釈としては最もよい。しかし、もしこの文が"fruit flies like a banana" であったなら、二番目のプロダクションが正しいマッチになるだろう。
To cater for such situtations, the
<score:...>
can be used. The directive is equivalent to an unconditional<reject>
, except that it allows you to specify a "score" for the current production. If that score is numerically greater than the best score of any preceding production, the current production is cached for later consideration. If no later production matches, then the cached production is treated as having matched, and the value of the item immediately before its<score:...>
directive is returned as the result.このような状況に対応するために、
<score:...>
が利用できる。 このディレクティブは、現在のプロダクションに"得点"を設定できることを 除いて、無条件<reject>
と同じである。もしこの得点が先行する プロダクションの得点よりも良いなら、現在のプロダクションは後で考慮される ためにキャッシュされる。後ろのプロダクションがマッチしなければ、 キャッシュされたプロダクションがマッチしたものとして扱われる。そして<score:...>
ディレクティブの直前のアイテムの値が結果として 返される。In other words, by putting a
<score:...>
directive at the end of each production, you can select which production matches using criteria other than specification order. For example:つまり、各プロダクションの最後に
<score:...>
ディレクティブを 置くことによって、指定順以外の基準でプロダクションマッチを選ぶことが できる。例えば:sentence: verb noun preposition article noun { [@item] } <score: sensible(@item)> | adjective noun verb article noun { [@item] } <score: sensible(@item)> | noun verb preposition article noun { [@item] } <score: sensible(@item)>
Now, when each production reaches its respective
<score:...>
directive, the subroutinesensible
will be called to evaluate the matched items (somehow). Once all productions have been tried, the one whichsensible
scored most highly will be the one that is accepted as a match for the rule.各プロダクションがそれぞれの
<score:...>
ディレクティブに 到達したとき、サブルーチンsensible
は呼び出されてマッチしたアイテムを (どうにかして)評価する。ひとたび全てのプロダクションが試されたなら、sensible
が最も高い点数を出したプロダクションが、その規則にマッチした ものとして受け入れられる。The variable $score always holds the current best score of any production, and the variable $score_return holds the corresponding return value.
変数$scoreは常に、プロダクションの現在の最高得点を保持する。そして 変数$score_returnは、対応する戻り値を保持する。
As another example, the following grammar matches lines that may be separated by commas, colons, or semi-colons. This can be tricky if a colon-separated line also contains commas, or vice versa. The grammar resolves the ambiguity by selecting the rule that results in the fewest fields:
もう一つの例として、カンマ、コロン、あるいはセミコロンで区切られた行に マッチする文法を考えてみよう。もしコロンで区切られた行がカンマも含んで いたら、あるいはその逆だったなら、これはトリッキーなものになる。 この文法は、最も少ないフィールドだった規則を選ぶことによって、 この曖昧さを解決している:
line: seplist[sep=>','] <score: -@{$item[1]}> | seplist[sep=>':'] <score: -@{$item[1]}> | seplist[sep=>" "] <score: -@{$item[1]}> seplist: <skip:""> <leftop: /[^$arg{sep}]*/ "$arg{sep}" /[^$arg{sep}]*/>
Note the use of negation within the
<score:...>
directive to ensure that the seplist with the most items gets the lowest score.<score:...>
ディレクティブ内でマイナスを使っているのは、最も多い アイテムを持ったseplistが最も低い点数になるようにするためであることに注意。As the above examples indicate, it is often the case that all productions in a rule use exactly the same
<score:...>
directive. It is tedious to have to repeat this identical directive in every production, so Parse::RecDescent also provides the<autoscore:...>
directive.上の例が示しているように、規則内の全プロダクションが、全く同じ
<score:...>
ディレクティブを使うことはよくある。全部の プロダクションで同じディレクティブを繰り返すのは退屈なことだ。そこで Parse::RecDescentは<autoscore:...>
ディレクティブも提供している。If an
<autoscore:...>
directive appears in any production of a rule, the code it specifies is used as the scoring code for every production of that rule, except productions that already end with an explicit<score:...>
directive. Thus the rules above could be rewritten:line: <autoscore: -@{$item[1]}> line: seplist[sep=>','] | seplist[sep=>':'] | seplist[sep=>" "]
ある規則のいずれかのプロダクションに
<autoscore:...>
ディレクティブが現れると、指定されたコードは、その規則の全プロダクションに 対する点数付けコードとして利用される。ただし、既に明示的に<score:...>
ディレクティブが付いたプロダクションは除く。 よって、先の規則を書き直すと:sentence: <autoscore: sensible(@item)> | verb noun preposition article noun { [@item] } | adjective noun verb article noun { [@item] } | noun verb preposition article noun { [@item] }
Note that the
<autoscore:...>
directive itself acts as an unconditional<reject>
, and (like the<rulevar:...>
directive) is pruned at compile-time wherever possible.注意して欲しいのだが、
<autoscore:...>
ディレクティブ自体は 無条件<reject>
として振る舞い、(<rulevar:...>
ディレクティブのように)可能ならコンパイル時に刈り込まれる。 - 文法チェックの免除 (Dispensing with grammar checks)
-
During the compilation phase of parser construction, Parse::RecDescent performs a small number of checks on the grammar it's given. Specifically it checks that the grammar is not left-recursive, that there are no "insatiable" constructs of the form:
パーサ構築のコンパイルフェーズ中、Parse::RecDescentは与えられた文法に対し 少しだけチェックを行う。特に、文法が左再帰でないこと、次の形式が"貪欲な" 構成でないことをチェックする。
rule: subrule(s) subrule
and that there are no rules missing (i.e. referred to, but never defined).
そして、失われた規則(参照してるが定義されてない規則)がないかチェックする。
These checks are important during development, but can slow down parser construction in stable code. So Parse::RecDescent provides the <nocheck> directive to turn them off. The directive can only appear before the first rule definition, and switches off checking throughout the rest of the current grammar.
これらのチェックは開発段階では重要だが、安定したコードではパーサ構築の スピードを遅くしうる。そこでParse::RecDescentは、このチェックを 切るために<nocheck>ディレクティブを提供する。このディレクティブは 最初の規則が定義される前にだけ現れることができ、現在の文法の残りについて チェックするのをやめる。
Typically, this directive would be added when a parser has been thoroughly tested and is ready for release.
典型的には、このディレクティブは、パーサが完全にテストされ、リリースの 準備ができたときに付け加えられるだろう。
サブ規則の引数リスト (Subrule argument lists)¶
It is occasionally useful to pass data to a subrule which is being invoked. For example, consider the following grammar fragment:
呼び出されるサブ規則にデータを渡すことができると便利なことがある。例えば、 次のような文法の一部を考えてみると:
classdecl: keyword decl
keyword: 'struct' | 'class';
decl: # WHATEVER
The decl
rule might wish to know which of the two keywords was used (since it may affect some aspect of the way the subsequent declaration is interpreted). Parse::RecDescent
allows the grammar designer to pass data into a rule, by placing that data in an argument list (that is, in square brackets) immediately after any subrule item in a production. Hence, we could pass the keyword to decl
as follows:
decl
規則は、二つのkeywordのどちらが使われているのかを知りたい (続く宣言の解釈方法に影響を与えるから)。Parse::RecDescent
は、 文法の設計者がデータを規則に渡すことができるようにしている。これは プロダクションの任意のサブ規則アイテムの直後に、そのデータを 引数リスト(ブラケット内)の中に置くことによってなされる。 それゆえ、decl
にkeywordを渡すには:
classdecl: keyword decl[ $item[1] ]
keyword: 'struct' | 'class';
decl: # WHATEVER
The argument list can consist of any number (including zero!) of comma-separated Perl expressions. In other words, it looks exactly like a Perl anonymous array reference. For example, we could pass the keyword, the name of the surrounding rule, and the literal 'keyword' to decl
like so:
引数リストは、任意の数(0も含む!)のカンマで区切られたPerlの式を含む ことができる。つまり、Perlの無名配列リファレンスにそっくりなのである。 例えば次のようにkeyword、取り囲んでいる規則、そして文字列の'keyword'を decl
に渡すことができる:
classdecl: keyword decl[$item[1],$item[0],'keyword']
keyword: 'struct' | 'class';
decl: # WHATEVER
Within the rule to which the data is passed (decl
in the above examples) that data is available as the elements of a local variable @arg
. Hence decl
might report its intentions as follows:
データが渡された規則(上の例ではdecl
)内では、そのデータは局所変数 @arg
の要素として利用することができる。よってdecl
はその性向を 次のようにして報告できる:
classdecl: keyword decl[$item[1],$item[0],'keyword']
keyword: 'struct' | 'class';
decl: { print "Declaring $arg[0] (a $arg[2])\n";
print "(this rule called by $arg[1])" }
Subrule argument lists can also be interpreted as hashes, simply by using the local variable %arg
instead of @arg
. Hence we could rewrite the previous example:
サブ規則の引数リストはハッシュとしても解釈できる。これは単に@arg
の 代わりに局所変数%arg
を利用すればよい。よって前の例は次のように書き直せる:
classdecl: keyword decl[keyword => $item[1],
caller => $item[0],
type => 'keyword']
keyword: 'struct' | 'class';
decl: { print "Declaring $arg{keyword} (a $arg{type})\n";
print "(this rule called by $arg{caller})" }
Both @arg
and %arg
are always available, so the grammar designer may choose whichever convention (or combination of conventions) suits best.
@arg
と%arg
はどちらもいつでも利用できる。だから文法の設計者は 利便に最も適した方を選べばよい。
Subrule argument lists are also useful for creating "rule templates" (especially when used in conjunction with the <matchrule:...>
directive). For example, the subrule:
サブ規則の引数リストは"規則テンプレート"を作成するのにも利用できる (特に<matchrule:...>
ディレクティブと一緒に使う場合)。 例えば、次のサブ規則:
list: <matchrule:$arg{rule}> /$arg{sep}/ list[%arg]
{ $return = [ $item[1], @{$item[3]} ] }
| <matchrule:$arg{rule}>
{ $return = [ $item[1]] }
is a handy template for the common problem of matching a separated list. For example:
は、区切られたリストにマッチする場合に共通する問題に対する便利な テンプレートになる。例えば:
function: 'func' name '(' list[rule=>'param',sep=>';'] ')'
param: list[rule=>'name',sep=>','] ':' typename
name: /\w+/
typename: name
When a subrule argument list is used with a repeated subrule, the argument list goes before the repetition specifier:
サブ規則の引数リストが反復サブ規則と一緒に使われるとき、引数リストは 反復指定子の前に来る:
list: /some|many/ thing[ $item[1] ](s)
The argument list is "late bound". That is, it is re-evaluated for every repetition of the repeated subrule. This means that each repeated attempt to match the subrule may be passed a completely different set of arguments if the value of the expression in the argument list changes between attempts. So, for example, the grammar:
引数リストは"遅延バインディング"である。そのため、反復サブ規則の 全ての繰り返しで再評価される。このことは、サブ規則の照合が試みられる 度に、引数の式の値が異なれば完全に異なった引数のセットが渡される ことを意味する。よって次の文法の例では:
{ $::species = 'dogs' }
pair: 'two' animal[$::species](s)
animal: /$arg[0]/ { $::species = 'cats' }
will match the string "two dogs cats cats" completely, whereas it will only match the string "two dogs dogs dogs" up to the eighth letter. If the value of the argument list were "early bound" (that is, evaluated only the first time a repeated subrule match is attempted), one would expect the matching behaviours to be reversed.
文字列"two dogs cats cats"は完全にマッチする。これに対し、文字列 "two dogs dogs dogs"は8文字目までしかマッチしない。もしも引数リスト の値が"事前バインディング"だったなら(すなわち、反復サブ規則のマッチ が試みられる一番最初だけ評価されるなら)、照合の振る舞いは逆になる のが期待できたであろうに。
Of course, it is possible to effectively "early bind" such argument lists by passing them a value which does not change on each repetition. For example:
もちろん、反復の度に変化しない値を渡すことによって、そのような引数リストを 効果的に"事前バインディング"することが可能である。例:
{ $::species = 'dogs' }
pair: 'two' { $::species } animal[$item[2]](s)
animal: /$arg[0]/ { $::species = 'cats' }
Arguments can also be passed to the start rule, simply by appending them to the argument list with which the start rule is called (after the "line number" parameter). For example, given:
単純に("行番号"パラメータの後で)開始規則が呼び出されるときに 使う引数リストに引数を加えることによって、開始規則に渡すこともできる。 次のような例の場合:
$parser = new Parse::RecDescent ( $grammar );
$parser->data($text, 1, "str", 2, \@arr);
# ^^^^^ ^ ^^^^^^^^^^^^^^^
# | | |
# 解析用テキスト | |
# スタート時の行番号 |
# 規則 data に渡される@argの要素データ
# ^^^^^ ^ ^^^^^^^^^^^^^^^
# | | |
# TEXT TO BE PARSED | |
# STARTING LINE NUMBER |
# ELEMENTS OF @arg WHICH IS PASSED TO RULE data
then within the productions of the rule data
, the array @arg
will contain ("str", 2, \@arr)
.
こうすると規則data
のプロダクション内で、配列@arg
は("str", 2, \@arr)
を含むことになる。
オルタネーション (Alternations)¶
Alternations are implicit (unnamed) rules defined as part of a production. An alternation is defined as a series of '|'-separated productions inside a pair of round brackets. For example:
オルタネーションとは、プロダクションの一部として定義される暗黙の (名前のない)規則である。オルタネーションは丸括弧の中の'|'で区切られた 一連のプロダクションとして定義される。例えば:
character: 'the' ( good | bad | ugly ) /dude/
Every alternation implicitly defines a new subrule, whose automatically-generated name indicates its origin: "_alternation_<I>_of_production_<P>_of_rule<R>" for the appropriate values of <I>, <P>, and <R>. A call to this implicit subrule is then inserted in place of the brackets. Hence the above example is merely a convenient short-hand for:
全てのオルタネーションは暗黙の内に新しいサブ規則を定義する。それらが 有する自動生成された名前はその出自を示している: "_alternation_<I>_of_production_<P>_of_rule<R>"は、<I>、<P>、そして<R> に相応しい値となる。この暗黙のサブ規則に対する呼び出しは、括弧の位置に 挿入される。それゆえ、上の例は単に以下に対する簡便な省略記法となる:
character: 'the'
_alternation_1_of_production_1_of_rule_character
/dude/
_alternation_1_of_production_1_of_rule_character:
good | bad | ugly
Since alternations are parsed by recursively calling the parser generator, any type(s) of item can appear in an alternation. For example:
オルタネーションは再帰的にパーサジェネレータを呼び出して解析されるので、 どんなタイプのアイテムもオルタネーションの中に現れうる。例えば:
character: 'the' ( 'high' "plains" # Silent, with poncho
| /no[- ]name/ # Silent, no poncho
| vengeance_seeking # Poncho-optional
| <error>
) drifter
In this case, if an error occurred, the automatically generated message would be:
この場合、エラーが生じると自動生成されるメッセージは:
ERROR (line <N>): Invalid implicit subrule: Expected
'high' or /no[- ]name/ or generic,
but found "pacifist" instead
Since every alternation actually has a name, it's even possible to extend or replace them:
全てのオルタネーションは実際には名前を持つので、それらを拡張したり 置換したりすることができる:
parser->Replace(
"_alternation_1_of_production_1_of_rule_character:
'generic Eastwood'"
);
More importantly, since alternations are a form of subrule, they can be given repetition specifiers:
さらに重要なことに、オルタネーションはサブ規則の一形式なので、反復指定子を 与えることができる:
character: 'the' ( good | bad | ugly )(?) /dude/
増加解析 (Incremental Parsing)¶
Parse::RecDescent
provides two methods - Extend
and Replace
- which can be used to alter the grammar matched by a parser. Both methods take the same argument as Parse::RecDescent::new
, namely a grammar specification string
Parse::RecDescent
は二つのメソッド - Extend
と Replace
- を提供 している。これらは、パーサが照合する文法に変更を加えるために用いられる。 どちらのメソッドもParse::RecDescent::new
と同じ引数、すなわち、文法仕様の 文字列をとる。
Parse::RecDescent::Extend
interprets the grammar specification and adds any productions it finds to the end of the rules for which they are specified. For example:
Parse::RecDescent::Extend
は文法仕様を解釈し、見出されたプロダクションを 指定されている規則の最後に加える。例えば:
$add = "name: 'Jimmy-Bob' | 'Bobby-Jim'\ndesc: colour /necks?/";
parser->Extend($add);
adds two productions to the rule "name" (creating it if necessary) and one production to the rule "desc".
上の例では、規則"name"(必要があれば生成される)に対して二つの プロダクションと、規則"desc"に対して一つのプロダクションが付け加えられる。
Parse::RecDescent::Replace
is identical, except that it first resets are rule specified in the additional grammar, removing any existing productions. Hence after:
Parse::RecDescent::Replace
も同様なのだが、既にあるプロダクションを削除する ことによって、追加文法で指定された規則が最初にリセットされる。 よって、使用後は:
$add = "name: 'Jimmy-Bob' | 'Bobby-Jim'\ndesc: colour /necks?/";
parser->Replace($add);
are are only valid "name"s and the one possible description.
上の例が唯一の正しい"name"で、一つの可能性のある説明になる。
A more interesting use of the Extend
and Replace
methods is to call them inside the action of an executing parser. For example:
Extend
とReplace
メソッドのさらに面白い使い方は、実行中のパーサの アクション内でそれらを呼び出すことだ。例えば:
typedef: 'typedef' type_name identifier ';'
{ $thisparser->Extend("type_name: '$item[3]'") }
| <error>
identifier: ...!type_name /[A-Za-z_]w*/
which automatically prevents type names from being typedef'd, or:
これは自動的に型名(type name)がtypedefされるのを妨げる。あるいは:
command: 'map' key_name 'to' abort_key
{ $thisparser->Replace("abort_key: '$item[2]'") }
| 'map' key_name 'to' key_name
{ map_key($item[2],$item[4]) }
| abort_key
{ exit if confirm("abort?") }
abort_key: 'q'
key_name: ...!abort_key /[A-Za-z]/
which allows the user to change the abort key binding, but not to unbind it.
これでユーザは中断キーを変更することができる。ただしバインド解除はできない。
The careful use of such constructs makes it possible to reconfigure a a running parser, eliminating the need for semantic feedback by providing syntactic feedback instead. However, as currently implemented, Replace()
and Extend()
have to regenerate and re-eval
the entire parser whenever they are called. This makes them quite slow for large grammars.
このような構築を気をつけて使えば、意味解析フィードバックの必要性を排除し、 代わりに構文上のフィードバックを提供することによって、実行中のパーサを 再構成することができる。しかし現在の実装では、Replace()
とExtend()
は呼び出される度に、パーサを丸ごと再生成し、再度eval
しなければ ならない。これは文法が巨大な場合、非常に遅くなってしまう。
In such cases, the judicious use of an interpolated regex is likely to be far more efficient:
そういう場合、展開される正規表現をうまく使うことによって、ずっと効率的な ものにすることができる:
typedef: 'typedef' type_name/ identifier ';'
{ $thisparser->{local}{type_name} .= "|$item[3]" }
| <error>
identifier: ...!type_name /[A-Za-z_]w*/
type_name: /$thisparser->{local}{type_name}/
パーサのプリコンパイル (Precompiling parsers)¶
Normally Parse::RecDescent builds a parser from a grammar at run-time. That approach simplifies the design and implementation of parsing code, but has the disadvantage that it slows the parsing process down - you have to wait for Parse::RecDescent to build the parser every time the program runs. Long or complex grammars can be particularly slow to build, leading to unacceptable delays at start-up.
通常、Parse::RecDescentは、実行時に文法からパーサを構築する。この アプローチはパース用コードの設計と実装を単純化してくれる。しかし、 パース処理が遅くなるという不利を抱えることになる。プログラムを実行する 度に、あなたはParse::RecDescentがパーサを構築するのを待たなくてはならない。 長い、あるいは複雑な文法は殊のほか構築を遅らせ、開始までに我慢できない 遅れをもたらすだろう。
To overcome this, the module provides a way of "pre-building" a parser object and saving it in a separate module. That module can then be used to create clones of the original parser.
これを何とかするために、このモジュールは、パーサオブジェクトの"先行構築"と、 それを別モジュールに分離して保存する方法を提供している。そのモジュールを 使えば、オリジナルのパーサのクローンを生成することができる。
A grammar may be precompiled using the Precompile
class method. For example, to precompile a grammar stored in the scalar $grammar, and produce a class named PreGrammar in a module file named PreGrammar.pm, you could use:
クラスメソッドPrecompile
を使うと、文法がプリコンパイルされる。例えば、 スカラー変数$grammarに格納されている文法をプリコンパイルし、PreGrammar.pm というモジュールファイルにPreGrammarという名前のクラスを作成するには、 次のようにする:
use Parse::RecDescent;
Parse::RecDescent->Precompile($grammar, "PreGrammar");
The first argument is the grammar string, the second is the name of the class to be built. The name of the module file is generated automatically by appending ".pm" to the last element of the class name. Thus
最初の引数は文法の文字列、二番目は構築されるクラスの名前だ。 モジュールファイルの名前は、クラス名の最終要素に".pm"を加えて自動的に 生成される。それゆえ:
Parse::RecDescent->Precompile($grammar, "My::New::Parser");
would produce a module file named Parser.pm.
これはParser.pmという名前のモジュールファイルができる。
It is somewhat tedious to have to write a small Perl program just to generate a precompiled grammar class, so Parse::RecDescent has some special magic that allows you to do the job directly from the command-line.
プリコンパイルされた文法用のクラスを生成するためだけに、小さな Perlプログラムを書かねばならないのは、なんとも退屈なことだ。そこで Parse::RecDescentは特別な魔法でもって、あなたがコマンドラインから ダイレクトにこの仕事を行なえるようにしてくれる。
If your grammar is specified in a file named grammar, you can generate a class named Yet::Another::Grammar like so:
grammarというファイルの中で文法が設定されているとして、あなたが Yet::Another::Grammarというクラスを生成するには:
> perl -MParse::RecDescent - grammar Yet::Another::Grammar
This would produce a file named Grammar.pm containing the full definition of a class called Yet::Another::Grammar. Of course, to use that class, you would need to put the Grammar.pm file in a directory named Yet/Another, somewhere in your Perl include path.
これでクラス名Yet::Another::Grammarの完全な定義を含んだ Grammar.pmという名前のファイルが生成される。もちろん、このクラスを 使うには、Perlのインクルードパスが通ったYet/Anotherという名前の ディレクトリにGrammar.pmファイルを置かなければならない。
Having created the new class, it's very easy to use it to build a parser. You simply use
the new module, and then call its new
method to create a parser object. For example:
新しいクラスをつくってしまえば、パーサ構築のためにこれを利用する のはいともたやすい。新しいモジュールを単にuse
し、パーサモジュール を生成するためにnew
メソッドを呼び出すだけだ。例えば:
use Yet::Another::Grammar;
my $parser = Yet::Another::Grammar->new();
The effect of these two lines is exactly the same as:
上の二行の結果は次のものと全く同じだ:
use Parse::RecDescent;
open GRAMMAR_FILE, "grammar" or die;
local $/;
my $grammar = <GRAMMAR_FILE>;
my $parser = Parse::RecDescent->new($grammar);
only considerably faster.
ただ、ずいぶん速いという点が違う。
Note however that the parsers produced by either approach are exactly the same, so whilst precompilation has an effect on set-up speed, it has no effect on parsing speed. RecDescent 2.0 will address that problem.
しかし注意して欲しい。いずれのアプローチで生成されたパーサも全く同じもの である。そしてプリコンパイルされた方がセットアップのスピードに影響を 与えるといえども、解析のスピードには影響がない。RecDescent 2.0 では この問題に取り組むつもりだ。
Parse::RecDescent
のメタ文法 (A Metagrammar for Parse::RecDescent
)¶
The following is a specification of grammar format accepted by Parse::RecDescent::new
(specified in the Parse::RecDescent
grammar format!):
以下は、Parse::RecDescent::new
が受け付ける文法書式の仕様である (Parse::RecDescent
の文法書式で設定されている!):
grammar : components(s)
component : rule | comment
rule : "\n" identifier ":" production(s?)
production : items(s)
item : lookahead(?) simpleitem
| directive
| comment
lookahead : '...' | '...!' # 肯定あるいは否定の先読み
simpleitem : subrule args(?) # 新たな規則にマッチ
| repetition # 繰り返されるサブ規則にマッチ
| terminal # 次の入力にマッチ
| bracket args(?) # 選択アイテムにマッチ
| action # 何か行なう
subrule : identifier # 規則名
args : {extract_codeblock($text,'[]')} # 配列リファレンスと全く同じ
repetition : subrule args(?) howoften
howoften : '(?)' # 0ないしは1回
| '(s?)' # 0回以上
| '(s)' # 1回以上
| /(\d+)[.][.](/\d+)/ # $1 から $2 回
| /[.][.](/\d*)/ # 最大でも $1 回
| /(\d*)[.][.])/ # 少なくとも $1 回
terminal : /[/]([\][/]|[^/])*[/]/ # 展開される正規表現パターン
| /"([\]"|[^"])*"/ # 展開されるリテラル
| /'([\]'|[^'])*'/ # 展開されないリテラル
action : { extract_codeblock($text) } # 埋め込みのPerlコード
bracket : '(' Item(s) production(s?) ')' # 選択的なサブ規則
directive : '<commit>' # プロダクションにcommit
| '<uncommit>' # commitのキャンセル
| '<resync>' # 改行をスキップ
| '<resync:' pattern '>' # <pattern>をスキップ
| '<reject>' # このプロダクションを失敗させる
| '<reject:' condition '>' # もし<condition>なら失敗
| '<error>' # エラーの報告
| '<error:' string '>' # "<string>"としてエラーを報告
| '<error?>' # commitされたときだけエラー
| '<error?:' string '>' # " " " "
| '<rulevar:' /[^>]+/ '>' # 規則内に局所化された変数を定義
| '<matchrule:' string '>' # stringという名の規則を呼び出す
identifier : /[a-z]\w*/i # アルファベットから始める
comment : /#[^\n]*/ # Perlと一緒
pattern : {extract_bracketed($text,'<')} # allow embedded "<..>"
condition : {extract_codeblock($text,'{<')} # 完全なPerlの式
string : {extract_variable($text)} # 任意のPerl変数
| {extract_quotelike($text)} # あるいはクォート風文字列
| {extract_bracketed($text,'<')} # あるいはブラケットで囲まれている
おや? (GOTCHAS)¶
This section describes common mistakes that grammar writers seem to make on a regular basis.
このセクションでは、文法作成者が基礎的なところで犯しがちなよくある間違い について言及する。
1. 常にパースを失敗させるエラーの予測¶
A common mistake when using error messages is to write the grammar like this:
エラーメッセージを利用する際によくある間違いは、次のような文法を書いてしまうことだ:
file: line(s)
line: line_type_1
| line_type_2
| line_type_3
| <error>
The expectation seems to be that any line that is not of type 1, 2 or 3 will invoke the <error>
directive and thereby cause the parse to fail.
ここで期待しているのは、lineがタイプ1,2,3のいずれにも合致しなければ、 <error>
ディレクティブを呼び出してパースを失敗させることであろう。
Unfortunately, that only happens if the error occurs in the very first line. The first rule states that a file
is matched by one or more lines, so if even a single line succeeds, the first rule is completely satisfied and the parse as a whole succeeds. That means that any error messages generated by subsequent failures in the line
rule are quietly ignored.
残念ながら、ちょうど一つ目のlineでエラーが現れたときにしかこれは起きない。 最初の規則は、file
が1つ以上のlineにマッチすると述べている。だから一つでも lineが成功すれば、一番目の規則は完全に要求が満たされ、パースは全体として 成功になる。つまり、その後で規則line
が失敗して生み出されたエラーメッセージ は完全に無視されることになる。
Typically what's really needed is this:
本当に必要だったことの典型例:
file: line(s) eofile { $return = $item[1] }
line: line_type_1
| line_type_2
| line_type_3
| <error>
eofile: /^\Z/
The addition of the eofile
subrule to the first production means that a file only matches a series of successful line
matches that consume the complete input text. If any input text remains after the lines are matched, there must have been an error in the last line
. In that case the eofile
rule will fail, causing the entire file
rule to fail too.
最初の規則にサブ規則eofile
を加えることによって、入力テキストを完全に 消費する形で、line
が連続してマッチに成功した場合にのみ、fileはマッチ する。もしlineがマッチした後で入力テキストが何か残っているなら、最後の line
にエラーがあったに違いない。この場合、規則eofile
は失敗し、 規則file
全体も失敗となる。
Note too that eofile
must match /^\Z/
(end-of-text), not /^\cZ/
or /^\cD/
(end-of-file).
eofile
は/^\Z/
(end-of-text) にマッチしなければならないのであって、 /^\cZ/
や /^\cD/
(end-of-file) ではないことにも注意。
And don't forget the action at the end of the production. If you just write:
そして忘れてならないのが、プロダクションの最後にあるアクションだ。もし 単に次のように書いたなら:
file: line(s) eofile
then the value returned by the file
rule will be the value of its last item: eofile
. Since eofile
always returns an empty string on success, that will cause the file
rule to return that empty string. Apart from returning the wrong value, returning an empty string will trip up code such as:
規則file
が返す値は、その最後のアイテム:eofile
の値である。 eofile
は成功すると常に空文字列を返すので、規則file
は空文字列を返す ことになる。この誤った値を返すのとは別に、空文字列が返ってくることによって 次のようなコードに問題が起きる:
$parser->file($filetext) || die;
(since "" is false).
(なぜなら、""は偽になるから。)
Remember that Parse::RecDescent returns undef on failure, so the only safe test for failure is:
Parse::RecDescentは失敗したときにundefを返すということを覚えておいて 欲しい。だから失敗したかどうかの唯一安全なテストは以下のようになる:
defined($parser->file($filetext)) || die;
診断 (DIAGNOSTICS)¶
Diagnostics are intended to be self-explanatory (particularly if you use -RD_HINT (under perl -s) or define $::RD_HINT
inside the program).
診断は自己説明のためにある(特に(perl -sで)-RD_HINTを使っている場合、 あるいはプログラム内で$::RD_HINT
を定義している場合)。
Parse::RecDescent
currently diagnoses the following:
現在、Parse::RecDescent
は以下の診断を報告する:
Invalid regular expressions used as pattern terminals (fatal error).
パターン型の終端記号に不正な正規表現を使った(致命的エラー)
Invalid Perl code in code blocks (fatal error).
コードブロック内に不正なPerlコード(致命的エラー)
Lookahead used in the wrong place or in a nonsensical way (fatal error).
間違った場所か意味の無い方法で先読みを使用した(致命的エラー)
"Obvious" cases of left-recursion (fatal error).
"明らかに"左再帰のケースである(致命的エラー)
Missing or extra components in a
<leftop>
or<rightop>
directive.<leftop>
あるいは<rightop>
ディレクティブにおいて 欠損、ないしは過剰な構成要素Unrecognisable components in the grammar specification (fatal error).
文法仕様において認識できない構成要素があった(致命的なエラー)
"Orphaned" rule components specified before the first rule (fatal error) or after an
<error>
directive (level 3 warning).最初の規則の前(致命的エラー)、あるいは
<error>
ディレクティブ の前(レベル3警告)で"孤立化した"規則の構成物が指定されたMissing rule definitions (this only generates a level 3 warning, since you may be providing them later via
Parse::RecDescent::Extend()
).規則定義の消失(レベル3警告を発生させるだけである。なぜなら後で
Parse::RecDescent::Extend()
を通じて定義を用意するかもしれないから。)Instances where greedy repetition behaviour will almost certainly cause the failure of a production (a level 3 warning - see "ON-GOING ISSUES AND FUTURE DIRECTIONS" below).
貪欲な繰り返しによってほぼ明らかにプロダクションが失敗する例( レベル3警告 -下の "進行中の問題と今後の方向性 (ON-GOING ISSUES AND FUTURE DIRECTIONS)"を参照のこと)
Attempts to define rules named 'Replace' or 'Extend', which cannot be called directly through the parser object because of the predefined meaning of
Parse::RecDescent::Replace
andParse::RecDescent::Extend
. (Only a level 2 warning is generated, since such rules can still be used as subrules).'Replace'あるいは'Extend'と名づけられた規則を定義しようと試みた。
Parse::RecDescent::Replace
とParse::RecDescent::Extend
によって既に 定義されているのでパーサオブジェクトを通じてこれらを直接呼び出すことは できない(レベル2警告が発生するだけである。なぜならこのような規則でも サブ規則としてまだ利用できるから。)Productions which consist of a single
<error?>
directive, and which therefore may succeed unexpectedly (a level 2 warning, since this might conceivably be the desired effect).一つの
<error?>
ディレクティブだけで構成されているプロダクション。 これは予想に反して成功してしまうだろう(レベル2警告。なぜなら考えように よっては望まれる結果かもしれないから)Multiple consecutive lookahead specifiers (a level 1 warning only, since their effects simply accumulate).
複数の連続した先読み指定(レベル1警告。その効果は単純に積み重なるから)
Productions which start with a
<reject>
or<rulevar:...>
directive. Such productions are optimized away (a level 1 warning).<reject>
ないしは<rulevar:...>
ディレクティブではじまる プロダクション。そのようなプロダクションは最適化される(レベル1警告)Rules which are autogenerated under
$::AUTOSTUB
(a level 1 warning).$::AUTOSTUB
で自動生成された規則(レベル1警告)
作者¶
Damian Conway ([email protected])
バグやイライラ (BUGS AND IRRITATIONS)¶
There are undoubtedly serious bugs lurking somewhere in this much code :-) Bug reports and other feedback are most welcome.
間違いなくこの膨大なコードのどこかに重大なバグが潜んでいる(笑) バグレポートその他のフィードバックは大歓迎。
Ongoing annoyances include:
現在進行中の悩ましい問題には以下のものが含まれる:
There's no support for parsing directly from an input stream. If and when the Perl Gods give us regular expressions on streams, this should be trivial (ahem!) to implement.
入力ストリームからダイレクトにパースするのをサポートしていない。 もしPerlの神様が我らにストリーム用正規表現をお与えくださるなら、 そのときには実装なんてへっちゃらさ(えっへん!)。
The parser generator can get confused if actions aren't properly closed or if they contain particularly nasty Perl syntax errors (especially unmatched curly brackets).
アクションが適切に閉じられていない場合、あるいは手に負えない Perlの構文エラー(特に、合致しないブレース)を含んでいる場合、 パーサジェネレータは混乱してしまう。
The generator only detects the most obvious form of left recursion (potential recursion on the first subrule in a rule). More subtle forms of left recursion (for example, through the second item in a rule after a "zero" match of a preceding "zero-or-more" repetition, or after a match of a subrule with an empty production) are not found.
ジェネレータは左再帰の最も明白な形態(規則内の最初のサブ規則に 再帰の可能性がある場合)しか検出しない。左再帰のより微妙な形態( 例えば先行する0回以上の繰り返しの0マッチ後、あるいは空プロダクションを 伴ったサブ規則のマッチ後の規則内の二番目のアイテムを通じて)は 発見されない。
Instead of complaining about left-recursion, the generator should silently transform the grammar to remove it. Don't expect this feature any time soon as it would require a more sophisticated approach to parser generation than is currently used.
左再帰に文句をいう代わりに、ジェネレータは左再帰を取り除くために黙って 文法を変更するのがよい。このような機能が現在使われているものよりも洗練 されたものとして必要だとしても、すぐには期待しないこと。
The generated parsers don't always run as fast as might be wished.
生成されたパーサは、いつも期待通りの速さで実行されるとは限らない。
The meta-parser should be bootstrapped using
Parse::RecDescent
:-)メタ-パーサは
Parse::RecDescent
を利用してブートストラップされるべきだ(笑)
進行中の問題と今後の方向性 (ON-GOING ISSUES AND FUTURE DIRECTIONS)¶
Repetitions are "incorrigibly greedy" in that they will eat everything they can and won't backtrack if that behaviour causes a production to fail needlessly. So, for example:
繰り返しは、可能な限り全てを飲み込むという点で、"どうしようもなく貪欲" である。そして、その振る舞いが不必要にプロダクションを失敗させるならば、 後戻りすることもできないだろう。だから、次の例が:
rule: subrule(s) subrule
will never succeed, because the repetition will eat all the subrules it finds, leaving none to match the second item. Such constructions are relatively rare (and
Parse::RecDescent::new
generates a warning whenever they occur) so this may not be a problem, especially since the insatiable behaviour can be overcome "manually" by writing:成功することは決してない。というのも、繰り返しは見つけたサブ規則を全て 食らうからであり、二番目のアイテムにマッチするようなものは何も残さないからだ。 このような構成は比較的まれである(それに、このようなことが起きると
Parse::RecDescent::new
は警告を発す)。だからこれは問題ではないかも しれない。特に、この貪欲な振る舞いは"手動で"書き直せば克服できるという点で:rule: penultimate_subrule(s) subrule penultimate_subrule: subrule ...subrule
The issue is that this construction is exactly twice as expensive as the original, whereas backtracking would add only 1/N to the cost (for matching N repetitions of
subrule
). I would welcome feedback on the need for backtracking; particularly on cases where the lack of it makes parsing performance problematical.問題は、後戻りによってたかだか1/Nのコスト(
subrule
がN回繰り返される のにマッチするから)が付け加えられるにも関わらず、この構成だと元の かっきり二倍のコストがかかるという点だ。後戻りの必要性に関するフィードバック を待ちたい;特に、これをやらない場合に、パースのパフォーマンスに問題が生じる ケースでは。Having opened that can of worms, it's also necessary to consider whether there is a need for non-greedy repetition specifiers. Again, it's possible (at some cost) to manually provide the required functionality:
パンドラの箱が開かれたついでに、貪欲でない反復指定子が必要であるかないかに ついても考えなければならない。ここでも再び、(ある程度コストはかかるが) 必要とする機能は手動で提供できる:
rule: nongreedy_subrule(s) othersubrule nongreedy_subrule: subrule ...!othersubrule
Overall, the issue is whether the benefit of this extra functionality outweighs the drawbacks of further complicating the (currently minimalist) grammar specification syntax, and (worse) introducing more overhead into the generated parsers.
全体的に、問題はこの追加的な機能がもたらす利益が、(現在最小限に押さえている) 文法仕様の構文をさらに繁雑にしてしまうことと、(より悪いことに)パーサ生成 にいっそうオーバーヘッドをかけてしまう欠点とを、上回るかどうかということだ。
An
<autocommit>
directive would be nice. That is, it would be useful to be able to say:<autocommit>
ディレクティブは良いものになるだろう。便利にも 次のような書き方ができることになる:command: <autocommit> command: 'find' name | 'find' address | 'do' command 'at' time 'if' condition | 'do' command 'at' time | 'do' command | unusual_command
and have the generator work out that this should be "pruned" thus:
そうするとジェネレータはこれを"刈り込んで"くれる。すなわち:
command: 'find' name | 'find' <commit> address | 'do' <commit> command <uncommit> 'at' time 'if' <commit> condition | 'do' <commit> command <uncommit> 'at' <commit> time | 'do' <commit> command | unusual_command
There are several issues here. Firstly, should the
<autocommit>
automatically install an<uncommit>
at the start of the last production (on the grounds that the "command" rule doesn't know whether an "unusual_command" might start with "find" or "do") or should the "unusual_command" subgraph be analysed (to see if it might be viable after a "find" or "do")?いくつかの問題がある。まず、
<autocommit>
は自動的に(規則 "command"は"unusual_command"が"find"か"do"ではじまるべきかどうかを知る ことはできないので)最後のプロダクションの始めに<uncommit>
を 導入するべきだろうか?あるいは、"unusual_command"の部分グラフが ("find"か"do"の後でそれが見出せるかどうか)解析されるべきだろうか?The second issue is how regular expressions should be treated. The simplest approach would be simply to uncommit before them (on the grounds that they might match). Better efficiency would be obtained by analyzing all preceding literal tokens to determine whether the pattern would match them.
二番目の問題は、正規表現をどう扱うかという点だ。最も単純なアプローチは、 正規表現の前で単にuncommitすることだ(それらはマッチするはずだから)。 より効率的なアプローチは、パターンがマッチするかどうかを決定するために、 先行するリテラルトークンを全て解析することよって得られる。
Overall, the issues are: can such automated "pruning" approach a hand-tuned version sufficiently closely to warrant the extra set-up expense, and (more importantly) is the problem important enough to even warrant the non-trivial effort of building an automated solution?
全体的に、問題は:このような自動化された"刈り込み"が、セットアップのための 余分な費用を保証するほど十分な手動チューニングによるアプローチたりうるのか どうかという点である。そして(さらに重要なことに)、この問題は、自動化され る解決方法を構築するための結構な努力を正当化するほど重要なのだろうか?
著作権¶
Copyright (c) 1997-2000, Damian Conway. All Rights Reserved. This module is free software. It may be used, redistributed and/or modified under the terms of the Perl Artistic License (see http://www.perl.com/perl/misc/Artistic.html)