File Coverage

blib/lib/Parse/Token.pm
Criterion Covered Total %
statement 157 432 36.3
branch 33 182 18.1
condition 7 33 21.2
subroutine 36 59 61.0
pod 13 25 52.0
total 246 731 33.6


line stmt bran cond sub pod time code
1             require 5.004;
2 10     10   56 use strict qw(vars);
  10         17  
  10         339  
3 10     10   47 use strict qw(refs);
  10         16  
  10         290  
4 10     10   50 use strict qw(subs);
  10         18  
  10         490  
5            
6             package Parse::Token; # or perhaps: Parse::AToken
7             $Parse::Token::VERSION = '2.21';
8 10     10   47 use Parse::Trace;
  10         18  
  10         388  
9             @Parse::Token::ISA = qw(Parse::Trace);
10            
11 10     10   50 use vars qw($AUTOLOAD $trace $PENDING_TOKEN $EOI);
  10         19  
  10         6165  
12             $trace = 0;
13            
14             # other possibilities: pseudo-hash, constants (see The Perl Journal Spring 99)
15             my %_map;
16             my @attributes = qw(STATUS TEXT NAME CONDITION
17             REGEXP SUB DECORATION LEXER HANDLER READ_MORE_RE EXPRESSION
18             TEMPLATE TRACE IN_PKG);
19             my($STATUS, $TEXT, $NAME, $CONDITION,
20             $REGEXP, $ACTION, $DECORATION, $LEXER, $HANDLER, $READ_MORE_RE, $EXPRESSION,
21             $CODE, $TRACE, $IN_PKG) = @_map{@attributes} = (0..$#attributes);
22             sub _map {
23 0     0   0 shift;
24 0 0       0 if (@_) {
25 0 0       0 wantarray ? @_map{@_} : $_map{$_[0]}
26             } else {
27 0         0 @attributes;
28             }
29             }
30            
31             $EOI = Parse::Token->new('EOI');
32            
33             # new()
34             # Purpose: token constructor
35             # Arguments: see definition
36             # Returns: Return a token object
37             sub new {
38 24     24 1 55 my $receiver = shift;
39 24   33     166 my $class = (ref $receiver or $receiver);
40 24         71 my $self = bless [], $class;
41            
42 24         42 my $debug = 0;
43 24 50       75 print STDERR "@_\n" if $debug;
44 24 50 33     96 print STDERR "@{$_[1]}\n" if $debug and ref $_[1];
  0         0  
45            
46             # initialize...
47 24         216 $self->[$STATUS] = 0; # object status
48 24         53 $self->[$TEXT] = ''; # recognized text
49             (
50 24         127 $self->[$CONDITION], # associated conditions
51             $self->[$NAME], # symbolic name
52             $self->[$REGEXP],
53             $self->[$ACTION],
54             $self->[$EXPRESSION],
55             $self->[$READ_MORE_RE],
56             $self->[$HANDLER],
57             $self->[$LEXER],
58             ) = (
59             $self->_parseName($_[0]), # condition + name
60             $_[1], # regexp, can be an array reference
61             $_[2], # associated sub
62             $_[3], # expression (Token::Action)
63             $_[4], # read more data if $_[4] =~ $LEX_BUFFER
64             $_[5], # name of the event handler
65             $_[6], # lexer instance
66             );
67 24         75 $self->[$IN_PKG] = ''; # defined in this package
68 24         53 $self->[$DECORATION] = {}; # token decoration
69 24         55 $self->[$CODE] = ''; # generated code
70 24         36 $self->[$TRACE] = $trace; # trace
71 24         66 $self;
72             }
73             # Purpose: export a token object in the caller's package or
74             # in the package returned by inpkg()
75             # Arguments:
76             # Returns: the token object
77             sub exportTo {
78 4     4 0 7 my $self = shift;
79 4         13 my $inpkg = $self->inpkg;
80 4 50       11 unless (defined $inpkg) {
81 0         0 $inpkg = caller; # (caller(0))[0];
82 0         0 $self->inpkg($inpkg);
83             }
84 4         24 my $name = $self->name;
85 10     10   60 no strict 'refs';
  10         19  
  10         29640  
86 4 50 33     21 if ($^W and defined ${"$inpkg" . "::" . "$name"}) {
  0         0  
87 0         0 require Carp;
88 0         0 Carp::carp "the '${inpkg}::$name' token is already defined";
89             }
90 4         4 ${"$inpkg" . "::" . "$name"} = $self;
  4         20  
91 4         15 $self;
92             }
93            
94             # Purpose: create a list of token objects
95             # Arguments: a list of token specification or token objects
96             # Returns: list of token objects
97             sub factory {
98 3     3 1 10 my $self = shift;
99            
100 3 50       9 unless (defined($_[0])) {
101 0         0 require Carp;
102 0         0 Carp::croak "argument of the factory() method must be a list of token specifications";
103             }
104 3         4 my $sub;
105             my $ref;
106 0         0 my @token; # returned list of tokens
107 0         0 my $token;
108 0         0 my $arg;
109 3         6 my $token_class = '';
110 3         5 my $debug = 0;
111 3 50       7 if (ref $_[0] eq 'ARRAY') { # [Type => Segmented, Name => Xxxx, Etc.]
112 0         0 my @args = @{$_[0]};
  0         0  
113 0         0 my @specif = ();
114 0         0 my $next_type = '';
115 0         0 while (@args) {
116 0         0 $arg = shift @args;
117 0 0 0     0 if (ref $arg and $arg->isa(__PACKAGE__)) {
    0          
118 0         0 push @token, $arg;
119             } elsif ($arg =~ /^[Tt]ype$/) {
120 0 0       0 if (@specif) {
121 0         0 $token_class = $next_type;
122 0 0       0 unless ($token_class->isa(__PACKAGE__)) {
123 0         0 eval { require $token_class };
  0         0  
124 0 0       0 if ($@) {
125 0         0 require Carp;
126 0         0 Carp::croak "$@"; # "unable to access to the $token_class class";
127             }
128             }
129 0 0       0 print STDERR "$token_class - @specif\n" if $debug;
130 0         0 push @token, $token_class->new(@specif);
131 0         0 @specif = ();
132             }
133 0         0 $next_type = __PACKAGE__ . '::' . shift(@args);
134             } else {
135 0         0 push @specif, $arg;
136             }
137             }
138 0 0       0 if (@specif) {
139 0         0 $token_class = $next_type; # todo: load if not defined;
140 0 0       0 print STDERR "$token_class - @specif\n" if $debug;
141 0         0 push @token, $token_class->new(@specif);
142 0         0 @specif = ();
143             }
144             } else {
145 3         10 while (@_) {
146 4         4 $arg = shift;
147             # it's already an instance
148 4 50 33     16 if (ref $arg and $arg->isa(__PACKAGE__)) { # isa()
149 0         0 push @token, $arg;
150             } else { # parse the specification
151 4         8 my($name, $regexp) = ($arg, shift);
152 4 100       8 if (@_) {
153 1         2 $ref = ref($_[0]);
154 1 50 33     5 if ($ref and $ref eq 'CODE') { # if next arg is a sub reference
155 0         0 $sub = shift;
156             } else {
157 1         1 $sub = undef;
158             }
159             } else {
160 3         5 $sub = undef;
161             }
162 4 50       10 unless (ref($regexp) eq 'ARRAY') {
163 4         6 $token_class = __PACKAGE__ . '::Simple';
164             } else {
165 0         0 $token_class = __PACKAGE__ . '::Segmented';
166             }
167 4         19 push @token, $token_class->new(Name => $name, Regex => $regexp, Sub => $sub);
168             }
169             }
170             }
171 3         19 @token;
172             }
173             sub _parseName {
174 24     24   41 my $self = shift;
175 24         40 my $name = shift;
176 24         38 my $condition = '';
177 24 50       101 if ($name =~ /^(.+:)(.+)/) { # Ex. A:B:C:SYMBOL, A,C:SYMBOL
178 0         0 ($condition, $name) = ($1, $2);
179             }
180 24         293 ($condition, $name);
181             }
182             sub code {
183 6     6 0 11 my $self = shift;
184 6 50       18 if (defined $_[0]) {
185 6         20 $self->[$CODE] = $_[0];
186             } else {
187 0         0 $self->[$CODE];
188             }
189             }
190             sub getCode {
191 0     0 0 0 my $self = shift;
192 0         0 my $part = shift;
193 0         0 $self->[$CODE];
194             }
195             sub setCode {
196 0     0 0 0 my $self = shift;
197 0         0 $self->[$CODE] = shift;
198             }
199             sub condition {
200 6     6 0 11 my $self = shift;
201 6 50       17 if (@_) {
202 0         0 $self->[$CONDITION] = shift;
203             } else {
204 6         36 $self->[$CONDITION];
205             }
206             }
207             sub expression {
208 0     0 0 0 my $self = shift;
209 0 0       0 if (@_) {
210 0         0 $self->[$EXPRESSION] = shift;
211             } else {
212 0         0 $self->[$EXPRESSION];
213             }
214             }
215             sub readmore {
216 2     2 0 4 my $self = shift;
217 2 50       6 if (@_) {
218 0         0 $self->[$READ_MORE_RE] = shift;
219             } else {
220 2         7 $self->[$READ_MORE_RE];
221             }
222             }
223             sub AUTOLOAD { # is this useful or dangerous? ;-)
224 0     0   0 my $self = shift;
225 0 0       0 return unless ref($self);
226 0 0       0 return if $AUTOLOAD =~ /\bDESTROY$/;
227 0         0 my $name = $AUTOLOAD;
228 0         0 $name =~ s/.*://;
229 0         0 my $value = shift;
230 0 0       0 if (defined $value) {
231 0         0 ${$self->[$DECORATION]}{$name} = $value;
  0         0  
232             } else {
233 0         0 ${$self->[$DECORATION]}{$name};
  0         0  
234             }
235             }
236             # set(ATTRIBUTE, VALUE)
237             # Purpose: set an attribute value
238 0     0 1 0 sub set { ${$_[0]->[$DECORATION]}{$_[1]} = $_[2];}
  0         0  
239             # get(ATT)
240             # Purpose: return an attribute value
241 0     0 1 0 sub get { ${$_[0]->[$DECORATION]}{$_[1]};}
  0         0  
242            
243             sub inpkg { # not documented
244 8     8 0 10 my $self = shift;
245 8 100       19 if (defined $_[0]) {
246 4         12 $self->[$IN_PKG] = $_[0]
247             } else {
248 4         10 $self->[$IN_PKG];
249             }
250             }
251             # status()
252             # Purpose: Indicate is the last token search has succeeded or not
253             # Arguments:
254             # Returns:
255             sub status {
256 0 0   0 1 0 defined($_[1]) ?
257             $_[0]->[$STATUS] = $_[1] :
258             $_[0]->[$STATUS];
259             }
260             # setText()
261             # Purpose: Return the symbolic name of the object
262             # Arguments:
263             # Returns: see purpose
264             # Extension: save $1, $2... in a list
265 25     25 1 564 sub setText { $_[0]->[$TEXT] = $_[1] } # set token string
266            
267             # getText()
268             # Purpose:
269             # Arguments:
270             # Returns:
271 0     0 1 0 sub getText { $_[0]->[$TEXT] } # get token string
272            
273             sub text {
274 2 50   2 1 14 defined($_[1]) ?
275             $_[0]->[$TEXT] = $_[1] :
276             $_[0]->[$TEXT];
277             }
278            
279 28     28 1 122 sub name { $_[0]->[$NAME] } # name of the token
280             *type = \&name; # synonym of the name() method
281            
282 6     6 1 32 sub regexp { $_[0]->[$REGEXP] } # regexp
283            
284 6     6 0 39 sub handler { $_[0]->[$HANDLER] } # name of an event handler
285            
286             # action()
287             # Purpose:
288             # Arguments:
289             # Returns:
290 6     6 1 22 sub action { $_[0]->[$ACTION] } # anonymous function
291            
292             # lexer(EXP)
293             # lexer
294             # Purpose: Defines or returns the associated lexer
295             # Arguments:
296             # Returns:
297             sub lexer {
298 10 100   10 0 29 if (defined $_[1]) {
299 4         10 $_[0]->[$LEXER] = $_[1];
300             } else {
301 6         25 $_[0]->[$LEXER];
302             }
303             }
304            
305             sub getRegisteredLexerType {
306 6     6 0 10 my $self = shift;
307 6         9 my $type = shift;
308 6   33     35 my $class = ref $self || $self;
309 10     10   82 no strict 'refs';
  10         16  
  10         6421  
310 6         10 foreach (@{"${class}::REGISTERED_LEXER_TYPE"}) {
  6         28  
311 6 50       57 return $_ if $type->isa("Parse::$_");
312             }
313 0         0 require Carp;
314 0         0 Carp::croak "no template defined for the '$class' token in the '$type' lexer";
315             }
316             # not documented
317             sub do {
318 0     0 0 0 my $self = shift;
319 0         0 &{(shift)}($self, @_)
  0         0  
320             }
321            
322             # next()
323             # Purpose: Return the string token if token is the pending token
324             # Arguments: no argument
325             # Returns: a token string if token is found, else undef
326             # Remark: $PENDING_TOKEN is set by the Parse::ALex class
327             sub next { # return the token string
328 0     0 1 0 my $self = shift;
329 0         0 my $lexer = $self->[$LEXER];
330 0         0 my $pendingToken = $lexer->[$PENDING_TOKEN];
331 0 0       0 if ($pendingToken == $EOI) {
332 0 0       0 $self->[$STATUS] = $self == $EOI ? 1 : 0;
333 0         0 return undef;
334             }
335 0 0       0 $lexer->next() unless $pendingToken;
336 0 0       0 if ($self == $lexer->[$PENDING_TOKEN]) {
337 0         0 $lexer->[$PENDING_TOKEN] = 0; # now no pending token
338 0         0 my $text = $self->[$TEXT];
339 0         0 $self->[$TEXT] = '';
340 0         0 $self->[$STATUS] = 1;
341 0         0 $text; # return token string
342             } else {
343 0         0 $self->[$STATUS] = 0;
344 0         0 undef;
345             }
346             }
347             # isnext()
348             # Purpose: Return the status of the token object, and the recognized string
349             # Arguments: scalar reference
350             # Returns:
351             # 1. the object status
352             # 2. the recognized string is put in the scalar reference
353             sub isnext {
354 0     0 1 0 my $self = shift;
355 0         0 my $lexer = $self->[$LEXER];
356 0         0 my $pendingToken = $lexer->[$PENDING_TOKEN];
357 0 0       0 if ($pendingToken == $EOI) {
358 0         0 ${$_[0]} = undef;
  0         0  
359 0 0       0 return $self->[$STATUS] = $self == $EOI ? 1 : 0;
360             }
361 0 0       0 $lexer->next() unless $pendingToken;
362 0 0       0 if ($self == $lexer->[$PENDING_TOKEN]) {
363 0         0 $lexer->[$PENDING_TOKEN] = 0; # now no pending token
364 0         0 ${$_[0]} = $self->[$TEXT];
  0         0  
365 0         0 $self->[$TEXT] = '';
366 0         0 $self->[$STATUS] = 1;
367 0         0 1;
368             } else {
369 0         0 $self->[$STATUS] = 0;
370 0         0 ${$_[0]} = undef;
  0         0  
371 0         0 0;
372             }
373             }
374            
375             package Parse::Token::Action;
376 10     10   11682 use Parse::Template;
  10         49036  
  10         576  
377             @Parse::Token::Action::ISA = qw(Parse::Token Parse::Trace);
378            
379 10     10   94 use vars qw(%TEMPLATE $template);
  10         21  
  10         6337  
380             %TEMPLATE =
381             (EXPRESSION_PART => q!
382             %%$CONDITION%%
383             %%$EXPRESSION%%
384             !
385             );
386             $template = new Parse::Template(%TEMPLATE);
387             sub new {
388 0     0   0 my $receiver = shift;
389 0         0 my ($name, $expression) = $receiver->_parse(@_);
390 0         0 my $token = $receiver->SUPER::new($name, '', '', $expression);
391 0         0 $token;
392             }
393             sub _parse {
394 0     0   0 my $self = shift;
395 0 0       0 unless (@_ >= 2) {
396 0         0 require Carp;
397 0         0 Carp::croak "bad argument number (@_)";
398             }
399 0         0 my ($key, $value);
400 0         0 my ($name, $expression);
401 0         0 my $escape = '';
402 0         0 while (@_ >= 2) {
403 0         0 ($key, $value) = (shift, shift);
404 0 0       0 if ($key =~ /^[Nn]ame$/) {
    0          
405 0         0 $name = $value;
406             } elsif ($key =~ /^[Ee]xpr$/) {
407 0         0 $expression = $value;
408             } else {
409 0         0 require Carp;
410 0         0 Carp::croak "'$key' is an invalid attribute for a ", __PACKAGE__, "'s instance";
411             }
412             }
413 0         0 ($name, $expression);
414             }
415             sub genCode {
416 0     0   0 my $self = shift;
417            
418 0         0 my $lexer = $self->lexer;
419 0         0 my $tokenid = $lexer->inpkg() . '::' . $self->name();
420 0         0 my $condition = $lexer->genCondition($self->condition);
421 0         0 my $expression = $self->expression;
422            
423 0         0 $template->env(
424             CONDITION => $condition,
425             EXPRESSION => $expression,
426             );
427 0         0 my $code = $template->eval('EXPRESSION_PART');
428 0         0 $self->code($code);
429 0         0 $code;
430             }
431            
432             package Parse::Token::Simple;
433 10     10   73 use Parse::Trace;
  10         16  
  10         226  
434 10     10   83 use Parse::Template;
  10         23  
  10         4582  
435             @Parse::Token::Simple::ISA = qw(Parse::Token Parse::Trace);
436            
437             sub new {
438 4     4   7 my $receiver = shift;
439 4         12 my $token = $receiver->SUPER::new($receiver->_parse(@_));
440 4         16 $token;
441             }
442             sub _parse {
443 4     4   5 my $self = shift;
444 4 50       17 unless (@_ >= 2) {
445 0         0 require Carp;
446 0         0 Carp::croak "bad argument number (@_)";
447             }
448 4         13 my ($name, $regex, $action, $expression, $readif, $handler) =
449             ('', '', '', '', '', '');
450 4         3 my ($key, $value, $escape);
451 4         15 while (@_ >= 2) {
452 12         15 ($key, $value) = (shift, shift);
453 12 100       77 if ($key =~ /^[Nn]ame$/) {
    100          
    50          
    50          
    0          
454 4         11 $name = $value;
455             } elsif ($key =~ /^(?:[Rr]egexp?|[Rr]e)$/) {
456 4         10 $regex = $value;
457             #} elsif ($key =~ /^[Rr]ead[Ii]f$/) { # regexp for continuation
458             } elsif ($key =~ /^[Rr]eadMore$/) { # regexp for continuation
459 0 0       0 $readif = $value == 1 ? "\$" : '';
460             } elsif ($key =~ /^[Ss]ub$/) {
461 4         11 $action = $value;
462             } elsif ($key =~ /^[Hh]andler$/) {
463 0         0 $handler = $value;
464             } else {
465 0         0 require Carp;
466 0         0 Carp::croak "'$key' is an invalid attribute for a ", __PACKAGE__, "'s instance";
467             }
468             }
469 4         27 ($name, $regex, $action, $expression, $readif, $handler);
470             }
471            
472 10     10   60 use vars qw(%TEMPLATE @REGISTERED_LEXER_TYPE $template);
  10         20  
  10         6692  
473             @REGISTERED_LEXER_TYPE = qw(Lex CLex LexEvent);
474             %TEMPLATE = ();
475             ####################################### Parse::Token::Simple - Parse::Lex class
476             $TEMPLATE{'LEX_HEADER_PART'} = q!
477             %%$CONDITION%%
478             $LEX_BUFFER =~ /\G(?:%%$REGEXP%%)/cg and do {
479             %%$READ_MORE_RE ne '' ? LEX_READ_MORE_DATA_PART() : LEX_SET_TOKEN_PART()%%
480             %%$WITH_TRACE ? LEX_TRACE_PART() : '' %%
481             %%$WITH_SUB ? LEX_FOOTER_WITH_SUB_PART() : LEX_FOOTER_PART() %%
482             !;
483             $TEMPLATE{'LEX_READ_MORE_DATA_PART'} = q!
484             my $pos = pos($LEX_BUFFER);
485             my $line;
486             $textLength = $pos - $LEX_POS;
487             $pos = pos($LEX_BUFFER);
488             while ($LEX_BUFFER =~ /\G(?:%%$READ_MORE_RE%%)/cg) {
489             $line = <$LEX_FH>;
490             if (defined $line) {
491             $LEX_BUFFER .= $line;
492             pos($LEX_BUFFER) = $pos;
493             if ($LEX_BUFFER =~ /\G(?:%%$REGEXP%%)/cg) {
494             $pos = pos($LEX_BUFFER);
495             } else {
496             last;
497             }
498             }
499             }
500             $textLength = pos($LEX_BUFFER) - $LEX_POS;
501             $content = substr($LEX_BUFFER, $LEX_POS, $textLength); # $&
502             $LEX_LENGTH = CORE::length($LEX_BUFFER);
503             $LEX_OFFSET += $textLength;
504             $LEX_POS += $textLength;
505             !;
506             $TEMPLATE{'LEX_SET_TOKEN_PART'} = q!
507             $textLength = pos($LEX_BUFFER) - $LEX_POS;
508             $content = substr($LEX_BUFFER, $LEX_POS, $textLength); # $&
509             $LEX_OFFSET += $textLength;
510             $LEX_POS += $textLength;
511             !;
512             $TEMPLATE{'LEX_TRACE_PART'} = q!
513             if ($self->[%%$TRACE%%]) {
514             my $tmp = '%%$REGEXP%%';
515             my $trace = "Token read (" . $%%$TOKEN_ID%%->name . ", $tmp): $content";
516             $self->context($trace);
517             }
518             !;
519             $TEMPLATE{'LEX_FOOTER_WITH_SUB_PART'} = q!
520             $%%$TOKEN_ID%%->setText($content);
521             $self->[%%$PENDING_TOKEN%%] = $LEX_TOKEN = $%%$TOKEN_ID%%;
522             $content = &{$%%$TOKEN_ID%%->action}($LEX_TOKEN, $content);
523             ($LEX_TOKEN = $self->getToken)->setText($content);
524             #print STDERR $LEX_TOKEN->name, " ", $self->[%%$PENDING_TOKEN%%]->name, " $content\n";
525             %%$WITH_TRACE ? LEX_FOOTER_WITH_SUB_TRACE_PART() : ''%%
526             last CASE;
527             };
528             !;
529             $TEMPLATE{'LEX_FOOTER_WITH_SUB_TRACE_PART'} = q!
530             if ($self->[%%$PENDING_TOKEN%%] ne $LEX_TOKEN) {
531             if ($self->[%%$TRACE%%]) { # Trace
532             $self->context("Token type has changed - " .
533             "Type: " . $LEX_TOKEN->name .
534             " - Content: $content\n");
535             }
536             }
537             !;
538             $TEMPLATE{'LEX_FOOTER_PART'} = q!
539             $%%$TOKEN_ID%%->setText($content);
540             $LEX_TOKEN = $%%$TOKEN_ID%%;
541             last CASE;
542             };
543             !;
544             ####################################### Parse::Token::Simple - Parse::LexEvent class
545             $TEMPLATE{'LEXEVENT_HEADER_PART'} = q!
546             %%$CONDITION%%
547             $LEX_BUFFER =~ /\G(?:%%$REGEXP%%)/cg and do {
548             %%$READ_MORE_RE ne '' ? LEX_READ_MORE_DATA_PART() : LEXEVENT_SET_TOKEN_PART()%%
549             %%$WITH_TRACE ? LEXEVENT_TRACE_PART() : '' %%
550             %%$TOKEN_HANDLER%%($%%$TOKEN_ID%%, $content);
551             redo PARSE;
552             };
553             !;
554             $TEMPLATE{'LEX_READ_MORE_DATA_PART'} = q!
555             my $pos = pos($LEX_BUFFER);
556             my $line;
557             $textLength = $pos - $LEX_POS;
558             $pos = pos($LEX_BUFFER);
559             while ($LEX_BUFFER =~ /\G(?:%%$READ_MORE_RE%%)/cg) {
560             $line = <$LEX_FH>;
561             if (defined $line) {
562             $LEX_BUFFER .= $line;
563             pos($LEX_BUFFER) = $pos;
564             if ($LEX_BUFFER =~ /\G(?:%%$REGEXP%%)/cg) {
565             $pos = pos($LEX_BUFFER);
566             } else {
567             last;
568             }
569             }
570             }
571             $textLength = pos($LEX_BUFFER) - $LEX_POS;
572             $content = substr($LEX_BUFFER, $LEX_POS, $textLength); # $&
573             $LEX_LENGTH = CORE::length($LEX_BUFFER);
574             $LEX_OFFSET += $textLength;
575             $LEX_POS += $textLength;
576             !;
577             $TEMPLATE{'LEXEVENT_SET_TOKEN_PART'} = q!
578             $textLength = pos($LEX_BUFFER) - $LEX_POS;
579             $content = substr($LEX_BUFFER, $LEX_POS, $textLength); # $&
580             $LEX_OFFSET += $textLength;
581             $LEX_POS += $textLength;
582             !;
583             $TEMPLATE{'LEXEVENT_TRACE_PART'} = q!
584             if ($self->[%%$TRACE%%]) {
585             my $tmp = '%%$REGEXP%%';
586             my $trace = "Token read (" . $%%$TOKEN_ID%%->name . ", $tmp): $content";
587             $self->context($trace);
588             }
589             !;
590             ####################################### Parse::Token::Simple - Parse::CLex class
591             $TEMPLATE{'CLEX_HEADER_PART'} = q!
592             %%$CONDITION%%
593             $LEX_BUFFER =~ s/^(?:%%$REGEXP%%)// and do {
594             $content = $&;
595             $textLength = CORE::length($content);
596             $LEX_OFFSET += $textLength;
597             $LEX_POS += $textLength;
598             %%$WITH_TRACE ? CLEX_TRACE_PART() : '' %%
599             %%$WITH_SUB ? CLEX_FOOTER_WITH_SUB_PART() : CLEX_FOOTER_PART() %%
600             !;
601             $TEMPLATE{'CLEX_TRACE_PART'} = q!
602             if ($self->[%%$TRACE%%]) {
603             my $tmp = '%%$REGEXP%%';
604             my $trace = "Token read (" . $%%$TOKEN_ID%%->name . ", $tmp): $content";
605             $self->context($trace);
606             }
607             !;
608            
609             $TEMPLATE{'CLEX_FOOTER_WITH_SUB_PART'} = q!
610             $%%$TOKEN_ID%%->setText($content);
611             $self->[%%$PENDING_TOKEN%%] = $LEX_TOKEN = $%%$TOKEN_ID%%;
612             $content = &{$%%$TOKEN_ID%%->action}($LEX_TOKEN, $content);
613             ($LEX_TOKEN = $self->getToken)->setText($content);
614             %%$WITH_TRACE ? CLEX_FOOTER_WITH_SUB_TRACE_PART() : ''%%
615             last CASE;
616             };
617             !;
618             $TEMPLATE{'CLEX_FOOTER_WITH_SUB_TRACE_PART'} = q!
619             if ($self->[%%$PENDING_TOKEN%%] ne $LEX_TOKEN) {
620             if ($self->isTrace) {
621             $self->context("token type has changed - " .
622             "Type: " . $LEX_TOKEN->name .
623             " - Content: $content\n");
624             }
625             }
626             !;
627             $TEMPLATE{'CLEX_FOOTER_PART'} = q!
628             $%%$TOKEN_ID%%->setText($content);
629             $LEX_TOKEN = $%%$TOKEN_ID%%;
630             last CASE;
631             };
632             !;
633             $template = new Parse::Template(%TEMPLATE);
634             sub genCode {
635 6     6   10 my $self = shift;
636            
637 6         25 my $lexer = $self->lexer;
638 6         47 my($TRACE, $EOI, $HOLD_TEXT, $PENDING_TOKEN) =
639             $lexer->_map('TRACE', 'EOI', 'HOLD_TEXT', 'PENDING_TOKEN');
640            
641 6         26 my $tokenid = $lexer->inpkg() . '::' . $self->name();
642 6         30 my $condition = $lexer->genCondition($self->condition);
643 6 50       24 my $with_sub = $self->action ? 1 : 0;
644 6 100       64 my $readmore = $lexer->isFromString ? '' : $self->readmore;
645 6   33     38 my $handler = $self->handler || $tokenid;
646 6 50       28 $handler = $handler =~ /::/ ? $handler : $lexer->inpkg . '::' . $handler;
647            
648 6         35 $template->env(
649             CONDITION => $condition,
650             TOKEN_ID=> $tokenid,
651             TOKEN_HANDLER => $handler,
652             SKIP => $lexer->skip,
653             'IS_HOLD' => $lexer->isHold,
654             'WITH_TRACE' => $lexer->isTrace,
655             READ_MORE_RE => $readmore,
656             'WITH_SUB' => $with_sub,
657             'HOLD_TEXT' => $HOLD_TEXT,
658             'EOI' => $EOI,
659             'TRACE' => $TRACE,
660             'PENDING_TOKEN' => $PENDING_TOKEN,
661             );
662            
663 6         624 my $ppregexp = $template->ppregexp($self->regexp);
664 6         282 my $debug = 0;
665 6 50       18 if ($debug) {
666 0         0 print STDERR "REGEXP[$tokenid]->\t\t$ppregexp\n";
667             }
668 6         92 $template->env('REGEXP' => $ppregexp);
669             # find the template code defined for this lexer type
670 6         128 my $lexer_type = __PACKAGE__->getRegisteredLexerType(ref $lexer);
671 6         36 my $code = $template->eval("\U$lexer_type" . '_HEADER_PART');
672 6         3448 $self->code($code);
673 6         77 $code;
674             }
675            
676             package Parse::Token::Segmented;
677 10     10   62 use Parse::Trace;
  10         19  
  10         4045  
