root/trunk/orca/context.c

Revision 496, 33.8 kB (checked in by krobillard, 11 months ago)

Orca - bind now binds lit-words.

Line 
1/*============================================================================
2    ORCA Interpreter
3    Copyright (C) 2005-2006  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 <assert.h>
22#include "os.h"
23#include "ovalue.h"
24#include "internal.h"
25#include "orca_atoms.h"
26
27
28typedef struct
29{
30    uint16_t nameIndex;     // Index into BIN_ATOM_NAMES ptr.c
31    uint16_t nameLen;
32}
33AtomRec;
34
35
36/**
37  Appends atom name to string.
38*/
39void orAtomStr( OAtom atom, OString* str )
40{
41    OBinary* names;
42    AtomRec* rec;
43
44    rec = ((AtomRec*) orEnv->atoms.buf) + atom;
45    names = orStringPtr( BIN_ATOM_NAMES );
46
47    orArrayReserve( str, sizeof(char), str->used + rec->nameLen );
48    memCpy( str->charArray + str->used,
49            names->charArray + rec->nameIndex,
50            rec->nameLen );
51    str->used += rec->nameLen;
52}
53
54
55const char* orAtomCString( OAtom atom )
56{
57    static char buf[ MAX_WORD_LEN + 1 ];
58    OBinary* names;
59    AtomRec* rec;
60
61    rec = ((AtomRec*) orEnv->atoms.buf) + atom;
62    names = orStringPtr( BIN_ATOM_NAMES );
63
64    memCpy( buf, names->charArray + rec->nameIndex, rec->nameLen );
65    buf[ rec->nameLen ] = '\0';
66    return buf;
67}
68
69
70/**
71  Add atom to environment.
72
73  \param str  Name of atom.
74  \param len  Number of characters.  Str will be truncated if greater than 32.
75
76  \returns Atom
77*/
78OAtom orInternAtom( const char* str, int len )
79{
80    char* cp;
81    const char* ep;
82    const char* sp;
83    OArray* atoms;
84    OString* names;
85    AtomRec* rec;
86    AtomRec* end;
87    char buf[ MAX_WORD_LEN ];
88
89
90    assert( len > 0 );
91
92    if( len > MAX_WORD_LEN )
93        len = MAX_WORD_LEN;
94
95    // Make lowercase.
96
97    cp = buf;
98    ep = buf + len;
99    while( cp != ep )
100    {
101        int c = *str++;
102        if( c >= 'A' && c <= 'Z' )
103            c -= 'A' - 'a';
104        *cp++ = c;
105    }
106
107    // Check if atom already exists.
108
109    atoms = &orEnv->atoms;
110    rec = (AtomRec*) atoms->buf;
111    end = rec + atoms->used;
112
113    names = orStringPtr( BIN_ATOM_NAMES );
114
115    while( rec != end )
116    {
117        if( rec->nameLen == len )
118        {
119            sp = names->charArray + rec->nameIndex;
120            cp = buf;
121            while( cp != ep )
122            {
123                if( *sp++ != *cp )
124                    break;
125                ++cp;
126            }
127
128            if( cp == ep )
129                goto done;
130        }
131        ++rec;
132    }
133
134
135    // Nope, add new atom.
136
137    OA_EXPAND1( AtomRec, atoms, rec );
138    rec->nameIndex = names->used;
139    rec->nameLen   = len;
140
141    orArrayReserve( names, sizeof(char), names->used + len );
142    memCpy( names->charArray + names->used, buf, len );
143    names->used += len;
144
145done:
146
147    return rec - ((AtomRec*) atoms->buf);
148}
149
150
151OIndex orInternA( OBlock* wordBlk, OAtom atom )
152{
153    OValue* it;
154    OValue* end;
155
156    it  = wordBlk->values;
157    end = it + wordBlk->used;
158
159    while( it != end )
160    {
161        if( orAtom(it) == atom )
162            return it - wordBlk->values;
163        ++it;
164    }
165
166    // Add new word.
167
168    OA_EXPAND1( OValue, wordBlk, it );
169    orSetTF( it, OT_WORD );
170    orSetWord( it, GLOBAL_WORDS_BLKN, GLOBAL_CTXN, 0, atom );
171
172    return it - wordBlk->values;
173}
174
175
176/**
177  Add word to context and return pointer to value.
178  If the word did not already exist then the value is initialied as unset.
179  If wordV is non-zero then wordV->word is bound to the new value.
180*/
181OValue* orIntern( OContext* ctx, const char* str, int len, OValue* wordV )
182{
183    OAtom  atom;
184    OIndex n;
185    int    count;
186    OValue* val;
187    OBlock* wblk = orBlockPtr( ctx->wblkN );
188    OBlock* vblk = orBlockPtr( ctx->vblkN );
189
190    atom = orInternAtom( str, len );
191    n    = orInternA( wblk, atom );
192    count = n + 1;
193
194    if( vblk->used < count )
195    {
196        orArrayReserve( vblk, sizeof(OValue), count );
197        vblk->used = count;
198
199        val = vblk->values + n;
200        orSetTF( val, OT_UNSET );
201    }
202
203    if( wordV )
204    {
205        orSetWord( wordV, ctx->wblkN, ctx->vblkN, n, atom );
206    }
207
208    return vblk->values + n;
209}
210
211
212/**
213  Find word in context by atom.
214  Returns word index or -1 if not found.
215*/
216int orLookup( const OContext* ctx, int atom )
217{
218    OBlock* wblk;
219    OValue* it;
220    OValue* end;
221
222    /* There is unused space in wblk->values which could be used to speed
223       up word search.  Some ideas:
224        - Min/max atom to fail early.
225        - Hash.
226        - Also store atoms in order and use binary search.
227     */
228
229    wblk = orBlockPtr( ctx->wblkN );
230    it  = wblk->values;
231    end = it + wblk->used;
232
233    while( it != end )
234    {
235        if( orAtom(it) == atom )
236            return it - wblk->values;
237        ++it;
238    }
239    return -1;
240}
241
242
243/**
244*/
245void orMakeContext( OContext* ctx, int size )
246{
247    ctx->wblkN = orBlockN( orMakeBlock( size ) );
248
249    // Assuming caller checks orRefAvail.
250    orRefPush( OT_BLOCK, ctx->wblkN );
251    ctx->vblkN = orBlockN( orMakeBlock( size ) );
252    orRefPop( 1 );
253}
254
255
256static void internSetWords( OContext* ctx, OValue* it, OValue* end )
257{
258    int wrdN;
259    OBlock* wordBlk = orBlockPtr( ctx->wblkN );
260
261    while( it != end )
262    {
263        if( it->type == OT_SETWORD )
264        {
265            // Word might be redefined mulitple times so we have to
266            // orLookup() first.
267            wrdN = orLookup( ctx, orAtom(it) );
268            if( wrdN < 0 )
269                wrdN = orInternA( wordBlk, orAtom(it) );
270        }
271        ++it;
272    }
273}
274
275
276static void unsetValues( OValue* it, OValue* end )
277{
278    while( it != end )
279    {
280        orSetTF( it, OT_UNSET );
281        ++it;
282    }
283}
284
285
286/**
287  Returns 0 if fails.
288*/
289int orMakeObject( OBlock* pblk, int blkI, OContext* ctx )
290{
291    OBlock* wblk;
292    OBlock* vblk;
293    OValue* it;
294    OValue* end;
295    OIndex blkN;
296    int wcount;
297
298
299    if( ! orRefAvail( 4 ) )
300        return 0;
301
302    wcount = 0;
303    it  = pblk->values + blkI;
304    end = pblk->values + pblk->used;
305    while( it != end )
306    {
307        // This could be optimized to not count duplicate words.
308        if( it->type == OT_SETWORD )
309            ++wcount;
310        ++it;
311    }
312
313    blkN = orBlockN( pblk );
314    orRefPush( OT_BLOCK, blkN );
315    orMakeContext( ctx, wcount + 1 );
316
317    // re-acquire
318    wblk = orBlockPtr( ctx->wblkN );
319    vblk = orBlockPtr( ctx->vblkN );
320    pblk = orBlockPtr( blkN );
321
322    // Intern words.
323    it = pblk->values + blkI;
324    orInternA( wblk, OR_ATOM_SELF );
325    internSetWords( ctx, it, end );
326
327    vblk->used = wblk->used;
328    it = vblk->values;
329    orSetTF( it, OT_OBJECT );
330    it->ctx = *ctx;
331    unsetValues( it + 1, it + vblk->used );
332
333    orRefPush( OT_BLOCK, ctx->wblkN );
334    orRefPush( OT_BLOCK, ctx->vblkN );
335
336    // Bind prototype block.
337    orBind( pblk, ctx );
338
339    pblk = orBlockPtr( blkN );  // re-acquire
340    orEvalBlock( pblk, blkI );
341
342    orRefPop( 3 );
343
344    if( orErrorThrown )
345        return 0;
346    return 1;
347}
348
349
350/*
351   Similar to deep orCopyBlock() but OT_FUNCTION bodies are also copied.
352*/
353static void copyObjectValues( OValue* copy, OValue* it, int n )
354{
355    OValue* end = it + n;
356    while( it != end )
357    {
358        orCopyV( copy, *it );
359
360        if( (it->type == OT_BLOCK) || (it->type == OT_PAREN) )
361        {
362            OBlock* blk = orCopyBlock( it->index, it->series.it, 1 );
363            orSetSeries( copy, orBlockN(blk), 0 );
364        }
365        else if( it->type == OT_FUNCTION )
366        {
367            OBlock* blk = orCopyBlock( it->func.bodyBlk, 0, 1 );
368            copy->func.bodyBlk = orBlockN(blk);
369        }
370
371        ++copy;
372        ++it;
373    }
374}
375
376
377/**
378  Orig & clone must not point to the same OContext.
379*/
380void orCloneObject( const OContext* orig, OContext* clone )
381{
382    int size;
383    OBlock* cblk;
384    OBlock* vblk;
385    OValue* val;
386
387
388    vblk = orBlockPtr( orig->vblkN );
389    size = vblk->used;
390    cblk = orMakeBlock( size );
391    cblk->used = size;
392
393    clone->wblkN = orig->wblkN;
394    clone->vblkN = orBlockN( cblk );
395
396    // Point self to the new object.
397    assert( cblk->avail );
398    val = cblk->values;
399    orSetTF( val, OT_OBJECT );
400    val->ctx = *clone;
401
402    // Must set cblk->used and clear values in case GC called in
403    // copyObjectValues().
404    unsetValues( val + 1, val + size );
405
406    orRefPush( OT_BLOCK, clone->vblkN );
407    vblk = orBlockPtr( orig->vblkN );   // re-acquire
408    copyObjectValues( cblk->values + 1, vblk->values + 1, size - 1 );
409    orRefPop( 1 );
410
411    cblk = orBlockPtr( clone->vblkN );  // re-acquire
412    orBind( cblk, clone );
413}
414
415
416/**
417  Clones the orig context if no new members are added in the block.
418  Returns new context number or -1 if fails.
419*/
420int orMakeClone( const OContext* orig, OContext* clone,
421                 OIndex blkN, OIndex blkI )
422{
423    OBlock* pblk;
424    OBlock* vblk;
425    OBlock* cblk;
426    OValue* it;
427    OValue* end;
428    int wcount;
429    int origUsed;
430
431
432    if( ! orRefAvail( 3 ) )
433        return -1;
434
435    orRefPush( OT_BLOCK, blkN );
436
437    vblk = orBlockPtr( orig->vblkN );
438    wcount = origUsed = vblk->used;
439
440    pblk = orBlockPtr( blkN );
441    it  = pblk->values + blkI;
442    end = pblk->values + pblk->used;
443    while( it != end )
444    {
445        if( it->type == OT_SETWORD )
446        {
447            if( orLookup( orig, orAtom(it) ) < 0 )
448                ++wcount;
449        }
450        ++it;
451    }
452
453    if( wcount == origUsed )
454    {
455        orCloneObject( orig, clone );
456    }
457    else
458    {
459        orMakeContext( clone, wcount );
460
461        // Re-acquire.
462        pblk = orBlockPtr( blkN );
463        cblk = orBlockPtr( clone->wblkN );
464        vblk = orBlockPtr( orig->wblkN );
465
466        assert( cblk->avail );
467        memCpy( cblk->values, vblk->values, sizeof(OValue) * origUsed );
468        cblk->used = origUsed;
469        internSetWords( clone, pblk->values + blkI, end );
470        wcount = cblk->used;
471
472
473        vblk = orBlockPtr( orig->vblkN );
474        cblk = orBlockPtr( clone->vblkN );
475        cblk->used = wcount;
476
477        // Point self to the new object.
478        it = cblk->values;
479        orSetTF( it, OT_OBJECT );
480        it->ctx = *clone;
481
482        unsetValues( it + 1, it + wcount );
483
484        orRefPush( OT_BLOCK, clone->wblkN );
485        orRefPush( OT_BLOCK, clone->vblkN );
486        copyObjectValues( it + 1, vblk->values + 1, origUsed - 1 );
487        orRefPop( 2 );
488    }
489
490    orBind( orBlockPtr( blkN ), clone );
491
492    orRefPop( 1 );
493
494    orRefPush( OT_BLOCK, clone->wblkN );
495    orRefPush( OT_BLOCK, clone->vblkN );
496    orEvalBlock( orBlockPtr( blkN ), blkI );
497    orRefPop( 2 );
498
499    if( orErrorThrown )
500        return 0;
501
502    return 1;
503}
504
505
506static void orBindWordVal( OValue* val, OContext* ctx )
507{
508    int wrdN;
509    wrdN = orLookup( ctx, orAtom(val) );
510    if( wrdN > -1 )
511    {
512        val->word.context = ctx->vblkN;
513        val->word.wordBlk = ctx->wblkN;
514        val->word.index   = wrdN;
515        // Atom remains the same.
516    }
517}
518
519
520void orBind( OBlock* blk, OContext* ctx )
521{
522    OValue* it  = blk->values;
523    OValue* end = it + blk->used;
524
525    while( it != end )
526    {
527        if( (it->type == OT_WORD) ||
528            (it->type == OT_SETWORD) ||
529            (it->type == OT_LITWORD) ||
530            (it->type == OT_GETWORD) )
531        {
532            orBindWordVal( it, ctx );
533        }
534        else if( (it->type == OT_PATH) || (it->type == OT_SETPATH) )
535        {
536            OValue* path1 = orBLOCK( it )->values;
537            if( (path1->type == OT_WORD) ||
538                (path1->type == OT_SETWORD) ||
539                (path1->type == OT_GETWORD) )
540                orBindWordVal( path1, ctx );
541        }
542        else if( (it->type == OT_BLOCK) || (it->type == OT_PAREN) )
543        {
544            orBind( orBLOCK( it ), ctx );
545        }
546        else if( it->type == OT_FUNCTION )
547        {
548            orBind( orBlockPtr( it->func.bodyBlk ), ctx );
549
550            // NOTE: Will probably need to rebind to local func context in
551            // case ctx contains the same words.
552            //orBind( orBlockPtr( it->func.bodyBlk ), it->func.context );
553        }
554
555        ++it;
556    }
557}
558
559
560#if 0
561/*
562   Returns true if block contains does or func.
563*/
564int orClosureRequired( OIndex blkN )
565{
566    OBlock* blk = orBlockPtr( blkN );
567    OValue* it  = blk->values;
568    OValue* end = it + blk->used;
569
570    while( it != end )
571    {
572        if( (it->type == OT_WORD) ||
573            (it->type == OT_SETWORD) ||
574            (it->type == OT_GETWORD) )
575        {
576            if( (orAtom(it) == OR_ATOM_FUNC) ||
577                (orAtom(it) == OR_ATOM_DOES) )
578                return 1;
579        }
580        else if( (it->type == OT_BLOCK) || (it->type == OT_PAREN) )
581        {
582            if( orClosureRequired( it->index ) )
583                return 1;
584        }
585
586        ++it;
587    }
588    return 0;
589}
590
591
592void orRebind( OIndex blkN, OIndex from, OIndex to )
593{
594    OBlock* blk = orBlockPtr( blkN );
595    OValue* it  = blk->values;
596    OValue* end = it + blk->used;
597
598    while( it != end )
599    {
600        if( (it->type == OT_WORD) ||
601            (it->type == OT_SETWORD) ||
602            (it->type == OT_GETWORD) )
603        {
604            if( it->word.context == from )
605                it->word.context = to;
606        }
607#if 0
608        else if( (it->type == OT_PATH) || (it->type == OT_SETPATH) )
609        {
610            OValue* path1 = orBLOCK( it )->values;
611            if( (path1->type == OT_WORD) ||
612                (path1->type == OT_SETWORD) ||
613                (path1->type == OT_GETWORD) )
614            {
615                if( path1->word.context == from )
616                    path1->word.context = to;
617            }
618        }
619#endif
620        else if( (it->type == OT_BLOCK) || (it->type == OT_PAREN) )
621        {
622            orRebind( it->index, from, to );
623        }
624
625        ++it;
626    }
627}
628#endif
629
630
631/*
632   words      [block! word!]
633   known-word [word! object!]
634   /copy
635*/
636OR_NATIVE_PUB( orBindNative )
637{
638#define REF_BIND_COPY   a1 + 2
639
640    OValue* a2 = a1 + 1;
641    OContext ctx;
642
643    if( a2->type == OT_WORD )
644    {
645        ctx.wblkN = a2->word.wordBlk;
646        ctx.vblkN = a2->word.context;
647    }
648    else
649    {
650        ctx = a2->ctx;
651    }
652
653    if( a1->type == OT_BLOCK )
654    {
655        OBlock* blk;
656
657        if( orRefineSet(REF_BIND_COPY) )
658            blk = orCopyBlock( a1->index, 0, 1 );
659        else
660            blk = orBLOCK( a1 );
661
662        orResult( OT_BLOCK, orBlockN(blk) );
663
664        orBind( blk, &ctx );
665    }
666    /*
667    else if( a1->type == OT_WORD )
668    {
669    }
670    */
671    else
672    {
673        orError( "Invalid bind values" );
674    }
675}
676
677
678OR_NATIVE_PUB( orProtectNative )
679{
680    OBlock* vblk;
681    OContext ctx;
682    int wrdN;
683
684    orGlobalCtx( ctx );
685    vblk = orBlockPtr( ctx.wblkN );
686
687    if( orIs(a1, OT_WORD) )
688    {
689        wrdN = orLookup( &ctx, orAtom(a1) );
690        if( wrdN > -1 )
691        {
692            vblk->values[ wrdN ].flags |= OR_WORD_PROT;
693        }
694    }
695    else if( orIs(a1, OT_BLOCK) )
696    {
697        OBlock* blk = orBLOCK( a1 );
698        OValue* it  = blk->values;
699        OValue* end = it + blk->used;
700        while( it != end )
701        {
702            if( orIs(it, OT_WORD) )
703            {
704                wrdN = orLookup( &ctx, orAtom(a1) );
705                if( wrdN > -1 )
706                {
707                    vblk->values[ wrdN ].flags |= OR_WORD_PROT;
708                }
709            }
710            ++it;
711        }
712    }
713    orResultUNSET;
714}
715
716
717/*
718   object [object!]
719   word   [word!]
720*/
721OR_NATIVE_PUB( orInNative )
722{
723    OValue* a2 = a1 + 1;
724    OIndex wrdN;
725
726    wrdN = orLookup( &a1->ctx, orAtom(a2) );
727    if( wrdN < 0 )
728    {
729        orResultNONE;
730    }
731    else
732    {
733        orSetTF( a1, OT_WORD );
734        // a1->context remains unchanged.
735        a1->word.index = wrdN;
736        orAtom(a1)     = orAtom(a2);
737    }
738}
739
740
741/*
742   words [block! word!]
743   body  [block!]
744*/
745OR_NATIVE_PUB( orUseNative )
746{
747    OContext ctx;
748    OBlock* blk;
749    OValue* it;
750    OValue* end;
751    OValue* a2 = a1 + 1;
752
753
754    orRefAvailErr( 2 )
755    orRefPush( OT_BLOCK, a2->index );
756
757    if( a1->type == OT_WORD )
758    {
759        orMakeContext( &ctx, 1 );
760        orInternA( orBlockPtr( ctx.wblkN ), orAtom(a1) );
761    }
762    else
763    {
764        orRefPush( OT_BLOCK, a1->index );
765
766        blk = orBLOCK( a1 );
767        it  = blk->values + a1->series.it;
768        end = blk->values + blk->used;
769
770        orMakeContext( &ctx, end - it );
771        blk = orBlockPtr( ctx.wblkN );
772
773        while( it != end )
774        {
775            if( it->type == OT_WORD )
776                orInternA( blk, orAtom(it) );
777            ++it;
778        }
779
780        orRefPop( 1 );
781    }
782
783    blk = orBlockPtr( ctx.vblkN );
784    it  = blk->values;
785    end = it + blk->used;
786    while( it != end )
787    {
788        orSetTF( it, OT_UNSET );
789        ++it;
790    }
791
792    orBind( orBlockPtr( a2->index ), &ctx );
793
794    orRefPop( 1 );
795
796    orEvalBlock( orBlockPtr( a2->index ), a2->series.it );
797}
798
799
800/*
801   'variables   [word! block!]
802   series       [series!]
803   body         [block!]
804*/
805OR_NATIVE_PUB( orForeachNative )
806{
807#ifdef OR_CONFIG_REBOL
808    int rlen;
809    int vlen;
810    OBlock* blk;
811    OValue* wval;
812    OContext ctx;
813    OValue* a2 = a1 + 1;
814    OIndex serN = a2->index;
815    OIndex si   = a2->series.it;
816    OIndex send;
817    OIndex body = (a1 + 2)->index;
818    OIndex bi   = (a1 + 2)->series.it;
819
820
821    // Binding a copy of the body to a private context is horribly
822    // expensive but what choice do we have?
823    // Still, 'foreach is faster than 'forall (probably because using paths
824    // is so slow).
825
826    orRefAvailErr( 4 )
827
828    // Must hold body & a2 in case orMakeContext() or orCopyBlock() trigger
829    // recycle.
830    orRefPush( OT_BLOCK, body );
831    orRefPush( a2->type, a2->index );
832
833    if( a1->type == OT_BLOCK )
834    {
835        OValue* it;
836        OValue* end;
837
838        blk = orBLOCK(a1);
839
840        orRefPush( OT_BLOCK, a1->index );
841        orMakeContext( &ctx, blk->used - a1->series.it );
842        orRefPop( 1 );
843
844        it  = blk->values + a1->series.it;
845        end = blk->values + blk->used;
846
847        blk = orBlockPtr( ctx.wblkN );
848        while( it != end )
849        {
850            if( it->type == OT_WORD )
851            {
852                orInternA( blk, orAtom(it) );
853            }
854            else
855            {
856                orError( "Invalid foreach argument" );
857                orRefPop( 2 );
858                return;
859            }
860            ++it;
861        }
862
863    }
864    else
865    {
866        orMakeContext( &ctx, 1 );
867        orInternA( orBlockPtr( ctx.wblkN ), orAtom(a1) );
868    }
869
870    orRefPush( OT_BLOCK, ctx.wblkN );
871    orRefPush( OT_BLOCK, ctx.vblkN );
872
873    blk = orBlockPtr( ctx.vblkN );
874    wval = blk->values;
875    vlen = blk->avail;
876
877    blk = orCopyBlock( body, bi, 1 );
878    body = orBlockN( blk );
879    bi   = 0;
880    a1[2].series.n = body;      // Replace original on stack to keep from GC.
881    orBind( blk, &ctx );
882
883
884    switch( a2->type )
885    {
886        case OT_BLOCK:
887        case OT_PAREN:
888        case OT_PATH:
889        {
890            blk = orBlockPtr( serN );
891            send = blk->used;
892
893            if( vlen > 1 )
894                send -= vlen - 1;
895
896            while( si < send )
897            {
898                memCpy( wval, blk->values + si, vlen * sizeof(OValue) );
899                si += vlen;
900
901                orEvalBlock( orBlockPtr( body ), bi );
902                if( orErrorThrown )
903                    goto error;
904                blk = orBlockPtr( serN );          // Reacquire.
905            }
906
907            if( si < blk->used )
908            {
909                rlen = blk->used - si;
910
911                memCpy( wval, blk->values + si, rlen * sizeof(OValue) );
912                wval += rlen;
913
914                while( rlen < vlen )
915                {
916                    orSetTF( wval, OT_NONE );
917                    ++wval;
918                    ++rlen;
919                }
920
921                orEvalBlock( orBlockPtr( body ), bi );
922                if( orErrorThrown )
923                    goto error;
924            }
925        }
926            break;
927
928        case OT_STRING:
929        case OT_FILE:
930        case OT_ISSUE:
931        case OT_TAG:
932        {
933            OString* str = orStringPtr( serN );
934            send = str->used;
935
936            orSetTF( wval, OT_CHAR );
937
938            if( vlen > 1 )
939            {
940                send -= vlen - 1;
941
942                for( rlen = 1; rlen < vlen; ++rlen )
943                    orSetTF( (wval + rlen), OT_CHAR );
944            }
945
946            while( si < send )
947            {
948                for( rlen = 0; rlen < vlen; ++rlen )
949                    orInt(wval + rlen) = str->charArray[ si++ ];
950
951                orEvalBlock( orBlockPtr( body ), bi );
952                if( orErrorThrown )
953                    goto error;
954                str = orStringPtr( serN );     // Reacquire.
955            }
956
957            if( si < str->used )
958            {
959                rlen = str->used - si;
960
961                while( si < str->used )
962                {
963                    orInt(wval) = str->charArray[ si++ ];
964                    ++wval;
965                }
966
967                while( rlen < vlen )
968                {
969                    orSetTF( wval, OT_NONE );
970                    ++wval;
971                    ++rlen;
972                }
973
974                orEvalBlock( orBlockPtr( body ), bi );
975                if( orErrorThrown )
976                    goto error;
977            }
978        }
979            break;
980
981        case OT_LIST:
982            if( a1->type == OT_BLOCK )
983            {
984                orError( "foreach block! list! not implemented" );
985                break;
986            }
987
988            orResultNONE;
989
990            if( a2->series.it )
991            {
992                OValue* begin;
993                OValue* it;
994
995                blk = orBLOCK(a2);
996                begin = blk->values;
997                it    = begin + a2->series.it;
998
999                while( it->LIST_NEXT > 0 )
1000                {
1001                    orCopyV( wval, it[1] );
1002
1003                    orEvalBlock( orBlockPtr( body ), bi );
1004                    if( orErrorThrown )
1005                        goto error;
1006
1007                    //blk = orBlockPtr( serN );          // Reacquire.
1008                    //orListNextNode( blk, it );
1009                    it = begin + it->LIST_NEXT;
1010                }
1011            }
1012            break;
1013    }
1014
1015free_private:
1016
1017    orFreeBlock( body );
1018    orFreeBlock( ctx.wblkN );
1019    orFreeBlock( ctx.vblkN );
1020    goto pop;
1021
1022error:
1023
1024    if( orErrorIsType(OR_ERROR_BREAK) )
1025    {
1026        orErrorClear;
1027        orSetTF( a1, OT_UNSET );
1028        goto free_private;
1029    }
1030
1031    // Cannot free body block if error is thrown and orError->block == body.
1032
1033pop:
1034
1035    orRefPop( 4 );
1036
1037#else  // --------------------------------------------------------------
1038    // Foreach without context creation overhead.
1039    // (Does not bind or allocate from heap).
1040
1041    OBlock* blk;
1042    OBlock* cblk;
1043    OValue* val;
1044    OValue* wit;
1045    OValue* wval;
1046    OValue* wend;
1047    OValue* a2 = a1 + 1;
1048    OIndex serN = a2->index;
1049    OIndex si   = a2->series.it;
1050    OIndex send;
1051    OIndex body = (a1 + 2)->index;
1052    OIndex bi   = (a1 + 2)->series.it;
1053    OValue saveA1;
1054
1055
1056    orRefAvailErr( 2 )
1057
1058    if( a1->type == OT_BLOCK )
1059    {
1060        blk = orBLOCK( a1 );
1061        wval = blk->values + a1->series.it;
1062        wend = blk->values + blk->used;
1063
1064        orRefPush( OT_BLOCK, a1->index );
1065    }
1066    else
1067    {
1068        orCopyV( &saveA1, *a1 );
1069        wval = &saveA1;
1070        wend = wval + 1;
1071
1072        orRefPush( OT_BLOCK, a1->word.context );
1073    }
1074
1075    orRefPush( a2->type, a2->index );
1076
1077    switch( a2->type )
1078    {
1079        case OT_BLOCK:
1080        case OT_PAREN:
1081        case OT_PATH:
1082        {
1083            blk = orBlockPtr( serN );
1084            send = blk->used;
1085
1086            while( si < send )
1087            {
1088                wit = wval;
1089                while( wit != wend )
1090                {
1091                    if( orIs(wit, OT_WORD) )
1092                    {
1093                        orWordVal( wit, cblk, val );
1094                        if( si < send )
1095                        {
1096                            orCopyV( val, blk->values[si] );
1097                        }
1098                        else
1099                        {
1100                            orSetTF( val, OT_NONE );
1101                        }
1102                        ++si;
1103                    }
1104                    ++wit;
1105                }
1106
1107                orEvalBlock( orBlockPtr( body ), bi );
1108                if( orErrorThrown )
1109                    goto error;
1110                blk = orBlockPtr( serN );          // Reacquire.
1111            }
1112        }
1113            break;
1114
1115        case OT_STRING:
1116        case OT_FILE:
1117        case OT_ISSUE:
1118        case OT_TAG:
1119        {
1120            OString* str = orStringPtr( serN );
1121            send = str->used;
1122
1123            while( si < send )
1124            {
1125                wit = wval;
1126                while( wit != wend )
1127                {
1128                    if( orIs(wit, OT_WORD) )
1129                    {
1130                        orWordVal( wit, cblk, val );
1131                        if( si < send )
1132                        {
1133                            orSetTF( val, OT_CHAR );
1134                            orInt(val) = str->charArray[ si ];
1135                        }
1136                        else
1137                        {
1138                            orSetTF( val, OT_NONE );
1139                        }
1140                        ++si;
1141                    }
1142                    ++wit;
1143                }
1144
1145                orEvalBlock( orBlockPtr( body ), bi );
1146                if( orErrorThrown )
1147                    goto error;
1148                str = orStringPtr( serN );     // Reacquire.
1149            }
1150        }
1151            break;
1152
1153        case OT_LIST:
1154            if( a1->type == OT_BLOCK )
1155            {
1156                orError( "foreach block! list! not implemented" );
1157                break;
1158            }
1159
1160            orResultNONE;
1161
1162            if( a2->series.it )
1163            {
1164                OValue* begin;
1165                OValue* it;
1166
1167                orWordVal( a1, cblk, wval );
1168
1169                blk = orBLOCK(a2);
1170                begin = blk->values;
1171                it    = begin + a2->series.it;
1172
1173                while( it->LIST_NEXT > 0 )
1174                {
1175                    orCopyV( wval, it[1] );
1176
1177                    orEvalBlock( orBlockPtr( body ), bi );
1178                    if( orErrorThrown )
1179                        goto error;
1180
1181                    //blk = orBlockPtr( serN );          // Reacquire.
1182                    //orListNextNode( blk, it );
1183                    it = begin + it->LIST_NEXT;
1184                }
1185            }
1186            break;
1187
1188#ifdef OR_CONFIG_NUMBER_ARRAYS
1189        // TODO
1190        case OT_DEC_ARRAY:
1191            break;
1192        case OT_INT_ARRAY:
1193            break;
1194#endif
1195    }
1196    goto pop;
1197
1198error:
1199
1200    if( orErrorIsType(OR_ERROR_BREAK) )
1201    {
1202        orErrorClear;
1203        orSetTF( a1, OT_UNSET );
1204    }
1205
1206pop:
1207
1208    orRefPop( 2 );
1209#endif
1210}
1211
1212
1213/*
1214  foreach: native [
1215    'variables   [word! block!]
1216    series       [series!]
1217    body         [block!]
1218  ]
1219*/
1220//see