<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tim Austin</title>
    <description>The latest articles on DEV Community by Tim Austin (@neenjaw).</description>
    <link>https://dev.to/neenjaw</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F122046%2F0a13b91e-b71b-4797-894c-f0421d4c4ed8.png</url>
      <title>DEV Community: Tim Austin</title>
      <link>https://dev.to/neenjaw</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/neenjaw"/>
    <language>en</language>
    <item>
      <title>Writing an Elixir Code Analyzer (with metaprogramming magic 🧙)</title>
      <dc:creator>Tim Austin</dc:creator>
      <pubDate>Wed, 09 Sep 2020 03:52:19 +0000</pubDate>
      <link>https://dev.to/neenjaw/writing-an-elixir-code-analyzer-with-metaprogramming-magic-fl4</link>
      <guid>https://dev.to/neenjaw/writing-an-elixir-code-analyzer-with-metaprogramming-magic-fl4</guid>
      <description>&lt;p&gt;Last year, my &lt;a href="https://dev.to/sleeplessbyte"&gt;co-contributor&lt;/a&gt; to &lt;a href="https://www.exercism.io"&gt;Exercism.io&lt;/a&gt; wrote a &lt;a href="https://dev.to/xpbytes/writing-a-code-analyzer-in-typescript-5ec3"&gt;fantastic article about writing a TypeScript code analyzer&lt;/a&gt;, and I wanted to follow up by sharing my thoughts and experiences writing one for Elixir.&lt;/p&gt;

&lt;p&gt;In case you have not yet read that article, &lt;a href="https://www.exercism.io"&gt;Exercism.io&lt;/a&gt; is a free platform designed to help you improve your coding skills through practice and mentorship.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://exercism.io/"&gt;Exercism&lt;/a&gt; provides you with thousands of exercises spread across numerous language tracks. Once you start a language track you are presented with a core set of exercises to complete. Each one is a fun and interesting challenge designed to teach you a little more about the features of a language.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An initiative was started in 2019 to incorporate automation into the mentoring process to improve the experience for students of each language. Automated feedback means faster responses, which means greater student satisfaction.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Over &lt;strong&gt;500,000&lt;/strong&gt; people have used &lt;a href="https://exercism.io/"&gt;Exercism&lt;/a&gt; and have submitted 1.5 million submissions, &lt;strong&gt;220,000&lt;/strong&gt; of which have been mentored by our volunteers. In 2019, &lt;a href="https://www.mozilla.org/en-US/moss/"&gt;MOSS&lt;/a&gt; supported [&lt;a href="https://exercism.io/"&gt;Exercism&lt;/a&gt;] in the creation of an automatic feedback system based on automated analysis of submissions. Since launching, [the system has] automatically approved &lt;strong&gt;25,000&lt;/strong&gt; solutions, and generated &lt;strong&gt;38,000&lt;/strong&gt; pre-canned comments for mentors to post, saving our [volunteer] mentors &lt;strong&gt;263&lt;/strong&gt; days of work! -&lt;a href="https://ihid.info/"&gt;Jeremy "iHiD" Walker&lt;/a&gt; (Exercism co-founder)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://dev.to/sleeplessbyte"&gt;Derk-Jan&lt;/a&gt; walked through the &lt;a href="https://dev.to/xpbytes/writing-a-code-analyzer-in-typescript-5ec3"&gt;reasoning and process of writing a TypeScript analyzer&lt;/a&gt;. But to briefly summarize:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unit tests are great for testing &lt;strong&gt;code behavior&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Unit tests are &lt;strong&gt;not suited&lt;/strong&gt; for testing &lt;strong&gt;code style&lt;/strong&gt; or &lt;strong&gt;code smells&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Using static code analysis techniques allows development of automated feedback to catch easy cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Removing even a fraction of work&lt;/strong&gt; from a human mentor's workload is &lt;strong&gt;valuable&lt;/strong&gt; to both the student and the mentor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As I set in to write Elixir's analyzer, it occurred to me that it is &lt;strong&gt;really hard&lt;/strong&gt; to write good static analysis tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Analysis is hard...

