Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

lib/var.x

Variant type for dynamic typing.

Primary API

FunctionSummary
x2c_var_descriptor_indexReturns value’s dense built-in descriptor row, or -1 when it has none.
x2c_var_tag_descriptor_indexReturns a built-in object or Symbol tag’s descriptor row, or -1.
Var.isReports whether value has the requested runtime tag.
Var.is_floatingReports whether v belongs to the floating runtime family.
Var.is_integerReports whether v belongs to the integer runtime family.
Var.is_nilReports whether v is the typed empty List.
Var.is_nullReports whether v is the all-zero Null value.
Var.is_objectReports whether v holds a registered boxed object.
Var.is_pointerReports whether v holds a native pointer.
Var.is_referenceReports whether v holds a pointer to a boxed handle.
Var.is_voidReports whether v is the absence sentinel void.
Var.newConstructs a Var with tag from its tag-directed variadic payload.
Var.nullReturns Null, the all-zero Var that stands for external nil.

Functions

x2c_var_descriptor_index

int x2c_var_descriptor_index(Var value)

Returns value’s dense built-in descriptor row, or -1 when it has none. The row is the tag’s offset from <array> and includes the Symbol row. Numbers, pointers, references, custom tags, and invalid encodings have no built-in row.

Source: lib/var.x:209

x2c_var_tag_descriptor_index

int x2c_var_tag_descriptor_index(Symbol tag)

Returns a built-in object or Symbol tag’s descriptor row, or -1.

Source: lib/var.x:222

Var

Var.is

int Var.is(Var var, Symbol tag)

Reports whether value has the requested runtime tag. This tests one family. A Var holding an unsigned char answers 0 for <i32> even though both are integers. When any integer will do, ask Var.is_integer, or compare Var.kind. As with Var.tag, validate externally constructed bits first: an invalid encoding uses the historical <f64> fallback.

Source: lib/var.x:284

Var.is_floating

int Var.is_floating(Var v)

Reports whether v belongs to the floating runtime family. True for <f32>, <f64>, and <ldouble>, and for the discrete <nan>, <+inf>, and <-inf> values. Read the payload with Var.floating, or with Var.long_double_value when the tag is <ldouble> and the extra precision matters.

Source: lib/var.x:309

Var.is_integer

int Var.is_integer(Var v)

Reports whether v belongs to the integer runtime family. True for every signed and unsigned integer family from <u8> through <ullong>, including the scope-owned wide boxes. Symbols are a kind of their own and answer 0 here, even though Var.integer returns a Symbol’s numeric value.

Source: lib/var.x:317

Var.is_nil

int Var.is_nil(Var v)

Reports whether v is the typed empty List. The empty List is a native null pointer carrying the <list> tag. Var.is_null answers 0 and Var.tag answers <list>. Every %() and every exhausted List.cdr yields this one value, so an identity test on the bits is a valid emptiness test.

Source: lib/var.x:381

Var.is_null

int Var.is_null(Var v)

Reports whether v is the all-zero Null value. Null is a value: the null pointer and the external nil. It is legal collection data, iterating a List can return it, and it is false in a condition. Its tag decodes as <p48>, so there is no dedicated null family for Var.is to match.

Write Null as (Var) { .u64 = 0 }. An unresolved C NULL macro also converts to this value when its x2c target is Var.

Source: lib/var.x:364

Var.is_object

int Var.is_object(Var v)

Reports whether v holds a registered boxed object. True for the builtin classes such as String, List, Array, Map, File, and Iter, and for any tag registered with Var.register_object_tag. A pointer to a handle, such as <string*>, is <reference> instead and answers 0 here.

Source: lib/var.x:331

Var.is_pointer

int Var.is_pointer(Var v)

Reports whether v holds a native pointer.

Source: lib/var.x:320

Var.is_reference

int Var.is_reference(Var v)

Reports whether v holds a pointer to a boxed handle.

Source: lib/var.x:322

Var.is_void

int Var.is_void(Var v)

Reports whether v is the absence sentinel void. An API returns void to say there is nothing here. It is excluded from every collection and iterator value domain: it cannot be pushed into an Array or stored in a Map. Equality and identity still inspect it. Two sentinels compare equal and identical, while one sentinel and one ordinary value compare unequal. Truthiness, hashing, ordering, conversion, and iteration still terminate on a void operand.

