WEBVTT 00:00.450 --> 00:01.150 Hello. 00:01.260 --> 00:06.390 We are now at the last video of this section interpreter design pattern. 00:06.390 --> 00:11.020 Previously we learned about the memento design pattern in this video. 00:11.040 --> 00:17.950 We will learn to create a reverse Polish notation calculator using the interpreter design pattern. 00:17.960 --> 00:21.530 Now we are going to dig into a quite complex pattern. 00:21.590 --> 00:27.650 The interpreter pattern is in fact widely used to solve business cases where it's useful to have a language 00:27.650 --> 00:29.970 to perform common operations. 00:30.020 --> 00:32.380 Let's see what we mean by language. 00:32.450 --> 00:36.620 The most famous interpreter We can talk about is probably askew. 00:36.740 --> 00:42.560 It is defined as a special purpose programming language for managing data held in relational databases 00:43.250 --> 00:49.520 as well as quite complex and big but all in all is a set of words and operators that allow us to perform 00:49.610 --> 00:53.410 operations such as insert select or delete. 00:53.810 --> 00:56.580 Another typical example is musical notation. 00:56.640 --> 01:01.610 It is the language itself and the interpreters the musician who knows the connection between a note 01:01.820 --> 01:07.870 and its representation on the instrument they are playing in computer science it can be useful to design 01:07.870 --> 01:14.710 a small language for a variety of reasons like repetitive tasks higher level languages for non developers 01:14.950 --> 01:16.690 or interface definition languages. 01:16.690 --> 01:22.210 That is Ida L. such as Protocol Buffers or Apache thrift. 01:22.210 --> 01:28.780 Designing a new language big or small can be time consuming so it's very important to have the objectives 01:28.810 --> 01:33.720 clear before investing time and resources on writing an interpreter of it. 01:33.730 --> 01:38.010 So here are a few objectives of the interpreter design pattern. 01:38.050 --> 01:43.750 Firstly it provides syntax for very common operations in some scope such as playing notes. 01:43.960 --> 01:48.850 It allows having an intermediate language to translate actions between two systems. 01:48.850 --> 01:54.640 For example the apps that generate the G code needed to print with 3D printers. 01:54.640 --> 02:02.620 It is is the use of some operations in an easier to use syntax as QoL allows the use of relational databases 02:02.800 --> 02:04.860 in a very easy to use syntax. 02:04.900 --> 02:10.030 But the idea is not to need to write your own functions to make insertions and searches. 02:10.240 --> 02:16.170 A very typical example of an interpreter is to create a reverse Polish notation calculator. 02:16.450 --> 02:21.730 For those of you who don't know what the Polish notation is it's a mathematical notation to make operations 02:21.970 --> 02:25.720 where you write your operations first and then the values. 02:25.720 --> 02:33.110 So plus 3 4 is equivalent to the more common 3 plus 4 and its results would be 7. 02:33.160 --> 02:39.330 So for a reverse Polish notation you put first the values and then the operation. 02:39.430 --> 02:45.730 So 3 4 followed by a plus sign would also be 7 for our calculator. 02:45.730 --> 02:47.580 The acceptance criteria we should pass. 02:47.590 --> 02:50.190 Consider it done or listed here. 02:50.500 --> 02:58.030 First create a language that allows making common arithmetic operations like sums subtractions multiplications 02:58.180 --> 02:59.770 and divisions. 02:59.770 --> 03:04.130 Next it must be done using reverse Polish notation. 03:04.150 --> 03:08.320 Also the user must be able to write as many operations in a row as they want. 03:08.830 --> 03:13.830 And lastly the operations must be performed from left to right. 03:13.960 --> 03:22.540 So the 3 for some 2 subtraction notation is the same as 3 plus 4 in parentheses minus 2 and the result 03:22.720 --> 03:24.560 would be 5. 03:24.560 --> 03:28.820 Now let's start with the unit test of some operations. 03:28.820 --> 03:34.400 In this case we will only have a public method called calculate that takes an operation with its values 03:34.400 --> 03:38.690 defined as a string and will return a value or an error. 03:38.730 --> 03:43.430 I've created a folder named interpreter let's open the interpreter dot go file. 03:43.460 --> 03:48.980 And here we first add the package name import statement and calculate function. 03:48.980 --> 03:56.030 So we will send a string like three four plus to the calculate method and it should return seven nil. 03:56.240 --> 03:59.850 Two tests more will check the correct implementation. 03:59.870 --> 04:03.140 Let's save the file and move to the interpreter test. 04:03.140 --> 04:08.960 Don't go here we write the function test calculator for our unit test. 04:09.140 --> 04:14.610 Wait we need to add the package name and import testing at the beginning of the code. 04:14.960 --> 04:16.530 Go first. 04:16.560 --> 04:20.180 We are going to make the operation we have used as an example. 04:20.180 --> 04:27.200 The 3 for some 2 sub notation is part of our language and we use it in the calculate function. 04:27.200 --> 04:30.300 If an error is returned the test fails. 04:30.350 --> 04:35.730 Finally the result must be equal to 5 and we check it on the last lines. 04:35.810 --> 04:41.150 The next test checks the rest of the operators on slightly more complex operations. 04:41.330 --> 04:45.830 Let me add a few more lines of code and explain it to you here. 04:45.870 --> 04:52.770 We repeat the previous version with a longer operation like the notation 5 minus three. 04:52.770 --> 05:00.810 The hole into 8 and then adding this product to 4 and dividing the whole value by 5 which is equal to 05:00.810 --> 05:09.970 4 let me show you how it could be simplified from left to right the tests must fail of course. 05:09.990 --> 05:16.820 So after saving the changes made to the file let us run the test go to the terminal and run the command 05:17.030 --> 05:19.020 go test hyphen V. 05:19.130 --> 05:21.890 As expected the test failed. 05:21.890 --> 05:25.460 Implementation is going to be longer than testing time to start. 05:25.460 --> 05:31.490 We will define our possible operators in constant let's open the interpreter dot go file. 05:31.610 --> 05:40.310 Here we define the constants and include SDR conf strings and F empty in the import statement interpreter 05:40.310 --> 05:46.640 patterns are usually implemented using an abstract syntax tree something that is commonly achieved using 05:46.640 --> 05:49.520 a stack we have created stacks before the course. 05:49.520 --> 05:52.000 So this should be already familiar to you. 05:52.010 --> 05:55.900 Now we add the Polish notation stack type. 05:55.900 --> 05:58.430 Okay here we have two methods. 05:58.430 --> 06:04.820 The push method to add elements to the top of the stack and the pop method to remove elements and return 06:04.820 --> 06:05.540 them. 06:05.720 --> 06:12.380 In case you're thinking that the line asterisk P equal 2 is a bit cryptic we'll explain it. 06:12.380 --> 06:17.750 The value stored in the direction of P will be overwritten with the actual value in the direction of 06:17.750 --> 06:23.990 P asterisk P but taking only the elements from the beginning to the penultimate element of the array 06:23.990 --> 06:25.520 length minus 1. 06:26.330 --> 06:32.840 So now we will go step by step with the calculate function creating more functions as far as we need 06:32.840 --> 06:33.650 them. 06:33.650 --> 06:37.340 So next we create a calculate function. 06:37.340 --> 06:43.340 The first two things we need to do are to create the stack and to get all different symbols from the 06:43.340 --> 06:45.140 incoming operation. 06:45.170 --> 06:48.590 In this case we aren't checking that it isn't empty. 06:48.590 --> 06:54.560 We split the incoming string operations by the space to get a nice slice of symbols that is values and 06:54.560 --> 06:56.060 operators. 06:56.060 --> 07:02.330 Next we will iterate over every symbol by using range but we need a function to know whether the incoming 07:02.330 --> 07:05.020 symbol is a value or an operator. 07:05.060 --> 07:08.450 So let's add the is operator function. 07:08.450 --> 07:15.650 If the incoming symbol is any of the ones defined in our constants the incoming symbol is an operator. 07:15.650 --> 07:20.180 Now let's add the code at the bottom of the code to the calculate function. 07:20.180 --> 07:21.140 Done. 07:21.140 --> 07:23.700 Don't worry about the code added as comment. 07:23.750 --> 07:25.380 We'll explain and use it later. 07:26.440 --> 07:32.500 Now if it is an operator we consider that we have already passed two values so that what we have to 07:32.500 --> 07:39.130 do is take those two values from the stack the first value taken would be the right most and the second 07:39.190 --> 07:40.320 the leftmost. 07:40.480 --> 07:46.270 Remember that in subtractions and divisions the order of the operations is important. 07:46.270 --> 07:50.500 Then we need some function to get the operation we want to perform. 07:50.500 --> 07:54.990 So let's add this function right below where we have declared the constants. 07:55.090 --> 08:01.960 Also remove the calculate function which we added before the get operation function returns a 2 argument 08:01.960 --> 08:04.380 function that returns an integer. 08:04.570 --> 08:10.420 We check the incoming operator and we return an anonymous function that performs the specified operation. 08:10.420 --> 08:14.160 So now our 4 range continues like this. 08:14.200 --> 08:16.010 Let me remove the comments. 08:16.120 --> 08:19.080 The math function variable is returned by the function. 08:19.300 --> 08:25.150 We use it immediately to perform the operation on the left and right values taken from the stack and 08:25.150 --> 08:28.450 we store its result in a new variable called rez. 08:28.450 --> 08:33.920 Finally we need to push this new value to the stack to keep operating with it later. 08:33.940 --> 08:39.550 Now here is the implementation when the incoming symbol is a value the piece of code which you see as 08:39.550 --> 08:40.410 a comment. 08:40.480 --> 08:42.730 Let me included as code. 08:42.970 --> 08:44.170 Cool. 08:44.350 --> 08:45.120 What do we need to do. 08:45.130 --> 08:48.430 Every time we get a symbol is to push it to the stack. 08:48.430 --> 08:52.420 We have to pass the string symbol to a usable end type. 08:52.450 --> 08:55.820 This is commonly done with the s t r conf package. 08:55.930 --> 09:04.480 By using its 80 o I function the API function takes a string and returns an integer from it or an error. 09:04.630 --> 09:10.150 If everything goes well the value is pushed into the stack at the end of the range statement. 09:10.210 --> 09:12.530 Just one value must be stored on it. 09:12.610 --> 09:15.310 So we just need to return it and the function is done. 09:15.910 --> 09:20.530 That's what we do with the last line of code which you see as a comment. 09:20.530 --> 09:22.820 Let us include this too as code. 09:22.870 --> 09:26.580 Finally let's have a look at the whole code before we run the test. 09:27.040 --> 09:27.940 Okay. 09:28.120 --> 09:32.290 We don't need def empty for now time to run the tests again. 09:32.380 --> 09:36.020 Save the file and move to the terminal type. 09:36.020 --> 09:38.780 Go test hyphen v grade. 09:38.840 --> 09:45.150 We have just created a reverse Polish notation interpreter and a very simple and easy way. 09:45.200 --> 09:52.290 We still lack the parser but that's another story in this example we haven't used any interfaces. 09:52.290 --> 09:58.050 This is not exactly how the interpreter design pattern is defined in more object oriented languages. 09:58.080 --> 10:04.050 However this example is the simplest example possible to understand the objectives of the language and 10:04.080 --> 10:11.360 the next level is inevitably more complex and not intended for beginner users with a more complex example. 10:11.430 --> 10:18.330 We will have to define the type containing more types of itself a value or nothing with a parser you 10:18.330 --> 10:21.840 create this abstract syntax tree to interpret it later. 10:22.170 --> 10:29.260 The same example done by using interfaces would be as in the coming part of this video we will now have 10:29.260 --> 10:32.550 the interpreter pattern again using interfaces. 10:32.830 --> 10:36.990 The main interface we are going to use is called the interpreter interface. 10:37.000 --> 10:43.360 This interface has a read method that every symbol must implement lets open the interpreter dot go file 10:43.510 --> 10:45.760 and write the interface. 10:45.760 --> 10:51.290 Here we first define the main package and import strings and s.t. are icons. 10:51.500 --> 10:54.570 Then we have declared constants sum and sub. 10:55.090 --> 10:58.390 And over here we have the interpreter interface. 10:58.390 --> 11:04.300 We will implement only the sum and the subtraction from the operators and the type called value for 11:04.300 --> 11:05.240 the numbers. 11:05.290 --> 11:07.310 So let's add the code for it. 11:07.420 --> 11:13.240 The value is a type int that when implementing the read method just returns its value. 11:13.240 --> 11:20.240 Next we add the operation sum struct the operation sum struct has the left and right fields and it's 11:20.260 --> 11:27.130 read method returns the sum of each of their read methods the operations subtract struct is the same 11:27.250 --> 11:28.320 but subtracting. 11:28.900 --> 11:32.880 Similarly let us create the operation subtract struct. 11:33.190 --> 11:36.310 We also need a factory pattern to create operators. 11:36.310 --> 11:39.090 We will call it to the operator factory method. 11:39.100 --> 11:45.220 The difference now is that it not only accepts the symbol but also the left and right values taken from 11:45.220 --> 11:46.860 the stack. 11:46.870 --> 11:51.880 So now we add the operator factory method as we have just mentioned. 11:51.910 --> 11:53.700 We also need a stack. 11:53.860 --> 11:58.030 We can reuse the one from the previous example by changing its type. 11:58.030 --> 12:00.010 Let's do it right now. 12:00.010 --> 12:06.250 Here we have our Polish notation stack type now the stack works with the interpreter pointers instead 12:06.250 --> 12:09.370 of int but its functionality is the same. 12:09.370 --> 12:14.380 Finally our main method also looks similar to our previous example. 12:14.440 --> 12:17.770 So now we move ahead to add the main method. 12:17.770 --> 12:24.280 Like before we check whether the symbol is operator or value first when it's a value it pushes it into 12:24.280 --> 12:26.950 the stack when the symbol is an operator. 12:26.950 --> 12:32.830 We also take the right and left values from the stack we call the factory pattern using the current 12:32.830 --> 12:37.120 operator and the left and right values that we just took from the stack. 12:37.720 --> 12:43.170 Once we have the operator type we just need to call it read method to push the return value to the stack 12:43.180 --> 12:45.210 to finally. 12:45.260 --> 12:47.560 Just one example must be left on the stack. 12:47.660 --> 12:48.980 So we print it. 12:49.310 --> 12:50.740 Now we're done with everything. 12:50.810 --> 12:55.420 Let's save the file and open the terminal execute the command. 12:55.420 --> 12:58.540 Go run interpreter dot go. 12:58.540 --> 12:59.500 Awesome. 12:59.500 --> 13:03.670 We have the expected output as 5 at the end. 13:03.670 --> 13:08.350 I would like to share a few important points about the interpreter pattern. 13:08.350 --> 13:14.350 This pattern is extremely powerful but it must also be used carefully to create a language. 13:14.410 --> 13:19.740 It generates a strong coupling between its users and the functionality it provides. 13:19.750 --> 13:25.150 One can fall into the error of trying to create a two flexible language that is incredibly complex to 13:25.150 --> 13:27.060 use and maintain. 13:27.100 --> 13:33.250 Also one can create a fairly small and useful language that doesn't interpret correctly sometimes and 13:33.250 --> 13:35.770 it could be a pain for its users. 13:35.770 --> 13:40.810 In our example we have a method quite a lot of error checking to focus on the implementation of the 13:40.810 --> 13:41.820 interpreter. 13:41.860 --> 13:47.740 However you'll need to do quite a lot of error checking and verbose output on errors to help the user 13:47.740 --> 13:49.750 correct its syntax errors. 13:50.020 --> 13:54.190 So have fun writing your language but be nice to your users. 13:54.370 --> 13:59.620 In this section we have dealt with three extremely powerful patterns that require a lot of practice 13:59.650 --> 14:02.200 before using them in production code. 14:02.200 --> 14:08.950 So we started with the template design pattern and then we moved on to the momento design pattern. 14:08.950 --> 14:15.730 Finally we looked into the interpreter design pattern in the next section we will look at visitor state 14:15.940 --> 14:18.850 mediator and observer design patterns.