function getType(orgScope : node, sType : value, newScope : reference) {
	if orgScope.classes.findElement(sType) {
		ref newScope = orgScope.classes[sType];
		return true;
	}
	if orgScope.structs.findElement(sType) {
		ref newScope = orgScope.structs[sType];
		return true;
	}
	if orgScope.typedefs.findElement(sType) {
		ref newScope = orgScope.typedefs[sType];
		return true;
	}
	if orgScope.enums.findElement(sType) {
		ref newScope = orgScope.enums[sType];
		return true;
	}
	if orgScope.unions.findElement(sType) {
		ref newScope = orgScope.unions[sType];
		return true;
	}
	if orgScope.namespaces.findElement(sType) {
		ref newScope = orgScope.namespaces[sType];
		return true;
	}
	if orgScope.parent.existVariable() return getType(orgScope.parent, sType, newScope);
	return false;
}

function getNestedType(scope : node, sType : value, theType : reference) {
	if scope.classes.findElement(sType) {
		ref theType = scope.classes[sType];
		return true;
	}
	if scope.structs.findElement(sType) {
		ref theType = scope.structs[sType];
		return true;
	}
	if scope.typedefs.findElement(sType) {
		ref theType = scope.typedefs[sType];
		return true;
	}
	if scope.enums.findElement(sType) {
		ref theType = scope.enums[sType];
		return true;
	}
	if scope.unions.findElement(sType) {
		ref theType = scope.unions[sType];
		return true;
	}
	if scope.namespaces.findElement(sType) {
		ref theType = scope.namespaces[sType];
		return true;
	}
	return false;
}

function getEnumValueType(scope : node, sName : value, theType : reference) {
	foreach i in scope.enums {
		if i.values.findElement(sName) {
			ref theType = i;
			return true;
		}
	}
	if scope.parent.existVariable() return getEnumValueType(scope.parent, sName, theType);
	return false;
}

function getThisClass(scope : node, theClass : reference) {
	if scope == "class" {
		ref theClass = scope;
		return true;
	}
	if !scope.parent.existVariable() return false;
	return getThisClass(scope.parent, theClass);
}

function getVariableType(scope : node, sName : value, theType : reference) {
	if scope.attributes.findElement(sName) {
		ref theType = scope.attributes[sName].type;
		return true;
	}
	if scope.variables.findElement(sName) {
		ref theType = scope.variables[sName].type;
		return true;
	}
	foreach i in scope.params {
		if i.name != sName continue;
		ref theType = i.type;
		return true;
	}
	if scope.parent.existVariable() return getVariableType(scope.parent, sName, theType);
	return false;
}

function getSTLVariableType(scope : node, sName : value, theType : reference) {
	switch(sName) {
		case "first":
			ref theType = theType.templates#front;
			break;
		case "second":
			ref theType = theType.templates#back;
			break;
	}
	return true;
}

