Um intérprete produz código de máquina?

42

Eu estudo os tópicos de compiladores e intérpretes intensivamente. Quero verificar se minha compreensão básica está correta, então vamos supor o seguinte:

Eu tenho uma linguagem chamada "Foobish" e suas palavras-chave são

<OUTPUT> 'TEXT', <Number_of_Repeats>;

Então, se eu quiser imprimir no console 10 vezes, eu escreveria

OUTPUT 'Hello World', 10;

Hello World.foobish-file.

Agora eu escrevo um intérprete na linguagem de minha escolha - C # neste caso:

using System;

namespace FoobishInterpreter
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            analyseAndTokenize(Hello World.foobish-file)//Pseudocode
            int repeats = Token[1];
            string outputString = Token[0];
            for (var i = 0; i < repeats; i++)
            {
                Console.WriteLine(outputString);
            }
        }
    }
}

Em um nível de intérprete muito fácil, o intérprete iria analisar o arquivo de script, etc. e executar a linguagem-no-texto no caminho da implementação do intérprete.

Um compilador criaria uma linguagem de máquina que fosse executada diretamente no hardware físico?

Assim, um interpretador não produz linguagem de máquina, mas um compilador faz isso por sua entrada?

Eu tenho algum mal-entendido no modo básico como os compiladores e os intérpretes funcionam?

    
por GrayFox 22.10.2015 / 19:35
fonte

6 respostas

79

Os termos "intérprete" e "compilador" são muito mais imprecisos do que costumavam ser. Muitos anos atrás, era mais comum para os compiladores produzir código de máquina a ser executado posteriormente, enquanto os interpretadores mais ou menos "executavam" o código-fonte diretamente. Então esses dois termos foram bem compreendidos na época.

Mas hoje existem muitas variações no uso de "compilador" e "intérprete". Por exemplo, o VB6 "compila" para código de byte (uma forma de Intermediate Language ), que é então "interpretado" pelo Tempo de Execução do VB. Um processo similar ocorre em C #, que produz CIL que é então executado por um Just-In-Time Compiler (JIT) que, antigamente, teria sido pensado como um intérprete. Você pode "congelar" a saída do JIT em um executável binário real usando NGen.exe , cujo produto teria sido o resultado de um compilador nos velhos tempos.

Assim, a resposta para a sua pergunta não é tão simples quanto antes.

Leitura adicional
Compiladores vs. intérpretes na Wikipedia

    
por 22.10.2015 / 20:10
fonte
35

O resumo abaixo é baseado em "Compiladores, Princípios, Técnicas e Ferramentas", Aho, Lam, Sethi, Ullman, (Pearson International Edition, 2007), páginas 1, 2, com a adição de algumas ideias do meu próprio.

Os dois mecanismos básicos para o processamento de um programa são compilação e interpretação .

A compilação leva como entrada um programa de origem em um determinado idioma e gera um programa de destino em um idioma de destino.

source program --> | compiler | --> target program

Se o idioma de destino for código de máquina, ele poderá ser executado diretamente em algum processador:

input --> | target program | --> output

A compilação envolve a varredura e tradução de todo o programa de entrada (ou módulo) e não envolve sua execução.

A interpretação toma como entrada o programa fonte e sua entrada, e produz a saída do programa fonte

source program, input --> | interpreter | --> output

Interpretação geralmente envolve o processamento (análise e execução) do programa, uma declaração de cada vez.

Na prática, muitos processadores de linguagem usam uma combinação das duas abordagens. Por exemplo, os programas Java são primeiro traduzidos (compilados) em um programa intermediário (código de bytes):

source program --> | translator | --> intermediate program

a saída desta etapa é então executada (interpretada) por uma máquina virtual:

intermediate program + input --> | virtual machine | --> output

Para complicar ainda mais as coisas, a JVM pode executar a compilação just-in-time no tempo de execução para converter o código de bytes em outro formato, que é então executado.

Além disso, mesmo quando você compila para a linguagem de máquina, existe um interpretador executando o arquivo binário que é implementado pelo processador subjacente. Portanto, mesmo neste caso, você está usando um híbrido de compilação + interpretação.

Assim, sistemas reais usam uma mistura dos dois, então é difícil dizer se um determinado processador de linguagem é um compilador ou um interpretador, porque ele provavelmente usará ambos os mecanismos em diferentes estágios de seu processamento. Nesse caso, provavelmente seria mais apropriado usar outro termo mais neutro.

