1. Enumeration

Enumerations are user-defined types that enumerates a list of values. The keyword enum is used to define an enumeration type. The type of the fields can be inferred from the value associated to the fields. This type can be forced using the type operator :, after the keyword enum. All the fields of an enumeration shares the same type.

The complete grammar of an enumeration is presented in the following source block. As for struct declaration, templates can be used, but this functionnality will only be discussed in the Templates chapter.

enum_type := 'enum' (':' type)? (inner_value)+ '->' identifier (templates)?;
inner_value := '|' identifier '=' expression
identifier := ('_')* [A-z] ([A-z0-9_])*


In the following source code, an example of an enumeration of type [c32] is presented. This enumeration lists the names of the days.

import std::io

enum
| MONDAY = "Mon"
| TUESDAY = "Tue"
| WEDNESDAY = "Wed"
| THURSDAY = "Thu"
| FRIDAY = "Fri"
| SATURDAY = "Sat"
| SUNDAY = "Sun"
 -> Day;

def foo (day : Day) {
    println (day);
}

def main () {
    let d = Day::MONDAY;
    foo (d);
}

1.1. Value access

The values of an enumeration are accessible using the double colon binary operator ::. In practice, access a value of the enumeration will past the content value of the field at the caller location. The value - result of the expression - is of the type of the enumeration (for example the type Day in the example below).

1.1.1. Value types

An example of enumeration access is presented in the following source code. In this example, implicit casting is perform from a Day to a [c32], when calling the function foo, at line 23. This implicit cast is allowed.

import std::io

enum : [c32] // the type is optional
| MONDAY = "Mon"
| TUESDAY = "Tue"
| WEDNESDAY = "Wed"
| THURSDAY = "Thu"
| FRIDAY = "Fri"
| SATURDAY = "Sat"
| SUNDAY = "Sun"
 -> Day;

def foo (day : [c32]) {
    println (day);
}

def bar (day : Day) {
    println (day);
}

def main () {
    // the internal type Day is of type [c32], so it can be implicitely casted into [c32]
    foo (Day::MONDAY);

    bar (Day::MONDAY);

    // However, it is impossible to transform a [c32] into a Day implicitely
    bar ("Mon")
}


However, the contrary is not allowed, because the source code tries to cast a [c32] into a Day at line 28, the compiler returns an error. The error is presented in the code block below. Such cast is forbidden, to avoid enumeration value to contain a value that is actually not defined in the list of the field of the enumeration. For example, if this was accepted, the string "NotADay" would be castable into a Day (note: the value of the string being possibly unknown at compilation time).

Error : the call operator is not defined for main::bar and {mut [c32]}
 --> main.yr:(28,9)
28  ┃     bar ("Mon")
    ╋         ^     ^
    ┃ Note : candidate bar --> main.yr:(17,5) : main::bar (day : main::Day([c32]))-> void
    ┃     ┃ Error : incompatible types main::Day and mut [c32]
    ┃     ┃  --> main.yr:(28,10)
    ┃     ┃ 28  ┃     bar ("Mon")
    ┃     ┃     ╋          ^
    ┃     ┃ Note : for parameter day --> main.yr:(17,10) of main::bar (day : main::Day([c32]))-> void
    ┃     ┗━━━━━━ 
    ┗━━━━━┻━ 


ymir1: fatal error: 
compilation terminated.


1.1.2. Value constructions

Enumeration values can be more complex than literals. Any kind of value can be used, for example call functions, condition block, scope guards, etc. In the following example, an enumeration creating structure from function call is presented. The type of the enumeration is Ipv4Addr.

import std::io

struct
| a : i32
| b : i32
| c : i32
| d : i32
 -> Ipv4Addr;

enum
| LOCALHOST = localhost ()
| BROADCAST = broadcast ()
 -> KnownAddr;

def localhost ()-> Ipv4Addr {
    println ("Call localhost");
    Ipv4Addr (127, 0, 0, 1)
}

