summaryrefslogtreecommitdiffstats
path: root/contrib/gperf/src/key-list.cc
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/gperf/src/key-list.cc')
-rw-r--r--contrib/gperf/src/key-list.cc1957
1 files changed, 1957 insertions, 0 deletions
diff --git a/contrib/gperf/src/key-list.cc b/contrib/gperf/src/key-list.cc
new file mode 100644
index 0000000..38341f3
--- /dev/null
+++ b/contrib/gperf/src/key-list.cc
@@ -0,0 +1,1957 @@
+/* Routines for building, ordering, and printing the keyword list.
+ Copyright (C) 1989-1998 Free Software Foundation, Inc.
+ written by Douglas C. Schmidt (schmidt@ics.uci.edu)
+
+This file is part of GNU GPERF.
+
+GNU GPERF is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 1, or (at your option)
+any later version.
+
+GNU GPERF is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU GPERF; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <stdio.h>
+#include <string.h> /* declares strncpy(), strchr() */
+#include <stdlib.h> /* declares malloc(), free(), abs(), exit(), abort() */
+#include <assert.h> /* defines assert() */
+#include <limits.h> /* defines SCHAR_MAX etc. */
+#include "options.h"
+#include "read-line.h"
+#include "hash-table.h"
+#include "key-list.h"
+#include "trace.h"
+#include "version.h"
+
+/* Make the hash table 8 times larger than the number of keyword entries. */
+static const int TABLE_MULTIPLE = 10;
+
+/* Efficiently returns the least power of two greater than or equal to X! */
+#define POW(X) ((!X)?1:(X-=1,X|=X>>1,X|=X>>2,X|=X>>4,X|=X>>8,X|=X>>16,(++X)))
+
+int Key_List::determined[MAX_ALPHA_SIZE];
+
+/* Destructor dumps diagnostics during debugging. */
+
+Key_List::~Key_List (void)
+{
+ T (Trace t ("Key_List::~Key_List");)
+ if (option[DEBUG])
+ {
+ fprintf (stderr, "\nDumping key list information:\ntotal non-static linked keywords = %d"
+ "\ntotal keywords = %d\ntotal duplicates = %d\nmaximum key length = %d\n",
+ list_len, total_keys, total_duplicates, max_key_len);
+ dump ();
+ fprintf (stderr, "End dumping list.\n\n");
+ }
+}
+
+/* Gathers the input stream into a buffer until one of two things occur:
+
+ 1. We read a '%' followed by a '%'
+ 2. We read a '%' followed by a '}'
+
+ The first symbolizes the beginning of the keyword list proper,
+ The second symbolizes the end of the C source code to be generated
+ verbatim in the output file.
+
+ I assume that the keys are separated from the optional preceding struct
+ declaration by a consecutive % followed by either % or } starting in
+ the first column. The code below uses an expandible buffer to scan off
+ and return a pointer to all the code (if any) appearing before the delimiter. */
+
+const char *
+Key_List::get_special_input (char delimiter)
+{
+ T (Trace t ("Key_List::get_special_input");)
+ int size = 80;
+ char *buf = new char[size];
+ int c, i;
+
+ for (i = 0; (c = getchar ()) != EOF; i++)
+ {
+ if (c == '%')
+ {
+ if ((c = getchar ()) == delimiter)
+ {
+
+ while ((c = getchar ()) != '\n')
+ ; /* discard newline */
+
+ if (i == 0)
+ return "";
+ else
+ {
+ buf[delimiter == '%' && buf[i - 2] == ';' ? i - 2 : i - 1] = '\0';
+ return buf;
+ }
+ }
+ else
+ buf[i++] = '%';
+ }
+ else if (i >= size) /* Yikes, time to grow the buffer! */
+ {
+ char *temp = new char[size *= 2];
+ int j;
+
+ for (j = 0; j < i; j++)
+ temp[j] = buf[j];
+
+ buf = temp;
+ }
+ buf[i] = c;
+ }
+
+ return 0; /* Problem here. */
+}
+
+/* Stores any C text that must be included verbatim into the
+ generated code output. */
+
+const char *
+Key_List::save_include_src (void)
+{
+ T (Trace t ("Key_List::save_include_src");)
+ int c;
+
+ if ((c = getchar ()) != '%')
+ ungetc (c, stdin);
+ else if ((c = getchar ()) != '{')
+ {
+ fprintf (stderr, "internal error, %c != '{' on line %d in file %s", c, __LINE__, __FILE__);
+ exit (1);
+ }
+ else
+ return get_special_input ('}');
+ return "";
+}
+
+/* Determines from the input file whether the user wants to build a table
+ from a user-defined struct, or whether the user is content to simply
+ use the default array of keys. */
+
+const char *
+Key_List::get_array_type (void)
+{
+ T (Trace t ("Key_List::get_array_type");)
+ return get_special_input ('%');
+}
+
+/* strcspn - find length of initial segment of S consisting entirely
+ of characters not from REJECT (borrowed from Henry Spencer's
+ ANSI string package, when GNU libc comes out I'll replace this...). */
+
+#ifndef strcspn
+inline int
+Key_List::strcspn (const char *s, const char *reject)
+{
+ T (Trace t ("Key_List::strcspn");)
+ const char *scan;
+ const char *rej_scan;
+ int count = 0;
+
+ for (scan = s; *scan; scan++)
+ {
+
+ for (rej_scan = reject; *rej_scan; rej_scan++)
+ if (*scan == *rej_scan)
+ return count;
+
+ count++;
+ }
+
+ return count;
+}
+#endif
+
+/* Sets up the Return_Type, the Struct_Tag type and the Array_Type
+ based upon various user Options. */
+
+void
+Key_List::set_output_types (void)
+{
+ T (Trace t ("Key_List::set_output_types");)
+ if (option[TYPE])
+ {
+ array_type = get_array_type ();
+ if (!array_type)
+ /* Something's wrong, but we'll catch it later on, in read_keys()... */
+ return;
+ /* Yow, we've got a user-defined type... */
+ int i = strcspn (array_type, "{\n\0");
+ /* Remove trailing whitespace. */
+ while (i > 0 && strchr (" \t", array_type[i-1]))
+ i--;
+ int struct_tag_length = i;
+
+ /* Set `struct_tag' to a naked "struct something". */
+ char *structtag = new char[struct_tag_length + 1];
+ strncpy (structtag, array_type, struct_tag_length);
+ structtag[struct_tag_length] = '\0';
+ struct_tag = structtag;
+
+ /* The return type of the lookup function is "struct something *".
+ No "const" here, because if !option[CONST], some user code might want
+ to modify the structure. */
+ char *rettype = new char[struct_tag_length + 3];
+ strncpy (rettype, array_type, struct_tag_length);
+ rettype[struct_tag_length] = ' ';
+ rettype[struct_tag_length + 1] = '*';
+ rettype[struct_tag_length + 2] = '\0';
+ return_type = rettype;
+ }
+}
+
+/* Reads in all keys from standard input and creates a linked list pointed
+ to by Head. This list is then quickly checked for ``links,'' i.e.,
+ unhashable elements possessing identical key sets and lengths. */
+
+void
+Key_List::read_keys (void)
+{
+ T (Trace t ("Key_List::read_keys");)
+ char *ptr;
+
+ include_src = save_include_src ();
+ set_output_types ();
+
+ /* Oops, problem with the input file. */
+ if (! (ptr = Read_Line::get_line ()))
+ {
+ fprintf (stderr, "No words in input file, did you forget to prepend %s or use -t accidentally?\n", "%%");
+ exit (1);
+ }
+
+ /* Read in all the keywords from the input file. */
+ else
+ {
+ const char *delimiter = option.get_delimiter ();
+ List_Node *temp, *trail = 0;
+
+ head = new List_Node (ptr, strcspn (ptr, delimiter));
+
+ for (temp = head;
+ (ptr = Read_Line::get_line ()) && strcmp (ptr, "%%");
+ temp = temp->next)
+ {
+ temp->next = new List_Node (ptr, strcspn (ptr, delimiter));
+ total_keys++;
+ }
+
+ /* See if any additional C code is included at end of this file. */
+ if (ptr)
+ additional_code = 1;
+
+ /* Hash table this number of times larger than keyword number. */
+ int table_size = (list_len = total_keys) * TABLE_MULTIPLE;
+
+#if LARGE_STACK_ARRAYS
+ /* By allocating the memory here we save on dynamic allocation overhead.
+ Table must be a power of 2 for the hash function scheme to work. */
+ List_Node *table[POW (table_size)];
+#else
+ // Note: we don't use new, because that invokes a custom operator new.
+ int malloc_size = POW (table_size) * sizeof(List_Node*);
+ if (malloc_size == 0) malloc_size = 1;
+ List_Node **table = (List_Node**)malloc(malloc_size);
+ if (table == NULL)
+ abort ();
+#endif
+
+ /* Make large hash table for efficiency. */
+ Hash_Table found_link (table, table_size);
+
+ /* Test whether there are any links and also set the maximum length of
+ an identifier in the keyword list. */
+
+ for (temp = head; temp; temp = temp->next)
+ {
+ List_Node *ptr = found_link (temp, option[NOLENGTH]);
+
+ /* Check for links. We deal with these by building an equivalence class
+ of all duplicate values (i.e., links) so that only 1 keyword is
+ representative of the entire collection. This *greatly* simplifies
+ processing during later stages of the program. */
+
+ if (ptr)
+ {
+ total_duplicates++;
+ list_len--;
+ trail->next = temp->next;
+ temp->link = ptr->link;
+ ptr->link = temp;
+
+ /* Complain if user hasn't enabled the duplicate option. */
+ if (!option[DUP] || option[DEBUG])
+ fprintf (stderr, "Key link: \"%s\" = \"%s\", with key set \"%s\".\n",
+ temp->key, ptr->key, temp->char_set);
+ }
+ else
+ trail = temp;
+
+ /* Update minimum and maximum keyword length, if needed. */
+ if (max_key_len < temp->length)
+ max_key_len = temp->length;
+ if (min_key_len > temp->length)
+ min_key_len = temp->length;
+ }
+
+#if !LARGE_STACK_ARRAYS
+ free ((char *) table);
+#endif
+
+ /* Exit program if links exists and option[DUP] not set, since we can't continue */
+ if (total_duplicates)
+ {
+ if (option[DUP])
+ fprintf (stderr, "%d input keys have identical hash values, examine output carefully...\n",
+ total_duplicates);
+ else
+ {
+ fprintf (stderr, "%d input keys have identical hash values,\ntry different key positions or use option -D.\n",
+ total_duplicates);
+ exit (1);
+ }
+ }
+ if (option[ALLCHARS])
+ option.set_keysig_size (max_key_len);
+ }
+}
+
+/* Recursively merges two sorted lists together to form one sorted list. The
+ ordering criteria is by frequency of occurrence of elements in the key set
+ or by the hash value. This is a kludge, but permits nice sharing of
+ almost identical code without incurring the overhead of a function
+ call comparison. */
+
+List_Node *
+Key_List::merge (List_Node *list1, List_Node *list2)
+{
+ T (Trace t ("Key_List::merge");)
+ List_Node *result;
+ List_Node **resultp = &result;
+ for (;;)
+ {
+ if (!list1)
+ {
+ *resultp = list2;
+ break;
+ }
+ if (!list2)
+ {
+ *resultp = list1;
+ break;
+ }
+ if (occurrence_sort && list1->occurrence < list2->occurrence
+ || hash_sort && list1->hash_value > list2->hash_value)
+ {
+ *resultp = list2;
+ resultp = &list2->next; list2 = list1; list1 = *resultp;
+ }
+ else
+ {
+ *resultp = list1;
+ resultp = &list1->next; list1 = *resultp;
+ }
+ }
+ return result;
+}
+
+/* Applies the merge sort algorithm to recursively sort the key list by
+ frequency of occurrence of elements in the key set. */
+
+List_Node *
+Key_List::merge_sort (List_Node *head)
+{
+ T (Trace t ("Key_List::merge_sort");)
+ if (!head || !head->next)
+ return head;
+ else
+ {
+ List_Node *middle = head;
+ List_Node *temp = head->next->next;
+
+ while (temp)
+ {
+ temp = temp->next;
+ middle = middle->next;
+ if (temp)
+ temp = temp->next;
+ }
+
+ temp = middle->next;
+ middle->next = 0;
+ return merge (merge_sort (head), merge_sort (temp));
+ }
+}
+
+/* Returns the frequency of occurrence of elements in the key set. */
+
+inline int
+Key_List::get_occurrence (List_Node *ptr)
+{
+ T (Trace t ("Key_List::get_occurrence");)
+ int value = 0;
+
+ for (const char *temp = ptr->char_set; *temp; temp++)
+ value += occurrences[(unsigned char)(*temp)];
+
+ return value;
+}
+
+/* Enables the index location of all key set elements that are now
+ determined. */
+
+inline void
+Key_List::set_determined (List_Node *ptr)
+{
+ T (Trace t ("Key_List::set_determined");)
+ for (const char *temp = ptr->char_set; *temp; temp++)
+ determined[(unsigned char)(*temp)] = 1;
+}
+
+/* Returns TRUE if PTR's key set is already completely determined. */
+
+inline int
+Key_List::already_determined (List_Node *ptr)
+{
+ T (Trace t ("Key_List::already_determined");)
+ int is_determined = 1;
+
+ for (const char *temp = ptr->char_set; is_determined && *temp; temp++)
+ is_determined = determined[(unsigned char)(*temp)];
+
+ return is_determined;
+}
+
+/* Reorders the table by first sorting the list so that frequently occuring
+ keys appear first, and then the list is reorded so that keys whose values
+ are already determined will be placed towards the front of the list. This
+ helps prune the search time by handling inevitable collisions early in the
+ search process. See Cichelli's paper from Jan 1980 JACM for details.... */
+
+void
+Key_List::reorder (void)
+{
+ T (Trace t ("Key_List::reorder");)
+ List_Node *ptr;
+ for (ptr = head; ptr; ptr = ptr->next)
+ ptr->occurrence = get_occurrence (ptr);
+
+ hash_sort = 0;
+ occurrence_sort = 1;
+
+ for (ptr = head = merge_sort (head); ptr->next; ptr = ptr->next)
+ {
+ set_determined (ptr);
+
+ if (already_determined (ptr->next))
+ continue;
+ else
+ {
+ List_Node *trail_ptr = ptr->next;
+ List_Node *run_ptr = trail_ptr->next;
+
+ for (; run_ptr; run_ptr = trail_ptr->next)
+ {
+
+ if (already_determined (run_ptr))
+ {
+ trail_ptr->next = run_ptr->next;
+ run_ptr->next = ptr->next;
+ ptr = ptr->next = run_ptr;
+ }
+ else
+ trail_ptr = run_ptr;
+ }
+ }
+ }
+}
+
+/* ============================ Output routines ============================ */
+
+/* The "const " qualifier. */
+static const char *const_always;
+
+/* The "const " qualifier, for read-only arrays. */
+static const char *const_readonly_array;
+
+/* The "const " qualifier, for the array type. */
+static const char *const_for_struct;
+
+/* Returns the smallest unsigned C type capable of holding integers up to N. */
+
+static const char *
+smallest_integral_type (int n)
+{
+ if (n <= UCHAR_MAX) return "unsigned char";
+ if (n <= USHRT_MAX) return "unsigned short";
+ return "unsigned int";
+}
+
+/* Returns the smallest signed C type capable of holding integers
+ from MIN to MAX. */
+
+static const char *
+smallest_integral_type (int min, int max)
+{
+ if (option[ANSIC] | option[CPLUSPLUS])
+ if (min >= SCHAR_MIN && max <= SCHAR_MAX) return "signed char";
+ if (min >= SHRT_MIN && max <= SHRT_MAX) return "short";
+ return "int";
+}
+
+/* A cast from `char' to a valid array index. */
+static const char *char_to_index;
+
+/* ------------------------------------------------------------------------- */
+
+/* Computes the maximum and minimum hash values. Since the
+ list is already sorted by hash value all we need to do is
+ find the final item! */
+
+void
+Key_List::compute_min_max (void)
+{
+ T (Trace t ("Key_List::compute_min_max");)
+ List_Node *temp;
+ for (temp = head; temp->next; temp = temp->next)
+ ;
+
+ min_hash_value = head->hash_value;
+ max_hash_value = temp->hash_value;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Returns the number of different hash values. */
+
+int
+Key_List::num_hash_values (void)
+{
+ T (Trace t ("Key_List::num_hash_values");)
+ int count = 1;
+ List_Node *temp;
+ int value;
+
+ for (temp = head, value = temp->hash_value; temp->next; )
+ {
+ temp = temp->next;
+ if (value != temp->hash_value)
+ {
+ value = temp->hash_value;
+ count++;
+ }
+ }
+ return count;
+}
+
+/* -------------------- Output_Constants and subclasses -------------------- */
+
+/* This class outputs an enumeration defining some constants. */
+
+struct Output_Constants
+{
+ virtual void output_start () = 0;
+ virtual void output_item (const char *name, int value) = 0;
+ virtual void output_end () = 0;
+ Output_Constants () {}
+ virtual ~Output_Constants () {}
+};
+
+/* This class outputs an enumeration in #define syntax. */
+
+struct Output_Defines : public Output_Constants
+{
+ virtual void output_start ();
+ virtual void output_item (const char *name, int value);
+ virtual void output_end ();
+ Output_Defines () {}
+ virtual ~Output_Defines () {}
+};
+
+void Output_Defines::output_start ()
+{
+ T (Trace t ("Output_Defines::output_start");)
+ printf ("\n");
+}
+
+void Output_Defines::output_item (const char *name, int value)
+{
+ T (Trace t ("Output_Defines::output_item");)
+ printf ("#define %s %d\n", name, value);
+}
+
+void Output_Defines::output_end ()
+{
+ T (Trace t ("Output_Defines::output_end");)
+}
+
+/* This class outputs an enumeration using `enum'. */
+
+struct Output_Enum : public Output_Constants
+{
+ virtual void output_start ();
+ virtual void output_item (const char *name, int value);
+ virtual void output_end ();
+ Output_Enum (const char *indent) : indentation (indent) {}
+ virtual ~Output_Enum () {}
+private:
+ const char *indentation;
+ int pending_comma;
+};
+
+void Output_Enum::output_start ()
+{
+ T (Trace t ("Output_Enum::output_start");)
+ printf ("%senum\n"
+ "%s {\n",
+ indentation, indentation);
+ pending_comma = 0;
+}
+
+void Output_Enum::output_item (const char *name, int value)
+{
+ T (Trace t ("Output_Enum::output_item");)
+ if (pending_comma)
+ printf (",\n");
+ printf ("%s %s = %d", indentation, name, value);
+ pending_comma = 1;
+}
+
+void Output_Enum::output_end ()
+{
+ T (Trace t ("Output_Enum::output_end");)
+ if (pending_comma)
+ printf ("\n");
+ printf ("%s };\n\n", indentation);
+}
+
+/* Outputs the maximum and minimum hash values etc. */
+
+void
+Key_List::output_constants (struct Output_Constants& style)
+{
+ T (Trace t ("Key_List::output_constants");)
+
+ style.output_start ();
+ style.output_item ("TOTAL_KEYWORDS", total_keys);
+ style.output_item ("MIN_WORD_LENGTH", min_key_len);
+ style.output_item ("MAX_WORD_LENGTH", max_key_len);
+ style.output_item ("MIN_HASH_VALUE", min_hash_value);
+ style.output_item ("MAX_HASH_VALUE", max_hash_value);
+ style.output_end ();
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Outputs a keyword, as a string: enclosed in double quotes, escaping
+ backslashes and double quote characters. */
+
+static void
+output_string (const char *key)
+{
+ T (Trace t ("output_string");)
+ char c;
+
+ putchar ('"');
+ while (c = *key++, c != '\0')
+ {
+ if (c == '"' || c == '\\')
+ putchar ('\\');
+ putchar (c);
+ }
+ putchar ('"');
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Outputs a type and a const specifier.
+ The output is terminated with a space. */
+
+static void
+output_const_type (const char *const_string, const char *type_string)
+{
+ if (type_string[strlen(type_string)-1] == '*')
+ printf ("%s %s", type_string, const_string);
+ else
+ printf ("%s%s ", const_string, type_string);
+}
+
+/* ----------------------- Output_Expr and subclasses ----------------------- */
+
+/* This class outputs a general expression. */
+
+struct Output_Expr
+{
+ virtual void output_expr () const = 0;
+ Output_Expr () {}
+ virtual ~Output_Expr () {}
+};
+
+/* This class outputs an expression formed by a single string. */
+
+struct Output_Expr1 : public Output_Expr
+{
+ virtual void output_expr () const;
+ Output_Expr1 (const char *piece1) : p1 (piece1) {}
+ virtual ~Output_Expr1 () {}
+private:
+ const char *p1;
+};
+
+void Output_Expr1::output_expr () const
+{
+ T (Trace t ("Output_Expr1::output_expr");)
+ printf ("%s", p1);
+}
+
+#if 0 /* unused */
+
+/* This class outputs an expression formed by the concatenation of two
+ strings. */
+
+struct Output_Expr2 : public Output_Expr
+{
+ virtual void output_expr () const;
+ Output_Expr2 (const char *piece1, const char *piece2)
+ : p1 (piece1), p2 (piece2) {}
+ virtual ~Output_Expr2 () {}
+private:
+ const char *p1;
+ const char *p2;
+};
+
+void Output_Expr2::output_expr () const
+{
+ T (Trace t ("Output_Expr2::output_expr");)
+ printf ("%s%s", p1, p2);
+}
+
+#endif
+
+/* --------------------- Output_Compare and subclasses --------------------- */
+
+/* This class outputs a comparison expression. */
+
+struct Output_Compare
+{
+ virtual void output_comparison (const Output_Expr& expr1,
+ const Output_Expr& expr2) const = 0;
+ Output_Compare () {}
+ virtual ~Output_Compare () {}
+};
+
+/* This class outputs a comparison using strcmp. */
+
+struct Output_Compare_Strcmp : public Output_Compare
+{
+ virtual void output_comparison (const Output_Expr& expr1,
+ const Output_Expr& expr2) const;
+ Output_Compare_Strcmp () {}
+ virtual ~Output_Compare_Strcmp () {}
+};
+
+void Output_Compare_Strcmp::output_comparison (const Output_Expr& expr1,
+ const Output_Expr& expr2) const
+{
+ T (Trace t ("Output_Compare_Strcmp::output_comparison");)
+ printf ("*");
+ expr1.output_expr ();
+ printf (" == *");
+ expr2.output_expr ();
+ printf (" && !strcmp (");
+ expr1.output_expr ();
+ printf (" + 1, ");
+ expr2.output_expr ();
+ printf (" + 1)");
+}
+
+/* This class outputs a comparison using strncmp.
+ Note that the length of expr1 will be available through the local variable
+ `len'. */
+
+struct Output_Compare_Strncmp : public Output_Compare
+{
+ virtual void output_comparison (const Output_Expr& expr1,
+ const Output_Expr& expr2) const;
+ Output_Compare_Strncmp () {}
+ virtual ~Output_Compare_Strncmp () {}
+};
+
+void Output_Compare_Strncmp::output_comparison (const Output_Expr& expr1,
+ const Output_Expr& expr2) const
+{
+ T (Trace t ("Output_Compare_Strncmp::output_comparison");)
+ printf ("*");
+ expr1.output_expr ();
+ printf (" == *");
+ expr2.output_expr ();
+ printf (" && !strncmp (");
+ expr1.output_expr ();
+ printf (" + 1, ");
+ expr2.output_expr ();
+ printf (" + 1, len - 1)");
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Generates C code for the hash function that returns the
+ proper encoding for each key word. */
+
+void
+Key_List::output_hash_function (void)
+{
+ T (Trace t ("Key_List::output_hash_function");)
+ const int max_column = 10;
+ int field_width;
+
+ /* Calculate maximum number of digits required for MAX_HASH_VALUE. */
+ field_width = 2;
+ for (int trunc = max_hash_value; (trunc /= 10) > 0;)
+ field_width++;
+
+ /* Output the function's head. */
+ if (option[CPLUSPLUS])
+ printf ("inline ");
+ else if (option[KRC] | option[C] | option[ANSIC])
+ printf ("#ifdef __GNUC__\n"
+ "__inline\n"
+ "#endif\n");
+
+ if (option[KRC] | option[C] | option[ANSIC])
+ printf ("static ");
+ printf ("unsigned int\n");
+ if (option[CPLUSPLUS])
+ printf ("%s::", option.get_class_name ());
+ printf ("%s ", option.get_hash_name ());
+ printf (option[KRC] ?
+ "(str, len)\n"
+ " register char *str;\n"
+ " register unsigned int len;\n" :
+ option[C] ?
+ "(str, len)\n"
+ " register const char *str;\n"
+ " register unsigned int len;\n" :
+ option[ANSIC] | option[CPLUSPLUS] ?
+ "(register const char *str, register unsigned int len)\n" :
+ "");
+
+ /* Note that when the hash function is called, it has already been verified
+ that min_key_len <= len <= max_key_len. */
+
+ /* Output the function's body. */
+ printf ("{\n");
+
+ /* First the asso_values array. */
+ printf (" static %s%s asso_values[] =\n"
+ " {",
+ const_readonly_array,
+ smallest_integral_type (max_hash_value + 1));
+
+ for (int count = 0; count < ALPHA_SIZE; count++)
+ {
+ if (count > 0)
+ printf (",");
+ if (!(count % max_column))
+ printf ("\n ");
+ printf ("%*d", field_width,
+ occurrences[count] ? asso_values[count] : max_hash_value + 1);
+ }
+
+ printf ("\n"
+ " };\n");
+
+ /* Optimize special case of ``-k 1,$'' */
+ if (option[DEFAULTCHARS])
+ printf (" return %sasso_values[%sstr[len - 1]] + asso_values[%sstr[0]];\n",
+ option[NOLENGTH] ? "" : "len + ",
+ char_to_index, char_to_index);
+ else
+ {
+ int key_pos;
+
+ option.reset ();
+
+ /* Get first (also highest) key position. */
+ key_pos = option.get ();
+
+ if (!option[ALLCHARS] && (key_pos == WORD_END || key_pos <= min_key_len))
+ {
+ /* We can perform additional optimizations here:
+ Write it out as a single expression. Note that the values
+ are added as `int's even though the asso_values array may
+ contain `unsigned char's or `unsigned short's. */
+
+ printf (" return %s",
+ option[NOLENGTH] ? "" : "len + ");
+
+ for (; key_pos != WORD_END; )
+ {
+ printf ("asso_values[%sstr[%d]]", char_to_index, key_pos - 1);
+ if ((key_pos = option.get ()) != EOS)
+ printf (" + ");
+ else
+ break;
+ }
+
+ if (key_pos == WORD_END)
+ printf ("asso_values[%sstr[len - 1]]", char_to_index);
+
+ printf (";\n");
+ }
+ else
+ {
+ /* We've got to use the correct, but brute force, technique. */
+ printf (" register int hval = %s;\n\n"
+ " switch (%s)\n"
+ " {\n"
+ " default:\n",
+ option[NOLENGTH] ? "0" : "len",
+ option[NOLENGTH] ? "len" : "hval");
+
+ /* User wants *all* characters considered in hash. */
+ if (option[ALLCHARS])
+ {
+ for (int i = max_key_len; i > 0; i--)
+ printf (" case %d:\n"
+ " hval += asso_values[%sstr[%d]];\n",
+ i, char_to_index, i - 1);
+
+ printf (" break;\n"
+ " }\n"
+ " return hval;\n");
+ }
+ else /* do the hard part... */
+ {
+ while (key_pos != WORD_END && key_pos > max_key_len)
+ if ((key_pos = option.get ()) == EOS)
+ break;
+
+ if (key_pos != EOS && key_pos != WORD_END)
+ {
+ int i = key_pos;
+ do
+ {
+ for ( ; i >= key_pos; i--)
+ printf (" case %d:\n", i);
+
+ printf (" hval += asso_values[%sstr[%d]];\n",
+ char_to_index, key_pos - 1);
+
+ key_pos = option.get ();
+ }
+ while (key_pos != EOS && key_pos != WORD_END);
+
+ for ( ; i >= min_key_len; i--)
+ printf (" case %d:\n", i);
+ }
+
+ printf (" break;\n"
+ " }\n"
+ " return hval");
+ if (key_pos == WORD_END)
+ printf (" + asso_values[%sstr[len - 1]]", char_to_index);
+ printf (";\n");
+ }
+ }
+ }
+ printf ("}\n\n");
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Prints out a table of keyword lengths, for use with the
+ comparison code in generated function ``in_word_set''. */
+
+void
+Key_List::output_keylength_table (void)
+{
+ T (Trace t ("Key_List::output_keylength_table");)
+ const int columns = 14;
+ int index;
+ int column;
+ const char *indent = option[GLOBAL] ? "" : " ";
+ List_Node *temp;
+
+ printf ("%sstatic %s%s lengthtable[] =\n%s {",
+ indent, const_readonly_array,
+ smallest_integral_type (max_key_len),
+ indent);
+
+ /* Generate an array of lengths, similar to output_keyword_table. */
+
+ column = 0;
+ for (temp = head, index = 0; temp; temp = temp->next)
+ {
+ if (option[SWITCH] && !option[TYPE]
+ && !(temp->link
+ || (temp->next && temp->hash_value == temp->next->hash_value)))
+ continue;
+
+ if (index < temp->hash_value && !option[SWITCH] && !option[DUP])
+ {
+ /* Some blank entries. */
+ for ( ; index < temp->hash_value; index++)
+ {
+ if (index > 0)
+ printf (",");
+ if ((column++ % columns) == 0)
+ printf ("\n%s ", indent);
+ printf ("%3d", 0);
+ }
+ }
+
+ if (index > 0)
+ printf (",");
+ if ((column++ % columns) == 0)
+ printf("\n%s ", indent);
+ printf ("%3d", temp->length);
+
+ /* Deal with links specially. */
+ if (temp->link) // implies option[DUP]
+ for (List_Node *links = temp->link; links; links = links->link)
+ {
+ ++index;
+ printf (",");
+ if ((column++ % columns) == 0)
+ printf("\n%s ", indent);
+ printf ("%3d", links->length);
+ }
+
+ index++;
+ }
+
+ printf ("\n%s };\n", indent);
+ if (option[GLOBAL])
+ printf ("\n");
+}
+
+/* ------------------------------------------------------------------------- */
+
+static void
+output_keyword_entry (List_Node *temp, const char *indent)
+{
+ printf ("%s ", indent);
+ if (option[TYPE])
+ printf ("{");
+ output_string (temp->key);
+ if (option[TYPE])
+ printf (",%s}", temp->rest);
+ if (option[DEBUG])
+ printf (" /* hash value = %d, index = %d */",
+ temp->hash_value, temp->index);
+}
+
+static void
+output_keyword_blank_entries (int count, const char *indent)
+{
+ const int columns = 9;
+ int column = 0;
+ for (int i = 0; i < count; i++)
+ {
+ if ((column % columns) == 0)
+ {
+ if (i > 0)
+ printf (",\n");
+ printf ("%s ", indent);
+ }
+ else
+ {
+ if (i > 0)
+ printf (", ");
+ }
+ if (option[TYPE])
+ printf ("{\"\"}");
+ else
+ printf ("\"\"");
+ column++;
+ }
+}
+
+/* Prints out the array containing the key words for the hash function. */
+
+void
+Key_List::output_keyword_table (void)
+{
+ T (Trace t ("Key_List::output_keyword_table");)
+ const char *indent = option[GLOBAL] ? "" : " ";
+ int index;
+ List_Node *temp;
+
+ printf ("%sstatic ",
+ indent);
+ output_const_type (const_readonly_array, struct_tag);
+ printf ("%s[] =\n"
+ "%s {\n",
+ option.get_wordlist_name (),
+ indent);
+
+ /* Generate an array of reserved words at appropriate locations. */
+
+ for (temp = head, index = 0; temp; temp = temp->next)
+ {
+ if (option[SWITCH] && !option[TYPE]
+ && !(temp->link
+ || (temp->next && temp->hash_value == temp->next->hash_value)))
+ continue;
+
+ if (index > 0)
+ printf (",\n");
+
+ if (index < temp->hash_value && !option[SWITCH] && !option[DUP])
+ {
+ /* Some blank entries. */
+ output_keyword_blank_entries (temp->hash_value - index, indent);
+ printf (",\n");
+ index = temp->hash_value;
+ }
+
+ temp->index = index;
+
+ output_keyword_entry (temp, indent);
+
+ /* Deal with links specially. */
+ if (temp->link) // implies option[DUP]
+ for (List_Node *links = temp->link; links; links = links->link)
+ {
+ links->index = ++index;
+ printf (",\n");
+ output_keyword_entry (links, indent);
+ }
+
+ index++;
+ }
+ if (index > 0)
+ printf ("\n");
+
+ printf ("%s };\n\n", indent);
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Generates the large, sparse table that maps hash values into
+ the smaller, contiguous range of the keyword table. */
+
+void
+Key_List::output_lookup_array (void)
+{
+ T (Trace t ("Key_List::output_lookup_array");)
+ if (option[DUP])
+ {
+ const int DEFAULT_VALUE = -1;
+
+ /* Because of the way output_keyword_table works, every duplicate set is
+ stored contiguously in the wordlist array. */
+ struct duplicate_entry
+ {
+ int hash_value; /* Hash value for this particular duplicate set. */
+ int index; /* Index into the main keyword storage array. */
+ int count; /* Number of consecutive duplicates at this index. */
+ };
+
+#if LARGE_STACK_ARRAYS
+ duplicate_entry duplicates[total_duplicates];
+ int lookup_array[max_hash_value + 1 + 2*total_duplicates];
+#else
+ // Note: we don't use new, because that invokes a custom operator new.
+ duplicate_entry *duplicates = (duplicate_entry *)
+ malloc (total_duplicates * sizeof(duplicate_entry) + 1);
+ int *lookup_array = (int *)
+ malloc ((max_hash_value + 1 + 2*total_duplicates) * sizeof(int));
+ if (duplicates == NULL || lookup_array == NULL)
+ abort();
+#endif
+ int lookup_array_size = max_hash_value + 1;
+ duplicate_entry *dup_ptr = &duplicates[0];
+ int *lookup_ptr = &lookup_array[max_hash_value + 1 + 2*total_duplicates];
+
+ while (lookup_ptr > lookup_array)
+ *--lookup_ptr = DEFAULT_VALUE;
+
+ /* Now dup_ptr = &duplicates[0] and lookup_ptr = &lookup_array[0]. */
+
+ for (List_Node *temp = head; temp; temp = temp->next)
+ {
+ int hash_value = temp->hash_value;
+ lookup_array[hash_value] = temp->index;
+ if (option[DEBUG])
+ fprintf (stderr, "keyword = %s, index = %d\n",
+ temp->key, temp->index);
+ if (temp->link
+ || (temp->next && hash_value == temp->next->hash_value))
+ {
+ /* Start a duplicate entry. */
+ dup_ptr->hash_value = hash_value;
+ dup_ptr->index = temp->index;
+ dup_ptr->count = 1;
+
+ for (;;)
+ {
+ for (List_Node *ptr = temp->link; ptr; ptr = ptr->link)
+ {
+ dup_ptr->count++;
+ if (option[DEBUG])
+ fprintf (stderr,
+ "static linked keyword = %s, index = %d\n",
+ ptr->key, ptr->index);
+ }
+
+ if (!(temp->next && hash_value == temp->next->hash_value))
+ break;
+
+ temp = temp->next;
+
+ dup_ptr->count++;
+ if (option[DEBUG])
+ fprintf (stderr, "dynamic linked keyword = %s, index = %d\n",
+ temp->key, temp->index);
+ }
+ assert (dup_ptr->count >= 2);
+ dup_ptr++;
+ }
+ }
+
+ while (dup_ptr > duplicates)
+ {
+ dup_ptr--;
+
+ if (option[DEBUG])
+ fprintf (stderr,
+ "dup_ptr[%d]: hash_value = %d, index = %d, count = %d\n",
+ dup_ptr - duplicates,
+ dup_ptr->hash_value, dup_ptr->index, dup_ptr->count);
+
+ int i;
+ /* Start searching for available space towards the right part
+ of the lookup array. */
+ for (i = dup_ptr->hash_value; i < lookup_array_size-1; i++)
+ if (lookup_array[i] == DEFAULT_VALUE
+ && lookup_array[i + 1] == DEFAULT_VALUE)
+ goto found_i;
+ /* If we didn't find it to the right look to the left instead... */
+ for (i = dup_ptr->hash_value-1; i >= 0; i--)
+ if (lookup_array[i] == DEFAULT_VALUE
+ && lookup_array[i + 1] == DEFAULT_VALUE)
+ goto found_i;
+ /* Append to the end of lookup_array. */
+ i = lookup_array_size;
+ lookup_array_size += 2;
+ found_i:
+ /* Put in an indirection from dup_ptr->hash_value to i.
+ At i and i+1 store dup_ptr->index and dup_ptr->count. */
+ assert (lookup_array[dup_ptr->hash_value] == dup_ptr->index);
+ lookup_array[dup_ptr->hash_value] = - 1 - total_keys - i;
+ lookup_array[i] = - total_keys + dup_ptr->index;
+ lookup_array[i + 1] = - dup_ptr->count;
+ /* All these three values are <= -2, distinct from DEFAULT_VALUE. */
+ }
+
+ /* The values of the lookup array are now known. */
+
+ int min = INT_MAX;
+ int max = INT_MIN;
+ lookup_ptr = lookup_array + lookup_array_size;
+ while (lookup_ptr > lookup_array)
+ {
+ int val = *--lookup_ptr;
+ if (min > val)
+ min = val;
+ if (max < val)
+ max = val;
+ }
+
+ const char *indent = option[GLOBAL] ? "" : " ";
+ printf ("%sstatic %s%s lookup[] =\n"
+ "%s {",
+ indent, const_readonly_array, smallest_integral_type (min, max),
+ indent);
+
+ int field_width;
+ /* Calculate maximum number of digits required for MIN..MAX. */
+ {
+ field_width = 2;
+ for (int trunc = max; (trunc /= 10) > 0;)
+ field_width++;
+ }
+ if (min < 0)
+ {
+ int neg_field_width = 2;
+ for (int trunc = -min; (trunc /= 10) > 0;)
+ neg_field_width++;
+ neg_field_width++; /* account for the minus sign */
+ if (field_width < neg_field_width)
+ field_width = neg_field_width;
+ }
+
+ const int columns = 42 / field_width;
+ int column;
+
+ column = 0;
+ for (int i = 0; i < lookup_array_size; i++)
+ {
+ if (i > 0)
+ printf (",");
+ if ((column++ % columns) == 0)
+ printf("\n%s ", indent);
+ printf ("%*d", field_width, lookup_array[i]);
+ }
+ printf ("\n%s };\n\n", indent);
+
+#if !LARGE_STACK_ARRAYS
+ free ((char *) duplicates);
+ free ((char *) lookup_array);
+#endif
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Generate all the tables needed for the lookup function. */
+
+void
+Key_List::output_lookup_tables (void)
+{
+ T (Trace t ("Key_List::output_lookup_tables");)
+
+ if (option[SWITCH])
+ {
+ /* Use the switch in place of lookup table. */
+ if (option[LENTABLE] && (option[DUP] && total_duplicates > 0))
+ output_keylength_table ();
+ if (option[TYPE] || (option[DUP] && total_duplicates > 0))
+ output_keyword_table ();
+ }
+ else
+ {
+ /* Use the lookup table, in place of switch. */
+ if (option[LENTABLE])
+ output_keylength_table ();
+ output_keyword_table ();
+ output_lookup_array ();
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Output a single switch case (including duplicates). Advance list. */
+
+static List_Node *
+output_switch_case (List_Node *list, int indent, int *jumps_away)
+{
+ T (Trace t ("output_switch_case");)
+
+ if (option[DEBUG])
+ printf ("%*s/* hash value = %4d, keyword = \"%s\" */\n",
+ indent, "", list->hash_value, list->key);
+
+ if (option[DUP]
+ && (list->link
+ || (list->next && list->hash_value == list->next->hash_value)))
+ {
+ if (option[LENTABLE])
+ printf ("%*slengthptr = &lengthtable[%d]\n",
+ indent, "", list->index);
+ printf ("%*swordptr = &%s[%d];\n",
+ indent, "", option.get_wordlist_name (), list->index);
+
+ int count = 0;
+ for (List_Node *temp = list; ; temp = temp->next)
+ {
+ for (List_Node *links = temp; links; links = links->link)
+ count++;
+ if (!(temp->next && temp->hash_value == temp->next->hash_value))
+ break;
+ }
+
+ printf ("%*swordendptr = wordptr + %d;\n"
+ "%*sgoto multicompare;\n",
+ indent, "", count,
+ indent, "");
+ *jumps_away = 1;
+ }
+ else
+ {
+ if (option[LENTABLE])
+ {
+ printf ("%*sif (len == %d)\n"
+ "%*s {\n",
+ indent, "", list->length,
+ indent, "");
+ indent += 4;
+ }
+ printf ("%*sresword = ",
+ indent, "");
+ if (option[TYPE])
+ printf ("&%s[%d]", option.get_wordlist_name (), list->index);
+ else
+ output_string (list->key);
+ printf (";\n");
+ printf ("%*sgoto compare;\n",
+ indent, "");
+ if (option[LENTABLE])
+ {
+ indent -= 4;
+ printf ("%*s }\n",
+ indent, "");
+ }
+ else
+ *jumps_away = 1;
+ }
+
+ while (list->next && list->hash_value == list->next->hash_value)
+ list = list->next;
+ list = list->next;
+ return list;
+}
+
+/* Output a total of size cases, grouped into num_switches switch statements,
+ where 0 < num_switches <= size. */
+
+static void
+output_switches (List_Node *list, int num_switches, int size, int min_hash_value, int max_hash_value, int indent)
+{
+ T (Trace t ("output_switches");)
+
+ if (option[DEBUG])
+ printf ("%*s/* know %d <= key <= %d, contains %d cases */\n",
+ indent, "", min_hash_value, max_hash_value, size);
+
+ if (num_switches > 1)
+ {
+ int part1 = num_switches / 2;
+ int part2 = num_switches - part1;
+ int size1 = (int)((double)size / (double)num_switches * (double)part1 + 0.5);
+ int size2 = size - size1;
+
+ List_Node *temp = list;
+ for (int count = size1; count > 0; count--)
+ {
+ while (temp->hash_value == temp->next->hash_value)
+ temp = temp->next;
+ temp = temp->next;
+ }
+
+ printf ("%*sif (key < %d)\n"
+ "%*s {\n",
+ indent, "", temp->hash_value,
+ indent, "");
+
+ output_switches (list, part1, size1, min_hash_value, temp->hash_value-1, indent+4);
+
+ printf ("%*s }\n"
+ "%*selse\n"
+ "%*s {\n",
+ indent, "", indent, "", indent, "");
+
+ output_switches (temp, part2, size2, temp->hash_value, max_hash_value, indent+4);
+
+ printf ("%*s }\n",
+ indent, "");
+ }
+ else
+ {
+ /* Output a single switch. */
+ int lowest_case_value = list->hash_value;
+ if (size == 1)
+ {
+ int jumps_away = 0;
+ assert (min_hash_value <= lowest_case_value);
+ assert (lowest_case_value <= max_hash_value);
+ if (min_hash_value == max_hash_value)
+ output_switch_case (list, indent, &jumps_away);
+ else
+ {
+ printf ("%*sif (key == %d)\n"
+ "%*s {\n",
+ indent, "", lowest_case_value,
+ indent, "");
+ output_switch_case (list, indent+4, &jumps_away);
+ printf ("%*s }\n",
+ indent, "");
+ }
+ }
+ else
+ {
+ if (lowest_case_value == 0)
+ printf ("%*sswitch (key)\n", indent, "");
+ else
+ printf ("%*sswitch (key - %d)\n", indent, "", lowest_case_value);
+ printf ("%*s {\n",
+ indent, "");
+ for (; size > 0; size--)
+ {
+ int jumps_away = 0;
+ printf ("%*s case %d:\n",
+ indent, "", list->hash_value - lowest_case_value);
+ list = output_switch_case (list, indent+6, &jumps_away);
+ if (!jumps_away)
+ printf ("%*s break;\n",
+ indent, "");
+ }
+ printf ("%*s }\n",
+ indent, "");
+ }
+ }
+}
+
+/* Generates C code to perform the keyword lookup. */
+
+void
+Key_List::output_lookup_function_body (const Output_Compare& comparison)
+{
+ T (Trace t ("Key_List::output_lookup_function_body");)
+
+ printf (" if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)\n"
+ " {\n"
+ " register int key = %s (str, len);\n\n",
+ option.get_hash_name ());
+
+ if (option[SWITCH])
+ {
+ int switch_size = num_hash_values ();
+ int num_switches = option.get_total_switches ();
+ if (num_switches > switch_size)
+ num_switches = switch_size;
+
+ printf (" if (key <= MAX_HASH_VALUE && key >= MIN_HASH_VALUE)\n"
+ " {\n");
+ if (option[DUP])
+ {
+ if (option[LENTABLE])
+ printf (" register %s%s *lengthptr;\n",
+ const_always, smallest_integral_type (max_key_len));
+ printf (" register ");
+ output_const_type (const_readonly_array, struct_tag);
+ printf ("*wordptr;\n");
+ printf (" register ");
+ output_const_type (const_readonly_array, struct_tag);
+ printf ("*wordendptr;\n");
+ }
+ if (option[TYPE])
+ {
+ printf (" register ");
+ output_const_type (const_readonly_array, struct_tag);
+ printf ("*resword;\n\n");
+ }
+ else
+ printf (" register %sresword;\n\n",
+ struct_tag);
+
+ output_switches (head, num_switches, switch_size, min_hash_value, max_hash_value, 10);
+
+ if (option[DUP])
+ {
+ int indent = 8;
+ printf ("%*s return 0;\n"
+ "%*smulticompare:\n"
+ "%*s while (wordptr < wordendptr)\n"
+ "%*s {\n",
+ indent, "", indent, "", indent, "", indent, "");
+ if (option[LENTABLE])
+ {
+ printf ("%*s if (len == *lengthptr)\n"
+ "%*s {\n",
+ indent, "", indent, "");
+ indent += 4;
+ }
+ printf ("%*s register %schar *s = ",
+ indent, "", const_always);
+ if (option[TYPE])
+ printf ("wordptr->%s", option.get_key_name ());
+ else
+ printf ("*wordptr");
+ printf (";\n\n"
+ "%*s if (",
+ indent, "");
+ comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s"));
+ printf (")\n"
+ "%*s return %s;\n",
+ indent, "",
+ option[TYPE] ? "wordptr" : "s");
+ if (option[LENTABLE])
+ {
+ indent -= 4;
+ printf ("%*s }\n",
+ indent, "");
+ }
+ if (option[LENTABLE])
+ printf ("%*s lengthptr++;\n",
+ indent, "");
+ printf ("%*s wordptr++;\n"
+ "%*s }\n",
+ indent, "", indent, "");
+ }
+ printf (" return 0;\n"
+ " compare:\n");
+ if (option[TYPE])
+ {
+ printf (" {\n"
+ " register %schar *s = resword->%s;\n\n"
+ " if (",
+ const_always, option.get_key_name ());
+ comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s"));
+ printf (")\n"
+ " return resword;\n"
+ " }\n");
+ }
+ else
+ {
+ printf (" if (");
+ comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("resword"));
+ printf (")\n"
+ " return resword;\n");
+ }
+ printf (" }\n");
+ }
+ else
+ {
+ printf (" if (key <= MAX_HASH_VALUE && key >= 0)\n");
+
+ if (option[DUP])
+ {
+ int indent = 8;
+ printf ("%*s{\n"
+ "%*s register int index = lookup[key];\n\n"
+ "%*s if (index >= 0)\n",
+ indent, "", indent, "", indent, "");
+ if (option[LENTABLE])
+ {
+ printf ("%*s {\n"
+ "%*s if (len == lengthtable[index])\n",
+ indent, "", indent, "");
+ indent += 4;
+ }
+ printf ("%*s {\n"
+ "%*s register %schar *s = %s[index]",
+ indent, "",
+ indent, "", const_always, option.get_wordlist_name ());
+ if (option[TYPE])
+ printf (".%s", option.get_key_name ());
+ printf (";\n\n"
+ "%*s if (",
+ indent, "");
+ comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s"));
+ printf (")\n"
+ "%*s return ",
+ indent, "");
+ if (option[TYPE])
+ printf ("&%s[index]", option.get_wordlist_name ());
+ else
+ printf ("s");
+ printf (";\n"
+ "%*s }\n",
+ indent, "");
+ if (option[LENTABLE])
+ {
+ indent -= 4;
+ printf ("%*s }\n", indent, "");
+ }
+ printf ("%*s else if (index < -TOTAL_KEYWORDS)\n"
+ "%*s {\n"
+ "%*s register int offset = - 1 - TOTAL_KEYWORDS - index;\n",
+ indent, "", indent, "", indent, "");
+ if (option[LENTABLE])
+ printf ("%*s register %s%s *lengthptr = &lengthtable[TOTAL_KEYWORDS + lookup[offset]];\n",
+ indent, "", const_always, smallest_integral_type (max_key_len));
+ printf ("%*s register ",
+ indent, "");
+ output_const_type (const_readonly_array, struct_tag);
+ printf ("*wordptr = &%s[TOTAL_KEYWORDS + lookup[offset]];\n",
+ option.get_wordlist_name ());
+ printf ("%*s register ",
+ indent, "");
+ output_const_type (const_readonly_array, struct_tag);
+ printf ("*wordendptr = wordptr + -lookup[offset + 1];\n\n");
+ printf ("%*s while (wordptr < wordendptr)\n"
+ "%*s {\n",
+ indent, "", indent, "");
+ if (option[LENTABLE])
+ {
+ printf ("%*s if (len == *lengthptr)\n"
+ "%*s {\n",
+ indent, "", indent, "");
+ indent += 4;
+ }
+ printf ("%*s register %schar *s = ",
+ indent, "", const_always);
+ if (option[TYPE])
+ printf ("wordptr->%s", option.get_key_name ());
+ else
+ printf ("*wordptr");
+ printf (";\n\n"
+ "%*s if (",
+ indent, "");
+ comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s"));
+ printf (")\n"
+ "%*s return %s;\n",
+ indent, "",
+ option[TYPE] ? "wordptr" : "s");
+ if (option[LENTABLE])
+ {
+ indent -= 4;
+ printf ("%*s }\n",
+ indent, "");
+ }
+ if (option[LENTABLE])
+ printf ("%*s lengthptr++;\n",
+ indent, "");
+ printf ("%*s wordptr++;\n"
+ "%*s }\n"
+ "%*s }\n"
+ "%*s}\n",
+ indent, "", indent, "", indent, "", indent, "");
+ }
+ else
+ {
+ int indent = 8;
+ if (option[LENTABLE])
+ {
+ printf ("%*sif (len == lengthtable[key])\n",
+ indent, "");
+ indent += 2;
+ }
+
+ printf ("%*s{\n"
+ "%*s register %schar *s = %s[key]",
+ indent, "",
+ indent, "", const_always, option.get_wordlist_name ());
+
+ if (option[TYPE])
+ printf (".%s", option.get_key_name ());
+
+ printf (";\n\n"
+ "%*s if (",
+ indent, "");
+ comparison.output_comparison (Output_Expr1 ("str"), Output_Expr1 ("s"));
+ printf (")\n"
+ "%*s return ",
+ indent, "");
+ if (option[TYPE])
+ printf ("&%s[key]", option.get_wordlist_name ());
+ else
+ printf ("s");
+ printf (";\n"
+ "%*s}\n",
+ indent, "");
+ }
+ }
+ printf (" }\n"
+ " return 0;\n");
+}
+
+/* Generates C code for the lookup function. */
+
+void
+Key_List::output_lookup_function (void)
+{
+ T (Trace t ("Key_List::output_lookup_function");)
+
+ /* Output the function's head. */
+ if (option[KRC] | option[C] | option[ANSIC])
+ printf ("#ifdef __GNUC__\n"
+ "__inline\n"
+ "#endif\n");
+
+ printf ("%s%s\n",
+ const_for_struct, return_type);
+ if (option[CPLUSPLUS])
+ printf ("%s::", option.get_class_name ());
+ printf ("%s ", option.get_function_name ());
+ printf (option[KRC] ?
+ "(str, len)\n"
+ " register char *str;\n"
+ " register unsigned int len;\n" :
+ option[C] ?
+ "(str, len)\n"
+ " register const char *str;\n"
+ " register unsigned int len;\n" :
+ option[ANSIC] | option[CPLUSPLUS] ?
+ "(register const char *str, register unsigned int len)\n" :
+ "");
+
+ /* Output the function's body. */
+ printf ("{\n");
+
+ if (option[ENUM] && !option[GLOBAL])
+ {
+ Output_Enum style (" ");
+ output_constants (style);
+ }
+
+ if (!option[GLOBAL])
+ output_lookup_tables ();
+
+ if (option[COMP])
+ output_lookup_function_body (Output_Compare_Strncmp ());
+ else
+ output_lookup_function_body (Output_Compare_Strcmp ());
+
+ printf ("}\n");
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* Generates the hash function and the key word recognizer function
+ based upon the user's Options. */
+
+void
+Key_List::output (void)
+{
+ T (Trace t ("Key_List::output");)
+
+ compute_min_max ();
+
+ if (option[C] | option[ANSIC] | option[CPLUSPLUS])
+ {
+ const_always = "const ";
+ const_readonly_array = (option[CONST] ? "const " : "");
+ const_for_struct = ((option[CONST] && option[TYPE]) ? "const " : "");
+ }
+ else
+ {
+ const_always = "";
+ const_readonly_array = "";
+ const_for_struct = "";
+ }
+
+ if (!option[TYPE])
+ {
+ return_type = (const_always[0] ? "const char *" : "char *");
+ struct_tag = (const_always[0] ? "const char *" : "char *");
+ }
+
+ char_to_index = (option[SEVENBIT] ? "" : "(unsigned char)");
+
+ printf ("/* ");
+ if (option[KRC])
+ printf ("KR-C");
+ else if (option[C])
+ printf ("C");
+ else if (option[ANSIC])
+ printf ("ANSI-C");
+ else if (option[CPLUSPLUS])
+ printf ("C++");
+ printf (" code produced by gperf version %s */\n", version_string);
+ Options::print_options ();
+
+ printf ("%s\n", include_src);
+
+ if (option[TYPE] && !option[NOTYPE]) /* Output type declaration now, reference it later on.... */
+ printf ("%s;\n", array_type);
+
+ if (option[INCLUDE])
+ printf ("#include <string.h>\n"); /* Declare strlen(), strcmp(), strncmp(). */
+
+ if (!option[ENUM])
+ {
+ Output_Defines style;
+ output_constants (style);
+ }
+ else if (option[GLOBAL])
+ {
+ Output_Enum style ("");
+ output_constants (style);
+ }
+
+ printf ("/* maximum key range = %d, duplicates = %d */\n\n",
+ max_hash_value - min_hash_value + 1, total_duplicates);
+
+ if (option[CPLUSPLUS])
+ printf ("class %s\n"
+ "{\n"
+ "private:\n"
+ " static inline unsigned int %s (const char *str, unsigned int len);\n"
+ "public:\n"
+ " static %s%s%s (const char *str, unsigned int len);\n"
+ "};\n"
+ "\n",
+ option.get_class_name (), option.get_hash_name (),
+ const_for_struct, return_type, option.get_function_name ());
+
+ output_hash_function ();
+
+ if (option[GLOBAL])
+ output_lookup_tables ();
+
+ output_lookup_function ();
+
+ if (additional_code)
+ for (int c; (c = getchar ()) != EOF; putchar (c))
+ ;
+
+ fflush (stdout);
+}
+
+/* ========================= End of Output routines ========================= */
+
+/* Sorts the keys by hash value. */
+
+void
+Key_List::sort (void)
+{
+ T (Trace t ("Key_List::sort");)
+ hash_sort = 1;
+ occurrence_sort = 0;
+
+ head = merge_sort (head);
+}
+
+/* Dumps the key list to stderr stream. */
+
+void
+Key_List::dump ()
+{
+ T (Trace t ("Key_List::dump");)
+ int field_width = option.get_max_keysig_size ();
+
+ fprintf (stderr, "\nList contents are:\n(hash value, key length, index, %*s, keyword):\n",
+ field_width, "char_set");
+
+ for (List_Node *ptr = head; ptr; ptr = ptr->next)
+ fprintf (stderr, "%11d,%11d,%6d, %*s, %s\n",
+ ptr->hash_value, ptr->length, ptr->index,
+ field_width, ptr->char_set, ptr->key);
+}
+
+/* Simple-minded constructor action here... */
+
+Key_List::Key_List (void)
+{
+ T (Trace t ("Key_List::Key_List");)
+ total_keys = 1;
+ max_key_len = INT_MIN;
+ min_key_len = INT_MAX;
+ array_type = 0;
+ return_type = 0;
+ struct_tag = 0;
+ head = 0;
+ total_duplicates = 0;
+ additional_code = 0;
+}
+
+/* Returns the length of entire key list. */
+
+int
+Key_List::keyword_list_length (void)
+{
+ T (Trace t ("Key_List::keyword_list_length");)
+ return list_len;
+}
+
+/* Returns length of longest key read. */
+
+int
+Key_List::max_key_length (void)
+{
+ T (Trace t ("Key_List::max_key_length");)
+ return max_key_len;
+}
+
OpenPOWER on IntegriCloud