Temos muitas linguagens de programação. Cada idioma é analisado e a sintaxe verificada antes de ser convertida em código, para que uma árvore de sintaxe abstrata (AST) seja criada.
Temos essa árvore de sintaxe abstrata, por que não armazenamos essa árvore de sintaxe em vez do código-fonte (ou ao lado do código-fonte)?
Usando um AST em vez do código fonte. Todo programador de uma equipe pode serializar essa árvore para qualquer idioma que desejar (com a gramática livre de contexto apropriada) e analisar novamente o AST quando terminar. Portanto, isso eliminaria o debate sobre as questões de estilo de codificação (onde colocar {e}, onde colocar espaços em branco, recuo etc.)
Quais são os prós e os contras dessa abordagem?
language-agnostic
Calmarius
fonte
fonte
Respostas:
Espaço em branco e comentários
Geralmente, um AST não inclui espaço em branco, terminadores de linha e comentários.
Formatação significativa
Você está certo de que na maioria dos casos isso é positivo (elimina a formatação de guerras santas), há muitos casos em que a formatação do código original transmite algum significado, como em literais de strings de várias linhas e "parágrafos de código" (separando blocos de instruções com uma linha vazia).
Código que não pode ser compilado
Embora muitos analisadores sejam muito resilientes à falta de sintaxe, o código com erros geralmente resulta em uma árvore de sintaxe muito estranha, que é fina e elegante até o ponto em que o usuário recarrega o arquivo. Você já cometeu um erro no seu IDE e, de repente, o arquivo inteiro tem "rabiscos"? Imagine como isso seria recarregado em outro idioma.
Talvez os usuários não comprometam código não analisável, mas certamente precisam salvar localmente.
Não há dois idiomas que correspondam perfeitamente
Como outros já apontaram, quase não existem dois idiomas com paridade de recursos perfeita. O mais próximo que posso pensar é VB e C #, ou JavaScript e CoffeeScript, mas mesmo assim o VB possui recursos como XML Literals que não possuem equivalentes em C #, e a conversão de JavaScript em CoffeeScript pode resultar em muitos literais JavaScript.
Experiência pessoal:Em um aplicativo de software que eu escrevo, precisamos fazer isso, pois os usuários devem digitar expressões "em inglês simples" que são convertidas em JS em segundo plano. Consideramos apenas armazenar a versão JS, mas não encontramos uma maneira aceitável de fazê-lo, carregado e descarregado de maneira confiável, por isso acabamos sempre armazenando o texto do usuário e a versão JS, além de um sinalizador que indicava "o inglês simples "versão analisada perfeitamente ou não.
fonte
De fato, essa é uma ideia razoável. A Microsoft teve um projeto de pesquisa nos anos 90 para fazer quase exatamente isso .
Vários cenários vêm à mente.
O primeiro é bastante trivial; como você diz, o AST pode ser renderizado em diferentes visualizações, dependendo das preferências de diferentes programadores para espaçamento e assim por diante. Mas armazenar um AST é um exagero para esse cenário; basta escrever para você uma bonita impressora. Quando você carrega um arquivo no seu editor, execute a impressora bonita para colocá-lo no formato preferido e volte ao formato original quando você o salvar.
O segundo é mais interessante. Se você pode armazenar a árvore de sintaxe abstrata, a alteração da diferença de código torna-se não textual, mas sintática. As refatorações nas quais o código é movido se tornam muito mais fáceis de entender. O lado negativo é claro que escrever os algoritmos de diferença de árvore não é exatamente trivial e geralmente precisa ser feito por idioma. O diff de texto funciona para quase qualquer idioma.
O terceiro é mais parecido com o que Simonyi imaginou para a Programação Intencional: que os conceitos fundamentais comuns às linguagens de programação são os que são serializados, e então você tem visões diferentes desses conceitos renderizados em linguagens diferentes. Embora seja uma idéia bonita, o fato feio é que os idiomas são suficientemente diferentes em seus detalhes que uma abordagem de menor denominador comum realmente não funciona.
Portanto, em suma, é uma idéia adorável, mas é uma quantidade enorme de trabalho extra para um benefício comparativamente pequeno. É por isso que quase ninguém faz isso.
fonte
Você poderia argumentar que esse é exatamente o código de bytes no .NET. De fato, o programa refletor do redgate converte o código de bytes de volta em várias linguagens de programação .NET.
No entanto, existem problemas. A sintaxe é um idioma específico, pois há coisas que você pode representar em um idioma que não tem representação em outros idiomas. Isso ocorre no .NET com C ++, sendo a única linguagem .NET que tem acesso aos 7 níveis de acesso.
Fora do ambiente .NET, fica muito mais complicado. Cada idioma começa a ter seu próprio conjunto de bibliotecas associadas. Não seria possível refletir uma sintaxe genérica em C e Java que refletisse a mesma execução de instruções, pois eles resolvem problemas simulados de maneiras muito diferentes.
fonte
Eu meio que gosto de algumas de suas idéias, mas você está superestimando significativamente o quão fácil é traduzir um idioma para outro. Se fosse assim tão fácil, você nem precisaria armazenar o AST, pois sempre era possível analisar o idioma X no AST e depois passar do AST para o idioma Y.
No entanto, desejo que as especificações do compilador pensem um pouco mais sobre a exposição de algumas das AST através de algum tipo de API. Coisas como programação orientada a aspectos, refatoração e análise estática de programas podem ser implementadas por meio dessa API, sem que o implementador desses recursos precise refazer grande parte do trabalho já implementado pelos criadores de compiladores.
É estranho a frequência com que a estrutura de dados do programador para representar um programa é como um monte de arquivos contendo seqüências de caracteres.
fonte
Eu acho que os pontos mais salientes são os seguintes:
Não há benefício. Você disse que isso significaria que todos poderiam usar sua linguagem de estimação. Mas isso não é verdade - usar uma representação em árvore de sintaxe eliminaria apenas diferenças sintáticas, mas não semânticas. Funciona até certo ponto para linguagens muito semelhantes - como VB e C #, ou Java e Scala. Mas nem mesmo lá completamente.
É problemático. Você ganhou liberdade de linguagem, mas perdeu a liberdade de ferramentas. Você não pode mais ler e editar o código em um editor de texto ou em qualquer IDE - depende de uma ferramenta específica que fala sua representação AST para ler e editar o código. Não há nada ganho aqui.
Para ilustrar esse último ponto, dê uma olhada no RealBasic, que é uma implementação proprietária de um poderoso dialeto BASIC. Por um tempo, quase parecia que o idioma poderia decolar, mas era completamente dependente do fornecedor, a ponto de você só poder ver o código no IDE deles, pois ele foi salvo em um formato proprietário sem texto. Grande erro.
fonte
astyle
UnniversalIndent. Não há necessidade de formatos binários arcanos.Penso que, se você armazena o texto e o AST, não adicionou realmente nada de útil, pois o texto já está em um idioma e o AST pode ser rapidamente reconstruído a partir do texto.
Por outro lado, se você armazenar apenas o AST, perderá coisas como comentários que não podem ser recuperados.
fonte
Acredito que a idéia seja interessante em teoria, mas não muito prática, pois diferentes linguagens de programação suportam construções diferentes, algumas que não têm equivalentes em outras linguagens.
Por exemplo, o X ++ possui uma instrução 'while select' que não poderia ser escrita em C # sem muito código extra (classes extras, lógica extra, etc.). http://msdn.microsoft.com/en-us/library/aa558063.aspx
O que estou dizendo aqui é que muitas línguas têm açúcares sintáticos que se traduzem em grandes blocos de código da mesma língua ou mesmo em elementos que não existem em outras. Aqui está um exemplo do porquê a abordagem AST não funcionará:
O idioma X possui uma palavra-chave K traduzida, em AST, em 4 instruções: S1, S2, S3 e S4. O AST agora está traduzido no idioma Y e um programador altera S2. Agora, o que acontece com a tradução de volta para o X? O código é traduzido como 4 instruções em vez de uma única palavra-chave ...
O último argumento contra a abordagem AST são as funções da plataforma: o que acontece quando uma função é incorporada na plataforma? Como o Environment.GetEnvironmentVariable do .NET. Como você traduz isso?
fonte
Existe um sistema construído em torno dessa idéia: JetBrains MPS . Um editor é um pouco estranho ou apenas diferente, mas em geral não é um problema tão grande. O maior problema é, bem, isso não é um texto mais, então você não pode usar qualquer uma das ferramentas normais baseados em texto - outros editores,
grep
,sed
, Junção e Diferenças ferramentas, etc.fonte
Na verdade, existem vários produtos, geralmente conhecidos como "bancadas de trabalho de idiomas" que armazenam ASTs e apresentam, em seus editores, uma "projeção" do AST de volta para um idioma específico. Como a @ sk-logic disse, o MPS do JetBrains é um desses sistemas. Outra é a bancada de trabalho intencional da Intentional Software.
O potencial para bancadas de trabalho de idiomas parece muito alto, principalmente na área de idiomas específicos de domínio, pois você pode criar uma projeção específica de domínio. Por exemplo, o intencional demonstra uma DSL relacionada à eletricidade que se projeta como um diagrama de circuitos - muito mais fácil e mais preciso para um especialista em domínio discutir e criticar do que um circuito descrito em uma linguagem de programação baseada em texto.
Na prática, as bancadas de trabalho da linguagem demoraram a perceber porque, além do trabalho DSL, os desenvolvedores provavelmente preferem trabalhar em uma linguagem de programação familiar e geral. Quando comparadas frente a frente com um editor de texto ou um IDE de programação, as bancadas de idiomas têm toneladas de sobrecarga e suas vantagens não são tão claras. Nenhuma das bancadas de trabalho de idiomas que eu vi se ajustou ao ponto de poderem facilmente estender seus próprios IDEs - isto é, se as bancadas de trabalho de idiomas são ótimas para produtividade, por que as ferramentas do ambiente de trabalho de linguagem não se tornaram melhores? e melhor a taxas cada vez mais rápidas?
fonte
Você está lendo minha mente.
Quando fiz um curso de compilador, alguns anos atrás, descobri que se você pega um AST e o serializa, com notação de prefixo em vez da notação de infixo usual, e usa parênteses para delimitar instruções inteiras, você obtém o Lisp. Embora eu tenha aprendido sobre o Scheme (um dialeto do Lisp) em meus estudos de graduação, nunca havia realmente apreciado isso. Definitivamente, apreciei o Lisp e seus dialetos, como resultado desse curso.
Problemas com o que você propõe:
é difícil / lento compor um AST em um ambiente gráfico. Afinal, a maioria de nós pode digitar mais rápido do que mover um mouse. E, no entanto, uma pergunta emergente é "como você escreve código de programa com um tablet?" Digitar em um tablet é lento / complicado, comparado a um teclado / laptop com um teclado de hardware. Se você pudesse criar um AST arrastando e soltando componentes de uma paleta em uma tela em um grande dispositivo com tela de toque, a programação em um tablet pode se tornar algo real.
poucas / nenhuma de nossas ferramentas existentes suportam isso. Temos décadas de desenvolvimento envolvidos na criação de IDEs cada vez mais complexos e editores cada vez mais inteligentes. Temos todas essas ferramentas para reformatar o texto, comparar o texto, pesquisar o texto. Onde estão as ferramentas que podem fazer o equivalente a uma pesquisa de expressão regular em uma árvore? Ou um diff de duas árvores? Todas essas coisas são facilmente feitas com texto. Mas eles só podem comparar as palavras. Altere o nome de uma variável, de modo que as palavras sejam diferentes, mas o significado semântico seja o mesmo, e essas ferramentas de comparação enfrentam problemas. Essas ferramentas, desenvolvidas para operar em ASTs em vez de texto, permitiriam que você se aproximasse da comparação do significado semântico. Isso seria uma coisa boa.
Embora transformar código-fonte de programa em AST seja relativamente bem compreendido (temos compiladores e intérpretes, não é?), transformar um AST em código de programa não é tão bem-compreendido. Multiplicar dois números primos para obter um número composto grande é relativamente simples, mas fatorar um número composto grande novamente em números primos é muito mais difícil; é onde estamos com ASTs de análise versus descompilação. É aí que as diferenças entre os idiomas se tornam um problema. Mesmo em um idioma específico, há várias maneiras de descompilar um AST. Iterando através de uma coleção de objetos e obtendo algum tipo de resultado, por exemplo. Use um loop for, iterando através de uma matriz? Isso seria compacto e rápido, mas há limitações. Use um iterador de algum tipo, operando em uma coleção? Essa coleção pode ser de tamanho variável, o que adiciona flexibilidade às (possíveis) despesas de velocidade. Mapear / Reduzir? Mais complexo, mas implicitamente paralelelizável. E isso é apenas para Java, dependendo de suas preferências.
Com o tempo, o esforço de desenvolvimento será despendido e desenvolveremos usando telas sensíveis ao toque e ASTs. A digitação se tornará menos necessária. Vejo isso como uma progressão lógica de onde estamos, observando como usamos computadores hoje. Isso resolverá o problema nº 1.
Já estamos trabalhando com árvores. Lisp é apenas ASTs serializados. XML (e HTML, por extensão) é apenas uma árvore serializada. Para fazer a pesquisa, já temos alguns protótipos: XPath e CSS (para XML e HTML, respectivamente). Quando são criadas ferramentas gráficas que nos permitem criar seletores e modificadores no estilo CSS, teremos resolvido parte do número 2. Quando esses seletores puderem ser estendidos para suportar expressões regulares, estaremos mais próximos. Ainda está procurando uma boa ferramenta gráfica de comparação para comparar dois documentos XML ou HTML. À medida que as pessoas desenvolvem essas ferramentas, o número 2 será resolvido. As pessoas já estão trabalhando nessas coisas; eles simplesmente não estão lá ainda.
A única maneira que vejo de poder descompilar esses ASTs para o texto da linguagem de programação seria algo em busca de objetivos. Se eu estiver modificando o código existente, o objetivo poderá ser alcançado por um algoritmo que torne o código modificado o mais semelhante possível ao código inicial (diferença textual mínima). Se estou escrevendo um código do zero, o objetivo pode ser o menor e mais apertado (provavelmente um loop for). Ou pode ser um código paralelo da maneira mais eficiente possível (provavelmente um mapa / redução ou algo que envolva CSP). Portanto, o mesmo AST pode resultar em código significativamente diferente, mesmo no mesmo idioma, com base em como os objetivos foram definidos. O desenvolvimento de um sistema desse tipo resolveria o problema nº 3. Seria computacionalmente complexo, o que significa que provavelmente precisaríamos de algum tipo de acordo cliente-servidor,
fonte
Se sua intenção é eliminar o debate sobre estilos de formatação, talvez o que você deseja seja um editor que leia um arquivo de origem, o formate de acordo com sua preferência pessoal para exibição e edição, mas ao salvá-lo, reformata o estilo escolhido para a equipe usos.
É muito fácil se você usar um editor como o Emacs . Alterar o estilo de formatação de um arquivo inteiro é uma tarefa de três comandos.
Você também deve criar ganchos para transformar automaticamente um arquivo em seu próprio estilo ao carregar e transformá-lo no estilo da equipe ao salvar.
fonte
É difícil ler e modificar um AST, em vez do código fonte.
No entanto, algumas ferramentas relacionadas ao compilador permitem usar o AST. O bytecode Java e o código intermediário .NET funcionam de maneira semelhante a um AST.
fonte
é uma boa ideia; mas o AST de cada idioma é diferente de todos os outros.
a única exceção que conheço é para VB.NET e C #, onde a Microsoft argumenta que eles são "exatamente a mesma linguagem com sintaxe diferente". Mesmo outras linguagens .NET (IronPython, F #, qualquer que seja) são diferentes no nível AST.
A mesma coisa com as linguagens JVM, todas elas têm como alvo o mesmo bytecode, mas as construções da linguagem são diferentes, tornando-as diferentes linguagens e ASTs diferentes.
Até linguagens de 'camada fina', como CoffeScript e Xtend, compartilham grande parte da teoria das linguagens subjacentes (JavaScript e Java, respectivamente); mas introduza conceitos de nível superior que são (ou deveriam ser) retidos no nível AST.
se o Xtend pudesse ser reconstruído a partir de um Java AST, acho que teria sido definido como um 'descompilador' de Java para Xtend que cria magicamente abstrações de nível superior a partir do código Java existente, você não acha?
fonte