678             @Parse::Token::Segmented::ISA = qw(Parse::Token Parse::Trace);
679             sub new {
680 0     0     my $receiver = shift;
681             #my ($name, $regex, $action) = $receiver->_parse(@_);
682 0           my $token = $receiver->SUPER::new($receiver->_parse(@_));
683 0           $token;
684             }
685             sub _parse {
686 0     0     my $self = shift;
687 0 0         unless (@_ >= 2) {
688 0           require Carp;
689 0           Carp::croak "bad argument number (@_)";
690             }
691 0           my ($name, $regex, $action, $expression, $readif, $handler) =
692             ('', '', '', '', '', '');
693 0           my ($key, $value, $escape) = ('', '', '');
694 0           while (@_ >= 2) {
695 0           ($key, $value) = (shift, shift);
696 0 0         if ($key =~ /^[Nn]ame$/) {
    0          
    0          
    0          
697 0           $name = $value;
698             } elsif ($key =~ /^(?:[Rr]egexp?|[Rr]e)$/) {
699 0           $regex = $value;
700             } elsif ($key =~ /^[Ss]ub$/) {
701 0           $action = $value;
702             } elsif ($key =~ /^[Hh]andler$/) {
703 0           $handler = $value;
704             } else {
705 0           require Carp;
706 0           Carp::croak "'$key' is an invalid attribute for a ", __PACKAGE__, "'s instance";
707             }
708             }
709 0           ($name, $regex, $action, $expression, $readif, $handler);
710             }
711            
712 10     10   58 use vars qw(%TEMPLATE @REGISTERED_LEXER_TYPE);
  10         20  
  10         8965  