&lt;ul&gt;
&lt;li&gt;...because of normal variation&lt;/li&gt;
&lt;li&gt;...because AST syntax is complex at scale&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;A new strategy emerges, Pattern Matching&lt;/li&gt;
&lt;li&gt;Searching through code&lt;/li&gt;
&lt;li&gt;
Creating a language to describe the tests

&lt;ul&gt;
&lt;li&gt;Designing the DSL&lt;/li&gt;
&lt;li&gt;Writing the macro&lt;/li&gt;
&lt;li&gt;Transforming the pattern&lt;/li&gt;
&lt;li&gt;Storing the pattern&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Putting it together&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Analysis is hard...
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ...because of normal variation
&lt;/h3&gt;

&lt;p&gt;Code, like language, is expressive and creative. It allows people to write the same things in many ways to express slight differences in meaning and usage. Let's look at a JavaScript example before moving into Elixir, as it illustrates the point clearly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="c1"&gt;// FunctionDeclaration&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="c1"&gt;// ArrowFunctionExpression&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;span class="c1"&gt;// FunctionExpression&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// ExportDefaultDeclaration + ObjectExpression + (Arrow)FunctionExpression&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are 4 ways to write the same thing: an exported function declaration. So analyzer tests have to account for normal, acceptable ways to write clear, concise code according to the values of the person writing the tests (Sometimes this is 'idiomatic' code; often it comes down to personal choices and beliefs about programming), which is why static analysis is often done on a represented intermediate form, such as an &lt;a href="https://hexdocs.pm/elixir/syntax-reference.html#the-elixir-ast"&gt;abstract syntax tree&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An abstract syntax tree (AST) is a representative form of the code often used as an intermediate form for compilers to perform optimizations and transformations of the code without worrying about how code is formatted.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Okay, leaving JavaScript behind, let's look at Elixir.&lt;/p&gt;

&lt;h3&gt;
  
  
  ...because AST syntax is complex at scale
&lt;/h3&gt;

&lt;p&gt;Abstract syntax trees, at their most basic, take only a few shapes in Elixir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Suppose we had the function:&lt;/span&gt;
&lt;span class="n"&gt;sum_three&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# And we used it like this:&lt;/span&gt;
&lt;span class="n"&gt;sum_three&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# The underlying AST that represents this function call is:&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:sum_three&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first element of the &lt;a href="https://hexdocs.pm/elixir/Tuple.html"&gt;tuple&lt;/a&gt; may be an &lt;a href="https://hexdocs.pm/elixir/Atom.html"&gt;atom&lt;/a&gt; or another tuple. The second element is a &lt;a href="https://hexdocs.pm/elixir/List.html"&gt;list&lt;/a&gt; of metadata (often line numbers when read from a file). The third element is a list of the arguments. We can get the AST of any Elixir code using the &lt;a href="https://hexdocs.pm/elixir/Kernel.SpecialForms.html#quote/2"&gt;&lt;code&gt;quote/2&lt;/code&gt;&lt;/a&gt; &lt;a href="https://hexdocs.pm/elixir/Macro.html"&gt;macro&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;iex&amp;gt; quote do
...&amp;gt;   1
...&amp;gt;   x = 2
...&amp;gt;   3 + x
...&amp;gt; end
{:__block__, [],
 [
   1,
   {:=, [], [{:x, [], Elixir}, 2]},
   {:+, [context: Elixir, import: Kernel], [3, {:x, [], Elixir}]}
 ]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the AST form, this code quickly loses meaning for the observer.&lt;/p&gt;

&lt;p&gt;But, like the &lt;a href="https://dev.to/xpbytes/writing-a-code-analyzer-in-typescript-5ec3"&gt;TypeScript Analyzer&lt;/a&gt;, why not use a standard such as &lt;a href="https://github.com/estree/estree"&gt;ESTree&lt;/a&gt;, in order to build upon semantic meaning and abstractions? Sadly, it is because such a library does not exist in the Elixir ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  A new strategy emerges: Pattern Matching
&lt;/h2&gt;

&lt;p&gt;Since much of static analysis is looking for patterns in code, Elixir's core strength is the ability to use &lt;a href="https://hexdocs.pm/elixir/Kernel.html#match?/2"&gt;pattern matching&lt;/a&gt;. Pattern matching can be used to compare the &lt;em&gt;shape&lt;/em&gt; of data without knowing the actual data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; [1, 2, 3]&lt;/span&gt;

&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; ** (MatchError) no match of right hand side value: [1, 2, 3]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are two examples. In the first, the underscore (&lt;code&gt;_&lt;/code&gt;) is used to say “we don't care what it is”, but only are concerned that we have 1 thing on the right side of the match (IE: 1 list of integers). In the second example, we declare we want to match a list with a single element. The right-hand side does not match this &lt;em&gt;shape&lt;/em&gt; and as a result it raises an error.&lt;/p&gt;

&lt;p&gt;So applying this to the AST problem:&lt;/p&gt;

&lt;p&gt;To write a test to ensure a student wrote a piece of code, we could look for a pattern match based on the desired &lt;em&gt;shape&lt;/em&gt; of the code without knowing all of the other implementation details!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Does a student bind any value to x?&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quoted_form&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;              &lt;span class="c1"&gt;# The match/bind function&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="no"&gt;Elixir&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;     &lt;span class="c1"&gt;# The x variable&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt;                     &lt;span class="c1"&gt;# The any-value being bound&lt;/span&gt;
  &lt;span class="p"&gt;]}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quoted_form&lt;/span&gt;
  &lt;span class="no"&gt;true&lt;/span&gt;
