#!/usr/bin/perl
use
5.006;
sub
usage {
die
"usage: $0 [ -b bison_executable ] [ file.y ]\n"
}
our
$Verbose
;
BEGIN {
require
'./regen/regen_lib.pl'
; }
my
$bison
=
'bison'
;
if
(
@ARGV
>= 2 and
$ARGV
[0] eq
'-b'
) {
shift
;
$bison
=
shift
;
}
my
$y_file
=
shift
||
'perly.y'
;
usage
unless
@ARGV
==0 &&
$y_file
=~ /\.y$/;
(
my
$h_file
=
$y_file
) =~ s/\.y$/.h/;
(
my
$act_file
=
$y_file
) =~ s/\.y$/.act/;
(
my
$tab_file
=
$y_file
) =~ s/\.y$/.tab/;
(
my
$tmpc_file
=
$y_file
) =~ s/\.y$/tmp.c/;
(
my
$tmph_file
=
$y_file
) =~ s/\.y$/tmp.h/;
die
"$0: must be run on an ASCII system\n"
unless
ord
'A'
== 65;
my
$version
= `
$bison
-V`;
unless
(
$version
) {
die
<<EOF; }
Could not find a version of bison in your path. Please install bison.
EOF
unless
(
$version
=~ /\b(2\.[567]|3\.[0-7])\b/) {
die
<<EOF; }
You have the wrong version of bison in your path; currently versions
2.5-2.7 or 3.0-3.7 are known to work. Try installing
or similar. Your bison identifies itself as:
$version
EOF
$version
= $1;
my_system(
"$bison -d -o $tmpc_file $y_file"
);
open
my
$ctmp_fh
,
'<'
,
$tmpc_file
or
die
"Can't open $tmpc_file: $!\n"
;
my
$clines
;
{
local
$/;
$clines
= <
$ctmp_fh
>; }
die
"failed to read $tmpc_file: length mismatch\n"
unless
length
$clines
== -s
$tmpc_file
;
close
$ctmp_fh
;
my
(
$actlines
,
$tablines
) = extract(
$clines
);
our
%tokens
;
$tablines
.= make_type_tab(
$y_file
,
$tablines
);
my
(
$act_fh
,
$tab_fh
,
$h_fh
) =
map
{
open_new(
$_
,
'>'
, {
by
=> $0,
from
=>
$y_file
});
}
$act_file
,
$tab_file
,
$h_file
;
print
$act_fh
$actlines
;
print
$tab_fh
$tablines
;
unlink
$tmpc_file
;
open
my
$tmph_fh
,
'<'
,
$tmph_file
or
die
"Can't open $tmph_file: $!\n"
;
{
$version
=~ /^(\d+)\.(\d+)/
or
die
"Can't handle bison version format: '$version'"
;
my
(
$v1
,
$v2
) = ($1,$2);
die
"Unexpectedly large bison version '$v1'"
if
$v1
> 99;
die
"Unexpectedly large bison subversion '$v2'"
if
$v2
> 9999;
printf
$h_fh
"#define PERL_BISON_VERSION %2d%04d\n\n"
,
$v1
,
$v2
;
}
my
$endcore_done
= 0;
my
$gather_tokens
= 0;
my
$tokens
;
while
(<
$tmph_fh
>) {
next
if
/YY_PERLYTMP_H/;
print
$h_fh
"#ifdef PERL_CORE\n"
if
$. == 1;
if
(!
$endcore_done
and /YYSTYPE_IS_DECLARED/) {
print
$h_fh
<<h;
#ifdef PERL_IN_TOKE_C
static bool
S_is_opval_token(int type) {
switch (type) {
h
print
$h_fh
<<i for sort grep $tokens{$_} eq 'opval', keys %tokens;
case $_:
i
print
$h_fh
<<j;
return 1;
}
return 0;
}
#endif /* PERL_IN_TOKE_C */
#endif /* PERL_CORE */
j
$endcore_done
= 1;
}
next
if
/^
if
(!
$gather_tokens
) {
$gather_tokens
= 1
if
/^\s* enum \s* yytokentype \s* \{/x;
}
else
{
if
(/^\
$gather_tokens
= 0;
$_
.=
"\n/* Tokens. */\n$tokens"
;
}
else
{
my
(
$tok
,
$val
) = /(\w+) \s* = \s* (\d+)/x;
$tokens
.=
"#define $tok $val\n"
if
$tok
;
}
}
print
$h_fh
$_
;
}
close
$tmph_fh
;
unlink
$tmph_file
;
foreach
(
$act_fh
,
$tab_fh
,
$h_fh
) {
read_only_bottom_close_and_rename(
$_
, [
'regen_perly.pl'
,
$y_file
]);
}
exit
0;
sub
extract {
my
$clines
=
shift
;
my
$tablines
;
my
$actlines
;
$clines
=~ m@
(?:
^/\* \s* Symbol \s+ kind\. \s* \*/\n
)?
enum \s+ yysymbol_kind_t \s* \{
.*?
\} \s* ;\n
typedef \s+ enum \s+ \w+ \s+ \w+ ; \n+
@xms
and
$tablines
.= $&;
my
$last_table
=
$version
>= 3 ?
'yyr2'
:
'yystos'
;
$clines
=~ m@
(?:
^/* YYFINAL[^\n]+\n
)?
\
.*?
$last_table
\[\]\s*=
.*?
}\s*;
@xms
or
die
"Can't extract tables from $tmpc_file\n"
;
$tablines
.= $&;
$clines
=~ m@
switch \s* \( \s* yyn \s* \) \s* { \s*
( .*?
default
: \s* break; \s* )
}
@xms
or
die
"Can't extract actions from $tmpc_file\n"
;
$actlines
= $1;
$actlines
=~ s!\s* /\* \s* Line \s* \d+ \s* of \s* yacc\.c \s* \*/!!gx;
$actlines
=~ s!\s* /\* \s* yacc\.c : \d+ \s* \*/!!gx;
$actlines
=~ s/^
$actlines
=~ s/yyvsp\[(.*?)\]/ps[$1].val/g
or
die
"Can't convert value stack name\n"
;
return
$actlines
.
"\n"
,
$tablines
.
"\n"
;
}
sub
make_type_tab {
my
(
$y_file
,
$tablines
) =
@_
;
my
%just_tokens
;
my
%tokens
;
my
%types
;
my
$default_token
;
open
my
$fh
,
'<'
,
$y_file
or
die
"Can't open $y_file: $!\n"
;
while
(<
$fh
>) {
if
(/(\$\d+)\s*=[^=]/) {
warn
"$y_file:$.: dangerous assignment to $1: $_"
;
}
if
(/__DEFAULT__/) {
m{(\w+) \s* ; \s* /\* \s* __DEFAULT__}x
or
die
"$y_file: can't parse __DEFAULT__ line: $_"
;
die
"$y_file: duplicate __DEFAULT__ line: $_"
if
defined
$default_token
;
$default_token
= $1;
next
;
}
next
unless
/^%(token|type)/;
s/^%((token)|type)\s+<(\w+)>\s+//
or
die
"$y_file: unparseable token/type line: $_"
;
for
(
split
' '
,
$_
) {
$tokens
{
$_
} = $3;
if
($2) {
$just_tokens
{
$_
} = $3;
}
}
$types
{$3} = 1;
}
*tokens
= \
%just_tokens
;
die
"$y_file: no __DEFAULT__ token defined\n"
unless
$default_token
;
$types
{
$default_token
} = 1;
$tablines
=~ /^\Qstatic const char
*const
yytname[] =\E\n
\{\n
(.*?)
^};
/xsm
or
die
"Can't extract yytname[] from table string\n"
;
my
$fields
= $1;
$fields
=~ s{
"((?:[^"
\\]|\\.)+)"}
{
"toketype_"
.
(
defined
$tokens
{$1} ?
$tokens
{$1} :
$default_token
)
}ge;
$fields
=~ s/, \s* (?:0|YY_NULL|YY_NULLPTR) \s* $//x
or
die
"make_type_tab: couldn't delete trailing ',0'\n"
;
return
"\ntypedef enum {\n\t"
.
join
(
", "
,
map
"toketype_$_"
,
sort
keys
%types
)
.
"\n} toketypes;\n\n"
.
"/* type of each token/terminal */\n"
.
"static const toketypes yy_type_tab[] =\n{\n"
.
$fields
.
"\n};\n"
;
}
sub
my_system {
if
(
$Verbose
) {
print
"executing: @_\n"
;
}
system
(
@_
);
if
($? == -1) {
die
"failed to execute command '@_': $!\n"
;
}
elsif
($? & 127) {
die
sprintf
"command '@_' died with signal %d\n"
,
($? & 127);
}
elsif
($? >> 8) {
die
sprintf
"command '@_' exited with value %d\n"
, $? >> 8;
}
}