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 é:
- função com parâmetros semânticos completos, tanto na definição quanto na chamada. Esta é a parte de que mais me orgulho.
- while / if / elif / else
- Os tipos Python foram convertidos em tipos JavaScript (não há nenhum tipo de Python)
- pois poderia iterar em matrizes Javascript apenas (para uma matriz in)
- Acesso transparente ao JavaScript: se você escrever Array no código Python, ele será traduzido para Array em javascript. Esta é a maior conquista em termos de usabilidade sobre seus concorrentes.
- Você pode passar a função definida no código-fonte Python para funções javascript. Argumentos padrão serão levados em consideração.
- Ele adiciona tem uma função especial chamada new que é traduzida para JavaScript new, por exemplo: new (Python) (1, 2, spam, "ovo") é traduzido como "novo Python (1, 2, spam," ovo ").
- "var" são tratados automaticamente pelo tradutor. (muito bom achado de Brett (contribuidor PythonJS).
- palavra-chave global
- fechamentos
- lambdas
- listar compreensões
- as importações são suportadas por meio de requirejs
- herança de classe única + mixin via classyjs
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 para getattribute(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.
- ordem de resolução do método: mesmo com o algoritmo escrito em Python, traduzi-lo para código compatível com Python Veloce foi um grande esforço.
- getattributre : o algoritmo de resolução getattribute real é meio complicado e ainda não suporta descritores de dados
- com base em classe de metaclasse: eu sei onde inserir o código, mas ainda ...
- por último, mas não menos importante: some_callable (...) é sempre traduzido para "call (some_callable)". O tradutor AFAIK não usa inferência de forma alguma, então cada vez que você faz uma chamada, você precisa verificar que tipo de objeto deve chamá-lo da maneira que deve ser chamado.
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:
- var / global: para poder var algo, devo saber o que preciso e não var. Em vez de gerar um bloco de rastreamento de quais variáveis são criadas em um determinado bloco e inseri-lo no topo do bloco de funções gerado, eu apenas procuro por atribuição de variável relevante quando entro no bloco antes de realmente visitar o nó filho para gerar o código associado.
- rendimento, os geradores têm, até o momento, uma sintaxe especial em JS, então preciso saber qual função Python é um gerador quando quero escrever a "var my_generator = function"
Portanto, não visito cada nó uma vez para cada fase da tradução.
O processo geral pode ser descrito como:
Python source code -> Python ast -> Python source code compatible with Veloce mode -> Python ast -> JavaScript source code
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:
- revisar extensivamente o assunto tanto na literatura quanto em projetos existentes de código-fonte fechado ou gratuito. Quando revisei os diferentes projetos existentes, deveria ter dedicado muito mais tempo e motivação.
- pergunte! Se eu soubesse de antemão que o back-end PyPy era inútil por causa da sobrecarga devido à incompatibilidade semântica C / Javascript. Eu talvez tivesse tido a ideia do Pythonium antes de 6 meses atrás, talvez 3 anos atrás.
- saiba o que quer fazer, tenha um alvo. Para este projeto eu tinha diferentes objetivos: praticar um pouco um javascript, aprender mais sobre Python e ser capaz de escrever código Python que rodaria no navegador (mais e mais abaixo).
- fracasso é experiência
- um pequeno passo é um passo
- Comece pequeno
- sonhe grande
- fazer demos
- iterar
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:
- encontrar uma prova provando que é impossível
e
- Se for impossível, pode haver um problema "inferior" que pode ter uma solução.
ou
- se não for impossível, encontrar uma solução
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.
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.
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:
- Ideias grandes e específicas
- Ideias pequenas e comuns compartilhadas
Disto surgirá:
- Coisas que são fáceis de traduzir
- Coisas que são difíceis de traduzir
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/