forked from jsoftware/jsource
-
Notifications
You must be signed in to change notification settings - Fork 0
/
jtype.h
1269 lines (1162 loc) · 88.5 KB
/
jtype.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Copyright (c) 1990-2023, Jsoftware Inc. All rights reserved. */
/* Licensed use only. Any other use is in violation of copyright. */
/* */
/* Type Definitions */
#define U unsigned
#if (SYS & SYS_UNIX)
#define _stdcall
#endif
#ifdef _WIN32
#define CDPROC
#elif defined(__GNUC__)
#define CDPROC __attribute__ ((visibility ("default")))
#else
#define CDPROC
#endif
#if SY_64
typedef long long I;
typedef long long SB;
typedef unsigned long long UI;
typedef unsigned long long UIL; /* for typecast 8 byte double */
typedef long long IL;
//NANFLAG is used internally as a special value that is not generated by any normal operations. It is represented in integer form,
// and is used only when SZI==SZD
#define NANFLAG 0x7ff28da91LL // signaling NaN with a particular value
#else
typedef int I;
typedef int SB;
typedef unsigned int UI;
typedef unsigned long long UIL;
typedef long long IL;
#endif
typedef unsigned short FLOAT16; // top 16 bits of a floating-point value, used to save space for approximate values
#define FLOAT16TOFLOAT(f) (*(float*)&(UI4){(f)<<16})
#define FLOATTOFLOAT16(f) ((FLOAT16)(*(UI4*)&(float){(f)}>>16))
typedef char B;
typedef unsigned char C;
typedef signed char I1;
typedef char* Ptr;
typedef short S;
typedef short C2;
typedef unsigned int C4;
typedef unsigned char UC;
typedef unsigned short US;
typedef unsigned short U2;
typedef unsigned int UINT;
typedef int I4;
typedef unsigned int UI4;
typedef double D;
typedef float DS;
typedef FILE* F;
typedef long double LD;
// This is the main structure for J entities
typedef UC RANKT;
#define RANKTX 8 // # bits in a RANKT
#define LGRANKTX 3 // lg2(RANKTX)
#define RANKTMSK (((I)1<<RANKTX)-1)
typedef US RANK2T; // 2 ranks, (l<<8)|r
typedef UI4 RANK4T; // 4 ranks
#define RANK2TX 16 // # bits in a RANK2T
#define LGRANK2TX 4 // lg2(RANK2TX)
#define RANK2TMSK 0xFFFFU
#define LGRMAX 6 // lg2(RMAX+1)
#define RMAX (((I)1<<LGRMAX)-1) // max rank, leaving 2 bits for flags
#define R2MAX ((RMAX<<RANKTX)+RMAX) // max value of a RANK2T
typedef I FLAGT;
typedef I4 LX; // index of an L block in SYMORIGIN
typedef struct AD AD;
typedef AD *A;
// Flag bits in the low-order part of jt - used only if the function being called understands inplacing
#define JTINPLACEWX 0 // turn this on in jt to indicate that w can be inplaced
#define JTINPLACEW (((I)1)<<JTINPLACEWX)
#define JTINPLACEAX 1 // turn this on in jt to indicate that a can be inplaced. Must be 1+JTINPLACEWX
#define JTINPLACEA (((I)1)<<JTINPLACEAX)
// following bit used as arg to parse
#define JTFROMEXECX 0 // parser call from ". - makes some features unavailable
#define JTFROMEXEC (((I)1)<<JTFROMEXECX)
// following bit used as arg to jtfolk
#define JTFOLKNOHFNX 2 // set to get the generic fork that does not expect the hfn in localuse
#define JTFOLKNOHFN (((I)1)<<JTFOLKNOHFNX)
// following bit is used on input to jtcvt only
#define JTNOFUZZX 1 // comparison on legal float conversion should be exact
#define JTNOFUZZ (((I)1)<<JTNOFUZZX)
// following bits is used inside/input to jtlrep only
#define JTNORESETERRX 0 // create fully parenthesized output
#define JTNORESETERR (((I)1)<<JTNORESETERRX)
#define JTPARENSX 1 // create fully parenthesized output
#define JTPARENS (((I)1)<<JTPARENSX)
// following bits are passed into jpr/jpr1/immex/immea/showerr/wri
#define JTPRTYO 7 // output class, see MTYO*
#define JTPRNOSTDOUTX 3 // set to suppress typing sentence result on stdout (as in scripts)
#define JTPRNOSTDOUT (((I)1)<<JTPRNOSTDOUTX)
// following bit is used in sort/grade to indicate sort direction
#define JTDESCENDX 2 // direction of sort
#define JTDESCEND (((I)1)<<JTDESCENDX)
// following 2 bits used as input to jtsymbis only
#define JTFINALASGNX 0 // turn this on in jt to indicate that the assignment is final and does not have to worry about protecting the input value
#define JTFINALASGN (((I)1)<<JTFINALASGNX)
// following bits are used in thorn for boxes
#define JTTHORNYX 2 // 0, 1, or 2 for min/center/max for positioning of formatted data in boxes: horiz
#define JTTHORNY (((I)3)<<JTTHORNYX)
#define JTTHORNXX 4 // 0, 1, or 2 for min/center/max for positioning of formatted data in boxes: vert
#define JTTHORNX (((I)3)<<JTTHORNXX)
// following bit is used in the call to jtcelloffset
#define JTCELLOFFROMX 0 // treat a single list as if a rank-2 array
#define JTCELLOFFROM (((I)1)<<JTCELLOFFROMX)
// Next flag must match result.h and VF2 flags, and must be above ZZFLAGBOXATOP
#define JTWILLBEOPENEDX 4 // result of this exec will be opened immediately, so it can contain virtual references to an input to the current verb
// Note: this flag MUST NOT equal BOX, or BOX<<1, or 1 or 2
#define JTWILLBEOPENED (((I)1)<<JTWILLBEOPENEDX)
// obsolete #define JTEMPTYX 5 // in va2, this bit indicates the result is empty
// obsolete #define JTEMPTY (((I)1)<<JTEMPTYX)
#define JTRETRYX 6 // in va2, this bit is set to indicate that the current execution is a retry
#define JTRETRY (((I)1)<<JTRETRYX)
// Next flag must match result.h and VF2 flags, and must be above ZZFLAGBOXATOP
#define JTCOUNTITEMSX 7 // result of this exec will be go into ;, so an item count in m would be helpful
#define JTCOUNTITEMS (((I)1)<<JTCOUNTITEMSX)
// following bit is used in the call to jtxdefn/unquote to indicate the execution is of a modifier
// This bit is set in ALL calls to modifiers, in case they are named
// ****** This bit should not be used for any other purpose ******
#define JTXDEFMODIFIERX 8 // the executed entity is an adverb or conjunction
#define JTXDEFMODIFIER (((I)1)<<JTXDEFMODIFIERX)
#define JTFLAGMSK 511 // mask big enough to cover all defined flags
#define JTALIGNBDY MAX(8192,(MAXTHREADSRND<<LGTHREADBLKSIZE)) // jt is aligned on this boundary - all lower bits are 0 (the value is the size of an SDRAM page, to avoid row precharges while accessing jt)
struct AD {
union {
I k;
A chain; // used when block is on free chain
A globalst; // for local symbol tables (SYMB types), AK points to the active global symbol table when the current sentence started parsing
A *locpath; // for non-local SYMB (named and numeric), AK points to the path, which is the end of a list of addresses of SYMBs
// LOCPATH is tricky because it is used during name lookup but can be modified all over, and can be assigned even when no thread is executing the
// locale, which means that the locale can be deleted while the path is being changed. We make these rules: (1) use atomic_exchange to modify LOCPATH;
// (2) if you exchange out a nonzero, you have to free it; (3) you can replace a nonzero with another nonzero only under system lock, and if you
// are changing the block address (i. e. not extending) you must take a system lock before freeing
} kchain;
FLAGT flag;
union {
I m; // Multi-use field. (1) For NJA/SMM blocks, size of allocation. (2) in syncos, a credential to allow pthread calls
// (3) for SYMB tables for explicit definitions, the address of the calling symbol table; for other SYMB tables,
// a Bloom filter of the hashes assigned in the locale (using the low bits of the hash) (4) for the block
// holding the amend offsets in x u} y, the number of axes of y that are built into the indexes in u
// (5) in the return from wordil, holds the number of words if any final NB. is discarded; (6) in the result of indexofsub when called for FORKEY, contains the
// number of partitions found; (7) in the self block for y L: n and u S: n, the address of the fs block for u; (8) in the call to jtisf (multiple assignment), holds the
// address of the symbol table being assigned to (9) in the y block internal to pv.c, used for flags (10) in all tables that use extendunderlock, the number of valid entries in the table (AN gives the allocation)
// (11) in file-lock list and file-number list, the # valid files (12) if AFUNIFORMITEMS is set, the total# items in all boxes (13) in JT(jt,stnum), the numbered-locale table, the number of locales outstanding
// (14) in the filenames pointed to by fopafl, the file handle for the open file (15) for fret block passed from /.. into ;., the address of the original a arg to /..
// (16) in permuted W block passed from /. to ;., pointer to information on where frets are in a
A back; // For VIRTUAL blocks, points to backing block
A jobpyx; // for user JOB blocks, points to the pyx
A *zaploc; // For all blocks, AM initially holds a pointer to the place in the tpop stack (or hijacked tpop stack) that points back to the allocated block. This value is guaranteed
// to remain valid as long as the block is nonvirtual inplaceable and might possibly return as a result to the parser or result assembly (in cases under m above, the block cannot become such a result)
A aarg; // for /.., the original a arg
} mback;
union {
I t; // type
A proxychain; // used when block is on free chain
} tproxy;
I c; // usecount
// NOTE!! result.h faux cellshape block depends on n, r, and s being in place from here to the end of this struct, with 2 Is from n to s
I n; // # atoms - always 1 for sparse arrays
#if C_LE
RANKT r; // rank. Used as flags in SYMB types (i. e. locales)
UC filler;
US h; // reserved for allocator. Not used for AFNJA memory
#if BW==64
// these two values initialized with a single store - must be in order
US origin; //
S lock; // can be used as a lock
#endif
#else
#if BW==64
S lock; // can be used as a lock
US origin;
#endif
US h; // reserved for allocator. Not used for AFNJA memory
UC filler;
RANKT r; // rank Used as flags in SYMB types (i. e. locales)
#endif
I s[1]; // shape starts here. NOTE!! s[0] is always OK to fetch. We allocate 8 words minimum and s[0] is the last.
// when AFUNIFORMITEMS is set, s[0] holds the number of items in the raze of the block
};
typedef struct {A a,t;}TA;
typedef A (*AF)();
typedef UI (*UF)();
typedef I (*VF)(); // action verb for atomic dyad
typedef I (*VA1F)(); // action verb for atomic monad
typedef void (*VARPSF)(); // action verb for atomic reduce/prefix/suffix routine
typedef B (*CMP)(); /* comparison function in sort */
typedef A X;
typedef struct {X n,d;} Q;
typedef struct {D re,im;} Z;
typedef union {D d;UINT i[2];UI ui;} DI;
#if (SYS & SYS_PC+SYS_MACINTOSH) /* for use by the session manager */
typedef S SI;
#else
typedef I SI;
#endif
/* Fields of type A */
#define AK(x) ((x)->kchain.k) /* offset of ravel wrt x */
#define AKASA(x) ((x)->kchain.chain) // the AK field for synthetic self blocks
#define AKGST(x) ((x)->kchain.globalst) // global symbol table for this local symbol table
#define AFLAG(x) ((x)->flag) /* flag */
#define AM(x) ((x)->mback.m) /* Max # bytes in ravel */
#define ABACK(x) ((x)->mback.back) /* In virtual noun, pointer to backing block */
#define AZAPLOC(x) ((x)->mback.zaploc) // on allocation, the address of the tstack entry that will free the block
#define AZAPLOCVOL(x) (((volatile AD *)x)->mback.zaploc) // on allocation, the address of the tstack entry that will free the block
#define AZAPLOCV(x) ((A*)((x)->s[(x)->r])) // for virtual blocks, the address of the tstack entry that will free the block
#define AT(x) ((x)->tproxy.t) /* Type; one of the #define below */
#define AC(x) ((x)->c) /* Reference count. */
#define AN(x) ((x)->n) /* # elements in ravel */
#define AR(x) ((x)->r) /* Rank */
#if PYXES
#define ARINIT(x,v) {*(US*)&((x)->r)=(v); /* Rank, clearing the high byte for initialization */ \
*(I4*)&(x)->origin=THREADID(jt);} // save the originating thread and clear the lock to 0
#else
#define ARINIT(x,v) *(US*)&((x)->r)=(v); /* Rank, clearing the high byte for initialization */
#endif
#define SMMAH 7L // number of header words in old-fashioned SMM alloc
#define NORMAH 7L // number of header words in new system
#define AS(x) ((x)->s) // Because s is an array, AS(x) is a pointer to the shape, which is in s. The shape is stored in the fixed position s.
// The following fields are used for private communication between /. and ;. and inside ;. for the fret buffer.
#define CUTFRETCHAIN(x) ((x)->kchain.chain) // pointer to next block of frets
#define CUTFRETCOUNT(x) ((x)->kchain.k) // when passed into cut, this is # frets. Overwritten by CUTFRETCHAIN
#define CUTFRETFRETS(x) ((UC*)((x)->s)) // address of first fret
#define CUTFRETEND(x) ((x)->n) // address of last+1 fret
#if SY_64
#define AKXR(x) (SZI*(NORMAH+(x)))
#define WP(t,n,r) (SMMAH+ r +(1&&t&LAST0)+(((t&NAME?sizeof(NM):0)+((n)<<bplg(t))+SZI-1)>>LGSZI)) // # I to allocate
#else
#define AKXR(x) (SZI*(NORMAH+((x)|1)))
#define WP(t,n,r) (SMMAH+(r|1)+ (1&&t&LAST0)+(((t&NAME?sizeof(NM):0)+((n)<<bplg(t))+SZI-1)>>LGSZI))
/* r|1 to make sure array values are double-word aligned */
#endif
#define AKX(x) AKXR(AR(x))
#define RCALIGN 1 // the rank to use to put the data on a cacheline boundary
#define AV(x) ( (I*)((C*)(x)+AK(x))) /* pointer to ravel */
#define BAV(x) ( (B*)(x)+AK(x) ) /* boolean */
#define BAV1(x) ( (B*)(x)+AKXR(1) ) // boolean non-virtual rank 1
#define CAV(x) ( (C*)(x)+AK(x) ) /* character */
#define CAV0(x) (((C*)(x)+AKXR(0))) // character in non-virtual rank-0 array (allocated as rank 0, that is)
#define CAV1(x) (((C*)(x)+AKXR(1))) // character in non-virtual rank-1 array
#define CAV2(x) (((C*)(x)+AKXR(2))) // character in non-virtual rank-2 array
#define UCAV(x) ( (UC*)(x)+AK(x) ) /* unsigned character */
#define USAV(x) ((US*)((C*)(x)+AK(x))) /* wchar */
#define UAV(x) ( (UC*)(x)+AK(x) ) /* unsigned character */
#define UIAV(x) ((UI*)((C*)(x)+AK(x))) /* unsigned integer */
#define UI4AV(x) ((UI4*)((C*)(x)+AK(x))) /* unsigned 32-bit int */
#define C4AV(x) ((C4*)((C*)(x)+AK(x))) /* literal4 */
#define NAV(x) ((NM*)((C*)(x)+AKXR(1))) // name, which is always allocated as rank 1, for some reason
#define NAVV(x) ((volatile NM*)((C*)(x)+AKXR(1))) // name, which is always allocated as rank 1, for some reason
#define IAV(x) AV(x) /* integer */
#define IAV0(x) ((I*)((C*)(x)+AKXR(0))) // integer in a stack- or heap-allocated atom (rank 0 - used for internal tables)
#define UIAV1(x) ((UI*)((C*)(x)+AKXR(1))) // unsigned integer "limb" in an X (or Q) value
#define IAV1(x) ((I*)((C*)(x)+AKXR(1))) // integer in a stack- or heap-allocated list (rank 1 - used for internal tables that need alignment or need AS[0])
#define IAV2(x) ((I*)((C*)(x)+AKXR(2))) // integer in a stack- or heap-allocated list (rank 2)
#define BAV0(x) ( (C*)((C*)(x)+AKXR(0)) ) // Boolean when rank is 0 - fixed position (known to avoid segfault)
#define LXAV0(x) ( (LX*)((C*)(x)+AKXR(0)) ) // Symbol when rank is 0 - fixed position (for SYMB hash tables). Note AK() is used in SYMB tables
#define LAV0(x) ( (L*)((C*)(x)+AKXR(0)) ) // Symbol array when rank is 0 - used for the symbol pool
#define DAV(x) ( (D*)((C*)(x)+AK(x))) /* double */
#define DAV0(x) ( (D*)((C*)(x)+AKXR(0))) // double atom
#define DAV2(x) ( (D*)((C*)(x)+AKXR(2)) ) // Double when rank is 2 - fixed position (for matrix inversion)
#define ZAV(x) ( (Z*)((C*)(x)+AK(x))) /* complex */
#define XAV(x) ( (X*)((C*)(x)+AK(x))) /* extended */
#define XAV0(x) ( (X*)((C*)(x)+AKXR(0))) /* extended */
#define QAV(x) ( (Q*)((C*)(x)+AK(x))) /* rational */
#define AAV(x) ( (A*)((C*)(x)+AK(x))) /* boxed */
#define AAV0(x) ((A*)((C*)(x)+AKXR(0))) // A block in a stack- or heap-allocated atom (rank 0 - used for internal tables)
#define AAV1(x) ((A*)((C*)(x)+AKXR(1))) // A block in a stack- or heap-allocated list (rank 1)
#define AAV2(x) ((A*)((C*)(x)+AKXR(2))) // A block in a stack- or heap-allocated list (rank 2)
#define VAV(x) ( (V*)((C*)(x)+AK(x))) /* verb, adverb, conj */
#define FAV(x) ( (V*)((C*)(x)+AKXR(0)) ) // verb, adverb, conj - always at fixed offset
#define FAVV(x) ( (volatile V*)((C*)(x)+AKXR(0)) ) // verb, adverb, conj volatile to avoid delayed fetch
#define PAV(x) ( (P*)((C*)(x)+AK(x))) /* sparse */
#define SBAV(x) ((SB*)((C*)(x)+AK(x))) /* symbol */
#define SBUV4(x) ((SBU*)((C*)(x)+AKXR(4))) // symbol, nonvirtual rank 4
#define voidAV(x) ((void*)((C*)(x)+AK(x))) // unknown
#define voidAVn(x,n) ((void*)((C*)(x)+AKXR(n))) // unknown, but rank is known
#define voidAV0(x) voidAVn(x,0) // unknown, but scalar
#define voidAV1(x) voidAVn(x,1) // unknown, but list
#define voidAV2(x) voidAVn(x,2) // unknown, but table
#define voidAVCACHE(x) voidAVn(x,RCALIGN) // unknown, aligned to cache
#define UNLXAV0(x) ((A)((I)(x)-AKXR(0))) // go from a pointer to LXAV0 back to the base of the A block
#define UNvoidAV0(x) ((A)((I)(x)-AKXR(0))) // go from a pointer to *AV0 back to the base of the A block
#define UNvoidAV1(x) ((A)((I)(x)-AKXR(1))) // go from a pointer to *AV1 back to the base of the A block
#if C_LE
#define BIV0(w) (IAV(w)[0]&(1-((AT(w)&INT)>>(INTX-1)))) // the first (presumably only) value in w, when w is an INT or B01 type
#endif
/* Types for AT(x) field of type A */
/* Note: BOOL name conflict with ???; SCHAR name conflict with sqltypes.h */
// NOTE!! the length of NOUN types must be power-of-2 multiples because of jtamend2
// NOTE: all noun types must be below all parsable non-nouns
#define B01X 0
#define B01 ((I)1L<<B01X) // B boolean
#define B01SIZE sizeof(B) // length of 1 atom
#define LITX 1
#define LIT ((I)1L<<LITX) // C literal (character)
#define LITSIZE sizeof(C)
#define INTX 2
#define INT ((I)1L<<INTX) // I integer
#define INTSIZE sizeof(I)
#define FLX 3
#define FL ((I)1L<<FLX) // D double (IEEE floating point)
#define FLSIZE sizeof(D)
#define CMPXX 4
#define CMPX ((I)1L<<CMPXX) // Z complex
#define CMPXSIZE sizeof(Z)
#define BOXX 5
#define BOX ((I)1L<<BOXX) // A boxed
#define BOXSIZE sizeof(A)
#define XNUMX 6
#define XNUM ((I)1L<<XNUMX) // X extended precision integer
#define XNUMSIZE sizeof(X)
#define RATX 7
#define RAT ((I)1L<<RATX) // Q rational number
#define RATSIZE sizeof(Q)
#define PYXX 8
#define PYX ((I)1L<<PYXX) // if BOX set, this flag is set if the value is a pyx. A pyx is an atomic box (which may be an element of an array).
// Task creation returns an atomic box (which is NOT a pyx) that CONTAINS a pyx. A pyx itself never becomes a result or argument, because
// the value of a pyx may not be accessed except through C(pyx), which will return the address of the contents. An A block pointing to the pyx (necessarily of BOX type)
// can be freely stored into boxed arrays or returned as a result. Our coding rule is that pyxes MAY be passed as a/w arguments, and may be stored unresolved in compounds;
// but they WILL NOT be passed as arguments into any other kind of routine. The practical effect of this rule is that when a routine pulls the address of a box out of
// an AAV area, any reference to the box's contents (AN, AR, AC, AT, AS, AK), or the value of the pyx, requires C(pyx).
// If the address of the pyx is being copied into another block, there is no need for C(). In particular, the pyx may be ra()'d if it is put into a recursive block.
// ra() on a pyx will affect the usecount of the pyx itself but NOT of the contents, because a pyx is always marked recursive.
// The pyx looks like an atomic box but it actually holds a PYXBLOK where the data would be. The PYXBLOK begins with the result value, so that when
// the pyx is freed the result will be also. The AN of the 'atomic' pyx is initialized to 1, and AAV[0] to 0. When the pyx is resolved, the address of the
// result A block is stored into AAV[0], error code is saved in the PYXBLOK, and the executing thread field of the PYXBLOK is set to -1.
// When a pyx is created, ownership is transferred to the enclosing box via zap. The enclosing box is active in the creating task. The PYXBLOK is ra()d
// before it is passed to the executing task; the task fa()s it after it is filled in.
//
// NOTE that this definition of pyx doesn't match the user docs. For the user, the pyx is the box enclosing what we have defined here as the true pyx.
// This user-pyx can be passed as an argument, and is resolved when opened. We document it this way because the user thinks of an array of 5 boxes
// a being 5 containers, whereas really it is one BOX with pointers to 5 contents (which are true pyxes).
// Bit 9 unused
#define SBTX 16
#define SBT ((I)1L<<SBTX) // SB symbol
#define SBTSIZE sizeof(SB)
#define C2TX 17
#define C2T ((I)1L<<C2TX) // C2 unicode (2-byte characters)
#define C2TSIZE sizeof(US)
#define C4TX 18
#define C4T ((I)1L<<C4TX) // C4 unicode (4-byte characters)
#define C4TSIZE sizeof(C4)
#define XDX 19
#define XD ((I)1L<<XDX) // DX extended floating point used to represent intolerant compare in jtiosc
#define XDSIZE sizeof(DX)
#define XZX 20
#define XZ ((I)1L<<XZX) // ZX extended complex
#define XZSIZE sizeof(ZX)
#define LASTNOUNX XZX // index of last noun bit
// upper flags may be used, except for RPAR and CONJ (used as stack count in parser), LPAR and ASGN (used in parser to calculate initial stack count), CONW (always means ASGNTONAME in parser)
// VERB/ADV/CONJ should not be used as flags in NOUN types. The main flags are MARK/SYMB/CONW
// if ADV is used, some tests may need to change
// NOTE: bit numbers for ADV, CONJ, and VERB, and 31, must be at least 2 bits apart because of code for type testing
#define NAMEX 21
#define NAME ((I)1L<<NAMEX) /* NM name */
#define NAMESIZE sizeof(C) // when we allocate a NAME type, the length is the length of the name string
// NOTE: SYMB and MARK are used as flags in names, see below, and CONW in some type args
#define MARKX 22 // don't try to move this! it ripples through and breaks JTflags
#define MARK ((I)1L<<MARKX) /* I end-of-stack marker */
#define MARKSIZE sizeof(I)
#define ADVX 23
#define ADV ((I)1L<<ADVX) /* V adverb */
#define ADVSIZE sizeof(V)
// NOTE: SYMB is set in an ADV value to indicate that the value is nameless, see below
#define ASGNX 24
#define ASGN ((I)1L<<ASGNX) /* I assignment */
#define ASGNSIZE sizeof(I) // only 1 byte, but all non-DIRECT are fullword multiples
// BOTE: SYMB and CONW are used as flags in ASGN, see below
#define SYMBX 25
#define SYMB ((I)1L<<SYMBX) /* I locale (symbol table) */
#define SYMBSIZE sizeof(LX)
#define CONWX 26
#define CONW ((I)1L<<CONWX) /* CW control word */
#define CONWSIZE sizeof(CW)
// NOTE: The parser assumes that CONW always means ASGNTONAME, so don't use it in any parseable type except ASGN
#define VERBX 27
#define VERB ((I)1L<<VERBX) /* V verb */
#define VERBSIZE sizeof(V) // Note: size of ACV in bp() is INTSIZE because the allocation in fdef() is of INTs
// NOTE: VERB must be above all NOUN bits because of CONJCASE. Must be >ADV because of AVN testing in parser
// es delayline in parser expects VERBX <= 27
#define LPARX 28
#define LPAR ((I)1L<<LPARX) /* I left parenthesis */
// note: LPAR used as flag to cvt() see below; also as modifier to ADV type
#define LPARSIZE sizeof(I)
// unquote requires that the spacing CONJX-ADVX equal VERBX-NAMEX
// CONJ must be 1 bit below RPAR
#define CONJX 29
#define CONJ ((I)1L<<CONJX) /* V conjunction */
#define CONJSIZE sizeof(V)
#define RPARX 30
#define RPAR ((I)1L<<RPARX) /* I right parenthesis */
#define RPARSIZE sizeof(I)
// Upper bits of a type can be used as flags, since we use CTTZ(AT) to indicate what the type is. Usually we avoid these in NOUN types to
// make testing easier; but see BOXMULTIASSIGN which is never an argument
// ** ASGN type can have the following informational bits set along with ASGN
#define ASGNLOCALX SYMBX // set for =. (but not when assigning to locative) aliases with SYMB
#define ASGNLOCAL ((I)1L<<ASGNLOCALX) // set for =. (but not when assigning to locative) aliases with SYMB
#define ASGNTONAME ((I)1L<<CONWX) // set when assignment is to name aliases with CONW
// NOTE: The parser assumes that CONW always means ASGNTONAME, so don't use it in any parseable type except ASGN
// ** NOUN types can have the following informational bits set
#define NOUNCVTVALIDCT ((I)1L<<SYMBX) // Flag for jtcvt arg only: if set, convert only the #atoms given in the parameter Aliases with SYMB
#define SPARSEX 31 // NOTE this extends to the sign bit
#if defined(_WIN64)||defined(__LP64__)
#define SPARSE (-((I)1L<<SPARSEX)) /* P sparse boxed */
#else
#define SPARSE (IMIN) /* P sparse boxed */
#endif
// ** NAME type can have the following information flags set
#define NAMEBYVALUEX MARKX // set if the name is one of u v u. v. that is always passed by value, never by reference
#define NAMEBYVALUE ((I)1L<<NAMEBYVALUEX) // set if the name is one of x x. m m. etc that is always passed by value, never by name
#define NAMEABANDONX SYMBX
#define NAMEABANDON ((I)1L<<NAMEABANDONX) // name is name::, which will be deassigned after the value is stacked. NAMEBYVALUE must also be set
// ** BOX type can have the following informational flags set
#define BOXMULTIASSIGN ((I)1L<<MARKX) // set for the target of a direct multiple assignment (i. e. 'x y' =.), which is stored as a boxed list whose contents are NAMEs aliases with MARK
// Restriction: CONW must be reserved for use as ASGNTONAME because of how parser tests for it
// Restriction: MARK must be reserved for use as BOXMULTIASSIGN because of how parser tests for it
// ** NOTE!! bits 28-30 are used in the call to cvt() (arg only) to override the conversion type for XNUMs
// MARK and ASGN hold the 2-bit rounding mode
#define XCVTXNUMORIDEMSK (MARK+ASGN+CONW) // in cvt(), the override to use if XCVTXNUMORIDEX is set, otherwise 00
#define XCVTXNUMORIDEX CONWX // in cvt(), indicates that forced precision for result is present
#define XMODETOCVT(x) (((((x)+2)&5)<<MARKX)|CONW) // convert XMODE to a request to cvt to convert to that mode
#define CVTTOXMODE(x) ((((x)+MARK)>>(MARKX+1))&3) // convert cvt request back to xmode
#define ANY -1L
#define NUMERIC (B01+INT+FL+CMPX+XNUM+RAT)
#define DIRECT ((LIT+C2T+C4T+B01+INT+FL+CMPX+SBT)|SPARSE) // AND must be >0
#define JCHAR (LIT+C2T+C4T)
#define NOUN (NUMERIC+JCHAR+BOX+SBT)
#define FUNC (VERB+ADV+CONJ)
#define RHS (NOUN+FUNC)
#define IS1BYTE (B01+LIT)
#define LAST0 (B01+LIT+C2T+C4T+NAME)
#define SPARSABLE (B01+INT+FL+CMPX+LIT) // types that can be made sparse
// Don't traverse for ra/fa unless one of these bits is set
#define TRAVERSIBLE (BOX|VERB|ADV|CONJ|RAT|XNUM|NAME|SYMB|SPARSE)
// Allow recursive usecount in one of these types
// A recursive block is flagged by having the recursible type bit copied into the AFLAG. But note: the block is not recursible unless the same bit is set in both
// the type and the flag, where something like (0$a:) + 0$0 might reuse an argument block and leave the flags showing boxed when the type is B01. scaf should fix this?
// We know that any block that has been ra()d is recursive, and therefore that fa() can assume recursibility for any recursible type
#define RECURSIBLE (BOX|VERB|ADV|CONJ|RAT|XNUM|NAME|SYMB) // sparse box not allowed
// SYMB is TRAVERSIBLE so that fa() will call to free the symbols, and RECURSIBLE so that fanapop will pass the type-flag. To ensure that a SYMB is always freed when
// its count goes to 0, we must ensure that it is always born recursive
#define RECURSIBLENOUN (RECURSIBLE&NOUN)
#define SGN1IFSPARSETYPE(t,dt) ((t)&-((t)&(dt))) // sign 1 if t is sparse and one of dt
#define SGN0IFDENSETYPE(t,dt) ((t)|(((t)&(dt))-1)) // sign 0 if t is dense and one of dt
#define ISDENSETYPE(t,dt) (((t)&SPARSE+(dt))>0) // true if t is dense and one of dt
#define SGNIFSPARSE(t) (t) // set sign bit if t is sparse
#define ISSPARSE(t) ((t)<0) // true if sparse
#define SGNIFDENSE(t) (~(t)) // set sign bit if t is dense
#define ISDENSE(t) ((t)>=0) // true if dense
// Modifiers that operate on subarrays do so with virtual blocks, and those blocks may be marked as inplaceable if the backing block is inplaceable.
// The inplaceability applies to the data area, but not necessarily to the block header: if UNINCORPORABLE is set, the header must not be modified (we clonevirtual() the header in that case)
// For speedy singletons, there is the additional problem that the operation expects always to write a FL value to the result area, which is OK for any
// real block but not for an inplaced virtual block, whose virtual data may be shorter than a FL. The pure solution would be for the singleton code
// to refrain from modifying a virtual block that is shorter than a FL, but that means we would have to test for it for every arithmetic operation. Thus
// we take the alternative, which is to not mark a virtual block inplaceable if it is a type shorter than a FL.
//
// BOX type would be OK, as singleton code doesn't touch it and all usecounts are held in the backer, except for the possibility that the backer is recursive and the virtual block isn't.
// Places that check TYPEVIPOK make an exception when the block is going to be processed by each or each2, because those routines are guaranteed not to disturb the boxes, but only
// the first level of contents, and that only when the block is pristine.
//
// Note: arithmetic dyads on bytes have similar issues, because the 8-byte-at-a-time operations may execute outside the cell of the array. We detect
// those cases inside the atomic-dyad code in va2.c.
#define TYPEVIPOK (FL+CMPX+SBT+(SZI==SZD?INT:0)) // sparse is never inplaceable
#define TYPESEQ(x,y) ((x)==(y)) // types are equal
#define TYPESXOR(x,y) ((x)^(y)) // types are not equal using full-word logical
#define TYPESNE(x,y) ((x)!=(y)) // types are not equal
#define TYPESLT(x,y) ((UI)(x)<(UI)(y)) // type x < type y
#define TYPESGT(x,y) ((UI)(x)>(UI)(y)) // type x > type y
#define PARTOFSPEECHEQ(x,y) (((((x)|(LPAR&-((x)&NOUN)))^((y)|(LPAR&-((y)&NOUN))))&LPAR+CONJ+VERB+ADV)==0) // using RPAR to hold NOUN status, verify parts-of-speech the same
#define PARTOFSPEECHEQACV(x,y) ((((x)^(y))&CONJ+VERB+ADV)==0) // verify known-nonnoun parts-of-speech the same
// Utility: keep the lowest 1 only
#define LOWESTBIT(x) ((x)&-(x))
#define POSIFHOMO(s,t) ( -(((s)^(t))&(BOX|SBT|JCHAR|MARK)) & -(((s)^(t))&(BOX|SBT|NUMERIC|MARK)) )
#define NEGIFHOMO(s,t) ( ~POSIFHOMO(s,t) )
#define HOMO(s,t) ( POSIFHOMO(s,t)>=0 )
#define HOMONE(s,t) HOMO(s,t)
#define STYPE(t) ((t)|SPARSE)
#define DTYPE(t) ((t)&~SPARSE)
// Flags in the count field of type A
#define ACINPLACEX (BW-1)
#define ACINPLACE ((I)((UI)1<<ACINPLACEX)) // set when this block CAN be used in inplace operations. Always the sign bit.
#define ACPERMANENTX (BW-2)
#define ACPERMANENT ((I)1<<ACPERMANENTX) // next-to-top bit, set in blocks that should never modify the AC field
#define ACUSECOUNT (I)1 // lower bits used for usecount
#define ACAND(a,v) __atomic_fetch_and(&AC(a),(v),__ATOMIC_ACQ_REL);
#define ACOR(a,v) __atomic_fetch_or(&AC(a),(v),__ATOMIC_ACQ_REL);
#define ACIPYESLOCAL(a) (AC(a)|=ACINPLACE)
#define ACIPISOK(a) (AC(a)<1) // OK to modify if INPLACE set - set only when usecount=1
#define ACUC(a) (AC(a)&(~ACINPLACE)) // just the usecount portion
#define ACUC1 (ACUSECOUNT*1) // <= this is usecount==1; > is UC>1
#define ACUC2 (ACUSECOUNT*2) // <= this is usecount<=2, which is inplaceable if you know the usecount has been uncremented earlier
#define ACADDLOCAL(a,n) if(likely(!ACISPERM(AC(a))))(AC(a)=(AC(a)+(n))&~ACINPLACE)
#define ACSUBLOCAL(a,n) if(likely(!ACISPERM(AC(a))))(AC(a)=(AC(a)-(n)))
// use ACINCR... when you know the block is recursive & you just want to adjust the usecount. POS means you know it is >0. SP means it might be sparse (which always requires recursion)
#define ACINCRVIRT(a) (AC(a)=(AC(a)&~ACINPLACE)+1)
#define ACIPNO(a) {if(AC(a)<0)AC(a)&=~ACINPLACE;} // if AC<0, the block must not be visible to other threads
#define ACIPNOABAND(a) {AC(a)&=~ACINPLACE;} // block is known to be inplace abandoned & thus not PERMANENT
#define ACADD(a,n) if(AC(a)<0)__atomic_store_n(&AC(a),(n)+1,__ATOMIC_RELEASE);else if(likely(!ACISPERM(AC(a))))__atomic_fetch_add(&AC(a),(n),__ATOMIC_ACQ_REL);
#define ACINCR(a) ACADD(a,1)
#define ACDECRNOPERM(a) __atomic_fetch_sub(&AC(a),1,__ATOMIC_ACQ_REL) // must not be PERM
#define ACINIT(a,v) AC(a)=(v); // used when it is known that a has just been allocated & is not shared
#define ACRESET(a,v) AC(a)=(v); // used when it is known that a is not shared (perhaps it's UNINCORPABLE)
#define ACSETLOCAL(a,v) AC(a)=(v); // used when a might be shared, but atomic not needed
#define ACSET(a,v) __atomic_store_n(&AC(a),(v),__ATOMIC_RELEASE); // used when a might be shared, but atomic not needed
#define ACFAUX(a,v) AC(a)=(v); // used when a is known to be a faux block
#define ACINITZAP(a) {*AZAPLOC(a)=0; ACINIT(a,ACUC1)} // effect ra() immediately after allocation, by zapping
#define ACINITUNPUSH(a) {A *pushp=jt->tpushnext; --pushp; \
if(unlikely((I)pushp&(NTSTACKBLOCK-1))){A *nextp=(A*)*pushp; if(unlikely(nextp!=pushp-1)){freetstackallo(); pushp=nextp;}} /* check start of block and start of allo */ \
jt->tpushnext=pushp; ACINIT(a,ACUC1)} // effect ra() immediately after allocation, by backing the tpush pointer
#define ACINITZAPRECUR(a,t) {*AZAPLOC(a)=0; ACINIT(a,ACUC1); AFLAG(a)|=(t)&RECURSIBLE;} // effect ra() immediately after allocation, by zapping, and make the block recursive if possible
#define ACZAPRA(x) {if(likely(AC(x)<0)){*AZAPLOC(x)=0 ACIPNO(x);}else ra(x);}
#define ACX(a) {AC(a)=ACPERMANENT; AFLAGORLOCAL(a,AT(a)&RECURSIBLE);} // used only in initializations
#define ACISPERM(c) ((I)!!(ACPERMANENT&(UI)(c))) // is PERMANENT bit set?
#define ACSETPERM(x) {AC(x)=ACPERMANENT+100000; __atomic_fetch_or(&AFLAG(x),(AT(x)&RECURSIBLE),__ATOMIC_ACQ_REL);} // Make a block permanent from now on. In case other threads have committed to changing the usecount, make it permanent with a margin of safety
#define SGNIFPRISTINABLE(c) ((c)+ACPERMANENT) // sign is set if this block is OK in a PRISTINE boxed noun
// same, but s is an expression that is neg if it's OK to inplace
#define ASGNINPLACESGN(s,w) (((s)&(AC(w)|SGNIF(jt->zombieval==w,0)))<0) // OK to inplace ordinary operation
#define ASGNINPLACESGNNJA(s,w) ASGNINPLACESGN(s,w) // OK to inplace ordinary operation
// define virtreqd and set it to 0 to start
// This is used in apip. We must ALWAYS allow inplacing for NJA types, but for ordinary inplacing we don't bother if the number of atoms of w pushes a over a power-of-2 boundary
#define EXTENDINPLACENJA(a,w) \
( ((AC(a)&((((AN(a)+NORMAH+1-1)+AN(w))^(AN(a)+NORMAH+1-1))-(AN(a)+NORMAH+1-1)))<0) || /* inplaceable value that will probably fit */ \
( ((((((AN(a)+NORMAH+1-1)+AN(w))^(AN(a)+NORMAH+1-1))-(AN(a)+NORMAH+1-1))|SGNIF(AFLAG(a),AFNJAX))<0) && /* value will probably fit OR is NJA, where any fit MUST be used */\
(jt->zombieval==a || (virtreqd=(AFLAG(a)>>AFKNOWNNAMEDX)&(((AC(a)^ACUC2)|(AFLAG(a)&(AFRO|AFVIRTUAL)))==0))>(UI)jt->zombieval) /* asg-in-place or virt extension. Remember if virt extension */ \
/* virt extension is (x { (a , item)). We require a to be named so that we know that usecount of 2 means value is stacked only once */ \
/* we require zombieval=0 so that (a =. b , 5) will not create a virtual that must immediately be realized */ \
/* the other requirements for inplacing are AC=2 and not VIRTUAL or RO */ \
) /* OK to inplace assignment/virtual */ \
)
/* Values for AFLAG(x) field of type A */
// the flags defined here must be mutually exclusive with TRAVERSIBLE
#define AFRO (I)1 /* read only; can't change data */
#define AFROX 0 /* read only; can't change data */
#define AFNJAX 1 /* non-J alloc; i.e. mem mapped */
#define AFNJA ((I)1<<AFNJAX)
#define AFDEBUGRESULTX 2 // special flag for values that alter debug state
#define AFDEBUGRESULT ((I)1<<AFDEBUGRESULTX)
// Note: bit 4 is LABANDONED which is merged here
// the spacing of VIRTUALBOXED->UNIFORMITEMS must match ZZFLAGWILLBEOPENED->ZZCOUNTITEMS
#define AFUNIFORMITEMSX MARKX // matches MARK
#define AFUNIFORMITEMS ((I)1<<AFUNIFORMITEMSX) // It is known that this boxed array has contents whose items are of uniform shape and type; the total number of those items is in AM (so this block cannot be virtual)
#define AFUNINCORPABLEX SBTX // matches SBTX 16
#define AFUNINCORPABLE ((I)1<<AFUNINCORPABLEX) // (used in result.h) this block is a virtual block used for subarray tracking and must not
// ever be put into a boxed array, even if WILLBEOPENED is set, because it changes. AFVIRTUAL must also be set. If this block is
// inplaceable, the data may be overwritten but the header must not be: clonevirtual() in that case to get a modifiable header
#define AFVIRTUALX C2TX // matches C2TX 17
#define AFVIRTUAL ((I)1<<AFVIRTUALX) // this block is a VIRTUAL block: a subsequence of another block. The data pointer points to the actual data, and the
// m field points to the start of the block containing the actual data. A VIRTUAL block cannot be incorporated into another block, and it
// cannot be assigned, unless it is 'realized' by creating another block and copying the data. We realize whenever we call ra() on the block,
// except during the EPILOG, where we don't realize the block unless the real block is about to be freed. Like PERMANENT blocks,
// VIRTUAL blocks are always recursive so that fa() will not recur. Virtual blocks are always freed from tpop. Since it cannot be copied or realized,
// the virtual block always has usecount of ACUC1 or ACUC1+ACINPLACE. EXCEPTION: for the initial assignment to x/y in an explicit
// definition, we allow assigning a virtual block, because we know that the block and its backer are allocated in a higher level
// and can never be freed until the explicit definition finishes. ra() is OK since all virtual blocks are recursive. It is OK to fa() the block on exit
// or on reassignment, because that will just decrement the usecount of the nonrecursive block.
// VIRTUAL blocks are normally not inplaceable (since they are by definition aliased to another block), but the temporary
// UNINCORPORABLE blocks created by partitioning modifers to track cells may be inplaceable, and a virtual block whose backer
// has been abandoned may be marked inplaceable as well.
// NOTE: AFVIRTUALX must be higher than any RECURSIBLENOUN type (for test in result.h)
#define AFKNOWNNAMEDX C4TX // matches C4TX 18 *** can be changed when block is shared
#define AFKNOWNNAMED ((I)1<<AFKNOWNNAMEDX) // set (often) in a value when the value is assigned to a name. It is possible that the name will be deleted, in which case the flag will be cleared
// even if the value is assigned to another name. The purpose is to allow virtual extension: if you know that a value is assigned to a name, then only one
// thread can encounter the value with AC=2, and that is safe for virtual extension.
#define AFVIRTUALBOXEDX XDX // matches XDX 19
#define AFVIRTUALBOXED ((I)1<<AFVIRTUALBOXEDX) // this block (created in result.h) is an array that is about to be opened, and thus may contain virtual blocks as elements
#define AFPRISTINEX ASGNX // matches ASGN 24 - must be above all DIRECT flags *** can be changed when block is shared
#define AFPRISTINE ((I)1<<AFPRISTINEX) // meaningful only for BOX type. This block's contents were made entirely of DIRECT inplaceable or PERMANENT values, and thus can be
// inplaced by &.> . If any of the contents are taken out, the PRISTINE flag must be cleared, unless the block is never going to be used again (i. e. is inplaceable).
// When a VIRTUAL block is created, it inherits the PRISTINE status of its backer; if the block is modified or a value escapes by address, PRISTINE status is cleared in the backer.
// If a PRISTINE virtual block is realized, the backer must become non-PRISTINE (because its contents are escaping).
// If a PRISTINE block is incorporated, it must lose PRISTINE status because it is no longer possible to know whether contents may have been fetched while the
// block was incorporated.
// NOTE: if a block becomes shared, the value of PRISTINE becomes immaterial
#define AFDPARENX CONWX // matches CONW 26
#define AFDPAREN ((I)1<<AFDPARENX) // In the words of an external definition, this word came from (( )) or noun () and must use linear rep for its display
// MUST BE GREATER THAN ANY DIRECT FLAG (not including the SPARSE flag)
#define AFUPPERTRIX RPARX // matches RPAR 30
#define AFUPPERTRI ((I)1<<AFUPPERTRIX) // (used in cip.c) This is an upper-triangular matrix
// NOTE: bit 28 (LPAR) is used to check for freed bufs in DEADARG
#define AFAUDITUCX 32 // this & above is used for auditing the stack (you must run stack audits on a 64-bit system)
#define AFAUDITUC ((I)1<<AFAUDITUCX) // this field is used for auditing the tstack, holds the number of deletes implied on the stack for the block
#define AFLAGINIT(a,v) AFLAG(a)=(v); // used when it is known that a has just been allocated & is not shared
#define AFLAGRESET(a,v) AFLAG(a)=(v); // used when it is known that a is not shared (perhaps it's UNINCORPABLE)
#define AFLAGFAUX(a,v) AFLAG(a)=(v); // used when a is known to be a faux block
#define AFLAGANDLOCAL(a,v) AFLAG(a)&=(v); // LOCAL functions are used when the block is known not to be shared
#define AFLAGORLOCAL(a,v) AFLAG(a)|=(v);
// Once a block has been shared, the flags do not change except for PRISTINE and KNOWNNAMED. (Pristine only gets cleared, KNOWNNAMED is set and cleared).
// To make sure a word-wide change doesn't store an old value, we store into these flags using single-byte operations. This will cause sharing in the exceedingly rare
// case of simultaneous modification, but it avoids the need for RFO cycles.
#if C_LE
#define AFLAGSETKNOWN(a) ((C*)&AFLAG(a))[2]|=AFKNOWNNAMED>>16; // if the value is ever exposed to another thread, the count will be too high for KNOWN to matter
#define AFLAGCLRKNOWN(a) ((C*)&AFLAG(a))[2]&=~(AFKNOWNNAMED>>16);
#define AFLAGSETPRIST(a) ((C*)&AFLAG(a))[3]|=AFPRISTINE>>24;
#define AFLAGCLRPRIST(a) ((C*)&AFLAG(a))[3]&=~(AFPRISTINE>>24);
#endif
#define AFLAGPRISTNO(a) if(unlikely(AFLAG(a)&AFPRISTINE))AFLAGCLRPRIST(a) // the test is to ensure we don't touch PERMANENT blocks
// rank flags in the AR field of symbol tables. The allocated rank is always 0
#define ARNAMEDX 0 // set in the rank of a named locale table. This bit is passed in the return from jtsyrd1
#define ARNAMED ((I)1<<ARNAMEDX) // set in the rank of a named locale table. This bit is passed in the return from jtsyrd1
// bit 1 not used
// the rest of the flags apply only to local symbol tables
#define ARLCLONEDX NMSHAREDX // 4 set if this is a cloned local symbol table (in which symbol numbers are invalid)
#define ARLCLONED (1LL<<ARLCLONEDX) // set if this is a cloned local symbol table (in which symbol numbers are invalid)
#define ARHASACVX 3 // set if this local symbol table contains an ACV
#define ARHASACV ((I)1<<ARHASACVX)
#define ARLOCALTABLE 16 // Set in rank of all local symbol tables. This indicates that the first hashchain holds x/y info and should not be freed as a symbol
#define ARLSYMINUSE 32 // This bit is set in the rank of the original local symbol table when it is in use
#define ARINVALID 64 // This (named or numbered) symbol table was never filled in and must not be analyzed when freed
#define ARNAMEADDEDX 7 // 128 Set in rank when a new name is added to the local symbol table. We transfer the bit from the L flags to the rank-flag. Keep as sign bit
#define ARNAMEADDED (1LL<<ARNAMEADDEDX)
#define SFNSIMPLEONLY 1 // to sfn: return simple name only, discarding any locative
#define FIXALOCSONLYLOWEST 4 // to fixa: replace only the first occurrence of u/v in each branch
#define FIXALOCSONLY 8 // to fixa: replace only u/v (IMPLOC)
#define FIXASTOPATINV 16 // to fixa: stop afixing a branch when it gets to a an explicit obverse
// AH field of all blocks
#define AFHRH(a) ((a)->h) // the workarea
typedef struct { // we could consider align(4) to save space - would require change to bpnonnoun
union{
struct {
C type; // control-word number from w.h
C canend; // Indicates that the most-recent B-block result can (1) or can't (2) become the result of the running definition. 0 means we don't know yet.
US sentn; // number of tokens in the sentence
I4 sentx; // index of the start of the sentence in the sequential list of tokens for the definition
} indiv; // individual fields
UI group[2-SY_64]; // fields all in one or two words
} ig;
US go; // line number. Depends on type; can be loop-to point, failing-branch point, or error handler
US source; // source line number
} CW;
/* control word (always has corresponding token string) */
/* type - as specified in w.h */
/* go - line number to go to */
/* source - source line number */
/* i - beginning index of token string */
/* n - length of token string */
// canend - Indicates that the most-recent B-block result can (1) or can't (2) become the result of the running definition. 0 means we don't know yet.
#define DCPARSE 1 /* sentence for parser */
#define DCSCRIPT 2 /* script -- line() */
#define DCCALL 3 /* verb/adv/conj call -- dbunquote() */
#define DCJUNK 4 /* stack entry is stale */
typedef struct DS{ /* 1 2 3 */
struct DS*dclnk; /* x x x link to next stack entry */
A dcy; /* x x x &tokens; text ; right argument */
I dcn; /* x x x #tokens; line # ; ptr to executing value */
I dcix; // x x x index ; next index ; line# in exp def being executed, or to be exec next
I dcj; /* x x x error#; prev index ; error # */
C dctype; /* x x x type of entry (see #define DC*) */
B dcsusp; /* x x 1 iff begins a debug suspension */
C dcss; // x x 1 if script is supplying sentences (0 if interrupted by prompt) ;single step code
C dcnewlineno; // x set when debug has installed a new line number into dcix
C dcpflags; // x prompt flags, see JTPRTYO
C dcredef; // x set if this definition has been reassigned while running on top of stack
A dca; /* x fn/op name */
A dcf; /* x fn/op */
A dcx; /* x left argument */
A dcloc; /* x local symb table (0 if not explicit) */
A dcc; /* x control matrix (0 if not explicit) */
I dcm; /* x x ; script index; # of non-locale part of name */
I dcstop; /* x the last stop in this function */
} DST;
typedef DST* DC;
typedef struct {I e,p;X x;} DX;
/* for the p field in DX */
#define DXIPREC ((I)-1) /* infinite precision */
#define DXINF ((I)-2) /* _ infinity */
#define DXMINF ((I)-3) /* __ negative infinity */
/* extended floating point */
/* e - exponent */
/* p - precision & other codes */
/* +ve # of significant digits */
/* _1 infinite precision (with trailing 0s) */
/* _2 infinity _ */
/* _3 negative infinity __ */
/* x - mantissa */
/* least significant digit first */
/* decimal point after last digit */
// LSB codes in value pointers. Set by enqueue() and symbis(), used by parsea(). Means that all boxes must be aligned to cacheline boundaries and freeing boxes must ignore these flags
// type of 0000 is unused; 1-11 are the type bits (following LASTNOUNX) in order
#define QCMASK 0x1fLL // all the LSB flags
#define QCWORD(x) ((A)((I)(x)&~QCMASK)) // the word pointer part of the QC
#define QCTYPE(x) ((I)(x)&QCMASK) // the type-code part
#define QCINSTALLTYPE(x,t) ((A)((I)(x)|(I)(t))) // install t into word-pointer x
// values 0-11 are the same in all contexts:
// the CAVN types are selected for comp ease in the typeval field of an assigned value. They are indexes into ptcol.
// The value which might also hold VALTYPESPARSE, or VALTYPENAMELESS which is converted to correct type before the lookup). These types are seen only in valtypes in named blocks, which have QCGLOBAL semantics
#define ATYPETOVALTYPEACV(t) (CTTZI((t)>>(LASTNOUNX-1))) // types 1=NOUN 4=ADV 7=SPARSE 8=VERB 10=CONJ 0 means 'no value'
#define ATYPETOVALTYPE(t) (((t)&NOUN)?(unlikely(ISSPARSE(t))?VALTYPESPARSE:QCNOUN):ATYPETOVALTYPEACV(t)) // types 1=NOUN 4=ADV 7=SPARSE 8=VERB 10=CONJ 0 means 'no value'
#define VALTYPETOATYPE(t) ((1LL<<(LASTNOUNX-1))<<(t)) // convert t from valtype form to AT form (suitable only for conversion to pt - actual noun type is lost)
#define QCNOUNX 0
#define QCNOUN ((LASTNOUNX-LASTNOUNX)+1) // this bit must not be set in any non-noun CAVN type, i. e. not in ACV. But it must be set in SPARSE. It can be used to test for FUNC in a named QCTYPE
#define QCADV ((ADVX-LASTNOUNX)+1) // 4
// note: code point 5 must be left unused so we don't mess up clearing the pull queue
#define QCVERB ((VERBX-LASTNOUNX)+1) // 8
#define QCLPAR ((LPARX-LASTNOUNX)+1) // 9
#define QCCONJ ((CONJX-LASTNOUNX)+1) // 10
#define QCNAMEASSIGNED ((NAMEX-LASTNOUNX)+1) // name followed by copula
// bit 4 and code points 12-15 depend on the context.
// In the words of a sentence, created by enqueue(), they are as follows:
// the last AT type is RPAR, which is 11 (30-20+1)
// assignments occupy 12-15, with 4 variants
#define QCASGN 0x0c // copula. QCASGNISLOCAL and QCASGNISTONAME are modifiers
#define QCASGNISLOCAL 0x1 // =. preceded by nonlocative name
#define QCASGNISTONAME 0x2 // copula is preceded by name
// named lookups have bit 4 set, and use other flags to indicate the type of name
#define QCISLKPNAME 0x10 // name requires lookup (i. e. not assigned)
#define QCNAMEBYVALUE 0x01 // combining flag - name is mnuvxy type
#define QCNAMEABANDON 0x08 // combining flag - name has :: - set only if not assigned
// In the LSBs returned by syrd() (stored by symbis()), bit 4 and the higher code points are as follows:
#define QCGLOBALX 4
#define QCGLOBAL 0x10 // set if the name was found in a global table
#define SETGLOBAL(w) (A)((I)(w)|QCGLOBAL)
#define CLRGLOBAL(w) (A)((I)(w)&~QCGLOBAL)
#define VALTYPENAMELESS ((SYMBX-LASTNOUNX)+1) // 6 set in nameless non-locative ACV, to suppress reference creation.
#define VALTYPESPARSE ((CONWX-LASTNOUNX)+1) // 7 set in sparse noun, which is the only type of a stored value that requires traverse. Has bit 0 set, as befits a noun
#define NAMELESSQCTOTYPEDQC(q) q=QCWORD(q), q=(A)((I)q+ATYPETOVALTYPEACV(AT(q))); // q is name of NAMELESS QC; result has QC type for t
// In the LSBs returned by syrd1() bit 4 means:
#define QCNAMEDX 4 // set if the value was found in a named locale, clear if numbered
#define QCNAMED ((I)1<<QCNAMEDX) // set if the value was found in a named locale, clear if numbered
// After the named value has been processed, bit 4 changes meaning to:
#define QCFAOWEDX 4
#define QCFAOWED 0x10 // when this bit is set in an address returned from lookup, it means that the value was ra()d when it was stacked and must be fa()d when it leaves execution
#define SETFAOWED(w) (A)((I)(w)|QCFAOWED)
#define CLRFAOWED(w) (A)((I)(w)&~QCFAOWED)
#define ISFAOWED(w) ((I)(w)&QCFAOWED) // is fa() required?
#define QCPTYPE(x) ((I)(x)&0xf) // the type-code part, 0-15 for the syntax units including assignment
// When the value is pushed onto the parser stack, the FAOWED bit moves to bit 0 where it can be distinguished from a tstack pointer
#define STKFAOWEDX 0
#define STKFAOWED ((I)1<<STKFAOWEDX) // set in parser stack if value needs to be freed
#define SETSTKFAOWED(w) (A)((I)(w)|STKFAOWED)
#define CLRSTKFAOWED(w) (A)((I)(w)&~STKFAOWED)
#define ISSTKFAOWED(w) ((I)(w)&STKFAOWED) // is fa() required?
#define SYMLINFO 0 // index of LINFO entry
#define SYMLEXECCT 1 // index of EXECCT for the locale
#define SYMLINFOSIZE 2 // Number of symbol-table entries that DO NOT root symbol chains, but instead are LINFO entries
// The MSB of LX values is used to indicate that the NEXT value is NOT permanent. We do this so that we can visit all PERMANENT entries without ever
// touching a non-PERMANENT one. By marking NON-permanent symbols with the sign bit, we allow the code for permanent symbols to assume the
// sign is 0, since the bucket #s are always for permanent symbols. The end-of-chain pointer does not have the PERMANENT flag set
#define SYMNONPERMX 31
#define SYMNONPERM (I4)(1L<<SYMNONPERMX) // flag set if next is non-permanent, or if an LX is invalid
#define SYMNEXT(s) ((s)&~SYMNONPERM) // address of next symbol
#define SYMNEXTISPERM(s) ((s)>0) // true if next symbol is permanent
// Macros to incr/decr execct of a locale
#define EXECCTNOTDELD 0x1000000 // This bit is set when a locale is created, and removed when the user asks to delete it. Lower bits are the exec count. The locale is half-deleted when exec ct goes to 0
#if PYXES
#define INCREXECCT(l) __atomic_fetch_add(&LXAV0(l)[SYMLEXECCT],1,__ATOMIC_ACQ_REL);
#define DECREXECCT(l) if(unlikely(__atomic_sub_fetch(&LXAV0(l)[SYMLEXECCT],1,__ATOMIC_ACQ_REL)==0))locdestroy(l);
#define DELEXECCT(l) if(unlikely(__atomic_and_fetch(&LXAV0(l)[SYMLEXECCT],~EXECCTNOTDELD,__ATOMIC_ACQ_REL)==0))locdestroy(l);
#else
#define INCREXECCT(l) ++LXAV0(l)[SYMLEXECCT];
#define DECREXECCT(l) if(--LXAV0(l)[SYMLEXECCT]==0)locdestroy(l);
#define DELEXECCT(l) if((LXAV0(l)[SYMLEXECCT]&=~EXECCTNOTDELD)==0)locdestroy(l);
#endif
typedef struct {
A name; // name on lhs of assignment; in LINFO, pointer to NM block. May be 0 in zombie values (modified cached values)
A val; // rhs of assignment, or 0 for PERMANENT symbols that have not yet been assigned. In LINFO, the number of a numbered locale, unused otherwise
C flag; // Lxx flags, see below. Not used for LINFO (AR is used for locale flags)
C valtype; // if a value is set, this holds the QCxxx type for the word 0 if no value. QCGLOBAL is set in global tables
S sn; // script index the name was defined in. Not used for LINFO
LX next; // LX of next value in chain. 0 for end-of-chain. SYMNONPERM is set in chain field if the next-in-chain exists and is not LPERMANENT. Not used in LINFO
} L; // name must come first because of the way we use validitymask[11]
// FOR EXECUTING LOCAL SYMBOL TABLES: AK() points to the active global symbol table, AM() points to the calling local symbol table.
// In all local symbol tables, the first 'hashchain' has the chain numbers for y/x; they are the first symbols in those chains, always permanent
#define LCH (I)1 /* changed since last exec of 4!:5 */
#define LPERMANENTX 1
#define LPERMANENT ((I)1<<LPERMANENTX) // set if the name is a local name assigned in the definition; these names are never deleted - the value is cleared instead
#define LINFO (I)4 // Indicates the symbol-table entry is info only and the value is not a valid pointer (diags only)
#define LWASABANDONEDX 4
#define LWASABANDONED ((I)1<<LWASABANDONEDX) // set if the name was assigned from an abandoned value, and we DID NOT raise the usecount of the value (we will have changed INPLACE to ACUC1, though).
// when the name is reassigned or deleted, we must refrain from fa(), and if the value still has AC=ACUC1, we should revert it to inplaceable so that the parser will free it
// immediately
// This value passes into AFLAGS and must not overlap anything there
#define LHASNAME (I)32 // name is nonnull - this value is not used internally; it appears in the result of 18!:_2
#define LHASVALUE (I)64 // value is nonnull - this value is not used internally; it appears in the result of 18!:_2
#define LREADONLY (I)128 // symbol cannot be reassigned (it is xxx or xxx_index)
// In Global symbol tables (including numbered) AK is LOCPATH, and AM is LOCBLOOM
// The first L block in a symbol table is used to point to the locale-name rather than hash chains
#define LOCNAME(g) ((SYMORIGIN)[LXAV0(g)[SYMLINFO]].name)
#define LOCNUMW(g) ((SYMORIGIN)[LXAV0(g)[SYMLINFO]].val) // locale number, for numbered locales
#define LOCNUM(g) (I)LOCNUMW(g)
#define LOCPATH(g) (g)->kchain.locpath // the path, allocated with rank 1 (so the path is in one cacheline). If 0, the locale has been deleted. The path runs from LOCPATH backwards
// to end with the ending 0 at AAV1()[0]
#define LOCBLOOM(x) AM(x)
#define BLOOMOR(x,v) {LOCBLOOM(x)|=(v);} // or a new value into the Bloom filter. MUST be done under lock
// Definition of callstack
typedef struct {
I type; // type of entry, flagged per below
void *value; // locale or name, depending on the type
} LS;
#define CALLSTACKPOPLOCALE 2 // value is jt->global that must be restored after function returns
#define CALLSTACKPOPFROM 4 // value is jt->global that must be modified in the caller of this function also
#define CALLSTACKCHANGELOCALE 8 // value is the value of jt->global before it was modified by the called function
#define CALLSTACKPOPLOCALEFIRST 16 // set in the POPLOCALE that is added when the first POPFROM is seen
#define CALLSTACKPUSHLOCALSYMS 32 // value is jt->locsyms that must be restored
// Add an entry to the call stack, and increment the index variable
#define pushcallstack(i,t,v) (jt->callstack[i].type=(t), jt->callstack[i].value=(v), ++i)
#define pushcallstack1(t,v) {ASSERT(jt->callstacknext<jt->fcalln,EVSTACK); pushcallstack(jt->callstacknext,(t),(v));}
#define pushcallstack1d(t,v) {FDEPDEC(d); ASSERT(jt->callstacknext<jt->fcalln,EVSTACK); pushcallstack(jt->callstacknext,(t),(v));}
#define pushcallstack1dsuff(t,v,suff) {FDEPDEC(d); ASSERTSUFF(jt->callstacknext<jt->fcalln,EVSTACK,suff); pushcallstack(jt->callstacknext,(t),(v));}
// NM struct: pointed to by the name field of a symbol, and used for lookups. Names are allocated with rank 1 (?? but it means first cacheline is unused)
typedef struct{
I bucketx; // (for local simple names, only if bucket!=0) the number of chain entries to discard before
// starting name search. If negative, use one's complement and do not bother with name search - symbol-table entry
// is guaranteed to be at that position
// (for direct locatives) the hash of the locative - if numbered, the number itself.
// (for indirect locatives) hash of the last indirect name
// (for locale names in SYMLINFO of a numbered locale) the locale number
A cachedref; // (only for cachable NAME blocks): the value to be used for this name entry, if it is not a noun. Has QCFAOWED semantics (with FAOWED always off). It may be (1) in a nameless modifier, the A block for the value, which has PERMANENT AC
// (2) otherwise, the nameref for the name block, which may or may not have the pointer to the looked-up value. This nameref is not PERMANENT AC and must be deleted when the name is deleted
LX symx; // (only for SHARED names, which are only local variables and never cachable) the index of the symbol allocated in the primary symbol table
I4 bucket; // (for local simple names) the index of the hash chain for this symbol when viewed as a local
// 0 if chain index not known or name is a locative
UI4 hash; // hash for non-locale part of name
UC m; // length of non-locale part of name note 255-byte limit! (AN holds the length of the entire name including the locative)
C flag, // string part of full name (1 to ?? characters, including locale of assignment if given)
s[1]; // up to 24 chars fit in a 128B allo
} NM;
// values in flag:
#define NMLOC 1 // direct locale abc_lm_ only one of NMLOC/NMILOC/NMIMPLOC is set
#define NMSHAREDX 2
#define NMSHARED (1LL<<NMSHAREDX) // This NM is for a locally-defined name and is shared by all references to the name
#define NMILOC 2 // indirect locale abc__de__fgh ... only one of NMLOC/NMILOC/NMIMPLOC is set
#define NMDOT 128 // one of the names m. n. u. v. x. y. */
#define NMIMPLOC 16 // this NM block is u./v. only one of NMLOC/NMILOC/NMIMPLOC is set
#define NMCACHEDX 5
#define NMCACHED (1LL<<NMCACHEDX) // This NM is to cache any valid lookup
typedef struct {I a,e,i,x;} P;
/* value fields of sparse array types */
/* fields are offsets from beginning of the P struct */
/* a: sparse axes */
/* e: sparse element */
/* i: index matrix, columns correspond to a */
/* x: value cells corresponding to rows of i */
#define SPA(p,a) ((A)((p)->a+(C*)(p))) // a is one of aeix; result is A pointer for that component
#define SPBV(p,a,v,x) {RZ(v = (x)); INCORP(v); (p)->a=(C*)v-(C*)(p);} // store x into component (a); return if x is 0. a is one of aeix. Result in v
#define SPB(p,ZWa,x) {A ZWo = (x); SPBV((p),ZWa,ZWo,(x))} // store x into component (a); return if x is 0. a is one of aeix
// Header for hashtables used in i.
// This sits at the beginning of the allocated block, holding info about the stored values
typedef struct {
I datasize; // number of bytes in the data area
I hashelelgsize; // lg(size of a hashed element)
UI currentlo; // the lowest position in the hashtable of the current allocation.
UI currenthi; // the highest position+1 of the current allocation.
UI currentindexofst; // the value in the hashtable that the minimum value in the input will map to
UI currentindexend; // the highest value that can possibly be written, +1
UI previousindexend; // All positions from 0 to currenthi are known to be less than currentindexend.
// Positions from currenthi to the end are known to be less than previousindexend.
UI invalidlo; // the start of the area that does not have constrained values because it was used for a bit table (as an index into hashtable, rounded up to multiple of I for endian reasons)
UI invalidhi; // the end+1 of the area used for a bit table (as an index into hash table, rounded up to multiple of I)
I datamin; // (for small-range use) minimum value in the data
UI datarange; // (for small-range use) range+1 of the data
D cct; // if this is tolerant, the cct used to build the table
// 12 words here; 7 words of header (we allocate no shape); so this block is 19 words after the
// memory allocation; add one fill to get on the same 256-bit boundary as the allocation.
I filler;
union { // We index into the data with varying strides, depending on the range of the data
UC UC[1]; // cannot have union of empty vectors
US US[1];
UI4 UI4[1];
UI UI[1];
} data;
} IH;
#define IHAV(x) ((IH*)((C*)(x)+AK(x))) // how to refer to the header area
// Return value from condrange
typedef struct {
I min; // smallest value found
I range; // max+1-min, or 0 if range exceeds max given
} CR;
/* performance monitoring stuff */
typedef struct{
A name; /* verb/adverb/conjunction name */
A loc; /* locale name */
I lc; /* line number (-1 for entry; -2 for exit) */
I s; /* space */
I t[2]; /* time */
C val; /* valence: 1 or 2 */
C unused[3]; /* padding */
} PM;
#define PMCOL 6 /* # of fields in PM */
typedef struct{
I n; /* maximum number of records */
I i; /* index of next record to be written */
I s; /* initial bytesmax value */
I pmctr; // counter, set > 0 to start sampling
B rec; /* what to record (0 entry & exit; 1 all) */
B trunc; /* what to do on overflow (0 wrap; 1 truncate) */
B wrapped; /* 1 iff wrapping has happened */
C unused[1]; /* padding */
} PM0;
/* each unique symbol has a row in JT(jt,sbu) */
/* a row is interpreted per SBU */
/* for best results make sizeof(SBU) a multiple of sizeof(I) */
typedef struct{
I i; /* index into sbs */
I n; /* length */
UI h; /* hash value */
I color; /* binary tree: color */
I parent; /* binary tree: index of parent */
I left; /* binary tree: index of left child */
I right; /* binary tree: index of right child */
I order; /* order number */
I down; /* predecessor in ordering */
I up; /* successor in ordering */
I flag; /* bit flags */
} SBU;
#define SBC2 1 /* 1 iff 2-byte character */
#define SBC4 2 /* 2 iff 4-byte character */
// Info for calling an atomic verb
typedef struct {VF f;I cv;} VA2; // for dyads
typedef struct {VA1F f;I cv;} VA1; // for monads
typedef struct {VARPSF f;I cv;} VARPS; // for reduce/prefix/suffix
typedef struct {I nprec; VARPS actrtns[];} VARPSA;
typedef struct {VA2 p2[13];VARPSA *rps;} VA;