&lt;span class="k"&gt;rescue&lt;/span&gt;
  &lt;span class="no"&gt;MatchError&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;false&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;quoted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kn"&gt;quote&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; {:=, [], [{:x, [], Elixir}, 3]}&lt;/span&gt;

&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quoted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How powerful! Now, a student may choose any value to bind to x, and we can find this pattern in the AST!&lt;/p&gt;

&lt;h2&gt;
  
  
  Searching through code
&lt;/h2&gt;

&lt;p&gt;But Exercism doesn’t consist of one-line problems. Problems, like &lt;a href="https://github.com/exercism/elixir/tree/master/exercises/two-fer"&gt;&lt;code&gt;TwoFer&lt;/code&gt;&lt;/a&gt;, can be very simple, but require at minimum several lines of code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Sample solution for ‘TwoFer’&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;TwoFer&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@doc&lt;/span&gt; &lt;span class="sd"&gt;"""
  Two-fer or 2-fer is short for two for one.
  One for you and one for me.
  """&lt;/span&gt;
  &lt;span class="nv"&gt;@spec&lt;/span&gt; &lt;span class="n"&gt;two_fer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;::&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;two_fer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;\\&lt;/span&gt; &lt;span class="s2"&gt;"you"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;when&lt;/span&gt; &lt;span class="n"&gt;is_binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="s2"&gt;"One for &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, one for me"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing a test like we did before to account for every variation would be very hard. Someone might rearrange statements or expressions and the test still has to work.&lt;/p&gt;

&lt;p&gt;If this solution is converted to an AST, the &lt;a href="https://hexdocs.pm/elixir/Macro.html"&gt;Elixir &lt;code&gt;Macro module&lt;/code&gt;&lt;/a&gt; provides functions that can be used to search through the tree. &lt;code&gt;Macro.traverse/4&lt;/code&gt; is a generalized function that will walk through the generated AST, maintain an accumulator, and apply a function to the node before it walks further down the tree, or after it returns, on its way back up the tree. We only need an accumulator (to track the status of our tests) and a function to test on the way down the tree, so it is simpler to use &lt;code&gt;Macro.prewalk/3&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;By using function from the Code &lt;a href="https://hexdocs.pm/elixir/Code.html"&gt;&lt;code&gt;Code&lt;/code&gt;&lt;/a&gt; module, the file can be read into a string, then converted to a quoted form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="s2"&gt;"code_file.ex"&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# read the file to a string&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;string_to_quoted!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# create the AST&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Macro&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prewalk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;matched?&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;matched?&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# if it already matched, propagate&lt;/span&gt;
    &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="no"&gt;true&lt;/span&gt;

    &lt;span class="c1"&gt;# test the node for a match&lt;/span&gt;
    &lt;span class="no"&gt;false&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a language to describe the tests
