Nenhuma tabela de símbolos no Go?
-
19-09-2019 - |
Pergunta
A nova linguagem do Google, "Go", diz em seu site:
a linguagem foi projetada para ser fácil de analisar e pode ser analisada sem uma tabela de símbolos
Certamente não sou especialista nesses assuntos, mas pensei que uma tabela de símbolos fosse uma construção básica comum a todos os compiladores de linguagens que usam variáveis, e Go claramente usa variáveis.O que não estou entendendo?
Solução
Analisar significa apenas descobrir a estrutura do programa:separar o módulo em instruções/declarações, dividir expressões em subexpressões, etc.Você acaba com uma estrutura em árvore, conhecida como "árvore de análise" ou "árvore de sintaxe abstrata" (AST).
Aparentemente, C++ requer uma tabela de símbolos para fazer a análise.
Esta página discute alguns motivos por que C++ requer uma tabela de símbolos para análise.
É claro que a análise é apenas uma parte da compilação e você precisará de uma tabela de símbolos para fazer uma compilação completa.
No entanto, a análise em si pode ser útil na escrita de ferramentas de análise (por exemplo,qual módulo importa quais módulos).Portanto, simplificar o processo de análise significa que é mais fácil escrever ferramentas de análise de código.
Outras dicas
A interpretação e a compilação requerem absolutamente tabelas de símbolos ou similares.Isso é verdade para quase todos os idiomas.
Em C e C++, mesmo análise a linguagem requer uma tabela de símbolos.
@Justiça está certa.Para expandir um pouco isso, em C a única parte realmente complicada é diferenciar os tipos das variáveis.Especificamente quando você vê isto:
T t;
Você precisa saber disso T
é um tipo para que seja uma análise legal.Isso é algo que você deve procurar em uma tabela de símbolos.Isso é relativamente simples de descobrir, desde que os tipos sejam adicionados à tabela de símbolos à medida que a análise continua.Você não precisa fazer muito trabalho extra no compilador:qualquer T
está presente na tabela ou não.
Em C++ as coisas são muitas, muito mais complicado.Há um enorme número de construções ambíguas ou potencialmente ambíguas.O mais óbvio é este:
B::C (c);
Além do fato de que não está claro se B
é um class
, a typedef
, ou um namespace
, também não está claro se C
é um tipo e c
um objeto desse tipo, ou se C
é uma função (ou construtor) que toma c
como argumento (ou mesmo se C for um objeto com operator()
sobrecarregado).Você precisa da tabela de símbolos para continuar a análise, embora ainda seja possível continuar com rapidez suficiente, pois o tipo do símbolo está na tabela de símbolos.
As coisas ficam muito, muito, muito piores do que isso quando os modelos entram na mistura.Se C (c)
está em um modelo, talvez você não saiba na definição real do modelo se C é um tipo ou uma função/objeto.Isso porque o modelo pode declarar C
ser um tipo ou uma variável.O que isto significa é que você precisa da tabela de símbolos, mas não ter um - e você não pode tenha um até que o modelo seja realmente declarado.Pior ainda, não é necessariamente suficiente ter apenas o tipo do símbolo:você pode criar situações que exijam informações completas do tipo que o símbolo representa, incluindo tamanho, alinhamento e outras informações específicas da máquina.
Tudo isto tem vários efeitos práticos.Os dois mais significativos que eu diria são:
- A compilação é muito mais rápida.Presumo que Go seja mais rápido de compilar que C, e C++ tem tempos de compilação notoriamente lentos para situações que envolvem muitos modelos.
- Você pode escrever analisadores que não dependam de um compilador completo.Isso é muito útil para fazer análise de código e refatoração.
Para analisar a maioria das linguagens você precisa saber quando os nomes são variáveis, tipos ou funções para desambiguar certas construções.Go não tem construções tão ambíguas.
Por exemplo:
int x = Foo(barra);
Foo pode ser um tipo ou uma função e são representados por diferentes tipos AST.Basicamente, o analisador nunca precisa pesquisar símbolos para saber como construir o AST.A gramática e o AST são mais simples que a maioria dos idiomas.Muito legal, realmente.
As tabelas de símbolos são lentas e geralmente desnecessárias.Então escolha ir embora com isso.Outras linguagens funcionais também não precisam de nenhuma.A pesquisa rápida requer um hash, mas para oferecer suporte a escopos aninhados, você precisa enviar/colocar nomes em uma pilha.Symtabs simples são implementados como pilha linear pesquisada, melhores symtabs como hash com uma pilha por símbolo.Mesmo assim, a pesquisa deve ser feita em tempo de execução.
A interpretação e compilação para linguagens com escopo lexical não requerem absolutamente nenhuma tabela de símbolos ou similar.Somente símbolos com escopo dinamicamente precisam de tabelas de símbolos, e alguns compiladores com idiomas estritamente digitados precisam de algum tipo de tabela de símbolos internos para manter as anotações do tipo.
Em C e C++, até mesmo a análise da linguagem requer uma tabela de símbolos, porque você precisa armazenar os tipos e declarações de globais e funções.
Símbolos com escopo lexical não são armazenados em symtab, mas como listas indexadas de nomes em quadros de bloco, como em linguagens funcionais.Esses índices são calculados em tempo de compilação.Portanto, o acesso em tempo de execução é imediato.Sair do escopo torna esses vars inacessíveis automaticamente, então você não precisa enviar/retirar nomes de namespaces/symtabs.
Linguagens não tão funcionais sem funções de primeira classe geralmente precisam armazenar seus nomes de funções em tabelas de símbolos.Como designer de linguagem, você tenta vincular funções a léxicos, para poder se livrar da pesquisa dinâmica de nomes em symtabs.