//note: forward declaration of method \samp{readType()}, so as to start explanations
//note: about how to implement BNF clause \samp{\textit{world} ::= [\textit{class_declaration}]*},
declare function readType();

//note: do a loop while the end of file hasn't been reached,
//note: skipping blanks and C++ comments: \samp{skipEmptyCpp()} returns \samp{false}
//note: only if an error occurs while reading the stream or the file has completed,
while skipEmptyCpp() {
//note: waiting for token \samp{"class"} as an identifier (doesn't accept \samp{"class"} as
//note: the beginning of another identifier, such as \samp{"\textit{class}es"}). If not found,
//note: an error occurs. This token announces a class declaration.
	if !readIfEqualToIdentifier("class") error("'class' expected");
//note: a disadvantage of writing a procedure-driven reading/parsing: don't forget to
//note: skip explicitly blanks and comments by yourself,
	skipEmptyCpp();
//note: populates a local variable with an identifier token that represents the name of the class
	local sClassName = readIdentifier();
//note: if an identifier token hasn't been found (token is empty), an error is thrown,
	if !sClassName error("class name expected");
	skipEmptyCpp();
//note: if the file location points to \samp{":"}, announcing the inheritance, function
//note: \samp{readIfEqualTo(":")} returns \samp{true}, and the location moves after the
//note: matched expression. If it fails, the file location remains the same.
	if readIfEqualTo(":") {
		skipEmptyCpp();
		local sParentName = readIdentifier();
		if !sParentName error("parent name expected for class '" + sClassName + "'");
		skipEmptyCpp();
	}
//note: body of the class declaration expected
	if !readIfEqualTo("{") error("'{' expected");
	skipEmptyCpp();
//note: while inside the class body, reading of attribute and method members,
	while !readIfEqualTo("}") {
		skipEmptyCpp();
//note: we don't conform exactly to the BNF: beginning of method and attribute declaration
//note: is factorized,
		readType();
		skipEmptyCpp();
//note: name of the attribute or method member,
		local sMemberName = readIdentifier();
		if !sMemberName error("attribute or method name expected");
		skipEmptyCpp();
//note: not any more ambiguity : it starts by a parenthesis when the members is a method,
		if readIfEqualTo("(") {
			skipEmptyCpp();
			if !readIfEqualTo(")") {
//note: the method expects at least one parameter,
				do {
					skipEmptyCpp();
//note: we keep the current file position, to be able to come back if the next token
//note: isn't an access mode (\samp{"in"}, \samp{"out"} or \samp{"inout"}),
					local iPosition = getInputLocation();
					local sMode = readIdentifier();
					if !sMode error("parameter type or mode expected");
					if (sMode != "in") && (sMode != "out") && (sMode != "inout") {
//note: we were reading a basic type, instead of a parameter access mode: we come
//note: back to the beginning of this token and the mode is set as empty (no mode).
//note: Of course, it is possible not to waste time like this, and to optimize
//note: function \samp{readType()} by passing the token as a parameter. But here is
//note: the occasion of discovering how to handle the file position.
						setInputLocation(iPosition);
						set sMode = "";
					}
					skipEmptyCpp();
//note: type of the current parameter is expected,
					readType();
					skipEmptyCpp();
//note: name of the current parameter is expected,
					local sParameterName = readIdentifier();
					if !sParameterName error("parameter name expected");
					skipEmptyCpp();
//note: parameters are separated by commas,
				} while readIfEqualTo(",");
				if !readIfEqualTo(")") error("')' expected");
			}
			skipEmptyCpp();
		}
//note: both attributes and methods must finish with a semi colon,
		if !readIfEqualTo(";") {
//note: function \samp{readChar()} reads just one character, or returns an empty string
//note: if the end of file has been reached,
			error("';' expected to close an attribute, instead of '" + readChar() + "'");
		}
		skipEmptyCpp();
	}
}
//note: once the read of file has completed, a message of success is written,
traceLine("the file has been read successfully");

//note: user-defined function ; may return a value or not. The declaration always starts
//note: with keyword \samp{function}, even if it announces a procedure (no return value).
//note: Reading a type is called at several points of the grammar, so the code is
//note: factorized in the procedure \samp{readType()}. It doesn't return any value about
//note: success or failure, because an error is thrown in case of syntax mismatch.
function readType() {
	local sType = readIdentifier();
	if !sType error("type modifier or name expected, instead of '" + readChar() + "'");
//note: does the keyword is a modifier? If not \samp{sType} contains a basic type or a class name
	if sType == "aggregate" {
		skipEmptyCpp();
//note: reads the name of the aggregated class
		sType = readIdentifier();
		if !sType error("aggregated class name expected");
	}
	skipEmptyCpp();
//note: perhaps that the type is an array, represented by \samp{\textbf{[]}},
	if readIfEqualTo("[") {
		skipEmptyCpp();
		if !readIfEqualTo("]") error("']' expected to close an array declaration");
	}
}
