diff --git a/README.org b/README.org index 7e8546b..35a735a 100644 --- a/README.org +++ b/README.org @@ -1,108 +1,291 @@ -* Linguagem Brisa +#+title: Linguagem Brisa +#+subtitle: Especificações +#+author: Blau Araujo +#+email: blau@debxp.org -** Tokens +* Visão geral -- Separadores de palavras: espaço, tabulação, /tokens/ -- Fim de linha: =\n= -- Comentário simples: =;= -- Comentário de bloco e /inline/: =;;; ... ;;;= -- Terminador de declarações: =.= (ponto) -- Operador de atribuição: =<= -- Operador de tipo de retorno: =>= -- Delimitador de indireção: =[IDENTIFICADOR/LISTA]= - -** Tipos - -*** Tipos inteiros com sinal - -- =i8=: 1 byte -- =i16=: 2 bytes -- =i32=: 4 bytes (tipo padrão!) -- =i64=: 8 bytes -- =i128=: 16 bytes - -*** Tipos inteiros sem sinal - -- =u8=: 1 byte -- =u16=: 2 bytes -- =u32=: 4 bytes -- =u64=: 8 bytes -- =u128=: 16 bytes - -*** Tipos de ponto flutuante (apenas com sinal) - -- =f32=: 4 bytes -- =f64=: 8 bytes -- =f128=: 16 bytes - -*** Tipo indeterminado - -Não existe tipo indeterminado (=void=) na linguagem. Por exemplo, =NULL= é implementado como =(int64)0=. - -*** Tipo booliano - -=bool=: 1 byte - -- Variáveis do tipo =bool= recebem apenas =true= ou =false=, respectivamente implementados como =1= e =0= do tipo =i8=. -- Expressões lógicas sempre resultam em valores =1= ou =0= do tipo =i32=, portanto não são =bool=. -- Ponteiros nulos (=NULL=), o terminador nulo (='\0'=) e inteiros de valor =0= sempre avaliam como falso, mas não são =bool=. - -** Qualificadores e modificadores de escopo - -- Constante: =const= -- Representa um dado imutável (=.rodata=?). -- Volátil: =volatile= -- Representa um dado de escopo global (=.data=?). -- Estático: =static= -- Representa um dado que mantém seu valor entre chamadas de função. -- Restrito: =restrict= -- Representa um endereço que só pode ser acessado por um nome de ponteiro. - -** Variáveis - -- Declaração: ~[QUALIFICADOR] [*][TIPO]:NOME.~ -- Inicialização: ~[QUALIFICADOR] [*][TIPO]:NOME < VALOR~ -- Atribuição: ~NOME < VALOR~ - -Exemplos: +Um código em Brisa tem esta aparência: #+begin_example -:var < 42 ; declaração e inicialização da variável 'var' (tipo 'i32') -var < 10 ; atribuição do valor 10 à variável 'var' -i8:char. ; declaração da variável 'char' com o tipo 'i8' (1 byte) -i8:char . ; espaços não fazem diferença após o nome da variável! -*:ptr < [var] ; declaração do ponteiro 'ptr' com o tipo *i32 e com o endereço de 'var' -[ptr] < 123 ; armazena o valor 123 no endereço apontado por 'ptr' +@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 #+end_example +** 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~ e ~until~; +- Estruturas de decisão ~if~, ~elif~, ~else~; +- Estruturas de seleção ~select~, ~case~. + +#+begin_quote +Nas estruturas ~if~ e ~select~, o terminador ~end~ só é utilizado após o bloco encabeçado pela última condição avaliada. +#+end_quote + +*** Blocos de comentários + +#+begin_example +@doc +... +@end +#+end_example + +- 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: + +#+begin_example +@doc: o que estamos comentando +... +@end +#+end_example + +*** Comentários inline + +#+begin_example +[...]; ... +#+end_example + +Tudo após ~;~ é ignorado. + +*** Declaração de identificadores + +Todo identificador (nome de função ou de variável) deve ser declarado como: + +#+begin_example +tipo[espaços]:[espaços]lista_de_nomes ; os espaços são ignorados na compilação +#+end_example + +- 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, avaliam ~NULL~ (ponteiro nulo). + +#+begin_quote +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. +#+end_quote + +*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 -Sintaxe: =[(TIPO)]EXPRESSÃO= +São palavras que representam valores literais associados a tipos de dados: -Sem especificação de tipo... +- *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~ (byte ~0x01~) e ~false~ (byte ~0x00~) -- ='CHAR'= são sempre =i8=; -- Notações numéricas inteiras são sempre =i32=; -- Notações de ponto flutuante e frações são sempre =f64=. +*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: + +#+begin_example +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 +#+end_example + +#+begin_quote +Os tipos ~int~ e ~i32~ são equivalentes, ou seja, são inteiros de 32 bits com sinal. +#+end_quote + +** Especificadores de tipos *** Inteiros -- =10=: o mesmo que =(i32)10= -- Qualquer outro tipo inteiro: =(tipo)número= +- ~int~ ou ~i32~: 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~ ou ~i8~: inteiro de 8 bits com sinal; +- ~u8~: inteiro de 8 bits sem sinal; +- ~word~ ou ~i16~: inteiro de 16 bits com sinal; +- ~u16~: inteiro de 16 bits sem sinal; -*** Bytes (i8) +*** Endereços -- Representação de caracteres: ='\n'= (valor ASCII do caractere '\n' - byte 0x0a) -- Outras representações numéricas requerem modelagem =(i8)= +- ~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); -*** Ponto flutuante +*Nota sobre ponteiros não inicializados:* -- =5.0=: o mesmo que =(f64)5.0= -- =20 / 4=: o mesmo que =(f64)5.0= -- Qualquer outro tipo de ponto flutuante: =(tipo)representação= +Todos os tipos relacionados a endereços avaliam ~NULL~ quando não inicializados. -*** Cadeias de bytes (vetores do tipo i8) +*Nota sobre tabelas e vetores:* -- =['c', 'a', 's', 'a', '\n', '\0']= (avalia o endereço do primeiro byte) -- ='casa\n\0'=: o mesmo que =['c', 'a', 's', 'a', '\n', '\0']= (avalia o endereço do primeiro byte) -- ="casa\n"=: o mesmo que =['c', 'a', 's', 'a', '\', 'n', '\0']= (avalia o endereço do primeiro byte) +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. +#+begin_example +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 +#+end_example + +*** 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érmino ~status~. + +- ~return(expressão)~: utilizada para retornar um valor do tipo especificado na declaração da função. + +#+begin_quote +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. +#+end_quote + +** 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: + +#+begin_example +; 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 +#+end_example + +- 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 de ~nil~. + +*** 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: + +#+begin_example +; Isso é válido... +print( + "Maria ", + "tinha ", + "um carneirinho." +) +#+end_example + +*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): + +#+begin_example +; 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 +#+end_example + +Entretanto, elas só podem ser criadas em atribuições de variáveis ou como argumentos de outras funções: + +#+begin_example +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' +#+end_example