&lt;/h2&gt;

&lt;p&gt;Despite having an approach and a method, testing was still very hard to accomplish, because writing AST-patterns is very complex and error-prone. A single bracket changes the meaning of the AST, causing either false-positive or false-negative results. And this being an automated system, issuing errors when nothing is erroneous is &lt;strong&gt;unacceptable&lt;/strong&gt; and would likely frustrate a student, possibly causing them to give up.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We have all been there, minding our own business filling out a form on the internet when a wild error appears! You try to troubleshoot it, but the error persists. Frustrated, you abandon the website, never to return.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So making tests easy to write and understand for a maintainer is a priority. So what if we wrote a domain specific language to describe the tests declaratively?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;strong&gt;domain-specific language&lt;/strong&gt; (&lt;strong&gt;DSL&lt;/strong&gt;) is a computer language specialized to a particular application domain. -&lt;a href="https://en.wikipedia.org/wiki/Domain-specific_language"&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Elixir can do this! With metaprogramming you can write macros that &lt;em&gt;transform&lt;/em&gt; and &lt;em&gt;generate&lt;/em&gt; code at compile-time, so that it has a specialized behavior at run-time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Designing the DSL
&lt;/h3&gt;

&lt;p&gt;The goal is to have a declarative syntax to write our test, so let's look at &lt;code&gt;ExUnit&lt;/code&gt;, Elixir's batteries-included test suite, to see if we can find some features to emulate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="s2"&gt;"addition"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;assert&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;ExUnit&lt;/code&gt;, we have a keyword &lt;code&gt;test&lt;/code&gt;, which calls a macro, which then uses the string, &lt;code&gt;"addition"&lt;/code&gt;, to create a function with the contents of the &lt;code&gt;do-block&lt;/code&gt;, which is then executed at run-time. So let's use this inspiration to dream up a specification for static analysis.&lt;/p&gt;

&lt;p&gt;For our static analysis pattern matching tests, we’d like to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Label the test with a name&lt;/li&gt;
&lt;li&gt;Be able to specify if the test should be run or skipped&lt;/li&gt;
&lt;li&gt;Decide what to do with the solution if the test fails&lt;/li&gt;
&lt;li&gt;Return a message to the student, preferably with an actionable step

&lt;ul&gt;
&lt;li&gt;In our case, we will write a separate markdown comment, and display that&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Further specify the conditions where in the code this pattern should be found

&lt;ul&gt;
&lt;li&gt;Like if it appears at the top-level, or nested in code (depth)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Be able to write regular Elixir code to define the pattern&lt;/li&gt;
&lt;li&gt;Be able to ignore certain parts of a pattern, so that we can generalize the pattern to match&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After a lot of wrestling about names and conventions, the DSL eventually became this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;feature&lt;/span&gt; &lt;span class="s2"&gt;"has spec defined"&lt;/span&gt;  &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt;                &lt;span class="c1"&gt;# :skip&lt;/span&gt;
  &lt;span class="n"&gt;on_fail&lt;/span&gt; &lt;span class="ss"&gt;:info&lt;/span&gt;               &lt;span class="c1"&gt;# :disapprove, or :refer&lt;/span&gt;
  &lt;span class="n"&gt;comment&lt;/span&gt; &lt;span class="s2"&gt;"Path.to.markdown.comment"&lt;/span&gt;
  &lt;span class="n"&gt;find&lt;/span&gt; &lt;span class="ss"&gt;:all&lt;/span&gt;                   &lt;span class="c1"&gt;# :any, :one, or :none&lt;/span&gt;
  &lt;span class="c1"&gt;# depth &amp;lt;number&amp;gt;            # optional&lt;/span&gt;

  &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="nv"&gt;@spec&lt;/span&gt; &lt;span class="n"&gt;_ignore&lt;/span&gt;             &lt;span class="c1"&gt;# the pattern looks for a @spec module attribute&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;                         &lt;span class="c1"&gt;# but doesn't need to match the contents&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;find&lt;/code&gt; keyword is used as tests become more complex. Multiple &lt;code&gt;form&lt;/code&gt; blocks can be declared for a test, and you can specify how it will use those blocks to match.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing the macro
