A distinção entre código interpretado e compilado é provavelmente uma ficção, conforme sublinhado pelo comentário de Raphael :
the claim seems to be trivially wrong without further assumptions: if there is
an interpreter, I can always bundle interpreter and code in one executable ...
O fato é que o código é sempre interpretado, por software, por hardware ou uma combinação de ambos, e o processo de compilação não pode dizer qual será.
O que você percebe como compilação é um processo de tradução de um idioma (para origem) para outro idioma (para destino). E, o intérprete de é geralmente diferente do intérprete para .STST
O programa compilado é traduzido de uma forma sintática para outra forma sintática , de modo que, dada a semântica pretendida dos idiomas e , e tenham o mesmo comportamento computacional, até algumas coisas que você geralmente está tentando alterar, possivelmente para otimizar, como complexidade ou eficiência simples (tempo, espaço, superfície, consumo de energia). Estou tentando não falar de equivalência funcional, pois exigiria definições precisas.PSPTSTPSPT
Alguns compiladores foram realmente usados simplesmente para reduzir o tamanho do código, não para "melhorar" a execução. Esse foi o caso da linguagem usada no sistema Platão (embora eles não tenham chamado de compilação).
Você pode considerar o seu código totalmente compilado se, após o processo de compilação, você não precisa mais do intérprete para . Pelo menos, é a única maneira de ler sua pergunta, como uma questão de engenharia, e não teórica (já que, teoricamente, sempre posso reconstruir o intérprete).S
Uma coisa que pode suscitar um problema, após um aumento, é a meta-circularidade . É quando um programa manipula estruturas sintáticas em sua própria linguagem de origem , criando um fragmento de programa que é então interpretado como se tivesse sido parte do programa original. Como você pode produzir fragmentos arbitrários de programas na linguagem como resultado da computação arbitrária manipulando fragmentos sintáticos sem sentido, eu acho que você pode tornar quase impossível (do ponto de vista da engenharia) compilar o programa na linguagem , para que agora gerar fragmentos de . Portanto, o intérprete para será necessário, ou pelo menos o compilador de paraSSTTSST para compilação on-the-fly de fragmentos gerados em (consulte também este documento ).S
Mas não tenho certeza de como isso pode ser formalizado adequadamente (e não tenho tempo agora para isso). E impossível é uma grande palavra para um problema que não está formalizado.
Outras observações
Adicionado após 36 horas. Você pode pular esta sequência muito longa.
Os muitos comentários a essa pergunta mostram duas visões do problema: uma visão teórica que a considera sem sentido e uma visão de engenharia que, infelizmente, não é tão facilmente formalizada.
Existem muitas maneiras de analisar a interpretação e a compilação, e tentarei esboçar algumas. Vou tentar ser o mais informal que conseguir
O diagrama da lápide
Uma das formalizações iniciais (do início da década de 1960 até o final de 1990) são os diagramas T ou
Tombstone . Esses diagramas apresentam em elementos gráficos composíveis a linguagem de implementação do intérprete ou compilador, a linguagem de origem sendo interpretada ou compilada e a linguagem de destino no caso de compiladores. Versões mais elaboradas podem adicionar atributos. Essas representações gráficas podem ser vistas como axiomas, regras de inferência, utilizáveis para derivar mecanicamente a geração de processadores a partir de uma prova de sua existência a partir dos axiomas, à la Curry-Howard (embora não tenha certeza de que isso tenha sido feito nos anos sessenta :).
Avaliação parcial
Outra visão interessante é o paradigma de avaliação parcial . Estou adotando uma visão simples dos programas como um tipo de implementação de função que calcula uma resposta, considerando alguns dados de entrada. Em seguida, um intérprete
para a linguagem é um programa que ter um programa
escritos em e os dados para esse programa, e calcula o resultado de acordo com a semântica de . A avaliação parcial é uma técnica para especializar um programa de dois argumentos e , quando apenas um argumento, digamos , é conhecido. A intenção é ter uma avaliação mais rápida quando você finalmente obtiver o segundo argumentoISSpSSdSa1a2a1a2 . É especialmente útil se mudar com mais frequência que pois o custo da avaliação parcial com pode ser amortizado em todos os cálculos em que apenas está sendo alterado.a2a1a1a2
Essa é uma situação frequente no design de algoritmos (geralmente o tópico do primeiro comentário no SE-CS), quando parte mais estática dos dados é pré-processada, para que o custo do pré-processamento possa ser amortizado em todos os aplicativos do algoritmo com partes mais variáveis dos dados de entrada.
Essa também é a própria situação dos intérpretes, pois o primeiro argumento é o programa a ser executado e geralmente é executado várias vezes com dados diferentes (ou as subpartes são executadas várias vezes com dados diferentes). Portanto, tornou-se uma idéia natural especializar um intérprete para uma avaliação mais rápida de um determinado programa, avaliando-o parcialmente neste programa como primeiro argumento. Isso pode ser visto como uma maneira de compilar o programa, e houve um trabalho de pesquisa significativo sobre a compilação por avaliação parcial de um intérprete em seu primeiro argumento (programa).
O teorema de Smn
O ponto positivo da abordagem da avaliação parcial é que ela tem suas raízes na teoria (embora a teoria possa ser uma mentirosa), principalmente no
teorema Smn de Kleene . Eu estou tentando aqui fazer uma apresentação intuitiva, esperando que isso não perturbe os teóricos puros.
Dada a numeração Gödel de funções recursivas, você pode visualizar como seu hardware, de modo que, dado o número Gödel
( código de objeto de leitura ) de um programa seja a função definida por (isto é, calculada pelo código de objeto em seu hardware).φφpφpp
Na sua forma mais simples, o teorema é declarado na Wikipedia da seguinte forma (até uma pequena alteração na notação):
Dado um número de Gödel de funções recursivas, existe uma função recursiva primitiva de dois argumentos com a seguinte propriedade: para todo número de Gödel de uma função parcial computável com dois argumentos, as expressões e são definidos para as mesmas combinações de números naturais e , e os seus valores são iguais para qualquer tal combinação. Em outras palavras, a seguinte igualdade extensional de funções é válida para todo :
φσqfφσ(q,x)(y)f(x,y)xyxφσ(q,x)≃λy.φq(x,y).
Agora, considerando como intérprete , como código-fonte de um programa e como dados para esse programa, podemos escrever:
qISxpSydφσ(IS,pS)≃λd.φIS(pS,d).
I S SφIS pode ser visto como a execução do intérprete
no hardware, ou seja, como uma caixa-preta pronto para interpretar programas escritos em linguagem .ISS
A função pode ser vista como uma função especializada no intérprete para o programa , como na avaliação parcial. Assim, o número de Gödel pode ser visto como código de objeto que é a versão compilada do programa .I SσIS σ ( I S , p S ) p SPSσ(IS,pS)pS
Portanto, a função pode ser vista como uma função que toma como argumento o código fonte de um programa
escrito na linguagem e retorna a versão do código do objeto para esse programa Então, é o que geralmente é chamado de compilador.q S S C SCS=λqS.σ((IS,qS)qSSCS
Algumas conclusões
No entanto, como eu disse: "a teoria pode ser uma mentirosa", ou na verdade parece ser uma. O problema é que não sabemos nada da função . Na verdade, existem muitas dessas funções, e meu palpite é que a prova do teorema pode usar uma definição muito simples para ele, o que pode não ser melhor, do ponto de vista da engenharia, do que a solução proposta por Raphael: simplesmente agrupar o código fonte com o intérprete . Isso sempre pode ser feito, para que possamos dizer: a compilação é sempre possível.q S I SσqSIS
Formalizar uma noção mais restritiva do que é um compilador exigiria uma abordagem teórica mais sutil. Não sei o que pode ter sido feito nessa direção. O trabalho muito real realizado na avaliação parcial é mais realista do ponto de vista da engenharia. E, claro, existem outras técnicas para escrever compiladores, incluindo extração de programas a partir da prova de suas especificações, conforme desenvolvido no contexto da teoria de tipos, com base no isomorfismo de Curry-Howard (mas estou saindo do meu domínio de competência) .
Meu objetivo aqui foi mostrar que a observação de Raphael não é "louca", mas um lembrete sensato de que as coisas não são óbvias e nem mesmo simples. Dizer que algo é impossível é uma afirmação forte que exige definições precisas e uma prova, apenas para ter uma compreensão precisa de como e por que é impossível . Mas construir uma formalização adequada para expressar essa prova pode ser bastante difícil.
Dito isto, mesmo que um recurso específico não seja compilável, no sentido entendido pelos engenheiros, as técnicas de compilação padrão sempre podem ser aplicadas a partes dos programas que não usam esse recurso, conforme observado pela resposta de Gilles.
Para seguir as principais observações de Gilles, que, dependendo do idioma, algo pode ser feito em tempo de compilação, enquanto outros precisam ser feitos em tempo de execução, exigindo código específico, e podemos ver que o conceito de compilação é realmente mal definido e provavelmente não é definível de maneira satisfatória. A compilação é apenas um processo de otimização, como tentei mostrar na seção de avaliação parcial , quando a comparei com o pré-processamento de dados estáticos em alguns algoritmos.
Como um processo de otimização complexo, o conceito de compilação na verdade pertence a um continuum. Dependendo da característica do idioma ou do programa, algumas informações podem estar disponíveis estaticamente e permitir uma melhor otimização. Outras coisas precisam ser adiadas para o tempo de execução. Quando as coisas ficam realmente ruins, tudo deve ser feito em tempo de execução, pelo menos em algumas partes do programa, e agrupar o código-fonte com o intérprete é tudo o que você pode fazer. Portanto, esse pacote é apenas o ponto mais baixo deste continuum de compilação. Grande parte da pesquisa sobre compiladores é sobre como encontrar maneiras de fazer estaticamente o que costumava ser feito dinamicamente. A coleta de lixo em tempo de compilação parece um bom exemplo.
Observe que dizer que o processo de compilação deve produzir código de máquina não ajuda. É exatamente isso que o agrupamento pode fazer como intérprete é o código da máquina (bem, as coisas podem ficar um pouco mais complexas com a compilação cruzada).