avatarMangirdas Kazlauskas

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

6507

Abstract

.org/wiki/Parsing_expression_grammar">PEG</a> terms, numbers are <b>terminal </b>symbols and operators are <b>nonterminal </b>symbols.</p><p id="8635">The evaluation of the postfix expression could be implemented using the stack data structure by processing the expression from left to right:</p><div id="f1e7"><pre><span class="hljs-keyword">for</span> <span class="hljs-keyword">each</span> <span class="hljs-keyword">token</span> <span class="hljs-keyword">in</span> <span class="hljs-keyword">the</span> postfix expression: <span class="hljs-keyword">if</span> <span class="hljs-keyword">token</span> is <span class="hljs-keyword">an</span> operator: operand_2 ← pop <span class="hljs-built_in">from</span> <span class="hljs-keyword">the</span> stack operand_1 ← pop <span class="hljs-built_in">from</span> <span class="hljs-keyword">the</span> stack <span class="hljs-built_in">result</span> ← evaluate <span class="hljs-keyword">token</span> <span class="hljs-keyword">with</span> operand_1 <span class="hljs-keyword">and</span> operand_2 push <span class="hljs-built_in">result</span> back onto <span class="hljs-keyword">the</span> stack <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">token</span> is <span class="hljs-keyword">an</span> operand: push <span class="hljs-keyword">token</span> onto <span class="hljs-keyword">the</span> stack <span class="hljs-built_in">result</span> ← pop <span class="hljs-built_in">from</span> <span class="hljs-keyword">the</span> stack</pre></div><p id="c6fb">Let’s change this algorithm a little bit. By processing the postfix expression from left to right and instead of evaluating the expression on finding the operator’s symbol, we could build an expression tree and evaluate it later. This kind of expression tree would look like this:</p><figure id="f90c"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*B_fYasmzWHTOdVRim_7KEQ.png"><figcaption>RPN expression tree (<a href="https://media.geeksforgeeks.org/wp-content/uploads/expression-tree.png">source</a>)</figcaption></figure><p id="0463">Terminal expressions (numbers) do not have any children in the expression tree, but each nonterminal expression (arithmetic operation) has two children — terminal and/or nonterminal expressions. To evaluate this kind of expression tree the program just has to start from the root, recursively execute (interpret) each expression and accumulate the final result.</p><p id="b106">I expect you have already noticed that the Interpreter design pattern fits the problem of implementing the postfix mathematical expression parser just perfectly. So let’s jump straight to the implementation details!</p><h2 id="0a83">Class diagram</h2><p id="e689">The class diagram below shows the implementation of the Interpreter design pattern:</p><figure id="f30a"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*ptop6htXGXl5xlrEvI0spw.png"><figcaption>Class Diagram — Implementation of the Interpreter design pattern</figcaption></figure><p id="801b"><i>IExpression</i> defines a common interface for both terminal and nonterminal expressions which implement the <i>interpret()</i> method:</p><ul><li><i>Number </i>— a terminal expression for numbers;</li><li><i>Multiply</i> — a nonterminal expression of the multiplication operation;</li><li><i>Subtract</i> — a nonterminal expression of the subtraction operation;</li><li><i>Add</i> — a nonterminal expression of the addition operation.</li></ul><p id="7c12">All of the nonterminal expressions contain left and right expressions of type <i>IExpression</i> which are used in the <i>interpret()</i> method to calculate the result of the arithmetic operation.</p><p id="2776"><i>ExpressionContext</i> class contains the solution steps of the postfix expression and is used by the <i>ExpressionSection</i> widget to retrieve those steps and the <i>IExpression</i> interface implementing classes to add a specific solution step to the context.</p><p id="daad"><i>ExpressionSection</i> uses the <i>ExpressionHelpers</i> class to build the expression tree of the postfix expression and the <i>ExpressionContext</i> to retrieve the solution steps of the specific postfix expression.</p><h2 id="b0dc">IExpression</h2><p id="ee49">An interface that defines the <i>interpret()</i> method to be implemented by the terminal and nonterminal expression classes. Dart language does not support the interface as a class type, so we define an interface by creating an abstract class and providing a method header (name, return type, parameters) without the default implementation.</p><figure id="84e0"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*VdXtAx76TdSyaNv6nSEG_A.png"><figcaption>iexpression.dart</figcaption></figure><h2 id="355e">ExpressionContext</h2><p id="2663">A class to define the context which stores the solution steps of the postfix expression and is used by the <i>Client</i> and classes implementing the <i>IExpression</i> interface.</p><figure id="19f4"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*vbTp0Ng0zjoXZn2Xx3utzQ.png"><figcaption>expression_context.dart</figcaption></figure><h2 id="8171">ExpressionHelpers</h2><p id="05c6">A helper class is used by the <i>Client</i> to build the expression tree from the provided postfix expression input.</p><figure id="ae2f"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*ryrMcXuTiSWGeseYH1Ksrg.png"><figcaption>expression_helpers.dart</figcaption></figure><h2 id="847b">Number</h2><p id="6db2">A terminal expression class to define the number in postfix expression.</p><figure id="0042"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*B0v6mxB20NfDSXIOwcNWug.png"><figcaption>number.dart</figcaption></figure><h2 id="b0d1">Nonterminal expressions</h2><p id="a736"><i>Add </i>defines the addition operation and adds the addition solution step to the <i>ExpressionContext</i>. The result of this operation — left and right expressions’ sum.</p><figure id="839f"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*hmzqfGtDnpeaVgLP_6kQBw.png"><figcaption>add.dart</figcaption></figure><p id="f51d"><i>Subtract </i>defines the subtraction operation and adds the subtraction solution step to the <i>ExpressionContext</i>. The result of this operation — left and right expressions’ difference.</p><figure id="7a93"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:80

