This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Merge branch 'embed.fnc' into blead
[perl5.git] / regen / embed_lib.pl
1 #!/usr/bin/perl -w
2 use strict;
3
4 # read embed.fnc and regen/opcodes, needed by regen/embed.pl and makedef.pl
5
6 require 5.004;  # keep this compatible, an old perl is all we may have before
7                 # we build the new one
8
9 # Records the current pre-processor state:
10 my @state;
11 # Nested structure to group functions by the pre-processor conditions that
12 # control when they are compiled:
13 my %groups;
14
15 sub current_group {
16     my $group = \%groups;
17     # Nested #if blocks are effectively &&ed together
18     # For embed.fnc, ordering within the && isn't relevant, so we can
19     # sort them to try to group more functions together.
20     foreach (sort @state) {
21         $group->{$_} ||= {};
22         $group = $group->{$_};
23     }
24     return $group->{''} ||= [];
25 }
26
27 sub add_level {
28     my ($level, $indent, $wanted) = @_;
29     my $funcs = $level->{''};
30     my @entries;
31     if ($funcs) {
32         if (!defined $wanted) {
33             @entries = @$funcs;
34         } else {
35             foreach (@$funcs) {
36                 if ($_->[0] =~ /[AC]/) { # 'C' is like 'A' for our purposes
37                                          # here
38                     push @entries, $_ if $wanted eq 'A';
39                 } elsif ($_->[0] =~ /E/) {
40                     push @entries, $_ if $wanted eq 'E';
41                 } else {
42                     push @entries, $_ if $wanted eq '';
43                 }
44             }
45         }
46         @entries = sort {$a->[2] cmp $b->[2]} @entries;
47     }
48     foreach (sort grep {length $_} keys %$level) {
49         my @conditional = add_level($level->{$_}, $indent . '  ', $wanted);
50         push @entries,
51             ["#${indent}if $_"], @conditional, ["#${indent}endif"]
52                 if @conditional;
53     }
54     return @entries;
55 }
56
57 sub setup_embed {
58     my $prefix = shift || '';
59     open IN, '<', $prefix . 'embed.fnc' or die $!;
60
61     my @embed;
62     my %seen;
63     my $macro_depth = 0;
64
65     while (<IN>) {
66         chomp;
67         next if /^:/;
68         next if /^$/;
69         while (s|\\$||) {
70             $_ .= <IN>;
71             chomp;
72         }
73         s/\s+$//;
74         my @args;
75         if (/^\s*(#|$)/) {
76             @args = $_;
77         }
78         else {
79             @args = split /\s*\|\s*/, $_;
80         }
81         if (@args == 1) {
82             if ($args[0] !~ /^#\s*(?:if|ifdef|ifndef|else|endif)/) {
83                 die "Illegal line $. '$args[0]' in embed.fnc";
84             }
85             $macro_depth++ if $args[0] =~/^#\s*if(n?def)?\b/;
86             $macro_depth-- if $args[0] =~/^#\s*endif\b/;
87             die "More #endif than #if in embed.fnc:$." if $macro_depth < 0;
88         }
89         else  {
90             die "Illegal line (less than 3 fields) in embed.fnc:$.: $_"
91                 unless @args >= 3;
92             my $name = $args[2];
93             # only check for duplicates outside of #if's - otherwise
94             # they may be alternate definitions of the same function
95             if ($macro_depth == 0) {
96                 die "Duplicate function name: '$name' in embed.fnc:$."
97                     if exists $seen{$name};
98             }
99             $seen{$name} = 1;
100         }
101
102         push @embed, \@args;
103     }
104     die "More #if than #endif by the end of embed.fnc" if $macro_depth != 0;
105
106     close IN or die "Problem reading embed.fnc: $!";
107
108     open IN, '<', $prefix . 'regen/opcodes' or die $!;
109     {
110         my %syms;
111
112         while (<IN>) {
113             chomp;
114             next unless $_;
115             next if /^#/;
116             my $check = (split /\t+/, $_)[2];
117             next if $syms{$check}++;
118
119             # These are all indirectly referenced by globals.c.
120             push @embed, ['pR', 'OP *', $check, 'NN OP *o'];
121         }
122     }
123     close IN or die "Problem reading regen/opcodes: $!";
124
125     # Cluster entries in embed.fnc that have the same #ifdef guards.
126     # Also, split out at the top level the three classes of functions.
127     # Output structure is actually the same as input structure - an
128     # (ordered) list of array references, where the elements in the
129     # reference determine what it is - a reference to a 1-element array is a
130     # pre-processor directive, a reference to 2+ element array is a function.
131
132     my $current = current_group();
133
134     foreach (@embed) {
135         if (@$_ > 1) {
136             push @$current, $_;
137             next;
138         }
139         $_->[0] =~ s/^#\s+/#/;
140         $_->[0] =~ /^\S*/;
141         $_->[0] =~ s/^#ifdef\s+(\S+)/#if defined($1)/;
142         $_->[0] =~ s/^#ifndef\s+(\S+)/#if !defined($1)/;
143         if ($_->[0] =~ /^#if\s*(.*)/) {
144             push @state, $1;
145         } elsif ($_->[0] =~ /^#else\s*$/) {
146             die "Unmatched #else in embed.fnc" unless @state;
147             $state[-1] = "!($state[-1])";
148         } elsif ($_->[0] =~ m!^#endif\s*(?:/\*.*\*/)?$!) {
149             die "Unmatched #endif in embed.fnc" unless @state;
150             pop @state;
151         } else {
152             die "Unhandled pre-processor directive '$_->[0]' in embed.fnc";
153         }
154         $current = current_group();
155     }
156
157     return ([add_level(\%groups, '')],
158             [add_level(\%groups, '', '')],    # core
159             [add_level(\%groups, '', 'E')],   # ext
160             [add_level(\%groups, '', 'A')]);  # api
161 }
162
163 1;