/* cee.fg - Felix Gaertner's first formatted grammar for C
 *          target is LaTeX, built for use with noweb
 *
 * $Id: cee.fg,v 1.2 1996/12/16 19:02:09 gaertner Exp $
 *
 * History at end.
 */

%token BINOP
%token UNORBINOP
%token QUESTION
%token UNOP
%token LPAR
%token RPAR
%token LBRACE
%token RBRACE
%token LBRACK
%token RBRACK
%token SEMI
%token COLON
%token LPROC

%token ID

%token NUMBER
%token INT_LIKE
%token CASE_LIKE
%token IF_LIKE
%token DO_LIKE
%token ELSE_LIKE
%token TYPEDEF_LIKE
%token STRUCT_LIKE
%token SIZEOF_LIKE
%token DEFINE_LIKE
%token COMMA

%token INSERT
%token STRING

%token CHUNK
%token IGNORE

%{

#include<string.h>
#include<stdlib.h>
#include<strstream.h>

 /* these functions help to typeset typedefs. they implement
  * a simple lookup table to see if an identifier is a
  * typedef or what...
  *
  * taken from Kernighan/Ritchie: The C Programming Language, p.144ff
  */


struct nlist {  /* a table entry: */
    struct nlist *next;   /* next entry in chain */
    char* name;           /* the identifier */
};

#define HASHSIZE 101

static struct nlist* hashtab[HASHSIZE];  /* the hash table */

  /* form the hash value of a string */

static unsigned hash(char* s)
{
    // cerr << "hash(" << s << ") called." << endl;

    unsigned hashval;

    for (hashval = 0; *s != '\0'; s++)
        hashval = *s + 31 * hashval;
    return hashval % HASHSIZE;
}

  /* lookup: look for s in hashtab
   *
   * returns a pointer to the found element or NULL if it was not
   * found.
   */

static struct nlist* lookup(char* s)
{
    // cerr << "lookup(" << s << ") called." << endl;

    struct nlist* np;

    for (np = hashtab[hash(s)]; np != NULL; np = np->next)
        if (strcmp(s, np->name) == 0)
            return np;  /* found */
    return NULL;        /* not found */
}

  /* install: put name in hashtab.
   *
   * returns a pointer to the new element if all went well,
   * returns NULL on error.
   */

static struct nlist *install(char* name)
{
    // cerr << "install(" << name << ") called." << endl;

    struct nlist* np;
    unsigned hashval;

    if ((np = lookup(name)) == NULL) {  /* not found */
        np = (struct nlist*) malloc(sizeof(*np));
        if (np == NULL || (np->name = strdup(name)) == NULL)
            return NULL;
        hashval = hash(name);
        np->next = hashtab[hashval];
        hashtab[hashval] = np;
    }
    return np;
}


 /* attibute_to_string  -
  * turn an attribute into a string
  *
  * idea: call print() with a strstream
  *
  * NB: Output has to be deleted and the strstream properly
  * terminated before calling .str()! Otherwise, the last
  * characters are garbeled.
  */

static char* attribute_to_string( Attribute* a )
{
    ostrstream printstream;
    Latex_cweb_output* o = new Latex_cweb_output(printstream);

    a->print(*o);
    delete o;
    printstream << ends;

    return printstream.str();
}

 /* forward declaration of debugging function */

static void debug_print( char* this_is, Attribute* stuff );


%}


%%

  // inserting $s for TeX's math mode is quite tricky because
  // at the beginning of \LA and \RA noweb expects to not be in
  // math mode and complains about missing $s when we're already in
  // it. Solution: We'll use Lee's \mathin and \mathout macros to
  // surround expressions and alike. It's better to selectively invoke
  // math mode than have everything in it.


final	: exp		{ "\\mathin" $1 "\\mathout" } // [ debug_print("Final exp", $1); ]
	| stmt		{ $1 } // [ debug_print("Final stmt", $1); ]
	| error		{ "syntax error" }
;




  // get formatting of identifiers right

id	: ID            { [ lookup(attribute_to_string($1)) ?
				create("{\\bf ") :
				create("{\\it ") ]
			     $1 "}" }
;




  // ************************************************************
  // format expressions:

exp	: NUMBER
	| STRING
;

  // easy things:
  // I don't know why I have duplicated everything (exp, id) but
  // I think it was in order for typedef to work correctly

exp	: exp UNOP		// x++
	| id UNOP
	| UNOP exp		// ++x
	| UNOP id
	| exp BINOP exp
	| id BINOP exp
	| exp BINOP id
	| id BINOP id
	| exp UNORBINOP exp
	| id UNORBINOP exp
	| exp UNORBINOP id
	| id UNORBINOP id
	| UNORBINOP exp		// *x
	| UNORBINOP id
        | LPAR exp RPAR         // (x+1)
	| LPAR id RPAR
	| id LBRACK exp RBRACK	// a[10]
	| id LBRACK id RBRACK	// a[i]
	| id LBRACK RBRACK	{ $1 $2 "\\," $3 }	// a[]

	| id LPAR decl_list RPAR	{ "\\hbox{" $1 $2 $3 $4 "}" }		// f(x+1)  f(a,b)
	| id LPAR RPAR		{ "\\hbox{" $1 $2 "\\," $3 "}" }		// f()
