10 KiB
Linguagem Brisa
- Visão geral
Visão geral
Um código em Brisa tem esta aparência:
@doc Bloco de comentários... @end ; Declaração de uma função com retorno 'int' (inteiro 32 bits com sinal)... int:add42 (int:a) return(a + 42) ; Declaração de uma função com retorno 'nil' (símbolo sem associação de valor)... nil:salve(str:name) print("Salve, ", name, "!\n") end ; Função 'main' (ponto de entrada)... int:main () int:x = 23 ; declaração de variável inicializada com expressão constante int:y = x + 58 ; declaração de variável inicializada com avaliação da expressão int:z ; declaração de variável não inicializada (valor 'nil') @doc: outras sintaxes válidas... int:a, b, c ; declaração de múltiplas variáveis de mesmo tipo não inicializadas int:a = 1, b = 2, c = 3 ; declaração de múltiplas variáveis de mesmo tipo inicializadas int:a = b = c ; variáveis 'a' e 'b' inicializadas com a avaliação de 'c' (que deve estar inicializada) @end salve("simpatia") ; chamada da função 'salve' z = x + 58 ; operação de atribuição print("z + 42 = ", add42(z), "\n") ; chamada da função 'add42' como argumento da função builtin 'print' exit(0) ; função builtin 'exit' (opcional) end
Características principais do exemplo
Terminação de instruções
- Todas as instruções simples (uma linha) são delimitadas por quebras de linha;
- Linhas longas podem ser quebradas com contrabarras (
\
); - Todas as instruções compostas (blocos) são delimitadas por um cabeçalho de bloco e pela palavra reservada
end
; - Se houver apenas uma instrução no bloco, o terminador
end
pode ser dispensado, desde que a instrução seja escrita na mesma linha do cabeçalho do bloco.
Os cabeçalhos de blocos podem ser:
- Assinaturas de funções;
- Estruturas de repetição
for
,while
euntil
; - Estruturas de decisão
if
,elif
,else
; - Estruturas de seleção
select
,case
.
Nas estruturas
if
eselect
, o terminadorend
só é utilizado após o bloco encabeçado pela última condição avaliada.
Blocos de comentários
@doc ... @end
- O cabeçalho
@doc
tem que iniciar a linha; - Espaços e tabulações antes de
@doc
ou@end
são ignorados; - Tudo após
@doc
é ignorado, inclusive ocorrências de@doc
antes do terminador@end
, ou de@end
, desde que não seja a única palavra na linha; - A palavra
@end
tem que estar sozinha na linha para ser interpretado como um terminador de bloco de comentários; - O cabeçalho
@doc
pode ser seguido que qualquer caractere, por exemplo:
@doc: o que estamos comentando ... @end
Comentários inline
[...]; ...
Tudo após ;
é ignorado.
Declaração de identificadores
Todo identificador (nome de função ou de variável) deve ser declarado como:
tipo[espaços]:[espaços]lista_de_nomes ; os espaços são ignorados na compilação
- Nomes seguidos de
([lista de parâmetros])
declaram funções; - Nomes seguidos de
= expressão
identificam variáveis inicializadas; - Nomes seguidos do fim da linha ou de uma vírgula (
,
) declaram variáveis não inicializadas; - Espaços entre
tipo
e:
, ou entre:
e o primeiro nome, são ignorados; - A
lista_de_nomes
consiste de um ou mais nomes de variáveis (inicializadas ou não) separadas por vírgulas (,
); - Todas as variáveis da lista são declaradas com o mesmo tipo;
- Variáveis não inicializadas avaliam
nil
(símbolo sem valor associado); - Variáveis do tipo
ptr
identificam endereços e, se não inicializadas, avaliamNULL
(ponteiro nulo).
Em Brisa, variáveis são nomes que identificam dados em endereços na memória e ponteiros são nomes que identificam endereços de dados na memória.
Importante! Para ser um nome válido, a palavra deve conter apenas a combinação de um ou mais
caracteres que casem com a expressão regular [A-Za-z_][0-9A-Za-z_]*
, ou seja, palavras iniciadas
com caracteres alfabéticos ou sublinhado (_
) seguidos de zero ou mais caracteres alfanuméricos
ou sublinhado.
Expressões constantes
São palavras que representam valores literais associados a tipos de dados:
- Inteiros decimais:
[+-]?(0|[1-9][0-9]*)
(0
,42
,-123
, etc) - Inteiros hexadecimais:
0[Xx][0-9A-Fa-f]+
(0x0a
,0xffcb
, etc) - Inteiros octais:
[+-]?0[Oo][1-7]+
(0o1
,0o123
,-0o42
, etc) - Inteiros binários:
0[Bb][0-1]
(0b0110
,0b10001010
, etc) - Ponto flutuante:
1.23
,3.4e23
… - Lista de caracteres:
'lista de caracteres imprimíveis ou escapados'
(sem terminação nula) - Strings:
"lista de caracteres imprimíveis ou escapados"
(inclui terminação nula) - Boolianos:
true
(byte0x01
) efalse
(byte0x00
)
Importante!
O sinal de representações hexadecimais e binárias depende do tipo associado à variável. No entanto,
por padrão, essas representações serão tratadas como unsigned int
quando utilizadas em expressões:
int:a = 0xff ; avalia -1 (em 32 bits) u32:b = 0xff ; avalia 255 (em 32 bits) int:c c = 5 + 0xff ; 5 + 255 = 300 c += a ; 300 + (-1) = 299 c += b ; 299 + 255 = 554
Os tipos
int
ei32
são equivalentes, ou seja, são inteiros de 32 bits com sinal.
Especificadores de tipos
Inteiros
int
oui32
: inteiros de 32 bits com sinal;u32
: inteiros de 32 bits sem sinal;i64
: inteiros de 64 bits com sinal;u64
: inteiros de 64 bits sem sinal;byte
oui8
: inteiro de 8 bits com sinal;u8
: inteiro de 8 bits sem sinal;word
oui16
: inteiro de 16 bits com sinal;u16
: inteiro de 16 bits sem sinal;
Endereços
ptr
: define a variável como um ponteiro (endereços de 64 bits);str
: mesmo que um ponteiro para um vetor de bytes na memória;arr
: mesmo que um ponteiro para uma tabela (vetor associativo de dados de tipos diferentes);vec
: mesmo que um ponteiro para um vetor (vetor indexado de dados de mesmo tipo);
Nota sobre ponteiros não inicializados:
Todos os tipos relacionados a endereços avaliam NULL
quando não inicializados.
Nota sobre tabelas e vetores:
Em Brisa, arrays (tabelas) são coleções dinâmicas e associativas de elementos de tipos diferentes, enquanto vetores são listas dinâmicas de elementos de um mesmo tipo, o que é determinado pelo tipo do primeiro elemento.
arr:tabela = { "banana", "qtd" 10, "unidade" "quilo" } @doc: o acesso pode ser por chave ou índice... tabela["0"] = "banana" -> tabela[0] = 0 tabela["qtd"] = 10 -> tabela[1] = "preço" tabela["unidade"] = "quilo" -> tabela[2] = "unidades" @end vec:lista = { 123, 42, 23 } @doc: acesso apenas pelos índices... lista[0] = 123 lista[1] = 42 lista[2] = 23 @end
O tipo especial 'nil'
A palavra reservada nil
representa um endereço fixo e único na memória sem um dado associado. Sua
avaliação depende do contexto:
- Expressões aritméticas: avalia
0
; - Expressões boolianas: avalia
false
; - Contexto de strings: representa uma string vazia (byte
0x00
); - Declarações de variáveis: expressa que a variável não foi inicializada;
- Retornos de funções: expressa que a função não tem retorno.
Funções builtin
A biblioteca padrão da linguagem Brisa (librisa
) oferece vários construtores de linguagem na forma
de funções builtin, como:
echo(str:string, ...)
: imprime, na saída padrão, todos os argumentos interpolados por um espaço e com uma quebra de linha ao final (os terminadores nulos são removidos).print(str:string, ...)
: imprime, na saída padrão, o resultado da concatenação de todos os argumentos (não inclui a quebra de linha final).strlen(str:string)
: retorna a quantidade de caracteres de uma string (desconta o terminador nulo)exit(byte:status)
: termina o programa com o estado de términostatus
.return(expressão)
: utilizada para retornar um valor do tipo especificado na declaração da função.
Em todas as funções builtin que trabalham com strings, é possível passar expressões como argumentos, inclusive valores numéricos retornados por funções, porque a conversão para string será feita em tempo de execução.
Declaração de funções
Não existe uma separação entre a declaração e a definição de funções: funções são declaradas no mesmo momento em que são definidas, o que pode ser feito de duas formas:
; Declaração em bloco... tipo:nome (lista_de_parâmetros) instruções... [return(expressão)] end ; Declaração em linha... tipo:nome (lista_de_parâmetros) uma_instrução
- Na declaração em bloco, a primeira instrução até pode ser escrita na mesma linha do cabeçalho da função, mas isso pode tornar o código confuso.
- A função builtin
return
é obrigatória em funções com retorno diferente denil
.
Delimitadores de argumentos
Os parêntesis da lista de parâmetros servem como delimitadores de blocos e, portanto, possibilitam a escrita de argumentos em várias linhas nas chamadas de funções:
; Isso é válido... print( "Maria ", "tinha ", "um carneirinho." )
Importante! Isso é válido apenas no contexto de chamadas de funções – em expressões, os parêntesis são operadores de precedência.
Funções anônimas (closures)
É possível criar funções anônimas com os nomes reservados function
, func
ou fun
(só pela diversão):
; função anônima em bloco... fun[c[tion]] (parâmetros) instruções... [return(expressão)] end ; função anônima em linha... tipo:fun[c[tion]] (parâmetros) uma_instrução
Entretanto, elas só podem ser criadas em atribuições de variáveis ou como argumentos de outras funções:
int:x x = fun(int:num) return(num + 42) ; o tipo do retorno é o mesmo da variável 'x' echo(x(42)) ; imprime '84' echo(x(81)) ; imprime '123'