[antlr-interest] ANTLR NUB

Jan Nielsen jan.sture.nielsen at gmail.com
Mon Jan 21 10:08:59 PST 2008


Um, of course that should have been:

 "from 1/January/2008"
 "from 1/January/2008 to 1/January/2009"
 "from 1/January/2008 to 1/January/2009 excluding 21/January/2008"
 "from 1/January/2008 to 1/January/2009 excluding 21/January/2008"
 "from 1/January/2008 to 1/January/2009 excluding Thursday-Sunday"
 "from 1/January/2008 to 1/January/2009 excluding Thursday-Sunday
including June-July"
 "from 1/January/2008 to 1/January/2009 excluding Monday-Thursday
including 21/January/2008"
 "from 1/January/2008 to 1/January/2009 excluding Monday-Thursday
including 'Dr. Martin Luther King Day'"

And I could have been more clear on my parser API usage question. I am
envisioning something like this:

  public final List<Date> getValidDates(
      final String expression
      )
  {
      CommonTree expressionAst = execParser(
          expression
      );

      // Process the AST here, presumably...but how???
      // ...???

      return <list-of-valid-dates>;
  }

  private CommonTree execParser(
      final String expression
      )
  {
    //similar to gUnit's JUnit...but return the AST instead.
  }

On Jan 21, 2008 10:32 AM, Jan Nielsen <jan.sture.nielsen at gmail.com> wrote:
> Hello all,
>
> I am a completely non-useful body when it comes to ANTLR, and grammars
> in general. But I have a problem which smells like I could solve with
> the help of ANTLR, but I'm finding myself leaning away from it after
> reading The Definitive Guide...I think I would benefit greatly from
> Ter's forthcoming recipe book...I apologize for pedantic nature and
> the length of this email.
>
> I am hoping to get a little advice from you ANTLR experts of the ilk -
> "why are you doing that?!?" or "there is a better way...". So, let me
> describe what I need:
>
> I have a simple API need:
>
>   List<Date> getValidDates(
>     String expression
>     );
>
> where "expression" is a simple domain specific language of the form:
>
>   from <start-date> [to <end-date>]
>     [excluding <period> [, <period>]]
>     [including <period> [, <period>]]
>
> and <period> take a couple of forms: a specific date, a date range, a
> day of week, or a range of days of week, a month, or a range of
> months:
>
>   "21/January/2008"
>   "21/January/2008-28/January/2008"
>   "Monday-Thursday"
>   "June-July"
>   "Monday[3]/January"
>
> I would also like to support pre-defined dates, like "Dr. Martin
> Luther King Day" which is defined at the "third Monday of January".
>
> Here are a few examples of valid expressions:
>
>   "from 1-January-2008"
>   "from 1-January-2008 to 1-January-2009"
>   "from 1-January-2008 to 1-January-2009 excluding 21-January-2008"
>   "from 1-January-2008 to 1-January-2009 excluding 21-January-2008"
>   "from 1-January-2008 to 1-January-2009 excluding Thursday-Sunday"
>   "from 1-January-2008 to 1-January-2009 excluding Thursday-Sunday
> including June-July"
>   "from 1-January-2008 to 1-January-2009 excluding Monday-Thursday
> including 21-January-2008"
>   "from 1-January-2008 to 1-January-2009 excluding Monday-Thursday
> including 'Dr. Martin Luther King Day'"
>
> A "including" after an "excluding", i.e., to the right of, overrides
> the exclusion.
>
> My first stab at a grammar:
>
> grammar T;
>
> prog
>     : 'from' date ('to' date)?
>       ('including' period)? (',' period)*
>       ('excluding' period)? (',' period)*
>     ;
>
> date
>     : DAY_OF_MONTH '/' MONTH '/' YEAR
>     ;
>
> period
>     : day_of_month_period
>     | day_of_week_period
>     ;
>
> day_of_month_period
>     : DAY_OF_MONTH (MONTH)? (YEAR)?
>     ;
>
> day_of_week_period
>     : DAY_OF_WEEK ('[' OCCURRENCE ']')? (YEAR)?
>     ;
>
> OCCURRENCE
>     : '1'..'4'
>     ;
>
> YEAR
>     : '1'..'9' '0'..'9' '0'..'9' '0'..'9'
>     ;
>
> MONTH
>     : 'January'
>     | 'February'
>     | 'March'
>     | 'April'
>     | 'May'
>     | 'June'
>     | 'July'
>     | 'August'
>     | 'September'
>     | 'October'
>     | 'November'
>     | 'December'
>     ;
>
> DAY_OF_MONTH : '1'..'9' | '1'..'2' '0'..'9' | '30' | '31';
>
> DAY_OF_WEEK
>     : 'Monday'
>     | 'Tuesday'
>     | 'Wednesday'
>     | 'Thursday'
>     | 'Friday'
>     | 'Saturday'
>     | 'Sunday'
>     ;
>
> WS  :  (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;}
>     ;
>
> COMMENT
>     :   '/*' ( options {greedy=false;} : . )* '*/' {$channel=HIDDEN;}
>     ;
>
> LINE_COMMENT
>     : '//' ~('\n'|'\r')* '\r'? '\n' {$channel=HIDDEN;}
>     ;
>
> You, unlike me, may be able to see that this does not work. The
> parsing of date is unsuccessful. And the range construct is not
> represented either. I think these issues are solvable, it'll just take
> me a while. But once I have a parser for my expression, how do I
> actually use the parser to implement my API???
>
> To answer this question I used gUnit to generate a JUnit test (see
> below) for me from which I learned how I can hook-in the parser, but
> I'm a bit worried about the apparent parser API needs. Specifically,
> I'm worried about the vacuuming needed to get the results back to an
> API. Is there a better way to integrate the parser into my program?
> Once I have the parser integrated, my plan is to process each of these
> dates to spit out the date list...is that the right way to do it?
>
> Any help you can provide is greatly appreciated.
>
> -Jan
>
>
> gunit T;
>
> prog
>     :
>     << "from 12/February/2008" >> OK
>     << "from 12/February/2008 to 1/March/2009" >> OK
>
> which produces JUnit code of the form:
>
> import junit.framework.TestCase;
> import java.io.*;
> import java.lang.reflect.*;
> import org.antlr.runtime.*;
> import org.antlr.runtime.tree.*;
>
> public class TestT extends TestCase {
>         String stdout;
>         String stderr;
>
>         public void testProg1() throws Exception {
>                 // test input: " "from 12/February/2008" "
>                 Object retval = execParser("prog", "from 12/February/2008", false);
>                 Object actual = examineParserExecResult(27, retval);
>                 Object expecting = "OK";
>
>                 assertEquals("testing rule " + "prog", expecting, actual);
>         }
>
>         public void testProg2() throws Exception {
>                 // test input: " "from 12/February/2008 to 1/March/2009" "
>                 Object retval = execParser("prog",
>                                 "from 12/February/2008 to 1/March/2009", false);
>                 Object actual = examineParserExecResult(27, retval);
>                 Object expecting = "OK";
>
>                 assertEquals("testing rule prog", expecting, actual);
>         }
>
>         // Invoke target parser.rule
>         public Object execParser(String testRuleName, String testInput,
>                         boolean isFile) throws Exception {
>                 CharStream input;
>                 /** Set up ANTLR input stream based on input source, file or String */
>                 if (isFile == true) {
>                         input = new ANTLRFileStream(testInput);
>                 } else {
>                         input = new ANTLRStringStream(testInput);
>                 }
>                 try {
>                         TLexer lexer = new TLexer(input);
>                         CommonTokenStream tokens = new CommonTokenStream(lexer);
>                         TParser parser = new TParser(tokens);
>                         /** Use Reflection to get rule method from parser */
>                         Method ruleName = Class.forName("TParser").getMethod(testRuleName);
>
>                         /** Start of I/O Redirecting */
>                         PipedInputStream pipedIn = new PipedInputStream();
>                         PipedOutputStream pipedOut = new PipedOutputStream();
>                         PipedInputStream pipedErrIn = new PipedInputStream();
>                         PipedOutputStream pipedErrOut = new PipedOutputStream();
>                         try {
>                                 pipedOut.connect(pipedIn);
>                                 pipedErrOut.connect(pipedErrIn);
>                         } catch (IOException e) {
>                                 System.err.println("connection failed...");
>                                 System.exit(1);
>                         }
>                         PrintStream console = System.out;
>                         PrintStream consoleErr = System.err;
>                         PrintStream ps = new PrintStream(pipedOut);
>                         PrintStream ps2 = new PrintStream(pipedErrOut);
>                         System.setOut(ps);
>                         System.setErr(ps2);
>                         /** End of redirecting */
>
>                         /** Invoke grammar rule, and store if there is a return value */
>                         Object ruleReturn = ruleName.invoke(parser);
>                         String astString = null;
>                         /** If rule has return value, determine if it's an AST */
>                         if (ruleReturn != null) {
>                                 /** If return object is instanceof AST, get the toStringTree */
>                                 if (ruleReturn.toString().indexOf(testRuleName + "_return") > 0) {
>                                         try { // NullPointerException may happen here...
>                                                 Class _return = Class.forName("TParser" + "$"
>                                                                 + testRuleName + "_return");
>                                                 Method[] methods = _return.getDeclaredMethods();
>                                                 for (Method method : methods) {
>                                                         if (method.getName().equals("getTree")) {
>                                                                 Method returnName = _return
>                                                                                 .getMethod("getTree");
>                                                                 CommonTree tree = (CommonTree) returnName
>                                                                                 .invoke(ruleReturn);
>                                                                 astString = tree.toStringTree();
>                                                         }
>                                                 }
>                                         } catch (Exception e) {
>                                                 System.err.println(e);
>                                         }
>                                 }
>                         }
>
>                         org.antlr.gunit.gUnitExecuter.StreamVacuum stdoutVacuum = new
> org.antlr.gunit.gUnitExecuter.StreamVacuum(
>                                         pipedIn);
>                         org.antlr.gunit.gUnitExecuter.StreamVacuum stderrVacuum = new
> org.antlr.gunit.gUnitExecuter.StreamVacuum(
>                                         pipedErrIn);
>                         ps.close();
>                         ps2.close();
>                         System.setOut(console); // Reset standard output
>                         System.setErr(consoleErr); // Reset standard err out
>                         this.stdout = null;
>                         this.stderr = null;
>                         stdoutVacuum.start();
>                         stderrVacuum.start();
>                         stdoutVacuum.join();
>                         stderrVacuum.join();
>                         // retVal could be actual return object from rule, stderr or stdout
>                         if (stderrVacuum.toString().length() > 0) {
>                                 this.stderr = stderrVacuum.toString();
>                                 return this.stderr;
>                         }
>                         if (stdoutVacuum.toString().length() > 0) {
>                                 this.stdout = stdoutVacuum.toString();
>                         }
>                         if (astString != null) { // Return toStringTree of AST
>                                 return astString;
>                         }
>                         if (ruleReturn != null) {
>                                 return ruleReturn;
>                         }
>                         if (stderrVacuum.toString().length() == 0
>                                         && stdoutVacuum.toString().length() == 0) {
>                                 return null;
>                         }
>                 } catch (ClassNotFoundException e) {
>                         e.printStackTrace();
>                         System.exit(1);
>                 } catch (SecurityException e) {
>                         e.printStackTrace();
>                         System.exit(1);
>                 } catch (NoSuchMethodException e) {
>                         e.printStackTrace();
>                         System.exit(1);
>                 } catch (IllegalAccessException e) {
>                         e.printStackTrace();
>                         System.exit(1);
>                 } catch (InvocationTargetException e) {
>                         e.printStackTrace();
>                         System.exit(1);
>                 } catch (InterruptedException e) {
>                         e.printStackTrace();
>                         System.exit(1);
>                 } catch (Exception e) {
>                         e.printStackTrace();
>                         System.exit(1);
>                 }
>                 return stdout;
>         }
>
>         // Modify the return value if the expected token type is OK or FAIL
>         public Object examineParserExecResult(int tokenType, Object retVal) {
>                 if (tokenType == 27) { // expected Token: OK
>                         if (this.stderr == null) {
>                                 return "OK";
>                         } else {
>                                 return "FAIL";
>                         }
>                 } else if (tokenType == 28) { // expected Token: FAIL
>                         if (this.stderr != null) {
>                                 return "FAIL";
>                         } else {
>                                 return "OK";
>                         }
>                 } else { // return the same object for the other token types
>                         return retVal;
>                 }
>         }
>
> }
>


More information about the antlr-interest mailing list