589 lines
No EOL
40 KiB
HTML
589 lines
No EOL
40 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en"
|
||
xmlns:og="http://ogp.me/ns#"
|
||
xmlns:fb="https://www.facebook.com/2008/fbml">
|
||
<head>
|
||
<title>Let’s Build A Simple Interpreter. Part 16: Recognizing Procedure Calls - Ruslan's Blog</title>
|
||
<!-- Using the latest rendering mode for IE -->
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
|
||
|
||
|
||
<link rel="canonical" href="index.html">
|
||
|
||
<meta name="author" content="Ruslan Spivak" />
|
||
<meta name="description" content="Learning is like rowing upstream: not to advance is to drop back. — Chinese proverb" />
|
||
|
||
<meta property="og:site_name" content="Ruslan's Blog" />
|
||
<meta property="og:type" content="article"/>
|
||
<meta property="og:title" content="Let’s Build A Simple Interpreter. Part 16: Recognizing Procedure Calls"/>
|
||
<meta property="og:url" content="https://ruslanspivak.com/lsbasi-part16/"/>
|
||
<meta property="og:description" content="Learning is like rowing upstream: not to advance is to drop back. — Chinese proverb"/>
|
||
<meta property="article:published_time" content="2019-07-23" />
|
||
<meta property="article:section" content="blog" />
|
||
<meta property="article:author" content="Ruslan Spivak" />
|
||
<meta property="og:image"
|
||
content="https://ruslanspivak.com/lsbasi-part16/lsbasi_part16_img01.png"/>
|
||
|
||
<meta name="twitter:card" content="summary">
|
||
<meta name="twitter:domain" content="https://ruslanspivak.com">
|
||
<meta property="twitter:image"
|
||
content="https://ruslanspivak.com/lsbasi-part16/lsbasi_part16_img01.png"/>
|
||
|
||
<!-- Bootstrap -->
|
||
<link rel="stylesheet" href="../theme/css/bootstrap.min.css" type="text/css"/>
|
||
<link href="../theme/css/font-awesome.min.css" rel="stylesheet">
|
||
|
||
<link href="../theme/css/pygments/tango.css" rel="stylesheet">
|
||
<link href="../theme/css/typogrify.css" rel="stylesheet">
|
||
<link rel="stylesheet" href="../theme/css/style.css" type="text/css"/>
|
||
<link href="../static/custom.css" rel="stylesheet">
|
||
|
||
<link href="../feeds/all.atom.xml" type="application/atom+xml" rel="alternate"
|
||
title="Ruslan's Blog ATOM Feed"/>
|
||
|
||
</head>
|
||
<body>
|
||
|
||
<div class="navbar navbar-default navbar-fixed-top" role="navigation">
|
||
<div class="container">
|
||
<div class="navbar-header">
|
||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-ex1-collapse">
|
||
<span class="sr-only">Toggle navigation</span>
|
||
<span class="icon-bar"></span>
|
||
<span class="icon-bar"></span>
|
||
<span class="icon-bar"></span>
|
||
</button>
|
||
<a href="../index.html" class="navbar-brand">
|
||
Ruslan's Blog </a>
|
||
</div>
|
||
<div class="collapse navbar-collapse navbar-ex1-collapse">
|
||
<ul class="nav navbar-nav">
|
||
</ul>
|
||
<ul class="nav navbar-nav navbar-right">
|
||
<li><a href="../pages/about.html"><i class="fa fa-question"></i><span class="icon-label">About</span></a></li>
|
||
<li><a href="../archives.html"><i class="fa fa-th-list"></i><span class="icon-label">Archives</span></a></li>
|
||
</ul>
|
||
</div>
|
||
<!-- /.navbar-collapse -->
|
||
</div>
|
||
</div> <!-- /.navbar -->
|
||
<!-- Banner -->
|
||
<!-- End Banner -->
|
||
<div class="container">
|
||
<div class="row">
|
||
<div class="col-sm-9">
|
||
|
||
<section id="content">
|
||
<article>
|
||
<header class="page-header">
|
||
<h1>
|
||
<a href="index.html"
|
||
rel="bookmark"
|
||
title="Permalink to Let’s Build A Simple Interpreter. Part 16: Recognizing Procedure Calls">
|
||
Let’s Build A Simple Interpreter. Part 16: Recognizing Procedure Calls
|
||
</a>
|
||
</h1>
|
||
</header>
|
||
<div class="entry-content">
|
||
<div class="panel">
|
||
<div class="panel-body">
|
||
<footer class="post-info">
|
||
<span class="label label-default">Date</span>
|
||
<span class="published">
|
||
<i class="fa fa-calendar"></i><time datetime="2019-07-23T08:20:00-04:00"> Tue, July 23, 2019</time>
|
||
</span>
|
||
|
||
|
||
|
||
|
||
</footer><!-- /.post-info --> </div>
|
||
</div>
|
||
<blockquote>
|
||
<p><em><span class="dquo">“</span>Learning is like rowing upstream: not to advance is to drop back.” — Chinese proverb</em></p>
|
||
</blockquote>
|
||
<p>Today we’re going to extend our interpreter to recognize procedure calls. I hope by now you’ve flexed your coding muscles and are ready to tackle this step. This is a necessary step for us before we can learn how to execute procedure calls, which will be a topic that we will cover in great detail in future articles.</p>
|
||
<p>The goal for today is to make sure that when our interpreter reads a program with a procedure call, the parser constructs an Abstract Syntax Tree (<span class="caps">AST</span>) with a new tree node for the procedure call, and the semantic analyzer and the interpreter don’t throw any errors when walking the <span class="caps">AST</span>.</p>
|
||
<p>Let’s take a look at a sample program that contains a procedure call <em>Alpha(3 + 5, 7)</em>:</p>
|
||
<p><img alt="" src="lsbasi_part16_img01.png" width="640"></p>
|
||
<p>Making our interpreter recognize programs like the one above will be our focus for today.</p>
|
||
<p>As with any new feature, we need to update various components of the interpreter to support this feature. Let’s dive into each of those components one by one.</p>
|
||
<p><br/>
|
||
First, we need to update the parser. Here is a list of all the parser changes that we need to make to be able to parse procedure calls and build the right <span class="caps">AST</span>:</p>
|
||
<ol>
|
||
<li>We need to add a new <span class="caps">AST</span> node to represent a procedure call</li>
|
||
<li>We need to add a new grammar rule for procedure call statements; then we need to implement the rule in code</li>
|
||
<li>We need to extend the <em>statement</em> grammar rule to include the rule for procedure call statements and update the <em>statement</em> method to reflect the changes in the grammar</li>
|
||
</ol>
|
||
<p>1. Let’s start by creating a separate class to represent a procedure call <span class="caps">AST</span> node. Let’s call the class <em>ProcedureCall</em>:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ProcedureCall</span><span class="p">(</span><span class="n">AST</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">proc_name</span><span class="p">,</span> <span class="n">actual_params</span><span class="p">,</span> <span class="n">token</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">proc_name</span> <span class="o">=</span> <span class="n">proc_name</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">actual_params</span> <span class="o">=</span> <span class="n">actual_params</span> <span class="c1"># a list of AST nodes</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">token</span> <span class="o">=</span> <span class="n">token</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>The <em>ProcedureCall</em> class constructor takes three parameters: a procedure name, a list of actual parameters (a.k.a arguments), and a token. Nothing really special here, just enough information for us to capture a particular procedure call.</p>
|
||
<p>2. The next step that we need to take is to extend our grammar and add a grammar rule for procedure calls. Let’s call the rule <em>proccall_statement</em>:</p>
|
||
<div class="highlight"><pre><span></span><span class="n">proccall_statement</span> <span class="o">:</span> <span class="n">ID</span> <span class="n">LPAREN</span> <span class="o">(</span><span class="n">expr</span> <span class="o">(</span><span class="n">COMMA</span> <span class="n">expr</span><span class="o">)*)?</span> <span class="n">RPAREN</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Here is a corresponding syntax diagram for the rule:</p>
|
||
<p><img alt="" src="lsbasi_part16_img02.png" width="640"></p>
|
||
<p>From the diagram above you can see that a procedure call is an <span class="caps">ID</span> token followed by a left parenthesis, followed by zero or more expressions separated by commas, followed by a right parenthesis. Here are some of the procedure call examples that fit the rule:</p>
|
||
<div class="highlight"><pre><span></span><span class="n">Alpha</span><span class="p">()</span><span class="o">;</span>
|
||
<span class="n">Alpha</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">;</span>
|
||
<span class="n">Alpha</span><span class="p">(</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">5</span><span class="o">,</span> <span class="mi">7</span><span class="p">)</span><span class="o">;</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Next, let’s implement the rule in our parser by adding a <em>proccall_statement</em> method</p>
|
||
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">proccall_statement</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="sd">"""proccall_statement : ID LPAREN (expr (COMMA expr)*)? RPAREN"""</span>
|
||
<span class="n">token</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_token</span>
|
||
|
||
<span class="n">proc_name</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_token</span><span class="o">.</span><span class="n">value</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">eat</span><span class="p">(</span><span class="n">TokenType</span><span class="o">.</span><span class="n">ID</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">eat</span><span class="p">(</span><span class="n">TokenType</span><span class="o">.</span><span class="n">LPAREN</span><span class="p">)</span>
|
||
<span class="n">actual_params</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_token</span><span class="o">.</span><span class="n">type</span> <span class="o">!=</span> <span class="n">TokenType</span><span class="o">.</span><span class="n">RPAREN</span><span class="p">:</span>
|
||
<span class="n">node</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expr</span><span class="p">()</span>
|
||
<span class="n">actual_params</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">node</span><span class="p">)</span>
|
||
|
||
<span class="k">while</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_token</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">TokenType</span><span class="o">.</span><span class="n">COMMA</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">eat</span><span class="p">(</span><span class="n">TokenType</span><span class="o">.</span><span class="n">COMMA</span><span class="p">)</span>
|
||
<span class="n">node</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">expr</span><span class="p">()</span>
|
||
<span class="n">actual_params</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">node</span><span class="p">)</span>
|
||
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">eat</span><span class="p">(</span><span class="n">TokenType</span><span class="o">.</span><span class="n">RPAREN</span><span class="p">)</span>
|
||
|
||
<span class="n">node</span> <span class="o">=</span> <span class="n">ProcedureCall</span><span class="p">(</span>
|
||
<span class="n">proc_name</span><span class="o">=</span><span class="n">proc_name</span><span class="p">,</span>
|
||
<span class="n">actual_params</span><span class="o">=</span><span class="n">actual_params</span><span class="p">,</span>
|
||
<span class="n">token</span><span class="o">=</span><span class="n">token</span><span class="p">,</span>
|
||
<span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">node</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>The implementation is pretty straightforward and follows the grammar rule: the method parses a procedure call and returns a new <em>ProcedureCall</em> <span class="caps">AST</span> node.</p>
|
||
<p>3. And the last changes to the parser that we need to make are: extend the <em>statement</em> grammar rule by adding the <em>proccall_statement</em> rule and update the <em>statement</em> method to call the <em>proccall_statement</em> method.</p>
|
||
<p>Here is the updated <em>statement</em> grammar rule, which includes the <em>proccall_statement</em> rule:</p>
|
||
<div class="highlight"><pre><span></span><span class="n">statement</span> <span class="o">:</span> <span class="n">compound_statement</span>
|
||
<span class="o">|</span> <span class="n">proccall_statement</span>
|
||
<span class="o">|</span> <span class="n">assignment_statement</span>
|
||
<span class="o">|</span> <span class="n">empty</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Now, we have a tricky situation on hand where we have two grammar rules - <em>proccall_statement</em> and <em>assignment_statement</em> - that start with the same token, the <span class="caps">ID</span> token. Here are their complete grammar rules put together for comparison:</p>
|
||
<div class="highlight"><pre><span></span><span class="n">proccall_statement</span> <span class="o">:</span> <span class="n">ID</span> <span class="n">LPAREN</span> <span class="o">(</span><span class="n">expr</span> <span class="o">(</span><span class="n">COMMA</span> <span class="n">expr</span><span class="o">)*)?</span> <span class="n">RPAREN</span>
|
||
|
||
<span class="n">assignment_statement</span> <span class="o">:</span> <span class="n">variable</span> <span class="n">ASSIGN</span> <span class="n">expr</span>
|
||
<span class="n">variable</span><span class="o">:</span> <span class="n">ID</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>How do you distinguish between a procedure call and an assignment in a case like that? They are both statements and they both start with an <span class="caps">ID</span> token. In the fragment of code below, the <span class="caps">ID</span> token’s value(lexeme) for both statements is <em>foo</em>:</p>
|
||
<div class="highlight"><pre><span></span><span class="n">foo</span><span class="p">()</span><span class="o">;</span> <span class="cm">{ procedure call }</span>
|
||
<span class="n">foo</span> <span class="o">:=</span> <span class="mi">5</span><span class="o">;</span> <span class="cm">{ assignment }</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>The parser should recognize <em>foo();</em> above as a procedure call and <em>foo := 5;</em> as an assignment. But what can we do to help the parser to distinguish between procedure calls and assignments? According to our new <em>proccall_statement</em> grammar rule, procedure calls start with an <span class="caps">ID</span> token followed by a left parenthesis. And that’s what we are going to rely on in the parser to distinguish between procedure calls and assignments to variables - the presence of a left parenthesis after the <span class="caps">ID</span> token:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">if</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">current_token</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">TokenType</span><span class="o">.</span><span class="n">ID</span> <span class="ow">and</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">lexer</span><span class="o">.</span><span class="n">current_char</span> <span class="o">==</span> <span class="s1">'('</span>
|
||
<span class="p">):</span>
|
||
<span class="n">node</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">proccall_statement</span><span class="p">()</span>
|
||
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_token</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">TokenType</span><span class="o">.</span><span class="n">ID</span><span class="p">:</span>
|
||
<span class="n">node</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">assignment_statement</span><span class="p">()</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>As you can see in the code above, first we check if the current token is an <span class="caps">ID</span> token and then we check if it’s followed by a left parenthesis. If it is, we parse a procedure call, otherwise we parse an assignment statement.</p>
|
||
<p>Here is the full updated version of the <em>statement</em> method:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">statement</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="sd">"""</span>
|
||
<span class="sd"> statement : compound_statement</span>
|
||
<span class="sd"> | proccall_statement</span>
|
||
<span class="sd"> | assignment_statement</span>
|
||
<span class="sd"> | empty</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_token</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">TokenType</span><span class="o">.</span><span class="n">BEGIN</span><span class="p">:</span>
|
||
<span class="n">node</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">compound_statement</span><span class="p">()</span>
|
||
<span class="k">elif</span> <span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">current_token</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">TokenType</span><span class="o">.</span><span class="n">ID</span> <span class="ow">and</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">lexer</span><span class="o">.</span><span class="n">current_char</span> <span class="o">==</span> <span class="s1">'('</span>
|
||
<span class="p">):</span>
|
||
<span class="n">node</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">proccall_statement</span><span class="p">()</span>
|
||
<span class="k">elif</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_token</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">TokenType</span><span class="o">.</span><span class="n">ID</span><span class="p">:</span>
|
||
<span class="n">node</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">assignment_statement</span><span class="p">()</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">node</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">empty</span><span class="p">()</span>
|
||
<span class="k">return</span> <span class="n">node</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><br/>
|
||
So far so good. The parser can now parse procedure calls. One thing to keep in mind though is that Pascal procedures don’t have return statements, so we can’t use procedure calls in expressions. For example, the following example will not work if <em>Alpha</em> is a procedure:</p>
|
||
<div class="highlight"><pre><span></span><span class="n">x</span> <span class="o">:=</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">Alpha</span><span class="p">(</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">5</span><span class="o">,</span> <span class="mi">7</span><span class="p">)</span><span class="o">;</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>That’s why we added <em>proccall_statement</em> to the <em>statements</em> method only and nowhere else. Not to worry, later in the series we’ll learn about Pascal functions that can return values and also can be used in expressions and assignments.</p>
|
||
<p>These are all the changes for our parser. Next up is the semantic analyzer changes.</p>
|
||
<p><br/>
|
||
The only change we need to make in our semantic analyzer to support procedure calls is to add a <em>visit_ProcedureCall</em> method:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">visit_ProcedureCall</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span>
|
||
<span class="k">for</span> <span class="n">param_node</span> <span class="ow">in</span> <span class="n">node</span><span class="o">.</span><span class="n">actual_params</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">visit</span><span class="p">(</span><span class="n">param_node</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>All the method does is iterate over a list of actual parameters passed to a procedure call and visit each parameter node in turn. It’s important not to forget to visit each parameter node because each parameter node is an <span class="caps">AST</span> sub-tree in itself.</p>
|
||
<p>That was easy, wasn’t it? Okay, now moving on to interpreter changes.</p>
|
||
<p><br/>
|
||
The interpreter changes, compared to the changes to the semantic analyzer, are even simpler - we only need to add an empty <em>visit_ProcedureCall</em> method to the <em>Interpreter</em> class:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">visit_ProcedureCall</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">node</span><span class="p">):</span>
|
||
<span class="k">pass</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>With all the above changes in place, we now have an interpreter that can recognize procedure calls. And by that I mean the interpreter can parse procedure calls and create an <span class="caps">AST</span> with <em>ProcedureCall</em> nodes corresponding to those procedure calls. Here is the sample Pascal program we saw at the beginning of the article that we want our interpreter to be tested on:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">program</span> <span class="n">Main</span><span class="o">;</span>
|
||
|
||
<span class="k">procedure</span> <span class="nf">Alpha</span><span class="p">(</span><span class="n">a</span> <span class="o">:</span> <span class="kt">integer</span><span class="o">;</span> <span class="n">b</span> <span class="o">:</span> <span class="kt">integer</span><span class="p">)</span><span class="o">;</span>
|
||
<span class="k">var</span> <span class="n">x</span> <span class="o">:</span> <span class="kt">integer</span><span class="o">;</span>
|
||
<span class="k">begin</span>
|
||
<span class="n">x</span> <span class="o">:=</span> <span class="p">(</span><span class="n">a</span> <span class="o">+</span> <span class="n">b</span> <span class="p">)</span> <span class="o">*</span> <span class="mi">2</span><span class="o">;</span>
|
||
<span class="k">end</span><span class="o">;</span>
|
||
|
||
<span class="k">begin</span> <span class="cm">{ Main }</span>
|
||
|
||
<span class="n">Alpha</span><span class="p">(</span><span class="mi">3</span> <span class="o">+</span> <span class="mi">5</span><span class="o">,</span> <span class="mi">7</span><span class="p">)</span><span class="o">;</span> <span class="cm">{ procedure call }</span>
|
||
|
||
<span class="k">end</span><span class="o">.</span> <span class="cm">{ Main }</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Download the above program from <a href="https://github.com/rspivak/lsbasi/tree/master/part16">GitHub</a> or save the code to the file part16.pas</p>
|
||
<p>See for yourself that running our <a href="https://github.com/rspivak/lsbasi/tree/master/part16">updated interpreter</a> with the part16.pas as its input file does not generate any errors:</p>
|
||
<div class="highlight"><pre><span></span>$ python spi.py part16.pas
|
||
$
|
||
</pre></div>
|
||
|
||
|
||
<p>So far so good, but no output is not that exciting. :) Let’s get a bit visual and generate an <span class="caps">AST</span> for the above program and then visualize the <span class="caps">AST</span> using an updated version of the <a href="https://github.com/rspivak/lsbasi/tree/master/part16/genastdot.py">genastdot.py</a> utility:</p>
|
||
<div class="highlight"><pre><span></span>$ python genastdot.py part16.pas > ast.dot <span class="o">&&</span> dot -Tpng -o ast.png ast.dot
|
||
</pre></div>
|
||
|
||
|
||
<p><img alt="" src="lsbasi_part16_img03.png" width="640"></p>
|
||
<p>That’s better. In the picture above you can see our new <em>ProcCall</em> <span class="caps">AST</span> node labeled <em>ProcCall:Alpha</em> for the <em>Alpha(3 + 5, 7)</em> procedure call. The two children of the <em>ProcCall:Alpha</em> node are the subtrees for the arguments <em>3 + 5</em> and <em>7</em> passed to the <em>Alpha(3 + 5, 7)</em> procedure call.</p>
|
||
<p>Okay, we have accomplished our goal for today: when encountering a procedure call, the parser constructs an <span class="caps">AST</span> with a <em>ProcCall</em> node for the procedure call, and the semantic analyzer and the interpreter don’t throw any errors when walking the <span class="caps">AST</span>.</p>
|
||
<p><br/>
|
||
Now, it’s time for an exercise.</p>
|
||
<p><img alt="" src="lsbasi_part16_img04.png" width="180"></p>
|
||
<p>Exercise: Add a check to the semantic analyzer that verifies that the number of arguments (actual parameters) passed to a procedure call equals the number of formal parameters defined in the corresponding procedure declaration. Let’s take the <em>Alpha</em> procedure declaration we used earlier in the article as an example:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">procedure</span> <span class="nf">Alpha</span><span class="p">(</span><span class="n">a</span> <span class="o">:</span> <span class="kt">integer</span><span class="o">;</span> <span class="n">b</span> <span class="o">:</span> <span class="kt">integer</span><span class="p">)</span><span class="o">;</span>
|
||
<span class="k">var</span> <span class="n">x</span> <span class="o">:</span> <span class="kt">integer</span><span class="o">;</span>
|
||
<span class="k">begin</span>
|
||
<span class="n">x</span> <span class="o">:=</span> <span class="p">(</span><span class="n">a</span> <span class="o">+</span> <span class="n">b</span> <span class="p">)</span> <span class="o">*</span> <span class="mi">2</span><span class="o">;</span>
|
||
<span class="k">end</span><span class="o">;</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>The number of formal parameters in the procedure declaration above is two (integers <em>a</em> and <em>b</em>). Your check should throw an error if you try to call the procedure with a number of arguments other than two:</p>
|
||
<div class="highlight"><pre><span></span><span class="n">Alpha</span><span class="p">()</span><span class="o">;</span> <span class="cm">{ 0 arguments —> ERROR }</span>
|
||
<span class="n">Alpha</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="o">;</span> <span class="cm">{ 1 argument —> ERROR }</span>
|
||
<span class="n">Alpha</span><span class="p">(</span><span class="mi">1</span><span class="o">,</span> <span class="mi">2</span><span class="o">,</span> <span class="mi">3</span><span class="p">)</span><span class="o">;</span> <span class="cm">{ 3 arguments —> ERROR }</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>You can find a solution to the exercise in the file <em>solutions.txt</em> on <a href="https://github.com/rspivak/lsbasi/tree/master/part16">GitHub</a>, but try to work out your own solution first before peeking into the file.</p>
|
||
<p><br/>
|
||
That’s all for today. In the next article we’ll begin to learn how to interpret procedure calls. We will cover topics like call stack and activation records. It is going to be a wild ride :) So stay tuned and see you next time!</p>
|
||
<p><br/>
|
||
<em>Resources used in preparation for this article (some links are affiliate links):</em></p>
|
||
<ol>
|
||
<li><a target="_blank" href="https://www.amazon.com/gp/product/193435645X/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=193435645X&linkCode=as2&tag=russblo0b-20&linkId=5d5ca8c07bff5452ea443d8319e7703d">Language Implementation Patterns: Create Your Own Domain-Specific and General Programming Languages (Pragmatic Programmers)</a><img src="https://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=am2&o=1&a=193435645X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></li>
|
||
<li><a target="_blank" href="https://www.amazon.com/gp/product/0470177071/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0470177071&linkCode=as2&tag=russblo0b-20&linkId=542d1267e34a529e0f69027af20e27f3">Writing Compilers and Interpreters: A Software Engineering Approach</a><img src="https://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=am2&o=1&a=0470177071" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></li>
|
||
<li><a target="_blank" href="https://www.freepascal.org/docs-html/current/ref/ref.html">Free Pascal Reference guide</a></li>
|
||
</ol>
|
||
<p><br/>
|
||
<p>If you want to get my newest articles in your inbox, then enter your email address below and click "Get Updates!"</p>
|
||
|
||
<!-- Begin MailChimp Signup Form -->
|
||
<link href="https://cdn-images.mailchimp.com/embedcode/classic-081711.css"
|
||
rel="stylesheet" type="text/css">
|
||
<style type="text/css">
|
||
#mc_embed_signup {
|
||
background: #f5f5f5;
|
||
clear: left;
|
||
font: 18px Helvetica,Arial,sans-serif;
|
||
}
|
||
|
||
#mc_embed_signup form {
|
||
text-align: center;
|
||
padding: 20px 0 10px 3%;
|
||
}
|
||
|
||
#mc_embed_signup .mc-field-group input {
|
||
display: inline;
|
||
width: 40%;
|
||
}
|
||
|
||
#mc_embed_signup div.response {
|
||
width: 100%;
|
||
}
|
||
</style>
|
||
<div id="mc_embed_signup">
|
||
<form
|
||
action="https://ruslanspivak.us4.list-manage.com/subscribe/post?u=7dde30eedc045f4670430c25f&id=6f69f44e03"
|
||
method="post"
|
||
id="mc-embedded-subscribe-form"
|
||
name="mc-embedded-subscribe-form"
|
||
class="validate"
|
||
target="_blank" novalidate>
|
||
<div id="mc_embed_signup_scroll">
|
||
|
||
<div class="mc-field-group">
|
||
<label for="mce-NAME">Enter Your First Name *</label>
|
||
<input type="text" value="" name="NAME" class="required" id="mce-NAME">
|
||
</div>
|
||
<div class="mc-field-group">
|
||
<label for="mce-EMAIL">Enter Your Best Email *</label>
|
||
<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
|
||
</div>
|
||
<div id="mce-responses" class="clear">
|
||
<div class="response" id="mce-error-response" style="display:none"></div>
|
||
<div class="response" id="mce-success-response" style="display:none"></div>
|
||
</div>
|
||
<!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
|
||
<div style="position: absolute; left: -5000px;"><input type="text" name="b_7dde30eedc045f4670430c25f_6f69f44e03" tabindex="-1" value=""></div>
|
||
<div class="clear"><input type="submit" value="Get Updates!" name="subscribe" id="mc-embedded-subscribe" class="button" style="background-color: rgb(63, 146, 236);"></div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
<!-- <script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[1]='NAME';ftypes[1]='text';fnames[0]='EMAIL';ftypes[0]='email';}(jQuery));var $mcj = jQuery.noConflict(true);</script> -->
|
||
<!--End mc_embed_signup-->
|
||
</p>
|
||
<p><br/>
|
||
<strong>All articles in this series:</strong>
|
||
|
||
<ul>
|
||
<li>
|
||
<a href="../lsbasi-part1/index.html">Let's Build A Simple Interpreter. Part 1.</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part2/index.html">Let's Build A Simple Interpreter. Part 2.</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part3/index.html">Let's Build A Simple Interpreter. Part 3.</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part4/index.html">Let's Build A Simple Interpreter. Part 4.</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part5/index.html">Let's Build A Simple Interpreter. Part 5.</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part6/index.html">Let's Build A Simple Interpreter. Part 6.</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part7/index.html">Let's Build A Simple Interpreter. Part 7: Abstract Syntax Trees</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part8/index.html">Let's Build A Simple Interpreter. Part 8.</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part9/index.html">Let's Build A Simple Interpreter. Part 9.</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part10/index.html">Let's Build A Simple Interpreter. Part 10.</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part11/index.html">Let's Build A Simple Interpreter. Part 11.</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part12/index.html">Let's Build A Simple Interpreter. Part 12.</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part13.html">Let's Build A Simple Interpreter. Part 13: Semantic Analysis</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part14/index.html">Let's Build A Simple Interpreter. Part 14: Nested Scopes and a Source-to-Source Compiler</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part15/index.html">Let's Build A Simple Interpreter. Part 15.</a>
|
||
</li>
|
||
<li>
|
||
<a href="index.html">Let's Build A Simple Interpreter. Part 16: Recognizing Procedure Calls</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part17.html">Let's Build A Simple Interpreter. Part 17: Call Stack and Activation Records</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part18/index.html">Let's Build A Simple Interpreter. Part 18: Executing Procedure Calls</a>
|
||
</li>
|
||
<li>
|
||
<a href="../lsbasi-part19/index.html">Let's Build A Simple Interpreter. Part 19: Nested Procedure Calls</a>
|
||
</li>
|
||
</ul>
|
||
</p>
|
||
</div>
|
||
<!-- /.entry-content -->
|
||
<hr/>
|
||
<section class="comments" id="comments">
|
||
<h2>Comments</h2>
|
||
|
||
<div id="disqus_thread"></div>
|
||
<script type="text/javascript">
|
||
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
|
||
var disqus_shortname = 'ruslanspivak'; // required: replace example with your forum shortname
|
||
|
||
var disqus_identifier = 'lets-build-a-simple-interpreter-part-16-recognizing-procedure-calls';
|
||
var disqus_url = 'https://ruslanspivak.com/lsbasi-part16/';
|
||
|
||
var disqus_config = function () {
|
||
this.language = "en";
|
||
};
|
||
|
||
/* * * DON'T EDIT BELOW THIS LINE * * */
|
||
(function () {
|
||
var dsq = document.createElement('script');
|
||
dsq.type = 'text/javascript';
|
||
dsq.async = true;
|
||
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
|
||
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
|
||
})();
|
||
</script>
|
||
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by
|
||
Disqus.</a></noscript>
|
||
<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
|
||
|
||
</section>
|
||
</article>
|
||
</section>
|
||
|
||
</div>
|
||
<div class="col-sm-3" id="sidebar">
|
||
<aside>
|
||
|
||
<section class="well well-sm">
|
||
<ul class="list-group list-group-flush">
|
||
<li class="list-group-item"><h4><i class="fa fa-home fa-lg"></i><span class="icon-label">Social</span></h4>
|
||
<ul class="list-group" id="social">
|
||
<li class="list-group-item"><a href="https://github.com/rspivak/"><i class="fa fa-github-square fa-lg"></i> github</a></li>
|
||
<li class="list-group-item"><a href="https://twitter.com/rspivak"><i class="fa fa-twitter-square fa-lg"></i> twitter</a></li>
|
||
<li class="list-group-item"><a href="https://linkedin.com/in/ruslanspivak/"><i class="fa fa-linkedin-square fa-lg"></i> linkedin</a></li>
|
||
</ul>
|
||
</li>
|
||
|
||
<li class="list-group-item"><h4><i class="fa fa-home fa-lg"></i><span class="icon-label">Popular posts</span></h4>
|
||
<ul class="list-group" id="popularposts">
|
||
<li class="list-group-item"
|
||
style="font-size: 15px; word-break: normal;">
|
||
<a href="../lsbaws-part1/index.html">
|
||
Let's Build A Web Server. Part 1.
|
||
</a>
|
||
</li>
|
||
<li class="list-group-item"
|
||
style="font-size: 15px; word-break: normal;">
|
||
<a href="../lsbasi-part1/index.html">
|
||
Let's Build A Simple Interpreter. Part 1.
|
||
</a>
|
||
</li>
|
||
<li class="list-group-item"
|
||
style="font-size: 15px; word-break: normal;">
|
||
<a href="../lsbaws-part2/index.html">
|
||
Let's Build A Web Server. Part 2.
|
||
</a>
|
||
</li>
|
||
<li class="list-group-item"
|
||
style="font-size: 15px; word-break: normal;">
|
||
<a href="../lsbaws-part3/index.html">
|
||
Let's Build A Web Server. Part 3.
|
||
</a>
|
||
</li>
|
||
<li class="list-group-item"
|
||
style="font-size: 15px; word-break: normal;">
|
||
<a href="../lsbasi-part2/index.html">
|
||
Let's Build A Simple Interpreter. Part 2.
|
||
</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
|
||
<li class="list-group-item">
|
||
<h4>
|
||
<span>Disclaimer</span>
|
||
</h4>
|
||
<p id="disclaimer-text"> Some of the links on this site
|
||
have my Amazon referral id, which provides me with a small
|
||
commission for each sale. Thank you for your support.
|
||
</p>
|
||
</li>
|
||
|
||
|
||
|
||
</ul>
|
||
</section>
|
||
</aside>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<footer>
|
||
<div class="container">
|
||
<hr>
|
||
<div class="row">
|
||
<div class="col-xs-10">© 2020 Ruslan Spivak
|
||
<!-- · Powered by <a href="https://github.com/DandyDev/pelican-bootstrap3" target="_blank">pelican-bootstrap3</a>, -->
|
||
<!-- <a href="http://docs.getpelican.com/" target="_blank">Pelican</a>, -->
|
||
<!-- <a href="http://getbootstrap.com" target="_blank">Bootstrap</a> -->
|
||
<!-- -->
|
||
</div>
|
||
<div class="col-xs-2"><p class="pull-right"><i class="fa fa-arrow-up"></i> <a href="index.html#">Back to top</a></p></div>
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
<script src="../theme/js/jquery.min.js"></script>
|
||
|
||
<!-- Include all compiled plugins (below), or include individual files as needed -->
|
||
<script src="../theme/js/bootstrap.min.js"></script>
|
||
|
||
<!-- Enable responsive features in IE8 with Respond.js (https://github.com/scottjehl/Respond) -->
|
||
<script src="../theme/js/respond.min.js"></script>
|
||
|
||
<!-- Disqus -->
|
||
<script type="text/javascript">
|
||
/* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
|
||
var disqus_shortname = 'ruslanspivak'; // required: replace example with your forum shortname
|
||
|
||
/* * * DON'T EDIT BELOW THIS LINE * * */
|
||
(function () {
|
||
var s = document.createElement('script');
|
||
s.async = true;
|
||
s.type = 'text/javascript';
|
||
s.src = '//' + disqus_shortname + '.disqus.com/count.js';
|
||
(document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);
|
||
}());
|
||
</script>
|
||
<!-- End Disqus Code -->
|
||
<!-- Google Analytics Universal -->
|
||
<script type="text/javascript">
|
||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||
|
||
ga('create', 'UA-2572871-3', 'auto');
|
||
ga('send', 'pageview');
|
||
</script>
|
||
<!-- End Google Analytics Universal Code -->
|
||
|
||
</body>
|
||
</html> |