Introdução
SkyLC é uma linguagem de programação moderna, projetada para integração facilitada com Rust, possui sistema de tipagem estática e inferência de tipos. O foco futuro da linguagem é a execução paralela massiva em multiplas arquiteturas de GPUs.
Este livro é o guia oficial da linguagem SkyLC. Nele, você aprenderá:
- Como compilar programas usando o compilador
skylc
- Como executar programas na máquina virtual
skyvm
- A sintaxe e os fundamentos da linguagem
- Como o sistema de tipos e inferência funciona
- Como criar suas próprias funções de módulo
- Como criar seus próprios tipos de objetos
- Como criar funções internas
- Conceitos avançados coerção e arquétipos
- Como organizar projetos maiores com módulos
- Exemplos práticos de programas reais escritos em SkyL
- Como chamar funções Rust por meio de código escrivo em SkyL
- Como escrever suas próprias bibliotecas Rust para integração com SkyL
Se você está vindo de outras linguagens como Python, C, Rust ou Java, vai se sentir em casa rapidamente com a sintaxe expressiva da SkyL e seu foco em segurança sem perder desempenho. Atualmente a máquina virtual não conta com um compilador JIT
Vamos começar!
Primeiros Passos
Bem-vindo! Vamos configurar o ambiente da linguagem localmente para que você possa começar a explorar.
Clonando o repositório
Por enquanto, o repositório da linguagem ainda é provisório. Para obtê-lo localmente, abra o terminal e execute:
git clone https://github.com/Vitorhenriquesilvadesa/rust-gppvm
cd rust-gppvm
Pré-requisitos
Este projeto utiliza Rust como base. Certifique-se de que o Rust esteja instalado na sua máquina.
Caso ainda não o tenha, você pode instalá-lo com:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Ou acesse: https://rustup.rs
Executando seu primeiro script
Dependendo do seu sistema operacional, você pode executar a linguagem de duas formas:
Windows
Execute o script PowerShell:
./run.ps1
Linux ou Windows (alternativa universal)
Você também pode rodar diretamente com cargo:
cargo run --release -- -c res/main.gpp
Se tudo estiver corretamente configurado, você verá mensagens do compilador, da máquina virtual e do linker no terminal, seguidas da saída:
Hello, World!
O que vem a seguir
Enquanto a linguagem estiver em fase beta, você pode continuar utilizando o comando cargo run
ou o script run.ps1
para executar seus scripts a partir do diretório raiz do projeto.
No próximo capítulo, vamos explorar os primeiros conceitos fundamentais da linguagem.
Primeiro Programa
Todo mundo começa com um clássico: Hello, World!
Vamos escrever nosso primeiro programa em SkyL. Ele vai simplesmente exibir uma mensagem na tela — mas é o primeiro passo para coisas muito maiores.
Abra seu editor de código favorito (pode ser um simples editor de texto ou uma IDE mais completa) e crie um arquivo chamado hello_world.gpp
dentro da pasta res
do projeto. Dentro desse arquivo, escreva o seguinte:
def main() -> void {
println("Hello World");
}
Esse pequeno trecho de código define a função main
, que é onde a execução do seu programa começa. Dentro dela, usamos println
para mostrar a mensagem no terminal.
Agora vamos compilar e rodar o programa.
Abra o terminal na raiz do projeto e execute:
cargo run --release -- -c res/hello_world.gpp
Se tudo estiver certo, o terminal mostrará:
Hello World
🎉 Parabéns! Você acabou de rodar seu primeiro programa em SkyL.
Entendendo o que aconteceu
Vamos por partes:
def
é usado para declarar uma função.main
é o nome especial da função onde todo programa começa.-> void
indica que essa função não retorna nenhum valor.- Dentro das chaves
{ ... }
vai o código que será executado. println("Hello World");
escreve a mensagem na tela, seguida de uma quebra de linha.
Simples assim!
Você não precisa configurar nada a mais para que println
, print
, input
e outras funções básicas funcionem — elas já estão disponíveis por padrão em todos os programas escritos em SkyL.
Nos próximos capítulos, vamos explorar variáveis, tipos, decisões, repetições e muito mais.
Mas por enquanto, aproveite esse momento: você escreveu, compilou e executou seu primeiro programa. Nada mal para o começo de uma nova jornada 🚀
Variáveis
Agora que já imprimimos algo na tela, que tal guardar informações?
É hora de conhecer as variáveis!
Em SkyL, declarar uma variável é simples e direto. Você pode fazer assim:
let nome;
Ou, se já quiser começar com um valor:
let nome = "SkyL";
A palavra-chave let
é usada para criar uma variável. Você escolhe um nome para ela, e pronto!
Se quiser já dar um valor desde o início, é só usar o sinal de igual (=
) depois do nome.
E o melhor: você não precisa dizer o tipo da variável.
O compilador é esperto o bastante para descobrir isso sozinho — esse processo se chama inferência de tipos. ✨
Exemplos em ação
Vamos ver isso na prática com alguns exemplos dentro da nossa função main
:
def main() -> void {
let nome = "SkyL";
let idade = 2;
print("Nome da linguagem: ");
println(nome);
print("Idade: ");
println(idade);
// Também podemos mudar o valor, desde que o tipo seja o mesmo!
idade = 3;
print("Nova idade: ");
println(idade);
}
Esse programa vai imprimir:
Nome da linguagem: SkyL Idade: 2 Nova idade: 3
Variável sem valor inicial
E se a gente só declarar uma variável, sem dar um valor de cara?
def main() -> void {
let mensagem;
mensagem = "Olá, mundo!";
println(mensagem);
}
Nesse caso, mensagem
foi declarada primeiro e inicializada depois. Tudo certo! SkyL permite isso.
Mas cuidado: usar uma variável antes de dar um valor pra ela vai dar problema!
Por exemplo, o código abaixo vai causar um erro:
def main() -> void {
let nome;
println(nome); // ⚠ Error: The type of 'x' is not known here. At line 3.
}
O compilador vai reclamar que você está tentando usar uma variável não inicializada.
SkyL se preocupa com sua segurança — ela quer ter certeza de que tudo tem um valor válido antes de ser usado.
Reatribuindo valores
Você pode mudar o valor de uma variável depois de criada, mas só se for do mesmo tipo:
def main() -> void {
let contador = 1;
contador = 2; // Ok!
// ❌ Error: Cannot assign 'int' with 'str' instance. At line 3.
contador = "dois";
}
O tipo é fixado na primeira vez que um valor é atribuído. Isso evita bugs esquisitos e te dá mais confiança no código.
Recapitulando:
- Use
let
para criar variáveis. - Você pode (ou não) dar um valor inicial.
- O tipo é descoberto automaticamente na primeira atribuição.
- O tipo não pode mudar depois.
- Não use variáveis antes de dar um valor a elas!
Nos próximos capítulos, vamos brincar com operadores, fazer contas, tomar decisões e muito mais.
Mas por enquanto, você já está dominando o universo das variáveis — e isso é incrível! 🌟
Tipos e Inferência
Toda linguagem tem seus tipos básicos, e SkyL não é diferente!
Aqui, esses tipos fundamentais são chamados de tipos primitivos ou tipos escalares.
Os tipos primitivos que você vai encontrar em SkyL são:
int
— para números inteiros, tipo 1, 42, -7float
— para números com casas decimais, tipo 3.14, -0.5str
— para textos, tipo "Olá, SkyL!"bool
— para valores lógicos, que só podem sertrue
(verdadeiro) oufalse
(falso)
Inferência de tipos
Não precisa se preocupar em dizer o tipo das suas variáveis na hora de declarar — o compilador SkyL é esperto e usa inferência de tipos para descobrir isso automaticamente.
Exemplos práticos
Vamos ver como criar variáveis com esses tipos dentro da função main
e imprimir seus valores:
def main() -> void {
let idade = 30; // int
let temperatura = 36.5; // float
let nome = "SkyL"; // str
let ligado = true; // bool
println("Idade:");
println(idade);
println("Temperatura:");
println(temperatura);
println("Nome:");
println(nome);
println("Ligado?");
println(ligado);
}
Nesse exemplo, cada tipo está representado por uma variável e seus valores são impressos um após o outro.
Não se preocupe ainda com juntar texto e números numa mesma linha, veremos isso mais adiante.
Operadores Aritméticos
Agora que você já conhece os tipos básicos, vamos ver como fazer contas usando os operadores aritméticos mais comuns: +
, -
, *
e /
.
Veja um exemplo simples dentro da função main
:
def main() -> void {
let a = 10;
let b = 3;
let soma = a + b;
let subtracao = a - b;
let multiplicacao = a * b;
let divisao = a / b;
println("Soma:");
println(soma);
println("Subtração:");
println(subtracao);
println("Multiplicação:");
println(multiplicacao);
println("Divisão:");
println(divisao);
}
Os operadores funcionam como você espera:
+
soma dois valores-
subtrai o segundo valor do primeiro*
multiplica/
divide
Agora você já pode criar variáveis, entender seus tipos e fazer operações matemáticas básicas.
Nos próximos capítulos, vamos explorar como controlar o fluxo do seu programa com decisões condicionais e laços de repetição — para que seu código fique ainda mais poderoso! 🚀
Strings e Codificação
Em SkyL, todas as strings são codificadas em UTF-8. Isso significa que você pode usar livremente caracteres com acentos, cedilhas, emojis e outros símbolos especiais dentro das suas strings!
Por exemplo, o código abaixo é perfeitamente válido:
def main() -> void {
println("Olá, mundo!");
println("Está tudo funcionando perfeitamente 😊");
println("Você pode usar acentuação: á, é, í, ó, ú, ç, ã, õ...");
}
Isso torna sua experiência de programação mais natural e acessível, especialmente para quem escreve textos em português ou outras línguas que usam caracteres especiais.
Aproveite para deixar suas mensagens e textos ainda mais expressivos! 🎉
Controle de Fluxo
Para tornar nossos programas mais inteligentes, precisamos que eles tomem decisões e repitam ações. É aqui que o controle de fluxo entra em cena!
SkyL oferece formas simples e claras de controlar o caminho que seu programa vai seguir, usando if, else if, else e while.
Condicional if
O comando if
permite executar um bloco de código somente se uma condição for verdadeira.
Veja um exemplo dentro da função main
:
def main() -> void {
let idade = 18;
if idade >= 18 {
println("Você é adulto!");
}
}
Aqui, a mensagem só será mostrada se a variável idade
for maior ou igual a 18.
Condicional else e else if
Às vezes, queremos testar outras possibilidades se a primeira condição não for verdadeira.
Exemplo:
def main() -> void {
let idade = 16;
if idade >= 18 {
println("Você é adulto!");
} else if idade >= 13 {
println("Você é adolescente!");
} else {
println("Você é criança!");
}
}
O programa verifica as condições em ordem:
- Se
idade
for 18 ou mais, imprime "Você é adulto!". - Se não, mas for 13 ou mais, imprime "Você é adolescente!".
- Caso contrário, imprime "Você é criança!".
Laço while
Às vezes, queremos repetir uma ação várias vezes enquanto uma condição for verdadeira. Para isso, usamos o comando while
.
Exemplo:
def main() -> void {
let contador = 0;
while contador < 5 {
println(contador);
contador = contador + 1;
}
}
Esse código vai imprimir os números de 0 até 4. A cada repetição, o valor de contador
aumenta em 1, até que a condição contador < 5
não seja mais verdadeira, encerrando o laço.
Com essas ferramentas, seus programas já podem tomar decisões e repetir ações, ficando muito mais poderosos e flexíveis!
Nos próximos capítulos vamos explorar ainda mais possibilidades para criar códigos cada vez mais incríveis 🚀
Funções
Funções são blocos de código reutilizáveis — pense nelas como pequenas máquinas que fazem algo específico para você, e depois devolvem o controle para onde foram chamadas. Isso ajuda a organizar seu programa, evitar repetição e deixar tudo mais claro.
Vamos analisar um exemplo:
def eh_adulto(idade: int) -> bool {
if idade >= 18 {
println("Você é adulto.");
return true;
} else {
println("Você não é adulto.");
return false;
}
}
def main() -> void {
let idade = 18;
let resultado = eh_adulto(idade);
println("Resultado da função:");
println(resultado);
}
Nesse exemplo, a função eh_adulto
recebe um número (idade
) e verifica se ele é maior ou igual a 18.
Se for, ela imprime "Você é adulto." e retorna true
(verdadeiro). Caso contrário, imprime "Você não é adulto." e retorna false
(falso).
No main
, chamamos a função passando a idade, e guardamos o valor que ela retornou na variável resultado
.
Depois, imprimimos esse resultado no terminal.
Assinatura da função
A linha que define a função tem essa estrutura:
def nome_da_funcao(parâmetros) -> tipo_de_retorno
nome_da_funcao
: o nome que você dá para a funçãoparâmetros
: os dados que a função recebe para trabalhar (podem ser zero ou mais)tipo_de_retorno
: o tipo do valor que a função vai devolver quando terminar (usevoid
se não retornar nada)
Por que usar funções?
- Organização: seu código fica mais fácil de entender
- Reutilização: você escreve uma vez e usa várias vezes
- Modularidade: cada função faz uma coisa só, deixando o programa mais limpo e seguro
Agora que você já sabe o básico sobre funções, nos próximos capítulos vamos explorar como criar funções que trabalham com mais tipos de dados, recebem múltiplos parâmetros e retornam valores que podemos usar para construir programas ainda mais legais!
Módulos e Importação
À medida que seus programas SkyL ficam maiores, organizar o código em vários arquivos ajuda muito a manter tudo limpo, fácil de entender e de manter. Para isso, SkyL permite que você divida seu código em módulos — arquivos separados que podem ser usados juntos.
Criando um módulo simples
Vamos criar um módulo chamado person.gpp
na raiz do seu projeto. Esse arquivo terá uma função que verifica se alguém é adulto:
def eh_adulto(idade: int) -> bool {
if idade >= 18 {
println("Você é adulto.");
return true;
} else {
println("Você não é adulto.");
return false;
}
}
Agora, em outro arquivo chamado main.gpp
, vamos usar essa função. Para isso, usamos a palavra-chave import:
import person;
def main() -> void {
let idade = 18;
let resultado = eh_adulto(idade);
println("Resultado da função:");
println(resultado);
}
Quando você executa main.gpp
, SkyL automaticamente traz todo o conteúdo do arquivo person.gpp
para dentro do main.gpp
. Isso funciona de forma parecida com linguagens como C: o import literalmente "inclui" o código do módulo importado, deixando todas as suas funções e variáveis disponíveis.
Importando módulos em subdiretórios
Quer organizar seus arquivos em pastas? Sem problemas!
Suponha que você tenha criado uma pasta chamada util
e colocou person.gpp
dentro dela. Para importar, basta usar o caminho com pontos:
import util.person;
def main() -> void {
let idade = 20;
let resultado = eh_adulto(idade);
println("Resultado da função:");
println(resultado);
}
SkyL entende que o caminho util.person
corresponde ao arquivo util/person.gpp
.
Algumas dicas importantes sobre importação
- Quando você importa um módulo, tudo que estiver naquele arquivo fica disponível no arquivo atual, como se você tivesse copiado e colado.
- Não é necessário (e nem permitido) importar módulos dentro de funções — sempre importe no começo do arquivo.
- Se dois módulos importados tiverem funções ou variáveis com o mesmo nome, isso pode causar conflitos. Por enquanto, evite nomes duplicados.
- Importar um módulo mais de uma vez no mesmo arquivo não causa erro, mas também não traz benefícios extras: o código será incluído só uma vez.
Exemplo completo
Imagine que além do person.gpp
, você tenha um módulo math_utils.gpp
com funções para cálculos simples:
// math_utils.gpp
def soma(a: int, b: int) -> int {
return a + b;
}
def multiplica(a: int, b: int) -> int {
return a * b;
}
No seu main.gpp
, você pode importar os dois módulos e usar todas as funções:
import person;
import math_utils;
def main() -> void {
let idade = 21;
let resultado = eh_adulto(idade);
println("Adulto?");
println(resultado);
let x = 10;
let y = 5;
println("Soma:");
println(soma(x, y));
println("Multiplicação:");
println(multiplica(x, y));
}
Recapitulando
- Use
import nome_do_modulo;
para trazer o código de outro arquivo para o seu. - Módulos são arquivos
.gpp
que organizam seu código. - Você pode criar pastas para organizar seus módulos e importar usando caminhos com pontos.
- Tudo importado fica disponível para uso no arquivo atual, facilitando a modularização do seu projeto.
Com a modularização, seu código fica mais organizado, fácil de manter e preparado para projetos cada vez maiores e mais complexos!
Pronto para explorar ainda mais? No próximo capítulo, vamos falar sobre variáveis globais, constantes e como organizar seus dados em SkyL!
Tipos customizados
Em SkyL, você pode criar seus próprios tipos para representar coisas do mundo real de forma organizada e clara. Esses tipos são chamados de tipos customizados e funcionam como estruturas que agrupam vários dados relacionados.
Declarando um tipo customizado
Para criar um tipo customizado, use a palavra-chave type
seguida do nome do tipo e as suas propriedades entre chaves. Por exemplo, um tipo Person
com nome e idade:
#![allow(unused)] fn main() { type Person { name: str, age: int } }
Aqui, Person
tem dois campos: name
que é uma string, e age
que é um número inteiro.
Criando instâncias
Para criar um valor desse tipo, você usa o nome do tipo como se fosse uma função, passando os valores dos campos na ordem correta:
let p = Person("Skyer", 20);
Agora p
é uma pessoa com nome "Skyer" e idade 20.
Acessando os campos
Você pode acessar os dados dentro do seu tipo usando o ponto (.
), assim como em outras linguagens:
println(p.name); // Saída: Skyer
println(p.age); // Saída: 20
println(p); // Saída: [Skyer, 20]
Imprimir o objeto inteiro mostra os valores entre colchetes.
Usando tipos customizados como parâmetros
Se você quiser passar esses tipos para funções, basta declarar o parâmetro com o tipo customizado esperado:
def is_adult(p: Person) -> bool {
return p.age >= 18;
}
def main() -> void {
let p = Person("Skyer", 20);
if is_adult(p) {
print(p.name);
println(" é adulto.");
} else {
print(p.name);
println(" não é adulto.");
}
}
Funções internas (métodos) em tipos customizados
Você também pode definir funções que "pertencem" a um tipo customizado, chamadas de funções internas ou métodos. Para isso, use a palavra-chave internal
e receba o objeto atual como self
:
internal def is_adult(self: Person) -> bool {
return self.age >= 18;
}
def main() -> void {
let p = Person("Skyer", 20);
if p.is_adult() {
print(p.name);
println(" é adulto.");
} else {
print(p.name);
println(" não é adulto.");
}
}
Recapitulando
- Tipos customizados organizam dados relacionados em uma única estrutura.
- Você cria instâncias usando o nome do tipo como função.
- Campos são acessados com
obj.campo
. - Tipos customizados podem ser usados em parâmetros de funções.
- Funções internas (
internal
) podem ser associadas a tipos para adicionar comportamento.
Com tipos customizados, você ganha mais controle e clareza para modelar o que seu programa precisa!
Vamos em frente e explorar ainda mais funcionalidades de SkyL!
Funções internas: Superpoderes dos tipos ✨
Em SkyL, os tipos não são apenas caixinhas de valores — eles vêm cheios de superpoderes! 🦸♂️
Esses superpoderes são chamados de funções internas (ou métodos), e permitem que você use operações diretamente nos valores, como se estivesse conversando com eles.
Explorando funções internas em inteiros (int)
Vamos começar com os inteiros. Eles vêm com várias funções úteis! Por exemplo:
let x = -10;
println(x.abs()); // Saída: 10
// Também podemos aplicar diretamente no valor
println((-10).abs()); // Saída: 10
Aqui estão algumas funções disponíveis para inteiros:
- .abs() → valor absoluto
- .is_even() → retorna se o número é par
- .is_odd() → retorna se o número é ímpar
- .sign() → retorna o sinal (-1, 0, ou 1)
- .max(outro) → retorna o maior entre dois valores
- .min(outro) → retorna o menor entre dois valores
- .clamp(min, max) → limita o número a um intervalo
- .sqrt() → raiz quadrada
- .pow(exp) → potência
- .to_float() → converte para float
- .to_string() → converte para string
Exemplos:
let x = 7;
println(x.is_even()); // false
println(x.pow(2)); // 49
println(x.clamp(0, 5)); // 5
Funções internas em strings (str)
As strings também têm várias habilidades poderosas:
- .length() → tamanho da string
- .find(sub) → posição de uma substring
- .contains(sub) → verifica se uma substring está presente
- .sub_string(i,f) → fatia a string
- .to_upper() → tudo em maiúsculas
- .to_lower() → tudo em minúsculas
- .starts_with(sub) → começa com?
- .ends_with(sub) → termina com?
- .concat(outra) → junta com outra string
- .replace(alvo, novo) → substitui partes da string
Exemplo divertido:
let msg = "SkyL é legal";
if msg.contains("SkyL") {
println("Contém SkyL");
}
println(msg.to_upper()); // SKYL É LEGAL
Criando suas próprias funções internas!
Agora vem a parte mais mágica: você pode ensinar novos truques aos tipos existentes! 🔮
Basta usar a palavra-chave internal
, e o primeiro parâmetro será o próprio valor, com o nome self
.
Por exemplo, vamos criar uma função negate
para booleanos (bool):
internal def negate(self: bool) -> bool {
return not self;
}
Agora podemos usar assim:
let x = true;
println(x.negate()); // false
println(false.negate()); // true
Mesmo que já tenhamos o operador not
, é incrível poder adicionar comportamentos novos assim!
Resumo mágico 🪄
- Tipos em SkyL já vêm com várias funções úteis embutidas.
- Você pode chamar essas funções com a sintaxe
valor.função()
. - E melhor ainda: você pode adicionar novas funções a tipos existentes!
Isso torna a linguagem poderosa e expressiva, como se você pudesse conversar com seus valores.
E o melhor: é você quem escreve o vocabulário!
Continue explorando — quem sabe o que mais seus tipos vão aprender? 😄
Desestruturação de objetos
SkyL permite que você desestruture objetos em variáveis individuais de forma direta e elegante. Isso significa que você pode extrair os campos de um tipo customizado para variáveis locais com facilidade.
Vamos começar com um exemplo básico. Suponha que temos um tipo definido assim:
type Person {
name: str,
age: int
}
Agora vamos instanciar um Person
e desestruturar seus campos:
def main() -> void {
let p = Person("Skyer", 20);
let { name, age } = p;
println(name); // Saída: Skyer
println(age); // Saída: 20
}
Você também pode extrair apenas alguns dos campos, se quiser:
def main() -> void {
let p = Person("Skyer", 20);
let { name } = p;
println(name); // Saída: Skyer
}
A ordem dos campos não importa. O exemplo abaixo também funciona:
def main() -> void {
let p = Person("Skyer", 20);
let { age, name } = p;
println(name); // Saída: Skyer
println(age); // Saída: 20
}
A desestruturação também pode ser usada em combinação com outras expressões. Por exemplo, você pode extrair campos dentro de uma função:
def imprimir_nome(p: Person) -> void {
let { name } = p;
println("Nome:");
println(name);
}
def main() -> void {
let p = Person("Skyer", 20);
imprimir_nome(p);
}
Essa funcionalidade facilita muito a leitura do código e evita chamadas repetidas como p.name
ou p.age
, deixando o código mais limpo e direto.
Observação
- A desestruturação funciona apenas com tipos definidos pelo usuário (tipos
type
). - O nome das variáveis precisa corresponder exatamente aos nomes dos campos do tipo.
- Não é necessário desestruturar todos os campos — você pode extrair apenas os que quiser.
Essa é uma das formas mais simples e poderosas de manipular dados em SkyL!