our
$VERSION
= 0.015_000;
use
constant
MAX_SINGLE_ERROR_LINE_LENGTH
=>
my
integer
$TYPED_MAX_SINGLE_ERROR_LINE_LENGTH
= 120;
sub
rperl_to_ast__parse {
{
my
object
$RETURN_TYPE
};
(
my
string
$rperl_source__file_name
) =
@ARG
;
rperl_source__check_syntax(
$rperl_source__file_name
);
rperl_source__criticize(
$rperl_source__file_name
);
return
( rperl_source__parse(
$rperl_source__file_name
) );
}
sub
rperl_source__check_syntax {
{
my
void
$RETURN_TYPE
};
(
my
string
$rperl_source__file_name
) =
@ARG
;
RPerl::verbose(
'PARSE PHASE 0: Check Perl syntax... '
);
my
string
$nul
=
$OSNAME
eq
'MSWin32'
?
'NUL'
:
'/dev/null'
;
my
string
$rperl_source__perl_syntax_command
=
$EXECUTABLE_NAME
.
q{ -Iblib/lib -M"warnings FATAL=>q(all)" -Mstrict -cw }
.
$rperl_source__file_name
;
my
string
$rperl_source__perl_syntax_command__no_output
=
$rperl_source__perl_syntax_command
.
' > '
.
$nul
.
' 2> '
.
$nul
;
my
string
$rperl_source__perl_syntax_command__all_output
=
$rperl_source__perl_syntax_command
.
' 2>&1'
;
my
integer
$rperl_source__perl_syntax_retval
=
system
$rperl_source__perl_syntax_command__no_output
;
my
string
$rperl_source__perl_syntax_retstring
= `
$rperl_source__perl_syntax_command__all_output
`;
if
(
$rperl_source__perl_syntax_retval
!= 0 ) {
my
$error_pretty
=
"\n\n"
.
'ERROR ECOPAPL02, RPERL PARSER, PERL SYNTAX ERROR'
.
"\n"
.
'Failed normal Perl strictures-and-fatal-warnings syntax check with the following information:'
.
"\n\n"
.
' File Name: '
.
$rperl_source__file_name
.
"\n"
.
' Return Value: '
. (
$rperl_source__perl_syntax_retval
>> 8 ) .
"\n"
.
' Error Message(s): '
;
if
( (
length
$rperl_source__perl_syntax_retstring
) < MAX_SINGLE_ERROR_LINE_LENGTH() ) {
$error_pretty
.=
$rperl_source__perl_syntax_retstring
.
"\n\n"
;
}
else
{
$error_pretty
.=
"\n\n"
.
$rperl_source__perl_syntax_retstring
.
"\n\n"
;
}
die
$error_pretty
;
}
my
string_arrayref
$rperl_source__perl_syntax_retstring_lines
;
@{
$rperl_source__perl_syntax_retstring_lines
} =
split
/\n/xms,
$rperl_source__perl_syntax_retstring
;
my
string_arrayref
$rperl_source__perl_syntax_retstring_warnings
= [];
foreach
my
string
$rperl_source__perl_syntax_retstring_line
(
@{
$rperl_source__perl_syntax_retstring_lines
} )
{
if
((
$rperl_source__perl_syntax_retstring_line
!~ m/WARNING\sW/xms )
and
(
$rperl_source__perl_syntax_retstring_line
!~ m/ERROR\sE/xms )
and
(
$rperl_source__perl_syntax_retstring_line
!~ m/\[\[\[\ BEGIN\s/xms )
and
(
$rperl_source__perl_syntax_retstring_line
!~ m/\[\[\[\ END\s/xms )
and
(
$rperl_source__perl_syntax_retstring_line
!~ m/syntax\sOK/xms )
)
{
push
@{
$rperl_source__perl_syntax_retstring_warnings
},
$rperl_source__perl_syntax_retstring_line
;
}
}
if
(((
scalar
@{
$rperl_source__perl_syntax_retstring_warnings
}) == 1) and
((
substr
$rperl_source__perl_syntax_retstring_warnings
->[0], 0, 59) eq
'Name "Win32::Locale::Lexicon" used only once: possible typo'
)) {
RPerl::warning(
"\n"
,
q{WARNING WCOPAPL00, RPERL PARSER, PERL SYNTAX WARNING: Non-fatal Perl v5.12 warning, 'Name "Win32::Locale::Lexicon" used only once', ignoring}
,
"\n"
);
}
elsif
( (
scalar
@{
$rperl_source__perl_syntax_retstring_warnings
} ) != 0 ) {
my
$error_pretty
=
"\n"
.
'ERROR ECOPAPL03, RPERL PARSER, PERL SYNTAX WARNING'
.
"\n"
.
'Failed normal Perl strictures-and-fatal-warnings syntax check with the following information:'
.
"\n\n"
.
' File Name: '
.
$rperl_source__file_name
.
"\n"
.
' Warning Message(s): '
;
if
( ( (
scalar
@{
$rperl_source__perl_syntax_retstring_warnings
} ) == 1 )
and ( (
length
$rperl_source__perl_syntax_retstring_warnings
->[0]) < MAX_SINGLE_ERROR_LINE_LENGTH() ) ) {
$error_pretty
.=
$rperl_source__perl_syntax_retstring_warnings
->[0] .
"\n\n"
;
}
else
{
$error_pretty
.=
"\n\n"
. (
join
"\n"
, @{
$rperl_source__perl_syntax_retstring_warnings
} ) .
"\n\n"
;
}
die
$error_pretty
;
}
RPerl::verbose(
' done.'
.
"\n"
);
}
sub
rperl_source__criticize {
{
my
void
$RETURN_TYPE
};
(
my
string
$rperl_source__file_name
) =
@ARG
;
RPerl::verbose(
'PARSE PHASE 1: Criticize Perl syntax... '
);
if
( not -f
$rperl_source__file_name
) {
die
'ERROR ECOPAPC10, RPERL PARSER, PERL CRITIC VIOLATION: File not found, '
.
q{'}
.
$rperl_source__file_name
.
q{'}
.
', dying'
.
"\n"
;
}
open
my
filehandleref
$FILE_HANDLE
,
'<'
,
$rperl_source__file_name
or
die
'ERROR ECOPAPC11, RPERL PARSER, PERL CRITIC VIOLATION: Cannot open file '
.
q{'}
.
$rperl_source__file_name
.
q{'}
.
' for reading, '
.
$OS_ERROR
.
', dying'
.
"\n"
;
my
string
$file_line
=
undef
;
my
string
$file_line_last
=
undef
;
while
(
$file_line
= <
$FILE_HANDLE
> ) {
$file_line_last
=
$file_line
;
}
close
$FILE_HANDLE
or
die
'ERROR ECOPAPC12, RPERL PARSER, PERL CRITIC VIOLATION: Cannot close file '
.
q{'}
.
$rperl_source__file_name
.
q{'}
.
' after reading, '
.
$OS_ERROR
.
', dying'
.
"\n"
;
if
(((
substr
$file_line_last
, -1, 1) ne
"\n"
) and (
$file_line_last
!~ m/^\s+$/xms )) {
die
'ERROR ECOPAPC13, RPERL PARSER, PERL CRITIC VIOLATION: RPerl source code input file '
.
q{'}
.
$rperl_source__file_name
.
q{'}
.
' does not end with newline character or line of all-whitespace characters, dying'
.
"\n"
;
}
my
object
$rperl_source__critic
= Perl::Critic->new(
'-exclude'
=> [
'RequireTidyCode'
,
'PodSpelling'
,
'RequireExplicitPackage'
,
'RequirePod'
,
'ProhibitBitwiseOperators'
],
'-severity'
=>
'brutal'
,
'-theme'
=>
'core'
,
'-verbose'
=> 11
);
my
@rperl_source__critic_violations
=
$rperl_source__critic
->critique(
$rperl_source__file_name
);
my
integer
$rperl_source__critic_num_violations
=
scalar
@rperl_source__critic_violations
;
if
(
$rperl_source__critic_num_violations
> 0 ) {
my
string
$violation_pretty
=
q{}
;
foreach
my
object
$violation
(
@rperl_source__critic_violations
) {
$violation_pretty
.=
' File Name: '
.
$rperl_source__file_name
.
"\n"
;
$violation_pretty
.=
' Line number: '
.
$violation
->{_location}->[0] .
"\n"
;
$violation_pretty
.=
' Policy: '
.
$violation
->{_policy} .
"\n"
;
$violation_pretty
.=
' Description: '
.
$violation
->{_description} .
"\n"
;
if
(
ref
(
$violation
->{_explanation} ) eq
'ARRAY'
) {
$violation_pretty
.=
' Explanation: See Perl Best Practices page(s) '
.
join
(
', '
, @{
$violation
->{_explanation} } ) .
"\n\n"
;
}
else
{
$violation_pretty
.=
' Explanation: '
.
$violation
->{_explanation} .
"\n\n"
;
}
}
die
"\n"
.
'ERROR ECOPAPC02, RPERL PARSER, PERL CRITIC VIOLATION'
.
"\n"
.
'Failed Perl::Critic brutal review with the following information:'
.
"\n\n"
.
$violation_pretty
;
}
else
{
RPerl::verbose(
' done.'
.
"\n"
);
}
}
sub
rperl_grammar_error {
{
my
void
$RETURN_TYPE
};
(
my
array
$argument
) =
@ARG
;
my
string
$value
=
$argument
->YYCurval;
if
( not(
defined
$value
) ) {
$value
=
'<<< NO TOKEN FOUND >>>'
;
}
my
string
$helpful_hint
=
q{}
;
if
(
$value
=~ /\d/xms ) {
$helpful_hint
=
q{ Helpful Hint: Possible case of PBP RequireNumberSeparators (see below)}
.
"\n"
.
q{ Policy: Perl::Critic::Policy::ValuesAndExpressions::RequireNumberSeparators}
.
"\n"
.
q{ Description: Long number not separated with underscores}
.
"\n"
.
q{ Explanation: See Perl Best Practices page(s) 59}
.
"\n"
;
}
my
integer
$line_number
=
$argument
->{TOKENLINE};
my
string
$rperl_source__file_name
=
$argument
->{rperl_source__file_name};
my
$current_state_num
=
$argument
->{STACK}[-1][0];
my
$current_state
=
$argument
->{STATES}[
$current_state_num
];
my
$expected_tokens
=
q{}
;
my
number
$is_first_expected
= 1;
foreach
my
$expected_token
(
sort
keys
%{
$current_state
->{ACTIONS} } ) {
if
(
$is_first_expected
) {
$is_first_expected
= 0;
$expected_tokens
.=
$expected_token
.
"\n"
;
}
else
{
$expected_tokens
.=
q{ }
.
$expected_token
.
"\n"
;
}
}
die
"\n"
.
'ERROR ECOPARP00, RPERL PARSER, RPERL SYNTAX ERROR'
.
"\n"
.
'Failed RPerl grammar syntax check with the following information:'
.
"\n\n"
.
' File Name: '
.
$rperl_source__file_name
.
"\n"
.
' Line Number: '
.
$line_number
.
"\n"
.
' Unexpected Token: '
.
$value
.
"\n"
.
' Expected Token(s): '
.
$expected_tokens
.
$helpful_hint
.
"\n"
;
}
sub
rperl_source__parse {
{
my
void
$RETURN_TYPE
};
(
my
string
$rperl_source__file_name
) =
@ARG
;
RPerl::verbose(
'PARSE PHASE 2: Parse RPerl syntax... '
);
my
object
$eyapp_parser
= RPerl::Grammar->new();
$eyapp_parser
->{rperl_source__file_name} =
$rperl_source__file_name
;
$eyapp_parser
->YYSlurpFile(
$rperl_source__file_name
);
my
object
$rperl_ast
=
$eyapp_parser
->YYParse(
yydebug
=> 0x00,
yyerror
=> \
&rperl_grammar_error
);
RPerl::verbose(
' done.'
.
"\n"
);
return
(
$rperl_ast
);
}
sub
rperl_ast__dump {
{
my
string
$RETURN_TYPE
};
(
my
object
$rperl_ast
) =
@ARG
;
$Data::Dumper::Indent
= 1;
my
string
$rperl_ast_dumped
= Dumper(
$rperl_ast
);
$Data::Dumper::Indent
= 2;
$rperl_ast_dumped
=~ s/[ ]{2}/ /gxms;
my
string
$replacee
;
my
string
$replacer
;
foreach
my
string
$rule
(
sort
keys
%{
$RPerl::Grammar::RULES
} ) {
$replacee
=
q{'}
.
$rule
.
q{'}
;
$replacer
=
q{'}
.
$rule
. ' ISA
' . $RPerl::Grammar::RULES->{$rule} . q{'
};
$rperl_ast_dumped
=~ s/
$replacee
/
$replacer
/gxms;
}
return
$rperl_ast_dumped
;
}
sub
rperl_rule__replace {
{
my
string
$RETURN_TYPE
};
(
my
string
$rperl_rule_string
) =
@ARG
;
my
string
$replacer
;
foreach
my
string
$rule
(
sort
keys
%{
$RPerl::Grammar::RULES
} ) {
if
(
$RPerl::Grammar::RULES
->{
$rule
} ne
'RPerl::NonGenerator'
) {
$replacer
=
q{(}
.
$rule
.
' ISA '
.
$RPerl::Grammar::RULES
->{
$rule
} .
q{)}
;
$replacer
=~ s/RPerl:://gxms;
$rperl_rule_string
=~ s/
$rule
/
$replacer
/gxms;
}
}
return
$rperl_rule_string
;
}
1;