Difference between revisions of "Compiladores/Projecto de Compiladores/Projecto 2021-2022/Manual de Referência da Linguagem L22 (rascunho)"

From Wiki**3

< Compiladores‎ | Projecto de Compiladores
(Inicialização)
 
(23 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{PRJCompiladoreAvisosEN20212022}}
+
#REDIRECT [[Compiladores/Projecto de Compiladores/Projecto 2021-2022/Manual de Referência da Linguagem L22]]
<!--{{PRJCompiladoreAvisosEE20212022}}-->
 
{{PRJCOMandatory20212022}}
 
{{TOCright}}
 
<font color="red">'''EM PREPARAÇÃO'''</font>
 
 
 
L22 é uma linguagem imperativa não estruturada. Um dos objectivos da linguagem é permitir a fácil leitura, pelo que é fortemente restritiva relativamente ao aspecto e à posição do código nas linhas (indentação).
 
 
 
Este manual apresenta características básicas da linguagem (tipos de dados, manipulação de nomes); convenções lexicais; estrutura/sintaxe; especificação das funções; semântica das instruções; semântica das expressões; e, finalmente, alguns exemplos.
 
 
 
= Tipos de Dados =
 
 
 
A linguagem é fracamente tipificada (são efectuadas algumas conversões implícitas). Existem 4 tipos de dados básicos, todos compatíveis com a [https://en.wikipedia.org/wiki/C_(programming_language) linguagem C], e com alinhamento em memória sempre a 32 bits:
 
 
 
* Tipos numéricos: os inteiros, em complemento para 2, ocupam 4 bytes; os reais, em [https://en.wikipedia.org/wiki/Floating_point vírgula flutuante], ocupam 8 bytes ([https://en.wikipedia.org/wiki/IEEE_floating_point IEEE 754]).
 
* As cadeias de caracteres são vectores de caracteres terminados por [https://en.wikipedia.org/wiki/ASCII ASCII] NUL (carácter com o valor zero). Variáveis e literais deste tipo só podem ser utilizados em atribuições, impressões, ou como argumentos/retornos de funções.
 
* Os ponteiros representam endereços de objectos e ocupam 4 bytes. Podem ser objecto de operações aritméticas (deslocamentos) e permitem aceder ao valor apontado.
 
 
 
Os tipos suportados por cada operador e a operação a realizar são indicados na definição das expressões.
 
 
 
Existem ainda tipos associado a valores funcionais, i.e., tipos que descrevem a interface de funções (ver abaixo). Os valores em memória associados a estes tipos são efectivamente ponteiros, mas para funções e não para dados, podendo ser usados para invocar as funções correspondentes. Estes ponteiros não aceitam operações de aritmética de ponteiros ou de indexação (embora ponteiros para estes ponteiros as aceitem).
 
 
 
= Manipulação de Nomes =
 
 
 
Os nomes (identificadores) correspondem exclusivamente a variáveis<!-- e funções-->. As funções são referenciadas através de ponteiros nomeados. Nos pontos que se seguem, usa-se o termo entidade para as designar indiscriminadamente, explicitando-se quando a descrição for válida apenas para um dos casos. Existem ainda nomes para funções externas: neste caso, os nomes referem directamente o código dessas funções (à la C; ver '''foreign''' abaixo).
 
 
 
== Espaço de nomes e visibilidade dos identificadores ==
 
 
 
O espaço de nomes global é único, pelo que um nome utilizado para designar uma entidade num dado contexto não pode ser utilizado para designar outras (ainda que de natureza diferente).
 
 
 
Os identificadores são visíveis desde a declaração até ao fim do alcance: ficheiro (globais) ou bloco (locais). A reutilização de identificadores em contextos inferiores encobre declarações em contextos superiores: redeclarações locais podem encobrir as globais até ao fim de um bloco. É possível utilizar símbolos globais nos contextos dos blocos das funções, mas não é possível declará-los (ver [[#Símbolos globais|símbolos globais]]).
 
 
 
== Validade das variáveis ==
 
 
 
As entidades globais (declaradas fora de qualquer função), existem durante toda a execução do programa. As variáveis locais a uma função existem apenas durante a sua execução. Os argumentos formais são válidos enquanto a função está activa.
 
 
 
= Convenções Lexicais =
 
 
 
Para cada grupo de elementos lexicais (tokens), considera-se a maior sequência de caracteres constituindo um elemento válido. Assim, por exemplo, a designação '''>=''' é sempre um único elemento lexical (por oposição à situação ilegal de se terem dois símbolos: '''>''' seguido de '''=''').
 
 
 
== Estrutura de linha (linhas lógicas) ==
 
 
 
Um programa está dividido em linhas lógicas e uma instrução não pode ocupar mais de uma linha lógica, excepto se a instrução permitir explicitamente mudanças de linha através, por exemplo, da utilização de blocos. Uma linha lógica pode ser constituída por uma ou mais linhas físicas separadas pelo marcador de continuação: '''...''' (três pontos seguidos).
 
 
 
=== Linhas físicas ===
 
 
 
Uma linha física pode ter qualquer comprimento. A terminaçâo de uma linha física depende da convenção utilizada pelo sistema operativo de suporte utilizado. Assim, em UNIX (o ambiente considerado neste projecto), o carácter utilizado é o ASCII 0x0A (linefeed). Embora sistemas diferentes possam considerar outros caracteres para marcar o final de linha, neste caso, apenas se considera o mencionado acima.
 
 
 
=== Marcador de continuação: junção explícita de linhas físicas ===
 
 
 
Duas ou mais linhas físicas podem ser juntas numa única linha lógica através da utilização do marcador de continuação: '''...''' (três pontos seguidos), quando ocorrem imediatamente antes da terminação da linha física (ver exemplo -- neste exemplo, a condição está dividida em várias linhas físicas).
 
<source lang="text">
 
if (year > 2006 and not year > 2010 and not day < 1 ...
 
  and ((month == 1 or month == 3 or month == 5 or month == 7 ...
 
        or month == 8 or month == 10 or month == 12) and not day > 31 ...
 
    or (month == 4 or month == 6 or month == 9 or month == 11) and not day > 36 ...
 
    or (month == 8 or month == 10 or month == 12) and not day > 31 ...
 
    or month == 2 and (year == 2008 and not day > 29 or not year == 2008 ...
 
          and not day > 28))) then:
 
  writeln "great day!"
 
else:
 
  writeln "not a great day..."
 
</source>
 
 
 
O marcador de continuação pode também ser utilizado em cadeias de caracteres literais, sendo ilegal em qualquer outro contexto. Em particular, não pode ser utilizado para permitir a continuação de tokens (identificadores, palavras-chave, delimitadores, operadores, literais inteiros ou reais), nem comentários explicativos.
 
 
 
== Caracteres brancos ==
 
 
 
São considerados caracteres brancos aqueles que, embora servindo para separar os elementos lexicais, não representam nenhum elemento lexical, São considerados caracteres brancos: ASCII 0x20 (espaço) e ASCII 0x09 (tabulação).
 
 
 
Note-se que os caracteres mencionados acima, apesar de brancos, têm significado especial se aparecerem no início de uma linha lógica. Analogamente, os caracteres de mudança de linha também têm significado especial (ver linhas brancas e indentação),
 
 
 
=== Linhas brancas ===
 
 
 
Uma linha lógica que contenha apenas caracteres brancos e comentários é ignorada, não sendo gerado nenhum elemento lexical nem alterada a indentação.
 
 
 
=== Indentação ===
 
 
 
Os caracteres espaço e tabulação horizontal não são considerados brancos no início de uma linha lógica. Neste caso, as suas cadeias determinam o nível de indentação do código.
 
 
 
Os caracteres de tabulação são substituídos (da esquerda para a direita) por 1 (um) a 8 (oito) espaços, de tal forma que o número total de caracteres brancos até ao carácter corrente (e incluindo este) seja o múltiplo de 8 seguinte (regra utilizada pelo UNIX). O número total de espaços brancos até ao primeiro carácter não branco determina o nível de indentação da expressão.
 
 
 
A indentação não pode continuar na linha física seguinte mediante a utilização de uma junção de linhas. Assim, no máximo, o número de espaços brancos até ao primeiro carácter de continuação determina o nível de indentação.
 
 
 
Embora os diversos níveis de indentação possam ser diferentes, o significado pode ser o mesmo (ver exemplo seguinte).
 
 
 
{|
 
| (a) OK: bom estilo
 
|-
 
|<source lang="text">
 
var max = (int a, int b) -> int:
 
  if (a > b) then:
 
    return a
 
  else:
 
    return b
 
</source>
 
|-
 
| (b) OK: mau estilo
 
|-
 
|<source lang="text">
 
var max = (int a, int b) -> int:
 
  if (a > b) then:
 
            return a
 
  else:
 
  return b
 
</source>
 
|-
 
| Errado!
 
|-
 
|<source lang="text">
 
    var max = (int a, int b) -> int:
 
  if (a > b) then:
 
              return a
 
    else:
 
    return b
 
</source>
 
|}
 
 
 
== Comentários ==
 
 
 
Existem dois tipos de comentários, que também funcionam como elementos separadores:
 
 
 
* '''explicativos''' -- começam com ''';''' e acabam no fim da linha física; e
 
* '''operacionais''' -- começam com '''(*''' e terminam com '''*)''', podendo estar aninhados.
 
 
 
Se as sequências de início fizerem parte de uma cadeia de caracteres, não iniciam um comentário (ver [[#Cadeias de caracteres|definição das cadeias de caracteres]]).
 
 
 
== Palavras-chave ==
 
 
 
As seguintes palavras-chave são reservadas, não constituindo identificadores (devem ser escritas exactamente como indicado):
 
 
 
* tipos: '''int double text void'''
 
* declarações: '''foreign use public var'''
 
* operadores: '''and or not'''
 
* instruções: '''if then: elif else: while do: stop again write writeln return'''
 
 
 
== Tipos ==
 
 
 
Os seguintes elementos lexicais designam tipos em declarações (ver gramática): '''int''' (inteiro), '''double''' (real), '''text''' (cadeia de caracteres).
 
 
 
Os tipos correspondentes a ponteiros são outros tipos delimitados por '''[''' e ''']''', designando uma indirecção e não o objecto directo (ver gramática).
 
 
 
== Operadores de expressões ==
 
 
 
São considerados operadores os elementos lexicais apresentados na definição das expressões.
 
 
 
== Delimitadores e terminadores ==
 
 
 
Os seguintes elementos lexicais são delimitadores/terminadores: ''',''' (vírgula) e '''(''' e ''')''' (delimitadores de expressões).
 
 
 
== Identificadores (nomes) ==
 
 
 
São iniciados por uma letra, seguindo-se 0 (zero) ou mais letras, dígitos, ou '''_''' (sublinhado). O comprimento do nome é ilimitado e dois nomes são distintos se houver alteração de maiúscula para minúscula, ou vice-versa, de pelo menos um carácter.
 
 
 
== Literais ==
 
 
 
São notações para valores constantes de alguns tipos da linguagem (não confundir com constantes, i.e., identificadores que, em algumas linguagens, designam elementos cujo valor não pode ser alterado durante a execução do programa).
 
 
 
=== Inteiros ===
 
 
 
Um literal inteiro é um número não negativo. Números negativos podem ser construídos pela aplicação do operador de negação unária ('''-''') a um literal (sempre positivo).
 
 
 
Literais inteiros decimais são constituídos por sequências de 1 (um) ou mais dígitos de '''0''' a '''9'''<!--, em que o primeiro digito não é 0 (zero), excepto no caso do número 0 (zero). Neste caso, é composto apenas pelo dígito o (zero) (em qualquer base)-->.
 
 
 
Literais inteiros em base 7 começam sempre pelo dígito 0 (zero), sendo seguidos de um ou mais dígitos de '''0''' a '''6''' (note-se que '''09''' é um literal invalido em base 7). Exemplo: '''006'''.
 
 
 
Se não for possível representar um literal Inteiro na máquina, devido a um overflow, deverá ser gerado um erro lexical.
 
 
 
=== Reais em vírgula flutuante ===
 
 
 
Os literais reais (sempre positivos) são expressos tal como em C (exclusivamente em base 10).
 
 
 
Não existem literais negativos (números negativos resultam da operação unária '''-''').
 
 
 
Um literal sem '''.''' (ponto decimal) nem parte exponencial é do tipo inteiro.
 
 
 
Exemplos: '''3.14''', '''1E3''' = 1000 (número inteiro representado em virgula flutuante). '''12.34e-24''' = 12.34 x 10<sup>-24</sup> (notação cientifica).
 
 
 
=== Cadeias de caracteres ===
 
 
 
As cadeias de caracteres são delimitadas por aspas ('''"''') e podem conter quaisquer caracteres, excepto ASCII NUL (0x00) e ASCII LF (0x0A). Nas cadeias, os delimitadores de comentários não têm significado especial. Se for escrito um literal que contenha '''\0''', então a cadela termina nessa posição. Exemplo: '''ab\0xy''' tem o mesmo significado que '''ab'''.
 
 
 
É possível designar caracteres por sequências especiais (iniciadas por '''\'''), especialmente úteis quando não existe representação gráfica directa. As sequências especiais correspondem aos caracteres ASCII HT, LF e CR ('''\t''', '''\n''' e '''\r''', respectivamente), aspa ('''\"'''). ''backslash'' ('''\\'''), ou a quaisquer outros especificados através de 1 a 3 dígitos em base 7, designando valores de 8 bits (e.g., '''\013''' ou apenas '''\13''' se o carácter seguinte não representar um digito em base 7). Exemplo: '''xy\013z''' tem o mesmo significado que '''xy\13z''' e que '''xy\nz'''.
 
 
 
Elementos lexicais distintos que representem duas ou mais cadeias consecutivas são representadas na linguagem como uma única cadeia que resulta da concatenação. Exemplo: '''"ab"''' '''"cd"''' é o mesmo que '''"abcd"'''.
 
 
 
=== Ponteiros ===
 
 
 
O único literal admissível para ponteiros corresponde ao ponteiro nulo e é indicado pela palavra-chave '''null'''. Este literal é compatível com todos os tipos de ponteiro. Os inteiros não são convertíveis em ponteiros, pelo que o valor '''0''' (zero) não é um valor inicial admissível para ponteiros.
 
 
 
= Gramática =
 
 
 
A gramática da linguagem está resumida abaixo. Considerou-se que os elementos em tipo fixo são literais, que os parênteses curvos agrupam elementos, que elementos alternativos são separados por uma barra vertical, que elementos opcionais estão entre parênteses rectos, que os elementos que se repetem zero ou mais vezes estão entre <math>\langle</math> e <math>\rangle</math>. Alguns elementos usados na gramática também são elementos da linguagem descrita se representados em tipo fixo (e.g., parênteses).
 
 
 
{|
 
! style="width: 140px; font-weight: normal;" | ''ficheiro''
 
! style="width: 50px; font-weight: normal;" | <math>\rightarrow</math>
 
| <math>\langle</math> ''declaração'' <math>\rangle</math>
 
|-
 
! style="width: 150px; font-weight: normal;" | ''declaração''
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| [ ''qualificador'' ] ''tipo'' ''identificador'' [ '''=''' ''expressão'' ]
 
|-
 
! style="width: 50px; font-weight: normal;" |
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| [ ''qualificador'' ] [ '''var''' ] ''identificador'' '''=''' ''expressão''
 
|-
 
! style="width: 50px; font-weight: normal;" | ''função''
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| '''(''' ''variáveis'' ''')''' '''->''' ''tipo'' ''':''' ''bloco''
 
|-
 
! style="width: 50px; font-weight: normal;" | ''variáveis''
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| ''variável'' <math>\langle</math> ''',''' ''variável'' <math>\rangle</math>
 
|-
 
! style="width: 50px; font-weight: normal;" | ''tipo''
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| '''int''' <math>|</math> '''double''' <math>|</math> '''text''' <math>|</math> '''[''' ''tipo'' ''']''' <math>|</math> ''tipo-de-função''
 
|-
 
! style="width: 50px; font-weight: normal;" | ''tipo-de-função''
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| ''tipo'' '''<''' <math>\langle</math> ''tipo'' <math>\rangle</math> '''>'''
 
|-
 
! style="width: 50px; font-weight: normal;" | ''bloco''
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| <math>\langle</math> ''declaração'' <math>\rangle</math> <math>\langle</math> ''instrução'' <math>\rangle</math>
 
|-
 
! style="width: 50px; font-weight: normal;" | ''instrução''
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| ''expressão'' <math>|</math> '''write''' ''expressões'' <math>|</math> '''writeln''' ''expressões''
 
|-
 
! style="width: 50px; font-weight: normal;" |
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| '''again''' [ ''literal-inteiro'' ] <math>|</math> '''stop''' [ ''literal-inteiro'' ] <math>|</math> '''return''' [ ''expressão'' ]
 
|-
 
! style="width: 50px; font-weight: normal;" |
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| ''instrução-condicional'' <math>|</math> ''instrução-de-iteração'' <math>|</math> ''bloco''
 
|-
 
! style="width: 50px; font-weight: normal;" | ''instrução-condicional''
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| '''if''' '''(''' ''expressão'' ''')''' '''then:''' ''bloco'' <math>\langle</math>  '''elif''' '''(''' ''expressão'' ''')''' '''then:''' ''bloco'' <math>\rangle</math> [ '''else:''' ''bloco'' ]
 
|-
 
! style="width: 50px; font-weight: normal;" | ''instrução-de-iteração''
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| '''while''' '''(''' ''expressão'' ''')''' '''do:''' ''bloco''
 
|-
 
! style="width: 50px; font-weight: normal;" | ''expressões''
 
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
 
| ''expressão'' <math>\langle</math> ''',''' ''expressão'' <math>\rangle</math>
 
|}
 
 
 
== Tipos, identificadores. literais e definição de expressões ==
 
 
 
Algumas definições foram omitidas da gramática: tipos de dados. qualificadores (ver [[#Declarações de variáveis|declarações de variáveis]]), identificador (ver [[#Identificadores (nomes)|identificadores]]), literal (ver [[#Literais|literais]]); expressão (ver [[#Expressões|expressões]]). Note-se que ''função'' é qualquer especificação de função (corpo ou ponteiro com o tipo apropriado). Neste sentido, as funções contam como expressões primitivas.
 
 
 
Quanto a tipos de dados, '''int''' designa valores inteiros, '''double''' designa valores reais, '''text''' designa cadeias de caracteres. Os ponteiros são tipos compostos por um dos tipos básicos entre parênteses rectos, e.g., '''[int]'''.
 
 
 
Ponteiros para funções são definidos a partir dos tipos de dados anteriormente descritos e do tipo especial '''void''' (ver a seguir). O tipo é indicado com o formato indicado na gramática, i.e., tipo de retorno seguido dos tipos dos argumentos.
 
 
 
Um ponteiro declarado com um tipo de função indica o endereço da função correspondente. Estes ponteiros não suportam aritmética de ponteiros.
 
 
 
O tipo '''void''' apenas pode ser usado para indicar a ausência de retorno ou para declarar um ponteiro genérico. Neste caso, o aninhamento é irrelevante, i.e., '''[void]''' e '''<nowiki>[[[void]]]</nowiki>''' são equivalentes. Um ponteiro deste tipo é compatível com todos os outros tipos de ponteiros. A aritmética de ponteiros decrementa/incrementa em uma unidade o valor de um ponteiro do tipo '''[void]'''.
 
 
 
== Left-values ==
 
 
 
Os ''left-values'' são posições de memória que podem ser modificadas (excepto onde proibido pelo tipo de dados). Os elementos de uma expressão que podem ser utilizados como left-values encontram-se individualmente identificados na semântica das expressões.
 
 
 
== Ficheiros ==
 
 
 
Um ficheiro é designado por principal se contiver a função principal (onde se inicia o programa).
 
 
 
== Declaração de variáveis ==
 
 
 
Uma declaração de variável indica sempre um tipo de dados (implícito ou explícito) e um identificador.
 
 
 
Exemplos:
 
 
 
* Inteiro: '''int i'''
 
* Real: '''double r'''
 
* Cadeia de caracteres: '''text s'''
 
* Ponteiro para inteiro: '''[int] p1''' (equivalente a '''int*''' em C)
 
* Ponteiro para real: '''[double] p2''' (equivalente a '''double*''' em C)
 
* Ponteiro para cadeia de caracteres: '''[text] p3''' (equivalente a '''char**''' em C)
 
* Ponteiro para ponteiro para inteiro: '''<nowiki>[[int]]</nowiki> p4''' (equivalente a '''int**''' em C)
 
 
 
== Símbolos globais ==
 
 
 
Por omissão, os símbolos são privados a um módulo, não podendo ser importados por outros módulos.
 
 
 
A palavra-chave '''public''' permite declarar um identificador como público, tornando-o acessível a partir de outros módulos. Quando usado com a palavra-chave '''var''', esta é opcional.
 
 
 
A palavra-chave '''use''' permite declarar num módulo variáveis definidas noutros módulos. Neste caso, não pode ser especificado o valor inicial dessas variáveis, pelo que não é possível o uso de '''var''' em conjunto com '''use'''.
 
 
 
A palavra-chave '''foreign''' deve ser usada para declarar símbolos de função com uma convenção de chamada diferente da da linguagem L22, e.g. para importar funções definidas em C. Além de poderem ser usados para chamar as funções que designam, os símbolos assim declarados podem ser atribuídos a variáveis com o tipo apropriado.
 
 
 
Exemplos:
 
* Declarar variável privada ao módulo: '''double d1 = 22.0'''
 
* Declarar variável pública: '''public double d2 = 7.0'''
 
* Declarar variável pública: '''public var d2 = 7.0''' (igual à anterior)
 
* Declarar variável pública: '''public d2 = 7.0''' (igual à anterior)
 
* Usar definição externa de variável pública: '''use double d2'''
 
* Usar ponteiro para função (nativa L22) definida externamente: '''use int<int> factorial'''
 
* Usar função definida externamente (e.g. em C): '''foreign [void]<int> malloc'''
 
 
 
== Inicialização ==
 
 
 
Quando existe, é a designação do objecto que segue o símbolo '''=''': inteiro, real, cadeia de caracteres, ponteiro ou função. Entidades reais podem ser inicializadas por expressões inteiras (conversão implícita). A expressão de inicialização deve ser um literal se a variável for global. A associação de valores funcionais a variáveis pode ser realizada quando os tipos forem covariantes.<!-- As cadeias de caracteres são (possivelmente) inicializadas com uma lista não nula de valores sem separadores.-->
 
 
 
A palavra '''var''' podem ser usadas em lugar do tipo para declarar uma variável. Quando usada, o tipo da variável é inferido a partir do valor inicial (nestes casos, o valor inicial é obrigatório).
 
 
 
Exemplos:
 
* Inteiro (literal): '''int i = 3'''
 
* Inteiro (expressão): '''int i = j + 1'''
 
* Real (literal): '''double r = 3.2'''
 
* Real (expressão): '''double r = i - 2.5'''
 
* Cadeia de caracteres (literal): '''text s = "olá"'''
 
* Cadeia de caracteres (literais): '''text s = "olá" "mãe"'''
 
* Ponteiro (literal): '''<nowiki>[[[double]]] p</nowiki> = null'''
 
* Ponteiro (expressão): '''[int] p = q + 1'''
 
* Função:
 
int<int> f1
 
int<double> g1
 
double<int> g2
 
int<int> f2 = f1  ;; mesmo tipo
 
f2 = g1 ;; ok: tipos covariantes
 
f2 = g2 ;; ERRADO
 

Latest revision as of 17:48, 3 May 2022