Options

0/1*GDTRngz45TvhOKsh9ghcsA.png"><figcaption>subtract.dart</figcaption></figure><p id="4c96"><i>Multiply </i>defines the multiplication operation and adds the multiplication solution step to the <i>ExpressionContext</i>. The result of this operation — left and right expressions’ product.</p><figure id="3531"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*kaaxT12hwZePdT8A-s-Bqg.png"><figcaption>multiply.dart</figcaption></figure><h2 id="36f5">Example</h2><p id="ce13">First of all, a markdown file is prepared and provided as a pattern’s description:</p><figure id="88a8"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*QcZx_ayci-xf23-cw2TyYw.gif"><figcaption></figcaption></figure><p id="7401"><i>InterpreterExample</i> widget contains the list of postfix expressions. For each expression in the <i>postfixExpressions</i> list, an <i>ExpressionSection</i> widget is created and a specific postfix expression is passed to it using the constructor.</p><figure id="51fb"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*4k4z_ZlT9lNSXNSNFJZ7vA.png"><figcaption>interpreter_example.dart</figcaption></figure><p id="aa41"><i>ExpressionSection</i> uses the provided <i>postfixExpression</i> and builds its expression tree using the <i>ExpressionHelpers</i> class on the ‘Solve’ button click.</p><figure id="3d35"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*rWH9f5CjOO5kDmBd5IBDwg.png"><figcaption>expression_section.dart</figcaption></figure><p id="a5c8">The <i>buildExpressionTree()</i> method returns a single nonterminal expression of type <i>IExpression</i> which is used to calculate the final result of the provided postfix expression. The widget/method itself does not care about the specific implementation of the nonterminal expression, it only calls the <i>interpret()</i> method on the expression to get the final result. Also, a list of solution steps to get the final result is retrieved from the <i>ExpressionContext</i> using the <i>getSolutionSteps()</i> method and presented in the UI.</p><p id="c003">The final result of the Interpreter design pattern’s implementation looks like this:</p><figure id="7e28"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*JRXSWGu0ibHjG2vwkQaodw.gif"><figcaption></figcaption></figure><p id="cdd7">As you can see in the example, every postfix expression is evaluated on a ‘Solve’ button click, all the solution steps are provided to the UI along with the final result.</p><p id="c942">All of the code changes for the Interpreter design pattern and its example implementation could be found <a href="https://github.com/MangirdasKazlauskas/flutter-design-patterns/pull/9">here</a>.</p><h1 id="cf51">Other articles in this series</h1><ul><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-0-introduction-5e88cfff6792">0 — Introduction</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-1-singleton-437f04e923ce">1 — Singleton</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-2-adapter-3f05c02a7c84">2 — Adapter</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-3-template-method-89799d84e378">3 — Template Method</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-4-composite-23473cccf2b3">4 — Composite</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-5-strategy-ef9cf5b5b694">5 — Strategy</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-6-state-be06cb05525c">6 — State</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-7-facade-eb40434fb973">7 — Facade</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-9-iterator-8da27ee83c17">9 — Iterator</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-10-factory-method-c53ad11d863f">10 — Factory Method</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-11-abstract-factory-7098112925d8">11 — Abstract Factory</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-12-command-e199172e16eb">12 — Command</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-13-memento-b487769cf104">13 — Memento</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-14-prototype-7d7d18bcf643">14 — Prototype</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-15-proxy-af8743b24269">15 — Proxy</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-16-decorator-bf0dd711f093">16 — Decorator</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-17-bridge-b31711b629f2">17 — Bridge</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-18-builder-cdc90b222724">18 — Builder</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-19-flyweight-3d41cfdf36c">19 — Flyweight</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-20-chain-of-responsibility-2ff122624297">20 — Chain of Responsibility</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-21-visitor-af5def0699be">21 — Visitor</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-22-mediator-575e7aa6bfa9">22 — Mediator</a></li><li><a href="https://mkobuolys.medium.com/flutter-design-patterns-23-observer-1e1b0ea81d73">23 — Observer</a></li></ul><h1 id="e2a4">Your contribution</h1><p id="e9a4">👏 Press the clap button below to show your support and motivate me to write better! 💬 Leave a response to this article by providing your insights, comments or wishes for the series. 📢 Share this article with your friends, colleagues on social media. ➕ Follow me on Medium. ⭐ Star the <a href="https://github.com/MangirdasKazlauskas/flutter-design-patterns">Github</a> repository.</p><div id="b8b6" class="link-block"> <a href="https://www.twitter.com/FlutterComm"> <div> <div> <h2>Flutter Community</h2> <div><h3>The latest Tweets from Flutter Community (@FlutterComm). Follow to get notifications of new articles and packages from…</h3></div> <div><p>www.twitter.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/0*gyQs94ue7kpLNdUz)"></div> </div> </div> </a> </div></article></body>

