College Online
0%

Projetando uma DSL

Modulo 6 · Aula 1 ~25 min de leitura Nivel: Avancado

Video da aula estara disponivel em breve

O que e uma DSL?

Uma DSL (Domain-Specific Language) e uma linguagem de programacao projetada para um dominio especifico, em contraste com linguagens de proposito geral (GPLs) como Python, Java ou C. DSLs sacrificam generalidade em troca de expressividade e concisao dentro do seu dominio.

Diagrama
GPL (General Purpose Language)       DSL (Domain-Specific Language)
+-------------------------------+    +-------------------------------+
| Turing-completa               |    | Pode ou nao ser Turing-completa|
| Dominio: qualquer problema    |    | Dominio: problema especifico  |
| Sintaxe generica              |    | Sintaxe otimizada para dominio|
| Curva de aprendizado longa    |    | Curva curta (para o dominio)  |
| Exemplos: Python, Java, C    |    | Exemplos:                     |
+-------------------------------+    |   SQL (banco de dados)        |
                                     |   HTML (marcacao)              |
                                     |   CSS (estilos)                |
                                     |   regex (padroes de texto)     |
                                     |   Make (build)                 |
                                     |   Terraform (infraestrutura)   |
                                     |   GraphQL (queries de API)     |
                                     +-------------------------------+

Por que criar uma DSL?
  1. Expressividade: dizer mais com menos (SQL vs. loops em Python)
  2. Validacao: restringir o que e possivel (erros impossveis)
  3. Comunicacao: nao-programadores podem ler/escrever
  4. Otimizacao: o compilador/runtime entende o dominio

DSL Interna vs. DSL Externa

Existem duas abordagens fundamentais para criar uma DSL:

Diagrama
DSL INTERNA (embedded DSL)
  Usa a sintaxe da linguagem hospedeira (Python, Ruby, Kotlin)
  Nao precisa de lexer/parser proprios
  Herda ferramentas da linguagem hospedeira (IDE, debugger)

  Exemplo: SQLAlchemy (Python)
    users.select().where(users.c.age > 18).order_by(users.c.name)

  Exemplo: pytest fixtures
    @pytest.fixture
    def database():
        db = create_test_db()
        yield db
        db.cleanup()

  Vantagens: rapida de criar, usa tooling existente
  Desvantagens: limitada pela sintaxe da linguagem hospedeira

---------------------------------------------------------

DSL EXTERNA (standalone DSL)
  Tem sua propria sintaxe, lexer, parser, semantica
  Precisa de tooling proprio (editor support, error messages)
  Pode ter sintaxe perfeita para o dominio

  Exemplo: SQL
    SELECT name, age FROM users WHERE age > 18 ORDER BY name

  Exemplo: Terraform
    resource "aws_instance" "web" {
      ami           = "ami-0c55b159"
      instance_type = "t2.micro"
    }

  Vantagens: sintaxe ideal, melhor validacao
  Desvantagens: mais trabalho para criar e manter

Design de DSL: Principios

Diagrama
Principios para projetar uma boa DSL:

1. MINIMALISMO
   Menos construcoes = menos para aprender
   Cada construcao deve "pagar" sua complexidade
   "Se voce pode dizer em 3 keywords, nao use 10"

2. LEITURA > ESCRITA
   Codigo e lido 10x mais que escrito
   A DSL deve ser legivel para quem nao a criou
   Favor English-like syntax quando possivel

3. FECHAMENTO DE DOMINIO
   A DSL deve cobrir 80%+ dos casos do dominio
   Os 20% restantes podem usar escape hatches para a GPL

4. ERROS CLAROS
   Mensagens de erro em termos do dominio, nao da implementacao
   "Regra 'commit-hygiene' referencia concern 'xyz' que nao existe"
   NAO: "KeyError: 'xyz' at line 42 in rule_evaluator.py"

5. COMPOSICAO
   Construcoes devem combinar naturalmente
   rule + condition = regra condicional
   workflow + step = workflow completo

6. IDEMPOTENCIA
   Executar duas vezes produz o mesmo resultado
   Importante para configuracao e infraestrutura

Projetando a DSL para harness.os

Vamos projetar uma DSL para definir regras (rules) do harness.os. Atualmente, regras sao armazenadas como texto livre no banco de dados. Uma DSL permitiria validacao, composicao e execucao automatica.

Contexto harness.os: O harness.os usa regras para governar o comportamento de agentes AI. Exemplos: "nunca adicionar co-author trailer", "sempre iniciar sessao com start_session", "publicar blog post quando melhorar o harness". Essas regras hoje sao texto em Markdown. Uma DSL as tornaria executaveis.

