460c087cf2
the way.
367 lines
6.3 KiB
C
367 lines
6.3 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
#include "symbol.h"
|
|
#include "debug.h"
|
|
|
|
/*
|
|
Symbol processor.
|
|
|
|
Stores symbols for the lexical scanner. Can later print them.
|
|
Implementation uses a hashtable, the size of which is defined in
|
|
symbols.h.
|
|
*/
|
|
|
|
/* accessible for externals */
|
|
|
|
int globalError; //!< If >0, stdout output goes to stderr (for e.g. terms)
|
|
char *globalStream; //!< Defaults to stdout
|
|
|
|
/* external declarations */
|
|
|
|
extern int yylineno;
|
|
|
|
/* global declarations */
|
|
|
|
//! Symbol hash table.
|
|
Symbol symbtab[HASHSIZE];
|
|
//! List of available (freed) symbol blocks.
|
|
Symbol symb_list;
|
|
//! List of all allocated symbol blocks.
|
|
Symbol symb_alloc;
|
|
|
|
/* main code */
|
|
|
|
//! Open symbols code.
|
|
void
|
|
symbolsInit (void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < HASHSIZE; i++)
|
|
symbtab[i] = NULL;
|
|
symb_list = NULL;
|
|
symb_alloc = NULL;
|
|
globalError = 0;
|
|
globalStream = (char *) stdout;
|
|
}
|
|
|
|
//! Close symbols code.
|
|
void
|
|
symbolsDone (void)
|
|
{
|
|
Symbol s;
|
|
|
|
while (symb_alloc != NULL)
|
|
{
|
|
s = symb_alloc;
|
|
symb_alloc = s->allocnext;
|
|
free (s);
|
|
}
|
|
}
|
|
|
|
//! Create a memory block for a symbol.
|
|
/**
|
|
* Internal memory management is used.
|
|
*@return A pointer to a memory block of size struct.
|
|
*/
|
|
Symbol
|
|
get_symb (void)
|
|
{
|
|
Symbol t;
|
|
if (symb_list != NULL)
|
|
{
|
|
t = symb_list;
|
|
symb_list = symb_list->next;
|
|
}
|
|
else
|
|
{
|
|
t = (Symbol) malloc (sizeof (struct symbol));
|
|
t->allocnext = symb_alloc;
|
|
symb_alloc = t;
|
|
}
|
|
t->keylevel = INT_MAX;
|
|
return t;
|
|
}
|
|
|
|
//! Declare a symbol to be freed.
|
|
void
|
|
free_symb (const Symbol s)
|
|
{
|
|
if (s == NULL)
|
|
return;
|
|
s->next = symb_list;
|
|
symb_list = s;
|
|
}
|
|
|
|
//! Return the index in the hash table for the string.
|
|
int
|
|
hash (const char *s)
|
|
{
|
|
int hv = 0;
|
|
int i;
|
|
|
|
for (i = 0; s[i] != EOS; i++)
|
|
{
|
|
int v = (hv >> 28) ^ (s[i] & 0xf);
|
|
hv = (hv << 4) | v;
|
|
}
|
|
hv = hv & 0x7fffffff;
|
|
return hv % HASHSIZE;
|
|
}
|
|
|
|
//! Insert a string into the hash table.
|
|
void
|
|
insert (const Symbol s)
|
|
{
|
|
int hv;
|
|
|
|
if (s == NULL)
|
|
return; /* illegal insertion of empty stuff */
|
|
|
|
hv = hash (s->text);
|
|
s->next = symbtab[hv];
|
|
symbtab[hv] = s;
|
|
}
|
|
|
|
//! Find a string in the hash table.
|
|
Symbol
|
|
lookup (const char *s)
|
|
{
|
|
int hv;
|
|
Symbol t;
|
|
|
|
if (s == NULL)
|
|
return NULL;
|
|
|
|
hv = hash (s);
|
|
t = symbtab[hv];
|
|
|
|
while (t != NULL)
|
|
{
|
|
if (strcmp (t->text, s) == 0)
|
|
break;
|
|
else
|
|
t = t->next;
|
|
}
|
|
return t;
|
|
}
|
|
|
|
//! Print a symbol.
|
|
void
|
|
symbolPrint (const Symbol s)
|
|
{
|
|
if (s == NULL)
|
|
return;
|
|
|
|
/* TODO maybe action depending on type? */
|
|
eprintf ("%s", s->text);
|
|
}
|
|
|
|
//! Print all symbols
|
|
void
|
|
symbolPrintAll (void)
|
|
{
|
|
int i, count;
|
|
|
|
eprintf ("List of all symbols\n");
|
|
count = 0;
|
|
for (i = 0; i < HASHSIZE; i++)
|
|
{
|
|
Symbol sym;
|
|
|
|
sym = symbtab[i];
|
|
if (sym != NULL)
|
|
{
|
|
eprintf ("H%i:\t", i);
|
|
while (sym != NULL)
|
|
{
|
|
count++;
|
|
eprintf ("[%s]\t", sym->text);
|
|
sym = sym->next;
|
|
}
|
|
eprintf ("\n");
|
|
}
|
|
}
|
|
eprintf ("Total:\t%i\n", count);
|
|
}
|
|
|
|
//! Insert a string into the symbol table, if it wasn't there yet.
|
|
/**
|
|
* Also sets line numbers and type.
|
|
*\sa T_SYSCONST
|
|
*/
|
|
Symbol
|
|
symbolSysConst (const char *str)
|
|
{
|
|
Symbol symb;
|
|
|
|
symb = lookup (str);
|
|
if (symb == NULL)
|
|
{
|
|
symb = get_symb ();
|
|
symb->lineno = yylineno;
|
|
symb->type = T_SYSCONST;
|
|
symb->text = str;
|
|
insert (symb);
|
|
}
|
|
return symb;
|
|
}
|
|
|
|
//! Generate the first fresh free number symbol, prefixed by a certain symbol's string.
|
|
/**
|
|
* Note that there is an upper limit to this, to avoid some problems with buffer overflows etc.
|
|
*/
|
|
Symbol
|
|
symbolNextFree (Symbol prefixsymbol)
|
|
{
|
|
char *prefixstr;
|
|
int n;
|
|
int len;
|
|
|
|
if (prefixsymbol != NULL)
|
|
{
|
|
prefixstr = (char *) prefixsymbol->text;
|
|
len = strlen (prefixstr);
|
|
}
|
|
else
|
|
{
|
|
prefixstr = "";
|
|
len = 0;
|
|
}
|
|
|
|
n = 1;
|
|
while (n <= 9999)
|
|
{
|
|
char buffer[len + 5]; // thus we must enforce a maximum of 9.999 (allowing for storage of \0 )
|
|
Symbol symb;
|
|
int slen;
|
|
|
|
slen = sprintf (buffer, "%s%i", prefixstr, n);
|
|
buffer[slen] = EOS;
|
|
symb = lookup (buffer);
|
|
if (symb == NULL)
|
|
{
|
|
char *newstring;
|
|
// Copy the buffer to something that will survive
|
|
/**
|
|
* Memory leak: although this routine should not be called recursively, it will never de-allocate this memory.
|
|
* Thus, some precaution is necessary.
|
|
* [x][CC]
|
|
*/
|
|
newstring = (char *) malloc (slen + 1);
|
|
memcpy (newstring, buffer, slen + 1);
|
|
|
|
/* This persistent string can be used to return a fresh symbol */
|
|
|
|
return symbolSysConst (newstring);
|
|
}
|
|
|
|
// Try next one
|
|
n++;
|
|
}
|
|
error ("We ran out of numbers (%i) when trying to generate a fresh symbol.",
|
|
n);
|
|
}
|
|
|
|
//! Fix all the unset keylevels
|
|
void
|
|
symbol_fix_keylevels (void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < HASHSIZE; i++)
|
|
{
|
|
Symbol sym;
|
|
|
|
sym = symbtab[i];
|
|
while (sym != NULL)
|
|
{
|
|
#ifdef DEBUG
|
|
if (DEBUGL (5))
|
|
{
|
|
eprintf ("Symbol ");
|
|
symbolPrint (sym);
|
|
}
|
|
#endif
|
|
if (sym->keylevel == INT_MAX)
|
|
{
|
|
// Nothing currently, this simply does not originate on a strand.
|
|
#ifdef DEBUG
|
|
if (DEBUGL (5))
|
|
{
|
|
eprintf (" doesn't have a keylevel yet.\n");
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef DEBUG
|
|
else
|
|
{
|
|
if (DEBUGL (5))
|
|
{
|
|
eprintf (" has keylevel %i\n", sym->keylevel);
|
|
}
|
|
}
|
|
#endif
|
|
sym = sym->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
//! Get output stream pointer
|
|
FILE *
|
|
getOutputStream (void)
|
|
{
|
|
if (globalError == 0)
|
|
return (FILE *) globalStream;
|
|
else
|
|
#ifdef linux
|
|
return stderr;
|
|
#else
|
|
// For non-linux, we simply omit it
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
//! Print out according to globalError
|
|
/**
|
|
* Input is comparable to printf, only depends on globalError. This should be
|
|
* used by any function trying to do output.
|
|
*
|
|
* Furthermore, if globalError == 0, it can still be overriden by
|
|
* globalStream, which can be another stream pointer. If it is null, stdout
|
|
* is assumed.
|
|
*
|
|
*\sa globalError
|
|
*/
|
|
void
|
|
eprintf (char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
FILE *stream;
|
|
|
|
va_start (args, fmt);
|
|
stream = getOutputStream ();
|
|
if (stream != NULL)
|
|
{
|
|
vfprintf (stream, fmt, args);
|
|
}
|
|
va_end (args);
|
|
}
|
|
|
|
// Variable list variant
|
|
void
|
|
veprintf (const char *fmt, va_list args)
|
|
{
|
|
FILE *stream;
|
|
|
|
stream = getOutputStream ();
|
|
if (stream != NULL)
|
|
{
|
|
vfprintf (stream, fmt, args);
|
|
}
|
|
}
|