rsalz@bbn.com (Rich Salz) (02/23/88)
Submitted-by: Nick Crossley <ccicpg!nick@uunet.uu.net> Posting-number: Volume 13, Issue 61 Archive-name: check This program has been posted as a result of a discussion in comp.lang.c concerning some possible errors in writing C programs. It is a C syntax checker, to be used as an adjunct to lint, not a replacement. It makes no attempt to duplicate any warning given by the standard lint, and is not at all forgiving about simple syntax errors. Warnings include things like "if (a = b);" nested comments, and dangling else's. See the README file for more details. If anyone implements the software metrics or upgrades the grammar significantly, please send the results back to me! Nick Crossley CCI email: ..!uunet!ccicpg!nick #! /bin/sh # This is a Shell Archive, containing the files/directories :- # Makefile README check.1 check.c check.h lex.l main.c metric.c parse.awk # parse.y symbol.c tables.sh test.c tree.c # To unpack it, execute it with /bin/sh # If run with standard input from a terminal, you will be prompted # for the name of a directory to hold the unpacked files. # Otherwise, it will unpack into the current directory. if [ -t 0 ] then while : do echo 'Directory for output : \c' read outdir if [ X$outdir = X ] then outdir=. break elif [ -d ${outdir} ] then break elif echo "$outdir does not exist; create it? \c" read reply [ `expr "$reply" : '[Yy].*'` = 0 ] then continue elif mkdir $outdir then break else echo "Cannot create $outdir" 1>&2 continue fi done cd $outdir fi sed -e 's/^XX//' <<'====End of SHAR Makefile====' >Makefile XX# Makefile for C checker XX# Has been used on SysV and BSD/SysV mixtures XX# I don't guarantee it will work on real BSD XX# Set the following macros :- XX# CHECK if you want to change the program name XX# LIB for the installation directory XX# CFLAGS for cc flags XX# LDFLAGS for final cc flags XX# BLIBS for any other libraries to scan XX# LFLAGS for lex flags XX# YFLAGS for yacc flags XX# LINTF for lint flags XX XXCHECK = check XXLIB = bin XXCFLAGS = -O XXLDFLAGS = XXBLIBS = XXLFLAGS = XXYFLAGS = -vd XXLINTF = XX XXOBJ = main.o lex.o parse.o symbol.o tree.o check.o metric.o XXSRC = main.c lex.l parse.y symbol.c tree.c check.c metric.c XX XXbuild : $(CHECK) XX XXinstall : $(CHECK) XX cp $(CHECK) $(LIB) XX -touch install XX XX$(CHECK): $(OBJ) XX $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(OBJ) $(BLIBS) -lm XX XX$(OBJ) : y.xxx.h check.h XX XXtree.o : tables.h XX XXtables.h: y.xxx.h tables.sh XX tables.sh XX XXy.xxx.h : y.tab.h XX -cmp -s y.xxx.h y.tab.h || cp y.tab.h y.xxx.h XX XXy.tab.h : parse.c XX XX.y.c :; $(YACC) $(YFLAGS) $< XX mv y.tab.c $*.c XX parse.awk & XX XX.y.o :; $(YACC) $(YFLAGS) $< XX mv y.tab.c $*.c XX parse.awk & XX $(CC) $(CFLAGS) -c $*.c XX XX.l.o :; $(LEX) $(LFLAGS) $< XX mv lex.yy.c $*.c XX $(CC) $(CFLAGS) -c $*.c XX XX.c.o :; $(CC) $(CFLAGS) -c $*.c XX XXlist : $(SRC) Makefile XX pr -n -f -h "C Checker" $? | lp XX -touch list XX XXlint : $(OBJ:.o=.c) tables.h XX lint $(LINTF) $(OBJ:.o=.c) XX XXclean :; rm -f *.o y.* yacc* parse.c lex.c tables.h XX XXclobber : clean XX rm -f $(CHECK) list install ====End of SHAR Makefile==== if [ "`wc -c <'Makefile'`" != ' 1426' ] then echo 'Unpack for Makefile failed!' exit 1 else echo 'Unpacked Makefile' fi sed -e 's/^XX//' <<'====End of SHAR README====' >README XX C Program Checker XX ----------------- XX XXThis program has been posted as a result of a discussion in comp.lang.c XXconcerning some possible errors in writing C programs. It is a C syntax XXchecker, to be used as an adjunct to lint, not a replacement. It makes XXno attempt to duplicate any warning given by the standard lint, and is not XXat all forgiving about simple syntax errors. It warns about the following XXpossible errors :- XX XX1. Assignment in conditional context XX A warning is issued for any assignment in any conditional context, XX where the programmer might have mistyped = when == was intended. XX Thus, 'if (a=b)' will give a warning, while 'if ((a=b)!=0)' will not. XX (NOTE: I am not saying that people should not write 'if (a=b)' and XX similar constructs - I frequently do - but that on SOME occasions XX programmers make typing errors which are not picked up by lint or cc, XX and are not easily visually seen as errors.) XX XX2. if with neither then nor else XX One of my colleagues once wrote XX if (a == b); XX { XX lots of useful code ... XX } XX and we spent a whole day tracking down why this was not doing as XX we expected before I noticed the extra semicolon. (The condition XX was actually more complicated, so the semicolon was not as obvious, XX and we spent our time working out why the condition did not appear XX to have the right value. We were also working on a machine which XX did not supply adb, only sdb, and that did not work!) XX XX3. I come from an Algol68 background, and I HATE the 'dangling-else' XX language ambiguity in C, Pascal, ...! So, not that I have ever XX seen it as a problem in practice, the program warns about potentially XX ambiguous elses :- XX XX if (a == b) XX if (c == d) XX action1 (); XX else action2 (); XX will generate a warning, while :- XX XX if (a == b) XX { XX if (c == d) XX action1 (); XX else action2 (); XX } XX will not. XX XX4. Nested comment XX I just added this as a result of further net discussion of errors; XX this will give a warning if '/*' is seen inside a comment, so that XX errors such as that below are noticed. XX a = 1; /* initialise a XX b = 2; /* initialise b */ XX The current line number and that where the outer comment started are XX included in the warning message. XX XX5. Unterminated comment XX If end-of-file is reached while in a comment. This is also noticed XX by both cpp and cc, but check gives the line number where the comment XX started, not just the line number where the error was detected, i.e., XX the last line! XX XX XXThe program uses yacc and lex. The grammar was written by me, and is not a XXcomplete C grammar - some obsolete or weird constructs are (deliberately) not XXsupported. ('Weird' as defined by me!!) No attempt has been made (yet) to XXsupport ANSI C. It is not very fast - profiling shows that most of the time XXis spent in the lex-generated analyser, as one might expect. XX XXThere is some code, mostly commented out, to calculate and print software XXmetrics, but this needs changes to the lexing, parsing and symbol handling XXwhich I have not yet done. If anyone completes this work, or has any improved XXmetrics to suggest, please send it back to me! XX XXAs an example, the output of 'check *.c' in the check source directory is :- XXlex.c: assignment in conditional context at or near line 163 XXlex.c: assignment in conditional context at or near line 588 XXlex.c: assignment in conditional context at or near line 616 XXsymbol.c: assignment in conditional context at or near line 79 XXtest.c: nested comment, starting at line 20, at or near line 21 XXtest.c: unterminated comment, starting at line 194, at or near line 202 XXtest.c: IF with neither THEN nor ELSE at or near line 93 XXtest.c: potentially mismatched ELSE - use {} at or near line 117 XXtest.c: assignment in conditional context at or near line 132 XX XXThis program is in no way derived from lint sources, which I have not even XXlooked at, and is free of all copyright restrictions. XX XXWed Jan 20 1988 XX XXNick Crossley XXComputer Consoles Inc. XX9801 Muirlands Boulevard, XXIrvine XXCA 92718 XX(714) 458-7282 XXemail: ...!uunet!ccicpg!nick ====End of SHAR README==== if [ "`wc -c <'README'`" != ' 4011' ] then echo 'Unpack for README failed!' exit 1 else echo 'Unpacked README' fi sed -e 's/^XX//' <<'====End of SHAR check.1====' >check.1 XX.TH CHECK 1 XX.SH NAME XXcheck \- a C program checker XX.SH SYNOPSIS XX.B check XX[ option ] ... file ... XX.SH DESCRIPTION XX.I Check\^ XXattempts to find possible errors in C programs. XXIt does not duplicate or replace XX.I lint XXbut finds some constructs about which XX.I lint XXis silent. XXAmong the things that are detected are XXnested comments, XXunterminated comments, XXassignments in conditional contexts, XXif statements with null then and no else XXand XXpotentially ambiguous else statements XX.PP XXFor each such construct a warning is given, XXidentifying the source file and line number where it occurs. XXIf multiple files are being checked, XXthe name of each file is printed as checking starts. XX.PP XXAll leading '-' options are assumed to be C preprocessor directives, XXexcept that an argument '--' ends the options and is otherwise ignored. XXAll following arguments are assumed to be C source file names. XX.SH SEE ALSO XXcpp(1), lint(1). XX.SH BUGS XX.I Check\^ XXis not at all forgiving of simple syntax errors, XXsuch as missing or extra punctuation. XX.PP XXThe grammar (deliberately) does not accept all currently legal C programs; XXsome obsolescent or weird constructs are not allowed. XXNo attempt has yet been made to handle ANSI C. ====End of SHAR check.1==== if [ "`wc -c <'check.1'`" != ' 1193' ] then echo 'Unpack for check.1 failed!' exit 1 else echo 'Unpacked check.1' fi sed -e 's/^XX//' <<'====End of SHAR check.c====' >check.c XX/*----------------------------------------------------------------------*/ XX/* */ XX/* This module contains the actual checking logic */ XX/* */ XX/*----------------------------------------------------------------------*/ XX XX XX#include <stdio.h> XX#include "check.h" XX#include "y.tab.h" XX XX XX/*ARGSUSED*/ XXvoid checknode (depth, parent, branch, np) XXint depth, parent, branch; XXNodePtr np; XX{ XX /* This routine is called as an 'action' by treewalk. */ XX /* It checks for the following things :- */ XX /* 1) assignment in conditional contexts; */ XX /* 2) potentially mismatched ELSE clauses; */ XX /* 3) IFs with no THEN or ELSE. */ XX XX XX if (np->type == Asgn_Op) XX { XX if ((parent == IF && branch == 1) || XX (parent == WHILE && branch == 1) || XX (parent == DO && branch == 2) || XX (parent == FOR && branch == 2) || XX parent == And || parent == Or) XX XX warn ("assignment in conditional context"); XX } XX XX if (np->type == IF && np->f3.ptr == NilNode) /* IF with no ELSE */ XX { XX if (np->f2.ptr == NilNode || XX np->f2.ptr->type == 0 || np->f2.ptr->type == ';') XX warn ("IF with neither THEN nor ELSE"); XX XX if (np->f2.ptr->type==IF && np->f2.ptr->f3.ptr!=NilNode) XX warn ("potentially mismatched ELSE - use {}"); XX } XX} XX XX XXvoid check_prog () XX{ XX /* Start off the checking process */ XX XX walk_prog (checknode); XX} ====End of SHAR check.c==== if [ "`wc -c <'check.c'`" != ' 1304' ] then echo 'Unpack for check.c failed!' exit 1 else echo 'Unpacked check.c' fi sed -e 's/^XX//' <<'====End of SHAR check.h====' >check.h XX/*=========== Header file for C Checker Program ================*/ XX XX#define elif else if XX#define TRUE 1 XX#define FALSE 0 XX#define Printf (void) printf XX#define Fprintf (void) fprintf XX#define NilNode ((NodePtr) 0) XX#define NilSym ((Symbol *) 0) XX XX XX/*=========== Type Definitions ================*/ XX XXtypedef int Token; /* type of Lex tokens */ XX XXtypedef enum { XX Find, /* Find only */ XX Create /* Find and create if required */ XX} Action; XX XXtypedef struct Symbol { /* type of Symbol Table Entry */ XX char *name; XX Token token; XX struct Symbol *next; XX} Symbol; XX XXtypedef struct Node { /* type of Parse Tree Node */ XX short type; /* variant for a node */ XX short line; /* source line number */ XX union u { XX int ival; /* integer value */ XX unsigned uval; /* unsigned value */ XX struct Node *ptr; /* pointer to another node */ XX } f1, f2, f3, f4; /* variable number of fields */ XX} Node, *NodePtr; /* Node and pointer to Node */ XX XX XX/*=========== Parse Tree Node Types ================*/ XX XX#define Seq 1000 /* a sequence of two nodes */ XX#define Type 1001 /* a type specifier */ XX#define Label 1002 /* a labelled statement */ XX#define Pre_Inc 1003 /* ++ (expression) */ XX#define Pre_Dec 1004 /* -- (expression) */ XX#define Post_Inc 1005 /* (expression) ++ */ XX#define Post_Dec 1006 /* (expression) -- */ XX#define Indirect 1007 /* a -> b */ XX#define Addr 1008 /* & id */ XX#define Uplus 1009 /* + expression */ XX#define Uminus 1010 /* - expression */ XX#define Cast 1011 /* (type) expression */ XX#define Size_Type 1012 /* SIZEOF (type) */ XX#define Size_Expr 1013 /* SIZEOF (expression) */ XX#define Error 9999 /* error */ XX XX XX/*=========== External Function Definitions ================*/ XX XXextern void lex_init (); /* Lexical Analyser initialise */ XXextern Token yylex (); /* Lexical Analyser interface */ XX XXextern void sy_init (); /* Initialise symbol table */ XXextern void sy_tidy (); /* Tidy symbol table */ XXextern Symbol *findsym (); /* look up identifier in table */ XXextern char *emalloc (); /* allocate space */ XX XXextern int yyparse (); /* interface to Yacc Parser */ XX XXextern NodePtr new_node (); /* build a parse tree node */ XXextern void check_prog (); /* check the parse tree */ XXextern void tidy_prog (); /* delete the parse tree */ XXextern void walk_prog (); /* walk the parse tree */ XXextern void treeprint (); /* print the parse tree */ XX XXextern void metrics (); /* produce software metrics */ XXextern void met_init (); /* initialise metrics */ XXextern void proc_start (); /* initialise local metrics */ XX XXextern void yyerror (); /* Yacc syntax error report */ XXextern void warn (); /* generate warning messages */ XXextern void error (); /* stop with error message */ XXextern void setline (); /* set line and filename */ XX XX XX/*=========== External Data Definitions ================*/ XX XXextern int yychar; /* current lookahead token */ XXextern char *filename; /* current filename */ XXextern int yylineno; /* current line number */ XXextern FILE *yyin; /* current input file */ XXextern NodePtr Tree; /* Root of Parse Tree */ ====End of SHAR check.h==== if [ "`wc -c <'check.h'`" != ' 3136' ] then echo 'Unpack for check.h failed!' exit 1 else echo 'Unpacked check.h' fi sed -e 's/^XX//' <<'====End of SHAR lex.l====' >lex.l XX%{ XX/*----------------------------------------------------------------------*/ XX/* */ XX/* LEX generated C lexical analyser */ XX/* */ XX/*----------------------------------------------------------------------*/ XX XX#include "check.h" XX#include "y.tab.h" XX XX#define yywrap() (1) XX XXextern Token ident (); XXextern void strings (), comment (); /* forward declarations */ XX%} XX XXD [0-9] XXNUM ({D}+|({D}+"."{D}*)|("."{D}+))([eE][-+]?{D}+)?[lL]? XXHEX 0[xX][0-9a-fA-F]+[lL]? XXID [A-Za-z_][A-Za-z0-9_]* XXASG ([-+*/%&^|]?"=")|"<<="|">>=" XX XX%% XX XX^"#".*"\n" { setline (yytext); } XX{ID} { return ident(); } XX{NUM} { return CONSTANT; } XX{HEX} { return CONSTANT; } XX{ASG} { return Asgn_Op; } XX"\"" { strings ('"'); return CONSTANT; } XX"'" { strings ('\''); return CONSTANT; } XX"<<" { return Shift; } XX">>" { return Shift; } XX"&&" { return And; } XX"||" { return Or; } XX"->" { return Point; } XX"<"|"<="|">="|">" { return Rel_Op; } XX"=="|"!=" { return Eq_Op; } XX"++"|"--" { return IncDec; } XX[ \t\n]+ ; XX"/*" { comment (); } XX. { return yytext[0]; } XX XX%% XX XXToken ident () XX{ XX /* Handle an Identifier or Reserved Word */ XX XX yylval.id = findsym (yytext, IDENTIFIER, Create); XX return yylval.id->token; XX} XX XX XXvoid cwarn (ln, s) XXint ln; XXchar *s; XX{ XX /* Give a warning about a comment, including starting line number */ XX XX XX char msg [120]; XX (void) sprintf (msg, "%s, starting at line %d,", s, ln); XX warn (msg); XX free (msg); XX} XX XX XXvoid comment () XX{ XX /* Swallow the rest of a comment */ XX XX register int c = input (); XX register int startline = yylineno; XX XX while (c) XX { XX if (c == '*') XX { XX if ((c = input()) == '/') return; XX } XX elif (c == '/') XX { XX if ((c = input()) != '*') continue; XX cwarn (startline, "nested comment"); XX } XX else c = input(); XX } XX cwarn (startline, "unterminated comment"); XX} XX XX XXvoid strings (term) XXint term; XX{ XX /* A string terminating with 'term' */ XX XX register int c; XX XX while (c = input ()) XX { XX if (c == term) break; XX elif (c == '\\') (void) input (); XX } XX} XX XX XXvoid lex_init () XX{ XX /* Initialise the Lexical Analyser for a new file */ XX /* lex itself should provide this! */ XX XX yyleng = 0; XX yymorfg = 0; XX yytchar = 0; XX yybgin = yysvec+1; XX yylineno = 1; XX yysptr = yysbuf; XX yyprevious = YYNEWLINE; XX} ====End of SHAR lex.l==== if [ "`wc -c <'lex.l'`" != ' 2254' ] then echo 'Unpack for lex.l failed!' exit 1 else echo 'Unpacked lex.l' fi sed -e 's/^XX//' <<'====End of SHAR main.c====' >main.c XX/*----------------------------------------------------------------------*/ XX/* */ XX/* C Checker Main Entry Point */ XX/* */ XX/*----------------------------------------------------------------------*/ XX XX XX#include <stdio.h> XX#include <ctype.h> XX#include "check.h" XX#include "y.tab.h" XX XX#define MAXNAME 120 XX#define CPP "/lib/cpp -C %s %s" XX XXextern void perror(), exit(); XX XXint errors = 0; /* count of number of errors */ XXchar filebuf [MAXNAME]; /* Buffer for file names */ XXchar *filename; /* Pointer to file name */ XXchar *progname; /* Name of this program */ XX XX/* Really should recode this to avoid using fixed buffer sizes; */ XX/* malloc and realloc are not difficult to use! */ XXchar options [BUFSIZ]; /* Buffer for cpp options */ XX XX XXint main (argc, argv) XXint argc; XXchar *argv[]; XX{ XX /* Main Entry Point to C Checker */ XX /* Check each file as specified by arguments */ XX XX char command [BUFSIZ]; /* for cpp command */ XX register char *opts = options; XX register char *opt_end = & options [BUFSIZ-2]; XX register int pnames; XX register int ptree = FALSE; XX extern char *strncat (); XX extern FILE *popen (); XX XX sy_init (); XX progname = *argv++; XX XX /* Extract C PreProcessor options */ XX XX while (--argc && **argv == '-') XX { XX register char *ap = *argv++; XX XX if (strcmp ("--", ap) == 0) XX { XX /* End of Options */ XX XX break; XX } XX#if YYDEBUG XX elif (strcmp ("-yydebug", ap) == 0) XX { XX /* Turn on Yacc Tracing */ XX XX extern int yydebug; XX yydebug = 1; XX } XX#endif XX elif (strcmp ("-TP", ap) == 0) XX { XX /* Print Parse Tree option (debug) */ XX XX ptree = TRUE; XX } XX else XX { XX /* C PreProcessor option */ XX XX while (*ap && (opts < opt_end)) *opts++ = *ap++; XX *opts++ = ' '; XX } XX } XX *opts++ = ' '; XX *opts++ = '\0'; XX XX pnames = argc - 1; XX while (argc--) XX { XX /* Open a pipe for reading from C PreProcessor */ XX XX filename = *argv++; XX (void) sprintf (command, CPP, options, filename); XX if (pnames > 0) Printf ("%s:\n", filename); XX XX if ((yyin = popen (command, "r")) == NULL) XX { XX perror ("cannot open pipe"); XX exit (1); XX } XX else XX { XX /* Analyse one C source file */ XX XX lex_init (); XX met_init (); XX if (yyparse ()) warn ("fatal syntax error"); XX XX /* Report on results of analysis */ XX XX if (ptree) treeprint (); XX check_prog (); XX metrics ((char *) 0); XX XX /* Tidy up, ready for another source file */ XX XX (void) pclose (yyin); XX tidy_prog (); XX sy_tidy (); XX } XX } XX XX return errors; XX} XX XX XXchar *emalloc (n) XXunsigned n; XX{ XX /*==== Allocate Memory ====*/ XX XX char *p, *malloc (); XX XX p = malloc (n); XX XX if (p == 0) error ("out of memory"); XX XX return p; XX} XX XX XXvoid yyerror (s) XXchar *s; XX{ XX /* Syntax Error from Yacc */ XX XX errors++; XX Fprintf (stderr, "%s: %s at or near line %d, token %d\n", XX filename, s, yylineno, yychar); XX} XX XX XXvoid warn (s) XXchar *s; XX{ XX /* Generate a warning message */ XX XX errors++; XX Fprintf (stderr, "%s: %s at or near line %d\n", XX filename, s, yylineno); XX} XX XX XXvoid error (s) XXchar *s; XX{ XX /* Handle fatal errors */ XX XX warn (s); XX exit (errors); XX} XX XX XXvoid setline (line) XXchar *line; XX{ XX /* Handle a #line directive */ XX XX register char *cp = line; XX register char *fn = filebuf; XX register int lnum = 0; XX XX if (*cp != '#') error ("invalid call to setline"); XX XX while (*cp && !isdigit(*cp)) cp++; XX while (*cp && isdigit (*cp)) lnum = lnum*10 + (*cp++ - '0'); XX while (*cp && *cp != '"') cp++; XX if (lnum) yylineno = lnum; XX XX if (*cp++ == 0) return; XX while (*cp && *cp != '"') *fn++ = *cp++; XX if (fn == filename) return; XX XX *fn = '\0'; XX filename = filebuf; XX} ====End of SHAR main.c==== if [ "`wc -c <'main.c'`" != ' 3532' ] then echo 'Unpack for main.c failed!' exit 1 else echo 'Unpacked main.c' fi sed -e 's/^XX//' <<'====End of SHAR metric.c====' >metric.c XX/*----------------------------------------------------------------------*/ XX/* */ XX/* This module will do software metrics one day... */ XX/* */ XX/*----------------------------------------------------------------------*/ XX XX XX#include <stdio.h> XX#include "check.h" XX#include "y.tab.h" XX XXtypedef struct Metric { XX int sep_nouns; XX int sep_verbs; XX int tot_nouns; XX int tot_verbs; XX int stmts; XX int comments; XX int lines; XX int decisions; XX int knots; XX} Metric; XX XXMetric total, local; XX XX XXvoid swm (m) XXMetric *m; XX{ XX /* This procedure will print the Software Metrics */ XX XX/* Commented out until Lex & Grammar re-written to increment counts ... XX extern double log(); XX XX double vocab, length, volume, diff, effort, lang; XX double decid, comms, layout; XX double mccabe, knots; XX XX vocab = m->sep_verbs + m->sep_nouns; XX length = m->tot_verbs + m->tot_nouns; XX volume = length * log(vocab) / log(2.0); XX diff = (m->sep_verbs*m->tot_nouns)/(2.0*m->sep_nouns); XX effort = volume * diff; XX lang = volume / (diff * diff); XX XX decid = m->decisions / m->stmts; XX comms = m->comments / m->stmts; XX layout = (m->stmts + m->comments) / m->lines; XX XX mccabe = m->decisions + 1; XX knots = m->knots; XX XX Printf ("%8.2g %8.2g %8.2g %8.2g %8.2g %8.2g %8.2g\n", XX volume, diff, effort, lang, XX decid, comms, layout, XX mccabe, knots XX ); XX............................................................ */ XX} XX XX XXvoid metrics (this_proc) XXchar *this_proc; XX{ XX /* Report on the Software Metrics for current procedure, or */ XX /* report on the total for the file if this_proc is null. */ XX XX/* Commented out until Lex re-written to increment counts ... XX if (this_proc) XX { XX Printf ("%12.10s ", this_proc); XX swm (&local); XX } XX else XX { XX Printf ("%12.10s ", "Total:"); XX swm (&total); XX } XX............................................................ */ XX} XX XX XXvoid proc_start () XX{ XX /* Initialise the counts for a procedure */ XX XX local.sep_nouns = 1; XX local.sep_verbs = 1; XX local.tot_nouns = 1; XX local.tot_verbs = 1; XX local.stmts = 1; XX local.comments = 1; XX local.lines = 1; XX local.decisions = 1; XX local.knots = 1; XX} XX XX XXvoid met_init () XX{ XX /* Initialise the counts for a file */ XX XX proc_start (); XX XX total.sep_nouns = 1; XX total.sep_verbs = 1; XX total.tot_nouns = 1; XX total.tot_verbs = 1; XX total.stmts = 1; XX total.comments = 1; XX total.lines = 1; XX total.decisions = 1; XX total.knots = 1; XX} ====End of SHAR metric.c==== if [ "`wc -c <'metric.c'`" != ' 2326' ] then echo 'Unpack for metric.c failed!' exit 1 else echo 'Unpacked metric.c' fi sed -e 's/^XX//' <<'====End of SHAR parse.awk====' >parse.awk XXawk ' XXBEGIN { state = 0; } XX/^state/ { --state; } XX/shift\/reduce/ { state = 2; } XX/reduce\/reduce/ { state = 2; } XX/terminals,/ { state = 2; } XX { if (state > 0) printf "%s\n", $0; } XX XX' y.output >yacclist ====End of SHAR parse.awk==== chmod +x parse.awk if [ "`wc -c <'parse.awk'`" != ' 208' ] then echo 'Unpack for parse.awk failed!' exit 1 else echo 'Unpacked parse.awk' fi sed -e 's/^XX//' <<'====End of SHAR parse.y====' >parse.y XX/* YACC Grammar for C - not very strict. */ XX/* No attempt is made to conform to or accept */ XX/* ANSI C yet. Some obsolete constructs are */ XX/* not supported (deliberately). */ XX/* */ XX/* If your terminal can handle it, view/edit this */ XX/* file in 120 or 132 column mode, as all the */ XX/* actions start in column 72. */ XX/* */ XX/* Note that TYPEDEF names must be recognised as */ XX/* such, and return a different token from the */ XX/* Lexical Analyser. */ XX/* */ XX/* The Actions build a Parse Tree. */ XX XX%{ XX#include <stdio.h> XX#include "check.h" XX XX XX /*---------- Macros for Tree Building ----------*/ XX XX XX#define Z (NodePtr) 0 XX#define node0(t) new_node (t, Z, Z, Z, Z); XX#define node1(t,a) new_node (t, a, Z, Z, Z); XX#define node2(t,a,b) new_node (t, a, b, Z, Z); XX#define node3(t,a,b,c) new_node (t, a, b, c, Z); XX#define node4(t,a,b,c,d) new_node (t, a, b, c, d); XX XX%} XX XX%union { /* Type for Parser Stack */ XX Symbol *id; /* Name for ID, or string */ XX int ival; /* integer constants */ XX unsigned uval; /* octal & hex constants */ XX NodePtr ptr; /* pointer to Parse Tree Node */ XX} XX XX%token <id> IDENTIFIER TYPENAME XX XX%token <ival> CONSTANT XX XX%token AUTO BREAK CASE CHAR XX CONTINUE DEFAULT DO DOUBLE XX ELSE ENUM EXTERN FLOAT XX FOR GOTO IF INT XX LONG REGISTER RETURN SHORT XX SIZEOF STATIC STRUCT SWITCH XX TYPEDEF UNION UNSIGNED VOID XX WHILE XX XX Shift And Or Rel_Op XX Eq_Op IncDec Asgn_Op Point XX XX%right Asgn_Op XX%right '?' ':' XX%left Or XX%left And XX%left '|' XX%left '^' XX%left '&' XX%left Eq_Op XX%left Rel_Op XX%left Shift XX%left '+' '-' XX%left '*' '/' '%' XX%left Prefix XX%left SizeOf XX%left IncDec Point '.' XX%left '[' '(' XX XX%type <id> declarator XX XX%type <ptr> top_level_decls top_level_decl function_decl XX type_name compound statements XX statement label expr_opt XX expr_list expression tag XX XX%% XX XX XX/*-------------------------- DECLARATIONS -------------------------------*/ XX XX XXprogram : top_level_decls { Tree = $1; } XX ; XX XXtop_level_decls : /* empty */ { proc_start(); $$ = node0 (0); } XX | top_level_decls top_level_decl { proc_start(); $$ = node2 (Seq, $1, $2); } XX ; XX XXtop_level_decl : function_decl { $$ = $1; } XX | declaration { $$ = node0 (0); } XX | error '}' { $$ = node0 (Error); } XX | error ';' { $$ = node0 (Error); } XX ; XX XXfunction_decl : specifiers declarator declarations compound { $$ = $4; metrics ($2->name); } XX | declarator declarations compound { $$ = $3; metrics ($1->name); } XX ; XX XXdeclarations : /* empty */ XX | declarations declaration XX ; XX XXdeclaration : specifiers init_dclrtrs ';' XX | specifiers ';' XX ; XX XXspecifiers : storage XX | storage type_spec XX | type_spec XX ; XX XXstorage : AUTO XX | EXTERN XX | REGISTER XX | STATIC XX ; XX XXtype_spec : int_spec XX | UNSIGNED int_spec XX | UNSIGNED XX | float_spec XX | enum_spec XX | struct_spec XX | union_spec XX | TYPENAME XX | VOID XX ; XX XXdeclarator : IDENTIFIER { $$ = $1; } XX | '(' declarator ')' { $$ = $2; } XX | declarator '(' parameter_list ')' { $$ = $1; } XX | declarator '(' ')' { $$ = $1; } XX | declarator '[' expr_opt ']' { $$ = $1; } XX | '*' declarator %prec Prefix { $$ = $2; } XX ; XX XXparameter_list : IDENTIFIER XX | parameter_list ',' IDENTIFIER XX ; XX XXinit_dclrtrs : declarator XX | declarator Asgn_Op initialiser XX | init_dclrtrs ',' declarator XX | init_dclrtrs ',' declarator Asgn_Op initialiser XX ; XX XXinitialiser : expression XX | '{' init_list '}' XX | '{' init_list ',' '}' XX ; XX XXinit_list : initialiser XX | init_list ',' initialiser XX ; XX XXdeclaration : TYPEDEF type_spec type_decls ';' XX ; XX XXtype_decls : declarator { $1->token = TYPENAME; } XX | type_decls ',' declarator { $3->token = TYPENAME; } XX ; XX XX XX/*---------------------------- TYPES ----------------------------------*/ XX XX XXint_spec : CHAR | SHORT | SHORT INT | INT | LONG INT | LONG ; XXfloat_spec : FLOAT | LONG FLOAT | DOUBLE ; XX XXenum_spec : ENUM IDENTIFIER XX | ENUM IDENTIFIER '{' enum_fields '}' XX | ENUM '{' enum_fields '}' XX ; XX XXstruct_spec : STRUCT IDENTIFIER XX | STRUCT IDENTIFIER '{' struct_fields '}' XX | STRUCT '{' struct_fields '}' XX ; XX XXunion_spec : UNION IDENTIFIER XX | UNION IDENTIFIER '{' struct_fields '}' XX | UNION '{' struct_fields '}' XX ; XX XXenum_fields : enum_const XX | enum_fields ',' enum_const XX ; XX XXenum_const : IDENTIFIER XX | IDENTIFIER Asgn_Op expression XX ; XX XXstruct_fields : type_spec field_list ';' XX | struct_fields type_spec field_list ';' XX | error ';' XX ; XX XXfield_list : field XX | field_list ',' field XX ; XX XXfield : declarator XX | declarator ':' expression XX | ':' expression XX ; XX XXtype_name : type_spec { $$ = node0 (Type); } XX | type_spec abstract { $$ = node0 (Type); } XX ; XX XXabstract : '(' abstract ')' XX | '(' ')' XX | abstract '(' ')' XX | '[' expr_opt ']' XX | abstract '[' expr_opt ']' XX | '*' %prec Prefix XX | '*' abstract %prec Prefix XX ; XX XX XX/*-------------------------- STATEMENTS --------------------------------*/ XX XX XXcompound : '{' declarations statements '}' { $$ = node1 (Seq, $3); } XX | '{' '}' { $$ = node0 (0); } XX | error '}' { $$ = node0 (Error); } XX ; XX XXstatements : statement { $$ = $1; } XX | statements statement { $$ = node2 (Seq, $1, $2); } XX ; XX XXstatement : expr_list ';' { $$ = $1; } XX | label ':' statement { $$ = node2 (Label, $1, $3); } XX | compound { $$ = $1; } XX | IF '(' expr_list ')' statement { $$ = node3 (IF, $3, $5, Z); } XX | IF '(' expr_list ')' statement ELSE statement { $$ = node3 (IF, $3, $5, $7); } XX | WHILE '(' expr_list ')' statement { $$ = node2 (WHILE, $3, $5); } XX | DO statement WHILE '(' expr_list ')' ';' { $$ = node2 (DO, $2, $5); } XX | FOR '(' expr_opt ';' expr_opt ';' expr_opt ')' XX statement { $$ = node4 (FOR, $3, $5, $7, $9); } XX | SWITCH '(' expr_list ')' statement { $$ = node2 (SWITCH, $3, $5); } XX | BREAK ';' { $$ = node0 (BREAK); } XX | CONTINUE ';' { $$ = node0 (CONTINUE); } XX | RETURN expr_opt ';' { $$ = node1 (RETURN, $2); } XX | GOTO tag ';' { $$ = node1 (GOTO, Z); } XX | ';' { $$ = node0 (';'); } XX | error ';' { $$ = node0 (Error); } XX ; XX XXlabel : tag { $$ = $1; } XX | CASE expression { $$ = node1 (CASE, $2); } XX | DEFAULT { $$ = node0 (DEFAULT); } XX ; XX XX XX/*--------------------------- EXPRESSIONS -------------------------------*/ XX XX XXexpr_opt : /* empty */ { $$ = node0 (0); } XX | expr_list { $$ = $1; } XX ; XX XXexpr_list : expression { $$ = $1; } XX | expr_list ',' expression { $$ = node2 (',', $1, $3); } XX ; XX XXexpression : expression Asgn_Op expression { $$ = node2 (Asgn_Op, $1, $3); } XX | expression '?' expr_list ':' expression { $$ = node3 ('?', $1, $3, $5); } XX | expression Or expression { $$ = node2 (Or, $1, $3); } XX | expression And expression { $$ = node2 (And, $1, $3); } XX | expression '|' expression { $$ = node2 ('|', $1, $3); } XX | expression '^' expression { $$ = node2 ('^', $1, $3); } XX | expression '&' expression { $$ = node2 ('&', $1, $3); } XX | expression Eq_Op expression { $$ = node2 (Eq_Op, $1, $3); } XX | expression Rel_Op expression { $$ = node2 (Rel_Op, $1, $3); } XX | expression Shift expression { $$ = node2 (Shift, $1, $3); } XX | expression '+' expression { $$ = node2 ('+', $1, $3); } XX | expression '-' expression { $$ = node2 ('-', $1, $3); } XX | expression '*' expression { $$ = node2 ('*', $1, $3); } XX | expression '/' expression { $$ = node2 ('/', $1, $3); } XX | expression '%' expression { $$ = node2 ('%', $1, $3); } XX | '*' expression %prec Prefix { $$ = node1 (Indirect, $2); } XX | '&' expression %prec Prefix { $$ = node1 (Addr, $2); } XX | '+' expression %prec Prefix { $$ = node1 (Uplus, $2); } XX | '-' expression %prec Prefix { $$ = node1 (Uminus, $2); } XX | '!' expression %prec Prefix { $$ = node1 ('!', $2); } XX | '~' expression %prec Prefix { $$ = node1 ('~', $2); } XX | '(' type_name ')' expression %prec Prefix { $$ = node2 (Cast, $2, $4); } XX | IncDec expression %prec Prefix { $$ = node1 (Pre_Inc, $2); } XX | SIZEOF '(' type_name ')' %prec SizeOf { $$ = node1 (Size_Type, $3); } XX | SIZEOF expression %prec SizeOf { $$ = node1 (Size_Expr, $2); } XX | expression IncDec { $$ = node1 (Post_Inc, $1); } XX | expression Point tag { $$ = node2 (Point, $1, $3); } XX | expression '.' tag { $$ = node2 ('.', $1, $3); } XX | expression '(' ')' { $$ = node2 ('(', $1, Z); } XX | expression '(' expr_list ')' { $$ = node2 ('(', $1, $3); } XX | expression '[' expr_list ']' { $$ = node2 ('[', $1, $3); } XX | '(' expr_list ')' { $$ = $2; } XX | tag { $$ = $1; } XX | CONSTANT { $$ = node1 (CONSTANT, Z); } XX ; XX XXtag : IDENTIFIER { $$ = node1 (IDENTIFIER, (NodePtr) $1); } XX ; ====End of SHAR parse.y==== if [ "`wc -c <'parse.y'`" != ' 8625' ] then echo 'Unpack for parse.y failed!' exit 1 else echo 'Unpacked parse.y' fi sed -e 's/^XX//' <<'====End of SHAR symbol.c====' >symbol.c XX/*----------------------------------------------------------------------*/ XX/* */ XX/* This module handles a symbol/type hash table */ XX/* */ XX/*----------------------------------------------------------------------*/ XX XX XX#include <stdio.h> XX#ifdef BSD XX#include <strings.h> XX#define memset(p,c,n) {register int i;for(i=0;i<(n);i++)(p)[i]=(c);} XX#else XX#include <string.h> XX#include <memory.h> XX#endif XX#include "check.h" XX#include "y.tab.h" XX XX#define HASH_SIZE 1000 /* Allow for 750 names at 75% full */ XX XXunsigned hsize; /* Size of the hash table */ XXSymbol **htable; /* Pointer to an array of ponters to hash entries */ XX XX#ifdef DEBUG XXunsigned *hcount; /* Pointer to an array of hash counts */ XX#endif XX XX XXunsigned hash (key) XXregister char *key; XX{ XX /* Hash a key string */ XX XX register unsigned hval = 0; XX XX while (*key) hval = (hval << 3) + (hval >> 29) + *key++; XX XX hval = (hval & 0x7fffffff) % hsize; XX return hval; XX} XX XX XXvoid mkhash (size) XXunsigned size; /* Minimum size for hash table */ XX{ XX /* Create a hash table of size rounded up to next power of two-1 */ XX XX register unsigned tsize = size; XX XX hsize = 1; /* Actual hash table size */ XX while (tsize) XX { XX tsize >>= 1; XX hsize <<= 1; XX } XX hsize--; XX if (hsize == 0) hsize++; /* Silly, but it will work! */ XX XX htable = (Symbol **) emalloc (hsize * sizeof(Symbol *)); XX memset ((char *) htable, 0, (int) (hsize * sizeof(Symbol *))); XX XX#ifdef DEBUG XX Printf ("mkhash table size %d\n", hsize); XX hcount = (unsigned *) emalloc (hsize * sizeof(unsigned)); XX memset ((char *) hcount, 0, (int) (hsize * sizeof(unsigned))); XX#endif XX} XX XX XXvoid rmhash () XX{ XX /* Destroy hash table and all chained entries */ XX XX register Symbol **pp, *p; XX extern void free(); XX XX for (pp = htable; pp < htable + hsize; pp++) XX { XX while (p = *pp) XX { XX *pp = p->next; XX free (p->name); XX free ((char *)p); XX } XX } XX XX free((char *) htable); XX htable = 0; XX} XX XX XXSymbol *findsym (name, token, action) XXchar *name; /* name to be inserted or found */ XXToken token; /* type of symbol */ XXAction action; /* Create or Find */ XX{ XX /* Search for, and create if required, an entry in the hash table */ XX XX register Symbol **pp; /* address of pointer to entry */ XX register Symbol *p; /* search through linked list */ XX register unsigned hval; /* hash value from name */ XX register int res; /* result of strcmp */ XX XX pp = &htable[hval = hash(name)]; XX p = *pp; XX XX while (p != NULL) XX { XX if ((res = strcmp(name, p->name)) == 0) XX { XX return p; XX } XX elif (res < 0) XX { XX /* past point where name would be in sorted chain */ XX break; XX } XX pp = &(p->next); XX p = *pp; XX } XX XX /* Item is not yet on list */ XX if (action == Find) XX return NilSym; XX else XX { XX#ifdef DEBUG XX /* Accumulate hashing statistics */ XX hcount[hval]++; XX#endif XX p = (Symbol *) emalloc (sizeof(Symbol)); XX p->name = strcpy (emalloc ((unsigned) strlen(name)+1), name); XX p->token = token; XX p->next = *pp; XX *pp = p; XX return p; XX } XX} XX XX XXstatic struct Keyword { XX char *name; XX Token token; XX} keywords [] = { XX "auto", AUTO, XX "break", BREAK, XX "case", CASE, XX "char", CHAR, XX "continue", CONTINUE, XX "default", DEFAULT, XX "do", DO, XX "double", DOUBLE, XX "else", ELSE, XX "enum", ENUM, XX "extern", EXTERN, XX "float", FLOAT, XX "for", FOR, XX "goto", GOTO, XX "if", IF, XX "int", INT, XX "long", LONG, XX "register", REGISTER, XX "return", RETURN, XX "short", SHORT, XX "sizeof", SIZEOF, XX "static", STATIC, XX "struct", STRUCT, XX "switch", SWITCH, XX "typedef", TYPEDEF, XX "union", UNION, XX "unsigned", UNSIGNED, XX "void", VOID, XX "while", WHILE, XX 0, 0 XX}; XX XX XXvoid sy_init () XX{ XX /* Initialise the Symbol Table with the reserved words */ XX XX register struct Keyword *kw; XX XX mkhash (HASH_SIZE); XX for (kw = keywords; kw->name; kw++) XX { XX (void) findsym (kw->name, kw->token, Create); XX } XX} XX XX XXvoid sy_tidy () XX{ XX /* Remove all but reserved words from the Symbol Table */ XX /* This is achieved by brute force; destroy & recreate! */ XX XX#ifdef DEBUG XX /* print hash statistics */ XX register unsigned i, j, tot = 0, maxc = 0, unused = 0; XX for (i=0; i<hsize; i++) XX { XX /* print hash count if non-zero */ XX if (j = hcount[i]) XX { XX Printf ("hash %6d : %d\n", i, j); XX if (j > maxc) maxc = j; XX tot += j; XX } XX else unused++; XX } XX Printf ("Total=%d, max chain length=%d, unused=%d\n",tot,maxc,unused); XX#endif XX rmhash (); XX sy_init (); XX} ====End of SHAR symbol.c==== if [ "`wc -c <'symbol.c'`" != ' 4271' ] then echo 'Unpack for symbol.c failed!' exit 1 else echo 'Unpacked symbol.c' fi sed -e 's/^XX//' <<'====End of SHAR tables.sh====' >tables.sh XX XX# Make tables.h from check.h and y.xxx.h XX XX( XXsed <check.h -n -e '/ Parse/,$ s/^#define[ ][ ]*//p' XXsed <y.xxx.h -n -e 's/^#[ ]*define[ ][ ]*//p' XX) | XXawk -e ' XXBEGIN { XX printf "typedef struct NodeName {\n" XX printf "\tint val;\n" XX printf "\tchar *str;\n" XX printf "} NodeName;\n\n" XX printf "NodeName\tnodenames[] = {\n" XX } XX { XX printf "\t%d, \"%s\",\n", $2, $1 XX } XXEND { XX printf " 0, 0\n};\n" XX }' >tables.h ====End of SHAR tables.sh==== chmod +x tables.sh if [ "`wc -c <'tables.sh'`" != ' 412' ] then echo 'Unpack for tables.sh failed!' exit 1 else echo 'Unpacked tables.sh' fi sed -e 's/^XX//' <<'====End of SHAR test.c====' >test.c XX/*----------------------------------------------------------------------*/ XX/* */ XX/* C Checker Main Entry Point */ XX/* Modified version of 'main.c' to see messages from check. */ XX/* */ XX/*----------------------------------------------------------------------*/ XX XX XX#include <stdio.h> XX#include <ctype.h> XX#include "check.h" XX#include "y.tab.h" XX XX#define MAXNAME 120 XX#define CPP "/lib/cpp -C %s %s" XX XXextern void perror(), exit(); XX XXint errors = 0; /* count of number of errors */ XXchar filebuf [MAXNAME]; /* Buffer for file names XXchar *filename; /* Pointer to file name */ XXchar *progname; /* Name of this program */ XX XX/* Really should recode this to avoid using fixed buffer sizes; */ XX/* malloc and realloc are not difficult to use! */ XXchar options [BUFSIZ]; /* Buffer for cpp options */ XX XX XXint main (argc, argv) XXint argc; XXchar *argv[]; XX{ XX /* Main Entry Point to C Checker */ XX /* Check each file as specified by arguments */ XX XX char command [BUFSIZ]; /* for cpp command */ XX register char *opts = options; XX register char *opt_end = & options [BUFSIZ-2]; XX register int pnames; XX register int ptree = FALSE; XX extern char *strncat (); XX extern FILE *popen (); XX XX sy_init (); XX progname = *argv++; XX XX /* Extract C PreProcessor options */ XX XX while (--argc && **argv == '-') XX { XX register char *ap = *argv++; XX XX if (strcmp ("--", ap) == 0) XX { XX /* End of Options */ XX XX break; XX } XX#if YYDEBUG XX elif (strcmp ("-yydebug", ap) == 0) XX { XX /* Turn on Yacc Tracing */ XX XX extern int yydebug; XX yydebug = 1; XX } XX#endif XX elif (strcmp ("-TP", ap) == 0) XX { XX /* Print Parse Tree option (debug) */ XX XX ptree = TRUE; XX } XX else XX { XX /* C PreProcessor option */ XX XX while (*ap && (opts < opt_end)) *opts++ = *ap++; XX *opts++ = ' '; XX } XX } XX *opts++ = ' '; XX *opts++ = '\0'; XX XX pnames = argc - 1; XX while (argc--) XX { XX /* Open a pipe for reading from C PreProcessor */ XX XX filename = *argv++; XX (void) sprintf (command, CPP, options, filename); XX if (pnames > 0 && filename != NULL && othertests()); XX Printf ("%s:\n", filename); XX XX if ((yyin = popen (command, "r")) == NULL) XX if (yyin == NULL) perror ("cannot open pipe"); XX else XX { XX /* Analyse one C source file */ XX XX lex_init (); XX met_init (); XX if (yyparse ()) warn ("fatal syntax error"); XX XX /* Report on results of analysis */ XX XX if (ptree) treeprint (); XX check_prog (); XX metrics ((char *) 0); XX XX /* Tidy up, ready for another source file */ XX XX (void) pclose (yyin); XX tidy_prog (); XX sy_tidy (); XX } XX } XX XX return errors; XX} XX XX XXchar *emalloc (n) XXunsigned n; XX{ XX /*==== Allocate Memory ====*/ XX XX char *p, *malloc (); XX XX p = malloc (n); XX XX if (p = 0) error ("out of memory"); XX XX return p; XX} XX XX XXvoid yyerror (s) XXchar *s; XX{ XX /* Syntax Error from Yacc */ XX XX errors++; XX Fprintf (stderr, "%s: %s at or near line %d, token %d\n", XX filename, s, yylineno, yychar); XX} XX XX XXvoid warn (s) XXchar *s; XX{ XX /* Generate a warning message */ XX XX errors++; XX Fprintf (stderr, "%s: %s at or near line %d\n", XX filename, s, yylineno); XX} XX XX XXvoid error (s) XXchar *s; XX{ XX /* Handle fatal errors */ XX XX warn (s); XX exit (errors); XX} XX XX XXvoid setline (line) XXchar *line; XX{ XX /* Handle a #line directive */ XX XX register char *cp = line; XX register char *fn = filebuf; XX register int lnum = 0; XX XX if (*cp != '#') error ("invalid call to setline"); XX XX while (*cp && !isdigit(*cp)) cp++; XX while (*cp && isdigit (*cp)) lnum = lnum*10 + (*cp++ - '0'); XX while (*cp && *cp != '"') cp++; XX if (lnum) yylineno = lnum; XX XX if (*cp++ == 0) return; XX while (*cp && *cp != '"') *fn++ = *cp++; XX if (fn == filename) return; XX XX *fn = '\0'; XX filename = filebuf; XX} XX XX/* Unterminated comment here ... XX XXvoid uncompiled (not_passed) XX{ XX int not_here; XX XX not_called (); XX} ====End of SHAR test.c==== if [ "`wc -c <'test.c'`" != ' 3731' ] then echo 'Unpack for test.c failed!' exit 1 else echo 'Unpacked test.c' fi sed -e 's/^XX//' <<'====End of SHAR tree.c====' >tree.c XX/*----------------------------------------------------------------------*/ XX/* */ XX/* This module handles Parse Tree building and walking */ XX/* */ XX/*----------------------------------------------------------------------*/ XX XX XX#include <stdio.h> XX#include <ctype.h> XX#include "check.h" XX#include "y.tab.h" XX#include "tables.h" XX XX#define CHUNK 1024 /* No. of nodes allocated */ XX XXNodePtr Tree; /* holds the base of the Parse Tree, */ XX /* built by the Yacc-generated Parser. */ XX XXNodePtr next_node, /* the next free node for allocation */ XX last_node, /* node not available for allocation */ XX node_list; /* head of list of blocks of nodes */ XX XX XXNodePtr new_node (nt, p1, p2, p3, p4) XXint nt; XXNodePtr p1, p2, p3, p4; XX{ XX /* Build a node for the Parse Tree */ XX XX register NodePtr np; XX XX if (next_node == last_node) XX { XX /* Allocate a new batch of nodes */ XX XX next_node = (NodePtr) emalloc (sizeof (Node) * CHUNK); XX last_node = next_node + CHUNK; XX XX next_node -> f1.ptr = node_list; XX node_list = next_node++; XX } XX XX np = next_node++; XX XX np -> type = nt; XX np -> line = yylineno; XX np -> f1.ptr = p1; XX np -> f2.ptr = p2; XX np -> f3.ptr = p3; XX np -> f4.ptr = p4; XX XX return np; XX} XX XX XXvoid tidy_prog () XX{ XX /* Delete the parse tree and release its memory space; */ XX /* Leave just the first block of nodes for the next program */ XX /* No attempt is made to return the freed space to the system */ XX /* by using (s)brk, but this could be added if desired. */ XX /* My own view is that if one program builds a huge tree, */ XX /* others in the same call to check may also, so let's keep */ XX /* the space. But in that case, why even return it to malloc? */ XX /* Because it makes it easier to add the (s)brk, and it might */ XX /* alleviate heap fragmentation if other modules are also */ XX /* using malloc, and because it just feels neater to me! */ XX XX register NodePtr np; XX extern void free(); XX XX Tree = NilNode; XX XX for (np = node_list->f1.ptr; np; np = np->f1.ptr) free ((char *) np); XX node_list -> f1.ptr = NilNode; XX next_node = node_list + 1; XX last_node = node_list + CHUNK; XX} XX XX XXvoid treewalk (depth, parent, branch, np, action) XXint depth, parent, branch; XXNodePtr np; XXvoid (*action) (); XX{ XX /* Perform required action for this node, and all nodes below it */ XX XX register int here; XX XX if (np == NilNode) return; XX yylineno = np -> line; XX XX action (depth, parent, branch, np); XX XX switch (here = np -> type) XX { XX /* = = = Terminal Nodes = = = */ XX XX case IDENTIFIER: XX case CONSTANT: XX case DEFAULT: XX case BREAK: XX case CONTINUE: XX case ';': XX case Type: XX case Error: XX case 0: break; XX XX /* = = = Unary Nodes = = = */ XX XX case GOTO: XX case RETURN: XX case CASE: XX case Indirect: XX case Addr: XX case Uplus: XX case Uminus: XX case '!': XX case '~': XX case Pre_Inc: XX case Post_Inc: XX case Size_Type: XX case Size_Expr: XX treewalk (depth+1,here,1,np->f1.ptr,action); XX break; XX XX /* = = = Binary Nodes = = = */ XX XX case Seq: XX case Label: XX case WHILE: XX case DO: XX case SWITCH: XX case ',': XX case Or: XX case And: XX case '|': XX case '^': XX case '&': XX case Eq_Op: XX case Rel_Op: XX case Shift: XX case '+': XX case '-': XX case '*': XX case '/': XX case '%': XX case Cast: XX case Point: XX case '.': XX case '(': XX case '[': XX case Asgn_Op: XX treewalk (depth+1,here,1,np->f1.ptr,action); XX treewalk (depth+1,here,2,np->f2.ptr,action); XX break; XX XX /* = = = Ternary Nodes = = = */ XX XX case IF: XX case '?': XX treewalk (depth+1,here,1,np->f1.ptr,action); XX treewalk (depth+1,here,2,np->f2.ptr,action); XX treewalk (depth+1,here,3,np->f3.ptr,action); XX break; XX XX /* = = = Quaternary Nodes = = = */ XX XX case FOR: XX treewalk (depth+1,here,1,np->f1.ptr,action); XX treewalk (depth+1,here,2,np->f2.ptr,action); XX treewalk (depth+1,here,3,np->f3.ptr,action); XX treewalk (depth+1,here,4,np->f4.ptr,action); XX break; XX XX XX default: Printf ("node type %d encountered\n", here); XX error ("treewalk: parse tree corrupt"); XX } XX} XX XX XXvoid walk_prog (action) XXvoid (*action) (); XX{ XX /* Start off the Parse Tree walk */ XX XX treewalk (0, 0, 0, Tree, action); XX} XX XX XX/*ARGSUSED*/ XXvoid printnode (depth, parent, branch, np) XXint depth, parent, branch; XXNodePtr np; XX{ XX /* Print one node of the tree */ XX XX register int type = np->type; XX XX while (depth--) Printf (" "); XX Printf ("(%d) line %d: ", branch, np->line); XX XX if (type > 256) XX { XX register NodeName *q = nodenames; XX XX while (q->val != 0 && q->val != type) q++; XX Printf ("%s\n", q->str); XX } XX elif (type == 256) XX Printf ("ERROR/EOF\n"); XX elif (isprint(type)) XX Printf ("%c\n", type); XX else Printf ("type %d/0x%02x\n", type, type); XX} XX XX XXvoid treeprint () XX{ XX /* Print the Parse Tree */ XX XX treewalk (0, 0, 0, Tree, printnode); XX} ====End of SHAR tree.c==== if [ "`wc -c <'tree.c'`" != ' 4678' ] then echo 'Unpack for tree.c failed!' exit 1 else echo 'Unpacked tree.c' fi exit -- For comp.sources.unix stuff, mail to sources@uunet.uu.net.