This page documents all types in the Jou programming language.
Jou has the following signed integer types:
| Name | Size | How to print | Min value | Max value |
|---|---|---|---|---|
int8 |
1 byte (8 bits) | %d |
-128 |
127 |
int16 |
2 bytes (16 bits) | %d |
-32_768 |
32_767 |
int32 or int |
4 bytes (32 bits) | %d |
-2_147_483_648 |
2_147_483_647 |
int64 |
8 bytes (64 bits) | %lld |
-9_223_372_036_854_775_808 |
9_223_372_036_854_775_807 |
intnative |
4 or 8 bytes | %zd |
one of the above two values | one of the above two values |
And the following unsigned integer types:
| Name | Size | How to print | Min value | Max value |
|---|---|---|---|---|
uint8 or byte |
1 byte (8 bits) | %d |
0 |
255 |
uint16 |
2 bytes (16 bits) | %d |
0 |
65_535 |
uint32 |
4 bytes (32 bits) | %u |
0 |
4_294_967_295 |
uint64 |
8 bytes (64 bits) | %llu |
0 |
18_446_744_073_709_551_615 |
Values of integers in Jou code may contain underscores. They are ignored, but they often make large numbers much more readable. The minimum and maximum values shown above are also in stdlib/limits.jou.
For convenience, the most commonly used types have simpler names:
byte always means same as uint8 and int always means same as int32.
The intnative type is defined in stdlib/intnative.jou,
not built-in like other integer types shown here.
There is a better explanation of it in the Jou tutorial.
Please create an issue on GitHub
if you need a corresponding uintnative type.
When printing, %d can be used for anything smaller than 32 bits,
because values smaller than int are automatically converted to int.
With 32-bit and 64-bit values, you need to be more careful:
use u for unsigned, and add ll for 64-bit values or z for native-size values.
The ll in %lld and %llu is short for "long long",
which is basically C's way to say "64-bit number".
There is also %ld and %lu,
but I don't recommend using them in Jou code because their behavior on different platforms is surprising:
they print a native-size value on all platforms except that on 64-bit Windows,
they print a 32-bit value.
Integers wrap around if you exceed their minimum/maximum values.
For example, (0 as byte) - (1 as byte) produces 255 as byte.
It is not possible to invoke UB by overflowing integers.
| Name | Example | Size | How to print |
|---|---|---|---|
float |
12.34 as float |
4 bytes (32 bits) | %f |
double |
12.34 |
8 bytes (64 bits) | %f |
When printing, floats are converted to doubles automatically,
so the same %f works for both floating-point types.
| Name | Example | Size | Description |
|---|---|---|---|
T[n] where T is any type |
[1, 2, 3] (type int[3]) |
n times the size of T |
Array of n elements (n known at compile time) |
T* where T is any type |
"hello" (type byte*) |
4 or 8 bytes (32 or 64 bits) | Pointer to T |
funcptr(...) -> ... |
funcptr(int) -> int |
4 or 8 bytes (32 or 64 bits) | Not documented yet :( |
void* |
NULL |
4 or 8 bytes (32 or 64 bits) | Pointer to anything |
See pointers in the Jou tutorial if you are not already familiar with pointers.
The size of a pointer is 32 bits on a 32-bit system and 64 bits on a 64-bit system.
In other words, the size of any pointer is the size of intnative:
a pointer is basically an intnative that contains the location of something in the computer's memory.
An array is simply n instances of type T next to each other in memory.
The array length n must be known at compile time,
because in Jou, the compiler knows the sizes of all types.
Use lists if you want an array that grows dynamically as items are added to it.
To get the number of elements of an array as int, you can use the array_count built-in.
Pointers and arrays can be combined with each other.
For example, byte[100]* means a pointer to an array of 100 bytes,
and int** means a pointer to a pointer to an integer.
Note that indexes and array sizes are reversed.
For example, array: int[10][100] means 100 rows of 10 ints each,
so array[99][9] is the bottom right corner, and array[9][99] is UB.
A void pointer (void*) is used when you don't want the compiler to know
the type of the object being pointed at.
You can use void pointers whenever a pointer is expected.
However, you can't do e.g. pointer++ or *pointer or pointer[index] with a void pointer,
because to do that, the compiler would need to know the size of the underlying value.
| Name | Example | Size |
|---|---|---|
| a class | see classes.md | total size of members + padding |
| an enum | see enums.md | 4 bytes (32 bits) |
bool |
True, False |
1 byte (8 bits) |
Once the program is compiled, enums basically become ints,
and bools become bytes (zero for False, one for True).
The size of a bool is one byte,
because byte is the smallest unit that computers like to work with.
This doesn't matter as much as you might think, but as usual,
please create an issue if this becomes a problem for you.
Jou has two kinds of casts:
- Implicit casts are done basically whenever a value must be of a specific type.
For example, you can pass an
intto a function that expects anint64argument, because function arguments are cast implicitly. - Explicit casts are done with the
askeyword, e.g.0 as byte.
The allowed implicit casts are:
- Array to pointer:
T[N](whereTis a type andNis an integer) casts implicitly toT*orvoid* - Integer to integer: Integer type
T1casts implicitly to another integer typeT2if the size ofT1is smaller than the size ofT2, and this is not a "signed → unsigned" cast. For example,bytecasts implicitly toint(8-bit unsigned → 32-bit signed), andintcasts implicitly toint64(32-bit signed → 64-bit signed). floatcasts implicitly todouble.- Any integer type (signed or unsigned) casts implicitly to
floatordouble. - Any pointer type casts implicitly to
void*, andvoid*casts implicitly to any pointer type.
Explicit casts are:
- Array to pointer:
T[N](whereTis a type andNis an integer) casts toT*orvoid*. Note thatarray_of_ints as int64*is a compiler error. - Any pointer type casts to any other pointer type. This includes
void*pointers. - Any number type casts to any other number type. This is described in more detail below.
- Any integer type casts to any enum.
- Any enum casts to any integer type.
boolcasts to any integer type and produces zero or one. For example,True as intis1. However, integers cannot be cast tobool. Use an explicitfoo == 1,foo != 0,foo > 0ormatch foo: ...depending on what you need.- Any pointer type casts to
intnative. (This gives the memory address as an integer.) intnativecasts to any pointer type.- Anything that type inference is capable of doing (see below).
For example
123123123123123 as int64or"foo" as byte[10].
This section defines what exactly "any number type casts to any other number type" mentioned above does.
Casting an integer to another integer wraps around:
import "stdlib/io.jou"
def main() -> int:
printf("%d\n", 260 as byte) # Output: 4
return 0Casting a float or double to an integer truncates away the fractional part.
Out-of-range values become the smallest or largest value of the integer type.
The result is zero if the float or double is a NaN value.
For example:
import "stdlib/io.jou"
import "stdlib/math.jou"
def main() -> int:
printf("%d\n", 1234.5 as byte) # Output: 255
printf("%d\n", 24.68 as int) # Output: 24
printf("%d\n", -24.68 as int) # Output: -24
printf("%d\n", INFINITY as int) # Output: 2147483647
printf("%d\n", NAN as int) # Output: 0
return 0When the Jou compiler is determining the type of a string or integer in the code, it considers how it is going to be used.
For example, this is an error, because the type of 1000000000000000 is int by default:
def main() -> int:
x = 1000000000000000 # Error: value does not fit into int
return 0But if you specify a type for the x variable,
the compiler infers the type of 1000000000000000 based on it:
def main() -> int:
x: int64 = 1000000000000000 # This works, no errors
return 0Jou's type inference is implemented in a very simple way compared to many other languages and compilers. For example, using the variable later does not affect the inference in any way:
def main() -> int:
x = 1000000000000000 # Error: value does not fit into int
y: int64 = x # Does not affect the type of x
return 0Jou also uses type inference for strings.
By default, strings are byte*, but they can also be inferred as arrays:
import "stdlib/io.jou"
def main() -> int:
string1 = "foo" # Type of "foo" is byte* (the default)
string2: byte[100] = "foo" # Type of "foo" is byte[100]
n = sizeof("foo") # Type of "foo" is byte[4] (smallest possible)
# Output: 4 bytes are needed for "foo".
printf("%d bytes are needed for \"%s\".\n", n, string1)
return 0When the sizeof operator is applied to a string,
the compiler chooses the smallest possible array size where the string fits.
Above "foo" needs 4 bytes because of the terminating zero byte:
in this context, "foo" is same as ['f', 'o', 'o', '\0'].