Passo 1: Levantar os Requisitos do Dominio

Diagrama
Conceitos do dominio (harness.os rules):

  RULE: unidade de governanca
    - tem nome, descricao, contexto
    - pode ser ativa ou inativa
    - pode ter condicoes de ativacao

  CONDITION: quando a regra se aplica
    - contexto: "testing", "deployment", "all"
    - projeto: "way2fly", "cortex.ai", "*"
    - dominio: "build", "product", "operations", "domain"

  ACTION: o que a regra faz quando ativada
    - "block": impede a acao
    - "require": exige uma acao adicional
    - "warn": emite aviso
    - "log": registra no harness

  PATTERN: o que a regra detecta
    - presenca de texto (ex: "Co-Authored-By")
    - tipo de operacao (ex: "git commit")
    - fase da sessao (ex: "session_end")

Operacoes:
  - Definir regra
  - Ativar/desativar regra
  - Listar regras por contexto
  - Avaliar regras contra uma acao

Passo 2: Projetar a Sintaxe

harness-rules DSL
# Sintaxe proposta para a DSL de regras do harness.os

rule "commit-hygiene" {
  description "Nunca adicionar Co-Authored-By: Claude a commits"
  context     all
  domain      build
  priority    high

  when git_commit {
    contains "Co-Authored-By: Claude"
  }

  then block {
    message "Remova o trailer Co-Authored-By: Claude do commit"
  }
}

rule "session-lifecycle" {
  description "Toda sessao deve comecar com start_session"
  context     all
  domain      build
  priority    critical

  when session_start {
    missing "start_session"
  }

  then require {
    action  "start_session"
    message "Chame start_session() antes de iniciar qualquer trabalho"
  }
}

rule "publish-improvements" {
  description "Publicar blog post ao melhorar o harness"
  context     development
  domain      build
  priority    medium

  when harness_change {
    type "schema_migration" or "new_workflow" or "new_knowledge"
  }

  then require {
    action  "write_blog_post"
    message "Escreva um blog post documentando a melhoria"
  }
}

# Regra com multiplas condicoes
rule "token-budget" {
  description "Alertar quando contexto excede 80% do budget"
  context     all
  domain      operations
  priority    medium

  when context_load {
    utilization > 0.8
  }

  then warn {
    message "Contexto em {utilization}% do budget. Considere otimizar."
  }
}

Passo 3: Definir a Gramatica Formal

BNF
# Gramatica da DSL de regras (BNF simplificado)

program     ::= rule_def*

rule_def    ::= "rule" STRING "{" rule_body "}"

rule_body   ::= property* when_clause then_clause

property    ::= IDENTIFIER value
              # description STRING
              # context IDENTIFIER
              # domain IDENTIFIER
              # priority IDENTIFIER

value       ::= STRING | IDENTIFIER | NUMBER

when_clause ::= "when" IDENTIFIER "{" condition+ "}"

condition   ::= IDENTIFIER STRING
              | IDENTIFIER comparison_op value
              | condition "or" condition
              | condition "and" condition

comparison_op ::= ">" | "<" | ">=" | "<=" | "==" | "!="

then_clause ::= "then" action_type "{" action_body "}"

action_type ::= "block" | "require" | "warn" | "log"

action_body ::= property+

# Tokens:
#   STRING: "..." (aspas duplas)
#   IDENTIFIER: [a-zA-Z_][a-zA-Z0-9_-]*
#   NUMBER: [0-9]+(\.[0-9]+)?
#   Comentarios: # ate fim de linha
#   Whitespace: ignorado (exceto dentro de strings)

Analise da DSL Proposta

Diagrama
Checklist de design da DSL:

  [OK] Minimalismo
       6 keywords: rule, when, then, and, or, not
       4 action types: block, require, warn, log
       Propriedades sao pares chave-valor

  [OK] Leitura > Escrita
       "when git_commit { contains '...' }" e legivel
       Nao-programadores entendem a intencao

  [OK] Fechamento de dominio
       Cobre: definicao de regras, condicoes, acoes
       Nao cobre: logica arbitraria (precisa GPL para isso)

  [OK] Erros claros
       Parser pode dizer: "Linha 5: action type 'blok' nao existe.
                           Voce quis dizer 'block'?"

  [OK] Composicao
       Condicoes combinam com and/or
       Regras sao independentes e composiveis

  [??] Extensibilidade
       Como adicionar novos tipos de condicao?
       Opcao: plugin system para condition handlers

