dcsimg
April 9, 2020
Hot Topics:

Working With Design Patterns: Interpreter

  • By Jeff Langr
  • Send Email »
  • More Articles »

Parsing

Unfortunately, the Interpreter pattern discussions rarely discuss how to create the composite structure in the first place. For my amusement, I chose to test-drive a simple parser that could take a string and generate the appropriate interpreter hierarchy. Listing 8 starts things off with a simple test to verify that I can parse a single terminal expression.

Listing 8: A first test for the parser.

import static org.junit.Assert.*;
import org.junit.*;

public class ParserTest {
   @Test
   public void singleExpression() {
      Parser parser = new Parser();
      Expression expression = parser.parse("contains text");
      assertEquals(Contains.class, expression.getClass());
      Contains contains = (Contains)expression;
      String[] keywords = contains.getKeywords();
      assertEquals("text", keywords[0]);
   }
}

Getting this test to pass is a simple matter (see Listing 9).

Listing 9: A very specific Parser implementation.

public class Parser {
   public Expression parse(String expression) {
      String[] tokens = expression.split(" ");
      return new Contains(tokens[1]);
   }
}

Listing 10 adds just a little more complexity—the "contains" keyword now needs to allow specifying multiple keywords.

Listing 10: A test for supporting multiple keywords.

import static org.junit.Assert.*;
import org.junit.*;

public class ParserTest {
   private Parser parser;

   @Before
   public void createParser() {
      parser = new Parser();
   }

   @Test
   public void singleContainsExpression() {
      Expression expression = parser.parse("contains text");
      assertKeywords((Contains)expression, "text");
   }

   @Test
   public void containsMultipleKeywords() {
      Expression expression = parser.parse("contains text bozo");
      assertKeywords((Contains)expression, "text", "bozo");
   }

   private void assertKeywords(Contains contains,
                               String... expected) {
      assertArrayEquals(expected, contains.getKeywords());
   }
}

Listing 11 provides the implementation for supporting multiple keywords. Note that the constructor for the Contains class will need to change to support taking a String array.

Listing 11: Supporting multiple keywords.

import java.util.*;

public class Parser {
   private List<String> arguments = new ArrayList<String>();

   public Expression parse(String expression) {
      String[] tokens = expression.split(" ");
      for (String token: tokens) {
         if (!token.equals("contains"))
            pushArgument(token);
      }
      return new Contains
         ((String[])arguments.toArray(new String[0]));
   }

   private void pushArgument(String token) {
      arguments.add(token);
   }
}

The next simplest thing to implement would be another terminal expression, such as "olderThan." Listings 12 and 13 lay out the test and changes to the Parser class.

Listing 12: Driving support for an additional terminal expression.

import static org.junit.Assert.*;
import java.util.*;
import org.junit.*;

public class ParserTest {
   private Parser parser;

   @Before
   public void createParser() {
      parser = new Parser();
   }

   @Test
   public void singleContainsExpression() {
      Expression expression = parser.parse("contains text");
      assertKeywords((Contains)expression, "text");
   }

   @Test
   public void containsMultipleKeywords() {
      Expression expression = parser.parse("contains text bozo");
      assertKeywords((Contains)expression, "text", "bozo");
   }

   @Test
   public void olderThan() {
      Expression expression = parser.parse("olderThan 03/31/2008");
      OlderThan olderThan = (OlderThan)expression;
      assertDate(2008, Calendar.MARCH, 31, olderThan.getDate());
   }

   private void assertDate(int year, int month,
                           int dayOfMonth, Date date) {
      Calendar calendar = GregorianCalendar.getInstance();
      calendar.setTime(date);
      assertEquals(year, calendar.get(Calendar.YEAR));
      assertEquals(month, calendar.get(Calendar.MONTH));
      assertEquals(dayOfMonth,
                   calendar.get(Calendar.DAY_OF_MONTH));
   }

   private void assertKeywords(Contains contains,
                               String... expected) {
      assertArrayEquals(expected, contains.getKeywords());
   }
}




Page 3 of 6



This article was originally published on May 22, 2008

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.


Thanks for your registration, follow us on our social networks to keep up-to-date