/*
 * javafx.fg -- experimental Pretzel grammar for Java
 *
 * based on original version by Lee Wittenberg and enhanced
 * for use with noweb and made into a self contained
 * Pretzel file by Felix Gaertner
 *
 * History at end
 *
 * $Header: /a/spelunke/export/vol1/gaertner/pretzel/contrib/noweb/java.latex/RCS/javafx.fg,v 1.1 1997/07/09 14:24:27 gaertner Exp $
 *
 */

 /*
  * A word concerning indexing: I couldn't get noweb to typset identifiers
  * in different fonts in the index. Help on this is very welcome. The
  * current strategy leaves identifiers unchanged up to the very moment
  * their final look is determined (i.e. they change from ID_LIKE to
  * id_like). In this moment, we think about TeX and escape underlines.
  */


%token	BINOP
%token	BREAK_LIKE
%token	CASE_LIKE
%token	CATCH_LIKE
%token	CHUNK
%token 	CLASS
%token	COLON
%token	COMMA
%token	COMMENT
%token	DEFAULT_LIKE
%token	DOT
%token	DOT_STAR
%token	DO_LIKE
%token	ELSE_LIKE
%token	EXTENDS_LIKE
%token	FINALLY_LIKE
%token	FOR_LIKE
%token	ID_LIKE
%token	IF_LIKE
%token	IGNORE
%token	IMPORT_LIKE
%token	INCROP
%token  INT_LIKE
%token	LBRACE
%token	LBRACK
%token	LPAR
%token	NUM
%token	QUESTION
%token	RBRACE
%token	RBRACK
%token	RPAR
%token  SHORT_ID
%token	SEMI
%token	STRING
%token	SYNC_LIKE
%token	TRY_LIKE
%token	UNOP
%token	UNORBINOP

%{

#include<string.h>
#include<stdlib.h>
#include<strstream.h>
#include<assert.h>  // for escaped_underlines


 /* flag to help distinguish betweet declaration and use of an identifier.
  * It's set to 1 whenever we think we're parsing a declaration.
  */

static int in_decl = 0;


 /* forward declarations: */


 /* these functions help to typeset classes. They implement
  * a simple lookup table to see if an identifier is a
  * class 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 */
};

 /* size of hash table. Should be changed if you are working on large
  * programs.
  */

#define HASHSIZE 101

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

  /* form the hash value of a string */
static unsigned hash(char* s);

  /* 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);

  /* 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);



 /* attibute_to_string  -
  * turn an attribute into a string
  *
  */

static char* attribute_to_string( Attribute* a );



  // forward declaration of a function that replaces `_'
  // with `\_' in a copy of s

static char* escaped_underlines(char* s);


 /* debugging function */

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


%}


%%

final	:	exp				{ "\\mathin " $1 "\\mathout" }
		|	decl_list
		|	IGNORE
		|	COMMENT			{ "\\qquad" break_space $1 force }
		|	IGNORE final
		|	COMMENT final	{ $1 force $2 }
		;


decl_list	:	decl
		|	decl_list decl			{ $1 force $2 }
		;

decl		:	exp block				{ "\\mathin" $1 "\\mathout\\ " $2 }
			;

block		:	lbrace rbrace			{ $1 force $2 }
			|	lbrace decl_list rbrace
					{ $1 cancel indent force $2 outdent force $3 }
			;

exp			:	expr
			|	exp dot expr
			|	exp comma expr		{ $1 $2 opt3 "\\mathin " $3 }
			|	exp expr			{ $1 $2 }
			;

expr		:	term
			|	expr binop term				{ $1 opt9 $2 $3 }
			|	expr unorbinop term			{ $1 opt9 $2 $3 }
			|	expr extends_like idlist	{ $1 "\\ " opt2 $2 "\\ " $3 }
			|	expr brack_exp
			;

binop		:	question exp colon		{ "\\mathbin{" $1 $2 $3 "}" }
			;

idlist		:	ident
			|	idlist comma ident		{ $1 $2 opt9 $3 }
			;


  // Identifiers come as plain strings from the scanner.
  // Identifiers that are class names are installed into the lookup table
  // and typeset in boldface. Also, a `definition' index entry must be
  // generated. Before typesetting identifiers, underlines must be escaped.
  //
  // Another issue is how to distinguish between definition and use? Locally
  // you can't distinguish whether the id `a' is part of `int a' or `a++'.
  // We'll try to use a global flag `in_decl' that is set to `true' whenever
  // we think we're just parsing a declaration.

