Criando DSLs (Domain Specific Languages) com Python
Neste post, vamos explorar como criar DSLs com Python, exemplos de DSLs internas e externas, técnicas de parsing, execução de comandos e boas práticas.
1. O que são DSLs?
Domain Specific Languages são linguagens criadas para um domínio específico, diferente de linguagens gerais (como Python ou Java). Características:
Focadas em um problema específico
Expressivas e legíveis para especialistas do domínio
Reduzem complexidade de tarefas recorrentes
Exemplos de DSLs conhecidas:
| DSL | Domínio |
|---|---|
| SQL | Manipulação de bancos de dados |
| Regex | Expressões regulares |
| CSS | Estilização de páginas web |
| Terraform HCL | Infraestrutura como código |
Python permite criar DSLs internas (embedded) usando a própria sintaxe Python, ou DSLs externas, com parser e executor próprios.
2. Tipos de DSLs
2.1 DSL interna (Embedded DSL)
Usa a linguagem host (Python) para criar abstrações que parecem uma linguagem específica do domínio.
Não precisa de parser complexo.
Exemplo clássico:
SQLAlchemypara consultas SQL em Python.
Exemplo simples: DSL de consulta matemática
class CalculatorDSL:
def __init__(self):
self.result = 0
def add(self, x):
self.result += x
return self
def multiply(self, x):
self.result *= x
return self
def subtract(self, x):
self.result -= x
return self
calc = CalculatorDSL()
result = calc.add(5).multiply(2).subtract(3).result
print(result) # Saída: 7
Vantagens:
Simples de implementar
Aproveita todas as ferramentas da linguagem Python
Fácil de integrar em sistemas existentes
2.2 DSL externa (External DSL)
Cria uma nova linguagem com sua própria sintaxe.
Requer parser e executor.
Útil quando se quer uma linguagem totalmente customizada independente do Python.
Exemplo: DSL para configurar tarefas
Arquivo tasks.dsl:
task backup:
source: "/data"
destination: "/backup"
schedule: "daily"
task cleanup:
path: "/tmp"
schedule: "hourly"
Python pode parsear e executar essas tarefas.
3. Técnicas para criar DSLs em Python
3.1 Funções e fluent interfaces
Criam sintaxe legível usando encadeamento de métodos (
method chaining).Excelente para DSLs internas.
Exemplo de DSL para fluxo de dados:
class DataPipeline:
def __init__(self, data):
self.data = data
def filter(self, func):
self.data = list(filter(func, self.data))
return self
def map(self, func):
self.data = list(map(func, self.data))
return self
def reduce(self, func):
from functools import reduce
self.data = reduce(func, self.data)
return self
pipeline = DataPipeline([1,2,3,4,5])
result = pipeline.filter(lambda x: x%2==0).map(lambda x: x*10).data
print(result) # Saída: [20, 40]
3.2 Decorators
Úteis para criar DSLs de configuração ou registro de funções.
registry = {}
def task(name):
def decorator(func):
registry[name] = func
return func
return decorator
@task("backup")
def backup_task():
print("Executando backup...")
registry["backup"]()
Vantagens:
Sintaxe limpa
Fácil de estender
Ótimo para DSLs de workflows e automação
3.3 Parsers para DSLs externas
Python oferece bibliotecas para parser de linguagens externas:
| Biblioteca | Função |
|---|---|
lark | Construção de grammars EBNF completas |
pyparsing | Parser combinator, ótimo para DSLs |
ANTLR | Gerador de parser para múltiplas linguagens |
PLY | Implementação de Lex/Yacc em Python |
Exemplo com lark:
from lark import Lark, Transformer
dsl_grammar = """
start: command+
command: "PRINT" STRING
%import common.STRING
%import common.WS
%ignore WS
"""
class DslTransformer(Transformer):
def command(self, items):
print(items[0][1:-1]) # remove aspas
parser = Lark(dsl_grammar, parser="lalr", transformer=DslTransformer())
parser.parse('PRINT "Olá mundo!" PRINT "DSL em Python"')
3.4 Execução dinâmica
Use
evalouexecpara interpretar DSLs simples em runtime.Deve ser feito com cuidado, apenas para DSLs internas confiáveis.
dsl_code = "x = 5 + 3"
exec(dsl_code)
print(x) # Saída: 8
Dica: combine parsing seguro com execução limitada (exec em locals restritos).
4. Exemplos práticos de DSLs em Python
4.1 DSL para consultas em dados (estilo SQL)
class QueryDSL:
def __init__(self, data):
self.data = data
self.filters = []
def where(self, func):
self.filters.append(func)
return self
def execute(self):
result = self.data
for f in self.filters:
result = list(filter(f, result))
return result
data = [{"nome":"Alice","idade":25},{"nome":"Bob","idade":20}]
query = QueryDSL(data).where(lambda x: x["idade"]>21).execute()
print(query) # Saída: [{'nome': 'Alice', 'idade': 25}]
4.2 DSL para pipelines de tarefas
class Workflow:
def __init__(self):
self.tasks = []
def add_task(self, func):
self.tasks.append(func)
return self
def run(self):
for task in self.tasks:
task()
workflow = Workflow()
workflow.add_task(lambda: print("Backup")).add_task(lambda: print("Limpeza")).run()
5. Boas práticas ao criar DSLs
Clareza e legibilidade: DSLs devem ser intuitivas para especialistas do domínio.
Evite sobrecarregar a sintaxe: simplicidade é mais importante que poder máximo.
Validação de entrada: sempre verifique comandos e parâmetros.
Documentação: exemplos de uso são essenciais.
Extensibilidade: permita adicionar comandos ou operadores facilmente.
Separação do domínio: lógica de negócios separada do parser ou execução da DSL.
6. Vantagens de criar DSLs com Python
Produtividade: abstrai complexidade do código Python puro
Expressividade: especialistas do domínio entendem rapidamente
Testabilidade: DSLs internas aproveitam ferramentas Python
Flexibilidade: integração com APIs, frameworks e pipelines existentes
Prototipagem rápida: Python permite iterar DSLs rapidamente
7. Conclusão
Criar DSLs com Python é uma estratégia poderosa para resolver problemas específicos do domínio de maneira expressiva e eficiente. Seja usando DSLs internas com fluent interfaces, decorators e classes, ou DSLs externas com parser e executor próprios, Python oferece ferramentas robustas para desenvolver linguagens customizadas.
Com boas práticas de design, validação, documentação e execução segura, é possível:
Reduzir complexidade do código
Tornar pipelines e workflows mais intuitivos
Integrar especialistas do domínio diretamente no desenvolvimento de sistemas

Comentários
Postar um comentário