Channels ▼


C Programming

Source Code Accompanies This Article. Download It Now.


Quincy's Translator and the C++ Library

There is no better example of software validating hardware than TV Nation, a news-magazine program created by Michael Moore, the "Me" in Roger and Me. Because of it, I bought a TV for my office in case I'm working late on any Tuesday at 8:00 PM. Moore demonstrated how lobbyists work by hiring one to get a resolution making August 16 national "TVNation Day" introduced onto the floors of the Senate and House.

In another edition, Moore visited the corporate headquarters of several Fortune 500 companies, stood on the sidewalk with a bullhorn, and challenged the CEOs to come down and demonstrate that they could use the products of their companies. The CEO of IBM did not come down and show us that he could format a floppy disk. I would prefer to see him try to install OS/2. Since my diatribe on that subject in the September issue, I have heard from several readers about it. Most of them had experienced, seen, or heard about similar episodes. The consensus is that IBM has not figured out installations yet, particularly where video drivers are concerned. However, some readers disagreed with me completely, and one suggested that I should get into a different line of work. That's funny--I was thinking the same thing during the whole ordeal.

One of my complaints concerned the number of crashes in OS/2, particularly when running Windows applications. That situation improved after I installed some upgrades that I found on a CD-ROM, and OS/2 became much more stable. There were problems with the upgrade installation, though. Grrr.


In September, I also mentioned Jim Coplien's "patterns" discussion at the Borland International Conference and relayed his concerns that trade books and CASE tools would abound before anyone really understands the concept. Cope responded by saying:

I did say it; I meant it; there are examples that illustrate it. My concern is that readers of the column may come to the conclusion that ALL imminent books on patterns are trash_I mentioned during my talk that the forthcoming book by Gamma, Helm, Johnson, and Vlissides (Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, ISBN 0-201-63361-2; due out October 14, 1994) is a solid foundation for further patterns work.

I am very interested in this area and plan to review the book in detail when it becomes available. I've always thought of software development as an infant craft; it lacks what centuries-old crafts enjoy--the intuitive ability of the craftsmen to visualize the result before it is designed. One of the problems is that the tools are part of the product. You don't build a Skil saw so that you can build a house and then include the Skil saw in the house. That's probably not a clear analogy, but you know what I mean. The point is, all the methodologies notwithstanding, we really don't know how to take full advantage of what we know from experience, and we don't know how to pass wisdom and experience on to succeeding generations because we don't have a crystal-clear model for expressing design--one that the designer and builder can see intuitively, not only on paper but in their heads, too. Structured and object-oriented design have addressed and improved the matter considerably but have not solved the problem completely.

An architect designs a structure. A carpenter reads the blueprint and builds the structure. If they know what they are doing, there are few surprises when the job is completed. Furthermore, everybody knows when they are finished; we software developers have none of that.

Quincy: Loosely Coupled Code

The Quincy C-interpreter project continues this month. The discussion focuses on how the design separates the IDE, the translator, and the interpreter. I've intentionally kept those three components as loosely coupled as possible. I might want to use the IDE for a different language or translator, and I might want to use the translator and interpreter in different environments. Table 1 lists Quincy's C source files, organized by their relative responsibilities among the three tasks.

The IDE column in Table 1 lists the source-code files that support the D-Flat IDE. The Translator column lists the files that support translation--preparing the source code for interpreting. The Interpreter column lists the files that support run-time interpreting.

There could be times where you would build a program using any one or two of the components. For example, if you wanted to build a different language into the IDE, you could compile and link the files in the first column with the D-Flat library and see what fell out as unresolved references. That would tell you what the new language module needed to provide or what you could eliminate. Similarly, if you wanted to build a run-time-only interpreter that reads and interprets files of translated token streams, you could compile and link only the source files in the Interpreter column of Table 1.

The source-code files in these columns share a few global references across the three categories. Linking any one or two of them will report unresolved functions and variables. These references represent the coupling between the modules. Depending on your requirements, you can either remove the references or provide the missing external item. For example, Quincy's IDE provides the main function. To build a command-line interface, you would delete the IDE column and add a module with the user interface and a main function. If the interface does not include a debugger, you would remove the references in the Translator and Interpreter code to debugger variables and functions. Or, to preserve the integrity of the source code, you could stub them in. I hesitate to supply a complex set of compile-time conditional preprocessing directives. I learned from D-Flat that they represent a large potential number of compile configurations that I cannot possibly test every time I modify the code.

Quincy's Translator

