Por que o primeiro compilador foi escrito antes do primeiro intérprete?

72

O primeiro compilador foi escrito por Grace Hopper em 1952, enquanto o interpretador de Lisp foi escrito em 1958 pelo aluno de John McCarthy, Steve Russell. Escrever um compilador parece ser um problema muito mais difícil do que um interpretador. Se é assim, por que o primeiro compilador foi escrito seis anos antes do primeiro intérprete?

    
por anguyen 28.07.2014 / 15:19
fonte

10 respostas

97

Writing a compiler seems like a much harder problem than an interpreter.

Isso pode ser verdade hoje, mas eu diria que não foi o caso há 60 anos atrás. Algumas razões pelas quais:

  • Com um intérprete, você precisa manter tanto o programa quanto o programa na memória. Em uma época em que 1kb de memória era um enorme luxo, manter a pegada de memória em execução baixa era fundamental. E a interpretação requer um pouco mais de memória do que a execução de um programa compilado.
  • CPUs modernas são extremamente complexas com enormes catálogos de instruções. Então, escrever um bom compilador é realmente um desafio. CPUs antigas eram muito mais simples, por isso mesmo a compilação era mais simples.
  • Linguagens modernas são muito mais complexas do que linguagens antigas, então até os compiladores são muito mais complexos. Idiomas antigos teriam assim compiladores mais simples.
por 28.07.2014 / 15:27
fonte
42

O ponto fundamental é que o ambiente de hardware de computação dos anos 50 fez com que apenas um compilador fosse viável, dado o processamento orientado em lote dos computadores naquela época.

Na época, as melhores interfaces de usuário eram limitadas principalmente a cartões perfurados e impressoras de teletipo . Em 1961, o sistema SAGE tornou-se o primeiro Exibição do tubo de raios catódicos (CRT) em um computador. Portanto, a natureza interativa de um intérprete não era preferível ou natural até muito mais tarde.

Numerosos computadores na década de 1950 usaram os interruptores do painel frontal para carregar instruções, e a saída era simplesmente fileira de lâmpadas / LEDs, e os amadores até usaram interruptores no painel frontal & LEDs na década de 1970. Talvez você esteja familiarizado com o infame Altair 8800 .

Outras limitações de hardware também tornaram os intérpretes inviáveis. Havia a disponibilidade extremamente limitada de memória primária (por exemplo, RAM) em computadores nos anos 50. Antes do circuito integrado de semicondutores (que não veio até 1958), a memória estava limitada à memória do núcleo magnético ou atrasar a memória de linha que foi medida em bits ou palavras , sem prefixo. Combinado com a lentidão da memória de armazenamento secundário (por exemplo, disco ou fita), seria considerado um desperdício, se não inviável, ter muito da memória usada para o intérprete, mesmo antes que o programa sendo interpretado fosse carregado.

As limitações de memória ainda eram um fator importante quando a equipe liderada por John Backus na IBM criou o compilador FORTRAN em 1954-57. Este compilador inovador foi bem-sucedido apenas porque era um otimizador do compilador .

A maioria dos computadores na década de 1950 tinha qualquer Sistema Operacional, sem falar em recursos modernos como link dinâmico e gerenciamento de memória virtual, então a ideia de um intérprete era radical e impraticável naquela época. .

Adendo

As línguas dos anos 1950 eram primitivas. Eles incluíam apenas um punhado de operações, muitas vezes influenciadas pelas instruções do hardware subjacente ou pela definição do problema de seu uso direcionado.

Naquela época, os computadores eram raramente computadores de uso geral , no sentido em que pensamos em computadores hoje em dia. O fato de serem reprogramáveis sem precisarem ser reconstruídos era considerado um conceito revolucionário - antes as pessoas usavam máquinas eletromecânicas (normalmente calculadoras) para computar ou calcular respostas (a maioria das aplicações na década de 1950 era numérica em natureza).

Do ponto de vista da Ciência da Computação, os compiladores e intérpretes são ambos tradutores , e aproximadamente iguais em complexidade para implementar.

    
por 28.07.2014 / 17:32
fonte
12

As primeiras linguagens de programação foram bastante simples (sem recursão para exemplo) e perto da arquitetura da máquina que era ela mesma simples. A tradução foi então um processo simples .

Um compilador era mais simples como um programa do que um interpretador que teria que manter juntos os dados para a execução do programa e as tabelas para interpretar o código fonte. E o intérprete ter mais espaço , por si só, para o código-fonte do programa e para o simbólico tabelas.

A memória pode ser tão escassa (por razões arquitetônicas e de custo) que os compiladores poderiam ser programas independentes que sobrescrevessem o sistema operacional (eu usei um desses). O sistema operacional teve que ser recarregado depois de compilar para executar o programa compilado. ... o que faz deixe claro que executar um intérprete para trabalho real simplesmente não é uma opção .

