------------------------------------------------------------------------------
MC logo
Context
[^] Tom's Lisp Code
------------------------------------------------------------------------------
[Classes for Lisp Atoms] [Functions for Lisp Built-Ins] [Classes for Closures] [Context] [Base For Evaluable Objects] [Callable Objects] [Interrupt Utility] [Main Program] [Pairs] [Code Reader] [Reference Counting]

A context is used to map symbols to values. It is a stack of symbol tables. The Context object represents a node of this stack. It contains a pointer next to another Context object, and a C++ library template map from string to RefPtr. The next pointer is used to organize context objects into stacks. When looking up a symbol in a context object, it first checks its own map, and returns the result if found. Otherwise, if the next pointer is non-null, it (recursively) checks that node. This is implemented by the find method.

The set method stores a name in the its own map. This is used by the set and setq operations, and is also used by the main function to store the built-in symbols before any user code is interpreted.

The stack organization supports the notion of nested scopes. New scopes are created:

  1. By the built-in scope operator.
  2. When user-defined functions are executed. A new scope is created to hold the arguments an any set operators performed by the function body.

The scope method allocates and returns a new Context object whose next pointer points to the context object which created it. This places it on top of the creating context object. Since the new top context object is empty, the new scope initially has exactly the same contents as the old one. New definitions are placed in this new top context, and appear to replace existing definitions since find looks there first. The previous definitions are not destroyed, but merely covered; when that top context is deleted, the definitions it contains are deleted, and the old (lower) ones reappear. This is what happens when the code using the new scope returns.

To see the scope in action, you might run this lisp code:

(setq fred 17)
fred
(scope (setq fred 99) fred)
fred
The output is 17, 99, 17.

//*****************************************************************************
//***  Context.
//***
//***    A context is a stack of symbol tables.  These are used to store
//***    variables placed by setq.  A stack is used to control contexts.
//***    
//*****************************************************************************

#include <map>
using namespace std;

#include "refct.h"

#ifndef _context_h_
#define _context_h_

/* A context is a stack of symbol tables.  Expressions are evaluated in some
   context, and function defintions (which are closures) use them. */
class Context: public RefCtObj {
        // Next context down.
        Ptr<Context> next;

        // Map itself -- holds the key->value items created by the
        // system or the (set ) builtin.
        map<string, RefPtr> content;
        typedef map<string, RefPtr>::iterator itype;

public:
        // Search for a symbol.  Goes down the stack until found.
        RefPtr find(const string &a);

        // Set a symbol
        void set(string a, RefPtr r) {
                content[a] = r;
        }

        // Allocate a base context.
        static Ptr<Context> alloc() {
                return RefCtObj::newRefPtr(new Context);
        }

        // Allocate a new context stacked above this one.
        Ptr<Context> scope() {
                Ptr<Context> ret = RefCtObj::newRefPtr(new Context);
                ret->next = newref();
                return ret;
        }

        // Compare for same type.
        virtual bool equal_same(RefCtObj& other) {
                return this == &other;
        }

        // Call this before removing a frame from the call stack.  If the
        // reference count is one, so that the pending removal will immediatly
        // delete the object, this does nothing.  Otherwise, it sets all
        // contained context pointers to non-supporting.
        void last_rites();
};

#endif
#include "context.h"

// Search for a symbol.  Goes down the stack until found.
RefPtr Context::find(const string &a) {
        itype loc = content.find(a);
        if(loc == content.end())
                if(next.isnull())
                        return RefPtr();
                else
                        return next->find(a);
        else
                return loc->second;
}

#include "func.h"
#include "closure.h"

// These are here because they calls a method from Closure.  They set all 
// Closure pointers inside the context to be non-supporting or supporting.
void Context::last_rites() 
{
        if(curr_refct() <= 1) return;
        for(itype p = content.begin(); p != content.end(); ++p)
                if(Ptr<Evaluable>(p->second)->closure())
                        Ptr<Closure>(p->second)->nonsup();
}