root/trunk/thune/eval.c

Revision 550, 43.3 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#include "urlan_atoms.h"
23#include "bignum.h"
24
25
26extern UContext ur_thrGlobal;
27extern UContext ur_envGlobal;
28
29extern void uc_console_out( UThread*, UCell* );
30
31
32#ifdef DEBUG
33void ur_dumpBlock( UThread* ut, UIndex blkN )
34{
35    UCell* cell;
36    UCell blk;
37    UString str;
38
39    ur_arrayInit( &str, 1, 0 );
40
41    cell = &blk;
42    ur_initType( cell, UT_BLOCK );
43    ur_setSeries( cell, blkN, 0 );
44
45    ur_toStr( cell, &str, 0 );
46
47    if( str.used )
48    {
49        UString* sp = &str;
50        ur_termCStr( sp );
51
52        dprint( str.ptr.c );
53        dprint( "\n" );
54    }
55
56    ur_arrayFree( &str );
57}
58#endif
59
60
61void ur_infuseOpcodes( UThread* ut, UIndex blkN )
62{
63    UBlock* blk;
64    UContext* global;
65    const UCell* ctx;
66    int wrdN;
67
68    global = (ut->env->blocks.arr.used) ? &ur_envGlobal : &ur_thrGlobal;
69
70    wrdN = ur_lookup( global, UR_ATOM_KERNEL_OPS );
71    assert( wrdN > -1 );
72    //if( wrdN > -1 )
73    {
74        blk = ur_blockPtr( global->ctx.valBlk );
75        ctx = blk->ptr.cells + wrdN;
76
77        assert( ur_is(ctx, UT_CONTEXT) );
78
79        blk = ur_blockPtr( blkN );
80        ur_infuse( ut, blk, ctx );
81    }
82}
83
84
85/*
86   Returns length of series from current iterator position.
87   If cell is not a series or beyond the tail then a negative number is
88   returned.
89*/
90static int ur_itLen( UThread* ut, UCell* cell )
91{
92    int len = ur_seriesEnd( cell );
93    if( len > -1 )
94        len -= cell->series.it;
95    return len;
96}
97
98
99typedef int (*UDatatypeCmpFunc)( UThread*, const UCell*, const UCell*, int );
100typedef int (*UDatatypeSelFunc)( UThread*, UCell*, const UCell*, UCell* );
101
102/**
103  Returns non-zero if successful.
104  Res may be the same as sel.
105*/
106int ur_getSelector( UThread* ut, const UCell* sel, UCell* res )
107{
108#define ATOM_OFF(val,atm)   (((unsigned int) val) - atm)
109    UBlock* blk;
110    UCell* val;
111
112    val = ur_wordCell( ut, sel );
113    if( ! val )
114        return 0;
115
116    if( ur_selIsAtom(sel) )
117    {
118        switch( ur_type(val) )
119        {
120            case UT_CALL:
121                // TODO: Avoid copying call cell here. Maybe we should
122                //       move ur_wordCell() outside of ur_getSelector()?
123                ur_copyCell( res, *val );
124                return 1;
125
126            case UT_CONTEXT:
127            case UT_PORT:
128#ifdef UR_CONFIG_DATAFLOW
129            case UT_COMPONENT:
130#endif
131            {
132                int wrdN = ur_lookup( val, ur_sel(sel) );
133                if( wrdN > -1 )
134                {
135                    blk = ur_blockPtr( val->ctx.valBlk );
136                    ur_copyCell( res, blk->ptr.cells[ wrdN ] );
137                    return 1;
138                }
139            }
140                break;
141
142            case UT_COORD:
143            {
144                unsigned int n;
145                n = ATOM_OFF( ur_sel(sel), UR_ATOM_R );
146                if( (n < 4) && (n < val->coord.len) )
147                {
148                    ur_initInt( res, val->coord.elem[ n ] );
149                    return 1;
150                }
151            }
152                goto set_none;
153
154            case UT_VEC3:
155            {
156                unsigned int n;
157                n = ATOM_OFF( ur_sel(sel), UR_ATOM_X );
158                if( n < 3 )
159                {
160                    ur_initDecimal( res, (double) val->vec3.xyz[ n ] );
161                    return 1;
162                }
163            }
164                goto set_none;
165
166            default:
167                if( ur_type(val) >= UT_BI_COUNT )
168                {
169                    UDatatypeSelFunc func = CUSTOM_DT(ur_type(val)).selectAtom;
170                    if( func )
171                        return func( ut, val, sel, res );
172                }
173                goto set_none;
174        }
175        ur_throwErr( UR_ERR_SCRIPT, "Invalid select!" );
176        return 0;
177    }
178    else
179    {
180        int n = ur_sel(sel);
181        if( n )
182        {
183            if( n > 0 )
184                --n;
185            if( ur_pick( ut, val, n, res ) )
186                return 1;
187        }
188    }
189
190set_none:
191
192    ur_setNone( res );
193    return 1;
194}
195
196
197#ifdef UR_CONFIG_DATAFLOW
198void ur_setPlug( UThread*, UBlock* cblk, UIndex wrdN, const UCell* nval );
199#endif
200
201
202/**
203  Returns non-zero if successful.
204*/
205int ur_setSelector( UThread* ut, const UCell* sel, const UCell* nval )
206{
207    UBlock* blk;
208    UCell* val;
209
210    val = ur_wordCell( ut, sel );
211    if( ! val )
212        return 0;
213
214    switch( ur_type(val) )
215    {
216        case UT_CONTEXT:
217            if( ur_selIsAtom(sel) )
218            {
219                int wrdN;
220                wrdN = ur_lookup( val, ur_sel(sel) );
221                if( wrdN > -1 )
222                {
223                    blk = ur_blockPtr( val->ctx.valBlk );
224                    ur_copyCell( blk->ptr.cells + wrdN, *nval );
225                    return 1;
226                }
227            }
228            break;
229
230        case UT_COORD:
231        {
232            unsigned int n;
233            n = ur_sel(sel) - (ur_selIsAtom(sel) ? UR_ATOM_X : 1);
234            if( n < val->coord.len )
235            {
236                if( ur_is(nval, UT_DECIMAL) )
237                    val->coord.elem[ n ] = (int) ur_decimal(nval);
238                else if( ur_is(nval, UT_INT) )
239                    val->coord.elem[ n ] = ur_int(nval);
240            }
241            return 1;
242        }
243            break;
244
245        case UT_VEC3:
246        {
247            unsigned int n;
248            n = ur_sel(sel) - (ur_selIsAtom(sel) ? UR_ATOM_X : 1);
249            if( n < 3 )
250            {
251                if( ur_is(nval, UT_DECIMAL) )
252                    val->vec3.xyz[ n ] = (float) ur_decimal(nval);
253                else if( ur_is(nval, UT_INT) )
254                    val->vec3.xyz[ n ] = (float) ur_int(nval);
255            }
256            return 1;
257        }
258            break;
259
260#ifdef UR_CONFIG_DATAFLOW
261        case UT_COMPONENT:
262            if( ur_selIsAtom(sel) )
263            {
264                int wrdN;
265                wrdN = ur_lookup( val, ur_sel(sel) );
266                ur_setPlug( ut, tBlockPtr(val->ctx.valBlk), wrdN, nval );
267                return 1;
268            }
269            break;
270#endif
271
272        default:
273            if( ! ur_selIsAtom(sel) )
274            {
275                if( ur_poke( ut, val, ur_sel(sel) - 1, nval ) )
276                    return 1;
277            }
278            break;
279    }
280
281    ur_throwErr( UR_ERR_SCRIPT, "Invalid select!" );
282    return 0;
283}
284
285
286// (val do-true do-false -- )
287UR_CALL( uc_either )
288{
289    UCell* res = ur_s_backN(tos, 2);
290
291    if( ur_is(res, UT_NONE) ||
292        (ur_is(res, UT_LOGIC) && ! ur_logic(res)) )
293    {
294        ur_copyCell( res, *tos );
295    }
296    else
297    {
298        ur_copyCell( res, *(ur_s_prev(tos)) );
299    }
300
301    UR_S_DROPN(2);
302    UR_CALL_OP = OP_DO;
303}
304
305
306// (value -- result)
307UR_CALL( uc_reduce )
308{
309    if( ur_is(tos, UT_BLOCK) || ur_is(tos, UT_PAREN) )
310    {
311        UR_CALL_OP = OP_REDUCE;
312    }
313    else if( ur_is(tos, UT_WORD) )
314    {
315        UCell* val;
316        val = ur_wordCell( ut, tos );
317        if( val )
318        {
319            ur_copyCell( tos, *val );
320        }
321    }
322    else if( ur_is(tos, UT_LITWORD) )
323    {
324        ur_type(tos) = UT_WORD;
325    }
326    else if( ur_is(tos, UT_LITSELECT) )
327    {
328        ur_type(tos) = UT_SELECT;
329    }
330    // else leave tos unchanged...
331}
332
333
334/**
335  Returns zero if cell type does not match datatype.
336*/
337int ur_verifyDatatype( const UCell* cell, const UCell* datatype )
338{
339    uint32_t dt = ur_type(cell);
340    if( dt < 32 )
341    {
342        if( datatype->dt.mask0 & (1 << dt) )
343            return 1;
344    }
345    else
346    {
347        if( datatype->dt.mask1 & (1 << (dt - 32)) )
348            return 1;
349    }
350    return 0;
351}
352
353
354void ur_appendTraceT( UThread* ut, UCellError* err, UIndex blkN, UIndex it )
355{
356    if( err->traceBlk )
357    {
358        UCell* cell;
359        UBlock* blk;
360
361        blk = ur_blockPtr( err->traceBlk );
362        cell = ur_appendCell( blk, UT_BLOCK );
363        ur_setSeries( cell, blkN, it );
364    }
365}
366
367
368#define _throwUnset(th,cell)    _throwUnsetF(th,cell,"is unset")
369#define _throwUnbound(th,cell)  _throwUnsetF(th,cell,"is unbound")
370
371static void _throwUnsetF( UThread* ut, const UCell* wcell, const char* umsg )
372{
373    UString* str;
374    str = ur_threadTmp( ut );
375    str->used = 0;
376    ur_atomStr( ur_atom(wcell), str );
377    ur_termCStr( str );
378    ur_throwErr( UR_ERR_SCRIPT, "word '%s %s", str->ptr.c, umsg );
379}
380
381
382/**
383   Returns cell pointer or zero if word does not reference a valid cell.
384*/
385UCell* ur_wordCell( UThread* ut, const UCell* pc )
386{
387    UBlock* blk;
388    int wrdN;
389
390    wrdN = pc->word.index;
391    if( wrdN < 0 )
392    {
393        _throwUnbound( ut, pc);     // UR_UNBOUND
394        return 0;
395    }
396
397    switch( pc->word.flags & UR_FLAG_BIND_MASK )
398    {
399        case UR_BIND_THREAD:
400#ifdef UR_CONFIG_DATAFLOW
401        case UR_BIND_PLUG:      // Each component has its own body for now.
402#endif
403            // ur_blockPtr( pc->word.valBlk )
404            blk = (((UBlock*) ut->blocks.arr.ptr.v) + pc->word.valBlk);
405            return blk->ptr.cells + wrdN;
406
407        case UR_BIND_GLOBAL:
408            // ur_blockPtr( pc->word.valBlk )
409            blk = (((UBlock*) ut->env->blocks.arr.ptr.v) - pc->word.valBlk);
410            return blk->ptr.cells + wrdN;
411
412        case UR_BIND_LOCAL:
413#if 1
414        {
415            LocalFrame* it  = UR_LF_BEG;
416            LocalFrame* end = UR_LF_END;
417            while( it != end )
418            {
419                if( it->n == pc->word.wordBlk )
420                    return it->cell + wrdN;
421                ++it;
422            }
423            _throwUnsetF( ut, pc, "is out-of-scope local" );
424            return 0;
425        }
426#else
427            if( pc->word.wordBlk != ut->localWordBlk )
428            {
429                _throwUnsetF( ut, pc, "is out-of-scope local" );
430                return 0;
431            }
432#if 0
433            blk = ((UBlock*) ut->blocks.arr.ptr.v) + BLK_DSTACK;
434            return blk->ptr.cells + wrdN + ut->localIdx;
435#else
436            return ut->localPos + wrdN;
437#endif
438#endif
439    }
440
441    _throwUnsetF( ut, pc, "has invalid bind mask" );
442    return 0;
443}
444
445
446/**
447   Assign value to word.
448   Returns non-zero if successful.  Upon failure, zero is returned and and
449   error is thrown.
450*/
451int ur_setWord( UThread* ut, const UCell* wc, const UCell* val )
452{
453    UBlock* blk;
454    UCell* dest;
455    int wrdN;
456
457    wrdN = wc->word.index;
458    if( wrdN < 0 )
459    {
460        _throwUnbound( ut, wc);     // UR_UNBOUND
461        return 0;
462    }
463
464    switch( wc->word.flags & UR_FLAG_BIND_MASK )
465    {
466        case UR_BIND_THREAD:
467            blk = tBlockPtr( wc->word.valBlk );
468            dest = blk->ptr.cells + wrdN;
469            break;
470
471        case UR_BIND_GLOBAL:
472            _throwUnsetF( ut, wc, "is in shared global storage" );
473            return 0;
474
475        case UR_BIND_LOCAL:
476        {
477            LocalFrame* it  = UR_LF_BEG;
478            LocalFrame* end = UR_LF_END;
479            while( it != end )
480            {
481                if( it->n == wc->word.wordBlk )
482                {
483                    dest = it->cell + wrdN;
484                    goto set_word;
485                }
486                ++it;
487            }
488            _throwUnsetF( ut, wc, "is out-of-scope local" );
489            return 0;
490        }
491
492#ifdef UR_CONFIG_DATAFLOW
493        case UR_BIND_PLUG:
494            // Using tBlockPtr - assuming there are no global components.
495            ur_setPlug( ut, tBlockPtr(wc->word.valBlk), wrdN, val );
496            return 1;
497#endif
498
499        default:
500            _throwUnsetF( ut, wc, "has invalid bind mask" );
501            return 0;
502    }
503
504set_word:
505
506    ur_copyCell(dest, *val);
507    return 1;
508}
509
510
511/**
512  Evaluate C string.
513
514  \param cmd  String to evaluate.
515  \param len  Length of cmd string.  May be -1 if cmd is null terminated.
516
517  \return UR_EVAL_ code.
518*/
519int ur_evalCStr( UThread* ut, const char* cmd, int len )
520{
521    const char* end;
522    UIndex blkN;
523
524    if( ! cmd )
525        return UR_EVAL_OK;
526
527    if( len < 0 )
528    {
529        end = cmd;
530        while( *end )
531            ++end;
532    }
533    else
534    {
535        end = cmd + len;
536    }
537
538    if( end != cmd )
539    {
540        blkN = ur_tokenize( ut, cmd, end );
541        if( blkN )
542        {
543            ur_infuseOpcodes( ut, blkN );
544            return ur_eval( ut, blkN, 0 );
545        }
546        return UR_EVAL_ERROR;
547    }
548    return UR_EVAL_OK;
549}
550
551
552/*--------------------------------------------------------------------------*/
553
554
555extern int memEqual( const char* a, const char* b, int len );
556
557/**
558  Returns 1 if equivalent or zero if not.
559*/
560int ur_equal( UThread* ut, const UCell* a, const UCell* b )
561{
562    switch( ur_type(a) )
563    {
564        case UT_DATATYPE:
565            if( ur_is(b, UT_DATATYPE) )
566            {
567                if( (a->dt.mask0 == b->dt.mask0) &&
568                    (a->dt.mask1 == b->dt.mask1) )
569                    return 1;
570            }
571            break;
572
573        case UT_NONE:
574            if( ur_is(b, UT_NONE) )
575            {
576                return 1;
577            }
578            else if( ur_is(b, UT_LOGIC) )
579            {
580                if( ! ur_logic(b) )
581                    return 1;
582            }
583            break;
584
585        case UT_WORD:
586        case UT_LITWORD:
587        case UT_SETWORD:
588        case UT_GETWORD:
589            if( ur_isAWord(b) )
590            {
591                if( ur_atom(a) == ur_atom(b) )
592                    return 1;
593            }
594            break;
595
596        case UT_OPCODE:
597            if( ur_is(b, UT_OPCODE) )
598            {
599                if( ur_atom(a) == ur_atom(b) )
600                    return 1;
601            }
602            break;
603
604        case UT_LOGIC:
605            if( ur_is(b, UT_LOGIC) )
606            {
607                if( ur_logic(a) == ur_logic(b) )
608                    return 1;
609            }
610            else if( ur_is(b, UT_NONE) )
611            {
612                if( ! ur_logic(a) )
613                    return 1;
614            }
615            break;
616
617        case UT_CHAR:
618        case UT_INT:
619            if( ur_is(b, UT_INT) || ur_is(b, UT_CHAR) )
620            {
621                if( ur_int(a) == ur_int(b) )
622                    return 1;
623            }
624            else if( ur_is(b, UT_DECIMAL) )
625            {
626                if( ur_int(a) == (int) ur_decimal(b) )
627                    return 1;
628            }
629            else if( ur_is(b, UT_BIGNUM) )
630            {
631                UCell big;
632                bignum_seti( &big, ur_int(a) );
633                if( bignum_equal( &big, b ) )
634                    return 1;
635            }
636            break;
637
638        case UT_DECIMAL:
639            if( ur_is(b, UT_DECIMAL) )
640            {
641                if( ur_decimal(a) == ur_decimal(b) )
642                    return 1;
643            }
644            else if( ur_is(b, UT_INT) )
645            {
646                if( ur_decimal(a) == (double) ur_int(b) )
647                    return 1;
648            }
649            else if( ur_is(b, UT_BIGNUM) )
650            {
651                UCell big;
652                bignum_setd( &big, ur_decimal(a) );
653                if( bignum_equal( &big, b ) )
654                    return 1;
655            }
656            break;
657
658        case UT_BIGNUM:
659            if( ur_is(b, UT_BIGNUM) )
660            {
661                if( bignum_equal( a, b ) )
662                    return 1;
663            }
664            else if( ur_is(b, UT_INT) )
665            {
666                UCell big;
667                bignum_seti( &big, ur_int(b) );
668                if( bignum_equal( &big, b ) )
669                    return 1;
670            }
671            else if( ur_is(b, UT_DECIMAL) )
672            {
673                UCell big;
674                bignum_setd( &big, ur_decimal(b) );
675                if( bignum_equal( &big, b ) )
676                    return 1;
677            }
678            break;
679
680        case UT_COORD:
681            if( ur_is(b, UT_COORD) )
682            {
683                if( a->coord.len == b->coord.len )
684                {
685                    int i;
686                    for( i = 0; i < a->coord.len; ++i )
687                    {
688                        if( a->coord.elem[i] != b->coord.elem[i] )
689                            return 0;
690                    }
691                    return 1;
692                }
693            }
694            break;
695
696        case UT_VEC3:
697            if( ur_is(b, UT_VEC3) )
698            {
699                if( (a->vec3.xyz[0] == b->vec3.xyz[0]) &&
700                    (a->vec3.xyz[1] == b->vec3.xyz[1]) &&
701                    (a->vec3.xyz[2] == b->vec3.xyz[2]) )
702                    return 1;
703            }
704            break;
705
706        case UT_BLOCK:
707        case UT_PAREN:
708        {
709            UCell *cpA1, *cpA2;
710            UCell *cpB1, *cpB2;
711            int len;
712
713            if( ur_blockSlice( ut, a, &cpA1, &cpA2 ) )
714            {
715                if( ur_blockSlice( ut, b, &cpB1, &cpB2 ) )
716                {
717                    len = cpB2 - cpB1;
718                    if( len == (cpA2 - cpA1) )
719                    {
720                        if( cpA1 == cpB1 )
721                            return 1;
722                        while( cpA1 != cpA2 )
723                        {
724                            if( ! ur_equal( ut, cpA1++, cpB1++ ) )
725                                return 0;
726                        }
727                        return 1;
728                    }
729                }
730            }
731        }
732            break;
733
734        case UT_BINARY:
735        case UT_STRING:
736        {
737            uint8_t *cpA1, *cpA2;
738            uint8_t *cpB1, *cpB2;
739            int len;
740
741            if( ur_stringSlice( ut, a, &cpA1, &cpA2 ) )
742            {
743                if( ur_stringSlice( ut, b, &cpB1, &cpB2 ) )
744                {
745                    // TODO: Handle different encodings.
746eq_bin_slice:
747                    len = cpB2 - cpB1;
748                    if( len == (cpA2 - cpA1) )
749                    {
750                        if( len == 0 )
751                            return 1;
752                        if( cpA1 == cpB1 )
753                            return 1;
754                        if( memEqual( (char*) cpA1, (char*) cpB1, len ) )
755                            return 1;
756                    }
757                }
758            }
759            else if( ur_binarySlice( ut, a, &cpA1, &cpA2 ) )
760            {
761                if( ur_binarySlice( ut, b, &cpB1, &cpB2 ) )
762                    goto eq_bin_slice;
763            }
764        }
765            break;
766
767        case UT_CONTEXT:
768            if( a->ctx.wordBlk == b->ctx.wordBlk )
769                return 1;
770            break;
771
772        case UT_DATE:
773        case UT_TIME:
774            if( ur_is(b, UT_DATE) || ur_is(b, UT_TIME) )
775            {
776                if( ur_seconds(a) == ur_seconds(b) )
777                    return 1;
778            }
779            break;
780    }
781    return 0;
782}
783
784
785/**
786  Returns 1 if cells are of the same type and value or zero if not.
787*/
788int ur_same( UThread* ut, const UCell* a, const UCell* b )
789{
790    (void) ut;
791
792    if( ur_type(a) != ur_type(b) )
793        return 0;
794
795    switch( ur_type(a) )
796    {
797        case UT_DATATYPE:
798            if( (a->dt.mask0 == b->dt.mask0) &&
799                (a->dt.mask1 == b->dt.mask1) )
800                return 1;
801            break;
802
803        case UT_NONE:
804            return 1;
805
806        case UT_WORD:
807        case UT_LITWORD:
808        case UT_SETWORD:
809        case UT_GETWORD:
810            if( ur_atom(a) == ur_atom(b) )
811                return 1;
812            break;
813
814        case UT_LOGIC:
815            if( ur_logic(a) == ur_logic(b) )
816                return 1;
817            break;
818
819        case UT_INT:
820            if( ur_int(a) == ur_int(b) )
821                return 1;
822            break;
823
824        case UT_DECIMAL:
825            if( ur_decimal(a) == ur_decimal(b) )
826                return 1;
827            break;
828
829        case UT_BIGNUM:
830            if( bignum_equal( a, b ) )
831                return 1;
832            break;
833
834        case UT_BINARY:
835        case UT_STRING:
836        case UT_BLOCK:
837        case UT_PAREN:
838        case UT_VECTOR:
839        case UT_LIST:
840            if( (a->series.n == b->series.n) &&
841                (a->series.it == b->series.it) )
842                return 1;
843            break;
844
845        case UT_CONTEXT:
846            if( (a->ctx.wordBlk == b->ctx.wordBlk) &&
847                (a->ctx.valBlk == b->ctx.valBlk) )
848                return 1;
849            break;
850
851        case UT_DATE:
852        case UT_TIME:
853            if( ur_seconds(a) == ur_seconds(b) )
854                return 1;
855            break;
856
857        default:
858            if( ur_type(a) >= UT_BI_COUNT )
859            {
860                UDatatypeCmpFunc func = CUSTOM_DT( ur_type(a) ).compare;
861                return func( ut, a, b, 1 );
862            }
863            break;
864    }
865    return 0;
866}
867
868
869// (val val -- logic)
870UR_CALL( uc_equal )
871{
872    UR_S_DROP;
873    ur_logic(UR_TOS) = ur_equal( ut, UR_TOS, tos );
874    ur_initType( UR_TOS, UT_LOGIC );
875}
876
877
878// (val val -- logic)
879UR_CALL( uc_sameQ )
880{
881    int same;
882    UCell* res = ur_s_prev(tos);
883
884    UR_S_DROP;
885
886    same = ur_same( ut, res, tos );
887    ur_initType( res, UT_LOGIC );
888    ur_logic(res) = same;
889}
890
891
892// (val -- logic)
893UR_CALL( uc_zeroQ )
894{
895    int n;
896    UR_CALL_UNUSED_TH
897
898    if( ur_is(tos, UT_INT) || ur_is(tos, UT_LOGIC) )
899        n = ur_int(tos) ? 0 : 1;
900    else if( ur_is(tos, UT_DECIMAL) )
901        n = ur_decimal(tos) ? 0 : 1;
902#ifdef UR_CONFIG_MATH3D
903    else if( ur_is(tos, UT_VEC3) )
904    {
905        n = (tos->vec3.xyz[0] == 0.0 &&
906             tos->vec3.xyz[1] == 0.0 &&
907             tos->vec3.xyz[2] == 0.0) ? 1 : 0;
908    }
909#endif
910    else
911        n = 0;
912    ur_initType(tos, UT_LOGIC);
913    ur_logic(tos) = n;
914}
915
916
917// (val -- datatype)
918UR_CALL( uc_typeQ )
919{
920    int t = ur_type(tos);
921    UR_CALL_UNUSED_TH
922    ur_initType(tos, UT_DATATYPE);
923    ur_datatype(tos) = t;
924    ur_setDatatypeMask(tos, t);
925}
926
927
928// (val -- word)
929UR_CALL( uc_typeQ_word )
930{
931    int t = ur_type(tos);
932    UR_CALL_UNUSED_TH
933    ur_initType(tos, UT_WORD);
934    tos->word.wordBlk = 0;
935    tos->word.valBlk  = BLK_GLOBAL_VAL;
936    tos->word.index   = t;
937    tos->word.atom    = t;
938}
939
940
941UR_CALL( _probe )
942{
943    // The tos argument is NOT assumed to be on the stack here.
944#if 1
945    UString* str;
946    str = ur_threadTmp( ut );
947    str->used = 0;
948    ur_toStr( tos, str, 0 );
949    ur_arrayReserve( str, sizeof(char), str->used + 1 );
950    str->ptr.c[ str->used++ ] = '\n';
951
952    UR_S_GROW;
953    tos = UR_TOS;
954    ur_initType( tos, UT_STRING );
955    ur_setSeries( tos, BIN_THREAD_TMP, 0 );
956    uc_console_out( ut, tos );
957#else
958    UString str;
959
960    ur_arrayInit( &str, 1, 0 );
961    ur_toStr( tos, &str, 0 );
962    if( str.used )
963    {
964        UString* sp = &str;
965        ur_termCStr( sp );
966
967        dprint( str.ptr.c );
968        dprint( "\n" );
969    }
970    ur_arrayFree( &str );
971#endif
972}
973
974
975#ifdef LANG_THUNE
976// (val -- )
977UR_CALL( uc_showTOS )
978{
979    _probe( ut, tos );
980    UR_S_SAFE_DROP;
981}
982
983
984UR_CALL( uc_printTOS )
985{
986    _probe( ut, tos );
987}
988
989
990// ( -- n)
991UR_CALL( uc_stack_level )
992{
993    UR_CALL_UNUSED_TOS;
994
995    UR_S_GROW;
996    ur_initType(UR_TOS, UT_INT);
997    ur_int(UR_TOS) = UR_TOS - UR_BOS;
998}
999#endif
1000
1001
1002// ( -- )
1003UR_CALL( uc_showStack )
1004{
1005    UCell* it = tos;
1006    UCell* end = ut->dstack;
1007
1008    if( it >= (end + UR_DSTACK_SIZE) )
1009    {
1010        dprint( "Stack overflow (%d)\n", (int) (it - end) );
1011        assert( 0 );
1012    }
1013
1014    while( it != end )
1015    {
1016        _probe( ut, it );
1017        --it;
1018    }
1019
1020#ifdef LANG_RUNE
1021    UR_S_GROW;
1022#endif
1023}
1024
1025
1026/*--------------------------------------------------------------------------*/
1027
1028
1029/*
1030  (val word -- )
1031  (val word context -- )    Add word to context.
1032
1033  If val is a series and word is a block then the words are set to the
1034  first N elements of the series (where N is the number of words in word).
1035*/
1036UR_CALL( uc_set )
1037{
1038    UBlock* blk;
1039    UCell* cell;
1040    UCell* val;
1041
1042    val = ur_s_prev( tos );
1043
1044    if( ur_isAWord(tos) )
1045    {
1046        if( ur_wordIsUnbound(tos) )
1047        {
1048            if( tos->word.valBlk == BLK_GLOBAL_VAL )
1049            {
1050                tos->word.index = ur_internWord( &ur_thrGlobal, ur_atom(tos) );
1051            }
1052            else   
1053            {
1054                _throwUnbound( ut, tos );
1055                return;
1056            }
1057        }
1058
1059        cell = ur_wordCell( ut, tos );
1060        if( ! cell )
1061            return;
1062        ur_copyCell( cell, *val );
1063    }
1064    else if( ur_is(tos, UT_BLOCK) )
1065    {
1066        UCell* it;
1067        UCell* end;
1068
1069        blk = ur_block(tos);
1070        UR_ITER_BLOCK( it, end, blk, tos );
1071
1072        if( ur_isASeries(val) )
1073        {
1074            int n = 0;
1075            while( it != end )
1076            {
1077                if( ur_isAWord(it) )
1078                {
1079                    cell = ur_wordCell( ut, it );
1080                    if( ! cell )
1081                        return;
1082                    ur_pick( ut, val, n++, cell );
1083                }
1084                ++it;
1085            }
1086        }
1087        else
1088        {
1089            while( it != end )
1090            {
1091                if( ur_isAWord(it) )
1092                {
1093                    cell = ur_wordCell( ut, it );
1094                    if( ! cell )
1095                        return;
1096                    ur_copyCell( cell, *val );
1097                }
1098                ++it;
1099            }
1100        }
1101    }
1102    else if( ur_is(tos, UT_CONTEXT) &&
1103             (ur_isAWord(val) || ur_is(val,UT_OPCODE)) )
1104    {
1105        int wrdN;
1106        wrdN = ur_internWord( tos, ur_atom(val) );
1107        cell = ur_blockPtr( tos->ctx.valBlk )->ptr.cells + wrdN;
1108        val = ur_s_prev(val);
1109        ur_copyCell( cell, *val );
1110        UR_S_DROP;
1111    }
1112    else
1113    {
1114        ur_throwErr( UR_ERR_DATATYPE, "set expected word!" );
1115        return;