Синтаксис

Все выражения в Haxe равноправны. Это значит, что их можно без проблем вкладывать одно в другое рекурсивно, например foo(if (x == 3) 5 else 8) - допустимое выражение в Haxe. Дополнительно этот пример показывает, что каждое выражение возвращает значение заданного типа.

Константы

Для значений ниже тип будет выведен автоматически:

    0;       // Int
    -134;    // Int
    0xFF00;  // Int

    123.0;  // Float
    .14179; // Float
    13e50;  // Float
    -1e-99; // Float

    "hello";             // String
    "hello \"world\" !"; // String
    'hello "world" !';   // String

    true;  // Bool
    false; // Bool

    null;  // Unknown<0>

    ~/[a-z]+/i;  // EReg : регулярное выражение

Заметьте, что null имеет специальное значение, подходящее к любому типу данных, и поведение, отличное от Dynamic. Мы рассмотрим это подробнее в разделе определение типа.

Операции

Список операций в порядке приоритетов приведён ниже :

  • v = e : присвоить значение выражению, возвращает e
  • += -= *= /= %= &= |= ^= <<= >>= >>>= : присвоение после соответствующей операциии
  • e1 || e2 : логическое или. Если e1 равно true, тогда true, иначе e2 . Значения e1 и e2 должны быть типа Bool.
  • e1 && e2 : логическое и. Если e1 равно false, тогда false, иначе e2 . Значения e1 и e2 должны быть типа Bool.
  • e1...e2 : создает целочисленный итератор (об итераторах далее).
  • == != > < >= <= : сравнение между выражениями одного типа. Возвращает Bool.
  • | & ^ : битовые операции с выражениями типа Int. Возвращает Int.
  • << >> >>> : побитовый сдвиг выражений типа Int. Возвращает Int.
  • e1 + e2 :сложение. Если оба выражения Int, возвращает Int, если выражения Int и/или Float, возвращает Float, иначе возвращает String.
  • e1 - e2 : вычитание двух значений типа Int или Float. Если оба выражения Int, возвращает Int, иначе возвращает Float.
  • e1 * e2 : умножение двух чисел, возвращаемые типы как у вычитания
  • e1 / e2 : деление двух чисел, возвращает Float.
  • e1 % e2 : деление по модулю, возвращаемые типы как у вычитания

Унарные операции

  • ! : логическое не. Инвертирует значение bool
  • - : отрицательное число, меняет знак у Int или Float значения.
  • ++ и -- может использоваться до и после переменной. Когда применяются "до", сначала изменяют соответствующую переменную и затем возвращают измененную величину. Когда применяются "после", изменяют переменную, но возвращают ту величину, которую она имела до изменения. Может применяться тоьлько к Int или Float переменным.
  • ~ : побитовое дополнение Int.

Замечание: ~ обычно используется с 32-bit integers, так что она не будет давать корректные результаты с Neko 31-bits integers, по этой причине она не работает с Neko.

Скобки

Выражение может быть разделено скобками, для того чтобы обеспечить особый приоритет при выполнении операций. Выражение ( e ) эквивалентно e и они оба возвращают одинаковое значение.

Блоки

Блоки могут выполнять несколько операций. Синтаксис у блоков такой :

    {
        e1;
        e2;
        // ...
        eX;
    }

Любой блок возвращает тип и значение последнего выражения в блоке. Например:

    { f(); x = 124; true; }

Этот блок имеет тип Bool и возвратит true.

Как исключение, пустой блок { } возвращает Void.

Локальные переменные

Локальные переменные могут быть объявлены в блоке с помошью var, как в следующем примере:

    {
        var x;
        var y = 3;
        var z : String;
        var w : String = "";
        var a, b : Bool, c : Int = 0;
    }

Любая переменная может быть объявлена с необязательным типом и необязательным начальным значением. Если значение не присвоено, тогда переменная будет навна null по умолчанию. Если тип не определен, тогда тип будет Unknown но все же строго типизирован. Что будет подробно описано в разделе определение типа.

Сразу несколько локальных переменных могут быть объявлены в одном выражении var.

Локальные переменные определены только внутри блока. К ним нельзя обратиться за пределами того блока, где они определены (т.е. Haxe имеет лексическую область видимости).

Идентификаторы

Идентификаторы переменных будут разрешены (resolved) в следующем порядке:

  • локальные переменные, объявленные последними имеют наибольший приоритет
  • члены класса (текущего и унаследованные поля)
  • статические поля текущего класса
  • конструкторы перечислений, объявленные в этом файле, либо импортированные
    enum Axis {
        x;
        y;
        z;
    }

    class C {
        static var x : Int;
        var x : Int;

        function new() {
            {
               // здесь x означает переменную-член this.x
                var x : String;
               // здесь x означает локальную переменную
            }
        }

        function f(x : String) {
            // здесь x означает параметр функции
        }

        static function f() {
            // здесь x означает статическую переменную класса
        }
    }

    class D {
        function new() {
            // здесь x будет значением из перечисления Axis
        }
    }

Идентификаторы типов разрешаются в соответствии с импортированными пакетами, как - будет объяснено в дальнейшем.

Доступ к полям

Доступ к членам класса осуществляется с помощью точки (dot notation) :

    o.field

Вызовы функций

Чтобы вызвать функцию используются скобки и запятые, как разделитель аргументов. Методы объектов вызываются с помощью точки:

    f(1,2,3);
    object.method(1,2,3);

New

Ключевое слово new используется для создания экземпляров классов. Оно требует имя класса и может принимать параметры :

    a = new Array();
    s = new String("hello");

Массивы

Массив можно создать непосредственно из списка значений, используя следующий синтаксис:

    var a : Array<Int> = [1,2,3,4];

