{"id":340,"date":"2018-08-07T08:14:45","date_gmt":"2018-08-07T11:14:45","guid":{"rendered":"http:\/\/suspensao.blog.br\/descrenca\/?p=340"},"modified":"2018-08-07T08:14:45","modified_gmt":"2018-08-07T11:14:45","slug":"nao-me-interprete-mal-improvisando-testes-para-um-interpretador","status":"publish","type":"post","link":"https:\/\/suspensao.blog.br\/descrenca\/nao-me-interprete-mal-improvisando-testes-para-um-interpretador\/","title":{"rendered":"N\u00e3o me Interprete Mal: Improvisando Testes para um Interpretador"},"content":{"rendered":"<p>Estou amando ler o livro <a href=\"http:\/\/craftinginterpreters.com\/\"><em>Crafting Interpreters<\/em><\/a>. Nele, <a href=\"http:\/\/journal.stuffwithstuff.com\/\">Bob Nystrom<\/a> nos ensina como escrever um interpretador implementando uma pequena linguagem de programa\u00e7\u00e3o chamada <a href=\"http:\/\/craftinginterpreters.com\/the-lox-language.html\">Lox<\/a>. H\u00e1 muito tempo n\u00e3o me divertia tanto programando! Al\u00e9m de bem escrito, o livro \u00e9 engra\u00e7ado e ensina bem mais coisas do que eu esperava. Mas estou tendo um problema.<\/p>\n<p>Os <em>snipptes<\/em> no livro s\u00e3o escritos para copiar e colar. Contudo, o livro tem desafios ao final de cada cap\u00edtulo, estes desafios n\u00e3o t\u00eam c\u00f3digo-fonte e por vezes nos for\u00e7am a alterar muito o interpretador. Eu fa\u00e7o todos estes exerc\u00edcios, e como resultado meu interpretador \u00e9 diferente demais do c\u00f3digo mostrado no livro. Como consequ\u00eancia, v\u00e1rias vezes quebro alguma parte do interpretador.<\/p>\n<p>Como resolver isso?<\/p>\n<p>Testes unit\u00e1rios seriam fr\u00e1geis, j\u00e1 que a estrutura do c\u00f3digo muda frequentemente. <a href=\"https:\/\/www.techopedia.com\/definition\/7035\/end-to-end-test\">Testes de fim a fim<\/a> parecem mais pr\u00e1ticos nesse caso. Assim, para cada nova funcionalidade da linguagem, escrevi um programinha. Por exemplo, meu interpretador deve criar <a href=\"http:\/\/craftinginterpreters.com\/functions.html#local-functions-and-closures\">clausuras<\/a>, e para garantir isso copiei o programa em Lox abaixo para o arquivo <a href=\"https:\/\/bitbucket.org\/brandizzi\/jlox\/src\/default\/examples\/counter.lox?at=default&#038;fileviewer=file-view-default\"><code>counter.lox<\/code><\/a>:<br \/>\n<script src=\"https:\/\/pastebin.com\/embed_js\/fzCFuC1k\"><\/script><noscript><\/p>\n<pre><code>fun makeCounter() {\r\n  var i = 0;\r\n  fun count() {\r\n    i = i + 1;\r\n    print i;\r\n  }\r\n\r\n  return count;\r\n}\r\n\r\nvar counter = makeCounter();\r\ncounter(); \/\/ \"1\".\r\ncounter(); \/\/ \"2\".<\/code><\/pre>\n<p><\/noscript><\/p>\n<p>O resultado deste programa devem ser os n\u00fameros 1 e 2 impressos em linhas separadas. Ent\u00e3o coloquei esses valores em um arquivo chamado <a href=\"https:\/\/bitbucket.org\/brandizzi\/jlox\/src\/default\/examples\/counter.lox.out\"><code>counter.lox.out<\/code><\/a>. O programa tamb\u00e9m n\u00e3o pode falhar, ent\u00e3o criei um arquivo vazio chamado <a href=\"https:\/\/bitbucket.org\/brandizzi\/jlox\/src\/default\/examples\/counter.lox.err\"><code>counter.lox.err<\/code><\/a>. (Em alguns casos \u00e9 preciso garantir que o programa Lox falhar\u00e1. Nesses casos, o arquivo <code>.lox.err<\/code> deve ter conte\u00fado.)<\/p>\n<p>Pois bem, escrevi programas e arquivos de sa\u00edda para v\u00e1rios exemplos. Agora preciso comparar os resultados dos programas com as sa\u00eddas esperadas. Resolvi ent\u00e3o usar a ferramenta que mais <a href=\"http:\/\/suspensao.blog.br\/descrenca\/trocando-figurinhas-sobre-o-terminal\/\">me ajuda nos momentos de urg\u00eancia<\/a>: shell script. Fiz um script Bash com um la\u00e7o <code>for<\/code> iterando sobre todos os exemplos:<\/p>\n<p><script src=\"https:\/\/pastebin.com\/embed_js\/hwW7nCM0\"><\/script><noscript><\/p>\n<pre><code>for l in examples\/*.lox\r\ndo\r\n\r\ndone<\/code><\/pre>\n<p><\/noscript><\/p>\n<p>Para cada exemplo, executei o programa Lox, redirecionando as sa\u00eddas para arquivos tempor\u00e1rios:<br \/>\n<script src=\"https:\/\/pastebin.com\/embed_js\/LDD3LjEk\"><\/script><noscript><\/p>\n<pre><code>for l in examples\/*.lox\r\ndo\r\n  out=$(mktemp)\r\n  err=$(mktemp)\r\n  java -classpath target\/classes\/ br.com.brandizzi.adam.myjlox.Lox $l &gt; $out 2&gt; $err\r\ndone<\/code><\/pre>\n<p><\/noscript><\/p>\n<p>Agora, comparamos a sa\u00edda real com a sa\u00edda esperada com <a href=\"https:\/\/www.gnu.org\/software\/diffutils\/\"><code>diff<\/code><\/a>. Quando compara dois arquivos, <a href=\"https:\/\/stackoverflow.com\/questions\/6971284\/what-are-the-error-exit-values-for-diff\"><code>diff<\/code> returna 0 se n\u00e3o h\u00e1 diferen\u00e7a, 1 se existe alguma diferen\u00e7a, ou 2 em caso de erro<\/a>. Como em Bash o condicional <code>if<\/code> considera 0 como verdadeiro, basta checar a nega\u00e7\u00e3o do retorno de <code>diff<\/code>.<\/p>\n<p>Se o programa imprimir algo na sa\u00edda-padr\u00e3o diferente do que est\u00e1 no arquivo <code>.lox.out<\/code>, temos uma falha:<\/p>\n<p><script src=\"https:\/\/pastebin.com\/embed_js\/Z8Fk2EyN\"><\/script><noscript><\/p>\n<pre><code>for l in examples\/*.lox\r\ndo\r\n  out=$(mktemp)\r\n  err=$(mktemp)\r\n  java -classpath target\/classes\/ br.com.brandizzi.adam.myjlox.Lox $l &gt; $out 2&gt; $err\r\n\r\n  if ! diff $l.out $out\r\n  then\r\n    FAIL=1\r\n  fi\r\ndone<\/code><\/pre>\n<p><\/noscript><\/p>\n<p>Tamb\u00e9m comparamos a sa\u00edda de erro com o arquivo <code>.lox.err<\/code>:<\/p>\n<p><script src=\"https:\/\/pastebin.com\/embed_js\/GbBJE4ff\"><\/script><noscript><\/p>\n<pre><code>for l in examples\/*.lox\r\ndo\r\n  out=$(mktemp)\r\n  err=$(mktemp)\r\n  java -classpath target\/classes\/ br.com.brandizzi.adam.myjlox.Lox $l &gt; $out 2&gt; $err\r\n\r\n  if ! diff $l.out $out\r\n  then\r\n    FAIL=1\r\n  fi\r\n\r\n  if ! diff $l.err $err\r\n  then\r\n    FAIL=1\r\n  fi\r\ndone<\/code><\/pre>\n<p><\/noscript><\/p>\n<p>Por fim, verifico se houve alguma falha e reporto o resultado:<\/p>\n<p><script src=\"https:\/\/pastebin.com\/embed_js\/XagVcuG2\"><\/script><noscript><\/p>\n<pre><code>for l in examples\/*.lox\r\ndo\r\n  out=$(mktemp)\r\n  err=$(mktemp)\r\n  java -classpath target\/classes\/ br.com.brandizzi.adam.myjlox.Lox $l &gt; $out 2&gt; $err\r\n\r\n  if ! diff $l.out $out\r\n  then\r\n    FAIL=1\r\n  fi\r\n\r\n  if ! diff $l.err $err\r\n  then\r\n    FAIL=1\r\n  fi\r\n\r\n  if [ &quot;$FAIL&quot; = &quot;1&quot; ]\r\n  then\r\n    echo &quot;FAIL&quot; $l\r\n  else\r\n    echo &quot;PASS&quot; $l\r\n  fi\r\ndone<\/code><\/pre>\n<p><\/noscript><\/p>\n<p>Nem todos os meus programas em Lox podem ser checados, por\u00e9m. Por exemplo, h\u00e1 <a href=\"https:\/\/bitbucket.org\/brandizzi\/jlox\/src\/default\/examples\/timeit.lox\">um programa que cronometra a execu\u00e7\u00e3o de loops<\/a>, \u00e9 imposs\u00edvel prever o valor que ele vai imprimir. Por isso, adicionei a possibilidade de pular alguns programas: basta criar <a href=\"https:\/\/bitbucket.org\/brandizzi\/jlox\/src\/default\/examples\/timeit.lox.skip\">um arquivo com a extens\u00e3o <code>.lox.skip<\/code><\/a>:<\/p>\n<p><script src=\"https:\/\/pastebin.com\/embed_js\/MM30zc01\"><\/script><noscript><\/p>\n<pre><code>for l in examples\/*.lox\r\ndo\r\n  if [ -e $l.skip ]\r\n  then\r\n    echo SKIP $l\r\n    continue\r\n  fi\r\n\r\n  out=$(mktemp)\r\n  err=$(mktemp)\r\n  java -classpath target\/classes\/ br.com.brandizzi.adam.myjlox.Lox $l &gt; $out 2&gt; $err\r\n\r\n  if ! diff $l.out $out\r\n  then\r\n    FAIL=1\r\n  fi\r\n\r\n  if ! diff $l.err $err\r\n  then\r\n    FAIL=1\r\n  fi\r\n\r\n  if [ &quot;$FAIL&quot; = &quot;1&quot; ]\r\n  then\r\n    echo &quot;FAIL&quot; $l\r\n  else\r\n    echo &quot;PASS&quot; $l\r\n  fi\r\ndone<\/code><\/pre>\n<p><\/noscript><\/p>\n<p>Se, por\u00e9m, tenho um exemplo em Lox e ele n\u00e3o possui os arquivos de sa\u00edda esperada (nem o arquivo <code>.lox.skip<\/code>) ent\u00e3o tenho um problema e o script inteiro falha:<\/p>\n<p><script src=\"https:\/\/pastebin.com\/embed_js\/arAxZYAN\"><\/script><noscript><\/p>\n<pre><code>for l in examples\/*.lox\r\ndo\r\n  if [ -e $l.skip ]\r\n  then\r\n    echo SKIP $l\r\n    continue\r\n  elif [ ! -e $l.out ] || [ ! -e $l.err ]\r\n  then\r\n    echo missing $l.out or $l.err\r\n    exit 1\r\n  fi\r\n\r\n  out=$(mktemp)\r\n  err=$(mktemp)\r\n  java -classpath target\/classes\/ br.com.brandizzi.adam.myjlox.Lox $l &gt; $out 2&gt; $err\r\n\r\n  if ! diff $l.out $out\r\n  then\r\n    FAIL=1\r\n  fi\r\n\r\n  if ! diff $l.err $err\r\n  then\r\n    FAIL=1\r\n  fi\r\n\r\n  if [ &quot;$FAIL&quot; = &quot;1&quot; ]\r\n  then\r\n    echo &quot;FAIL&quot; $l\r\n  else\r\n    echo &quot;PASS&quot; $l\r\n  fi\r\ndone<\/code><\/pre>\n<p><\/noscript><\/p>\n<p>Com isto, meu script testador est\u00e1 pronto. Vejamos como se comporta:<\/p>\n<p><code>$ .\/lcheck.sh<br \/>\nPASS examples\/attr.lox<br \/>\nPASS examples\/bacon.lox<br \/>\nPASS examples\/badfun.lox<br \/>\nPASS examples\/badret.lox<br \/>\nPASS examples\/bagel.lox<br \/>\nPASS examples\/bostoncream.lox<br \/>\nPASS examples\/cake.lox<br \/>\nPASS examples\/checkuse.lox<br \/>\nPASS examples\/circle2.lox<br \/>\nPASS examples\/circle.lox<br \/>\n1d0<br \/>\n< 3\n1c1\n<\n---\n> [line 1] Error at ',': Expect ')' after expression.<br \/>\nFAIL examples\/comma.lox<br \/>\nPASS examples\/counter.lox<br \/>\nPASS examples\/devonshinecream.lox<br \/>\nPASS examples\/eclair.lox<br \/>\nPASS examples\/fibonacci2.lox<br \/>\nPASS examples\/fibonacci.lox<br \/>\nPASS examples\/func.lox<br \/>\nPASS examples\/funexprstmt.lox<br \/>\nPASS examples\/hello2.lox<br \/>\nPASS examples\/hello3.lox<br \/>\nPASS examples\/hello.lox<br \/>\nPASS examples\/math.lox<br \/>\nPASS examples\/notaclass.lox<br \/>\nPASS examples\/noteveninaclass.lox<br \/>\nPASS examples\/point.lox<br \/>\nPASS examples\/retthis.lox<br \/>\nPASS examples\/scope1.lox<br \/>\nPASS examples\/scope.lox<br \/>\nPASS examples\/supersuper.lox<br \/>\nPASS examples\/thisout.lox<br \/>\nPASS examples\/thrice.lox<br \/>\nSKIP examples\/timeit.lox<br \/>\nPASS examples\/twovars.lox<br \/>\nPASS examples\/usethis.lox<br \/>\nPASS examples\/varparam.lox<\/code><\/p>\n<p>Opa, aparentemente removi o suporte ao <a href=\"http:\/\/www.1bit.com.br\/content.1bit\/weblog\/cpp_comma_op\">operador v\u00edrgula<\/a> por acidente. Ainda bem que fiz este script, n\u00e3o \u00e9?<\/p>\n<p>Espero que este post tenha sido minimamente interessante! Agora, vou consertar meu operador v\u00edrgula e seguir lendo <a href=\"http:\/\/craftinginterpreters.com\/\">este livro maravilhoso<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Estou amando ler o livro Crafting Interpreters. Nele, Bob Nystrom nos ensina como escrever um interpretador implementando uma pequena linguagem de programa\u00e7\u00e3o chamada Lox. H\u00e1 muito tempo n\u00e3o me divertia tanto programando! Al\u00e9m de bem escrito, o livro \u00e9 engra\u00e7ado e ensina bem mais coisas do que eu esperava. Mas estou tendo um problema. Os [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[24,27,93],"tags":[101,94,95,96,97,91,99,98,100],"class_list":["post-340","post","type-post","status-publish","format-standard","hentry","category-linguagens-de-programacao","category-shell-script","category-testes-programacao","tag-bash","tag-crafting-interpeters","tag-interpretadores","tag-linguagens-de-programacao","tag-lox","tag-shell-script","tag-testes","tag-testes-automatizados","tag-testes-fim-a-fim"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p23QLV-5u","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/posts\/340","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/comments?post=340"}],"version-history":[{"count":7,"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/posts\/340\/revisions"}],"predecessor-version":[{"id":350,"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/posts\/340\/revisions\/350"}],"wp:attachment":[{"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/media?parent=340"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/categories?post=340"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/suspensao.blog.br\/descrenca\/wp-json\/wp\/v2\/tags?post=340"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}