def broadcast ()-> Ipv4Addr {
    println ("Call broadcast");    
    Ipv4Addr (255, 255, 255, 255)
}

def main ()
    throws &AssertError
{
    let addr = KnownAddr::LOCALHOST; // will call localhost here

    assert (KnownAddr::LOCALHOST.a == 127); // a second time here
    assert (KnownAddr::BROADCAST.d == 255); // call broadcast here
    assert (addr.a == 127);
}


The enumeration value of a field is constructed at each access, this means for example that when enumeration values are constructed using function call, the function is called each time the enumeration field is accessed. Thus the result of the execution of the compiled source code above is the following:

Call localhost
Call localhost
Call broadcast


1.1.3. Value context

If the value of the enumeration seems to be passed at the caller location, they don't share the context of the caller. In other words, the fields of an enumeration have access to the symbol accessible from the enumeration context, and not from the caller context. An example, of enumeration trying to access symbols is presented in the source code bellow.

import std::io

static __GLOB__ = true;

enum 
| FOO = (if (x) { 42 } else { 11 })
| BAR = (if (__GLOB__) { 42 } else { 11 })
 -> ErrorEnum;

def main () {
    let x = false;

    println (ErrorEnum::FOO); 
}


From the above example, the compiler returns an error. In this error, the compiler informs that the variable x is not defined from the context of the enumeration. Even if the variable is declared inside the main function, it is not accessible from the enumeration context. The global variable __GLOB__ is accessible from the enumeration context, and thus accessing it is not an issue.

Note : 
 --> main.yr:(6,3)
 6  ┃ | FOO = (if (x) { 42 } else { 11 })
    ╋   ^^^
    ┃ Error : undefined symbol x
    ┃  --> main.yr:(6,14)
    ┃  6  ┃ | FOO = (if (x) { 42 } else { 11 })
    ┃     ╋              ^
    ┗━━━━━┻━ 

Note : 
 --> main.yr:(13,11)
13  ┃     println (ErrorEnum::FOO); 
    ╋              ^^^^^^^^^
    ┃ Error : the type main::ErrorEnum is not complete due to previous errors
    ┃  --> main.yr:(8,5)
    ┃  8  ┃  -> ErrorEnum;
    ┃     ╋     ^^^^^^^^^
    ┃     ┃ Note : 
    ┃     ┃  --> main.yr:(13,11)
    ┃     ┃ 13  ┃     println (ErrorEnum::FOO); 
    ┃     ┃     ╋              ^^^^^^^^^
    ┃     ┗━━━━━┻━ 
    ┗━━━━━┻━ 


ymir1: fatal error: 
compilation terminated.

1.2. Enumeration specific attributes

As for any type, enumeration have specific type attributes. The table below lists the enumeration type specific attributes.

Name Meaning
members A slice containing all the values of the enumeration
member_names A slice of [c32], containing all the names of the fields of the enumeration (same order than members)
typeid The type identifier of the enumeration type in a [c32] typed value
typeinfo The typeinfo of the inner type of the enumeration (cf. Dynamic types)
inner The inner type of the enumeration


One may note that the operator to access specific attributes and field is the same (double colon binary operator ::), and therefore that if an enumeration have a field named as a specific attributes there is a conflict. To avoid conflict, the priority is given to the fields of the enumeration, and specific attributes can be accessed using their identifier surrounded by _ tokens. For example, accessing the members specific attributes can be made using the identifier __members__. An example of the principle is presented in the following source code. The specific attribute surrounding is applicable to all types, but can be really usefull here.

mod main;

enum
| typeid       = 1
| typeinfo     = 2
| members      = 3
| member_names = 4
| inner        = 5
 -> AnnoyingEnum;

def main ()
    throws &AssertError
{
    assert (AnnoyingEnum::typeid == 1);
    assert (AnnoyingEnum::__typeid == AnnoyingEnum::__typeid__);
    assert (AnnoyingEnum::__typeid == "main::AnnoyingEnum");    
}

results matching ""

    No results matching ""