# reflect.t | documentation

    #charset "us-ascii"

    /*
     *   Copyright (c) 2000, 2006 Michael J. Roberts
     *
     *   This file is part of TADS 3.
     */

    #include <tads.h>
    #include <strbuf.h>
    #include "reflect.h"

    /* ------------------------------------------------------------------------ */
    /*
     *   Main reflection services object.
     *
     *   During pre-initialization, we'll plug this into the _main module's
     *   globals so that the _main module will know it can use reflection
     *   services.
     */
    reflectionServices: PreinitObject
        /* execute preinitialization */
        execute()
        {
            /* plug ourselves into the main globals */
            mainGlobal.reflectionObj = self;

            /* store the main symbol table */
            symtab_ = t3GetGlobalSymbols();

            /* create a reverse lookup table from the main symbol table */
            if (symtab_ != nil)
            {
                /*
                 *   create a lookup table for the reverse table - it'll be
                 *   the same size as the original table, so create it using
                 *   the same statistics
                 */
                reverseSymtab_ = new LookupTable(symtab_.getBucketCount(),
                                                 symtab_.getEntryCount());

                /*
                 *   for each entry in the main table, create an entry in the
                 *   reverse table with the role of key and value reversed -
                 *   this will allow us to look up any value and find its
                 *   global symbol, if it has one
                 */
                symtab_.forEachAssoc({key, val: reverseSymtab_[val] = key});
            }
        }

        /*
         *   Convert a value to a symbol, or to a string representation if
         *   it's not of a symbolic type.
         */
        valToSymbol(val)
        {
            local sym;

            /* the representation depends on the type */
            switch(dataType(val))
            {
            case TypeNil:
                return 'nil';

            case TypeTrue:
                return 'true';

            case TypeInt:
                return toString(val);

            case TypeSString:
            case TypeList:
            case TypeObject:
                /*
                 *   If we're asking about 'self', inherit the handling.  Note
                 *   that, for any object type, x.ofKind(x) is always true, so
                 *   there's no need for a separate test to see if val equals
                 *   self.
                 */
                if (val.ofKind(self))
                    return inherited();

                /* check for intrinsic classes */
                if (IntrinsicClass.isIntrinsicClass(val))
                {
                    /* intrinsic classes should always be in the symbol table */
                    sym = reverseSymtab_[val];
                    return (sym != nil ? sym : '{intrinsicClass}');
                }

                /* use our special value-to-symbol method on the object itself */
                return val.valToSymbol();

            case TypeProp:
                /*
                 *   this should usually convert to a symbol, but might have
                 *   been allocated dynamically
                 */
                sym = reverseSymtab_[val];
                return (sym != nil ? sym : '{prop}');

            case TypeFuncPtr:
                /*
                 *   look for a name; if it doesn't have one, it must be an
                 *   anonymous function
                 */
                sym = reverseSymtab_[val];
                return (sym != nil ? sym : '{anonFunc}');

            case TypeEnum:
                /* these should always convert directly to symbols */
                sym = reverseSymtab_[val];
                return (sym != nil ? sym : '{enum}');

            case TypeBifPtr:
                /* these should always convert directly to symbols */
                sym = reverseSymtab_[val];
                return (sym != nil ? sym : '{intrinsicFunc}');

            case TypeNativeCode:
                return '{native code}';

            default:
                return '???';
            }
        }

        /*
         *   Format a stack frame object (of class T3StackInfo).
         */
        formatStackFrame(fr, includeSourcePos)
        {
            local ret = new StringBuffer();

            /* see what kind of frame we have */
            if (fr.func_ != nil)
            {
                /* it's a function */
                ret.append(valToSymbol(fr.func_));
            }
            else if (fr.obj_ != nil)
            {
                /*
                 *   It's an object.property.  Check for one special case we
                 *   want to show specially: if the object is an AnonFuncPtr
                 *   object, ignore the property and just show it as an
                 *   anonymous function.
                 */
                if (fr.obj_.ofKind(AnonFuncPtr))
                    ret.append('{anonFunc}');
                else
                {
                    ret.append(valToSymbol(fr.self_));
                    ret.append('.');
                    ret.append(valToSymbol(fr.prop_));
                }
            }
            else
            {
                /* no function or object - must be a system routine */
                ret.append('(System)');
            }

            /* if it's not a system routine, add the argument list */
            if (fr.argList_ != nil)
            {
                /* add the open paren */
                ret.append('(');

                /* add the arguments */
                local i, len = fr.argList_.length();
                for (i = 1 ; i <= len ; ++i)
                {
                    /* if it's not the first one, add a comma */
                    if (i != 1)
                        ret.append(', ');

                    /* add this value */
                    ret.append(valToSymbol(fr.argList_[i]).htmlify());
                }

                /* add any named arguments */
                if (fr.namedArgs_ != nil)
                {
                    /* add each key from the named argument table */
                    fr.namedArgs_.forEachAssoc(function(key, val)
                    {
                        /* add a separator if this isn't the first item */
                        if (i++ != 1)
                            ret.append(', ');

                        /* add this "name: value" */
                        ret.append(key);
                        ret.append(':');
                        ret.append(valToSymbol(val));
                    });
                }

                /* add the close paren */
                ret.append(')');

                /* if desired, add the source location */
                if (includeSourcePos && fr.srcInfo_ != nil)
                {
                    ret.append(' ');
                    ret.append(fr.srcInfo_[1]);
                    ret.append(', line ');
                    ret.append(fr.srcInfo_[2]);
                }
            }

            /* return the result */
            return toString(ret);
        }

        /* the global symbol table */
        symtab_ = nil

        /* the global reverse-lookup symbol table */
        reverseSymtab_ = nil
    ;

    /* ------------------------------------------------------------------------ */
    /*
     *   Export the reflection services interfaces used by the VM
     */
    export reflectionServices 'reflection.reflectionServices';
    export valToSymbol 'reflection.valToSymbol';

    /* ------------------------------------------------------------------------ */
    /*
     *   Modify the basic Object class to provide a to-symbol mapping
     */
    modify Object
        valToSymbol()
        {
            /* get my symbol from the global reflection table */
            local sym = reflectionServices.reverseSymtab_[self];

            /* if we got a symbol, return it */
            if (sym != nil)
                return sym;

            /*
             *   We didn't get a symbol, so there's no source file name.  See
             *   if we can find source-file names for the superclasses, though.
             */
            sym = '{obj:';
            local found = nil;
            foreach (local sc in getSuperclassList())
            {
                local scSym;

                /* add a comma to the list if this isn't the first element */
                if (sym != '{obj:')
                    sym += ',';

                /* if we have a name here, add it to the list */
                if ((scSym = reflectionServices.reverseSymtab_[sc]) != nil)
                {
                    /* note that we found a named superclass */
                    found = true;

                    /* add the superclass name to the list */
                    sym += scSym;
                }
                else
                {
                    /* we don't have a name for this superclass; say so */
                    sym += '{anonymous}';
                }
            }

            /*
             *   if we found any named superclasses, return the list of names;
             *   otherwise, just say (obj)
             */
            return (found ? sym + '}' : '{obj}');
        }
    ;

    /* ------------------------------------------------------------------------ */
    /*
     *   Modify the String intrinsic class to provide a to-symbol mapping
     */
    modify String
        valToSymbol()
        {
            local ret;
            local i;
            local start;

            /* start with an open quote */
            ret = '\'';

            /* loop through the string to find each special character */
            for (i = 1, local len = length(), start = 1 ;
                 i <= len ; ++i)
            {
                local qu;

                /* presume we won't add a quoted character on this round */
                qu = nil;

                /* see what we have here */
                switch(substr(i, 1))
                {
                case '\\':
                    qu = '\\\\';
                    break;

                case '\'':
                    qu = '\\\'';
                    break;

                case '\n':
                    qu = '\\n';
                    break;

                case '\t':
                    qu = '\\t';
                    break;

                case '\b':
                    qu = '\\b';
                    break;

                case '\ ':
                    qu = '\\ ';
                    break;

                case '\^':
                    qu = '\\^';
                    break;

                case '\v':
                    qu = '\\v';
                    break;
                }

                /*
                 *   if we have a quoted character, add the part up to the
                 *   quoted character plus the quoted character
                 */
                if (qu != nil)
                {
                    /* add the part up to here but not including this char */
                    if (i != start)
                        ret += substr(start, i - start);

                    /* add the quoted form of the character */
                    ret += qu;

                    /* start again after this character */
                    start = i + 1;
                }
            }

            /* add the trailing unquoted part if we haven't already */
            if (i != start)
                ret += substr(start, i - start);

            /* add a close quote and return the result */
            return ret + '\'';
        }
    ;

    /* ------------------------------------------------------------------------ */
    /*
     *   Modify the List intrinsic class to provide a to-symbol mapping
     */
    modify List
        valToSymbol()
        {
            local ret;

            /* start off with an open bracket */
            ret = '[';

            /* convert each element to symbolic form */
            for (local i = 1, local len = length() ; i <= len ; ++i)
            {
                /* add a comma if this isn't the first element */
                if (i != 1)
                    ret += ', ';

                /* add this element converted to symbolic form */
                ret += reflectionServices.valToSymbol(self[i]);
            }

            /* add the close bracket and return the result */
            return ret + ']';
        }
    ;

    /* ------------------------------------------------------------------------ */
    /*
     *   If desired, modify the BigNumber intrinsic class to provide a
     *   to-symbol mapping.  We only include this modification if the program
     *   is compiled with REFLECT_BIGNUM defined.
     */
    #ifdef REFLECT_BIGNUM
    #include "bignum.h"

    modify BigNumber
        valToSymbol()
        {
            /* use the default formatting */
            return formatString(12);
        }
    ;

    #endif /* REFLECT_BIGNUM */

TADS 3 Library Manual
Generated on 5/16/2013 from TADS version 3.1.3