Синтаксис
Все выражения в 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 : "Том" };
Обратите внимание, что благодаря определению типов, анонимные объекты также строготипизированы.
«« Основы языка - Определение типа »»