This file is indexed.

/usr/share/perl5/PPI/Token/Word.pm is in libppi-perl 1.215-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
package PPI::Token::Word;

=pod

=head1 NAME

PPI::Token::Word - The generic "word" Token

=head1 INHERITANCE

  PPI::Token::Word
  isa PPI::Token
      isa PPI::Element

=head1 DESCRIPTION

A C<PPI::Token::Word> object is a PPI-specific representation of several
different types of word-like things, and is one of the most common Token
classes found in typical documents.

Specifically, it includes not only barewords, but also any other valid
Perl identifier including non-operator keywords and core functions, and
any include C<::> separators inside it, as long as it fits the
format of a class, function, etc.

=head1 METHODS

There are no methods available for C<PPI::Token::Word> beyond those
provided by its L<PPI::Token> and L<PPI::Element> parent
classes.

We expect to add additional methods to help further resolve a Word as
a function, method, etc over time.  If you need such a thing right
now, look at L<Perl::Critic::Utils>.

=cut

use strict;
use PPI::Token ();

use vars qw{$VERSION @ISA %OPERATOR %QUOTELIKE};
BEGIN {
	$VERSION = '1.215';
	@ISA     = 'PPI::Token';

	# Copy in OPERATOR from PPI::Token::Operator
	*OPERATOR  = *PPI::Token::Operator::OPERATOR;

	%QUOTELIKE = (
		'q'  => 'Quote::Literal',
		'qq' => 'Quote::Interpolate',
		'qx' => 'QuoteLike::Command',
		'qw' => 'QuoteLike::Words',
		'qr' => 'QuoteLike::Regexp',
		'm'  => 'Regexp::Match',
		's'  => 'Regexp::Substitute',
		'tr' => 'Regexp::Transliterate',
		'y'  => 'Regexp::Transliterate',
	);
}

=pod

=head2 literal

Returns the value of the Word as a string.  This assumes (often
incorrectly) that the Word is a bareword and not a function, method,
keyword, etc.  This differs from C<content> because C<Foo'Bar> expands
to C<Foo::Bar>.

=begin testing literal 9

my @pairs = (
	"F",          'F',
	"Foo::Bar",   'Foo::Bar',
	"Foo'Bar",    'Foo::Bar',
);
while ( @pairs ) {
	my $from  = shift @pairs;
	my $to    = shift @pairs;
	my $doc   = PPI::Document->new( \"$from;" );
	isa_ok( $doc, 'PPI::Document' );
	my $word = $doc->find_first('Token::Word');
	isa_ok( $word, 'PPI::Token::Word' );
	is( $word->literal, $to, "The source $from becomes $to ok" );
}

=end testing

=cut

sub literal {
	my $self = shift;
	my $word = $self->content;

	# Expand Foo'Bar to Foo::Bar
	$word =~ s/\'/::/g;

	return $word;
}

=pod

=head2 method_call

Answers whether this is the name of a method in a method call. Returns true if
yes, false if no, and nothing if unknown.

=begin testing method_call 24

my $Document = PPI::Document->new(\<<'END_PERL');
indirect $foo;
indirect_class_with_colon Foo::;
$bar->method_with_parentheses;
print SomeClass->method_without_parentheses + 1;
sub_call();
$baz->chained_from->chained_to;
a_first_thing a_middle_thing a_last_thing;
(first_list_element, second_list_element, third_list_element);
first_comma_separated_word, second_comma_separated_word, third_comma_separated_word;
single_bareword_statement;
{ bareword_no_semicolon_end_of_block }
$buz{hash_key};
fat_comma_left_side => $thingy;
END_PERL

