Desenvolvendo uma linguagem dinâmica

11

Eu criei vários compiladores escritos à mão para linguagens muito simples, mas agora quero tentar desenvolver uma linguagem dinâmica, semelhante a um Python ou Ruby simplificado. No entanto, foi fácil para mim entender como os compiladores funcionam. Compiladores primitivos são traduzidos. Mas não posso fazer isso se a linguagem for dinâmica. Eu tenho que escrever um intérprete ou VM que controla as informações em tempo de execução e coloca muito mais trabalho em mim.

Em resumo, há algum recurso que eu deva verificar, considerando que eu sei como os compiladores funcionam, mas quero migrar para a criação de um intérprete? Existem algumas VMs por aí para linguagens dinâmicas, mas não tenho problema em criar minhas próprias. Tudo isso é apenas para a minha experiência pessoal.

Estou buscando informações sobre como passar de um compilador para um intérprete. Se eu já fiz um compilador para a linguagem X, mas agora o que escrever para um intérprete, o que precisa ser feito e há algum recurso que repasse o processo?

Não quero recursos amplos ou abstratos que abordem como os compiladores ou máquinas virtuais funcionam. Eu tenho muitos livros didáticos sobre o assunto. Todos os recursos que encontrei on-line assumem que você tem 0 experiência e, assim, começam com análises lexicais ou sintáticas ou são extremamente abstratos. Eu tenho um compilador funcional, mas agora desejo transformar isso em um intérprete e adicionar recursos dinâmicos ao idioma.

Não consegui encontrar recursos nesse processo, ele pode ter um escopo muito limitado ou recursos no "back-end" de um intérprete sem ser muito teórico, e foi por isso que publiquei aqui.

Austin Henley
fonte
1
Existem toneladas de recursos como este. Observe que a linha entre compilador e intérprete é mais embaçada do que você pensa; o compilador C # 4.0 suporta programação dinâmica, assim como vários outros compiladores.
Robert Harvey
@RobertHarvey Sim, estou pedindo recursos para criar meu próprio tempo de execução / intérprete / máquina virtual. O interpretador .Net é muito complicado para eu basear o meu!
Austin Henley
1
E confira esta pergunta SO , há um par de comentários com referências a outras questões que são bastante interessante ...
yannis

Respostas:

4

Primeiro aprenda sobre a implementação de intérpretes. Eu recomendo o PLAI (Linguagens de Programação: Aplicação e Interpretação) . Ele chega rapidamente ao ponto principal da interpretação, sem insistir na sintaxe.

No seu idioma, você poderá reutilizar o front-end do compilador (analisador, principalmente) e a biblioteca de tempo de execução (GC, estruturas de dados, operações primitivas etc.).

Obviamente, você também pode implementar uma linguagem dinâmica com um compilador que produz código que manipula (algumas) as mesmas estruturas de dados que você usaria em um intérprete. Por exemplo, em um intérprete, você pode implementar variáveis ​​globais como uma tabela de hash indexada por string. Em um compilador, você compila referências de variáveis ​​globais no código que faz a pesquisa usando a mesma tabela. Por outro lado, você pode compilar variáveis ​​lexicais em uma representação mais eficiente (argumentos "nativos" e referências à estrutura de fechamento).

Ryan Culpepper
fonte
5

Se você quiser aprender o básico da implementação de um intérprete para uma linguagem dinâmica, não consigo imaginar um lugar melhor para começar do que as origens da primeira linguagem de programação dinâmica e interpretada: Lisp.

Em seu artigo original de 1960 , John McCarthy definiu 5 funções primitivas necessárias para um Lisp. É claro que McCarthy pretendia apenas seu trabalho sobre Lisp como um exercício acadêmico; foi um aluno de pós-graduação que implantou evalna montagem e criou o primeiro intérprete do Lisp. Paul Graham identifica sete primitivas : citação, átomo, eq, contras, carro, cdr e cond.

O problema é que você pode realmente implementar o Lisp em qualquer idioma; Depois de implementar eval, é fácil configurar um REPL e você tem um intérprete interativo . As pessoas ficaram entediadas ou curiosas o suficiente para implementar Lisps em C, Java, Ruby, Python e muitas outras linguagens. E nem sempre de propósito; é importante lembrar a décima regra de Greenspun :

Qualquer programa C ou Fortran suficientemente complicado contém uma implementação lenta ad hoc, especificada informalmente, cheia de bugs e com erros, de metade do Common Lisp.

Não estou dizendo que seu objetivo final deve ser uma implementação do Lisp; mas a homoiconicidade tem seus benefícios ao aprender a implementar uma linguagem dinâmica; por que lidar com problemas de sintaxe quando você pode aprender em um idioma em que a sintaxe idiomática é idêntica ao AST de um idioma que usa um lexer / parser?

De qualquer forma ... apenas uma sugestão. Mas é por uma boa razão que a maioria das grandes linguagens de programação desde C possui pelo menos um pouco da natureza Lisp.

Jason Lewis
fonte
1
Eu gostaria de poder aceitar duas respostas. Obrigado, acho que realmente implementarei um intérprete Lisp. É fácil de analisar, possui muita documentação e código existente e deve me fornecer uma base para trabalhar. Infelizmente eu levei uma classe de graduação que usou Esquema e isso me fez arrancar meus cabelos;)
Austin Henley
1
Agora estou tentado a compilar minha língua em meu próprio dialeto do Lisp!
Austin Henley
1
Veja também Lisp em pequenos pedaços
coredump
0

Eu coloquei isso (~ 600 linhas de C #) no domínio público, que suporta quote / list / apply / eval / test / etc, e permite personalizar uma sintaxe do tipo Lisp e / ou os recursos semânticos facilmente:

https://repl.it/CdjV/3

Por exemplo:

        var factorial = (Lambda)language.
            Evaluate
            (@"
                ( => ( n ) (
                        ? ( != n 0 )
                        ( * n ( this ( - n 1 ) ) )
                        1
                    )
                )
            ");

        var sw = new System.Diagnostics.Stopwatch();
        var n = 12;
        var r = 0;
        int k;
        sw.Start();
        for (k = 0; k < 10000; k++)
        {
            r = (int)factorial.Invoke(null, n);
        }
        sw.Stop();
        Console.WriteLine("{0}! = {1}", n, r);
        Console.WriteLine();
        Console.WriteLine("in {0} ms (for {1} times)", sw.ElapsedMilliseconds, k.ToString("0,0"));

«HTH,

YSharp
fonte
0

Supondo que você conheça um pouco de Scheme (por exemplo, tenha lido o SICP ) ou o Lisp, recomendo o livro Lisp In Small Pieces do Queinnec . Ele explica várias variantes de intérpretes e compiladores do tipo Lisp (incluindo o bytecode ou o C).

Leia também Pragmática da linguagem de programação de Scott , o mais recente Dragon Book , o manual do GC , os tipos e as linguagens de programação de Pierce .

Estou buscando informações sobre como passar de um compilador para um intérprete.

Então, a avaliação parcial (projeções de Futamura) e o estilo de passagem de continuação podem ser relevantes.

Basile Starynkevitch
fonte