recdescent.pod

来自「视频监控网络部分的协议ddns,的模块的实现代码,请大家大胆指正.」· POD 代码 · 共 1,783 行 · 第 1/5 页

POD
1,783
字号
        { 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 I<any> valid Perl codewithin a parser's special namespace.Start-up actions can appear within a grammar extension or replacement(that is, a partial grammar installed via C<Parse::RecDescent::Extend()> or C<Parse::RecDescent::Replace()> - see L<Incremental Parsing>), and will beexecuted before the new grammar is installed. Note, however, that a particular start-up action is only ever executed once.=head2 AutoactionsIt is sometimes desirable to be able to specify a default action to betaken at the end of every production (for example, in order to easilybuild a parse tree). If the variable C<$::RD_AUTOACTION> is definedwhen C<Parse::RecDescent::new()> is called, the contents of thatvariable are treated as a specification of an action which is to appendedto each production in the corresponding grammar. So, for example, to constructa simple parse tree:    $::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 differentclasses 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 appendedto 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 C<identifier> match produces a C<terminal_node> object, I<not> anC<identifier_node> object.A level 1 warning is issued each time an "autoaction" is added tosome production.=head2 AutotreesA commonly needed autoaction is one that builds a parse-tree. It is moderatelytricky to set up such an action (which must treat terminals differently fromnon-terminals), so Parse::RecDescent simplifies the process by providing theC<E<lt>autotreeE<gt>> directive.If this directive appears at the start of grammar, it causesParse::RecDescent to insert autoactions at the end of any rule exceptthose which already end in an action. The action inserted depends on whetherthe production is an intermediate rule (two or more items), or a terminalof the grammar (i.e. a single pattern or string item).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 nameas the rule itself. This makes it easy to build object-orientedprocessors for the parse-trees that the grammar produces. Note too thatthe last two rules produce special objects with the single attribute'__VALUE__'. This is because they consist solely of a single terminal.This autoaction-ed grammar would then produce a parse tree in a datastructure 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 intothe appropriate class).=head2 AutostubbingNormally, if a subrule appears in some production, but no rule of thatname is ever defined in the grammar, the production which refers to thenon-existent subrule fails immediately. This typically occurs as aresult of misspellings, and is a sufficiently common occurance that awarning is generated for such situations.However, when prototyping a grammar it is sometimes useful to beable 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*/iwhere the possible format of an argument is sufficiently complex thatit is not worth specifying in full until the general function callsyntax has been debugged. In this situation it is convenient to leavethe real rule C<arg> undefined and just slip in a placeholder (or"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 C<Parse::RecDescent> provides a means of automating their definition.If the variable C<$::RD_AUTOSTUB> is defined when a parser is built,a subrule reference to any non-existent rule (say, C<sr>),causes a "stub" rule of the form:        sr: 'sr'to be automatically defined in the generated parser.A level 1 warning is issued for each such "autostubbed" rule.Hence, with C<$::AUTOSTUB> defined, it is possible to only partiallyspecify a grammar, and then "fake" matches of the unspecified(sub)rules by just typing in their name.=head2 Look-aheadIf a subrule, token, or action is prefixed by "...", then it istreated 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 matchingI<does not consume any of the text being parsed>. This is very similar to theC</(?=...)/> look-ahead construct in Perl patterns. Thus, the rule:        inner_word: word ...wordwill match whatever the subrule "word" matches, provided that match is followedby some more text which subrule "word" would also match (although thissecond substring is not actually consumed by "inner_word")Likewise, a "...!" prefix, causes the following item to succeed (withoutconsuming any text) if and only if it would normally fail. Hence, arule such as:        identifier: ...!keyword ...!'_' /[A-Za-z_]\w*/matches a string of characters which satisfies the patternC</[A-Za-z_]\w*/>, but only if the same sequence of characters wouldnot match either subrule "keyword" or the literal token '_'.Sequences of look-ahead prefixes accumulate, multiplying their positive and/ornegative senses. Hence:        inner_word: word ...!......!wordis exactly equivalent the the original example above (a warning is issued incases like these, since they often indicate something left out, ormisunderstood).Note that actions can also be treated as look-aheads. In such cases,the state of the parser text (in the local variable C<$text>)I<after> the look-ahead action is guaranteed to be identical to itsstate I<before> the action, regardless of how it's changed I<within>the action (unless you actually undefine C<$text>, in which case youget the disaster you deserve :-).=head2 DirectivesDirectives are special pre-defined actions which may be used to alterthe behaviour of the parser. There are currently eighteen directives:C<E<lt>commitE<gt>>,C<E<lt>uncommitE<gt>>,C<E<lt>rejectE<gt>>,C<E<lt>scoreE<gt>>,C<E<lt>autoscoreE<gt>>,C<E<lt>skipE<gt>>,C<E<lt>resyncE<gt>>,C<E<lt>errorE<gt>>,C<E<lt>rulevarE<gt>>,C<E<lt>matchruleE<gt>>,C<E<lt>leftopE<gt>>,C<E<lt>rightopE<gt>>,C<E<lt>deferE<gt>>,C<E<lt>nocheckE<gt>>,C<E<lt>perl_quotelikeE<gt>>,C<E<lt>perl_codeblockE<gt>>,C<E<lt>perl_variableE<gt>>,and C<E<lt>tokenE<gt>>.=over 4=item Committing and uncommittingThe C<E<lt>commitE<gt>> and C<E<lt>uncommitE<gt>> directives permit the recursivedescent of the parse tree to be pruned (or "cut") for efficiency.Within a rule, a C<E<lt>commitE<gt>> directive instructs the rule to ignore subsequentproductions if the current production fails. For example:        command: 'find' <commit> filename               | 'open' <commit> filename               | 'move' filename filenameClearly, if the leading token 'find' is matched in the first production but thatproduction fails for some other reason, then the remainingproductions cannot possibly match. The presence of theC<E<lt>commitE<gt>> causes the "command" rule to fail immediately ifan invalid "find" command is found, and likewise if an invalid "open"command is encountered.It is also possible to revoke a previous commitment. For example:        if_statement: 'if' <commit> condition                                'then' block <uncommit>                                'else' block                    | 'if' <commit> condition                                'then' blockIn this case, a failure to find an "else" block in the firstproduction shouldn't preclude trying the second production, but afailure to find a "condition" certainly should.As a special case, any production in which the I<first> item is anC<E<lt>uncommitE<gt>> immediately revokes a preceding C<E<lt>commitE<gt>>(even though the production would not otherwise have been tried). Forexample, in the rule:        request: 'explain' expression               | 'explain' <commit> keyword               | 'save'               | 'quit'               | <uncommit> term '?'if the text being matched was "explain?", and the first twoproductions failed, then the C<E<lt>commitE<gt>> in production two would causeproductions three and four to be skipped, but the leadingC<E<lt>uncommitE<gt>> in the production five would allow that production toattempt a match.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?