isa_ok( $Document, 'PPI::Document' );
my $words = $Document->find('Token::Word');
is( scalar @{$words}, 23, 'Found the 23 test words' );
my %words = map { $_ => $_ } @{$words};
is(
	scalar $words{indirect}->method_call,
	undef,
	'Indirect notation is unknown.',
);
is(
	scalar $words{indirect_class_with_colon}->method_call,
	1,
	'Indirect notation with following word ending with colons is true.',
);
is(
	scalar $words{method_with_parentheses}->method_call,
	1,
	'Method with parentheses is true.',
);
is(
	scalar $words{method_without_parentheses}->method_call,
	1,
	'Method without parentheses is true.',
);
is(
	scalar $words{print}->method_call,
	undef,
	'Plain print is unknown.',
);
is(
	scalar $words{SomeClass}->method_call,
	undef,
	'Class in class method call is unknown.',
);
is(
	scalar $words{sub_call}->method_call,
	0,
	'Subroutine call is false.',
);
is(
	scalar $words{chained_from}->method_call,
	1,
	'Method that is chained from is true.',
);
is(
	scalar $words{chained_to}->method_call,
	1,
	'Method that is chained to is true.',
);
is(
	scalar $words{a_first_thing}->method_call,
	undef,
	'First bareword is unknown.',
);
is(
	scalar $words{a_middle_thing}->method_call,
	undef,
	'Bareword in the middle is unknown.',
);
is(
	scalar $words{a_last_thing}->method_call,
	0,
	'Bareword at the end is false.',
);
foreach my $false_word (
	qw<
		first_list_element second_list_element third_list_element
		first_comma_separated_word second_comma_separated_word third_comma_separated_word
		single_bareword_statement
		bareword_no_semicolon_end_of_block
		hash_key
		fat_comma_left_side
	>
) {
	is(
		scalar $words{$false_word}->method_call,
		0,
		"$false_word is false.",
	);
}

=end testing

=cut

sub method_call {
	my $self = shift;

	my $previous = $self->sprevious_sibling;
	if (
		$previous
		and
		$previous->isa('PPI::Token::Operator')
		and
		$previous->content eq '->'
	) {
		return 1;
	}

	my $snext = $self->snext_sibling;
	return 0 unless $snext;

	if (
		$snext->isa('PPI::Structure::List')
		or
		$snext->isa('PPI::Token::Structure')
		or
		$snext->isa('PPI::Token::Operator')
		and (
			$snext->content eq ','
			or
			$snext->content eq '=>'
		)
	) {
		return 0;
	}

	if (
		$snext->isa('PPI::Token::Word')
		and
		$snext->content =~ m< \w :: \z >xms
	) {
		return 1;
	}

	return;
}

=begin testing __TOKENIZER__on_char 27

my $Document = PPI::Document->new(\<<'END_PERL');
$foo eq'bar';
$foo ne'bar';
$foo ge'bar';
$foo le'bar';
$foo gt'bar';
$foo lt'bar';
END_PERL

isa_ok( $Document, 'PPI::Document' );
my $words = $Document->find('Token::Operator');
is( scalar @{$words}, 6, 'Found the 6 test operators' );

is( $words->[0], 'eq', q{$foo eq'bar'} );
is( $words->[1], 'ne', q{$foo ne'bar'} );
is( $words->[2], 'ge', q{$foo ge'bar'} );
is( $words->[3], 'le', q{$foo le'bar'} );
is( $words->[4], 'gt', q{$foo ht'bar'} );
is( $words->[5], 'lt', q{$foo lt'bar'} );

$Document = PPI::Document->new(\<<'END_PERL');
q'foo';
qq'foo';
END_PERL

isa_ok( $Document, 'PPI::Document' );
$words = $Document->find('Token::Quote');
is( scalar @{$words}, 2, 'Found the 2 test quotes' );

is( $words->[0], q{q'foo'}, q{q'foo'} );
is( $words->[1], q{qq'foo'}, q{qq'foo'} );

$Document = PPI::Document->new(\<<'END_PERL');
qx'foo';
qw'foo';
qr'foo';
END_PERL

isa_ok( $Document, 'PPI::Document' );
$words = $Document->find('Token::QuoteLike');
is( scalar @{$words}, 3, 'Found the 3 test quotelikes' );

is( $words->[0], q{qx'foo'}, q{qx'foo'} );
is( $words->[1], q{qw'foo'}, q{qw'foo'} );
is( $words->[2], q{qr'foo'}, q{qr'foo'} );

$Document = PPI::Document->new(\<<'END_PERL');
m'foo';
s'foo'bar';
tr'fo'ba';
y'fo'ba';
END_PERL

isa_ok( $Document, 'PPI::Document' );
$words = $Document->find('Token::Regexp');
is( scalar @{$words}, 4, 'Found the 4 test quotelikes' );

is( $words->[0], q{m'foo'},     q{m'foo'} );
is( $words->[1], q{s'foo'bar'}, q{s'foo'bar'} );
is( $words->[2], q{tr'fo'ba'},  q{tr'fo'ba'} );
is( $words->[3], q{y'fo'ba'},   q{y'fo'ba'} );

$Document = PPI::Document->new(\<<'END_PERL');
pack'H*',$data;
unpack'H*',$data;
END_PERL

isa_ok( $Document, 'PPI::Document' );
$words = $Document->find('Token::Word');
is( scalar @{$words}, 2, 'Found the 2 test words' );

