Primary Expressions

Grammar§

PrimaryExpression ::= ArrayLiteralExpression | AsmExpression | BoolExpression | EchoExpression | FloatExpression | IdentExpression | IntegerExpression | LambdaExpression | NullExpression | ParenExpression | StringExpression | SuperExpression | ThisExpression | TypeExpression ; SingleOrRangeExpression ::= Expression ( ".." Expression)?

Semantics§

Primary expressions are expressions that contain, after optional static evaluation, no other expressions. They represent simple values and mostly consist of a single token.

ArrayLiteralExpression§

Grammar§

ArrayLiteralExpression ::= "[" Expression ("," Expression)* "]"

Semantics§

Array literals are arrays with a TypeStaticArray type that is infered from the content. The first element that is not a NullExpression gives the type. The other elements must be implictly convertible to this type.

// first elem gives the type
const auto a = [0:s64, 0:s8];
assert(echo(is, a, s64[2]));

// second elem gives the type
var s64[]* b =  &a;
const auto c = [null, b, null];
assert(echo(is, c, s64[2]*[3]));

The whole array converts implicitly to a TypeStaticArray if each individual element converts implicitly to the type of the TypeStaticArray elements.

// accepted by IntegerExpression conversion rules
// i.e values are range-checked with s8 boundaries
const s8[2] a = [0, 1];
// rejected as 2nd element does not convert to s8
const s8[2] b = [0, 256];

The array and its content are rvalues.

AsmExpression§

Grammar§

AsmExpression ::= "asm" "(" StringLiteral ("," StringLiteral ("," Expression)* )? ")" (":" Type)?

Semantics§

The asm expression is a minimal interface to LLVM inline assembler. It allows to inject target specific assembler code.

The styx interface does not add any specific arguments to the LLVM instruction:

Note that lvalue arguments are always passed as-is, e.g as if passed to a var parameter so the expression sees a pointer.

BoolExpression§

Grammar§

BoolExpression ::= "false" | "true" ;

Semantics§

Bool expressions are rvalues that have the bool Type. They implictly convert to all other integer type.

var s32 c = true + true;
assert(c == 2);

EchoExpression§

Grammar§

EchoExpression ::= "echo" "(" Identifier ("," EchoParameter)* ")" EchoParameter ::= Expression | Keyword ;

Semanticss§

The compiler echo expression is the way static and run-time reflection is done in STYX. The echo identifier represents a command, the count of echo parameters required depends on the command.

During compilation an echo is always simplified to a another primary expression. At run-time, only this new expression is known.

The convert command§

This command yields a BoolExpression that indicates if the expression given by the first parameter implicitly converts to the Type (contained by a TypeExpression).

var s32 a;
assert( echo(convert, a, s64));
assert(!echo(convert, a, s16));

The filename command§

This commands yields a StringExpression that represents the current unit file name.

The func_t command§

This commands yields a TypeExpression that represents the current function type.

@foreign function puts(s8* str): s32;
// combined with .stringof to pretty print
function foo()
{
    puts(echo(func_t).stringof); // outputs "function foo()"
}

The getUnittests command§

This command yields a static array of static function()* that contains the @unittest functions declared in the unit obtained using the parameter.

unit u;
@unittest function test1(){}

function main(): s32
{
    const auto tests = echo(getUnittests, u);
    assert(tests.length == 1);
    foreach const auto test in tests do
        test();
    return 0;
}

The is command§

First class type checks§

When the second parameter is keyword used to declare a first class entity, the command yields a BoolExpression that indicates if the symbol or the type given by the first parameter is of this first class type.

struct S{}
var S s;
assert(echo(is, S, struct));
assert(echo(is, s, struct));
Identity checks§

This command yields a BoolExpression that indicates if the two symbols or the two types given by the two parameters are identical;

var s32 a;
alias aa = a;
alias at = s32;
assert(echo(is, a, aa));
assert(echo(is, at, s32));

When one of the parameter gives a symbol and the other one a type, the type of the symbol is automatically extracted

var s32 a;
alias at = s32;
assert(echo(is, at, a));
Storage class checks§

When the second parameter is a StorageClass and that the first expression gives a symbol then the echo yields a BoolExpression that indicates if the declaration for the symbol has the matching storage class.

var s32 a;
assert(echo(is, a, var));

This also works on function parameters

function foo(const s32 p)
{
    assert(!echo(is, p, var));
    assert( echo(is, p, const));
}

The line command§

const u32 line = echo(line);

This command yields an IntegerExpression that gives the line where the echo is located. It takes no parameter.

The version command§

const auto v = echo($version);

This command yields a StringExpression that represents the compiler version, following the semantic versioning rules. It takes no parameter.

As version is a keyword, the command has to be prefixed with $.

FloatExpression§

Grammar§

FloatExpression ::= FloatLiteral

Semantics§

Float expressions are rvalues that have the f64 Type. Note that they dont implicitly convert ot f32.

IdentExpression§

Grammar§

IdentExpression ::= Identifier

Semantics§

IdentExpressions are resolved to either to a declarations or Types.