I discussed the IDE in May, the preprocessor in June and July, the debugger in August, and the lexical scanner in October. This month I'll begin to describe the translator, the code that builds an interpretable program from the token stream built by the lexical scanner.

Quincy interprets the token stream, which encodes source code. While some interpreters compile the tokens into a pseudocode that, when interpreted, implements a virtual-machine architecture, Quincy does not. Its translation consists of scanning the source code into tokens; building and initializing the global and static declarations; and resolving symbol references to global, local, and argument identifiers. However, the translator must first establish the run-time environment and call the preprocessor and scanner. Listing One is cinterp.c, the code that initiates translation and interpreting. It represents the interpreter's shell. The debugger calls the qinterpmain main function to run a program, passing the address of the source-code buffer and the argc and argv command-line arguments.

Listing One declares a number of global variables. It sets off those shared by the IDE to make them easy to find if I want to split out the components. The interpreter uses lists of global variables, structures, and functions. The data structures that define these tables and lists are declared in cinterp.h (Listing Two), which also provides the prototypes and global declarations for the translator and interpreter.

The qinterpmain function in Listing One allocates memory for the tokens, stack, variable definitions, data memory, functions, symbol table, and function prototypes. The sizes of these allocations are determined by global integer values that the IDE and the translator share. The IDE has a dialog box that lets the programmer change these sizes. After allocating the run-time memory, translation begins. The program uses a setjmp to specify where translation and run-time errors should return. It calls the preprocessor, lexical-scanner, and compiler functions in that order to translate the program. I'll discuss the compiler operation next month.

To execute the program, the translator builds a small, one-line program that calls the interpreted program's main function, passing the argc and argv parameters. It calls the lexical scanner to tokenize the statement and then calls the interpreter's statement function to interpret the statement. The one-line statement does not need to be compiled because it has no function or variable declarations to resolve. The only references it has are to its own two parameters and main, which has already been built. When statement returns, the program has completed running, and the interpreter cleans up all the allocated memory.

If an error occurs during translation or run time, the error function in Listing One is called. It posts the error code that identifies the error and does a longjmp. If the Watching variable is true, the error occurred when the user specified a variable to watch or examine from the IDE, and the error function makes its longjmp to the Watchjmp jmp_buf. Otherwise, the longjmp goes to the Shelljmp jmp_buf, which the program set just before it started translation. In this case, the program jumps to where the statement program would have returned. As far as the translator and interpreter are concerned, this is a normal completion. It's up to the IDE to recognize that the error code has been set and report the error to the programmer.

Listing One includes a function named AssertFail, which implements a D-Flat-friendly variant of the Standard C assert function. Listing Two defines the Assert macro under control of the NDEBUG compile-time conditional after the fashion of the Standard-C assert function. There are uses of Assert throughout the program. The AssertFail function does not abort Quincy the way that assert would. It uses the IDE's error-reporting mechanism to report the error instead. Because D-Flat programs hook and chain interrupt vectors, untimely aborts crash the system.

Listing One also includes the getoken function, which the translator and interpreter share to retrieve tokens from the token stream. Different tokens cause different actions beyond being retrieved and returned to the translator and interpreter. The T_LINENO token posts the current file and line number to the program's context and then proceeds to retrieve the next token. This action permits the error-reporting mechanism to report the file and line number of a translation or run-time error. Space tokens are bypassed. Symbols update a global current-variable data structure. Functions update a global current-function data structure. The token retriever recognizes constants and posts their values to a global current-value data structure. These data structures are defined in Listing Two.

"C Programming" Column Source Code

Quincy, D-Flat, and D-Flat++ are available to download from the DDJ Forum on CompuServe and on the Internet by anonymous ftp. See page 3 for details. If you cannot get to one of the online sources, send a diskette and a stamped, addressed mailer to me at Dr. Dobb's Journal, 411 Borel, San Mateo, CA 94402. I'll send you a copy of the source code. It's free, but if you want to support my Careware charity, include a dollar for the Brevard County Food Bank.

The Draft Standard C++ Library

The Draft Standard C++ Library, by P.J. Plauger (Prentice Hall, 1995, ISBN 0-13-117-0031), follows in the tradition of the author's earlier book, The Standard C Library, which explains and implements the Standard-C function library as defined by ANSI X3J11. The new book takes a similar approach, presenting what the draft C++ Standard says about each of the Standard library header files, amplifying those terse descriptions, and providing an implementation in source code of the classes defined by the ANSI X3J16 committee. The book explains the details of the implementation, testing, and use of the draft Standard classes.

