diff options
Diffstat (limited to 'feed.xml')
| -rw-r--r-- | feed.xml | 76 |
1 files changed, 41 insertions, 35 deletions
@@ -3,14 +3,19 @@ <channel> <title>Marc's Programming Notes</title> <link>https://marc.vertes.org/</link> +<atom:link href="https://marc.vertes.org/feed.xml" rel="self" type="application/rss+xml" /> <description>A blog about programming</description> -<pubDate>Thu, 18 May 2023 19:05:48 +0200</pubDate> +<managingEditor>mvertes@free.fr (Marc Vertes)</managingEditor> +<pubDate>Fri, 19 May 2023 18:59:21 +0200</pubDate> <item> +<guid isPermaLink="true">https://marc.vertes.org/yaegi-internals/</guid> <title>Yaegi Internals</title> <link>https://marc.vertes.org/yaegi-internals/</link> <description>Design and implementation of a Go interpreter</description> +<author>mvertes@free.fr (Marc Vertes)</author> <pubDate>Wed, 03 May 2023 12:00:00 +0200</pubDate> -<content:encoded><![CDATA[<h1 id="yaegi-internals">Yaegi internals</h1> +<content:encoded> +<![CDATA[<h1 id="yaegi-internals">Yaegi internals</h1> <p><a href="https://github.com/traefik/yaegi">Yaegi</a> is an interpreter of the Go language written in Go. This project was started in Traefik-Labs initially to provide a simple and practical embedded @@ -36,7 +41,7 @@ overview.</p> <h2 id="overview-of-architecture">Overview of architecture</h2> <p>Let’s see what happens inside yaegi when one executes the following line:</p> -<div class="sourceCode" id="cb1"><pre class="sourceCode go"><code class="sourceCode go"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>interp<span class="op">.</span>Eval<span class="op">(</span><span class="st">`print("hello", 2+3)`</span><span class="op">)</span></span></code></pre></div> +<div class="sourceCode" id="cb1"><pre class="sourceCode go"><code class="sourceCode go"><span id="cb1-1"><a href="https://marc.vertes.org/yaegi-internals/#cb1-1" aria-hidden="true" tabindex="-1"></a>interp<span class="op">.</span>Eval<span class="op">(</span><span class="st">`print("hello", 2+3)`</span><span class="op">)</span></span></code></pre></div> <p>The following figure 1 displays the main steps of evaluation:</p> <figure> <img src="https://marc.vertes.org/yaegi-internals/yaegi_internals_fig1.drawio.svg" @@ -94,37 +99,37 @@ our AST representation.</p> interpreter or other language tool, and the function to use it (extracted from <a href="https://github.com/traefik/yaegi/blob/8de3add6faf471a807182c7b8198fe863debc9d8/interp/interp.go#L284-L296">here</a>).</p> -<div class="sourceCode" id="cb2"><pre class="sourceCode go"><code class="sourceCode go"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">// node defines a node of a (abstract syntax) tree.</span></span> -<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> node <span class="kw">struct</span> <span class="op">{</span></span> -<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="co">// Node children</span></span> -<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a> child <span class="op">[]*</span>node</span> -<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="co">// Node metadata</span></span> -<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span></span> -<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span> -<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a></span> -<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="co">// walk traverses AST n in depth first order, invoking in function</span></span> -<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="co">// at node entry and out function at node exit.</span></span> -<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> <span class="op">(</span>n <span class="op">*</span>node<span class="op">)</span> walk<span class="op">(</span>in <span class="kw">func</span><span class="op">(</span>n <span class="op">*</span>node<span class="op">)</span> <span class="dt">bool</span><span class="op">,</span> out <span class="kw">func</span><span class="op">(</span>n <span class="op">*</span>node<span class="op">))</span> <span class="op">{</span></span> -<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> in <span class="op">!=</span> <span class="ot">nil</span> <span class="op">&&</span> <span class="op">!</span>in<span class="op">(</span>n<span class="op">)</span> <span class="op">{</span></span> -<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span></span> -<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span> -<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> _<span class="op">,</span> child <span class="op">:=</span> <span class="kw">range</span> n<span class="op">.</span>child <span class="op">{</span></span> -<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a> child<span class="op">.</span>Walk<span class="op">(</span>in<span class="op">,</span> out<span class="op">)</span></span> -<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span> -<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> out <span class="op">!=</span> <span class="ot">nil</span> <span class="op">{</span></span> -<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a> out<span class="op">(</span>n<span class="op">)</span></span> -<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span> -<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div> +<div class="sourceCode" id="cb2"><pre class="sourceCode go"><code class="sourceCode go"><span id="cb2-1"><a href="https://marc.vertes.org/yaegi-internals/#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">// node defines a node of a (abstract syntax) tree.</span></span> +<span id="cb2-2"><a href="https://marc.vertes.org/yaegi-internals/#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> node <span class="kw">struct</span> <span class="op">{</span></span> +<span id="cb2-3"><a href="https://marc.vertes.org/yaegi-internals/#cb2-3" aria-hidden="true" tabindex="-1"></a> <span class="co">// Node children</span></span> +<span id="cb2-4"><a href="https://marc.vertes.org/yaegi-internals/#cb2-4" aria-hidden="true" tabindex="-1"></a> child <span class="op">[]*</span>node</span> +<span id="cb2-5"><a href="https://marc.vertes.org/yaegi-internals/#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="co">// Node metadata</span></span> +<span id="cb2-6"><a href="https://marc.vertes.org/yaegi-internals/#cb2-6" aria-hidden="true" tabindex="-1"></a> <span class="op">...</span></span> +<span id="cb2-7"><a href="https://marc.vertes.org/yaegi-internals/#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span> +<span id="cb2-8"><a href="https://marc.vertes.org/yaegi-internals/#cb2-8" aria-hidden="true" tabindex="-1"></a></span> +<span id="cb2-9"><a href="https://marc.vertes.org/yaegi-internals/#cb2-9" aria-hidden="true" tabindex="-1"></a><span class="co">// walk traverses AST n in depth first order, invoking in function</span></span> +<span id="cb2-10"><a href="https://marc.vertes.org/yaegi-internals/#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="co">// at node entry and out function at node exit.</span></span> +<span id="cb2-11"><a href="https://marc.vertes.org/yaegi-internals/#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="kw">func</span> <span class="op">(</span>n <span class="op">*</span>node<span class="op">)</span> walk<span class="op">(</span>in <span class="kw">func</span><span class="op">(</span>n <span class="op">*</span>node<span class="op">)</span> <span class="dt">bool</span><span class="op">,</span> out <span class="kw">func</span><span class="op">(</span>n <span class="op">*</span>node<span class="op">))</span> <span class="op">{</span></span> +<span id="cb2-12"><a href="https://marc.vertes.org/yaegi-internals/#cb2-12" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> in <span class="op">!=</span> <span class="ot">nil</span> <span class="op">&&</span> <span class="op">!</span>in<span class="op">(</span>n<span class="op">)</span> <span class="op">{</span></span> +<span id="cb2-13"><a href="https://marc.vertes.org/yaegi-internals/#cb2-13" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span></span> +<span id="cb2-14"><a href="https://marc.vertes.org/yaegi-internals/#cb2-14" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span> +<span id="cb2-15"><a href="https://marc.vertes.org/yaegi-internals/#cb2-15" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> _<span class="op">,</span> child <span class="op">:=</span> <span class="kw">range</span> n<span class="op">.</span>child <span class="op">{</span></span> +<span id="cb2-16"><a href="https://marc.vertes.org/yaegi-internals/#cb2-16" aria-hidden="true" tabindex="-1"></a> child<span class="op">.</span>Walk<span class="op">(</span>in<span class="op">,</span> out<span class="op">)</span></span> +<span id="cb2-17"><a href="https://marc.vertes.org/yaegi-internals/#cb2-17" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span> +<span id="cb2-18"><a href="https://marc.vertes.org/yaegi-internals/#cb2-18" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> out <span class="op">!=</span> <span class="ot">nil</span> <span class="op">{</span></span> +<span id="cb2-19"><a href="https://marc.vertes.org/yaegi-internals/#cb2-19" aria-hidden="true" tabindex="-1"></a> out<span class="op">(</span>n<span class="op">)</span></span> +<span id="cb2-20"><a href="https://marc.vertes.org/yaegi-internals/#cb2-20" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span> +<span id="cb2-21"><a href="https://marc.vertes.org/yaegi-internals/#cb2-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div> <p>The above code is deceptively simple. As in many complex systems, an important part of the signification is carried by the relationships between the elements and the patterns they form. It’s easier to understand it by displaying the corresponding graph and consider the system as a whole. We can do that using a simple example:</p> -<div class="sourceCode" id="cb3"><pre class="sourceCode go"><code class="sourceCode go"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a>a <span class="op">:=</span> <span class="dv">3</span></span> -<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> a <span class="op">></span> <span class="dv">2</span> <span class="op">{</span></span> -<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span><span class="op">(</span><span class="st">"ok"</span><span class="op">)</span></span> -<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span> -<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span><span class="op">(</span><span class="st">"bye"</span><span class="op">)</span></span></code></pre></div> +<div class="sourceCode" id="cb3"><pre class="sourceCode go"><code class="sourceCode go"><span id="cb3-1"><a href="https://marc.vertes.org/yaegi-internals/#cb3-1" aria-hidden="true" tabindex="-1"></a>a <span class="op">:=</span> <span class="dv">3</span></span> +<span id="cb3-2"><a href="https://marc.vertes.org/yaegi-internals/#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> a <span class="op">></span> <span class="dv">2</span> <span class="op">{</span></span> +<span id="cb3-3"><a href="https://marc.vertes.org/yaegi-internals/#cb3-3" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span><span class="op">(</span><span class="st">"ok"</span><span class="op">)</span></span> +<span id="cb3-4"><a href="https://marc.vertes.org/yaegi-internals/#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span> +<span id="cb3-5"><a href="https://marc.vertes.org/yaegi-internals/#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span><span class="op">(</span><span class="st">"bye"</span><span class="op">)</span></span></code></pre></div> <p>The corresponding AST is:</p> <figure> <img src="https://marc.vertes.org/yaegi-internals/ex1_raw_ast.drawio.svg" alt="figure 2: a raw AST" /> @@ -202,11 +207,11 @@ step, a full interpretation can still be necessary on an expression sub-tree if this one serves to implement a global type definition. For example if an array size is computed by an expression as in the following valid Go declarations:</p> -<div class="sourceCode" id="cb4"><pre class="sourceCode go"><code class="sourceCode go"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> <span class="op">(</span></span> -<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> prefix <span class="op">=</span> <span class="st">"/usr"</span></span> -<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> path <span class="op">=</span> prefix <span class="op">+</span> <span class="st">"/local/bin"</span></span> -<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span> -<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> a <span class="op">[</span><span class="bu">len</span><span class="op">(</span>prefix<span class="op">+</span>path<span class="op">)</span> <span class="op">+</span> <span class="dv">2</span><span class="op">]</span><span class="dt">int</span></span></code></pre></div> +<div class="sourceCode" id="cb4"><pre class="sourceCode go"><code class="sourceCode go"><span id="cb4-1"><a href="https://marc.vertes.org/yaegi-internals/#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">const</span> <span class="op">(</span></span> +<span id="cb4-2"><a href="https://marc.vertes.org/yaegi-internals/#cb4-2" aria-hidden="true" tabindex="-1"></a> prefix <span class="op">=</span> <span class="st">"/usr"</span></span> +<span id="cb4-3"><a href="https://marc.vertes.org/yaegi-internals/#cb4-3" aria-hidden="true" tabindex="-1"></a> path <span class="op">=</span> prefix <span class="op">+</span> <span class="st">"/local/bin"</span></span> +<span id="cb4-4"><a href="https://marc.vertes.org/yaegi-internals/#cb4-4" aria-hidden="true" tabindex="-1"></a><span class="op">)</span></span> +<span id="cb4-5"><a href="https://marc.vertes.org/yaegi-internals/#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span> a <span class="op">[</span><span class="bu">len</span><span class="op">(</span>prefix<span class="op">+</span>path<span class="op">)</span> <span class="op">+</span> <span class="dv">2</span><span class="op">]</span><span class="dt">int</span></span></code></pre></div> <p>A paradox is that the compiler needs an interpreter to perform the type analysis! Indeed, in the example above, <code>[16]int</code> (because <code>len(prefix+path) + 2 = 16</code>) is a specific type in @@ -384,7 +389,8 @@ in a next article:</p> <p>P.S. Thanks to <a href="https://twitter.com/@lejatorn"><span class="citation" data-cites="lejatorn">@lejatorn</span></a> for his feedback and suggestions on this post.</p> -]]></content:encoded> +<hr>From: Marc Vertes, 03 may 2023]]> +</content:encoded> </item> </channel> </rss> |
