Compiladores (LEIC-A – 2o Semestre 2010/2011)
Linguagem “at” – Manual de Referência
10 de Maio de 2011
A nota do projecto (40% da nota final) deve ser superior a 9,5 (sem arredondamentos). Fraudes na execução do projecto terão
como resultado a classificação de 0 (zero) valores e reprovação à disciplina.
Datas: 2011/04/15 12:00 (entrega intermédia); 2011/05/25 12:00 (entrega final); 2011/05/30–2011/06/03 (teste prático).
Dúvidas: [email protected] ou presencialmente (consultar horários e locais no Fénix).
Advertem-se os alunos contra a utilização de fontes de informação não oficialmente associadas ao corpo docente.
A linguagem “at” é uma linguagem imperativa e fortemente tipificada e é apresentada de forma intuitiva neste manual. São apresentadas caracterı́sticas básicas da linguagem (§1); convenções lexicais (§2); estrutura/sintaxe da linguagem (§3); especificação
das funções (§4); semântica das instruções (§5); semântica das expressões (§6); e, finalmente, alguns exemplos (§7).
1
Caracterı́sticas Básicas
1.1
Tipos de dados
Existem 4 tipos de dados (§3): números inteiros (int) e reais (real), cadeias de caracteres (string) e ponteiros (<tipo>).
• Ocupam 4 bytes em complemento para dois, alinhados a 32 bits. Exemplo: int i = 1;
• Ocupam 8 bytes em vı́rgula flutuante (norma IEEE 754). Exemplo: real r = 3.14;
• São terminadas pelo carácter com o valor 0 ASCII (NULL). As variáveis e literais do tipo string podem apenas ser
utilizados em atribuições, em impressões e como argumentos/retornos de funções. Exemplo: string s = "abc";
• Representam endereços de outros objectos (§3), ocupando 4 bytes, alinhados a 32 bits. Podem ser objecto de operações
aritméticas (deslocamentos) e permitem aceder ao valor apontado. Exemplo: <int>pi = 0;
As operações dependem dos tipos de dados a que são aplicadas. Os tipos suportados por cada operador e a operação a realizar
são indicados na definição das expressões (§6).
1.2
Manipulação de nomes
As entidades nomeadas (§2.6) são constantes, variáveis e funções. Nos pontos que se seguem, usa-se o termo entidade para as
designar indiscriminadamente, usando-se listas explı́citas quando a descrição for válida apenas para um subconjunto.
1.2.1
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 função (locais). A reutilização
de um identificador num contexto inferior encobre possı́veis declarações em contextos superiores (excepto como definido no
próximo parágrafo). Em particular, a redeclaração de um identificador numa função encobre o global até ao fim da função. É
possı́vel importar sı́mbolos globais nos contextos das funções (visibilidade restringida), mas não é possı́vel defini-los (§3.4.2).
É possı́vel definir funções dentro de funções. As funções internas têm acesso às variáveis das funções que as contêm. Se houver
colisão de nomes, é possı́vel utilizar a notação @.var, para ver var no nı́vel superior, ou @[email protected], para ver var dois nı́veis
acima, etc. (var representa qualquer variável da função contentora – os prefixos podem omitir-se se não houver ambiguidade).
1
1.2.2
Validade das variáveis
As entidades globais (declaradas fora de qualquer função), existem durante toda a execução do programa. As contantes e 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.
2
Convenções Lexicais
Para cada um dos seis grupos de elementos lexicais (tokens), considera-se a maior sequência de caracteres que constitua um
elemento lexical válido.
2.1
Caracteres brancos
São considerados caracteres brancos aqueles que, embora servindo para separar os elementos lexicais, não representam nenhum
elemento lexical. São considerados caracteres brancos: espaço ASCII SP (0x20, ou ), mudança de linha ASCII LF (0x0A,
ou \n), recuo do carreto ASCII CR (0x0D, \r) e tabulação horizontal ASCII HT (0x9, ou \t).
2.2
Comentários
Os comentários funcionam como separadores de elementos lexicais. Existem dois tipos de comentários.
Explicativos – Começam com // (desde que a sequência não faça parte de uma cadeia de caracteres), e acabam no fim da linha.
Operacionais – Começam com /* e terminam com */ (se não fizerem parte de cadeias de caracteres). Podem estar aninhados.
2.3
Palavras chave
As seguintes palavras são reservadas e não constituindo identificadores (devem ser escritas exactamente como indicado):
const void int real string public use next stop return
2.4
Operadores de expressões
São considerados operadores (§6) os seguintes elementos lexicais:
-
2.5
+
#
*
/
%
ˆ
=
<
>
==
>=
<=
!=
||
&&
˜ [ ] ?
Delimitadores e terminadores
Os elementos lexicais seguintes são considerados delimitadores/terminadores:
, ; ! !! { } :
2.6
Identificadores (nomes)
São iniciados por uma letra (maiúscula ou minúscula) ou por _ (carácter sublinhado). O primeiro carácter é seguido por 0
(zero) ou mais letras, dı́gitos ou _. O número de caracteres que constituem um identificador é ilimitado e dois nomes designam
identificadores distintos se houver alteração de maiúscula para minúscula, ou vice-versa, de pelo menos um carácter.
2.7
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 sofrer alterações durante a execução do programa).
2
2.7.1
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 menos unário (-) a um literal positivo).
Um literal inteiro em decimal é constituı́do por uma sequência de 1 (um) ou mais dı́gitos decimais (dı́gitos de 0 a 9) em que o
primeiro dı́gito não é um 0 (zero), excepto no caso do número 0 (zero). Neste caso, é composto apenas pelo dı́gito único 0 (zero)
(em qualquer base de numeração).
Um literal inteiro em octal começa sempre pelo dı́gito 0 (zero), sendo seguido de um ou mais dı́gitos de 0 a 7. Um literal inteiro
em hexadecimal começa sempre pela sequência 0x, sendo seguido de um ou mais digitos de 0 a 9, de a a f ou de A a F. As
letras de a a f, ou de A a F, representam os valores de 10 a 15 respectivamente. Um literal inteiro em binário começa sempre
pela sequência 0b, sendo seguido de um ou mais digitos 0 ou 1. Exemplos: 007, 0x07, 0b0111.
Se não for possı́vel representar o literal inteiro na máquina, devido a um overflow, deverá ser gerado um erro lexical.
2.7.2
Reais
Os literais reais são expressos em notação cientı́fica (tal como em C) ou em notação de engenharia, onde a parte exponencial
não existe e o ponto decimal é substituı́do por uma das letras y (yocto/iocto = 10−24 = 1E-24), z (zepto = 10−21 = 1E-21), a
(atto/ato = 10−18 = 1E-18), f (femto/fento = 10−15 = 1E-15), p (pico = 10−12 = 1E-12), n (nano = 10−9 = 1E-9), u (micro
= 10−6 = 1E-6), m (milli/mili = 10−3 = 1E-3), k (kilo/quilo = 103 = 1E+3), M (mega = 106 = 1E+6), G (giga = 109 = 1E+9), T
(tera = 1012 = 1E+12), P (peta = 1015 = 1E+15), E (exa = 1018 = 1E+18), Z (zetta/zeta = 1021 = 1E+21), Y (yotta/iota = 1024
= 1E+24). Quando a notação for ambı́gua, é preferida a interpretação segundo a notação de engenharia.
Exemplo: 12y34 = 12.34e-24 = 12.34 × 10−24
2.7.3
Cadeias de caracteres
São consituı́das por dois ou mais caracteres iniciadores seguidos, sem separadores, mas constituindo elementos lexicais distintos;
ou uma só cadeia de texto (opcionalmente, podem existir caracteres brancos entre os elementos lexicais que compõem a cadeia).
Os caracteres iniciadores podem ser valores inteiros (em qualquer base e que representem um só carácter), caracteres individuais
ou cadeias de texto. Os caracteres individuais são delimitados com o carácter plica (’) e contêm um só carácter ou uma sequência
especial iniciada por (\). Sequências especiais podem ser representada pelos caracteres ASCII LF, CR e HT (\n, \r e \t,
respectivamente), plica (\’), barra (\\), ou 1 ou 2 digitos hexadecimais (e.g. \0a ou apenas \A se o carácter seguinte não
representar um digito hexadecimal).
Uma cadeia de texto, começa e termina com o carácter aspa ("), pode conter qualquer número de caracteres (excepto o 0 ou
NULL). Dentro das cadeias de texto, os caracteres utilizados para iniciar ou terminar comentários têm o seu valor normal ASCII,
não iniciando ou terminando qualquer comentário. As sequências especiais dos caracteres individuais são válidas nas cadeias de
texto, excepto a sequência \’ que é substituı́da pela sequência \".
2.7.4
Ponteiros
O único literal admissı́vel para ponteiros é 0 (zero), indicando o ponteiro nulo.
3
Gramática
A gramática da linguagem pode ser resumida pelas regras 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 . A barra vertical
e os parênteses são elementos lexicais da linguagem quando representados em tipo fixo.
3.1
Tipos, elementos lexicais e definição de expressões
A gramática omite as definições dos tipos de dados (§1.1), dos elementos lexicais identificador (§2.6), literal-inteiro (§2.7.1),
literal-real (§2.7.2), carácter (§2.7.3) e cadeia (§2.7.3), assim como a definição de expressões (§6).
3
ficheiro
declaração
variável
variáveis
função
tipo
qualificador
corpo
literal
secção-inicial
secção
secção-final
bloco
instrução
instrução-condicional
instrução-de-iteração
3.2
→
→
→
→
→
→
→
→
→
→
→
→
→
→
→
→
→
→
→
→
→
declaração declaração variável ; | função
[ const ] [ qualificador ] tipo ident [ = expressão ]
variável , variável [ qualificador ] ( tipo | void ) ident ( [ variáveis ] ) [ -> literal ] [ corpo ]
int | real | string | < tipo >
public | use
[ secção-inicial ] secção [ secção-final ]
inteiro | real | carácter | cadeia
<< bloco
[ [ expressão ] ] bloco
( [ expressão ] ) bloco
bloco
>> bloco
{ declaração instrução }
expressão ( ; | ! | !! )
next | stop | return | instrução-condicional | instrução-de-iteração | bloco
[ expressão ] # instrução
[ expressão ] ? instrução [ expressão ] ? instrução [ : instrução ]
[ [ variáveis ] ; [ expressão ] ; [ expressão ] ] instrução
[ [ expressão ] ; [ expressão ] ; [ expressão ] ] instrução
Left value
Os elementos de uma expressão que podem ser utilizados como left-values encontram-se individualmente identificados em §6.
Todos os left-values representam posições de memória passı́veis de ser modificadas.
3.3
Ficheiros
Os ficheiros que contêm os programas são uniformes. É designado por principal o que contiver a função de arranque (at).
3.4
Declaração de variáveis e constantes
Cada declaração permite declarar uma única variável ou constante e inclui os componentes descritos nos pontos seguintes.
3.4.1
Constante
A linguagem define identificadores constantes: precedem a declaração pela palavra reservada const, impedindo que o identificador declarado possa ser utilizado em operações que modifiquem o seu valor. Caso um identificador designe uma constante
inteira não pública (ver §3.4.2) o seu valor deverá ser directamente substituı́do no código, não ocupando espaço.
3.4.2
Sı́mbolos globais
Existem dois qualificadores opcionais, que gerem a utilização de identificadores globais:
Sı́mbolos públicos – public – são globalmente visı́veis no módulo actual e acessı́veis a partir de outros módulos.
Pré-declarações – use – são constantes, variáveis ou funções, que podem estar declaradas mais à frente no ficheiro ou num
outro ficheiro (possivelmente noutra linguagem).
Apenas é possı́vel aplicar use a declarações. public pode também ser aplicada a definições.
3.4.3
Inicialização
A existir, inicia-se com o operador = seguido de expressão do tipo declarado: inteiro (uma expressão inteira), real (uma expressão
real), ponteiro (uma expressão do tipo ponteiro).
4
As cadeia de caracteres são (possivelmente) inicializadas com uma lista não nula de valores sem separadores (opcionalmente,
poderão existir caracteres brancos entre os elementos lexicais que compõem a cadeia de caracteres). Os valores podem ser inteiros (§2.7.3), caracteres individuais ou cadeias de texto. Estes valores são sempre constantes, independentemente de o identificador
que as designa ser constante ou não.
Notar que declarações de constantes não iniciadas só são possı́veis se corresponderem a identificadores pré-declarados, pertencentes a outros módulos (§3.4.2).
4
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.
4.1
Declaração
As funções são sempre designadas por identificadores constantes precedidos do tipo de dados devolvido pela função.
As funções que recebam argumentos devem indicá-los no cabeçalho. Funções sem argumentos definem um cabeçalho vazio.
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.
Quando a declaração de uma função ocorra dentro de outra função, terá de ser necessariamente terminada por ;.
4.2
Invocação
A função só pode ser invocada através de um identificador que refira uma função previamente declarada ou definida. O identificador especial @ pode ser utilizado para invocar recursivamente a função actual.
Caso existam argumentos, na invocação da função, o seu identificador é seguido de uma lista de expressões delimitadas por
parênteses curvos. A lista de expressões é uma sequência, possivelmente vazia, de expressões separadas por vı́rgulas. As
expressões são avaliadas da direita para a esquerda antes da invocação da função (convenção Cdecl) e o valor resultante passado
por cópia (passagem de argumentos por valor).
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. Caso existam
parâmetros opcionais, iniciados na declaração da função, os seus valores são utilizados na falta de parâmetros actuais e apenas
se forem os últimos. A ordem dos parâmetros actuais deverá ser a mesma dos argumentos formais da função a ser invocada. Os
parâmetros actuais devem ser colocados na pilha de dados pela ordem inversa da sua declaração (o primeiro no topo da pilha) e
o endereço de retorno no topo da pilha.
A função chamadora coloca os argumentos na pilha e é também responsável pela sua remoção, após o retorno da função chamada
(convenção Cdecl).
4.3
Corpo
O corpo de uma função consiste num conjunto de secções condicionais. Existem duas secções especiais: a secção inicial (se
existir, é sempre executada e antes de qualquer outra) e a secção final (se existir, é sempre executada e depois de qualquer outra).
As outras secções são executadas se a condição que as rege for verdadeira. As condições entre parênteses rectos correspodem
a secções exclusivas (depois de seleccionada e executada uma delas, apenas a final é executada). As condições entre parênteses
curvos correspondem a secções inclusivas (depois de seleccionada e executada uma delas, é testada a seguinte). Condições vazias
ou ausentes correspondem a secções exclusivas com expressões verdadeiras.
O valor devolvido por uma função, através de atribuição ao left-value especial @, deve ser sempre do tipo declarado. Variáveis
definidas na secção-inicial são visı́veis em toda a função. Variáveis definidas noutras secções são visı́veis apenas nessa secção.
Se existir um valor declarado por omissão para o retorno da função (indicado pela notação -> seguindo a assinatura da função),
então deve ser utilizado se não for especificado outro. A especificação do valor de retorno por omissão é obrigatoriamente um
literal do tipo indicado. É um erro especificar um valor de retorno se o tipo de retorno for void.
5
Uma instrução return numa qualquer secção que não a final, causa a sua interrupção imediata e a execução da secção final (se
existir). Se a instrução return ocorrer na secção final, causa a interrupção da função.
Um sub-bloco de função (usado, por exemplo, numa instrução condicional ou de iteração) pode definir variáveis ou constantes.
As secções do corpo de uma função podem definir outras funções que têm acesso a todas as declarações (prévias ou da secção
inicial) da função que a contém. Caso existam ambiguidades na nomenclatura, o prefixo @. pode ser usado para referir os
identificadores do nı́vel acima (§1.2.1).
5
Instruções
Excepto quando indicado, as instruções são executadas em sequência.
5.1
Instrução condicional
Se a expressão contida ente [ e ] for diferente de 0 (zero) então o corpo que segue o ? ou # é executado.
Caso existam cadeias entre [ e ] consecutivas, as suas expressões serão sucessivamente executadas, caso todas as condições
anteriores sejam nulas (iguais a 0). Assim que uma dessas condições seja diferente de 0 (zero), o seu corpo (depois do ?
seguinte) é executado e a instrução termina. Caso exista um conjunto :, o seu corpo só é executado se nenhum dos anteriores
corpos da instrução o tenha sido.
5.2
Instruções de iteração
Controladas por três grupos de expressões, podendo o primeiro grupo corresponde a declarações de variáveis locais ao ciclo. O
comportamento do ciclo é análogo ao for da linguagem C.
5.3
Instrução de retorno
Indicada pela palavra reservada return, a existir deverá ser a última instrução do bloco em que se insere. Ver §4.3.
5.4
Instrução de continuação
Indicada pela palavra reservada next (quando existe, é a última instrução do seu bloco): 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.
5.5
Instrução de terminação
Indicada pela palavra reservada stop (quando existe, é a última instrução do seu bloco): 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.
5.6
Expressão como instrução
Qualquer expressão pode ser utilizada como instrução, mesmo que não produza qualquer efeito secundário. Caso a expressão
seja terminada por ! ou !! (impressão com mudança de linha) o seu valor deve ser impresso. O valor deve ser impresso em
decimal para os valores numéricos (inteiros ou reais) e na codificação nativa para valores do tipo string. Os valores do tipo
ponteiro não podem ser impressos.
6
Expressões
Uma expressão é uma representação algébrica de uma quantidade, i.e., todas as expressões devolvem um valor. As expressões
são sempre avaliadas da esquerda para a direita, independentemente da associatividade do operador.
6
A precedência dos operadores é a mesma para operadores na mesma secção, sendo as secções seguintes de menor prioridade que
as anteriores. O valor resultante da aplicação da expressão bem como a sua associatividade são indicados para cada operador.
A tabela seguinte que resume os operadores, por grupos de precedência decrescente:
primária
unária
potência
multiplicativa
aditiva
comparativa
igualdade
“não” lógico
“e” lógico
“ou” lógico
atribuição
( ) [ ]
- + # ?
ˆ
* / %
+ < > <= >=
== !=
˜
&&
||
=
não associativos
não associativos
da direita para a esquerda
da esquerda para a direita
da esquerda para a direita
da esquerda para a direita
da esquerda para a direita
não associativo
da esquerda para a direita
da esquerda para a direita
da direita para a esquerda
Os operadores são como em C, excepto os operadores de leitura (@), de potência (ˆ), reserva de memória (#) e negação lógica (˜).
6.1
Expressões primárias
6.1.1
Identificadores
Um identificador é uma expressão se tiver sido declarado. Um identificador pode denotar uma variável ou uma constante.
Um identificador é o caso mais simples de um left-value, ou seja, uma entidade que pode ser utilizada no lado esquerdo (leftvalue) de uma atribuição. O identificador especial @ designa o valor de retorno da função actual (quando usado como left-value).
6.1.2
Literais
Os literais são como definidos nas convenções lexicais (§2.7).
6.1.3
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 §6.1.4).
6.1.4
Indexação
Uma expressão indexação devolve o valor contido numa posição indicada por um ponteiro. Consiste de uma expressão ponteiro
seguida do ı́ndice entre parênteses rectos. Se a expressão ponteiro for um left-value, então a expressão indexação poderá também
ser um left-value. Exemplo: p[0] (acesso à posição apontada por p).
6.1.5
Invocação
Uma função só pode ser invocada através de um identificador que corresponda a uma função previamente declarada ou definida.
O identificador especial @ designa a função actual (para chamadas recursivas).
6.1.6
Leitura
A operação de leitura de um valor inteiro ou real pode ser efectuado pela expressão @, 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 !!), deve ser lido um inteiro.
6.2
Expressões unárias
6.2.1
Identidade e simétrico
A expressão identidade (+) devolve o valor do seu argumento inteiro ou real.
A expressão simétrico (-) devolve o simétrico do seu argumento inteiro ou real.
7
6.2.2
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. O tipo de retorno é idêntico ao do left-value
utilizado e o espaço deve ser calculado em função do tipo apontado.
6.3
Expressão de potência
A operação é apenas aplicável a valores inteiros, devolvendo a multiplicação do primeiro argumento por ele próprio tantas vezes
quantas o valor do segundo argumento.
6.4
Expressões aditivas
As operações são apenas aplicáveis a valores inteiros e reais, devolvendo o resultado da respectiva operação algébrica.
Estas operações podem realizar-se sobre ponteiros, tendo o significado das operações correspondentes em C/C++: (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).
6.5
Expressões multiplicativas
As operações são apenas aplicáveis a valores inteiros e reais, devolvendo o resultado da respectiva operação algébrica.
6.6
Expressões de grandeza
As operações são aplicáveis a valores inteiros ou reais devolvendo o valor inteiro 0 (zero) caso seja falsa e um valor diferente de
zero caso contrário.
6.7
Expressões de igualdade
As operações são aplicáveis a valores inteiros e reais, tal como no caso anterior.
6.8
Expressões de negação lógica
A operação é aplicável a valores inteiros, devolvendo o valor inteiro 0 (zero) caso o argumento seja diferente de 0 (zero) e um
valor diferente de zero caso contrário.
6.9
Expressões de junção lógica
A operação é aplicável a valores inteiros, devolvendo um valor diferente de zero caso ambos os argumentos sejam diferente de 0
(zero) e um valor diferente de zero caso contrário. Se o primeiro argumento for 0 (zero) o segundo argumento não é avaliado.
6.10
Expressões de alternativa lógica
A operação é aplicável a valores inteiros, devolvendo o valor inteiro 0 (zero) caso ambos os argumentos sejam iguais a 0 (zero)
e um valor diferente de zero caso contrário. Se o primeiro argumento for diferente de zero o segundo argumento não é avaliado.
6.11
Expressões de atribuição
O valor da expressão do lado direito do operador é guardado na posição indicada pelo left-value do lado esquerdo do operador
de atribuição. Só podem atribuı́dos valores a left-values do mesmo tipo.
8
6.12
Expressão de indicação de posição
Expressa pelo operador sufixo ?, indica o endereço do objecto (endereçável) indicado como argumento. Retorna um valor do
tipo do ponteiro apropriado para o objecto em causa. Exemplos: a? (indica o endereço de a).
7
Exemplos
Os exemplos apresentados não são exaustivos, pelo que não ilustram todos os aspectos da linguagem.
O cálculo da função de Ackermann: esta função tem um crescimento muito rápido pelo que nos computadores actuais, mesmo
utilizando C, os argumentos não deverão exceder m=3 e n=12 para executar em poucos segundos.
public int cnt = 0;
public int ackermann(int m, int n) {
cnt = cnt + 1;
[m == 0] ? @ = n+1;
[n == 0] ? @ = @(m-1, 1);
: @ = @(m-1, @(m, n-1));
}
Exemplo da utilização das funções noutro ficheiro:
use
use
use
use
int argc()
string argv(int n)
int ackermann(int m, int n)
int getcnt()
public int at() -> 0 {
int m = 0;
int n = 0;
"Teste para a função de Ackermann"!!
"Introduza M (<3): "! m = @;
"Introduza N (<5): "! n = @;
"Valor de ackermann("! m! ", "! n! "): "! ackermann(m, n)!!
"Calls to ackermann: "! getcnt()!!
}
Estão disponı́veis outros exemplos na página da disciplina.
8
Omissões e Erros
Casos omissos e erros serão corrigidos em futuras versões do manual de referência.
9
Download

Linguagem “at” – Manual de Referência