Para ser verdade, a simplicidade requerida dos compiladores era tal que compiladores não eram muito bons (a otimização de código ainda estava na infância, quando considerada). Código de máquina escrito à mão tinha, pelo menos até o final dos anos sessenta em alguns lugares, a reputação de ser significativamente mais eficiente do que o código gerado pelo compilador. Houve até mesmo um conceito de relação de expansão de código , que comparou o tamanho de código compilado para o trabalho de um programador muito bom. Foi geralmente maior que 1 para a maioria (todos?) compiladores, o que significava programas mais lentos e, muito mais importante, programas maiores que exigem mais memória. Isso foi ainda uma questão nos anos sessenta.

O interesse do compilador foi na facilidade de programação, especialmente para usuários que não estavam computando especialistas, como cientistas em vários Campos. Esse interesse não foi o desempenho do código. Mas ainda assim, programador O tempo era então considerado um recurso barato. O custo estava no computador tempo, até 1975-1980, quando o custo mudou de hardware para Programas. O que significa que o mesmo compilador não foi levado a sério alguns profissionais .

O custo muito alto do tempo de computador foi mais um motivo para desqualificando intérpretes , a tal ponto que a própria idéia teria foi risível para a maioria das pessoas.

O caso do Lisp é muito especial, porque era extremamente simples linguagem que tornou viável (eo computador tornou-se um pouco maior em 58). Mais importante, o interpretador de Lisp foi uma prova de conceito em relação à autodefinibilidade da Lisp ( meta-circularidade ), independentemente de qualquer questão de usabilidade.

O sucesso do Lisp se deve em grande parte ao fato de que essa autodefinibilidade fez dele um excelente testbed para estudar estruturas de programação e design novo idiomas (e também por sua conveniência para computação simbólica).

    
por 28.07.2014 / 18:29
fonte
4

Eu não concordo com a premissa da pergunta.

Adm. O primeiro compilador do Hopper (o A-0) era mais como um linker ou uma linguagem de macro. Ela armazenou sub-rotinas em uma fita (cada uma atribuiu um número) e seus programas seriam escritos como uma lista de sub-rotinas e argumentos. O compilador copiaria as sub-rotinas solicitadas da fita e as reordenaria em um programa completo.

Ela usou a palavra "compile" no mesmo sentido em que compila uma antologia de poemas: coletando vários itens em um único volume.

Esse primeiro compilador não possuía um lexer ou parser, até onde eu saiba, o que o torna um ancestral distante de um compilador moderno. Mais tarde ela criou outro compilador (o B-0, também conhecido como FLOW-MATIC) com o objetivo de uma sintaxe mais parecida com o inglês, mas não foi completado até 1958 ou 1959 - aproximadamente ao mesmo tempo que o interpretador Lisp. p>

Portanto, acho que a questão em si está um pouco equivocada. Parece que compiladores e intérpretes co-evoluíram quase exatamente ao mesmo tempo, sem dúvida devido ao compartilhamento de idéias que teriam muitos cientistas pensando na mesma linha naqueles dias.

Uma resposta melhor com citações aqui: link .

    
por 10.11.2014 / 15:49
fonte
3

A outra parte da equação é que os compiladores eram uma abstração de etapa acima de um montador. Primeiro nós tínhamos código de máquina codificado. "Nós" éramos a montadora. Cada salto e deslocamento, etc. foi calculado manualmente em hexadecimal (ou octal) e depois perfurado em fita de papel ou cartões. Então, quando os montadores entraram em cena, foi uma grande economia de tempo. O próximo passo foi o de montadores macro. Isso deu a capacidade de escrever uma macro que se expandisse em um conjunto de instruções de máquina. Então Fortran e Cobol foram um grande passo à frente. A falta de recursos (armazenamento, memória e ciclos de CPU) fez com que os intérpretes de propósito geral tivessem que esperar que a tecnologia crescesse. A maioria dos intérpretes iniciais eram mecanismos de código de bytes (como Java ou CLR de hoje, mas com muito menos capacidade). O UCSD Pascal era uma linguagem muito popular (e rápida). O MS Basic era um mecanismo de código de bytes sob o capô. Portanto, a "compilação" foi realmente para gerar um fluxo de código de bytes que foi realmente executado pelo tempo de execução.

Em termos de sobrecarga de instruções, dependia totalmente do processador que estava sendo executado. A indústria passou por uma grande turbulência RISC vs CISC por um tempo. Eu pessoalmente escrevi montador para IBM, Data General, Motorola, Intel (quando eles apareceram), TI e vários outros. Houve uma diferença bastante significativa nos conjuntos de instruções, registros, etc. que influenciariam o que era necessário para "interpretar" um código-p.

Como referência temporal, comecei a programar na companhia telefônica por volta de 1972.

    
por 29.07.2014 / 05:53
fonte
2

Se você não está segurando tudo na memória, o código compilado é muito mais rápido. Não se esqueça de que, nessas horas, as funções foram unidas ao código compilado. Se você não estiver compilando, não sabe quais funções serão necessárias. Então, você está chamando funções de ... Oh, não do disco ainda, estamos no início dos 50 laços, mas de cartas! Em tempo de execução!

