root/trunk/thune/context.c

Revision 550, 16.1 kB (checked in by krobillard, 7 weeks ago)

Thune:

  • 8-bit string encoding is now Latin-1.
  • Now using WELL512a generator for random numbers.
  • Added hash-map datatype. List datatype can now be disabled in config.
  • Added project-point, remap.
  • Unique & fill now handle vector!.
  • File port 'read now retuns none when end of file reached.

Thune-GL:

  • Added draw-prog! & vertex-buffer! datatypes.
  • Display now accepts /fullscreen option.
  • Added particle-sim dialect.
Line 
1/*============================================================================
2    Thune Interpreter
3    Copyright (C) 2005-2007  Karl Robillard
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18===========================================================================*/
19
20
21#include "internal.h"
22
23
24#define SEARCH_LEN      5
25
26#define LOWERCASE(c)    if(c >= 'A' && c <= 'Z') c -= 'A' - 'a'
27
28
29/*
30   UrlanEnv::atoms & UrlanEnv::atomNames can be made thread safe through one
31   of the following:
32
33   1. Use LOCK_ATOMS in or around these functions:
34        ur_internT()
35        ur_atomStrT()
36        ur_atomCStrT() (and all use of returned pointer)
37        ur_atomHash()
38        dumpAtoms()
39
40   2. Fix size of atom arrays and throw error/assert when full.
41      Must still lock these functions to access head/chain AtomRec members:
42        ur_internT()
43        dumpAtoms()
44
45   Option #2 is currently being used.
46*/
47
48
49/**
50  Appends atom name to string.
51*/
52void ur_atomStrT( UThread* ut, UAtom atom, UString* str )
53{
54    AtomRec* rec = ((AtomRec*) ut->env->atoms.ptr.v) + atom;
55
56    ur_arrayReserve( str, sizeof(char), str->used + rec->nameLen );
57    memCpy( str->ptr.c + str->used,
58            ut->env->atomNames.ptr.c + rec->nameIndex,
59            rec->nameLen );
60    str->used += rec->nameLen;
61}
62
63
64uint32_t ur_atomHash( UrlanEnv* env, UAtom atom )
65{
66    return ((AtomRec*) env->atoms.ptr.v)[ atom ].hash;
67}
68
69
70const char* ur_atomCStrT( UThread* ut, UAtom atom, int* plen )
71{
72    AtomRec* rec = ((AtomRec*) ut->env->atoms.ptr.v) + atom;
73    if( plen )
74        *plen = rec->nameLen;
75    return ut->env->atomNames.ptr.c + rec->nameIndex;
76}
77
78
79uint32_t ur_hash( const uint8_t* str, const uint8_t* end )
80{
81    int c;
82    uint32_t hashval = 0;
83
84    while( str != end )
85    {
86        c = *str++;
87        LOWERCASE( c );
88        hashval = (33 * hashval) + 720 + c;
89    }
90
91    return hashval;
92}
93
94
95#define HASH_INSERT( atoms, table, node, hash, index ) \
96    node = table + (hash % atoms->avail); \
97    if( node->head == 0xffff ) \
98        node->head = index; \
99    else { \
100        node = table + node->head; \
101        while( node->chain != 0xffff ) \
102            node = table + node->chain; \
103        node->chain = index; \
104    }
105
106
107void ur_rebuildAtomHash( UArray* arr )
108{
109    AtomRec* table;
110    AtomRec* node;
111    AtomRec* it;
112    AtomRec* end;
113
114    table = (AtomRec*) arr->ptr.v;
115
116    // Clear lookup table.
117
118    it  = table;
119    end = table + arr->avail;
120    while( it != end )
121    {
122        it->head  = 0xffff;
123        it->chain = 0xffff;
124        ++it;
125    }
126
127    // Re-insert existing entries.
128
129    it  = table;
130    end = table + arr->used;
131    while( it != end )
132    {
133        HASH_INSERT( arr, table, node, it->hash, it - table )
134        ++it;
135    }
136}
137
138
139#ifdef DEBUG
140void dumpAtoms( UThread* ut )
141{
142    LOCK_ATOMS
143    {
144    const char* names = ut->env->atomNames.ptr.c;
145    AtomRec* table = (AtomRec*) ut->env->atoms.ptr.v;
146    AtomRec* it  = table;
147    AtomRec* end = table + ut->env->atoms.used;
148
149#if __WORDSIZE == 64
150#define OFFINT  "%4ld"
151#else
152#define OFFINT  "%4d"
153#endif
154
155    while( it != end )
156    {
157        dprint( OFFINT " %08x %5d %5d %s\n", it - table, it->hash,
158                it->head, it->chain,
159                names + it->nameIndex );
160        ++it;
161    }
162
163    end = table + ut->env->atoms.avail;
164    while( it != end )
165    {
166        dprint( OFFINT " %08x %5d %5d none\n", it - table, it->hash,
167                it->head, it->chain );
168        ++it;
169    }
170    }
171    UNLOCK_ATOMS
172}
173#endif
174
175
176/**
177  Add atom to environment.
178
179  \param str  Name of atom.
180  \param len  Number of characters.
181
182  \return  Atom
183*/
184UAtom ur_internT( UThread* ut, const char* str, int len )
185{
186    char* cp;
187    const char* ep;
188    const char* sp;
189    UArray* atoms;
190    UString* names;
191    int c;
192    uint32_t hash;
193    AtomRec* table;
194    AtomRec* node;
195
196    assert( len > 0 );
197
198
199    // Check if atom already exists.
200
201    hash = ur_hash( (uint8_t*) str, (uint8_t*) str + len );
202
203    LOCK_ATOMS
204
205    atoms = &ut->env->atoms;
206    table = (AtomRec*) atoms->ptr.v;
207    names = &ut->env->atomNames;
208
209    node = table + (hash % atoms->avail);
210    if( node->head == 0xffff )
211    {
212        node->head = atoms->used;
213    }
214    else
215    {
216        node = table + node->head;
217        while( 1 )
218        {
219#if 0
220            if( node->nameLen == len )
221                goto done;
222#else
223            if( node->nameLen == len )
224            {
225                const char* it;
226                sp = names->ptr.c + node->nameIndex;
227                it = str;
228                ep = str + len;
229                while( it != ep )
230                {
231                    c = *it++;
232                    LOWERCASE( c );
233                    if( *sp++ != c )
234                        break;
235                }
236
237                if( it == ep )
238                    goto done;
239            }
240#endif
241
242            if( node->chain == 0xffff )
243            {
244                node->chain = atoms->used;
245                break;
246            }
247            node = table + node->chain;
248        }
249    }
250
251
252    // Nope, add new atom.
253
254    if( atoms->used == atoms->avail )
255    {
256#if 1
257        // Atom table size is fixed so read only access does not need to be
258        // locked.  When the table is full, we are finished.
259        assert( 0 && "Atom table is full" );
260        return 0;       // TODO: Report fatal error
261#else
262        ur_arrayReserve( atoms, sizeof(AtomRec), atoms->used + 1 );
263        ur_rebuildAtomHash( atoms );
264        table = (AtomRec*) atoms->ptr.v;
265
266        HASH_INSERT( atoms, table, node, hash, atoms->used )
267#endif
268    }
269    node = table + atoms->used;
270    ++atoms->used;
271
272    node->hash      = hash;
273    node->nameIndex = names->used;
274    node->nameLen   = len;
275
276#if 1
277    if( (names->used + len + 1) > names->avail )
278    {
279        assert( 0 && "Atom name buffer is full" );
280        return 0;       // TODO: Report fatal error
281    }
282#else
283    ur_arrayReserve( names, sizeof(char), names->used + len + 1 );
284#endif
285
286    cp = names->ptr.c + names->used;
287    ep = cp + len;
288    names->used += len + 1;
289    while( cp != ep )
290    {
291        c = *str++;
292        LOWERCASE( c );
293        *cp++ = c;
294    }
295    *cp = '\0';
296
297done:
298
299    c = node - table;
300
301    UNLOCK_ATOMS
302
303    return c;
304}
305
306
307static int _binarySearch( UCell* words, int count, UAtom atom );
308
309
310/**
311  Find word in context by atom.
312  \return  Word index or -1 if not found.
313*/
314static int _lookupNoSort( UThread* ut, const UContext* ctx, UAtom atom )
315{
316    UBlock* wblk;
317    UCell* it;
318    UCell* end;
319
320    wblk = ur_blockPtr( ctx->ctx.wordBlk );
321    it = wblk->ptr.cells;
322
323    if( (wblk->used < SEARCH_LEN) || (it->word.sel == UR_CTX_UNORDERED) )
324    {
325        end = it + wblk->used;
326        while( it != end )
327        {
328            if( it->word.atom == atom )
329                return it - wblk->ptr.cells;
330            ++it;
331        }
332        return -1;
333    }
334    else
335    {
336        return _binarySearch( it, wblk->used, atom );
337    }
338}
339
340
341static int _internWord( UThread* ut, const UContext* ctx, UAtom atom )
342{
343    int wrdN;
344    UBlock* vblk;
345    UBlock* wblk;
346    UCell* it;
347
348    wblk = ur_blockPtr( ctx->ctx.wordBlk );
349    wrdN = wblk->used;
350    UR_EXPAND_1( UCell, wblk, it );
351
352    ur_initType( it, UT_WORD );
353    it->word.atom    = atom;
354    it->word.wordBlk = 0;
355    it->word.valBlk  = ctx->ctx.valBlk;
356    it->word.index   = 0;
357    it->word.sel     = 0;
358
359    wblk->ptr.cells->word.sel = UR_CTX_UNORDERED;
360
361    vblk = ur_blockPtr( ctx->ctx.valBlk );
362    if( vblk->used < wblk->used )
363    {
364        ur_arrayReserve( vblk, sizeof(UCell), wblk->used );
365        vblk->used = wblk->used;
366
367        it = vblk->ptr.cells + wrdN;
368        ur_initType( it, UT_UNSET );
369    }
370
371    return wrdN;
372}
373
374
375/**
376  Add word to context if it does not already exist.
377  If added, the word is initialied as unset.
378
379  \return  Index of word in context.
380*/
381int ur_internWordT( UThread* ut, const UContext* ctx, UAtom atom )
382{
383    int wrdN;
384
385    wrdN = _lookupNoSort( ut, ctx, atom );
386    if( wrdN > -1 )
387        return wrdN;
388
389    // Not found - add new word.
390    return _internWord( ut, ctx, atom );
391}
392
393
394#define QS_VAL(a)   cells[a].word.sel
395
396// Using series.end to swap word.index & word.sel together.
397#define QS_SWAP(a,b) \
398    stmp = cells[a].series.end; \
399    cells[a].series.end = cells[b].series.end; \
400    cells[b].series.end = stmp
401
402static void _quickSort( UCell* cells, int low, int high )
403{
404    int i, j;
405    UAtom val;
406    UIndex stmp;
407
408    if( low >= high )
409        return;
410
411    val = QS_VAL(low);
412    i = low;
413    j = high+1;
414    for(;;)
415    {
416        do i++; while( i <= high && QS_VAL(i) < val );
417        do j--; while( QS_VAL(j) > val );
418        if( i > j )
419            break;
420        QS_SWAP( i, j );
421    }
422    QS_SWAP( low, j );
423    _quickSort( cells, low, j-1 );
424    _quickSort( cells, j+1, high );
425}
426
427
428static void _orderWords( UCell* words, int count )
429{
430    UCell* it;
431    UCell* end;
432    int index = 0;
433
434    it  = words;
435    end = words + count;
436
437    while( it != end )
438    {
439        it->word.index = index++;
440        it->word.sel   = ur_atom(it);
441        ++it;
442    }
443
444    _quickSort( words, 0, count - 1 );
445}
446
447
448/*
449   Returns index of atom in word block or -1 if not found.
450*/
451static int _binarySearch( UCell* words, int count, UAtom atom )
452{
453    int sAtom;
454    int mid;
455    int low = 0;
456    int high = count - 1;
457
458    while( low <= high )
459    {
460        mid = ((unsigned int) (low + high)) >> 1;
461        sAtom = words[ mid ].word.sel;
462
463        if( sAtom < atom )
464            low = mid + 1;
465        else if( sAtom > atom )
466            high = mid - 1;
467        else
468            return words[ mid ].word.index;
469    }
470
471    // Atom not found.
472    return -1;
473}
474
475
476/**
477  Find word in context by atom.
478  \return  Word index or -1 if not found.
479*/
480int ur_lookupT( UThread* ut, const UContext* ctx, UAtom atom )
481{
482    UBlock* wblk;
483    UCell* it;
484    UCell* end;
485
486    wblk = ur_blockPtr( ctx->ctx.wordBlk );
487    it = wblk->ptr.cells;
488
489    if( wblk->used < SEARCH_LEN )
490    {
491        end = it + wblk->used;
492        while( it != end )
493        {
494            if( it->word.atom == atom )
495                return it - wblk->ptr.cells;
496            ++it;
497        }
498        return -1;
499    }
500    else
501    {
502        if( it->word.sel == UR_CTX_UNORDERED )
503        {
504            _orderWords( it, wblk->used );
505        }
506        return _binarySearch( it, wblk->used, atom );
507    }
508}
509
510
511// Atom remains the same.
512
513#define _bindWord(cell) \
514    wrdN = ur_lookup( ctx, cell->word.atom ); \
515    if( wrdN > -1 ) { \
516        if( ur_bindType(cell) != btype ) \
517            ur_setBindType(cell,btype); \
518        cell->word.wordBlk = ctx->ctx.wordBlk; \
519        cell->word.valBlk  = ctx->ctx.valBlk; \
520        cell->word.index   = wrdN; }
521
522
523UBlock* ur_bindT( UThread* ut, UIndex blkN, const UContext* ctx, int btype )
524{
525    int wrdN;
526    UBlock* blk = ur_blockPtr( blkN );
527    UCell* it  = blk->ptr.cells;
528    UCell* end = it + blk->used;
529
530    if( ur_isGlobal(blkN) )
531        return 0;       // Cannot bind global blocks.
532
533    while( it != end )
534    {
535        switch( ur_type(it) )
536        {
537            case UT_WORD:
538            case UT_SETWORD:
539            case UT_GETWORD:
540            case UT_LITWORD:
541            case UT_SELECT:
542            case UT_SETSELECT:
543            case UT_LITSELECT:
544            case UT_OPCODE:
545                _bindWord( it );
546                break;
547
548            case UT_PATH:
549            case UT_SETPATH:
550            {
551                UCell* path1 = ur_blockPtr( it->series.n )->ptr.cells;
552                if( ur_isAWord(path1) )
553                {
554                    _bindWord( path1 );
555                }
556            }
557                break;
558
559            case UT_BLOCK:
560            case UT_PAREN:
561                ur_bindT( ut, it->series.n, ctx, btype );
562                break;
563
564            case UT_FUNCTION:
565                ur_bindT( ut, it->func.bodyN, ctx, btype );
566
567                // NOTE: Will probably need to rebind to local func context in
568                // case ctx contains the same words.
569                //orBind( orBlockPtr( it->func.bodyBlk ), it->func.context );
570                break;
571        }
572
573        ++it;
574    }
575
576    return blk;
577}
578
579
580extern UContext ur_envGlobal;
581
582/*
583   wc->flags, wc->word wordBlk, valBlk, & index are set.
584   ctop must be greater than cbot.
585*/
586void ur_internDefault( UThread* ut, UCell* wc, UAtom atom,
587                       UCell* cbot, UCell* ctop )
588{
589    UCell* cit;
590    int wrdN;
591
592    // Look for atom in module context stack.
593    cit = ctop;
594    do
595    {
596        --cit;
597        wrdN = _lookupNoSort( ut, cit, atom );
598        if( wrdN > -1 )
599            goto assign;
600    }
601    while( cit != cbot );
602
603
604    // Now try the shared global context.
605    if( ut->env->blocks.arr.used )
606    {
607        wrdN = ur_lookup( &ur_envGlobal, atom );
608        if( wrdN > -1 )
609        {
610            ur_setBindType( wc, UR_BIND_GLOBAL );
611            wc->word.wordBlk = ur_envGlobal.ctx.wordBlk;
612            wc->word.valBlk  = ur_envGlobal.ctx.valBlk;
613            wc->word.index   = wrdN;
614            return;
615        }
616    }
617
618    // Word not found, so intern into current module (top of context stack).
619    cit = ctop - 1;
620    wrdN = _internWord( ut, cit, atom );
621
622assign:
623
624    ur_setBindType( wc, UR_BIND_THREAD );
625    wc->word.wordBlk = cit->ctx.wordBlk;
626    wc->word.valBlk  = cit->ctx.valBlk;
627    wc->word.index   = wrdN;
628}
629
630
631static
632UBlock* _bindDefault( UThread* ut, UIndex blkN, UCell* cbot, UCell* ctop )
633{
634    UBlock* blk;
635    UCell* it;
636    UCell* end;
637
638    if( ur_isGlobal(blkN) )
639        return 0;       // Cannot bind global blocks.
640
641    blk = ur_blockPtr( blkN );
642    it  = blk->ptr.cells;
643    end = it + blk->used;
644
645    while( it != end )
646    {
647        switch( ur_type(it) )
648        {
649            case UT_WORD:
650            case UT_SETWORD:
651            case UT_GETWORD:
652            case UT_LITWORD:
653            case UT_SELECT:
654            case UT_SETSELECT:
655            case UT_LITSELECT:
656            case UT_OPCODE:
657                ur_internDefault( ut, it, ur_atom(it), cbot, ctop );
658                break;
659
660            case UT_PATH:
661            case UT_SETPATH:
662            {
663                UCell* path1 = ur_blockPtr( it->series.n )->ptr.cells;
664                if( ur_isAWord(path1) )
665                {
666                    ur_internDefault( ut, path1, ur_atom(path1), cbot, ctop );
667                }
668            }
669                break;
670
671            case UT_BLOCK:
672            case UT_PAREN:
673                _bindDefault( ut, it->series.n, cbot, ctop );
674                break;
675
676            case UT_FUNCTION:
677                _bindDefault( ut, it->func.bodyN, cbot, ctop );
678                break;
679        }
680
681        ++it;
682    }
683
684    return blk;
685}
686
687
688void ur_bindDefault( UThread* ut, UIndex blkN )
689{
690    UBlock* blk;
691    UBlock* cblk;
692    UCell* ctx;
693    UCell* it;
694    UCell* end;
695
696    cblk = tBlockPtr( BLK_CTX_STACK );
697    ctx = cblk->ptr.cells + (cblk->used - 1);
698
699    // Intern all set-words found in block to the top context.
700    blk = ur_blockPtr( blkN );
701    it  = blk->ptr.cells;
702    end = it + blk->used;
703    while( it != end )
704    {
705        if( ur_is(it, UT_SETWORD) )
706            ur_internWord( ctx, it->word.atom );
707        ++it;
708    }
709
710    _bindDefault( ut, blkN, cblk->ptr.cells,
711                            cblk->ptr.cells + cblk->used );
712}
713
714
715#if 0
716/**
717   Intern all set-words found in block to the top context.
718*/
719void ur_internSetWords( UThread* ut, UIndex blkN )
720{
721    UBlock* blk;
722    UBlock* cblk;
723    UCell* ctx;
724    UCell* it;
725    UCell* end;
726
727    cblk = tBlockPtr( BLK_CTX_STACK );
728    ctx = cblk->ptr.cells + (cblk->used - 1);
729
730    blk = ur_blockPtr( blkN );
731    it  = blk->ptr.cells;
732    end = it + blk->used;
733    while( it != end )
734    {
735        if( ur_is(it, UT_SETWORD) )
736            ur_internWord( ctx, it->word.atom );
737        ++it;
738    }
739}
740#endif
741
742
743/*EOF*/
Note: See TracBrowser for help on using the browser.