📄 parse::recdescent.3
字号:
propagate\fR out of unsuccessful productions, but \fIdo\fR survivesuccessful productions. Hence it is possible to dynamically alter thetext being parsed \- for example, to provide a \f(CW\*(C`#include\*(C'\fR\-like facility:.Sp.Vb 2\& hash_include: \*(Aq#include\*(Aq filename\& { $text = ::loadfile($item[2]) . $text }\&\& filename: \*(Aq<\*(Aq /[a\-z0\-9._\-]+/i \*(Aq>\*(Aq { $return = $item[2] }\& | \*(Aq"\*(Aq /[a\-z0\-9._\-]+/i \*(Aq"\*(Aq { $return = $item[2] }.Ve.ie n .IP "$thisline\fR and \f(CW$prevline" 4.el .IP "\f(CW$thisline\fR and \f(CW$prevline\fR" 4.IX Item "$thisline and $prevline"\&\f(CW$thisline\fR stores the current line number within the current parse(starting from 1). \f(CW$prevline\fR stores the line number for the last character which was already successfully parsed (this will be different from\&\f(CW$thisline\fR at the end of each line)..SpFor efficiency, \f(CW$thisline\fR and \f(CW$prevline\fR are actually tiedhashes, and only recompute the required line number when the variable'svalue is used..SpAssignment to \f(CW$thisline\fR adjusts the line number calculator, so thatit believes that the current line number is the value being assigned. Notethat this adjustment will be reflected in all subsequent line numberscalculations..SpModifying the value of the variable \f(CW$text\fR (as in the previous\&\f(CW\*(C`hash_include\*(C'\fR example, for instance) will confuse the linecounting mechanism. To prevent this, you should call\&\f(CW\*(C`Parse::RecDescent::LineCounter::resync($thisline)\*(C'\fR \fIimmediately\fRafter any assignment to the variable \f(CW$text\fR (or, at least, before thenext attempt to use \f(CW$thisline\fR)..SpNote that if a production fails after assigning to orresync'ing \f(CW$thisline\fR, the parser's line counter mechanism willusually be corrupted..SpAlso see the entry for \f(CW@itempos\fR..SpThe line number can be set to values other than 1, by calling the startrule with a second argument. For example:.Sp.Vb 1\& $parser = new Parse::RecDescent ($grammar);\&\& $parser\->input($text, 10); # START LINE NUMBERS AT 10.Ve.ie n .IP "$thiscolumn\fR and \f(CW$prevcolumn" 4.el .IP "\f(CW$thiscolumn\fR and \f(CW$prevcolumn\fR" 4.IX Item "$thiscolumn and $prevcolumn"\&\f(CW$thiscolumn\fR stores the current column number within the current linebeing parsed (starting from 1). \f(CW$prevcolumn\fR stores the column numberof the last character which was actually successfully parsed. Usually \&\f(CW\*(C`$prevcolumn == $thiscolumn\-1\*(C'\fR, but not at the end of lines..SpFor efficiency, \f(CW$thiscolumn\fR and \f(CW$prevcolumn\fR areactually tied hashes, and only recompute the required column numberwhen the variable's value is used..SpAssignment to \f(CW$thiscolumn\fR or \f(CW$prevcolumn\fR is a fatal error..SpModifying the value of the variable \f(CW$text\fR (as in the previous\&\f(CW\*(C`hash_include\*(C'\fR example, for instance) may confuse the columncounting mechanism..SpNote that \f(CW$thiscolumn\fR reports the column number \fIbefore\fR anywhitespace that might be skipped before reading a token. Henceif you wish to know where a token started (and ended) use something like this:.Sp.Vb 2\& rule: token1 token2 startcol token3 endcol token4\& { print "token3: columns $item[3] to $item[5]"; }\&\& startcol: \*(Aq\*(Aq { $thiscolumn } # NEED THE \*(Aq\*(Aq TO STEP PAST TOKEN SEP\& endcol: { $prevcolumn }.Ve.SpAlso see the entry for \f(CW@itempos\fR..ie n .IP "$thisoffset\fR and \f(CW$prevoffset" 4.el .IP "\f(CW$thisoffset\fR and \f(CW$prevoffset\fR" 4.IX Item "$thisoffset and $prevoffset"\&\f(CW$thisoffset\fR stores the offset of the current parsing positionwithin the complete textbeing parsed (starting from 0). \f(CW$prevoffset\fR stores the offsetof the last character which was actually successfully parsed. In allcases \f(CW\*(C`$prevoffset == $thisoffset\-1\*(C'\fR..SpFor efficiency, \f(CW$thisoffset\fR and \f(CW$prevoffset\fR areactually tied hashes, and only recompute the required offsetwhen the variable's value is used..SpAssignment to \f(CW$thisoffset\fR or <$prevoffset> is a fatal error..SpModifying the value of the variable \f(CW$text\fR will \fInot\fR affect theoffset counting mechanism..SpAlso see the entry for \f(CW@itempos\fR..ie n .IP "@itempos" 4.el .IP "\f(CW@itempos\fR" 4.IX Item "@itempos"The array \f(CW@itempos\fR stores a hash reference corresponding toeach element of \f(CW@item\fR. The elements of the hash provide thefollowing:.Sp.Vb 6\& $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].Ve.SpNote that the various \f(CW\*(C`$itempos[$n]...{from}\*(C'\fR values record theappropriate value \fIafter\fR any token prefix has been skipped..SpHence, instead of the somewhat tedious and error-prone:.Sp.Vb 9\& 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: \*(Aq\*(Aq { $thiscolumn } # NEED THE \*(Aq\*(Aq TO STEP PAST TOKEN SEP\& endcol: { $prevcolumn }.Ve.Spit is possible to write:.Sp.Vb 7\& 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}" }.Ve.SpNote however that (in the current implementation) the use of \f(CW@itempos\fRanywhere in a grammar implies that item positioning information is collected \fIeverywhere\fR during the parse. Depending on the grammarand the size of the text to be parsed, this may be prohibitivelyexpensive and the explicit use of \f(CW$thisline\fR, \f(CW$thiscolumn\fR, etc. maybe a better choice..ie n .IP "$thisparser" 4.el .IP "\f(CW$thisparser\fR" 4.IX Item "$thisparser"A reference to the \f(CW\*(C`Parse::RecDescent\*(C'\fR object through whichparsing was initiated..SpThe value of \f(CW$thisparser\fR propagates down the subrules of a parsebut not back up. Hence, you can invoke subrules from another parserfor the scope of the current rule as follows:.Sp.Vb 4\& rule: subrule1 subrule2\& | { $thisparser = $::otherparser } <reject>\& | subrule3 subrule4\& | subrule5.Ve.SpThe result is that the production calls \*(L"subrule1\*(R" and \*(L"subrule2\*(R" ofthe current parser, and the remaining productions call the named subrulesfrom \f(CW$::otherparser\fR. Note, however that \*(L"Bad Things\*(R" will happen if\&\f(CW\*(C`::otherparser\*(C'\fR isn't a blessed reference and/or doesn't have methodswith the same names as the required subrules!.ie n .IP "$thisrule" 4.el .IP "\f(CW$thisrule\fR" 4.IX Item "$thisrule"A reference to the \f(CW\*(C`Parse::RecDescent::Rule\*(C'\fR object corresponding to the rule currently being matched..ie n .IP "$thisprod" 4.el .IP "\f(CW$thisprod\fR" 4.IX Item "$thisprod"A reference to the \f(CW\*(C`Parse::RecDescent::Production\*(C'\fR objectcorresponding to the production currently being matched..ie n .IP "$score\fR and \f(CW$score_return" 4.el .IP "\f(CW$score\fR and \f(CW$score_return\fR" 4.IX Item "$score and $score_return"\&\f(CW$score\fR stores the best production score to date, as specified byan earlier \f(CW\*(C`<score:...>\*(C'\fR directive. \f(CW$score_return\fR storesthe corresponding return value for the successful production..SpSee \*(L"Scored productions\*(R"..PP\&\fBWarning:\fR the parser relies on the information in the various \f(CW\*(C`this...\*(C'\fRobjects in some non-obvious ways. Tinkering with the other members ofthese objects will probably cause Bad Things to happen, unless you\&\fIreally\fR know what you're doing. The only exception to this advice isthat the use of \f(CW\*(C`$this...\->{local}\*(C'\fR is always safe..Sh "Start-up Actions".IX Subsection "Start-up Actions"Any actions which appear \fIbefore\fR the first rule definition in agrammar are treated as \*(L"start-up\*(R" actions. Each such action isstripped of its outermost brackets and then evaluated (in the parser'sspecial namespace) just before the rules of the grammar are firstcompiled..PPThe main use of start-up actions is to declare local variables within theparser's special namespace:.PP.Vb 1\& { my $lastitem = \*(Aq???\*(Aq; }\&\& list: item(s) { $return = $lastitem }\&\& item: book { $lastitem = \*(Aqbook\*(Aq; }\& bell { $lastitem = \*(Aqbell\*(Aq; }\& candle { $lastitem = \*(Aqcandle\*(Aq; }.Ve.PPbut start-up actions can be used to execute \fIany\fR valid Perl codewithin a parser's special namespace..PPStart-up actions can appear within a grammar extension or replacement(that is, a partial grammar installed via \f(CW\*(C`Parse::RecDescent::Extend()\*(C'\fR or \&\f(CW\*(C`Parse::RecDescent::Replace()\*(C'\fR \- see \*(L"Incremental Parsing\*(R"), and will beexecuted before the new grammar is installed. Note, however, that a particular start-up action is only ever executed once..Sh "Autoactions".IX Subsection "Autoactions"It 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 \f(CW$::RD_AUTOACTION\fR is definedwhen \f(CW\*(C`Parse::RecDescent::new()\*(C'\fR 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:.PP.Vb 1\& $::RD_AUTOACTION = q { [@item] };\&\& parser = new Parse::RecDescent (q{\& expression: and_expr \*(Aq||\*(Aq expression | and_expr\& and_expr: not_expr \*(Aq&&\*(Aq and_expr | not_expr\& not_expr: \*(Aq!\*(Aq brack_expr | brack_expr\& brack_expr: \*(Aq(\*(Aq expression \*(Aq)\*(Aq | identifier\& identifier: /[a\-z]+/i\& });.Ve.PPwhich is equivalent to:.PP.Vb 5\& parser = new Parse::RecDescent (q{\& expression: and_expr \*(Aq||\*(Aq expression\& { [@item] }\& | and_expr\& { [@item] }\&\& and_expr: not_expr \*(Aq&&\*(Aq and_expr \& { [@item] }\& | not_expr\& { [@item] }\&\& not_expr: \*(Aq!\*(Aq brack_expr \& { [@item] }\& | brack_expr\& { [@item] }\&\& brack_expr: \*(Aq(\*(Aq expression \*(Aq)\*(Aq \& { [@item] }\& | identifier\& { [@item] }\&\& identifier: /[a\-z]+/i\& { [@item] }\& });.Ve.PPAlternatively, we could take an object-oriented approach, use differentclasses for each node (and also eliminating redundant intermediate nodes):.PP.Vb 2\& $::RD_AUTOACTION = q\& { $#item==1 ? $item[1] : new ${"$item[0]_node"} (@item[1..$#item]) };\&\& parser = new Parse::RecDescent (q{\& expression: and_expr \*(Aq||\*(Aq expression | and_expr\& and_expr: not_expr \*(Aq&&\*(Aq and_expr | not_expr\& not_expr: \*(Aq!\*(Aq brack_expr | brack_expr\& brack_expr: \*(Aq(\*(Aq expression \*(Aq)\*(Aq | identifier\& identifier: /[a\-z]+/i\& });.Ve.PPwhich is equivalent to:.PP.Vb 4\& parser = new Parse::RecDescent (q{\& expression: and_expr \*(Aq||\*(Aq expression\& { new expression_node (@item[1..3]) }\& | and_expr\&\& and_expr: not_expr \*(Aq&&\*(Aq and_expr \& { new and_expr_node (@item[1..3]) }\& | not_expr\&\& not_expr: \*(Aq!\*(Aq brack_expr \& { new not_expr_node (@item[1..2]) }\& | brack_expr\&\& brack_expr: \*(Aq(\*(Aq expression \*(Aq)\*(Aq \& { new brack_expr_node (@item[1..3]) }\& | identifier\&\& identifier: /[a\-z]+/i\& { new identifer_node (@item[1]) }\& });.Ve.PPNote that, if a production already ends in an action, no autoaction is appendedto it. For example, in this version:.PP.Vb 2\& $::RD_AUTOACTION = q\& { $#item==1 ? $item[1] : new ${"$item[0]_node"} (@item[1..$#item]) };\&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -