#! /bin/sh
# ----------------------------------------------------------------
#   FILE
#	inherits.sh
#
#   DESCRIPTION
#       shell script which generates tags.h and inh.c
#	and $TREE/$OD/lib/H/slots, which is used by Gen_creator.sh
#	later on during the node function generation process.
#
#   NOTES
#
#   IDENTIFICATION
# 	$Header: /private/postgres/src/lib/Gen/RCS/inherits.sh,v 1.11 1991/04/28 09:16:09 cimarron Exp $
# ----------------------------------------------------------------
TMPDIR=${TMPDIR-/tmp}
INHFILE=$TMPDIR/inh.$$
TAGFILE=$TREE/$OD/lib/H/tags.h
TAGTEMP=$TREE/$OD/lib/H/tags.temp
SLOTFILE=$TREE/$OD/lib/H/slots
OUTFILE=inh.c

# ----------------
# 	collect nodefiles
# ----------------
NODEFILES=''
x=1
numargs=$#
while test $x -le $numargs ; do
   NODEFILES="$NODEFILES $1"
   x=`expr $x + 1`
   shift
done

# ----------------
# 	generate the initial inheritance graph
# ----------------
egrep -h '^class' $NODEFILES | \
sed \
 -e 's/^class (\([A-Za-z]*\))/\1/' \
 -e 's/ public (\([A-Za-z]*\))/ \1/' \
 -e 's/[ {}]*$//' > $INHFILE

# ----------------
#	generate tags.h from the inheritance graph
# ----------------
cat > $TAGTEMP << 'EOF'
/* ----------------------------------------------------------------
 *   FILE
 *	tags.h
 *	
 *   DESCRIPTION
 *	node tag header file - generated by the inherits.sh
 *	script from the contents of the nodes files.
 *
 *   NOTES
 *
 *   IDENTIFICATION
 *	$Header: /private/postgres/src/lib/Gen/RCS/inherits.sh,v 1.11 1991/04/28 09:16:09 cimarron Exp $
 * ----------------------------------------------------------------
 */

EOF
awk 'BEGIN { i = -1 }\
     { printf("#define T_%s %d\n", $1, ++i) }' $INHFILE >> $TAGTEMP

# ----------------
# 	now extract slot names from node files and generate slots.temp
# ----------------
rm -f $SLOTFILE
cat $NODEFILES | \
egrep -v '(^#|^[ 	/]*\*|typedef|extern|Defs)'  | \
sed -e 's/;//' \
    -e '/\/\*/,/\*\//D' \
    -e 's/	/ /g' \
    -e 's/  */ /g' \
    -e 's/\\//' | \
awk '
# ----------------
#	ORS and OFS are the output field and record separators
#	nc is the number of "class"es we have scanned
#	inside is a variable set to 1 when we are scanning the
#	   contents of a class definition.
# ----------------
BEGIN { 
	ORS = " "; 
	OFS = "";
	nc = 0;
	inside = 0;
}

# ----------------
#	first search for the "class" tag.  once found
#	extract class information into the classes[] array.
#	i is the slot number of the next slot we scan..
# ----------------
/class /,/{/ 	{ 
	class = substr($2,2,length($2)-2); 
	classes[ nc++ ] = class;
        i = 1;
	inside = 1;
}

# ----------------
#	process the contents of the class definition
#
#	decl[ class 0 ] contains the number of slots + 1
#	decl[ class x ] contains the slot name for slot x
#	whole[ class x ] contains the entire declaration for slot x
#	parents[ class ] contains the parent class names
# ----------------
/{/,/}/ {
	if (inside == 0)
		next;

        if ($1 ~ /{/)
		next;

	if ($1 ~ /}/) {
		decl[ class 0 ] = i;
		i = 1;
		inside = 0;
		next;
	}

	if ($1 ~ /inherits/) {
		parent = substr($1,10,length($1)-10);
		parents[ class ] = parent;

		if (parent != "Node") {
			ndecs = decl[ parent 0 ];
			for (j=1; j<ndecs; j++) {
	  			whole[ class i] = whole[ parent j ];
				decl[ class i++ ] = decl[ parent j ];
			}
		}
		next;
	}

	if ($1 ~ /struct/) {
		whole[ class i ] = $0;
	   	decl[ class i++ ] = substr($3,2,length($3)-1);
		next;
	}

	if ($1 !~ /inherits/ && $1 !~ /struct/ && $1 !~ /class/) {
		whole[ class i ] = $0;
		decl[ class i++ ] = $2;
	}
}

# ----------------
#	all nodes have been scanned, now write node slot information.
#	the output format is:
#	
#	{
#	number-of-slots  class
#	slot-name: slot-declaration
#	slot-name: slot-declaration
#	   ...
#	}
#	
#	example:
#	
#	{
#	7 Plan
#	cost:  Cost cost
#	fragment:  Index fragment
#	state:  struct EState *state
#	qptargetlist:  List qptargetlist
#	qpqual:  List qpqual
#	lefttree:  struct Plan *lefttree
#	righttree:  struct Plan *righttree
#	}
#
# ----------------
END {
	decl[ class 0 ] = i;
	
	for (j=0; j<nc; j++) {
		class = classes[ j ];
		ndecs = decl[ class 0 ];
		if (class != "Node") {
			print "\n{";
			print "\n" ndecs-1 " " class;
			for (i=1; i<ndecs; i++) {
				print "\n" decl[ class i ] ":" \
				           whole[ class i ];
			}
			print "\n}";
			print "\n";
		}
	}
	print "\n";
}
' > $SLOTFILE

# ----------------
#	now generate inh.c
# ----------------
cat > $OUTFILE << 'EOF'
/* ----------------------------------------------------------------
 *   FILE
 *	inh.c
 *	
 *   DESCRIPTION
 *	node inheritance graph file - generated by the inherits.sh
 *	script from the contents of the nodes files.
 *
 *   NOTES
 *	NodeIsType() now uses a lookup table instead of doing a
 *	tree walk.  The first time you ask for a node's type, we
 *	walk the tree and initialize the corresponding entries in
 *	the table.  Subsequent calls go directly to the table.
 *
 *   IDENTIFICATION
 *	$Header: /private/postgres/src/lib/Gen/RCS/inherits.sh,v 1.11 1991/04/28 09:16:09 cimarron Exp $
 * ----------------------------------------------------------------
 */

#include "tmp/c.h"
#include "nodes/pg_lisp.h"
#include "nodes/nodes.h"
#include "nodes/primnodes.h"
#include "nodes/execnodes.h"
#include "nodes/relation.h"
#include "nodes/plannodes.h"
#include "nodes/mnodes.h"
EOF

echo '#include' \"$TAGFILE\" >> $OUTFILE
cat >> $OUTFILE << 'EOF'

struct nodeinfo {
	char	*ni_name;
	TypeId	ni_id;
	TypeId	ni_parent;
	Size	ni_size;
};
struct nodeinfo _NodeInfo[] = {
EOF

awk '{ if ($2 == "") { $2 = "Node" };\
       printf("	{ \"%s\", T_%s, T_%s, sizeof(struct _%s) },\n", $1, $1, $2, $1) }' \
      $INHFILE >> $OUTFILE
cat >> $OUTFILE << 'EOF'
	{ "INVALID", 0, 0, 0 }
};

#define _NClasses 	(lengthof(_NodeInfo) - 1)
#define _NClassBytes	((_NClasses / 8) + 1)

TypeId _InvalidTypeId = (TypeId) _NClasses;
bits8  _NodeClassArray[_NClasses][_NClassBytes];    
bits8  _NodeBitMask[] = { 1<<0, 1<<1, 1<<2, 1<<3, 1<<4, 1<<5, 1<<6, 1<<7 };

bool   _NodeInfoTrace = false;

/* ----------------
 *	InitNodeArray initializes our node's inheritance table.
 * ----------------
 */
void
InitNodeArray(i)
    register TypeId i;
{
    register TypeId j;
    register int    q;
    register int    r;

    /* ----------------
     *	base clause for recursion: initializing the root node
     * ----------------
     */
    if (i == T_Node) {
	_NodeClassArray[i][0] = 1<<0;
	return;
    }

    /* ----------------
     *	recursive initialization: initialize our parent, if necessary
     * ----------------
     */
    j = _NodeInfo[i].ni_parent;
    if (_NodeClassArray[j][0] == 0)
	InitNodeArray(j);

    /* ----------------
     *	now initialize ourself by copying our parent's bitmask and
     *  setting our own bit.
     * ----------------
     */
    for (q=0; q<_NClassBytes; q++)
	_NodeClassArray[i][q] = _NodeClassArray[j][q];
    
    q = i / 8; r = i % 8;
    _NodeClassArray[i][q] |= _NodeBitMask[r];
}

/* ----------------
 *	NodeIsType
 *
 * 	determine if (thisNode) is of type (tag) or is a subclass
 *	of type (tag).  We do this by consulting the master
 *	node class array bitmap: look in the row corresponding
 *	to (thisNode)'s type.  Now look at the bit in the position
 *	indicated by (tag).  If this is set, then (thisNode) is
 *	of type (tag), otherwise it's not.
 *		     
 *	The table is initialized on demand.  If nobody ever asks
 *	about a given class, then we never initialize that class's
 *	information...
 * ----------------
 */
bool
NodeIsType(thisNode, tag)
    Node		thisNode;
    register TypeId 	tag;
{
    register TypeId i;
    register int    q;
    register int    r;
	
    Assert(NodeIsValid(thisNode));
    
    i = NodeType(thisNode);
    Assert(TypeIdIsValid(i));
    Assert(TypeIdIsValid(tag));

    if (_NodeClassArray[i][0] == 0)
	InitNodeArray(i);

    q = tag / 8; r = tag % 8;
    return (bool)
	(_NodeClassArray[i][q] & _NodeBitMask[r]);
}
    
void
Dump_NodeInfo()
{
    register TypeId	i;

    printf("%16.16s%16.16s%16.16s\n", 
	   "NODE NAME:", "NODE TAG:", "PARENT NODE:");

    for (i = 0; i < _InvalidTypeId; ++i)
	printf("%16.16s%16.1d%16.16s\n",
	       _NodeInfo[i].ni_name,
	       _NodeInfo[i].ni_id, 
	       _NodeInfo[_NodeInfo[i].ni_parent].ni_name);
}

EOF
rm -f $INHFILE

# ----------------
#	finally, compare the new tagfile with the old.
#
#	if the tagfile has changed, then it means that
#	cinterface.a has to be remade because its tag #defines
#	are different.
# ----------------
if [ -r $TAGFILE ]; then 
	if cmp -s $TAGFILE $TAGTEMP ; then 
		echo "tags.h unchanged";
	else
		mv $TAGTEMP $TAGFILE; 
		echo "tags.h has changed; remake cinterface.a";
	fi
else
	mv $TAGTEMP $TAGFILE;
fi

# ----------------
#	all done
# ----------------
exit 0
