+# Generate a table, yy_type_tab[], that specifies for each token, what
+# type of value it holds.
+#
+# Read the .y file and extract a list of all the token names and
+# non-terminal names; then scan the string $tablines for the table yytname,
+# which gives the token index of each token/non-terminal; then use this to
+# create yy_type_tab.
+#
+# ie given (in perly.y),
+#
+# %token <opval> A
+# %token <ival> B
+# %type <pval> C
+# %type <opval> D
+#
+# and (in $tablines),
+#
+# yytname[] = { "A" "B", "C", "D", "E" };
+#
+# then return
+#
+# typedef enum { toketype_ival, toketype_opval, toketype_pval } toketypes;
+#
+# static const toketypes yy_type_tab[]
+# = { toketype_opval, toketype_ival, toketype_pval,
+# toketype_opval, toketype_ival }
+#
+# where "E" has the default type. The default type is determined
+# by the __DEFAULT__ comment next to the appropriate union member in
+# perly.y
+
+sub make_type_tab {
+ my ($y_file, $tablines) = @_;
+ 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: $_";
+ $tokens{$_} = $2 for (split ' ', $_);
+ $types{$2} = 1;
+ }
+ 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 \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";
+}
+
+