is( $words->[0], 'pack', q{pack'H*',$data} );
is( $words->[1], 'unpack', q{unpack'H*',$data} );

=end testing

=cut

my %backoff = map { $_ => 1 } qw{
    eq ne ge le gt lt
    q qq qx qw qr m s tr y
    pack unpack
};

sub __TOKENIZER__on_char {
	my $class = shift;
	my $t     = shift;

	# Suck in till the end of the bareword
	my $rest = substr( $t->{line}, $t->{line_cursor} );
	if ( $rest =~ /^(\w+(?:(?:\'|::)\w+)*(?:::)?)/ ) {
		my $word = $1;
		# Special Case: If we accidentally treat eq'foo' like
		# the word "eq'foo", then just make 'eq' (or whatever
		# else is in the %backoff hash.
		if ( $word =~ /^(\w+)'/ && $backoff{$1} ) {
		    $word = $1;
		}
		$t->{token}->{content} .= $word;
		$t->{line_cursor} += length $word;

	}

	# We might be a subroutine attribute.
	my $tokens = $t->_previous_significant_tokens(1);
	if ( $tokens and $tokens->[0]->{_attribute} ) {
		$t->{class} = $t->{token}->set_class( 'Attribute' );
		return $t->{class}->__TOKENIZER__commit( $t );
	}

	# Check for a quote like operator
	my $word = $t->{token}->{content};
	if ( $QUOTELIKE{$word} and ! $class->__TOKENIZER__literal($t, $word, $tokens) ) {
		$t->{class} = $t->{token}->set_class( $QUOTELIKE{$word} );
		return $t->{class}->__TOKENIZER__on_char( $t );
	}

	# Or one of the word operators
	if ( $OPERATOR{$word} and ! $class->__TOKENIZER__literal($t, $word, $tokens) ) {
	 	$t->{class} = $t->{token}->set_class( 'Operator' );
 		return $t->_finalize_token->__TOKENIZER__on_char( $t );
	}

	# Unless this is a simple identifier, at this point
	# it has to be a normal bareword
	if ( $word =~ /\:/ ) {
		return $t->_finalize_token->__TOKENIZER__on_char( $t );
	}

	# If the NEXT character in the line is a colon, this
	# is a label.
	my $char = substr( $t->{line}, $t->{line_cursor}, 1 );
	if ( $char eq ':' ) {
		$t->{token}->{content} .= ':';
		$t->{line_cursor}++;
		$t->{class} = $t->{token}->set_class( 'Label' );

	# If not a label, '_' on its own is the magic filehandle
	} elsif ( $word eq '_' ) {
		$t->{class} = $t->{token}->set_class( 'Magic' );

	}

	# Finalise and process the character again
	$t->_finalize_token->__TOKENIZER__on_char( $t );
}



# We are committed to being a bareword.
# Or so we would like to believe.
sub __TOKENIZER__commit {
	my ($class, $t) = @_;

	# Our current position is the first character of the bareword.
	# Capture the bareword.
	my $rest = substr( $t->{line}, $t->{line_cursor} );
	unless ( $rest =~ /^((?!\d)\w+(?:(?:\'|::)\w+)*(?:::)?)/ ) {
		# Programmer error
		die "Fatal error... regex failed to match in '$rest' when expected";
	}

	# Special Case: If we accidentally treat eq'foo' like the word "eq'foo",
	# then unwind it and just make it 'eq' (or the other stringy comparitors)
	my $word = $1;
	if ( $word =~ /^(\w+)'/ && $backoff{$1} ) {
	    $word = $1;
	}

	# Advance the position one after the end of the bareword
	$t->{line_cursor} += length $word;

	# We might be a subroutine attribute.
	my $tokens = $t->_previous_significant_tokens(1);
	if ( $tokens and $tokens->[0]->{_attribute} ) {
		$t->_new_token( 'Attribute', $word );
		return ($t->{line_cursor} >= $t->{line_length}) ? 0
			: $t->{class}->__TOKENIZER__on_char($t);
	}

	# Check for the end of the file
	if ( $word eq '__END__' ) {
		# Create the token for the __END__ itself
		$t->_new_token( 'Separator', $1 );
		$t->_finalize_token;

		# Move into the End zone (heh)
		$t->{zone} = 'PPI::Token::End';

		# Add the rest of the line as a comment, and a whitespace newline
		# Anything after the __END__ on the line is "ignored". So we must
		# also ignore it, by turning it into a comment.
		$rest = substr( $t->{line}, $t->{line_cursor} );
		$t->{line_cursor} = length $t->{line};
		if ( $rest =~ /\n$/ ) {
			chomp $rest;
			$t->_new_token( 'Comment', $rest ) if length $rest;
			$t->_new_token( 'Whitespace', "\n" );
		} else {
			$t->_new_token( 'Comment', $rest ) if length $rest;
		}
		$t->_finalize_token;

		return 0;
	}

	# Check for the data section
	if ( $word eq '__DATA__' ) {
		# Create the token for the __DATA__ itself
		$t->_new_token( 'Separator', "$1" );
		$t->_finalize_token;

		# Move into the Data zone
		$t->{zone} = 'PPI::Token::Data';

		# Add the rest of the line as the Data token
		$rest = substr( $t->{line}, $t->{line_cursor} );
		$t->{line_cursor} = length $t->{line};
		if ( $rest =~ /\n$/ ) {
			chomp $rest;
			$t->_new_token( 'Comment', $rest ) if length $rest;
			$t->_new_token( 'Whitespace', "\n" );
		} else {
			$t->_new_token( 'Comment', $rest ) if length $rest;
		}
		$t->_finalize_token;

		return 0;
	}

	my $token_class;
	if ( $word =~ /\:/ ) {
		# Since its not a simple identifier...
		$token_class = 'Word';

	} elsif ( $class->__TOKENIZER__literal($t, $word, $tokens) ) {
		$token_class = 'Word';

	} elsif ( $QUOTELIKE{$word} ) {
		# Special Case: A Quote-like operator
		$t->_new_token( $QUOTELIKE{$word}, $word );
		return ($t->{line_cursor} >= $t->{line_length}) ? 0
			: $t->{class}->__TOKENIZER__on_char( $t );

	} elsif ( $OPERATOR{$word} ) {
		# Word operator
		$token_class = 'Operator';

	} else {
		# If the next character is a ':' then its a label...
		my $string = substr( $t->{line}, $t->{line_cursor} );
		if ( $string =~ /^(\s*:)(?!:)/ ) {
			if ( $tokens and $tokens->[0]->{content} eq 'sub' ) {
				# ... UNLESS its after 'sub' in which
				# case it is a sub name and an attribute
				# operator.
				# We COULD have checked this at the top
				# level of checks, but this would impose
				# an additional performance per-word
				# penalty, and every other case where the
				# attribute operator doesn't directly
				# touch the object name already works.
				$token_class = 'Word';
			} else {
				$word .= $1;
				$t->{line_cursor} += length($1);
				$token_class = 'Label';
			}
		} elsif ( $word eq '_' ) {
			$token_class = 'Magic';
		} else {
			$token_class = 'Word';
		}
	}

	# Create the new token and finalise
	$t->_new_token( $token_class, $word );
	if ( $t->{line_cursor} >= $t->{line_length} ) {
		# End of the line
		$t->_finalize_token;
		return 0;
	}
	$t->_finalize_token->__TOKENIZER__on_char($t);
}

# Is the word in a "forced" context, and thus cannot be either an
# operator or a quote-like thing. This version is only useful
# during tokenization.
sub __TOKENIZER__literal {
	my ($class, $t, $word, $tokens) = @_;

	# Is this a forced-word context?
	# i.e. Would normally be seen as an operator.
	unless ( $QUOTELIKE{$word} or $PPI::Token::Operator::OPERATOR{$word} ) {
		return '';
	}

	# Check the cases when we have previous tokens
	my $rest = substr( $t->{line}, $t->{line_cursor} );
	if ( $tokens ) {
		my $token = $tokens->[0] or return '';

		# We are forced if we are a method name
		return 1 if $token->{content} eq '->';

		# We are forced if we are a sub name
		return 1 if $token->isa('PPI::Token::Word') && $token->{content} eq 'sub';

		# If we are contained in a pair of curly braces,
		# we are probably a bareword hash key
		if ( $token->{content} eq '{' and $rest =~ /^\s*\}/ ) {
			return 1;
		}
	}

	# In addition, if the word is followed by => it is probably
	# also actually a word and not a regex.
	if ( $rest =~ /^\s*=>/ ) {
		return 1;
	}

	# Otherwise we probably arn't forced
	'';
}

1;

=pod

=head1 TO DO

- Add C<function>, C<method> etc detector methods

=head1 SUPPORT

See the L<support section|PPI/SUPPORT> in the main module.

=head1 AUTHOR

Adam Kennedy E<lt>adamk@cpan.orgE<gt>

=head1 COPYRIGHT

Copyright 2001 - 2011 Adam Kennedy.

This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.

The full text of the license can be found in the
LICENSE file included with this module.

=cut