ident		:	id_like
			| class ID_LIKE [ install(attribute_to_string($2)); ]
					{ "{\\bf " $1 "}\\ \\mbox{\\bf "
                                                [ create(escaped_underlines(attribute_to_string($2))) ] "}"
						"%\n@index defn " $2 "\n" }
			| class SHORT_ID [ install(attribute_to_string($2)); ]
					{ "{\\bf " $1 "}\\ \\mbox{\\bf "
                                                [ create(escaped_underlines(attribute_to_string($2))) ] "}"
						"%\n@index defn " $2 "\n" }


			| INT_LIKE class ID_LIKE [ install(attribute_to_string($3)); ]
					{ $1 "\\ {\\bf " $2 "}\\ \\mbox{\\bf "
                                                [ create(escaped_underlines(attribute_to_string($3))) ] "}"
						"%\n@index defn " $3 "\n" }

			| INT_LIKE class SHORT_ID [ install(attribute_to_string($3)); ]
					{ $1 "\\ {\\bf " $2 "}\\ \\mbox{\\bf "
                                                [ create(escaped_underlines(attribute_to_string($3))) ] "}"
						"%\n@index defn " $3 "\n" }

			| sync_like id_like		{ $1 "\\ " $2 }

			| ident id_like			{ $1 "\\ " $2 }
			;

brack_exp	:	lbrack rbrack			{ $1 "\\thinspace " $2 }
			|	lbrack exp rbrack
			|	brack_exp ident			{ $1 "\\ " $2 }
			;

term		:	factor
			|	unop term				{ "\\mathin " $1 $2 }
			|	unorbinop term			{ "\\mathin " $1 $2 }
			|	incrop term				{ "\\mathin " $1 $2 }
			|	term incrop				{ "\\mathin " $1 $2 }
			;

factor		:	ident
			|	num
			|	string
			|	lpar rpar				{ $1 "\\thinspace" $2 }
			|	lpar exp rpar
			;

decl		:	block
			|	non_block_stmt
			;

non_block_stmt
			:	semi					{ cancel $1 }
			|	exp semi				{ "\\mathin " $1 "\\mathout" $2 }
			|	break_like semi
			|	break_like exp semi		{ $1 "\\ \\mathin " $2 "\\mathout" $3 }
			|	sync_like lpar exp rpar decl
					{ $1 "\\ \\mathin " $2 $3 $4 "\\mathout\\ " $5 }
			|	import_head semi
			|	label colon decl		{ force backup $1 $2 force $3 }
			|	try_stmt
			|	if_block
			|	if_simple
			|	else_head block			{ $1 "\\ " $2 }
			|	else_head non_block_stmt
					{ $1 indent force $2 outdent }
			|	for_stmt
			|	do_stmt
			;

import_head	:	import_like id_like		{ $1 "\\ " $2 }
			|	import_head dot id_like
			|	import_head dot_star
			;

if_head		:	if_like lpar exp rpar
					{ $1 "\\ \\mathin " $2 $3 $4 "\\mathout" }
			;

if_block	:	if_head block			{ $1 "\\ " $2 }
			;

if_simple	:	if_head non_block_stmt	{ $1 indent force $2 outdent }
			;

else_head	:	else_like
			|	if_block else_like		{ $1 "\\ " $2 }
			|	if_simple else_like		{ $1 force $2 }
			;

try_stmt	:	try_like block			{ $1 "\\ " $2 }
			|	try_stmt finally_like block
					{ $1 "\\ " $2 "\\ " $3 }
			|	try_stmt catch_like lpar exp rpar block
					{ $1 "\\ \\mathin " $2 $3 $4 $5 "\\mathout\\ " $6 }
			;

label		:	default_like
			|	case_like exp			{ $1 "\\ \\mathin " $2 "\\mathout"}
			|	id_like
			;

do_stmt		:	do_like block if_head semi
					{ $1 "\\ " $2 "\\ " $3 $4 }
			|	do_like non_block_stmt if_head semi
					{ $1 indent force $2 outdent $3 $4 }
			;

for_head	:	for_like lpar for_control rpar
					{ $1 "\\ " $2 $3 $4 }
			;

for_control	:	opt_exp semi opt_exp semi opt_exp
			;

opt_exp		:	// empty
			|	exp			{ "\\mathin " $1 "\\mathout" }
			;

for_stmt	:	for_head block			{ $1 "\\ " $2 }
			|	for_head non_block_stmt	{ $1 indent force $2 outdent }
			;

// rules to pass comments and ignores through unnoticed

binop			:	BINOP
				|	binop IGNORE
				|	binop COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
break_like		:	BREAK_LIKE
				|	break_like IGNORE
				|	break_like COMMENT		{ $1 "\\qquad" break_space $2 force  }
				;
case_like		:	CASE_LIKE
				|	case_like IGNORE
				|	case_like COMMENT		{ $1 "\\qquad" break_space $2 force  }
				;
catch_like		:	CATCH_LIKE
				|	catch_like IGNORE
				|	catch_like COMMENT		{ $1 "\\qquad" break_space $2 force  }
				;
class			:	CLASS
				|	class IGNORE
				|	class COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
colon			:	COLON
				|	colon IGNORE
				|	colon COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
comma			:	COMMA
				|	comma IGNORE
				|	comma COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
default_like	:	DEFAULT_LIKE
				|	default_like IGNORE
				|	default_like COMMENT	{ $1 "\\qquad" break_space $2 force  }
				;
dot				:	DOT
				|	dot IGNORE
				|	dot COMMENT				{ $1 "\\qquad" break_space $2 force  }
				;
dot_star		:	DOT_STAR
				|	dot_star IGNORE
				|	dot_star COMMENT		{ $1 "\\qquad" break_space $2 force  }
				;
do_like			:	DO_LIKE
				|	do_like IGNORE
				|	do_like COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
else_like		:	ELSE_LIKE
				|	else_like IGNORE
				|	else_like COMMENT		{ $1 "\\qquad" break_space $2 force  }
				;
extends_like	:	EXTENDS_LIKE
				|	extends_like IGNORE
				|	extends_like COMMENT	{ $1 "\\qquad" break_space $2 force  }
				;
finally_like	:	FINALLY_LIKE
				|	finally_like IGNORE
				|	finally_like COMMENT	{ $1 "\\qquad" break_space $2 force  }
				;
for_like		:	FOR_LIKE
				|	for_like IGNORE
				|	for_like COMMENT		{ $1 "\\qquad" break_space $2 force  }
				;

  // here we come across an identifier that's not part of a
  // `class' or `interface' clause. If it's in the lookup
  // table then it has already been defined as a class or
  // interface and so it looks like we're dealing with
  // a declaration now.
  //
  // If it's not in the table, it's got to be in italics and
  // the index entry depends on the `in_decl' flag.

id_like			:	ID_LIKE 	{
						// begin index entry
						"%\n@index "
						[ (in_decl == 0) ? create("use ") :
							create("defn ") ]
						 $1 "\n"
						// end index entry
						[ lookup(attribute_to_string($1)) ?
			                                create("{\\bf ") :
                        			        create("{\\it ") ]
			                             [create(escaped_underlines(attribute_to_string($1)))] "}" }

  // don't index short ids but still put correct markup around them:

			 	|	SHORT_ID 	{ "{"
						[ lookup(attribute_to_string($1)) ?
			                                create("\\bf") :
                        			        create("\\it") ]
						  " "
			                          [create(escaped_underlines(attribute_to_string($1)))]
						  "}" }

				|	CHUNK		{ $1 force }
				|	id_like IGNORE
				|	id_like COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
if_like			:	IF_LIKE
				|	if_like IGNORE
				|	if_like COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
import_like		:	IMPORT_LIKE
				|	import_like IGNORE
				|	import_like COMMENT		{ $1 "\\qquad" break_space $2 force  }
				;
incrop			:	INCROP
				|	incrop IGNORE
				|	incrop COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
  // INT_LIKE's become id_like, but it looks like a declaration now

id_like			:	INT_LIKE	[ in_decl = 1; ] { $1 }
;