Single identifiers or first identifiers of a DotExpression chain are solved using a “wild search” that starts from the most nested scope and continue in the parent scopes. Other identifiers are solved using a “strict search” (only children are considered) in the scope found by a previous “wild search”.

IdentExpressions take the type and the lvalueness of their source symbol.

IntegerExpression§

Grammar§

IntegerExpression ::= IntegerLiteral | HexLiteral | BinLiteral ;

Semantics§

Integer expressions are rvalues that have the u64 Type. During implicit conversions the value range is analyzed so that explicit post-CastExpression are rarily needed.

var u8 v;
switch v do
{
    on 0    do {} // no need to cast to u8
    on 255  do {} // no need to cast to u8
    else    do {}
}

LambdaExpression§

Grammar§

LambdaExpression ::= "function" "(" FunctionParameters? ")" (":" Type)? "=>" LambdaBody LambdaBody ::= BlockStatement | Expression ;

Semantics§

The lambda expression returns a pointer to a function. It is the equivalent of an AtExpression that takes the address of a FunctionDeclaration.

function filter(u64[] p; (static function(u64 a): bool)* pred): u64[]
{
    var u64[] result;
    foreach const auto a in p do
        if pred(a) do
            result ~= a;
    return result;
}

// without LambdaExp:
static function predicate(u64 a): bool
{
    return a != 42;
}

function standardSyntax(u64[] array): u64[]
{
    return array.filter(&predicate);
}

// using a LambdaExp
function shorthandSyntax(u64[] array): u64[]
{
    return array.filter(function (u64 a): bool => a != 42);
}

As usually the function type must match exactly to a parameter type, LambdaExps dont support frame parameters.

NullExpression§

Grammar§

NullExpression ::= "null"

Semantics§

The null expression is a rvalue that implicitly converts to all pointer and reference Types. It is also their initial value.

var s32* a;
var s8*  b;
var s64  c;
assert(a == null);
assert(b == null);
assert(&c != null);

ParenExpression§

Grammar§

ParenExpression ::= "(" Expression ")"

Semantics§

The paren expression wraps another expression. It have the type of its nested expression, its lvalueness and complies to its conversion rules. It’s usually used to overcome precedence rules or more simply for readability of combined AndAndExpressions and OrOrExpressions.

const auto a = (b + c) * 8;

StringExpression§

Grammar§

StringExpression ::= StringLiteral | RawStringLiteral ;

Semantics§

A String expression is a non-modifiable Lvalue that has the s8* Type. StringExpression also implicitly converts to s8[]. If it has a length of 1 then it also converts to integer types, e.g to represent a character.

SuperExpression§

Grammar§

SuperExpression ::= "super"

Semantics§

The SuperExpression is only allowed in non-static member functions of classes and in classes that inherits. It allows to access to the inherited members.

Variables§

If super is used to access a parent variable then the semantics are equivalent to a DotExpression that contains a Type, i.e a downcast is performed on this.

class C1
{
    var s32 a;
}
class C2 : C1
{
    var s32 a;
    function f()
    {
        super.a = 0; // equivalent to `(this:C1*).a = 0`
                     // or w/ the DotExp flavor,  `this.C1.a = 0`
    }
}

Functions§

If super is used to access a parent function then the parent virtual table of the class given by the this parameter is used to perform a devirtualized call.

class C1
{
    @virtual function t(): auto {return true;}
}
class C2 : C1
{
    @override function t(): auto {return false;}

    function test()
    {
        assert(super.t()); // `this` is C1*, use C2 vtable entry for t().
        assert(!this.t());
    }
}

Shorthand call syntax§

If super is used as called of a CallExpression but if it is not also part of a DotExpression then the call automatically targets the previous definition of the current function.

@override function test()
{
    super(); // shorthand for `super.test()`
}

ThisExpression§

Grammar§

ThisExpression ::= "this"

Semantics§

Non-static Member function§

In a member function that is not static, this allows to access the aggregate instance. this is actually a parameter name that takes the precedence in identifier resolution rules.

struct S
{
    var s32 a;
    function setA(s32 a /*; var S this*/ )
    {
        // resolves the LHS "a" using `this`,
        // resolves the RHS "a" using the matching parameter, before trying implicitly the `this`.
        this.a = a;
    }
}

Static Member function§

In a member function that is static, this resolves to the parent declaration, allowing to access its static members with a “strict search”, for example when disambiguation is required.

Other§

this has always for effect to set a strict scope so it cannot be chained.

TypeExpression§

Grammar§

TypeExpression ::= ":" Type

Semantics§

Type expressions simply wrap a Type. They are the only expression kind that have no Type. This is possible because they are only used in static contexts (i.e during compilation) and by other expressions that will have a valid type.

Type properties§

Type expressions are allowed in DotExpressions when the goal is to get a property.

const auto a = (:s32).sizeof; // allowed because .sizeof yields another expression

Echoes§

Type expressions are allowed as EchoParameter, as EchoExpressions are always simplified to other expressions.

const bool a = echo(is, :s32 , :s64); // allowed because echo "is" yields a bool