&lt;/h3&gt;

&lt;p&gt;In Elixir, a macro is similar to a function definition, but it can only be used in certain circumstances:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The module containing the macro must be imported using &lt;code&gt;require&lt;/code&gt; to opt-in. (There are some ways to get around this, but in general this is true)&lt;/li&gt;
&lt;li&gt;The macro must be defined before it is used (it isn't "hoisted", to use a JavaScript term)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's write a basic macro for the &lt;code&gt;feature&lt;/code&gt; call in the DSL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Module to contain the macro&lt;/span&gt;
&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;ExerciseTest&lt;/span&gt;

  &lt;span class="c1"&gt;# Define the name, arguments of the macro&lt;/span&gt;
  &lt;span class="k"&gt;defmacro&lt;/span&gt; &lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# do the transformation here using the arguments here&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Often, we would use this macro to return a result from the transformation (like the &lt;a href="https://hexdocs.pm/elixir/Kernel.html#in/2"&gt;&lt;code&gt;in/2&lt;/code&gt;&lt;/a&gt; macro), but here, we are going to use the macro to write a function using the transformation, so it can be called later. So at a high level, our macro needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;transform the elixir code pattern we are looking for into an AST form in order to be able to pattern match on it&lt;/li&gt;
&lt;li&gt;store the pattern in a module attribute for use when called&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;(This code is quite involved, and not perfect! So I will be only touching on select parts, &lt;a href="https://github.com/exercism/elixir-analyzer/blob/b2760ee89fc48fd5d9d9591bb67191723d354413/lib/elixir_analyzer/exercise_test.ex"&gt;see the whole source&lt;/a&gt; for how it all works together)&lt;/p&gt;

&lt;h4&gt;
  
  
  Transforming the pattern
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;(See the source, &lt;a href="https://github.com/exercism/elixir-analyzer/blob/b2760ee89fc48fd5d9d9591bb67191723d354413/lib/elixir_analyzer/exercise_test.ex#L66"&gt;&lt;code&gt;exercise_test.ex&lt;/code&gt;, line 66&lt;/a&gt;)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To recap, we want to transform a readable pattern like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@spec&lt;/span&gt; &lt;span class="n"&gt;_ignore&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Into a pattern to match on like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;:spec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;]}]}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the macro is being executed, the block that was passed to it, is transformed into the AST and has the form (with or without line number metadata):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;context:&lt;/span&gt; &lt;span class="no"&gt;Elixir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;Kernel&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="ss"&gt;:spec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;context:&lt;/span&gt; &lt;span class="no"&gt;Elixir&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="ss"&gt;:_ignore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="no"&gt;Elixir&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two transformations need to be done to normalize the pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Remove metadata&lt;/li&gt;
&lt;li&gt;Change &lt;code&gt;:_ignore&lt;/code&gt; into &lt;code&gt;_&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Following the pattern we have already observed, we can use &lt;code&gt;Macro.prewalk&lt;/code&gt; to walk through the AST to do this for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Remove the metadata, changing it into something we can ignore&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="no"&gt;Macro&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prewalk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:_ignore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;then&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Change _ignore into _&lt;/span&gt;
&lt;span class="no"&gt;Macro&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prewalk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;cond&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;atom&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt;  &lt;span class="ss"&gt;:_ignore&lt;/span&gt;  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  &lt;span class="s2"&gt;"_"&lt;/span&gt;
      &lt;span class="n"&gt;meta&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt;  &lt;span class="ss"&gt;:_ignore&lt;/span&gt;  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;atom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"_"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="no"&gt;true&lt;/span&gt;  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;node&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="n"&gt;node&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;# AST to a string, since _ can't appear outside of pattern match&lt;/span&gt;
&lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"_"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Collapse duplicate quotes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After those two transformations we have &lt;code&gt;"{:@, _, [{:spec, _, [_]}]}"&lt;/code&gt; which can be used as a pattern with the function &lt;code&gt;Code.string_to_quoted!/2&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Storing the pattern
&lt;/h4&gt;

&lt;p&gt;An interesting feature of Elixir modules are module attributes. They serve several features, but we will use them as temporary storage to pass along our patterns from the pre-compile step (where macros are expanded), to the compile step (where top-level functions are expanded).&lt;/p&gt;

&lt;p&gt;Look at this example module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;PassItForward&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@data&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;            &lt;span class="c1"&gt;# Bind the initial empty list to the attribute&lt;/span&gt;
  &lt;span class="nv"&gt;@data&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;@data&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# Rebind the attribute, building on the data&lt;/span&gt;
  &lt;span class="nv"&gt;@data&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;@data&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# Rebind again, resulting in `[1, 2]`&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once our feature macro has made the pattern for the test, the function will store the pattern (and the other test parameters) in a module attribute to be used when creating the final test function.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(See the source, &lt;a href="https://github.com/exercism/elixir-analyzer/blob/b2760ee89fc48fd5d9d9591bb67191723d354413/lib/elixir_analyzer/exercise_test.ex#L42"&gt;&lt;code&gt;exercise_test.ex&lt;/code&gt;, line 42&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="kn"&gt;quote&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="nv"&gt;@feature_tests&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kn"&gt;unquote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feature_data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kn"&gt;unquote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Macro&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feature_forms&lt;/span&gt;&lt;span class="p"&gt;))}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;@feature_tests&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait a second! What is that &lt;code&gt;unquote&lt;/code&gt; function?! 🤯 For a macro to affect the outside context, it &lt;em&gt;&lt;strong&gt;must&lt;/strong&gt;&lt;/em&gt; return a &lt;em&gt;quoted&lt;/em&gt; block. When the macro returns, it transforms the &lt;em&gt;quoted&lt;/em&gt; representation (AST) to Elixir code. So to summarize &lt;a href="https://elixir-lang.org/getting-started/meta/quote-and-unquote.html"&gt;Elixir's getting started guide&lt;/a&gt;: to inject the value from the variable, we use the &lt;code&gt;unquote&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Rather than this:&lt;/span&gt;
&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;
&lt;span class="no"&gt;Macro&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kn"&gt;quote&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; "11 + number"&lt;/span&gt;

&lt;span class="c1"&gt;# We want this:&lt;/span&gt;
&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;
&lt;span class="no"&gt;Macro&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kn"&gt;quote&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="kn"&gt;unquote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; "11 + 13"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Macro.escape&lt;/code&gt; prevents the &lt;em&gt;unquoted&lt;/em&gt; value from being altered on return.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it together
&lt;/h2&gt;

&lt;p&gt;We have an approach to analyze the solution through pattern matching, and with the DSL we have a way to declaratively write tests &lt;em&gt;(write what we want it to do rather than how to do it)&lt;/em&gt;. But how do we use this?&lt;/p&gt;

&lt;p&gt;You could just copy and paste these functions into every module to be tested, but that would be unmaintainable and precarious.&lt;/p&gt;

