Aprendendo compiladores fazendo um - Parte 2

Aprendendo compiladores fazendo um - Parte 2

Vídeo dessa talk: https://youtu.be/q9T6Y2ZjE54
Vídeo da Parte 1: https://youtu.be/t77ThZNCJGY
Vídeo da demonstração do compilador: https://youtu.be/BAS0EZ0Yg6g

Aprender mais sobre compiladores é uma excelente forma de desmitificar o que acontece no momento entre iniciar a build do código e obter o output. Isso é ótimo para se tornar um melhor desenvolver e expandir seus horizontes; desse modo é possível entender mais sobre o funcionamento de ferramentas como Babel e máquinas virtuais, assim como outras áreas, como otimização e engenharia reversa.

Nessa segunda parte da talk "Aprendendo compiladores fazendo um" vamos adentrar bem mais no código de como o Macro Compiler funciona. Veremos todas as etapas de compilação, como as gramáticas, parser combinator, análise semântica, tratamento de erros, otimização, geração de código e testes! Assim clarificando como um compilador pode ser implementado.

Repositório do compilador de EventMacro: https://github.com/macabeus/macro-compiler

F380535da59d6cdd5754e2e31bda8a0e?s=128

Bruno Macabeus

September 04, 2018
Tweet

Transcript

  1. APRENDENDO COMPILADORES FAZENDO UM - PARTE 2

  2. Veja a Parte 1: https://youtu.be/t77ThZNCJGY

  3. “The language or notation we are using to express or

    record our thoughts, are the major factors determining what we can think or express at all!” Edsger W. Dijkstra
  4. DEMONSTRAÇÃO

  5. None
  6. None
  7. None
  8. None
  9. None
  10. Parser Código fonte Análise semântica Otimização Geração de código

  11. Parser Código fonte Análise semântica Otimização Geração de código PARSER

  12. Gramática

  13. Context-free Grammars Parsing Expression Grammar Regular Grammars eDSL

  14. Context-free Grammars Parsing Expression Grammar Regular Grammars eDSL É uma

    forma muito simples de representar a gramática de uma linguagem.
  15. Context-free Grammars Parsing Expression Grammar Regular Grammars eDSL move prontera

    30 42 move 30 42 É uma forma muito simples de representar a gramática de uma linguagem. Regexp (regular expression) é um exemplo
  16. Context-free Grammars Parsing Expression Grammar Regular Grammars eDSL /move (?:(\w+)

    )?(\d+) (\d+)/ move prontera 30 42 move 30 42 É uma forma muito simples de representar a gramática de uma linguagem. Regexp (regular expression) é um exemplo
  17. Context-free Grammars Parsing Expression Grammar Regular Grammars eDSL Uma das

    limitações é não ser possível especificar uma sequência arbitrária de comandos. Por conta disso, não é possível descrever uma sequência aninhada de blocos de comandos
  18. Context-free Grammars Parsing Expression Grammar Regular Grammars eDSL { evil_query(id:

    42) { complex_field {
 complex_field { field } } } } Uma das limitações é não ser possível especificar uma sequência arbitrária de comandos. Por conta disso, não é possível descrever uma sequência aninhada de blocos de comandos
  19. Context-free Grammars Parsing Expression Grammar Regular Grammars eDSL { evil_query(id:

    42) { complex_field {
 complex_field { field } } } } Uma das limitações é não ser possível especificar uma sequência arbitrária de comandos. Por conta disso, não é possível descrever uma sequência aninhada de blocos de comandos
  20. Uma eDSL é uma biblioteca com interface da qual mimetiza

    uma linguagem de programação, isto é, apresenta palavras primitivas e a possibilidade combiná-las para construir rotinas Context-free Grammars Parsing Expression Grammar eDSL Regular Grammars
  21. Pode-se embutir em uma linguagem uma outra linguagem cuja finalidade

    dela é especificar gramáticas Context-free Grammars Parsing Expression Grammar eDSL Regular Grammars
  22. Context-free Grammars Parsing Expression Grammar eDSL Regular Grammars sequence([
 ignore(string("move")),


    ignore(spaces()),
 
 many(letter()),
 skip(spaces()),
 
 integer(),
 ignore(spaces()),
 
 integer()
 ]) Pode-se embutir em uma linguagem uma outra linguagem cuja finalidade dela é especificar gramáticas
  23. Define um conjunto de símbolos e as respectivas transformações válidas

    de cada um. Context-free Grammars Parsing Expression Grammar Regular Grammars eDSL
  24. Define um conjunto de símbolos e as respectivas transformações válidas

    de cada um. Um exemplo de CFG (Context-free Grammars) é o BNF (Backus–Naur Form): Context-free Grammars Parsing Expression Grammar Regular Grammars eDSL <vowel> :== "a" | "e" | "i" | "o" | "u" <digit> :== "0" | "1" | "2" | "3" | "4" |
 "5" | "6" | "7" | "8" | "9" <character> :== <vowel> | <digit> <text> :== <character> | <text> <character>
  25. Define um conjunto de símbolos e as respectivas transformações válidas

    de cada um. Um exemplo de CFG (Context-free Grammars) é o BNF (Backus–Naur Form): Context-free Grammars Parsing Expression Grammar Regular Grammars eDSL <vowel> :== "a" | "e" | "i" | "o" | "u" <digit> :== "0" | "1" | "2" | "3" | "4" |
 "5" | "6" | "7" | "8" | "9" <character> :== <vowel> | <digit> <text> :== <character> | <text> <character> ei9 zyp
  26. Similar ao CFG. Também define um conjunto de símbolos e

    as respectivas transformações. Context-free Grammars Parsing Expression Grammar Regular Grammars eDSL
  27. Similar ao CFG. Também define um conjunto de símbolos e

    as respectivas transformações. Exemplo de PEG (Parsing Expression Grammar): Context-free Grammars Parsing Expression Grammar Regular Grammars eDSL vowel ← 'a' / 'e' / 'i' / 'o' / 'u' digit ← [0-9] character ← vowel / digit text ← character+
  28. Parsing Expression Grammar Regular Grammars eDSL Context-free Grammars As principais

    diferenças entre CFG e PEG são:
  29. Parsing Expression Grammar Regular Grammars eDSL Context-free Grammars As principais

    diferenças entre CFG e PEG são:
 - notação
  30. Parsing Expression Grammar Regular Grammars eDSL Context-free Grammars As principais

    diferenças entre CFG e PEG são:
 - notação <vowel> :== "a" | "e" | "i" | "o" | "u" <digit> :== "0" | "1" | "2" | "3" | "4" |
 "5" | "6" | "7" | "8" | "9" <character> :== <vowel> | <digit> <text> :== <character> | <text> <character> vowel ← 'a' / 'e' / 'i' / 'o' / 'u' digit ← [0-9] character ← vowel / digit text ← character+
  31. Parsing Expression Grammar Regular Grammars eDSL Context-free Grammars As principais

    diferenças entre CFG e PEG são:
 - notação - interpretação das regras
  32. Parsing Expression Grammar Regular Grammars eDSL Context-free Grammars As principais

    diferenças entre CFG e PEG são:
 - notação - interpretação das regras CFG Regra A
 Regra B
 Regra C CFG Regra B
 Regra C
 Regra A = PEG Regra A
 Regra B
 Regra C PEG Regra B
 Regra C
 Regra A ≠
  33. Parsing Expression Grammar Regular Grammars eDSL Context-free Grammars As principais

    diferenças entre CFG e PEG são:
 - notação - interpretação das regras CFG Regra A
 Regra B
 Regra C CFG Regra B
 Regra C
 Regra A = PEG Regra A
 Regra B
 Regra C PEG Regra B
 Regra C
 Regra A ≠ “Hmm.. posso usar tanto A ou B… Então devo usar qual?”
  34. Parsing Expression Grammar Regular Grammars eDSL Context-free Grammars As principais

    diferenças entre CFG e PEG são:
 - notação - interpretação das regras CFG Regra A
 Regra B
 Regra C CFG Regra B
 Regra C
 Regra A = PEG Regra A
 Regra B
 Regra C PEG Regra B
 Regra C
 Regra A ≠ Sei lá! Sei lá! “Hmm.. posso usar tanto A ou B… Então devo usar qual?”
  35. Parsing Expression Grammar Regular Grammars eDSL Context-free Grammars As principais

    diferenças entre CFG e PEG são:
 - notação - interpretação das regras CFG Regra A
 Regra B
 Regra C CFG Regra B
 Regra C
 Regra A = PEG Regra A
 Regra B
 Regra C PEG Regra B
 Regra C
 Regra A ≠ Sei lá! Use B,
 veio
 antes! Sei lá! Use A,
 veio
 antes! “Hmm.. posso usar tanto A ou B… Então devo usar qual?”
  36. Algoritmos

  37. Parser Generators Parser Combinators

  38. Parser Generators Parser Combinators Descrição

  39. Parser Generators Parser Combinators Descrição Parser
 Generator

  40. Parser Generators Parser Combinators Descrição Parser
 Generator Parser

  41. Parser Combinators Parser Generators

  42. Parser Combinators Parser Generators Parser A Parser B Parser C

  43. Parser Combinators Parser Generators Parser A Parser B Parser C

    Parser D
  44. Parser Combinators Parser Generators Parser A Parser B Parser C

    Parser D Parser E
  45. Legal!
 Mas como eu posso usar no Elixir? ?

  46. github.com/bitwalker/combine

  47. github.com/bitwalker/combine Combine, uma lib de parser combinators
 Descrição por meio

    de uma eDSL
  48. github.com/bitwalker/combine Combine, uma lib de parser combinators
 Descrição por meio

    de uma eDSL Abordagem scannerless parsing
  49. None
  50. None
  51. Exemplo prático #1
 Parsear um comando de Event Macro

  52. &push( ) @ori text ,

  53. Keyword &push( ) @ori text ,

  54. Nome de uma variável array &push( ) @ori text ,

  55. Vírgula &push( ) @ori text ,

  56. Valor textual &push( ) @ori text ,

  57. Keyword Nome de uma variável array Valor textual Vírgula &push(

    ) @ori text ,
  58. None
  59. None
  60. None
  61. None
  62. None
  63. Push
 Command

  64. Push
 Command Array
 Variable

  65. Push
 Command Identifier Array
 Variable

  66. Push
 Command TextValue Identifier Array
 Variable

  67. Push
 Command TextValue Identifier Array
 Variable Scalar
 Variable Hash
 Variable

  68. Após parsear o comando, precisamos mapeá-lo!

  69. Após parsear o comando, precisamos mapeá-lo!

  70. Após parsear o comando, precisamos mapeá-lo!

  71. Após parsear o comando, precisamos mapeá-lo!

  72. Após parsear o comando, precisamos mapeá-lo!

  73. Exemplo prático #2
 Parsear de um bloco de comandos

  74. ref-while # comments log message do c hi if (1)

    macro { }
  75. macro { } ref-while # comments log message do c

    hi if (1) Bloco de comandos
  76. if (1) { # comments log message do c hi

    if (1) }
  77. # comments log message do c hi if (1) Bloco

    de comandos if (1) { }
  78. None
  79. None
  80. None
  81. None
  82. None
  83. None
  84. Tenha cuidado com a recursão!

  85. Em alguns momentos, pode ser que a sua gramática seja

    recursiva.
  86. Em alguns momentos, pode ser que a sua gramática seja

    recursiva. &rand(0, 5) ScalarValue
  87. &rand(0, 5)

  88. &rand(0, 5) &rand(0, $value)

  89. &rand(0, 5) &rand(0, $value) &rand(0, &rand(0, 5))

  90. Quando estiver construindo o parser…

  91. Quando estiver construindo o parser…

  92. Quando estiver construindo o parser…

  93. Quando estiver construindo o parser…

  94. Quando estiver construindo o parser…

  95. Quando estiver construindo o parser…

  96. Quando estiver construindo o parser…

  97. Quando estiver construindo o parser…

  98. Quando estiver construindo o parser…

  99. Quando estiver construindo o parser…

  100. Quando estiver construindo o parser…

  101. Quando estiver construindo o parser…

  102. Quando estiver construindo o parser…

  103. Quando estiver construindo o parser…

  104. Quando estiver construindo o parser…

  105. Quando estiver construindo o parser… Quando for executar o parser…

  106. Quando estiver construindo o parser… Quando for executar o parser…

  107. Quando estiver construindo o parser… Quando for executar o parser…

  108. None
  109. Parser Código fonte Análise semântica Otimização Geração de código

  110. Parser Código fonte Análise semântica Otimização Geração de código ANÁLISE


    SEMÂNTICA
  111. A symbols table é importante para tornar mais acessível as

    informações da AST para as demais etapas de compilação
  112. None
  113. None
  114. None
  115. None
  116. None
  117. None
  118. None
  119. None
  120. None
  121. None
  122. None
  123. None
  124. None
  125. None
  126. None
  127. None
  128. None
  129. None
  130. read macros: [ "foo", "bar" ]

  131. read macros: [ "foo", "bar" ] write macros: [ "foo"

    ]
  132. read macros: [ "foo", "bar" ] write macros: [ "foo"

    ] difference: [ "bar" ]
  133. read macros: [ "foo", "bar" ] write macros: [ "foo"

    ] difference: [ "bar" ] Devemos disparar um erro
 devido a leitura de "bar" !
  134. Parser Código fonte Análise semântica Otimização Geração de código

  135. Parser Código fonte Análise semântica Otimização Geração de código ERROR

  136. Para mostrar o erro de sintaxe, tive que lançar uma

    exceção
  137. Para mostrar o erro de sintaxe, tive que lançar uma

    exceção
  138. Para mostrar o erro de sintaxe, tive que lançar uma

    exceção
  139. Para mostrar o erro de sintaxe, tive que lançar uma

    exceção
  140. Erro de sintaxe

  141. Erro de sintaxe

  142. Erro de sintaxe

  143. Erro semântico

  144. log foo Erro semântico

  145. log foo Erro semântico

  146. log foo Erro semântico

  147. log foo Erro semântico

  148. log foo Erro semântico

  149. log foo Erro semântico

  150. log foo Erro semântico

  151. log foo Erro semântico

  152. log foo Erro semântico

  153. Erro semântico macro example { $never_read_var = &rand(1, 4) #

    warning log number: $never_written_var # fatal error }
  154. Parser Código fonte Análise semântica Otimização Geração de código

  155. Parser Código fonte Análise semântica Otimização Geração de código OTIMIZAÇÃO

  156. Constant folding

  157. Constant folding

  158. None
  159. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: $foo log bar: $bar log name: $name $name = pagarme log name: $name }
  160. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: $foo log bar: $bar log name: $name $name = pagarme log name: $name } ➡
  161. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: $foo log bar: $bar log name: $name $name = pagarme log name: $name } macro setVars { $foo = value $bar = &rand(1, 4) } macro example { $name = macabeus call setVars log foo: value log bar: $bar log name: macabeus $name = pagarme log name: pagarme } ➡
  162. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: $foo log bar: $bar log name: $name $name = pagarme log name: $name } macro setVars { $foo = value $bar = &rand(1, 4) } macro example { $name = macabeus call setVars log foo: value log bar: $bar log name: macabeus $name = pagarme log name: pagarme } ➡
  163. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: log bar: log name: $name = pagarme log name: } Contexto das últimas
 variáveis na macro Contexto das variáveis na seta setVars
 $foo: value
 $bar: is nondeterministic
 example
 $name: pagarme $foo $bar $name
  164. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: log bar: log name: $name = pagarme log name: } Contexto das últimas
 variáveis na macro Contexto das variáveis na seta setVars
 $foo: value
 $bar: is nondeterministic
 example
 $name: pagarme $foo $bar $name $name
  165. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: log bar: log name: $name = pagarme log name: } Contexto das últimas
 variáveis na macro Contexto das variáveis na seta setVars
 $foo: value
 $bar: is nondeterministic
 example
 $name: pagarme $name: macabeus $foo $bar $name $name
  166. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: log bar: log name: $name = pagarme log name: } Contexto das últimas
 variáveis na macro Contexto das variáveis na seta setVars
 $foo: value
 $bar: is nondeterministic
 example
 $name: pagarme $name: macabeus $foo: value
 $bar: is nondeterministic $foo $bar $name $name
  167. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: log bar: log name: $name = pagarme log name: } Contexto das últimas
 variáveis na macro Contexto das variáveis na seta setVars
 $foo: value
 $bar: is nondeterministic
 example
 $name: pagarme $name: macabeus $foo: value
 $bar: is nondeterministic $bar $name value $name
  168. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: log bar: log name: $name = pagarme log name: } Contexto das últimas
 variáveis na macro Contexto das variáveis na seta setVars
 $foo: value
 $bar: is nondeterministic
 example
 $name: pagarme $name: macabeus $foo: value
 $bar: is nondeterministic $bar $name value $name
  169. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: log bar: log name: $name = pagarme log name: } Contexto das últimas
 variáveis na macro Contexto das variáveis na seta setVars
 $foo: value
 $bar: is nondeterministic
 example
 $name: pagarme $name: macabeus $foo: value
 $bar: is nondeterministic $bar value macabeus $name
  170. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: log bar: log name: $name = pagarme log name: } Contexto das últimas
 variáveis na macro Contexto das variáveis na seta setVars
 $foo: value
 $bar: is nondeterministic
 example
 $name: pagarme $foo: value
 $bar: is nondeterministic $name: pagarme $bar value macabeus $name
  171. macro setVars { $foo = value $bar = &rand(1, 4)

    } macro example { $name = macabeus call setVars log foo: log bar: log name: $name = pagarme log name: } Contexto das últimas
 variáveis na macro Contexto das variáveis na seta setVars
 $foo: value
 $bar: is nondeterministic
 example
 $name: pagarme $foo: value
 $bar: is nondeterministic $name: pagarme $bar value macabeus pagarme
  172. Parser Código fonte Análise semântica Otimização Geração de código

  173. Parser Código fonte Análise semântica Otimização Geração de código GERAÇÃO

    DE CÓDIGO
  174. header body footer

  175. header body footer

  176. header body footer

  177. header body footer

  178. header body footer

  179. header body footer [
 "push",
 "@values",
 ",",
 [ "f", "o",

    "o" ], ";" ]
  180. header body footer [
 "push",
 "@values",
 ",",
 [ "f", "o",

    "o" ], ";" ] [
 "push",
 "@values",
 ",", "f", "o", "o", ";" ]
  181. header body footer push @values, "foo";

  182. header body footer

  183. header body footer gerar boilerplate

  184. header body footer encontrar quais variáveis são escritas para declará-las


    encontrar os módulos do OpenKore que precisa importar
  185. header body footer encontrar quais variáveis são escritas para declará-las


    encontrar os módulos do OpenKore que precisa importar
  186. header body footer encontrar quais variáveis são escritas para declará-las


    encontrar os módulos do OpenKore que precisa importar
  187. header body footer encontrar quais variáveis são escritas para declará-las


    encontrar os módulos do OpenKore que precisa importar
  188. header body footer encontrar quais variáveis são escritas para declará-las


    encontrar os módulos do OpenKore que precisa importar
  189. header body footer ✏ encontrar quais macros são escritas para

    tornar chamáveis
  190. header body footer ✏ encontrar quais macros são escritas para

    tornar chamáveis
  191. header body footer gerar boilerplate
 encontrar quais variáveis são escritas

    para declará-las
 encontrar os módulos do OpenKore que precisa importar
 ✏ encontrar quais macros são escritas para tornar chamáveis
  192. header body footer

  193. header body footer &push(@values, foo) push @values, "foo";

  194. header body footer &push(@values, foo) ...

  195. header body footer &push(@values, foo) ...

  196. header body footer &push(@values, foo) ...

  197. header body footer &push(@values, foo) [ "push", ..., ",", ...,

    ";" ]
  198. header body footer &push(@values, foo) [ "push", "@values", ",", ...,

    ";" ]
  199. header body footer &push(@values, foo) [ "push", "@values", ",", [

    "f", "o", "o" ], ";" ]
  200. header body footer push @values, "foo"; [ "push", "@values", ",",

    [ "f", "o", "o" ], ";" ]
  201. header body footer &rand(1, 4) ("1" + int(rand(1 + "4"

    - "1")))
  202. body footer header

  203. Parser Código fonte Análise semântica Otimização Geração de código

  204. Parser Código fonte Análise semântica Otimização Geração de código TESTE

  205. Unit test Property-based test Functional test Integration test Mutation test

    E2e test Contract test
  206. Unit test Property-based test Functional test Integration test Mutation test

    E2e test Contract test
  207. Functional test E2e test

  208. Functional test E2e test Testo as otimizações checando a AST

  209. Functional test E2e test Testo as otimizações checando a AST

  210. Functional test E2e test Testo as otimizações checando a AST…

    porém, o importante é verificar se a semântica está preservada. 
 Verificar se está gerando uma AST equivalente garante trivialmente que a semântica é preservada
  211. Functional test E2e test

  212. Functional test E2e test

  213. Functional test E2e test

  214. Functional test E2e test

  215. Functional test E2e test

  216. Functional test E2e test

  217. Functional test E2e test

  218. Functional test E2e test

  219. Functional test E2e test

  220. Functional test E2e test

  221. Functional test E2e test

  222. Functional test E2e test

  223. Functional test E2e test

  224. FINALIZANDO…

  225. Agradecimentos ao Pedro Castilho por ter me ajudado pra caralho

    em desenvolver o compilador e essa talk
  226. Para aprender mais - Material bem didática sobre como funciona

    parser funciona http://theorangeduck.com/page/you-could- have-invented-parser-combinators - Para ver mais possibilidades de fazer interprocess communication no Elixir https://youtu.be/ZrBhuP6OrFI - Manual foda sobre parser https://tomassetti.me/guide- parsing-algorithms-terminology/ - Lib em Ruby de parser generator com PEG, a Parslet
  227. http://bit.ly/asciinema-compiler

  228. github.com/macabeus/macro-compiler

  229. OBRIGADO!