Skip to content

Commit addb8b2

Browse files
committed
ParseXS: refactor: add Node::C_part
(This commit is part of a series which will extend the AST parse tree from just representing individual XSUBs to representing the whole XS file.) Add these new AST node types: ExtUtils::ParseXS::Node::C_part; ExtUtils::ParseXS::Node::C_part_POD; ExtUtils::ParseXS::Node::C_part_code; and add this method: ExtUtils::ParseXS::Node::is_xs_module_line() These are collectively used to parse and hold the "C" part of the XS file - i.e. everything that comes before the first MODULE line. A C_part node has children consisting of C_part_POD and C_part_code nodes.
1 parent ae631d9 commit addb8b2

File tree

2 files changed

+198
-63
lines changed

2 files changed

+198
-63
lines changed

dist/ExtUtils-ParseXS/lib/ExtUtils/ParseXS.pm

Lines changed: 9 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -408,12 +408,6 @@ sub process_file {
408408
$self->{config_optimize} = $Options{optimize};
409409

410410

411-
my $AST = $self->{AST} = ExtUtils::ParseXS::Node::XS_file->new();
412-
$AST->parse($self)
413-
or $self->death("Failed to parse XS file\n");
414-
415-
$AST->as_code($self);
416-
417411
# Open the input file (using $self->{in_filename} which
418412
# is a basename'd $Options{filename} due to chdir above)
419413
{
@@ -424,72 +418,24 @@ sub process_file {
424418
or die "cannot open $self->{in_filename}: $!\n";
425419
}
426420

427-
# ----------------------------------------------------------------
428-
# Process the first (C language) half of the XS file, up until the first
429-
# MODULE: line
430-
# ----------------------------------------------------------------
431-
432-
FIRSTMODULE:
433-
while (readline($self->{in_fh})) {
434-
if (/^=/) {
435-
my $podstartline = $.;
436-
do {
437-
if (/^=cut\s*$/) {
438-
# We can't just write out a /* */ comment, as our embedded
439-
# POD might itself be in a comment. We can't put a /**/
440-
# comment inside #if 0, as the C standard says that the source
441-
# file is decomposed into preprocessing characters in the stage
442-
# before preprocessing commands are executed.
443-
# I don't want to leave the text as barewords, because the spec
444-
# isn't clear whether macros are expanded before or after
445-
# preprocessing commands are executed, and someone pathological
446-
# may just have defined one of the 3 words as a macro that does
447-
# something strange. Multiline strings are illegal in C, so
448-
# the "" we write must be a string literal. And they aren't
449-
# concatenated until 2 steps later, so we are safe.
450-
# - Nicholas Clark
451-
print("#if 0\n \"Skipped embedded POD.\"\n#endif\n");
452-
printf("#line %d \"%s\"\n", $. + 1, escape_file_for_line_directive($self->{in_pathname}))
453-
if $self->{config_WantLineNumbers};
454-
next FIRSTMODULE;
455-
}
456-
457-
} while (readline($self->{in_fh}));
458-
459-
# At this point $. is at end of file so die won't state the start
460-
# of the problem, and as we haven't yet read any lines &death won't
461-
# show the correct line in the message either.
462-
die ("Error: Unterminated pod in $self->{in_filename}, line $podstartline\n")
463-
unless $self->{lastline};
464-
}
421+
my $AST = $self->{AST} = ExtUtils::ParseXS::Node::XS_file->new();
422+
$AST->parse($self)
423+
or $self->death("Failed to parse XS file\n");
465424

466-
last if ($self->{PACKAGE_name}, $self->{PREFIX_pattern}) =
467-
/^MODULE\s*=\s*[\w:]+(?:\s+PACKAGE\s*=\s*([\w:]+))?(?:\s+PREFIX\s*=\s*(\S+))?\s*$/;
425+
# At this point, $_ should hold the first MODULE line
468426

469-
print $_;
470-
}
471-
472-
unless (defined $_) {
473-
warn "Didn't find a 'MODULE ... PACKAGE ... PREFIX' line\n";
474-
exit 0; # Not a fatal error for the caller process
475-
}
427+
$self->{lastline} = $_;
428+
$self->{lastline_no} = $.;
429+
$self->{XS_parse_stack_top_if_idx} = 0;
430+
my $cpp_next_tmp_define = 'XSubPPtmpAAAA';
476431

477-
print 'ExtUtils::ParseXS::CountLines'->end_marker, "\n"
478-
if $self->{config_WantLineNumbers};
432+
$AST->as_code($self);
479433

480434
standard_XS_defs();
481435

482436
print 'ExtUtils::ParseXS::CountLines'->end_marker, "\n"
483437
if $self->{config_WantLineNumbers};
484438

485-
$self->{lastline} = $_;
486-
$self->{lastline_no} = $.;
487-
488-
$self->{XS_parse_stack_top_if_idx} = 0;
489-
490-
my $cpp_next_tmp_define = 'XSubPPtmpAAAA';
491-
492-
493439
# ----------------------------------------------------------------
494440
# Main loop: for each iteration, read in a paragraph's worth of XSUB
495441
# definition or XS/CPP directives into @{ $self->{line} }, then try to

dist/ExtUtils-ParseXS/lib/ExtUtils/ParseXS/Node.pm

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,20 @@ sub parse_keywords {
362362
return @kids;
363363
}
364364

365+
# return (module, package, prefix) values if the line
366+
# is a valid 'MODULE = ...' line
367+
368+
sub is_xs_module_line {
369+
my __PACKAGE__ $self = shift;
370+
my $line = shift;
371+
372+
$line =~
373+
/^ MODULE \s* = \s* [\w:]+
374+
(?: \s+ PACKAGE \s* = \s* ( [\w:]+ ) )?
375+
(?: \s+ PREFIX \s* = \s* ( \S+ ) )?
376+
\s* $/x;
377+
}
378+
365379

366380
sub as_code { }
367381

@@ -374,6 +388,7 @@ package ExtUtils::ParseXS::Node::XS_file;
374388

375389
BEGIN { $build_subclass->(
376390
'preamble', # Node::preamble object which emits preamble C code
391+
'C_part', # the C part of the XS file, before the first MODULE
377392
)};
378393

379394
sub parse {
@@ -392,6 +407,15 @@ sub parse {
392407
or return;
393408
push @{$self->{kids}}, $preamble;
394409

410+
# Process the first (C language) half of the XS file, up until the first
411+
# MODULE: line
412+
413+
my $C_part = ExtUtils::ParseXS::Node::C_part->new();
414+
$self->{C_part} = $C_part;
415+
$C_part->parse($pxs, $self)
416+
or return;
417+
push @{$self->{kids}}, $C_part;
418+
395419
1;
396420
}
397421

@@ -448,6 +472,171 @@ EOM
448472
}
449473

450474

475+
# ======================================================================
476+
477+
package ExtUtils::ParseXS::Node::C_part;
478+
479+
# A node representing the C part of the XS file - i.e. everything
480+
# before the first MODULE line
481+
482+
BEGIN { $build_subclass->(
483+
)};
484+
485+
sub parse {
486+
my __PACKAGE__ $self = shift;
487+
my ExtUtils::ParseXS $pxs = shift;
488+
489+
$self->{line_no} = 1;
490+
$self->{file} = $pxs->{in_pathname};
491+
492+
# Read in lines until the first MODULE line, creating a list of
493+
# Node::C_part_code and Node::C_part_POD nodes as children.
494+
# Returns with $_ holding the (unprocessed) next line (or undef for
495+
# EOF)
496+
497+
$_ = readline($pxs->{in_fh});
498+
499+
while (defined $_) {
500+
return 1 if $self->is_xs_module_line($_);
501+
502+
my $node =
503+
/^=/ ? ExtUtils::ParseXS::Node::C_part_POD->new()
504+
: ExtUtils::ParseXS::Node::C_part_code->new();
505+
506+
# Read in next block of code or POD lines
507+
$node->parse($pxs)
508+
or return;
509+
push @{$self->{kids}}, $node;
510+
}
511+
512+
warn "Didn't find a 'MODULE ... PACKAGE ... PREFIX' line\n";
513+
exit 0; # Not a fatal error for the caller process
514+
}
515+
516+
517+
sub as_code {
518+
my __PACKAGE__ $self = shift;
519+
my ExtUtils::ParseXS $pxs = shift;
520+
521+
$_->as_code($pxs, $self) for @{$self->{kids}};
522+
523+
print 'ExtUtils::ParseXS::CountLines'->end_marker, "\n"
524+
if $pxs->{config_WantLineNumbers};
525+
}
526+
527+
528+
# ======================================================================
529+
530+
package ExtUtils::ParseXS::Node::C_part_POD;
531+
532+
# A node representing a section of POD within the C part of the XS file
533+
534+
BEGIN { $build_subclass->(
535+
'pod_lines', # array of lines containing pod, including start and end
536+
# '=foo' lines
537+
)};
538+
539+
sub parse {
540+
my __PACKAGE__ $self = shift;
541+
my ExtUtils::ParseXS $pxs = shift;
542+
543+
$self->{line_no} = $.;
544+
$self->{file} = $pxs->{in_pathname};
545+
546+
# This method is called with $_ holding the first line of POD
547+
# and returns with $_ holding the (unprocessed) next line
548+
549+
do {
550+
push @{$self->{pod_lines}}, $_;
551+
if (/^=cut\s*$/) {
552+
$_ = readline($pxs->{in_fh});
553+
return 1;
554+
}
555+
} while (readline($pxs->{in_fh}));
556+
557+
# At this point $. is at end of file so die won't state the start
558+
# of the problem, and as we haven't yet read any lines &death won't
559+
# show the correct line in the message either.
560+
die ( "Error: Unterminated pod in $pxs->{in_filename}, "
561+
. "line $self->{line_no}\n");
562+
}
563+
564+
565+
sub as_code {
566+
my __PACKAGE__ $self = shift;
567+
my ExtUtils::ParseXS $pxs = shift;
568+
569+
# Emit something in the C file to indicate that a section of POD has
570+
# been elided, while maintaining the correct lines numbers using
571+
# #line.
572+
#
573+
# We can't just write out a /* */ comment, as our embedded POD might
574+
# itself be in a comment. We can't put a /**/ comment inside #if 0, as
575+
# the C standard says that the source file is decomposed into
576+
# preprocessing characters in the stage before preprocessing commands
577+
# are executed.
578+
#
579+
# I don't want to leave the text as barewords, because the spec isn't
580+
# clear whether macros are expanded before or after preprocessing
581+
# commands are executed, and someone pathological may just have
582+
# defined one of the 3 words as a macro that does something strange.
583+
# Multiline strings are illegal in C, so the "" we write must be a
584+
# string literal. And they aren't concatenated until 2 steps later, so
585+
# we are safe.
586+
# - Nicholas Clark
587+
588+
print ExtUtils::ParseXS::Q(<<"EOF");
589+
|#if 0
590+
| "Skipped embedded POD."
591+
|#endif
592+
EOF
593+
594+
printf("#line %d \"%s\"\n",
595+
$self->{line_no} + @{$self->{pod_lines}},
596+
ExtUtils::ParseXS::Utilities::escape_file_for_line_directive(
597+
$pxs->{in_pathname}))
598+
if $pxs->{config_WantLineNumbers};
599+
}
600+
601+
602+
# ======================================================================
603+
604+
package ExtUtils::ParseXS::Node::C_part_code;
605+
606+
# A node representing a section of C code within the C part of the XS file
607+
608+
BEGIN { $build_subclass->(
609+
'code_lines', # array of lines containing C code
610+
)};
611+
612+
sub parse {
613+
my __PACKAGE__ $self = shift;
614+
my ExtUtils::ParseXS $pxs = shift;
615+
616+
$self->{line_no} = $.;
617+
$self->{file} = $pxs->{in_pathname};
618+
619+
# This method is called with $_ holding the first line of C code
620+
# and returns with $_ holding the (unprocessed) next line
621+
622+
do {
623+
return 1 if $self->is_xs_module_line($_);
624+
return 1 if /^=/;
625+
push @{$self->{code_lines}}, $_;
626+
} while (readline($pxs->{in_fh}));
627+
628+
1;
629+
}
630+
631+
sub as_code {
632+
my __PACKAGE__ $self = shift;
633+
my ExtUtils::ParseXS $pxs = shift;
634+
635+
print @{$self->{code_lines}};
636+
}
637+
638+
639+
451640
# ======================================================================
452641

453642
package ExtUtils::ParseXS::Node::xsub;

0 commit comments

Comments
 (0)