&lt;p&gt;So we are going to organize our code like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Organize our DSL macro function into a module (&lt;code&gt;ElixirAnalyzer.ExerciseTest.ex&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Write a module for a specific problem that contains tests to be compiled (&lt;code&gt;ElixirAnalyzer.ExerciseTest.TwoFer.ex&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then we will inline the macro, calling &lt;code&gt;use ElixirAnalyzer.ExerciseTest&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This is a macro call to &lt;code&gt;__using__/1&lt;/code&gt; which then generates this code in &lt;code&gt;TwoFer&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmacro&lt;/span&gt; &lt;span class="n"&gt;__using__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;quote&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# Inline ExerciseTest into TwoFer&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="kn"&gt;unquote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Call the __before_compile__ function to happen after&lt;/span&gt;
    &lt;span class="c1"&gt;# macro expansion, but before compilation&lt;/span&gt;
    &lt;span class="nv"&gt;@before_compile&lt;/span&gt; &lt;span class="kn"&gt;unquote&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;__MODULE__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="c1"&gt;# A default flag for our analyzer system&lt;/span&gt;
    &lt;span class="nv"&gt;@auto_approvable&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

    &lt;span class="c1"&gt;# the initial state of the module attribute&lt;/span&gt;
    &lt;span class="nv"&gt;@feature_tests&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;Just before the code is compiled &lt;code&gt;__before_compile__&lt;/code&gt; is called, which uses the collected &lt;code&gt;@feature_tests&lt;/code&gt; to create the &lt;code&gt;analyze/2&lt;/code&gt; function&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This allows &lt;code&gt;ElixirAnalyzer.ExerciseTest&lt;/code&gt; to generate specific inline functions in each module that calls this.&lt;/p&gt;

&lt;p&gt;Once this is compiled, and the application is running, we can tell the application what problem is to be analyzed. We can then use &lt;a href="https://elixir-lang.org/getting-started/typespecs-and-behaviours.html#dynamic-dispatch"&gt;dynamic dispatch&lt;/a&gt; to call the specific module's own &lt;code&gt;analyze/2&lt;/code&gt; function:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;(See the source, &lt;a href="https://github.com/exercism/elixir-analyzer/blob/b2760ee89fc48fd5d9d9591bb67191723d354413/lib/elixir_analyzer.ex#L156-166"&gt;&lt;code&gt;elixir_analyzer.ex&lt;/code&gt; line 156-166&lt;/a&gt;)&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt;&lt;span class="no"&gt;Submission&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;_params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="k"&gt;cond&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# If analysis has been halted for any reason&lt;/span&gt;
    &lt;span class="c1"&gt;# refer the solution to a human mentor&lt;/span&gt;
    &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;halted&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;s&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Submission&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kn"&gt;refer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="c1"&gt;# Dynamic dispatch call to the specific problem test module&lt;/span&gt;
    &lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
      &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;analysis_module&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Submission&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;set_analyzed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;We now have a system of interacting modules which compose themselves into an analyzer. An overview of the code is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nWcCnPse--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/80rz1aa251s97c94tklb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nWcCnPse--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/80rz1aa251s97c94tklb.png" alt="Code Diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;(See the full source on GitHub: &lt;a href="https://github.com/exercism/elixir-analyzer/tree/b2760ee89fc48fd5d9d9591bb67191723d354413"&gt;&lt;code&gt;exercism/elixir-analyzer&lt;/code&gt;&lt;/a&gt;)&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The end result is a method to produce readable analyzer tests. If they are readable, then hopefully they are understandable. If they are understandable, then they are maintainable.&lt;/p&gt;

&lt;p&gt;Our flexible DSL should allow us to also write tests that can target specific mistakes or anti-patterns in a submitted solution.&lt;/p&gt;

&lt;p&gt;Thanks for sticking with me through this walk through Exercism's Elixir Analyzer!&lt;/p&gt;

&lt;p&gt;Hope to see you around &lt;a href="https://www.exercism.io"&gt;Exercism&lt;/a&gt;!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you enjoy programming and looking for an opportunity to learn a new language in community, check out &lt;a href="https://www.exercism.io"&gt;Exercism.io&lt;/a&gt;! If you have programming experience in a language and also looking for opportunities to mentor and help others, come visit!&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>elixir</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