Null is the all-zero Var: legal collection data and false in a condition. A void result may mean missing, exhausted, or invalid; each API documents its meaning.

Var raw = (Var) { .u64 = 0 };
printf("null: void=%d null=%d\n", raw is void, raw.is_null());
printf("void: void=%d null=%d\n", void is void, void.is_null());

This is the test that accepts a void argument.

Source: lib/var.x:353

Var.new

Var Var.new(Symbol tag, ...)

Constructs a Var with tag from its tag-directed variadic payload. The tag chooses how the argument is read, so pass exactly the C type the tag names: an int for <i32>, an unsigned long for <u48>, a double for <f64>, a long double for <ldouble>, a pointer for any pointer, reference, or object family, and a Symbol’s numeric value for <symbol>. <void> consumes no payload. Variadic arguments are not converted for you. Assignment, Var boxed = 42;, is the usual way to box a value. Use this constructor when the tag is computed at run time.

Immediate values are stored inline. Wide numeric tags allocate a box in the active Scope. Pointer, reference, and object tags borrow the address and encode only its low 48 bits; they do not take ownership, and the address must satisfy the alignment implied by the tag. A tag previously registered with Var.register_object_tag is accepted too and requires an 8-byte- aligned pointer.

Raises: <bad-target> when tag is neither known nor registered, <conv-range> when a scalar does not fit the tag’s payload width, <bad-arg> when an <array> or <map> pointer is null, <alloc-fail> when a wide box cannot be allocated, and <bad-enc> when a box address cannot be represented or a custom object pointer is not 8-byte aligned.

Source: lib/var.x:642

Var.null

Var Var.null(void)

Returns Null, the all-zero Var that stands for external nil. Generated call adapters return it for a void target.

Source: lib/var.x:369

Advanced and interop API

FunctionSummary
Var.box_longBoxes a long into a scope-owned <long> value.
Var.box_long_doubleBoxes a long double into a scope-owned <ldouble> value.
Var.box_long_longBoxes a native signed long-long value without losing precision.
Var.box_ulongBoxes an unsigned long into a scope-owned <ulong> value.
Var.box_ulong_longBoxes a native unsigned long-long value without losing precision.
Var.encoding_validReports whether value has a valid structural Var encoding.
Var.floatingReturns v’s payload as a double when its tag is floating, or 0.0.
Var.integerReturns v’s payload as a long when its tag is integral, or 0.
Var.integer_compareOrders the integer payloads of a and b, returning -1, 0, or 1.
Var.integer_floating_compareOrders an integer-kinded integer against a floating floating.
Var.kindReturns the Symbol naming the coarse category of v’s payload.
Var.known_tagReports whether tag has a built-in encoding or registered custom row.
Var.long_double_valueReturns the payload of an <ldouble> box, or 0.0 if v has another tag.
Var.long_long_valueReturns an <llong> box’s signed payload, or 0 for another tag.
Var.long_valueReturns the payload of a <long> box, or 0 if v has a different tag.
Var.parseParses str as source text of kind kind and returns the boxed value.
Var.pointerReturns the raw address stored in v, or NULL if it holds no address.
Var.register_object_tagReserves or returns a process-lifetime row for custom boxed-object tag.
Var.tagReturns the Symbol naming the exact family of v’s payload.
Var.ulong_long_valueReturns a <ullong> box’s unsigned payload, or 0 for another tag.
Var.ulong_valueReturns the payload of a <ulong> box, or 0 if v has a different tag.
Var.wide_compareOrders two wide boxes carrying the same tag, returning -1, 0, or 1.
Var.wide_equalReports content equality for supported wide scalar boxes.
Var.wide_hashReturns a supported wide scalar box’s content hash, or 0 otherwise.

Var

Var.box_long

Var Var.box_long(long value)

Boxes a long into a scope-owned <long> value. A Var is eight bytes, and the tag consumes some of them. The five widest native families, long, unsigned long, long long, unsigned long long, and long double, cannot be stored inline. Boxing one allocates a small immutable box in the active scope, and the result lives for that scope’s lifetime like any other scope allocation.

