home *** CD-ROM | disk | FTP | other *** search
- /*
- * This file is part of the portable Forth environment written in ANSI C.
- * Copyright (C) 1995 Dirk Uwe Zoller
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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 Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * This file is version 0.9.13 of 17-July-95
- * Check for the latest version of this package via anonymous ftp at
- * roxi.rz.fht-mannheim.de:/pub/languages/forth/pfe-VERSION.tar.gz
- * or sunsite.unc.edu:/pub/languages/forth/pfe-VERSION.tar.gz
- * or ftp.cygnus.com:/pub/forth/pfe-VERSION.tar.gz
- *
- * Please direct any comments via internet to
- * duz@roxi.rz.fht-mannheim.de.
- * Thank You.
- */
- /*
- * dictionary.c --- Implements dictionary and words lists.
- * (duz 06Feb94)
- */
-
- #include "forth.h"
- #include "support.h"
- #include "compiler.h"
- #include "term.h"
- #include "lined.h"
-
- #include <string.h>
- #include <ctype.h>
-
- #include "missing.h"
-
- /*
- * A vocabulary is organized as a mixture between hash-table and
- * linked list. (This is a practice you can observe in several
- * systems.) It works like this: Given a name, first a hash-code is
- * generated. This hash-code selects one of several linked lists
- * called threads. The hooks to these threads are stored in a table.
- *
- * The body of a WORDLIST is essentially such a table of pointers to
- * threads, while in FIG-Forth it was just a pointer to the one and
- * only linked list a VOCABULARY consists of in FIG-Forth.
- */
-
- int
- wl_hash (const char *s, int l)
- /* s string, l length of string, returns hash-code for that name */
- {
- int n = *s++ - '@';
-
- while (--l > 0)
- n = n * 37 + *s++ - '@'; /* a maybe-stupid hash function :-) */
- return n & (THREADS - 1); /* i.e. modulo threads */
- }
-
- /*
- * If we want to traverse a WORDLIST in it's entirety, we must follow
- * all threads simultaneously. The following definition eases this by
- * locating the thread with the hook pointing to the highest memory
- * location, assuming that this thread contains the latest definition
- * entered in the given WORDLIST. For usage refer to the definition of
- * WORDS.
- *
- * When following a wordlist using topmost, a copy of the word list
- * must be made. Everytime the topmost item was processed it must be
- * replaced by its successor in the linked list.
- */
-
- char **
- topmost (Wordl *w)
- /* find the thread with the latest word in the given word list */
- {
- int n = THREADS;
- char **p, **s = w->thread;
-
- for (p = s++; --n; s++)
- if (*s > *p)
- p = s;
- return p;
- }
-
- char * /* return the NFA of the latest */
- latest (void) /* definition in the CURRENT WORDLIST */
- {
- return *topmost (CURRENT);
- }
-
- /* word list and forget */
-
- Wordl *
- word_list (void)
- /* create a word list in the dictionary */
- {
- Wordl *w = (Wordl *) DP; /* allocate word list in HERE */
- INC (DP, Wordl);
-
- ZERO (w->thread); /* initialize all threads to empty */
- w->prev = VOC_LINK; /* chain word list in VOC-LINK */
- VOC_LINK = w;
- return w;
- }
-
- void
- forget (char *above)
- /* remove words from dictionary, free dictionary space */
- {
- Wordl *wl;
-
- if ((Byte *) above < FENCE)
- tHrow (THROW_INVALID_FORGET);
- /* unchain words in all threads of all word lists: */
- for (wl = VOC_LINK; wl; wl = wl->prev)
- {
- char **p = wl->thread;
- int i;
-
- for (i = THREADS; --i >= 0; p++)
- /* unchain words in thread: */
- while (*p >= (char *) above)
- *p = *name_to_link (*p);
- }
- /* unchain word lists: */
- while (VOC_LINK >= (Wordl *) above)
- VOC_LINK = VOC_LINK->prev;
- /* free dictionary space: */
- DP = (Byte *) above;
- LAST = NULL;
- if (CURRENT >= (Wordl *) above)
- tHrow (THROW_CURRENT_DELETED);
- }
-
- /* search a header */
-
- static char *
- search_thread (const char *nm, int l, char *t)
- {
- char name[32];
-
- if (l > 31)
- return NULL;
- memcpy (name, nm, l);
- if (LOWER_CASE)
- upper (name, l);
- while (t)
- {
- if ((*t & 0x3F) == l && strncmp (name, t + 1, l) == 0)
- break;
- t = *name_to_link (t);
- }
- return t;
- }
-
- char *
- search_wordlist (const char *nm, int l, /*const */ Wordl *w)
- {
- return search_thread (nm, l, w->thread[wl_hash (nm, l)]);
- }
-
- char *
- find (const char *nm, int l)
- /* search all word lists in the search order for name, return NFA */
- {
- Wordl **p, **q;
- char *w = NULL;
- int n = wl_hash (nm, l);
-
- for (p = CONTEXT; !w && p <= &ONLY; p++)
- {
- if (*p == NULL)
- continue;
- for (q = CONTEXT; *q != *p; q++);
- if (q != p)
- continue;
- w = search_thread (nm, l, (*p)->thread[n]);
- }
- return w;
- }
-
- char *
- tick (Xt *xt) /* tick next word, store Xt in xt, */
- { /* return count byte of name field */
- char *p; /* (to detect immediacy) */
-
- p = word (' ');
- p = find ((char *) p + 1, *(Byte *) p);
- if (!p)
- tHrow (THROW_UNDEFINED);
- *xt = name_from (p);
- return p;
- }
-
- /* create a header */
-
- char *
- alloc_string (const char *s, int len)
- /* writes counted string into dictionary, returns address */
- {
- char *p = (char *) DP;
-
- if (len >= (1 << CHAR_BIT))
- tHrow (THROW_ARG_TYPE);
- *DP++ = len; /* store count byte */
- while (--len >= 0) /* store string */
- *DP++ = (Byte) *s++;
- align_ ();
- return p;
- }
-
- char *
- alloc_parsed_string (char del)
- {
- char *p;
- uCell n;
-
- parse (del, &p, &n);
- return alloc_string (p, (int) n);
- }
-
- char *
- alloc_word (char del)
- {
- char *p = word (del);
-
- DP += *p + 1;
- align_ ();
- return p;
- }
-
- static void /* written to cfa by make_head() */
- illegal_xt (void) /* to give an error msg when calling */
- { /* a word without execution semantics */
- tHrow (THROW_INVALID_NAME);
- }
-
- Head *
- make_head (const char *name, int count, char **nfa, Wordl *wid)
- /* make a new dictionary entry in the word list identified by wid */
- {
- Head *h;
- int hc;
-
- if (count == 0)
- tHrow (THROW_ZERO_NAME);
- if (count > 0x1F)
- tHrow (THROW_NAME_TOO_LONG);
- if (REDEFINED_MSG && find (name, count))
- outf ("\n\"%.*s\" is redefined ", count, name);
- *nfa = LAST = alloc_string (name, count);
- if (LOWER_CASE)
- upper (*nfa + 1, *(Byte *) *nfa);
- **nfa |= 0x80;
- h = (Head *) DP;
- INC (DP, Head);
-
- hc = wl_hash (name, count);
- h->link = wid->thread[hc];
- wid->thread[hc] = *nfa;
- h->aux = illegal_xt;
- h->cfa = illegal_xt;
- return h;
- }
-
- void
- header (pCode cfa, char flags)
- {
- char *p = word (' ');
-
- make_head (p + 1, *(Byte *) p, &p, CURRENT)->cfa = cfa;
- *p |= flags;
- }
-
- /* navigation in the header */
-
- char **
- name_to_link (const char *p)
- {
- return (char **) aligned ((Cell) p + 1 + (*p & 0x1F));
- }
-
- char *
- link_to_name (char **l)
- /*
- * scan backward for count byte preceeding name of definition
- * returns pointer to count byte of name field or NULL
- */
- {
- char *p = (char *) l;
- int n;
-
- /* Skip possible alignment padding: */
- for (n = 0; *--p == '\0'; n++)
- if (n > sizeof (Cell) - 1)
- return NULL;
-
- /* Scan for count byte. Note: this is not reliable! */
- for (n = 0; n < 32; n++, p--)
- {
- if (*p & 0x80 && (*p & 0x1F) == n)
- return p;
- if (!printable (*p))
- return NULL;
- }
- return NULL;
- }
-
- Semant * /* I don't like this either. :-) */
- to_semant (Xt xt)
- {
- #define TO_SEMANT(XT,ELEMENT) \
- ((Semant *)((char *)XT - OFFSET_OF (Semant, ELEMENT)))
- Semant *s;
-
- s = TO_SEMANT (xt, exec[0]);
- if (s->magic == SEMANT_MAGIC)
- return s;
- s = TO_SEMANT (xt, exec[1]);
- if (s->magic == SEMANT_MAGIC)
- return s;
- return NULL;
- #undef TO_SEMANT
- }
-
- Xt
- link_from (char **lnk)
- {
- return (Xt) ((void **) lnk + 2);
- }
-
- char **
- to_link (Xt xt)
- {
- Semant *s = to_semant (xt);
-
- return s ? name_to_link (s->name)
- : (char **) xt - 2;
- }
-
- Xt
- name_from (const char *p)
- {
- return link_from (name_to_link (p));
- }
-
- char *
- to_name (Xt c)
- {
- return link_to_name (to_link (c));
- }
-
- Xt
- runtime (void)
- {
- if (!LAST)
- tHrow (THROW_ARG_TYPE);
- return name_from (LAST);
- }
-
- void
- dot_name (const char *nfa)
- {
- int len;
-
- if (!nfa || !(*nfa & 0x80))
- {
- outs ("<\?\?\?> "); /* avoid trigraph interpretation */
- return;
- }
- len = *nfa++ & 0x1F;
- type (nfa, len);
- space_ ();
- }
-
- /* words with wildcards */
-
- void
- wild_words (const Wordl *wl, const char *pattern, const char *categories)
- /*
- * Show words in word list matching pattern, and of one of the
- * categories in string `categories'. NULL pointer or zero length
- * string means all kinds of words.
- */
- {
- Wordl wcopy = *wl; /* clobbered while following it */
- char **t;
-
- cr_ ();
- start_question_cr_ ();
- if (categories && *categories == '\0')
- categories = NULL;
- for (t = topmost (&wcopy); *t; t = topmost (&wcopy))
- {
- char wbuf[0x20];
- char *w = *t;
- char **s = name_to_link (w);
- int l = *w++ & 0x1F;
-
- store_c_string (w, l, wbuf, sizeof wbuf);
- if (match (pattern, wbuf))
- {
- char c = category (*link_from (s));
- if (!categories || strchr (categories, c))
- {
- if (OUT + 20 - OUT % 20 + 2 + l > cols)
- {
- if (question_cr ())
- break;
- }
- else
- {
- if (OUT)
- tab (20);
- }
- outf ("%c %.*s ", c, l, w);
- }
- }
- *t = *s;
- }
- }
-
- /* completion of word against dictionary */
-
- static char *
- search_incomplete (const char *name, int len, Wordl *w)
- /*
- * traverses the entire given wordlist to find a matching word
- * caution: clobbers *w. This is needed to be able to continue the search.
- */
- {
- char **t, *s;
-
- for (t = topmost (w); *t; t = topmost (w))
- {
- s = *t;
- *t = *name_to_link (*t);
- if ((*s & 0x1F) >= len && strncmp (s + 1, name, len) == 0)
- return s;
- }
- return NULL;
- }
-
- static int
- complete_word (const char *in, int len, char *out, int display)
- /*
- * Try to complete string in/len from dictionary.
- * Store completion in out (asciiz), return number of possible completions.
- * If display is true, display alternatives.
- */
- {
- Wordl w, **p, **q;
- char *s, *t; /* no, s and m are NOT used uninitialized */
- int n, m, cnt = 0;
-
- for (p = CONTEXT; p <= &ONLY; p++)
- {
- if (!*p)
- continue;
- for (q = CONTEXT; *q != *p; q++);
- if (q != p)
- continue;
- for (w = **p; (t = search_incomplete (in, len, &w)) != NULL; cnt++)
- {
- if (display)
- {
- space_ ();
- type_on_line (t + 1, *t & 0x1F);
- }
- if (cnt == 0)
- {
- s = t + 1;
- m = *t & 0x1F;
- }
- else
- {
- ++t;
- for (n = 0; n < m; n++)
- if (s[n] != t[n])
- break;
- m = n;
- }
- }
- }
- if (cnt)
- store_c_string (s, m, out, 32);
- return cnt;
- }
-
- int
- complete_dictionary (char *in, char *out, int display)
- {
- char *lw, buf[32];
- int n;
-
- lw = strrchr (in, ' ');
- if (lw)
- lw++;
- else
- lw = in;
- memcpy (out, in, lw - in);
- upper (lw, strlen (lw));
- n = complete_word (lw, strlen (lw), buf, display);
- strcpy (&out[lw - in], buf);
- return n;
- }
-
- /************************************************************************/
- /* initial dictionary setup */
- /************************************************************************/
-
- static int
- load_words (const Words * wl, Wordl *wid)
- /*
- * Load a list of words from a C-language module into the dictionary.
- */
- {
- const Word *w = wl->w;
- Head *h;
- char *nfa;
- int i;
-
- for (i = wl->n; --i >= 0; w++)
- {
- const char *name = w->name;
- char type = *name++;
-
- h = make_head (name, strlen (name), &nfa, wid);
- if (type & 010)
- *nfa |= IMMEDIATE;
- switch (type)
- {
- case _CS:
- h->aux = w->ptr;
- h->cfa = ((Semant *) w->ptr)->comp;
- ((Semant *) w->ptr)->name = nfa;
- continue;
- case _CI:
- case _CO:
- h->cfa = (pCode) w->ptr;
- continue;
- case _VO:
- h->cfa = vocabulary_runtime;
- ((preloadList *) w->ptr)->wid = word_list ();
- continue;
- case _OY:
- h->cfa = only_runtime;
- ((preloadList *) w->ptr)->wid = word_list ();
- continue;
- case _SV:
- h->cfa = sysvar_runtime;
- break;
- case _DV:
- h->cfa = dictvar_runtime;
- break;
- case _DC:
- h->cfa = dictconst_runtime;
- break;
- case _SC:
- h->cfa = sysconst_runtime;
- break;
- case _OV:
- case _IV:
- h->cfa = create_runtime;
- break;
- case _OC:
- case _IC:
- h->cfa = constant_runtime;
- break;
- case _OL:
- case _IL:
- h->cfa = value_runtime;
- break;
- }
- COMMA (w->ptr);
- }
- #if defined DEBUG
- if (option.debug)
- outf ("preloaded %3d words of %s\n",
- wl->n, wl->name);
- #endif
- return wl->n;
- }
-
- #if defined DEBUG
- static void
- word_list_statistics (Wordl *w, int *n)
- {
- char *thread[THREADS];
- int i;
-
- COPY (thread, w->thread);
- for (i = 0; i < THREADS; i++)
- for (n[i] = 0; thread[i]; n[i]++)
- thread[i] = *name_to_link (thread[i]);
- }
-
- #endif
-
- void
- preload_dictionary (void)
- {
- Wordl only; /* scratch ONLY word list */
- int i, j, sum;
-
- DP = (Byte *) &sys.dict[1];
- /* Load the ONLY word list to the empty dictionary using the scratch ONLY: */
- memset (&only, 0, sizeof only);
- sum = load_words (preload_list[0]->ws[0], &only);
- /* Copy scratch ONLY to real ONLY: */
- ONLY = only_list.wid;
- COPY (ONLY->thread, only.thread);
- /* initialize FORTH: */
- FORTH = forth_list.wid;
- /* Load signals to EXTENSIONS word list: */
- load_signals (extensions_list.wid);
-
- /* Load all other word sets into their WORDLISTs: */
- for (i = 1; i < preload_lists; i++)
- for (j = preload_list[i]->n; --j >= 0;)
- sum += load_words (preload_list[i]->ws[j],
- preload_list[i]->wid);
-
- FENCE = DP;
- LAST = NULL;
-
- #if defined DEBUG
- /* Maybe output some statistics: */
- if (option.debug)
- {
- int stat[THREADS];
-
- outf ("preloaded %3d words total.\n", sum);
- printf ("Words per thread:\nFORTH: ");
- word_list_statistics (FORTH, stat);
- for (i = 0; i < THREADS; i++)
- printf ("%3d ", stat[i]);
- printf ("\nONLY: ");
- word_list_statistics (ONLY, stat);
- for (i = 0; i < THREADS; i++)
- printf ("%3d ", stat[i]);
- printf ("\n\n");
- }
- #endif
- }
-
- /************************************************************************/
- /* Save and reload dictionary. */
- /************************************************************************/
-
- #if 1
- static int
- encode_dist (int i1, int i2, Byte *p)
- /*
- * i2 is an index larger than i1.
- * Encodes distance between i1 and i2 as follows:
- * 6-bit distance: 00bbbbbb
- * 14-bit distance: 01bbbbbb bbbbbbbb
- * 22-bit distance: 10bbbbbb bbbbbbbb bbbbbbbb
- * 30-bit distance: 11bbbbbb bbbbbbbb bbbbbbbb bbbbbbbb
- * Stores the encoding in *p, returns number of bytes stored.
- */
- {
- uCell dist = i2 - i1;
-
- if ((dist & 0xFFFFFFC0ul) == 0)
- {
- *p++ = dist;
- return 1;
- }
- if ((dist & 0xFFFFC000ul) == 0)
- {
- *p++ = dist >> 8 | 0x40;
- *p++ = dist;
- return 2;
- }
- if ((dist & 0xFFC00000ul) == 0)
- {
- *p++ = dist >> 16 | 0x80;
- *p++ = dist >> 8;
- *p++ = dist;
- return 3;
- }
- *p++ = dist >> 24 | 0xC0;
- *p++ = dist >> 16;
- *p++ = dist >> 8;
- *p++ = dist;
- return 4;
- }
-
- static int
- next_position (Byte *p, int *dist)
- /*
- * Reads a distance from p, encoding like described above.
- * Returns number of bytes read, adds distance to *dist.
- */
- {
- Byte *q = p, c = *q++;
- uCell d = c & 0x3F;
-
- switch (c & 0xC0)
- {
- case 0xC0:
- d <<= 8;
- d |= *q++;
- case 0x80:
- d <<= 8;
- d |= *q++;
- case 0x40:
- d <<= 8;
- d |= *q++;
- }
- *dist += d;
- return q - p;
- }
-
- static int
- compare_dictionary (uCell *src, uCell *dst, uCell cells,
- Byte *p, uCell *length)
- /*
- * Extract relocation information and prepare for later relocation.
- * Compares two chunks of dictionary space, that were built identically
- * on different memory locations.
- * Returns success and stores indices of cells to relocate in a
- * list starting at p, stores length of list in length.
- */
- {
- Byte *q = p;
- uCell i0 = 0, i, dist, diff;
- int ok = 1;
-
- dist = (uCell) dst - (uCell) src;
- for (i = 0; i < cells; i++)
- {
- diff = dst[i] - src[i];
- if (diff == 0)
- /*
- * Cells are same, nothing to relocate:
- */
- continue;
- if (diff == dist)
- /*
- * Cells differ by the distance dst-src, that means cell is
- * an address pointing inside the chunk, must be relocated:
- */
- {
- dst[i] -= (uCell) dst;
- q += encode_dist (i0, i, q);
- i0 = i;
- continue;
- }
- /*
- * Cells differ by some other amount. Hugh!?
- */
- ok = 0;
- }
- *length = q - p;
- return ok;
- }
-
- static void
- relocate (uCell *dst, Byte *p, int length)
- /*
- * Given a piece of code starting at dst and relocation info
- * starting at p with the given length: this relocates the code
- * by adding dst to all locations in the table.
- */
- {
- Byte *q = p + length;
- int i;
-
- i = 0;
- while (p < q)
- {
- p += next_position (p, &i);
- dst[i] += (uCell) dst;
- }
- }
-
- #else
-
- #define relocate(DST,P,LEN)
-
- #endif
-
- struct saved_header /* header of saved entire dictionary */
- {
- uCell magic; /* makes sure it's a saved dictionary */
- uCell pfe_ver; /* version of pfe system that saved it */
- uCell size; /* size of dictionary body in bytes */
- uCell reloc_size; /* size of relocation info in bytes */
- };
-
- long
- save_dictionary (Dict *d1, Dict *d2, const char *fn)
- /*
- * Save entire dictionary to file, return file size.
- * Clobbers the dictionary d2. So don't use it afterwards.
- */
- {
- FILE *f;
- struct saved_header svhd;
- long len;
-
- svhd.magic = SAVE_MAGIC;
- svhd.pfe_ver = pfe_version_code ();
- svhd.size = d1->dp - (Byte *) d1;
- if (!compare_dictionary
- ((uCell *) d1,
- (uCell *) d2,
- svhd.size / sizeof (Cell),
- (Byte *) d2 + svhd.size,
- &svhd.reloc_size))
- return 0;
-
- f = fopen (fn, "wb");
- if (f == NULL)
- tHrow (THROW_FILE_NEX);
- len = fwrite (&svhd, 1, sizeof svhd, f);
- len += fwrite (d2, 1, svhd.size + svhd.reloc_size, f);
- fclose (f);
- if (len != sizeof svhd + svhd.size + svhd.reloc_size)
- tHrow (THROW_FILE_ERROR);
-
- return len;
- }
-
- static void /* words with different compile/runtime */
- fixnames (void) /* semantics: fill in pointer back from */
- { /* semantics structure to name field */
- Wordl *wl;
- int i;
- char *nfa;
- Head *hd;
- Semant *s;
-
- for (wl = VOC_LINK; wl; wl = wl->prev)
- for (i = 0; i < THREADS; i++)
- for (nfa = wl->thread[i]; nfa; nfa = hd->link)
- /* now nfa runs for all words in the dictionary */
- {
- hd = (Head *) name_to_link (nfa);
- s = (Semant *) hd->aux;
- if (*nfa & IMMEDIATE && s && s->magic == SEMANT_MAGIC)
- ((Semant *) hd->aux)->name = nfa;
- }
- }
-
- int
- reload_dictionary (const char *fn, Dict *dict)
- {
- FILE *f = fopen (fn, "rb");
- struct saved_header svhd;
- long len;
-
- if (f == NULL)
- tHrow (THROW_FILE_NEX);
- len = fread (&svhd, 1, sizeof svhd, f);
- if (svhd.magic != SAVE_MAGIC ||
- svhd.pfe_ver != pfe_version_code ())
- return 0;
- len += fread (dict, 1, svhd.size + svhd.reloc_size, f);
- fclose (f);
- if (len != sizeof svhd + svhd.size + svhd.reloc_size)
- return 0;
- relocate ((uCell *) dict, (Byte *) dict + svhd.size, svhd.reloc_size);
- fixnames ();
- return 1;
- }
-