Boron 2.1.0
cfunc.c
1/*
2 Copyright 2009-2023 Karl Robillard
3
4 This file is part of the Boron programming language.
5
6 Boron is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Boron is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with Boron. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20
21#ifdef CONFIG_HASHMAP
22#include "hashmap.h"
23#endif
24
25
40
41
42/*
43 Undocumented debug function.
44*/
45CFUNC(cfunc_nop)
46{
47 (void) ut;
48 (void) a1;
49#if 1
50 (void) res;
51#else
52 // Must set result to something; it may be uninitialized.
53 ur_setId(res, UT_UNSET);
54#endif
55 return UR_OK;
56}
57
58
59/*-cf-
60 quit
61 /return Returns value as exit status to operating system.
62 value Normally an int! between 0 and 255.
63 return: NA
64 group: control
65
66 Exit interpreter. The exit status will be 0 if the return value is not
67 specified.
68*/
69CFUNC(cfunc_quit)
70{
71 UIndex n = ut->stack.used;
72 res = ut->stack.ptr.cell + n;
73 if( CFUNC_OPTIONS & 1 )
74 {
75 *res = *a1;
76 }
77 else
78 {
79 ur_setId(res, UT_INT);
80 ur_int(res) = 0;
81 }
82 return boron_throwWord( ut, UR_ATOM_QUIT, n );
83}
84
85
86/*-cf-
87 halt
88 return: NA
89 group: control
90
91 Halt interpreter.
92*/
93CFUNC(cfunc_halt)
94{
95 (void) a1;
96 (void) res;
97 return boron_throwWord( ut, UR_ATOM_HALT, 0 );
98}
99
100
101/*-cf-
102 exit
103 return: NA
104 group: control
105 see: return
106
107 Exit from function with result unset.
108*/
109CFUNC(cfunc_exit)
110{
111 (void) a1;
112 ur_setId(res, UT_UNSET);
113 return boron_throwWord( ut, UR_ATOM_RETURN, 0 );
114}
115
116
117/*-cf-
118 return
119 result
120 return: NA
121 group: control
122 see: exit
123
124 Exit from function with result.
125*/
126CFUNC(cfunc_return)
127{
128 *res = *a1;
129 return boron_throwWord( ut, UR_ATOM_RETURN, 0 );
130}
131
132
133/*-cf-
134 break
135 return: NA
136 group: control
137 see: continue
138
139 Exit from loop, while, foreach, forall, forever, or map.
140*/
141CFUNC(cfunc_break)
142{
143 (void) a1;
144 (void) res;
145 return boron_throwWord( ut, UR_ATOM_BREAK, 0 );
146}
147
148
149/*-cf-
150 continue
151 return: NA
152 group: control
153 see: break
154
155 Start next iteration of loop, while, foreach, forall, forever, or map.
156*/
157CFUNC(cfunc_continue)
158{
159 (void) a1;
160 (void) res;
161 return boron_throwWord( ut, UR_ATOM_CONTINUE, 0 );
162}
163
164
165/*-cf-
166 throw
167 value
168 /name Give exception a name.
169 word word!
170 return: NA
171 group: control
172 see: catch, try
173
174 Stop evaluation of the current function and pass an exception up the
175 call stack.
176*/
177CFUNC(cfunc_throw)
178{
179 UCell* cell = ur_exception(ut);
180 (void) res;
181
182 if( CFUNC_OPTIONS & 1 )
183 {
184 *cell = a1[1]; // CFUNC_OPT_ARG(1)
185 // Place value after ur_exception word! on stack.
186 assert( cell == ut->stack.ptr.cell );
187 ur_binding(cell) = UR_BIND_STACK;
188 cell->word.index = 1;
189 ++cell;
190 }
191 *cell = *a1;
192 return UR_THROW;
193}
194
195
196/*-cf-
197 catch
198 body block! Code to evaluate.
199 /name Only catch exceptions with a certain name.
200 word word!/block! Names to catch.
201 return: Result of block evaluation or thrown value.
202 group: control
203 see: throw, try
204
205 Do body and return any exception thrown.
206*/
207static UStatus catch_catch(UThread* ut, EvalFrame* ef)
208{
209 const UCell* name;
210 UCell* cell = ur_exception(ut);
211
212 if (ef->invoke.userBuf)
213 {
214 if (! ur_is(cell, UT_WORD))
215 return UR_THROW;
216
217 name = ut->stack.ptr.cell + ef->invoke.userBuf;
218 if (ur_is(name, UT_WORD))
219 {
220 if (ur_atom(name) == ur_atom(cell))
221 {
222 ++cell;
223 goto set_result;
224 }
225 }
226 else if (ur_is(name, UT_BLOCK))
227 {
228 UBlockIt bi;
229 ur_blockIt(ut, &bi, name);
230 ur_foreach(bi)
231 {
232 if (ur_is(bi.it, UT_WORD) && ur_atom(bi.it) == ur_atom(cell))
233 {
234 ++cell;
235 goto set_result;
236 }
237 }
238 }
239 return UR_THROW;
240 }
241
242set_result:
243 *ef->invoke.result = *cell;
244 return UR_OK;
245}
246
247CFUNC(cfunc_catch)
248{
249 int origStack;
250 EvalFrame* ef = boron_reuseFrame(ut, 1, &origStack);
251 if (! ef)
252 return UR_THROW;
253
254 boron_initEvalCatch(ef, catch_catch, origStack, res);
255
256 if (CFUNC_OPTIONS & 1)
257 {
258 const UCell* name = a1 + 1; // CFUNC_OPT_ARG(1);
259 ef->invoke.userBuf = name - ut->stack.ptr.cell;
260 }
261 else
262 ef->invoke.userBuf = 0;
263
264 ++ef;
265 boron_initEvalBlock(ef, ut, a1->series.buf, res);
266 return CFUNC_REFRAMED;
267}
268
269
270/*-cf-
271 try
272 body block! Code to evaluate.
273 return: Result of block evaluation or error.
274 group: control
275 see: catch, throw
276
277 Do body and catch any thrown error!. Other thrown types are ignored
278 and will be passed up the call chain.
279*/
280static UStatus catch_try(UThread* ut, EvalFrame* ef)
281{
282 UCell* cell = ur_exception(ut);
283 if (! ur_is(cell, UT_ERROR))
284 return UR_THROW;
285 *ef->invoke.result = *cell;
286 return UR_OK;
287}
288
289CFUNC(cfunc_try)
290{
291 int origStack;
292 EvalFrame* ef = boron_reuseFrame(ut, 1, &origStack);
293 if (! ef)
294 return UR_THROW;
295
296 boron_initEvalCatch(ef, catch_try, origStack, res);
297 ++ef;
298 boron_initEvalBlock(ef, ut, a1->series.buf, res);
299 return CFUNC_REFRAMED;
300}
301
302
303/*-cf-
304 recycle
305 return: NA
306 group: storage
307
308 Run the garbage collector.
309*/
310CFUNC(cfunc_recycle)
311{
312 (void) a1;
313 (void) res;
314 ur_recycle( ut );
315 return UR_OK;
316}
317
318
319/*-cf-
320 set
321 words Any word type or block!/path!.
322 values Any value.
323 return: unset!
324 group: data
325 see: get, in, value?
326
327 Assign a value to one or more words.
328
329 set 'a 22
330 a
331 == 22
332
333 If words and values are both a block! then each word in words is set
334 to the corresponding value in values.
335
336 set [a b] [1 4.0]
337 a
338 == 1
339 b
340 == 4.0
341*/
342CFUNC(cfunc_set)
343{
344 UCell* cell;
345 if( ur_isWordType( ur_type(a1) ) )
346 {
347 if( ! (cell = ur_wordCellM(ut, a1)) )
348 return UR_THROW;
349 *cell = *a2;
350 }
351 else if( ur_is(a1, UT_PATH) )
352 {
353 if( ! ur_setPath( ut, a1, a2 ) )
354 return UR_THROW;
355 }
356 else if( ur_is(a1, UT_BLOCK) )
357 {
358 UBlockIterM bi;
359 ur_blkSliceM( ut, &bi, a1 );
360 if( ur_is(a2, UT_BLOCK) )
361 {
362 UBlockIt b2;
363 ur_blockIt( ut, &b2, a2 );
364 ur_foreach( bi )
365 {
366 if( ur_isWordType( ur_type(bi.it) ) )
367 {
368 if( ! (cell = ur_wordCellM(ut, bi.it)) )
369 return UR_THROW;
370 if( b2.it != b2.end )
371 *cell = *b2.it++;
372 else
373 ur_setId(cell, UT_NONE);
374 }
375 }
376 }
377 else
378 {
379 ur_foreach( bi )
380 {
381 if( ur_isWordType( ur_type(bi.it) ) )
382 {
383 if( ! (cell = ur_wordCellM(ut, bi.it)) )
384 return UR_THROW;
385 *cell = *a2;
386 }
387 }
388 }
389 }
390 else
391 {
392 return ur_error( ut, UR_ERR_TYPE, "set expected word!/block!/path!" );
393 }
394 ur_setId(res, UT_UNSET);
395 return UR_OK;
396}
397
398
399/*-cf-
400 get
401 word Any word type or context!
402 return: Value of word or block of values in context.
403 group: data
404 see: in, set
405*/
406CFUNC(cfunc_get)
407{
408 if( ur_isWordType( ur_type(a1) ) )
409 {
410 const UCell* cell;
411 if( ! (cell = ur_wordCell( ut, a1 )) )
412 return UR_THROW;
413 *res = *cell;
414 return UR_OK;
415 }
416 else if( ur_is(a1, UT_CONTEXT) )
417 {
418 UBuffer* blk = ur_makeBlockCell( ut, UT_BLOCK, 0, res );
419 const UBuffer* ctx = ur_bufferSer( a1 );
420 ur_blkAppendCells( blk, ctx->ptr.cell, ctx->used );
421 return UR_OK;
422 }
423 *res = *a1;
424 return UR_OK;
425}
426
427
428/*-cf-
429 value?
430 value Any value.
431 return: True unless value is an unset word.
432 group: data
433 see: get, set
434
435 Determine if a word has already been set.
436
437 value? 'blah
438 == false
439*/
440CFUNC(cfunc_valueQ)
441{
442 int logic = 1;
443 if( ur_isWordType( ur_type(a1) ) )
444 {
445 const UCell* cell;
446 if( ! (cell = ur_wordCell( ut, a1 )) )
447 return UR_THROW;
448 if( ur_is(cell, UT_UNSET) )
449 logic = 0;
450 }
451 ur_setId(res, UT_LOGIC);
452 ur_logic(res) = logic;
453 return UR_OK;
454}
455
456
457/*-cf-
458 in
459 context context!
460 word Any word type.
461 return: Word bound to context or none!.
462 group: data
463 see: set, value?
464*/
465CFUNC(cfunc_in)
466{
467 if( ur_isWordType( ur_type(a2) ) )
468 {
469 const UBuffer* ctx;
470 int wrdN;
471
472 if( ! (ctx = ur_sortedContext( ut, a1 )) )
473 return UR_THROW;
474 wrdN = ur_ctxLookup( ctx, ur_atom(a2) );
475 if( wrdN < 0 )
476 {
477 ur_setId(res, UT_NONE);
478 }
479 else
480 {
481 int ctxN = a1->series.buf;
482 *res = *a2;
483 ur_setBinding( res,
485 res->word.ctx = ctxN;
486 res->word.index = wrdN;
487 }
488 return UR_OK;
489 }
490 return errorType( "in expected word of type word!/lit-word!" );
491}
492
493
494#if 0
495/*
496 use
497 context context!
498 block block!
499 return: Last evaluated value.
500 group: eval
501
502 Bind block to context and evaluate it.
503*/
504CFUNC(cfunc_use)
505{
506 UBuffer* blk;
507 const UBuffer* ctx;
508 const UCell* bc = a2;
509
510 if( ur_is(a1, UT_CONTEXT) && ur_is(bc, UT_BLOCK) )
511 {
512 if( ! (blk = ur_bufferSerM(bc)) )
513 return UR_THROW;
514 if( ! (ctx = ur_sortedContext(ut, a1)) )
515 return UR_THROW;
516 ur_bind( ut, blk, ctx,
517 ur_isShared(a1->context.buf) ? UR_BIND_ENV : UR_BIND_THREAD );
518 return boron_doBlock( ut, bc, res );
519 }
520 return errorType( "use expected context! and block!" );
521}
522#endif
523
524
525extern void _contextWords( UThread* ut, const UBuffer* ctx, UIndex ctxN,
526 UCell* res );
527
528/*-cf-
529 words-of
530 context context!
531 return: Block of words defined in context.
532 group: data
533 see: values-of
534*/
535/*-cf-
536 values-of
537 context context!/hash-map!
538 return: Block of values defined in context or map.
539 group: data
540 see: words-of
541*/
542CFUNC(cfunc_words_of)
543{
544 const UBuffer* ctx;
545
546 if( ur_int(a2) )
547 {
548 // Return values.
549 if( ur_is(a1, UT_CONTEXT) )
550 {
551 const UCell* cell;
552 UBuffer* blk;
553 int used;
554
555 ctx = ur_bufferSer(a1);
556 // Save what we need from ctx before ur_makeBlockCell
557 // invalidates it.
558 cell = ctx->ptr.cell;
559 used = ctx->used;
560
561 blk = ur_makeBlockCell( ut, UT_BLOCK, used, res );
562 memCpy( blk->ptr.cell, cell, used * sizeof(UCell) );
563 blk->used = used;
564 }
565#ifdef CONFIG_HASHMAP
566 else //if( ur_is(a1, UT_HASHMAP) )
567 {
568 UBuffer* blk = ur_makeBlockCell( ut, UT_BLOCK, 0, res );
569 hashmap_values( ut, a1, blk );
570 }
571#endif
572 }
573 else
574 {
575 // Return words.
576 if( ur_is(a1, UT_CONTEXT) )
577 {
578 if( ! (ctx = ur_sortedContext( ut, a1 )) )
579 return UR_THROW;
580 _contextWords( ut, ctx, a1->context.buf, res );
581 }
582 }
583 return UR_OK;
584}
585
586
587/*-cf-
588 binding?
589 word word!/lit-word!/set-word!/get-word!/option!
590 return: context!/datatype!
591 group: data
592 see: bind, unbind
593
594 Get the context which a word is bound to.
595*/
596CFUNC(cfunc_bindingQ)
597{
598 if( ! ur_isWordType( ur_type(a1) ) )
599 return errorType( "binding? expected word type" );
600 switch( ur_binding(a1) )
601 {
602 case UR_BIND_THREAD:
603 case UR_BIND_ENV:
604 ur_setId( res, UT_CONTEXT );
605 ur_setSeries( res, a1->word.ctx, 0 );
606 break;
607
608 case BOR_BIND_FUNC:
609 ur_setId( res, UT_WORD );
610 ur_setWordUnbound( res, UT_FUNC );
611 break;
612
613 case BOR_BIND_OPTION:
614 ur_setId( res, UT_WORD );
615 ur_setWordUnbound( res, UT_OPTION );
616 break;
617
618 default:
619 ur_setId( res, UT_NONE );
620 break;
621 }
622 return UR_OK;
623}
624
625
626#define BIND_ERR_MSG "%s expected words argument of word!/block!"
627
628/*-cf-
629 bind
630 words word!/block!
631 context word!/context!
632 /secure Unbind any words not found in the context.
633 return: Bound words
634 group: data
635 see: binding?, unbind
636*/
637CFUNC(cfunc_bind)
638{
639#define OPT_BIND_SECURE 0x01
640 UIndex ctxN;
641 UBuffer* blk;
642 UCell* ctxArg = a2;
643 int bindType;
644
645 if( ur_is(ctxArg, UT_WORD) )
646 {
647 ctxN = ctxArg->word.ctx;
648 if( ctxN == UR_INVALID_BUF )
649 return ur_error( ut, UR_ERR_SCRIPT, "bind word '%s is unbound",
650 ur_wordCStr( ctxArg ) );
651 }
652 else if( ur_is(ctxArg, UT_CONTEXT) )
653 ctxN = ctxArg->series.buf;
654 else
655 return boron_badArg( ut, ur_type(ctxArg), 1 );
656
657 if( ur_isShared(ctxN) )
658 bindType = UR_BIND_ENV;
659 else if( CFUNC_OPTIONS & OPT_BIND_SECURE )
660 bindType = UR_BIND_SECURE;
661 else
662 bindType = UR_BIND_THREAD;
663
664 if( ur_is(a1, UT_BLOCK) )
665 {
666 const UBuffer* ctx;
667
668 if( ! (blk = ur_bufferSerM(a1)) )
669 return UR_THROW;
670 if( ! (ctx = ur_sortedContext(ut, ctxArg)) )
671 return UR_THROW;
672 ur_bind( ut, blk, ctx, bindType );
673 *res = *a1;
674 return UR_OK;
675 }
676 else if( ur_is(a1, UT_WORD) )
677 {
678 UBindTarget bt;
679
680 if( ! (bt.ctx = ur_sortedContext(ut, ctxArg)) )
681 return UR_THROW;
682 bt.ctxN = ctxN;
683 if( bindType == UR_BIND_SECURE )
684 {
686 bt.self = UR_BIND_SECURE;
687 }
688 else
689 {
690 bt.bindType = bindType;
691 bt.self = UR_INVALID_ATOM;
692 }
693
694 *res = *a1;
695 ur_bindCells( ut, res, res + 1, &bt );
696 return UR_OK;
697 }
698 return ur_error(ut, UR_ERR_TYPE, BIND_ERR_MSG, "bind" );
699}
700
701
702/*-cf-
703 unbind
704 words word!/block!
705 /deep If words is a block, unbind all sub-blocks.
706 return: Unbound words
707 group: data
708 see: bind, binding?
709*/
710CFUNC(cfunc_unbind)
711{
712#define OPT_UNBIND_DEEP 0x01
713 *res = *a1;
714 if( ur_is(a1, UT_BLOCK) )
715 {
716 UBlockIterM bi;
717 if( ! ur_blkSliceM( ut, &bi, a1 ) )
718 return UR_THROW;
719 ur_unbindCells( ut, bi.it, bi.end, CFUNC_OPTIONS & OPT_UNBIND_DEEP );
720 return UR_OK;
721 }
722 else if( ur_is(a1, UT_WORD) )
723 {
724 ur_unbindCells( ut, res, res + 1, 0 );
725 return UR_OK;
726 }
727 return ur_error(ut, UR_ERR_TYPE, BIND_ERR_MSG, "unbind" );
728}
729
730
731/*-cf-
732 infuse
733 block block!
734 context word!/context!
735 return: Modified block.
736 group: data
737 see: bind
738
739 Replace words with their value in context.
740*/
741CFUNC(cfunc_infuse)
742{
743 UIndex ctxN;
744 UBlockIterM bi;
745 UCell* ctxArg = a2;
746
747 if( ur_is(ctxArg, UT_WORD) )
748 {
749 ctxN = ctxArg->word.ctx;
750 if( ctxN == UR_INVALID_BUF )
751 return ur_error( ut, UR_ERR_SCRIPT, "infuse word '%s is unbound",
752 ur_wordCStr( ctxArg ) );
753 }
754 else if( ur_is(ctxArg, UT_CONTEXT) )
755 ctxN = ctxArg->series.buf;
756 else
757 return boron_badArg( ut, ur_type(ctxArg), 1 );
758
759 if( ! ur_blkSliceM( ut, &bi, a1 ) )
760 return UR_THROW;
761
762 ur_infuse( ut, bi.it, bi.end, ur_bufferSer(ctxArg) );
763 *res = *a1;
764 return UR_OK;
765}
766
767
768#define OPER_FUNC(name,OP) \
769CFUNC(name) { \
770 int t = ur_type(a1); \
771 if( t < ur_type(a2) ) \
772 t = ur_type(a2); \
773 return ut->types[ t ]->operate( ut, a1, a2, res, OP ); \
774}
775
776
777/*-cf-
778 add
779 a int!/double!/vec3!
780 b int!/double!/vec3!/block!
781 return: Sum of two numbers.
782 group: math
783 see: sub, mul, div
784
785 The second argument may be a block:
786 add 0 [4 1 3]
787 == 8
788*/
789/*-cf-
790 sub
791 a int!/double!/vec3!
792 b int!/double!/vec3!/block!
793 return: Difference of two numbers.
794 group: math
795 see: add, mul, div
796
797 The second argument may be a block:
798 sub 4 [3 1 3]
799 == -3
800*/
801/*-cf-
802 mul
803 a int!/double!/vec3!
804 b int!/double!/vec3!/block!
805 return: Product of two numbers.
806 group: math
807 see: add, sub, div
808
809 The second argument may be a block:
810 mul 3.3 [2 0.4]
811 == 2.64
812*/
813/*-cf-
814 div
815 a int!/double!/vec3!
816 b int!/double!/vec3!
817 return: Quotient of a divided by b.
818 group: math
819 see: add, sub, mul, mod
820*/
821/*-cf-
822 mod
823 a int!/double!/coord!
824 b int!/double!/coord!
825 return: Remainder of a divided by b.
826 group: math
827 see: div
828*/
829OPER_FUNC( cfunc_add, UR_OP_ADD )
830OPER_FUNC( cfunc_sub, UR_OP_SUB )
831OPER_FUNC( cfunc_mul, UR_OP_MUL )
832OPER_FUNC( cfunc_div, UR_OP_DIV )
833OPER_FUNC( cfunc_mod, UR_OP_MOD )
834
835
836/*-cf-
837 and
838 a logic!/char!/int!
839 b logic!/char!/int!/block!
840 return: Bitwise AND.
841 group: math
842 see: or, xor
843*/
844/*-cf-
845 or
846 a logic!/char!/int!
847 b logic!/char!/int!/block!
848 return: Bitwise OR.
849 group: math
850 see: and, xor
851*/
852/*-cf-
853 xor
854 a logic!/char!/int!
855 b logic!/char!/int!/block!
856 return: Bitwise exclusive OR.
857 group: math
858 see: and, or
859*/
860OPER_FUNC( cfunc_and, UR_OP_AND )
861OPER_FUNC( cfunc_or, UR_OP_OR )
862OPER_FUNC( cfunc_xor, UR_OP_XOR )
863
864
865/*-cf-
866 minimum
867 a
868 b
869 return: Lesser of two values.
870 group: math
871 see: maximum
872*/
873CFUNC(cfunc_minimum)
874{
875 *res = (ur_compare( ut, a1, a2 ) < 0) ? *a1 : *a2;
876 return UR_OK;
877}
878
879
880/*-cf-
881 maximum
882 a
883 b
884 return: Greater of two values.
885 group: math
886 see: minimum
887*/
888CFUNC(cfunc_maximum)
889{
890 *res = (ur_compare( ut, a1, a2 ) > 0) ? *a1 : *a2;
891 return UR_OK;
892}
893
894
895/*-cf-
896 abs
897 n int!/double!/time!
898 return: Absolute value of n.
899 group: math
900*/
901CFUNC(cfunc_abs)
902{
903 int type = ur_type(a1);
904 switch( type )
905 {
906 case UT_INT:
907 ur_setId(res, type);
908 ur_int(res) = llabs( ur_int(a1) );
909 break;
910
911 case UT_DOUBLE:
912 case UT_TIME:
913 ur_setId(res, type);
914 ur_double(res) = fabs( ur_double(a1) );
915 break;
916
917 default:
918 return boron_badArg( ut, ur_type(a1), 0 );
919 }
920 return UR_OK;
921}
922
923
924static int _mathFunc( const UCell* a1, UCell* res, double (*func)(double) )
925{
926 double n;
927 if( ur_is(a1, UT_DOUBLE) )
928 n = ur_double(a1);
929 else //if( ur_is(a1, UT_INT) )
930 n = (double) ur_int(a1);
931 ur_setId(res, UT_DOUBLE);
932 ur_double(res) = func( n );
933 return UR_OK;
934}
935
936
937/*-cf-
938 sqrt
939 n int!/double!
940 return: Square root of number.
941 group: math
942*/
943/*-cf-
944 cos
945 n int!/double!
946 return: Cosine of number.
947 group: math
948*/
949/*-cf-
950 sin
951 n int!/double!
952 return: Sine of number.
953 group: math
954*/
955CFUNC(cfunc_sqrt) { (void) ut; return _mathFunc( a1, res, sqrt ); }
956CFUNC(cfunc_cos) { (void) ut; return _mathFunc( a1, res, cos ); }
957CFUNC(cfunc_sin) { (void) ut; return _mathFunc( a1, res, sin ); }
958
959
960/*-cf-
961 atan
962 n int!/double!/coord!/vec3!
963 return: Arc tangent of number or x,y coordinate.
964 group: math
965*/
966CFUNC(cfunc_atan)
967{
968 double n, rise;
969 (void) ut;
970
971 switch( ur_type(a1) )
972 {
973 case UT_INT:
974 n = (double) ur_int(a1);
975 goto arctan1;
976 case UT_DOUBLE:
977 n = ur_double(a1);
978arctan1:
979 n = atan( n );
980 break;
981
982 case UT_COORD:
983 n = (double) a1->coord.n[0];
984 rise = (double) a1->coord.n[1];
985 goto arctan2;
986 case UT_VEC3:
987 n = a1->vec3.xyz[0];
988 rise = a1->vec3.xyz[1];
989arctan2:
990 n = atan2( rise, n );
991 break;
992 default:
993 n = 0.0;
994 break;
995 }
996 ur_setId(res, UT_DOUBLE);
997 ur_double(res) = n;
998 return UR_OK;
999}
1000
1001
1002/*-cf-
1003 make
1004 prototype datatype!/context!
1005 attributes
1006 return: New value.
1007 group: data
1008 see: construct, to-_type_
1009
1010 Create a new value.
1011*/
1012CFUNC(cfunc_make)
1013{
1014 if( ur_is(a1, UT_DATATYPE) )
1015 {
1016 int t = ur_datatype(a1);
1017 if( t < UT_MAX )
1018 return DT( t )->make( ut, a2, res );
1019 }
1020 else if( ur_is(a1, UT_CONTEXT) )
1021 {
1022 UBlockIterM bi;
1023 UBuffer* ctx = ur_ctxClone( ut, ur_bufferSer(a1), res );
1024 if( ur_is(a2, UT_BLOCK) )
1025 {
1026 if( ! ur_blkSliceM( ut, &bi, a2 ) )
1027 return UR_THROW;
1028 ur_ctxSetWords( ctx, bi.it, bi.end );
1029 ur_bind( ut, bi.buf, ur_ctxSort(ctx), UR_BIND_SELF );
1030 return boron_reframeDoBlock(ut, a2->series.buf, NULL, 0);
1031 }
1032 }
1033 return ur_error( ut, UR_ERR_TYPE,
1034 "make requires a context! or single datatype!" );
1035}
1036
1037
1038/*-cf-
1039 copy
1040 value
1041 /deep If value is a block, copy all sub-blocks.
1042 return: New value.
1043 group: data
1044*/
1045CFUNC(cfunc_copy)
1046{
1047#define OPT_COPY_DEEP 0x01
1048 int type = ur_type(a1);
1049
1050 if( ur_isBlockType( type ) && (CFUNC_OPTIONS & OPT_COPY_DEEP) )
1051 {
1052 *res = *a1;
1053 res->series.buf = ur_blkClone( ut, a1->series.buf );
1054 return UR_OK;
1055 }
1056 DT( type )->copy( ut, a1, res );
1057 return UR_OK;
1058}
1059
1060
1061/*-cf-
1062 reserve
1063 series
1064 size int! Number of elements to reserve.
1065 return: Series.
1066 group: data, storage
1067 see: free
1068
1069 Expand the capacity of series buffer. This cannot be used to make the
1070 buffer smaller.
1071*/
1072CFUNC(cfunc_reserve)
1073{
1074 UBuffer* buf;
1075 int size;
1076 int type = ur_type(a1);
1077
1078 if( ! ur_isSeriesType( type ) )
1079 return errorType( "reserve expected series" );
1080 if( ! (buf = ur_bufferSerM(a1)) )
1081 return UR_THROW;
1082
1083 size = a1->series.it + ur_int(a2);
1084#if 1
1085 switch( type )
1086 {
1087 case UT_BINARY:
1088 case UT_BITSET:
1089 ur_binReserve( buf, size );
1090 break;
1091
1092 case UT_BLOCK:
1093 case UT_PAREN:
1094 case UT_VECTOR:
1095 case UT_STRING:
1096 case UT_FILE:
1097 ur_arrReserve( buf, size );
1098 break;
1099 }
1100#else
1101 DT( type )->reserve( ut, a1, size );
1102#endif
1103 *res = *a1;
1104 return UR_OK;
1105}
1106
1107
1108/*-cf-
1109 does
1110 body block!
1111 return: func!
1112 group: eval
1113 see: func
1114
1115 Create a function which takes no arguments.
1116*/
1117CFUNC(cfunc_does)
1118{
1119 // Must copy block to preserve bindings when the same function defintion
1120 // is used in multiple contexts.
1121 UIndex bodyN = ur_blkClone( ut, a1->series.buf ); // gc!
1122 ur_setId(res, UT_FUNC);
1123 ur_setSeries(res, bodyN, 0);
1124 return UR_OK;
1125}
1126
1127
1128void boron_compileArgProgram( BoronThread*, const UCell* specC, UBuffer* prog,
1129 UIndex bodyN, int* sigFlags );
1130//#define DUMP_ARG_PROG
1131#ifdef DUMP_ARG_PROG
1132void boron_argProgramToStr(UThread*, const void* prog, UBuffer* str);
1133#endif
1134
1135/*-cf-
1136 func
1137 spec block!
1138 body block!
1139 return: func!
1140 group: eval
1141 see: does
1142
1143 Create function.
1144*/
1145CFUNC(cfunc_func)
1146{
1147 static const uint8_t _types[2] = {UT_BLOCK, UT_BINARY};
1148 const UBuffer* body;
1149 UBuffer* prog;
1150 UBuffer* blk;
1151 UIndex bufN[2];
1152 int sigFlags;
1153 const int prelude = 2; // arg-program & signature cells.
1154
1155 // UT_FUNC references a block which holds the argument program cell
1156 // followed by a copy of the function body.
1157
1158 blk = ur_generate( ut, 2, bufN, _types ); // gc!
1159 //printf( "KR func blk:%d\n", bufN[0] );
1160
1161 body = ur_bufferSer(a2);
1162 ur_arrReserve( blk, prelude + body->used );
1163 ur_initSeries(blk->ptr.cell, UT_BINARY, bufN[1]);
1164 blk->ptr.cell[1] = *a1;
1165 memcpy( blk->ptr.cell + prelude, body->ptr.cell,
1166 sizeof(UCell) * body->used );
1167 blk->used += prelude + body->used;
1168
1169 prog = ur_buffer(bufN[1]);
1170 //prog->storage |= UR_BUF_PROTECT;
1171 boron_compileArgProgram( BT, a1, prog, bufN[0], &sigFlags );
1172
1173#ifdef DUMP_ARG_PROG
1174 {
1175 UBuffer str;
1176 ur_strInit(&str, UR_ENC_LATIN1, 40);
1177 boron_argProgramToStr(ut, prog->ptr.b, &str);
1178 ur_strTermNull(&str);
1179 printf("KR func %s\n", str.ptr.c);
1180 ur_strFree(&str);
1181 }
1182#endif
1183
1184 // Slice to skip arg-program.
1185 ur_setId(res, UT_FUNC);
1186 ur_setSeries(res, bufN[0], prelude);
1187 if( sigFlags )
1188 ur_setFlags(res, FUNC_FLAG_NOTRACE);
1189 return UR_OK;
1190}
1191
1192
1193/*-cf-
1194 not
1195 value
1196 return: Inverse logic! of value.
1197 group: data
1198 see: complement, negate
1199*/
1200CFUNC(cfunc_not)
1201{
1202 (void) ut;
1203 ur_setId(res, UT_LOGIC);
1204 if( ! ur_true(a1) )
1205 ur_logic(res) = 1;
1206 return UR_OK;
1207}
1208
1209
1210/*-cf-
1211 if
1212 test Test condition.
1213 body Value or block to evaluate when true.
1214 return: Result of body if test is true, or none! when it is false.
1215 group: control
1216 see: either, ifn, while
1217
1218 Conditionally evaluate code.
1219*/
1220CFUNC(cfunc_if)
1221{
1222 if( ur_true(a1) )
1223 {
1224 UCell* bc = a2;
1225 if( ur_is(bc, UT_BLOCK) )
1226 return boron_reframeDoBlock( ut, bc->series.buf, res,
1227 FUNC_FLAG_NOTRACE );
1228 *res = *bc;
1229 return UR_OK;
1230 }
1231 ur_setId(res, UT_NONE);
1232 return UR_OK;
1233}
1234
1235
1236/*-cf-
1237 ifn
1238 test Test condition.
1239 body Value or block to evaluate when false.
1240 return: Result of body if test is false, or none! when it is true.
1241 group: control
1242 see: either, if
1243
1244 This is shorthand for "if not test body".
1245*/
1246CFUNC(cfunc_ifn)
1247{
1248 if( ! ur_true(a1) )
1249 {
1250 UCell* bc = a2;
1251 if( ur_is(bc, UT_BLOCK) )
1252 return boron_reframeDoBlock( ut, bc->series.buf, res,
1253 FUNC_FLAG_NOTRACE );
1254 *res = *bc;
1255 return UR_OK;
1256 }
1257 ur_setId(res, UT_NONE);
1258 return UR_OK;
1259}
1260
1261
1262/*-cf-
1263 either
1264 test Test condition.
1265 body-t Value or block to evaluate when true.
1266 body-f Value or block to evaluate when false.
1267 return: result of body-t if exp is true, or body-f if it is false.
1268 group: control
1269 see: if, ifn
1270*/
1271CFUNC(cfunc_either)
1272{
1273 UCell* bc = ur_true(a1) ? a2 : a3;
1274 if( ur_is(bc, UT_BLOCK) )
1275 return boron_reframeDoBlock( ut, bc->series.buf, res,
1276 FUNC_FLAG_NOTRACE );
1277 *res = *bc;
1278 return UR_OK;
1279}
1280
1281
1282// boron_catchWord of both break & continue.
1283#define CATCH_BREAK_CONTINUE(cell) \
1284 cell = ur_exception(ut); \
1285 if( ur_is(cell, UT_WORD) ) { \
1286 if( ur_atom(cell) == UR_ATOM_BREAK ) \
1287 break; \
1288 if( ur_atom(cell) == UR_ATOM_CONTINUE ) \
1289 continue; \
1290 }
1291
1292static UStatus catch_breakContinue(UThread* ut, EvalFrame* ef)
1293{
1294 const UCell* cell = ur_exception(ut);
1295 if (ur_is(cell, UT_WORD)) {
1296 if (ur_atom(cell) == UR_ATOM_BREAK)
1297 return UR_OK;
1298 if (ur_atom(cell) == UR_ATOM_CONTINUE)
1299 return boron_resetEvalFrame(ut, ef + 1);
1300 }
1301 return UR_THROW;
1302}
1303
1304
1305/*-cf-
1306 while
1307 exp block! Test condition.
1308 body block! Code to evaluate.
1309 return: false
1310 group: control
1311 see: forever, if, loop, break
1312
1313 Repeat body as long as exp is true.
1314*/
1315static UStatus _whileLoop(UThread* ut, EvalFrameInvoke* invoke)
1316{
1317 EvalFrame* ef;
1318 int n;
1319 if (invoke->userBuf)
1320 {
1321 invoke->userBuf = 0;
1322 if (! ur_true(invoke->result))
1323 return UR_OK;
1324 n = -2; // Do body
1325 }
1326 else
1327 {
1328 invoke->userBuf = 1;
1329 n = -3; // Do expression
1330 }
1331 ef = boron_pushEvalFrame(ut);
1332 ef->block = ef[n].block;
1333 return CFUNC_REFRAMED;
1334}
1335
1336CFUNC(cfunc_while)
1337{
1338 int origStack;
1339 EvalFrame* ef = boron_reuseFrame(ut, 3, &origStack);
1340 if (! ef)
1341 return UR_THROW;
1342 boron_initEvalBlock(ef, ut, a1->series.buf, res);
1343
1344 ++ef;
1345 boron_initEvalBlock(ef, ut, a2->series.buf, res);
1346
1347 ++ef;
1348 ef->invoke.eop = EOP_INVOKE_LOOP;
1349 ef->invoke.state = 3; // opCount
1350 ef->invoke.origStack = origStack;
1351 ef->invoke.userBuf = 1;
1352 ef->invoke.func = _whileLoop;
1353 ef->invoke.dat.catchf = catch_breakContinue;
1354 ef->invoke.result = res;
1355
1356 ++ef;
1357 ef->block = ef[-3].block;
1358 return CFUNC_REFRAMED;
1359}
1360
1361
1362/*-cf-
1363 forever
1364 body block! Code to evaluate.
1365 return: Result of body.
1366 group: control
1367 see: loop, while, break, continue
1368
1369 Repeat body until break or exception thrown.
1370*/
1371static UStatus _foreverLoop(UThread* ut, EvalFrameInvoke* invoke)
1372{
1373 (void) invoke;
1374 EvalFrame* ef = boron_pushEvalFrame(ut);
1375 ef->block = ef[-2].block;
1376 return CFUNC_REFRAMED;
1377}
1378
1379CFUNC(cfunc_forever)
1380{
1381 int origStack;
1382 EvalFrame* ef = boron_reuseFrame(ut, 1, &origStack);
1383 if (! ef)
1384 return UR_THROW;
1385 boron_initEvalBlock(ef, ut, a1->series.buf, res);
1386
1387 ++ef;
1388 ef->invoke.eop = EOP_INVOKE_LOOP;
1389 ef->invoke.state = 2; // opCount
1390 ef->invoke.origStack = origStack;
1391 //ef->invoke.userBuf = 0;
1392 ef->invoke.func = _foreverLoop;
1393 ef->invoke.dat.catchf = catch_breakContinue;
1394 //ef->invoke.result = res;
1395
1396 return CFUNC_REFRAMED;
1397}
1398
1399
1400/*-cf-
1401 loop
1402 range int!/block!
1403 body block!
1404 return: Result of body.
1405 group: control
1406 see: forever, while, break, continue
1407
1408 Use 'break in the body to terminate the loop.
1409 Use 'continue to immediately start the next iteration.
1410*/
1411static UStatus _loopNLoop(UThread* ut, EvalFrameInvoke* invoke)
1412{
1413 EvalFrame* ef;
1414
1415 if (--invoke->userBuf == 0)
1416 return UR_OK;
1417
1418 ef = boron_pushEvalFrame(ut);
1419 ef->block = ef[-2].block;
1420 return CFUNC_REFRAMED;
1421}
1422
1423static UStatus _loopRangeLoop(UThread* ut, EvalFrameInvoke* invoke)
1424{
1425 EvalFrame* ef = ((EvalFrame*) invoke) - 1;
1426 int32_t* n = (int32_t*) &ef->block.codeBlk;
1427 const UCell* cword = ef->block.result;
1428
1429 // Zero userBuf indicates first call.
1430 if (invoke->userBuf)
1431 n[0] += n[2];
1432 else
1433 invoke->userBuf = 1;
1434
1435 if (n[0] > n[1])
1436 return UR_OK;
1437
1438 if (cword)
1439 {
1440 UCell* counter = ur_wordCellM(ut, cword);
1441 if (! counter)
1442 return UR_THROW;
1443 ur_setId(counter, UT_INT);
1444 ur_int(counter) = n[0];
1445 }
1446
1447 ef = boron_pushEvalFrame(ut);
1448 ef->block = ef[-3].block;
1449 return CFUNC_REFRAMED;
1450}
1451
1452CFUNC(cfunc_loop)
1453{
1454 EvalFrame* ef;
1455 int origStack;
1456
1457 if (ur_is(a1, UT_INT))
1458 {
1459 if (ur_int(a1) < 1)
1460 return UR_OK;
1461
1462 ef = boron_reuseFrame(ut, 2, &origStack);
1463 if (! ef)
1464 return UR_THROW;
1465 boron_initEvalBlock(ef, ut, a2->series.buf, res);
1466
1467 ++ef;
1468 ef->invoke.eop = EOP_INVOKE_LOOP;
1469 ef->invoke.state = 2; // opCount
1470 ef->invoke.origStack = origStack;
1471 ef->invoke.userBuf = (UIndex) ur_int(a1);
1472 ef->invoke.func = _loopNLoop;
1473 ef->invoke.dat.catchf = catch_breakContinue;
1474 ef->invoke.result = res;
1475
1476 ++ef;
1477 ef->block = ef[-2].block;
1478 }
1479 else
1480 {
1481 const UCell* cword = NULL;
1482 int32_t* n; // First, last & increment.
1483 UBlockIt bi;
1484 int state = 0;
1485
1486 ef = boron_reuseFrame(ut, 2, &origStack);
1487 if (! ef)
1488 return UR_THROW;
1489 boron_initEvalBlock(ef, ut, a2->series.buf, res);
1490
1491 ++ef;
1492 ef->block.eop = EOP_NOP;
1493 //ef->block.result = cword;
1494 n = (int32_t*) &ef->block.codeBlk;
1495
1496 ++ef;
1497 ef->invoke.eop = EOP_INVOKE_LOOP;
1498 ef->invoke.state = 3; // opCount
1499 ef->invoke.origStack = origStack;
1500 ef->invoke.userBuf = 0; // Indicates first call.
1501 ef->invoke.func = _loopRangeLoop;
1502 ef->invoke.dat.catchf = catch_breakContinue;
1503 ef->invoke.result = res;
1504
1505 // Initialize loop range.
1506 n[0] = n[2] = 1;
1507 n[1] = 0;
1508
1509 ur_blockIt( ut, &bi, a1 );
1510 ur_foreach( bi )
1511 {
1512 if( ur_is(bi.it, UT_WORD ) )
1513 {
1514 cword = bi.it;
1515 }
1516 else if( ur_is(bi.it, UT_INT ) )
1517 {
1518 if( state < 3 )
1519 n[ state++ ] = ur_int(bi.it);
1520 }
1521 else
1522 errorType( "loop range values must be word!/int!" );
1523 }
1524 if( state == 1 )
1525 {
1526 n[1] = n[0];
1527 n[0] = 1;
1528 }
1529
1530 ef[-1].block.result = (UCell*) cword;
1531 }
1532 return CFUNC_REFRAMED;
1533}
1534
1535
1536/*-cf-
1537 select
1538 series
1539 match
1540 /last Search from end of series.
1541 /case Case of characters in strings must match
1542 return: Value after match or none! if match not found.
1543 group: data
1544*/
1545CFUNC(cfunc_select)
1546{
1547#define OPT_SELECT_LAST 0x01
1548#define OPT_SELECT_CASE 0x02
1549 USeriesIter si;
1550 const USeriesType* dt;
1551 int type = ur_type(a1);
1552 int n = 0;
1553
1554 if( ! ur_isSeriesType( type ) )
1555 return boron_badArg( ut, ur_type(a1), 0 );
1556
1557 if( CFUNC_OPTIONS & OPT_SELECT_LAST )
1558 n |= UR_FIND_LAST;
1559 if( CFUNC_OPTIONS & OPT_SELECT_CASE )
1560 n |= UR_FIND_CASE;
1561
1562 ur_seriesSlice( ut, &si, a1 );
1563 dt = SERIES_DT( type );
1564 n = dt->find( ut, &si, a2, n );
1565 ++n;
1566 if( n > 0 && n < si.end )
1567 dt->pick( si.buf, n, res );
1568 else
1569 ur_setId(res, UT_NONE);
1570 return UR_OK;
1571}
1572
1573
1574/*-cf-
1575 switch
1576 value
1577 options block!
1578 return: Result of selected switch case.
1579 group: control
1580
1581 If the size of the options block is odd, then the last value will be
1582 the default result.
1583*/
1584CFUNC(cfunc_switch)
1585{
1586 UBlockIt bi;
1587 const UCell* found = 0;
1588
1589 ur_blockIt( ut, &bi, a2 );
1590 if( (bi.end - bi.it) & 1 )
1591 found = --bi.end; // Default option
1592
1593 for( ; bi.it != bi.end; bi.it += 2 )
1594 {
1595 if( ur_equal( ut, a1, bi.it ) )
1596 {
1597 found = bi.it + 1;
1598 break;
1599 }
1600 }
1601
1602 if( found )
1603 {
1604 if( ur_is(found, UT_BLOCK) || ur_is(found, UT_PAREN) )
1605 return boron_reframeDoBlock( ut, found->series.buf, res, 0 );
1606
1607 *res = *found;
1608 return UR_OK;
1609 }
1610
1611 ur_setId(res, UT_NONE);
1612 return UR_OK;
1613}
1614
1615
1616/*-cf-
1617 case
1618 options block!
1619 return: Result of value following the first true case.
1620 group: control
1621*/
1622static UStatus eval_case(UThread* ut, EvalFrameInvoke* invoke)
1623{
1624 EvalFrame* ef;
1625 const UCell* it;
1626 UCell* res = invoke->result;
1627
1628 if (invoke->state == DO_BLOCK1_COMPLETE)
1629 {
1630 ur_setId(res, UT_NONE);
1631 return UR_OK;
1632 }
1633
1634 ef = ((EvalFrame*) invoke) + 1;
1635 if (ur_true(res))
1636 {
1637 boron_breakDoBlock1(ut, invoke);
1638
1639 it = ef->block.it;
1640 if (it == ef->block.end)
1641 {
1642 ur_setId(res, UT_NONE);
1643 }
1644 else if (ur_is(it, UT_BLOCK) || ur_is(it, UT_PAREN))
1645 {
1646 ef = boron_pushEvalFrame(ut);
1647 boron_initEvalBlock(ef, ut, it->series.buf, res);
1648 }
1649 else
1650 {
1651 *res = *it;
1652 }
1653 return CFUNC_REFRAMED;
1654 }
1655
1656 ++ef->block.it;
1657 return UR_OK;
1658}
1659
1660CFUNC(cfunc_case)
1661{
1662 return boron_reframeDoBlock1(ut, a1->series.buf, eval_case, res);
1663}
1664
1665
1666extern void coord_pick( const UCell* cell, int index, UCell* res );
1667extern void vec3_pick ( const UCell* cell, int index, UCell* res );
1668extern void path_pick ( UThread*, const UCell* cell, int index, UCell* res );
1669
1670
1671/*-cf-
1672 first
1673 series series/coord!/vec3!
1674 return: First item in series or none!.
1675 group: series
1676 see: last, second, third
1677*/
1678CFUNC(cfunc_first)
1679{
1680 int type = ur_type(a1);
1681 if( ur_isSeriesType( type ) )
1682 SERIES_DT( type )->pick( ur_bufferSer(a1), a1->series.it, res );
1683 else if( type == UT_COORD )
1684 coord_pick( a1, 0, res );
1685 else if( type == UT_VEC3 )
1686 vec3_pick( a1, 0, res );
1687 else if( ur_isPathType( type ) )
1688 path_pick( ut, a1, 0, res );
1689 else
1690 return boron_badArg( ut, type, 0 );
1691 return UR_OK;
1692}
1693
1694
1695/*-cf-
1696 second
1697 series series/coord!/vec3!
1698 return: Second item in series or none!.
1699 group: series
1700 see: first, third
1701*/
1702/*-cf-
1703 third
1704 series series/coord!/vec3!
1705 return: Third item in series or none!.
1706 group: series
1707 see: first, second
1708*/
1709CFUNC(cfunc_second)
1710{
1711 int type = ur_type(a1);
1712 int n = ur_int(a2);
1713 if( ur_isSeriesType( type ) )
1714 SERIES_DT( type )->pick( ur_bufferSer(a1), a1->series.it + n, res );
1715 else if( type == UT_COORD )
1716 coord_pick( a1, n, res );
1717 else if( type == UT_VEC3 )
1718 vec3_pick( a1, n, res );
1719 else if( ur_isPathType( type ) )
1720 path_pick( ut, a1, n, res );
1721 else
1722 return boron_badArg( ut, type, 0 );
1723 return UR_OK;
1724}
1725
1726
1727/*-cf-
1728 last
1729 series
1730 return: Last item in series or none! if empty.
1731 group: series
1732 see: first
1733*/
1734CFUNC(cfunc_last)
1735{
1736 USeriesIter si;
1737 int type = ur_type(a1);
1738 if( ! ur_isSeriesType( type ) )
1739 return boron_badArg( ut, type, 0 );
1740 ur_seriesSlice( ut, &si, a1 );
1741 if( si.it == si.end )
1742 ur_setId(res, UT_NONE);
1743 else
1744 SERIES_DT( type )->pick( ur_bufferSer(a1), si.end - 1, res );
1745 return UR_OK;
1746}
1747
1748
1749/*-cf-
1750 ++
1751 'word word!
1752 return: Value before increment.
1753 group: series
1754
1755 Increments series or number bound to word.
1756*/
1757CFUNC(cfunc_2plus)
1758{
1759 UCell* cell;
1760
1761 if( ! ur_is(a1, UT_WORD) )
1762 goto bad_arg;
1763
1764 if( ! (cell = ur_wordCellM(ut, a1)) )
1765 return UR_THROW;
1766 *res = *cell;
1767
1768 if( ur_isSeriesType( ur_type(cell) ) )
1769 {
1770 if( cell->series.it < boron_seriesEnd(ut, cell) )
1771 ++cell->series.it;
1772 }
1773 else if( ur_is(cell, UT_INT) )
1774 ur_int(cell) += 1;
1775 else if( ur_is(cell, UT_DOUBLE) )
1776 ur_double(cell) += 1.0;
1777 else
1778 goto bad_arg;
1779 return UR_OK;
1780
1781bad_arg:
1782 return boron_badArg( ut, ur_type(a1), 0 );
1783}
1784
1785
1786/*-cf-
1787 --
1788 'word word!
1789 return: Value before decrement.
1790 group: series
1791
1792 Decrements series or number bound to word.
1793*/
1794CFUNC(cfunc_2minus)
1795{
1796 UCell* cell;
1797
1798 if( ! ur_is(a1, UT_WORD) )
1799 goto bad_arg;
1800
1801 if( ! (cell = ur_wordCellM(ut, a1)) )
1802 return UR_THROW;
1803 *res = *cell;
1804
1805 if( ur_isSeriesType( ur_type(cell) ) )
1806 {
1807 if( cell->series.it > 0 )
1808 --cell->series.it;
1809 }
1810 else if( ur_is(cell, UT_INT) )
1811 ur_int(cell) -= 1;
1812 else if( ur_is(cell, UT_DOUBLE) )
1813 ur_double(cell) -= 1.0;
1814 else
1815 goto bad_arg;
1816 return UR_OK;
1817
1818bad_arg:
1819 return boron_badArg( ut, ur_type(a1), 0 );
1820}
1821
1822
1823#if 0
1824/*- cf -
1825 rot
1826 value int!/coord!/vec3! or series
1827 step int!
1828 return: Rotated value.
1829 group: series
1830
1831 Rotate components of value.
1832 If step is positive then components are moved up and those at the end
1833 are moved to the start.
1834 If step is negative then components are moved dowan and those at the start
1835 are moved to the end.
1836 For integers this does a bitwise shift (positive step is left).
1837*/
1838/*
1839 rot cycle shift
1840*/
1841CFUNC(cfunc_rot)
1842{
1843 static const uint8_t _coordRotOff[] = { 0,0,2,5,9,14,20 };
1844 static const uint8_t _coordRot[] = {
1845 0,1,
1846 0,1,2,
1847 0,1,2,3,
1848 0,1,2,3,4,
1849 0,1,2,3,4,5,0,1,2,3,4,5
1850 };
1851 int rot;
1852 int type = ur_type(a1);
1853
1854 if( ! ur_is(a2, UT_INT) )
1855 return ur_error( ut, UR_ERR_TYPE, "rot expected int! step" );
1856 rot = ur_int(a2);
1857
1858 if( ur_isSeriesType( type ) )
1859 {
1860 UIndex size;
1861
1862 *res = *a1;
1863 size = boron_seriesEnd(ut, res);
1864 if( rot > 0 )
1865 res->series.it = (res->series.it + rot) % size;
1866 else
1867 res->series.it = (res->series.it + rot) % size;
1868 }
1869 else if( type == UT_INT )
1870 {
1871 //ur_setId(res, UT_INT);
1872 res->id = a1->id; // Preserve hex flag.
1873 if( rot >= 0 )
1874 ur_int(res) = ur_int(a1) << rot;
1875 else
1876 ur_int(res) = ur_int(a1) >> -rot;
1877 }
1878 else if( type == UT_COORD )
1879 {
1880 int len = a1->coord.len;
1881 const uint8_t* index;
1882 int i;
1883
1884 rot %= len;
1885 if( rot < 0 )
1886 rot += len;
1887 index = _coordRot + _coordRotOff[len] - rot;
1888
1889 ur_setId(res, UT_COORD);
1890 res->coord.len = len;
1891 for( i = 0; i < len; ++i )
1892 res->coord.n[i] = a1->coord.n[ *index++ ];
1893 }
1894 else if( type == UT_VEC3 )
1895 {
1896 int i;
1897 const uint8_t* index;
1898
1899 rot %= 3;
1900 if( rot < 0 )
1901 rot += 3;
1902 index = _coordRot + 5 - rot;
1903
1904 ur_setId(res, UT_VEC3);
1905 for( i = 0; i < 3; ++i )
1906 res->vec3.xyz[i] = a1->vec3.xyz[ *index++ ];
1907 }
1908 else
1909 return ur_error( ut, UR_ERR_TYPE,
1910 "rotd expected int!/coord!/vec3! or series" );
1911
1912 return UR_OK;
1913}
1914#endif
1915
1916
1917/*-cf-
1918 prev
1919 series
1920 return: Previous element of series or the head.
1921 group: series
1922 see: head, next, skip
1923*/
1924CFUNC(cfunc_prev)
1925{
1926 int type = ur_type(a1);
1927 if( ! ur_isSeriesType( type ) )
1928 return boron_badArg( ut, type, 0 );
1929 *res = *a1;
1930 if( res->series.it > 0 )
1931 --res->series.it;
1932 return UR_OK;
1933}
1934
1935
1936/*-cf-
1937 next
1938 series
1939 return: Next element of series or the tail.
1940 group: series
1941 see: prev, tail, skip
1942*/
1943CFUNC(cfunc_next)
1944{
1945 int type = ur_type(a1);
1946 if( ! ur_isSeriesType( type ) )
1947 return boron_badArg( ut, type, 0 );
1948 *res = *a1;
1949 if( res->series.it < boron_seriesEnd(ut, res) )
1950 ++res->series.it;
1951 return UR_OK;
1952}
1953
1954
1955static int positionPort( UThread* ut, const UCell* portC, int where )
1956{
1957 UCell tmp;
1958 PORT_SITE(dev, pbuf, portC);
1959 if( ! dev )
1960 return errorScript( "port is closed" );
1961 ur_setId( &tmp, UT_INT );
1962 ur_int( &tmp ) = 0;
1963 return dev->seek( ut, pbuf, &tmp, where );
1964}
1965
1966
1967/*-cf-
1968 head
1969 series Series or port!
1970 return: Start of series.
1971 group: series
1972 see: head?, skip, tail
1973
1974 For seekable ports, head re-positions it to the start.
1975*/
1976CFUNC(cfunc_head)
1977{
1978 int type = ur_type(a1);
1979 *res = *a1;
1980 if( ur_isSeriesType( type ) )
1981 {
1982 res->series.it = 0;
1983 return UR_OK;
1984 }
1985 else if( type == UT_PORT )
1986 {
1987 return positionPort( ut, a1, UR_PORT_HEAD );
1988 }
1989 return boron_badArg( ut, type, 0 );
1990}
1991
1992
1993/*-cf-
1994 tail
1995 series Series or port!
1996 return: End of series.
1997 group: series
1998 see: head, skip, tail?
1999
2000 For seekable ports, tail re-positions it to the end.
2001*/
2002CFUNC(cfunc_tail)
2003{
2004 int type = ur_type(a1);
2005 *res = *a1;
2006 if( ur_isSeriesType( type ) )
2007 {
2008 res->series.it = boron_seriesEnd(ut, res);
2009 return UR_OK;
2010 }
2011 else if( type == UT_PORT )
2012 {
2013 return positionPort( ut, a1, UR_PORT_TAIL );
2014 }
2015 return boron_badArg( ut, type, 0 );
2016}
2017
2018
2019/*-cf-
2020 pick
2021 series Series or coord!/vec3!/hash-map!
2022 position char!/int!/logic! (or key value for hash-map!)
2023 return: Value at position or none! if position is out of range.
2024 group: series
2025 see: index?, poke
2026
2027 Note that series use one-based indexing.
2028
2029 If position is a logic! value, then true will return the first series
2030 value, and false the second.
2031
2032 A char! position can only be used with a bitset!.
2033*/
2034CFUNC(cfunc_pick)
2035{
2036 UCell* c2 = a1 + 1;
2037 UIndex n;
2038 int type = ur_type(a1);
2039
2040#ifdef CONFIG_HASHMAP
2041 if( type == UT_HASHMAP )
2042 {
2043 const UCell* cell = hashmap_select( ut, a1, c2, res );
2044 if( ! cell )
2045 return UR_THROW;
2046 if( cell != res )
2047 *res = *cell;
2048 return UR_OK;
2049 }
2050#endif
2051
2052 if( ur_is(c2, UT_INT) )
2053 {
2054 n = ur_int(c2);
2055 if( n > 0 )
2056 --n;
2057 else if( ! n )
2058 {
2059 ur_setId(res, UT_NONE);
2060 return UR_OK;
2061 }
2062 }
2063 else if( ur_is(c2, UT_LOGIC) )
2064 n = ur_logic(c2) ? 0 : 1;
2065 else if( ur_is(c2, UT_CHAR) && type == UT_BITSET )
2066 n = ur_char(c2);
2067 else
2068 return boron_badArg( ut, ur_type(c2), 1 );
2069
2070 if( ur_isSeriesType( type ) )
2071 SERIES_DT( type )->pick( ur_bufferSer(a1), a1->series.it + n, res );
2072 else if( type == UT_VEC3 )
2073 vec3_pick( a1, n, res );
2074 else if( type == UT_COORD )
2075 coord_pick( a1, n, res );
2076 else
2077 return boron_badArg( ut, type, 0 );
2078 return UR_OK;
2079}
2080
2081
2082extern int coord_poke( UThread*, UCell* cell, int index, const UCell* src );
2083extern int vec3_poke ( UThread*, UCell* cell, int index, const UCell* src );
2084
2085/*-cf-
2086 poke
2087 series series/coord!/vec3!/hash-map!
2088 position char!/int!/logic!
2089 value
2090 return: series.
2091 group: series
2092 see: index?, pick
2093
2094 Note that series use one-based indexing.
2095
2096 If position is a logic! value, then true will set the first series
2097 value, and false the second.
2098
2099 A char! position can only be used with a bitset!.
2100*/
2101CFUNC(cfunc_poke)
2102{
2103 UCell* c2 = a1 + 1;
2104 UBuffer* buf;
2105 UIndex n;
2106 int type = ur_type(a1);
2107
2108#ifdef CONFIG_HASHMAP
2109 if( type == UT_HASHMAP )
2110 {
2111 if( hashmap_insert( ut, a1, c2, a3 ) )
2112 {
2113 *res = *a1;
2114 return UR_OK;
2115 }
2116 return UR_THROW;
2117 }
2118#endif
2119
2120 if( ur_is(c2, UT_INT) )
2121 {
2122 n = ur_int(c2);
2123 if( n > 0 )
2124 --n;
2125 else if( ! n )
2126 return errorScript( "poke position out of range" );
2127 }
2128 else if( ur_is(c2, UT_LOGIC) )
2129 n = ur_logic(c2) ? 0 : 1;
2130 else if( ur_is(c2, UT_CHAR) && type == UT_BITSET )
2131 n = ur_char(c2);
2132 else
2133 return boron_badArg( ut, ur_type(c2), 1 );
2134
2135 *res = *a1;
2136 if( ur_isSeriesType( type ) )
2137 {
2138 if( ! (buf = ur_bufferSerM(a1)) )
2139 return UR_THROW;
2140 SERIES_DT( type )->poke( buf, a1->series.it + n, a3 );
2141 return UR_OK;
2142 }
2143 else if( type == UT_VEC3 )
2144 return vec3_poke( ut, res, n, a3 );
2145 else if( type == UT_COORD )
2146 return coord_poke( ut, res, n, a3 );
2147 return boron_badArg( ut, type, 0 );
2148}
2149
2150
2151/*-cf-
2152 pop
2153 series
2154 return: Last item of series or none! if empty.
2155 group: series
2156
2157 Removes last item from series and returns it.
2158*/
2159CFUNC(cfunc_pop)
2160{
2161 USeriesIterM si;
2162 int type = ur_type(a1);
2163 if( ! ur_isSeriesType( type ) )
2164 return boron_badArg( ut, type, 0 );
2165 if( ! ur_seriesSliceM( ut, &si, a1 ) )
2166 return UR_THROW;
2167 if( si.it == si.end )
2168 {
2169 ur_setId(res, UT_NONE);
2170 }
2171 else
2172 {
2173 const USeriesType* dt = SERIES_DT( type );
2174 si.it = si.end - 1;
2175 dt->pick( si.buf, si.it, res );
2176 dt->remove( ut, &si, 0 );
2177 }
2178 return UR_OK;
2179}
2180
2181
2182/*-cf-
2183 skip
2184 series Series or port!
2185 offset logic!/int!
2186 /wrap Cycle around to other end when new position is out of range.
2187 return: Offset series.
2188 group: series
2189
2190 If offset is a logic! type then the series will move to the next element
2191 if its value is true.
2192*/
2193CFUNC(cfunc_skip)
2194{
2195#define OPT_SKIP_WRAP 0x01
2196 if( ur_isSeriesType( ur_type(a1) ) )
2197 {
2198 UIndex n;
2199 UIndex end;
2200
2201 if( ur_is(a2, UT_INT) )
2202 n = ur_int(a2);
2203 else if( ur_is(a2, UT_LOGIC) )
2204 n = ur_logic(a2) ? 1 : 0;
2205 else
2206 return boron_badArg( ut, ur_type(a2), 1 );
2207
2208 *res = *a1;
2209 if( n )
2210 {
2211 n += a1->series.it;
2212 if( n < 0 )
2213 {
2214 if( (CFUNC_OPTIONS & OPT_SKIP_WRAP) &&
2215 (end = boron_seriesEnd( ut, a1 )) )
2216 {
2217 do
2218 n += end;
2219 while( n < 0 );
2220 }
2221 else
2222 n = 0;
2223 }
2224 else
2225 {
2226 end = boron_seriesEnd( ut, a1 );
2227 if( (CFUNC_OPTIONS & OPT_SKIP_WRAP) && end )
2228 {
2229 while( n >= end )
2230 n -= end;
2231 }
2232 else if( n > end )
2233 n = end;
2234 }
2235 res->series.it = n;
2236 }
2237 return UR_OK;
2238 }
2239 else if( ur_is(a1, UT_PORT) )
2240 {
2241 PORT_SITE(dev, pbuf, a1);
2242 if( ! dev )
2243 return errorScript( "port is closed" );
2244 *res = *a1;
2245 return dev->seek( ut, pbuf, a2, UR_PORT_SKIP );
2246 }
2247 return boron_badArg( ut, ur_type(a1), 0 );
2248}
2249
2250
2251/*-cf-
2252 append
2253 series Series or context!
2254 value Data to append.
2255 /block If series and value are blocks, push value as a single item.
2256 /repeat Repeat append.
2257 count int!
2258 return: Modified series or bound word!.
2259 group: series
2260 see: remove, terminate, appair
2261
2262 Add data to end of series.
2263
2264 Examples:
2265 append "apple" 's'
2266 == "apples"
2267
2268 append/repeat #{0000} 0xf6 4
2269 == #{0000F6F6F6F6}
2270
2271 dat: [a b]
2272 append dat [1 2]
2273 == [a b 1 2]
2274 append/block dat [3 4]
2275 == [a b 1 2 [3 4]]
2276*/
2277CFUNC(cfunc_append)
2278{
2279#define OPT_APPEND_BLOCK 0x01
2280#define OPT_APPEND_REPEAT 0x02
2281 UBuffer* buf;
2282 const USeriesType* dt;
2283 uint32_t opt;
2284 int type = ur_type(a1);
2285 int count;
2286
2287 if( ur_isSeriesType( type ) )
2288 {
2289 if( ! (buf = ur_bufferSerM(a1)) )
2290 return UR_THROW;
2291
2292 dt = SERIES_DT( type );
2293 if( (opt = CFUNC_OPTIONS) )
2294 {
2295 count = (opt & OPT_APPEND_REPEAT) ? ur_int(CFUNC_OPT_ARG(2)) : 1;
2296 if( (opt & OPT_APPEND_BLOCK) && (type == UT_BLOCK) )
2297 {
2298 while( --count >= 0 )
2299 ur_blkPush( buf, a2 );
2300 }
2301 else
2302 {
2303 while( --count >= 0 )
2304 {
2305 if( ! dt->append( ut, buf, a2 ) )
2306 return UR_THROW;
2307 }
2308 }
2309 }
2310 else
2311 {
2312 if( ! dt->append( ut, buf, a2 ) )
2313 return UR_THROW;
2314 }
2315
2316 *res = *a1;
2317 return UR_OK;
2318 }
2319 else if( type == UT_CONTEXT )
2320 {
2321 if( ! (buf = ur_bufferSerM(a1)) )
2322 return UR_THROW;
2323
2324 type = ur_type(a2);
2325 if( ur_isWordType( type ) )
2326 {
2327 UIndex n = a1->series.buf;
2328 ur_setId(res, UT_WORD);
2329 ur_setBinding( res, ur_isShared(n) ? UR_BIND_ENV : UR_BIND_THREAD );
2330 res->word.ctx = n;
2331 res->word.index = ur_ctxAddWordI( buf, ur_atom(a2) );
2332 return UR_OK;
2333 }
2334#if 0
2335 else if( type == UT_BLOCK )
2336 {
2337 UBlockIt bi;
2338 ur_blockIt( ut, &bi, a2 );
2339 ur_ctxSetWords( buf, bi.it, bi.end );
2340 *res = *a2;
2341 return UR_OK;
2342 }
2343#endif
2344 return boron_badArg( ut, type, 1 );
2345 }
2346 return boron_badArg( ut, type, 0 );
2347}
2348
2349
2350/*-cf-
2351 appair
2352 series Series.
2353 value1 First value to append.
2354 value2 Second value to append.
2355 return: Modified series.
2356 group: series
2357 see: append, remove, terminate
2358
2359 Add a pair of values to the end of series.
2360*/
2361CFUNC(cfunc_appair)
2362{
2363 UBuffer* buf;
2364 const USeriesType* dt;
2365 int type = ur_type(a1);
2366
2367 if( ur_isSeriesType( type ) )
2368 {
2369 if( ! (buf = ur_bufferSerM(a1)) )
2370 return UR_THROW;
2371
2372 dt = SERIES_DT( type );
2373 if( ! dt->append( ut, buf, a2 ) )
2374 return UR_THROW;
2375 if( ! dt->append( ut, buf, a3 ) )
2376 return UR_THROW;
2377
2378 *res = *a1;
2379 return UR_OK;
2380 }
2381 return boron_badArg( ut, type, 0 );
2382}
2383
2384
2385/*-cf-
2386 insert
2387 series
2388 value
2389 /block Insert block value as a single item.
2390 /part Insert only a limited number of elements from value.
2391 limit
2392 /repeat Repeat insertion.
2393 count int!
2394 return: Modified series.
2395 group: series
2396*/
2397CFUNC(cfunc_insert)
2398{
2399#define OPT_INSERT_BLOCK 0x01
2400#define OPT_INSERT_PART 0x02
2401#define OPT_INSERT_REPEAT 0x04
2402 UBuffer* buf;
2403 const USeriesType* dt;
2404 uint32_t opt;
2405 UIndex part = INT32_MAX;
2406 int type = ur_type(a1);
2407 int count = 1;
2408
2409 if( ! ur_isSeriesType( type ) )
2410 return boron_badArg( ut, type, 0 );
2411
2412 if( ! (buf = ur_bufferSerM(a1)) )
2413 return UR_THROW;
2414
2415 if( (opt = CFUNC_OPTIONS) )
2416 {
2417 if( opt & OPT_INSERT_REPEAT )
2418 count = ur_int(CFUNC_OPT_ARG(3));
2419
2420 if( (opt & OPT_INSERT_BLOCK) && (type == UT_BLOCK) )
2421 {
2422 while( --count >= 0 )
2423 ur_blkInsert( buf, a1->series.it, a2, 1 );
2424 goto done;
2425 }
2426 else if( opt & OPT_INSERT_PART )
2427 {
2428 UCell* parg = CFUNC_OPT_ARG(2);
2429 if( ur_is(parg, UT_INT) )
2430 part = ur_int(parg);
2431 else if( ur_isSeriesType( ur_type(parg) ) )
2432 part = parg->series.it - a1->series.it;
2433 else
2434 return ur_error( ut, UR_ERR_TYPE,
2435 "insert /part expected series or int!" );
2436 if( part < 1 )
2437 goto done;
2438 }
2439 }
2440
2441 dt = SERIES_DT( type );
2442 while( --count >= 0 )
2443 {
2444 if( ! dt->insert( ut, buf, a1->series.it, a2, part ) )
2445 return UR_THROW;
2446 }
2447done:
2448 *res = *a1;
2449 return UR_OK;
2450}
2451
2452
2453/*-cf-
2454 change
2455 series
2456 replacement
2457 /slice Remove slice and insert replacement.
2458 /part Remove to limit and insert replacement.
2459 limit Series or int!
2460 return: Series at end of change.
2461 group: series
2462*/
2463CFUNC(cfunc_change)
2464{
2465#define OPT_CHANGE_SLICE 0x01
2466#define OPT_CHANGE_PART 0x02
2467 USeriesIterM si;
2468 UIndex part = 0;
2469 int type = ur_type(a1);
2470 uint32_t opt = CFUNC_OPTIONS;
2471
2472 if( ! ur_isSeriesType( type ) )
2473 return boron_badArg( ut, type, 0 );
2474 if( ! ur_seriesSliceM( ut, &si, a1 ) )
2475 return UR_THROW;
2476
2477 if( (opt & OPT_CHANGE_SLICE) /*&& ur_isSliced(a1)*/ )
2478 {
2479 part = si.end - si.it;
2480 }
2481 else if( opt & OPT_CHANGE_PART )
2482 {
2483 UCell* parg = CFUNC_OPT_ARG(2);
2484 if( ur_is(parg, UT_INT) )
2485 part = ur_int(parg);
2486 else if( ur_isSeriesType( ur_type(parg) ) )
2487 part = parg->series.it - si.it;
2488 else
2489 return ur_error( ut, UR_ERR_TYPE,
2490 "change /part expected series or int!" );
2491 }
2492
2493 if( ! SERIES_DT( type )->change( ut, &si, a2, part ) )
2494 return UR_THROW;
2495
2496 *res = *a1;
2497 res->series.it = si.it;
2498 return UR_OK;
2499}
2500
2501
2502/*-cf-
2503 remove
2504 series series or none!/hash-map!
2505 /slice Remove to end of slice.
2506 /part Remove more than one element.
2507 number int!
2508 /key Remove value from hash-map!
2509 kval
2510 return: series or none!/hash-map!
2511 group: series
2512 see: append, clear, remove-each
2513
2514 Remove element at series position.
2515*/
2516CFUNC(cfunc_remove)
2517{
2518#define OPT_REMOVE_SLICE 0x01
2519#define OPT_REMOVE_PART 0x02
2520#define OPT_REMOVE_KEY 0x04
2521 USeriesIterM si;
2522 uint32_t opt = CFUNC_OPTIONS;
2523 int part = 0;
2524 int type = ur_type(a1);
2525
2526 if( ! ur_isSeriesType( type ) )
2527 {
2528 if( type == UT_NONE )
2529 {
2530 ur_setId(res, UT_NONE);
2531 return UR_OK;
2532 }
2533#ifdef CONFIG_HASHMAP
2534 else if( ur_is(a1, UT_HASHMAP) )
2535 {
2536 if( opt & OPT_REMOVE_KEY )
2537 {
2538 hashmap_remove( ut, a1, a2 );
2539 goto set_result;
2540 }
2541 return errorType( "remove requires /key for hash-map!" );
2542 }
2543#endif
2544 return boron_badArg( ut, type, 0 );
2545 }
2546 if( ! ur_seriesSliceM( ut, &si, a1 ) )
2547 return UR_THROW;
2548
2549 if( opt & OPT_REMOVE_PART )
2550 {
2551 UCell* parg = CFUNC_OPT_ARG(2);
2552 part = ur_int(parg);
2553 }
2554 else if( opt & OPT_REMOVE_SLICE )
2555 {
2556 part = si.end - si.it;
2557 }
2558
2559 SERIES_DT( type )->remove( ut, &si, part );
2560
2561#ifdef CONFIG_HASHMAP
2562set_result:
2563#endif
2564
2565 *res = *a1;
2566 return UR_OK;
2567}
2568
2569
2570/*-cf-
2571 reverse
2572 series
2573 /part Limit change to part of series.
2574 number int!
2575 return: series
2576 group: series
2577 see: swap
2578
2579 Reverse the order of elements in a series.
2580*/
2581CFUNC(cfunc_reverse)
2582{
2583#define OPT_REVERSE_PART 0x01
2584 USeriesIterM si;
2585 int part;
2586 int type = ur_type(a1);
2587
2588 if( ! ur_isSeriesType( type ) )
2589 return boron_badArg( ut, type, 0 );
2590 if( ! ur_seriesSliceM( ut, &si, a1 ) )
2591 return UR_THROW;
2592
2593 if( CFUNC_OPTIONS & OPT_REVERSE_PART )
2594 {
2595 UCell* parg = CFUNC_OPT_ARG(1);
2596 part = ur_int(parg);
2597 if( part < 1 )
2598 goto done;
2599 if( part < (si.end - si.it) )
2600 si.end = si.it + part;
2601 }
2602
2603 SERIES_DT( type )->reverse( &si );
2604done:
2605 *res = *a1;
2606 return UR_OK;
2607}
2608
2609
2610/*-cf-
2611 find
2612 series
2613 value Element or pattern to search for.
2614 /last Search from end of series.
2615 /case Case of characters in strings must match.
2616 /part Restrict search to part of series.
2617 limit series/int!
2618 return: Position of value in series or none!.
2619 group: series
2620*/
2621CFUNC(cfunc_find)
2622{
2623#define OPT_FIND_LAST UR_FIND_LAST
2624#define OPT_FIND_CASE UR_FIND_CASE
2625#define OPT_FIND_PART 0x04
2626 USeriesIter si;
2627 UIndex i;
2628 uint32_t opt = CFUNC_OPTIONS;
2629 int type = ur_type(a1);
2630
2631 assert( UR_FIND_LAST == 1 );
2632 assert( UR_FIND_CASE == 2 );
2633
2634 if( ! ur_isSeriesType( type ) )
2635 return boron_badArg( ut, type, 0 );
2636 ur_seriesSlice( ut, &si, a1 );
2637
2638 if( opt & OPT_FIND_PART )
2639 {
2640 UIndex part;
2641 UCell* parg = CFUNC_OPT_ARG(3);
2642
2643 if( ur_is(parg, UT_INT) )
2644 part = ur_int(parg);
2645 else if( ur_isSeriesType( ur_type(parg) ) )
2646 part = parg->series.it - si.it;
2647 else
2648 return errorType( "find /part expected series or int!" );
2649
2650 if( part < 1 )
2651 goto set_none;
2652 part += si.it;
2653 if( part < si.end )
2654 si.end = part;
2655 }
2656
2657 i = SERIES_DT( type )->find( ut, &si, a2, opt );
2658 if( i < 0 )
2659 {
2660set_none:
2661 ur_setId(res, UT_NONE);
2662 }
2663 else
2664 {
2665 *res = *a1;
2666 res->series.it = i;
2667 }
2668 return UR_OK;
2669}
2670
2671
2672/*-cf-
2673 clear
2674 series series or none!/hash-map!
2675 return: Empty series or none!/hash-map!.
2676 group: series
2677 see: remove
2678
2679 Erase to end of series.
2680*/
2681CFUNC(cfunc_clear)
2682{
2683 UBuffer* buf;
2684
2685 if( ! ur_isSeriesType( ur_type(a1) ) )
2686 {
2687 if( ur_is(a1, UT_NONE) )
2688 {
2689 ur_setId(res, UT_NONE);
2690 return UR_OK;
2691 }
2692#ifdef CONFIG_HASHMAP
2693 else if( ur_is(a1, UT_HASHMAP) )
2694 {
2695 hashmap_clear( ut, a1 );
2696 goto set_result;
2697 }
2698#endif
2699 return boron_badArg( ut, ur_type(a1), 0 );
2700 }
2701 if( ! (buf = ur_bufferSerM(a1)) )
2702 return UR_THROW;
2703
2704 // Limit erase to slice end?
2705 if( a1->series.it < buf->used )
2706 buf->used = a1->series.it;
2707
2708#ifdef CONFIG_HASHMAP
2709set_result:
2710#endif
2711
2712 *res = *a1;
2713 return UR_OK;
2714}
2715
2716
2717extern void coord_slice( const UCell* cell, int index, int count, UCell* res );
2718
2719/*-cf-
2720 slice
2721 start Series or coord!
2722 limit Series or none!/int!/coord!
2723 return: Start with adjusted end.
2724 group: series
2725 see: skip
2726
2727 Slice gives a series an end position.
2728
2729 A positive integer limit value sets the length of the slice.
2730 If limit is negative, then that number of elements (negated) are removed
2731 from the end.
2732
2733 slice "There and back" 5
2734 == "There"
2735 slice "There and back" -5
2736 == "There and"
2737
2738 A coord! limit value will modify both the start and the end. The start
2739 will be adjusted by the first coord! number (like skip). The second
2740 coord! number will set the length or remove from the end (if negative).
2741
2742 slice "There and back" 6,3
2743 == "and"
2744 slice "There and back" 6,-3
2745 == "and b"
2746
2747 If limit is from the the same series as start, then the end is simply
2748 set to the limit start position.
2749
2750 f: %my_song.mp3
2751 slice f find f '.'
2752 == %my_song
2753
2754 Passing a none! limit removes any slice end and returns an un-sliced series.
2755*/
2756CFUNC(cfunc_slice)
2757{
2758 const UBuffer* buf;
2759 const UCell* limit = a2;
2760 int end;
2761
2762 if( ! ur_isSeriesType( ur_type(a1) ) )
2763 {
2764 if( ur_is(a1, UT_COORD) )
2765 {
2766 if( ur_is(limit, UT_INT) )
2767 {
2768 end = ur_int(limit);
2769 if( end < 0 )
2770 end += a1->coord.len;
2771 coord_slice( a1, 0, end, res );
2772 }
2773 else if( ur_is(limit, UT_COORD) )
2774 coord_slice( a1, limit->coord.n[0], limit->coord.n[1], res );
2775 else if( ur_is(limit, UT_NONE) )
2776 *res = *a1;
2777 else
2778 goto bad_limit;
2779 return UR_OK;
2780 }
2781 return boron_badArg( ut, ur_type(a1), 0 );
2782 }
2783
2784 buf = ur_bufferSer(a1);
2785 *res = *a1;
2786
2787 if( ur_is(limit, UT_NONE) )
2788 {
2789 res->series.it = 0;
2790 res->series.end = -1;
2791 }
2792 else if( ur_is(limit, UT_INT) )
2793 {
2794 end = ur_int(limit);
2795set_end:
2796 if( end < 0 )
2797 {
2798 res->series.end = (res->series.end < 0) ? buf->used + end
2799 : res->series.end + end;
2800 if( res->series.end < res->series.it )
2801 res->series.end = res->series.it;
2802 }
2803 else
2804 {
2805 res->series.end = res->series.it + end;
2806 if( res->series.end > buf->used )
2807 res->series.end = -1;
2808 }
2809 }
2810 else if( ur_is(limit, UT_COORD) )
2811 {
2812 res->series.it += limit->coord.n[0];
2813 if( res->series.it < 0 )
2814 res->series.it = 0;
2815 end = limit->coord.n[1];
2816 goto set_end;
2817 }
2818 else if( ur_type(a1) == ur_type(a2) )
2819 {
2820 if( limit->series.it < res->series.it )
2821 res->series.end = res->series.it;
2822 else
2823 res->series.end = limit->series.it;
2824 }
2825 else
2826 {
2827bad_limit:
2828 return boron_badArg( ut, ur_type(limit), 1 );
2829 }
2830 return UR_OK;
2831}
2832
2833
2834/*-cf-
2835 empty?
2836 value series or none!
2837 return: logic!
2838 group: series
2839
2840 Return true if the size of a series is zero, its position is out of range,
2841 or the value is none!.
2842*/
2843CFUNC(cfunc_emptyQ)
2844{
2845 USeriesIter si;
2846
2847 if( ! ur_isSeriesType( ur_type(a1) ) )
2848 {
2849 if( ur_is(a1, UT_NONE) )
2850 {
2851 si.it = 1;
2852 goto set_logic;
2853 }
2854 return boron_badArg( ut, ur_type(a1), 0 );
2855 }
2856
2857 ur_seriesSlice( ut, &si, a1 );
2858 si.it = (si.it == si.end) ? 1 : 0;
2859
2860set_logic:
2861 ur_setId( res, UT_LOGIC );
2862 ur_logic(res) = si.it;
2863 return UR_OK;
2864}
2865
2866
2867/*-cf-
2868 head?
2869 series
2870 return: logic! True if position is at the start.
2871 group: series
2872 see: head, tail?
2873
2874 Test if the series position is at the start.
2875*/
2876CFUNC(cfunc_headQ)
2877{
2878 if( ! ur_isSeriesType( ur_type(a1) ) )
2879 return boron_badArg( ut, ur_type(a1), 0 );
2880
2881 ur_setId( res, UT_LOGIC );
2882 if( a1->series.it < 1 )
2883 ur_logic(res) = 1;
2884 return UR_OK;
2885}
2886
2887
2888/*-cf-
2889 size?
2890 series
2891 return: int!
2892 group: series
2893 see: index?
2894
2895 Length of series from current position to end.
2896*/
2897CFUNC(cfunc_sizeQ)
2898{
2899 int len;
2900 if( ur_isSeriesType( ur_type(a1) ) )
2901 {
2902 USeriesIter si;
2903 ur_seriesSlice( ut, &si, a1 );
2904 len = si.end - si.it;
2905 }
2906 else if( ur_is(a1, UT_COORD) )
2907 len = a1->coord.len;
2908 else
2909 return boron_badArg( ut, ur_type(a1), 0 );
2910
2911 ur_setId( res, UT_INT );
2912 ur_int(res) = len;
2913 return UR_OK;
2914}
2915
2916
2917/*-cf-
2918 index?
2919 series Series, word! or datatype!.
2920 /zero Return zero-based series position.
2921 return: int!
2922 group: series
2923 see: size?
2924
2925 Get the current position of series, starting with 1.
2926
2927 For words the internal atom value is returned.
2928 For datatypes a type bitmask is returned.
2929*/
2930CFUNC(cfunc_indexQ)
2931{
2932 int type = ur_type(a1);
2933
2934 if( ur_isSeriesType( type ) ) {
2935 int64_t n = a1->series.it;
2936 if( ! (CFUNC_OPTIONS & 1) )
2937 ++n;
2938 ur_int(res) = n;
2939 } else if( ur_isWordType( type ) )
2940 ur_int(res) = ur_atom(a1);
2941 else if( type == UT_DATATYPE )
2942 ur_int(res) = (int64_t) a1->datatype.mask0 |
2943 (int64_t) a1->datatype.mask1 << 32;
2944 //else if( type == UT_PORT )
2945 else
2946 return boron_badArg( ut, type, 0 );
2947
2948 ur_setId( res, UT_INT );
2949 return UR_OK;
2950}
2951
2952
2953/*-cf-
2954 series?
2955 value
2956 return: True if value is a series type.
2957 group: data, series
2958*/
2959CFUNC(cfunc_seriesQ)
2960{
2961 (void) ut;
2962 ur_setId( res, UT_LOGIC );
2963 if( ur_isSeriesType( ur_type(a1) ) )
2964 ur_logic(res) = 1;
2965 return UR_OK;
2966}
2967
2968
2969/*-cf-
2970 any-block?
2971 value
2972 return: True if value is a block type.
2973 group: data
2974
2975 Test if value is one of: block!/paren!
2976*/
2977CFUNC(cfunc_any_blockQ)
2978{
2979 (void) ut;
2980 ur_setId( res, UT_LOGIC );
2981 if( ur_isBlockType( ur_type(a1) ) )
2982 ur_logic(res) = 1;
2983 return UR_OK;
2984}
2985
2986
2987/*-cf-
2988 any-word?
2989 value
2990 return: True if value is a word type.
2991 group: data
2992
2993 Test if value is one of: word!/lit-word!/set-word!/get-word!/option!
2994*/
2995CFUNC(cfunc_any_wordQ)
2996{
2997 (void) ut;
2998 ur_setId( res, UT_LOGIC );
2999 if( ur_isWordType( ur_type(a1) ) )
3000 ur_logic(res) = 1;
3001 return UR_OK;
3002}
3003
3004
3005/*-cf-
3006 complement
3007 value logic!/char!/int!/binary!/bitset!
3008 return: Complemented value.
3009 group: data
3010 see: negate, not
3011*/
3012CFUNC(cfunc_complement)
3013{
3014 *res = *a1;
3015 switch( ur_type(a1) )
3016 {
3017 case UT_LOGIC:
3018 ur_logic(res) ^= 1;
3019 break;
3020
3021 case UT_CHAR:
3022 case UT_INT:
3023 ur_int(res) = ~ur_int(a1);
3024 break;
3025
3026 case UT_BINARY:
3027 case UT_BITSET:
3028 {
3029 UBinaryIterM bi;
3030 if( ! ur_binSliceM( ut, &bi, res ) )
3031 return UR_THROW;
3032 ur_foreach( bi )
3033 {
3034 *bi.it = ~(*bi.it);
3035 }
3036 }
3037 break;
3038
3039 default:
3040 return boron_badArg( ut, ur_type(a1), 0 );
3041 }
3042 return UR_OK;
3043}
3044
3045
3046/*-cf-
3047 negate
3048 value int!/double!/time!/coord!/vec3!/bitset!
3049 return: Negated value.
3050 group: data
3051 see: complement, not
3052*/
3053CFUNC( cfunc_negate )
3054{
3055 int type = ur_type(a1);
3056 switch( type )
3057 {
3058 case UT_INT:
3059 ur_setId(res, type);
3060 ur_int(res) = -ur_int(a1);
3061 break;
3062
3063 case UT_DOUBLE:
3064 case UT_TIME:
3065 ur_setId(res, type);
3066 ur_double(res) = -ur_double(a1);
3067 break;
3068
3069 case UT_COORD:
3070 {
3071 int16_t* it = a1->coord.n;
3072 int16_t* end = it + a1->coord.len;
3073 int16_t* dst = res->coord.n;
3074 while( it != end )
3075 {
3076 *dst++ = -*it;
3077 ++it;
3078 }
3079 ur_setId(res, type);
3080 res->coord.len = a1->coord.len;
3081 }
3082 break;
3083
3084 case UT_VEC3:
3085 ur_setId(res, type);
3086 res->vec3.xyz[0] = -a1->vec3.xyz[0];
3087 res->vec3.xyz[1] = -a1->vec3.xyz[1];
3088 res->vec3.xyz[2] = -a1->vec3.xyz[2];
3089 break;
3090
3091 case UT_BITSET:
3092 return cfunc_complement( ut, a1, res );
3093
3094 default:
3095 return boron_badArg( ut, type, 0 );
3096 }
3097 return UR_OK;
3098}
3099
3100
3101enum SetOperation
3102{
3103 SET_OP_INTERSECT,
3104 SET_OP_DIFF,
3105 SET_OP_UNION
3106};
3107
3108
3109static int set_relation( UThread* ut, const UCell* a1, UCell* res,
3110 enum SetOperation op, int findOpt )
3111{
3112 USeriesIter si;
3113 const USeriesType* dt;
3114 const UCell* argB = a2;
3115 int type = ur_type(a1);
3116
3117 if( type != ur_type(argB) )
3118 return ur_error( ut, UR_ERR_TYPE,
3119 "intersect/difference expected series of the same type" );
3120
3121 dt = SERIES_DT( type );
3122
3123 if( ur_isBlockType(type) )
3124 {
3125 UBlockIt bi;
3126 UBuffer* blk = ur_makeBlockCell( ut, type, 0, res );
3127
3128 ur_blockIt( ut, &bi, a1 );
3129
3130 switch( op )
3131 {
3132 case SET_OP_INTERSECT:
3133 {
3134 USeriesIter ri;
3135
3136 ur_seriesSlice( ut, &si, argB );
3137
3138 ri.buf = blk;
3139 ri.it = ri.end = 0;
3140
3141 ur_foreach( bi )
3142 {
3143 if( (dt->find( ut, &si, bi.it, findOpt ) > -1) &&
3144 (dt->find( ut, &ri, bi.it, findOpt ) == -1) )
3145 {
3146 ur_blkPush( blk, bi.it );
3147 ++ri.end;
3148 }
3149 }
3150 }
3151 break;
3152
3153 case SET_OP_DIFF:
3154 ur_seriesSlice( ut, &si, argB );
3155
3156 ur_foreach( bi )
3157 {
3158 if( dt->find( ut, &si, bi.it, findOpt ) < 0 )
3159 ur_blkPush( blk, bi.it );
3160 }
3161 break;
3162
3163 case SET_OP_UNION:
3164 {
3165 si.buf = blk;
3166 si.it = si.end = 0;
3167union_loop:
3168 ur_foreach( bi )
3169 {
3170 if( dt->find( ut, &si, bi.it, findOpt ) < 0 )
3171 {
3172 ur_blkPush( blk, bi.it );
3173 ++si.end;
3174 }
3175 }
3176
3177 if( type )
3178 {
3179 type = 0;
3180 ur_blockIt( ut, &bi, argB );
3181 goto union_loop;
3182 }
3183 }
3184 break;
3185 }
3186 }
3187 else
3188 {
3189 return ur_error( ut, UR_ERR_INTERNAL,
3190 "FIXME: set_relation only supports block!" );
3191 }
3192
3193 return UR_OK;
3194}
3195
3196
3197/*-cf-
3198 intersect
3199 setA series
3200 setB series
3201 /case Character case must match when comparing strings.
3202 return: New series that contains only the elements common to both sets.
3203 group: series
3204 see: difference, union
3205*/
3206CFUNC(cfunc_intersect)
3207{
3208 return set_relation( ut, a1, res, SET_OP_INTERSECT,
3209 (CFUNC_OPTIONS & 1) ? UR_FIND_CASE : 0 );
3210}
3211
3212
3213/*-cf-
3214 difference
3215 setA series
3216 setB series
3217 /case Character case must match when comparing strings.
3218 return: New series that contains the elements of setA which are not in setB.
3219 group: series
3220 see: intersect, union
3221
3222 This function generates the set-theoretic difference, not the symmetric
3223 difference (the elements unique to both sets).
3224*/
3225CFUNC(cfunc_difference)
3226{
3227 return set_relation( ut, a1, res, SET_OP_DIFF,
3228 (CFUNC_OPTIONS & 1) ? UR_FIND_CASE : 0 );
3229}
3230
3231
3232/*-cf-
3233 union
3234 setA series
3235 setB series
3236 /case Character case must match when comparing strings.
3237 return: New series that contains the distinct elements of both sets.
3238 group: series
3239 see: difference, intersect
3240*/
3241CFUNC(cfunc_union)
3242{
3243 return set_relation( ut, a1, res, SET_OP_UNION,
3244 (CFUNC_OPTIONS & 1) ? UR_FIND_CASE : 0 );
3245}
3246
3247
3248static inline UIndex _sliceEnd( const UBuffer* buf, const UCell* cell )
3249{
3250 if( cell->series.end > -1 && cell->series.end < buf->used )
3251 return cell->series.end;
3252 return buf->used;
3253}
3254
3255
3256/*-cf-
3257 foreach
3258 'words word!/block! Value of element(s).
3259 series Series or none!
3260 body block! Code to evaluate for each element.
3261 return: Result of body.
3262 group: control
3263 see: forall, remove-each, break, continue
3264
3265 Iterate over each element of a series.
3266*/
3267/*-cf-
3268 remove-each
3269 'words word!/block! Value of element(s).
3270 series Series or none!
3271 body block! Code to evaluate for each element.
3272 return: Result of body.
3273 group: control
3274 see: remove, break, continue
3275
3276 Remove elements when result of body is true.
3277
3278 Example:
3279 items: [1 5 2 3]
3280 remove-each i items [gt? i 2]
3281 == true
3282 probe items
3283 == [1 2]
3284*/
3285static UStatus loop_foreach(UThread* ut, EvalFrameInvoke* invoke)
3286{
3287 UCell* sarg = invoke->result;
3288 USeriesIter si;
3289
3290 si.buf = ur_bufferSer(sarg);
3291 si.end = _sliceEnd(si.buf, sarg);
3292
3293 if (invoke->userBuf < si.end)
3294 {
3295 UCell* cell;
3296 const UCell* wi;
3297 const USeriesType* dt = SERIES_DT( ur_type(sarg) );
3298 EvalFrame* ef = (EvalFrame*) invoke;
3299 --ef;
3300 for (wi = ef->block.it; wi != ef->block.end; ++wi)
3301 {
3302 if( ! (cell = ur_wordCellM(ut, wi)) )
3303 return UR_THROW;
3304 dt->pick(si.buf, invoke->userBuf++, cell);
3305 }
3306
3307 ef = boron_pushEvalFrame(ut);
3308 ef->block = ef[-3].block;
3309 return CFUNC_REFRAMED;
3310 }
3311
3312 return UR_OK;
3313}
3314
3315static UStatus loop_removeEach(UThread* ut, EvalFrameInvoke* invoke)
3316{
3317 // NULL catchf indicates first call.
3318 if (invoke->dat.catchf)
3319 {
3320 EvalFrame* dof = ((EvalFrame*) invoke) - 2;
3321 if (ur_true(dof->block.result))
3322 {
3323 USeriesIter si;
3324 int remove = dof[1].block.codeBlk;
3325 UCell* sarg = invoke->result;
3326 const USeriesType* dt = SERIES_DT( ur_type(sarg) );
3327
3328 invoke->userBuf -= remove;
3329
3330 si.it = invoke->userBuf;
3331 si.buf = ur_bufferSer(sarg);
3332 dt->remove(ut, (USeriesIterM*) &si, remove);
3333 }
3334 }
3335 else
3336 invoke->dat.catchf = catch_breakContinue;
3337
3338 return loop_foreach(ut, invoke);
3339}
3340
3341CFUNC(cfunc_foreach)
3342{
3343 UCell* sarg = a2;
3344 UCell* body = a3;
3345 UCell* words;
3346 EvalFrame* ef;
3347 USeriesIter si;
3348 UBlockIterM wi;
3349 int remove = ur_int(a1 + 3);
3350 int origStack;
3351
3352
3353 // TODO: Handle custom series type.
3354 if( ! ur_isSeriesType( ur_type(a2) ) )
3355 {
3356 if( ur_type(a2) == UT_NONE )
3357 {
3358 ur_setId(res, UT_NONE);
3359 return UR_OK;
3360 }
3361 return errorType( "foreach expected series" );
3362 }
3363 if( ! ur_is(body, UT_BLOCK) )
3364 return errorType( "foreach expected block! body" );
3365
3366 if( ur_is(a1, UT_WORD) )
3367 {
3368 words = a1;
3369 wi.end = a1 + 1;
3370 }
3371 else if( ur_is(a1, UT_BLOCK) )
3372 {
3373 if( ! ur_blkSliceM( ut, &wi, a1 ) ) // FIXME: Prevents shared foreach.
3374 return UR_THROW;
3375 words = wi.it;
3376
3377 {
3378 UBuffer* localCtx;
3379 int wordsShared = 0;
3380
3381 ur_foreach( wi )
3382 {
3383 if( ! ur_is(wi.it, UT_WORD) )
3384 return errorType( "foreach has non-words in word block" );
3385 if( ur_isShared(wi.it->word.ctx) )
3386 wordsShared = 1;
3387 }
3388 if( wordsShared )
3389 {
3390 if( ur_isShared(body->series.buf) )
3391 return errorScript( "foreach cannot bind shared body" );
3392
3393 localCtx = ur_buffer(ur_makeContext( ut, wi.end - words ));
3394 wi.it = words;
3395 ur_foreach( wi )
3396 ur_ctxAddWordI( localCtx, ur_atom(wi.it) );
3397 ur_bind( ut, wi.buf, ur_ctxSort(localCtx), UR_BIND_THREAD );
3398 ur_bind( ut, ur_buffer(body->series.buf), localCtx, UR_BIND_THREAD );
3399 }
3400 }
3401
3402 // Now that all validation is done the loop can finally begin.
3403 }
3404 else
3405 return errorType( "foreach expected word!/block! for words" );
3406
3407 if( remove )
3408 {
3409 remove = wi.end - words;
3410 if( ! ur_seriesSliceM( ut, (USeriesIterM*) &si, sarg ) )
3411 return UR_THROW;
3412 }
3413 else
3414 {
3415 ur_seriesSlice( ut, &si, sarg );
3416 }
3417
3418 ef = boron_reuseFrame(ut, 2, &origStack);
3419 if (! ef)
3420 return UR_THROW;
3421 boron_initEvalBlock(ef, ut, body->series.buf, res);
3422
3423 ++ef;
3424 ef->block.eop = EOP_NOP;
3425 ef->block.origStack = 0;
3426 ef->block.codeBlk = remove;
3427 ef->block.it = words;
3428 ef->block.end = wi.end;
3429 ef->block.result = NULL;
3430
3431 ++ef;
3432 ef->invoke.eop = EOP_INVOKE_LOOP;
3433 ef->invoke.state = 3; // opCount
3434 ef->invoke.origStack = origStack;
3435 ef->invoke.userBuf = si.it;
3436 if (remove)
3437 {
3438 ef->invoke.func = loop_removeEach;
3439 ef->invoke.dat.catchf = NULL; // NULL indicates first call.
3440 }
3441 else
3442 {
3443 ef->invoke.func = loop_foreach;
3444 ef->invoke.dat.catchf = catch_breakContinue;
3445 }
3446 ef->invoke.result = sarg;
3447
3448 return CFUNC_REFRAMED;
3449}
3450
3451
3452/*-cf-
3453 forall
3454 'ref word! Reference to series or none!.
3455 body block! Code to evaluate for each element.
3456 return: Result of body.
3457 group: control
3458 see: foreach, break
3459
3460 Iterate over each element of a series, changing the reference position.
3461
3462 Example:
3463 a: [1 2 3]
3464 forall a [probe a]
3465
3466 [1 2 3]
3467 [2 3]
3468 [3]
3469*/
3470static UStatus loop_forall(UThread* ut, EvalFrameInvoke* invoke)
3471{
3472 UCell* sarg;
3473 USeriesIter si;
3474
3475 if (! (sarg = ur_wordCellM(ut, invoke->result)))
3476 return UR_THROW;
3477
3478 // Terminate loop if series cell changed.
3479 if (ur_type(sarg) != invoke->userBuf)
3480 return UR_OK;
3481
3482 ++sarg->series.it;
3483 ur_seriesSlice(ut, &si, sarg);
3484 if (si.it < si.end)
3485 {
3486 EvalFrame* ef = boron_pushEvalFrame(ut);
3487 ef->block = ef[-2].block;
3488 return CFUNC_REFRAMED;
3489 }
3490 return UR_OK;
3491}
3492
3493CFUNC(cfunc_forall)
3494{
3495 UCell* sarg;
3496 USeriesIter si;
3497 EvalFrame* ef;
3498 int origStack;
3499 int type;
3500
3501 if( ! (sarg = ur_wordCellM(ut, a1)) )
3502 return UR_THROW;
3503 type = ur_type(sarg);
3504 if( ! ur_isSeriesType( type ) )
3505 {
3506 if( type == UT_NONE )
3507 {
3508 ur_setId(res, UT_NONE);
3509 return UR_OK;
3510 }
3511 return boron_badArg( ut, type, 0 );
3512 }
3513
3514 ur_seriesSlice( ut, &si, sarg );
3515 if (si.it == si.end)
3516 return UR_OK; // Series is empty.
3517
3518 ef = boron_reuseFrame(ut, 2, &origStack);
3519 if (! ef)
3520 return UR_THROW;
3521 boron_initEvalBlock(ef, ut, a1[1].series.buf, res);
3522
3523 ++ef;
3524 ef->invoke.eop = EOP_INVOKE_LOOP;
3525 ef->invoke.state = 2; // opCount
3526 ef->invoke.origStack = origStack;
3527 ef->invoke.userBuf = type;
3528 ef->invoke.func = loop_forall;
3529 ef->invoke.dat.catchf = catch_breakContinue;
3530 ef->invoke.result = a1;
3531
3532 ++ef;
3533 ef->block = ef[-2].block;
3534 return CFUNC_REFRAMED;
3535}
3536
3537
3538/*-cf-
3539 map
3540 'word word!
3541 series
3542 body block!
3543 return: Modified series
3544 group: series
3545
3546 Replace each element of series with result of body.
3547 Use 'break in body to terminate mapping.
3548*/
3549static UStatus catch_map(UThread* ut, EvalFrame* ef)
3550{
3551 // Same as catch_breakContinue except for result copy.
3552 const UCell* cell = ur_exception(ut);
3553 if (ur_is(cell, UT_WORD)) {
3554 if (ur_atom(cell) == UR_ATOM_BREAK)
3555 {
3556 *(ef[-1].block.result) = *ef->invoke.result;
3557 return UR_OK;
3558 }
3559 if (ur_atom(cell) == UR_ATOM_CONTINUE)
3560 return boron_resetEvalFrame(ut, ef + 1);
3561 }
3562 return UR_THROW;
3563}
3564
3565static UStatus _mapLoop(UThread* ut, EvalFrameInvoke* invoke)
3566{
3567 EvalFrame* dof = ((EvalFrame*) invoke) - 1;
3568 UCell* sarg = invoke->result;
3569 const USeriesType* dt = SERIES_DT( ur_type(sarg) );
3570 USeriesIter si;
3571
3572 si.buf = ur_bufferSer(sarg);
3573 si.end = _sliceEnd(si.buf, sarg);
3574
3575 // NULL catchf indicates first call.
3576 if (invoke->dat.catchf)
3577 dt->poke((UBuffer*) si.buf, invoke->userBuf++, dof->block.result);
3578 else
3579 invoke->dat.catchf = catch_map;
3580
3581 if (invoke->userBuf < si.end)
3582 {
3583 UCell* cell;
3584 EvalFrame* ef;
3585
3586 if( ! (cell = ur_wordCellM(ut, sarg - 1)) )
3587 return UR_THROW;
3588 dt->pick(si.buf, invoke->userBuf, cell);
3589
3590 ef = boron_pushEvalFrame(ut);
3591 ef->block = dof->block;
3592 return CFUNC_REFRAMED;
3593 }
3594
3595 *dof->block.result = *sarg;
3596 return UR_OK;
3597}
3598
3599CFUNC(cfunc_map)
3600{
3601 UCell* sarg = a2;
3602 EvalFrame* ef;
3603 USeriesIter si;
3604 int origStack;
3605
3606
3607 if( ! ur_isSeriesType( ur_type(sarg) ) )
3608 return boron_badArg( ut, ur_type(sarg), 1 );
3609 if( ur_isShared( sarg->series.buf ) )
3610 return errorType( "map cannot modify shared series" );
3611
3612 ur_seriesSlice(ut, &si, sarg);
3613
3614 ef = boron_reuseFrame(ut, 1, &origStack);
3615 if (! ef)
3616 return UR_THROW;
3617 boron_initEvalBlock(ef, ut, a1[2].series.buf, res);
3618
3619 ++ef;
3620 ef->invoke.eop = EOP_INVOKE_LOOP;
3621 ef->invoke.state = 2; // opCount
3622 ef->invoke.origStack = origStack;
3623 ef->invoke.userBuf = si.it;
3624 ef->invoke.func = _mapLoop;
3625 ef->invoke.dat.catchf = NULL; // Initially NULL to indicate first call.
3626 ef->invoke.result = sarg;
3627
3628 return CFUNC_REFRAMED;
3629}
3630
3631
3632/*-cf-
3633 all
3634 tests block! Expressions to test.
3635 return: logic!
3636 group: control
3637 see: any
3638
3639 Return true only when all expressions are true.
3640*/
3641static UStatus eval_all(UThread* ut, EvalFrameInvoke* ef)
3642{
3643 UCell* res = ef->result;
3644
3645 if (ef->state == DO_BLOCK1_COMPLETE)
3646 {
3647 ur_setId(res, UT_LOGIC);
3648 ur_logic(res) = 1;
3649 }
3650 else if (! ur_true(res))
3651 {
3652 return boron_breakDoBlock1(ut, ef);
3653 }
3654 return UR_OK;
3655}
3656
3657CFUNC(cfunc_all)
3658{
3659 return boron_reframeDoBlock1(ut, a1->series.buf, eval_all, res);
3660}
3661
3662
3663/*-cf-
3664 any
3665 tests block! Expressions to test.
3666 return: Result of first true test or false.
3667 group: control
3668 see: all
3669
3670 Return true if any expressions are true.
3671*/
3672static UStatus eval_any(UThread* ut, EvalFrameInvoke* ef)
3673{
3674 UCell* res = ef->result;
3675
3676 if (ef->state == DO_BLOCK1_COMPLETE)
3677 {
3678 ur_setId(res, UT_LOGIC);
3679 //ur_logic(res) = 0;
3680 }
3681 else if (ur_true(res))
3682 {
3683 return boron_breakDoBlock1(ut, ef);
3684 }
3685 return UR_OK;
3686}
3687
3688CFUNC(cfunc_any)
3689{
3690 return boron_reframeDoBlock1(ut, a1->series.buf, eval_any, res);
3691}
3692
3693
3694/*-cf-
3695 mold
3696 value
3697 /contents Omit the outer braces from block, context, and vector
3698 values.
3699 return: string!
3700 group: data
3701 see: to-text
3702
3703 Convert value to its string form with datatype syntax features.
3704*/
3705CFUNC(cfunc_mold)
3706{
3707#define OPT_MOLD_CONTENTS 0x01
3708 UBuffer* buf;
3709 int enc, len;
3710
3711 if( ur_isStringType( ur_type(a1) ) )
3712 {
3713 const UBuffer* str = ur_bufferSer(a1);
3714 enc = str->form;
3715 len = str->used + 2;
3716 }
3717 else
3718 {
3719 enc = UR_ENC_LATIN1;
3720 len = 0;
3721 }
3722
3723 buf = ur_makeStringCell( ut, enc, len, res ),
3724 buf->flags |= UR_STRING_ENC_UP;
3725 ur_toStr( ut, a1, buf, (CFUNC_OPTIONS & OPT_MOLD_CONTENTS) ? -1 : 0 );
3726 buf->flags &= ~UR_STRING_ENC_UP;
3727 return UR_OK;
3728}
3729
3730
3731#ifdef __ANDROID__
3732#undef stdout
3733#define stdout stderr
3734#endif
3735
3736enum PrintFlags {
3737 PrintNewline = 1,
3738 PrintAsText = 2
3739};
3740
3741static void _printValue(UThread* ut, const UCell* a1, int flags)
3742{
3743 UBuffer str;
3744
3745 ur_strInit( &str, UR_ENC_UTF8, 0 );
3746 if (flags & PrintAsText)
3747 ur_toText(ut, a1, &str);
3748 else
3749 ur_toStr(ut, a1, &str, 0);
3750 ur_strTermNull( &str );
3751 fputs( str.ptr.c, stdout );
3752 ur_strFree( &str );
3753
3754 if (flags & PrintNewline)
3755 {
3756 putc( '\n', stdout );
3757#ifdef _WIN32
3758 // Unix line buffers stdout if attached to a terminal (see setvbuf)
3759 // but Windows does not. A manual flush is required or else the
3760 // user may not see any feedback until the program exits.
3761 fflush( stdout );
3762#endif
3763 }
3764}
3765
3766
3767/*-cf-
3768 probe
3769 value
3770 return: value
3771 group: io
3772 see: mold, print
3773
3774 Print value with its datatype syntax features.
3775*/
3776CFUNC(cfunc_probe)
3777{
3778 _printValue(ut, a1, PrintNewline);
3779 *res = *a1;
3780 return UR_OK;
3781}
3782
3783
3784static UStatus _printReduced(UThread* ut, EvalFrameInvoke* ef)
3785{
3786 UCell tmp;
3787 ur_initSeries(&tmp, UT_BLOCK, ef->userBuf);
3788
3789 _printValue(ut, &tmp, PrintAsText | ef->state);
3790 ur_setId(ef->result, UT_UNSET);
3791 return UR_OK;
3792}
3793
3794
3795/*-cf-
3796 prin
3797 value
3798 return: unset!
3799 group: io
3800 see: print
3801
3802 Print reduced value without a trailing linefeed.
3803*/
3804CFUNC(cfunc_prin)
3805{
3806 if (ur_is(a1, UT_BLOCK))
3807 {
3808 EvalFrame* ef = boron_reframeReduce(ut, a1, res, _printReduced);
3809 ef->invoke.state = 0;
3810 return CFUNC_REFRAMED;
3811 }
3812
3813 _printValue(ut, a1, PrintAsText);
3814 ur_setId(res, UT_UNSET);
3815 return UR_OK;
3816}
3817
3818
3819/*-cf-
3820 print
3821 value
3822 return: unset!
3823 group: io
3824 see: prin, probe
3825
3826 Print reduced value and a trailing linefeed.
3827*/
3828CFUNC(cfunc_print)
3829{
3830 if (ur_is(a1, UT_BLOCK))
3831 {
3832 EvalFrame* ef = boron_reframeReduce(ut, a1, res, _printReduced);
3833 ef->invoke.state = PrintNewline;
3834 return CFUNC_REFRAMED;
3835 }
3836
3837 _printValue(ut, a1, PrintAsText | PrintNewline);
3838 ur_setId(res, UT_UNSET);
3839 return UR_OK;
3840}
3841
3842
3843/*-cf-
3844 to-text
3845 value
3846 return: string!
3847 group: data
3848 see: mold
3849
3850 Convert value to text without datatype syntax features.
3851
3852 This example compares to-text with mold:
3853 values: ["str" 'c' [a b]]
3854 to-text values
3855 == "str c a b"
3856 mold values
3857 == {["str" 'c' [a b]]}
3858*/
3859CFUNC(cfunc_to_text)
3860{
3861 int enc = UR_ENC_LATIN1;
3862 int len = 0;
3863
3864 if( ur_isStringType( ur_type(a1) ) )
3865 {
3866 const UBuffer* str = ur_bufferSer(a1);
3867 enc = str->form;
3868 len = str->used;
3869 }
3870 ur_toText( ut, a1, ur_makeStringCell( ut, enc, len, res ) );
3871 return UR_OK;
3872}
3873
3874
3875void ur_setCellI64( UCell* cell, int64_t n )
3876{
3877 ur_setId(cell, UT_INT);
3878 ur_int(cell) = n;
3879}
3880
3881
3882/*-cf-
3883 exists?
3884 path file!/string!
3885 return: True if file or directory exists.
3886 group: os
3887 see: dir?, info?
3888
3889 Test if path exists in the filesystem.
3890*/
3891/*-cf-
3892 dir?
3893 path file!/string!
3894 return: logic! or none! if path does not exist.
3895 group: os
3896 see: exists?, info?
3897
3898 Test if path is a directory.
3899*/
3900/*-cf-
3901 info?
3902 path file!/string!
3903 return: block! of information or none! if path does not exist.
3904 group: os
3905 see: exists?, dir?
3906
3907 Get information about a file. The values returned are the file type,
3908 byte size, modification date, path, and permissions.
3909
3910 Example:
3911 info? %Makefile
3912 == [file 9065 2013-10-13T17:15:51-07:00 %Makefile 7,5,5,0]
3913*/
3914CFUNC(cfunc_infoQ)
3915{
3916 static const uint8_t _infoMask[3] =
3917 {
3918 FI_Type, FI_Type, FI_Size | FI_Time | FI_Type
3919 };
3920 static const char* _infoType[5] =
3921 {
3922 "file", "link", "dir", "socket", "other"
3923 };
3924 OSFileInfo info;
3925 int ok;
3926 int func = ur_int(a2);
3927
3928
3929 assert( func >= 0 && func <= 2 );
3930 ok = ur_fileInfo( boron_cpath(ut, a1, 0), &info, _infoMask[ func ] );
3931 switch( func )
3932 {
3933 case 0:
3934set_logic:
3935 ur_setId(res, UT_LOGIC);
3936 ur_logic(res) = ok ? 1 : 0;
3937 return UR_OK;
3938
3939 case 1:
3940 if( ok )
3941 {
3942 ok = (info.type == FI_Dir);
3943 goto set_logic;
3944 }
3945 break;
3946
3947 case 2:
3948 if( ok )
3949 {
3950 const char* tn = _infoType[ info.type ];
3951 UCell* cell;
3952 UBuffer* blk = ur_makeBlockCell( ut, UT_BLOCK, 5, res );
3953 blk->used = 5;
3954 cell = blk->ptr.cell;
3955
3956 ur_setId(cell, UT_WORD);
3957 ur_setWordUnbound( cell, ur_intern( ut, tn, strLen(tn) ) );
3958 ++cell;
3959
3960 ur_setCellI64( cell, info.size );
3961 ++cell;
3962
3963 ur_setId(cell, UT_DATE);
3964 ur_double(cell) = info.modified;
3965 ++cell;
3966
3967 *cell++ = *a1;
3968
3969 ur_initCoord(cell, 4);
3970 memCpy( cell->coord.n, info.perm, sizeof(int16_t) * 4 );
3971 return UR_OK;
3972 }
3973 break;
3974 }
3975 ur_setId(res, UT_NONE);
3976 return UR_OK;
3977}
3978
3979
3980extern int ur_makeDir( UThread* ut, const char* path );
3981
3982static int _makeDirParents( UThread* ut, char* path, char* end )
3983{
3984#define MAX_PATH_PARTS 16
3985 OSFileInfo info;
3986 uint16_t index[ MAX_PATH_PARTS ];
3987 char* it = path + 1;
3988 int parts = 0;
3989 int i = 0;
3990
3991 while( it != end )
3992 {
3993 if( (*it == '/' || *it == '\\') && (it[-1] != ':') )
3994 {
3995 *it = '\0';
3996 if( parts >= MAX_PATH_PARTS )
3997 break;
3998 index[ parts++ ] = it - path;
3999 }
4000 ++it;
4001 }
4002
4003 while( (i < parts) && ur_fileInfo( path, &info, FI_Type ) )
4004 path[ index[ i++ ] ] = '/';
4005
4006 while( i < parts )
4007 {
4008 if( ! ur_makeDir( ut, path ) )
4009 return UR_THROW;
4010 path[ index[ i++ ] ] = '/';
4011 }
4012 return UR_OK;
4013}
4014
4015
4016/*-cf-
4017 make-dir
4018 dir file!/string!
4019 /all Make any missing parent directories.
4020 return: unset!
4021 group: os
4022
4023 If the directory already exists then the function returns normally.
4024 If the specified path points to an existing file or fails for another
4025 reason then an error is thrown.
4026*/
4027CFUNC(cfunc_make_dir)
4028{
4029#define OPT_MAKE_DIR_ALL 0x01
4030 UBuffer* bin = &BT->tbin;
4031 char* path = boron_cpath( ut, a1, bin );
4032
4033 if( ! boron_requestAccess( ut, "Make directory \"%s\"", path ) )
4034 return UR_THROW;
4035
4036 if( CFUNC_OPTIONS & OPT_MAKE_DIR_ALL )
4037 {
4038 if( ! _makeDirParents( ut, path, path + bin->used ) )
4039 return UR_THROW;
4040 }
4041 if( ! ur_makeDir( ut, path ) )
4042 return UR_THROW;
4043 ur_setId(res, UT_UNSET);
4044 return UR_OK;
4045}
4046
4047
4048#include <errno.h>
4049
4050/*-cf-
4051 change-dir
4052 dir string!/file!
4053 return: unset!
4054 group: os
4055
4056 Set current working directory.
4057*/
4058CFUNC(cfunc_change_dir)
4059{
4060#ifdef _WIN32
4061#define chdir _chdir
4062#endif
4063 if( chdir( boron_cstr(ut, a1, 0) ) == 0 )
4064 {
4065 ur_setId(res, UT_UNSET);
4066 return UR_OK;
4067 }
4068 return ur_error( ut, UR_ERR_ACCESS, strerror(errno) );
4069}
4070
4071
4072/*-cf-
4073 current-dir
4074 return: File! of current working directory.
4075 group: os
4076*/
4077CFUNC(cfunc_current_dir)
4078{
4079#ifdef _WIN32
4080#define getcwd _getcwd
4081#define DIR_SLASH '\\'
4082#else
4083#define DIR_SLASH '/'
4084#endif
4085 UBuffer* str;
4086 (void) a1;
4087
4088 str = ur_makeStringCell( ut, UR_ENC_LATIN1, 512, res );
4089 ur_type(res) = UT_FILE;
4090 if( getcwd( str->ptr.c, 512 ) )
4091 {
4092 str->used = strLen( str->ptr.c );
4093 if( str->ptr.c[ str->used - 1 ] != DIR_SLASH )
4094 {
4095 str->ptr.c[ str->used ] = DIR_SLASH;
4096 ++str->used;
4097 }
4098 return UR_OK;
4099 }
4100 return ur_error( ut, UR_ERR_ACCESS, strerror(errno) );
4101}
4102
4103
4104/*-cf-
4105 getenv
4106 name string!
4107 return: string! or none!
4108 group: os
4109 see: setenv
4110
4111 Get operating system environment variable.
4112
4113 Example:
4114 getenv "PATH"
4115 == {/usr/local/bin:/usr/bin:/bin:/usr/games:/home/karl/bin}
4116*/
4117CFUNC(cfunc_getenv)
4118{
4119 const char* cp = getenv( boron_cstr(ut, a1, 0) );
4120 if( cp )
4121 {
4122 UBuffer* str;
4123 int len = strLen(cp);
4124
4125 str = ur_makeStringCell( ut, UR_ENC_LATIN1, len, res );
4126 memCpy( str->ptr.c, cp, len );
4127 str->used = len;
4128 }
4129 else
4130 ur_setId(res, UT_NONE);
4131 return UR_OK;
4132}
4133
4134
4135/*-cf-
4136 setenv
4137 name string!
4138 value
4139 return: value
4140 group: os
4141 see: getenv
4142
4143 Set operating system environment variable. Pass a value of none! to
4144 unset the variable.
4145*/
4146CFUNC(cfunc_setenv)
4147{
4148#ifdef _WIN32
4149#define setenv(name,val,over) SetEnvironmentVariable(name, val)
4150#define unsetenv(name) SetEnvironmentVariable(name, 0)
4151#endif
4152 const char* name = boron_cstr(ut, a1, 0);
4153
4154 if( ur_is(a1, UT_NONE) )
4155 {
4156 unsetenv( name );
4157 }
4158 else
4159 {
4160 UBuffer* str = ur_makeStringCell( ut, UR_ENC_UTF8, 0, res );
4161 ur_toStr( ut, a2, str, 0 );
4162 ur_strTermNull( str );
4163 setenv( name, str->ptr.c, 1 );
4164 }
4165
4166 *res = *a2;
4167 return UR_OK;
4168}
4169
4170
4171/*-cf-
4172 open
4173 device int!/string!/file!/block!
4174 /read Read-only mode.
4175 /write Write-only mode.
4176 /new Create empty file.
4177 /nowait Non-blocking reads.
4178 return: port!
4179 group: io
4180 see: close
4181
4182 Create port!.
4183
4184 Network ports are created when the device is a URI with a "tcp://" or
4185 "udp://" scheme.
4186*/
4187CFUNC(cfunc_open)
4188{
4189 if( ur_is(a1, UT_FILE) )
4190 return port_file.open( ut, &port_file, a1, CFUNC_OPTIONS, res );
4191 return port_makeOpt( ut, a1, CFUNC_OPTIONS, res );
4192}
4193
4194
4195#if 0
4196/*
4197 close
4198 port port!
4199 return: unset!
4200
4201 Destroy port!.
4202*/
4203CFUNC(cfunc_close)
4204{
4205 UBuffer* buf;
4206 if( ! ur_is(a1, UT_PORT) )
4207 return ur_error( ut, UR_ERR_TYPE, "close expected port!" );
4208 if( ! (buf = ur_bufferSerM(a1)) )
4209 return UR_THROW;
4210 DT( UT_PORT )->destroy( buf );
4211 ur_setId(res, UT_UNSET);
4212 return UR_OK;
4213}
4214#endif
4215
4216
4217#define OPT_READ_TEXT 0x01
4218#define OPT_READ_INTO 0x02
4219#define OPT_READ_APPEND 0x04
4220#define OPT_READ_PART 0x08
4221
4222/*
4223 \param len Default length.
4224
4225 Return read length of /part or the greater of default size and the
4226 /into buffer available memory. A /part less than zero returns zero.
4227 If an error is thrown -1 is returned.
4228*/
4229static int _readBuffer( UThread* ut, uint32_t opt, const UCell* a1,
4230 UCell* res, int len )
4231{
4232 int n;
4233
4234 if( opt & OPT_READ_PART )
4235 {
4236 n = ur_int(CFUNC_OPT_ARG(4));
4237 len = (n > 1) ? n : 0;
4238 }
4239
4240 if( opt & (OPT_READ_INTO | OPT_READ_APPEND) )
4241 {
4242 UBuffer* buf;
4243 const UCell* ic;
4244 int type;
4245 int rlen;
4246
4247 ic = CFUNC_OPT_ARG( (opt & OPT_READ_APPEND ? 3 : 2) );
4248 type = ur_type(ic);
4249
4250 if( type == UT_BINARY || type == UT_STRING )
4251 {
4252 buf = ur_bufferSerM(ic);
4253 if( ! buf )
4254 return -1;
4255 if( opt & OPT_READ_INTO )
4256 buf->used = 0;
4257 rlen = len + buf->used;
4258
4259 n = ur_testAvail( buf );
4260 if( n < rlen )
4261 {
4262 if( type == UT_STRING )
4263 {
4264 if( ur_strIsUcs2( buf ) )
4265 {
4266 errorType( "cannot read /into UCS2 string!" );
4267 return -1;
4268 }
4269 ur_arrReserve( buf, rlen );
4270 }
4271 else
4272 {
4273 ur_binReserve( buf, rlen );
4274 }
4275 //printf( "KR resb len:%d used:%d %d->%d\n",
4276 // len, buf->used, n, ur_avail( buf ) );
4277 n = ur_avail( buf );
4278 }
4279
4280 if( ! (opt & OPT_READ_PART) )
4281 {
4282 len = n;
4283 if( opt & OPT_READ_APPEND )
4284 len -= buf->used;
4285 }
4286
4287 *res = *ic;
4288 }
4289 else
4290 {
4291 errorType( "read /into expected binary!/string! buffer" );
4292 return -1;
4293 }
4294 }
4295 else if( opt & OPT_READ_TEXT )
4296 {
4297 ur_makeStringCell( ut, UR_ENC_UTF8, len, res );
4298 }
4299 else
4300 {
4301 ur_makeBinaryCell( ut, len, res );
4302 }
4303 return len;
4304}
4305
4306
4307CFUNC_PUB(cfunc_readPort)
4308{
4309 PORT_SITE(dev, pbuf, a1);
4310 int len;
4311
4312 if( ! dev )
4313 return errorScript( "cannot read from closed port" );
4314
4315 len = dev->defaultReadLen;
4316 if( len > 0 )
4317 {
4318 len = _readBuffer( ut, CFUNC_OPTIONS, a1, res, len ); // gc!
4319 if( len <= 0 )
4320 return (len < 0) ? UR_THROW : UR_OK;
4321 pbuf = ur_buffer( a1->port.buf ); // Re-aquire
4322 }
4323
4324 return dev->read( ut, pbuf, res, len );
4325}
4326
4327
4328extern int ur_readDir( UThread*, const char* filename, UCell* res );
4329
4330/*-cf-
4331 read
4332 source file!/string!/port!
4333 /text Read as text rather than binary.
4334 /into Put data into existing buffer.
4335 buffer binary!/string!
4336 /append Append data to existing buffer.
4337 abuf binary!/string!
4338 /part Read a specific number of bytes.
4339 size int!
4340 return: none!/binary!/string!/block!/port!
4341 group: io
4342 see: load, write
4343
4344 Read binary! or UTF-8 string!. None is returned when nothing is read
4345 (e.g. if the end of a file is reached or a TCP socket is disconnected).
4346
4347 When source is a file name the entire file will be read into memory
4348 unless /part is used.
4349
4350 If the /text option is used or the /into buffer is a string! then the
4351 file is read as UTF-8 data and carriage returns are filtered on Windows.
4352
4353 If source is a directory name then a block containing file names is
4354 returned.
4355
4356 When source is a TCP listen socket port! then a new connected socket port!
4357 is created.
4358*/
4359CFUNC(cfunc_read)
4360{
4361 const char* filename;
4362 OSFileInfo info;
4363 uint32_t opt;
4364 int len;
4365
4366
4367 if( ur_is(a1, UT_PORT) )
4368 return cfunc_readPort( ut, a1, res );
4369
4370 if( ! ur_isStringType( ur_type(a1) ) )
4371 return errorType( "read expected file!/string!/port! source" );
4372
4373 filename = boron_cpath( ut, a1, 0 );
4374
4375 if( ! ur_fileInfo( filename, &info, FI_Size | FI_Type ) )
4376 return ur_error( ut, UR_ERR_ACCESS,
4377 "could not access file %s", filename );
4378
4379 if( info.type == FI_Dir )
4380 return ur_readDir( ut, filename, res );
4381
4382 opt = CFUNC_OPTIONS;
4383 len = _readBuffer( ut, opt, a1, res, (int) info.size ); // gc!
4384 if( len > 0 )
4385 {
4386 UBuffer* dest;
4387 const char* mode;
4388 FILE* fp;
4389 size_t n;
4390
4391#ifdef _WIN32
4392 if( ur_is(res, UT_STRING) || (opt & OPT_READ_TEXT) )
4393 mode = "r"; // Read as text to filter carriage ret.
4394 else
4395#endif
4396 mode = "rb";
4397
4398 fp = fopen( filename, mode );
4399 if( ! fp )
4400 {
4401 return ur_error( ut, UR_ERR_ACCESS,
4402 "could not open file %s", filename );
4403 }
4404 dest = ur_buffer( res->series.buf );
4405 n = fread( dest->ptr.b + dest->used, 1, len, fp );
4406 if( n > 0 )
4407 {
4408 dest->used += n;
4409 if( dest->type == UT_STRING )
4410 ur_strFlatten( dest );
4411 }
4412 else if( ferror( fp ) )
4413 {
4414 fclose( fp );
4415 return ur_error( ut, UR_ERR_ACCESS, "fread error %s", filename );
4416 }
4417 else
4418 {
4419 ur_setId(res, UT_NONE);
4420 }
4421 fclose( fp );
4422 }
4423 else if( len < 0 )
4424 return UR_THROW;
4425 else
4426 ur_setId(res, UT_NONE);
4427 return UR_OK;
4428}
4429
4430
4431/*-cf-
4432 write
4433 dest file!/string!/port!
4434 data binary!/string!/context!
4435 /append
4436 /text Emit new lines with carriage returns on Windows.
4437 return: unset!
4438 group: io
4439 see: read, save
4440*/
4441CFUNC(cfunc_write)
4442{
4443#define OPT_WRITE_APPEND 0x01
4444#define OPT_WRITE_TEXT 0x02
4445 const UCell* data = a2;
4446
4447 if( ur_is(a1, UT_PORT) )
4448 {
4449 PORT_SITE(dev, pbuf, a1);
4450 if( ! dev )
4451 return errorScript( "cannot write to closed port" );
4452 return dev->write( ut, pbuf, data );
4453 }
4454
4455 if( ! ur_isStringType( ur_type(a1) ) )
4456 return errorType( "write expected file!/string!/port! dest" );
4457
4458 if( ur_is(data, UT_CONTEXT) )
4459 {
4460 UBuffer* str = ur_makeStringCell( ut, UR_ENC_UTF8, 0, res );
4461 ut->types[ UT_CONTEXT ]->toText( ut, data, str, 0 );
4462 data = res;
4463 }
4464
4465 if( ur_is(data, UT_BINARY) || ur_is(data, UT_STRING) )
4466 {
4467 FILE* fp;
4468 const char* filename;
4469 const char* mode;
4470 USeriesIter si;
4471 UIndex size;
4472 size_t n;
4473
4474 filename = boron_cstr( ut, a1, 0 );
4475
4476 if( ! boron_requestAccess( ut, "Write file \"%s\"", filename ) )
4477 return UR_THROW;
4478
4479 ur_seriesSlice( ut, &si, data );
4480 size = si.end - si.it;
4481
4482 if( ur_is(data, UT_STRING) )
4483 {
4484 if( ur_strIsUcs2(si.buf) ||
4485 ((si.buf->form == UR_ENC_LATIN1) && ! ur_strIsAscii(si.buf)) )
4486 {
4487 // Convert to UTF-8.
4488
4489 UIndex nn = ur_makeString( ut, UR_ENC_UTF8, 0 );
4490 // si.buf is invalid after make.
4491 si.buf = ur_buffer(nn);
4492 ur_strAppend( (UBuffer*) si.buf, ur_bufferSer(data),
4493 si.it, si.end );
4494 si.it = 0;
4495 size = si.buf->used;
4496 }
4497 }
4498
4499 n = CFUNC_OPTIONS;
4500 {
4501 int append = n & OPT_WRITE_APPEND;
4502 if( n & OPT_WRITE_TEXT )
4503 mode = append ? "a" : "w";
4504 else
4505 mode = append ? "ab" : "wb";
4506 }
4507
4508 fp = fopen( filename, mode );
4509 if( ! fp )
4510 {
4511 return ur_error( ut, UR_ERR_ACCESS,
4512 "could not open %s", filename );
4513 }
4514
4515 n = fwrite( si.buf->ptr.b + si.it, 1, size, fp );
4516 fclose( fp );
4517
4518 ur_setId(res, UT_UNSET);
4519 return UR_OK;
4520 }
4521 else
4522 return errorType( "write expected binary!/string!/context! data" );
4523}
4524
4525
4526/*-cf-
4527 delete
4528 file file!/string!
4529 return: unset! or error thrown.
4530 group: os
4531*/
4532CFUNC(cfunc_delete)
4533{
4534 const char* fn;
4535
4536 fn = boron_cstr(ut, a1, 0);
4537 if( ! boron_requestAccess( ut, "Delete file \"%s\"", fn ) )
4538 return UR_THROW;
4539
4540 if( remove( fn ) != 0 )
4541 return ur_error( ut, UR_ERR_ACCESS, strerror(errno) );
4542 ur_setId(res, UT_UNSET);
4543 return UR_OK;
4544}
4545
4546
4547/*-cf-
4548 rename
4549 file file!/string!
4550 new-name file!/string!
4551 return: unset! or error thrown.
4552 group: os
4553*/
4554CFUNC(cfunc_rename)
4555{
4556 const char* cp1;
4557 const char* cp2;
4558 UIndex binN;
4559 int ok;
4560
4561 binN = ur_makeBinary(ut, 0); // gc!
4562
4563 cp1 = boron_cstr(ut, a1, 0);
4564 cp2 = boron_cstr(ut, a2, ur_buffer(binN));
4565
4566 if( ! boron_requestAccess( ut, "Rename file \"%s\"", cp1 ) )
4567 return UR_THROW;
4568
4569#ifdef _WIN32
4570 // We want Unix rename overwrite behavior.
4571 {
4572 OSFileInfo info;
4573 ok = ur_fileInfo( cp2, &info, FI_Type );
4574 if( ok && (info.type == FI_File) )
4575 {
4576 if( remove( cp2 ) == -1 )
4577 return ur_error( ut, UR_ERR_ACCESS, strerror(errno) );
4578 }
4579 }
4580#endif
4581 ok = rename( cp1, cp2 );
4582 if( ok != 0 )
4583 return ur_error( ut, UR_ERR_ACCESS, strerror(errno) );
4584 ur_setId(res, UT_UNSET);
4585 return UR_OK;
4586}
4587
4588
4589/*-cf-
4590 serialize
4591 data block!
4592 return: binary!
4593 group: data
4594 see: unserialize
4595
4596 Pack data into binary image for transport.
4597 Series positions, slices, and non-global word bindings are retained.
4598*/
4599CFUNC( cfunc_serialize )
4600{
4601 return ur_serialize( ut, a1->series.buf, res );
4602}
4603
4604
4605/*-cf-
4606 unserialize
4607 data binary!
4608 return: Re-materialized block!.
4609 group: data
4610 see: serialize
4611*/
4612CFUNC( cfunc_unserialize )
4613{
4614 UBinaryIter bi;
4615 ur_binSlice( ut, &bi, a1 );
4616 return ur_unserialize( ut, bi.it, bi.end, res );
4617}
4618
4619
4620extern int ur_serializedHeader( const uint8_t* data, int len );
4621
4622/*-cf-
4623 load
4624 file file!/string!/binary!
4625 return: block! or none! if file is empty.
4626 group: io
4627 see: read, save
4628
4629 Load file or serialized data with default bindings.
4630*/
4631CFUNC(cfunc_load)
4632{
4633 if( ur_is(a1, UT_BINARY) )
4634 {
4635 if( cfunc_unserialize( ut, a1, res ) )
4636 {
4637bind_sb:
4638 boron_bindDefault( ut, res->series.buf );
4639 return UR_OK;
4640 }
4641 }
4642 else
4643 {
4644 UCell args[2];
4645
4646 ur_setId(args, UT_UNSET); // Clear read CFUNC_OPTIONS.
4647 args[1] = *a1;
4648
4649 if( BENV->funcRead( ut, args + 1, res ) )
4650 {
4651 const uint8_t* cp;
4652 UBuffer* bin;
4653 UIndex hold;
4654 UIndex blkN;
4655#if CONFIG_COMPRESS == 2
4656check_str:
4657#endif
4658 bin = ur_buffer( res->series.buf );
4659 if( ! bin->used )
4660 {
4661 ur_setId(res, UT_NONE);
4662 return UR_OK;
4663 }
4664
4665 // Skip any Unix shell interpreter line.
4666 cp = bin->ptr.b;
4667 if( cp[0] == '#' && cp[1] == '!' )
4668 {
4669 cp = find_uint8_t( cp, cp + bin->used, '\n' );
4670 if( ! cp )
4671 cp = bin->ptr.b;
4672 }
4673 else if( ur_serializedHeader( cp, bin->used ) )
4674 {
4675 if( ! ur_unserialize( ut, cp, cp + bin->used, res ) )
4676 return UR_THROW;
4677 goto bind_sb;
4678 }
4679#if CONFIG_COMPRESS == 2
4680 else if( bin->used > (12 + 8) )
4681 {
4682 const uint8_t* pat = (const uint8_t*) "BZh";
4683 cp = find_pattern_8( cp, cp + 12, pat, pat + 3 );
4684 if( cp && (cp[3] >= '1') && (cp[3] <= '9') )
4685 {
4686 *args = *res;
4687 hold = ur_hold( res->series.buf );
4688 blkN = cfunc_decompress( ut, args, res );
4689 ur_release( hold );
4690 if( ! blkN )
4691 return UR_THROW;
4692 goto check_str;
4693 }
4694 else
4695 cp = bin->ptr.b;
4696 }
4697#endif
4698
4699 hold = ur_hold( res->series.buf );
4700 blkN = ur_tokenize( ut, (char*) cp, bin->ptr.c + bin->used, res );
4701 ur_release( hold );
4702
4703 if( blkN )
4704 {
4705 boron_bindDefault( ut, blkN );
4706 return UR_OK;
4707 }
4708 }
4709 }
4710 return UR_THROW;
4711}
4712
4713
4714/*-cf-
4715 save
4716 dest file!/string!/port!
4717 data
4718 return: unset!
4719 group: io
4720 see: load, write
4721
4722 Convert data to string! then write it. Use 'load to restore the data.
4723*/
4724CFUNC(cfunc_save)
4725{
4726 UCell args[3];
4727 UBuffer* str;
4728
4729 ur_setId(args, UT_UNSET); // Clear write CFUNC_OPTIONS.
4730 args[1] = *a1;
4731
4732 str = ur_makeStringCell( ut, UR_ENC_UTF8, 0, res );
4733 ur_toStr( ut, a2, str, -1 );
4734 if( str->used && str->ptr.c[ str->used - 1 ] != '\n' )
4735 ur_strAppendChar( str, '\n' );
4736 args[2] = *res;
4737
4738 return cfunc_write( ut, args + 1, res );
4739}
4740
4741
4742/*-cf-
4743 split
4744 input Series to split.
4745 delim Delimiter value.
4746 /keep Keep any empty parts left by subsequent delimiters.
4747 return: Block of slices from input.
4748 group: data
4749 see: parse
4750*/
4751CFUNC(cfunc_split)
4752{
4753 USeriesIter si;
4754 UCell tmp;
4755 UBuffer* out;
4756 const USeriesType* dt;
4757 const UCell* delim = a2;
4758 int type = ur_type(a1);
4759 int keepEmpty = CFUNC_OPTIONS & 1;
4760 int n;
4761
4762 if( ! ur_isSeriesType( type ) )
4763 return boron_badArg( ut, type, 0 );
4764
4765 dt = SERIES_DT( type );
4766 out = ur_makeBlockCell( ut, UT_BLOCK, 0, res );
4767 tmp = *a1;
4768
4769 ur_seriesSlice( ut, &si, a1 );
4770 while( (n = dt->find( ut, &si, delim, 0 )) >= 0 )
4771 {
4772 if( keepEmpty || n > si.it )
4773 {
4774 tmp.series.it = si.it;
4775 tmp.series.end = n;
4776 ur_blkPush( out, &tmp );
4777 }
4778 si.it = n + 1;
4779 }
4780
4781 if( out->used )
4782 {
4783 if( si.it >= si.end && ! keepEmpty )
4784 return UR_OK;
4785 tmp.series.it = si.it;
4786 tmp.series.end = a1->series.end;
4787 }
4788 ur_blkPush( out, &tmp );
4789 return UR_OK;
4790}
4791
4792
4793extern UStatus ur_parseBlock( UThread* ut, UBuffer*, UIndex start, UIndex end,
4794 UIndex* parsePos, const UBuffer* ruleBlk,
4795 UStatus (*eval)( UThread*, const UCell* ) );
4796
4797extern UStatus ur_parseString( UThread* ut, UBuffer*, UIndex start, UIndex end,
4798 UIndex* parsePos, const UBuffer* ruleBlk,
4799 UStatus (*eval)( UThread*, const UCell* ), int );
4800
4801static UStatus parse_doBlock(UThread* ut, const UCell* blkC)
4802{
4803 EvalFrame* ef = boron_findEvalFrame(ut, EOP_RUN_RECURSE);
4804 assert(ef);
4805 return boron_evalBlock(ut, blkC->series.buf, ef->invoke.result);
4806}
4807
4808/*-cf-
4809 parse
4810 input string!/binary!/block!
4811 rules block!
4812 /case Character case must match when comparing strings.
4813 return: True if end of input reached.
4814 group: data
4815 see: split
4816*/
4817CFUNC(cfunc_parse)
4818{
4819#define OPT_PARSE_CASE 0x01
4820 uint32_t opt = CFUNC_OPTIONS;
4821 USeriesIterM si;
4822 const UBuffer* rules;
4823 UIndex callFrame;
4824 UIndex pos;
4825 UStatus ok = UR_THROW;
4826
4827 if( ! ur_seriesSliceM( ut, &si, a1 ) )
4828 return UR_THROW;
4829
4830 callFrame = boron_evalRecurse(ut, res);
4831 rules = ur_bufferSer(a2);
4832
4833 switch( ur_type(a1) )
4834 {
4835 case UT_BINARY:
4836 case UT_STRING:
4837 ok = ur_parseString( ut, si.buf, si.it, si.end, &pos, rules,
4838 parse_doBlock, opt & OPT_PARSE_CASE );
4839 break;
4840 case UT_BLOCK:
4841 ok = ur_parseBlock( ut, si.buf, si.it, si.end, &pos, rules,
4842 parse_doBlock );
4843 break;
4844 }
4845
4846 BT->evalOp.used = callFrame; // Inline boron_evalSetTop(ut, callFrame)
4847 if( ! ok )
4848 return UR_THROW;
4849
4850 if( ur_isSliced(a1) )
4851 {
4852 // Result for a slice may be wrong if the series size gets changed.
4853 pos = (pos == si.end);
4854 }
4855 else
4856 {
4857 // Re-aquire.
4858 if( ! (si.buf = ur_bufferSerM(a1)) )
4859 return UR_THROW;
4860 // Pos can be greater than used if input was erased.
4861 pos = (pos >= si.buf->used);
4862 }
4863
4864 ur_setId(res, UT_LOGIC);
4865 ur_logic(res) = pos ? 1 : 0;
4866 return UR_OK;
4867}
4868
4869
4870/*-cf-
4871 same?
4872 a
4873 b
4874 return: True if two values are identical.
4875 group: data
4876 see: equal?
4877*/
4878CFUNC(cfunc_sameQ)
4879{
4880 ur_setId(res, UT_LOGIC);
4881 ur_logic(res) = ur_same( ut, a1, a2 ) ? 1 : 0;
4882 return UR_OK;
4883}
4884
4885
4886/*-cf-
4887 equal?
4888 a
4889 b
4890 return: True if two values are equivalent.
4891 group: data, math
4892 see: eq?, ne?, gt?, lt?, ge?, le?, same?
4893*/
4894CFUNC(cfunc_equalQ)
4895{
4896 ur_setId(res, UT_LOGIC);
4897 ur_logic(res) = ur_equal( ut, a1, a2 ) ? 1 : 0;
4898 return UR_OK;
4899}
4900
4901
4902/*-cf-
4903 ne?
4904 a
4905 b
4906 return: True if two values are not equivalent.
4907 group: data, math
4908 see: equal?, eq?, gt?, lt?, ge?, le?
4909*/
4910CFUNC(cfunc_neQ)
4911{
4912 ur_setId(res, UT_LOGIC);
4913 ur_logic(res) = ur_equal( ut, a1, a2 ) ? 0 : 1;
4914 return UR_OK;
4915}
4916
4917
4918/*-cf-
4919 gt?
4920 a
4921 b
4922 return: True if first value is greater than the second.
4923 group: math
4924 see: lt?, eq?, ne?, ge?, le?
4925*/
4926CFUNC(cfunc_gtQ)
4927{
4928 ur_setId(res, UT_LOGIC);
4929 ur_logic(res) = (ur_compare( ut, a1, a2 ) > 0) ? 1 : 0;
4930 return UR_OK;
4931}
4932
4933
4934/*-cf-
4935 lt?
4936 a
4937 b
4938 return: True if first value is less than the second.
4939 group: math
4940 see: gt?, eq?, ne?, ge?, le?
4941*/
4942CFUNC(cfunc_ltQ)
4943{
4944 ur_setId(res, UT_LOGIC);
4945 ur_logic(res) = (ur_compare( ut, a1, a2 ) < 0) ? 1 : 0;
4946 return UR_OK;
4947}
4948
4949
4950/*-cf-
4951 ge?
4952 a
4953 b
4954 return: True if first value is greater than or equal to the second.
4955 group: math
4956 see: le?, eq?, ne?, gt?, lt?
4957*/
4958CFUNC(cfunc_geQ)
4959{
4960 ur_setId(res, UT_LOGIC);
4961 ur_logic(res) = (ur_compare( ut, a1, a2 ) >= 0) ? 1 : 0;
4962 return UR_OK;
4963}
4964
4965
4966/*-cf-
4967 le?
4968 a
4969 b
4970 return: True if first value is less than or equal to the second.
4971 group: math
4972 see: ge?, eq?, ne?, gt?, lt?
4973*/
4974CFUNC(cfunc_leQ)
4975{
4976 ur_setId(res, UT_LOGIC);
4977 ur_logic(res) = (ur_compare( ut, a1, a2 ) <= 0) ? 1 : 0;
4978 return UR_OK;
4979}
4980
4981
4982/*-cf-
4983 zero?
4984 value int!/char!/double!
4985 return: logic!
4986 group: math
4987
4988 Return true if value is the number zero.
4989*/
4990CFUNC(cfunc_zeroQ)
4991{
4992 int logic;
4993 (void) ut;
4994
4995 if( ur_is(a1, UT_INT) || ur_is(a1, UT_CHAR) )
4996 logic = ur_int(a1) ? 0 : 1;
4997 else if( ur_is(a1, UT_DOUBLE) )
4998 logic = ur_double(a1) ? 0 : 1;
4999 else
5000 logic = 0;
5001
5002 ur_setId(res, UT_LOGIC);
5003 ur_logic(res) = logic;
5004 return UR_OK;
5005}
5006
5007
5008/*-cf-
5009 type?
5010 value
5011 return: Datatype of value.
5012 group: data
5013 see: to-_type_
5014
5015 Determine type of value.
5016*/
5017CFUNC(cfunc_typeQ)
5018{
5019 (void) ut;
5020 ur_makeDatatype( res, ur_type(a1) );
5021 return UR_OK;
5022}
5023
5024
5025/*-cf-
5026 swap
5027 data binary!/block!
5028 /group Specify number of elements to reverse
5029 size int!
5030 return: Modified data.
5031 group: series
5032 see: reverse
5033
5034 Swap adjacent elements of a series.
5035*/
5036CFUNC(cfunc_swap)
5037{
5038#define OPT_SWAP_GROUP 0x01
5039//#define OPT_SWAP_ORDER 0x02 /order block block!
5040 USeriesIterM si;
5041 if( ! ur_seriesSliceM( ut, &si, a1 ) )
5042 return UR_THROW;
5043
5044 if( CFUNC_OPTIONS & OPT_SWAP_GROUP )
5045 {
5046 int group = ur_int(CFUNC_OPT_ARG(1));
5047 if( group < 2 || group > (si.end - si.it) )
5048 return ur_error( ut, UR_ERR_SCRIPT,
5049 "swap group size (%d) is invalid", group );
5050 if( ur_is(a1, UT_BINARY) )
5051 {
5052 uint8_t* bp = si.buf->ptr.b + si.it;
5053 si.it += group;
5054 for( ; si.it <= si.end; si.it += group, bp += group )
5055 reverse_uint8_t( bp, bp + group );
5056 }
5057 }
5058 else
5059 {
5060 if( (si.end - si.it) & 1 )
5061 --si.end;
5062 if( ur_is(a1, UT_BINARY) )
5063 {
5064 uint8_t* bp = si.buf->ptr.b + si.it;
5065 uint8_t* bend = si.buf->ptr.b + si.end;
5066 int tmp;
5067 for( ; bp != bend; bp += 2 )
5068 {
5069 tmp = bp[0];
5070 bp[0] = bp[1];
5071 bp[1] = tmp;
5072 }
5073 }
5074 else
5075 {
5076 UCell* cp = si.buf->ptr.cell + si.it;
5077 UCell* cend = si.buf->ptr.cell + si.end;
5078 UCell tmp;
5079 for( ; cp != cend; cp += 2 )
5080 {
5081 tmp = cp[0];
5082 cp[0] = cp[1];
5083 cp[1] = tmp;
5084 }
5085 }
5086 }
5087
5088 *res = *a1;
5089 return UR_OK;
5090}
5091
5092
5093/*-cf-
5094 lowercase
5095 value char!/string!/file!
5096 return: Value converted to lowercase.
5097 group: series
5098 see: uppercase
5099*/
5100CFUNC(cfunc_lowercase)
5101{
5102 if( ur_isStringType( ur_type(a1) ) )
5103 {
5104 USeriesIterM si;
5105 if( ! ur_seriesSliceM( ut, &si, a1 ) )
5106 return UR_THROW;
5107 *res = *a1;
5108 ur_strLowercase( si.buf, si.it, si.end );
5109 return UR_OK;
5110 }
5111 else if( ur_is(a1, UT_CHAR) )
5112 {
5113 ur_setId(res, UT_CHAR);
5114 ur_int(res) = ur_charLowercase( ur_int(a1) );
5115 return UR_OK;
5116 }
5117 return boron_badArg( ut, ur_type(a1), 0 );
5118}
5119
5120
5121/*-cf-
5122 uppercase
5123 value char!/string!/file!
5124 return: Value converted to uppercase.
5125 group: series
5126 see: lowercase
5127*/
5128CFUNC(cfunc_uppercase)
5129{
5130 if( ur_isStringType( ur_type(a1) ) )
5131 {
5132 USeriesIterM si;
5133 if( ! ur_seriesSliceM( ut, &si, a1 ) )
5134 return UR_THROW;
5135 *res = *a1;
5136 ur_strUppercase( si.buf, si.it, si.end );
5137 return UR_OK;
5138 }
5139 else if( ur_is(a1, UT_CHAR) )
5140 {
5141 ur_setId(res, UT_CHAR);
5142 ur_int(res) = ur_charUppercase( ur_int(a1) );
5143 return UR_OK;
5144 }
5145 return boron_badArg( ut, ur_type(a1), 0 );
5146}
5147
5148
5149#define TRIM_FUNC_HEAD trim_head_u16
5150#define TRIM_FUNC_TAIL trim_tail_u16
5151#define TRIM_FUNC_LINES trim_lines_u16
5152#define TRIM_FUNC_INDENT trim_indent_u16
5153#define TRIM_T uint16_t
5154#include "trim_string.c"
5155
5156#define TRIM_FUNC_HEAD trim_head_char
5157#define TRIM_FUNC_TAIL trim_tail_char
5158#define TRIM_FUNC_LINES trim_lines_char
5159#define TRIM_FUNC_INDENT trim_indent_char
5160#define TRIM_T char
5161#include "trim_string.c"
5162
5163
5164/*-cf-
5165 trim
5166 string string!
5167 /indent Remove same amount of whitespace from start of all lines.
5168 /lines Remove all newlines and extra whitespace.
5169 return: Modified string.
5170 group: series
5171*/
5172CFUNC(cfunc_trim)
5173{
5174#define OPT_TRIM_INDENT 0x01
5175#define OPT_TRIM_LINES 0x02
5176 uint32_t opt = CFUNC_OPTIONS;
5177 USeriesIterM si;
5178 UIndex origEnd;
5179
5180 if( ! ur_seriesSliceM( ut, &si, a1 ) )
5181 return UR_THROW;
5182
5183 origEnd = si.end;
5184 *res = *a1;
5185
5186 //dprint( "KR trim %d\n", opt );
5187 if( opt & OPT_TRIM_INDENT )
5188 {
5189 if( ur_strIsUcs2(si.buf) )
5190 {
5191 uint16_t* ss = si.buf->ptr.u16;
5192 si.end -= trim_indent_u16( ss + si.it, ss + si.end );
5193 }
5194 else
5195 {
5196 char* ss = si.buf->ptr.c;
5197 si.end -= trim_indent_char( ss + si.it, ss + si.end );
5198 }
5199 }
5200 else if( opt & OPT_TRIM_LINES )
5201 {
5202 if( ur_strIsUcs2(si.buf) )
5203 {
5204 uint16_t* ss = si.buf->ptr.u16;
5205 si.it += trim_head_u16 ( ss + si.it, ss + si.end );
5206 si.end -= trim_tail_u16 ( ss + si.it, ss + si.end );
5207 si.end -= trim_lines_u16( ss + si.it, ss + si.end );
5208 }
5209 else
5210 {
5211 char* ss = si.buf->ptr.c;
5212 si.it += trim_head_char ( ss + si.it, ss + si.end );
5213 si.end -= trim_tail_char ( ss + si.it, ss + si.end );
5214 si.end -= trim_lines_char( ss + si.it, ss + si.end );
5215 }
5216 }
5217 else
5218 {
5219 if( ur_strIsUcs2(si.buf) )
5220 {
5221 uint16_t* ss = si.buf->ptr.u16;
5222 si.it += trim_head_u16( ss + si.it, ss + si.end );
5223 si.end -= trim_tail_u16( ss + si.it, ss + si.end );
5224 }
5225 else
5226 {
5227 char* ss = si.buf->ptr.c;
5228 si.it += trim_head_char( ss + si.it, ss + si.end );
5229 si.end -= trim_tail_char( ss + si.it, ss + si.end );
5230 }
5231 }
5232
5233 res->series.it = si.it;
5234 if( si.end != origEnd )
5235 {
5236 if( ur_isSliced(res) )
5237 {
5238 // TODO: Erase from end.
5239 res->series.end = si.end;
5240 }
5241 else
5242 si.buf->used = si.end;
5243 }
5244 return UR_OK;
5245}
5246
5247
5248/*-cf-
5249 terminate
5250 series Series to append to.
5251 value Value to append.
5252 /dir Check if end is '/' or '\'.
5253 return: Modified series.
5254 group: series
5255 see: append
5256
5257 Append value to series only if it does not already end with it.
5258*/
5259CFUNC(cfunc_terminate)
5260{
5261#define OPT_TERMINATE_DIR 0x01
5262 USeriesIterM si;
5263 const UCell* val = a2;
5264 int type = ur_type(a1);
5265
5266 if( ! ur_isSeriesType( type ) )
5267 return boron_badArg( ut, type, 0 );
5268
5269 ur_seriesSliceM( ut, &si, a1 );
5270 if( si.it != si.end )
5271 {
5272 SERIES_DT( type )->pick( si.buf, si.end - 1, res );
5273 if( ur_equal( ut, val, res ) )
5274 goto done;
5275 if( CFUNC_OPTIONS & OPT_TERMINATE_DIR )
5276 {
5277 if( ur_is(res, UT_CHAR) &&
5278 (ur_char(res) == '/' || ur_char(res) == '\\') )
5279 goto done;
5280 }
5281 }
5282 if( ! SERIES_DT( type )->append( ut, si.buf, val ) )
5283 return UR_THROW;
5284done:
5285 *res = *a1;
5286 return UR_OK;
5287}
5288
5289
5290extern int64_t str_hexToInt64(const uint8_t*,const uint8_t*,const uint8_t**);
5291
5292/*-cf-
5293 to-hex
5294 number char!/int!/binary!/string!
5295 return: Number shown as hexidecimal.
5296 group: data
5297*/
5298CFUNC(cfunc_to_hex)
5299{
5300 switch( ur_type(a1) )
5301 {
5302 case UT_CHAR:
5303 *res = *a1;
5304 ur_type(res) = UT_INT;
5305 break;
5306
5307 case UT_INT:
5308 *res = *a1;
5309 break;
5310
5311 case UT_BINARY:
5312 case UT_STRING:
5313 {
5314 int64_t n;
5315 USeriesIter si;
5316 ur_seriesSlice( ut, &si, a1 );
5317 if( ur_strIsUcs2( si.buf ) && ur_is(a1, UT_STRING) )
5318 n = 0; // TODO: Implement for UCS2.
5319 else
5320 n = str_hexToInt64( si.buf->ptr.b + si.it,
5321 si.buf->ptr.b + si.end, 0 );
5322 ur_setCellI64( res, n );
5323 }
5324 break;
5325
5326 default:
5327 return boron_badArg( ut, ur_type(a1), 0 );
5328 }
5329 ur_setFlags(res, UR_FLAG_INT_HEX);
5330 return UR_OK;
5331}
5332
5333
5334/*-cf-
5335 to-dec
5336 number int!
5337 return: Number shown as decimal.
5338 group: data
5339*/
5340CFUNC(cfunc_to_dec)
5341{
5342 if( ur_is(a1, UT_INT) )
5343 {
5344 ur_clrFlags(a1, UR_FLAG_INT_HEX);
5345 *res = *a1;
5346 return UR_OK;
5347 }
5348 return boron_badArg( ut, ur_type(a1), 0 );
5349}
5350
5351
5352/*-cf-
5353 mark-sol
5354 value
5355 /block Mark block rather than value at block position.
5356 /clear Clear start of line flag.
5357 return: Value with flag set.
5358 group: data
5359
5360 Flag value so that it is printed at the start of a line.
5361*/
5362CFUNC(cfunc_mark_sol)
5363{
5364#define OPT_MARK_SOL_BLOCK 0x01
5365#define OPT_MARK_SOL_CLEAR 0x02
5366 uint32_t opt = CFUNC_OPTIONS;
5367 int type = ur_type(a1);
5368
5369 *res = *a1;
5370
5371 if( ur_isBlockType( type ) && ! (opt & OPT_MARK_SOL_BLOCK) )
5372 {
5373 UBlockIterM bi;
5374 if( ! ur_blkSliceM( ut, &bi, a1 ) )
5375 return UR_THROW;
5376 if( bi.it == bi.end )
5377 return UR_OK;
5378 res = bi.it;
5379 }
5380 if( opt & OPT_MARK_SOL_CLEAR )
5381 ur_clrFlags(res, UR_FLAG_SOL);
5382 else
5383 ur_setFlags(res, UR_FLAG_SOL);
5384 return UR_OK;
5385}
5386
5387
5388extern double ur_now();
5389
5390/*-cf-
5391 now
5392 /date Return date! rather than time!
5393 return: time! or date!
5394 group: io
5395*/
5396CFUNC(cfunc_now)
5397{
5398#define OPT_NOW_DATE 0x01
5399 uint32_t opt = CFUNC_OPTIONS;
5400 (void) ut;
5401
5402 if( opt & OPT_NOW_DATE )
5403 ur_setId(res, UT_DATE);
5404 else
5405 ur_setId(res, UT_TIME);
5406 ur_double(res) = ur_now();
5407 return UR_OK;
5408}
5409
5410
5411#include "cpuCounter.h"
5412
5413/*-cf-
5414 cpu-cycles
5415 loop int! Number of times to evaluate block.
5416 block block!
5417 return: int!
5418 group: io
5419
5420 Get the number of CPU cycles used to evaluate a block.
5421*/
5422#ifdef HAVE_CPU_COUNTER
5423#define CYCLES data.var.u64[0]
5424#define CYCLES_LOW data.var.u64[1]
5425
5426static UStatus loop_cpuCycles(UThread* ut, EvalFrameInvoke* invoke)
5427{
5428 EvalFrame* ef = ((EvalFrame*) invoke) - 1;
5429 if (ef->data.state)
5430 {
5431 ef->CYCLES = cpuCounter() - ef->CYCLES;
5432 if (ef->CYCLES < ef->CYCLES_LOW)
5433 ef->CYCLES_LOW = ef->CYCLES;
5434
5435 if (--invoke->userBuf == 0)
5436 {
5437 UCell* res = invoke->result;
5438 ur_setId(res, UT_INT);
5439 ur_int(res) = (int64_t) ef->CYCLES_LOW;
5440 return UR_OK;
5441 }
5442 }
5443 else
5444 ef->data.state = 1;
5445
5446 ef->CYCLES = cpuCounter();
5447
5448 ef = boron_pushEvalFrame(ut);
5449 ef->block = ef[-3].block;
5450 return CFUNC_REFRAMED;
5451}
5452#endif
5453
5454CFUNC(cfunc_cpu_cycles)
5455{
5456#ifdef HAVE_CPU_COUNTER
5457 int loop = ur_int(a1);
5458 int origStack;
5459
5460 // Signature verifies args.
5461
5462 EvalFrame* ef = boron_reuseFrame(ut, 2, &origStack);
5463 if (! ef)
5464 return UR_THROW;
5465 boron_initEvalBlock(ef, ut, a2->series.buf, res);
5466
5467 ++ef;
5468 ef->data.eop = EOP_NOP;
5469 ef->data.state = 0; // firstCall
5470 ef->CYCLES = 0;
5471 ef->CYCLES_LOW = ~0L;
5472
5473 ++ef;
5474 ef->invoke.eop = EOP_INVOKE_LOOP;
5475 ef->invoke.state = 3; // opCount
5476 ef->invoke.origStack = origStack;
5477 ef->invoke.userBuf = (loop < 1) ? 1 : loop;
5478 ef->invoke.func = loop_cpuCycles;
5479 ef->invoke.dat.catchf = catch_breakContinue;
5480 ef->invoke.result = res;
5481 return CFUNC_REFRAMED;
5482#else
5483 (void) a1;
5484 (void) res;
5485 return ur_error( ut, UR_ERR_INTERNAL,
5486 "FIXME: cpu-cycles is not implemented on this system" );
5487#endif
5488}
5489
5490
5491/*-cf-
5492 free
5493 resource series/port!
5494 return: unset!
5495 group: storage
5496 see: close, reserve
5497
5498 Clear series and free its memory buffer or close port.
5499*/
5500CFUNC_PUB(cfunc_free)
5501{
5502 UBuffer* buf;
5503 int type = ur_type(a1);
5504 if( ! ur_isSeriesType( type ) && (type != UT_PORT) )
5505 return boron_badArg( ut, type, 0 );
5506 if( ! (buf = ur_bufferSerM(a1)) )
5507 return UR_THROW;
5508 DT( type )->destroy( buf );
5509 ur_setId(res, UT_UNSET);
5510 return UR_OK;
5511}
5512
5513
5514#ifdef CONFIG_CHECKSUM
5515extern uint32_t ur_hash( const uint8_t* str, const uint8_t* end );
5516
5517/*-cf-
5518 hash
5519 string word!/string!
5520 return: int!
5521 group: data
5522
5523 Compute hash value from string (treated as lowercase).
5524*/
5525CFUNC(cfunc_hash)
5526{
5527 uint32_t hash = ur_type(a1);
5528
5529 if( ur_isStringType(hash) )
5530 {
5531 USeriesIter si;
5532 ur_seriesSlice( ut, &si, a1 );
5533 if( ur_strIsUcs2( si.buf ) )
5534 return errorType( "FIXME: hash does not handle UCS2 strings" );
5535 hash = ur_hash( si.buf->ptr.b + si.it, si.buf->ptr.b + si.end );
5536 }
5537 else if( ur_isWordType(hash) )
5538 {
5539 const uint8_t* str = (const uint8_t*) ur_wordCStr(a1);
5540 hash = ur_hash( str, str + strLen((const char*) str) );
5541 }
5542 else
5543 {
5544 return boron_badArg( ut, ur_type(a1), 0 );
5545 }
5546
5547 ur_setId(res, UT_INT);
5548 ur_setFlags(res, UR_FLAG_INT_HEX);
5549 ur_int(res) = hash;
5550 return UR_OK;
5551}
5552#endif
5553
5554
5555/*-cf-
5556 _datatype?_
5557 value
5558 return: True if value is a specific datatype.
5559 group: data
5560 see: to-_type_
5561
5562 Each datatype has its own test function which is named the same as the
5563 type but ending with '?' rather than a '!'.
5564
5565 This example shows testing for string! and int! types:
5566 string? 20
5567 == false
5568 int? 20
5569 == true
5570*/
5571CFUNC(cfunc_datatypeQ)
5572{
5573 (void) ut;
5574 ur_setId(res, UT_LOGIC);
5575 if( ur_type(a1) == ur_int(a2) ) // Type variation is in a2.
5576 ur_logic(res) = 1;
5577 return UR_OK;
5578}
5579
5580
5581/*-cf-
5582 to-_type_
5583 value Any value.
5584 return: New datatype!.
5585 group: data
5586 see: make, type?, _datatype?_
5587
5588 Convert a value to another datatype.
5589
5590 Each datatype has its own convert function which is named the same as the
5591 type but starting with "to-".
5592 For example, to convert a value to a string! use:
5593 to-string 20
5594 == "20"
5595*/
5596CFUNC(cfunc_to_type)
5597{
5598 // Type variation is in a2.
5599 return DT( ur_int(a2) )->convert( ut, a1, res );
5600}
5601
5602
5603/*-cf-
5604 collect
5605 types datatype!
5606 source block!/paren!
5607 /unique Only add equal values once.
5608 /into Add values to dest rather than a new block.
5609 dest block!
5610 return: New block containing matching values.
5611 group: data
5612
5613 Get all values of a certain type from source block.
5614*/
5615CFUNC(cfunc_collect)
5616{
5617#define OPT_COLLECT_UNIQUE 0x01
5618#define OPT_COLLECT_INTO 0x02
5619 UBuffer* dest;
5620 uint32_t opt = CFUNC_OPTIONS;
5621
5622 if( opt & OPT_COLLECT_INTO )
5623 {
5624 const UCell* into = CFUNC_OPT_ARG(2);
5625 if( ! (dest = ur_bufferSerM(into)) )
5626 return UR_THROW;
5627 *res = *into;
5628 }
5629 else
5630 {
5631 ur_makeBlockCell( ut, UT_BLOCK, 0, res );
5632 dest = ur_buffer(res->series.buf);
5633 }
5634 ur_blkCollectType( ut, a2, a1->datatype.mask0, dest,
5635 opt & OPT_COLLECT_UNIQUE );
5636 return UR_OK;
5637}
5638
5639
5640//EOF
int boron_reframeDoBlock(UThread *, UIndex blkN, UCell *res, int flags)
Reuse the current CFUNC call frame for a EOP_DO_BLOCK operation.
Definition eval.c:964
#define CFUNC(name)
Macro to define C functions.
Definition boron.h:57
void boron_bindDefault(UThread *, UIndex blkN)
Bind block in thread dataStore to default contexts.
Definition eval.c:2042
UStatus boron_badArg(UThread *, UIndex atom, int argN)
Throw a standardized error for an unexpected function argument.
Definition eval.c:696
#define CFUNC_OPTIONS
Macro to get uint16_t option flags from inside a C function.
Definition boron.h:59
UIndex boron_evalRecurse(UThread *, UCell *res)
Begin a section where boron_evalBlock() can be recursively called.
Definition eval.c:762
UStatus boron_throwWord(UThread *, UAtom atom, UIndex stackPos)
Throw named exception.
Definition boron.c:308
char * boron_cstr(UThread *, const UCell *strC, UBuffer *bin)
Make null terminated UTF-8 string in binary buffer.
Definition boron.c:244
UStatus boron_requestAccess(UThread *, const char *msg,...)
Request user permission to access a resource.
Definition boron.c:988
char * boron_cpath(UThread *, const UCell *strC, UBuffer *bin)
Make null terminated UTF-8 string in binary buffer.
Definition boron.c:260
UStatus ur_binSliceM(UThread *, UBinaryIterM *, const UCell *cell)
Set UBinaryIterM to binary slice.
Definition binary.c:456
UBuffer * ur_makeBinaryCell(UThread *, int size, UCell *cell)
Generate a single binary and set cell to reference it.
Definition binary.c:74
void ur_binReserve(UBuffer *, int size)
Allocates enough memory to hold size bytes.
Definition binary.c:138
void ur_binSlice(UThread *, UBinaryIter *, const UCell *cell)
Set UBinaryIter to binary slice.
Definition binary.c:423
UIndex ur_makeBinary(UThread *, int size)
Generate and initialize a single binary buffer.
Definition binary.c:56
UIndex ur_blkClone(UThread *, UIndex blkN)
Make deep copy of block.
Definition block.c:256
void ur_blkInsert(UBuffer *, UIndex it, const UCell *cells, int count)
Insert cells into block.
Definition block.c:143
void ur_blkCollectType(UThread *, const UCell *blkCell, uint32_t typeMask, UBuffer *dest, int unique)
Find all values of a certain type and append them to another block.
Definition block.c:297
void ur_blkPush(UBuffer *, const UCell *cell)
Copy cell to end of block.
Definition block.c:153
UStatus ur_blkSliceM(UThread *, UBlockIterM *, const UCell *cell)
Set UBlockIterM to block slice.
Definition block.c:224
void ur_blkAppendCells(UBuffer *, const UCell *cells, int count)
Append cells to block.
Definition block.c:125
UBuffer * ur_makeBlockCell(UThread *, int type, int size, UCell *cell)
Generate a single block and set cell to reference it.
Definition block.c:76
void ur_infuse(UThread *, UCell *it, UCell *end, const UBuffer *ctx)
Replace words in cells with their values from a context.
Definition context.c:837
UBuffer * ur_ctxClone(UThread *, const UBuffer *src, UCell *cell)
Clone a new context and set cell to reference it.
Definition context.c:220
int ur_ctxAddWordI(UBuffer *, UAtom atom)
Add word to context if it does not already exist.
Definition context.c:420
int ur_ctxLookup(const UBuffer *, UAtom atom)
Find word in context by atom.
Definition context.c:579
void ur_bindCells(UThread *, UCell *it, UCell *end, const UBindTarget *bt)
Bind an array of cells to a target.
Definition context.c:614
void ur_ctxSetWords(UBuffer *, const UCell *it, const UCell *end)
Add the set-word! values in a series of cells to the words in a context.
Definition context.c:283
UBuffer * ur_ctxSort(UBuffer *)
Sort the internal context search table so ur_ctxLookup() is faster.
Definition context.c:510
UIndex ur_makeContext(UThread *, int size)
Generate and initialize a single context.
Definition context.c:93
void ur_unbindCells(UThread *, UCell *it, UCell *end, int deep)
Unbind all words in an array of cells.
Definition context.c:790
void ur_bind(UThread *, UBuffer *blk, const UBuffer *ctx, int bindType)
Bind block to context.
Definition context.c:690
const UBuffer * ur_sortedContext(UThread *, const UCell *)
Get context and make sure it is fully sorted for minimal ur_ctxLookup() time.
Definition context.c:547
UStatus ur_setPath(UThread *, const UCell *path, const UCell *src)
Set path.
Definition path.c:361
#define ur_strFree
A string is a simple array.
Definition urlan.h:629
UBuffer * ur_makeStringCell(UThread *, int enc, int size, UCell *cell)
Generate a single string and set cell to reference it.
Definition string.c:104
int ur_strIsAscii(const UBuffer *)
Test if all characters are ASCII.
Definition string.c:1066
void ur_strAppendChar(UBuffer *, int)
Append a single UCS2 character to a string.
Definition string.c:611
UIndex ur_makeString(UThread *, int enc, int size)
Generate and initialize a single string buffer.
Definition string.c:85
void ur_strTermNull(UBuffer *)
Terminate with null character so buffer can be used as a C string.
Definition string.c:1049
void ur_strInit(UBuffer *, int enc, int size)
Initialize buffer to type UT_STRING.
Definition string.c:430
void ur_strAppend(UBuffer *, const UBuffer *strB, UIndex itB, UIndex endB)
Append another string buffer to this string.
Definition string.c:899
void ur_strLowercase(UBuffer *str, UIndex start, UIndex send)
Convert characters of string slice to lowercase.
Definition string.c:1178
void ur_strFlatten(UBuffer *)
Convert a UTF-8 or UCS-2 string buffer to Latin-1 if possible.
Definition string.c:1104
void ur_strUppercase(UBuffer *str, UIndex start, UIndex send)
Convert characters of string slice to uppercase.
Definition string.c:1216
void ur_seriesSlice(const UThread *, USeriesIter *si, const UCell *cell)
Set USeriesIter to series slice.
Definition env.c:1338
#define ur_bufferSer(c)
Convenience macro for ur_bufferSeries().
Definition urlan.h:752
#define ur_setId(c, t)
Set type and initialize the other 24 bits of UCellId to zero.
Definition urlan.h:701
UIndex ur_tokenize(UThread *, const char *it, const char *end, UCell *res)
Convert a UTF-8 data string into a block.
Definition tokenize.c:1197
UIndex it
Start position.
Definition urlan.h:338
UStatus ur_error(UThread *, int errorType, const char *fmt,...)
Create error! exception.
Definition env.c:964
const UCell * ur_wordCell(UThread *, const UCell *cell)
Get word value for read-only operations.
Definition env.c:1132
UAtom ur_intern(UThread *, const char *name, int len)
Add a single atom to the shared environment.
Definition env.c:596
const UCell * end
End position.
Definition urlan.h:388
int ur_same(UThread *, const UCell *a, const UCell *b)
Definition env.c:1041
void ur_toText(UThread *, const UCell *cell, UBuffer *str)
Append textual representation of cell to a string.
Definition env.c:1118
UStatus ur_seriesSliceM(UThread *, USeriesIterM *si, const UCell *cell)
Set USeriesIterM to modifiable series slice.
Definition env.c:1356
UIndex end
End position.
Definition urlan.h:339
const UCell * it
Start position.
Definition urlan.h:387
@ UR_THROW
Returned to indicate an evaluation exception occured.
Definition urlan.h:117
@ UR_OK
Returned to indicate successful evaluation/operation.
Definition urlan.h:118
#define ur_foreach(bi)
Loop over all members of an iterator struct.
Definition urlan.h:760
#define ur_hold(n)
Convenience macro for ur_holdBuffer().
Definition urlan.h:748
UBuffer * ur_generate(UThread *, int count, UIndex *index, const uint8_t *)
Generate and initialize buffers of given types.
Definition env.c:771
int ur_true(const UCell *cell)
Check if a value is "true".
Definition env.c:1029
#define ur_release(h)
Convenience macro for ur_releaseBuffer().
Definition urlan.h:749
#define ur_bufferSerM(c)
Convenience macro for ur_bufferSeriesM().
Definition urlan.h:753
const UBuffer * ur_blockIt(const UThread *ut, UBlockIt *bi, const UCell *blkCell)
Set UBlockIt to the start and end of a block slice.
Definition env.c:1388
int ur_equal(UThread *, const UCell *a, const UCell *b)
Definition env.c:1053
const UBuffer * buf
Buffer pointer.
Definition urlan.h:337
#define ur_isShared(n)
True if buffer number refers to a buffer in the shared environment.
Definition urlan.h:726
#define ur_isSliced(c)
True if the end member of a series cell is set.
Definition urlan.h:727
#define ur_buffer(n)
Macro to get buffer known to be in thread dataStore.
Definition urlan.h:750
int ur_compare(UThread *, const UCell *a, const UCell *b)
Definition env.c:1079
#define ur_type(c)
Return UrlanDataType of cell.
Definition urlan.h:695
UCell * ur_wordCellM(UThread *, const UCell *cell)
Get modifiable word value.
Definition env.c:1178
void ur_toStr(UThread *, const UCell *cell, UBuffer *str, int depth)
Append data representation of cell to a string.
Definition env.c:1107
UStatus ur_parseString(UThread *ut, UBuffer *, UIndex start, UIndex end, UIndex *parsePos, const UBuffer *ruleBlk, UStatus(*eval)(UThread *, const UCell *), int)
Parse a string or binary using the parse language.
Definition parse_string.c:828
UStatus ur_parseBlock(UThread *ut, UBuffer *, UIndex start, UIndex end, UIndex *parsePos, const UBuffer *ruleBlk, UStatus(*eval)(UThread *, const UCell *))
Parse a block using the parse language.
Definition parse_block.c:609
Holds information for binding functions.
Definition urlan.h:402
const UBuffer * ctx
Context buffer to lookup words in.
Definition urlan.h:403
UIndex ctxN
Words in the block which are found in the UBindTarget::ctx will have their UCell.word....
Definition urlan.h:404
int bindType
Binding type to make.
Definition urlan.h:405
Iterator for const UCell array.
Definition urlan.h:386
The UBuffer struct holds information about a resource, usually a chunk of memory.
Definition urlan.h:266
uint8_t * b
bytes
Definition urlan.h:277
uint8_t form
This can indicate a specific form of the data (such as a string encoding).
Definition urlan.h:269
char * c
chars
Definition urlan.h:276
UIndex used
This typically holds the number of elements in the buffer.
Definition urlan.h:271
uint16_t * u16
uint16_t
Definition urlan.h:279
uint8_t type
UrlanDataType identifier.
Definition urlan.h:267
uint8_t flags
Indicates special features of the buffer (UR_STATIC, UR_STRING_ENC_UP).
Definition urlan.h:270
UCell * cell
Array of cells.
Definition urlan.h:275
union UBuffer::@312146223224040072236377336057316010374162171270 ptr
This typically holds a pointer to a chunk of memory.
int16_t n[UR_COORD_MAX]
Holds six, 16-bit integers.
Definition urlan.h:195
uint16_t len
Number of integers used in array n.
Definition urlan.h:194
uint32_t mask0
Low 32 bits of type mask.
Definition urlan.h:169
uint32_t mask1
Middle 32 bits of type mask.
Definition urlan.h:170
UIndex end
Slice end index.
Definition urlan.h:230
UIndex buf
Buffer id.
Definition urlan.h:228
UIndex it
Iterator index.
Definition urlan.h:229
float xyz[3]
The three float values.
Definition urlan.h:203
uint16_t index
Normally the word index into a context.
Definition urlan.h:218
UIndex ctx
Normally the buffer id of a context.
Definition urlan.h:217
void(* toText)(UThread *, const UCell *cell, UBuffer *str, int depth)
Convert cell to its string textual representation.
Definition urlan.h:450
Iterator for const series of any type.
Definition urlan.h:336
The USeriesType struct holds extra methods for series datatypes.
Definition urlan.h:469
int(* insert)(UThread *, UBuffer *buf, UIndex index, const UCell *val, UIndex part)
Insert a value into the series.
Definition urlan.h:474
int(* find)(UThread *, const USeriesIter *si, const UCell *val, int opt)
Search for a value in the series.
Definition urlan.h:480
void(* remove)(UThread *, USeriesIterM *si, UIndex part)
Remove part of the series.
Definition urlan.h:478
int(* append)(UThread *, UBuffer *buf, const UCell *val)
Append a value to the series.
Definition urlan.h:473
void(* poke)(UBuffer *buf, UIndex n, const UCell *val)
Replace a single value in the series.
Definition urlan.h:472
void(* pick)(const UBuffer *buf, UIndex n, UCell *res)
Get a single value from the series.
Definition urlan.h:471
The UThread struct stores the data specific to a thread of execution.
Definition urlan.h:309
const UDatatype ** types
Pointer to the datatypes.
Definition urlan.h:322
A cell holds a single value of a simple type or a reference (often to a UBuffer) for a complex type.
Definition urlan.h:248
UCellWord word
For word!, lit-word!, set-word!, get-word! types.
Definition urlan.h:251
UCellVec3 vec3
For vec3! type.
Definition urlan.h:254
UCellCoord coord
For coord! type.
Definition urlan.h:253
UCellSeries series
For binary!, bitset!, string!, file!, block!, paren!, path! types.
Definition urlan.h:255
UCellId id
Basic type identification.
Definition urlan.h:249
UCellSeries port
For port! type.
Definition urlan.h:257
UCellDatatype datatype
For datatype! type.
Definition urlan.h:250
UStatus ur_serialize(UThread *, UIndex blkN, UCell *res)
Serialize block.
Definition serialize.c:527
#define UR_FLAG_SOL
This UCellId::flags bit indicates that the cell starts a new line of code.
Definition urlan.h:82
#define ur_testAvail(buf)
Returns the capacity of a UBuffer.
Definition urlan.h:688
#define ur_avail(buf)
Returns the capacity of a UBuffer that is known to have been allocated.
Definition urlan.h:687
UStatus
Definition urlan.h:116
int ur_charUppercase(int c)
Convert UCS2 character to uppercase.
Definition ucs2_case.c:313
void ur_makeDatatype(UCell *cell, int type)
Initialize cell to be a UT_DATATYPE value of the given type.
Definition datatypes.c:389
#define ur_datatype(c)
Access the UrlanDataType that a UCellDatatype represents.
Definition urlan.h:711
int ur_charLowercase(int c)
Convert UCS2 character to lowercase.
Definition ucs2_case.c:293
@ UR_ERR_SCRIPT
General script evaluation error.
Definition urlan.h:126
@ UR_ERR_INTERNAL
Fatal internal problem.
Definition urlan.h:129
@ UR_ERR_ACCESS
Problem accessing external resources.
Definition urlan.h:128
@ UR_ERR_TYPE
Invalid argument/parameter datatype.
Definition urlan.h:125
UStatus ur_unserialize(UThread *, const uint8_t *start, const uint8_t *end, UCell *res)
Unserialize binary.
Definition serialize.c:1002
#define UR_FLAG_INT_HEX
This UCellId::flags bit indicates that an UR_INT value is printed as hexidecimal.
Definition urlan.h:80
@ UR_BIND_THREAD
Bound to buffer in thread dataStore.
Definition urlan.h:88
@ UR_BIND_SECURE
As UR_BIND_THREAD but unbind if not in context.
Definition urlan.h:92
@ UR_BIND_ENV
Bound to buffer in shared env dataStore.
Definition urlan.h:89
@ UR_BIND_SELF
Evaluate to bound context rather than value.
Definition urlan.h:91
@ UR_BIND_STACK
Bound to thread stack.
Definition urlan.h:90
int32_t UIndex
This is an index into an array.
Definition urlan.h:150
void ur_arrReserve(UBuffer *, int count)
Allocates enough memory to hold count elements.
Definition array.c:98
void ur_recycle(UThread *)
Perform garbage collection on thread dataStore.
Definition gc.c:156