713             @REGISTERED_LEXER_TYPE = qw(Lex CLex LexEvent);
714             %TEMPLATE = ();
715             ####################################### Parse::Token::Segmented - Parse::Lex class
716             $TEMPLATE{'LEX_HEADER_PART'} = q!
717             %%$FROM_STRING ? LEX_HEADER_STRING_PART() : LEX_HEADER_STREAM_PART() %%
718             !;
719            
720             $TEMPLATE{'LEX_HEADER_STRING_PART'} = q!
721             %%$CONDITION%%
722             $LEX_BUFFER =~ /\G(?:%%$REGEXP%%)/cg and do {
723             $textLength = pos($LEX_BUFFER) - $LEX_POS; # length $&
724             $content = substr($LEX_BUFFER, $LEX_POS, $textLength); # $&
725             $LEX_OFFSET += $textLength;
726             $LEX_POS += $textLength;
727             %%$WITH_TRACE ? LEX_TOKEN_TRACE_PART() : '' %%
728             %%$WITH_SUB ? LEX_FOOTER_WITH_SUB_PART() : LEX_FOOTER_PART() %%
729             !;
730             $TEMPLATE{'LEX_HEADER_STREAM_PART'} = q@
731             %%$CONDITION%%
732             $LEX_BUFFER =~ /\G(?:%%"$REGEXP_START"%%)/cg and do {
733             my $before_pos = $LEX_POS;
734             my $start_pos = pos($LEX_BUFFER);
735             my $tmp = substr($LEX_BUFFER, $start_pos);
736             my $line_read = 0;
737             # don't use \G
738             #print STDERR "before: $LEX_POS - initpos: $start_pos - tmp: $tmp\n";
739             unless ($tmp =~ /^(?:%%"$REGEXP_MIDDLE$REGEXP_END"%%)/g) {
740             my $line = '';
741             do {
742             while (1) {
743             $line = <$LEX_FH>;
744             $line_read = 1;
745             unless (defined($line)) { #
746             $self->[%%$EOI%%] = 1;
747             $LEX_TOKEN = $Parse::Token::EOI;
748             require Carp;
749             Carp::croak "unable to find end of the '", $%%$TOKEN_ID%%->name, "' token";
750             }
751             $LEX_RECORD++;
752             $tmp .= $line;
753             last if $line =~ /%%$REGEXP_END%%/;
754             }
755             } until ($tmp =~ /^(?:%%"$REGEXP_MIDDLE$REGEXP_END"%%)/g); # don't forget /g
756             }
757             $LEX_POS = $start_pos + pos($tmp);
758             $LEX_OFFSET += $LEX_POS;
759             if ($line_read) {
760             $LEX_BUFFER = substr($LEX_BUFFER, 0, $start_pos) . $tmp;
761             $LEX_LENGTH = CORE::length($LEX_BUFFER);
762             }
763             $content = substr($LEX_BUFFER, $before_pos, $LEX_POS - $before_pos);
764             pos($LEX_BUFFER) = $LEX_POS;
765             #print STDERR "LEX_BUFFER: $LEX_BUFFER\n";
766             #print STDERR "pos: $before_pos - length: ", $LEX_POS -$before_pos, " - content->$content<-\n";
767             %%$WITH_TRACE ? LEX_TOKEN_TRACE_PART() : '' %%
768             %%$WITH_SUB ? LEX_FOOTER_WITH_SUB_PART() : LEX_FOOTER_PART() %%
769             @;
770             $TEMPLATE{'LEX_TOKEN_TRACE_PART'} = q!
771             if ($self->[%%$TRACE%%]) { # Trace
772             my $tmp = '%%$REGEXP%%';
773             my $trace = "Token read (" . $%%$TOKEN_ID%%->name . ", $tmp): $content";
774             $self->context($trace);
775             }
776             !;
777             $TEMPLATE{'LEX_FOOTER_WITH_SUB_PART'} = q!
778             $%%$TOKEN_ID%%->setText($content);
779             $self->[%%$PENDING_TOKEN%%] = $LEX_TOKEN = $%%$TOKEN_ID%%;
780             $content = &{$%%$TOKEN_ID%%->action}($LEX_TOKEN, $content);
781             ($LEX_TOKEN = $self->getToken)->setText($content);
782             %%$WITH_TRACE ? LEX_FOOTER_WITH_SUB_TRACE_PART() : ''%%
783             last CASE;
784             };
785             !;
786             $TEMPLATE{'LEX_FOOTER_WITH_SUB_TRACE_PART'} = q!
787             if ($self->[%%$PENDING_TOKEN%%] ne $LEX_TOKEN) {
788             if ($self->[%%$TRACE%%]) { # Trace
789             $self->context("Token type has changed - " .
790             "Type: " . $LEX_TOKEN->name .
791             " - Content: $content\n");
792             }
793             }
794             !;
795             $TEMPLATE{'LEX_FOOTER_PART'} = q!
796             $%%$TOKEN_ID%%->setText($content);
797             $LEX_TOKEN = $%%$TOKEN_ID%%;
798             last CASE;
799             };
800             !;
801             ####################################### Parse::Token::Segmented - Parse::LexEvent class
802             $TEMPLATE{'LEXEVENT_HEADER_PART'} = q!
803             %%$FROM_STRING ? LEXEVENT_HEADER_STRING_PART() : LEXEVENT_HEADER_STREAM_PART() %%
804             !;
805            
806             $TEMPLATE{'LEXEVENT_HEADER_STRING_PART'} = q!
807             %%$CONDITION%%
808             $LEX_BUFFER =~ /\G(?:%%$REGEXP%%)/cg and do {
809             $textLength = pos($LEX_BUFFER) - $LEX_POS;
810             $content = substr($LEX_BUFFER, $LEX_POS, $textLength); # $&
811             $LEX_OFFSET += $textLength;
812             $LEX_POS += $textLength;
813             %%$WITH_TRACE ? LEXEVENT_TRACE_PART() : '' %%
814             %%$TOKEN_HANDLER%%($%%$TOKEN_ID%%, $content);
815             redo PARSE;
816             };
817             !;
818             $TEMPLATE{'LEXEVENT_HEADER_STREAM_PART'} = q@
819             %%$CONDITION%%
820             $LEX_BUFFER =~ /\G(?:%%"$REGEXP_START"%%)/cg and do {
821             my $before_pos = $LEX_POS;
822             my $start_pos = pos($LEX_BUFFER);
823             my $tmp = substr($LEX_BUFFER, $start_pos);
824             my $line_read = 0;
825             # don't use \G
826             #print STDERR "before: $LEX_POS - initpos: $start_pos - tmp: $tmp\n";
827             unless ($tmp =~ /^(?:%%"$REGEXP_MIDDLE$REGEXP_END"%%)/g) {
828             my $line = '';
829             do {
830             while (1) {
831             $line = <$LEX_FH>;
832             $line_read = 1;
833             unless (defined($line)) { #
834             $self->[%%$EOI%%] = 1;
835             $LEX_TOKEN = $Parse::Token::EOI;
836             require Carp;
837             Carp::croak "unable to find end of the '", $%%$TOKEN_ID%%->name, "' token";
838             }
839             $LEX_RECORD++;
840             $tmp .= $line;
841             last if $line =~ /%%$REGEXP_END%%/;
842             }
843             } until ($tmp =~ /^(?:%%"$REGEXP_MIDDLE$REGEXP_END"%%)/g); # don't forget /g
844             }
845             $LEX_POS = $start_pos + pos($tmp);
846             $LEX_OFFSET += $LEX_POS;
847             if ($line_read) {
848             $LEX_BUFFER = substr($LEX_BUFFER, 0, $start_pos) . $tmp;
849             $LEX_LENGTH = CORE::length($LEX_BUFFER);
850             }
851             $content = substr($LEX_BUFFER, $before_pos, $LEX_POS - $before_pos);
852             pos($LEX_BUFFER) = $LEX_POS;
853             #print STDERR "LEX_BUFFER: $LEX_BUFFER\n";
854             #print STDERR "pos: $before_pos - length: ", $LEX_POS -$before_pos, " - content->$content<-\n";
855             %%$WITH_TRACE ? LEXEVENT_TRACE_PART() : '' %%
856             %%$TOKEN_HANDLER%%($%%$TOKEN_ID%%, $content);
857             redo PARSE;
858             };
859             @;
860             $TEMPLATE{'LEXEVENT_TRACE_PART'} = q!
861             if ($self->[%%$TRACE%%]) {
862             my $tmp = '%%$REGEXP%%';
863             my $trace = "Token read (" . $%%$TOKEN_ID%%->name . ", $tmp): $content";
864             $self->context($trace);
865             }
866             !;
867             ####################################### Parse::Token::Segmented - Parse::CLex class
868             $TEMPLATE{'CLEX_HEADER_PART'} = q!
869             %%$FROM_STRING ? CLEX_HEADER_STRING_PART() : CLEX_HEADER_STREAM_PART() %%
870             !;
871             $TEMPLATE{'CLEX_HEADER_STRING_PART'} = q!
872             %%$CONDITION%%
873             $LEX_BUFFER =~ s/^(?:%%$REGEXP%%)// and do {
874             $content = $&;
875             $textLength = CORE::length($content);
876             $LEX_OFFSET += $textLength;
877             $LEX_POS += $textLength;
878             %%$WITH_TRACE ? CLEX_TOKEN_TRACE_PART() : '' %%
879             %%$WITH_SUB ? CLEX_FOOTER_WITH_SUB_PART() : CLEX_FOOTER_PART() %%
880             !;
881             $TEMPLATE{'CLEX_HEADER_STREAM_PART'} = q!
882             %%$CONDITION%%
883             $LEX_BUFFER =~ s/^(?:%%$REGEXP_START%%)// and do {
884             my $string = $LEX_BUFFER;
885             $content = $&;
886             my $length = CORE::length($content) + CORE::length($LEX_BUFFER);
887             do {
888             until ($string =~ /%%$REGEXP_END%%/) {
889             $string = <$LEX_FH>;
890             unless (defined($string)) { #
891             $self->[%%$EOI%%] = 1;
892             $LEX_TOKEN = $Parse::Token::EOI;
893             require Carp;
894             Carp::croak "unable to find end of the '", $%%$TOKEN_ID%%->name, "' token";
895             }
896             $length = CORE::length($string);
897             $LEX_RECORD++;
898             $LEX_BUFFER .= $string;
899             }
900             $string = '';
901             } until ($LEX_BUFFER =~ s/^(?:%%"$REGEXP_MIDDLE$REGEXP_END"%%)//);
902             $content .= $&;
903             $textLength = CORE::length($content);
904             $LEX_OFFSET += $textLength;
905             $LEX_POS += $length - CORE::length($LEX_BUFFER);
906             %%$WITH_TRACE ? CLEX_TOKEN_TRACE_PART() : '' %%
907             %%$WITH_SUB ? CLEX_FOOTER_WITH_SUB_PART() : CLEX_FOOTER_PART() %%
908             !;
909             $TEMPLATE{'CLEX_TOKEN_TRACE_PART'} = q!
910             if ($self->[%%$TRACE%%]) { # Trace
911             my $tmp = '%%$REGEXP%%';
912             my $trace = "Token read (" . $%%$TOKEN_ID%%->name . ", $tmp): $content";
913             $self->context($trace);
914             }
915             !;
916             $TEMPLATE{'CLEX_FOOTER_WITH_SUB_PART'} = q!
917             $%%$TOKEN_ID%%->setText($content);
918             $self->[%%$PENDING_TOKEN%%] = $LEX_TOKEN = $%%$TOKEN_ID%%;
919             $content = &{$%%$TOKEN_ID%%->action}($LEX_TOKEN, $content);
920             ($LEX_TOKEN = $self->getToken)->setText($content);
921             %%$WITH_TRACE ? CLEX_FOOTER_WITH_SUB_TRACE_PART() : ''%%
922             last CASE;
923             };
924             !;
925             $TEMPLATE{'CLEX_FOOTER_WITH_SUB_TRACE_PART'} = q!
926             if ($self->[%%$PENDING_TOKEN%%] ne $LEX_TOKEN) {
927             if ($self->isTrace) {
928             $self->context("token type has changed - " .
929             "Type: " . $LEX_TOKEN->name .
930             " - Content: $content\n");
931             }
932             }
933             !;
934             $TEMPLATE{'CLEX_FOOTER_PART'} = q!
935             $%%$TOKEN_ID%%->setText($content);
936             $LEX_TOKEN = $%%$TOKEN_ID%%;
937             last CASE;
938             };
939             !;
940             my $template = new Parse::Template(%TEMPLATE);
941             sub genCode {
942 0     0     my $self = shift;
943 0           my $debug = 0;
944            
945 0           my $lexer = $self->lexer;
946 0           my $tokenid = $lexer->inpkg() . '::' . $self->name();
947 0           my $condition = $lexer->genCondition($self->condition);
948 0   0       my $handler = $self->handler || $tokenid;
949 0 0         $handler = $handler =~ /::/ ? $handler : $lexer->inpkg . '::' . $handler;
950            
951 0           my($TRACE, $EOI, $HOLD_TEXT, $PENDING_TOKEN) =
952             $lexer->_map('TRACE', 'EOI', 'HOLD_TEXT', 'PENDING_TOKEN');
953            
954 0 0         my $with_sub = $self->action ? 1 : 0;
955 0           $template->env(
956             'CONDITION' => $condition,
957             'TOKEN_ID' => $tokenid,
958             TOKEN_HANDLER => $handler,
959             'SKIP' => $lexer->skip,
960             'FROM_STRING' => $lexer->isFromString,
961             'IS_HOLD' => $lexer->isHold,
962             'WITH_TRACE' => $lexer->isTrace,
963             'WITH_SUB' => $with_sub,
964             'HOLD_TEXT' => $HOLD_TEXT,
965             'EOI' => $EOI,
966             'TRACE' => $TRACE,
967             'PENDING_TOKEN' => $PENDING_TOKEN,
968             );
969            
970 0           my $ppregexp;
971             my $tmpregexp;
972 0           my $regexp = $self->regexp;
973 0 0         print STDERR "REGEXP[$tokenid]->\t\t@{$self->[$REGEXP]}\n" if $debug;
  0            
974            
975 0 0         if ($#{$regexp} >= 3) {
  0            
976 0           require Carp;
977 0           Carp::carp join " " , "Warning!", $#{$regexp} + 1,
  0            
978             "arguments in token definition";
979             }
980 0           $ppregexp = $tmpregexp = $template->ppregexp(${$regexp}[0]);
  0            
981 0           $template->env('REGEXP_START' => $ppregexp);
982            
983 0 0         $ppregexp = ${$regexp}[1] ? $template->ppregexp(${$regexp}[1]) : '(?:.*?)';
  0            
  0            
984 0           $tmpregexp .= $ppregexp;
985 0           $template->env('REGEXP_MIDDLE' => $ppregexp);
986            
987 0   0       $ppregexp = $template->ppregexp(${$regexp}[2] or ${$regexp}[0]);
988 0           $template->env('REGEXP_END' => $ppregexp);
989 0           $ppregexp = "$tmpregexp$ppregexp";
990            
991 0 0         if ($debug) {
992 0           print STDERR "REGEXP[$tokenid]->\t\t$ppregexp\n";
993             }
994 0           $template->env('REGEXP' => $ppregexp);
995            
996             # find the template code defined for this lexer type
997 0           my $lexer_type = __PACKAGE__->getRegisteredLexerType(ref $lexer);
998 0           my $code = $template->eval("\U$lexer_type" . '_HEADER_PART');
999 0           $self->code($code);
1000 0           $code;
1001             }
1002            
1003             package Parse::Token::Delimited;
1004 10     10   63 use Parse::Trace;
  10         24  
  10         5042  
