Skip to content

Escopos

Leonel Sanches da Silva edited this page Aug 13, 2025 · 10 revisions

Escopos são momentos na execução de um programa em que uma porção do código é isolada do restante do programa, e certos elementos existem ou não, tais como variáveis, funções, objetos, etc. Escopos são definidos por blocos de escopo. Um bloco de escopo começa com uma chave esquerda ({) e termina com uma chave direita (}).

Blocos de escopo normalmente são usados para definir o funcionamento de funções, métodos e classes, mas Delégua suporta blocos de escopo órfãos, como no exemplo abaixo:

{ // Isso é um bloco de escopo órfão, ou seja, não pertence a qualquer estrutura da linguagem, 
  // como uma classe, uma função ou um método
  var a = "1";
}

escreva(a); // sinaliza erro, porque `a` foi declarada dentro de um bloco de escopo que já finalizou sua execução.

se a = "1" { // Este bloco de escopo pertence à instrução `se`, e é executado quando a é igual ao literal "1", um texto.
  escreva("A variável `a` tem o valor '1'.")
}

Se a variável não for um objeto de classe, ou se não tiver um método finalizar() declarado, a variável é simplesmente descartada com o restante do escopo.

Toda execução de Delégua, seja pelo modo LAIR, seja pela execução de um programa, possui um escopo-padrão, também chamado de escopo global, implicitamente declarado (ou seja, ele existe sem precisar de qualquer declaração). É nele que ficam variáveis e funções globais.

Variáveis declaradas no escopo global podem ser acessadas por um escopo mais interno, mas o contrário não é possível, como no exemplo anterior do escopo órfão. O exemplo a seguir demonstra como é possível acessar uma variável definida no escopo global por um bloco de escopo órfão.

var a = "1";
{
  escreva(a); // escreve 1, pois um escopo mais restritivo tem acesso ao escopo ao qual foi declarado.
}

O escopo interno pode alterar variáveis de escopos ancestrais sem necessidade de nova declaração.

var a = "1";
{
  a = "2";
}

escreva(a); // escreve 2

No entanto, se uma nova variável é declarada dentro de um escopo filho com o mesmo nome de outra variável num escopo pai, a variável declarada no escopo pai não pode mais ser acessada pelo nome no escopo filho.

var a = "1";
{
  var a = "2";
  escreva(a); // escreve 2
}

escreva(a); // escreve 1

A funcionalidade de escopos é implementada por uma pilha, comumente chamada de pilha de escopos. Cada elemento da pilha de escopos armazena:

  • As declarações daquele escopo;
  • As variáveis e constantes declaradas naquele escopo;
  • A declaração atual sendo executada;
  • As referências a estruturas de dados mais complexas, que existem no montão (ver mais abaixo).

Referências e estrutura montão

Determinados tipos são armazenados no escopo com seus respectivos valores, como números, textos e valores lógicos. Já tipos mais complexos, como dicionários, não são armazenados no escopo, mas sim em uma estrutura controlada pelo interpretador chamada montão.

Diferentemente da pilha de escopos, em que todas as estruturas de dados estão ordenadas e seguem a cadência da execução do programa, o montão contém uma série de objetos e seus endereços. A pilha de escopos guarda, para determinadas variáveis e constantes, apenas uma referência ao montão, com o respectivo endereço onde a estrutura de dados vive no montão. Quando necessário, o valor é recuperado, seja para ser lido ou modificado. Quando um elemento da pilha de escopos é descartado, também são descartadas todas as estruturas de dados no montão com endereços registrados no elemento de escopo descartado da pilha.

Para ilustrar a necessidade de tal estrutura, suponha o seguinte código:

var meuDicionario = {
    "um": "dois",
    "tres": {
        "quatro": 5
    }
}
var meuSegundoDicionario = meuDicionario['tres']
meuSegundoDicionario['quatro'] = 7
escreva(meuDicionario['tres'])

Antes da versão 0.47.0, a primeira versão de Delégua que implementa o montão, a última linha imprimiria {'quatro': 5} na saída padrão. Isto porque toda passagem de dados para variáveis era por valor. Já a partir da versão 0.47.0 de Delégua, o valor impresso para escreva(meuDicionario['tres']) será {'quatro': 7}, assim como escreva(meuSegundoDicionario).

Variáveis controladas por escopos e a declaração tendo ... como

A partir da versão 0.33.0, Delégua possui a declaração tendo ... como, que cuida do ciclo de vida da variável até o final do seu bloco de escopo. Ao finalizá-lo, se a variável é um objeto e possui um método chamado finalizar(), o método é chamado para executar lógicas adicionais necessárias ao descarte da variável. Por exemplo, se a variável é uma classe que manipula arquivos, pode ser interessante descartar buffers, fechar ponteiros de fluxo e descartar porções de memória que não serão mais utilizadas.

O exemplo a seguir ocorre ao utilizarmos funções da biblioteca delegua-arquivos:

const arquivos = importar('arquivos')
tendo arquivos.abrir('meu-arquivo.txt') como a {
    a.escrever('123')
} // Ao executar o fechamento de escopo, o arquivo é fechado para escrita e a variável `a` descartada.

Se a variável não é um objeto de classe, ou se não possui um método finalizar() implementado, a variável é simplesmente descartada juntamente com o escopo.

Clone this wiki locally