www.pudn.com > LexYaccProgs.zip > YREF.Y


 
/* YREF.Y: YREF cross reference utility, V1.1 5-1-91 AG 
 
   This is a sample Yacc program which produces complete cross reference 
   listings of Yacc source (.y) files. It is based on the same grammar 
   which has actually been used to implement Yacc itself, and thus might 
   also be useful for other utilities which have to parse Yacc sources. 
   The lexical analyzer for this program can be found in YREFLEX.L. The 
   units YRefTools and YRefTables supply the additional routines and data 
   structures used by the program. To compile the program, issue the 
   commands: 
     yacc yref 
     lex yreflex 
     tpc /m yref 
 
   The YREF program reads and parses a Yacc source file and produces 
   a listing of the source file (with linenumbers), followed by a cross 
   reference table of all (literal and nonterminal) identifiers used in the 
   grammar, in alphabetical order. For each symbol, YREF lists the type of 
   the symbol (if type tags are used), and the corresponding line numbers 
   where the symbol occurs. Line numbers marked with an asterisk denote 
   places where the corresponding symbol is "defined" (the lines where a 
   terminal is introduced in a %token definition, or a nonterminal appears 
   on the left-hand side of a rule). Symbols which do not have at least 
   one defining position are listed as "undefined symbols" at the end of 
   the list; this is useful to check Yacc grammars for "completeness." 
 
   YREF does not handle syntactic errors in the source file; it is 
   assumed that the input file is a syntactically correct Yacc program. 
   If a syntax error is encountered, YREF simply gives an error message 
   indicating the offending position, and exits with return code 1. 
 
   The command line syntax is as follows: 
 
   YREF yacc-file[.Y] [output-file[.REF]] 
 
   Default extensions .Y for the input, and .REF for the output file 
   are supplied automatically. If the output file name is ommitted, 
   it defaults to the name of the input file with new suffix .REF. 
 
*/ 
 
