Un programa en Aleph es una lista de sentencias. Las sentencias se delimitan
con ;
o fin de línea. Algunas sentencias requieren de un bloque, que no es más
que una lista de sentencias encerrada entre {
y }
.
El lenguaje es case sensitive (distingue entre mayúsculas y minúsculas).
Los comentarios inician con //
y terminan al final de la línea.
La sintaxis completa en formato EBNF se puede consultar aquí.
La documentación del intérprete se genera con Doxygen y puede consultarse aquí.
Se definen combinando los dígitos del 0
al 9
.
123
Sus operaciones son:
-
Opuesto
-32 // -32 --421 // 421
-
Suma
1 + 2 // 3
-
Resta
2 - 4 // -2
-
Multiplicación
2 * 2 // 4
-
División (entera)
3 / 2 // 1
Son las cadenas de caracteres usuales pero no pueden ser operadas. Se definen entre comillas dobles o comillas simples.
'abc123"{}[]'
"abc123'{}[]"
Colecciones desordenadas de elementos únicos y de cualquier tipo.
Como características principales: no mantienen un orden específico de los elementos, no permiten elementos duplicados, son mutables (se pueden modificar después de su creación) y son heterogéneos (pueden contener elementos de diferentes tipos).
{'b', 'a', {['d'], 'c'}, 'b'} // {'a', 'b', {'c', ['d']}}
// - El orden de los elementos puede cambiar.
// - El elemento 'b' duplicado se elimina automáticamente.
// - Puede contener elementos de diferentes tipos, incluyendo otros conjuntos.
Sus operaciones son:
-
Unión
{'a', 'b'} + {'c'} // {'a', 'b', 'c'}
-
Intersección
{'a', 'b'} * {'b' ,'c'} // {'b'}
-
Diferencia
{'a', 'b', 'c'} - {'b'} // {'a', 'c'}
-
Cardinal
#{'a', 'b', 'c'} // 3
-
Pertenencia: devuelve
1
si el elemento está en el conjunto y0
en caso contrario.'a' in {'a', 'b', 'c'} // 1
La negación
not E in S
también puede escribirse comoE not in S
. -
Eliminar: caso especial de la sentencia
del
.del 'a' in {'a', 'b', 'c'} // {'b', 'c'}
Colecciones ordenadas de elementos de cualquier tipo.
A diferencia de muchos lenguajes de programación, la primera posición se
indica con el índice 1
.
Como características principales: mantienen un orden específico de los elementos, permiten elementos duplicados, son mutables (se pueden modificar después de su creación) y son heterogéneas (pueden contener elementos de diferentes tipos).
['b', 'a', {['d'], 'c'}, 'b'] // ['b', 'a', {['d'], 'c'}, 'b']
// - El orden de los elementos se mantiene.
// - Los elementos duplicados ('b') se conservan.
// - Puede contener elementos de diferentes tipos, incluyendo otras listas.
Sus operaciones son:
-
Concatenación
['a'] + ['b', 'c'] // ['a', 'b', 'c']
-
Tamaño
#['a', 'b', {'c'}, ['d']] // 4
-
Pertenencia: devuelve la posición del elemento o
0
si no está en la lista.{'c'} in ['a', 'b', {'c'}] // 3
La negación
not E in L
también puede escribirse comoE not in L
. -
Obtener: los índices negativos se cuentan desde el último elemento.
['a', 'b', {'c'}][2] // 'b' ['a', 'b', 'c'][-1] // 'c'
-
Insertar: inserción en una posición específica
i
(!i
) o a su derecha (i!
), desplazando los elementos siguientes una posición a la derecha.['b', 'c'][1] = 'a' // ['a', 'c'] ['b', 'c'][!1] = 'a' // ['a', 'b', 'c'] ['b', 'c'][1!] = 'a' // ['b', 'a', 'c']
-
Eliminar: caso especial de la sentencia
del
.del ['a', 'b', 'c'][-2] // ['a', 'c']
Las funciones en Aleph pueden ser asignadas a variables, pasadas como argumentos y retornadas como valores. Más detalles.
fn doble(x) { return x * 2 }
let func = doble
println(func(5)) // 10
println(doble) // Función: doble(x)
Tipo de dato especial asignado automáticamente a toda variable declarada hasta que se le asigne un valor. Puede ser usado como valor falso. No existe como valor literal, es decir, no hay una palabra reservada que lo represente.
let indefinido
println(indefinido) // Indefinido
Tipo de dato especial usado como resultado de funciones que no definen un valor de retorno. Puede ser usado como valor falso. No existe como valor literal, es decir, no hay una palabra reservada que lo represente.
// Todas devuelven nulo
fn sin_valor_1() {
println("Hola mundo!")
}
fn sin_valor_2() {
println("Hola mundo!")
return
}
fn sin_valor_3() {
println("Hola mundo!")
return sin_valor_1()
}
let nulo = sin_valor_3()
println(nulo) // Nulo
No existe un tipo de dato booleano como tal. Por convención, las expresiones con
operaciones lógicas y relacionales devuelven 1
como verdadero y 0
como falso
pero no son los únicos valores que se pueden usar:
// Falso:
0 // Entero cero
"" // Átomo vacío
'' // Átomo vacío
[] // Lista vacía
{} // Conjunto vacío
let indefinido // Indefinido
sin_valor() // Nulo
// Verdadero: cualquier otro valor
Los operadores lógicos son and
, or
y not
.
Los operadores relacionales son ==
, !=
, <
, <=
, >
, >=
.
Dados dos valores cualquiera, la comparación funciona de la siguiente manera:
- Si sus IDs son iguales entonces son iguales.
- Si sus tipos son diferentes, es un error.
- Si son listas o conjuntos, se comparan sus elementos.
- Si son enteros, se comparan como enteros.
- Si son cadenas, se comparan como cadenas.
- Si son nulos o indefinidos, son iguales.
En Aleph las variables son nombres que hacen referencia a valores (similar a Python). Deben ser declaradas explícitamente antes de su uso.
Las variables se declaran usando let
. No es necesario asignarles un valor
inmediatamente.
let x, y, z = 5, 6, 7 // Asignación múltiple
let nombre
let lista = [ { "abc" }, [ "def", {} ], "g" ] + [ "h" ]
// ...
nombre = "Pepe"
x, y, z = -z, -y, -x
Operación inversa a la declaración. Elimina la referencia al valor, pero no necesariamente el valor en sí mismo si hay otras referencias a él.
let a = "Hola"
println(a) // Hola
del a
println(a) // Error: variable 'a' no definida
Las variables tienen alcance de bloque. Esto significa que una variable declarada dentro de un bloque (como en una función o un bucle) solo es accesible dentro de ese bloque.
let x = 5
if 1 {
let y = 10
println(x, " ", y) // 5 10
}
println(x) // 5
println(y) // Error: símbolo 'y' no declarado
Asignación en la que se reemplaza =
por +=
, -=
, *=
o /=
.
En el caso de enteros, este tipo de asignación simplemente reduce el código:
let x = 5
id(x) // A
x += 1 // Exactamente lo mismo que `x = x + 1`
id(x) // B
println(x) // 6
Cuando se trata de listas y conjuntos, además de reducir el código, modifica el valor original:
let x = [1, 2, 3]
id(x) // A
x += [4, 5] // NO es lo mismo que `x = x + [4, 5]`
id(x) // A
println(x) // [1, 2, 3, 4, 5]
En el primer caso el resultado es un nuevo entero mientras que en el segundo es la misma lista pero con los elementos de la segunda lista insertados al final. Lo mismo sucede con el resto de operaciones.
let a = 0
if a > 0 {
println("Positivo")
} elif a < 0 { // Opcional
println("Negativo")
} else { // Opcional
println("Cero")
}
// Cero
Cuando el iterable (conjuntos y listas) es una lista se crea la variable
especial _i
, que contiene el índice del elemento actual. El iterable puede ser
modificado durante la ejecución de la estructura.
for elem in ["a", "b", "c"] {
println(_i, " ", elem)
}
// 1 a
// 2 b
// 3 c
let a = 2
while a > 0 {
print(a, " ")
a -= 1
}
// 2 1
Finaliza la ejecución de una estructura de repetición.
let iter = [1, 2, 3, 4, 5]
for e in iter {
if e == 3 { break }
print(e, " ")
}
// 1 2
Omite el resto del bloque y avanza a la siguiente iteración de una estructura de repetición.
iter = [1, 2, 3, 4, 5]
for e in iter {
if e == 2 or e == 4 { continue }
print(e, " ")
}
// 1 3 5
Finaliza la ejecución del programa.
fn fin() {
println("Finalizando...")
exit
println("Esto no se ejecuta")
}
fin()
// Finalizando...
Las funciones en Aleph son bloques de código reutilizables que pueden tomar
parámetros y devolver valores. Se definen utilizando la palabra clave fn
,
seguida del nombre de la función, parámetros entre paréntesis y un bloque de
código entre llaves.
El nombre sigue las reglas de nombres de variables. Los parámetros son
opcionales. La sentencia return
es opcional y puede encontrarse en cualquier
lugar del bloque.
fn cuadrado(x) {
return x * x
}
Las funciones se invocan usando su nombre seguido de los argumentos entre paréntesis.
println(cuadrado(5)) // 25
println(cuadrado(cuadrado(5)) + cuadrado(2)) // 629
El parámetro especial ...
se puede utilizar al final de la lista de parámetros
para indicar que la función puede tomar cualquier cantidad de argumentos. Estos
argumentos pueden ser accedidos en el cuerpo de la función por la variable
especial _args
, de tipo lista.
fn promedio(...) {
let sum = 0
for arg in _args {
sum += arg
}
return sum / #_args
}
println(promedio(1, 2, 3, 4, 5)) // 3
Las funciones pueden ser asignadas a variables, pasadas como argumento y devueltas como resultado. Serían first-class citizens de no ser porque solo pueden definirse en el ámbito global.
fn map(f, iter) {
let res = []
for e in iter {
res += [f(e)]
}
return res
}
fn doble(elem) { return elem * 2 }
let lista = [1, 2, 3, 4]
let dobles = map(doble, lista)
println(dobles) // [2, 4, 6, 8]
Aleph cuenta con funciones predefinidas para algunas tareas comunes en otros lenguajes de programación.
Imprime los argumentos.
Imprime los argumentos y agrega un salto de línea.
⛔ No implementado
Devuelve la cadena ingresada por el usuario hasta el primer fin de línea.
Devuelve el resultado de la función time
del lenguaje C.
Devuelve la dirección en memoria del valor de x
.
Devuelve la cantidad de referencias al valor de x
sin contar las referencias
de la propia función.
Devuelve una lista de enteros desde desde
hasta hasta
(inclusive) con un
salto de paso
.
range(0, 5, 1) // [0, 1, 2, 3, 4, 5]
range(5, -1, 0) // [5, 4, 3, 2, 1, 0]
range(0, 2, 10) // [0, 2, 4, 6, 8, 10]
range(1, 1, 0) // []
Devuelve una cadena con el tipo del valor de x
.
type(1) // 'int'
type("1") // 'atom'
type([1]) // 'list'
type({1}) // 'set'
let uno
type(uno) // 'undef'
type(sin_valor()) // 'void'
type(type) // 'fn'
Devuelve una copia del valor de x
completamente independiente de la original,
es decir, sin ninguna referencia entre ellas.
⛔ No implementado
lst(1) // [1]
lst("{1}") // ["{1}"]
lst("['1']") // ["['1']"]
lst({1}) // [1]
lst(["1"]) // ["1"]
⛔ No implementado
set(1) // {1}
set("[1]") // {"[1]"}
set("{'1'}") // {"{'1'}"}
set([1]) // {1}
set({"1"}) // {"1"}
⛔ No implementado
str(1) // "1"
str("[1]") // "[1]"
str("{'1'}") // "{'1'}"
str([1]) // "[1]"
str({"1"}) // '{"1"}'
⛔ No implementado
int(1) // 1
int("1") // 1
int("a") // Error
int([1]) // Error
int({"1"}) // Error