In the preface, Plauger states five purposes for the book:

  • To present the text of the library portion of the draft Standard, which it does.
  • To be a model for implementers of the library, which it certainly is.
  • To be a tutorial on the library's use, which succeeds, but only with respect to the version of the library addressed.
  • To teach by example how to "design and implement class libraries in general," presumably without respect to the language.
  • To address the issues specific to building C++ class libraries.
These goals are delineated in the preface and addressed in each of the following chapters--one for each of the library header files defined in the draft Standard.

This book is based on the Standard C++ library as defined in a February publication by X3J16 of a draft Standard for public review. This document has been overtaken by events. For whatever reasons, members of the committee followed that publication almost immediately with new proposals for language and library changes that, if accepted, will significantly change the Standard. Some of what the public saw--particularly with respect to the library--was incomplete and obsolete shortly thereafter. Plauger's book, then, presents a snapshot of the library as it existed for one brief moment in the history of C++. The book's implementation may well be the only one of this momentary version of the library ever to see the light of day. You're not left in the dark to wonder about the future, though. Each chapter includes a section titled "Future Directions" that describes what's changing.

An unstated purpose, but one that the book serves well, is to provide insight into the complex language that C++ is becoming. Someone who has not participated in Committee deliberations is likely to reel with the impact of the changes. For better or worse, Standard C++ will be a much bigger language than the one implemented by most contemporary compilers. Plauger, an active participant (he is editor of the library portion of the Standard) and an old hand at language definition and translator development, understands the changes well and respects their consequences. He brings a mature perspective to the implications of some of the new features and is candid about them. From his comments in the book and from reading parts of the draft, I conclude that some changes are probably underspecified; their proponents may have developed the details of new features without benefit of extensive experience in their use.

Much of the book's implementation is offset by those changes to the Standard. The string and stream classes from the February draft are being replaced by template classes that take advantage of a new language feature, default template parameters, to provide one-class support for wide-character strings and streams. The bits<T> template and bitstring class may be replaced by the Standard Template Library, a proposal made in May for standard template container classes. Not that the code or the book are without use. You can develop to this interim standard, and your work will readily port to the next one. The changes do not affect the library's interface, only its implementation.

Plauger could not have modified the book to meet the new draft Standard. No one compiler is available to readers that implements all the new language features needed to support the library changes. Publishing deadlines and commitments could have been involved, too. His choices were to delay the project unduly or to charge ahead with the library as accepted by the Committee and published in the February draft. This circumstance must have compromised his influence on the Committee process. To oppose a change for any reason would have suggested a conflict of interests. The merit of his arguments might have been overshadowed by the appearance of an outside agenda, whether real or imagined. That is too bad. Plauger is one of the more experienced members both in language standardization and in dealing with committees. On the other hand, his book well achieves its stated goals considering the erratically shifting target. We have the benefit of that achievement, and Plauger can always do a second edition (and a third and a fourth, ad infinitum) as the Committee continues, Sybil-like, in its endless cycle of innovation.

Table 1: Quincy .c source-code files.

    IDE          Translator   Interpreter

    qnc.c        cinterp.c    stmt.c
    qdialogs.c   preproc.c    expr.c
    qmenus.c     preexpr.c    primary.c
    print.c      scanner.c    func.c
    debugger.c   ccompile.c   stack.c
    watch.c                   symbol.c
    break.c                   symbols.c
    qconfig.c                 sys.c

Listing One

/* ------------ cinterp.c ------------ */
/* QUINCY Runtime Interpreter */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <setjmp.h>
#include <sys\stat.h>
#include <alloc.h>
#include <errno.h>

#include "dflat.h"
#include "cinterp.h"
#include "debugger.h"
#include "quincy.h"

