ActionScript 3.0 Interpreter Design Pattern: A PostScript Tutor

ActionScript 3.0 Interpreter Design Pattern

Back in the day I used to do a lot of FORTH programming. For about 10 years I’d attend a weekly meeting of FORTH programming engineers at General Dynamics in San Diego and marvel at what they could do with this postfix language. (Postfix is often called reverse Polish notation after the Polish mathematician Jan Łukasiewicz who developed the mathematical notation placing the operator after the operands.) The following shows an example of this notation:

30 2 * 4 +
Result: 64

Later, when Adobe came out with PostScript and Apple came out with their first laser printer, I was able to program in PostScript using a text editor and shooting it directly to the printer. Out would come these fabulous fonts, drawings and whatever else I had programmed in. Because PostScript used a similar postfix notation as FORTH, I was allowed to do a FORTH newsletter in PostScript. Only a few were produced since it was no easy task to develop a newsletter in code using Notepad! (Click here to see the final PostScript calculator that this application generates. It works with PostScript data entry — use add, sub, mul, and div for +, -, *, and / respectively.)

Interpret This!

The Interpreter design pattern as described by Gamma, et al can be used to express instances of a recurring problem as sentences. Interpreting the sentences then solves the problem. Right off the bat I was thinking, ¿como está usted? translates to how’s it going? but that’s not exactly what GoF had in mind. Any spoken language is a bit too big for this kind of interpreter. Instead, the pattern describes how to define a grammar for simple languages. Gamma and his associates use the example of regular expressions, so loved by Perl programmers.

So when I went looking at some Interpreter examples, besides regular expressions, I found converters from Roman numerals to regular numbers, a Boolean language, a musical note interpreter from do, rey, me, fa, so, la, ti to Hertz (Hz) values, calculations using postfix notations and some other fairly modest examples. Because of my experiences with postfix languages, I decided to do one that set up as a PostScript data entry that would resolve to an outcome (solution) to the results of a postfix statement. I decided on PostScript whose math operators are word-like and not symbols. (e.g., Instead of using / for division, it uses div.) This would be a little more than the usual minimalist example since the user can use it to practice and learn PostScript math entries.

Interpreter Design Pattern Formal Features

Figure 1 provides an overview of the Interpreter pattern. Note that the Client is part of the pattern (instead of implied or not at all). Also note that the Client holds references to both the Context class and AbstractExpression interface.

Figure 1: Interpreter Class Diagram

Of all of the design patterns I’ve seen, this one has the most loose ends. Any statement put into a string and then interpreted generally requires some kind of parsing. GoF note the need for parsing and point out it can be from a table driven source, a recursive descent (or some other hand-crafted parser) or in the Client. The Context class is used as a global entry point for the Interpreter. It’s probably heresy to do so, but I decided to put the parser in the Context. The Client sends the request to the Context and indirectly to the AbstractExpression. The Context class still acts as a global entry point, but it also parses the data (statement in PostScript notation), and the Terminal Expression classes still do the actual interpretation. I don’t see it as adding tighter coupling. (If the parser were placed in the Client, I suppose it would be a purer version of the design pattern. If you think so, I’d like to see your implementation, and I’d be glad to put it on this blog.) Also, like most of the examples that I saw, this one does not include a NonterminalExpression. That is because I did not need alternation, repetition or sequence expressions.

The good news is that this implementation of the design pattern is wonderfully flexible and easy to update. If I wanted to change the language example from PostScript to something like Scheme, the pattern would be able to handle it with ease. Eventually, I’d like to re-do it with the parser in the Client and add several PostScript drawing commands by adding more terminal expression classes. I’d also like to add a NonterminalExpression for handling certain repetitions and sequences.

Building a PostScript Learning Tool

Before going any further there are a couple of things you need to know. First, I used the Vector class in the examples. In order to use Vector classes you need Flash CS4. It does not work with Flex 3 either. Also, you’ll the need Flash 10 player. (You may find an updated pre-release version of Flex at Adobe Labs that might work.) If none of those options are available to you, just change all of the Vector instances to Arrays. Why the Vector class rather than the Array class? Vectors have typed elements and Arrays don’t.

The goal of this Interpreter is to create an application that will take a string statement ordered in the correct PostScript format and display accurate results. So for example, if you enter,