1005             @Parse::Token::Delimited::ISA = qw(Parse::Token::Segmented Parse::Trace);
1006            
1007             # Examples:
1008             # [qw(/[*] (?s:.*?) [*]/)]
1009             # [qw()]
1010             # [qw(<[?] (?s:.*?) [?]>)]
1011            
1012             sub _parse {
1013 0     0     my $self = shift;
1014 0 0         unless (@_ >= 2) {
1015 0           require Carp;
1016 0           Carp::croak "bad argument number (@_)";
1017             }
1018 0           my ($name, $regex, $action, $expression, $readif, $handler) =
1019             ('', '', '', '', '', '');
1020 0           my ($key, $value, $start, $end, $escape) = ('', '');
1021 0           while (@_ >= 2) {
1022 0           ($key, $value) = (shift, shift);
1023 0 0         if ($key =~ /^[Nn]ame$/) {
    0          
    0          
    0          
    0          
1024 0           $name = $value;
1025             } elsif ($key =~ /^[Ss]tart$/) {
1026 0           $start = $value;
1027 0 0         $end = $value unless defined $end;
1028             } elsif ($key =~ /^[Ee]nd$/) {
1029 0           $end = $value;
1030 0 0         $start = $value unless defined $start;
1031             } elsif ($key =~ /^[Ss]ub$/) {
1032 0           $action = $value;
1033             } elsif ($key =~ /^[Hh]andler$/) {
1034 0           $handler = $value;
1035             } else {
1036 0           require Carp;
1037 0           Carp::croak "'$key' is an invalid attribute for a ", __PACKAGE__, "'s instance";
1038             }
1039             }
1040 0 0         unless (defined $start) {
1041 0           require Carp;
1042 0           Carp::croak "'Start' regex not defined";
1043             }
1044 0 0         unless (defined $end) {
1045 0           require Carp;
1046 0           Carp::croak "'End' regex not defined";
1047             }
1048 0           $regex = $self->_buildRegexp($start, $end);
1049 0           ($name, $regex, $action, $expression, $readif, $handler);
1050             }
1051             sub _buildRegexp {
1052 0     0     my $self = shift;
1053 0           my ($start, $end) = @_;
1054 0           my $content;
1055 0           $content = q!(?s:.*?)!;
1056             #print STDERR "[$start, $content, $end]\n";
1057 0           [$start, $content, $end];
1058             }
1059            
1060             package Parse::Token::Quoted;
1061 10     10   60 use Parse::Trace;
  10         17  
  10         6485  