Naturalmente, é possível encontrar soluções alternativas, mas elas não foram encontradas ainda, pois os idiomas eram muito simples e não tão distantes do código de máquina. E compilar foi fácil e suficiente.

    
por 28.07.2014 / 15:49
fonte
1

Antes do primeiro compilador ser escrito, as pessoas escreviam código assembler, o que era um enorme progresso comparado ao código binário simples. Na época, havia um strong argumento de que o código compilado por um compilador seria menos eficiente do que o código assembler - naquela época a relação de (custo do computador) para (custo do programador) era muito, muito diferente da atual. Portanto, houve strong resistência contra compiladores desse ponto de vista.

Mas os compiladores são muito mais eficientes que os intérpretes. Se você tivesse sugerido escrever um intérprete naquele momento, as pessoas teriam pensado que você é louco. Você pode imaginar comprar um computador de um milhão de dólares e depois desperdiçar 90% de seu poder para interpretar o código?

    
por 28.07.2014 / 16:35
fonte
1

Antes que um programa de looping possa ser interpretado, ele deve ser armazenado em um meio que possa ser lido repetidamente. Na maioria dos casos, o único meio adequado seria RAM. Como o código normalmente será inserido em cartões perfurados que - para idiomas legíveis por humanos - provavelmente estarão em grande parte vazios, algum tipo de processamento deve ser executado no código antes de ser armazenado na RAM. Em muitos casos, processar o texto do cartão perfurado em uma forma que seja adequada para execução direta pelo processador não é realmente mais difícil do que processá-lo em um formato que poderia ser manipulado eficientemente por meio de um intérprete.

Observe que o objetivo dos primeiros compiladores não era produzir um arquivo de linguagem de montagem ou código de objeto no disco, mas sim encerrar o código na RAM que estava pronto para ser executado. Isso é surpreendentemente fácil quando não há sistema operacional para atrapalhar. Um compilador pode gerar código iniciando em uma extremidade da memória e alocar variáveis e destinos de ramificação começando na outra. Se uma instrução estiver marcada com o rótulo "1234", o compilador armazenará em uma variável chamada "1234" uma instrução para pular para o endereço de geração de código atual, criando essa variável se ela não existir. Uma instrução "goto 1234" criará uma variável "1234" se ela não existir e, em seguida, saltará para essa variável [que esperançosamente terá um salto para o local adequado armazenado nela antes que a instrução seja executada]. Note que o compilador não precisa fazer nada especial para permitir o código para goto um rótulo que ainda não foi definido, pois ele sabe quando o goto compila onde ele vai pular - para uma variável. Essa pode não ser a maneira mais eficiente de gerar código, mas é adequada para os tamanhos de programas que os computadores deveriam manipular.

    
por 28.07.2014 / 20:21
fonte
0

Porque os intérpretes precisam de compiladores para trabalhar.

A afirmação acima não é realmente verdadeira. Estritamente falando, você pode criar um interpretador sem nunca usar ou interagir com um compilador. Mas os resultados de fazer isso não pareceriam muito com o que eu acho que você quer dizer com esses termos.

No sentido estrito, compiladores e intérpretes fazem coisas totalmente diferentes. Um compilador lê texto de alguma fonte e o transforma em outro formato: linguagem de montagem, código de máquina, outra linguagem de alto nível, uma estrutura de dados ou qualquer outra coisa. Um intérprete, por sua vez, recebe uma estrutura de dados de algum tipo e executa instruções com base nela.

O que tendemos a considerar como um "compilador" atualmente é um compilador que foi emparelhado com um gerador de código : um programa que coleta dados de alguns source e produz o código em algum formato com base no que vê. Esse é um uso bastante intuitivo para compiladores, e foi uma das primeiras coisas para as quais os compiladores foram criados. Mas se você olhar de outra maneira, isso parece muito semelhante ao que um intérprete faz. Ele sempre gera código em vez de executar operações mais gerais, mas o princípio é o mesmo.

Se olharmos para ele do outro lado, um intérprete precisa obter seus dados de algum lugar . Esses são apenas dados, para que você possa criá-los da mesma maneira que criaria qualquer outro tipo de dados. Como estamos falando de interpretação, parece natural que você possa construir seus dados com base em instruções em um arquivo de texto. Mas para fazer isso, você precisaria de algo para ler no texto e criar sua estrutura de dados, e isso é um compilador . Está ligado ao interpretador em vez de a um gerador de código, mas é um compilador do mesmo jeito.

É por isso que os compiladores foram escritos primeiro. A ideia de interpretar estruturas de dados não era nova mesmo quando os compiladores eram concebidos, mas os compiladores eram o "elo perdido" que permitia que os programadores construíssem essas estruturas a partir do texto.

    
por 29.07.2014 / 19:16
fonte
-1

Outro fator: quando os primeiros compiladores foram escritos, o custo do tempo da máquina era muito maior do que é agora. Os intérpretes usam muito mais tempo de máquina.

    
por 29.07.2014 / 06:47
fonte