Flutter Design Patterns: 8 — Interpreter

An overview of the Interpreter design pattern and its implementation in Dart and Flutter

Previously in the series, I have analysed a relatively simple and straightforward design pattern — Facade. In this article, I will analyse and implement a pattern, which has a similar structure to Composite but is used in a different context and for a different purpose — the Interpreter design pattern.

Update 2022–09–15: I moved this blog to my personal website. For a better reading experience, up to date articles, interactive code examples and some extra content FOR FREE, check kazlauskas.dev.

Table of Contents

  • What is the Interpreter design pattern?
  • Analysis
  • Implementation
  • Other articles in this series
  • Your contribution

What is the Interpreter design pattern?

Wrong interpretation (source)

The Interpreter is a behavioural design pattern, which intention in the GoF book is described like this:

Given a language, define a representation for its grammar along with an inter­preter that uses the representation to interpret sentences in the language.

The main idea behind this is that a language is a set of valid sentences. Each of those sentences can be constructed by following a set of grammar rules defining the language. Sometimes the program which needs to interpret those sentences process a lot of repeated occurrences of similar requests that are a combination of a set of grammar rules, but all of these requests are composed using the same grammar rules. For instance, arithmetic expressions could be different and provide different results (addition, subtraction, multiplication, division), but all of them are constructed of the same basic rules defining the language of arithmetic expressions.

So where the Interpreter design pattern plays its role? Well, this pattern could represent each of those basic rules as a separate class representing a separate grammar rule and by making a hierarchy of these rules you can define any sentence of that particular language. Also, having a separate class for every grammar rule makes it easy to change the grammar itself and maintain it, adding new kinds of interpret operations becomes an easy task, too.

It could sound complicated at first, but let’s move to the analysis and implementation parts where you can find some specific examples of the Interpreter design pattern and its usage.

Analysis

The class diagram below shows the general structure of the Interpreter design pattern:

Class Diagram — Interpreter
  • AbstractExpression — declares an abstract interpret() operation that is common to all nodes in the abstract syntax tree;
  • TerminalExpression — implement an interpret() operation associated with terminal symbols in the grammar;
  • NonterminalExpression — implement an interpret() operation for nonterminal symbols in the grammar. This operation typically calls itself recursively on the variables representing other expressions (grammar rules);
  • Context — contains the global information of the interpreter which is shared among the expression instances;
  • Client — builds an abstract syntax tree representing a particular sentence in the language that the grammar defines and invokes the interpret() operation.

Composite vs Interpreter

The Interpreter design pattern’s similarity to the Composite is evident. The Interpreter itself uses the Composite design pattern to build and represent a sentence in a simple language as a tree. But that’s all — the Composite pattern is only used to define the static properties of the system, to define the structure (it is a structural design pattern, right?), but the Interpreter represents the language itself, defines the behaviour, has some additional logic to interpret each entity in the tree, shares the same context between them — that’s the main difference and the reason why this pattern is considered as a behavioural design pattern.

Applicability

The usage of the Interpreter design pattern is quite dedicated to interpreting languages in which statements could be represented as an abstract syntax tree. Usually, this kind of language has a simple grammar defined in specific rules e.g. RegEx (regular expression), bar codes, mathematical expressions/notations, etc. Also, the Interpreter design pattern should be used when efficiency is not a critical concern since building and parsing expression trees for the bigger sentences of the language is relatively inefficient (e.g. comparing to parsers and interpreters that translate the expression tree to the other form before interpreting it).

Implementation