unsigned char *Progstart;   /* start of user program         */
unsigned char *NextProto;   /* addr of next prototype        */
int Saw_return;     /* return encountered in user program    */
int Saw_break;      /* break encountered in user program     */
int Saw_continue;   /* continue" encountered in user program */
int Linking;        /* set when in linker                    */
unsigned char *pSrc;
VARIABLE *Blkvar;      /* beginning of local block variables */
VARIABLELIST Globals;  /* table of program global variables  */
char *PrototypeMemory; /* table of prototypes                */
jmp_buf Shelljmp;
/* -------- IDE/interpreter common global items ------------ */
int inSystem;
jmp_buf BreakJmp;
CONTEXT Ctx;              /* running program's context        */
ITEM *Stackbtm;           /* start of program stack           */
ITEM *Stacktop;           /* end of program stack             */
SYMBOLTABLE *SymbolTable; /* symbol table                     */
int SymbolCount;          /* symbols in table                 */
VARIABLE *VariableMemory; /* table of variables               */
FUNCTION *FunctionMemory; /* table of functins                */
FUNCTION *NextFunction;   /* next available function in table */
char *DataSpace;          /* data space for autos             */
unsigned Progused;        /* bytes of program space used      */
static int ExecuteProgram(unsigned char *source, int argc, char *argv[]);
static void qprload(unsigned char *SourceCode, char *prog);
/* ----- deallocate memory ----- */
static void ClearMemory(void **buf, void **end, int *count)
    *buf = NULL;
    if (end)
        *end = NULL;
    if (count)
        *count = 0;
/* ----- main entry to compile & interpret program ----- */
int qinterpmain(unsigned char *source, int argc, char *argv[])
    int rtn = -1;
    Globals.vfirst = NULL;
    Globals.vlast = NULL;
    Ctx.Curvar = NULL;
    Ctx.Curstruct.vfirst = NULL;
    Ctx.Curstruct.vlast = NULL;
    Ctx.Curfunc = NULL;
    ConstExpression = 0;
    /* Allocate memory for program runtime tokens */
    errno = 0;
    Progstart = getmem(qCfg.MaxProgram);
    Ctx.Progptr = Progstart;
    /* Allocate stack, variables, data, functions, symbols, prototypes */
    Stackbtm = getmem((qCfg.MaxStack+1) * sizeof(struct item));
    Ctx.Stackptr = Stackbtm;
    Stacktop = Stackbtm + qCfg.MaxStack;
    VariableMemory = getmem(qCfg.MaxVariables*sizeof(VARIABLE));
    Ctx.NextVar = VariableMemory;
    if ((DataSpace = malloc(qCfg.MaxDataSpace)) == NULL)
    Ctx.NextData = DataSpace;
    FunctionMemory = getmem(qCfg.MaxFunctions * sizeof(FUNCTION));
    NextFunction = FunctionMemory;
    SymbolTable = getmem(qCfg.MaxSymbolTable * sizeof(SYMBOLTABLE));
    NextProto = PrototypeMemory = getmem(qCfg.MaxPrototype);
    *ErrorMsg = '\0';
    /* compile and interpret the program */
    rtn = ExecuteProgram(source, argc, argv);
    /* clean up after the program */
    if (ErrorCode && Ctx.CurrFileno)
        sprintf(ErrorMsg+strlen(ErrorMsg), " %s Line %d: ",
        SrcFileName(Ctx.CurrFileno), Ctx.CurrLineno);
    ClearMemory(&(void*)pSrc, NULL, NULL);
    ClearMemory(&(void*)SymbolTable, NULL, &SymbolCount);
    ClearMemory(&(void*)FunctionMemory, &(void*)NextFunction,NULL);
    ClearMemory(&(void*)DataSpace, &(void*)Ctx.NextData,NULL);
    errno = 0;
    return rtn;
