#charset "us-ascii"

/*
*  Copyright (c) 2009 by Kevin Forchione. All rights reserved.
*
*  This file is part of the TADS 3 Inherited Multi-Method
*  library extension
*
*  imm.t
*
*  Implements an inherited multi-method mechanism.
*/

#include <tads.h>


/*
*   Add a base function lookup to the multi-method registry.
*/
modify _multiMethodRegistry
{
   /* table of registered base functions */
   baseFuncTab_ = static new LookupTable(128, 256)
}

/*
*   Modify the function to store information in the base function Lookup
*/
modify _multiMethodRegister(baseFunc, func, params)
{
   _baseMethodRegister(baseFunc, func, params);

   replaced(baseFunc, func, params);
}

/*
*  This function stores base function and params as values in base function
*  lookup, using func as the key.
*/
_baseMethodRegister(baseFunc, func, params)
{
   /* if there's no hash entry for the function yet, add one */
   local tab = _multiMethodRegistry.baseFuncTab_;

   /* set the entry as the base function for this multi-method */
   tab[func] = [baseFunc, params];
}

/*
*   An analog to "inherited()" this function will attempt to find the
*   appropriate multi-method and execute it.
*
*   Each parameter value requiring a type should be composed of a 2-element
*   list, the 1st element designating the type, the 2nd element the value
*   to be passed to the retrieved multi-method.
*
*  For instance, to invoke the following multi-method:
*
*      functionName(Type1 value1, value2, Type3, value3) {}
*
*  You would code the call as following from the calling multi-method:
*
*      functionName(TypeX value1, TypeY value2, TypeZ value3)
*      {
*           inheritedMultiMethod([Type1, value1], value2, [Type3, value3]);
*      }
*/
inheritedMultiMethod([params])
{
   local func, result, mmFunc, typeList = [], valueList = [];

   /* get the invoked function */
   func        = t3GetStackTrace(2).func_;

   /* retrieve the associated entry from the base function lookup */
   result      = _multiMethodRegistry.baseFuncTab_[func];

   if (result)
   {
       /*
        *   Build a "types" list and "values" list from the function's
        *   parameters
        */
       params.forEach(new function(elm)
       {
           switch(dataType(elm))
           {
               case TypeList:
                   typeList    += elm[1];
                   valueList   += elm[2];
                   break;

               default:
                   typeList    += elm;
                   valueList   += elm;
           }
       });

       /*
        *   Retrieve the multi-method for the base function and type list
        */
       mmFunc = getMultiMethodPointer(result[1], typeList...);

       /* if we have a multi-method, invoke it */
       if (mmFunc)
           return mmFunc(valueList...);
   }

   /* we don't have a multi-method, return nil */
   return nil;
}