| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | ##-*- Mode: CPerl -*- | 
| 2 |  |  |  |  |  |  | ## | 
| 3 |  |  |  |  |  |  | ## File: DDC::PP::CQueryCompiler.pm | 
| 4 |  |  |  |  |  |  | ## Author: Bryan Jurish | 
| 5 |  |  |  |  |  |  | ## Description: pure-perl DDC query parser, top-level | 
| 6 |  |  |  |  |  |  | ##====================================================================== | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | package DDC::PP::CQueryCompiler; | 
| 9 | 20 |  |  | 20 |  | 145 | use DDC::Utils qw(:escape); | 
|  | 20 |  |  |  |  | 48 |  | 
|  | 20 |  |  |  |  | 3079 |  | 
| 10 | 20 |  |  | 20 |  | 198 | use DDC::PP::Constants; | 
|  | 20 |  |  |  |  | 45 |  | 
|  | 20 |  |  |  |  | 446 |  | 
| 11 | 20 |  |  | 20 |  | 119 | use DDC::PP::Object; | 
|  | 20 |  |  |  |  | 56 |  | 
|  | 20 |  |  |  |  | 479 |  | 
| 12 | 20 |  |  | 20 |  | 120 | use DDC::PP::CQuery; | 
|  | 20 |  |  |  |  | 40 |  | 
|  | 20 |  |  |  |  | 438 |  | 
| 13 | 20 |  |  | 20 |  | 111 | use DDC::PP::CQCount; | 
|  | 20 |  |  |  |  | 49 |  | 
|  | 20 |  |  |  |  | 487 |  | 
| 14 | 20 |  |  | 20 |  | 133 | use DDC::PP::CQFilter; | 
|  | 20 |  |  |  |  | 42 |  | 
|  | 20 |  |  |  |  | 520 |  | 
| 15 | 20 |  |  | 20 |  | 113 | use DDC::PP::CQueryOptions; | 
|  | 20 |  |  |  |  | 45 |  | 
|  | 20 |  |  |  |  | 578 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 | 20 |  |  | 20 |  | 10078 | use DDC::PP::yyqlexer; | 
|  | 20 |  |  |  |  | 55 |  | 
|  | 20 |  |  |  |  | 806 |  | 
| 18 | 20 |  |  | 20 |  | 15764 | use DDC::PP::yyqparser; | 
|  | 20 |  |  |  |  | 83 |  | 
|  | 20 |  |  |  |  | 764 |  | 
| 19 |  |  |  |  |  |  |  | 
| 20 | 20 |  |  | 20 |  | 151 | use strict; | 
|  | 20 |  |  |  |  | 51 |  | 
|  | 20 |  |  |  |  | 29553 |  | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | ##====================================================================== | 
| 23 |  |  |  |  |  |  | ## Globals etc. | 
| 24 |  |  |  |  |  |  | our @ISA = qw(DDC::PP::Object); | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | ##====================================================================== | 
| 28 |  |  |  |  |  |  | ## $qc = $CLASS_OR_OBJ->new(%args) | 
| 29 |  |  |  |  |  |  | ## + abstract constructor | 
| 30 |  |  |  |  |  |  | ## + object structure, %args: | 
| 31 |  |  |  |  |  |  | ##   { | 
| 32 |  |  |  |  |  |  | ##    ##-- DDC::XS::CQueryCompiler emulation | 
| 33 |  |  |  |  |  |  | ##    Query => $query,		##-- last query parsed | 
| 34 |  |  |  |  |  |  | ## | 
| 35 |  |  |  |  |  |  | ##    ##-- guts: status flags | 
| 36 |  |  |  |  |  |  | ##    error => $current_errstr, ##-- false indicates no error | 
| 37 |  |  |  |  |  |  | ## | 
| 38 |  |  |  |  |  |  | ##    ##-- guts: underlying lexer/parser pair | 
| 39 |  |  |  |  |  |  | ##    lexer  => $yylexer,   ##-- a DDC::PP::yyqlexer object | 
| 40 |  |  |  |  |  |  | ##    parser => $yyparser,  ##-- a DDC::PP::yyqparser object | 
| 41 |  |  |  |  |  |  | ##    yydebug => $mask,     ##-- yydebug value | 
| 42 |  |  |  |  |  |  | ## | 
| 43 |  |  |  |  |  |  | ##    ##-- guts: closures | 
| 44 |  |  |  |  |  |  | ##    yylex    => \&yylex,   ##-- yapp-friendly lexer sub | 
| 45 |  |  |  |  |  |  | ##    yyerror  => \&yyerror, ##-- yapp-friendly parser sub | 
| 46 |  |  |  |  |  |  | ##   } | 
| 47 |  |  |  |  |  |  | sub new { | 
| 48 | 15 |  |  | 15 | 0 | 2207 | my $that = shift; | 
| 49 | 15 |  | 33 |  |  | 249 | my $qc = bless({ | 
| 50 |  |  |  |  |  |  | ##-- DDC::XS emulation | 
| 51 |  |  |  |  |  |  | Query => undef, | 
| 52 |  |  |  |  |  |  | KeepLexerComments => 0, | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | ##-- guts: status flags | 
| 55 |  |  |  |  |  |  | error => undef, | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | ##-- guts: underlying lexer/parser pair | 
| 58 |  |  |  |  |  |  | lexer  => DDC::PP::yyqlexer->new(), | 
| 59 |  |  |  |  |  |  | parser => DDC::PP::yyqparser->new(), | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | ##-- guts: runtime data | 
| 62 |  |  |  |  |  |  | qopts => undef, | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | ##-- parser debugging | 
| 65 |  |  |  |  |  |  | yydebug  => 0, # no debug | 
| 66 |  |  |  |  |  |  | #yydebug => 0x01,  # lexer debug | 
| 67 |  |  |  |  |  |  | #yydebug => 0x02,  # state info | 
| 68 |  |  |  |  |  |  | #yydebug => 0x04,  # driver actions (shift/reduce/etc.) | 
| 69 |  |  |  |  |  |  | #yydebug => 0x08,  # stack dump | 
| 70 |  |  |  |  |  |  | #yydebug => 0x10,  # Error recovery trace | 
| 71 |  |  |  |  |  |  | #yydebug => 0x01 | 0x02 | 0x04 | 0x08, # almost everything | 
| 72 |  |  |  |  |  |  | #yydebug => 0xffffffff, ##-- pretty much everything | 
| 73 |  |  |  |  |  |  |  | 
| 74 |  |  |  |  |  |  | ##-- User args | 
| 75 |  |  |  |  |  |  | @_ | 
| 76 |  |  |  |  |  |  | }, | 
| 77 |  |  |  |  |  |  | ref($that)||$that); | 
| 78 | 15 |  |  |  |  | 95 | $qc->getClosures(); | 
| 79 | 15 |  |  |  |  | 68 | return $qc; | 
| 80 |  |  |  |  |  |  | } | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | ## undef = $qc->free() | 
| 83 |  |  |  |  |  |  | ##  + clears $qc itself, as well as $qc->{parser}{USER} | 
| 84 |  |  |  |  |  |  | ##  + makes $qc subsequently useless, but destroyable | 
| 85 |  |  |  |  |  |  | sub free { | 
| 86 | 0 |  |  | 0 | 0 | 0 | my $qc = shift; | 
| 87 | 0 | 0 |  |  |  | 0 | delete($qc->{parser}{USER}) if ($qc->{parser}); | 
| 88 | 0 |  |  |  |  | 0 | %$qc = qw(); | 
| 89 |  |  |  |  |  |  | } | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | ## $qc = $qc->getClosures() | 
| 92 |  |  |  |  |  |  | ##  + compiles lexer & parser closures | 
| 93 |  |  |  |  |  |  | sub getClosures { | 
| 94 | 15 |  |  | 15 | 0 | 38 | my $qc = shift; | 
| 95 | 15 |  |  |  |  | 163 | delete(@$qc{qw(yylex yyerror)}); | 
| 96 | 15 |  |  |  |  | 77 | $qc->{yylex}   = $qc->_yylex_sub(); | 
| 97 | 15 |  |  |  |  | 80 | $qc->{yyerror} = $qc->_yyerror_sub(); | 
| 98 | 15 |  |  |  |  | 55 | return $qc; | 
| 99 |  |  |  |  |  |  | } | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | ##====================================================================== | 
| 102 |  |  |  |  |  |  | ## DDC::XS emulation | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | __PACKAGE__->defprop('Query'); | 
| 105 |  |  |  |  |  |  | __PACKAGE__->defprop('KeepLexerComments'); | 
| 106 |  |  |  |  |  |  |  | 
| 107 |  |  |  |  |  |  | ## undef = $qc->CleanParser() | 
| 108 |  |  |  |  |  |  | ##  + reset all parse-relevant data structures | 
| 109 | 0 |  |  | 0 | 0 | 0 | sub CleanParser { $_[0]->reset; } | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | ## $CQuery = $qc->ParseQuery($qstr) | 
| 112 |  |  |  |  |  |  | sub ParseQuery { | 
| 113 | 199 |  |  | 199 | 0 | 2673 | my ($qc,$qstr) = @_; | 
| 114 | 199 |  |  |  |  | 324 | $qc->{Query} = eval { $qc->parse(string=>\$qstr); }; | 
|  | 199 |  |  |  |  | 507 |  | 
| 115 | 199 | 100 |  |  |  | 450 | die(__PACKAGE__."::ParseQuery() failed: could not parse query: $@") if ($@); | 
| 116 | 197 |  |  |  |  | 707 | return $qc->{Query}; | 
| 117 |  |  |  |  |  |  | } | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | ## $s = $qc->QueryToString() | 
| 120 | 2 |  |  | 2 | 0 | 11 | sub QueryToString { return $_[0]->getQuery->toStringFull(); } | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | ## $s = $qc->QueryToJson() | 
| 123 |  |  |  |  |  |  | sub QueryToJson { | 
| 124 | 2 |  |  | 2 | 0 | 12 | return "{\"Query\":".$_[0]->getQuery->toJson().",\"Options\":".$_[0]->getQuery->getOptions->toJson()."}"; | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | ##====================================================================== | 
| 129 |  |  |  |  |  |  | ## Local API: Input selection | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | ## undef = $qc->reset() | 
| 132 |  |  |  |  |  |  | ##  + reset all parse-relevant data structures | 
| 133 |  |  |  |  |  |  | sub reset { | 
| 134 | 199 |  |  | 199 | 0 | 309 | my $qc = shift; | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | ##-- runtime data | 
| 137 | 199 |  |  |  |  | 1086 | delete(@$qc{qw(Query qopts)}); | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | ##-- lexer & parser state | 
| 140 | 199 |  |  |  |  | 841 | $qc->{lexer}->reset(); | 
| 141 |  |  |  |  |  |  |  | 
| 142 | 199 |  |  |  |  | 457 | delete($qc->{parser}{USER}{hint}); | 
| 143 | 199 |  |  |  |  | 417 | $qc->{parser}{USER}{qc}      = $qc; | 
| 144 | 199 |  |  |  |  | 423 | $qc->{parser}{USER}{lex}     = $qc->{lexer}; | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | } | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | ## $qc = $qc->from($which,$src, %opts) | 
| 149 |  |  |  |  |  |  | ##  + wraps $qc->{lexer}->from() | 
| 150 |  |  |  |  |  |  | ##  + $which is one of qw(fh file string) | 
| 151 |  |  |  |  |  |  | ##  + $src is the actual source (default: 'string') | 
| 152 |  |  |  |  |  |  | ##  + %opts may contain (src=>$name) | 
| 153 |  |  |  |  |  |  | sub from { | 
| 154 | 199 | 50 |  | 199 | 0 | 710 | return $_[0]{lexer}->from(@_[1..$#_]) ? $_[0] : undef; | 
| 155 |  |  |  |  |  |  | } | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | ## $qc = $qc->fromFile($filename_or_handle,%opts) | 
| 158 |  |  |  |  |  |  | ##  + wraps $qc->{lexer}->fromFile() | 
| 159 |  |  |  |  |  |  | sub fromFile { | 
| 160 | 0 | 0 |  | 0 | 0 | 0 | return $_[0]{lexer}->fromFile(@_[1..$#_]) ? $_[0] : undef; | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | ## $qc = $qc->fromFh($fh,%opts) | 
| 164 |  |  |  |  |  |  | ##  + wraps $qc->{lexer}->fromFh() | 
| 165 |  |  |  |  |  |  | sub fromFh { | 
| 166 | 0 | 0 |  | 0 | 0 | 0 | return $_[0]{lexer}->fromFh(@_[1..$#_]) ? $_[0] : undef; | 
| 167 |  |  |  |  |  |  | } | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | ## $qc = $qc->fromString($str,%opts) | 
| 170 |  |  |  |  |  |  | ## $qc = $qc->fromString(\$str,%opts) | 
| 171 |  |  |  |  |  |  | ##  + wraps $qc->{lexer}->fromString() | 
| 172 |  |  |  |  |  |  | sub fromString { | 
| 173 | 0 | 0 |  | 0 | 0 | 0 | return $_[0]{lexer}->fromString(@_[1..$#_]) ? $_[0] : undef; | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | ##====================================================================== | 
| 178 |  |  |  |  |  |  | ## Local API: High-level Parsing | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | ## $query_or_undef = $qc->parse(string=>$str) | 
| 181 |  |  |  |  |  |  | ## $query_or_undef = $qc->parse(string=>\$str) | 
| 182 |  |  |  |  |  |  | ## $query_or_undef = $qc->parse(file=>$filename) | 
| 183 |  |  |  |  |  |  | ## $query_or_undef = $qc->parse(fh=>$handle) | 
| 184 |  |  |  |  |  |  | sub parse { | 
| 185 | 199 |  |  | 199 | 0 | 327 | my $qc = shift; | 
| 186 | 199 |  |  |  |  | 571 | $qc->reset(); | 
| 187 | 199 |  |  |  |  | 568 | $qc->from(@_); | 
| 188 | 199 |  |  |  |  | 364 | my $result = eval { $qc->yyparse(); }; | 
|  | 199 |  |  |  |  | 470 |  | 
| 189 | 199 |  |  |  |  | 13043 | my $err    = $@; | 
| 190 | 199 |  |  |  |  | 423 | delete($qc->{parser}{qc});       ##-- chop circular reference we know how to get at... | 
| 191 | 199 |  |  |  |  | 391 | delete($qc->{parser}{USER}{qc}); ##-- chop circular reference we know how to get at... | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | ##-- adopt lexer comments | 
| 194 |  |  |  |  |  |  | $result->{Options}{LexerComments} = $qc->{lexer}{comments} | 
| 195 | 199 | 50 | 66 |  |  | 640 | if ($qc->{KeepLexerComments} && $result && $result->{Options}); | 
|  |  |  | 66 |  |  |  |  | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | ##-- how'd it go? | 
| 198 | 199 | 100 |  |  |  | 439 | die($err) if ($err); | 
| 199 | 197 |  |  |  |  | 484 | return $result; | 
| 200 |  |  |  |  |  |  | } | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | ## $query_or_undef = $qc->yyparse() | 
| 203 |  |  |  |  |  |  | ##  + parses from currently selected input source; no reset or error catching | 
| 204 |  |  |  |  |  |  | sub yyparse { | 
| 205 | 199 |  |  | 199 | 0 | 328 | my $qc = shift; | 
| 206 |  |  |  |  |  |  | return $qc->{parser}->YYParse( | 
| 207 |  |  |  |  |  |  | yylex   => $qc->{yylex}, | 
| 208 |  |  |  |  |  |  | yyerror => $qc->{yyerror}, | 
| 209 |  |  |  |  |  |  | yydebug => $qc->{yydebug}, | 
| 210 | 199 |  |  |  |  | 814 | ); | 
| 211 |  |  |  |  |  |  | } | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | ##====================================================================== | 
| 214 |  |  |  |  |  |  | ## Local API: Mid-level: Query Generation | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | ## $q = $qc->newq($class,@args) | 
| 217 |  |  |  |  |  |  | ##  + wrapper for "DDC::PP::$class"->new(@args); called by yapp parser | 
| 218 |  |  |  |  |  |  | sub newq { | 
| 219 | 371 |  |  | 371 | 0 | 2107 | return "DDC::PP::$_[1]"->new(@_[2..$#_]); | 
| 220 |  |  |  |  |  |  | } | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | ## $qf = $qc->newf(@args) | 
| 223 |  |  |  |  |  |  | ##  + wrapper for DDC::Query::Filter->new(@args); called by yapp parser | 
| 224 |  |  |  |  |  |  | sub newf { | 
| 225 | 16 |  |  | 16 | 0 | 132 | return "DDC::PP::$_[1]"->new(@_[2..$#_]); | 
| 226 |  |  |  |  |  |  | } | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | ## $re = $qc->newre($re,$modifiers) | 
| 229 |  |  |  |  |  |  | sub newre { | 
| 230 | 11 |  |  | 11 | 0 | 30 | my ($qc,$re,$mods) = @_; | 
| 231 | 11 | 50 | 50 |  |  | 54 | if (($mods||'') =~ /g/) { | 
| 232 | 0 |  |  |  |  | 0 | $re   = "^(?:${re})\$"; | 
| 233 | 0 |  |  |  |  | 0 | $mods =~ s/g//g; | 
| 234 |  |  |  |  |  |  | } | 
| 235 | 11 | 50 |  |  |  | 43 | return $re if (!$mods); | 
| 236 | 0 |  |  |  |  | 0 | return "(?:${mods})$re"; | 
| 237 |  |  |  |  |  |  | } | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | ## $qo = $qc->qopts() | 
| 240 |  |  |  |  |  |  | ## $qo = $qc->qopts($opts) | 
| 241 |  |  |  |  |  |  | ##  + get/set current query options | 
| 242 |  |  |  |  |  |  | sub qopts { | 
| 243 | 534 | 100 |  | 534 | 0 | 1474 | $_[0]{qopts} = $_[1] if ($_[1]); | 
| 244 | 534 | 100 |  |  |  | 1592 | $_[0]{qopts} = DDC::PP::CQueryOptions->new if (!defined($_[0]{qopts})); | 
| 245 | 534 |  |  |  |  | 1796 | return $_[0]{qopts}; | 
| 246 |  |  |  |  |  |  | } | 
| 247 |  |  |  |  |  |  |  | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | ##====================================================================== | 
| 250 |  |  |  |  |  |  | ## API: Low-LEVEL: Parse::Lex <-> Parse::Yapp interface | 
| 251 |  |  |  |  |  |  | ## | 
| 252 |  |  |  |  |  |  | ## - REQUIREMENTS on yylex() sub: | 
| 253 |  |  |  |  |  |  | ##   + Yapp-compatible lexing routine | 
| 254 |  |  |  |  |  |  | ##   + reads input and returns token values to the parser | 
| 255 |  |  |  |  |  |  | ##   + our only argument ($MyParser) is the YYParser object itself | 
| 256 |  |  |  |  |  |  | ##   + We return a list ($TOKENTYPE, $TOKENVAL) of the next tokens to the parser | 
| 257 |  |  |  |  |  |  | ##   + on end-of-input, we should return the list ('', undef) | 
| 258 |  |  |  |  |  |  | ## | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | ## \&yylex_sub = $qc->_yylex_sub() | 
| 261 |  |  |  |  |  |  | ##   + returns a Parse::Yapp-friendly lexer subroutine | 
| 262 |  |  |  |  |  |  | sub _yylex_sub { | 
| 263 | 15 |  |  | 15 |  | 37 | my $qc = shift; | 
| 264 | 15 |  |  |  |  | 36 | my ($type,$text,@expect); | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | return sub { | 
| 267 | 1057 |  |  | 1057 |  | 34709 | $qc->{yyexpect} = [$qc->{parser}->YYExpect]; | 
| 268 | 1057 |  |  |  |  | 12587 | ($type,$text) = $qc->{lexer}->yylex(); | 
| 269 | 1057 | 100 |  |  |  | 3031 | return ('',undef) if ($type eq '__EOF__'); | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | ##-- un-escape single-quoted symbols (this happens in the parser) | 
| 272 |  |  |  |  |  |  | #    if ($type =~ /^SQ_(.*)$/) { | 
| 273 |  |  |  |  |  |  | #      $type = $1; | 
| 274 |  |  |  |  |  |  | #      $text = unescapeq($text); | 
| 275 |  |  |  |  |  |  | #    } | 
| 276 |  |  |  |  |  |  | #    elsif ($type eq 'SYMBOL') { | 
| 277 |  |  |  |  |  |  | #      $text = unescape($text); | 
| 278 |  |  |  |  |  |  | #    } | 
| 279 |  |  |  |  |  |  |  | 
| 280 | 860 | 50 |  |  |  | 1969 | if ($qc->{yydebug} & 0x01) { | 
| 281 | 0 | 0 |  |  |  | 0 | print STDERR ": yylex(): type=($type) ; text=(".(defined($text) ? $text : '-undef-')." ; state=(".($qc->{lexer}{state}).")\n"; | 
| 282 |  |  |  |  |  |  | } | 
| 283 |  |  |  |  |  |  |  | 
| 284 | 860 |  |  |  |  | 2487 | return ($type,$text); | 
| 285 | 15 |  |  |  |  | 131 | }; | 
| 286 |  |  |  |  |  |  | } | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  |  | 
| 289 |  |  |  |  |  |  | ## \&yyerror_sub = $qc->_yyerror_sub() | 
| 290 |  |  |  |  |  |  | ##  + returns error subroutine for the underlying Yapp parser | 
| 291 |  |  |  |  |  |  | sub _yyerror_sub { | 
| 292 | 15 |  |  | 15 |  | 39 | my $qc = shift; | 
| 293 | 15 |  |  |  |  | 38 | my (%expect,@expecting); | 
| 294 |  |  |  |  |  |  | return sub { | 
| 295 | 2 | 50 |  | 2 |  | 65 | @expect{@{$qc->{yyexpect}||[]}}=qw(); | 
|  | 2 |  |  |  |  | 11 |  | 
| 296 | 2 | 50 |  |  |  | 4 | @expect{@{$qc->{yyexpect}||[]}, $qc->{parser}->YYExpect}=qw(); | 
|  | 2 |  |  |  |  | 14 |  | 
| 297 | 2 | 100 |  |  |  | 32 | @expecting = sort map {$_ eq '' ? '$end' : $_} keys %expect; | 
|  | 4 |  |  |  |  | 21 |  | 
| 298 |  |  |  |  |  |  | die("syntax error, unexpected ".$qc->{lexer}->yytype | 
| 299 |  |  |  |  |  |  | .", expecting ".join(' or ', @expecting) | 
| 300 |  |  |  |  |  |  | ." at line ".$qc->{lexer}->yylineno | 
| 301 | 2 |  |  |  |  | 13 | .", near token \`".$qc->{lexer}->yytext."'"); | 
| 302 |  |  |  |  |  |  | #    $qc->{error} = ("syntax error in ".$qc->{lexer}->yywhere().":\n" | 
| 303 |  |  |  |  |  |  | #		    #." > Expected one of (here): ".join(', ', map {$_ eq '' ? '__EOF__' : $_} $qc->{parser}->YYExpect)."\n" | 
| 304 |  |  |  |  |  |  | #		    #." > Expected one of (prev): ".join(', ', map {$_ eq '' ? '__EOF__' : $_} @{$qc->{yyexpect}||['???']})."\n" | 
| 305 |  |  |  |  |  |  | #		    ." > Expected one of: ".join(', ', sort map {$_ eq '' ? '__EOF__' : $_} keys %expect)."\n" | 
| 306 |  |  |  |  |  |  | #		    ." > Got: ".$qc->{lexer}->yytype.' "'.$qc->{lexer}->yytext."\"\n" | 
| 307 |  |  |  |  |  |  | #               ); | 
| 308 | 15 |  |  |  |  | 79 | }; | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | 1; ##-- be happy | 
| 313 |  |  |  |  |  |  |  | 
| 314 |  |  |  |  |  |  | __END__ |