-
Notifications
You must be signed in to change notification settings - Fork 81
Syntax and coding style
In the beginning, the Jai language was similar to C with some constructs somewhat inspired by other languages. Now that the language is more mature some weird syntaxes started to appear so as a first impact you might get a bit confused. In this brief intro, to begin to familiarize with the syntax we'll take a look at the very basic stuff.
// Simple one line comment
/* Multi line comment */
/* A strange /* nested */ comment */
ALERT :: 0xFFAA00; // An hexadecimal constant
RAW_PI :: 3.1416; // A floating point constant
ROLE_1 :: "Wizard"; // A static string constant
Style: for constants prefer using all upper case naming convention;
The syntax name: type = value;
specifies that a variable named name
is of the type type
will be assigned the value value
. It is similar to Typescript style and was proposed by Sean Barrett.
If the type
is omitted it will be automatically inferred by the compiler through the type of the assigned value.
Some examples:
// Unassigned declarations will be initialized to default values (zeroes) unless specified
counter : int; // Signed 32 bit integer
key : s64; // Signed 64 bit integer
flags : u8; // Unsigned 8 bit integer
// Declarations and assignments
counter : int = 0; // Simple signed 32 bits integer with assignment
mode := 10; // Type int automatically inferred by the compiler
area : float = r * r * RAW_PI; // Single precision 32 bit floating point
average := 0.5 * (x+y); // Another float automatically inferred by the compiler
message : string = "Hello "; // A standard native unicode 16 bit char string
name := "Sailor"; // Type string automatically inferred by the compiler
/* Multiple declarations */
// Declare two quaternions initialized with a default value by a factory function
rot1, rot2: Quaternion = q(0, 0, 1, 0);
// Declare two 3D vectors both assigned to uninitialized values
v1, v2: Vector3 = ---;
Style: For variables prefer using all lower case naming convention;
Note: If not necessary avoid type declaration in assignments. Let the compiler infer the type.
Are reusable blocks of code that could accept arguments.
// Define a procedure with arguments
do_something :: (what: string){
printf("I'm doing: %", what);
}
// Another procedure without arguments
main_loop :: () -> void{
while !should_exit {
get_user_input();
simulate();
render();
post_process();
}
}
Originally procedures were functions that don't return any value ( -> void ) as in C. But now you can omit the return value.
Style: prefer using snake case naming convention for naming procedures and all the compound terms in general
In Jai functions return single or multiple values
// Define a simple function
square :: (val: float) -> float {
return val * val;
}
// Define a function with multiple unnamed results
circle_area:: (radious: float) -> float, bool {
return square(radious) * RAW_PI, radious > 0 ;
}
// Define a function with multiple named results
complex_calc:: (a: float, b: float) -> (result: float, success: bool) {
r1 := 0.0;
r2 := true;
...
return r1, r2;
}
Note: In the second case, the names for the results are only descriptive. (This might change in the future)
// define a new struct type
Spring :: struct {
relaxed_length : float; // millimeters
theta : float; // N/m
force : float; // N
current_length : float; // mm
}
Style: prefer using capitalization for the first letter of the user-defined types.
// declare a new spring allocate it on the stack and initialize to zeroes
a_spring : Spring;
// use dot notation to access the fields
a_spring.relaxed_length = 100;
// or use the 'using' keyword for a block
using a_spring {
theta = 100.0;
force = 25;
current_length = get_current_lenght(a_spring);
}
// or use the 'using' keyword for the current scope
stronger_spring := a_spring; // declare another spring and copy the data
using stronger_spring;
theta = 1000.0;
current_length = get_current_lenght(stronger_spring);
// or use the 'using' keyword in a function
get_current_length(a_spring: Spring){
using a_spring;
return theta / relaxed_length * force + relaxed_lenght;
}
// define a new static array on the stack automatically initialized to zeroes
storage : [50] int;
// assign a value to an item by index
storage[0] = 20;
// define a variable-length array and allocate it on the heap
coefficients: [..] float;
// deallocate at the end of the scope
defer array_free(coefficients);
// define a list of constant values
verbs: [] string (:string: "put","get","post","delete");
Arrays do not automatically cast to memory pointers as in C. Because they are special data structures that contain also array size information. Functions and procedures can take array types and query them for the size of the array.
print_int_array :: (a: [] int) {
n := a.count;
for i : 0..n-1 {
print("array[%] = %\n", i, a[i]);
}
}
Retaining the array size information can help developers avoid the pattern of passing array lengths as additional parameters (see C2x proposal) and assist in automatic bounds checking (see C’s Biggest Mistake).
Note: The libraries of the language have many other useful and powerful collection data types already defined.
Player_roles :: enum {
SOLDIER; // Automatically assigned to 0
PALADIN :: 1; // Can be defined explicitly
WIZARD :: 100; // Can be defined leaving gaps
SUPER_WIZ; // And this will have the value of 101
}
This syntax mimics a series of constant definitions, so you can easily convert them in an enumeration.
These documents were verified using Grammarly free browser plugin for Chrome. Please use some spell checker before submitting new content.
- Variables and assignments
- Language data types
- Simple user-defined data types
- Expressions and operators
- Type-casting
- Pointers
- Declarations
- Arguments / Parameters
- Return values
- Overloading / Polymorhism
- Advanced features
- Lambdas
- Arrays
- Strings
- Composition of Structs
- Metaprogramming
- Templates / Generics