This document has tips for getting started using the language in its current beta version, that aren't currently covered in the official distributed documentation. The distributed README.txt and the core programs in the how_to/ folder should be read first!

Also check out the Jai-Community-Library wiki.

Last updated 2021-10-21

Getting started info

Navigating and finding functionality

  • Read the names of all the included modules in the distributed modules/ folder, to get a sense of what's available.

  • Check out this Jai Awesome List which has community-made modules.

  • Using grep-like tools to search the distributed code is a useful and common way to "get around" and find what you're looking for in the language. Some examples:

    • If I want to find where the string-related functions are, I might search for "string_".
    • If I want to find some example usage of a certain module, function, or keyword, I might search for "#import "Module_Name"" or "function_name" or "keyword".
    • If I want to find the definition of a function, I might search for "function_name ::".
  • These are some grep-like tools:

  • These are modules I recommend looking at when starting out:

    • Basic
    • String
    • Hash_Table
    • Math
    • File
    • Compiler

Directory organization and custom modules

You can organize your directories however you like. One format some people use is:

jai/
    jai/            - The distributed compiler folder. For compiler updates, just replace this folder.
        bin/
        examples/
        ...
    jai_modules/    - Downloaded modules and your own modules. Use the compiler's -import_dir command line flag for it to find your custom modules folder.
    my_project_1/
    my_project_2/
    ...

Debugging

You can use a debugger with an exe and the generated .pdb file. For example, in Visual Studio, open the .exe file using File -> Open -> Projects/Solution, and you can set breakpoints and run.

Crashes

If your program crashes, as of the time of this writing, the program will stop and nothing will be printed out. This can be confusing! You can use the Debug module to print the stack trace on crashes:

Debug :: #import "Debug";

main :: () {
    Debug.init();

    a := 0;
    b := 1 / a; // Cause a crash

    write_string("done\n"); // The execution will not reach this point
}

Pointer operators

a: int;
b: *int = *a; // address-of
c: int = <<b; // dereference

Non getting started info

The following information is misc. things not covered in the documentation currently, but is also not especially useful when getting started with the language.

Auto-cast

The xx operator (syntax may be changed) is a shortcut to auto-cast to the required type.

a: s64;
b: s32 = xx a;

Uninitialized variable

a: Vector3 = ---; // This variable will be uninitialized

Anonymous structs

This can be a useful grouping for data that only has one copy:

state: struct {
    a, b: int;
};
some_function :: () {
    state.a = 4;
}

Anonymous enum

This can be useful for an enum that only has one copy:

state: enum {
    A;
    B;
};
some_function :: () {
    state = .B;
}

Struct "using base"

It can be useful for a struct to have a 'parent" struct as its first member, so that you can treat a pointer to one as a pointer to its parent. This is also described in the types how_to.

Entity :: struct {
    type: Type;
    x, y: float;
}

Player :: struct {
    using #as base: Entity;
    player_index: int;
}

entities: [..]*Entity;
p := New(Player);
p.type = Player;
p.player_index = 1;
array_add(*entities, p);

for entities {
    if it.type == Player {
        player := cast(*Player) it;
        print("%\n", p.player_index);
    }
}

Compiler directives

This list is very rough, may miss some important ones, and descriptions are just rudimentary. The ones with descriptions are the ones I use / see used most commonly. Search the modules/ and examples/ folders for example usage.

#add_context        Add a declaration to the context
#align
#assert
#bake
#c_call
#caller_location
#char               Character literal
#code
#compiler
#complete           Ensure an if-case satement checks all values of the enum
#define
#else
#endif
#expand
#file
#filepath           The directory of the running executable
#foreign
#foreign_library
#foreign_system_library
#if
#ifdef
#ifndef
#import             Import a module
#insert
#insert_internal
#intrinsic
#load               Load source code, as if it were placed right here (very commonly used)
#modify
#module_parameters
#must               Require a return value to be assigned to a variable
#no_abc
#no_alias
#no_padding
#place
#placeholder
#program_export
#run                Run this code right now (compile time)
#run_and_insert
#runtime_support
#scope_export       Set the scope for future declarations to be accessible by code that imports this module
#scope_file         Set the scope for foture declarations to just this file
#scope_module       Set the scope for foture declarations to just this module
#specified          Declare intention of maintaining enum values compatibility over time
#string             String literal with delimiter
#symmetric
#through
#type               Type literal, e.g. for function types
#type_info_none
#type_info_procedures_are_void_pointers