Two boxes of the same number are equal but not identical. On Var operands == is Var.equal, which compares payloads, while === is Var.same, which compares the 64 bits and therefore the box addresses.

Var five = Var.box_long(5L);
Var also = Var.box_long(5L);
printf("equal=%d same=%d tag=%s\n", five == also, five === also,
       five.tag().str());

Raises: <alloc-fail> when the box cannot be allocated and <bad-enc> when its address cannot be represented in a Var. Before Error initialization, allocation failure uses the raw fatal floor.

Source: lib/var.x:470

Var.box_long_double

Var Var.box_long_double(long double value)

Boxes a long double into a scope-owned <ldouble> value. This is the only family that keeps a full long double payload. The tag names that C family; it does not promise a bit width. Var.floating narrows the value to double; Var.long_double_value preserves the extra precision.

Raises: <alloc-fail> when the box cannot be allocated and <bad-enc> when its address cannot be represented in a Var. Before Error initialization, allocation failure uses the raw fatal floor.

Source: lib/var.x:522

Var.box_long_long

Var Var.box_long_long(long long value)

Boxes a native signed long-long value without losing precision.

Raises: <alloc-fail> when the box cannot be allocated and <bad-enc> when its address cannot be represented in a Var. Before Error initialization, allocation failure uses the raw fatal floor.

Source: lib/var.x:496

Var.box_ulong

Var Var.box_ulong(unsigned long value)

Boxes an unsigned long into a scope-owned <ulong> value. <ulong> is a distinct family from <long>, so a box made here never compares equal to one made by Var.box_long even when both hold the same bit pattern; Var.wide_equal requires matching tags. Read it back with Var.ulong_value, the one reader that returns the payload unsigned.

Raises: <alloc-fail> when the box cannot be allocated and <bad-enc> when its address cannot be represented in a Var. Before Error initialization, allocation failure uses the raw fatal floor.

Source: lib/var.x:485

Var.box_ulong_long

Var Var.box_ulong_long(unsigned long long value)

Boxes a native unsigned long-long value without losing precision.

Raises: <alloc-fail> when the box cannot be allocated and <bad-enc> when its address cannot be represented in a Var. Before Error initialization, allocation failure uses the raw fatal floor.

Source: lib/var.x:507

Var.encoding_valid

int Var.encoding_valid(Var value)

Reports whether value has a valid structural Var encoding. An address-bearing encoding must still refer to live storage of the right type; wide encodings in particular require a readable Scope-owned box.

Source: lib/var.x:231

Var.floating

double Var.floating(Var v)

Returns v’s payload as a double when its tag is floating, or 0.0. Handles <f32>, <f64>, and <ldouble>, and reconstructs NaN, +Inf, and -Inf from their discrete tags. An <ldouble> payload is narrowed to double; call Var.long_double_value to keep the full precision.

Like the other raw payload readers this one is silent. An integer, a String, or void reads as 0.0, and nothing distinguishes that from a stored zero. When the tag is not already known, convert with Var.convert in lib/varconvert.x, which raises a conversion failure; the scalar-named readers such as Var.double do this for you.

An unhandled tag yields 0.0.

Source: lib/var.x:707

Var.integer

long Var.integer(Var v)

Returns v’s payload as a long when its tag is integral, or 0. This reads the payload; it does not convert. It decodes every integer family, from <u8> through <ullong>, including the scope-owned boxes, and returns 0 for a tag it does not handle. A double reads as 0, and so does a String; nothing reports the mismatch. A <ullong> or <llong> payload is truncated to long, and a <ulong> above LONG_MAX comes back negative. Var.ulong_value preserves the unsigned payload. A Symbol reads as its numeric Symbol value.

When the tag might not be what you expect, convert instead of reading. Var.convert in lib/varconvert.x raises a conversion failure. Assigning a Var to an int or a long, and the scalar-named readers such as Var.int, go through Var.convert too.

Var text = %"ada";
Var small = (unsigned char) 44;
printf("small=%ld text=%ld\n", small.integer(), text.integer());

