Having slept on it, I think the right answer is to change my approach. In the above definition we have
Field:
type=[FieldType] name=ID ";";
This defines a rule called "Field" which is made up of two parts; the "type" and the "name". It is the type part that is presenting the issue. The square brackets denote we are expecting an instance of FieldType
, which is:
FieldType: Class | 'String' | 'Number';
Now, it's clear to see that you can have an instance of class, but semantically there's no way to have an instance of the 'String' or 'Number' Literals.
I believe this to be why my DSL above won't let me declare the primitives. The String/Number "types" simply aren't elements that you can have an instance of.
Thinking further, there are some very important distinctions between the definitions of fields that are primitive vs. those which are instances of a class. For example, you can only invoke methods on instances of a class (in my case I am treating String as a true primitive, so no methods).
Thus, it's probably important to have two different type of declarations, one for PrimitiveField
and one for ObjectField
. A field can be either of these:
Model:
(classes+=Class)*
(fields+=Field)*;
PrimitiveType: 'String' | 'Number' | 'Boolean';
Field:
PrimitiveField | ObjectField
;
PrimitiveField:
type=PrimitiveType name=ID ";"
;
ObjectField:
type=[Class] name=ID ";";
Class:
"class" name=ID
"{"
(fields+=Field)*
(methods+=Method)*
"}";