A resposta curta é: você está certo em sua suspeita, sempre precisa de outro intérprete escrito em X ou um compilador de Y para outro idioma para o qual você já tenha um intérprete. Os intérpretes são executados, os compiladores são traduzidos apenas de um idioma para outro. Em algum momento do sistema, deve haver um intérprete ... mesmo que seja apenas a CPU.
Não importa quantos novos intérpretes você escreva no idioma Y , você sempre precisará usar o primeiro intérprete escrito em X para interpretar os intérpretes subsequentes. Isso parece ser um problema simplesmente devido à natureza dos intérpretes.
Corrigir. O que você pode fazer é escrever um compilador de Y para X (ou outro idioma para o qual você tem um intérprete), e você pode até fazer isso em Y . Em seguida, você pode executar o compilador Y escrito em Y no intérprete Y escrito em X (ou no intérprete Y escrito em Y executando no interpretador Y escrito em X ou no intérprete Y escrito em Y executando no interpretador Y escrito em Y rodando no Yintérprete escrito em X ou… ad infinitum) para compilar seu intérprete Y escrito em Y a X , para que você possa executá-lo em um intérprete X. Dessa forma, você se livrou do seu intérprete Y escrito em X , mas agora você precisa do intérprete X (sabemos que já temos um, pois, caso contrário, não poderíamos executar o intérprete X escrito em Y ), e você teve que escrever um compilador Y- para- X primeiro.
No entanto , por outro lado, o artigo da Wikipedia sobre intérpretes fala sobre intérpretes auto-hospedados. Aqui está um pequeno trecho que é relevante:
Um auto-intérprete é um intérprete de linguagem de programação escrito em uma linguagem de programação que pode se interpretar; um exemplo é um intérprete BASIC escrito em BASIC. Os auto-intérpretes estão relacionados aos compiladores de hospedagem automática.
Se não existir nenhum compilador para a linguagem ser interpretada, a criação de um auto-intérprete requer a implementação da linguagem em uma linguagem host (que pode ser outra linguagem de programação ou assembler). Por ter um primeiro intérprete como esse, o sistema é inicializado e novas versões do intérprete podem ser desenvolvidas no próprio idioma
Ainda não está claro para mim, como exatamente isso seria feito. Parece que não importa o quê, você sempre será forçado a usar a primeira versão do seu intérprete escrita no idioma do host.
Corrigir. Observe que o artigo da Wikipedia diz explicitamente que você precisa de uma segunda implementação do seu idioma e não diz que pode se livrar da primeira.
Agora, o artigo mencionado acima tem um link para outro artigo no qual a Wikipedia fornece alguns exemplos de supostos intérpretes de hospedagem própria. Após uma inspeção mais minuciosa, parece que a principal parte "de interpretação" de muitos desses intérpretes de hospedagem automática (especialmente alguns dos mais comuns, como PyPy ou Rubinius), é realmente escrita em outros idiomas, como C ++ ou C.
Mais uma vez, correto. Esses são realmente exemplos ruins. Veja Rubinius, por exemplo. Sim, é verdade que a parte Ruby do Rubinius é auto-hospedada, mas é um compilador, não um intérprete: compila no código-fonte do Ruby o código de bytes do Rubinius. A parte do intérprete OTOH não é auto-hospedada: interpreta o bytecode do Rubinius, mas é escrita em C ++. Portanto, chamar Rubinius de "intérprete auto-hospedado" está errado: a parte auto-hospedada não é um intérprete e a parte intérprete não é auto-hospedada .
O PyPy é semelhante, mas ainda mais incorreto: nem mesmo está escrito em Python, mas em RPython, que é uma linguagem diferente. É sintaticamente semelhante ao Python, semanticamente um "subconjunto estendido", mas na verdade é uma linguagem de tipo estatístico aproximadamente no mesmo nível de abstração que Java, e sua implementação é um compilador com vários back-end que compila RPython para código-fonte C, ECMAScript código-fonte, código de byte CIL, bytecode da JVM ou código-fonte Python.
Então, o que eu descrevo acima é possível? Um intérprete de host próprio pode ser independente do host original? Se sim, como exatamente isso seria feito?
Não, não por si só. Você precisaria manter o intérprete original ou escrever um compilador e compilar seu auto-intérprete.
Não são alguns meta-circular VMs, como Klein (escrito em auto ) e Maxine (escrito em Java). Observe, no entanto, que aqui a definição de "meta-circular" ainda é diferente: essas VMs não são escritas na linguagem que executam: Klein executa o auto bytecode, mas é escrito em Self, Maxine executa o bytecode da JVM, mas é escrito em Java. No entanto, o código-fonte Self / Java da VM é realmente compilado no bytecode Self / JVM e, em seguida, executado pela VM; portanto, quando a VM é executada, ela está no idioma que é executado. Ufa.
Observe também que isso é diferente das VMs, como o SqueakVM e o Jikes RVM . O Jikes é escrito em Java, e o SqueakVM é escrito em Slang (um subconjunto sintático e semântico do Smalltalk estaticamente tipificado, aproximadamente no mesmo nível de abstração que um assembler de alto nível) e ambos são compilados estaticamente no código nativo antes de serem executados. Eles não correm dentro de si mesmos. No entanto, você pode executá-los por cima deles mesmos (ou por cima de outra VM / JVM Smalltalk). Mas isso não é "meta-circular" nesse sentido.
Maxine e Klein, OTOH fazemcorrer dentro de si mesmos; eles executam seu próprio bytecode usando sua própria implementação. Isso é realmente impressionante! Ele permite algumas oportunidades interessantes de otimização, por exemplo, como a VM se executa em conjunto com o programa do usuário, pode integrar chamadas do programa do usuário para a VM e vice-versa, por exemplo, chamar o coletor de lixo ou o alocador de memória pode ser incorporado ao usuário código e retornos de chamada reflexivos no código do usuário podem ser incorporados na VM. Além disso, todos os truques inteligentes de otimização que as VMs modernas fazem, onde assistem ao programa em execução e o otimizam, dependendo da carga de trabalho e dos dados reais, a VM pode aplicar esses mesmos truques a si mesma enquanto executa o programa do usuário enquanto o programa do usuário está executando a carga de trabalho específica. Em outras palavras, a VM se especializou muito para issoprograma específico executando essa carga de trabalho específica.
No entanto, observe que eu evitei o uso da palavra "intérprete" acima e sempre usei "executar"? Bem, essas VMs não são construídas em torno de intérpretes, elas são construídas em torno de compiladores (JIT). Houve um intérprete adicionado ao Maxine posteriormente, mas você sempre precisa do compilador: é necessário executar a VM uma vez em cima de outra VM (por exemplo, Oracle HotSpot no caso de Maxine), para que a VM possa (JIT) se compilar. No caso de Maxine, o JIT compila sua própria fase de inicialização, depois serializa o código nativo compilado em uma imagem de VM de inicialização e coloca um carregador de inicialização muito simples na frente (o único componente da VM escrito em C, embora isso seja apenas por conveniência) , também pode estar em Java). Agora você pode usar o Maxine para se executar.
Você está certo ao observar que um intérprete de hospedagem automática ainda exige que um intérprete seja executado por si mesmo e não pode ser inicializado no mesmo sentido que um compilador.
No entanto, um idioma auto-hospedado não é a mesma coisa que um intérprete auto-hospedado. Geralmente é mais fácil criar um intérprete do que criar um compilador. Portanto, para implementar um novo idioma, podemos primeiro implementar um intérprete em um idioma não relacionado. Em seguida, podemos usar esse intérprete para desenvolver um compilador para a nossa linguagem. O idioma é auto-hospedado, pois o compilador é interpretado. O compilador pode então se compilar e pode ser considerado totalmente inicializado.
Um caso especial disso é um tempo de execução de compilação de JIT auto-hospedado. Ele pode começar com um intérprete em um idioma host, que usa o novo idioma para implementar a compilação JIT, após o qual o compilador JIT pode se compilar. Parece um intérprete auto-hospedado, mas evita o problema dos infinitos intérpretes. Essa abordagem é usada, mas ainda não é muito comum.
Outra técnica relacionada é um intérprete extensível, onde podemos criar extensões no idioma que está sendo interpretado. Por exemplo, podemos implementar novos códigos de operação no idioma. Isso pode transformar um intérprete básico em um intérprete rico em recursos, desde que evitemos dependências circulares.
Um caso que ocorre com bastante frequência é a capacidade do idioma de influenciar sua própria análise, por exemplo, como macros avaliadas no tempo de análise. Como o idioma da macro é o mesmo que o idioma que está sendo processado, ele tende a ser muito mais rico em recursos do que os idiomas de macro dedicados ou restritos. No entanto, é correto observar que o idioma que executa a extensão é um idioma ligeiramente diferente do idioma após a extensão.
Quando são usados intérpretes auto-hospedados "reais", isso geralmente é feito por razões de educação ou pesquisa. Por exemplo, implementar um intérprete para o Scheme dentro do Scheme é uma maneira legal de ensinar linguagens de programação (consulte SICP).
fonte