Let’s say you want to create a program that parses and solves the postfix mathematical expression. A postfix a.k.a. Reverse Polish notation (RPN) is a mathematical notation in which every operator follows all of its operands e.g. 69+242-*+ which is equivalent to 6+9+(4-2)*2.

The postfix expression contains two types of symbols — numbers and operators. Talking in PEG terms, numbers are terminal symbols and operators are nonterminal symbols.

The evaluation of the postfix expression could be implemented using the stack data structure by processing the expression from left to right:

for each token in the postfix expression:
  if token is an operator:
    operand_2 ← pop from the stack
    operand_1 ← pop from the stack
    result ← evaluate token with operand_1 and operand_2
    push result back onto the stack
  else if token is an operand:
    push token onto the stack
result ← pop from the stack

Let’s change this algorithm a little bit. By processing the postfix expression from left to right and instead of evaluating the expression on finding the operator’s symbol, we could build an expression tree and evaluate it later. This kind of expression tree would look like this:

RPN expression tree (source)

Terminal expressions (numbers) do not have any children in the expression tree, but each nonterminal expression (arithmetic operation) has two children — terminal and/or nonterminal expressions. To evaluate this kind of expression tree the program just has to start from the root, recursively execute (interpret) each expression and accumulate the final result.

I expect you have already noticed that the Interpreter design pattern fits the problem of implementing the postfix mathematical expression parser just perfectly. So let’s jump straight to the implementation details!

Class diagram

The class diagram below shows the implementation of the Interpreter design pattern:

Class Diagram — Implementation of the Interpreter design pattern

IExpression defines a common interface for both terminal and nonterminal expressions which implement the interpret() method:

  • Number — a terminal expression for numbers;
  • Multiply — a nonterminal expression of the multiplication operation;
  • Subtract — a nonterminal expression of the subtraction operation;
  • Add — a nonterminal expression of the addition operation.

All of the nonterminal expressions contain left and right expressions of type IExpression which are used in the interpret() method to calculate the result of the arithmetic operation.

ExpressionContext class contains the solution steps of the postfix expression and is used by the ExpressionSection widget to retrieve those steps and the IExpression interface implementing classes to add a specific solution step to the context.

ExpressionSection uses the ExpressionHelpers class to build the expression tree of the postfix expression and the ExpressionContext to retrieve the solution steps of the specific postfix expression.

IExpression

An interface that defines the interpret() method to be implemented by the terminal and nonterminal expression classes. Dart language does not support the interface as a class type, so we define an interface by creating an abstract class and providing a method header (name, return type, parameters) without the default implementation.

iexpression.dart

ExpressionContext

A class to define the context which stores the solution steps of the postfix expression and is used by the Client and classes implementing the IExpression interface.

expression_context.dart

ExpressionHelpers

A helper class is used by the Client to build the expression tree from the provided postfix expression input.

expression_helpers.dart

Number

A terminal expression class to define the number in postfix expression.

number.dart

Nonterminal expressions

Add defines the addition operation and adds the addition solution step to the ExpressionContext. The result of this operation — left and right expressions’ sum.

add.dart

Subtract defines the subtraction operation and adds the subtraction solution step to the ExpressionContext. The result of this operation — left and right expressions’ difference.

subtract.dart

Multiply defines the multiplication operation and adds the multiplication solution step to the ExpressionContext. The result of this operation — left and right expressions’ product.

multiply.dart

Example

First of all, a markdown file is prepared and provided as a pattern’s description:

InterpreterExample widget contains the list of postfix expressions. For each expression in the postfixExpressions list, an ExpressionSection widget is created and a specific postfix expression is passed to it using the constructor.

interpreter_example.dart

ExpressionSection uses the provided postfixExpression and builds its expression tree using the ExpressionHelpers class on the ‘Solve’ button click.

expression_section.dart

The buildExpressionTree() method returns a single nonterminal expression of type IExpression which is used to calculate the final result of the provided postfix expression. The widget/method itself does not care about the specific implementation of the nonterminal expression, it only calls the interpret() method on the expression to get the final result. Also, a list of solution steps to get the final result is retrieved from the ExpressionContext using the getSolutionSteps() method and presented in the UI.

The final result of the Interpreter design pattern’s implementation looks like this:

As you can see in the example, every postfix expression is evaluated on a ‘Solve’ button click, all the solution steps are provided to the UI along with the final result.

All of the code changes for the Interpreter design pattern and its example implementation could be found here.

Other articles in this series

Your contribution

👏 Press the clap button below to show your support and motivate me to write better! 💬 Leave a response to this article by providing your insights, comments or wishes for the series. 📢 Share this article with your friends, colleagues on social media. ➕ Follow me on Medium. ⭐ Star the Github repository.

Software Design Patterns
Software Engineering
Programming
Flutter
Technology
Recommended from ReadMedium