7 10 mul 20 add

you will be returned,

90

It will be able to work through addition, subtraction, division and multiplication using PostScript formatting.

The Terminal Expression Classes

To get started, you will need classes to interpret the statements you enter. To do so, each of the elements of the entry must be interpreted to behave in a certain way. The parser (placed in the Context) divides up the string statement and sends each element to be evaluated by the terminal expression classes. The terminal class then does something, depending on whether the element is a number or one of the four arithmetic operations. If entered in the correct sequence, everything will work as it should. However, if not, the result will be unexpected. For example, if the user enters,

2 4 6 8 add

expecting that all the numbers will be added, he should find that only the last two numbers are added. Were he using PostScript, that’s what he should see as well. So, each of the four different operators have to have a separate class as do the numbers that are added to the Vector class used in parsing. The following six classes show the AbstractExperssion interface and the five terminal classes.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//Abstract expression interface
package
{
     public interface AbExpression
     {
          function interpret(v:Vector.):void;
     }
}
 
//Terminal class for addition
package
{
     public class AddTermExp implements AbExpression
     {
          public function interpret(v:Vector.):void
          {
               v.push( v.pop() + v.pop() );
 
          }
     }
}
 
//Terminal class for subtraction
package
{
     public class SubtractTermExp implements AbExpression
     {
          public function interpret(v:Vector.):void
          {
               v.push( -v.pop() + v.pop() );
          }
     }
}
 
//Terminal class for multiplication
package
{
     public class MultiplyTermExp implements AbExpression
     {
          public function interpret(v:Vector.):void
          {
               v.push( v.pop() * v.pop() );
 
          }
     }
}
 
//Terminal class for division
package
{
     public class DivideTermExp implements AbExpression
     {
          public function interpret(v:Vector.):void
          {
               var v1:Number=v.pop();
               var v2:Number=v.pop();
               v.push( v2 / v1 );
          }
     }
}
 
//Terminal class for numbers
package
{
     class ValueTermExp implements AbExpression
     {
          private var num:Number;
          public function ValueTermExp(num:Number)
          {
               this.num = num;
          }
          public function interpret(v:Vector.):void
          {
               v.push(num);
          }
     }
}

By using the pop() method with the Vector holding the parsed elements of the PostScript statement, we’re able to follow the LIFO (Last In First Out) sequence, which just happens to be the same sequence of operations followed by postfix languages. Were we using some language like Schema, we’d have to use different algorithms because the operators precede the operands, but when you stop to think about it, that’s not too difficult to make the changes. (We’d also have to so something about those parentheses!) Instead of having to re-structure the whole program, all you have to do is to make changes to the terminal classes.

The Client and Context

The Client is a big part of the Interpreter pattern. In fact, it is one of the recommended places for the syntax tree and parsing operations, but in this case it requests the parsing operations from the Context class. The Context class sets up and maintains the parsing sent to it by the Client class. This part of the design is a bit problematic because the parser is supposed to go in the Client and the Context is meant to be a global access point for the Interpreter pattern. However, setting it up as I did makes sense to me, and if anyone can find any violated OOP, I’ll go about fixing it.

?View Code ACTIONSCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//Client class
package
{
     import flash.display.Sprite;
     import flash.text.TextField;
     import flash.text.TextFieldType;
     import flash.events.MouseEvent;
 
     public class Client extends Sprite
     {
          private var expression:String;
          private var context:Context;
          private var input:TextField=new TextField();
          private var output:TextField=new TextField();
          private var calculate:Calculate=new Calculate();
 
          public function Client()
          {
               input.type = TextFieldType.INPUT;
               input.x = 140,input.y = 80;
               input.width = 120,input.height = 20;
               input.borderColor = 0xcccccc;
               input.border = true;
               addChild(input);
 
               calculate.x = 270,calculate.y = 75;
               addChild(calculate);
 
               calculate.addEventListener(MouseEvent.CLICK,showResults);
               output.type = TextFieldType.DYNAMIC;
               output.x = 140,output.y = 110;
               addChild(output);
          }
 
          private function showResults(e:MouseEvent)
          {
               expression = input.text;
               context = new Context(expression);
               output.text = "Result: " + context.evaluate();
          }
     }
}
 
