640 lines
47 KiB
HTML
640 lines
47 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 6. - 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="Today is the day :) “Why?” you might ask. The reason is that today we’re wrapping up our discussion of arithmetic expressions (well, almost) by adding parenthesized expressions to our grammar and implementing an interpreter that will be able to evaluate parenthesized expressions with arbitrarily deep nesting, like the expression …" />
|
|||
|
|
|||
|
<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 6."/>
|
|||
|
<meta property="og:url" content="https://ruslanspivak.com/lsbasi-part6/"/>
|
|||
|
<meta property="og:description" content="Today is the day :) “Why?” you might ask. The reason is that today we’re wrapping up our discussion of arithmetic expressions (well, almost) by adding parenthesized expressions to our grammar and implementing an interpreter that will be able to evaluate parenthesized expressions with arbitrarily deep nesting, like the expression …"/>
|
|||
|
<meta property="article:published_time" content="2015-11-02" />
|
|||
|
<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 6.">
|
|||
|
Let’s Build A Simple Interpreter. Part 6.
|
|||
|
</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-11-02T07:00:00-05:00"> Mon, November 02, 2015</time>
|
|||
|
</span>
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
</footer><!-- /.post-info --> </div>
|
|||
|
</div>
|
|||
|
<p>Today is <em>the</em> day :) “Why?” you might ask. The reason is that today we’re wrapping up our discussion of arithmetic expressions (well, almost) by adding parenthesized expressions to our grammar and implementing an interpreter that will be able to evaluate parenthesized expressions with arbitrarily deep nesting, like the expression 7 + 3 * (10 / (12 / (3 + 1) - 1)).</p>
|
|||
|
<p>Let’s get started, shall we?</p>
|
|||
|
<p>First, let’s modify the grammar to support expressions inside parentheses. As you remember from <a href="../lsbasi-part5/index.html" title="Part 5">Part 5</a>, the <em>factor</em> rule is used for basic units in expressions. In that article, the only basic unit we had was an integer. Today we’re adding another basic unit - a parenthesized expression. Let’s do it.</p>
|
|||
|
<p>Here is our updated grammar:</p>
|
|||
|
<p><img alt="" src="lsbasi_part6_grammar.png" width="640"></p>
|
|||
|
<p>The <em>expr</em> and the <em>term</em> productions are exactly the same as in <a href="../lsbasi-part5/index.html" title="Part 5">Part 5</a> and the only change is in the <em>factor</em> production where the terminal <span class="caps">LPAREN</span> represents a left parenthesis ‘(‘, the terminal <span class="caps">RPAREN</span> represents a right parenthesis ‘)’, and the non-terminal <em>expr</em> between the parentheses refers to the <em>expr</em> rule.</p>
|
|||
|
<p>Here is the updated syntax diagram for the <em>factor</em>, which now includes alternatives:</p>
|
|||
|
<p><img alt="" src="lsbasi_part6_factor_diagram.png" width="640"></p>
|
|||
|
<p>Because the grammar rules for the <em>expr</em> and the <em>term</em> haven’t changed, their syntax diagrams look the same as in <a href="../lsbasi-part5/index.html" title="Part 5">Part 5</a>:</p>
|
|||
|
<p><img alt="" src="lsbasi_part6_expr_term_diagram.png" width="640"></p>
|
|||
|
<p>Here is an interesting feature of our new grammar - it is recursive. If you try to derive the expression 2 * (7 + 3), you will start with the <em>expr</em> start symbol and eventually you will get to a point where you will recursively use the <em>expr</em> rule again to derive the (7 + 3) portion of the original arithmetic expression.</p>
|
|||
|
<p>Let’s decompose the expression 2 * (7 + 3) according to the grammar and see how it looks:</p>
|
|||
|
<p><img alt="" src="lsbasi_part6_decomposition.png" width="640"></p>
|
|||
|
<p>A little aside: if you need a refresher on recursion, take a look at Daniel P. Friedman and Matthias Felleisen’s <a rel="nofollow" href="http://www.amazon.com/gp/product/0262560992/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0262560992&linkCode=as2&tag=russblo0b-20&linkId=IM7CT7RLWNGJ7J54">The Little Schemer</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=russblo0b-20&l=as2&o=1&a=0262560992" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> book - it’s really good.</p>
|
|||
|
<p>Okay, let’s get moving and translate our new updated grammar to code.</p>
|
|||
|
<p>The following are the main changes to the code from the previous article:</p>
|
|||
|
<ol>
|
|||
|
<li>The <em>Lexer</em> has been modified to return two more tokens: <span class="caps">LPAREN</span> for a left parenthesis and <span class="caps">RPAREN</span> for a right parenthesis.</li>
|
|||
|
<li>The <em>Interpreter</em>‘s <em>factor</em> method has been slightly updated to parse parenthesized expressions in addition to integers.</li>
|
|||
|
</ol>
|
|||
|
<p>Here is the complete code of a calculator that can evaluate arithmetic expressions containing integers; any number of addition, subtraction, multiplication and division operators; and parenthesized expressions with arbitrarily deep nesting:</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">PLUS</span><span class="p">,</span> <span class="n">MINUS</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">LPAREN</span><span class="p">,</span> <span class="n">RPAREN</span><span class="p">,</span> <span class="n">EOF</span> <span class="o">=</span> <span class="p">(</span>
|
|||
|
<span class="s1">'INTEGER'</span><span class="p">,</span> <span class="s1">'PLUS'</span><span class="p">,</span> <span class="s1">'MINUS'</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">'('</span><span class="p">,</span> <span class="s1">')'</span><span class="p">,</span> <span class="s1">'EOF'</span>
|
|||
|
<span class="p">)</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="bp">self</span><span class="o">.</span><span class="n">type</span> <span class="o">=</span> <span class="nb">type</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(PLUS, '+')</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. "4 + 2 * 3 - 6 / 2"</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">PLUS</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">MINUS</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">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="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">LPAREN</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">RPAREN</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">"""factor : INTEGER | LPAREN 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="k">if</span> <span class="n">token</span><span class="o">.</span><span class="n">type</span> <span class="o">==</span> <span class="n">INTEGER</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>
|
|||
|
<span class="k">return</span> <span class="n">token</span><span class="o">.</span><span class="n">value</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">LPAREN</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">LPAREN</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">expr</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">RPAREN</span><span class="p">)</span>
|
|||
|
<span class="k">return</span> <span class="n">result</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">term</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|||
|
<span class="sd">"""term : factor ((MUL | DIV) factor)*"""</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">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"> calc> 7 + 3 * (10 / (12 / (3 + 1) - 1))</span>
|
|||
|
<span class="sd"> 22</span>
|
|||
|
|
|||
|
<span class="sd"> expr : term ((PLUS | MINUS) term)*</span>
|
|||
|
<span class="sd"> term : factor ((MUL | DIV) factor)*</span>
|
|||
|
<span class="sd"> factor : INTEGER | LPAREN expr RPAREN</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">term</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">PLUS</span><span class="p">,</span> <span class="n">MINUS</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">PLUS</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">PLUS</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">term</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">MINUS</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">MINUS</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">term</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 <a href="https://github.com/rspivak/lsbasi/blob/master/part6/calc6.py">calc6.py</a> file, try it out and see for yourself that your new interpreter properly evaluates arithmetic expressions that have different operators and parentheses.</p>
|
|||
|
<p>Here is a sample session:</p>
|
|||
|
<div class="highlight"><pre><span></span>$ python calc6.py
|
|||
|
calc> <span class="m">3</span>
|
|||
|
<span class="m">3</span>
|
|||
|
calc> <span class="m">2</span> + <span class="m">7</span> * <span class="m">4</span>
|
|||
|
<span class="m">30</span>
|
|||
|
calc> <span class="m">7</span> - <span class="m">8</span> / <span class="m">4</span>
|
|||
|
<span class="m">5</span>
|
|||
|
calc> <span class="m">14</span> + <span class="m">2</span> * <span class="m">3</span> - <span class="m">6</span> / <span class="m">2</span>
|
|||
|
<span class="m">17</span>
|
|||
|
calc> <span class="m">7</span> + <span class="m">3</span> * <span class="o">(</span><span class="m">10</span> / <span class="o">(</span><span class="m">12</span> / <span class="o">(</span><span class="m">3</span> + <span class="m">1</span><span class="o">)</span> - <span class="m">1</span><span class="o">))</span>
|
|||
|
<span class="m">22</span>
|
|||
|
calc> <span class="m">7</span> + <span class="m">3</span> * <span class="o">(</span><span class="m">10</span> / <span class="o">(</span><span class="m">12</span> / <span class="o">(</span><span class="m">3</span> + <span class="m">1</span><span class="o">)</span> - <span class="m">1</span><span class="o">))</span> / <span class="o">(</span><span class="m">2</span> + <span class="m">3</span><span class="o">)</span> - <span class="m">5</span> - <span class="m">3</span> + <span class="o">(</span><span class="m">8</span><span class="o">)</span>
|
|||
|
<span class="m">10</span>
|
|||
|
calc> <span class="m">7</span> + <span class="o">(((</span><span class="m">3</span> + <span class="m">2</span><span class="o">)))</span>
|
|||
|
<span class="m">12</span>
|
|||
|
</pre></div>
|
|||
|
|
|||
|
|
|||
|
<p><br/>
|
|||
|
And here is a new exercise for you for today:</p>
|
|||
|
<p><img alt="" src="lsbasi_part6_exercises.png" width="280"></p>
|
|||
|
<ul>
|
|||
|
<li>Write your own version of the interpreter of arithmetic expressions as described in this article. Remember: repetition is the mother of all learning.</li>
|
|||
|
</ul>
|
|||
|
<p><br/>
|
|||
|
Hey, you read all the way to the end! Congratulations, you’ve just learned how to create (and if you’ve done the exercise - you’ve actually written) a basic <em>recursive-descent parser / interpreter</em> that can evaluate pretty complex arithmetic expressions.</p>
|
|||
|
<p>In the next article I will talk in a lot more detail about <em>recursive-descent parsers</em>. I will also introduce an important and widely used data structure in interpreter and compiler construction that we’ll use throughout the series.</p>
|
|||
|
<p>Stay tuned and see you soon. Until then, keep working on your interpreter and most importantly: have fun and enjoy the process!</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="../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="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-6';
|
|||
|
var disqus_url = 'https://ruslanspivak.com/lsbasi-part6/';
|
|||
|
|
|||
|
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>
|