//------------------------------------------------------------------------
// Extended-BNF script for parsing a tiny modeling language.
//
// Called by "tinyDSL_leader.cws".
//
// Logical structure of the parse tree:
//   |
//   +- classes: array of classes, indexed by the class name,
//      |
//      +- name: the class name,
//      |
//      +- attributes: array of attributes, indexed by their name
//         |
//         +- name: attribute's name
//         |
//         +- aggregate (optional): 'true' if the attribute points to
//         |                        object(s) that belong(s) to the class
//         +- type: type specifier
//            |
//            +- name: "double", "string" or class name
//            |
//            +- isArray (optional): 'true' if the attribute is an array
//------------------------------------------------------------------------

tinyDSL	::= #continue #ignore(C++) [class_definition]* #empty;

class_definition	::=
		#readIdentifier:"class" #continue #readIdentifier:sClassName
		=> if this.classes.findElement(sClassName) error("class '" + sClassName + "' is already defined");
		=> insert this.classes[sClassName].name = sClassName;
		[':' #continue #readIdentifier:this.classes[sClassName].superClass]?
		'{'
		[attribute_definition(this.classes[sClassName])]*
		'}'
		;

attribute_definition(theClass : node)	::=
		#readIdentifier:sAttributeName
		=> if theClass.attributes.findElement(sAttributeName) error("attribute '" + sAttributeName + "' already exists in class '" + theClass.name + "'");
		#continue
		=> insert theClass.attributes[sAttributeName].name = sAttributeName;
		=> localref theAttribute = theClass.attributes[sAttributeName];
		':'
		[
			#readIdentifier:"aggregate"
			=> insert theAttribute.aggregate = true;
		]?
		type_specifier(theAttribute.type)
		=> if theAttribute.aggregate && !theAttribute.type.isObject set theAttribute.aggregate = false;
		';'
		;

type_specifier(theType : node)	::=
		simple_type(theType)
		['[' #continue ']' => insert theType.isArray = true;]?
		;

simple_type(theType : node)	::=
		#readIdentifier:{"double", "string"}:theType.name
	|
		#readIdentifier:sClassName
		=> {
			if !this.classes.findElement(sClassName) error("unknown simple type '" + sClassName + "'");
			insert theType.name = sClassName;
			insert theType.isObject = true;
		}
		;