/* -------- compile and execute the program -------- */
static int ExecuteProgram(unsigned char *source, int argc, char *argv[])
    unsigned char Tknbuf[80];
    unsigned char ln[40];
    WINDOW wwnd = WatchIcon();
    if (setjmp(Shelljmp) == 0)  {
        /* --- preprocess and lexical scan --- */
        qprload(source, Progstart);
        /* --- compile --- */
        /* ---- execute ----- */
        sprintf(ln, "return main(%d,(char**)%lu);", argc, argv);
        tokenize(Tknbuf, ln);
        Ctx.Progptr = Tknbuf;
        SendMessage(wwnd, CLOSE_WINDOW, 0, 0);
        wwnd = NULL;
        if (!Stepping)
    if (wwnd != NULL)
        SendMessage(wwnd, CLOSE_WINDOW, 0, 0);
    return ErrorCode ? ErrorCode : popint(); 
/* ----- preprocess and lexical scan ----- */
static void qprload(unsigned char *SourceCode, char *prog)
    /* tokenize program */
    pSrc = getmem(MAXTEXTLEN);
    PreProcessor(pSrc, SourceCode);
    Progused = tokenize(prog, pSrc);
    pSrc = NULL;
    Ctx.Progptr = Progstart;
/* ----- compiler and runtime error function ------ */
void error(int errnum)
    ErrorCode = errnum;
    if (Watching)
        longjmp(Watchjmp, 1);
    else if (Running)
        longjmp(Shelljmp, 1);
/* ----- gets memory for the interpreter ----- */
void *getmem(unsigned size)
    void *ptr;
    if ((ptr = calloc(1, size)) == NULL)
    return ptr;
#ifndef NDEBUG
/* ------- Quincy's version of assert ------- */
void AssertFail(char *cond, char *file, int lno)
    sprintf(errs[ASSERTERR-1], "Assert(%s) %s, Line %d", cond, file, lno);
/* ----- compile and interpret get token ------ */
int getoken()
    static int isStruct;
    for (;;)    {
        switch (Ctx.Token = *Ctx.Progptr++) {
            case T_LINENO:
                Ctx.CurrFileno = *Ctx.Progptr++;
                Ctx.CurrLineno = *(int*)Ctx.Progptr;
                Ctx.Progptr += sizeof(int);
            case ' ':
            case T_EOF:
                Ctx.Value.ival = *Ctx.Progptr--;
                isStruct = 0;
                return Ctx.Token;
            case T_SYMBOL:
                Ctx.Value.ival = *(int*)Ctx.Progptr;
                Ctx.Curvar = SearchVariable(Ctx.Value.ival, isStruct);
                if (!isStruct && Ctx.Curvar == NULL)
                    Ctx.Curvar = SearchVariable(Ctx.Value.ival, 1);
                Ctx.Progptr += sizeof(int);
                isStruct = 0;
                return Ctx.Token;
            case T_IDENTIFIER:
                isStruct = 0;
                Ctx.Curvar = MK_FP(FP_SEG(VariableMemory),
                Ctx.Progptr += sizeof(int);
                return Ctx.Token;
            case T_FUNCTION:
                Ctx.Progptr += sizeof(int);
                return Ctx.Token;
            case T_FUNCTREF:
                Ctx.Curfunction = FunctionMemory + *(int*)Ctx.Progptr;
                Ctx.Progptr += sizeof(int);
                return Ctx.Token;
            case T_CHRCONST:
                Ctx.Value.ival = *Ctx.Progptr++;
                return Ctx.Token;
            case T_STRCONST:
                Ctx.Value.cptr = Ctx.Progptr + 1;
                Ctx.Progptr += *Ctx.Progptr;
                return Ctx.Token;
            case T_INTCONST:
                Ctx.Value.ival = *((int *)Ctx.Progptr);
                Ctx.Progptr += sizeof(int);
                return Ctx.Token;
            case T_LNGCONST:
                Ctx.Value.lval = *((long *)Ctx.Progptr);
                Ctx.Progptr += sizeof(long);
                return Ctx.Token;
            case T_FLTCONST:
                Ctx.Value.fval = *((double *)Ctx.Progptr);
                Ctx.Progptr += sizeof(double);
                return Ctx.Token;
            case T_STRUCT:
            case T_UNION:
                isStruct = 1;
                return Ctx.Token;
                isStruct = 0;
                return Ctx.Token;

Listing Two

/* cinterp.h QUINCY Interpreter - header file  */

#ifndef CINTERP_H
#define CINTERP_H

#include <setjmp.h>
#include <ctype.h>

#undef isxdigit
#undef isalnum
#undef isdigit
#undef isalpha

#include "dflat.h"
#include "errs.h"
#include "tokens.h"

#define PROGTITLE "The Quincy C Interpreter"
#define QVERSION "4.2"
/* Table size constants */
#define MAXSTACK       256       /* default program stack size */
#define MAXPR          (16*1024) /* default user program space */
#define MAXVARIABLES   1024      /* max variables              */
#define MAXFUNCTIONS   200       /* max functions in program   */
#define DATASPACE      (16*1024) /* data space for program     */
#define MAXPARMS       10        /* maximum macro parameters   */
#define MAXSYMBOLTABLE 1024      /* symbol table space         */
#define AVGPROTOTYPES  10        /* avg prototype bytes/func   */
#define MAXDIM         4         /* max dimensions for arrays  */
#define MAXOPENFILES   15        /* max open FILEs             */
#define MAXINCLUDES    10        /* max nested #include files  */
#define MAXIFLEVELS    25        /* max nested #if...s         */
/* Constants */
#define RVALUE  0               /* a constant */
#define LVALUE  1               /* a variable */
#define FUNCT       1   /* a function        */
#define STRUCTELEM  2   /* structure element */
#define LABEL       4   /* goto label        */
#define TYPEDEF     8   /* typedef           */
/* ---- storage classes ----- */
#define AUTO        1
#define REGISTER    2
#define VOLATILE    4
#define EXTERN      8
/* Variable table entry -- (one for each declared variable) */
typedef struct variable {
    int     vsymbolid;      /* variable identifier               */
    char    vclass;         /* its indirection level             */
    char    vkind;          /* kind of variable (func, struct elem, etc. */
    int     vtype;          /* type, INT, CHAR, etc.             */
    int     vsize;          /* size of variable                  */
    int     vdims[MAXDIM];  /* lengths (if an array)             */
    char    vconst;         /* 0=read/write, 1=variable is const,*/
                            /* 2=pointer -> const, 3=both        */
    char   vstatic;         /* 1 = static                        */
    char   vqualifier;      /* 1=auto, 2=register, 4=volatile, 8=extern */
    char   islocal;         /* 1=local variable, 2=argument    */
    char   isunsigned;      /* 1 = unsigned, 0 = signed        */
    char   isinitialized;   /* 1 = variable is initialized     */
    int    voffset;         /* offset of data fr start buffer  */
    int    vwidth;          /* width of data space             */
    int    vBlkNesting;     /* block nesting level             */
    struct variable *vstruct;   /* for a struct var, -> definition */
    int    fileno;          /* file number where declared      */
    int    lineno;          /* line number where declared      */
    int    enumval;         /* integer value for enum constant */
    /* ----- must be same structure as VARIABLELIST below ---- */
    struct  {
        struct variable *vfirst;
        struct variable *vlast;
    } velem;                /* VARIABLELIST of struct elements */
    struct variable *vprev; /* backward link (1st item ->last) */
    struct variable *vnext; /* forward link                    */
/* Variable list */
typedef struct {
    VARIABLE *vfirst;   
    VARIABLE *vlast;
/* Function definition -- (one for each declared function) */
typedef struct {
    int symbol;           /* function symbol id               */
    char ismain;          /* 1 = main()                       */
    int libcode;          /* > 0 = standard library function  */
    char *proto;          /* function prototype               */
    void *code;           /* function code                    */
    int type;             /* return type, INT, CHAR, etc.     */
    char class;           /* indirection level of func return */
    unsigned char fileno; /* where the function is            */
    int lineno;           /* line no of function header       */
    char fconst;          /* 0=read/write, 1=function is const*/
                          /* 2=pointer -> const, 3=both       */
    int width;            /* width of auto variables          */
    VARIABLELIST locals;  /* list of local variables          */
    int BlkNesting;       /* block nesting level              */
/* Running function table entry 
 * (one instance for each iteration of recursive function) */
typedef struct funcrunning {
    FUNCTION *fvar;             /* function variable    */
    char *ldata;                /* local data           */
    int arglength;              /* length of arguments  */
    struct funcrunning *fprev;  /* calling function     */
    /* need this so debugger can find correct variables */
    int BlkNesting;             /* block nesting level  */
/* Stack data item's value */
typedef union datum {
    char            cval;       /* character values           */
    int             ival;       /* integer values             */
    long            lval;       /* long values                */
    double          fval;       /* floating point values      */
    char            *cptr;      /* pointers to chars          */
    unsigned char   *ucptr;     /* pointers to unsigned chars */
    int             *iptr;      /* pointers to ints           */
    unsigned int    *uiptr;     /* pointers to unsigned ints  */
    long            *lptr;      /* pointers to longs          */
    unsigned long   *ulptr;     /* pointers to unsigned longs */
    double          *fptr;      /* pointers to floats         */
    FUNCTION        *funcptr;   /* pointers to functions      */
    char            **pptr;     /* pointers to pointers       */
/* Stack item with attributes */
typedef struct item {
    char kind;          /* STRUCTELEM, FUNCT, LABEL, TYPEDEF  */
    char isunsigned;    /* 1 = unsigned, 0 = signed           */
    char class;         /* pointer or array indirection level */
    char lvalue;        /* 1 == LVALUE, 0 == RVALUE           */
    char vconst;        /* 0 = read/write, 1,2,3 = const      */
    char vqualifier;    /* storage class, etc.                */
    int size;           /* size of the thing on the stack     */
    char type;          /* type of the thing on the stack     */
    int dims[MAXDIM];   /* array dimensions                   */
    VARIABLE *vstruct;  /* for a struct var, -> definition    */
    VARIABLELIST *elem; /* structure's element variable list  */
    DATUM value;        /* the value of the thing             */
/* ----- preprocessor tokens ----- */
enum PreProcTokens {
/* ----- program running context ----- */
typedef struct context {
    unsigned char *Progptr; /* statement pointer               */
    unsigned char *svpptr;  /* saved statement pointer         */
    char svToken;           /* saved token value               */
    VARIABLE *svCurvar;     /* saved variable                  */
    int CurrFileno;         /* current source file             */
    int CurrLineno;         /* current source file line number */
    VARIABLE *Curvar;       /* -> current variable declaration */
    FUNCTION *Curfunction;  /* -> current function declaration */
    FUNCTION *Linkfunction; /* -> function being linked        */
    ITEM *Stackptr;         /* stack pointer                   */
    DATUM Value;            /* value on stack                  */
    char Token;             /* current token value             */
    FUNCRUNNING *Curfunc;   /* current running function        */
    VARIABLE *NextVar;      /* next avail stack frame variable */
    VARIABLELIST Curstruct; /* list of current struct members  */
    char *NextData;         /* next available data space       */
    int Looping;            /* set inside while or for loop    */
    int Switching;          /* set inside switch               */
/* -------- setjmp buffer ----------- */
typedef struct jmpbuf {
    int jmp_id;
    jmp_buf jb;
    CONTEXT jmp_ctx;
typedef struct symbol {
    char *symbol;
    int ident;
/* -------- shell prototypes --------- */
void *getmem(unsigned);
void error(int);
/* ------- preprocessor/linker/compiler prototypes ------ */
void PreProcessor(unsigned char*,unsigned char*);
void CleanUpPreProcessor(void);
int FindPreProcessor(char*);

VARIABLE *SearchVariable(int,int);
VARIABLE *InstallVariable(VARIABLE*,VARIABLELIST*,int,int,int,int);
FUNCTION *FindFunction(int);
void InstallFunction(FUNCTION*);
VARIABLE *DeclareVariable(VARIABLELIST*,int,int,int,int);
void Initializer(VARIABLE*,char*,int);
int VariableWidth(VARIABLE*);
void *AllocVariable(void);
void *GetDataSpace(int,int);
int isTypeDeclaration(void);
void ccompile(VARIABLELIST*);
int tokenize(char*,char*);
int istypespec(void);
int SearchLibrary(char*);
int FindKeyword(char*);
int FindOperator(char*);
int SearchSymbols(char*,struct symbol*,int,int);
int FindSymbol(char*);
char *FindSymbolName(int);
int AddSymbol(char*);
void DeleteSymbols(void);
char *SrcFileName(int);
void *DataAddress(VARIABLE*pvar);
void ClearHeap(void);
void PromptIDE(void);
int CBreak(void);
/* -------- interpreter prototypes----------- */
void stmtend(void);
void stmtbegin(void);
int ExpressionOne(void);
int expression(void);
void cond(void);
void assignment(void);
void callfunc(void);
void DeleteJmpbufs(void);
void torvalue(ITEM*);
int getoken(void);
void skip(char,char);
void statement(void);
VARIABLE *primary(void);
void sys(void);
int readonly(ITEM*sp);
char MakeType(char tok);
int TypeSize(char type);
void TestZeroReturn(void);
void OpenStdout(void);
int ArrayElements(VARIABLE*);
int ArrayDimensions(VARIABLE*);
int ItemArrayDimensions(ITEM*);
int ItemArrayElements(ITEM*);
int ElementWidth(ITEM*);
void TypeQualifier(VARIABLE*);
char MakeTypeToken(char,int*);
void TerminateProgram(void);
/* ------- stack prototypes ------- */
int popint(void);
long poplng(void);
double popflt(void);
void store(void*,int,void*,int,char);
void psh(void);
void pop(void);
void popn(int);
void push(char,char,char,char,unsigned,char,VARIABLELIST*,DATUM*,char);
void pushint(int);
void pushlng(long);
void pushptr(void*,char);
void pushflt(double);
int popnint(int);
int popint(void);
void *popptr(void);
long poplng(void);
double popflt(void);
int StackItemisNumericType(void);
void topget(ITEM*);
void topset(ITEM*);
void topdup(void);
void FixStackType(char);
/* -------------- global data definitions ------------------ */
extern unsigned char *Progstart; /* start of user program    */
extern unsigned char *NextProto; /* addr of next prototype   */
extern int Saw_return;      /* return in user program        */
extern int Saw_break;       /* break in user program         */
extern int Saw_continue;    /* continue in user program      */
extern int Looping;         /* inside while or for loop      */
extern int Switching;       /* inside switch                 */
extern int Linking;         /* in linker                     */
extern int Linklib;         /* linking stdlib                */
extern int ConstExpression; /* initializing globals          */
extern int SkipExpression;  /* skipping effect of expression */
extern FUNCTION *Functions;      /* functions                */
extern VARIABLE *Blkvar;         /* beg of lcl block autos   */
extern VARIABLELIST Globals;     /* global variables         */
extern VARIABLELIST Curstruct;   /* current struct members   */
extern char *PrototypeMemory;    /* prototypes               */
extern int inSystem;
extern CONTEXT Ctx;              /* running program context  */
extern ITEM *Stackbtm;           /* start of program stack   */
extern ITEM *Stacktop;           /* end of program stack     */
extern SYMBOLTABLE *SymbolTable; /* symbol table             */
extern int SymbolCount;          /* symbols in table         */
extern VARIABLE *VariableMemory; /* table of auto variables  */
extern FUNCTION *FunctionMemory; /* table of functions       */
extern FUNCTION *NextFunction;   /* next avail func in table */
extern char *DataSpace;          /* data space for autos     */
extern unsigned Progused;        /* program space used       */
/* ----------------- configuration items ------------------- */
extern struct QuincyConfig  {
    unsigned int MaxProgram;     /* user program space       */
    unsigned int MaxStack;       /* stack size               */
    unsigned int MaxVariables;   /* number of variables      */
    unsigned int MaxFunctions;   /* number of functions      */
    unsigned int MaxDataSpace;   /* data bytes               */
    unsigned int MaxSymbolTable; /* symbol table space       */
    unsigned int MaxPrototype;   /* prototype table space    */
    char scrollbars;             /* display scrollbars       */
    char inTutorial;             /* start in tutorial        */
    char tutorhelp[11];          /* current tutorial help wnd*/
} qCfg;
/* ----- jmp_bufs ---------- */
extern jmp_buf Shelljmp;
extern jmp_buf PreProcessjmp;
extern jmp_buf Includejmp;
extern jmp_buf BreakJmp;
/* ----- state variables ------- */
extern int Including;
extern int PreProcessing;
extern int ErrorCode;
/* ---------- system-wide macros -------- */
#define rslva(a,l) ((l)?(a):(char*)(&a))
#define rslvs(s,c) ((c)?sizeof(void *):s)
#define NullVariable(var) memset(var, 0, sizeof(VARIABLE))
#define NullFunction(fnc) memset(fnc, 0, sizeof(FUNCTION))
#define alphanum(c) (isalpha(c)||isdigit(c)||c=='_')
#define isSymbol() \
#define ItemisAddressOrPointer(i) ((i).class)
#define ItemisPointer(i) \
    (ItemisAddressOrPointer(i) && (i).lvalue)
#define ItemisAddress(i) \
    (ItemisAddressOrPointer(i) && !(i).lvalue)
#define ItemisArray(i) ((i).dims[0] != 0)
#define ItemisInteger(i) \
    ((i)->type==INT || (i)->type==CHAR || (i)->type==LONG)
#define StackItemisAddressOrPointer() (Ctx.Stackptr->class)
#define StackItemisPointer() \
    (StackItemisAddressOrPointer() && Ctx.Stackptr->lvalue)
#define StackItemisAddress() \
    (StackItemisAddressOrPointer() && !Ctx.Stackptr->lvalue)
/* (this is a bad test. It returns true for char address, too) */
#define StackItemisString() \
    (StackItemisAddress() && Ctx.Stackptr->type == CHAR)
#define isTypedef(var) (((var)->vkind&TYPEDEF) != 0)
#define isArray(var) ((var)->vdims[0])
#define isPointerArray(var) \
    (((var)->vclass) > ArrayDimensions(var) && isArray(var))
#define isPointer(var) \
    ((((var)->vclass) && !isArray(var))||isPointerArray(var))
#define isAddressOrPointer(var) ((var)->vclass)
#define rslvaddr(addr, lval) (lval ? *addr : (char *)addr)
#define rslvsize(size, class) (class ? sizeof(void *) : size)
/* -------- Quincy's version of assert ------ */
#ifdef NDEBUG
#define Assert(p) ((void)0)
void AssertFail(char*,char*,int);
#define Assert(p) ((p)?(void)0:AssertFail(#p,__FILE__,__LINE__))


Copyright © 1994, Dr. Dobb's Journal

Related Reading

More Insights

Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.