Introduccion
Desde 2016 (con interrupciones) he estado desarrollando mi propio lenguaje de programación. El nombre de este idioma es "Ü". ¿Por qué Ü? Porque quería un nombre de una letra, y todas las letras del conjunto de AZ ya están tomadas.
Este artículo tiene la tarea de familiarizar al público con este lenguaje, para dar una idea general del mismo. El artículo no tiene la intención de describir el idioma por completo, se proporcionará una descripción más completa del idioma, si es necesario, en artículos posteriores.
¿Por qué necesitamos otro idioma?
Examiné varios lenguajes compilados de tipo estático, más o menos conocidos, y llegué a la conclusión de que ninguno de ellos me conviene por completo. Todos ellos tienen defectos "fatales".
Puntos específicos:
- C - nivel demasiado bajo e inseguro
- C ++: heredó muchos defectos de bajo nivel, nuevas formas de dispararse en el pie, falta de reflejo
- D - recolector de basura, tipos de referencia separados
- Java es un recolector de basura, se hace referencia a todos los tipos compuestos, fuerte apego a una máquina virtual. Mucho de esto también se aplica a los lenguajes basados en JVM.
- C # - las fallas son muy parecidas a Java
- Óxido: la necesidad de tomar enlaces explícitamente y desreferenciarlos explícitamente (subjetivamente) el peligro de un enfoque cuando todo es una expresión y devuelve un resultado, la presencia de una indicación explícita de cumplimiento del protocolo, la falta de herencia
- Ir - recolector de basura, falta de plantillas
- Swift - tipos de referencia, la necesidad de indicar explícitamente el cumplimiento del protocolo
Después de descubrir la imperfección de todos los idiomas anteriores, decidí crear el mío, desprovisto, como me parece, de todos los defectos.
Información general
Ü es un lenguaje compilado, tipado estáticamente con tipeo fuerte. El lenguaje contiene funciones libres, estructuras, clases, métodos para estructuras y clases. Tipos de tipos existentes: fundamentales, estructurales (estructuras, clases), matrices de tamaño constante, tuplas, enumeraciones, punteros a funciones. Las clases pueden participar en la herencia y tener funciones virtuales. El lenguaje admite plantillas, hay plantillas para clases y estructuras, alias de tipos, funciones y métodos. Hay una sobrecarga de funciones y operadores (con algunas restricciones).
El lenguaje admite destructores para clases y estructuras. Los destructores se utilizan para administrar recursos, incluida la memoria.
Lo que ya hay
Implementado el compilador basado en LLVM. El compilador admite todas las plataformas que admiten LLVM. El compilador aún no sabe cómo crear archivos puramente ejecutables, pero puede generar archivos de objetos, código de ensamblador, código binario o de texto llvm. Existe la posibilidad de comunicarse con el código C, hay una utilidad que facilita la escritura de Ü-encabezados para bibliotecas C.
Metas y objetivos
El lenguaje se crea de tal manera que captura el número máximo de errores típicos en la etapa de compilación. Las decisiones sobre el diseño del lenguaje se toman principalmente sobre la base de esta tarea. El segundo objetivo es la facilidad (relativa) de escribir programas y la facilidad de leerlos.
El lenguaje no contiene construcciones que puedan provocar la escritura de código erróneo, y tampoco contiene características que puedan complicar significativamente la comprensión de lo que está sucediendo en el programa.
A pesar de los puntos anteriores, el problema del rendimiento no puede ser ignorado. Por lo tanto, en detrimento del rendimiento, no se toman decisiones de diseño del lenguaje.
Entonces, veamos cuáles son los momentos en Ü que corresponden a los objetivos establecidos.
Sistema de tipo
A diferencia de C ++, en Ü no hay punteros y referencias como tipos. No hay punteros en absoluto, hay enlaces, pero solo como enlaces de pila, argumentos de referencia de función, campos de referencia. Además, los modificadores mut / imut no son parte de un tipo, sino parte de una variable, referencia, argumento de función, campo.
Gracias a esta simplificación, se comprende mejor dónde está el tipo, especialmente en el código repetitivo. No hay dudas sobre si se declarará un enlace o una variable, dudas como resultado de typeof (análogo de decltype de C ++), dudas sobre constante / no constante.
La otra cara de esta simplificación es la necesidad de un procesamiento separado de consistencia y enlaces en las plantillas. Aunque, esto agrega claridad cuando el parámetro de constancia se pasa a la plantilla como un argumento explícitamente separado.
Inicializadores
Es imposible declarar una variable y no inicializarla. Cualquier byte de la variable declarada debe ser inicializado.
Los tipos fundamentales (y algunos otros) requieren una inicialización obligatoria:
var i32 x= 22, y(12345);
:
struct Vec{ f32 x; f32 y; }
...
var Vec v{ .x= -56.1f, .y= 42.0f };
var Vec v_uninit; //
, :
struct Vec
{
f32 x; f32 y;
fn constructor()
( x= 0.0f, y= 0.0f )
{}
}
...
var Vec v; //
, .
, :
struct Vec
{
f32 x= 0.0f;
f32 y= 0.0f;
}
, [] , :
var [ i32, 4 ] a[ 1, 1, 3, 4 ];
var tup[ i32, f32 ] t[ 8, 0.5f ];
var [ f32, 16 ] a_uninit; // , .
var[ i32, 3 ] aa[ 0, 1 ]; // ,
C++, , . , , — .
. , , — . this, - /, :
struct Vec
{
f32 x; f32 y;
fn constructor()
( x= y, y= 0.0f ) // , «y»
{}
fn constructor( f32 val )
( x= val, y= x ) // . «x» «y»
{}
}
. , , . .
:
var i32 mut x= 0;
var i32 &mut ref0= x;
++x; // ,
var i32 &imut ref1= x; // , , . .
:
var i32 mut x= 0;
var i32 &imut ref0= x;
var i32 &mut ref1= x; // , ,
:
var i32 mut x= 0;
var i32 &mut ref0= x;
var i32 &mut ref1= ref0; // , . . ,
:
fn Mutate( i32 &mut x, i32 &mut y );
...
var i32 mut x= 0;
Mutate( x, x ); // ,
. C++
std::vector<int> vec;
vec.push_back(1);
int& ref= vec.front();
vec.push_back(2); // , ref
Ü :
var ust::vector</i32/> mut vec;
vec.push_back(1);
var i32 &imut ref= vec.front();
vec.push_back(2); // , vec,
( ). — polymorph, interface, abstract . . , .
:
- . , , .
:
class A interface
{
fn virtual pure Foo(this);
}
- . . . , this, , . . . abstract call, , , C++.
:
class A abstract
{
fn virtual pure Foo(this);
fn virtual Bar(this){}
i32 x= 0;
}
- . . . - , .
:
class A interface
{
fn virtual pure Foo(this);
}
class B : A
{
fn virtual override Foo(this){}
}
- . , , .
. .
fn virtual pure Foo(this); // . , , .
fn virtual Foo(this){} // . , .
fn virtual override Foo(ths){} // , . , .
fn virtual final Foo(this){} // , . , . - , .
2019— , , Ü . . , , , , .
:
fn Foo( i32 mut x ) : i32
{
while(x < 100 )
{
x+= 10;
continue;
x+= 20; // ,
}
return x;
++x; // ,
}
:
fn Foo( i32 x ) : i32
{
if( x < 10 ) { return 0; }
else if( x > 100 ) { return 1; }
// ,
}
else , return .
, - , halt ( ). , , , return.
. , (. ). () .
:
struct CallableObject
{
i32 &mut x;
op()( mut this )
{
++x;
}
}
...
var i32 mut x= 0;
var CallableObject mut obj{ .x= x };
auto thread= ust::thread_create( move(obj) );
++x; // , «x», «thread»
++ obj.x; //
- , , - , .
(mutable C++), . . . , - , .
, ? — shared_ptr_mt . , , lock_mut() lock_imut(), . « , ».
:
struct Incrementer
{
ust::shared_ptr_mt_mut</ i32 /> ptr;
op()( mut this )
{
auto mut lock= ptr.lock_mut(); // «lock»
++lock.get_ref();
}
}
...
var ust::shared_ptr_mt_mut</ i32 /> ptr(0);
var size_type mut i(0);
var ust::vector</ ust::thread</Incrementer/> /> mut threads;
while( i < size_type(64) )
{
var Incrementer incrementer{ .ptr= ptr };
threads.push_back( ust::thread_create(incrementer) );
++i;
}
threads.clear(); // .
halt if( ptr.lock_imut().get_ref() != 64 ); // , ,
, , . . «shared». , , , - , «shared».
. , , constexpr . , .
, Ü, - , C.
C++, (=, +=, *= . .) , , . ++ –.
:
if( x = 0 ) {}
x++; // ++
x += x += x; // , +=
x+= x++ + ++x; //
. void, .
, , . :
auto a = select( condition ? x : y );
select .
. . . .
? , - :
- . - , , .
- . . - NullPointerException - , - . , , . . .
- . , , , . - - , . , .
- . , , .
, , ?
, . . «» , , std::optional/std::variant C++ Option/Result Rust.
, -, , halt. ( Ü ) , , , .
, - . , import, , . C++, , .
. , *.cpp *.hpp C++.
, , , .
, , . , , , . , - , .
→
→
, , .
→
.
, . . , .