Executando verificação de segurança...
8

Criei um interpretador simples em Python

Fala, devs! Recentemente estava procurando o que fazer e decidi implementar um interpretador "completo" em Python. Digo "completo", pois ainda há muito há ser trabalhado.

Motivação

Desde pequeno, sempre fui fascinado por tecnologia e mais louco ainda por saber como as coisas funcionam por de baixo dos panos. Então, com muita curiosidade e sofrimento, decidi implementar alguns algoritmos utilizados em compiladores/interpretadores reais!

Como começou?

Trabalho como TI, com foco em HelpDesk. Durante minha rotina, acabo ficando sem demandas e então surge o tédio kkk.

Eu já havia criado interesse por teoria e prática de compiladores e queria tentar algo novo, algo que nunca tentei.

O projeto começou pelo bloco de notas, fiz uma implementação hard coded de um lexer e parser de forma extremamente simples e nada escalável. Quando executei e funcionou, veio a idéia: "E se eu fizer isso do jeito certo?" e foi assim que esse projeto nasceu.

As primeiras versões tinham sintaxes extremamente simples, era basicamente orientado a instruções (para realizar uma soma, era obrigado a palavra chave ADD). Hoje o projeto consegue avaliar expressões de forma recursiva!

Código Fonte e Refêrencias

O código está disponível no GitHub e há muitas funcionalidades que quero implementar, tais como: bloco de condicionais if/else if/else, variáveis de escopo, entre muito mais!

Link do repositório e referências:
https://github.com/AfonsoDolmen/Tiny-Interpreter

Carregando publicação patrocinada...
6

Se me permite algumas sugestões...

Sei que, como o próprio README já diz, é um projeto feito "com fins educacionais" e não parece ter a intenção de virar algo mais sério (pelo menos é o que parece no momento), mas acho que dá pra fazer alguns comentários.

Para números, vc usou \d, que funciona. Mas em Python, uma regex por padrão está no "modo Unicode", o que quer dizer que muitos atalhos vão além do ASCII. No caso do \d, por exemplo, também vai pegar outros caracteres como o ٤ ("ARABIC-INDIC DIGIT FOUR"), além de muitos outros (veja aqui como a lista é grande).

Ou seja, se for para restringir (e em um compilador/interpretador, entendo que é importante definir bem quais caracteres são permitidos e quais não são), pode trocar para [0-9]+, assim vc garante que pode ter somente dígitos de 0 a 9.

Indo um pouco mais além, daria pra fazer uma expressão mais complexa para permitir números negativos, com casas decimais e notação científica, como -10, .2, -294.3594 e 2.5e12. Além de tratar outros casos: em muitas linguagens, se o primeiro dígito é zero, o restante é tratado como um número na base 8, mas se começa com 0x, o restante é tratado como hexadecimal (e aí teria que permitir também letras de A e F), etc.


Quanto a re.compile(r'[a-zA-Z_]\w*', re.IGNORECASE), algo parecido ocorre com o \w: ele pega qualquer caractere alfanumérico de qualquer alfabeto suportado pelo Unicode. Ou seja, caracteres árabes, japoneses, chineses, coreanos, cirílicos, etc. Se a ideia é permitir tudo isso, ok, mas se a ideia é permitir somente ASCII, teria que mudar para algo como [a-zA-Z_][a-zA-Z_0-9]*. E repare que, como vc já incluiu letras maiúsculas e minúsculas na expressão, não precisa usar a flag re.IGNORECASE (ela é redundante neste caso).


Dito isso, regex não é a melhor opção para parsing

Sei que para casos mais simples é uma opção rápida e "funciona", mas conforme a linguagem vai se tornando mais complexa, regex se torna inviável, ineficiente e de difícil manutenção.

Por exemplo, suas strings atualmente não permitem escapes, como \" para ter aspas dentro do texto. Ou seja, se tiver algo como "blabla \"entre aspas\" blabla", ela vai considerar apenas "blabla \" como sendo uma string. Os formatos numéricos podem ficar extremamente complexos para permitir todas as variações, etc. E alterar as suas regex para tratar esses casos vai ficando cada vez mais complicado, já que as expressões vão ficando cada vez mais difíceis de entender e manter.

Só pra ter uma ideia, no caso de escapes dentro da string, seria algo como re.compile(r'"([^"\\]|\\.)*"'). Para números, algo como re.compile(r'(?:[1-9]\d+|0|0[oO]?[0-7]+|0[xX][0-9a-fA-F]+|0[bB][01]+)[lL]?') e isso é só para inteiros (ainda falta adicionar casas decimais, o que deixaria a regex ainda pior).

Sei que talvez esteja saindo do escopo, mas se quiser evoluir o projeto, sugiro dar uma olhada em ferramentas como o Antlr, que tem suporte para Python.

Eu fiz um post sobre Antlr, mas se for usar, melhor ver a documentação. Além disso, ele já tem várias gramáticas prontas, inclusive para parsing de linguagens, que vc pode usar para se inspirar (melhor que começar do zero).

1

Cara, suas sugestões valeram ouro! Realmente, não tenho a intenção de transformar em um projeto sério, mas com certeza quero implementar com a melhor abordagem possível.

Obrigado pelas dicas, vou estudar mais a fundo sobre a ferramentas que você citou.

3

Hey man,
Eu também fiz faculdade em Ribeirão Preto, cara.
Eu moro no litoral a 9 anos mas estudei programação e me formei em 2008.
Eu já li um pouco sobre desenvolvimento de compiladores e realmente é muito interessante mas eu ainda nunca implementei nada nessa área.

2

Criar um compilador/interpretador é um excelente exercício em Ciência da Computação!

Uma dica que dou é estudar o fonte da linguagem Lua.

É super simples, pouquíssimos arquivos, e leve. Dá para aprender muito. 😉

2