711 lines
No EOL
52 KiB
HTML
711 lines
No EOL
52 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 4. - 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="Have you been passively learning the material in these articles or have you been actively practicing it? I hope you’ve been actively practicing it. I really do :) Remember what Confucius said? “I hear and I forget.” “I see and I remember.” “I do and I understand.” In the previous …" />
|
||
|
||
<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 4."/>
|
||
<meta property="og:url" content="https://ruslanspivak.com/lsbasi-part4/"/>
|
||
<meta property="og:description" content="Have you been passively learning the material in these articles or have you been actively practicing it? I hope you’ve been actively practicing it. I really do :) Remember what Confucius said? “I hear and I forget.” “I see and I remember.” “I do and I understand.” In the previous …"/>
|
||
<meta property="article:published_time" content="2015-09-11" />
|
||
<meta property="article:section" content="blog" />
|
||
<meta property="article:author" content="Ruslan Spivak" />
|
||
|
||
<meta name="twitter:card" content="summary">
|
||
<meta name="twitter:domain" content="https://ruslanspivak.com">
|
||
|
||
<!-- 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 4.">
|
||
Let’s Build A Simple Interpreter. Part 4.
|
||
</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="2015-09-11T07:00:00-04:00"> Fri, September 11, 2015</time>
|
||
</span>
|
||
|
||
|
||
|
||
|
||
</footer><!-- /.post-info --> </div>
|
||
</div>
|
||
<p>Have you been passively learning the material in these articles or have you been actively practicing it? I hope you’ve been actively practicing it. I really do :)</p>
|
||
<p><br/>
|
||
Remember what Confucius said?</p>
|
||
<blockquote>
|
||
<p><em><span class="dquo">“</span>I hear and I forget.”</em></p>
|
||
</blockquote>
|
||
<p><img alt="Hear" src="LSBAWS_confucius_hear.png" width="640"></p>
|
||
<blockquote>
|
||
<p><em><span class="dquo">“</span>I see and I remember.”</em></p>
|
||
</blockquote>
|
||
<p><img alt="See" src="LSBAWS_confucius_see.png" width="640"></p>
|
||
<blockquote>
|
||
<p><em><span class="dquo">“</span>I do and I understand.”</em></p>
|
||
</blockquote>
|
||
<p><img alt="Do" src="LSBAWS_confucius_do.png" width="640"></p>
|
||
<p><br/>
|
||
In the previous article you learned how to parse (recognize) and interpret arithmetic expressions with any number of plus or minus operators in them, for example “7 - 3 + 2 - 1”. You also learned about syntax diagrams and how they can be used to specify the syntax of a programming language.</p>
|
||
<p>Today you’re going to learn how to parse and interpret arithmetic expressions with any number of multiplication and division operators in them, for example “7 * 4 / 2 * 3”. The division in this article will be an integer division, so if the expression is “9 / 4”, then the answer will be an integer: 2.</p>
|
||
<p>I will also talk quite a bit today about another widely used notation for specifying the syntax of a programming language. It’s called <em><strong>context-free grammars</strong></em> (<em><strong>grammars</strong></em>, for short) or <em><strong><span class="caps">BNF</span></strong></em> (Backus-Naur Form). For the purpose of this article I will not use pure <a href="https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_Form"><span class="caps">BNF</span></a> notation but more like a modified <a href="https://en.wikipedia.org/wiki/Extended_Backus%E2%80%93Naur_Form"><span class="caps">EBNF</span></a> notation.</p>
|
||
<p>Here are a couple of reasons to use grammars:</p>
|
||
<ol>
|
||
<li>A grammar specifies the syntax of a programming language in a concise manner. Unlike syntax diagrams, grammars are very compact. You will see me using grammars more and more in future articles.</li>
|
||
<li>A grammar can serve as great documentation.</li>
|
||
<li>A grammar is a good starting point even if you manually write your parser from scratch. Quite often you can just convert the grammar to code by following a set of simple rules.</li>
|
||
<li>There is a set of tools, called <em>parser generators</em>, which accept a grammar as an input and automatically generate a parser for you based on that grammar. I will talk about those tools later on in the series.</li>
|
||
</ol>
|
||
<p>Now, let’s talk about the mechanical aspects of grammars, shall we?</p>
|
||
<p>Here is a grammar that describes arithmetic expressions like “7 * 4 / 2 * 3” (it’s just one of the many expressions that can be generated by the grammar):</p>
|
||
<p><img alt="" src="lsbasi_part4_bnf1.png" width="640"></p>
|
||
<p>A grammar consists of a sequence of <em>rules</em>, also known as <em>productions</em>. There are two rules in our grammar:</p>
|
||
<p><img alt="" src="lsbasi_part4_bnf2.png" width="640"></p>
|
||
<p>A rule consists of a <em>non-terminal</em>, called the <em><strong>head</strong></em> or <em><strong>left-hand side</strong></em> of the production, a colon, and a sequence of terminals and/or non-terminals, called the <em><strong>body</strong></em> or <em><strong>right-hand side</strong></em> of the production:</p>
|
||
<p><img alt="" src="lsbasi_part4_bnf3.png" width="640"></p>
|
||
<p>In the grammar I showed above, tokens like <span class="caps">MUL</span>, <span class="caps">DIV</span>, and <span class="caps">INTEGER</span> are called <em><strong>terminals</strong></em> and variables like <em>expr</em> and <em>factor</em> are called <em><strong>non-terminals</strong></em>. Non-terminals usually consist of a sequence of terminals and/or non-terminals:</p>
|
||
<p><img alt="" src="lsbasi_part4_bnf4.png" width="640"></p>
|
||
<p>The non-terminal symbol on the left side of the first rule is called the <em><strong>start symbol</strong></em>. In the case of our grammar, the start symbol is <em>expr</em>:</p>
|
||
<p><img alt="" src="lsbasi_part4_bnf5.png" width="640"></p>
|
||
<p>You can read the rule <em>expr</em> as “An <em>expr</em> can be a <em>factor</em> optionally followed by a <em>multiplication</em> or <em>division</em> operator followed by another <em>factor</em>, which in turn is optionally followed by a <em>multiplication</em> or <em>division</em> operator followed by another <em>factor</em> and so on and so forth.”</p>
|
||
<p>What is a <em>factor</em>? For the purpose of this article a <em>factor</em> is just an integer.</p>
|
||
<p>Let’s quickly go over the symbols used in the grammar and their meaning.</p>
|
||
<ul>
|
||
<li><strong>|</strong> - Alternatives. A bar means “or”. So (<span class="caps">MUL</span> | <span class="caps">DIV</span>) means either <span class="caps">MUL</span> or <span class="caps">DIV</span>.</li>
|
||
<li><strong>( … )</strong> - An open and closing parentheses mean grouping of terminals and/or non-terminals as in (<span class="caps">MUL</span> | <span class="caps">DIV</span>).</li>
|
||
<li><strong>( … )</strong>* - Match contents within the group zero or more times.</li>
|
||
</ul>
|
||
<p>If you worked with regular expressions in the past, then the symbols <strong>|</strong>, <strong>()</strong>, and <strong>(…)</strong>* should be pretty familiar to you.</p>
|
||
<p>A grammar defines a <em>language</em> by explaining what sentences it can form. This is how you can <em>derive</em> an arithmetic expression using the grammar: first you begin with the start symbol <em>expr</em> and then repeatedly replace a non-terminal by the body of a rule for that non-terminal until you have generated a sentence consisting solely of terminals. Those sentences form a <em>language</em> defined by the grammar.</p>
|
||
<p>If the grammar cannot derive a certain arithmetic expression, then it doesn’t support that expression and the parser will generate a syntax error when it tries to recognize the expression.</p>
|
||
<p>I think a couple of examples are in order. This is how the grammar derives the expression <em>3</em>:</p>
|
||
<p><img alt="" src="lsbasi_part4_derive1.png" width="600"></p>
|
||
<p>This is how the grammar derives the expression <em>3 * 7</em>:</p>
|
||
<p><img alt="" src="lsbasi_part4_derive2.png" width="600"></p>
|
||
<p>And this is how the grammar derives the expression <em>3 * 7 / 2</em>:</p>
|
||
<p><img alt="" src="lsbasi_part4_derive3.png" width="600"></p>
|
||
<p>Whoa, quite a bit of theory right there!</p>
|
||
<p>I think when I first read about grammars, the related terminology, and all that jazz, I felt something like this:</p>
|
||
<p><img alt="" src="lsbasi_part4_bnf_hmm.png" width="280"></p>
|
||
<p>I can assure you that I definitely was not like this:</p>
|
||
<p><img alt="" src="lsbasi_part4_bnf_yes.png" width="280"></p>
|
||
<p>It took me some time to get comfortable with the notation, how it works, and its relationship with parsers and lexers, but I have to tell you that it pays to learn it in the long run because it’s so widely used in practice and compiler literature that you’re bound to run into it at some point. So, why not sooner rather than later? :)</p>
|
||
<p>Now, let’s map that grammar to code, okay?</p>
|
||
<p>Here are the guidelines that we will use to convert the grammar to source code. By following them, you can literally translate the grammar to a working parser:</p>
|
||
<ol>
|
||
<li>Each rule, <strong>R</strong>, defined in the grammar, becomes a method with the same name, and references to that rule become a method call: <em><strong>R()</strong></em>. The body of the method follows the flow of the body of the rule using the very same guidelines.</li>
|
||
<li>Alternatives <strong>(a1 | a2 | aN)</strong> become an <em><strong>if-elif-else</strong></em> statement</li>
|
||
<li>An optional grouping <strong>(…)*</strong> becomes a <em><strong>while</strong></em> statement that can loop over zero or more times</li>
|
||
<li>Each token reference <strong>T</strong> becomes a call to the method <em><strong>eat</strong></em>: <em><strong>eat(T)</strong></em>. The way the <em>eat</em> method works is that it consumes the token <em>T</em> if it matches the current <em>lookahead</em> token, then it gets a new token from the lexer and assigns that token to the <em>current_token</em> internal variable.</li>
|
||
</ol>
|
||
<p>Visually the guidelines look like this:</p>
|
||
<p><img alt="" src="lsbasi_part4_rules.png" width="780"></p>
|
||
<p>Let’s get moving and convert our grammar to code following the above guidelines.</p>
|
||
<p>There are two rules in our grammar: one <em>expr</em> rule and one <em>factor</em> rule. Let’s start with the <em>factor</em> rule (production). According to the guidelines, you need to create a method called <em>factor</em> (guideline 1) that has a single call to the <em>eat</em> method to consume the <span class="caps">INTEGER</span> token (guideline 4):</p>
|
||
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">factor</span><span class="p">(</span><span class="bp">self</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">INTEGER</span><span class="p">)</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>That was easy, wasn’t it?</p>
|
||
<p>Onward!</p>
|
||
<p>The rule <em>expr</em> becomes the <em>expr</em> method (again according to the guideline 1). The body of the rule starts with a reference to <em>factor</em> that becomes a <em>factor()</em> method call. The optional grouping <em>(…)*</em> becomes a <em>while</em> loop and <em>(<span class="caps">MUL</span> | <span class="caps">DIV</span>)</em> alternatives become an <em>if-elif-else</em> statement. By combining those pieces together we get the following <em>expr</em> method:</p>
|
||
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">expr</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">factor</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="ow">in</span> <span class="p">(</span><span class="n">MUL</span><span class="p">,</span> <span class="n">DIV</span><span class="p">):</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="k">if</span> <span class="n">token</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">MUL</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">MUL</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">factor</span><span class="p">()</span>
|
||
<span class="k">elif</span> <span class="n">token</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">DIV</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">DIV</span><span class="p">)</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">factor</span><span class="p">()</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Please spend some time and study how I mapped the grammar to the source code. Make sure you understand that part because it’ll come in handy later on.</p>
|
||
<p>For your convenience I put the above code into the <em>parser.py</em> file that contains a lexer and a parser without an interpreter. You can download the file directly from <a href="https://github.com/rspivak/lsbasi/blob/master/part4/parser.py">GitHub</a> and play with it. It has an interactive prompt where you can enter expressions and see if they are valid: that is, if the parser built according to the grammar can recognize the expressions.</p>
|
||
<p>Here is a sample session that I ran on my computer:</p>
|
||
<div class="highlight"><pre><span></span>$ python parser.py
|
||
calc> <span class="m">3</span>
|
||
calc> <span class="m">3</span> * <span class="m">7</span>
|
||
calc> <span class="m">3</span> * <span class="m">7</span> / <span class="m">2</span>
|
||
calc> <span class="m">3</span> *
|
||
Traceback <span class="o">(</span>most recent call last<span class="o">)</span>:
|
||
File <span class="s2">"parser.py"</span>, line <span class="m">155</span>, in <module>
|
||
main<span class="o">()</span>
|
||
File <span class="s2">"parser.py"</span>, line <span class="m">151</span>, in main
|
||
parser.parse<span class="o">()</span>
|
||
File <span class="s2">"parser.py"</span>, line <span class="m">136</span>, in parse
|
||
self.expr<span class="o">()</span>
|
||
File <span class="s2">"parser.py"</span>, line <span class="m">130</span>, in expr
|
||
self.factor<span class="o">()</span>
|
||
File <span class="s2">"parser.py"</span>, line <span class="m">114</span>, in factor
|
||
self.eat<span class="o">(</span>INTEGER<span class="o">)</span>
|
||
File <span class="s2">"parser.py"</span>, line <span class="m">107</span>, in eat
|
||
self.error<span class="o">()</span>
|
||
File <span class="s2">"parser.py"</span>, line <span class="m">97</span>, in error
|
||
raise Exception<span class="o">(</span><span class="s1">'Invalid syntax'</span><span class="o">)</span>
|
||
Exception: Invalid syntax
|
||
</pre></div>
|
||
|
||
|
||
<p>Try it out!</p>
|
||
<p>I couldn’t help but mention syntax diagrams again. This is how a syntax diagram for the same <em>expr</em> rule will look:</p>
|
||
<p><img alt="" src="lsbasi_part4_sd.png" width="640"></p>
|
||
<p>It’s about time we dug into the source code of our new arithmetic expression interpreter. Below is the code of a calculator that can handle valid arithmetic expressions containing integers and any number of multiplication and division (integer division) operators. You can also see that I refactored the lexical analyzer into a separate class <em>Lexer</em> and updated the <em>Interpreter</em> class to take the <em>Lexer</em> instance as a parameter:</p>
|
||
<div class="highlight"><pre><span></span><span class="c1"># Token types</span>
|
||
<span class="c1">#</span>
|
||
<span class="c1"># EOF (end-of-file) token is used to indicate that</span>
|
||
<span class="c1"># there is no more input left for lexical analysis</span>
|
||
<span class="n">INTEGER</span><span class="p">,</span> <span class="n">MUL</span><span class="p">,</span> <span class="n">DIV</span><span class="p">,</span> <span class="n">EOF</span> <span class="o">=</span> <span class="s1">'INTEGER'</span><span class="p">,</span> <span class="s1">'MUL'</span><span class="p">,</span> <span class="s1">'DIV'</span><span class="p">,</span> <span class="s1">'EOF'</span>
|
||
|
||
|
||
<span class="k">class</span> <span class="nc">Token</span><span class="p">(</span><span class="nb">object</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="nb">type</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||
<span class="c1"># token type: INTEGER, MUL, DIV, or EOF</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">type</span> <span class="o">=</span> <span class="nb">type</span>
|
||
<span class="c1"># token value: non-negative integer value, '*', '/', or None</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">value</span> <span class="o">=</span> <span class="n">value</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__str__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="sd">"""String representation of the class instance.</span>
|
||
|
||
<span class="sd"> Examples:</span>
|
||
<span class="sd"> Token(INTEGER, 3)</span>
|
||
<span class="sd"> Token(MUL, '*')</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">return</span> <span class="s1">'Token({type}, {value})'</span><span class="o">.</span><span class="n">format</span><span class="p">(</span>
|
||
<span class="nb">type</span><span class="o">=</span><span class="bp">self</span><span class="o">.</span><span class="n">type</span><span class="p">,</span>
|
||
<span class="n">value</span><span class="o">=</span><span class="nb">repr</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">value</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__repr__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="fm">__str__</span><span class="p">()</span>
|
||
|
||
|
||
<span class="k">class</span> <span class="nc">Lexer</span><span class="p">(</span><span class="nb">object</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">text</span><span class="p">):</span>
|
||
<span class="c1"># client string input, e.g. "3 * 5", "12 / 3 * 4", etc</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">text</span>
|
||
<span class="c1"># self.pos is an index into self.text</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">pos</span> <span class="o">=</span> <span class="mi">0</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">current_char</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">text</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">]</span>
|
||
|
||
<span class="k">def</span> <span class="nf">error</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s1">'Invalid character'</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">advance</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="sd">"""Advance the `pos` pointer and set the `current_char` variable."""</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">pos</span> <span class="o">+=</span> <span class="mi">1</span>
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">pos</span> <span class="o">></span> <span class="nb">len</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">text</span><span class="p">)</span> <span class="o">-</span> <span class="mi">1</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">current_char</span> <span class="o">=</span> <span class="bp">None</span> <span class="c1"># Indicates end of input</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">current_char</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">text</span><span class="p">[</span><span class="bp">self</span><span class="o">.</span><span class="n">pos</span><span class="p">]</span>
|
||
|
||
<span class="k">def</span> <span class="nf">skip_whitespace</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">while</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_char</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_char</span><span class="o">.</span><span class="n">isspace</span><span class="p">():</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">advance</span><span class="p">()</span>
|
||
|
||
<span class="k">def</span> <span class="nf">integer</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="sd">"""Return a (multidigit) integer consumed from the input."""</span>
|
||
<span class="n">result</span> <span class="o">=</span> <span class="s1">''</span>
|
||
<span class="k">while</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_char</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="ow">and</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_char</span><span class="o">.</span><span class="n">isdigit</span><span class="p">():</span>
|
||
<span class="n">result</span> <span class="o">+=</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_char</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">advance</span><span class="p">()</span>
|
||
<span class="k">return</span> <span class="nb">int</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">get_next_token</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="sd">"""Lexical analyzer (also known as scanner or tokenizer)</span>
|
||
|
||
<span class="sd"> This method is responsible for breaking a sentence</span>
|
||
<span class="sd"> apart into tokens. One token at a time.</span>
|
||
<span class="sd"> """</span>
|
||
<span class="k">while</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_char</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
|
||
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_char</span><span class="o">.</span><span class="n">isspace</span><span class="p">():</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">skip_whitespace</span><span class="p">()</span>
|
||
<span class="k">continue</span>
|
||
|
||
<span class="k">if</span> <span class="bp">self</span><span class="o">.</span><span class="n">current_char</span><span class="o">.</span><span class="n">isdigit</span><span class="p">():</span>
|
||
<span class="k">return</span> <span class="n">Token</span><span class="p">(</span><span class="n">INTEGER</span><span class="p">,</span> <span class="bp">self</span><span class="o">.</span><span class="n">integer</span><span class="p">())</span>
|
||
|
||
<span class="k">if</span> <span class="bp">self</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="bp">self</span><span class="o">.</span><span class="n">advance</span><span class="p">()</span>
|
||
<span class="k">return</span> <span class="n">Token</span><span class="p">(</span><span class="n">MUL</span><span class="p">,</span> <span class="s1">'*'</span><span class="p">)</span>
|
||
|
||
<span class="k">if</span> <span class="bp">self</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="bp">self</span><span class="o">.</span><span class="n">advance</span><span class="p">()</span>
|
||
<span class="k">return</span> <span class="n">Token</span><span class="p">(</span><span class="n">DIV</span><span class="p">,</span> <span class="s1">'/'</span><span class="p">)</span>
|
||
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">error</span><span class="p">()</span>
|
||
|
||
<span class="k">return</span> <span class="n">Token</span><span class="p">(</span><span class="n">EOF</span><span class="p">,</span> <span class="bp">None</span><span class="p">)</span>
|
||
|
||
|
||
<span class="k">class</span> <span class="nc">Interpreter</span><span class="p">(</span><span class="nb">object</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">lexer</span><span class="p">):</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">lexer</span> <span class="o">=</span> <span class="n">lexer</span>
|
||
<span class="c1"># set current token to the first token taken from the input</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">current_token</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">lexer</span><span class="o">.</span><span class="n">get_next_token</span><span class="p">()</span>
|
||
|
||
<span class="k">def</span> <span class="nf">error</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="k">raise</span> <span class="ne">Exception</span><span class="p">(</span><span class="s1">'Invalid syntax'</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">eat</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">token_type</span><span class="p">):</span>
|
||
<span class="c1"># compare the current token type with the passed token</span>
|
||
<span class="c1"># type and if they match then "eat" the current token</span>
|
||
<span class="c1"># and assign the next token to the self.current_token,</span>
|
||
<span class="c1"># otherwise raise an exception.</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">token_type</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="bp">self</span><span class="o">.</span><span class="n">lexer</span><span class="o">.</span><span class="n">get_next_token</span><span class="p">()</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="bp">self</span><span class="o">.</span><span class="n">error</span><span class="p">()</span>
|
||
|
||
<span class="k">def</span> <span class="nf">factor</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="sd">"""Return an INTEGER token value.</span>
|
||
|
||
<span class="sd"> factor : INTEGER</span>
|
||
<span class="sd"> """</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="bp">self</span><span class="o">.</span><span class="n">eat</span><span class="p">(</span><span class="n">INTEGER</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">token</span><span class="o">.</span><span class="n">value</span>
|
||
|
||
<span class="k">def</span> <span class="nf">expr</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
||
<span class="sd">"""Arithmetic expression parser / interpreter.</span>
|
||
|
||
<span class="sd"> expr : factor ((MUL | DIV) factor)*</span>
|
||
<span class="sd"> factor : INTEGER</span>
|
||
<span class="sd"> """</span>
|
||
<span class="n">result</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">factor</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="ow">in</span> <span class="p">(</span><span class="n">MUL</span><span class="p">,</span> <span class="n">DIV</span><span class="p">):</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="k">if</span> <span class="n">token</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">MUL</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">MUL</span><span class="p">)</span>
|
||
<span class="n">result</span> <span class="o">=</span> <span class="n">result</span> <span class="o">*</span> <span class="bp">self</span><span class="o">.</span><span class="n">factor</span><span class="p">()</span>
|
||
<span class="k">elif</span> <span class="n">token</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">DIV</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">DIV</span><span class="p">)</span>
|
||
<span class="n">result</span> <span class="o">=</span> <span class="n">result</span> <span class="o">/</span> <span class="bp">self</span><span class="o">.</span><span class="n">factor</span><span class="p">()</span>
|
||
|
||
<span class="k">return</span> <span class="n">result</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
|
||
<span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
|
||
<span class="k">try</span><span class="p">:</span>
|
||
<span class="c1"># To run under Python3 replace 'raw_input' call</span>
|
||
<span class="c1"># with 'input'</span>
|
||
<span class="n">text</span> <span class="o">=</span> <span class="nb">raw_input</span><span class="p">(</span><span class="s1">'calc> '</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">EOFError</span><span class="p">:</span>
|
||
<span class="k">break</span>
|
||
<span class="k">if</span> <span class="ow">not</span> <span class="n">text</span><span class="p">:</span>
|
||
<span class="k">continue</span>
|
||
<span class="n">lexer</span> <span class="o">=</span> <span class="n">Lexer</span><span class="p">(</span><span class="n">text</span><span class="p">)</span>
|
||
<span class="n">interpreter</span> <span class="o">=</span> <span class="n">Interpreter</span><span class="p">(</span><span class="n">lexer</span><span class="p">)</span>
|
||
<span class="n">result</span> <span class="o">=</span> <span class="n">interpreter</span><span class="o">.</span><span class="n">expr</span><span class="p">()</span>
|
||
<span class="k">print</span><span class="p">(</span><span class="n">result</span><span class="p">)</span>
|
||
|
||
|
||
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s1">'__main__'</span><span class="p">:</span>
|
||
<span class="n">main</span><span class="p">()</span>
|
||
</pre></div>
|
||
|
||
|
||
<p>Save the above code into the <em>calc4.py</em> file or download it directly from <a href="https://github.com/rspivak/lsbasi/blob/master/part4/calc4.py">GitHub</a>. As usual, try it out and see for yourself that it works.</p>
|
||
<p>This is a sample session that I ran on my laptop:</p>
|
||
<div class="highlight"><pre><span></span>$ python calc4.py
|
||
calc> <span class="m">7</span> * <span class="m">4</span> / <span class="m">2</span>
|
||
<span class="m">14</span>
|
||
calc> <span class="m">7</span> * <span class="m">4</span> / <span class="m">2</span> * <span class="m">3</span>
|
||
<span class="m">42</span>
|
||
calc> <span class="m">10</span> * <span class="m">4</span> * <span class="m">2</span> * <span class="m">3</span> / <span class="m">8</span>
|
||
<span class="m">30</span>
|
||
</pre></div>
|
||
|
||
|
||
<p><br/>
|
||
I know you couldn’t wait for this part :) Here are new exercises for today:</p>
|
||
<p><img alt="" src="lsbasi_part4_exercises.png" width="280"></p>
|
||
<ul>
|
||
<li>Write a grammar that describes arithmetic expressions containing any number of +, -, *, or / operators. With the grammar you should be able to derive expressions like “2 + 7 * 4”, “7 - 8 / 4”, “14 + 2 * 3 - 6 / 2”, and so on.</li>
|
||
<li>Using the grammar, write an interpreter that can evaluate arithmetic expressions containing any number of +, -, *, or / operators. Your interpreter should be able to handle expressions like “2 + 7 * 4”, “7 - 8 / 4”, “14 + 2 * 3 - 6 / 2”, and so on.</li>
|
||
<li>If you’ve finished the above exercises, relax and enjoy :)</li>
|
||
</ul>
|
||
<p><br/>
|
||
<strong>Check your understanding.</strong></p>
|
||
<p>Keeping in mind the grammar from today’s article, answer the following questions, referring to the picture below as needed:</p>
|
||
<p><img alt="" src="lsbasi_part4_bnf1.png" width="640"></p>
|
||
<ol>
|
||
<li>What is a context-free grammar (grammar)?</li>
|
||
<li>How many rules / productions does the grammar have?</li>
|
||
<li>What is a terminal? (Identify all terminals in the picture)</li>
|
||
<li>What is a non-terminal? (Identify all non-terminals in the picture)</li>
|
||
<li>What is a head of a rule? (Identify all heads / left-hand sides in the picture)</li>
|
||
<li>What is a body of the rule? (Identify all bodies / right-hand sides in the picture)</li>
|
||
<li>What is the start symbol of a grammar?</li>
|
||
</ol>
|
||
<p><br/>
|
||
Hey, you read all the way to the end! This post contained quite a bit of theory, so I’m really proud of you that you finished it.</p>
|
||
<p>I’ll be back next time with a new article - stay tuned and don’t forget to do the exercises, they will do you good.</p>
|
||
<p><br/>
|
||
Here is a list of books I recommend that will help you in your study of interpreters and compilers:</p>
|
||
<ol>
|
||
<li>
|
||
<p><a href="http://www.amazon.com/gp/product/193435645X/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=193435645X&linkCode=as2&tag=russblo0b-20&linkId=MP4DCXDV6DJMEJBL">Language Implementation Patterns: Create Your Own Domain-Specific and General Programming Languages (Pragmatic Programmers)</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=193435645X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="http://www.amazon.com/gp/product/0470177071/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0470177071&linkCode=as2&tag=russblo0b-20&linkId=UCLGQTPIYSWYKRRM">Writing Compilers and Interpreters: A Software Engineering Approach</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=0470177071" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="http://www.amazon.com/gp/product/052182060X/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=052182060X&linkCode=as2&tag=russblo0b-20&linkId=ZSKKZMV7YWR22NMW">Modern Compiler Implementation in Java</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=052182060X" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="http://www.amazon.com/gp/product/1461446988/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1461446988&linkCode=as2&tag=russblo0b-20&linkId=PAXWJP5WCPZ7RKRD">Modern Compiler Design</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=1461446988" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>
|
||
</li>
|
||
<li>
|
||
<p><a href="http://www.amazon.com/gp/product/0321486811/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321486811&linkCode=as2&tag=russblo0b-20&linkId=GOEGDQG4HIHU56FQ">Compilers: Principles, Techniques, and Tools (2nd Edition)</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=0321486811" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /></p>
|
||
</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="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="../lsbasi-part16/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-4';
|
||
var disqus_url = 'https://ruslanspivak.com/lsbasi-part4/';
|
||
|
||
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> |