############################################################################ # # File: iundecl.icn # # Subject: Program to find undeclared Icon identifiers # # Authors: Robert J. Alexander and Ralph E. Griswold # # Date: August 14, 1996 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # This program invokes icont to find undeclared variables in an Icon # source program. The output is in the form of a "local" declaration, # preceded by a comment line that identifies that procedure and file # name from whence it arose. Beware that undeclared variables aren't # necessarily local, so any which are intended to be global must be # removed from the generated list. # # Multiple files can be specified as arguments, and will be processed # in sequence. A file name of "-" represents the standard input file. # If there are no arguments, standard input is processed. # # The program works only if procedures are formatted such that the # keywords "procedure" and "end" are the first words on their # respective lines. # # Only for UNIX, since the "p" (pipe) option of open() is used. # ############################################################################ # # Requires: UNIX # ############################################################################ # # Links: io # ############################################################################ link io procedure main(arg) local f, fn, line, names, p, sep, t, argstring, undeclared, pn # # Process command line file names. # if *arg = 0 then arg := ["-"] # if no arguments, standard input # # Build a set of all the undeclared identifiers. # argstring := "" every argstring ||:= " " || !arg p := open("icont -s -u -o /dev/null 2>&1" || argstring,"p") | stop("popen failed") undeclared := set() while line := read(p) do line ? { if find("version mismatch") then { write(&errout, line) exit() } if find("undeclared identifier") then tab(find("\"") + 1) & insert(undeclared,tab(find("\""))) } close(p) # # Loop through files to process individual procedures. # every fn := !arg do { f := if fn == "-" then &input else { fn := \suffix(fn)[1] || ".icn" open(fn) | stop("Can't open input file \"",fn,"\"") } # # Loop to process lines of file (in string scanning mode). # while line := read(f) do line ? { if tab(many(' \t')) | "" & ="procedure" & tab(many(' \t')) then { t := open("undeclared_tmp.icn","w") | stop("Can't open work file") write(t,line) while line := read(f) do line ? { write(t,line) if tab(many(' \t')) | "" & ="end" & many(' \t') | pos(0) then break } close(t) # # Now we have an isolated Icon procedure -- invoke icont to # determine its undeclared variables. # p := open("icont -s -u -o /dev/null 2>&1 undeclared_tmp.icn","p") | stop("popen failed") names := [] while line := read(p) do line ? if find("undeclared identifier") then tab(find("\"") + 1) & put(names,member(undeclared,tab(find("\"")))) close(p) # # Output the declaration. # pn := "\"" || tab(upto(' \t(')) || "\"" || if *arg > 1 then " (" || fn || ")" else "" if *names = 0 then write("# ",pn," is OK") else { write("# Local declarations for procedure ",pn) sep := " local " every writes(sep,!sort(names)) do sep := ", " write() } } } # # Close this input file. # close(f) } remove("undeclared_tmp.icn") end