perl_code_analyzer.pl 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #!/usr/bin/perl
  2. # Author: Daniel "Trizen" Șuteu
  3. # License: GPLv3
  4. # Date: 04 January 2015
  5. # Website: https://github.com/trizen
  6. #
  7. ## Analyze your Perl code and see whether you are or not a true Perl hacker!
  8. #
  9. # More info about this script:
  10. # https://trizenx.blogspot.com/2015/01/perl-code-analyzer.html
  11. use utf8;
  12. use 5.010;
  13. use strict;
  14. use warnings;
  15. use IPC::Open2 qw(open2);
  16. use Encode qw(encode_utf8 decode_utf8);
  17. use Getopt::Long qw(GetOptions);
  18. use Algorithm::Diff qw(LCS_length);
  19. use Perl::Tokenizer qw(perl_tokens);
  20. my $strict_level = 1;
  21. my %ignored_types;
  22. sub help {
  23. my ($code) = @_;
  24. print <<"HELP";
  25. usage: $0 [options] [file] [...]
  26. options:
  27. --strict [level] : sets the strictness level (default: $strict_level)
  28. Valid strict levels:
  29. >= 1 : ignores strings, PODs, comments, spaces and semicolons
  30. >= 2 : ignores round parentheses
  31. >= 3 : ignores here-documents, (q|qq|qw|qx) quoted strings
  32. >= 4 : ignores hex and binary literal numbers
  33. If level=0, any stricture will be disabled.
  34. HELP
  35. exit($code // 0);
  36. }
  37. GetOptions('strict=i' => \$strict_level,
  38. 'help|h' => sub { help(0) },)
  39. or die("Error in command line arguments\n");
  40. @ARGV || help(2);
  41. if ($strict_level >= 1) {
  42. @ignored_types{
  43. qw(
  44. pod
  45. data
  46. comment
  47. vertical_space
  48. horizontal_space
  49. other_space
  50. semicolon
  51. double_quoted_string
  52. single_quoted_string
  53. )
  54. } = ();
  55. }
  56. if ($strict_level >= 2) {
  57. @ignored_types{
  58. qw(
  59. parenthesis_open
  60. parenthesis_close
  61. )
  62. } = ();
  63. }
  64. if ($strict_level >= 3) {
  65. @ignored_types{
  66. qw(
  67. heredoc
  68. heredoc_beg
  69. q_string
  70. qq_string
  71. qw_string
  72. qx_string
  73. )
  74. } = ();
  75. }
  76. if ($strict_level >= 4) {
  77. @ignored_types{
  78. qw(
  79. hex_number
  80. binary_number
  81. )
  82. } = ();
  83. }
  84. sub deparse {
  85. my ($code) = @_;
  86. local (*CHLD_IN, *CHLD_OUT);
  87. my $pid = open2(\*CHLD_OUT, \*CHLD_IN, $^X, '-MO=Deparse', '-T');
  88. print CHLD_IN encode_utf8($code);
  89. close(CHLD_IN);
  90. my $deparsed = do {
  91. local $/;
  92. decode_utf8(<CHLD_OUT>);
  93. };
  94. waitpid($pid, 0);
  95. my $child_exit_status = $? >> 8;
  96. if ($child_exit_status != 0) {
  97. die "B::Deparse failed with code: $child_exit_status\n";
  98. }
  99. return $deparsed;
  100. }
  101. sub get_tokens {
  102. my ($code) = @_;
  103. my @tokens;
  104. perl_tokens {
  105. my ($token) = @_;
  106. if (not exists $ignored_types{$token}) {
  107. push @tokens, $token;
  108. }
  109. }
  110. $code;
  111. return @tokens;
  112. }
  113. foreach my $script (@ARGV) {
  114. print STDERR "=> Analyzing: $script\n";
  115. my $code = do {
  116. open my $fh, '<:utf8', $script;
  117. local $/;
  118. <$fh>;
  119. };
  120. my $d_code = eval { deparse($code) };
  121. $@ && do { warn $@; next };
  122. my @types = get_tokens($code);
  123. my @d_types = get_tokens($d_code);
  124. if (@types == 0 or @d_types == 0) {
  125. warn "This script seems to be empty! Skipping...\n";
  126. next;
  127. }
  128. my $len = LCS_length(\@types, \@d_types) - abs(@types - @d_types);
  129. my $score = (100 - ($len / @types * 100));
  130. if ($score >= 60) {
  131. printf("WOW!!! We have here a score of %.2f! This is obfuscation, isn't it?\n", $score);
  132. }
  133. elsif ($score >= 40) {
  134. printf("Outstanding! This code seems to be written by a true legend! Score: %.2f\n", $score);
  135. }
  136. elsif ($score >= 20) {
  137. printf("Amazing! This code is very unique! Score: %.2f\n", $score);
  138. }
  139. elsif ($score >= 15) {
  140. printf("Excellent! This code is written by a true Perl hacker. Score: %.2f\n", $score);
  141. }
  142. elsif ($score >= 10) {
  143. printf("Awesome! This code is written by a Perl expert. Score: %.2f\n", $score);
  144. }
  145. elsif ($score >= 5) {
  146. printf("Just OK! We have a score of %.2f! This is production code, isn't it?\n", $score);
  147. }
  148. else {
  149. printf("What is this? I guess it is some baby Perl code, isn't it? Score: %.2f\n", $score);
  150. }
  151. }