1. Introdução
Desde 2016 (com interrupções), desenvolvo minha própria linguagem de programação. O nome deste idioma é "Ü". Por quê? Porque eu queria um nome de uma letra e todas as letras do conjunto de AZ já foram usadas.
Este artigo tem a tarefa de familiarizar o público com esse idioma, para dar uma idéia geral dele. O artigo não pretende descrever o idioma completamente, uma descrição mais completa do idioma, se necessário, será fornecida nos artigos subsequentes.
Por que precisamos de outro idioma?
Examinei várias linguagens compiladas estaticamente, de tipo estatístico, mais ou menos conhecidas, e cheguei à conclusão de que nenhuma delas me convém completamente. Todos eles têm falhas "fatais".
Pontos específicos:
- C - nível muito baixo e inseguro
- C ++ - herdou muitas falhas de baixo nível, novas maneiras de se dar um tiro no pé, falta de reflexão
- D - coletor de lixo, tipos de referência separados
- Java é um coletor de lixo, todos os tipos compostos são referenciados, forte anexo a uma máquina virtual. Muito disso também se aplica a idiomas baseados em JVM.
- Falhas são muito parecidas com Java
- Ferrugem - a necessidade de pegar explicitamente links e desreferenciá-los explicitamente, (subjetivamente) o perigo de uma abordagem quando tudo é uma expressão e retorna um resultado, a presença de uma indicação explícita de conformidade com o protocolo, a falta de herança
- Go - coletor de lixo, falta de modelos
- Swift - tipos de referência, a necessidade de indicar explicitamente a conformidade com o protocolo
Tendo descoberto a imperfeição de todas as línguas acima, decidi criar a minha própria, desprovida, como me parece, de todas as falhas.
Informação geral
Ü é uma linguagem compilada, estaticamente tipada, com digitação forte. A linguagem contém funções, estruturas, classes, métodos para estruturas e classes livres. Tipos de tipos existentes - fundamental, estrutural (estruturas, classes), matrizes de tamanho constante, tuplas, enumerações, ponteiros para funções. As classes podem participar da herança e ter funções virtuais. A linguagem suporta modelos, existem modelos para classes e estruturas, aliases de tipos, funções e métodos. Há uma sobrecarga de funções e operadores (com algumas restrições).
A linguagem suporta destruidores de classes e estruturas. Destruidores são usados para gerenciar recursos, incluindo memória.
O que já está lá
Implementado compilador baseado em LLVM. O compilador suporta todas as plataformas que suportam LLVM. O compilador ainda não sabe como criar arquivos puramente executáveis, mas pode gerar arquivos de objetos, código de assembler, código binário ou de texto llvm. É possível se comunicar com o código C, existe um utilitário que facilita a criação de cabeçalhos para bibliotecas em C.
Metas e objetivos
O idioma é criado de forma a capturar o número máximo de erros típicos no estágio de compilação. As decisões sobre o design da linguagem são tomadas principalmente com base nesta tarefa. O segundo objetivo é a facilidade (relativa) de escrever programas e a facilidade de lê-los.
O idioma não contém construções que possam provocar a gravação de código incorreto e também não contém recursos que podem complicar significativamente o entendimento do que está acontecendo no programa.
Apesar dos pontos acima, a questão do desempenho não pode ser ignorada. Portanto, em detrimento do desempenho, as decisões de design de linguagem não são tomadas.
Então, vejamos quais são os momentos em que correspondem aos objetivos declarados.
Sistema de tipos
Ao contrário do C ++, em Ü não há ponteiros e referências como tipos. Não há ponteiros, existem links, mas apenas como links de pilha, argumentos de referência de função, campos de referência. Além disso, modificadores mut / imut não fazem parte de um tipo, mas fazem parte de uma variável, referência, argumento de função, campo.
Graças a essa simplificação, há uma maior compreensão de onde está o tipo, especialmente no código padrão. Não há dúvidas se um link ou uma variável será declarada, dúvidas como resultado do typeof (análogo do decltype de C ++), dúvidas sobre constante / não constante.
O outro lado dessa simplificação é a necessidade de processamento separado de consistência e links em modelos. Embora isso adicione clareza quando o parâmetro constancy é passado para o modelo como um argumento explicitamente separado.
Inicializadores
É impossível declarar uma variável e não inicializá-la. Qualquer byte da variável declarada deve ser inicializado.
Tipos fundamentais (e alguns outros) requerem inicialização obrigatória:
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++.
, , , .
, , . , , , . , - , .
→
→
, , .
→
.
, . . , .