September 17, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Working With Design Patterns: Interpreter

  • May 22, 2008
  • 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



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel