SAMUEL HP


Asa Introduction

Important

This is page is very much still a work in progress

Introduction to the Asa programming language



So, you want to use Asa?

Asa is a systems programming language built to be used similarly to C++, but be a good replacement. It aims to improve upon many of the shortcomings of that aging language, without sacrificing performance.

main :: (){
    printl("Hello World!");
}

The Compile Time Define Operator:

One of the operators you will be using most often in Asa is the compile time define. This is defined as the double colon, ::.
It shares many of the characteristics of the set operator (=), and sometimes their functionality even overlaps. It can also be compared to the #define preprocessor keyword from C or C++, although Asa does not have a preprocessor.

Compile time define is used to assign an expression to a name. The most basic example would be:

x :: 5;
Note

This code will evaluate to the exact same as x = 5;

Of course, more interesting expressions are better. Let's put a lambda expression on the right side:

x :: int(){ return 5 }

Now this is a function.

Other uses:

:: can be used for defining other things as well. Such as structs, modules, and special functions.

Defining Structs:

You can create a struct by writing the name, and defining it as a struct expression:

someStruct :: struct{

}

Defining Modules:

You can define a module by writing the name, and defining it as a module expression:

moduleName :: module{

}

Structs:

You can create a struct by writing the name, and defining it as a struct expression:

someStruct :: struct{
    // ...
}

Then to create an instance:

y : someStruct = someStruct();

Struct Members:

Member variables and functions are defined as normal. Functions, though, have slightly different behavior.
For example:

someStruct :: struct{

    x : int = 10;

    memberFunction :: (v : int){
        this.x = v;
    }
}

The difference with functions defined inside of a struct is that that they can only be called with the access operator (.), but they can also access members within the struct itself. To do this, you must use the this keyword, which references the instance the function is within.
Then member access is as normal like:

s : someStruct = someStruct();

printl(s.x); // -> 10

s.memberFunction(3);

printl(s.x); // -> 3

Struct specific functions:

There are some functions that have specific names and functionality for handling structs.

Create:

create :: someStruct(){
    // ...
}

Expressions:

In Asa, most things you write are "expressions". In other words, the individual components should be able to be evaluated all on their own. For example, take the following function:

functionName :: (){
}

Given the above function, we can split it into its sub-expressions:

The name:

functionName

and a lambda:

(){}

The lambda is simply an unnamed function. You can create one with parentheses containing the function arguments, followed by curly braces containing the body. Like: (x : int){}.
If you want the lambda to have a return value, it should be preceded by the type. Like: int(x : int){ return x+1; }


Special Function Definitions:

Just like in C++, there are some special ways to define functions for certain use cases.

Operator Overloading:

Operator overloading is used to override builtin behavior or add new behavior to existing or new symbols. For example:

operator@ :: int(x : int, y : int){
    return x * y;
}

printl(3 @ 1);
// -> Outputs 3

Defining an operator overload is done with the following syntax:

operator<symbol> :: <return type>(<Left value>, <Right value>){
}

The <symbol> can be any ASCII special character, and can be a double character as well. Example: $ or $$. The only symbols you cannot overload are the compiler define :: and a few punctuation symbols. Also, the list of available symbols is predefined, so some combinations may be missing.


Compiler keywords:

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


#import

One of the most used keywords, 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.


#file

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:

#file "./otherFile.asa";

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


#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.


#inline

This is a function modifier that tells the compiler it should be in-lined for performance.


#replaceable

This is a function modifier that tells the compiler another function with the same identity can be defined. While function overloading typically requires a different identity, if #replaceable is used in the original, it can be overloaded without an error. This will effectively delete the #replaceable function and use the newly defined one.


#hideast

This is a function modifier that just hides the function from showing in the AST verbose output. This is primarily used for compiler development.


#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 the following (except for the identities being the same, which would throw an error):

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;
}

#new

TODO: #new is for manually creating objects from their name, like: #new structName. It should be removed and replaced by an automatic create function addition.


#cast

TODO: #cast is for manually casting a value to a builtin type, like: #cast 5 : float. It should be removed and replaced by an automatic cast function addition.