Factor Three
Factor Three

Reputation: 2274

Number/String syntax errors in Xtext

I am attempting to create a syntax that contains numbers and strings, as in the following:

Date: 2 Mar 2013

I have created a set of rules that more- or- less accommodate this:

Date:
     'Date': DAY Month YEAR
;

terminal DAY:
     ('1'..'9') | (('1'..'3')('0'..'9'))
;

Month:
     name = ('Jan'|'Feb'|'Mar'|'Apr)
;

terminal YEAR
     ('0'..'2)('0'..'9')('0'..'9')('0'..'9')
;

This set of rules seems to have a number of problems:

  1. The Date rule, as written, seems to generate an error: "Cannot change type twice within a rule". I have no idea what that means, except that Xtext will apparently not allow numbers and choice strings in the same rule.

  2. If I remove the Month rule from Date, Xtext compiles, but the syntax doesn't seem to work right. The DAY rule is supposed to provide a choice between a single digit from 1 to 9 and a two- digit number, but for some reason it only accepts a two- digit number. So while I can enter a line like:

Date: 12 2013

is accepted but a date like:

Date: 2 2013

is not.

Have I found a bug in the rules that breaks numbers in terminal rules? Or is there something missing that causes the ignoring of the '|' in my numbers? Also: what on Earth does the "Cannot change type twice within a rule" error mean and how can I fix it???

Someone please advise.

Upvotes: 0

Views: 1125

Answers (1)

Boris Brodski
Boris Brodski

Reputation: 8695

Here you have a design problem. Generally, you have to use custom terminals as little as possible. Custom terminals have a global impact on your grammar, makes the syntax less flexible and error recovery inefficient.

I understand your intentions: you want to constraint your DSL to prevent insensible data to be parsed. This is a good idea, but custom terminals are not designed for this kind of task. The rules of thumb are:

  • Use as much standard terminals as possible
  • Make your lexer parse almost any input
  • Make your parser detect syntax errors (like missing ']'), but not domain constraints, like (1 <= Day <= 31).
  • Add validation to check domain constraints and provide user frendly error and warning messages
  • Add quick fixes
  • Adjust content assistant to your domain constraints

In your scenario I would suggest the following simple grammar:

Date:
    'Date': day=INT month=Month year=INT
;

enum Month:
    JANUARY='Jan'
|   FEBRUARY='Feb'
|   MARCH='Mar'
|   APRIL='Apr'
;

Than I would implement a custom validator to check, if the day and the year are within the allowed ranges. Here is a documentation, how to do it:

Upvotes: 1

Related Questions