Estou me preparando para fazer um projeto paralelo que tem o objetivo de traduzir o código de uma linguagem de programação para outra. As linguagens com as quais estou começando são PHP e Python (Python para PHP deve ser mais fácil de começar), mas de preferência eu seria capaz de adicionar outras linguagens com (relativa) facilidade. O plano é:
Isso é voltado para o desenvolvimento web. O código original e o código-alvo ficarão sobre os frameworks (que também terei que escrever). Essas estruturas adotarão um padrão de design MVC e seguirão convenções de codificação rígidas. Isso deve tornar a tradução um pouco mais fácil.
Também estou analisando o IOC e a injeção de dependência, pois eles podem tornar o processo de tradução mais fácil e menos sujeito a erros.
Vou usar o módulo analisador do Python , que me permite mexer na árvore de sintaxe abstrata. Aparentemente, o mais próximo que posso chegar com PHP é token_get_all () , que é um começo.
A partir daí posso construir o AST, tabelas de símbolos e fluxo de controle.
Então acredito que posso começar a produzir o código. Não preciso de uma tradução perfeita . Ainda terei que revisar o código gerado e corrigir os problemas. Idealmente, o tradutor deve sinalizar traduções problemáticas.
Antes que você pergunte "Qual é o objetivo disso?" A resposta é ... Será uma experiência de aprendizado interessante. Se você tiver alguma ideia sobre como tornar isso menos assustador, entre em contato.
EDITAR:
Estou mais interessado em saber que tipos de padrões eu poderia aplicar no código para torná-lo mais fácil de traduzir (ou seja: IoC, SOA?) Do código do que como fazer a tradução.
fonte
Respostas:
Eu tenho construído ferramentas (DMS Software Reengineering Toolkit) para fazer manipulação de programas de propósito geral (com tradução de linguagem sendo um caso especial) desde 1995, apoiado por uma forte equipe de cientistas da computação. O DMS fornece análise genérica, construção de AST, tabelas de símbolos, análise de fluxo de dados e controle, aplicação de regras de tradução, regeneração do texto fonte com comentários, etc., tudo parametrizado por definições explícitas de linguagens de computador.
A quantidade de maquinário de que você precisa para fazer isso bem é vasta (especialmente se você quiser ser capaz de fazer isso para várias linguagens de uma maneira geral), e então você precisa de analisadores confiáveis para linguagens com definições não confiáveis (PHP é um exemplo perfeito disso )
Não há nada de errado em você pensar em construir um tradutor de idioma para idioma ou tentar fazê-lo, mas acho que você achará essa tarefa muito maior para idiomas reais do que espera. Temos cerca de 100 homens-ano investidos apenas em DMS e outros 6-12 meses em cada definição de linguagem "confiável" (incluindo aquela que construímos penosamente para PHP), muito mais para linguagens desagradáveis como C ++. Será um "inferno de uma experiência de aprendizado"; tem sido para nós. (Você pode achar a seção de artigos técnicos no site acima interessante para impulsionar esse aprendizado).
Muitas vezes as pessoas tentam construir algum tipo de maquinário generalizado, começando com alguma peça de tecnologia com a qual estão familiarizados, que faz parte do trabalho. (Python ASTs são um ótimo exemplo). A boa notícia é que parte do trabalho está feito. A má notícia é que o maquinário tem um zilhão de suposições embutidas, a maioria das quais você não descobrirá até que tente lutar para fazer outra coisa. Nesse ponto, você descobre que o maquinário está programado para fazer o que faz originalmente e realmente resistirá à sua tentativa de fazer algo diferente. (Eu suspeito que tentar obter o Python AST para modelar o PHP será muito divertido).
A razão pela qual comecei a construir o DMS originalmente foi construir fundações que tinham muito poucas dessas suposições embutidas. Algumas delas nos dão dores de cabeça. Até agora, nenhum buraco negro. (A parte mais difícil do meu trabalho nos últimos 15 anos é tentar evitar que tais suposições se insinuem).
Muitas pessoas também cometem o erro de presumir que, se puderem analisar (e talvez obter um AST), estarão no caminho certo para fazer algo complicado. Uma das lições difíceis é que você precisa de tabelas de símbolos e análise de fluxo para fazer uma boa análise ou transformação do programa. Os ASTs são necessários, mas não suficientes. Esta é a razão pela qual o livro do compilador de Aho & Ullman não pára no capítulo 2. (O OP tem esse direito, pois está planejando construir maquinário adicional além do AST). Para obter mais informações sobre este tópico, consulte Vida após análise .
O comentário sobre "Não preciso de uma tradução perfeita" é problemático. O que os tradutores fracos fazem é converter os 80% "fáceis" do código, deixando os 20% difíceis para fazer manualmente. Se o aplicativo que você pretende converter for muito pequeno e você só pretende convertê-lo bem uma vez, esses 20% estão OK. Se você deseja converter muitos aplicativos (ou mesmo o mesmo com pequenas alterações ao longo do tempo), isso não é bom. Se você tentar converter 100K SLOC então 20% são 20.000 linhas originais de código que são difíceis de traduzir, entender e modificar no contexto de outras 80.000 linhas de programa traduzido que você ainda não entende. Isso exige um grande esforço. No nível de um milhão de linhas, isso é simplesmente impossível na prática.mais difícil e eles normalmente descobrem dolorosamente com longos atrasos, altos custos e, muitas vezes, falha total.)
O que você precisa almejar para traduzir sistemas em grande escala são altas taxas de conversão percentuais dos anos noventa, ou é provável que você não consiga concluir a parte manual da atividade de tradução.
Outra consideração importante é o tamanho do código a ser traduzido. É preciso muita energia para construir um tradutor robusto e funcional, mesmo com boas ferramentas. Embora pareça atraente e legal construir um tradutor em vez de simplesmente fazer uma conversão manual, para pequenas bases de código (por exemplo, até cerca de 100K SLOC em nossa experiência) a economia simplesmente não o justifica. Ninguém gosta dessa resposta, mas se você realmente tiver que traduzir apenas 10K SLOC de código, provavelmente será melhor apenas morder o marcador e fazê-lo. E sim, isso é doloroso.
Eu considero nossas ferramentas extremamente boas (mas sou bastante tendencioso). E ainda é muito difícil construir um bom tradutor; leva cerca de 1,5-2 anos-homem e sabemos como usar nossas ferramentas. A diferença é que, com tanto maquinário, temos muito mais sucesso do que falha.
fonte
Minha resposta tratará da tarefa específica de analisar Python para traduzi-lo para outra linguagem, e não dos aspectos de nível superior que Ira abordou bem em sua resposta.
Resumindo: não use o módulo parser, existe uma maneira mais fácil.
O
ast
módulo, disponível desde o Python 2.6, é muito mais adequado às suas necessidades, pois oferece um AST pronto para trabalhar. Escrevi um artigo sobre isso no ano passado, mas, em resumo, use oparse
método deast
para analisar o código-fonte do Python em um AST. Oparser
módulo fornecerá uma árvore de análise, não um AST. Desconfie da diferença .Agora, como os ASTs do Python são bastante detalhados, dado um AST, o trabalho de front-end não é terrivelmente difícil. Suponho que você possa ter um protótipo simples para algumas partes da funcionalidade prontas rapidamente. Porém, chegar a uma solução completa levará mais tempo, principalmente porque a semântica das linguagens é diferente. Um subconjunto simples da linguagem (funções, tipos básicos e assim por diante) pode ser prontamente traduzido, mas assim que você entrar nas camadas mais complexas, precisará de maquinário pesado para emular o núcleo de uma linguagem em outra. Por exemplo, considere os geradores de Python e as compreensões de lista que não existem no PHP (até onde sei, o que é reconhecidamente ruim quando o PHP está envolvido).
Para lhe dar uma dica final, considere a
2to3
ferramenta criada pelos desenvolvedores do Python para traduzir o código do Python 2 para o código do Python 3. No front-end, ele contém a maioria dos elementos de que você precisa para traduzir Python em algo . No entanto, como os núcleos do Python 2 e 3 são semelhantes, nenhum mecanismo de emulação é necessário.fonte
2to3
é apenas AST para AST. Ele não oferece suporte para fazer nada que vá além dos recursos doast
módulo. Observe que todas as traduções vão da sintaxe suportada pelo processo host python para a sintaxe suportada pelo processo host python. Não há tradutor que adicione, digamos, anotações de função, porque o 2.6 não oferece suporte para isso.2to3
pode ser visto como um exemplo de uso do AST gerado a partirast
.Escrever um tradutor não é impossível, especialmente considerando que o estagiário de Joel fez isso durante um verão.
Se você quiser fazer um idioma, é fácil. Se você quiser fazer mais, é um pouco mais difícil, mas não muito. A parte mais difícil é que, embora qualquer linguagem completa possa fazer o que outra linguagem completa faz, os tipos de dados integrados podem mudar o que uma linguagem faz de maneira fenomenal.
Por exemplo:
requer muito código C ++ para duplicar (ok, bem, você pode fazer isso de forma bem curta com algumas construções de loop, mas ainda assim).
Isso é um pouco à parte, eu acho.
Você já escreveu um tokenizer / analisador baseado em uma gramática de linguagem? Você provavelmente vai querer aprender como fazer isso, se ainda não o fez, porque essa é a parte principal deste projeto. O que eu faria é criar uma sintaxe completa de Turing básica - algo bastante semelhante ao bytecode Python . Em seguida, você cria um lexer / analisador que pega uma gramática de linguagem (talvez usando BNF ) e, com base na gramática, compila a linguagem em sua linguagem intermediária. Então, o que você vai querer fazer é fazer o inverso - criar um analisador de seu idioma para os idiomas de destino com base na gramática.
O problema mais óbvio que vejo é que no início você provavelmente criará um código terrivelmente ineficiente, especialmente em linguagens mais poderosas * como Python.
Mas se você fizer isso dessa maneira, provavelmente será capaz de descobrir maneiras de otimizar a saída à medida que avança. Para resumir:
* por poderoso, quero dizer que leva 4 linhas:
Mostre-me outra linguagem que pode fazer algo parecido em 4 linhas, e eu mostrarei a você uma linguagem que é tão poderosa quanto Python.
fonte
Existem algumas respostas dizendo para você não se preocupar. Bem, quão útil é isso? Você quer aprender? Você pode aprender. Esta é uma compilação. Acontece que sua linguagem de destino não é um código de máquina, mas outra linguagem de alto nível. Isso é feito o tempo todo.
Existe uma maneira relativamente fácil de começar. Primeiro, vá buscar http://sourceforge.net/projects/lime-php/ (se você quiser trabalhar em PHP) ou algo parecido e vá até o código de exemplo. Em seguida, você pode escrever um analisador léxico usando uma sequência de expressões regulares e tokens de alimentação para o analisador que você gerar. Suas ações semânticas podem produzir código diretamente em outra linguagem ou construir alguma estrutura de dados (pense em objetos, cara) que você pode massagear e atravessar para gerar código de saída.
Você tem sorte com PHP e Python porque em muitos aspectos eles são a mesma linguagem, mas com sintaxe diferente. A parte difícil é superar as diferenças semânticas entre as formas gramaticais e as estruturas de dados. Por exemplo, Python possui listas e dicionários, enquanto PHP possui apenas matrizes associadas.
A abordagem do "aluno" consiste em construir algo que funcione bem para um subconjunto restrito da linguagem (como apenas declarações impressas, matemática simples e atribuição de variáveis) e, em seguida, remover progressivamente as limitações. Isso é basicamente o que todos os "grandes" da área faziam.
Ah, e como você não tem tipos estáticos no Python, pode ser melhor escrever e confiar em funções PHP como "python_add", que adiciona números, strings ou objetos de acordo com a maneira como Python faz isso.
Obviamente, isso pode ficar muito maior se você permitir.
fonte
Vou apoiar o ponto de vista de @EliBendersky em relação ao uso de ast.parse em vez de analisador (sobre o qual eu não sabia antes). Eu também recomendo vivamente que você analise seu blog. Usei ast.parse para fazer Python-> tradutor JavaScript (@ https://bitbucket.org/amirouche/pythonium ). Eu vim com o design do Pythonium revisando um pouco outras implementações e testando-as sozinho. Eu criei um fork do Pythonium de https://github.com/PythonJS/PythonJS, que também comecei. Na verdade, é uma reescrita completa. O design geral é inspirado no papel PyPy e http://www.hpl.hp.com/techreports/Compaq-DEC/WRL-89-1.pdf .
Tudo o que tentei, do início à melhor solução, mesmo que pareça marketing do Pythonium, na verdade não (não hesite em me dizer se algo não parecer correto para a netiqueta):
Implementar a semântica do Python em Plain Old JavaScript usando herança de protótipo: AFAIK é impossível implementar herança múltipla de Python usando sistema de objeto de protótipo JS. Eu tentei fazer isso usando outros truques posteriormente (cf. getattribute). Até onde eu sei, não há implementação de herança múltipla em Python em JavaScript, o melhor que existe é Herança única + mixins e não tenho certeza se eles lidam com herança de diamante. Mais ou menos semelhante ao Skulpt, mas sem google clojure.
Eu tentei com o Google clojure, assim como o Skulpt (compilador) em vez de realmente ler o código #fail do Skulpt. De qualquer forma, por causa do sistema de objetos baseado em protótipo JS ainda é impossível. Criar vinculação foi muito difícil, você precisa escrever JavaScript e muito código padrão (cf. https://github.com/skulpt/skulpt/issues/50 onde eu sou o fantasma). Naquela época, não havia uma maneira clara de integrar a vinculação no sistema de compilação. Acho que o Skulpt é uma biblioteca e você só tem que incluir seus arquivos .py no html para serem executados, nenhuma fase de compilação exigida pelo desenvolvedor.
Tentei pyjaco (compilador), mas criar vínculos (chamar código Javascript do código Python) era muito difícil, havia muito código clichê para criar todas as vezes. Agora acho que o pyjaco é o que está mais perto do Pythonium. pyjaco é escrito em Python (ast.parse também), mas muito é escrito em JavaScript e usa herança de protótipo.
Na verdade, nunca tive sucesso em executar Pijamas #fail e nunca tentei ler o código #fail novamente. Mas, em minha mente, pijamas estava fazendo tradução de API-> API (ou framework para framework) e não tradução de Python para JavaScript. A estrutura JavaScript consome dados que já estão na página ou dados do servidor. O código Python é apenas "encanamento". Depois disso, descobri que pijama era na verdade um verdadeiro tradutor python-> js.
Ainda assim, acho que é possível fazer tradução API-> API (ou framework-> framework) e isso é basicamente o que faço no Pythonium, mas em um nível inferior. Provavelmente pijamas usam o mesmo algoritmo que Pythonium ...
Então descobri o brython totalmente escrito em Javascript como o Skulpt, sem necessidade de compilação e muito fluff ... mas escrito em JavaScript.
Desde a linha inicial escrita no decorrer deste projeto, eu conhecia o PyPy, até mesmo o back-end JavaScript para PyPy. Sim, você pode, se encontrar, gerar diretamente um interpretador Python em JavaScript a partir do PyPy. As pessoas dizem que foi um desastre. Eu não li por quê. Mas acho que a razão é que a linguagem intermediária que eles usam para implementar o interpretador, RPython, é um subconjunto do Python adaptado para ser traduzido para C (e talvez asm). Ira Baxter diz que você sempre faz suposições ao construir algo e provavelmente faz o ajuste fino para ser o melhor no que se refere ao PyPy: tradução Python-> C. Essas suposições podem não ser relevantes em outro contexto, e pior, elas podem inferir sobrecarga, caso contrário, a tradução direta provavelmente sempre será melhor.
Ter o interpretador escrito em Python parecia uma (muito) boa ideia. Mas eu estava mais interessado em um compilador por motivos de desempenho, também é realmente mais fácil compilar Python para JavaScript do que interpretá-lo.
Comecei o PythonJS com a ideia de reunir um subconjunto do Python que pudesse facilmente traduzir para JavaScript. No início, nem me preocupei em implementar o sistema OO por causa da experiência anterior. O subconjunto de Python que consegui traduzir para JavaScript é:
Isso parece muito, mas na verdade é muito restrito em comparação com a semântica desenvolvida do Python. É realmente JavaScript com sintaxe Python.
O JS gerado é perfeito, ou seja. não há sobrecarga, não pode ser melhorado em termos de desempenho editando-o posteriormente. Se você pode melhorar o código gerado, também pode fazê-lo a partir do arquivo-fonte Python. Além disso, o compilador não contou com nenhum truque JS que você possa encontrar em .js escritos por http://superherojs.com/ , então é muito legível.
O descendente direto desta parte do PythonJS é o modo Pythonium Veloce. A implementação completa pode ser encontrada em @ https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/veloce/veloce.py?at=master 793 SLOC + cerca de 100 SLOC de código compartilhado com o outro tradutor.
Uma versão adaptada de pystones.py pode ser traduzida no modo Veloce cf. https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pystone/?at=master
Depois de configurar a tradução básica do Python-> JavaScript, escolhi outro caminho para traduzir o Python completo para o JavaScript. A maneira de glib fazer código baseado em classe orientada a objetos, exceto a linguagem alvo é JS, para que você tenha acesso a arrays, objetos semelhantes a mapas e muitos outros truques e toda essa parte foi escrita em Python. IIRC não há código javascript escrito por no tradutor Pythonium. Obter herança única não é difícil, aqui estão as partes difíceis de tornar o Pythonium totalmente compatível com o Python:
spam.egg
em Python é sempre traduzido paragetattribute(spam, "egg")
Não fiz o perfil disso em particular, mas acho que perdi muito tempo e não tenho certeza se posso melhorar com asm.js ou qualquer outra coisa.Esta parte é fatorada em https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/runtime.py?at=master Está escrito em Python compatível com Python Veloce.
O tradutor compatível real https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/compliant.py?at=master não gera código JavaScript diretamente e, o mais importante, não faz a transformação ast-> ast . Eu tentei fazer ast-> ast e ast mesmo se melhor que cst não é bom trabalhar mesmo com ast.NodeTransformer e mais importante, eu não preciso fazer ast-> ast.
Fazer python ast para python ast no meu caso, pelo menos, talvez seja uma melhoria de desempenho, já que às vezes eu inspeciono o conteúdo de um bloco antes de gerar o código associado a ele, por exemplo:
Portanto, não visito cada nó uma vez para cada fase da tradução.
O processo geral pode ser descrito como:
Python builtins são escritos em código Python (!), IIRC há algumas restrições relacionadas aos tipos de bootstraping, mas você tem acesso a tudo que pode traduzir Pythonium em modo compatível. Dê uma olhada em https://bitbucket.org/amirouche/pythonium/src/33898da731ee2d768ced392f1c369afd746c25d7/pythonium/compliant/builtins/?at=master
A leitura do código JS gerado em compatível com pythonium pode ser entendida, mas os mapas de origem ajudarão muito.
O conselho valioso que posso lhe dar à luz desta experiência são peidos velhos e gentis:
Com o modo Python Veloce apenas, estou muito feliz! Mas ao longo do caminho descobri que o que eu realmente estava procurando era liberar a mim e aos outros do Javascript, mas mais importante, ser capaz de criar de uma maneira confortável. Isso me levou a Scheme, DSL, Models e, eventualmente, modelos específicos de domínio (cf. http://dsmforum.org/ ).
Sobre a resposta de Ira Baxter:
As estimativas não ajudam em nada. Eu tirei mais ou menos 6 meses de tempo livre para PythonJS e Pythonium. Portanto, posso esperar mais de 6 meses em tempo integral. Acho que todos nós sabemos o que 100 homens-ano em um contexto empresarial pode significar e não significar de forma alguma ...
Quando alguém diz que algo é difícil ou mais frequentemente impossível, eu respondo que "só leva tempo para encontrar uma solução para um problema que é impossível", caso contrário, nada é impossível, exceto se for provado impossível neste caso uma prova matemática ...
Se não for impossível, então há espaço para a imaginação:
e
ou
Não é apenas um pensamento otimista. Quando comecei o Python-> Javascript, todo mundo dizia que era impossível. PyPy impossível. Metaclasses muito difíceis. etc ... Eu acho que a única revolução que traz o PyPy sobre o papel Scheme-> C (que tem 25 anos) é alguma geração JIT automática (dicas baseadas escritas no interpretador RPython, eu acho).
A maioria das pessoas que dizem que uma coisa é "difícil" ou "impossível" não fornece as razões. C ++ é difícil de analisar? Eu sei disso, eles ainda são analisadores de C ++ (gratuitos). O mal está nos detalhes? Eu sei disso. Dizer que é impossível sozinho não ajuda. É ainda pior do que "não ajuda", é desanimador e algumas pessoas pretendem desencorajar outras. Eu ouvi sobre essa questão via /programming/22621164/how-to-automatically-generate-a-parser-code-to-code-translator-from-a-corpus .
O que seria perfeição para você ? É assim que você define a próxima meta e talvez alcance a meta geral.
Não vejo padrões que não possam ser traduzidos de um idioma para outro, pelo menos de uma forma menos que perfeita. Como a tradução de idioma para idioma é possível, é melhor você tentar isso primeiro. Já que, eu acho que de acordo com http://en.wikipedia.org/wiki/Graph_isomorphism_problem , a tradução entre duas linguagens de computador é uma árvore ou isomorfismo DAG. Mesmo que já saibamos que os dois estão se tornando completos, então ...
Framework-> Framework que eu visualizo melhor como API-> tradução de API ainda pode ser algo que você deve manter em mente como uma forma de melhorar o código gerado. Ex: Prolog como uma sintaxe muito específica, mas ainda assim você pode fazer Prolog como computação, descrevendo o mesmo gráfico em Python ... Se eu fosse implementar um tradutor de Prolog para Python, não implementaria a unificação em Python, mas em uma biblioteca C e viria com uma "sintaxe Python" muito legível para um pythonista. No final, a sintaxe é apenas "pintura" para a qual damos um significado (é por isso que comecei o esquema). O mal está nos detalhes da linguagem e não estou falando sobre a sintaxe. Os conceitos que são usados na linguagem getattributegancho (você pode viver sem ele), mas os recursos de VM necessários, como otimização de recursão de cauda, podem ser difíceis de lidar. Você não se importa se o programa inicial não usa recursão de cauda e mesmo se não houver recursão de cauda no idioma de destino, você pode emulá-lo usando greenlets / loop de evento.
Para idiomas de destino e de origem, procure por:
Disto surgirá:
Você provavelmente também saberá o que será traduzido em código rápido e lento.
Também existe a questão do stdlib ou de qualquer biblioteca, mas não há uma resposta clara, depende de seus objetivos.
Código idiomático ou código gerado legível também tem soluções ...
Almejar uma plataforma como o PHP é muito mais fácil do que almejar navegadores, pois você pode fornecer implementação C de caminho lento e / ou crítico.
Considerando que seu primeiro projeto é traduzir Python para PHP, pelo menos para o subconjunto PHP3 que conheço, personalizar veloce.py é sua melhor aposta. Se você pode implementar veloce.py para PHP, então provavelmente você será capaz de executar o modo compatível ... Além disso, se você pode traduzir PHP para o subconjunto de PHP que você pode gerar com php_veloce.py, significa que você pode traduzir PHP para o subconjunto de Python que veloce.py pode consumir, o que significa que você pode traduzir PHP para Javascript. Apenas dizendo...
Você também pode dar uma olhada nessas bibliotecas:
Você também pode estar interessado nesta postagem do blog (e comentários): https://www.rfk.id.au/blog/entry/pypy-js-poc-jit/
fonte
Você poderia dar uma olhada no compilador Vala , que traduz Vala (uma linguagem semelhante a C #) em C.
fonte