An unhandled tag yields 0.

Source: lib/var.x:752

Var.integer_compare

int Var.integer_compare(Var a, Var b)

Orders the integer payloads of a and b, returning -1, 0, or 1. Each value is decomposed into a sign and an unsigned magnitude first, so the whole integer range orders correctly, including a <ullong> above LONG_MAX against a negative <long>. Subtracting in a fixed-width integer type could overflow.

Both arguments are assumed to be integer-kinded. Another value is decoded by Var.integer, which reads it as 0. Confirm with Var.is_integer when the kinds are not known.

Source: lib/var.x:932

Var.integer_floating_compare

int Var.integer_floating_compare(Var integer, Var floating)

Orders an integer-kinded integer against a floating floating. Returns -1, 0, or 1 for less, equal, and greater. The integer is never converted to floating point, so a large <ullong> and a nearby double order by their true values. A floating value with a fractional part is never equal to an integer.

+Inf is greater than every integer and -Inf is less than every integer. NaN is not ordered and reports -1. Test the tag for <nan> first if that distinction matters.

Source: lib/var.x:960

Var.kind

Symbol Var.kind(Var v)

Returns the Symbol naming the coarse category of v’s payload. The categories are <integer>, <floating>, <symbol>, <object>, <pointer>, <reference>, and <void>, and every tag belongs to exactly one. Use kind when a group of families is treated alike. All twelve integer widths answer <integer>, and every builtin class handle such as String, List, Map, and File answers <object>.

<pointer> means a raw C pointer such as <u8*>, while <reference> means a pointer to an object handle such as <string*>. NaN and the infinities are <floating>. Only void is <void>.

Source: lib/var.x:297

Var.known_tag

int Var.known_tag(Symbol tag)

Reports whether tag has a built-in encoding or registered custom row.

Source: lib/var.x:61

Var.long_double_value

long double Var.long_double_value(Var v)

Returns the payload of an <ldouble> box, or 0.0 if v has another tag. This is the only reader that preserves a long double. Every other floating tag, <f64> included, yields 0.0 here instead of being widened. Var.floating is the general floating reader.

A tag mismatch yields 0.0.

Source: lib/var.x:824

Var.long_long_value

long long Var.long_long_value(Var v)

Returns an <llong> box’s signed payload, or 0 for another tag.

Source: lib/var.x:810

Var.long_value

long Var.long_value(Var v)

Returns the payload of a <long> box, or 0 if v has a different tag. The test is on the tag, so a <ulong>, <llong>, or <i32> value reads as 0 with no complaint. Var.integer accepts any integer family; use this one when the tag is known and the payload must survive intact.

A tag mismatch yields 0.

Source: lib/var.x:795

Var.parse

Var Var.parse(String str, Symbol kind)

Parses str as source text of kind kind and returns the boxed value. The kinds understood are <int>, <float>, <double>, <string>, <symbol>, and <char>. For <string>, matching %"..." or "..." delimiters are removed; unquoted input is also accepted, and either form is unescaped. <char> expects 'a' complete with its quotes and produces an <i32>. Both <float> and <double> produce an <f64>; there is no path here to <f32>.

Integer and floating kinds require the whole input to parse, apart from surrounding whitespace. Malformed input, trailing text, a value outside the native reader’s range, or an <int> outside int range returns void, so a parsed numeric zero remains distinguishable from failure.

<symbol> accepts the leading atom or <...> Symbol literal and ignores trailing text; no recognized leading Symbol produces the zero Symbol. A kind this function does not handle returns void, and a bad character literal gives <i32> -1.

Var count = Var.parse(%"42", <int>);
Var broken = Var.parse(%"abc", <int>);
Var refused = Var.parse(%"3", <u8>);
printf("%s=%s %s=%s refused=%d\n", count.tag().str(), count,
       broken.tag().str(), broken, refused is void);

Raises: <alloc-fail> while constructing String or quoted-Symbol output.

Source: lib/var.x:1061

Var.pointer

void *Var.pointer(Var v)

