Literal Dates

Sep 17, 2009 at 1:47 PM

I'm a newbie to Irony, but so far I am very impressed.  One thing that seems to be missing however are Date Literals. 

 

In languages such as Visual Basic (VBA, VB6, and VB.NET), you can enter a date as #12/25/2009# and this would be equivalent to new DateTime(2009, 12, 25).  Here is some information from the MSDN concerning Literal Dates in Visual Basic - http://msdn.microsoft.com/en-us/library/3eaydw6e%28VS.100%29.aspx.

If someone already has this functionality and doesn't mind sharing with the rest of the Irony community, I'm sure it would be greatly appreciated.

Thanks,

Kevin

Coordinator
Sep 17, 2009 at 6:19 PM

Yeah, you're right, it's not there. I will try to add such a terminal in the next code drop.

For now you can just create it yourself. Create DateLiteral class inherited from Terminal. Follow the same pattern as in RegexLiteral, it is very similar, it contains predefined start/end symbols (# instead of /); as the last step convert the string value to DateTime and put it into Token.Value.

Sep 24, 2009 at 5:37 PM

Here is my implementation of a DateTimeLiteral. I added the ability to have differing start and end symbols as the project I am working on uses a { to start and a } to end.  Feedback would be greatly appreciated.

  public class DateTimeLiteral : Terminal
  {
    public DateTimeStyles Styles = DateTimeStyles.None;
    public IFormatProvider Format = CultureInfo.InvariantCulture;
    public char StartSymbol = '#';
    public char EndSymbol = '#';

    private char[] _stopChars;

    public DateTimeLiteral(string name)
      : base(name, TokenCategory.Literal) { }

    public DateTimeLiteral(string name, char startSymbol, char endSymbol)
      : base(name, TokenCategory.Literal)
    {
      StartSymbol = startSymbol;
      EndSymbol = endSymbol;
    }

    public DateTimeLiteral(string name, char symbol)
      : this(name, symbol, symbol) { }

    public DateTimeLiteral(string name, char startSymbol, char endSymbol, IFormatProvider format, DateTimeStyles styles)
      : base(name, TokenCategory.Literal)
    {
      StartSymbol = startSymbol;
      EndSymbol = endSymbol;
      Format = format;
      Styles = styles;
    }

    public DateTimeLiteral(string name, char startSymbol, char endSymbol, DateTimeStyles styles)
      : this(name, startSymbol, endSymbol, CultureInfo.InvariantCulture, styles) { }

    public DateTimeLiteral(string name, char startSymbol, char endSymbol, IFormatProvider format)
      : this(name, startSymbol, endSymbol, format, DateTimeStyles.None) { }

    public override void Init(GrammarData grammarData)
    {
      base.Init(grammarData);
      _stopChars = new char[] { EndSymbol, '\r', '\n' };
    }

    public override IList<string> GetFirsts()
    {
      var result = new StringList();
      result.Add(StartSymbol.ToString());
      return result;
    }

    public override Token TryMatch(ParsingContext context, ISourceStream source)
    {
      //Find next position
      var newPos = source.Text.IndexOfAny(_stopChars, source.PreviewPosition + 1);

      if (newPos == -1)
        return source.CreateErrorToken(String.Format("A DateTime Literal must end with a '{0}'.", EndSymbol.ToString()));

      source.PreviewPosition = newPos;
      if (source.PreviewChar != EndSymbol)
        //we hit CR or LF before the end symbol, this is an error
        return source.CreateErrorToken(String.Format("A DateTime Literal must end with a '{0}'.", EndSymbol.ToString()));

      source.PreviewPosition++; //move after end symbol

      // we will need the length of the DateTime string
      var lngth = source.PreviewPosition - source.Location.Position - 2; //exclude end symbol

      var token = source.CreateToken(this);

      //we have token, now what's left is to set its Value field.
      string str = token.Text.Substring(1, lngth); //exclude start symbol
      DateTime value = DateTime.MinValue;

      if (!DateTime.TryParse(str, Format, Styles, out value))
      {
        return source.CreateErrorToken("Can not convert '{0}' into a DateTime value", str);
      }

      token.Value = value;
      return token;
    }

  }

Coordinator
Sep 24, 2009 at 6:43 PM

There may be one problem. The token.Text property should include all symbols "covered" by token, so start/end symbol should be included; otherwise scanner may be confused and get an error when it incorrectly advances the current position after you return the token to it. The token.Value on the other hand might contain whatever you want or need it to contain. Have a close look at RegExLiteral implementation.

Roman