Sintaxis

En Haxe, todas las expresiones se encuentran al mismo nivel. Esto significa que puedes anidarlas recursivamente sin problemas. Por ejemplo : foo(if (x == 3) 5 else 8). De este ejemplo podemos extraer que cada expresión devuelve un valor de un tipo concreto.

Constantes

Se pueden utilizar los siguientes valores constantes :

    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 : expresión regular

Observa que null tiene un valor especial que puede ser usado con cualquier tipo y su comportamiento es diferente al de Dynamic. Se explicará en detalle cuando se presente la inferencia de tipos.

Operaciones

Se pueden usar las siguientes operaciones, presentadas por orden de prioridad :

  • v = e : asignación de un valor a una expresión, devuelve e
  • += -= *= /= %= &= |= ^= <<= >>= >>>= : asignación tras realizar la operación correspondiente
  • e1 || e2 : Si e1 es true entonces true y si no, se evalúa e2 . Tanto e1 como e2 deben ser Bool.
  • e1 && e2 : Si e1 es false entonces false y si no, se evalúa e2 . Tanto e1 como e2 deben ser Bool.
  • e1...e2 : Construye un iterador de enteros (véanse los Iteradores más adelante).
  • == != > < >= <= : realiza las comparaciones normales o físicas entre dos expresiones que comparten un tipo común. Devuelve Bool.
  • | & ^ : realizan operaciones de bits entre dos expresiones Int. Devuelve Int.
  • << >> >>> : realiza desplazamientos de bits entre dos expresiones Int. Devuelve Int.
  • e1 + e2 : adición. Si ambas expresiones son Int entonces devuelve Int; si una expresión es Float y la otra Int o Float entonces devuelve Float; en cualquier otro caso devuelve String.
  • e1 - e2 : sustracción entre dos expresiones Int o Float. Devuelve Int si ambas son Int; y devuelve Float en cualquier otro caso.
  • e1 * e2 : multiplica dos números, devuelve el mismo tipo que la sustracció.
  • e1 / e2 : divide dos números, devuelve Float.
  • e1 % e2 : módulo (resto) de dos números, devuelve el mismo tipo que la sustracción.

Opeaciones unarias

Se dispone de las siguientes operaciones unarias :

  • ! : negación booleana. Invierte el valor de una expresión Bool.
  • - : número negativo, cambia el signo de un valor Int o Float.
  • ++ y -- se pueden usar antes o después de una expresión. Cuando se usan antes, primero incrementan la variable correspondiente y después devuelven el valor incrementado. Si se usan después, se incrementa la variable pero el valor devuelto es el que tenía antes del incremento. Sólo se puede usar con valores Int o Float.
  • ~ : Complemento a uno de un Int.

Nota: ~ se usa normalmente con enteros de 32 bits, así que no proporcionará el resultado esperado con los enteros de 31 bits de Neko, razón por la cual no funciona en Neko.

Paréntesis

Las expresiones pueden ir delimitadas con paréntesis para especificar una cierta prioridad al realizar las operaciones. El tipo de ( e ) es el mismo de e y ambos se evalúan al mismo valor.

Bloques

Los bloques pueden ejecutar varias expresiones. La sintaxis de un bloque es la siguiente :

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

Un bloque se evalúa al tipo y valor de la última expresión del bloque. Por ejemplo :

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

Este bloque es de tipo Bool y se evaluará como true.

Como excepción, el bloque vacío { } se evalúa a Void.

Variables locales

Se pueden declarar variables locales dentro de los bloques usando var, de la manera mostrada en los siguientes ejemplos :

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

Opcionalmente se puede declarar una variable indicando su tipo y su valor inicial. Si no se le da un valor, por omisión, la variable es null. Si no se indica un tipo, entonces la variable es de tipo Unknown pero aún así tiene un tipo estricto. Esto se explica en detalle al presentar la inferencia de tipos.

Se pueden declarar varias variables locales en la misma expresión var.

Las variables locales quedan definidas sólo hasta que se cierra el bloque en el que se declaran. No se puede acceder a ellas desde fuera del bloque en el que se declaran.

Identificadores

Cuando se encuentra el identificador de una variable, se resuelve siguiendo este orden :

  • variables locales, teniendo más prioridad las últimas en haber sido declaradas
  • miembros de clases (la clase actual y campos heredados)
  • campos estáticos de la clase actual
  • constructores de enumeraciones que tienen que haber sido importados o declarados en este fichero
    enum Axis {
        x;
        y;
        z;
    }

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

        function new() {
            {
               // en este punto, x significa la variable miembro this.x
                var x : String;
                // en este punto, x significa la variable local
            }
        }

        function f(x : String) {
            // en este punto, x significa el parámetro de la función
        }

        static function f() {
            // x at this point means the class static variable
        }
    }

    class D {
        function new() {
            // x significa el Axis x
        }
    }

Los identificadores de tipo se resuelven de acuerdo con los paquetes importados, como se explicará más adelante.

Acceso a campos

El acceso a objetos se realiza mediante la tradicional notación de puntos :

    o.campo

Invocación

Se puede invocar funciones usando paréntesis y comas para delimitar sus argumentos. Se puede invocar métodos usando el punto de acceso sobre los objetos :

    f(1,2,3);
    objeto.método(1,2,3);

New

La palabra clave new se usa en expresiones para crear una instancia de una clase. Necesita un nombre de clase y puede tomar parámetros :

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

Arrays

Se pueden crear arreglos tomando directamente una lista de valores usando la siguiente sintaxis :

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