Comparacao com alternativas:
  JSON/YAML: verboso, sem validacao de dominio
  Python dict: poderoso demais, erros genericos
  DSL propria: concisa, validavel, legivel

DSL Interna em Python (Alternativa)

Antes de construir o lexer e parser (proxima aula), vejamos como seria uma DSL interna equivalente em Python, usando decorators e method chaining:

Python
from dataclasses import dataclass, field
from typing import List, Callable, Optional

@dataclass
class Rule:
    name: str
    description: str = ""
    context: str = "all"
    domain: str = "build"
    priority: str = "medium"
    conditions: List[Callable] = field(default_factory=list)
    action_type: str = "warn"
    action_message: str = ""
    action_handler: Optional[Callable] = None

class RuleBuilder:
    """DSL interna em Python usando builder pattern."""

    def __init__(self, name: str):
        self.rule = Rule(name=name)

    def desc(self, text: str):
        self.rule.description = text
        return self

    def for_context(self, ctx: str):
        self.rule.context = ctx
        return self

    def for_domain(self, domain: str):
        self.rule.domain = domain
        return self

    def with_priority(self, p: str):
        self.rule.priority = p
        return self

    def when(self, condition: Callable):
        self.rule.conditions.append(condition)
        return self

    def then_block(self, message: str):
        self.rule.action_type = "block"
        self.rule.action_message = message
        return self

    def then_require(self, message: str, handler: Callable = None):
        self.rule.action_type = "require"
        self.rule.action_message = message
        self.rule.action_handler = handler
        return self

    def then_warn(self, message: str):
        self.rule.action_type = "warn"
        self.rule.action_message = message
        return self

    def build(self) -> Rule:
        return self.rule


# Uso da DSL interna
commit_hygiene = (
    RuleBuilder("commit-hygiene")
    .desc("Nunca adicionar Co-Authored-By: Claude")
    .for_context("all")
    .for_domain("build")
    .with_priority("high")
    .when(lambda event: "Co-Authored-By: Claude" in event.get("message", ""))
    .then_block("Remova o trailer Co-Authored-By do commit")
    .build()
)

print(f"Rule: {commit_hygiene.name}")
print(f"Action: {commit_hygiene.action_type}")

# Testar a regra
event = {"type": "git_commit", "message": "Add feature\n\nCo-Authored-By: Claude"}
for cond in commit_hygiene.conditions:
    if cond(event):
        print(f"BLOCKED: {commit_hygiene.action_message}")

Comparando as Duas Abordagens

Diagrama
DSL Externa (proposta):                DSL Interna (Python):
+----------------------------------+   +----------------------------------+
| rule "commit-hygiene" {          |   | RuleBuilder("commit-hygiene")    |
|   when git_commit {              |   |   .when(lambda e:                |
|     contains "Co-Authored-By"    |   |     "Co-Authored-By" in          |
|   }                              |   |     e.get("message",""))         |
|   then block {                   |   |   .then_block("Remova...")       |
|     message "Remova..."          |   |   .build()                       |
|   }                              |   |                                  |
| }                                |   |                                  |
+----------------------------------+   +----------------------------------+

Pro: Sintaxe perfeita              Pro: Zero infra (sem parser)
Pro: Nao-devs podem editar         Pro: Full power de Python
Pro: Validacao rica                 Pro: IDE support gratis
Con: Precisa de lexer + parser     Con: Sintaxe limitada
Con: Tooling proprio               Con: Erros genericos
Con: Mais trabalho inicial         Con: Precisa saber Python

Para harness.os:
  DSL externa para regras publicadas (legibilidade)
  DSL interna para prototipagem e regras complexas

Resumo

Exercicio

Projete uma DSL para um dominio de sua escolha (configuracao de CI/CD, definicao de workflows, queries de knowledge base, etc.). Defina: (1) pelo menos 5 conceitos do dominio, (2) a sintaxe proposta com 3+ exemplos, (3) a gramatica BNF completa, (4) uma versao interna equivalente em Python. Justifique suas decisoes de design usando os 6 principios discutidos.

Verifique seu entendimento

Qual e a principal diferenca entre uma DSL interna e uma DSL externa?

  • DSL interna e mais poderosa que DSL externa
  • DSL externa nao precisa de parser, DSL interna precisa
  • DSL interna usa a sintaxe da linguagem hospedeira; DSL externa tem sintaxe propria com lexer e parser dedicados
  • DSL interna so funciona em Python, DSL externa funciona em qualquer linguagem