Returns the raw address stored in v, or NULL if it holds no address. Every pointer, reference, and object family shares one decoder: the payload is masked free of the subtype bits its family reserves, so the result is the stored low-48-bit address for a <u8*>, a <string> handle, and a registered custom object. The returned pointer is borrowed; this operation does not retain it or change its lifetime. A value that is not address-shaped, such as an integer, a double, a Symbol, a wide box, or void, reads as NULL, and nothing distinguishes that from a stored null pointer.

Nothing here proves that an accepted address is live or came from the right constructor; that remains the typed API’s precondition. This raw decoder also accepts some reserved pointer-shaped bit patterns. Validate external bits with Var.encoding_valid, then confirm the family with Var.tag or Var.is before trusting the result.

Source: lib/var.x:1015

Var.register_object_tag

int Var.register_object_tag(Symbol tag)

Reserves or returns a process-lifetime row for custom boxed-object tag. Before registration freezes, a repeated custom tag returns its existing row; NULL, a built-in tag, or a full 32-row registry returns -1 without changing the registry. Registration must finish before the first successful Thread.start; afterward it raises <bad-state>. Native registry-mutex failure aborts.

Source: lib/var.x:240

Var.tag

Symbol Var.tag(Var v)

Returns the Symbol naming the exact family of v’s payload. The tag names one family: <i32>, <string>, <f64>, <symbol>, or a registered custom object tag. Built-in families have one row in the encoding table at the top of this file. Use Var.kind for a group of families and Var.is to test one family.

Raw Null, the all-zero Var written (Var) { .u64 = 0 }, is a native null pointer. It decodes as <p48>; there is no dedicated null family. Test it with Var.is_null. An encoding matching no reserved row decodes as <f64>, since the shifted double range covers everything left over.

Var raw = (Var) { .u64 = 0 };
printf("%s %s\n", raw.tag().str(), void.tag().str());

Source: lib/var.x:270

Var.ulong_long_value

unsigned long long Var.ulong_long_value(Var v)

Returns a <ullong> box’s unsigned payload, or 0 for another tag.

Source: lib/var.x:814

Var.ulong_value

unsigned long Var.ulong_value(Var v)

Returns the payload of a <ulong> box, or 0 if v has a different tag. Prefer this to Var.integer for a <ulong>: the general reader casts the payload to long, which reinterprets anything above LONG_MAX as negative, while this reader returns the unsigned value. A tag that is not <ulong> yields 0 silently, so test with Var.is when the tag is in doubt.

A tag mismatch yields 0.

Source: lib/var.x:806

Var.wide_compare

int Var.wide_compare(Var a, Var b)

Orders two wide boxes carrying the same tag, returning -1, 0, or 1. Integer families delegate to Var.integer_compare. An <ldouble> pair compares as long double and falls back to a byte comparison when neither operand is less than the other, so distinct NaN representations still order deterministically.

0 means equal, and it is also the answer for mismatched tags and for an argument that is not a wide box. Check the tags first, or use the relational operators, which reach the runtime’s full ordering.

Source: lib/var.x:984

Var.wide_equal

int Var.wide_equal(Var a, Var b)

Reports content equality for supported wide scalar boxes. Boxed <long>, <ulong>, <llong>, <ullong>, and <ldouble> values are separate allocations, so bit identity says nothing about their contents. This is the payload comparison that == uses for those tags. An <ldouble> pair is compared byte for byte, so two NaNs sharing one representation compare equal here.

A 0 result means “not equal as wide boxes”. It also covers an argument that is not a wide box and a pair whose tags differ. For a general equality test use ==, which reaches Var.equal and covers every family.

Source: lib/var.x:867

Var.wide_hash

unsigned Var.wide_hash(Var v)

Returns a supported wide scalar box’s content hash, or 0 otherwise.

Source: lib/var.x:834

Runtime-internal callables

These callables connect runtime translation units. They are documented for source readers but are not supported as user API.

FunctionSummary
Var.clone_wideClones the wide numeric value into a new box in the active Scope.
Var.custom_descriptor_indexReturns a registered custom object’s row, or -1 for another value.
Var.move_wide_toMoves a wide numeric box into the destination Scope held by scope.
Var.wide_ownerReturns the Scope owning a live wide numeric box, or NULL otherwise.

Var

Var.clone_wide

