Especificação Sintática¶
Programa¶
Um programa consiste de uma sequência não vazia de declarações de variáveis e subprogramas.
programa ::= dec {dec}
Variáveis¶
Existem dois tipos de variáveis: as simples e as agregadas. Variáveis simples suportam apenas um único valor de um determinado tipo primitivo em um determinado momento. Variáveis agregadas são de tipos agregados (arranjos) e suportam mais de um valor de um mesmo tipo em um determinado momento.
Declaração de Variáveis¶
decVar ::= 'var' listaSpecVars ':' tipo ';'
listaSpecVars ::= specVar {',' specVar}
specVar ::= specVarSimples | specVarSimplesIni |
specVarArranjo | specVarArranjoIni
Exemplo:
var a, b = 3, c = 2 + b: int;
var str1, str2 = "String 2": string;
var i, j = true: bool;
var x, v[10], z[3] = {1, 5, 8}: int;
Observe que a declaração de variáveis é indicada pela palavra reservada var. Observe também que múltiplas variáveis podem ser declaradas de uma vez e que elas podem ser inicializadas durante a declaração.
Na declaração de arranjos, o tamanho da estrutura deve ser especificada como um literal numérico.
O tipo string¶
Dados do tipo string não são indexados como na maioria das linguagens. O objetivo da existência do tipo string na linguagem é apenas fornecer uma maneira de apresentar (escrever) mensagens na tela.
No momento da declaração de uma variável do tipo string, é possível indicar a quantidade de memória que será reservada a ela. Por exemplo, nas declarações
var palavra: string[32]; // memória reservada para 32 caracteres.
var texto: string; // memória reservada para 256 caracteres.
var nome = "Fulano": string; // memória reservada para 6 caracteres.
var titulo = "Meu programa": string[64]; // memória reservada para 64 caracteres.
a variável palavra foi declarada como uma string capaz de armazenar 32 caracteres. Caso não seja informado o tamanho da string, como na declaração da variável texto, serão reservados 256 caracteres. Quando a variável é inicializada na declaração, será alocado o maior espaço entre o suficiente para a string ou o especificado na declaração.
Subprogramas (procedimentos e funções)¶
A definição de procedimentos e funções possui uma sintaxe comum, exceto pela ausência do tipo de retorno para procedimentos. Não há separação entre declaração e definição de subprogramas, isto é, o subprograma deve ser definido durante sua própria declaração.
Declaração de Subprogramas¶
decSub ::= decProc | decFunc
Declaração de Procedimento¶
decProc ::= 'def' id '(' [listaParâmetros] ')' bloco
Exemplo
def proc(y: int) {
if (y < 0) {
return;
}
x = 2 * y; // x é global!
}
Declaração de Função¶
decFunc ::= 'def' id '(' [listaParâmetros] ')' ':' tipo bloco
Exemplo
def func(x[], y: int; z: bool): int {
a = x[y-1]: int;
return a + 1;
}
Lista de Parâmetros¶
listaParâmetros ::= specParams {';' specParams}
specParams ::= param {',' param} ':' tipo
param ::= id | id '[' ']'
Parâmetros de tipo inteiro ou lógico são passados naturalmente por cópia e parâmetros de tipo arranjo ou string são passados naturalmente por referência.
Declaração Aninhada de Subprogramas¶
A sintaxe de declaração de subprogramas permite que eles sejam declarados de maneira aninhada, como exemplificado a seguir:
def adicionar(v[]: int; n: int; x: int) {
var i: int;
def soma(a: int): int {
return a + x;
}
for (i=0; i<n; i+=1) {
v[i] = soma(v[i]);
}
}
Observe o funcionamento do escopo local permitindo que o parâmetro x do procedimento adicionar seja utilizado dentro da função soma.
Comandos¶
Existem duas classes de comandos: os comandos simples e os blocos de comando.
comando ::= cmdSimples | bloco
A seguir são especificados os comandos simples:
Atribuição¶
cmdAtrib ::= atrib ';'
atrib ::= variável ('='|'+='|'-='|'*='|'/='|'%=') expressão
O comando de atribuição avalia o valor da expressão e o armazena na variável.
As atribuições compostas devem ser traduzidas da seguinte maneira:
var X= expressão -> var = var X expressão
Condicional If¶
cmdIf ::= 'if' '(' expressão ')' comando ['else' comando]
A estrutura condicional if é executada verificando o resultado da expressão de teste. Se ela resultar no valor true, apenas o primeiro comando será executado. Se a expressão resultar no valor false, caso a estrutura else esteja presente, apenas o segundo comando será executado.
Laço While¶
cmdWhile ::= 'while' '(' expressão ')' comando
O laço while inicia verificando o resultado da expressão de teste. Caso o valor seja true, o comando do seu corpo é executado e o laço volta a testar o valor da expressão de teste para a próxima iteração. Caso o valor seja false, a execução do laço é interrompida.
Laço For¶
cmdFor ::= 'for' '(' atrib-ini ';' expressão ';' atrib-passo ')' comando
O laço for inicia executando a atribuição de inicialização. A partir daí, antes de cada iteração, o resultado da expressão de teste é verificado. Se ele for true, o comando corpo é executado e a atribuição de passo é executada em seguida, reiniciando o processo. Se antes de qualquer iteração o valor resultado pela expressão de teste for false, a execução do laço é interrompida.
Interrupção do laço¶
cmdStop ::= 'stop' ';'
O comando stop interrompe o laço mais próximo que o cerca. Ele só pode aparecer dentro do corpo de comandos de repetição while e for.
Salto de iteração do laço¶
cmdSkip ::= 'skip' ';'
O comando skip salta para a próxima iteração do laço mais próximo que o cerca, ignorando a execução dos comandos que o seguem dentro deste laço. Ele só pode aparecer dentro do corpo de comandos de repetição while e for.
Retorno de subprograma¶
cmdReturn ::= 'return' [expressão] ';'
O comando return encerra a execução do subprograma que o cerca retornando o valor resultado pela expressão. A expressão de retorno de uma função deve resultar em um valor do mesmo tipo para o qual a função foi definida. Funções devem obrigatoriamente conter pelo menos um comando return. Já procedimentos podem ou não conter comandos return. Caso o tenham, eles devem retornar nada: return; Como o programa principal é definido por meio de uma função, ele deve conter pelo menos um comando return e o valor retornado deve ser um número inteiro.
Chamada de procedimento¶
cmdChamadaProc ::= id '(' [expressão {',' expressão}] ')' ';'
Como a chamada de procedimentos não resulta em um valor, é necessário um comando para sua execução. A chamada de funções possui sintaxe semelhante, exceto por não ser um comando, e sim uma expressão.
Entrada Read¶
cmdRead ::= 'read' variável ';'
Saída Write¶
cmdWrite ::= 'write' expressão {',' expressão} ';'
Bloco de Comandos¶
Um bloco é uma sequência de (nenhuma ou várias) declarações de subprogramas e variáveis seguida de uma sequência de (nenhum ou vários) comandos. Um bloco é circundado por chaves { }.
bloco ::= '{' {dec} {comando} '}'
Expressão¶
Uma expressão pode conter valores dos três tipos definidos (inteiros, lógicos e strings), uso de variáveis, chamadas de função e outras expressões. Uma expressão pode estar cercada por parênteses e se relacionar a outras expressões por meio dos seguintes operadores:
| Precedência | Operador | Descrição | Associatividade |
|---|---|---|---|
| 1 | - |
Negativo Unário | À direita |
| 2 | *, /, % |
Multiplicação, divisão e resto | À esquerda |
| 3 | +, - |
Adição e subtração | |
| 4 | <, <= |
Operadores relacionais < e ≤ respectivamente |
|
>, >= |
Operadores relacionais > e ≥ respectivamente |
||
| 5 | ==, != |
Operadores relacionais = e ≠ respectivamente |
|
| 6 | && |
E lógico | |
| 7 | || |
OU lógico | |
| 8 | ? : |
Condicional ternário | À direita |
O operador condicional ternário é formado da seguinte maneira:
opTern ::= expressão-teste '?' expressão-então ':' expressão-senão
A expressão teste é avaliada. Se o resultado for true, a expressão-então é resultada, caso contrário, a expressão-senão é resultada. Dessa forma, o resultado desse operador é sempre uma expressão. O operador pode ser utilizado assim:
x = a > 0 ? a * 2 : a + 1;
O operador condicional ternário terá associatividade à direita, ilustrado no exemplo abaixo, onde a expressão b > 0 ? a / b : a + b é uma expressão-senão, como em C e C++, ao invés de tratar a expressão a > 0 ? a * 2 : b > 0 como expressão-teste, como em PHP.
x = a > 0 ? a * 2 : b > 0 ? a / b : a + b;
x = a > 0 ? a * 2 : (b > 0 ? a / b : a + b); // Associação à Direita (C, C++)
x = (a > 0 ? a * 2 : b > 0) ? a / b : a + b; // Associação à Esquerda (PHP)
Uso de variável¶
Como o uso de uma variável resulta no valor armazenado pela variável, todo uso de variável é uma expressão. Variáveis simples são usadas por meio do identificador (nome) associado a ela e variáveis compostas (arranjo) são usadas por meio do identificador e a posição numérica do elemento acessado.
variável ::= id | id '[' expressão ']'
Observe que a sintaxe do uso de variável não impede que uma variável simples seja utilizada como arranjo. Essa associação deve ser verificada na etapa de análise semântica.