No entanto, compilação e interpretação são dois tipos distintos de processamento, conforme descrito nos diagramas acima,

Para responder às perguntas iniciais.

A compiler would create machine language which runs on the physical hardware directly?

Não necessariamente, um compilador traduz um programa escrito para uma máquina M1 para um programa equivalente escrito para uma máquina M2. A máquina de destino pode ser implementada em hardware ou ser uma máquina virtual. Conceitualmente, não há diferença. O ponto importante é que um compilador olha para um trecho de código e traduz para outro idioma sem executá-lo.

So an interpreter doesn't produce machine language but a compiler does it for its input?

Se por produzindo você está se referindo à saída, então um compilador produz um programa de destino que pode estar em linguagem de máquina, um interpretador não.

    
por 22.10.2015 / 23:05
fonte
22

A compiler would create machine language

Não. Um compilador é simplesmente um programa que toma como entrada um programa escrito em linguagem A e produz como saída um programa semanticamente equivalente na linguagem B . A linguagem B pode ser qualquer coisa, não precisa ser linguagem de máquina.

Um compilador pode compilar de uma linguagem de alto nível para outra linguagem de alto nível (por exemplo, GWT, que compila Java para ECMAScript), de uma linguagem de alto nível a uma linguagem de baixo nível (eg Gambit, que compila Scheme para C), de uma linguagem de alto nível para código de máquina (eg GCJ, que compila Java para código nativo), de uma linguagem de baixo nível para uma linguagem de alto nível (eg Clue, que compila C para Java, Lua, Perl, ECMAScript e Common Lisp), de uma linguagem de baixo nível a outra de baixo nível (por exemplo, o Android SDK, que compila o bytecode JVML para o bytecode da Dalvik), de uma linguagem de baixo nível para código de máquina (por exemplo, o compilador C1X de HotSpot, que compila o bytecode JVML ao código de máquina), código de máquina para uma linguagem de alto nível (qualquer chamado "decompiler", também Emscripten, que compila o código de máquina LLVM para ECMAScript), código de máquina para linguagem de baixo nível o compilador JIT no JPC, que compila o código nativo x86 para o bytecode JVML) e o código nativo para o código nativo (por exemplo, Compilador JIT no PearPC, que compila o código nativo do PowerPC para o código nativo x86).

Note também que "código de máquina" é um termo muito difuso por várias razões. Por exemplo, existem CPUs que executam nativamente o código de byte da JVM e existem interpretadores de software para código de máquina x86. Então, o que faz um "código de máquina nativa", mas não o outro? Além disso, toda a linguagem é o código de uma máquina abstrata para essa linguagem.

Existem muitos nomes especializados para compiladores que executam funções especiais. Apesar do fato de que estes são nomes especializados, todos eles ainda são compiladores, apenas tipos especiais de compiladores:

  • se a linguagem A for percebida como tendo aproximadamente o mesmo nível de abstração que a linguagem B , o compilador pode ser chamado de transpiler ( por exemplo, um transpiler Ruby para ECMAScript ou um ECMAScript2015 para ECMAScript5-transpiler)
  • se for entendido que a linguagem A está em um nível de abstração de nível inferior à linguagem B , o compilador pode ser chamado de decompiler ( por exemplo, um decodificador x86-machine-code-to-C)
  • se idioma A == idioma B , o compilador pode ser chamado de otimizador , obfuscator , ou minifier (dependendo da função específica do compilador)

which runs on the physical hardware directly?

Não necessariamente. Pode ser executado em um intérprete ou em uma VM. Poderia ainda ser compilado para um idioma diferente.

So an interpreter doesn't produce machine language but a compiler does it for its input?

Um intérprete não produz nada. Apenas roda o programa.

Um compilador produz algo, mas não necessariamente tem que ser linguagem de máquina, pode ser qualquer linguagem. Pode até ser a mesma linguagem que a linguagem de entrada! Por exemplo, Supercompilers, LLC tem um compilador que usa Java como entrada e produz Java otimizado como sua saída. Existem muitos compiladores ECMAScript que tomam ECMAScript como suas entradas e produzem ECMAScript otimizado, minimizado e ofuscado como sua saída.

Você também pode estar interessado em:

por 23.10.2015 / 09:23
fonte
15