;


  // **************************************************************
  // declarations of variables with or without initializers



decl	: INT_LIKE decl_list		{ $1 "\\ " $2 }		// int a, b, c

	| INT_LIKE INT_LIKE decl_list	{ $1 "\\ " $2 "\\ " $3 }	// extern int a
	| id decl_list		{ $1 "\\ " $2 }			// foo bar
;

  // lists like in function headings, i.e. f(int i, int b)

params_list : decl
	| decl COMMA params_list		{ $1 $2 opt9 "\\ " $3 }

;

  // a sequence of variables (possibly with initializers)

decl_like : exp		        { "\\mathin " $1 "\\mathout " }
	  | id									// i
;

decl_list : decl_like
	| decl_like COMMA decl_list  { $1 $2 opt9 $3 }
;



stmt	: decl SEMI			// int i;
;



  // ***************************************************************
  // typedefs. it's important that the identifier is an ID token and
  // not an id token (which has italics around it already)

typedef	: TYPEDEF_LIKE INT_LIKE ID	[ install(attribute_to_string($3)); ]
                                        { $1 "\\ " $2 "\\ {\\bf " $3 "}" }

	| TYPEDEF_LIKE INT_LIKE UNORBINOP ID		// typedef char* String
					[ install(attribute_to_string($4)); ]
                                        { $1 "\\ " $2 $3 "\\ {\\bf " $4 "}" }
;

stmt	: typedef SEMI
;




  // ***************************************************************
  // statements


stmt	: exp SEMI			{ "\\mathin " $1 "\\mathout "
					  $2 }
;

  // empty block

stmt	: LBRACE RBRACE			{ $1 "\\," $2 }
	| SEMI
;

  // block statement
stmt	: LBRACE stmt RBRACE	 { force $1 indent force $2 outdent force
					$3 force }
;


  // *****************************************************************
  // control structures

  // if then / if then else / while / case

stmt	: IF_LIKE exp stmt		    { $1 "\\ \\mathin " $2 "\\mathout\\ " $3 }
	| IF_LIKE exp stmt ELSE_LIKE stmt   { $1 "\\ \\mathin " $2 "\\mathout\\ "
						$3 "\\ " break_space $4 "\\ " $5 }
;

  // for loops

stmt	: IF_LIKE LPAR stmt stmt exp RPAR stmt				// for (i=1;i<1;i++);
				{ $1 "\\ \\mathin " $2 $3 "\\ " $4 "\\ "
				  $5 $6 "\\mathout\\ " $7 }
	| IF_LIKE LPAR stmt stmt RPAR stmt				// for (;;);
				{ $1 "\\ \\mathin " $2 $3 "\\ " $4 $5 "\\mathout\\ " $6 }
;

  // do, case etc

stmt	: DO_LIKE stmt IF_LIKE exp SEMI
				{ $1 $2 $3 "\\ \\mathin " $4 "\\mathout " $5 }

	| CASE_LIKE SEMI	// return;

	| CASE_LIKE exp SEMI	{ $1 "\\ \\mathin " $2 "\\mathout " $3 }
;

tag	: CASE_LIKE COLON	// default:
	| id COLON		{ "\n@def " $1 "\n" "\\mathin " $1
				"\\mathout " $2 }
	| CASE_LIKE exp COLON	{ $1 "\\ \\mathin " $2 "\\mathout " $3 }
	| tag tag		{ $1 break_space $2 }				// 1: 2:
;

stmt	: tag stmt		{ force backup $1 "\\ " opt9 $2 }		// 1: a=1;
;





  // join up statements

stmt	: stmt stmt		{ $1 force $2 }
  	| stmt SEMI			// empty statemet
;

  // be sure not to be in math mode for chunks

stmt	: CHUNK SEMI		{ "\\mathout " $1 $2 force }
	| CHUNK
;

exp	: CHUNK                 { $1 }
;




  // ***********************************************************
  // preprocessor directives


stmt	: LPROC
	| LPROC stmt
	| LPROC STRING		// #include "abc.h"
	| LPROC LPROC		// #include <abc.h>
	| INSERT
	| IGNORE
;



    // *********************************************************
    // do nothing rules for comments and ignores ...



%%

static void debug_print( char* this_is, Attribute* stuff )
{
    cerr << "*** " << this_is << ":" << endl;
    Latex_cweb_output os(cerr);
    stuff->print(os);
}


/*
 * $Log: cee.fg,v $
 * Revision 1.2  1996/12/16 19:02:09  gaertner
 * release version
 *
 * Revision 1.1  1996/12/11 17:14:16  gaertner
 * Initial revision
 *
 */
