Compiladores/Projecto de Compiladores/Projecto 2025-2026/Manual de Referência da Linguagem P6 (rascunho): Difference between revisions

From Wiki**3

Root (talk | contribs)
Root (talk | contribs)
No edit summary
Line 1: Line 1:
{{PRJHEADER}}
{{PRJCompiladoreAvisosEN20222023}}
<!--{{PRJCompiladoreAvisosEE20222023}}-->
{{PRJCOMandatory20222023}}
{{TOCright}}
{{TOCright}}
<font color="red">'''RASCUNHO'''</font>
<font color="red">'''EM PREPARAÇÃO'''</font>
<font color="red">'''EM PREPARAÇÃO'''</font>


<font color="red">'''ÉPOCA NORMAL'''</font>
'''P6''' é uma linguagem imperativa. Este manual apresenta de forma intuitiva as características da linguagem: [[#Tipos de Dados|tipos de dados]]; [[#Manipulação de Nomes|manipulação de nomes]]; [[#Convenções Lexicais|convenções lexicais]]; [[#Gramática|estrutura/sintaxe]]; [[#Funções|especificação das funções]]; [[#Instruções|semântica das instruções]]; [[#Expressões|semântica das expressões]]; e, finalmente, [[#Exemplos|alguns exemplos]].
 
'''UDF''' é uma linguagem imperativa. Este manual apresenta de forma intuitiva as características da linguagem: [[#Tipos de Dados|tipos de dados]]; [[#Manipulação de Nomes|manipulação de nomes]]; [[#Convenções Lexicais|convenções lexicais]]; [[#Gramática|estrutura/sintaxe]]; [[#Funções|especificação das funções]]; [[#Instruções|semântica das instruções]]; [[#Expressões|semântica das expressões]]; e, finalmente, [[#Exemplos|alguns exemplos]].


= Tipos de Dados =
= 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:
A linguagem é fracamente tipificada (são efectuadas algumas conversões implícitas). Existem 4 tipos de dados básicos, apenas alguns dos quais são compatíveis com a [https://en.wikipedia.org/wiki/C_(programming_language) linguagem C]. O alinhamento em memória binária é sempre a 32 bits. Note-se que alguns dos tipos de dados são ternários e devem ser manipulados com as funções das ALUs disponibilizadas.


* 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]).
* Tipos numéricos: os inteiros são ternários equilibrados, ocupam 40 trits (empacotados em 64 bits); os reais são ternários e estão representados no formato [https://arxiv.org/abs/2404.18603 Takum] (note-se que esta definição é para representação binária e não é exactamente a que é implementada pelas ALUs), ocupam 80 trits (empacotados em 128 bits).
* 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 caracteres são valores de 8 bits não directamente manipuláveis.
* As cadeias de caracteres são vectores de caracteres binários 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 caracteres são valores de 8 bits não directamente manipuláveis.
* 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 ponteiros representam endereços de objectos (de qualquer tipo) e ocupam 4 bytes. Podem ser objecto de operações aritméticas (deslocamentos) e permitem aceder ao valor apontado.


Existem ainda tipos associados a tensores, i.e., tipos que descrevem estruturas multidimensionais (ver abaixo). Os valores em memória associados a estes tipos são efectivamente ponteiros, mas para estruturas que representam os tensores e não directamente para os dados. Estes ponteiros não aceitam operações de aritmética de ponteiros ou de indexação (embora ponteiros para estes ponteiros as aceitem).
Os tipos suportados por cada operador e a operação a realizar são indicados na definição das expressões.


Os tipos suportados por cada operador e a operação a realizar são indicados na [[#Expressões|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 =
= Manipulação de Nomes =


Os nomes ([[#Identificadores|identificadores]]) correspondem a variáveis e funções. 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 subconjunto.
Os nomes (identificadores) correspondem exclusivamente a variáveis. 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 a posição do código dessas funções (à la C; ver '''direct''' abaixo).


== Espaço de nomes e visibilidade dos identificadores ==
== Espaço de nomes e visibilidade dos identificadores ==
Line 27: Line 29:
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).
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 função (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 uma função. Não é possível importar ou definir símbolos globais nos contextos das funções (ver [[#Símbolos globais|símbolos globais]]).
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]]).
 
Não é possível definir funções dentro de blocos.


== Validade das variáveis ==
== Validade das variáveis ==
Line 41: Line 41:
== Caracteres brancos ==
== Caracteres brancos ==


São considerados separadores e não representam nenhum elemento lexical: '''mudança de linha''' ASCII LF ('''0x0A''', '''\n'''), '''recuo do carreto''' ASCII CR ('''0x0D''', '''\r'''), '''espaço''' ASCII SP ('''0x20''', ) e '''tabulação horizontal''' ASCII HT ('''0x09''', '''\t''').
São considerados separadores e não representam nenhum elemento lexical: '''mudança de linha''' ASCII LF ('''0x0A''', '''\n'''), '''recuo do carreto''' ASCII CR ('''0x0D''', '''\r'''), '''espaço''' ASCII SP ('''0x20''', " ") e '''tabulação horizontal''' ASCII HT ('''0x09''', '''\t''').


== Comentários ==
== Comentários ==


Existem dois tipos de comentários, que também funcionam como elementos separadores:
Existem dois tipos de comentários, que também funcionam como elementos separadores:
* '''explicativos''' -- começam com '''//''' e acabam no fim da linha; e
* '''explicativos''' -- começam com '''//''' e acabam no fim da linha; e
* '''operacionais''' -- começam com '''/*''' e terminam com '''*/''', podendo estar aninhados.
* '''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 definição das [[#Cadeias de caracteres|cadeias de caracteres]]).
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 ==
== Palavras-chave ==


As seguintes palavras-chave são reservadas, não constituindo identificadores (devem ser escritas exactamente como indicado):
As seguintes palavras-chave são reservadas, não constituindo identificadores (devem ser escritas exactamente como indicado):


* tipos: '''int real ptr string tensor void'''
* tipos: '''int real string void'''
* declarações: '''forward public auto'''
* declarações: '''direct forward public auto'''
* instruções: '''if elif else for break continue return write writeln'''
* instruções: '''if elif else while stop next return'''
* expressões: '''input nullptr objects sizeof'''
* expressões: '''input null sizeof'''
* operadores de tensores: (ver abaixo)
* outras: '''begin end'''
 
O identificador '''udf''', embora não reservado, quando refere uma função, corresponde à [[#Função principal e execução de programas|função principal]], devendo ser declarado público.


== Tipos ==
== Tipos ==


Os seguintes elementos lexicais designam tipos em declarações (ver [[#Gramática|gramática]]): '''int''' (inteiro), '''real''' (real), '''string''' (cadeia de caracteres), '''tensor''' (tensores), '''auto''' (tipo inferido a partir do valor inicial), '''ptr''' (ponteiros). Ver [[#Gramática|gramática]].
Os seguintes elementos lexicais designam tipos em declarações (ver gramática): '''int''' (inteiro), '''real''' (real), '''string''' (cadeia de caracteres).
 
O tipo especial '''auto''' é utilizado para indicar que o tipo da variável ou do retorno da função deve ser inferido a partir do tipo do seu valor inicial. Quando aplicado a uma função, implica que o tipo de retorno deve ser inferido a partir da expressão que é usada na instrução '''return''' (a expressão deve concordar no tipo a retornar).


O tipo '''auto''' pode ser utilizado para definir ponteiros genéricos (como '''void*''' em C/C++), compatíveis com todos os tipos de ponteiros. São ainda o único tipo de ponteiro convertível para um número inteiro (o valor do inteiro é o valor do endereço de memória). O nível de aninhamento é irrelevante neste caso, i.e., '''ptr<auto>''' designa o mesmo tipo que '''ptr<ptr<...ptr<auto>...>>'''. O uso da palavra '''void''' neste contexto é idêntico.
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 ==
== Operadores de expressões ==


São considerados operadores os elementos lexicais apresentados na [[#Expressões|definição das expressões]].
São considerados operadores os elementos lexicais apresentados na definição das expressões.


== Delimitadores e terminadores ==
== Delimitadores e terminadores ==


Os seguintes elementos lexicais são delimitadores/terminadores: ''',''' (vírgula), ''';''' (ponto e vírgula), e '''(''' e ''')''' (delimitadores de expressões).
Os seguintes elementos lexicais são delimitadores/terminadores:  
* ''',''' (vírgula)
* ''';''' (ponto e vírgula)
* '''!''' e '''!!''' (operações de impressão)
* '''(''' e ''')''' (delimitadores de expressões)
* '''{''' e '''}''' (delimitadores de blocos)


== Identificadores (nomes) ==
== 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.
São iniciados por uma letra, seguindo-se 0 (zero) ou mais letras ou dígitos. 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 ==
== Literais ==


São notações para valores constantes de alguns tipos da linguagem (não confundir com constantes, i.e., identificadores que designam elementos cujo valor não pode ser alterado durante a execução do programa).
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 ===
=== Inteiros ===


Um literal inteiro é um número não negativo. Uma constante inteira pode, contudo, ser negativa: números negativos são construídos pela aplicação do operador de negação aritmética unária ('''-''') a um literal positivo.
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 dígito não é '''0''' (zero), excepto no caso do número 0 (zero). Neste caso, é composto apenas pelo dígito '''0''' (zero) (em qualquer base).-->
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 0 (zero) (em qualquer base).
<!-- Um literal inteiro em octal começa sempre pelo dígito \verb|0| (zero), sendo seguido de um ou mais dígitos de \verb|0| a \verb|7| (note-se que \verb|09| é um literal octal inválido com valor \verb|011|). Exemplo: \verb|007|.-->


Literais inteiros hexadecimais começam sempre com a sequência '''0x''', seguida de um ou mais dígitos de '''0''' a '''9''' ou de '''a''' a '''f''' (sem distinguir maiúsculas de minúsculas). As letras de '''a''' a '''f''' representam os valores de 10 a 15 respectivamente. Exemplo: '''0x07'''.
Literais inteiros em base 9 começam sempre pelo dígito 0 (zero), sendo seguidos de um ou mais dígitos de '''0''' a '''8''' (note-se que '''09''' é um literal inválido em base 9). Exemplo: '''006'''.


<!-- Um literal inteiro em binário começa sempre pela sequência \verb|0b|, sendo seguido de um ou mais digitos \verb|0| ou \verb|1|. -->
Literais inteiros em base 3 (equilibrada) começam sempre com a sequência '''0t''', seguida de um ou mais símbolos dessa base: '''-''' (valor '''-1'''), '''0''' (valor '''0''') e '''+''' (valor '''1'''). Exemplo: '''0t+0-0'''.
Se não for possível representar o literal inteiro na máquina, devido a um overflow, deverá ser gerado um erro lexical.
 
Se não for possível representar um literal na máquina, devido a overflow, deverá ser gerado um erro lexical.


=== Reais em vírgula flutuante ===
=== Reais em vírgula flutuante ===


Os literais reais positivos são expressos tal como em C (apenas é suportada a base 10).
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 aplicação da operação de negação unária).
Note-se que, apesar de C normalmente usar uma representação interna em binário no formato IEEE 754, a linguagem P6 usa um formato, representado em ternário, completamente diferente (embora a forma de escrita dos literais seja a mesma).


Um literal sem '''.''' (ponto decimal) nem parte exponencial é do tipo inteiro.
Não existem literais negativos (números negativos resultam da operação unária '''-''').


Se não for possível representar o literal real na máquina, devido a um overflow, deverá ser gerado um erro lexical.
Note-se que 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).
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).


<!--Se não for possível representar um literal real na máquina, devido a overflow, deverá ser gerado um erro lexical.
-->
=== Cadeias de caracteres ===
=== Cadeias de caracteres ===


As cadeias de caracteres são delimitadas por aspas ('''"''') e podem conter quaisquer caracteres, excepto ASCII NULL ('''0x00''' '''\0'''). Nas cadeias, os delimitadores de comentários não têm significado especial. Se for escrito um literal que contenha '''\0''', então a cadeia termina nessa posição. Exemplo: '''"ab\0xy"''' tem o mesmo significado que '''"ab"'''.
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 cadeia 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 LF, CR e HT ('''\n''', '''\r''' e '''\t''', respectivamente), aspa ('''\"'''), barra ('''\\'''), ou a quaisquer outros especificados através de 1 ou 2 digitos hexadecimais (e.g. '''\0a''' ou apenas '''\a''' se o carácter seguinte não representar um dígito hexadecimal).
É 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 8, designando valores de 8 bits (e.g., '''\012''' ou apenas '''\12''' se o carácter seguinte não representar um dígito em base 8). Exemplo: '''xy\012z''' tem o mesmo significado que '''xy\12z''' 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.  
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"'''.


Exemplos:
=== Ponteiros ===
* '''"ab"''' '''"cd"''' é o mesmo que '''"abcd"'''.
* '''"ab" /* comentário com "cadeia de caracteres falsa" */ "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.


O único literal admissível para ponteiros é indicado pela palavra reservada '''nullptr''', indicando o ponteiro nulo.
Note-se que a aritmética de ponteiros é possível apenas com inteiros binários, mas não com inteiros ternários (estes devem ser convertidos em binário, case seja necessário).


= Gramática =
= 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: <math>(</math> e <math>)</math>; que elementos alternativos são separados por uma barra vertical: <math>|</math>; que elementos opcionais estão entre parênteses rectos: <math>[</math> e <math>]</math>; 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).
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: 140px; font-weight: normal;" | ''ficheiro''
! style="width: 50px; font-weight: normal;" | <math>\rightarrow</math>
! style="width: 50px; font-weight: normal;" | <math>\rightarrow</math>
| ''declaração'' <math>\langle</math> ''declaração'' <math>\rangle</math>
| <math>\langle</math> ''declaração'' <math>\rangle</math> [ ''programa-principal'' ]
|-
|-
! style="width: 150px; font-weight: normal;" | ''declaração''
! style="width: 150px; font-weight: normal;" | ''declaração''
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| ''variável'' ''';''' <math>|</math> ''função''  
| [ ''qualificador'' ] ''tipo'' ''identificador'' [ '''=''' ''expressão'' ] ''';'''
|-
|-
! style="width: 50px; font-weight: normal;" | ''variável''
! style="width: 50px; font-weight: normal;" |  
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| <math>[</math> '''public''' <math>|</math> '''forward''' <math>]</math> ''tipo'' ''identificador'' <math>[</math> '''=''' ''expressão'' <math>]</math>
| [ ''qualificador'' ] [ '''auto''' ] ''identificador'' '''=''' ''expressão'' ''';'''
|-
|-
! style="width: 50px; font-weight: normal;" |
! style="width: 150px; font-weight: normal;" | ''programa-principal''
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| <math>[</math> '''public''' <math>]</math> '''auto''' ''identificador'' '''=''' ''expressão''
| '''begin''' <math>\langle</math> ''declaração'' <math>\rangle</math> <math>\langle</math> ''instrução'' <math>\rangle</math> '''end'''
|-
|-
! style="width: 50px; font-weight: normal;" | ''função''
! style="width: 50px; font-weight: normal;" | ''função''
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| <math>[</math> '''public''' <math>|</math> '''forward''' <math>]</math> <math>(</math> ''tipo'' <math>|</math> '''auto''' <math>)</math> ''identificador'' '''(''' <math>[</math> ''variáveis'' <math>]</math> ''')''' <math>[</math> ''bloco'' <math>]</math>
| '''(''' ''')''' '''->''' ''tipo'' ''bloco''
|-
! style="width: 50px; font-weight: normal;" |
! 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;" | ''variáveis''
Line 158: Line 164:
! style="width: 50px; font-weight: normal;" | ''tipo''
! style="width: 50px; font-weight: normal;" | ''tipo''
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| '''int''' <math>|</math> '''real''' <math>|</math> '''string''' <math>|</math> '''void''' <math>|</math> '''tensor<''' ''dimensão'' <math>\langle</math> ''',''' ''dimensão'' <math>\rangle</math> '''>''' <math>|</math> '''ptr<''' <math>(</math> ''tipo'' <math>|</math> '''auto''' <math>)</math> '''>'''
| '''int''' <math>|</math> '''real''' <math>|</math> '''string''' <math>|</math> '''void''' <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'' '''<''' '''>'''  
|-
! style="width: 50px; font-weight: normal;" |
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| ''tipo'' '''<''' ''tipos'' '''>'''
|-
! style="width: 50px; font-weight: normal;" | ''tipos''
! 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;" | ''bloco''
Line 166: Line 184:
! style="width: 50px; font-weight: normal;" | ''instrução''
! style="width: 50px; font-weight: normal;" | ''instrução''
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| ''expressão'' ''';''' <math>|</math> '''write''' ''expressões'' ''';''' <math>|</math> '''writeln''' ''expressões'' ''';'''
| ''expressão'' ''';''' <math>|</math> ''expressões'' '''!''' <math>|</math> ''expressões'' '''!!'''  
|-
|-
! style="width: 50px; font-weight: normal;" |
! style="width: 50px; font-weight: normal;" |  
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| '''break''' <math>|</math> '''continue''' <math>|</math> '''return''' <math>[</math> ''expressão'' <math>]</math> ''';'''
| '''stop''' [ ''literal-inteiro'' ] ''';''' <math>|</math> '''next''' [ ''literal-inteiro'' ] ''';''' <math>|</math> '''return''' [ ''expressão'' ] ''';'''
|-
|-
! style="width: 50px; font-weight: normal;" |
! style="width: 50px; font-weight: normal;" |  
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| ''instrução-condicional'' <math>|</math> ''instrução-de-iteração'' <math>|</math> ''bloco''
| ''instrução-condicional'' <math>|</math> ''instrução-de-iteração'' <math>|</math> ''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>
|-
|-
! style="width: 50px; font-weight: normal;" | ''instrução-condicional''
! style="width: 50px; font-weight: normal;" | ''instrução-condicional''
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| '''if''' '''(''' ''expressão'' ''')''' ''instrução''
| '''if''' '''(''' ''expressão'' ''')''' ''instrução'' <math>\langle</math>  '''elif''' '''(''' ''expressão'' ''')''' ''instrução'' <math>\rangle</math> [ '''else''' ''instrução'' ]
|-
! style="width: 50px; font-weight: normal;" |
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| '''if''' '''(''' ''expressão'' ''')''' ''instrução'' <math>\langle</math>  '''elif''' '''(''' ''expressão'' ''')''' ''instrução'' <math>\rangle</math> <math>[</math> '''else''' ''instrução'' <math>]</math>
|-
|-
! style="width: 50px; font-weight: normal;" | ''instrução-de-iteração''
! style="width: 50px; font-weight: normal;" | ''instrução-de-iteração''
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| '''for''' '''(''' <math>[</math> ''variáveis'' <math>]</math> ''';''' <math>[</math> ''expressões'' <math>]</math> ''';''' <math>[</math> ''expressões'' <math>]</math> ''')''' ''instrução''
| '''while''' '''(''' ''expressão'' ''')''' ''instrução''
|-
|-
! style="width: 50px; font-weight: normal;" |
! style="width: 50px; font-weight: normal;" | ''expressões''
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
! style="width: 50px; font-weight: normal;" |<math>\rightarrow</math>
| '''for''' '''(''' <math>[</math> ''expressões'' <math>]</math> ''';''' <math>[</math> ''expressões'' <math>]</math> ''';''' <math>[</math> ''expressões'' <math>]</math> ''')''' ''instrução''
| ''expressão'' <math>\langle</math> ''',''' ''expressão'' <math>\rangle</math>
|}
|}


== Tipos, identificadores, literais e definição de expressões ==
== Tipos, identificadores, literais e definição de expressões ==


Algumas definições foram omitidas da gramática: [[#Tipos de Dados|tipos de dados]], ''identificador'' (ver [[#Identificadores|identificadores]]), ''literal'' (ver [[#Literais|literais]]); ''expressão'' (ver [[#Expressões|expressões]]), ''dimensão'' (corresponde a um literal inteiro).
Algumas definições foram omitidas da gramática: tipos de dados. qualificadores e variável (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, '''real''' designa valores reais, '''string''' designa cadeias de caracteres. Os ponteiros são tipos compostos por um tipo 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 (zero ou mais separados por vírgulas).
 
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 ==
== 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 [[#Expressões|semântica das expressões]].
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 ==
== Ficheiros ==


Um ficheiro é designado por principal se contiver a [[#Função principal e execução de programas|função principal]] (a que inicia o programa).
Um ficheiro é designado por principal se contiver a função principal (onde se inicia o programa).


== Declaração de variáveis ==
== Declaração de variáveis ==


Uma declaração de variável indica sempre um [[#Tipos de Dados|tipo de dados]] e um [[#Manipulação de nomes|identificador]]. Não podem ser declaradas variáveis do tipo especial '''void'''.
Uma declaração de variável indica sempre um tipo de dados (implícito ou explícito) e um identificador.


Exemplos:
Exemplos:
* Inteiro: '''int i'''
* Inteiro: '''int i'''
* Real: '''real r'''
* Real: '''real r'''
* Cadeia de caracteres: '''string s'''
* Cadeia de caracteres: '''string s'''
* Ponteiro para inteiro: '''ptr<int> p1''' (equivalente a '''int*''' em C)
* Ponteiro para inteiro: '''[int] p1''' (semelhante a '''int*''' em C)
* Ponteiro para real: '''ptr<real> p2''' (equivalente a '''double*''' em C)
* Ponteiro para real: '''[real] p2''' (semelhante a '''double*''' em C)
* Ponteiro para cadeia de caracteres: '''ptr<string> p3''' (equivalente a '''char**''' em C)
* Ponteiro para cadeia de caracteres: '''[string] p3''' (semelhante a '''char**''' em C)
* Ponteiro para ponteiro para inteiro: '''<nowiki>ptr<ptr<int>> p4</nowiki>''' (equivalente a '''int**''' em C)
* Ponteiro para ponteiro para inteiro: '''<nowiki>[[int]]</nowiki> p4''' (semelhante a '''int**''' em C)
* Ponteiro genérico: '''<nowiki>ptr<auto> p5</nowiki>''' (equivalente a '''void*''' em C)
 
* Tensor de 3 dimensões (7x8x9) inicializado com zeros: '''<nowiki>tensor<7,8,9> t</nowiki>'''
É possível usar o pseudo-tipo '''auto''' se a declaração tiver um valor inicial: neste caso, o tipo é o do valor inicial.
 
Exemplo:
 
* Variável inteira: '''auto i = 1'''
* Variável real: '''auto f = 2.0'''
* etc.


== Símbolos globais ==
== Símbolos globais ==
Line 228: Line 253:
Por omissão, os símbolos são privados a um módulo, não podendo ser importados por outros módulos.
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.
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 '''auto''', esta é opcional. Note-se que a declaração de uma variável tem de ter sempre, ou o qualificador, ou o tipo, podendo estar ambos presentes.


A palavra chave '''forward''' permite declarar num módulo entidades definidas em outros módulos (por este motivo, os símbolos por ela declarados são também implicitamente públicos). As entidades não podem ser inicializadas nestas declarações. A definição de funções em avanço, privadas a um módulo, são feitas omitindo o corpo da função, i.e., sem usar esta palavra. <!--Não é possível utilizar '''require''' em declarações que utilizem a palavra chave '''auto''' (excepto nos casos em que se use '''ptr<auto>''').-->
A palavra-chave '''forward''' 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 '''auto''' em conjunto com '''forward'''.
 
A palavra-chave '''direct''' deve ser usada para declarar símbolos de função com uma convenção de chamada diferente da da linguagem P6, 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:
Exemplos:
* Declarar variável privada ao módulo: '''real pi = 22'''
* Declarar variável privada ao módulo: '''real r1 = 22.0'''
* Declarar variável pública: '''public real pi = 22'''
* Declarar variável pública: '''public real r2 = 7.0'''
* Usar definição externa: '''forward real pi'''
* Declarar variável pública: '''public auto r2 = 7.0''' (igual à anterior)
* Declarar variável pública: '''public r2 = 7.0''' (igual à anterior)
* Usar definição externa de variável pública: '''forward real r2'''
* Usar ponteiro para função (nativa P6) definida externamente: '''forward int<int> factorial'''
* Usar função definida noutra linguagem (e.g. em C): '''direct [void]<int> malloc'''


== Inicialização ==
== Inicialização ==


Quando existe, é uma expressão que segue o sinal '''=''' ("igual"): inteira, real, ponteiro. 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 declaração de um tensor sem um valor inicial explícito implica a sua inicialização com zeros.
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.-->


As [[#Cadeias de caracteres|cadeias de caracteres]] são (possivelmente) inicializadas com uma lista não nula de valores sem separadores. <!--Estes valores são sempre constantes, independentemente de o identificador que as designa ser constante ou não.-->
A palavra '''auto''' pode ser usada 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:
Exemplos:
* Inteiro (literal): '''int i = 3'''
* Inteiro (literal): '''int i = 3'''
* Inteiro (expressão): '''int i = j+1'''
* Inteiro (expressão): '''int i = j + 1'''
* Real (literal): '''real r = 3.2'''
* Real (literal): '''real r = 3.2'''
* Real (expressão): '''real r = i - 2.5 + f(3)'''
* Real (expressão): '''real r = i - 2.5'''
* Cadeia de caracteres (literal): '''string s = "olá"'''
* Cadeia de caracteres (literal): '''string s = "olá"'''
* Cadeia de caracteres (literais): '''string s = "olá" "mãe"'''
* Cadeia de caracteres (literais): '''string s = "olá" "mãe"'''
* Ponteiro (literal): '''<nowiki>ptr<ptr<ptr<real>>></nowiki> p = nullptr'''
* Ponteiro (literal): '''<nowiki>[[[real]]] p</nowiki> = null'''
* Ponteiro (expressão): '''ptr<real> p = q + 1'''
* Ponteiro (expressão): '''[int] p = q + 1'''
* Ponteiro (genérico): '''ptr<auto> p = q'''
* Função:
* Tensor (literal): '''tensor<2,2,3> t = [ [ [1, 2, 3], [4, 5, 6] ], [ [7, 8, 9], [10, 11, 12] ] ]'''
int<int> f1
* Tensor (expressão): '''tensor<2,2,3> t2 = 2 * t1'''
int<double> g1
* Genérica (simples): '''auto p = 2.1'''
double<int> g2
* Genérica (tensor 2x2): '''auto t = [[1,2],[3,4]]'''
int<int> f2 = f1  // ok: mesmo tipo
f2 = g1 // ok: tipos covariantes
f2 = g2 // ERRADO


= Funções =
= Funções =
Line 264: Line 297:
== Declaração ==
== Declaração ==


As funções são sempre designadas por identificadores constantes precedidos do tipo de dados devolvido pela função. Se a função não devolver um valor, é declarada com o tipo de retorno '''void'''. Neste caso, a instrução '''return''' não admite uma expressão.
As funções são anónimas, mas sempre referenciadas por identificadores ou ponteiros, tal como outros tipos de dados. O tipo de retorno de uma função que não produz valores de retorno é '''void'''.  


As funções que recebam argumentos devem indicá-los no cabeçalho. Funções sem argumentos definem um cabeçalho vazio. Não é possível aplicar os qualificadores de exportação/importação '''public''' ou '''forward''' (ver [[#Símbolos globais|símbolos globais]]) às declarações dos argumentos de uma função. Não é possível especificar valores iniciais (valores por omissão) para argumentos de funções. O tipo '''auto''' não pode ser usado para declarar tipos de argumentos (excepto para definição de ponteiros genéricos).
As funções que recebam argumentos devem indicá-los no cabeçalho. Funções sem argumentos definem um cabeçalho vazio. Os qualificadores de exportação/importação '''public''' ou '''forward''' (ver símbolos globais) são aplicados às variáveis usadas para referir as funções. Não é possível aplicá-los às declarações dos argumentos de uma função. Não é possível especificar valores por omissão para os argumentos de uma função, nem para o valor de retorno.


A declaração de uma função sem corpo é utilizada para tipificar um identificador exterior ou para efectuar declarações antecipadas (utilizadas para pré-declarar funções que sejam usadas antes de ser definidas, por exemplo, entre duas funções mutuamente recursivas). Caso a declaração tenha corpo, define-se uma nova função (neste caso, não pode utilizar-se a palavra chave '''forward''').
A declaração de uma variável do tipo função sem ser iniciada é utilizada para caracterizar um identificador exterior ou para efectuar declarações antecipadas (utilizadas para pré-declarar funções que sejam usadas antes de ser definidas, por exemplo, entre duas funções mutuamente recursivas). <!--Caso a declaração tenha corpo, define-se uma nova função.-->


== Invocação ==
== Invocação ==


A função pode ser invocada através de um identificador que refira uma função previamente declarada ou definida.
Uma função pode ser invocada através de qualquer expressão (ponteiro) do tipo apropriado que refira essa função (ponteiro não nulo). O símbolo '''@''' pode ser usado dentro da própria função para efectuar uma invocação recursiva. Não é possível o uso de '''@''' no programa principal.


Se existirem argumentos, na invocação da função, o identificador é seguido de uma lista de expressões delimitadas por parênteses curvos. Esta lista é uma sequência, possivelmente vazia, de expressões separadas por vírgulas. O número e tipo de parâmetros actuais deve ser igual ao número e tipo dos parâmetros formais da função invocada. A ordem dos parâmetros actuais deverá ser a mesma dos argumentos formais da função a ser invocada.
Se existirem argumentos, na invocação da função, o ponteiro é seguido de uma lista de expressões delimitadas por parênteses curvos. Esta lista é uma sequência, possivelmente vazia, de expressões separadas por vírgulas. O número e tipo de parâmetros actuais deve ser igual ao número e tipo dos parâmetros formais da função invocada (a menos de conversões implícitas). A ordem dos parâmetros actuais deverá ser a mesma dos argumentos formais da função a ser invocada.


De acordo com a convenção Cdecl, a função chamadora coloca os argumentos na pilha e é responsável pela sua remoção, após o retorno da chamada. Assim, os parâmetros actuais devem ser colocados na pilha pela ordem inversa da sua declaração (i.e., são avaliados da direita para a esquerda antes da invocação da função e o resultado passado por cópia/valor). O endereço de retorno é colocado no topo da pilha pela chamada à função.
De acordo com a convenção Cdecl, a função chamadora coloca os argumentos na pilha e é responsável pela sua remoção, após o retorno da chamada. Assim, os parâmetros actuais devem ser colocados na pilha pela ordem inversa da sua declaração (i.e., são avaliados da direita para a esquerda antes da invocação da função e o resultado passado por cópia/valor). O endereço de retorno é colocado no topo da pilha pela chamada à função.
Line 280: Line 313:
== Corpo ==
== Corpo ==


O corpo de uma função consiste num bloco que pode ter declarações (opcionais) seguidas de instruções (opcionais). Uma função sem corpo é uma declaração e é considerada indefinida.
O corpo de uma função consiste num bloco que contém declarações (opcionais) seguidas de instruções (opcionais). Não é possível aplicar os qualificadores de exportação ('''public''') ou de importação ('''forward''' ou '''foreign''') (ver [[#Símbolos globais|símbolos globais]]) dentro do corpo de uma função.


Não é possível aplicar as palavras chave '''public''' ou '''forward''' (ver [[#Símbolos globais|símbolos globais]]) dentro do corpo de uma função.
Uma instrução '''return''' causa a interrupção da função. O valor devolvido por uma função, através de expressão usada como argumento da instrução '''return''', deve ser do tipo declarado no cabeçalho da função.


Uma instrução '''return''' causa a interrupção imediata da função, assim como o retorno do valor indicado como argumento da instrução. O tipo deste valor tem de concordar com o tipo declarado.
É um erro especificar um valor de retorno se a função for declarada como não retornando um valor (indicada como '''void''').


Qualquer sub-bloco (usado, por exemplo, numa instrução condicional ou de iteração) pode definir variáveis.
Qualquer bloco (usado, por exemplo, numa instrução condicional ou de iteração) pode definir variáveis, cujos valores podem ser outras funções. Funções definidas dentro de um bloco não têm acesso às variáveis em contexto na função actual onde ocorrem.


== Função principal e execução de programas ==
== Função principal e execução de programas ==


Um programa inicia-se com a invocação da função '''udf''' (sem argumentos). Os argumentos com que o programa foi chamado podem ser obtidos através das seguintes funções:
Um programa é a sequência de instruções que seguem as declarações globais num ficheiro, delimitadas pelas palavras-chave '''begin''' e '''end'''. Esta sequência forma o que em algumas linguagens se chama a função principal. Os argumentos com que o programa foi chamado podem ser obtidos através de funções '''argc''' (devolve o número de argumentos); '''argv''' (devolve o n-ésimo argumento como uma cadeia de caracteres, com n>0); e '''envp''' (devolve a n-ésima variável de ambiente como uma cadeia de caracteres, com n>0). Apenas um dos módulos do programa pode definir a função principal, i.e., se existirem vários módulos, apenas um deles pode conter mais do que declarações de variáveis globais.
* '''int argc()''' (devolve o número de argumentos);
* '''string argv(int n)''' (devolve o n-ésimo argumento como uma cadeia de caracteres) ('''n>0'''); e
* '''string envp(int n)''' (devolve a n-ésima variável de ambiente como uma cadeia de caracteres) ('''n>0''').


O valor de retorno da função principal é devolvido ao ambiente que invocou o programa. Este valor de retorno segue as seguintes regras (sistema operativo):
direct int<> argc
* 0 (zero): execução sem erros;
direct string<int> argv
* 1 (um): argumentos inválidos (em número ou valor);
direct string<int> envp
* 2 (dois): erro de execução.
Os valores superiores a 128 indicam que o programa terminou com um sinal. Em geral, para correcto funcionamento, os programas devem retornar 0 (zero) se a execução foi bem sucedida e um valor diferente de 0 (zero) em caso de erro.


A [[Compiladores/Projecto de Compiladores/Material de Apoio ao Desenvolvimento|biblioteca de run-time]] (RTS) contém informação sobre outras funções de suporte disponíveis, incluindo chamadas ao sistema (ver também o [[Manual da RTS|manual da RTS]]).
O valor de retorno da função principal é devolvido ao ambiente que invocou o programa. Este valor de retorno segue as seguintes regras (sistema operativo): 0 (zero), execução sem erros; 1 (um), argumentos inválidos (em número ou valor); 2 (dois), erro de execução. Os valores superiores a 128 indicam que o programa terminou com um sinal. Em geral, para correcto funcionamento, os programas devem devolver 0 (zero) se a execução foi bem-sucedida e um valor diferente de 0 (zero) em caso de erro.
 
A biblioteca de run-time (RTS) contém informação sobre outras funções de suporte disponíveis. incluindo chamadas ao sistema (ver também o manual da RTS).


= Instruções =
= Instruções =
Line 309: Line 339:
== Blocos ==
== Blocos ==


Cada bloco tem uma zona de declarações de variáveis locais (facultativa), seguida por uma zona com instruções (possivelmente vazia). Não é possível declarar ou definir funções dentro de blocos.
Cada bloco tem uma zona de declarações de variáveis locais (facultativa), seguida por uma zona com instruções (possivelmente vazia). É possível declarar e definir funções dentro de blocos.


A visibilidade das variáveis é limitada ao bloco em que foram declaradas. As entidades declaradas podem ser directamente utilizadas em sub-blocos ou passadas como argumentos para funções chamadas dentro do bloco. Caso os identificadores usados para definir as variáveis locais já estejam a ser utilizados para definir outras entidades ao alcance do bloco, o novo identificador passa a referir uma nova entidade definida no bloco até que ele termine (a entidade previamente definida continua a existir, mas não pode ser directamente referida pelo seu nome). Esta regra é também válida relativamente a argumentos de funções (ver [[#Corpo|corpo das funções]]).
A visibilidade das variáveis é limitada ao bloco em que foram declaradas. As entidades declaradas podem ser directamente utilizadas em sub-blocos ou passadas como argumentos para funções chamadas dentro do bloco. Caso os identificadores usados para definir as variáveis locais já estejam a ser utilizados para definir outras entidades ao alcance do bloco, o novo identificador passa a referir uma nova entidade definida no bloco até que ele termine (a entidade previamente definida continua a existir, mas não pode ser directamente referida pelo seu nome). Esta regra é também válida relativamente a
argumentos de funções (ver corpo das funções).


== Instrução condicional ==
== Instrução condicional ==


Esta instrução tem comportamento idêntico ao da instrução '''if-else''' em C. As partes '''elif''' comportam-se como as sequências '''else if'''.
Esta instrução tem comportamento semelhante ao da instrução '''if-else''' em C. As partes correspondentes a '''elif''' comportam-se como encadeamentos de instruções condicionais na parte '''else''' de um '''if-else''' à la C.


== Instrução de iteração ==
== Instrução de iteração ==


Esta instrução tem comportamento idêntico ao da instrução '''for''' em C. Na zona de declaração de variáveis, apenas pode ser usada uma declaração '''auto''', devendo ser, nesse caso, a única.
Esta instrução tem comportamento idêntico ao da instrução '''while''' em C.


== Instrução de terminação ==
== Instrução de terminação ==


A instrução '''break''' termina o ciclo mais interior em que a instrução se encontrar, tal como a instrução '''break''' em C. Esta instrução só pode existir dentro de um ciclo, sendo a última instrução do seu bloco.
A instrução '''stop''' termina o n-ésimo ciclo mais interior em que a instrução se encontrar (quando o argumento é omitido, assume-se '''n=1'''), tal como a instrução '''break''' em C. Esta instrução só pode existir dentro de um ciclo, sendo a última instrução do seu bloco.


== Instrução de continuação ==
== Instrução de continuação ==


A instrução '''continue''' reinicia o ciclo mais interior em que a instrução se encontrar, tal como a instrução '''continue''' em C. Esta instrução só pode existir dentro de um ciclo, sendo a última instrução do seu bloco.
A instrução '''next''' reinicia o n-ésimo ciclo mais interior em que a instrução se encontrar (quando o argumento é omitido, assume-se '''n=1'''), tal como a instrução '''continue''' em C. Esta instrução só pode existir dentro de um ciclo, sendo a última instrução do seu bloco.


== Instrução de retorno ==
== Instrução de retorno ==
Line 335: Line 366:
== Expressões como instruções ==
== Expressões como instruções ==


As expressões utilizadas como instruções são avaliadas, mesmo que não produzam efeitos secundários. A notação é como indicada na gramática (expressão seguida de ''';''').
As expressões utilizadas como instruções são sempre avaliadas, mesmo que não produzam efeitos secundários.


== Instruções de impressão ==
== Instruções de impressão ==


As palavras chave '''write''' e '''writeln''' podem ser utilizadas para apresentar valores na saída do programa. A primeira apresenta a expressão sem mudar de linha; a segunda apresenta a expressão mudando de linha. Quando existe mais de uma expressão, as várias expressões são apresentadas sem separação. Valores numéricos (inteiros ou reais) são impressos em decimal. As cadeias de caracteres são impressas na codificação nativa. Ponteiros não podem ser impressos.
As notações '''!''' e '''!!''' podem ser utilizadas para apresentar valores na saída do programa. A primeira forma apresenta os valores sem mudar de linha; a segunda forma apresenta os valores mudando de linha depois de os apresentar a todos. Quando existe mais de uma expressão, as várias expressões são apresentadas sem separação. Valores numéricos (inteiros ou reais) são impressos em decimal. As cadeias de caracteres são impressas na codificação nativa. Ponteiros não podem ser impressos.


= Expressões =
= Expressões =
Line 345: Line 376:
Uma expressão é uma representação algébrica de uma quantidade: todas as expressões têm um tipo e devolvem um valor.
Uma expressão é uma representação algébrica de uma quantidade: todas as expressões têm um tipo e devolvem um valor.


Existem [[#Expressões primitivas|expressões primitivas]] e expressões que resultam da [[#Expressões resultantes de avaliação de operadores|avaliação de operadores]].
Existem [[#Expressões primitivas|expressões primitivas]] e expressões que resultam da [[#Expressões resultantes de avaliação de operadores|avaliação de operadores]].


A tabela seguinte apresenta as precedências relativas dos operadores: é a mesma para operadores na mesma linha, sendo as linhas seguintes de menor prioridade que as anteriores. A maioria dos operadores segue a semântica da linguagem C (excepto onde explicitamente indicado). Tal como em C, os valores lógicos são 0 (zero) (valor falso), e diferente de zero (valor verdadeiro).
A tabela seguinte apresenta as precedências relativas dos operadores: é a mesma para operadores na mesma linha, sendo as linhas seguintes de menor prioridade que as anteriores. A maioria dos operadores segue a semântica da linguagem C (excepto onde explicitamente indicado). Tal como em C, os valores lógicos são 0 (zero) (valor falso), e diferente de zero (valor verdadeiro).
Além das expressões na tabela, existem ainda algumas especiais sobre tensores ([[#Expressões dependentes de tensores|ver abaixo]]).


  {|
  {|
Line 362: Line 391:
| não associativos
| não associativos
| -
| -
| [[#Parênteses curvos|parênteses curvos]], [[#Indexação|indexação]]
| [[#Parênteses curvos|parênteses curvos]], [[#Indexação|indexação]], [[#Reserva de memória|reserva de memória]]
|-
|-
| unária
| unária
Line 373: Line 402:
! style="vertical-align: top; font-weight: normal;" | '''* / %'''
! style="vertical-align: top; font-weight: normal;" | '''* / %'''
! style="vertical-align: top; font-weight: normal;" | da esquerda para a direita
! style="vertical-align: top; font-weight: normal;" | da esquerda para a direita
! style="vertical-align: top; font-weight: normal;" | inteiros, reais, tensores
! style="vertical-align: top; font-weight: normal;" | inteiros, reais
! style="vertical-align: top; font-weight: normal;" | C (% é apenas para inteiros e não pode ser aplicado a tensores). A aplicação dos operadores a tensores resulta em operações coeficiente a coeficiente (se aplicados a dois tensores com a mesma forma) e na operação entre o escalar e cada elemento do tensor (se for aplicados a um tensor e a um escalar).
! style="vertical-align: top; font-weight: normal;" | C (% é apenas para inteiros)
|-
|-
! style="vertical-align: top; font-weight: normal;" | aditiva
! style="vertical-align: top; font-weight: normal;" | aditiva
! style="vertical-align: top; font-weight: normal;" | '''+ -'''
! style="vertical-align: top; font-weight: normal;" | '''+ -'''
! style="vertical-align: top; font-weight: normal;" | da esquerda para a direita
! style="vertical-align: top; font-weight: normal;" | da esquerda para a direita
! style="vertical-align: top; font-weight: normal;" | inteiros, reais, ponteiros, tensores
! style="vertical-align: top; font-weight: normal;" | inteiros, reais, ponteiros
! style="vertical-align: top; font-weight: normal;" | C: se envolverem ponteiros, calculam: (i) deslocamentos, i.e., um dos operandos deve ser do tipo ponteiro e o outro do tipo inteiro; (ii) diferenças de ponteiros, i.e., apenas quando se aplica o operador '''-''' a dois ponteiros do mesmo tipo (o resultado é o número de objectos do tipo apontado entre eles). Se a memória não for contígua, o resultado é indefinido. A aplicação dos operadores a tensores resulta em operações coeficiente a coeficiente (se aplicados a dois tensores com a mesma forma) e na operação entre o escalar e cada elemento do tensor (se for aplicados a um tensor e a um escalar).
! style="vertical-align: top; font-weight: normal;" | C: se envolverem ponteiros, calculam: (i) deslocamentos, i.e., um dos operandos deve ser do tipo ponteiro e o outro do tipo inteiro; (ii) diferenças de ponteiros, i.e., apenas quando se aplica o operador '''-''' a dois ponteiros do mesmo tipo (o resultado é o número de objectos do tipo apontado entre eles). Se a memória não for contígua, o resultado é indefinido.
|-
|-
! style="vertical-align: top; font-weight: normal;" | comparativa
! style="vertical-align: top; font-weight: normal;" | comparativa
Line 391: Line 420:
! style="vertical-align: top; font-weight: normal;" | '''== !='''
! style="vertical-align: top; font-weight: normal;" | '''== !='''
! style="vertical-align: top; font-weight: normal;" | da esquerda para a direita
! style="vertical-align: top; font-weight: normal;" | da esquerda para a direita
! style="vertical-align: top; font-weight: normal;" | inteiros, reais, ponteiros, tensores
! style="vertical-align: top; font-weight: normal;" | inteiros, reais, ponteiros
! style="vertical-align: top; font-weight: normal;" | C. A aplicação dos operadores a tensores resulta em operações coeficiente a coeficiente (se aplicados a dois tensores com a mesma forma).
! style="vertical-align: top; font-weight: normal;" | C
|-
|-
! style="vertical-align: top; font-weight: normal;" | "não" lógico
! style="vertical-align: top; font-weight: normal;" | "não" lógico
Line 416: Line 445:
! style="vertical-align: top; font-weight: normal;" | da direita para a esquerda
! style="vertical-align: top; font-weight: normal;" | da direita para a esquerda
! style="vertical-align: top; font-weight: normal;" | todos os tipos
! style="vertical-align: top; font-weight: normal;" | todos os tipos
! style="vertical-align: top; font-weight: normal;" | O valor da expressão do lado direito do operador é guardado na posição indicada pelo ''left-value'' (operando esquerdo do operador). Podem ser atribuídos valores inteiros a ''left-values'' reais (conversão automática). Nos outros casos, ambos os tipos têm de concordar.
! style="vertical-align: top; font-weight: normal;" | O valor da expressão do lado direito do operador é guardado na posição indicada pelo ''left-value'' (operando esquerdo do operador). Podem ser atribuídos valores inteiros a ''left-values'' reais (conversão automática). Nos outros casos, ambos os tipos têm de concordar. O literal '''null''' é compatível com todos os tipos de ponteiros.
|}
|}


Line 431: Line 460:
=== Leitura ===
=== Leitura ===


A operação de leitura de um valor inteiro ou real pode ser efectuado pela expressão indicada pela palavra reservada '''input''', que devolve o valor lido, de acordo com o tipo esperado (inteiro ou real). Caso se use como argumento dos operadores de impressão ou noutras situações que permitam várias tipos ('''write''' ou '''writeln'''), deve ser lido um inteiro.
A operação de leitura de um valor inteiro ou real pode ser efectuado pela expressão indicada pela palavra-chave '''input''', que devolve o valor lido, de acordo com o tipo esperado (inteiro ou real). Caso se use como argumento dos operadores de impressão ou noutras situações que permitam vários tipos (e.g. '''!''' ou '''!!'''), deve ser lido um inteiro.


Exemplos:  '''a = input''' (leitura para '''a'''), '''f(input)''' (leitura para argumento de função), '''write input''' (leitura e impressão).
Exemplos:  '''a = input''' (leitura para '''a'''), '''f(input)''' (leitura para argumento de função), '''input!!''' (leitura e impressão), '''@(input)''' (chamada recursiva a função com argumento que é lido da entrada).


=== Parênteses curvos ===
=== Parênteses curvos ===


Uma expressão entre parênteses curvos tem o valor da expressão sem os parênteses e permite alterar a prioridade dos operadores. Uma expressão entre parênteses não pode ser utilizada como ''left-value'' (ver também a [[#Indexação|expressão de indexação]]).
Uma expressão entre parênteses curvos tem o valor da expressão sem os parênteses e permite alterar a prioridade dos operadores. Uma expressão entre parênteses não pode ser utilizada como ''left-value'' (ver também a [[#Indexação|expressão de indexação]]).
=== Funções ===
As funções (ponteiros ou o seu código: não confundir com chamadas a funções) podem ser usadas como expressões tipificadas como funções, i.e., ponteiros para funções (mesmo quando não se usa explicitamente um ponteiro).
Exemplo:
auto f = (int i) -> int { return i + 1; };
auto g = (int n, int<int> fun) -> int { return fun(n); };
begin
  g(3, f)!!  // escreve 4
  g(3, (int i) -> int { return i * 2; })!!  // escreve 6
end


== Expressões resultantes de avaliação de operadores ==
== Expressões resultantes de avaliação de operadores ==
Line 443: Line 486:
=== Indexação de ponteiros ===
=== Indexação de ponteiros ===


A indexação de ponteiros devolve o valor de uma posição de memória indicada por um ponteiro. Consiste de uma expressão ponteiro seguida do índice entre parênteses rectos. O resultado de uma indexação de ponteiros é um ''[[#Left-values|left-value]]''.
A indexação de ponteiros devolve o valor de uma posição de memória indicada por um ponteiro. Consiste em uma expressão ponteiro seguida do índice entre parênteses rectos. O resultado de uma indexação de ponteiros é um ''[[#Left-values|left-value]]''. Não é possível indexar ponteiros que designem funções.


Exemplo (acesso à posição 0 da zona de memória indicada por '''p'''): '''p[0]'''
Exemplo (acesso à posição 0 da zona de memória indicada por '''p'''): '''p[0]'''
Line 449: Line 492:
=== Identidade e simétrico ===
=== Identidade e simétrico ===


Os operadores identidade ('''+''') e simétrico ('''-''') aplicam-se a inteiros, reais e tensores. Têm o mesmo significado que em C.
Os operadores identidade ('''+''') e simétrico ('''-''') aplicam-se a inteiros e reais. Têm o mesmo significado que em C.
 
A aplicação do operador a tensores resulta na distribuição da operação por todos os coeficientes.


=== Reserva de memória ===
=== Reserva de memória ===


A expressão reserva de memória, indicada por '''objects''' (argumento inteiro), devolve o ponteiro que aponta para a zona de memória, na pilha da função actual, contendo espaço suficiente para o número de objectos indicados pelo seu argumento inteiro.
A expressão reserva de memória devolve o ponteiro que aponta para a zona de memória, na pilha da função actual, contendo espaço suficiente para o número de objectos indicados pelo seu argumento inteiro.


Exemplo (reserva vector com 5 reais, apontados por '''p'''): '''ptr<real> p = objects(5)'''
Exemplo (reserva vector com 5 reais, apontados por '''p'''): '''[double] p = [5]'''


=== Expressão de indicação de posição ===
=== Expressão de indicação de posição ===
Line 467: Line 508:
=== Expressão de dimensão ===
=== Expressão de dimensão ===


O operador '''sizeof''' aplica-se a expressões, retornando a dimensão correspondente em bytes. Aplicado a um tensor, retorna a capacidade do tensor (em bytes).
O operador '''sizeof''' tem um único argumento e aplica-se a expressões, retornando a dimensão correspondente em bytes.
 
Exemplos: '''sizeof(a)''' (dimensão de '''a''').
 
== Expressões dependentes de tensores ==


=== Tensor explicitamente escrito no programa ===
Exemplo: '''sizeof(a)''' (dimensão de '''a''').


Os tensores podem ser escritos directamente nos programas. O formato utiliza a notação de parênteses rectos que envolvem expressões (estas expressões têm de ser convertíveis para valores de vírgula flutuante).
= Exemplos e Testes =


Exemplos:
Os exemplos abaixo não são exaustivos e não ilustram todos os aspectos da linguagem.
* Tensor 2x2: '''[[2,2], [2,3]]'''
* Tensor 2x2 com expressões: '''[[2+1, i], [f(1), 2]]'''


=== Capacidade ===
Estão ainda disponíveis outros [[Compiladores/Projecto de Compiladores/Testes Automáticos 2022-2023|pacotes de testes]].


Para um tensor '''t''', o comprimento, indicado por '''t.capacity''' (valor inteiro), corresponde ao número de células do tensor (não confundir com '''sizeof''', que indica o número de bytes ocupado por essas células).
O seguinte exemplo ilustra um programa com dois módulos: um que define a função '''factorial''' e outro que define a função principal.


=== Número de dimensões ===
Definição da função '''factorial''' no ficheiro '''factorial.p6''':


Para um tensor '''t''', o número de dimensões é indicado por '''t.rank''' (valor inteiro).
<source lang="text">
public factorial = (int n) -> int {
  if (n > 1)
    return n * @(n - 1);  // could also use 'factorial' (buggy approach!)
  else
    return 1;
};
</source>


=== Dimensões ===
Exemplo da utilização da função '''factorial''' no ficheiro '''main.p6''':


Para um tensor '''t''', as dimensões, indicadas por '''t.dims''' (ponteiro para um vector inteiro), correspondem ao vector com as dimensões do tensor. É ainda possível obter uma única dimensão através da função '''dim''': e.g. '''t.dim(0)''' (primeira dimensão). São válidas dimensões até ao limite definido por '''t.rank'''.
<source lang="text">
// external builtin functions (non-P6)
direct int<> argc;
direct string<int> argv;
direct int<string> atoi;


=== Indexação ===
// external user functions (MML)
forward int<int> factorial;


A indexação de tensores devolve o valor de uma posição de um tensor indicada por um conjunto de coordenadas (com início em 0). Consiste de um tensor seguido do operador '''@''' e as coordenadas a indexar. O resultado de uma indexação de tensores é um ''[[#Left-values|left-value]]''.
// the main function
 
begin
Exemplo (acesso a uma posição do tensor tridimensional '''t'''): '''t@(x,y,z)''' ('''x''', '''y''' e '''z''' são expressões inteiras)
  auto value = 1;
 
  "Teste para a função factorial."!!
=== Redimensionamento ===
  if (argc() == 2) {
 
    string s = argv(1);
Para um tensor '''t1''', com capacidade '''D1 x ... x Dn''' ('''D1''' ... '''Dn''' são os comprimentos em cada dimensão), a operação de redimensionamento '''t.reshape(S1, ..., Sk)''' ('''S1''' ... '''Sk''' são os comprimentos em cada nova dimensão) produz um novo tensor '''t2''' com a mesma capacidade de '''t1''' e, por consequência, onde '''D1 x ... x Dn == S1 x ... x Sk'''. Os argumentos de '''reshape''' são sempre literais inteiros positivos.
    value = atoi(s);
 
  }
Exemplo:
  value, "! é ", factorial(value)!!
tensor<2,2,2> t1 = [ [ [1, 2], [3, 4] ], [ [5, 6], [7, 8] ] ];
  return 0;
tensor<2, 4> t2 = t1.reshape(2,4);  // [ [1, 2, 3, 4], [5, 6, 7, 8] ]
end
 
</source>
=== Contracção ===
 
Dados dois tensores, '''t1''' e '''t2''', a operação de contracção, representada por '''t1 ** t2''' é possível se a última dimensão do primeiro tensor for igual à primeira do segundo tensor. Para matrizes (i.e., tensores de duas dimensões), esta operação corresponde à multiplicação de matrizes habitual. Esta operação tem uma precedência imediatamente superior à da multiplicação habitual.
 
Exemplo:
tensor<3,2,2> t1 = [[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 0], [8, 7]]];
tensor<2,2,3> t2 = [[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [0, 8, 7]]];
writeln t1**t2;
writeln t2**t1;
 
Resultado:
Tensor<3,2,2,3>[[[[1.5E1, 1.8E1, 2.1E1], [4, 2.1E1, 2E1]], [[3.1E1, 3.8E1, 4.5E1], [1.2E1, 4.7E1, 4.6E1]]], [[[4.7E1, 5.8E1, 6.9E1], [2E1, 7.3E1, 7.2E1]], [[6.3E1, 7.8E1, 9.3E1], [2.8E1, 9.9E1, 9.8E1]]], [[[9, 1.8E1, 2.7E1], [3.6E1, 4.5E1, 5.4E1]], [[5.7E1, 7.2E1, 8.7E1], [3.2E1, 9.6E1, 9.7E1]]]]
Tensor<2,2,2,2>[[[[3.8E1, 1.4E1], [4.1E1, 4.1E1]], [[8.3E1, 3.8E1], [9.5E1, 9.8E1]]], [[[1.28E2, 6.2E1], [1.49E2, 1.55E2]], [[1.03E2, 4.8E1], [1.12E2, 1.13E2]]]]
 
= Exemplos =
 
Os exemplos não são exaustivos e não ilustram todos os aspectos da linguagem. Podem obter-se outros na página da disciplina.
 
== Programa com vários módulos ==
 
Definição da função ''factorial'' num ficheiro ('''factorial.udf'''):
 
public int factorial(int n) {
  if (n > 1) return n * factorial(n-1); else return 1;
}
 
Exemplo da utilização da função ''factorial'' num outro ficheiro ('''main.udf'''):
 
// external builtin functions
forward int argc()
forward string argv(int n)
forward int atoi(string s)
// external user functions
forward int factorial(int n)
// the main function
public int udf() {
  int f = 1;
  writeln "Teste para a função factorial";
  if (argc() == 2) f = atoi(argv(1));
  writeln f, "! = ", factorial(f);
  return 0;
}


Como compilar:
Como compilar:
 
<source lang="text">
udf --target asm factorial.udf
p6 --target asm factorial.p6
udf --target asm main.udf
p6 --target asm main.p6
yasm -gdwarf2 -felf32 factorial.asm
yasm -felf32 factorial.asm
yasm -gdwarf2 -felf32 main.asm
yasm -felf32 main.asm
ld -melf_i386 -o main factorial.o main.o -lrts
ld -melf_i386 -o main factorial.o main.o -lrts
 
</source>
== Outros testes ==
 
Estão disponíveis outros pacotes de testes no repositório GIT: https://gitlab.rnl.tecnico.ulisboa.pt/leic-a-co25/eval/co25


= Omissões e Erros =
= Omissões e Erros =
Line 570: Line 570:
[[category:Projecto de Compiladores]]
[[category:Projecto de Compiladores]]
[[category:Compiladores]]
[[category:Compiladores]]
[[category:Ensino]]
[[category: Ensino]]

Revision as of 13:32, 12 April 2026

AVISOS - Avaliação em Época Normal

Esclarecimento de dúvidas:

  • Consultar sempre o corpo docente atempadamente: presencialmente ou através do endereço oficial da disciplina [1].
  • Não utilizar fontes de informação não oficialmente associadas ao corpo docente (podem colocar em causa a aprovação à disciplina).
  • Não são aceites justificações para violações destes conselhos: quaisquer consequências nefastas são da responsabilidade do aluno.

Requisitos para desenvolvimento, material de apoio e actualizações do enunciado (ver informação completa em Projecto de Compiladores):

  • O material de apoio é de uso obrigatório e não pode ser alterado.
  • Verificar atempadamente (mínimo de 48 horas antes do final de cada prazo) os requisitos exigidos pelo processo de desenvolvimento.

Processo de avaliação (ver informação completa em Avaliação do Projecto):

  • Datas: 2023/05/15 17:00 (inicial); 2023/05/30 17:00 (intercalar); 2023/06/16 17:00 (final); 2023/06/16-19 (teste prático).
  • Todas as entregas são cruciais para o bom desenvolvimento do projecto, sendo obrigatórias: a não realização de uma entrega implica a exclusão da avaliação do projecto e, por consequência, da avaliação da disciplina.
  • Verificar atempadamente (até 48 horas antes do final de cada prazo) os requisitos exigidos pelo processo de avaliação, incluindo a capacidade de acesso ao repositório.
  • Apenas se consideram para avaliação os projectos existentes no repositório oficial. Apenas se considera para avaliação o ramo 'master'.
  • Trabalhos não presentes no repositório no final do prazo têm classificação 0 (zero) (não são aceites outras formas de entrega). Não são admitidas justificações para atrasos em sincronizações do repositório. A indisponibilidade temporária do repositório, desde que inferior a 24 horas, não justifica atrasos na submissão de um trabalho.
  • A avaliação do projecto pressupõe o compromisso de honra de que o trabalho correspondente foi realizado pelos alunos correspondentes ao grupo de avaliação.
  • Fraudes na execução do projecto terão como resultado a exclusão dos alunos implicados do processo de avaliação em curso.
Material de Uso Obrigatório
As bibliotecas CDK e RTS de apoio ao desenvolvimento do projecto são de uso obrigatório:
A máquina virtual, fornecida para desenvolvimento do projecto, já contém todo o material de apoio.
Uso Obrigatório: Repositório GIT
Apenas se consideram para avaliação os projectos existentes no repositório GIT oficial. Apenas se considera para avaliação o ramo master.

Trabalhos não presentes no repositório no final do prazo têm classificação 0 (zero) (não são aceites outras formas de entrega). Não são admitidas justificações para atrasos em sincronizações do repositório. A indisponibilidade temporária do repositório, desde que inferior a 24 horas, não justifica atrasos na submissão de um trabalho.

RASCUNHO

EM PREPARAÇÃO

P6 é uma linguagem imperativa. Este manual apresenta de forma intuitiva as características 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, apenas alguns dos quais são compatíveis com a linguagem C. O alinhamento em memória binária é sempre a 32 bits. Note-se que alguns dos tipos de dados são ternários e devem ser manipulados com as funções das ALUs disponibilizadas.

  • Tipos numéricos: os inteiros são ternários equilibrados, ocupam 40 trits (empacotados em 64 bits); os reais são ternários e estão representados no formato Takum (note-se que esta definição é para representação binária e não é exactamente a que é implementada pelas ALUs), ocupam 80 trits (empacotados em 128 bits).
  • As cadeias de caracteres são vectores de caracteres binários terminados por 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 caracteres são valores de 8 bits não directamente manipuláveis.
  • Os ponteiros representam endereços de objectos (de qualquer tipo) 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. 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 a posição do código dessas funções (à la C; ver direct 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).

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 =).

Caracteres brancos

São considerados separadores e não representam nenhum elemento lexical: mudança de linha ASCII LF (0x0A, \n), recuo do carreto ASCII CR (0x0D, \r), espaço ASCII SP (0x20, " ") e tabulação horizontal ASCII HT (0x09, \t).

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; 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 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 real string void
  • declarações: direct forward public auto
  • instruções: if elif else while stop next return
  • expressões: input null sizeof
  • outras: begin end

Tipos

Os seguintes elementos lexicais designam tipos em declarações (ver gramática): int (inteiro), real (real), string (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)
  • ; (ponto e vírgula)
  • ! e !! (operações de impressão)
  • ( e ) (delimitadores de expressões)
  • { e } (delimitadores de blocos)

Identificadores (nomes)

São iniciados por uma letra, seguindo-se 0 (zero) ou mais letras ou dígitos. 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 0 (zero) (em qualquer base).

Literais inteiros em base 9 começam sempre pelo dígito 0 (zero), sendo seguidos de um ou mais dígitos de 0 a 8 (note-se que 09 é um literal inválido em base 9). Exemplo: 006.

Literais inteiros em base 3 (equilibrada) começam sempre com a sequência 0t, seguida de um ou mais símbolos dessa base: - (valor -1), 0 (valor 0) e + (valor 1). Exemplo: 0t+0-0.

Se não for possível representar um literal na máquina, devido a 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).

Note-se que, apesar de C normalmente usar uma representação interna em binário no formato IEEE 754, a linguagem P6 usa um formato, representado em ternário, completamente diferente (embora a forma de escrita dos literais seja a mesma).

Não existem literais negativos (números negativos resultam da operação unária -).

Note-se que 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-24 (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 cadeia 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 8, designando valores de 8 bits (e.g., \012 ou apenas \12 se o carácter seguinte não representar um dígito em base 8). Exemplo: xy\012z tem o mesmo significado que xy\12z 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.

Note-se que a aritmética de ponteiros é possível apenas com inteiros binários, mas não com inteiros ternários (estes devem ser convertidos em binário, case seja necessário).

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 e . Alguns elementos usados na gramática também são elementos da linguagem descrita se representados em tipo fixo (e.g., parênteses).

ficheiro declaração [ programa-principal ]
declaração [ qualificador ] tipo identificador [ = expressão ] ;
[ qualificador ] [ auto ] identificador = expressão ;
programa-principal begin declaração instrução end
função ( ) -> tipo bloco
( variáveis ) -> tipo bloco
variáveis variável , variável
tipo int | real | string | void | [ tipo ] | tipo-de-função
tipo-de-função tipo < >
tipo < tipos >
tipos tipo , tipo
bloco { declaração instrução }
instrução expressão ; | expressões ! | expressões !!
stop [ literal-inteiro ] ; | next [ literal-inteiro ] ; | return [ expressão ] ;
instrução-condicional | instrução-de-iteração | bloco
instrução-condicional if ( expressão ) instrução elif ( expressão ) instrução [ else instrução ]
instrução-de-iteração while ( expressão ) instrução
expressões expressão , expressão

Tipos, identificadores, literais e definição de expressões

Algumas definições foram omitidas da gramática: tipos de dados. qualificadores e variável (ver declarações de variáveis), identificador (ver identificadores), literal (ver literais); expressão (ver 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, real designa valores reais, string designa cadeias de caracteres. Os ponteiros são tipos compostos por um tipo 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 (zero ou mais separados por vírgulas).

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 [[[void]]] 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: real r
  • Cadeia de caracteres: string s
  • Ponteiro para inteiro: [int] p1 (semelhante a int* em C)
  • Ponteiro para real: [real] p2 (semelhante a double* em C)
  • Ponteiro para cadeia de caracteres: [string] p3 (semelhante a char** em C)
  • Ponteiro para ponteiro para inteiro: [[int]] p4 (semelhante a int** em C)

É possível usar o pseudo-tipo auto se a declaração tiver um valor inicial: neste caso, o tipo é o do valor inicial.

Exemplo:

  • Variável inteira: auto i = 1
  • Variável real: auto f = 2.0
  • etc.

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 auto, esta é opcional. Note-se que a declaração de uma variável tem de ter sempre, ou o qualificador, ou o tipo, podendo estar ambos presentes.

A palavra-chave forward 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 auto em conjunto com forward.

A palavra-chave direct deve ser usada para declarar símbolos de função com uma convenção de chamada diferente da da linguagem P6, 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: real r1 = 22.0
  • Declarar variável pública: public real r2 = 7.0
  • Declarar variável pública: public auto r2 = 7.0 (igual à anterior)
  • Declarar variável pública: public r2 = 7.0 (igual à anterior)
  • Usar definição externa de variável pública: forward real r2
  • Usar ponteiro para função (nativa P6) definida externamente: forward int<int> factorial
  • Usar função definida noutra linguagem (e.g. em C): direct [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.

A palavra auto pode ser usada 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): real r = 3.2
  • Real (expressão): real r = i - 2.5
  • Cadeia de caracteres (literal): string s = "olá"
  • Cadeia de caracteres (literais): string s = "olá" "mãe"
  • Ponteiro (literal): [[[real]]] p = null
  • Ponteiro (expressão): [int] p = q + 1
  • Função:
int<int> f1
int<double> g1
double<int> g2
int<int> f2 = f1  // ok: mesmo tipo
f2 = g1 // ok: tipos covariantes
f2 = g2 // ERRADO

Funções

Uma função permite agrupar um conjunto de instruções num corpo, executado com base num conjunto de parâmetros (os argumentos formais), quando é invocada a partir de uma expressão.

Declaração

As funções são anónimas, mas sempre referenciadas por identificadores ou ponteiros, tal como outros tipos de dados. O tipo de retorno de uma função que não produz valores de retorno é void.

As funções que recebam argumentos devem indicá-los no cabeçalho. Funções sem argumentos definem um cabeçalho vazio. Os qualificadores de exportação/importação public ou forward (ver símbolos globais) são aplicados às variáveis usadas para referir as funções. Não é possível aplicá-los às declarações dos argumentos de uma função. Não é possível especificar valores por omissão para os argumentos de uma função, nem para o valor de retorno.

A declaração de uma variável do tipo função sem ser iniciada é utilizada para caracterizar um identificador exterior ou para efectuar declarações antecipadas (utilizadas para pré-declarar funções que sejam usadas antes de ser definidas, por exemplo, entre duas funções mutuamente recursivas).

Invocação

Uma função pode ser invocada através de qualquer expressão (ponteiro) do tipo apropriado que refira essa função (ponteiro não nulo). O símbolo @ pode ser usado dentro da própria função para efectuar uma invocação recursiva. Não é possível o uso de @ no programa principal.

Se existirem argumentos, na invocação da função, o ponteiro é seguido de uma lista de expressões delimitadas por parênteses curvos. Esta lista é uma sequência, possivelmente vazia, de expressões separadas por vírgulas. O número e tipo de parâmetros actuais deve ser igual ao número e tipo dos parâmetros formais da função invocada (a menos de conversões implícitas). A ordem dos parâmetros actuais deverá ser a mesma dos argumentos formais da função a ser invocada.

De acordo com a convenção Cdecl, a função chamadora coloca os argumentos na pilha e é responsável pela sua remoção, após o retorno da chamada. Assim, os parâmetros actuais devem ser colocados na pilha pela ordem inversa da sua declaração (i.e., são avaliados da direita para a esquerda antes da invocação da função e o resultado passado por cópia/valor). O endereço de retorno é colocado no topo da pilha pela chamada à função.

Corpo

O corpo de uma função consiste num bloco que contém declarações (opcionais) seguidas de instruções (opcionais). Não é possível aplicar os qualificadores de exportação (public) ou de importação (forward ou foreign) (ver símbolos globais) dentro do corpo de uma função.

Uma instrução return causa a interrupção da função. O valor devolvido por uma função, através de expressão usada como argumento da instrução return, deve ser do tipo declarado no cabeçalho da função.

É um erro especificar um valor de retorno se a função for declarada como não retornando um valor (indicada como void).

Qualquer bloco (usado, por exemplo, numa instrução condicional ou de iteração) pode definir variáveis, cujos valores podem ser outras funções. Funções definidas dentro de um bloco não têm acesso às variáveis em contexto na função actual onde ocorrem.

Função principal e execução de programas

Um programa é a sequência de instruções que seguem as declarações globais num ficheiro, delimitadas pelas palavras-chave begin e end. Esta sequência forma o que em algumas linguagens se chama a função principal. Os argumentos com que o programa foi chamado podem ser obtidos através de funções argc (devolve o número de argumentos); argv (devolve o n-ésimo argumento como uma cadeia de caracteres, com n>0); e envp (devolve a n-ésima variável de ambiente como uma cadeia de caracteres, com n>0). Apenas um dos módulos do programa pode definir a função principal, i.e., se existirem vários módulos, apenas um deles pode conter mais do que declarações de variáveis globais.

direct int<> argc
direct string<int> argv
direct string<int> envp

O valor de retorno da função principal é devolvido ao ambiente que invocou o programa. Este valor de retorno segue as seguintes regras (sistema operativo): 0 (zero), execução sem erros; 1 (um), argumentos inválidos (em número ou valor); 2 (dois), erro de execução. Os valores superiores a 128 indicam que o programa terminou com um sinal. Em geral, para correcto funcionamento, os programas devem devolver 0 (zero) se a execução foi bem-sucedida e um valor diferente de 0 (zero) em caso de erro.

A biblioteca de run-time (RTS) contém informação sobre outras funções de suporte disponíveis. incluindo chamadas ao sistema (ver também o manual da RTS).

Instruções

Excepto quando indicado, as instruções são executadas em sequência.

Blocos

Cada bloco tem uma zona de declarações de variáveis locais (facultativa), seguida por uma zona com instruções (possivelmente vazia). É possível declarar e definir funções dentro de blocos.

A visibilidade das variáveis é limitada ao bloco em que foram declaradas. As entidades declaradas podem ser directamente utilizadas em sub-blocos ou passadas como argumentos para funções chamadas dentro do bloco. Caso os identificadores usados para definir as variáveis locais já estejam a ser utilizados para definir outras entidades ao alcance do bloco, o novo identificador passa a referir uma nova entidade definida no bloco até que ele termine (a entidade previamente definida continua a existir, mas não pode ser directamente referida pelo seu nome). Esta regra é também válida relativamente a argumentos de funções (ver corpo das funções).

Instrução condicional

Esta instrução tem comportamento semelhante ao da instrução if-else em C. As partes correspondentes a elif comportam-se como encadeamentos de instruções condicionais na parte else de um if-else à la C.

Instrução de iteração

Esta instrução tem comportamento idêntico ao da instrução while em C.

Instrução de terminação

A instrução stop termina o n-ésimo ciclo mais interior em que a instrução se encontrar (quando o argumento é omitido, assume-se n=1), tal como a instrução break em C. Esta instrução só pode existir dentro de um ciclo, sendo a última instrução do seu bloco.

Instrução de continuação

A instrução next reinicia o n-ésimo ciclo mais interior em que a instrução se encontrar (quando o argumento é omitido, assume-se n=1), tal como a instrução continue em C. Esta instrução só pode existir dentro de um ciclo, sendo a última instrução do seu bloco.

Instrução de retorno

A instrução return, se existir, é a última instrução do seu bloco. Ver comportamento na descrição do corpo de uma função.

Expressões como instruções

As expressões utilizadas como instruções são sempre avaliadas, mesmo que não produzam efeitos secundários.

Instruções de impressão

As notações ! e !! podem ser utilizadas para apresentar valores na saída do programa. A primeira forma apresenta os valores sem mudar de linha; a segunda forma apresenta os valores mudando de linha depois de os apresentar a todos. Quando existe mais de uma expressão, as várias expressões são apresentadas sem separação. Valores numéricos (inteiros ou reais) são impressos em decimal. As cadeias de caracteres são impressas na codificação nativa. Ponteiros não podem ser impressos.

Expressões

Uma expressão é uma representação algébrica de uma quantidade: todas as expressões têm um tipo e devolvem um valor.

Existem expressões primitivas e expressões que resultam da avaliação de operadores.

A tabela seguinte apresenta as precedências relativas dos operadores: é a mesma para operadores na mesma linha, sendo as linhas seguintes de menor prioridade que as anteriores. A maioria dos operadores segue a semântica da linguagem C (excepto onde explicitamente indicado). Tal como em C, os valores lógicos são 0 (zero) (valor falso), e diferente de zero (valor verdadeiro).

Tipo de Expressão Operadores Associatividade Operandos Semântica
primária ( ) [ ] não associativos - parênteses curvos, indexação, reserva de memória
unária + - ? não associativos - identidade e simétrico, indicação de posição
multiplicativa * / % da esquerda para a direita inteiros, reais C (% é apenas para inteiros)
aditiva + - da esquerda para a direita inteiros, reais, ponteiros C: se envolverem ponteiros, calculam: (i) deslocamentos, i.e., um dos operandos deve ser do tipo ponteiro e o outro do tipo inteiro; (ii) diferenças de ponteiros, i.e., apenas quando se aplica o operador - a dois ponteiros do mesmo tipo (o resultado é o número de objectos do tipo apontado entre eles). Se a memória não for contígua, o resultado é indefinido.
comparativa < > <= >= da esquerda para a direita inteiros, reais C
igualdade == != da esquerda para a direita inteiros, reais, ponteiros C
"não" lógico ~ não associativo inteiros C
"e" lógico && da esquerda para a direita inteiros C: o 2º argumento só é avaliado se o 1º não for falso.
"ou" lógico || da esquerda para a direita inteiros C: o 2º argumento só é avaliado se o 1º não for verdadeiro.
atribuição = da direita para a esquerda todos os tipos O valor da expressão do lado direito do operador é guardado na posição indicada pelo left-value (operando esquerdo do operador). Podem ser atribuídos valores inteiros a left-values reais (conversão automática). Nos outros casos, ambos os tipos têm de concordar. O literal null é compatível com todos os tipos de ponteiros.

Expressões primitivas

As expressões literais e a invocação de funções foram definidas acima.

Identificadores

Um identificador é uma expressão se tiver sido declarado. Um identificador pode denotar uma variável.

Um identificador é o caso mais simples de um left-value, ou seja, uma entidade que pode ser utilizada no lado esquerdo (left) de uma atribuição.

Leitura

A operação de leitura de um valor inteiro ou real pode ser efectuado pela expressão indicada pela palavra-chave input, que devolve o valor lido, de acordo com o tipo esperado (inteiro ou real). Caso se use como argumento dos operadores de impressão ou noutras situações que permitam vários tipos (e.g. ! ou !!), deve ser lido um inteiro.

Exemplos: a = input (leitura para a), f(input) (leitura para argumento de função), input!! (leitura e impressão), @(input) (chamada recursiva a função com argumento que é lido da entrada).

Parênteses curvos

Uma expressão entre parênteses curvos tem o valor da expressão sem os parênteses e permite alterar a prioridade dos operadores. Uma expressão entre parênteses não pode ser utilizada como left-value (ver também a expressão de indexação).

Funções

As funções (ponteiros ou o seu código: não confundir com chamadas a funções) podem ser usadas como expressões tipificadas como funções, i.e., ponteiros para funções (mesmo quando não se usa explicitamente um ponteiro).

Exemplo:

auto f = (int i) -> int { return i + 1; };
auto g = (int n, int<int> fun) -> int { return fun(n); };

begin
  g(3, f)!!  // escreve 4
  g(3, (int i) -> int { return i * 2; })!!  // escreve 6
end

Expressões resultantes de avaliação de operadores

Indexação de ponteiros

A indexação de ponteiros devolve o valor de uma posição de memória indicada por um ponteiro. Consiste em uma expressão ponteiro seguida do índice entre parênteses rectos. O resultado de uma indexação de ponteiros é um left-value. Não é possível indexar ponteiros que designem funções.

Exemplo (acesso à posição 0 da zona de memória indicada por p): p[0]

Identidade e simétrico

Os operadores identidade (+) e simétrico (-) aplicam-se a inteiros e reais. Têm o mesmo significado que em C.

Reserva de memória

A expressão reserva de memória devolve o ponteiro que aponta para a zona de memória, na pilha da função actual, contendo espaço suficiente para o número de objectos indicados pelo seu argumento inteiro.

Exemplo (reserva vector com 5 reais, apontados por p): [double] p = [5]

Expressão de indicação de posição

O operador sufixo ? aplica-se a left-values, retornando o endereço (com o tipo ponteiro) correspondente.

Exemplo (indica o endereço de a): a?

Expressão de dimensão

O operador sizeof tem um único argumento e aplica-se a expressões, retornando a dimensão correspondente em bytes.

Exemplo: sizeof(a) (dimensão de a).

Exemplos e Testes

Os exemplos abaixo não são exaustivos e não ilustram todos os aspectos da linguagem.

Estão ainda disponíveis outros pacotes de testes.

O seguinte exemplo ilustra um programa com dois módulos: um que define a função factorial e outro que define a função principal.

Definição da função factorial no ficheiro factorial.p6:

public factorial = (int n) -> int {
  if (n > 1) 
    return n * @(n - 1);  // could also use 'factorial' (buggy approach!)
  else 
    return 1;
};

Exemplo da utilização da função factorial no ficheiro main.p6:

// external builtin functions (non-P6)
direct int<> argc;
direct string<int> argv;
direct int<string> atoi;

// external user functions (MML)
forward int<int> factorial;

// the main function
begin
  auto value = 1;
  "Teste para a função factorial."!!
  if (argc() == 2) {
    string s = argv(1);
    value = atoi(s);
  }
  value, "! é ", factorial(value)!!
  return 0;
end

Como compilar:

p6 --target asm factorial.p6
p6 --target asm main.p6
yasm -felf32 factorial.asm
yasm -felf32 main.asm
ld -melf_i386 -o main factorial.o main.o -lrts

Omissões e Erros

Casos omissos e erros serão corrigidos em futuras versões do manual de referência.