Literals#
| Type | Example |
|---|---|
| Integer | 42, 0, -5 |
| Float | 3.14, -2.5, 0.0 |
| String | "Hello, World!" |
| Boolean | true, false |
| Hexadecimal | 0xFF, 0xDEADBEEF |
| Binary | 0b10110100, 0b1111 |
| Octal | 0o17, 0o755 |
| Character | 'A', '!' |
Note: Float literals are recognized by the presence of a decimal point. Floats and integers can be mixed in arithmetic expressions.
Note: Arithmetic operates on numbers (booleans count as 0/1). Text, buffers, and lists must be cast with as a number or as a float before they can be used in arithmetic - using them directly is a compile error, since they hold pointers rather than numeric values.
Hex, Binary and Octal:
- Hexadecimal literals use
0xprefix:0xFFequals 255 - Binary literals use
0bprefix:0b1010equals 10 - Octal literals use
0oprefix:0o17equals 15 - The prefix letter may be a capital (
0X1F,0B1,0O17), and so may a hex digit - A leading zero without a prefix means nothing:
0234equals 234 - Character literals use single quotes:
'A'equals 65
Variable Reference#
the x- references the variablexthe number- references loop iterator (insidefor each)x- direct identifier reference
Arithmetic#
the x add 5
y subtract 3
the lhs multiply rhs
total divide 2
x modulo 3
{x add y} multiply z
{fibonacci of n subtract 1} add {fibonacci of n subtract 2}
Note: the is optional before variable names in expressions.
For complex arithmetic subexpressions, use curly braces {...} to group each subexpression. A cast (as a <type>) binds tighter than arithmetic and applies to the expression immediately to its left, so s as a number add 1 casts s and then adds 1. To cast a whole arithmetic expression, brace it: {a add b} as a number. Comma-separated arithmetic continuation (for example ..., add ...) is not valid syntax. Dividing by 0, a modulo by 0, and dividing the most negative number by -1 each set the error flag and give 0.
Comparisons#
the x is greater than 5
y is less than 10
lhs is equal to rhs
x is 0
Note: the is optional before variable names in comparisons.
An ordering comparison takes a value once it is a number. A value's type is only known at runtime, so is greater than, is less than, their or equal to forms and their not forms are a compile error with a value on either side, whether it is a variable or a call that returns one, as arithmetic on a value is. A text, a list, a map and a buffer have no order, so the same forms refuse them; compare two texts with is, and a list, a map or a buffer by its length. Equality (is, is equal to, is not) takes a value as it is. To compare a value by order, check it with is a number, put it in a number variable, and compare that:
a value called v is "two".
a number called n is 0.
If v is a number, set n to v.
If n is greater than 1 then, print "greater".
Otherwise, print "not greater".
(prints: not greater)
Property Checks#
the x is even
the y is odd
the z is positive
the n is negative
the value is zero
the list is empty
Logical Operators#
<condition> and <condition> (true if both conditions are true)
<condition> or <condition> (true if either condition is true)
not <condition> (true if condition is false)
not takes the whole condition after it, exactly as the fence above says and exactly as English does: If not heat is limit then, reads "if it is not the case that heat is limit", never "if the negation of heat is limit". So not binds looser than every comparison and property check, and tighter than and and or: not heat is 4 and limit is 6 is {not (heat is 4)} and (limit is 6). A not in front of a boolean is that same rule with the shortest condition: If not door_open then,.
A not always answers a boolean, whatever it is applied to: not 5 and not greeting are booleans, not a number and a text. On a text, list, map or buffer, not tests the value's pointer (which a declared variable always has), so it answers false whether or not the collection holds anything. Ask about contents with is empty (see Property Checks above), never with not.
Plural Comparisons with are#
Test multiple variables against the same value using comma-separated subjects:
if x, y, and z are true
if a, b, and c are not false
if 'door open', lift_moving, and lift_full are not true
Expansion:
if x, y, and z are true
expands internally to:
if x is true and y is true and z is true
Rules:
- Subjects are separated by commas
- The word
andbefore the last subject is optional but recommended for natural language readability - The predicate after
areapplies to ALL subjects are notnegates the comparison for all subjects
Type Casting#
Convert values between types using the as or in keywords.
Syntax:
<value> as a <type>
<value> as <type>
<value> in <unit>
Basic Conversions:
| From | To | Syntax | Result |
|---|---|---|---|
| float | number | 3.14 as a number | 3 (truncated) |
| number | float | 42 as a float | 42.0 |
| number | text | 25 as text | "25" |
| text | number | "123" as a number | 123 |
| float | text | 3.14 as text | "3.14" |
| text | float | "3.14" as a float | 3.14 |
| boolean | number | true as a number | 1 |
| boolean | number | false as a number | 0 |
| number | boolean | 0 as a boolean | false |
| number | boolean | 42 as a boolean | true |
| boolean | text | true as text | "true" |
| text | boolean | "true" as a boolean | true |
| buffer | text | data as text | a copy of the buffer's bytes |
A text made from a buffer is an independent copy, not a window onto the buffer. a text called line is data as text. reads the buffer's current bytes once and keeps its own copy, so clearing, refilling, or resizing data afterwards leaves line exactly as it was: the same promise format strings make (see "Format Strings as Values"). This matters because resizing frees the buffer's old allocation: without the copy, reading such a text would be reading freed memory.
The cast is optional for this one conversion. Every spelling that puts a buffer into a slot that holds text means the same thing and makes the same copy (a text called line is data., Set line to data., the line is data., a text parameter given a buffer argument, and Return a text, data.) as do data as text and "{data}". Writing the cast is still good style where the type change is worth pointing at, but leaving it out never changes what the sentence does. This does not loosen type immutability: line is text before the write and text after it, and every other mismatched write is still the compile error described under "Type Immutability".
A value slot is one of those slots. A buffer written into a value (by declaration, by Set, by the ... is, as a value argument, or by Return a value) arrives as text and reports Text (dynamic), carrying the same independent copy of the buffer's bytes. A value never holds a buffer as a buffer; there is no Buffer (dynamic) tag.
A float read from text is the same double as the literal. "0.88" as a float and the literal 0.88 are one value, and comparing them with is finds them equal: the runtime reads a decimal exactly the way the compiler reads one written in the source. The guarantee covers up to eighteen significant digits with the point up to twenty-two places away from them - wider than a float can tell apart - and a longer decimal is read as the nearest float those eighteen digits describe. This is what lets a number read from a file, an argument or an environment variable be compared against a literal in the same program. A source literal longer than eighteen significant digits is itself read to the nearest float all its digits describe, so beyond the guarantee the literal and the same decimal read from text can differ by one unit in the last place. Compare an over-precise value within one route, never across the two.
Radix (Base) Conversions:
Text-to-number casting isn't limited to base 10. A radix word can be inserted right before number to parse in a different base:
| Syntax | Base | Example | Result |
|---|---|---|---|
as a number | 10 (default) | "42" as a number | 42 |
as a hex number / as a hexadecimal number | 16 | "ff" as a hex number | 255 |
as an octal number | 8 | "17" as an octal number | 15 |
as a binary number | 2 | "1010" as a binary number | 10 |
as a base N number (spaced) | any 2-36 | "z9a" as a base 36 number | 45694 |
as a baseN number (fused) | any 2-36 | "6543" as a base7 number | 2334 |
Any base from 2 through 36 is supported, not just the aliased ones (hex/octal/binary) - digits above 9 use letters a-z (case- insensitive), so base 36 is the practical maximum for a single- character-per-digit representation.
(Hex string to number)
a text called hexstr is "3fa2c1e4".
a number called n is hexstr as a hex number.
(Arbitrary base, fused or spaced form - both work)
a text called s is "6543".
a number called n2 is s as a base7 number.
a number called n3 is s as a base 7 number.
(Negative numbers and uppercase hex digits both work)
a text called neg is "-1a".
a number called n4 is neg as a hex number. (-26)
a text called upper is "FF".
a number called n5 is upper as a hex number. (255)
A radix cast reads the whole text as digits of that base, after an optional - and, for base 16, 8 and 2, an optional 0x, 0o or 0b prefix: "0x1F" as a hex number is 31, "0o17" as an octal number is 15. Any other character makes the text not a number in that base: "12g5" as a hex number (g is not a hex digit), "abc" as a base5 number (a's value of 10 is too big for base 5), and "0x1F" as a base 10 number (a prefix belongs to its own base) are not numbers; see the Casting Rules below for what follows.
Examples:
(Float to number - truncates)
a float called pi is 3.14159.
a number called 'pi truncated' is pi as a number.
(Number to text)
a number called age is 25.
a text called agestr is the age as text.
(Text to number - parsing)
a text called userinput is "123".
a number called parsed is the userinput as a number.
(Boolean to number)
a boolean called done is true.
a number called 'done num' is the done as a number.
(Inline casting)
Print 3.14159 as a number.
The in Keyword:
The in keyword reads more naturally for timer duration casts. It applies to a timer's duration or elapsed, not to a plain number:
(Duration from timer)
Print the timer's duration in seconds.
Print the timer's elapsed in milliseconds.
in only works on a timer's duration/elapsed (it lowers to a duration cast); <number> in <unit> on a plain number is not valid syntax. To convert a plain number of milliseconds to seconds, divide: the millis divide 1000.
Formatted Output:
Numbers can be converted to padded text for display formatting with the zero-pad format specifier:
(Pad to 2 digits - for times like 09:05)
a number called h is 9.
a text called hpadded is "{h:02}".
Print the hpadded. (prints "09")
Casting Rules:
as a <type>andas <type>are equivalent (article is optional)- A cast binds tighter than arithmetic and applies to the expression immediately to its left:
n as a number add 1is(n as a number) add 1. Brace to cast a whole expression:{a add b} as a number - Float to number truncates (does not round)
- To round: add 0.5 before casting (
{3.7 add 0.5} as a number→4) - Text, buffers, and lists cannot be used directly in arithmetic; cast them with
as a number/as a floatfirst - A text is a number when the whole text could be written as a number literal in Vox source (see Literals), after one optional
-: decimal digits, where a leading zero means nothing ("0234"is234), with an optional fractional part ("4.8","-2.5"), or a whole number after0x,0bor0o("-0x345A"is-13402,"0o234"is156,"0b101"is5). Nothing else may be in the text:"12 apples","1.5x","1e5","+7"," 7","7 ","3.",".5","0x","-"and the empty text""are not numbers. as a numberof a text with a fractional part keeps the whole part and drops the fraction, as a float cast to a number does:"4.8" as a numberis4,"-2.5" as a numberis-2. The whole number must fit in a number (-9223372036854775808 to 9223372036854775807), in any spelling.as a floatreads the same texts:"4.8" as a floatis4.8,"0x10" as a floatis16.0. A text with no fractional part is a whole number, so it must fit in a number, as it must when written in the source.- A buffer cast to a number or a float is read exactly as a text.
- A text literal that is not a number cannot be cast to one:
"hello" as a numberis a compile error, because the compiler can read the text for itself. - A text known only when the program runs (a variable, a field, a buffer, a line of input, a
valueholding text) that is not a number raises the error flag, and the sentence that stores the cast changes nothing: the destination keeps the value it had, and a declaration holds its type's default (0,0.0). Avalueretyped in place (answer is a number.) keeps its text. A text that is a number clears the flag. - Zero is
false, any non-zero number istrue inkeyword is for timerduration/elapsedcasts (see above)
a text called reply is "hello".
a number called count is 5.
Set count to reply as a number.
On error print "not a number, so count is still {count}".