Sumário Parte I: Narrativas Capítulo 1 Um exemplo introdutório 1.1 1 3 Segurança gótica 1.1.1 O controlador da senhorita Grant 1.2 O modelo de máquina de estados 1.3 Programando o controlador da senhorita Grant 1.4 Linguagens e Modelo Semântico 1.5 Usando geração de código 1.6 Usando bancadas de linguagem (workbenches) 1.7 Visualização 3 4 5 9 16 19 22 25 Capítulo 2 Usando linguagens específicas de domínio 27 2.1 Definindo linguagens específicas de domínio 2.1.1 As fronteiras das DSLs 2.1.2 DSLs fragmentárias e autossuficientes 2.2 Por que usar uma DSL? 2.2.1 Aprimorando a produtividade de desenvolvimento 2.2.2 Comunicação com especialistas em domínio 2.2.3 Mudança no contexto de execução 2.2.4 Modelo computacional alternativo 2.3 Problemas com DSLs 2.3.1 Cacofonia de linguagem 2.3.2 Custo de construção 2.3.3 Linguagem de gueto 2.3.4 Abstração restrita 2.4 Processamento de linguagem de uma maneira mais ampla 2.5 O ciclo de vida das DSLs 2.6 O que é um bom projeto de DSL? Capítulo 3 Implementando DSLs 3.1 3.2 3.3 Fowler DSL_Iniciais.indd xvii Arquitetura do processamento de DSL O funcionamento de um analisador sintático Gramáticas, sintaxe e semântica 27 29 32 33 33 34 35 36 36 37 37 38 39 39 40 42 43 43 47 49 15/08/12 11:26 xviii Sumário 3.4 3.5 3.6 Analisando dados sintaticamente Macros Testando DSLs 3.6.1 Testando o Modelo Semântico 3.6.2 Testando o analisador sintático 3.6.3 Testando os scripts 3.7 Tratando erros 3.8 Migrando DSLs 50 52 53 54 57 61 62 64 Capítulo 4 Implementando uma DSL interna 67 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 4.11 4.12 APIs fluentes e comando-consulta A necessidade de uma camada de análise sintática Usando funções Coleções de literais Usando gramáticas para escolher elementos internos Fechos (closures) Manipulação de árvores de análise sintática Anotações Extensão de literais Reduzindo o ruído sintático Recepção dinâmica Fornecendo verificação de tipos Capítulo 5 Implementando uma DSL externa 5.1 5.2 5.3 Estratégia de análise sintática Estratégia de produção de saída Conceitos de análise sintática 5.3.1 Análise léxica separada 5.3.2 Gramáticas e linguagens 5.3.3 Gramáticas regulares, livres de contexto e sensíveis ao contexto 5.3.4 Análise sintática descendente e ascendente 5.4 Misturando outra linguagem 5.5 DSLs em XML Capítulo 6 DSLs internas e externas 6.1 6.2 6.3 6.4 6.5 6.6 6.7 6.8 6.9 6.10 Fowler DSL_Iniciais.indd xviii Curva de aprendizado Custo de construção Familiaridade dos programadores Comunicação com especialistas em domínio Misturando-se com a linguagem hospedeira Fronteira forte de expressividade Configuração em tempo de execução Escorregando para a generalidade Compondo DSLs Resumindo 68 71 72 77 79 80 82 84 85 86 86 87 89 89 92 94 94 95 96 98 100 102 105 105 106 107 108 108 109 109 110 111 111 15/08/12 11:26 Sumário Capítulo 7 Modelos computacionais alternativos 7.1 Alguns modelos alternativos 7.1.1 Tabela de decisão 7.1.2 Sistema de regras de produção 7.1.3 Máquina de estados 7.1.4 Rede de dependências 7.1.5 Escolhendo um modelo Capítulo 8 Geração de código 8.1 8.2 8.3 8.4 8.5 8.6 Escolhendo o que gerar Como gerar Mesclando código gerado e escrito manualmente Gerando código legível Geração de código antes da análise sintática Leitura complementar Capítulo 9 Bancadas de linguagem 9.1 9.2 9.3 Elementos das bancadas de linguagem Linguagens de definição de esquemas e metamodelos Edição de código-fonte e edição projecional 9.3.1 Representações múltiplas 9.4 Programação ilustrativa 9.5 Um passeio pelas ferramentas 9.6 Bancadas de linguagem e ferramentas CASE 9.7 Você deveria usar uma bancada de linguagem? Parte II: Tópicos comuns Capítulo 10 Um zoológico de DSLs 10.1 10.2 10.3 10.4 10.5 10.6 10.7 Como funciona Quando usar O exemplo introdutório (Java) Capítulo 12 Tabela de símbolos 12.1 Como funciona 12.1.1 Símbolos estaticamente tipados 12.2 Quando usar 12.3 Leitura adicional Fowler DSL_Iniciais.indd xix 113 116 116 117 118 119 120 121 122 124 126 127 128 128 129 130 131 136 138 138 140 142 142 145 147 Graphviz 147 JMock 149 CSS 150 Linguagem de consulta do Hibernate (HQL – Hibernate Query Language) 151 XAML 152 FIT 155 Make et al. 156 Capítulo 11 Modelo semântico 11.1 11.2 11.3 xix 159 159 162 163 165 166 167 168 168 15/08/12 11:26 xx Sumário 12.4 12.5 12.6 Rede de dependências em uma DSL externa (Java e ANTLR) Usando chaves simbólicas em uma DSL interna (Ruby) Usando enumerações para símbolos estaticamente tipados (Java) Capítulo 13 Variável de contexto 13.1 13.2 13.3 Como funciona Quando usar Lendo um arquivo INI (C#) Capítulo 14 Construtor de construções 14.1 14.2 14.3 Como funciona Quando usar Construindo dados de voo simples (C#) Capítulo 15 Macro 15.1 Como funciona 15.1.1 Macros textuais 15.1.2 Macros sintáticas 15.2 Quando usar Capítulo 16 Notificação 16.1 16.2 16.3 16.4 Como funciona Quando usar Uma notificação muito simples (C#) Notificação de análise sintática (Java) Parte III: Tópicos de DSLs externas Capítulo 17 Tradução dirigida por delimitadores 17.1 Como funciona 17.2 Quando usar 17.3 Pontos de clientes assíduos (C#) 17.3.1 Modelo semântico 17.3.2 O analisador sintático 17.4 Analisando sintaticamente sentenças não autônomas com o controlador da senhorita Grant (Java) Capítulo 18 Tradução dirigida por sintaxe 18.1 Como funciona 18.1.1 Analisador léxico 18.1.2 Analisador sintático 18.1.3 Produção de saída 18.1.4 Predicados semânticos 18.2 Quando usar 18.3 Leitura complementar Fowler DSL_Iniciais.indd xx 168 170 172 175 175 176 176 179 179 180 181 183 184 184 188 192 193 194 194 194 195 199 201 201 204 205 205 207 211 219 220 221 223 226 226 227 227 15/08/12 11:26 Sumário Capítulo 19 BNF 19.1 Como funciona 19.1.1 Símbolos de multiplicidade (operadores de Kleene) 19.1.2 Outros operadores úteis 19.1.3 Gramáticas de expressão de análise sintática 19.1.4 Convertendo uma EBNF em uma BNF básica 19.1.5 Ações de código 19.2 Quando usar Capítulo 20 Tabela de expressões regulares de análise léxica 20.1 20.2 20.3 Como funciona Quando usar Analisando lexicamente o controlador da senhorita Grant (Java) Capítulo 21 Analisador sintático descendente recursivo 21.1 21.2 21.3 21.4 Como funciona Quando usar Leitura adicional Descendente recursivo e o controlador da senhorita Grant (Java) Capítulo 22 Combinador de analisadores sintáticos 22.1 Como funciona 22.1.1 Lidando com as ações 22.1.2 Estilo funcional de combinadores 22.2 Quando usar 22.3 Combinadores de analisadores sintáticos e o controlador da senhorita Grant (Java) Capítulo 23 Gerador de analisadores sintáticos 23.1 Como funciona 23.1.1 Embarcando ações 23.2 Quando usar 23.3 Hello World (Java e ANTLR) 23.3.1 Escrevendo a gramática básica 23.3.2 Construindo o analisador sintático 23.3.3 Adicionando ações de código à gramática 23.3.4 Usando lacuna de geração Capítulo 24 Construção de árvore 24.1 Como funciona 24.2 Quando usar 24.3 Usando a sintaxe de construção de árvore do ANTRL (Java e ANTLR) 24.3.1 Analisando lexicamente 24.3.2 Analisando sintaticamente 24.3.3 Preenchendo o modelo semântico 24.4 Construção de árvore usando ações de código (Java e ANTLR) Fowler DSL_Iniciais.indd xxi xxi 229 229 231 232 233 234 236 238 239 240 241 241 245 246 249 249 250 255 256 259 260 261 261 269 269 270 272 272 273 275 277 278 281 281 284 284 285 286 288 292 15/08/12 11:26 xxii Sumário Capítulo 25 Tradução embarcada 25.1 25.2 25.3 Como funciona Quando usar O controlador da senhorita Grant (Java e ANTLR) Capítulo 26 Interpretação embarcada 26.1 26.2 26.3 Como funciona Quando usar Uma calculadora (ANTLR e Java) Capítulo 27 Código estrangeiro 27.1 Como funciona 27.2 Quando usar 27.3 Embarcando código dinâmico (ANTLR, Java e Javascript) 27.3.1 Modelo semântico 27.3.2 O analisador sintático 299 299 300 300 305 305 306 306 309 309 311 311 312 315 Capítulo 28 Análise léxica alternativa 319 28.1 Como funciona 28.1.1 Citações 28.1.2 Estado léxico 28.1.3 Mutação de tipos de token 28.1.4 Ignorando tipos de tokens 28.2 Quando usar 319 320 322 324 325 326 Capítulo 29 Expressão de operadores aninhados 29.1 Como funciona 29.1.1 Usando analisadores sintáticos ascendentes 29.1.2 Analisadores sintáticos descendentes 29.2 Quando usar Capítulo 30 Separadores de novas linhas 30.1 30.2 Como funciona Quando usar Capítulo 31 Miscelânea sobre DSLs externas 31.1 31.2 327 327 328 329 331 333 333 336 337 Endentação sintática Gramáticas modulares 337 339 Parte IV: Tópicos de DSLs internas 341 Capítulo 32 Construtor de expressões 32.1 32.2 32.3 32.4 Fowler DSL_Iniciais.indd xxii Como funciona Quando usar Um calendário fluente com e sem um construtor (Java) Usando múltiplos construtores para o calendário (Java) 343 344 344 345 348 15/08/12 11:26 Sumário Capítulo 33 Sequência de funções 33.1 33.2 33.3 Como funciona Quando usar Configuração simples de computadores (Java) Capítulo 34 Função aninhada 34.1 Como funciona 34.2 Quando usar 34.3 O exemplo da configuração simples de computadores (Java) 34.4 ratando múltiplos argumentos diferentes com tokens (C#) 34.5 Usando tokens de subtipo para suporte a IDEs (Java) 34.6 Usando inicializadores de objetos (C#) 34.7 Eventos recorrentes (C#) 34.7.1 Modelo semântico 34.7.2 A DSL Capítulo 35 Encadeamento de métodos 35.1 Como funciona 35.1.1 Construtores ou valores 35.1.2 O problema do término 35.1.3 Estrutura hierárquica 35.1.4 Interfaces progressivas 35.2 Quando usar 35.3 O exemplo da configuração simples de computadores (Java) 35.4 Encadeamento com propriedades (C#) 35.5 Interfaces progressivas (C#) Capítulo 36 Escopo de objeto 36.1 Como funciona 36.2 Quando usar 36.3 Códigos de segurança (C#) 36.3.1 Modelo semântico 36.3.2 DSL 36.4 Usando avaliação de instância (Ruby) 36.5 Usando um inicializador de instância (Java) Capítulo 37 Fecho 37.1 37.2 Como funciona Quando usar Capítulo 38 Fecho aninhado 38.1 Como funciona 38.2 Quando usar 38.3 Empacotando uma sequência de funções em um fecho aninhado (Ruby) 38.4 Um exemplo simples em C# Fowler DSL_Iniciais.indd xxiii xxiii 351 351 352 353 357 357 359 360 361 363 365 366 366 369 373 373 375 375 376 377 377 378 381 382 385 386 386 387 388 390 392 394 397 397 402 403 403 405 405 408 15/08/12 11:26 xxiv Sumário 38.5 38.6 38.7 Usando encadeamento de métodos (Ruby) Sequência de funções com argumentos de fecho explícitos (Ruby) Usando avaliação de instância (Ruby) Capítulo 39 Lista de literais 39.1 39.2 Como funciona Quando usar Capítulo 40 Mapa de literais 40.1 40.2 40.3 40.4 Como funciona Quando usar A configuração de computadores usando listas e mapas (Ruby) Evoluindo para a forma de Greenspun (Ruby) Capítulo 41 Recepção dinâmica 41.1 Como funciona 41.2 Quando usar 41.3 Pontos promocionais usando nomes de métodos analisados sintaticamente (Ruby) 41.3.1 Modelo 41.3.2 Construtor 41.4 Pontos promocionais usando encadeamento (Ruby) 41.4.1 Modelo 41.4.2 Construtor 41.5 Removendo as aspas no controlador do painel secreto (JRuby) Capítulo 42 Anotação 42.1 Como funciona 42.1.1 Definindo uma anotação 42.1.2 Processando anotações 42.2 Quando usar 42.3 Sintaxe customizada com processamento em tempo de execução (Java) 42.4 Usando um método de classe (Ruby) 42.5 Geração dinâmica de código (Ruby) Capítulo 43 Manipulação de árvore de análise sintática 43.1 Como funciona 43.2 Quando usar 43.3 Gerando consultas IMAP a partir de condições C# (C#) 43.3.1 Modelo semântico 43.3.2 Construindo a partir de C# 43.3.3 Voltando Capítulo 44 Tabela de símbolos de classe 44.1 Como funciona 44.2 Quando usar 44.3 Tabela de símbolos de classe estaticamente tipada (Java) Fowler DSL_Iniciais.indd xxiv 409 411 412 417 417 417 419 419 420 420 422 427 428 429 430 431 433 434 435 435 438 445 446 446 448 449 450 452 453 455 455 457 458 458 460 465 467 468 469 469 15/08/12 11:26 Sumário Capítulo 45 Polimento textual 45.1 45.2 45.3 Como funciona Quando usar Regras de desconto polidas (Ruby) Capítulo 46 Extensão de literal 46.1 46.2 46.3 Como funciona Quando usar Ingredientes de receitas (C#) Parte V: Modelos computacionais alternativos Capítulo 47 Modelo adaptativo 47.1 Como funciona 47.1.1 Incorporando código imperativo em um modelo adaptativo 47.1.2 Ferramentas 47.2 Quando usar Capítulo 48 Tabela de decisão 48.1 Como funciona 48.2 Quando usar 48.3 Calculando a taxa para um pedido (C#) 48.3.1 Modelo 48.3.2 O analisador sintático Capítulo 49 Rede de dependências 49.1 Como funciona 49.2 Quando usar 49.3 Analisando poções (C#) 49.3.1 Modelo semântico 49.3.2 O analisador sintático Capítulo 50 Sistema de regras de produção 50.1 Como funciona 50.1.1 Encadeamento 50.1.2 Inferências contraditórias 50.1.3 Padrões em estruturas de regras 50.2 Quando usar 50.3 Validações para se associar a um clube (C#) 50.3.1 Modelo 50.3.2 Analisador sintático 50.3.3 Desenvolvendo a DSL 50.4 Regras de elegibilidade: estendendo a associação ao clube (C#) 50.4.1 O modelo 50.4.2 O analisador sintático Fowler DSL_Iniciais.indd xxv xxv 477 477 478 478 481 481 482 483 485 487 488 489 491 492 495 495 497 497 497 502 505 506 508 508 509 511 513 514 515 515 516 517 517 518 519 520 522 523 525 15/08/12 11:26 xxvi Sumário Capítulo 51 Máquina de estados 51.1 51.2 51.3 Como funciona Quando usar O controlador do painel secreto (Java) Parte VI: Geração de código Capítulo 52 Geração por transformação 52.1 52.2 52.3 Como funciona Quando usar O controlador do painel secreto (Java gerando C) Capítulo 53 Geração por templates 53.1 Como funciona 53.2 Quando usar 53.3 Gerando a máquina de estados do painel secreto com condicionais aninhados (Velocit e Java gerando C) Capítulo 54 Auxiliar de embarcação 54.1 54.2 54.3 54.4 Como funciona Quando usar Estados do painel secreto (Java e ANTLR) Um auxiliar deveria gerar HTML? (Java e Velocity) Capítulo 55 Geração ciente do modelo 55.1 55.2 55.3 55.4 Como funciona Quando usar O painel secreto da máquina de estados (C) Carregando dinamicamente a máquina de estados (C) Capítulo 56 Geração ignorante ao modelo 56.1 Como funciona 56.2 Quando usar 56.3 O painel secreto da máquina de estados como condicionais aninhados (C) Capítulo 57 Lacuna de geração 57.1 Como funciona 57.2 Quando usar 57.3 Gerando classes a partir de um esquema de dados (Java e um pouco de Ruby) Bibliografia Índice Fowler DSL_Iniciais.indd xxvi 527 527 529 530 531 533 533 535 535 539 539 541 541 547 548 549 549 552 555 556 556 557 564 567 567 568 568 571 571 573 573 579 581 15/08/12 11:26