Var Var.clone_wide(Var value)

Clones the wide numeric value into a new box in the active Scope. The clone compares equal but not identical to value. Returns void when value is not wide.

Raises: <alloc-fail> when the clone cannot be allocated, or <bad-enc> if its address cannot be represented in a Var.

Source: lib/var.x:534

Var.custom_descriptor_index

int Var.custom_descriptor_index(Var value)

Returns a registered custom object’s row, or -1 for another value.

Source: lib/var.x:216

Var.move_wide_to

Self Var.move_wide_to(Self value, Scope *scope)

Moves a wide numeric box into the destination Scope held by scope. The box is not copied and value keeps its identity. If the destination slot is NULL, a new Scope is created there. Returns value unchanged for another family. For a wide value, raises <bad-arg> when scope is NULL, or <alloc-fail> when a new destination Scope cannot be allocated. Ownership is unchanged on failure.

Source: lib/var.x:559

Var.wide_owner

Scope Var.wide_owner(Var value)

Returns the Scope owning a live wide numeric box, or NULL otherwise.

Source: lib/var.x:566

Design notes

Var encodes dynamic values in one 64-bit word. Its layout uses the IEEE 754 double-precision ranges:

0000:0000:0000:0000 - 7FEF:FFFF:FFFF:FFFF : [+0.0, MAX_DOUBLE] 7FF0:0000:0000:0000 - 7FFF:FFFF:FFFF:FFFF : [+Inf, +NaNs] 8000:0000:0000:0000 - FFEF:FFFF:FFFF:FFFF : [-0.0, MIN_DOUBLE] FFF0:0000:0000:0000 - FFFF:FFFF:FFFF:FFFF : [-Inf, -NaNs]

Special-value ranges carry custom tags and payloads. An f64 is shifted by (1 << 52); shifted -DBL_MAX has a reserved encoding because all-one bits mean void. Native pointers retain their raw lower 48 address bits.

The 64-bit Var layout is structured as follows:

Tag : Payload Tag : Payload bits Use


0000:0000:0000:0000 - 0000:FFFF:FFFF:FFFF 48+0 void* 0001:0000:0000:0000 - 0001:FFFF:FFFF:FFFF 48+0 u8* 0002:0000:0000:0000 - 0002:FFFF:FFFF:FFFF 48+0 i8* 0003:0000:0000:0000 - 0003:FFFF:FFFF:FFFF 47+1 u16*,i16* 0004:0000:0000:0000 - 0004:FFFF:FFFF:FFFF 46+2 u32*,i32*,f32*,ulong* 0005:0000:0000:0000 - 0005:FFFF:FFFF:FFFF 45+3 long*,f64*,ldouble*, llong*,ullong*,long, ulong,llong 0006:0000:0000:0000 - 0007:FFFF:FFFF:FFFF 45+3 builtin pointer families, ullong,ldouble 0008:0000:0000:0000 - 000B:FFFF:FFFF:FFFF 45+3 builtin (Class) x 32 000C:0000:0000:0000 - 000F:FFFF:FFFF:FFFF 45+3 builtin (Class ) x 32 0010:0000:0000:0000 - 7FFF:FFFF:FFFF:FFFF 64+0 f64 > 0 + (52<<1) 8000:0000:0000:0000 - 8000:FFFF:FFFF:FFFF 48+0 u48 8001:0000:0000:0000 - 8001:FFFF:FFFF:FFFF 48+0 i48 8002:0000:0000:0000 - 8002:FFFF:FFFF:FFFF 32+16 32-bit vals, 16-bit tag 8003:0000:0000:0000 - 8003:FFFF:FFFF:FFFF 32+16 Special values 8004:0000:0000:0000 - 800B:FFFF:FFFF:FFFF 50+1 Symbols 800C:0000:0000:0000 - 800F:FFFF:FFFF:FFFF 45+3 user (Class) 32 8010:0000:0000:0000 - FFFF:FFFF:FFFF:FFFF 64+0 f64 < 0 + (52<<1)


A Var preserves its runtime kind. void is excluded from value-bearing protocols.

Tests and examples

make verify (unittest/test-var.x) and make examples (type-conversion-matrix).