3 #@ grammar2latex: Generate typesetted LaTeX grammar from Prop's report.
4 #@ usage: grammar2latex [options] <report-file>
6 #@ [-S] Generate section only
7 #@ [-t] Emit output at stdout
9 #@ [-o<output-file>] Send output to a specified file
10 #@ [-D] Depth first presentation
11 #@ [-B] Breadth first presentation
16 $BAD = Getopts
("hStvo:DB");
19 $SECTION_ONLY = $opt_S;
21 $OUTPUT_FILE = $opt_o;
22 $DEPTH_FIRST = 0 if $opt_B;
24 print_help_and_die
() if (!$BAD || $HELP || $#ARGV != 0);
26 ###############################################################################
30 ###############################################################################
31 $GRAMMAR2LATEX_VERSION="0.1";
33 $DATE=`date`; chop($DATE);
34 $DOCUMENT_STYLE = "\\documentclass{article}";
36 $STARTRC = "~$USER/.grammar2latex";
37 $INPUT_FILE = $ARGV[0];
38 $QUOTED_INPUT_FILE = quote
($INPUT_FILE);
39 $SECTION = "\\section";
40 $SUBSECTION = "\\subsection";
41 $OUTPUT_FILE = "${INPUT_FILE}.tex" if ! $OUTPUT_FILE;
42 $NON_TERMINAL_FONT = "\\nonterm";
43 $EPSILON = '\epsilon';
45 $CHARACTERS_PER_LINE = 66;
46 $CHARACTERS_PER_PRODUCTION = 40;
47 $HTTP = quote
('http://chimera.cs.nyu.edu:8888/~leunga');
48 $EMAIL = quote
('leunga@valis.cs.nyu.edu');
51 \setlength{\textwidth}{6.5in}
52 \setlength{\evensidemargin}{-.2in}
53 \setlength{\oddsidemargin}{-.2in}
54 \setlength{\textheight}{9.00in}
55 \setlength{\topmargin}{-0.5in}
56 \setlength{\parskip}{2mm}
57 \setlength{\parindent}{0mm}
59 if ($OUTPUT_FILE =~ /^.*\/([^\
/]+)$/)
62 require "$STARTRC" if (-r
$STARTRC);
64 ###############################################################################
66 # Setup data structures.
68 ###############################################################################
81 ###############################################################################
85 ###############################################################################
86 open(INPUT
,$INPUT_FILE) || die("$0: $!: $INPUT_FILE\n");
90 if (/^{\d+}\s+(\<.*\>)\s+->\s+(.*)\s+/) # A special production
93 if ($lhs eq '<start>')
94 { $non_terminals{$lhs} = 0;
95 my($toks) = split_rhs
($rhs);
96 $P{$lhs}[$Alts{$lhs}++] = $toks;
97 build_graph
("<start>",$toks);
101 if (/^{\d+}\s+(.*)\s+->\s+(.*)\s+/) # A production
104 $non_terminals{$lhs} = $line_no;
105 my($toks) = split_rhs
($rhs);
106 $P{$lhs}[$Alts{$lhs}++] = $toks;
107 build_graph
($lhs,$toks);
108 print STDERR
'.' if $VERBOSE;
111 if (/^{\d+}\s+\|\s+(.*)\s+/) # continuing
113 my($toks) = split_rhs
($rhs);
114 $P{$lhs}[$Alts{$lhs}++] = $toks;
115 build_graph
($lhs,$toks);
118 $number_of_states = $1 if /^Number of states\s+=\s+(\d+)/;
119 $number_of_items = $1 if /^Number of items\s+=\s+(\d+)/;
120 $shift_reduce_conflicts = $1 if /^Number of shift\/reduce conflicts\s
+=\s
+(\d
+)/;
121 $reduce_reduce_conflicts = $1 if /^Number of reduce\/reduce conflicts\s
+=\s
+(\d
+)/;
122 $next_check_size = $1 if /^Max next\/check\s
+=\s
+(\d
+)/;
123 $SYNTAX_CLASS = $1 if /^\[Syntax class (\S+)\]/;
126 ###############################################################################
128 # Check for consistency
130 ###############################################################################
131 if ($SYNTAX_CLASS eq "")
132 { print STDERR
"$0: no syntax class found in file '$INPUT_FILE'\n";
135 $SYNTAX_CLASS = quote
($SYNTAX_CLASS);
137 ###############################################################################
141 ###############################################################################
143 { local(*OUTPUT
) = *STDOUT
; }
145 { open(OUTPUT
,">$OUTPUT_FILE") || die("$0: $!: $OUTPUT_FILE\n");
148 print STDERR
'[Printing header]' if $VERBOSE;
150 print STDERR
'[Building keyword list]' if $VERBOSE;
152 print STDERR
'[Computing index]' if $VERBOSE;
153 compute_nonterm_index
();
154 print STDERR
'[Printing grammar]' if $VERBOSE;
158 print STDOUT
"$OUTPUT_FILE\n";
161 ###############################################################################
163 # Print help and die.
165 ###############################################################################
166 sub print_help_and_die
167 { open (HELP
,$0) || die("$!: $0\n");
169 { print STDERR
$1, "\n" if /^#@ (.*)/;
174 ###############################################################################
178 ###############################################################################
182 $text =~ s/([_%&^$@!{}])/\\\1/g;
183 $text =~ s/\</${dollar}\\langle${dollar}/g;
184 $text =~ s/\>/${dollar}\\rangle${dollar}/g;
185 $text =~ s/~/{\\char126}/g;
189 ###############################################################################
193 ###############################################################################
196 if (! ($text =~ /\|/))
197 { return "\\verb|${text}|"; }
198 if (! ($text =~ /\./))
199 { return "\\verb.${text}."; }
200 if (! ($text =~ /\!/))
201 { return "\\verb!${text}!"; }
202 if (! ($text =~ /\@/))
203 { return "\\verb@${text}@"; }
204 die ("$0: can't verbatimize: $text\n");
207 ###############################################################################
211 ###############################################################################
218 ###############################################################################
220 # Check if a token is a symbol
222 ###############################################################################
225 if ($tok =~ /^"[a-zA-Z_]/) { return 0; }
229 ###############################################################################
231 # Split string into tokens
233 ###############################################################################
237 while (! $text =~ /^\s*$/)
238 { if ($text =~ /^("(([^"]|\\.)*)")\s*(.*)$/)
239 { push @tokens, $1; $text = $4;
240 if (is_symbol
($1)) { $symbols{$2} = 1; }
241 else { $keywords{$2} = 1; }
244 if ($text =~ /^('(([^']|\\.)*)')\s*(.*)$/)
245 { push @tokens, $1; $text = $4; $symbols{$2} = 1; next; }
246 if ($text =~ /^\?\s+(.*)$/)
247 { push @tokens, "<error>"; $text = $1; next; }
248 if ($text =~ /^\$\s+(.*)$/)
249 { push @tokens, "<EOF>"; $text = $1; next; }
250 if ($text =~ /^\<\d+\>\s+(.*)$/)
251 { $text = $1; next; }
252 if ($text =~ /^(\S+)\s+(.*)$/)
253 { push @tokens, $1; $text = $2; $others{$1} = 1; next; }
258 ###############################################################################
260 # Build the dependency graph
262 ###############################################################################
264 { my($lhs,$rhs) = @_;
266 { if (! $children{$lhs}{$i})
267 { push @
{$child_list{$lhs}}, $i;
268 #print STDERR "{$lhs -> $i}";
269 $children{$lhs}{$i} = 1;
274 ###############################################################################
278 ###############################################################################
282 { print OUTPUT
<<EOF;
285 \\title{\\Large \\bf Syntax class $SYNTAX_CLASS\\thanks{
286 Generated from the file ``\\tt $QUOTED_INPUT_FILE'' using
287 {\\em grammar2latex} version $GRAMMAR2LATEX_VERSION on $DATE.
288 {\\em grammar2latex} is part of the {\\sf Prop} tool set.
289 Please visit {\\tt $HTTP} for more details.
294 \\newfont{\\nonterm}{cmssq9}
300 ###############################################################################
304 ###############################################################################
315 ###############################################################################
319 ###############################################################################
321 { print OUTPUT
<<END;
322 ${SECTION}\{Lexical structure}
328 my($line,$k,$s) = "";
329 for $k (sort keys %keywords)
330 { $line = "$line $k";
331 if (length($line) >= $CHARACTERS_PER_LINE)
332 { print OUTPUT
"$line\n"; $line = ""; }
334 if ($line ne "") { print OUTPUT
"$line\n"; }
343 for $s (sort keys %symbols)
344 { $line = "$line $s";
345 if (length($line) >= $CHARACTERS_PER_LINE)
346 { print OUTPUT
"$line\n"; $line = ""; }
348 if ($line ne "") { print OUTPUT
"$line\n"; }
357 for $k (sort keys %others)
358 { if (! $keywords{$k} && ! $symbols{$k} && ! $non_terminals{$k})
359 { $line = "$line $k";
360 if (length($line) >= $CHARACTERS_PER_LINE)
361 { print OUTPUT
"$line\n"; $line = ""; }
364 if ($line ne "") { print OUTPUT
"$line\n"; }
373 ###############################################################################
377 ###############################################################################
378 sub print_diagnostics
381 $SECTION\{Diagonsistics}
384 \\bf Number of states & $number_of_states \\\\
385 \\bf Number of items & $number_of_items \\\\
386 \\bf Shift/reduce conflicts & $shift_reduce_conflicts \\\\
387 \\bf Reduce/reduce conflicts & $reduce_reduce_conflicts \\\\
388 \\bf Next/check table size & $next_check_size \\\\
394 ###############################################################################
396 # Topological sort to determine the non-terminal number
398 ###############################################################################
399 sub compute_nonterm_index
401 my (@ready) = ("<start>");
402 my (%processed) = ();
408 { ($lhs,@rest) = @ready;
411 print STDERR
"+" if $VERBOSE;
412 $non_term_no{$lhs} = $index++;
414 for ($i = 0; $i < $n; $i++)
417 for $child (reverse @
{$child_list{$lhs}})
418 { if ($non_terminals{$child} && ! $processed{$child})
419 { push @ready, $child;
420 $processed{$child} = 1;
425 for $child (@
{$child_list{$lhs}})
426 { if ($non_terminals{$child} && ! $processed{$child})
427 { push @ready, $child;
428 $processed{$child} = 1;
436 ###############################################################################
440 ###############################################################################
452 my (@ready) = ("<start>");
453 my (%processed) = ();
459 { ($lhs,@rest) = @ready;
462 print STDERR
"[$lhs]" if $VERBOSE;
464 if ($lines + $n > $LINES_PER_PAGE)
476 for ($i = 0; $i < $n; $i++)
478 { my($quoted_lhs) = quote
($lhs);
479 my($lhs_label) = $non_term_no{$lhs};
481 "\\langle{\\sf $lhs_label}\\rangle & ", $lhs_label;
482 printf OUTPUT
"\\mbox{${NON_TERMINAL_FONT} %s} ", $quoted_lhs;
483 print OUTPUT
"& ::= &";
485 printf OUTPUT
"& & | &";
487 my(@rhs) = @
{$P{$lhs}[$i]};
488 print OUTPUT
"$EPSILON" if ($#rhs == -1);
490 for ($j = 0; $j <= $#rhs; $j++)
491 { my($tok) = $rhs[$j];
492 $line = "$line $tok";
493 if (length($line) >= $CHARACTERS_PER_PRODUCTION)
495 printf OUTPUT
"\\\\\n& & &\\quad ";
498 if (! $non_terminals{$tok})
499 { printf OUTPUT
"%s", verbatim
($tok); }
501 { my($quoted_tok) = quote
($tok);
502 printf OUTPUT
"\\mbox{${NON_TERMINAL_FONT} %s}", $quoted_tok;
503 if ($non_terminals{$tok})
504 { printf OUTPUT
"_{\\sf %s} ", $non_term_no{$tok};
509 print OUTPUT
"\\\\\n";
514 for $child (reverse @
{$child_list{$lhs}})
515 { if ($non_terminals{$child} && ! $processed{$child})
516 { push @ready, $child;
517 $processed{$child} = 1;
523 for $child (@
{$child_list{$lhs}})
524 { if ($non_terminals{$child} && ! $processed{$child})
525 { push @ready, $child;
526 $processed{$child} = 1;
531 print OUTPUT
"\\\\\n";