Observa que el tipo Array toma un parámetro de tipo que es el de los elementos almacenados dentro del Array. De esta manera todas las operaciones sobre arreglos son seguras. Como consecuencia, todos los elementos de un Array dado deben ser del mismo tipo.

Puedes leer y modificar un Array usando la siguiente notación tradicional de corchetes :

    primero = a[0];
    a[1] = valor;

Los índices de los arreglos deben ser de tipo Int.

If

Aquí tienes algunos ejemplos de expresiones if :

    if (vida == 0) destruir();
    if (flag) 1 else 2;

Ésta es la sintaxis genérica de las expresiones if :

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

Primero se evalúa expr-cond. Debe ser de tipo Bool. Entonces, si es true se evalúa expr-1, en caso contrario, si existe una expr-2 se evalúa ésta en su lugar.

Si no existe un else, y la expresión if es falsa, entonces la expresión en su conjunto tiene tipo Void. Si hay un else, entonces expr-1 y expr-2 deben ser del mismo tipo y éste será el tipo de la expresión if :

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

En Haxe, if es similar a la sintaxis ternaria a?b:c de C.

Como excepción, si un bloque if no debe devolver ningún valor (por ejemplo, en mitad de un Bloque), entonces expr-1 y expr-2 pueden tener tipos diferentes y el bloque if será de tipo Void.

While

Los "mientras" son bucles estándar que usan una precondición o una poscondición :

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

Por ejemplo :

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

O, usando do...while :

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

Al igual que con if, la expr-cond de un bucle-mientras debe ser de tipo Bool.

Otro ejemplo útil producirá un bucle para contar de 10 a 1:

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

For

Los bucles "para" son ligeramente diferentes de los bucles for tradicionales de C. En realidad se usan con iteradores, que se presentarán más adelante. Éste es un ejemplo de bucle-para :

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

Return

Para salir de una función antes de su final o para devolver un valor, se puede usar la expresión return :

    function impar( x : Int ) : Bool {
        if( x % 2 != 0 )
            return true;
        return false;
    }

La expresión return puede usarse sin argumentos si la función no precisa devolver un valor :

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

Break y Continue

Estas dos palabras clave son maneras útiles de salir anticipadamente de un bucle for o while o de pasar a la siguiente iteración de un bucle :

    var i = 0;
    while( i < 10 ) {
        if( i == 7 )
            continue; // saltarse esta iteración.
            // no se ejecuta ninguna sentencia más en este bloque,
            // PERO se vuelve a evaluar la condición de ''while''.
        if( flag )
            break; // parar anticipadamente.
            // Se sale del bucle ''while'' y se continúa
            // la ejecución en la sentencia que sigue al bucle.
    }

Exceptiones

Las excepciones son una manera de realizar saltos no locales. Puedes "lanzar" (throw) una excepción y "capturarla" (catch) en cualquier función de la pila :

    function foo() {
        // ...
        throw new Error("foo no válido");
    }

    // ...

    try {
        foo();
    } catch( e : Error ) {
        // gestionar la excepción
    }

Puede haber varios bloques catch tras un try, para poder capturar diferentes tipos de excepciones. Se comprueban en el orden en que son declarados. Capturar Dynamic implica capturar todas las excepciones :

    try {
        foo();
    } catch( e : String ) {
        // gestiona este tipo de error
    } catch( e : Error ) {
        // gestiona otro tipo de error
    } catch( e : Dynamic ) {
        // gestiona todos los demás errores
    }

Todas las expresiones try y catch deben tener el mismo tipo de retorno excepto cuando no se necesita dicho valor (igual que con if).

Switch

Las expresiones switch son maneras de expresar comprobaciones if...else if... else if múltiples sobre un mismo valor :

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

Se puede traducir al siguiente switch :

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

Nota: En el ejemplo anterior, una de las sentencias de caso indica "65, 90". Éste es un ejemplo en el que un caso espera coincidir con cualquiera de los dos (o más) valores, enumerados delimitándolos con comas.

Los switch de Haxe son diferentes a los tradicionales : todos los case son expresiones independientes de manera que tras ejecutarse una expresión, se sale automáticamente del bloque switch. Como consecuencia, no se puede usar break en un switch y el lugar en que se declare el caso default no es de importancia.

En algunas platformas, puede ser que se optimice la velocidad de los switches sobre valores constantes (especialmente sobre constantes enteras).

También se pueden usar switches con enum con una semántica diferente. Se explicará más adelante.

Funciones locales

Las funciones locales se declaran usando la palabra clave function pero no pueden tener un numbre. Son valores igual que un literal entero o una cadena :

    var f = function() { /* ... */ };
    f(); // invocamos la función

Las funciones locales pueden acceder a sus parámetros, a los miembros estáticos de la clase actual y a las variables locales que se declararon antes de ellas :

    var x = 10;
    var add = function(n) { x += n; };
    add(2);
    add(3);
    // ahora x es 15

Sin embargo, las funciones locales declaradas en métodos no pueden acceder al valor this. Para ello hace falta asignarlo a una variable local como yo :

    class C {

        var x : Int;

        function f() {
            // NO COMPILAR?
            var add = function(n) { this.x += n; };
        }

        function f2() {

            // compilará
            var yo = this;
            var add = function(n) { yo.x += n; };
        }
    }

Objetos anónimos

Se pueden declarar objetos anónimos usando la siguiente sintaxis :

    var o = { edad : 26, nombre : "Tom" };

Obsérvese que debido a la inferencia de tipos, los objetos anónimos también son de tipo estricto.

«« Tipos Básicos - Inferencia de tipos »»

version #7409, modified 2009-12-27 22:07:40 by Heimy