Top-levels ========== In an LRL file there are three kinds of basic **top-level declarations**: - data - functions - types Of these three, data and type declarations may also appear inside functions. There are also some special top-levels: - namespace - interop - uses All declarations declare a name in the current namespace. The name may optionally use nested namespaces in the current namespace, or may be a "here" identifier. See identifiers.md. Any declaration may optionally have a linkname before it. See the section on "name mangling" for more information. In addition, there are **declaration flags**, which modify the behavior in some way. Not all kinds of declarations can have all of these flags. - alias - deprecated - incomplete In addition, a data or function declaration may optionally have one of these linkage flags: - declonly - export - import - local Data declarations ----------------- A data declaration has the following syntax: flags type name = value; The flags and the value is optional. For example: int x; local int y; alias int z = 2; The value can be any kind of expression, with one exception. When a data declaration is used as a top-level (and not as a statement in a function), the value must compile-time constant. If a data declaration has the **alias** flag, then it is simply replaced with it's value everywhere where it's used. Otherwise it's allocated in the data section. If the value is omitted, the initial value will be undefined. It is an error to attempt to read from an undefined value, and what will happen is undefined and depends on the backend. Function declarations --------------------- A function declaration has the following syntax: flags return-type name(parameter-declarations) { function-body } The flags and the function body are optional parts. For example: local () do_work(); alias int add_numbers(int x, int y) { return x + y; } (int, bool) return_a_struct() { return (3, :true); } noreturn fail() { print_error(); exit_the_program(); } If a function does not return a value, use the empty struct () as the return type. If a function _never_ returns, use the **noreturn** keyword in place of the return type. This should be used on functions that always exit the program when called. If a function has the **alias** flag, then it will be inlined in each place where it is used. Otherwise it is allocated in the code section. Type declarations ----------------- A type declarations has the following syntax: typedef flags name = type; The flags are optional. For example: typedef checksum = uint32; typedef alias cheksum = checksum; // e.g. mispelled in old version typedef Point = (int x, int y); typedef incomplete Screen = ( int width, int height, bool is_external, private // the struct may have more members here, but only the // the part(s) of the program that have complete // definition of the struct can access them ); typedef Secret = private; The **alias** flag makes types compatible with each other. By default, types with different names are not compatible. The **incomplete** flag indicates that the full type is not known. Incomplete types can be used to prevent access information outside of a particular part of a program or library. That part can contain the full definition. It is also possible to create a fully hidden type, by defining it to be the **private** type. In this case, the incomplete keyword should not be used. Namespace --------- A namespace declaration has the following syntax: namespace name { contents } For example: namespace calc { int add(int x, int y); int sub(int x, int y); } Note that each file also has an automatically created namespace based on the filename and directories it's contained in. So usually it's not necessary to create namespaces with the **namespace** keyword. The same namespace may be declared in multiple places. In that case the namespaces are simply merged together. Also, a namespace may have the same name as data declaration, function declaration or type declaration. For example: typedef Num = int; namespace Num { Num add(Num x, Num y); } Uses ---- **TODO** Interop ------- **TODO** Linkage flags ------------- Function declarations and data declarations may have linkage flags. **TODO** Name mangling ------------- The output files compiled by LRL can typically NOT contain namespaces. Therefore, the namespaces are glued into a string. This process is called "name mangling". LRL uses the "_" to glue namespaces together, which makes LRL code easy to call from C code, and vice versa. However, C++ compilers use different name mangling schemes, which are not compatible with LRL. For example, names are be "name mangled" as follows. It works the same way for both functions and data: int a; // a int a:b; // a_b namespace x { int c; // x_c () f(); // x_f } In addition the filename is also added, if you compile from a file. So if the file above would be called "file.lc" in a directory called "dir", then we would get the following result: int a; // dir_file_a int a:b; // dir_file_a_b namespace x { int c; // dir_file_x_c () f(); // dir_file_x_f } To override the default name mangling used by LRL, one can use the **linkname** keyword. It may be placed in front of declarations like this: linkname "aa" int a; // aa linkname "bb" int a:b; // bb linkname "example" namespace x { int c; // example_c linkname "g" () f(); // g } The linkname keyword may also be written on a single line, in which case it changes the linkname of the namespace it's placed in. // change linkname for the file. // this line must appear first in the file or namespace. linkname "changed"; int a; // changed_a; int a:b // changed_a_b namespace x { // make linkname empty in this namespace. // again, it must appear first. linkname ""; int c; // c () f(); // f } **TODO:** Add a check in the parser that the linkname statement is actually first. It is not allowed to have different identifiers with the same mangled name. It is also not allowed to have different linknames for the same namespace. For example: linkname ""; linkname "b" int a; int b; // problem! linkname "one" namespace to_be_merged { int x; } // problem! we can't have a different linkname here. linkname "another" namespace to_be_merged { int y; } Linknames do not affect the "uses" or "interop" declarations.