//Context class with Parser
package
{
     public class Context
     {
          private var tree:Vector. = new Vector.  ;
          private var context:Vector. = new Vector.  ;
          private var current:Number;
          private var expElement:String;
          private var arr:Array;
 
          public function Context(statement:String)
          {
               arr = statement.split(" ");
               for (var piece:String in statement.split(" "))
               {
                    //el will be an element number
                    var el = int(piece);
                    //expElement is an expression element
                    expElement = arr[piece];
 
                    switch (expElement)
                    {
                         case "add" :
                              tree.push(new AddTermExp  );
                              break;
 
                         case "sub" :
                              tree.push(new SubtractTermExp  );
                              break;
 
                         case "div" :
                              tree.push(new DivideTermExp  );
                              break;
 
                         case "mul" :
                              tree.push(new MultiplyTermExp  );
                              break;
 
                         default :
                              tree.push(new ValueTermExp(int(expElement)));
                    }
               }
          }
 
          public function evaluate():Number
          {
               for each (var e:AbExpression in tree)
               {
                    e.interpret(context);
               }
               current = context.pop();
               return current;
          }
     }
}

The way this Interpreter is set up in the example is pretty simple. The Client sends a request to the Context in the form of a string to be parsed. The Context class sends each part of the string to the terminal classes for a conversion to a number or operation. It then evaluates the Vector tree and returns the value to the client.

More than Meets the Eye

In the articles on the Interpreter design pattern, some of the authors grumbled about it as being rarely used, blew smoke, or cobbled together a sort-of Interpreter example. I can’t remember a decent explanation of the non-terminal expression class that did more than paraphrase exactly what the Gang of Four had said. In one example, the client could send a request to the NonTerminal Expression class but it did nothing. Most of the examples (including mine) reminded me of some lines from an old Jim Croche song, Bad, bad, Leroy Brown. To wit:

Leroy [The Interpreter] looked like a jigsaw puzzle
With a couple of pieces gone

This is one of those design patterns to which I would like to return. Much of the core of the pattern lies in the parser, and I’d like to better flesh out the non-terminal expression, both as a concept and as a class. Most likely I will add the parser to the Client, and because the Client is a full participant in the Interpreter pattern, there’s no reason not to do so. Any code or concepts from the readers on this aspect of the Interpreter would be most welcome.

Getting Started in PostScript

Figure 2 shows what you will see when you run the program:

Figure 2:Coding in PostScript

The postfix notation is a stack-based way of working. Some calculators use postfix as well, but for the most part you don’t see it much any more. Before any processing is done, however, most languages rearrange entries so that they are set up in postfix order for better processing speed. Using this little application you can easily simulate working in a PostScript math environment. If you want the full PostScript language tutorial, you can download the book published in 1985 by Adobe at:

http://www-cdf.fnal.gov/offline/PostScript/BLUEBOOK.PDF

Then, as long as you stick with simple math operations, you can practice your PostScript skills here. The zipped file with all of the elements that make up the application can be found here.

The rules are simple. You add one or two values and an operator:

  • add
  • sub
  • div
  • mul

Then click Calculate. If you can predict what you’ll get before you click the Calculate button, then you’ve got it!

Wouldn’t it be fun to add more PostScript commands and then write terminal classes for the different commands? We’d really like to see anything you cook up with this. So get to coding and have some fun!

2 Responses to “ActionScript 3.0 Interpreter Design Pattern: A PostScript Tutor”


  • Nice article.
    But I’m having problems:

    Your link for downloading zip files is broken

    When I try to compile I get the error:
    AbExpression.as(7): col: 31 Error: Syntax error: expecting identifier before rightparen.

    Think this is due bad html formating? I see “Vector.” should it be something else?
    When I remove the period I get:
    Type was not found or was not a compile-time constant: Vector.

  • Hi Stever1975,

    I fixed the link (mea culpa). As for the Vector, it only works if you’re using Flash CS4. I tried it in Flex 3 and Flash CS3, and it doesn’t work because Vector is a new ActionScript 3.0 class in CS4. I’m guessing that it works in the next version of Flex as well. If you’re using anything earlier than CS4, use the Array class instead of the Vector class.

    Bill

Leave a Reply