%{ 
 
{$I-} 
 
uses YaccLib, LexLib, Dos, YRefTools, YRefTables; 
 
procedure yyerror ( msg : String ); 
  begin 
    writeln(msg , ' in line ', yylineno, ' at or near `', yytext, ''''); 
  end(*yyerror*); 
 
var tag : Integer;       (* type tag *) 
    symlineno : Integer; (* line number of last identifier *) 
 
procedure scan ( var c : Char ); forward; 
  (* scan for nonempty character, skipping comments *) 
 
procedure skip ( delim : String ); forward; 
  (* skip up to next occurrence of delim *) 
 
procedure search ( delim : String ); forward; 
  (* like skip, but retain found delimiter, and handle embedded strings *) 
 
%} 
 
/* Tokens of the Yacc language: 
 
   Note the use of C_ID to distinguish identifiers which start a new rule. 
   This is necessary because the parser is limited to one-symbol lookahead 
   and hence could not determine whether an identifier is followed by a 
   colon, starting a new rule, or is simply just another nonterminal or 
   token identifier in the right-hand of a rule. Thus the lexical analyzer 
   performs the necessary lookahead, and returns C_ID if the identifier is 
   followed by a colon (skipping blanks and comments), and ID otherwise. */ 
 
%token 
  ID		/* identifiers: {letter}{letter_or_digit}* */ 
  C_ID		/* identifier which forms left side of rule, i.e. is 
		   followed by a colon */ 
  LITERAL       /* literals (strings enclosed in single or double quotes) */ 
  NUMBER	/* nonnegative integers: {digit}+ */ 
  PTOKEN PLEFT PRIGHT PNONASSOC PTYPE PSTART PPREC 
  		/* reserved words: PTOKEN=%token, etc. */ 
  PP		/* source sections separator %% */ 
  LCURL		/* curly braces: %{ and %} */ 
  RCURL 
  ',' ':' ';' '|' '{' '}' '<' '>' '=' 
		/* literals */ 
 
%start grammar 
 
%% 
 
grammar		: defs PP rules aux_procs 
		; 
 
aux_procs	: /* empty: aux_procs is optional */ 
 
		| PP { yyaccept; } 
 
		; 
 
 
defs		: /* empty */ 
 
		| defs def 
 
		; 
 
def		: PSTART ID 
 
		| LCURL { search('%}'); } RCURL 
 
		| PTOKEN tag token_list 
 
		| PLEFT tag token_list 
 
		| PRIGHT tag token_list 
 
		| PNONASSOC tag token_list 
 
		| PTYPE tag nonterm_list 
 
                | PTYPE tag 
 
		; 
 
tag		: /* empty: type tag is optional */ 
				{ tag := 0; } 
		| '<' ID '>' 
                		{ tag := $2; } 
		; 
 
token_list	: token_num 
 
		| token_list token_num 
 
		| token_list ',' token_num 
 
		; 
 
token_num	: LITERAL opt_num 
 
               	| ID 
				{ add_ref($1, symlineno, true); 
                                  set_type($1, tag); } 
                  opt_num 
 
		; 
 
opt_num         : /* empty */ 
 
                | NUMBER 
 
                ; 
 
nonterm_list	: nonterm 
 
		| nonterm_list nonterm 
 
		| nonterm_list ',' nonterm 
 
		; 
 
nonterm		: ID 
				{ add_ref($1, symlineno, false); 
                                  set_type($1, tag); } 
		; 
 
 
rules		: rule1 
 
		| LCURL { search('%}'); } RCURL rule1 
					/* rules section may be prefixed 
					   with `local' Turbo Pascal 
					   declarations */ 
		| rules rule 
 
		; 
 
rule1		: C_ID 
				{ add_ref($1, symlineno, true); } 
		  ':' body prec 
 
		; 
 
rule		: rule1 
 
		| '|' body prec 
 
		; 
 
body		: /* empty */ 
 
		| body LITERAL 
 
		| body ID 
				{ add_ref($2, symlineno, false); } 
                | body action 
 
		; 
 
action		: '{' { search('}'); } '}' 
 
		| '=' { skip(';'); } 
                		/* old language feature; code must be 
				   single statement ending with `;' */ 
		; 
 
prec		: /* empty */ 
 
		| PPREC LITERAL opt_action 
 
		| PPREC ID 
				{ add_ref($2, symlineno, false); } 
		  opt_action 
 
		| prec ';' 
 
		; 
 
opt_action	: /* empty */ 
 
		| action 
 
		; 
 
%% 
 
procedure scan ( var c : Char ); 
  label next; 
  const tab = #9; nl = #10; 
  begin 
next: 
    c := get_char; 
    case c of 
      ' ', tab, nl: goto next; 
      '/': begin 
             c := get_char; 
             if c='*' then 
               begin 
                 skip('*/'); 
                 goto next; 
               end 
             else 
               begin 
                 unget_char(c); 
                 unget_char('/'); 
                 c := '/'; 
               end; 
           end; 
      else unget_char(c); 
    end; 
  end(*scan*); 
 
procedure skip ( delim : String ); 
  var i, j : Integer; c : Char; 
  begin 
    i := 1; 
    while i<=length(delim) do 
      begin 
        c := get_char; 
        if c=delim[i] then 
          inc(i) 
        else if c<>#0 then 
          begin 
            for j := i-1 downto 2 do unget_char(delim[j]); 
            i := 1; 
          end 
        else 
          exit; 
      end; 
  end(*skip*); 
 
procedure search ( delim : String ); 
  var i, j : Integer; c : Char; 
  begin 
    i := 1; 
    while i<=length(delim) do 
      begin 
        c := get_char; 
        if c=delim[i] then 
          inc(i) 
        else if c<>#0 then 
          begin 
            for j := i-1 downto 2 do unget_char(delim[j]); 
            i := 1; 
            if c='''' then 
              (* skip string *) 
              skip(''''); 
          end 
        else 
          exit; 
      end; 
    for i := length(delim) downto 1 do 
      unget_char(delim[i]); 
  end(*search*); 
 
(* Lexical analyzer: *) 
 
{$I YRefLex} 
 
(* Main program: *) 
 
function addExt ( filename, ext : String ) : String; 
  (* add default extension to filename *) 
  var d : DirStr; n : NameStr; e : ExtStr; 
  begin 
    fsplit(filename, d, n, e); 
    if e='' then e := '.'+ext; 
    addExt := d+n+e; 
  end(*addExt*); 
 
function root ( filename : String ) : String; 
  (* return filename with extension stripped off *) 
  var d : DirStr; n : NameStr; e : ExtStr; 
  begin 
    fsplit(filename, d, n, e); 
    root := d+n; 
  end(*root*); 
 
var result, lineno : Integer; 
    yfile, reffile, line : String; 
 
begin 
 
  (* sign-on: *) 
 
  writeln('YREF Version 1.0 [Mar 91], Copyright (c) 1991 by Albert Graef'); 
 
  (* parse command line: *) 
 
  case paramCount of 
    1 : begin 
          yfile   := addExt(paramStr(1), 'y'); 
          reffile := root(paramStr(1))+'.ref'; 
        end; 
    2 : begin 
          yfile   := addExt(paramStr(1), 'y'); 
          reffile := addExt(paramStr(2), 'ref'); 
        end; 
    else 
      begin 
        writeln('Usage: YREF yacc-file[.Y] [output-file[.REF]]'); 
        halt(0); 
      end; 
  end; 
 
  (* open files: *) 
 
  assign(yyinput,  yfile); 
  assign(yyoutput, reffile); 
 
  reset(yyinput); 
  if ioresult<>0 then 
    begin 
      writeln('cannot open file '+yfile); 
      halt(1); 
    end; 
 
  rewrite(yyoutput); 
  if ioresult<>0 then 
    begin 
      writeln('cannot open file '+reffile); 
      halt(1); 
    end; 
 
  (* produce numbered listing: *) 
 
  lineno := 1; 
  while not eof(yyinput) do 
    begin 
      readln(yyinput, line);  
      writeln(yyoutput, lineno:5, ':  ', line); 
      inc(lineno); 
    end; 
 
  close(yyinput); reset(yyinput); 
 
  (* parse: *) 
 
  result := yyparse; 
 
  (* produce cross reference listing *) 
 
  if result=0 then ref_list; 
 
  (* close files: *) 
 
  close(yyinput); close(yyoutput); 
  if result>0 then erase(yyoutput); 
 
  (* terminate: *) 
 
  if (result=0) and (n_undef>0) then 
    writeln(n_undef, ' undefined symbol(s)'); 
 
  halt(result); 
 
end.