Close

Curiouser and Curiouser: But not quite that Curious - Yet

A project log for Prometheus A.I.

One set of rings that will control all.

glgormanglgorman 12/31/2022 at 02:260 Comments

For those who actually read this stuff, you should know by now that I always try to come up with a catchy title for my log entries, that just as often as not reflects on, or hints at some famous movie quite, or alludes to an excerpt from some song lyric, or else quotes, or perhaps misquotes some other famous piece of literature.  So why should this post be any different?  Especially if you know that there is a template pattern method that is sometimes used in C++ to allow member functions in an instance of a base class to explicitly call member functions contained in a derived class, without requiring the use of virtual functions - and this method is sometimes referred to as being the "curiously recursive template pattern."

Well, I'm not quite doing anything with that, just quite yet - but something got to my curiosity nonetheless, so that, well something just might be leading in that direction.  Earlier I was messing around with a C++ port of Eliza, and testing a template-based method for constructing binary trees, which would be nice to have for use with lots of things.  So I wrote a quick hack that stores the Eliza strings in pascal-like strings which are usually just fixed-length packed arrays of char with size 0..255.  Yet rather than allocate a binary tree node that contains a pointer to the data object, in this case, a "string", I decided to encapsulate the data object and the tree node in a single monolithic object, even if I am not using the curiously recursive pattern, quite yet.  So the existing code looks like this:

bTreeType<pstring> *ELIZA::make_tree (char **list)
{
    size_t sz1 = sizeof (bTreeType<pstring>);
//    69362 bytes = p-system SANDBOX
    size_t sz2 = sz1*256;  
    bTreeType<pstring> *ptr = (bTreeType<pstring>*) sandbox::allocate_p (sz2);
    void *ptr2;
    bTreeType <pstring> *bTree[256];
    int i;
    for (i=0;i<256;i++)
    {
//    if the pointer arithmetic works the way it is supposed
//    to, we should get an array which is 69632 bytes 
//    which contains 256 binary tree nodes - each of which
//    directly encapsulates an array of 256 bytes of char  
        ptr2 = (void*) (ptr+i);
        bTree[i] = new (ptr2) bTreeType<pstring>;
        if (i<114)
            bTree[i]->m_pData = list[i];
    }
    return ptr;
}

And as described in an earlier post, it appears to be working as it is supposed to.  So now I am looking into the possibility of doing a code merge which could be used to combine the binary tree structures that are used in the UCSD pascal compiler to store identifiers, for example - with the generic binary tree template methods that I wrote some 27 years ago.  So the first step as I figure it is to replace all calls to operator new in the compiler source with calls to identifier::allocate, which in turn now calls the placement version of operator new - so that I can begin experimenting with performing all major data structure allocations in a special Pascal sandbox, which is also in anticipation of wanting to make a code base that is more emulator or microcontroller friendly since in those situations we will have an explicit requirement for managed allocations.  Thus, the identifier::allocator(s) now looks like this:

identifier *identifier::allocate ()
{
    identifier *id;
    void *pascal_heap = NULL;
    id = new (pascal_heap) identifier;
    if (identifiers::m_bTracing==true)
        debug1 (id,true);
    return id;
}

identifier *identifier::allocate (IDCLASS idclass)
{
    identifier *id;
    void *pascal_heap = NULL;
    id = new (pascal_heap) identifier;
    id->KLASS = idclass;
    if (identifiers::m_bTracing==true)
        debug1 (id,true);
    return id;
}

identifier *identifier::allocate (char *str, STP ptr, IDCLASS idclass)
{
    identifier *id;
    void *pascal_heap = NULL;
    id = new (pascal_heap) identifier;
    if (str!=NULL)
        strcpy_s(id->NAME,IDENTSIZE,str);
    id->IDTYPE = ptr;
    id->KLASS = idclass;
    if (identifiers::m_bTracing==true)
        debug1 (id,true);
    return id;
}

 And for now - the version of placement new that I added to the identifier class simply checks to see if a valid pointer was passed in, and if the pointer is NULL, then a regular call to malloc is made, otherwise, hopefully, a sandboxed allocation will eventually be passed in, eventually.

void *identifier::operator new (size_t sz1,void* ptr2)
{
    char fill_char = 0x0;
    size_t sz2;
    sz2 = sizeof(identifier);
    identifier *ptr;

    if (ptr2==NULL)
        ptr = reinterpret_cast<identifier*> (malloc (sz2));
    else
        ptr = reinterpret_cast<identifier*> (ptr2);

    memset(ptr,fill_char,sizeof(identifier));
    return ptr;
}

Of course, if you ever looked at the C++ declaration of the Pascal identifier class or the structure class for that matter - then you know that trying to make sense of either one of them is hardly a matter for the faint of heart. 

To be continued:

Discussions