Acho que você deveria abandonar completamente a noção de "interpretador versus ", porque é uma falsa dicotomia.

  • Um compilador é um transformador : transforma um programa de computador escrito em idioma de origem e gera um equivalente em um destino idioma . Normalmente, o idioma de origem é de nível superior que o idioma de destino - e, se é o contrário, costumamos chamar esse tipo de transformador de um decompilador .
  • Um intérprete é um mecanismo de execução . Executa um programa de computador escrito em um idioma, de acordo com a especificação daquele idioma. Usamos principalmente o termo para software (mas, de certa forma, uma CPU clássica pode ser vista como um "intérprete" baseado em hardware para seu código de máquina).

A palavra coletiva para tornar uma linguagem de programação abstrata útil no mundo real é implementação .

No passado, uma implementação de linguagem de programação consistia em apenas um compilador (e a CPU para a qual ele gerava código) ou apenas um interpretador - então talvez parecesse que esses dois tipos de ferramentas são Mutualmente exclusivo. Hoje, você pode ver claramente que este não é o caso (e nunca foi para começar). Tomar uma implementação de linguagem de programação sofisticada e tentar empurrar o nome "compilador" ou "intérprete" para ela, geralmente levará a resultados inconclusivos ou inconsistentes.

Uma única implementação de linguagem de programação pode envolver qualquer número de compiladores e intérpretes , muitas vezes em vários formulários (autônomo, on-the-fly), qualquer número de outras ferramentas, como analisadores estáticos e otimizadores e qualquer número de etapas. Pode até mesmo incluir implementações completas de qualquer número de linguagens intermediárias (que podem não estar relacionadas ao que está sendo implementado).

Exemplos de esquemas de implementação incluem:

  • Um compilador C que transforma o código de máquina C em x86 e uma CPU x86 que executa esse código.
  • Um compilador C que transforma C em LLVM IR, um compilador de backend LLVM que transforma o IR da LLVM em código de máquina x86 e uma CPU x86 que executa esse código.
  • Um compilador C que transforma C em LLVM IR e um interpretador LLVM que executa LLVM IR.
  • Um compilador Java que transforma o bytecode Java em JVM e um JRE com um interpretador que executa esse código.
  • Um compilador Java que transforma o bytecode Java em JVM e um JRE com um interpretador que executa algumas partes desse código e um compilador que transforma outras partes desse código em código de máquina x86 e uma CPU x86 que executa esse código .
  • Um compilador Java que transforma o bytecode Java em JVM e uma CPU ARM que executa esse código.
  • Um compilador C # que transforma C # em CIL, um CLR com um compilador que transforma código de máquina CIL em x86 e uma CPU x86 que executa esse código.
  • Um interpretador Ruby que executa o Ruby.
  • Um ambiente Ruby com um interpretador que executa Ruby e um compilador que transforma Ruby em código de máquina x86 e uma CPU x86 que executa esse código.

... e assim por diante.

    
por 23.10.2015 / 14:04
fonte
7

Embora as linhas entre compiladores e intérpretes tenham sido difusas ao longo do tempo, ainda é possível traçar uma linha entre elas observando a semântica do que o programa deve fazer e o que o compilador / interpretador faz.

Um compilador gerará outro programa (normalmente em uma linguagem de nível inferior, como código de máquina) que, se esse programa for executado, fará o que seu programa deve fazer.

Um intérprete fará o que seu programa deve fazer.

Com essas definições, os locais em que fica confuso são os casos em que seu compilador / interpretador pode ser considerado como fazendo coisas diferentes dependendo de como você o vê. Por exemplo, o Python pega seu código Python e o compila em um bytecode compilado em Python. Se esse bytecode do Python for executado por meio de um interpretador do Python , ele fará o que seu programa deveria fazer. Na maioria das situações, no entanto, os desenvolvedores de Python pensam em ambas as etapas sendo executadas em uma grande etapa, então optam por pensar no CPython intérprete como interpretando seu código fonte, e o fato de que ele foi compilado ao longo do caminho é considerado um detalhe de implementação. Desta forma, é tudo uma questão de perspectiva.

    
por 23.10.2015 / 01:50
fonte
6

Aqui está uma simples desambiguação conceitual entre compiladores e intérpretes.

Considere 3 idiomas: linguagem programação , P (em que o programa está escrito); domain language, D (para o que acontece com o programa em execução); e a linguagem target , T (alguma terceira língua).

Conceitualmente,

  • um compilador traduz P para T para que você possa avaliar T (D); Considerando que

  • um intérprete avalia diretamente P (D).

por 22.10.2015 / 22:09
fonte