Estou pesquisando o CoffeeScript no site http://coffeescript.org/ , e ele tem o texto
O compilador CoffeeScript é ele próprio escrito em CoffeeScript
Como um compilador pode se compilar, ou o que essa declaração significa?
Estou pesquisando o CoffeeScript no site http://coffeescript.org/ , e ele tem o texto
O compilador CoffeeScript é ele próprio escrito em CoffeeScript
Como um compilador pode se compilar, ou o que essa declaração significa?
Respostas:
A primeira edição de um compilador não pode ser gerada por máquina a partir de uma linguagem de programação específica; sua confusão é compreensível. Uma versão posterior do compilador com mais recursos de idioma (com a fonte reescrita na primeira versão do novo idioma) pode ser criada pelo primeiro compilador. Essa versão pode então compilar o próximo compilador e assim por diante. Aqui está um exemplo:
Nota: Não sei exatamente como as versões do CoffeeScript são numeradas, isso foi apenas um exemplo.
Esse processo geralmente é chamado de inicialização . Outro exemplo de um compilador de inicialização é rustc
o compilador da linguagem Rust .
No artigo Reflexões sobre Confiança na Confiança , Ken Thompson, um dos criadores do Unix, escreve uma visão geral fascinante (e de fácil leitura) de como o compilador C se compila. Conceitos semelhantes podem ser aplicados ao CoffeeScript ou a qualquer outra linguagem.
A idéia de um compilador que compila seu próprio código é vagamente semelhante a um quine : código fonte que, quando executado, produz como saída o código fonte original. Aqui está um exemplo de uma solução CoffeeScript. Thompson deu este exemplo de uma coluna C:
char s[] = {
'\t',
'0',
'\n',
'}',
';',
'\n',
'\n',
'/',
'*',
'\n',
… 213 lines omitted …
0
};
/*
* The string s is a representation of the body
* of this program from '0'
* to the end.
*/
main()
{
int i;
printf("char\ts[] = {\n");
for(i = 0; s[i]; i++)
printf("\t%d,\n", s[i]);
printf("%s", s);
}
Em seguida, você pode se perguntar como é ensinado ao compilador que uma sequência de escape como '\n'
representa o código ASCII 10. A resposta é que, em algum lugar do compilador C, existe uma rotina que interpreta os literais de caracteres, contendo algumas condições como esta para reconhecer as seqüências de barra invertida:
…
c = next();
if (c != '\\') return c; /* A normal character */
c = next();
if (c == '\\') return '\\'; /* Two backslashes in the code means one backslash */
if (c == 'r') return '\r'; /* '\r' is a carriage return */
…
Então, podemos adicionar uma condição ao código acima…
if (c == 'n') return 10; /* '\n' is a newline */
... para produzir um compilador que saiba que '\n'
representa o ASCII 10. Curiosamente, esse compilador e todos os compiladores subseqüentes compilados por ele "conhecem" esse mapeamento; portanto, na próxima geração do código-fonte, você pode alterar a última linha para
if (c == 'n') return '\n';
… E fará a coisa certa! O 10
vem do compilador e não precisa mais ser definido explicitamente no código-fonte do compilador. 1
Esse é um exemplo de recurso de linguagem C que foi implementado no código C. Agora, repita esse processo para cada recurso de idioma único e você terá um compilador "auto-hospedado": um compilador C escrito em C.
1 A reviravolta na trama descrita no artigo é que, como o compilador pode ser "ensinado" a fatos como esse, também pode ser mal ensinado a gerar executáveis de Troia de uma maneira que é difícil de detectar, e esse ato de sabotagem pode persistir. em todos os compiladores produzidos pelo compilador contaminado.
Você já recebeu uma resposta muito boa, no entanto, quero lhe oferecer uma perspectiva diferente, que, espero, será esclarecedora para você. Vamos primeiro estabelecer dois fatos com os quais podemos concordar:
Tenho certeza de que você pode concordar que os números 1 e 2 são verdadeiros. Agora, observe as duas declarações. Você vê agora que é completamente normal que o compilador CoffeeScript consiga compilar o compilador CoffeeScript?
O compilador não se importa com o que compila. Desde que seja um programa escrito em CoffeeScript, ele pode compilá-lo. E o próprio compilador CoffeeScript é um programa desse tipo. O compilador CoffeeScript não se importa que seja o próprio compilador CoffeeScript que está compilando. Tudo o que vê é algum código CoffeeScript. Período.
Como um compilador pode se compilar, ou o que essa declaração significa?
Sim, é exatamente o que essa afirmação significa, e espero que você possa ver agora como essa afirmação é verdadeira.
Como um compilador pode se compilar, ou o que essa declaração significa?
Isso significa exatamente isso. Primeiro de tudo, algumas coisas a considerar. Há quatro objetos que precisamos examinar:
Agora, deve ficar óbvio que você pode usar o assembly gerado - o executável - do compilador CoffeScript para compilar qualquer programa arbitrário do CoffeScript e gerar o assembly para esse programa.
Agora, o próprio compilador CoffeScript é apenas um programa CoffeScript arbitrário e, portanto, pode ser compilado pelo compilador CoffeScript.
Parece que sua confusão decorre do fato de que, quando você cria seu novo idioma, ainda não possui um compilador, mas pode usá-lo para compilá-lo. Isso certamente parece um problema de ovo de galinha , certo?
Apresente o processo chamado inicialização .
Agora você precisa adicionar novos recursos. Digamos que você tenha implementado apenas while
-loops, mas também deseja for
-loops. Isso não é um problema, pois você pode reescrever qualquer for
loop de forma que ele seja um while
loop. Isso significa que você só pode usar- while
loops no código-fonte do seu compilador, já que o assembly que você tem em mãos só pode compilá-los. Mas você pode criar funções dentro do seu compilador que podem for
executar e compilar- loops com ele. Então você usa o assembly que você já possui e compila a nova versão do compilador. E agora você tem uma montagem de um compilador que também pode analisar e compilar- for
loops! Agora você pode voltar ao arquivo de origem do seu compilador e reescrever quaisquer while
loops que você não deseja em for
loops.
Enxágue e repita até que todos os recursos de idioma desejados possam ser compilados com o compilador.
while
e, for
obviamente, foram apenas exemplos, mas isso funciona para qualquer novo recurso de idioma que você desejar. E então você está na situação em que o CoffeScript está agora: O compilador se compila.
Há muita literatura por aí. Reflexões sobre confiança Confiança é um clássico que todos os interessados nesse tópico devem ler pelo menos uma vez.
Aqui, o termo compilador encobre o fato de que há dois arquivos envolvidos. Um é um executável que recebe como arquivos de entrada escritos no CoffeScript e produz como arquivo de saída outro executável, um arquivo de objeto vinculável ou uma biblioteca compartilhada. O outro é um arquivo de origem CoffeeScript, que apenas descreve o procedimento de compilação do CoffeeScript.
Você aplica o primeiro arquivo ao segundo, produzindo um terceiro que é capaz de executar o mesmo ato de compilação que o primeiro (possivelmente mais, se o segundo arquivo definir recursos não implementados pelo primeiro) e, portanto, poderá substituir o primeiro se você tão desejo.
Como a versão Ruby do compilador CoffeeScript já existia, ela foi usada para criar a versão CoffeeScript do compilador CoffeeScript.
Isso é conhecido como um compilador auto-hospedado .
É extremamente comum e geralmente resulta do desejo de um autor de usar seu próprio idioma para manter o crescimento desse idioma.
Não é uma questão de compiladores aqui, mas uma questão de expressividade da linguagem, pois um compilador é apenas um programa escrito em alguma linguagem.
Quando dizemos que "um idioma é escrito / implementado", na verdade queremos dizer que um compilador ou intérprete para esse idioma foi implementado. Existem linguagens de programação nas quais você pode escrever programas que implementam a linguagem (são compiladores / intérpretes para a mesma linguagem). Esses idiomas são chamados de idiomas universais .
Para entender isso, pense em um torno de metal. É uma ferramenta usada para moldar metal. É possível, usando exatamente essa ferramenta, criar outra ferramenta idêntica, criando suas partes. Assim, essa ferramenta é uma máquina universal. Obviamente, o primeiro foi criado usando outros meios (outras ferramentas) e provavelmente era de qualidade inferior. Mas o primeiro foi usado para construir novos com maior precisão.
Uma impressora 3D é quase uma máquina universal. Você pode imprimir a impressora 3D inteira usando uma impressora 3D (não é possível construir a ponta que derrete o plástico).
A n + 1ª versão do compilador é escrita em X.
Assim, ele pode ser compilado pela enésima versão do compilador (também escrita em X).
Mas a primeira versão do compilador escrita em X deve ser compilada por um compilador para X escrito em um idioma diferente de X. Essa etapa é chamada de inicialização do compilador.
Os compiladores pegam uma especificação de alto nível e a transformam em uma implementação de baixo nível, como pode ser executada no hardware. Portanto, não há relação entre o formato da especificação e a execução real além da semântica da linguagem que está sendo direcionada.
Os compiladores cruzados passam de um sistema para outro, enquanto os compiladores de idiomas compilam uma especificação de idioma em outra especificação de idioma.
Basicamente, compilar é uma tradução justa, e o nível geralmente é de nível superior para o nível inferior, mas existem muitas variantes.
Os compiladores de inicialização são os mais confusos, é claro, porque compilam o idioma em que foram escritos. Não se esqueça da etapa inicial na inicialização, que requer pelo menos uma versão existente mínima que seja executável. Muitos compiladores inicializados trabalham primeiro nos recursos mínimos de uma linguagem de programação e adicionam recursos adicionais de linguagem complexa a partir de agora, desde que o novo recurso possa ser expresso usando os recursos anteriores. Se não fosse esse o caso, seria necessário que essa parte do "compilador" fosse desenvolvida em outro idioma previamente.
self-hosting
compilador. Veja programmers.stackexchange.com/q/263651/6221