Обратите внимание, что Array требует параметр типа элементов, хранимых в массиве. Поэтому все операции с массивами имеют тип. Как следствие, все элементы данного Array должны быть одного типа.

Для чтения и записи элементов массива используется традиционный синтаксис с квадратными скобками:

    first = a[0];
    a[1] = value;

Индекс массив должен быть типа Int.

If

Вот примеры с выражением if:

    if (life == 0) destroy();
    if (flag) 1 else 2;

Вот общий синтаксис if:

    if( expr-cond ) expr-1 [else expr-2]

Сначала вычисляется expr-cond. Оно должно иметь тип Bool. Если результат его вычисления true, тогда выполняется expr-1, иначе выполняется expr-2, если оно задано.

Если нет else, и выражение в if ложно, тогда всё выражение имеет тип Void. Если else задан, то expr-1 и expr-2 должны быть одного типа, и этот тип будет типом выражения if:

    var x : Void = if( flag ) destroy();
    var y : Int = if( flag ) 1 else 2;

В Haxe, if похож на синтаксис тернарного оператора a?b:c языка C.

Как исключение, если блок if не возвращает значение (например, в середине другого блока), тогда оба expr-1 и expr-2 могут иметь разные типы, и типом блока if станет Void.

While

While - стандартные циклы с пред- или постусловием:

    while( expr-cond ) expr-loop;
    do expr-loop while( expr-cond );

К примеру :

    var i = 0;
    while( i < 10 ) {
        // ...
        i++;
    }

Или, с помощью do...while :

    var i = 0;
    do {
        // ...
        i++;
    } while( i < 10 );

Как и у if, в цикле while expr-cond должно быть типа Bool.

Вот еще пример цикла, который считает с 10 до 1:

    var i = 10;
    while( i > 0 ) {
    .......
    i--;
    }

For

Циклы for в Haxe несколько отличаются от традиционных. Они используются с итераторами, которые будут описаны ниже. Вот пример цикла for:

    for( i in 0...a.length ) {
        foo(a[i]);
    }

Return

Для возврата из функции до ее конца, либо для возврата значения из функции, используется выражение return:

    function odd( x : Int ) : Bool {
        if( x % 2 != 0 )  //x нечетно
            return true;
        return false;
    }

Если функции не нужно возвращать значение, return можно использовать без аргумента:

    function foo() : Void {
        // ...
        if( abort )
            return;
        // ....
    }

Break и Continue

Эти ключевые слова используются соответственно для досрочного выхода из цикла for или while - или перехода к следующей итерации:

    var i = 0;
    while( i < 10 ) {
        if( i == 7 )
            continue; // пропустить итерацию
            // не выполнять ничего ниже из этого блока
            // ВМЕСТО этого перейти к вычислению условия "while"
        if( flag )
            break; // остановиться
            // Выпрыгнуть из "while"
            // продолжить выполнение выражением после while
    }

Исключения

Исключения - способ нелокальной передачи управления. Можно бросить (throw) исключение и поймать (catch) его любой функцией выше по стеку вызовов:

    function foo() {
        // ...
        throw new Error("нехорошее foo");
    }

    // ...

    try {
        foo();
    } catch( e : Error ) {
        // обработать исключение
    }

Может быть несколько блоков catch для обработки разных типов исключений. Они проверяются в порядке объявления. Попытка поймать Dynamic поймает любое исключение:

    try {
        foo();
    } catch( e : String ) {
        // этот тип ошибки
    } catch( e : Error ) {
        // другой тип ошибки
    } catch( e : Dynamic ) {
        // все оставшиеся ошибки
    }

Все try и catch должны иметь одинаковое возвращаемое значение, за исключением случаев, когда оно не используется (как в if).

Switch

Switch - способ записи множественных if...else if... else if, проверяющих одно и то же значение:

    if( v == 0 )
        e1
    else if( v == foo(1) )
        e2
    else if( v == 65 || v == 90 )
        e3
    else
        e4;

Можно заменить таким switch :

    switch( v ) {
    case 0:
        e1;
    case foo(1):
        e2;
    case 65, 90:
        e3;
    default:
        e4;
    }

Внимание: В примере выше, если запись case, читающая "65, 90". Это синтаксис, когда case обрабатывает любое из двух и более значений, разделенных запятыми.

Switch в Haxe отличается от общепринятого: все case отдельные выражения - после выполнения одного switch автоматически выходит. Поэтому break не используется, а позиция default не имеет значения.

На некоторых платформах, switch на константах (особенно целочисленных) может быть оптимизирован по скорости выполнения.

Switch можно использовать как перечисление с отличающейся семантикой. Про это будет рассказано позже.

Локальные функции

Локальные функции объявляются ключевым словом function, но не могут иметь имя. Это - значения, такие же как integer или string:

    var f = function() { /* ... */ };
    f(); // вызов функции

Локальные функции имеют доступ к своим параметрам, статическим членам текущего класса, а также к локальным переменным, объявленным раньше их:

    var x = 10;
    var add = function(n) { x += n; };
    add(2);
    add(6);
    // x уже 18

Локальные функции, объявленные в методах, не имеют доступа к this.
Для доступа к "this", его потребуется разыменовать, объявив локальную переменную, к примеру me :

    class C {

        var x : Int;

        function f() {
            // вызовет ошибку компиляции
            var add = function(n) { this.x += n; };
        }

        function f2() {

            // сработает
            var me = this;
            var add = function(n) { me.x += n; };
        }
    }

Анонимные объекты

Анонимные объекты могут быть объявлены с помощью следующего синтаксиса :

    var o = { age : 26, name : "Том" };

Обратите внимание, что благодаря определению типов, анонимные объекты также строготипизированы.

«« Основы языка - Определение типа »»

version #10387, modified 2011-04-02 12:54:43 by Scythian