1062             @Parse::Token::Quoted::ISA = qw(Parse::Token::Segmented Parse::Trace);
1063            
1064             sub _parse {
1065 0     0     my $self = shift;
1066 0 0         unless (@_ >= 2) {
1067 0           require Carp;
1068 0           Carp::croak "bad argument number (@_)";
1069             }
1070            
1071 0           my ($name, $regex, $action, $expression, $readif, $handler) =
1072             ('', '', '', '', '', '');
1073 0           my ($key, $value, $start, $end, $escape) = ('', '');
1074 0           while (@_ >= 2) {
1075 0           ($key, $value) = (shift, shift);
1076 0 0         if ($key =~ /^[Nn]ame$/) {
    0          
    0          
    0          
    0          
    0          
    0          
1077 0           $name = $value;
1078             } elsif ($key =~ /^[Qq]uote$/) {
1079 0 0         $start = $value unless defined $start;
1080 0 0         $end = $value unless defined $end;
1081             } elsif ($key =~ /^[Ss]tart$/) {
1082 0           $start = $value;
1083 0 0         $end = $value unless defined $end;
1084             } elsif ($key =~ /^[Ee]nd$/) {
1085 0           $end = $value;
1086 0 0         $start = $value unless defined $start;
1087             } elsif ($key =~ /^[Ee]scape$/) {
1088 0           $escape = $value;
1089             } elsif ($key =~ /^[Ss]ub$/) {
1090 0           $action = $value;
1091             } elsif ($key =~ /^[Hh]andler$/) {
1092 0           $handler = $value;
1093             } else {
1094 0           require Carp;
1095 0           Carp::croak "'$key' is an invalid attribute for a ", __PACKAGE__, "'s instance";
1096             }
1097             }
1098 0 0         unless (defined $start) {
1099 0           require Carp;
1100 0           Carp::croak "'Start' char not defined";
1101             }
1102 0 0         unless (defined $end) {
1103 0           require Carp;
1104 0           Carp::croak "'end' char not defined";
1105             }
1106 0           $regex = $self->_buildRegexp($start, $end, $escape);
1107 0           ($name, $regex, $action, $expression, $readif, $handler);
1108             }
1109             # Examples:
1110             # [qw(" [^"]+(?:""[^"]*)* ")]
1111             # [qw(" [^\\"]+(?:\\.[^\\"]*)* ")]
1112             sub _buildRegexp {
1113 0     0     my $self = shift;
1114 0           my ($start, $end, $escape) = @_;
1115 0           my $content;
1116 0           $start = quotemeta $start;
1117 0           $end = quotemeta $end;
1118 0 0 0       if (defined $escape and $escape ne '') {
1119 0           $escape = quotemeta $escape;
1120 0           $content = qq![^$end$escape]*(?:$escape.! . qq![^$end$escape]*)*!;
1121             } else {
1122 0           $content = qq![^$end]*(?:$end$end! . qq![^$end]*)*!;
1123             }
1124             #print STDERR "[$start, $content, $end]\n";
1125 0           [$start, $content, $end];
1126             }
1127            
1128             package Parse::Token::Nested;
1129 10     10   60 use Parse::Trace;
  10         21  
  10         1456  
1130             @Parse::Token::Nested::ISA = qw(Parse::Trace);
1131            
1132             # Examples:
1133             # (+ (* 3 4) 4)
1134             #
1135             sub new {
1136 0     0     die "Sorry! Not yet implemented";
1137             }
1138            
1139             1;
1140             __END__