Compiler Directives

In Asa, there are a number of directives for modifying the behavior of the compiler during compilation. These all start with the character #. Any compiler directive is an expression that will be evaluated at compile time.


#import

One of the most used directives, used for importing modules by name. For example:

#import Rendering.Window;
#import Rendering.Drawing.Line;

This imports the module Window in the directory modules/Rendering. The module expression can be more complex, for example sub-directories: #import A.B.C.ModuleName;, which would be in modules/A/B/C. You can also wildcard import modules by using the base path followed by an asterisk: #import Builtin.*;, which would import all modules of all files in the directory modules/Builtin/.

#import looks in multiple locations for the specified module. First, it looks in the local directory from where it is called. If there is a matching module (including all path components), then it will stop there. If it does not find a matching module locally, it will look in the global modules folder, which is installed next to the asa executable. This allows you to override builtin modules for specific use cases.


#embed

This is similar to #import, but instead of only including a single module, it loads the entire file. Also, it does so by an explicit path. For example:

#embed "./otherFile.asa";

This would compile and import the entire other file at the given path. The path is evaluated relative to the file which #embed is in.


#library

Used to specify the name of a library to link against. For example:

#library "ssl";

This would link against libssl. It is equivalent to passing -lssl with --clangoptions.


#library_static

Used to specify the name of a library to statically link against. For example:

#library_static "/path/to/libssl.a";

This would link against libssl. It is equivalent to passing /path/to/libssl.a with --clangoptions.


#linenum

Gets the line number as an integer of its own location in the source code file. For example:

// some line
print(#linenum); // Prints 4

#linecol

Gets the line column as an integer of its own location in the source code file. For example:

print(#linecol); // Prints 6

#line

Gets the line contents as a string at its own location in the source code file. For example:

print(#line);

The above prints print(#line);. (Putting this in a comment would be recursive.)


#filepath

Gets the full path of the source code file as a string. For example:

// In a file at /home/user/main.asa
print(#filepath);

The above would print /home/user/main.asa.


#funcname

Gets the name of the function it is in as a string. For example:

someFunc :: int(){
    print(#funcname);
    ...
}

The above would print someFunc.


#modulename

Gets the name of the immediate module it is in as a string. For example:

moduleA :: module{
    print(#modulename);
}

The above would print moduleA. But in the case of nested modules like so:

moduleA :: module{
    moduleB :: module{
        print(#modulename);
    }
}

The above would print moduleB.


#asaversion

Gets the version of the Asa compiler as a string as it was when compiled. For example:

print(#asaversion);

The above would print the current version string of the asa compiler.


#counter

Acts as an automatically incrementing integer. For example:

print(#counter); // Prints 0
print(#counter); // Prints 1
print(#counter); // Prints 2

#nameof(I)

Gets the name of an identifier as a string. For example:

someVar : int = 0;
print(#nameof(someVar));

The above would print the string someVar.


#typeof(T)

Gets the type name of an identifier or type as a string. For example:

someVar : int = 0;
print(#typeof(someVar)); // Would print `int`
print(#typeof(string));  // Would print `string`

#sizeof(T)

Gets the size in bytes of an identifier or type as an integer. For example:

someVar : int = 0;
print(#sizeof(someVar)); // Would print `4`
print(#sizeof(int8));    // Would print `1`

#extern

This is a method to reference external functions from libraries. For example:

#extern printf :: int32 (s : const *char, ...);

The above would allow you to use the printf function from the C standard library. #extern use is simply stating the function name and parameters as they are defined, and not including a body.


#compiles(expression)

Checks at compile time if expression will compile successfully, or cause a compiler error. Returns true if it compiled without error, and false if it failed. The error message is thrown out, and never printed to stdout or stderr.

printl(#compiles(1 + 1)); // Prints `true`
printl(#compiles(undefinedVariable + 1)); // Prints `false`

#variant

This is a function modifier that creates variants of the function. For example:

foo :: ()
    #variant w = 0;
{
    printl(w);
}

Then you can use it like so:

foo<w=7>(); // -> 7
foo<w=2>(); // -> 2

This looks like arguments with extra steps, but the main difference is that it permanently creates a completely different function for each combination during the compilation process. So the above would have equivalent code to:

foo :: (){
    printl(7);
}

foo :: (){
    printl(2);
}

This is useful if you want to allow for a function to work with multiple types:

foo :: T(v : T)
    #variant T = int;
{
    return v * 2;
}