function matchSignatures(theMember : node, callerTypes : node, theFunction : reference) {
	if $theMember.size() == 1$ {
		ref theFunction = theMember#front;
		return true;
	}
	foreach i in theMember {
		if $i.params.size() < callerTypes.size()$ continue;
		if $i.params.size() > callerTypes.size()$ {
			if !i.params#[callerTypes.size()].default continue;
		}
		local iIndex = 0;
		foreach j in callerTypes {
			if !equalTrees(j, i.params#[iIndex].type) break;
			increment(iIndex);
		}
		if iIndex == callerTypes.size() {
			ref theFunction = i;
			return true;
		}
	}
	return false;
}

function getMethod(scope : node, sName : value, callerTypes : node, theFunction : reference) {
	local theMember;
	if scope.methods.findElement(sName) ref theMember = scope.methods[sName];
	else if scope.functions.findElement(sName) ref theMember = scope.functions[sName];
	else {
		if scope.inheritance.indirect.existVariable() && getMethod(scope.inheritance.indirect, sName, callerTypes, theFunction) return true;
		return false;
	}
	return matchSignatures(theMember, callerTypes, theFunction);
}

function getFunction(scope : node, sName : value, callerTypes : node, theFunction : reference) {
	local theMember;
	if scope.methods.findElement(sName) ref theMember = scope.methods[sName];
	else if scope.functions.findElement(sName) ref theMember = scope.functions[sName];
	else {
		if scope.inheritance.indirect.existVariable() && getMethod(scope.inheritance.indirect, sName, callerTypes, theFunction) return true;
		if scope.parent.existVariable() return getFunction(scope.parent, sName, callerTypes, theFunction);
		return false;
	}
	if !matchSignatures(theMember, callerTypes, theFunction) error("unable to find function " + sName + " in the scope");
	return true;
}

function getSTLFunction(sClass : node, sFunction : value, callerTypes : node, theFunction : reference) {
	switch(sFunction) {
		case "clear":
			if !project.stl_classes[sClass].functions[sFunction].existVariable() {
				insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
				insert project.stl_classes[sClass].functions[sFunction].return_type = "base";
				insert project.stl_classes[sClass].functions[sFunction].return_type.name = "void";
			}
			break;
		case "size":
			if !project.stl_classes[sClass].functions[sFunction].existVariable() {
				insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
				insert project.stl_classes[sClass].functions[sFunction].return_type = "base";
				insert project.stl_classes[sClass].functions[sFunction].return_type.name = "int";
				insert project.stl_classes[sClass].functions[sFunction].return_type.is_unsigned = true;
			}
			break;
		case "empty":
			if !project.stl_classes[sClass].functions[sFunction].existVariable() {
				insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
				insert project.stl_classes[sClass].functions[sFunction].return_type = "base";
				insert project.stl_classes[sClass].functions[sFunction].return_type.name = "bool";
			}
			break;
		case "erase":
			if !project.stl_classes[sClass].functions[sFunction].existVariable() {
				insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
				insert project.stl_classes[sClass].functions[sFunction].return_type = "stl";
				insert project.stl_classes[sClass].functions[sFunction].return_type.name = sClass + "::const_iterator";
				pushItem project.stl_classes[sClass].functions[sFunction].params;
				insert project.stl_classes[sClass].functions[sFunction].params#front.type = "stl";
				insert project.stl_classes[sClass].functions[sFunction].params#front.type.name = sClass + "::const_iterator";
			}
			break;
		case "find":
			if !project.stl_classes[sClass].functions[sFunction].existVariable() {
				insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
				insert project.stl_classes[sClass].functions[sFunction].return_type = "stl";
				insert project.stl_classes[sClass].functions[sFunction].return_type.name = sClass + "::const_iterator";
				pushItem project.stl_classes[sClass].functions[sFunction].params;
				insert project.stl_classes[sClass].functions[sFunction].params#front.type = "stl";
				insert project.stl_classes[sClass].functions[sFunction].params#front.type.name = "string";
			}
			break;
		case "begin":
		case "end":
			if !project.stl_classes[sClass].functions[sFunction].existVariable() {
				insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
				insert project.stl_classes[sClass].functions[sFunction].return_type = "stl";
				insert project.stl_classes[sClass].functions[sFunction].return_type.name = sClass + "::iterator";
			}
			break;
		default:
			switch(sClass) {
				case "string":
					switch(sFunction) {
						case "c_str":
							if !project.stl_classes[sClass].functions[sFunction].existVariable() {
								insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
								insert project.stl_classes[sClass].functions[sFunction].return_type = "base";
								insert project.stl_classes[sClass].functions[sFunction].return_type.name = "char";
								insert project.stl_classes[sClass].functions[sFunction].return_type.is_const = true;
								insert project.stl_classes[sClass].functions[sFunction].return_type.pointers = 1;
							}
							break;
						case "find_last_of":
							if !project.stl_classes[sClass].functions[sFunction].existVariable() {
								insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
								insert project.stl_classes[sClass].functions[sFunction].return_type = "base";
								insert project.stl_classes[sClass].functions[sFunction].return_type.name = "void";
								pushItem project.stl_classes[sClass].functions[sFunction].params;
								insert project.stl_classes[sClass].functions[sFunction].params#front.type = "base";
								insert project.stl_classes[sClass].functions[sFunction].params#front.type.name = "char";
								insert project.stl_classes[sClass].functions[sFunction].params#front.type.pointers = 1;
								insert project.stl_classes[sClass].functions[sFunction].params#front.type.is_const = true;
							}
							break;
						case "substr":
							if !project.stl_classes[sClass].functions[sFunction].existVariable() {
								insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
								insert project.stl_classes[sClass].functions[sFunction].return_type = "stl";
								insert project.stl_classes[sClass].functions[sFunction].return_type.name = "string";
								pushItem project.stl_classes[sClass].functions[sFunction].params;
								insert project.stl_classes[sClass].functions[sFunction].params#front.type = "base";
								insert project.stl_classes[sClass].functions[sFunction].params#front.type.name = "size_t";
							}
							break;
						case "resize":
							if !project.stl_classes[sClass].functions[sFunction].existVariable() {
								insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
								insert project.stl_classes[sClass].functions[sFunction].return_type = "base";
								insert project.stl_classes[sClass].functions[sFunction].return_type.name = "void";
								pushItem project.stl_classes[sClass].functions[sFunction].params;
								insert project.stl_classes[sClass].functions[sFunction].params#front.type = "base";
								insert project.stl_classes[sClass].functions[sFunction].params#front.type.name = "size_t";
							}
							break;
					}
					break;
				case "ifstream":
					switch(sFunction) {
						case "close":
							if !project.stl_classes[sClass].functions[sFunction].existVariable() {
								insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
								insert project.stl_classes[sClass].functions[sFunction].return_type = "base";
								insert project.stl_classes[sClass].functions[sFunction].return_type.name = "void";
							}
							break;
						case "fail":
							if !project.stl_classes[sClass].functions[sFunction].existVariable() {
								insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
								insert project.stl_classes[sClass].functions[sFunction].return_type = "base";
								insert project.stl_classes[sClass].functions[sFunction].return_type.name = "bool";
							}
							break;
					}
					break;
				case "set":
					switch(sFunction) {
						case "insert":
							if !project.stl_classes[sClass].functions[sFunction].existVariable() {
								insert project.stl_classes[sClass].functions[sFunction].name = sFunction;
								insert project.stl_classes[sClass].functions[sFunction].return_type = "base";
								insert project.stl_classes[sClass].functions[sFunction].return_type.name = "void";
								pushItem project.stl_classes[sClass].functions[sFunction].params;
								insert project.stl_classes[sClass].functions[sFunction].params#front.type = "stl";
								insert project.stl_classes[sClass].functions[sFunction].params#front.type.name = "string";
							}
							break;
					}
					break;
			}
	}
	ref theFunction = project.stl_classes[sClass].functions[sFunction];
	return true;
}


function getArgumentTypes(arguments : node, callerTypes : node) {
	foreach i in arguments {
		pushItem callerTypes;
		setall callerTypes#back = i.type;
	}
}


function serializeType(theType : node) {
	local sText;
	if theType.is_register sText = "register ";
	if theType.is_mutable sText += "mutable ";
	if theType.is_const sText += "const ";
	if theType.is_unsigned sText += "unsigned ";
	if theType == "base" sText += theType.name;
	else if theType == "stl" {
		sText += theType.name;
		if !theType.templates.empty() {
			sText += '<';
			foreach i in theType.templates {
				if !i.first() sText += ',';
				sText += serializeType(i);
			}
			sText += '>';
		}
	} else if theType == "indirect" sText += theType.indirect.name;
	else {
		traceObject(theType);
		error("unknown type");
	}
	if theType.pointers sText += repeatString('*', theType.pointers);
	if theType.is_reference sText += '&';
	return sText;
}

function serializeSignature(sName : value, params : node, bConst : value) {
	local sSignature = sName;
	foreach i in params {
		if i.type.existVariable() sSignature += '.' + serializeType(i.type);
		else sSignature += '.';
	}
	if bConst sSignature += ".const";
	return sSignature;
}


CWCPP ::=
	#ignore(C++)
	#continue
	=> insert this.parsed_files[getShortFilename(getInputFilename())];
	declarations(this)
	#empty
	;

source_file ::=
	'#' "ifdef" "WIN32"
	['#' "pragma" [#!ignore ->'\n']]+
	'#' "endif"
	declarations(this)
	;

declarations(scope : node) ::= [declaration(scope)]*;
declaration(scope : node) ::=
		'#' #readIdentifier:sPreprocessor
		preprocessor<sPreprocessor>(scope)
	|
		#readIdentifier:sClass
		"::"
		=> local theClass;
		#check(getType(scope, sClass, theClass))
		[
			#readIdentifier:sClass
			"::" #continue
			#check(getNestedType(theClass, sClass, theClass))
		]*
		=> local bDestructor;
		['~':bDestructor]?
		#readText(sClass)
		!!'(' #continue
		// a constructor or a destructor
		=> local theModifiers;
		=> local theType;
		method_declaration(theClass, sClass, theModifiers, false, theType)
	|
		function_declaration(scope)
	|
		#readIdentifier:sKeyword
		keyword<sKeyword>(scope)
	;

preprocessor<"ifndef">(scope : node) ::=
	#continue
	#readIdentifier:sKey
	declarations(scope)
	'#' "endif"
	;

preprocessor<"ifdef">(scope : node) ::=
	#continue
	#readIdentifier:sKey
	declarations(scope)
	[
		'#' #readIdentifier:"else"
		#continue
		declarations(scope)
	]?
	'#' "endif"
	;

preprocessor<"endif">(scope : node) ::= #check(false);
preprocessor<"else">(scope : node) ::= #check(false);

preprocessor<"define">(scope : node) ::=
	#continue
	#readIdentifier
	[' ' | '\t']*
	#!ignore
	->['\\' ['\r']? '\n' #nextStep #check(false) | '\n']
	;

preprocessor<"undef">(scope : node) ::=
	#continue
	#readIdentifier:sKey
	;

preprocessor<"include">(scope : node) ::=
	#continue
	[
			#readCString:sIncludeFile
			#continue
			[
					#check(!this.parsed_files.findElement(sIncludeFile))
					#continue
					#parsedFile(sIncludeFile)
					=> insert this.parsed_files[sIncludeFile];
					declarations(this)
					#empty
			]?
		|
			'<' #continue ->'>'
	]
	;

preprocessor<"pragma">(scope : node) ::=
	#continue
	#!ignore
	->['\\' ['\r']? '\n' #nextStep #check(false) | '\n']
	;

keyword<"namespace">(scope : node) ::=
	#continue
	#readIdentifier:sName
	=> insert scope.namespaces[sName] = "namespace";
	=> insert scope.namespaces[sName].name = sName;
	=> ref scope.namespaces[sName].parent = scope;
	'{'
	declarations(scope.namespaces[sName])
	'}'
	;

keyword<"typedef">(scope : node) ::=
	#continue
	=> local theType;
	type(scope, theType)
	[
			#readIdentifier:sName
			=> insert scope.typedefs[sName] = "typedef";
			=> insert scope.typedefs[sName].name = sName;
			=> setall scope.typedefs[sName].type = theType;
		|
			'(' #continue '*' 
			#readIdentifier:sName
			')'
			'('
			[
				parameter_decl(scope, scope.typedefs[sName].params)
				[
					',' #continue
					parameter_decl(scope, scope.typedefs[sName].params)
				]*
			]?
			')'
			=> insert scope.typedefs[sName] = "typedef";
			=> insert scope.typedefs[sName].name = sName;
			=> insert scope.typedefs[sName].is_function = true;
			=> setall scope.typedefs[sName].return_type = theType;
	]
	';'
	;

keyword<"class">(scope : node) ::=
	#continue
	#readIdentifier:sName
	=> insert scope.classes[sName] = "class";
	=> insert scope.classes[sName].name = sName;
	=> ref scope.classes[sName].parent = scope;
	[
			';'
		|
			#continue
			[
				':' #continue
				[
					#readIdentifier:{"public", "protected", "private"}
				]?
				class_type(scope, scope.classes[sName].inheritance)
			]?
			'{'
			member_declarations(scope.classes[sName], "private")
			'}'
			';'
			=> insert scope.classes[sName].is_defined = true;
	]
	;

keyword<"struct">(scope : node) ::=
	#continue
	#readIdentifier:sName
	=> insert scope.structs[sName] = "struct";
	=> insert scope.structs[sName].name = sName;
	=> ref scope.structs[sName].parent = scope;
	[
			';'
		|
			#continue
			'{'
			member_declarations(scope.structs[sName], "public")
			'}'
			';'
			=> insert scope.structs[sName].is_defined = true;
	]
	;

keyword<"union">(scope : node) ::=
	#continue
	#readIdentifier:sName
	=> insert scope.unions[sName] = "union";
	=> insert scope.unions[sName].name = sName;
	=> ref scope.unions[sName].parent = scope;
	[
			';'
		|
			#continue
			'{'
			member_declarations(scope.unions[sName], "public")
			'}'
			';'
			=> insert scope.unions[sName].is_defined = true;
	]
	;

keyword<"enum">(scope : node) ::=
	#continue
	#readIdentifier:sName
	=> insert scope.enums[sName] = "enum";
	=> insert scope.enums[sName].name = sName;
	=> ref scope.enums[sName].parent = scope;
	[
			';'
		|
			#continue
			'{'
			enum_value(scope.enums[sName])
			[
				',' #continue
				enum_value(scope.enums[sName])
			]*
			'}'
			';'
	]
	;

keyword<"extern">(scope : node) ::=
	#continue
	#readCString:"C"
	[
			'{' #continue
			declarations(scope)
			'}'
		|
			#continue
			declarations(scope)
	]
	;

enum_value(scope : node) ::=
	#readIdentifier:sName
	=> insert scope.values[sName];
	[
		'=' #continue
		#readInteger:scope.values[sName]
	]?
	;

member_declarations(scope : node, sAccess : value) ::= [member_declaration(scope, sAccess)]*;
member_declaration(scope : node, sAccess : node) ::=
		#readIdentifier:{"public", "protected", "private"}:sAccess
		#continue
		':'
	|
		#readIdentifier:"friend" #continue
		#readIdentifier:"class"
		#readIdentifier:sName
		';'
		=> insert scope.friends[sName];
	|
		'#' #continue #readIdentifier:sPreprocessor
		preprocessor<sPreprocessor>(scope)
	|
		#readIdentifier:{"class", "typedef", "struct", "union", "enum"}:sKeyword
		keyword<sKeyword>(scope)
	|
		=> local theModifiers;
		[member_modifier(theModifiers)]*
		=> local theType;
		[
				'~' #continue #readIdentifier:sMember
				// a destructor
				method_declaration(scope, '~' + sMember, theModifiers, sAccess, theType)
			|
				#readText(scope.name)
				!!'(' #continue
				// a constructor
				method_declaration(scope, scope.name, theModifiers, sAccess, theType)
			|
				type(scope, theType)
				#continue
				#readIdentifier:sMember
				[
					#check(sMember == "operator")
					#continue
					operator:+sMember
				]?
				[
						!!'(' #continue
						// a method
						method_declaration(scope, sMember, theModifiers, sAccess, theType)
					|
						#continue
						// an attribute
						attribute_declaration(scope, sMember, theModifiers, sAccess, theType)
				]
		]
	;

method_declaration(scope : node, sMember : value, theModifiers : node, sAccess : value, theType : node) ::=
	#continue '('
	=> local theParams;
	[
		parameter_decl(scope, theParams)
		[
			',' #continue
			parameter_decl(scope, theParams)
		]*
	]?
	')'
	=> local bConst;
	[
			#readIdentifier:"const":bConst
		|
			#readIdentifier:"throw" #continue
			'(' ->')'
	]*
	=> local sSignature = serializeSignature(sMember, theParams, bConst);
	=> insert scope.methods[sMember][sSignature].name = sMember;
	=> localref theMethod = scope.methods[sMember][sSignature];
	=> ref theMethod.parent = scope;
	=>	if theMethod.params.empty() setall theMethod.params = theParams;
		else {
			local iIndex = 0;
			while $iIndex < theParams.size()$ {
				if theParams#[iIndex].name != theMethod.params#[iIndex].name {
					error("Please synchronize the parameter names of the method " + scope.name + "::" + sSignature + ": " + theParams#[iIndex].name + " != " + theMethod.params#[iIndex].name);
				}
				increment(iIndex);
			}
		}
	=> if !theModifiers.empty() setall theMethod.modifiers = theModifiers;
	=> if sAccess insert theMethod.access = sAccess;
	=> setall theMethod.return_type = theType;
	=> if bConst insert theMethod.is_const = true;
	[
			':' #continue
			var_initializer(scope, theMethod)
			[
				',' #continue
				var_initializer(scope, theMethod)
			]*
			'{'
			function_body(scope, theMethod)
			'}'
		|
			'{' #continue
			function_body(scope, theMethod)
			'}'
		|
			'=' #continue '0' ';'
			=> insert theMethod.is_abstract = true;
		|
			#continue
			';'
	]
	;

var_initializer(scope : node, theMethod : node) ::=
	#readIdentifier:sVariable #continue
	'('
	[
			#check(sVariable == scope.inheritance.indirect.name)
			#continue
			=> pushItem theMethod.initializers.super;
			expression(theMethod, theMethod.initializers.super#back)
			[
				',' #continue
				=> pushItem theMethod.initializers.super;
				expression(theMethod, theMethod.initializers.super#back)
			]*
		|
			#continue
			=> pushItem theMethod.initializers[sVariable];
			expression(theMethod, theMethod.initializers[sVariable]#back)
			[
				',' #continue
				=> pushItem theMethod.initializers[sVariable];
				expression(theMethod, theMethod.initializers[sVariable]#back)
			]*
	]
	')'
	;


parameter_decl(scope : node, theParams : node) ::=
		"..."
		=> pushItem theParams = "...";
	|
		!')' #continue
		=> pushItem theParams;
		type(scope, theParams#back.type)
		[
			#readIdentifier:theParams#back.name
			[
				'[' #continue
				#readInteger:theParams#back.type.size ']'
			]?
			[
				'=' #continue
				literal_expression(scope, theParams#back.default)
			]?
		]?
	;

attribute_declaration(scope : node, sMember : value, theModifiers : node, sAccess : value, theType : node) ::=
	#continue
	=> insert scope.attributes[sMember].name = sMember;
	=> setall scope.attributes[sMember].type = theType;
	=> if !theModifiers.empty() setall scope.attributes[sMember].modifiers = theModifiers;
	=> if sAccess insert scope.attributes[sMember].access = sAccess;
	[
		'[' #continue
		[
				#readInteger:scope.attributes[sMember].type.size
			|
				=> insert scope.attributes[sMember].type.size = "[]";
		]
		']'
	]?
	';'
	;

member_modifier(theModifiers : node) ::=
	#readIdentifier:{"static", "inline", "virtual"}:sModifier
	=> insert theModifiers[sModifier];
	;

function_declaration(scope : node) ::=
	=> local bStatic;
	[
		#readIdentifier:"static"
		=> bStatic = true;
	]?
	=> local theType;
	type(scope, theType)
	#readIdentifier:sName
	[
			'(' #continue
			=> local theParams;
			[
				parameter_decl(scope, theParams)
				[
					',' #continue
					parameter_decl(scope, theParams)
				]*
			]?
			')'
			[
				#readIdentifier:"throw" #continue
				'(' ->')'
			]*
			=> local sSignature = serializeSignature(sName, theParams, false);
			=> insert scope.functions[sName][sSignature].name = sName;
			=> localref theFunction = scope.functions[sName][sSignature];
			=> ref theFunction.parent = scope;
			=> setall theFunction.params = theParams;
			=> setall theFunction.return_type = theType;
			[
					'{' #continue
					function_body(scope, theFunction)
					'}'
				|
					#continue
					';'
			]
		|
			"::"
			#readIdentifier:sMember
			[
				#check(sMember == "operator")
				#continue
				operator:+sMember
			]?
			#continue
			=> local theClass;
			#check(getType(scope, sName, theClass))
			=> local theModifiers;
			[
					!!'(' #continue
					method_declaration(theClass, sMember, theModifiers, false, theType)
				|
					#continue
					#check(theClass.attributes.findElement(sMember))
					=> local iSize;
					[
						'[' #continue
						[
								#readInteger:iSize
							|
								=> iSize = true;
						]
						']'
					]?
					[
							'=' #continue
							[
									#check(iSize)
									#continue
									=> insert theClass.attributes[sMember].type.size = iSize;
									'{'
									=> pushItem theClass.attributes[sMember].default;
									expression(scope, theClass.attributes[sMember].default#back)
									[
										',' #continue
										=> pushItem theClass.attributes[sMember].default;
										expression(scope, theClass.attributes[sMember].default)
									]*
									'}'
								|
									expression(scope, theClass.attributes[sMember].default)
							]
					]?
					';'
			]
		|
			';'
			=> insert scope.attributes[sName].name = sName;
			=> setall scope.attributes[sName].type = theType;
			=> if bStatic insert scope.attributes[sName].modifiers["static"];
		|
			[
					'=' #continue
					expression(scope, scope.attributes[sName].default)
					';'
				|
					'[' #readInteger:iSize ']'
					'=' #continue
					'{'
					=> pushItem scope.attributes[sName].default;
					expression(scope, scope.attributes[sName].default#back)
					[
						',' #continue
						=> pushItem scope.attributes[sName].default;
						expression(scope, scope.attributes[sName].default#back)
					]*
					'}'
					';'
					=> insert theType.size = iSize;
			]
			=> insert scope.attributes[sName].name = sName;
			=> setall scope.attributes[sName].type = theType;
			=> if bStatic insert scope.attributes[sName].modifiers["static"];
	]
	;

instructions(scope : node) ::= [instruction(scope)]*;
instruction(scope : node) ::=
		'{' #continue
		=> pushItem scope = "{}";
		=> ref scope#back.parent = scope;
		instructions(scope#back)
		'}'
	|
		["++" | "--"]:sOperator #continue
		=> pushItem scope = sOperator;
		literal_expression(scope, scope#back.variable)
		';'
	|
		=> local sId;
		=> local bStatic;
		!![#readIdentifier:sId]
		[
				#check(sId == "CodeWorker")
				#readIdentifier
				#continue "::"
				!![#readIdentifier:sId]
			|
				#check(sId == "static")
				#readIdentifier
				#continue
				!![#readIdentifier:sId]
		]?
		#check(sId != "case" && sId != "default")
		#continue
		[
				=> local theType;
				#check(getVariableType(scope, sId, theType))
				#continue
				#readIdentifier
				=> local theExpr = "var";
				=> insert theExpr.name = sId;
				literal_navigation(scope, theExpr, theType)
				[
						['=' | "*=" | "+=" | "-="]:sAssignment #continue
						=> pushItem scope = sAssignment;
						=> setall scope#back.variable = theExpr;
						expression(scope, scope#back.expr)
					|
						[
							"<<" #continue
							=> pushItem scope = "operator<<()";
							=> setall scope#back.instance = theExpr;
							=> pushItem scope#back.params;
							literal_expression(scope, scope#back.params#back)
						]+
				]?
				';'
			|
				=> local theType;
				type(scope, theType)
				#continue
				#readIdentifier:sVarName
				[
					'[' #continue
					expression(scope, theType.array_size)
					']'
					=> increment(theType.pointers);
				]?
				=> insert scope.variables[sVarName].name = sVarName;
				=> setall scope.variables[sVarName].type = theType;
				=> pushItem scope = "declaration";
				=> ref scope#back.variable = scope.variables[sVarName];
				=> if bStatic {
					insert scope.variables[sVarName].is_static = true;
					scope#back = "static";
				}
				[
						!!'(' #continue
						caller_arguments(scope, scope#back.constructor)
					|
						'=' #continue
						expression(scope, scope#back.init)
				]?
				';'
			|
				#readIdentifier
				#continue
				[
						#check(!(sId in {"if", "while", "catch", "throw", "for", "return", "switch", "THROW_UTLEXCEPTION"}))
						!!'(' #continue
						=> local arguments;
						caller_arguments(scope, arguments)
						=> local theFunction, callerTypes;
						=> getArgumentTypes(arguments, callerTypes);
						#check(getFunction(scope, sId, callerTypes, theFunction))
						=> pushItem scope = "()";
						=> ref scope#back.function = theFunction;
						=> setall scope#back.params = arguments;
						';'
					|
						#continue
						statement<sId>(scope)
				]
		]
	|
		'#' #continue
		[
				#readIdentifier:"ifdef" #continue
				"WIN32"
				->['#' #readIdentifier:"else"]
			|
				#readIdentifier:"endif"
		]
	|
		"(*" #continue
		=> pushItem scope = "*()";
		#readIdentifier:scope#back.function
		')'
		caller_arguments(scope, scope#back.params)
		';'
	|
		'*'
		#readIdentifier:sVariable
		=> local theType;
		#check(getVariableType(scope, sVariable, theType))
		#continue
		=> local theExpr = "var";
		=> insert theExpr.name = sVariable;
		["++" | "--"]?:sOperator
		=> if sOperator {
			slideNodeContent(theExpr, expr);
			theExpr = sOperator;
		}
		[
			['=' | "*=" | "+=" | "-="]:sAssignment #continue
			=> pushItem scope = sAssignment;
			=> setall scope#back.variable = theExpr;
			expression(scope, scope#back.expr)
		]?
		';'
	;

statement<"return">(scope : node) ::=
		';'
	|
		#continue
		=> pushItem scope = "return";
		expression(scope, scope#back.expr)
		';'
	;

statement<"break">(scope : node) ::=
	#continue
	';'
	=> pushItem scope = "break";
	;

statement<"continue">(scope : node) ::=
	#continue
	';'
	=> pushItem scope = "continue";
	;

statement<"if">(scope : node) ::=
		#continue
		=> pushItem scope = "if";
		'('
		expression(scope, scope#back.condition)
		')'
		=> ref scope#back.then.parent = scope;
		instruction(scope#back.then)
		[
			#readIdentifier:"else" #continue
			=> ref scope#back.else.parent = scope;
			instruction(scope#back.else)
		]?
	;

statement<"while">(scope : node) ::=
		#continue
		=> pushItem scope = "while";
		'('
		expression(scope, scope#back.condition)
		')'
		=> ref scope#back.body.parent = scope;
		instruction(scope#back.body)
	;

statement<"do">(scope : node) ::=
		#continue
		=> pushItem scope = "do";
		=> ref scope#back.body.parent = scope;
		instruction(scope#back.body)
		"while"
		'('
		expression(scope, scope#back.condition)
		')'
		';'
	;

statement<"for">(scope : node) ::=
		#continue
		=> pushItem scope = "for";
		'('
		[
				";;"
				=> ref scope#back.body.parent = scope;
			|
				#continue
				[
						#readIdentifier:sIndex
						'='
						=> insert scope#back.init = '=';
						=> insert scope#back.init.index = sIndex;
						=> ref scope#back.body.parent = scope;
					|
						#continue
						type(scope, scope#back.init.type)
						#readIdentifier:sIndex
						'='
						=> scope#back.init = "declaration";
						=> insert scope#back.init.index = sIndex;
						=> insert scope#back.variables[sIndex].name = sIndex;
						=> setall scope#back.variables[sIndex].type = scope#back.init.type;
						=> ref scope#back.body.parent = scope#back;
						=> ref scope#back.parent = scope;
				]
				expression(scope, scope#back.init.expr)
				';'
				expression(scope#back.body, scope#back.condition)
				';'
				expression(scope#back.body, scope#back.next)
		]
		')'
		instruction(scope#back.body)
	;

statement<"switch">(scope : node) ::=
		#continue
		=> pushItem scope = "switch";
		'('
		expression(scope, scope#back.expr)
		')' '{'
		[
				#readIdentifier:"case" #continue
				=> pushItem scope#back.cases;
				literal_expression(scope, scope#back.cases#back.constant)
				':'
				=> ref scope#back.cases#back.body.parent = scope;
				instructions(scope#back.cases#back.body)
			|
				"default" ':'
				=> ref scope#back.default.parent = scope;
				instructions(scope#back.default)
		]*
		'}'
	;

statement<"THROW_UTLEXCEPTION">(scope : node) ::=
		#continue
		=> pushItem scope = "throw";
		=> insert scope#back.type = "UtlException";
		'('
		expression(scope, scope#back.message)
		')'
		';'
	;

statement<"delete">(scope : node) ::=
		#continue
		=> pushItem scope = "delete";
		["[]"]?
		expression(scope, scope#back.variable)
		';'
	;

function_body(scope : node, theFunction : node) ::=
		#check(theFunction.parent.parse_body)
		=> ref theFunction.body.parent = theFunction;
		instructions(theFunction.body)
	|
		[
				#readCString
			|
				#readCChar
			|
				'{' #continue function_body(scope, theFunction) '}'
			|
				~'}'
		]*
	;

expression(scope : node, theExpr : node) ::= boolean_expression(scope, theExpr);
boolean_expression(scope : node, theExpr : node) ::=
	comparison_expression(scope, theExpr)
	[
		["&&" | "||"]:sOperator
		#continue
		=> slideNodeContent(theExpr, left);
		=> theExpr = sOperator;
		comparison_expression(scope, theExpr.right)
		=> insert theExpr.type = "base";
		=> insert theExpr.type.name = "bool";
	]*
	;

comparison_expression(scope : node, theExpr : node) ::=
	arithmetic_expression(scope, theExpr)
	[
		["<=" | ">=" | "==" | "!=" | '<' | '>']:sOperator
		#continue
		=> slideNodeContent(theExpr, left);
		=> theExpr = sOperator;
		arithmetic_expression(scope, theExpr.right)
		=> insert theExpr.type = "base";
		=> insert theExpr.type.name = "bool";
	]?
	;

arithmetic_expression(scope : node, theExpr : node) ::=
	mult_expression(scope, theExpr)
	[
		["+" | "-"]:sOperator
		#continue
		=> slideNodeContent(theExpr, left);
		=> theExpr = sOperator;
		mult_expression(scope, theExpr.right)
		=> setall theExpr.type = theExpr.left.type;
	]*
	;

mult_expression(scope : node, theExpr : node) ::=
	binary_expression(scope, theExpr)
	[
		["*" | "/"]:sOperator
		#continue
		=> slideNodeContent(theExpr, left);
		=> theExpr = sOperator;
		binary_expression(scope, theExpr.right)
		=> setall theExpr.type = theExpr.left.type;
	]*
	;

binary_expression(scope : node, theExpr : node) ::=
	shift_expression(scope, theExpr)
	[
		["|" !'|' | "&" !'&']:sOperator
		#continue
		=> slideNodeContent(theExpr, left);
		=> theExpr = sOperator;
		shift_expression(scope, theExpr.right)
		=> setall theExpr.type = theExpr.left.type;
	]*
	;

shift_expression(scope : node, theExpr : node) ::=
	literal_expression(scope, theExpr)
	[
		["<<" | ">>"]:sOperator
		#continue
		=> slideNodeContent(theExpr, left);
		=> theExpr = sOperator;
		literal_expression(scope, theExpr.right)
		=> setall theExpr.type = theExpr.left.type;
	]*
	;

literal_expression(scope : node, theExpr : node) ::=
		#readIdentifier:"NULL"
		=> theExpr = "null";
	|
		hexa_value:theExpr.value
		=> theExpr = "hexa";
		=> insert theExpr.type = "base";
		=> insert theExpr.type.name = "int";
	|
		#readNumeric:theExpr.value
		=> theExpr = "double";
		=> insert theExpr.type = "base";
		=> insert theExpr.type.name = "double";
	|
		#readCString:theExpr.value
		=> theExpr = "string";
		=> insert theExpr.type = "base";
		=> insert theExpr.type.name = "char";
		=> insert theExpr.type.pointers = 1;
		=> insert theExpr.type.is_const = true;
	|
		#readCChar:theExpr.value
		=> theExpr = "char";
		=> insert theExpr.type = "base";
		=> insert theExpr.type.name = "char";
	|
		'(' #continue
		[
				=> local castType;
				#try
					type(scope, castType)
				#catch(sError)
				')'
				#continue
				=> theExpr = "cast";
				=> setall theExpr.type = castType;
				literal_expression(scope, theExpr.cast)
			|
				'*'
				#readIdentifier:sFunction
				')'
				!!'(' #continue
				=> theExpr = "(*";
				=> insert theExpr.function = sFunction;
				caller_arguments(scope, theExpr.params)
			|
				#continue
				expression(scope, theExpr)
				')'
				[
					'?' #continue
					=> slideNodeContent(theExpr, condition);
					=> theExpr = '?';
					expression(scope, theExpr.then)
					':'
					expression(scope, theExpr.else)
					=> setall theExpr.type = theExpr.then.type;
				]?
		]
	|
		'&' #continue literal_expression(scope, theExpr.reference)
		=> theExpr = "&ref";
		=> setall theExpr.type = theExpr.reference.type;
		=> increment(theExpr.type.pointers);
	|
		'*' #continue literal_expression(scope, theExpr.star)
		=> theExpr = "star";
		=> setall theExpr.type = theExpr.star;
		=> decrement(theExpr.type.pointers);
		=> if theExpr.type.pointers == 0 theExpr.type.pointers = false;
	|
		#readIdentifier:"new" #continue
		type(scope, theExpr.type)
		[
				!!'(' #continue
				caller_arguments(scope, theExpr.params)
			|
				'[' #continue expression(scope, theExpr.array) ']'
		]?
		=> theExpr = "new";
	|
		#readIdentifier:sClass
		=> local theClass;
		#check(getType(scope, sClass, theClass))
		!!'(' #continue
		caller_arguments(scope, theExpr.params)
		=> theExpr = "stack new";
		=> ref theExpr.class = theClass;
		=> insert theExpr.type = "indirect";
		=> ref theExpr.type.indirect = theClass;
	|
		#readIdentifier:sIdentifier
		[
			"::" #continue
			[
					#check(sIdentifier == "std")
					#continue
					[
						#readIdentifier:"string":sIdentifier
						[
							"::" #continue
							#readIdentifier:"npos":sIdentifier
						]?
					|
						#readIdentifier:"ios"
						#continue "::"
						#readIdentifier:{"in", "binary"}:sIdentifier
						=> sIdentifier = "ios::" + sIdentifier;
					]
				|
					#check(getType(scope, sIdentifier, scope))
					#continue
					#readIdentifier:sIdentifier
			]
		]?
		#continue
		[
				#check(sIdentifier in {"const_cast", "dynamic_cast"})
				#continue
				=> theExpr = sIdentifier;
				'<'
				type(scope, theExpr.type)
				'>'
				'('
				expression(scope, theExpr.expr)
				')'
			|
				#check(sIdentifier in {"npos", "ios::in", "ios::binary"})
				=> theExpr = sIdentifier;
				=> insert theExpr.type = "base";
				=> insert theExpr.type.name = "size_t";
			|
				#check(sIdentifier == "true" || sIdentifier == "false")
				=> theExpr = "bool";
				=> insert theExpr.value = sIdentifier;
				=> insert theExpr.type = "base";
				=> insert theExpr.type.name = "bool";
			|
				#check(sIdentifier == "this")
				#continue
				=> local theClass;
				#check(getThisClass(scope, theClass))
				=> theExpr = "this";
				=> insert theExpr.class = theClass;
				=> insert theExpr.type = "indirect";
				=> insert theExpr.type.indirect = theClass;
			|
				#continue
				[
						!'(' #continue
						=> local theType;
						[
								#check(getEnumValueType(scope, sIdentifier, theType))
								=> theExpr = "enum";
								=> insert theExpr.name = sIdentifier;
								=> setall theExpr.type = theType;
							|
								#continue
								#check(getVariableType(scope, sIdentifier, theType))
								=> theExpr = "var";
								=> insert theExpr.name = sIdentifier;
								literal_navigation(scope, theExpr, theType)
								[
									["++" | "--"]:sOperator
									=> slideNodeContent(theExpr, expr);
									=> theExpr = "post" + sOperator;
								]?
						]
					|
						#check(sIdentifier == "string")
						#continue
						'('
						expression(scope, theExpr)
						[
							',' #continue
							=> slideNodeContent(theExpr, char_array);
							expression(scope, theExpr.length)
							=> theExpr = "substring";
						]?
						')'
					|
						#continue
						=> local arguments;
						caller_arguments(scope, arguments)
						=> local theFunction, callerTypes;
						=> getArgumentTypes(arguments, callerTypes);
						#check(getFunction(scope, sIdentifier, callerTypes, theFunction))
						=> theExpr = "()";
						=> ref theExpr.function = theFunction;
						=> setall theExpr.params = arguments;
						=> localref theType = theFunction.return_type;
						literal_navigation(scope, theExpr, theType)
				]
		]
	|
		'!':theExpr #continue
		literal_expression(scope, theExpr.expr)
	|
		["++" | "--"]:theExpr #continue
		literal_expression(scope, theExpr.expr)
	;

caller_arguments(scope : node, arguments : node) ::=
	#continue
	'('
	[
		!')' #continue
		=> local theExpr;
		expression(scope, theExpr)
		=> pushItem arguments;
		=> setall arguments#back = theExpr;
		[
			',' #continue
			=> pushItem arguments;
			expression(scope, arguments#back)
		]*
	]?
	')'
	;

literal_navigation(scope : node, theExpr : node, theType : reference) ::=
	=> localref theOriginalExpr = theExpr;
	[
			['.' | "->"]
			#continue
			#readIdentifier:sIdentifier
			[
					!'(' #continue
					#check(getVariableType(theType.indirect, sIdentifier, theType) || (theType == "stl" && getSTLVariableType(theType, sIdentifier, theType)))
					=> insert theExpr.next = "var";
					=> ref theExpr = theExpr.next;
					=> insert theExpr.name = sIdentifier;
				|
					#continue
					=> local arguments;
					caller_arguments(scope, arguments)
					=> local theFunction, callerTypes;
					=> getArgumentTypes(arguments, callerTypes);
					#check((theType == "indirect" && getFunction(theType.indirect, sIdentifier, callerTypes, theFunction)) || (theType == "stl" && getSTLFunction(theType.name, sIdentifier, callerTypes, theFunction)))
					=> insert theExpr.next = "()";
					=> ref theExpr = theExpr.next;
					=> ref theExpr.function = theFunction;
					=> setall theExpr.params = arguments;
					=> ref theType = theFunction.return_type;
			]
		|
			'[' #continue
			=> pushItem theExpr.array;
			expression(scope, theExpr.array#back)
			']'
	]*
	=> setall theOriginalExpr.type = theType;
	;

type(scope : node, theType : node) ::=
	[
		#readIdentifier:{"const", "mutable", "register"}:sModifier
		=> switch(sModifier) {
			case "const": insert theType.is_const = true;break;
			case "mutable": insert theType.is_mutable = true;break;
			case "register": insert theType.is_register = true;break;
		}
	]*
	[
			#readIdentifier:"unsigned"
			=> insert theType.is_unsigned = true;
		|
			#readIdentifier:"signed"
	]?
	#readIdentifier:sType
	[
			nested_class_type(scope, sType, theType)
		|
			#check(sType in {"__int64", "bool", "char", "double", "float", "int", "long", "short", "size_t", "void"})
			[
					#check(sType == "long")
					#readIdentifier:"long"
					=> sType = "long long";
				|
					#check(sType == "__int64")
					=> sType = "long long";
			]?
			[
				#check(sType == "long" || sType == "long long" || sType == "short")
				#readIdentifier:"int"
			]?
			=> theType = "base";
			=> insert theType.name = sType;
		|
			#check(getType(scope, sType, theType.indirect))
			=> theType = "indirect";
	]
	[
		'*'
		=> increment(theType.pointers);
	]*
	[
		'&'
		=> insert theType.is_reference = true;
	]?
	;

class_type(scope : node, theType : node) ::=
	#readIdentifier:sType
	[
			nested_class_type(scope, sType, theType)
		|
			#check(getType(scope, sType, theType.indirect))
			=> theType = "indirect";
	]
	;

nested_class_type(scope : node, sType : value, theType : node) ::=
	"::" #continue
	=> local sNamespace = sType;
	#readIdentifier:sType
	[
			#check(sNamespace == "std")
			#continue
			[
					#check(sType in {"auto_ptr", "exception", "fstream", "ifstream", "iostream", "istream", "list", "map", "ofstream", "ostream", "set", "string", "vector"})
					[
						#check(sType == "string")
						"::" #continue
						#readIdentifier:"size_type"
						=> sType += "::size_type";
					]?
				|
					=> error("STL type '" + sType + "' not handled yet");
			]
			[
				'<' #continue
				=> pushItem theType.templates;
				type(scope, theType.templates#back)
				[
					',' #continue
					=> pushItem theType.templates;
					type(scope, theType.templates#back)
				]*
				'>'
				[
					"::" #continue
					#readIdentifier:{"const_iterator", "iterator"}:sIterator
					=> sType += "::" + sIterator;
				]?
			]?
			=> theType = "stl";
			=> insert theType.name = sType;
		|
			=> local theParent;
			#continue
			#check(getType(scope, sNamespace, theParent))
			#check(getNestedType(theParent, sType, theType.indirect))
			=> theType = "indirect";
	]
	;

operator ::= "==" | '=' | ">>" | "<<" | "!=" | "<=" | ">=" | '<' | '>' | "++" | "--" | '+' | '-';

hexa_value ::= #!ignore "0x" #continue ['0'..'9' | 'A'..'F' | 'a'..'f']+;