lbrace			:	LBRACE
				|	lbrace IGNORE
				|	lbrace COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
lbrack			:	LBRACK
				|	lbrack IGNORE
				|	lbrack COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
lpar			:	LPAR
				|	lpar IGNORE
				|	lpar COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
num				:	NUM
				|	num IGNORE
				|	num COMMENT				{ $1 "\\qquad" break_space $2 force  }
				;
question		:	QUESTION
				|	question IGNORE
				|	question COMMENT		{ $1 "\\qquad" break_space $2 force  }
				;
rbrace			:	RBRACE
				|	rbrace IGNORE
				|	rbrace COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
rbrack			:	RBRACK
				|	rbrack IGNORE
				|	rbrack COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
rpar			:	RPAR
				|	rpar IGNORE
				|	rpar COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;

  // a semicolon seems to end a declaration

semi			:	SEMI	[ in_decl = 0; ] { $1 }
				|	semi IGNORE
				|	semi COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
string			:	STRING
				|	string IGNORE
				|	string COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
sync_like		:	SYNC_LIKE
				|	sync_like IGNORE
				|	sync_like COMMENT		{ $1 "\\qquad" break_space $2 force  }
				;
try_like		:	TRY_LIKE
				|	try_like IGNORE
				|	try_like COMMENT		{ $1 "\\qquad" break_space $2 force  }
				;
unop			:	UNOP
				|	unop IGNORE
				|	unop COMMENT			{ $1 "\\qquad" break_space $2 force  }
				;
unorbinop		:	UNORBINOP
				|	unorbinop IGNORE
				|	unorbinop COMMENT		{ $1 "\\qquad" break_space $2 force  }
				;

%%


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


  /* 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 )
{
    // cerr << "entering attribute to string." << endl;
    // debug_print("attribute is", a);

    ostrstream printstream;
    Latex_cweb_output* o = new Latex_cweb_output(printstream);

    // o->debug_on();

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

    char* tmp = printstream.str();

    //cerr << "attribute to string returning `" << tmp << "'." << endl;

    return tmp;
}


  // escaped_underines replaces every occurrence of `_' by
  // the two characters `\_' in a copy of the string pointed to
  // by s
  //
  // the implementation first counts the number of underlines in
  // the string s and allocates new memory for the string. It then
  // copies the string byte after byte into the new place and
  // takes care to insert a `\' before any underline.
  //
  // this is a bit dirty as we don't have a chance to free the
  // allocated space later on, but it's still better than messing
  // around with yytext. To minimize the amount of memory I have
  // added a test whether copying is needed or not.

static char* escaped_underlines(char* s)
{

    // cerr << "entering escaped underlines with `" << s << "'." << endl;
    char* tmp = s;
    int number_of_ul = 0;
    int left = 0;   // s[left] is the next char to copy
    int right = 0;  // place s[left] in tmp[right]

    // fprintf(stderr, "call of escaped_underlines\n");

    // count underlines in s
    while (*tmp != 0) {
        if (*tmp == '_') number_of_ul++;
        tmp++;
    }

    // fprintf(stderr, "escaped_underlines: Number of ul's in %s is %d\n", s, number_of_ul);

    if (number_of_ul == 0) {
      tmp = s;  // skip copying
    } else {

      tmp = new char[strlen(s) + number_of_ul + 1];
      assert(tmp!=NULL);

      // now start to copy
      while (s[left] != 0) { // should be `!= NULL', shouldn,t it, but
                             // some compilers choke on it
          if (s[left] == '_') {
              tmp[right] = '\\';
              right++;
          }
          tmp[right] = s[left];
          left++; right++;
      }
      tmp[right] = s[left];  /* terminate string */
    }

    // cerr << "escaped underlines returning `" << tmp << "'." << endl;

    return(tmp);
}



 /* debugging function */

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


/*
 * $Log: javafx.fg,v $
 * Revision 1.1  1997/07/09 14:24:27  gaertner
 * Initial revision
 *
 * Revision 1.3  1997/07/08 19:08:10  theedge
 * small additions to commmentary
 *
 * Revision 1.2  1997/07/08 18:48:34  theedge
 * Versions that work.
 *
 * Revision 1.1  1997/06/26 16:23:56  theedge
 * Initial revision
 *
 */
