/**
 * @file src/demangler/gparser.cpp
 * @brief Parser of LL grammar.
 * @copyright (c) 2017 Avast Software, licensed under the MIT license
 */

#include <algorithm>
#include <cassert>
#include <cctype>
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <regex>
#include <sstream>
#include <string>

#include "demangler/demglobal.h"
#include "demangler/demtools.h"
#include "demangler/gparser.h"
#include "demangler/igrams.h"

// #define DEMANGLER_SUBDBG
// #define DEMANGLER_IGRAM

#define BSUBMAX 40 //max size of parameter substitution vector

using namespace std;

namespace demangler {

/**
 * @brief Global array of semantic action names. Used when building internal LL table from external grammar.
 */
const char *cGram::semactname[] = {
	"SA_NULL",
	"SA_SETNAMEF",
	"SA_SETNAMETF",
	"SA_SETNAMEO",
	"SA_SETNAMEC",
	"SA_SETNAMED",
	"SA_SETNAMEX",
	"SA_SETNAMEVT",
	"SA_SETOPXX",
	"SA_SETOPNW",
	"SA_SETOPNA",
	"SA_SETOPDL",
	"SA_SETOPDA",
	"SA_SETOPPS",
	"SA_SETOPNG",
	"SA_SETOPAD",
	"SA_SETOPDE",
	"SA_SETOPCO",
	"SA_SETOPPL",
	"SA_SETOPMI",
	"SA_SETOPML",
	"SA_SETOPDV",
	"SA_SETOPRM",
	"SA_SETOPAN",
	"SA_SETOPOR",
	"SA_SETOPEO",
	"SA_SETOPASS",
	"SA_SETOPPLL",
	"SA_SETOPMII",
	"SA_SETOPMLL",
	"SA_SETOPDVV",
	"SA_SETOPRMM",
	"SA_SETOPANN",
	"SA_SETOPORR",
	"SA_SETOPEOO",
	"SA_SETOPLS",
	"SA_SETOPRS",
	"SA_SETOPLSS",
	"SA_SETOPRSS",
	"SA_SETOPEQ",
	"SA_SETOPNE",
	"SA_SETOPLT",
	"SA_SETOPGT",
	"SA_SETOPLE",
	"SA_SETOPGE",
	"SA_SETOPNT",
	"SA_SETOPAA",
	"SA_SETOPOO",
	"SA_SETOPPP",
	"SA_SETOPMM",
	"SA_SETOPCM",
	"SA_SETOPPM",
	"SA_SETOPPT",
	"SA_SETOPCL",
	"SA_SETOPIX",
	"SA_SETOPQU",
	"SA_SETOPST",
	"SA_SETOPSZ",
	"SA_SETOPAT",
	"SA_SETOPAZ",
	"SA_SETOPCV",
	"SA_SETTYPEV",
	"SA_SETTYPEW",
	"SA_SETTYPEB",
	"SA_SETTYPEC",
	"SA_SETTYPEA",
	"SA_SETTYPEH",
	"SA_SETTYPES",
	"SA_SETTYPET",
	"SA_SETTYPEI",
	"SA_SETTYPEJ",
	"SA_SETTYPEL",
	"SA_SETTYPEM",
	"SA_SETTYPEX",
	"SA_SETTYPEY",
	"SA_SETTYPEN",
	"SA_SETTYPEO",
	"SA_SETTYPEF",
	"SA_SETTYPED",
	"SA_SETTYPEE",
	"SA_SETTYPEG",
	"SA_SETTYPEZ",
	"SA_SETCONST",
	"SA_SETRESTRICT",
	"SA_SETVOLATILE",
	"SA_SETPTR",
	"SA_SETREF",
	"SA_SETRVAL",
	"SA_SETCPAIR",
	"SA_SETIM",
	"SA_SUBSTD", //std::
	"SA_SUBALC", //std::allocator
	"SA_SUBSTR", //std::basic_string
	"SA_SUBSTRS", //std::basic_string<char",std::char_traits<char>",std::allocator<char>>
	"SA_SUBISTR", //std::basic_istream<char",  std::char_traits<char>>
	"SA_SUBOSTR", //std::basic_ostream<char",  std::char_traits<char>>
	"SA_SUBIOSTR", //std::basic_iostream<char",  std::char_traits<char>>
	"SA_LOADID", //load an unqualified name into the qualified vector of names
	"SA_LOADSUB", //load a substitution
	"SA_LOADTSUB", //load a template sub
	"SA_LOADARR", //load an array dimension
	"SA_STOREPAR", //store current parameter to vector of parameters
	"SA_STORETEMPARG", //store current parameter to current vector of template arguments
	"SA_STORETEMPLATE", //store the whole template into the last name element of last name vector
	"SA_BEGINTEMPL", //begin a template
	"SA_SKIPTEMPL", //skip a template
	"SA_PAR2F", //store current vector of parameters into the function
	"SA_PAR2RET", //store current parameter to the return value
	"SA_PAR2SPEC", //store current parameter to the special value
	"SA_UNQ2F", //future identifiers are added to the function name
	"SA_UNQ2P", //function identifiers are added to parameter name
	"SA_SSNEST", //nested sub
	"SA_STUNQ", //unqualified std:: sub
	"SA_SSNO", //other sub derived from <name>
	"SA_TYPE2EXPR", //builtin type is converted to primary expression
	"SA_EXPRVAL", //load expression value
	"SA_BEGINPEXPR", //begin a primary expression
	"SA_STOREPEXPR", //end a primary expression
	"SA_COPYTERM", //copy the terminal on the input into current_name in substitution analyzer
	"SA_ADDCHARTONAME",
	"SA_STORENAME",
	"SA_REVERSENAME",
	"SA_SETPRIVATE",
	"SA_SETPUBLIC",
	"SA_SETPROTECTED",
	"SA_SETFCDECL",
	"SA_SETFPASCAL",
	"SA_SETFFORTRAN",
	"SA_SETFTHISCALL",
	"SA_SETFSTDCALL",
	"SA_SETFFASTCALL",
	"SA_SETFINTERRUPT",
	"SA_SETUNION",
	"SA_SETSTRUCT",
	"SA_SETCLASS",
	"SA_SETENUM",
	"SA_SETSTATIC",
	"SA_SETVIRTUAL",
	"SA_STCLCONST",
	"SA_STCLVOL",
	"SA_STCLFAR",
	"SA_STCLHUGE",
	"SA_SAVENAMESUB",
	"SA_LOADNAMESUB",
	"SA_MSTEMPLPSUB",
	"SA_SETNAMER0",
	"SA_SETNAMER1",
	"SA_SETNAMER2",
	"SA_SETNAMER3",
	"SA_SETNAMER4",
	"SA_SETNAME_A",
	"SA_SETNAME_B",
	"SA_SETNAME_C",
	"SA_SETNAME_D",
	"SA_SETNAME_E",
	"SA_SETNAME_F",
	"SA_SETNAME_G",
	"SA_SETNAME_H",
	"SA_SETNAME_I",
	"SA_SETNAME_J",
	"SA_SETNAME_K",
	"SA_SETNAME_L",
	"SA_SETNAME_M",
	"SA_SETNAME_N",
	"SA_SETNAME_O",
	"SA_SETNAME_P",
	"SA_SETNAME_Q",
	"SA_SETNAME_R",
	"SA_SETNAME_S",
	"SA_SETNAME_T",
	"SA_SETNAME_U",
	"SA_SETNAME_V",
	"SA_SETNAME_W",
	"SA_SETNAME_X",
	"SA_SETNAME_Y",
	"SA_SETNAME_Z",
	"SA_TEMPL2TFTPL",
	"SA_BEGINBSUB",
	"SA_LOADBSUB",
	"SA_ADDMCONST",
	"SA_ADDMVOL",
	"SA_ADDMFAR",
	"SA_ADDMHUGE",
	"SA_LOADMSNUM",
	"SA_NUMTORTTIBCD",
	"SA_NUMTOTYPE",
	"SA_BORLANDNORMALIZEPARNAME", //normalize the last current_name element by replacing "@" with "::". Usef dor Borland demangler
	"SA_BORLANDID", //insert separator "|" after a number of characters specified by following characters (numbers). Borland only
	"SA_LOADBORLANDSUB", //load borland sub (based on current char - numbering 1 - 9, a - z)
	"SA_BORLANDARR", //load an array dimension (Borland only)
	"SA_END"
};

/**
 * @brief Constructor of cGram class.
 */
cGram::cGram() {
	internalGrammarStruct.llst = nullptr; //nullify the llst
	SubAnalyzeEnabled = false;
	errValid = false;
	errString = "Everything OK";
	lex_position = 0;
}

/**
 * @brief Destructor of cGram class.
 */
cGram::~cGram() {

	//delete the used internal grammar, if any way used
	deleteIgrams(this);

	//deallocate parsed external grammar
	if (!internalGrammar) {
		for(vector<rule_t>::iterator i=rules.begin(); i != rules.end(); ++i) {
			free(i->left.nt);
				//for every element on the right side
				for(vector<gelem_t>::iterator j=i->right.begin(); j != i->right.end(); ++j) {
					if (j->type == GE_NONTERM) {
						free(j->nt);
					}
				} //for every element on the right
		}
	} //if (!internalGrammar)
}

/**
 * @brief Initialize the grammar in cGram class.
 * @param gname Grammar name. If internal grammar is used, internal grammar of this name must exist.
 * If external grammar is used file with this name must exist and it used as external grammar.
 * @param i Use internal grammar? Default setting is true. If set to false, external grammar is used.
 */
cGram::errcode cGram::initialize(string gname, bool i) {
	errcode retvalue = ERROR_OK;
	internalGrammar = i;
	compiler = gname;
	//initialize internal grammar
	if (internalGrammar) {

		//this is only for gcc, the special substitution analysis
		if (gname == "gcc") {
			SubAnalyzeEnabled = true;
		}

		//initialize the internal grammar
		if (!initIgram(gname,this)) {
			errValid = false;
			errString = "Failed to load internal grammar " + gname + ".";
			return ERROR_GRAM;
		}
		//successfully initialised internal grammar
		else {
			errValid = true;
		}
	}

	//initialize an external grammar
	else {
		//parse external grammar file
		if ((retvalue = parse(gname)) == ERROR_OK) {
			errValid = true;
		}
	}
	return retvalue;
}

/**
 * @brief Set substitution analysis manually to enabled or disabled.
 * @param x Boolean value. True means enable, false means disable.
 */
void cGram::setSubAnalyze(bool x) {
	SubAnalyzeEnabled = x;
}

/**
 * @brief Constructor of cName class. Initializes return and special type.
 */
cName::cName() {
	return_type.type = TT_UNKNOWN;
	special_type.type = TT_UNKNOWN;
	member_function_access = MFM_NULL;
	function_call = FCC_NULL;
	is_static = false;
	is_virtual = false;
	tf_tpl = nullptr;
}

/**
 * @brief Initializer of the static const array of final states of the input file parsing FSM. Terminated by S_NULL.
 */
const cGram::fsmstate cGram::fsm_final[] = {S_START,S_IGNORE,S_RIGHT,S_T,S_NULL};

/**
 * @brief Definition of the EOF terminal element.
 */
const cGram::gelem_t cGram::T_EOF = {GE_TERM, const_cast<char *>(""), 0, '\0'};

/**
 * @brief Initializer of error message array.
 */
const char *cGram::errmsg[] = {
	"Everything is ok. However, if you can see this error message something has gone terribly wrong.",
	"File error,",
	"Grammar input invalid.",
	"Syntax error.",
	"Unknown error."
};

/**
 * @brief An envelop function to load a file into the "source" string of cGram class.
 * @param filename Name of the file to be loaded.
 * @return An error code.
 * @retval ERROR_FILE File loading failed.
 */
cGram::errcode cGram::loadfile(const string filename) {
	errcode retvalue = ERROR_OK;
	source = new fstream(filename,fstream::in);
	return retvalue;
}

/**
 * @brief An envelop function to retrieve one byte from the input source.
 * @return One byte of the input file.
 */
char cGram::getc() {
	char c;
	source->get(c);
	return c;
}

/**
 * @brief Bool function which tests whether end of source file has been reached.
 * @return "Has the pointer reached end of source file?"
 */
bool cGram::eof() {
	return (source->peek() == EOF);
}

/**
 * @brief Bool function which tests whether end of line in the source file has been reached.
 * @return "Has the pointer reached end of line?"
 */
bool cGram::lf() {
	return (static_cast<char>(source->peek()) == '\n' || static_cast<char>(source->peek()) == '\r');
}

/**
 * @brief Function which tests whether a state is a final state of FSM.
 * @param s The input state.
 * @return "Is s a final state of the FSM?"
 */
bool cGram::is_final(fsmstate s) {
	bool retvalue = false;
	//for every state in array check whether it equals input parameter s
	for (int i=0; fsm_final[i] != S_NULL; ++i) {
		if (s == fsm_final[i]) {
			return true;
		}
	}
	return retvalue;
}

/**
 * @brief The FSM which parses the input file into a vector of grammar rules.
 * @param filename Name of the file from which the grammar should be read.
 * @return An errorcode.
 * @retval ERROR_FILE An error ocurred when trying to load the file.
 * @retval ERROR_FSM Error caused by invalid data in the input file.
 */
cGram::errcode cGram::getgrammar(const string filename) {
	errcode retvalue = ERROR_OK;
	fsmstate state = S_START;
	fsmstate nextstate = S_START;
	string current_nt;
	/*
	 * Variables for internal grammar generator
	 */
	std::size_t current_offset = 0;
	newIG_ruleaddrs = "";
	newIG_ruleelements = "\t";
	/*
	 * End of block "Variables for internal grammar generator"
	 */

	//load the file
	if ((retvalue = loadfile(filename)) != ERROR_OK) {return retvalue;}

	rule_t current_rule;
	gelem_t current_gelem;

	current_gelem.nt = nullptr;

	//rule numbering starts at 1
	unsigned int rulenum = 1;


	while (retvalue == ERROR_OK) {
		//if reached end of line or file, check whether current state is final
		if (eof() || lf()) {
			//current state is not final, end with error
			if (!is_final(nextstate)) {
				errString = "cGram::parse: Unexpected end of line/file.";
				retvalue = ERROR_FSM;
				break;
			}
			//state is final, save current rule
			else {
				//when generating new internal grammar
				if (createIGrammar != "") {
					//add new nonterminal into nonterminal list
					if (isnt(nonterminals,current_rule.left.nt) == 0) {
						nonterminals.push_back(current_rule.left.nt);
					}
					//assign the ntst number (starting from 0)
					current_rule.left.ntst = isnt(nonterminals,current_rule.left.nt)-1;
					//for every non-terminal on the right side of the rule, do the same
					for(vector<gelem_t>::iterator j=current_rule.right.begin(); j != current_rule.right.end(); ++j) {
						if (j->type == GE_NONTERM) {
							if (isnt(nonterminals,j->nt) == 0) {
								nonterminals.push_back(j->nt);
							}
							j->ntst = isnt(nonterminals,j->nt)-1;
						}
					}

					//create the rules table
					if (rulenum != 1 && !current_rule.right.empty()) {
						newIG_ruleelements += string("") + "," + "\n\t";
					}
					//for every element on the right side of the rule
					for(vector<gelem_t>::iterator j=current_rule.right.begin(); j != current_rule.right.end(); ++j) {
						if (j!=current_rule.right.begin()) {
							newIG_ruleelements += ",";
						}
						newIG_ruleelements += "{";
						if (j->type == GE_NONTERM) {
							newIG_ruleelements += "cGram::GE_NONTERM,const_cast<char *>(\"";
							newIG_ruleelements += j->nt;
							newIG_ruleelements += string("") + "\")," + std::to_string(j->ntst) + ",\'\\0\'";
						}
						else if (j->type == GE_TERM) {
							newIG_ruleelements += string("") + "cGram::GE_TERM,const_cast<char *>(\"\"),0,\'" + j->t + "\'";
						}
						newIG_ruleelements += "}";
					}

					//rules offset table
					if (rulenum != 1) {
						newIG_ruleaddrs += ",";
					}
					if ((rulenum - 1) % 10 == 0) {
						newIG_ruleaddrs += "\n\t";
					}
					newIG_ruleaddrs += string("") + "{" + std::to_string(current_offset) + "," + std::to_string(static_cast<unsigned int>(current_rule.right.size())) + "}";
					current_offset += current_rule.right.size();

				}

				current_rule.n=rulenum++;
				rules.push_back(current_rule);
				current_rule.left.nt = nullptr;
				current_rule.right.clear();
				nextstate = S_START;
			}
		}
		//if FSM reached end of file, exit the main cycle
		if (eof() || (retvalue != ERROR_OK)) {
			break;
		}
		char c = getc();
		state=nextstate;
		switch(state) {
			//beginning of a line
			case S_START:
				//ignore whitespaces
				if (isspace(c)) {
					nextstate = S_START;
				}
				else {
					switch(c) {
						//left non-terminal begins
						case '<':
							current_gelem.type = GE_NONTERM;
							nextstate = S_NT_LEFT;
							break;
						//start of a comment - ignore rest of the line
						case '#':
							nextstate = S_IGNORE;
							break;
						//anything else = error
						default:
							nextstate = S_ERROR;
							break;
					}
				}
				break;
			//left non-terminal
			case S_NT_LEFT:
				switch(c) {
					//end the non.terminal and save it to the rule struct
					case '>':
						current_gelem.nt = static_cast<char *>(malloc(sizeof(char)*(current_nt.length()+1)));
						if (current_gelem.nt == nullptr) {
							errString = "cGram::getgrammar: Memory allocation error.";
							retvalue = ERROR_MEM;
							break;
						}
						strcpy(current_gelem.nt,current_nt.c_str());
						current_rule.left = current_gelem;
						current_nt.clear();
						current_gelem.nt = nullptr;
						nextstate = S_OP1;
						break;
					//anything else is part of the non-terminal's name
					default:
						current_nt += c;
						nextstate = S_NT_LEFT;
						break;
				}
				break;
			//first symbol of "::=" here
			case S_OP1:
				if (isspace(c)) {
					nextstate = S_OP1;
				}
				else {
					switch(c) {
						case ':':
							nextstate = S_OP2;
							break;
						default:
							nextstate = S_ERROR;
							break;
					}
				}
				break;
			//second symbol of "::=" here
			case S_OP2:
				switch(c) {
					case ':':
						nextstate = S_OP3;
						break;
					default:
						nextstate = S_ERROR;
						break;
				}
				break;
			//third symbol of "::=" here
			case S_OP3:
				switch(c) {
					case '=':
						nextstate = S_RIGHT;
						break;
					default:
						nextstate = S_ERROR;
						break;
				}
				break;
			//right side of the rule - expect a terminal, quoted terminal, non-terminal or nothing
			case S_RIGHT:
				//ignore whitespaces
				if (isspace(c)) {
					nextstate = S_RIGHT;
				}
				else {
					switch(c) {
						//start of a non-terminal
						case '<':
							current_gelem.type = GE_NONTERM;
							nextstate = S_NT_RIGHT;
							break;
						//start of a quoted terminal
						case '"':
							nextstate = S_QT;
							break;
						//start of a comment - ignore rest of the line
						case '#':
							nextstate = S_IGNORE;
							break;
						//anything else is a one-byte terminal - save it to the right side of the rule
						default:
							current_gelem.type = GE_TERM;
							current_gelem.t=c;
							current_rule.right.push_back(current_gelem);
							nextstate = S_T;
							break;
					}
				}
				break;
			//non-terminal on the right side of the rule
			case S_NT_RIGHT:
				switch(c) {
					//end of non-terminal - save it
					case '>':
						current_gelem.nt = static_cast<char *>(malloc(sizeof(char)*(current_nt.length()+1)));
						if (current_gelem.nt == nullptr) {
							errString = "cGram::getgrammar: Memory allocation error.";
							retvalue = ERROR_MEM;
							break;
						}
						strcpy(current_gelem.nt,current_nt.c_str());

						current_rule.right.push_back(current_gelem);
						current_gelem.nt = nullptr;
						current_nt.clear();
						nextstate = S_RIGHT;
						break;
					//anything else is part of the non-terminal's name
					default:
						current_nt += c;
						nextstate = S_NT_RIGHT;
						break;
				}
				break;
			//terminal
			case S_T:
				//ignore whitespaces
				if (isspace(c)) {
					nextstate = S_RIGHT;
				}
				else {
					switch(c) {
						//a non-terminal starts
						case '<':
							current_gelem.type = GE_NONTERM;
							nextstate = S_NT_RIGHT;
							break;
						//start of a quoted terminal
						case '"':
							nextstate = S_QT;
							break;
						//anything else is another one-byte terminal - save it
						default:
							current_gelem.type = GE_TERM;
							current_gelem.t=c;
							current_rule.right.push_back(current_gelem);
							nextstate = S_T;
							break;
					}
				}
				break;
			//quoted terminal
			case S_QT:
				switch(c) {
					//end of quoted non-terminal
					case '"':
						nextstate = S_RIGHT;
						break;
					//an escape sequence
					case '\\':
						nextstate = S_QT_ESC;
						break;
					//anything else is a one-byte terminal - save it
					default:
						current_gelem.type = GE_TERM;
						current_gelem.t=c;
						current_rule.right.push_back(current_gelem);
						nextstate = S_QT;
						break;
				}
				break;
			//escape sequence
			case S_QT_ESC:
				//anything here is a one byte terminal - save it
				current_gelem.type = GE_TERM;
				current_gelem.t=c;
				current_rule.right.push_back(current_gelem);
				nextstate = S_QT;
				break;
			//ignore everything until end of line
			case S_IGNORE:
				nextstate = S_IGNORE;
				break;
			//error ocurred because of invalid data in input file
			case S_ERROR:
				errString = "cGram::parse: Syntax error in line " + std::to_string(rulenum);
				break;
			default:
				break;
		} //switch()state
	} //while

	//generate new internal grammar
	if (createIGrammar != "") {
		newIG_ruleaddrs += "\n";
		newIG_ruleelements_x = current_offset;
		newIG_ruleaddrs_x = rules.size();
	}

	return retvalue;
}

/**
 * @brief Function which generates the "Empty" set based on the parsed grammar rules.
 */
void cGram::genempty() {
	bool right_empty = true;
	bool change = false;
	empty.clear();
	//initialisation phase
	for(vector<rule_t>::iterator i=rules.begin(); i != rules.end(); ++i) {
		//if there is nothing on the right side, add left nonterminal to "empty"
		if (i->right.empty()) {
			empty[i->left.nt] = true;
		}
		else {
			empty[i->left.nt] = false;
		}
	}

	//do while there is still anything to change in "empty"
	do {
		change = false;
		//for every rule
		for(vector<rule_t>::iterator i=rules.begin(); i != rules.end(); ++i) {
			//if left non-terminal still is not true in "empty"
			if (!empty[i->left.nt]) {
				right_empty = true;
				//for every element on the right side of the rule check whether it is in "empty"
				for(vector<gelem_t>::iterator j=i->right.begin(); j != i->right.end(); ++j) {
					//terminal - can not be added to empty
					if (j->type == GE_TERM) {
						right_empty = false;
						break;
					}
					//non-terminal which is not in "empty"
					else if (!empty[j->nt]) {
						right_empty = false;
						break;
					}
				} //for every element on the right
				//if the right side of the rule could be emptied, add the left non-terminal to empty
				if (right_empty) {
					empty[i->left.nt] = true;
					change = true;
				}
			} //if left not in "empty"
		} //for every rule
	} while(change);

}

/**
 * @brief Function which generates the "First" set based on the parsed grammar rules and the "Empty" set.
 */
void cGram::genfirst() {
	bool change = false;
	first.clear();
	//do while there is still something to change
	do {
		change = false;
		//for every rule
		for(vector<rule_t>::iterator i=rules.begin(); i != rules.end(); ++i) {
				//for every element on the right side of the rule get its "First" set
				for(vector<gelem_t>::iterator j=i->right.begin(); j != i->right.end(); ++j) {
					//terminal - add it to the first of the left NT and stop parsing this rule
					if (j->type == GE_TERM) {
						if (first[i->left.nt].insert(*j).second) {
							change = true;
						}
						break;
					}
					//non-terminal
					else {
						if (j->nt != i->left.nt) {
							//add the contents of its "first" to the "first" of the left non-terminal
							if (copyset(first[i->left.nt],first[j->nt])) {
								change = true;
							}
						}
						//if the nonterminal is not in "empty", stop parsing this rule
						if (!empty[j->nt]) {
							break;
						}
					}
				} //for every element on the right
		} //for every rule

	} while(change);
}

/**
 * @brief Function which generates the "Empty" set for a sequence of grammar elements.
 * @param src Reference to a vector of grammar elements.
 * @return "May the sequence of grammar elements be emptied?"
 */
bool cGram::getempty(vector<gelem_t> & src) {
	bool retvalue = true;
	//empty sequence is empty :)
	if (src.empty()) {
		return true;
	}
	//for every element in the sequence...
	for(vector<gelem_t>::iterator i=src.begin(); (i != src.end()) && (retvalue == true); ++i) {
		//if there is a terminal, the sequence can not be emptied
		if (i->type == GE_TERM) {
			retvalue = false;
		}
		//for non-terminals, get the info about emptiability from the "Empty" set
		else {
			retvalue = empty[i->nt];
		}
	}
	return retvalue;
}

/**
 * @brief Function which generates the "First" set for a sequence of grammar elements.
 * @param src Reference to a vector of grammar elements.
 * @return A std::set of terminals that belong to the "First" set of the grammar element sequence.
 */
set<cGram::gelem_t,cGram::comparegelem_c> cGram::getfirst(vector<gelem_t> & src) {
	set<gelem_t,comparegelem_c> retvalue;
	//for every element of the sequence
	for(vector<gelem_t>::iterator i=src.begin(); i != src.end(); ++i) {
		//terminal - just insert into output set and end
		if (i->type == GE_TERM) {
			retvalue.insert(*i);
			break;
		}
		//nonterminal
		else {
			//copy the "First" set of the nonterminal into the output set
			copyset(retvalue,first[i->nt]);
			//if the non.terminal can not be emptied, end
			if (!empty[i->nt]) {
				break;
			}
		}
	}
	return retvalue;
}

/**
 * @brief Function which creates union of two sets by copying elements of the second set into the first set.
 * @param dst Reference to the destination std::set, into which elements will be copied.
 * @param src Referrence to the source std::set.
 * @return "Have any new elements been added to the destination set?"
 *
 */
bool cGram::copyset(set<cGram::gelem_t,cGram::comparegelem_c> & dst, set<cGram::gelem_t,cGram::comparegelem_c> & src) {
	bool retvalue = false;
	//for every element in the source set
	for(set<gelem_t,comparegelem_c>::iterator i = src.begin(); i != src.end(); ++i) {
		//insert it into the destination set and if it wasn't already present in the destination set...
		if (dst.insert(*i).second) {
			//set return value to true
			retvalue = true;
		}
	}
	return retvalue;
}

/**
 * @brief Function which makes a dynamically allocated copy of an existing template.
 * @param src The existing template (vector of types) to be copied.
 * @return A pointer to the copy of the source template.
 * @retval nullptr The source pointer was also nullptr or an error occurred.
 */
void * cGram::copynametpl(void * src) {
	void * retvalue = nullptr;
	bool all_ok = true;
	cName::type_t temp_type;
	void * temp_expr = nullptr;

	//if there is no source to copy from, quit
	if (src == nullptr) {
		return retvalue;
	}
	//allocate the destination vector
	retvalue = static_cast<void *>(new vector<cName::type_t>);
	if (retvalue == nullptr) {
		return retvalue;
	}
	//iterate through all types of the source vector
	for (vector<cName::type_t>::iterator i=(*(static_cast<vector<cName::type_t>*>(src))).begin();
										i != (*(static_cast<vector<cName::type_t>*>(src))).end(); ++i) {
		//dynamically allocate a copy of p-expression
		if (i->type == cName::TT_PEXPR) {
			temp_expr = malloc(sizeof(bool));
			if (temp_expr == nullptr) {
				all_ok = false;
				break;
			}
			temp_expr = static_cast<bool *>(i->value);
			temp_type = *i;
			temp_type.value = temp_expr;
			temp_expr = nullptr;
		}
		//if there is a template in the named type, recursively call this function
		else if (i->type == cName::TT_NAME) {
			temp_type = *i;
			for (vector<cName::name_t>::iterator j=temp_type.n.begin(); j != temp_type.n.end(); ++j) {
				if (j->tpl != nullptr) {
					j->tpl = copynametpl(j->tpl);
					//if there was an error during allocation, clean up
					if (j->tpl == nullptr) {
						all_ok = false;
						--j;
						do {
							if (j->tpl != nullptr) {
								delete static_cast<vector<cName::type_t>*>(j->tpl);
							}
						} while (j != temp_type.n.begin());
						break;
					}
				}
			}
			if (!all_ok) {
				break;
			}
		}
		//any other type
		else {
			temp_type = *i;
		}
		//push the whole type struct into destination vector
		(*(static_cast<vector<cName::type_t>*>(retvalue))).push_back(temp_type);
	}

	if (!all_ok) {
		delete static_cast<vector<cName::type_t>*>(retvalue);
		retvalue = nullptr;
	}
	return retvalue;
}

/**
 * @brief Function which generates the "Follow" set based on the parsed grammar rules, the "Empty" set and the "First" set.
 */
void cGram::genfollow() {
	bool change = false;
	follow.clear();
	//Y = the rest of the right element sequence
	vector<gelem_t> y;
	vector<gelem_t>::iterator k;
	//"First" of Y
	set<gelem_t,comparegelem_c> first_y;
	//set Follow of the root non-terminal to EOF
	follow[(*rules.begin()).left.nt].insert(T_EOF);
	do {
		change = false;
		//for every rule
		for(vector<rule_t>::iterator i=rules.begin(); i != rules.end(); ++i) {
			//for every element (J) on the right side...
			for(vector<gelem_t>::iterator j=i->right.begin(); j != i->right.end(); ++j) {
				//there is no point in getting Follow for terminals
				if (j->type == GE_NONTERM) {
					y.clear();
					k=j;
					//load Y from the rest of the right side of the rule
					for(++k; k != i->right.end(); ++k) {
						y.push_back(*k);
					}
					//get "First" for Y
					first_y = getfirst(y);
					//insert first(Y) into follow(J)
					if (copyset(follow[j->nt],first_y)) {
						change = true;
					}
					//get "Empty" for Y and if true, copy Follow of the left non-terminal to follow(Y)
					if (getempty(y) && copyset(follow[j->nt],follow[i->left.nt])) {
						change = true;
					}
				} //if non-terminal
			} //for every element on the right
		} //for every rule
	} while(change);
}

/**
 * @brief Function which generates the "Predict" set based on the parsed grammar rules,"Empty" set,"First" set and "Follow" set.
 */
void cGram::genpredict() {
	predict.clear();
	set<gelem_t,comparegelem_c> y;
	//for every rule
	for(vector<rule_t>::iterator i=rules.begin(); i != rules.end(); ++i) {
		y.clear();
		//get first for the element sequence on the right side of the rule and insert into Predict for this rule
		y = getfirst(i->right);
		copyset(predict[i->n],y);
		//if right side can be emptied, copy Follow of the left non-terminal into this rule's Predict
		if (getempty(i->right)) {
			copyset(predict[i->n],follow[i->left.nt]);
		}
	}
}

/**
 * @brief Function which generates the LL table from the "Predict" set.
 */
cGram::errcode cGram::genll() {
	errcode retvalue = ERROR_OK;
	ll.clear();
	pair<unsigned int,semact> current_pair;
	current_pair.second = SA_NULL;
	//for every rule
	for(vector<rule_t>::iterator i=rules.begin(); i != rules.end(); ++i) {
		//for every element in predict
		for(set<gelem_t,comparegelem_c>::iterator j = predict[i->n].begin(); j != predict[i->n].end(); ++j) {
			//if there already is a rule number on this position, the resulting table wouldn't be LL
			if ((ll[i->left.nt].find(j->t) != ll[i->left.nt].end())) {
				errString = string("genll: Duplicate entry at Nonterminal=") + i->left.nt + ", Terminal=" + j->t + ", NewRule=" + std::to_string(i->n) + ", OldRule=" + std::to_string(ll[i->left.nt][j->t].first);
				return ERROR_LL;
			}
			//insert the rule number into the table - line adressed by left non-terminal
			//column adressed by the current terminal in Predict
			current_pair.first = i->n;
			ll[i->left.nt][j->t] = current_pair;
		}
	}
	return retvalue;
}

unsigned int cGram::isnt(vector<string> & v_nonterminals, string nonterminal) {
	unsigned int retvalue = 0;
	unsigned int num = 0;
	for(vector<string>::iterator i=v_nonterminals.begin(); i != v_nonterminals.end(); ++i) {
		++num;
		if ((*i) == nonterminal) {
			retvalue = num;
			break;
		}
	}
	return retvalue;
}

unsigned int cGram::ist(vector<unsigned char> & v_terminals, unsigned char terminal) {
	unsigned int retvalue = 0;
	unsigned int num = 0;
	for(vector<unsigned char>::iterator i=v_terminals.begin(); i != v_terminals.end(); ++i) {
		++num;
		if ((*i) == terminal) {
			retvalue = num;
			break;
		}
	}
	return retvalue;
}

/**
 * @brief Function which generates the static code for an internal LL table.
 */
cGram::errcode cGram::genconstll() {
	errcode retvalue = ERROR_OK;
	ll.clear();
	pair<unsigned int,semact> current_pair;
	current_pair.second = SA_NULL;
	//for every rule
	for(vector<rule_t>::iterator i=rules.begin(); i != rules.end(); ++i) {
		//for every element in predict
		for(set<gelem_t,comparegelem_c>::iterator j = predict[i->n].begin(); j != predict[i->n].end(); ++j) {
			//if there already is a rule number on this position, the resulting table wouldn't be LL
			if (ll[i->left.nt].find(j->t) != ll[i->left.nt].end()) {
				errString = string("genconstll: Duplicate entry at Nonterminal=") + i->left.nt + ", Terminal=" + j->t + ", NewRule=" + std::to_string(i->n) + ", OldRule=" + std::to_string(ll[i->left.nt][j->t].first);
				return ERROR_LL;
			}
			if (ist(terminals,j->t) == 0) {
				terminals.push_back(j->t);
			}
			//insert the rule number into the table - line adressed by left non-terminal
			//column adressed by the current terminal in Predict
			current_pair.first = i->n;
			ll[i->left.nt][j->t] = current_pair;
		}
	}

	newIG_terminal_static_x = 256;
	newIG_terminal_static = "";
	//generate the terminal table - reduces the size of static LL table
	for (int i = 0; i < 256; ++i) {
		newIG_terminal_static += '\t';
		newIG_terminal_static += std::to_string(ist(terminals,static_cast<unsigned char>(i)));
		if (i != 255) {
			newIG_terminal_static += ",";
		}
		newIG_terminal_static += string("") + " // " + std::to_string(i);

		newIG_terminal_static += "\n";
	}

	//static LL table
	newIG_llst_x = nonterminals.size();
	newIG_llst_y = terminals.size()+1;
	newIG_llst = "";

	this->genllsem();
	//for every non-terminal (row)
	for (vector<string>::iterator i=nonterminals.begin(); i != nonterminals.end(); ++i) {
		if (i!=nonterminals.begin())
			newIG_llst += ",\n";
		newIG_llst += "\t{\n";
		//for every terminal (column)
		unsigned int llelCnt = 0;
		for (vector<unsigned char>::iterator j=terminals.begin(); j != terminals.end(); ++j) {
			if (j==terminals.begin()) {
				newIG_llst += "\t\t{0, cGram::SA_NULL}, ";
			}
			else {
				newIG_llst += ", ";
				if ((llelCnt + 1) % 4 == 0) {
					newIG_llst += "\n\t\t";
				}
			}
			newIG_llst += "{";
			newIG_llst += string("") + std::to_string(ll[*i][*j].first) + ", " + "cGram::" + semactname[ll[*i][*j].second];
			newIG_llst += "}";
			++llelCnt;
		}
		newIG_llst += "\n\t}";
	}
	return retvalue;
}

/**
 * @brief: Function which assigns semantic action for each cell of the LL table.
 */
void cGram::genllsem() {
	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-addchartoname"].begin(); i != ll["sem-addchartoname"].end(); ++i) {
		i->second.second = SA_ADDCHARTONAME;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-storename"].begin(); i != ll["sem-storename"].end(); ++i) {
		i->second.second = SA_STORENAME;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-loadid"].begin(); i != ll["sem-loadid"].end(); ++i) {
		i->second.second = SA_LOADID;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-loadarr"].begin(); i != ll["sem-loadarr"].end(); ++i) {
		i->second.second = SA_LOADARR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-ssno"].begin(); i != ll["sem-ssno"].end(); ++i) {
		i->second.second = SA_SSNO;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-ssnest"].begin(); i != ll["sem-ssnest"].end(); ++i) {
		i->second.second = SA_SSNEST;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-stunq"].begin(); i != ll["sem-stunq"].end(); ++i) {
		i->second.second = SA_STUNQ;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-loadtsub"].begin(); i != ll["sem-loadtsub"].end(); ++i) {
		i->second.second = SA_LOADTSUB;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-type2expr"].begin(); i != ll["sem-type2expr"].end(); ++i) {
		i->second.second = SA_TYPE2EXPR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-exprval"].begin(); i != ll["sem-exprval"].end(); ++i) {
		i->second.second = SA_EXPRVAL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-beginexpr"].begin(); i != ll["sem-beginexpr"].end(); ++i) {
		i->second.second = SA_BEGINPEXPR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-storeexpr"].begin(); i != ll["sem-storeexpr"].end(); ++i) {
		i->second.second = SA_STOREPEXPR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-copyexpr"].begin(); i != ll["sem-copyexpr"].end(); ++i) {
		i->second.second = SA_COPYTERM;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-storepar"].begin(); i != ll["sem-storepar"].end(); ++i) {
		i->second.second = SA_STOREPAR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-begintempl"].begin(); i != ll["sem-begintempl"].end(); ++i) {
		i->second.second = SA_BEGINTEMPL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-skiptempl"].begin(); i != ll["sem-skiptempl"].end(); ++i) {
		i->second.second = SA_SKIPTEMPL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-storetemparg"].begin(); i != ll["sem-storetemparg"].end(); ++i) {
		i->second.second = SA_STORETEMPARG;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-storetemplate"].begin(); i != ll["sem-storetemplate"].end(); ++i) {
		i->second.second = SA_STORETEMPLATE;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setnametf"].begin(); i != ll["sem-setnametf"].end(); ++i) {
		i->second.second = SA_SETNAMETF;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-par2f"].begin(); i != ll["sem-par2f"].end(); ++i) {
		i->second.second = SA_PAR2F;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-unq2f"].begin(); i != ll["sem-unq2f"].end(); ++i) {
		i->second.second = SA_UNQ2F;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-unq2p"].begin(); i != ll["sem-unq2p"].end(); ++i) {
		i->second.second = SA_UNQ2P;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setnamex"].begin(); i != ll["sem-setnamex"].end(); ++i) {
		i->second.second = SA_SETNAMEX;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setnameo"].begin(); i != ll["sem-setnameo"].end(); ++i) {
		i->second.second = SA_SETNAMEO;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-par2ret"].begin(); i != ll["sem-par2ret"].end(); ++i) {
		i->second.second = SA_PAR2RET;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-par2spec"].begin(); i != ll["sem-par2spec"].end(); ++i) {
		i->second.second = SA_PAR2SPEC;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypev"].begin(); i != ll["sem-settypev"].end(); ++i) {
		i->second.second = SA_SETTYPEV;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypew"].begin(); i != ll["sem-settypew"].end(); ++i) {
		i->second.second = SA_SETTYPEW;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypeb"].begin(); i != ll["sem-settypeb"].end(); ++i) {
		i->second.second = SA_SETTYPEB;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypec"].begin(); i != ll["sem-settypec"].end(); ++i) {
		i->second.second = SA_SETTYPEC;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypea"].begin(); i != ll["sem-settypea"].end(); ++i) {
		i->second.second = SA_SETTYPEA;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypeh"].begin(); i != ll["sem-settypeh"].end(); ++i) {
		i->second.second = SA_SETTYPEH;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypes"].begin(); i != ll["sem-settypes"].end(); ++i) {
		i->second.second = SA_SETTYPES;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypet"].begin(); i != ll["sem-settypet"].end(); ++i) {
		i->second.second = SA_SETTYPET;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypei"].begin(); i != ll["sem-settypei"].end(); ++i) {
		i->second.second = SA_SETTYPEI;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypej"].begin(); i != ll["sem-settypej"].end(); ++i) {
		i->second.second = SA_SETTYPEJ;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypel"].begin(); i != ll["sem-settypel"].end(); ++i) {
		i->second.second = SA_SETTYPEL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypem"].begin(); i != ll["sem-settypem"].end(); ++i) {
		i->second.second = SA_SETTYPEM;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypex"].begin(); i != ll["sem-settypex"].end(); ++i) {
		i->second.second = SA_SETTYPEX;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypey"].begin(); i != ll["sem-settypey"].end(); ++i) {
		i->second.second = SA_SETTYPEY;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypen"].begin(); i != ll["sem-settypen"].end(); ++i) {
		i->second.second = SA_SETTYPEN;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypeo"].begin(); i != ll["sem-settypeo"].end(); ++i) {
		i->second.second = SA_SETTYPEO;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypef"].begin(); i != ll["sem-settypef"].end(); ++i) {
		i->second.second = SA_SETTYPEF;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settyped"].begin(); i != ll["sem-settyped"].end(); ++i) {
		i->second.second = SA_SETTYPED;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypee"].begin(); i != ll["sem-settypee"].end(); ++i) {
		i->second.second = SA_SETTYPEE;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypeg"].begin(); i != ll["sem-settypeg"].end(); ++i) {
		i->second.second = SA_SETTYPEG;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-settypez"].begin(); i != ll["sem-settypez"].end(); ++i) {
		i->second.second = SA_SETTYPEZ;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setrestrict"].begin(); i != ll["sem-setrestrict"].end(); ++i) {
		i->second.second = SA_SETRESTRICT;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setvolatile"].begin(); i != ll["sem-setvolatile"].end(); ++i) {
		i->second.second = SA_SETVOLATILE;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setconst"].begin(); i != ll["sem-setconst"].end(); ++i) {
		i->second.second = SA_SETCONST;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setptr"].begin(); i != ll["sem-setptr"].end(); ++i) {
		i->second.second = SA_SETPTR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setref"].begin(); i != ll["sem-setref"].end(); ++i) {
		i->second.second = SA_SETREF;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setrval"].begin(); i != ll["sem-setrval"].end(); ++i) {
		i->second.second = SA_SETRVAL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setcpair"].begin(); i != ll["sem-setcpair"].end(); ++i) {
		i->second.second = SA_SETCPAIR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setim"].begin(); i != ll["sem-setim"].end(); ++i) {
		i->second.second = SA_SETIM;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-substd"].begin(); i != ll["sem-substd"].end(); ++i) {
		i->second.second = SA_SUBSTD;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-subalc"].begin(); i != ll["sem-subalc"].end(); ++i) {
		i->second.second = SA_SUBALC;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-substr"].begin(); i != ll["sem-substr"].end(); ++i) {
		i->second.second = SA_SUBSTR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-substrs"].begin(); i != ll["sem-substrs"].end(); ++i) {
		i->second.second = SA_SUBSTRS;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-subistr"].begin(); i != ll["sem-subistr"].end(); ++i) {
		i->second.second = SA_SUBISTR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-subostr"].begin(); i != ll["sem-subostr"].end(); ++i) {
		i->second.second = SA_SUBOSTR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-subiostr"].begin(); i != ll["sem-subiostr"].end(); ++i) {
		i->second.second = SA_SUBIOSTR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-loadsub"].begin(); i != ll["sem-loadsub"].end(); ++i) {
		i->second.second = SA_LOADSUB;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setnamec"].begin(); i != ll["sem-setnamec"].end(); ++i) {
		i->second.second = SA_SETNAMEC;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setnamed"].begin(); i != ll["sem-setnamed"].end(); ++i) {
		i->second.second = SA_SETNAMED;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopnw"].begin(); i != ll["sem-setopnw"].end(); ++i) {
		i->second.second = SA_SETOPNW;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopna"].begin(); i != ll["sem-setopna"].end(); ++i) {
		i->second.second = SA_SETOPNA;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopdl"].begin(); i != ll["sem-setopdl"].end(); ++i) {
		i->second.second = SA_SETOPDL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopda"].begin(); i != ll["sem-setopda"].end(); ++i) {
		i->second.second = SA_SETOPDA;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopps"].begin(); i != ll["sem-setopps"].end(); ++i) {
		i->second.second = SA_SETOPPS;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopng"].begin(); i != ll["sem-setopng"].end(); ++i) {
		i->second.second = SA_SETOPNG;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopad"].begin(); i != ll["sem-setopad"].end(); ++i) {
		i->second.second = SA_SETOPAD;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopde"].begin(); i != ll["sem-setopde"].end(); ++i) {
		i->second.second = SA_SETOPDE;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopco"].begin(); i != ll["sem-setopco"].end(); ++i) {
		i->second.second = SA_SETOPCO;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setoppl"].begin(); i != ll["sem-setoppl"].end(); ++i) {
		i->second.second = SA_SETOPPL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopmi"].begin(); i != ll["sem-setopmi"].end(); ++i) {
		i->second.second = SA_SETOPMI;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopml"].begin(); i != ll["sem-setopml"].end(); ++i) {
		i->second.second = SA_SETOPML;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopdv"].begin(); i != ll["sem-setopdv"].end(); ++i) {
		i->second.second = SA_SETOPDV;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setoprm"].begin(); i != ll["sem-setoprm"].end(); ++i) {
		i->second.second = SA_SETOPRM;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopan"].begin(); i != ll["sem-setopan"].end(); ++i) {
		i->second.second = SA_SETOPAN;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopor"].begin(); i != ll["sem-setopor"].end(); ++i) {
		i->second.second = SA_SETOPOR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopeo"].begin(); i != ll["sem-setopeo"].end(); ++i) {
		i->second.second = SA_SETOPEO;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopass"].begin(); i != ll["sem-setopass"].end(); ++i) {
		i->second.second = SA_SETOPASS;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setoppll"].begin(); i != ll["sem-setoppll"].end(); ++i) {
		i->second.second = SA_SETOPPLL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopmii"].begin(); i != ll["sem-setopmii"].end(); ++i) {
		i->second.second = SA_SETOPMII;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopmll"].begin(); i != ll["sem-setopmll"].end(); ++i) {
		i->second.second = SA_SETOPMLL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopdvv"].begin(); i != ll["sem-setopdvv"].end(); ++i) {
		i->second.second = SA_SETOPDVV;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setoprmm"].begin(); i != ll["sem-setoprmm"].end(); ++i) {
		i->second.second = SA_SETOPRMM;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopann"].begin(); i != ll["sem-setopann"].end(); ++i) {
		i->second.second = SA_SETOPANN;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setoporr"].begin(); i != ll["sem-setoporr"].end(); ++i) {
		i->second.second = SA_SETOPORR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopeoo"].begin(); i != ll["sem-setopeoo"].end(); ++i) {
		i->second.second = SA_SETOPEOO;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopls"].begin(); i != ll["sem-setopls"].end(); ++i) {
		i->second.second = SA_SETOPLS;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setoprs"].begin(); i != ll["sem-setoprs"].end(); ++i) {
		i->second.second = SA_SETOPRS;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setoplss"].begin(); i != ll["sem-setoplss"].end(); ++i) {
		i->second.second = SA_SETOPLSS;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setoprss"].begin(); i != ll["sem-setoprss"].end(); ++i) {
		i->second.second = SA_SETOPRSS;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopeq"].begin(); i != ll["sem-setopeq"].end(); ++i) {
		i->second.second = SA_SETOPEQ;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopne"].begin(); i != ll["sem-setopne"].end(); ++i) {
		i->second.second = SA_SETOPNE;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setoplt"].begin(); i != ll["sem-setoplt"].end(); ++i) {
		i->second.second = SA_SETOPLT;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopgt"].begin(); i != ll["sem-setopgt"].end(); ++i) {
		i->second.second = SA_SETOPGT;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setople"].begin(); i != ll["sem-setople"].end(); ++i) {
		i->second.second = SA_SETOPLE;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopge"].begin(); i != ll["sem-setopge"].end(); ++i) {
		i->second.second = SA_SETOPGE;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopnt"].begin(); i != ll["sem-setopnt"].end(); ++i) {
		i->second.second = SA_SETOPNT;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopaa"].begin(); i != ll["sem-setopaa"].end(); ++i) {
		i->second.second = SA_SETOPAA;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopoo"].begin(); i != ll["sem-setopoo"].end(); ++i) {
		i->second.second = SA_SETOPOO;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setoppp"].begin(); i != ll["sem-setoppp"].end(); ++i) {
		i->second.second = SA_SETOPPP;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopmm"].begin(); i != ll["sem-setopmm"].end(); ++i) {
		i->second.second = SA_SETOPMM;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopcm"].begin(); i != ll["sem-setopcm"].end(); ++i) {
		i->second.second = SA_SETOPCM;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setoppm"].begin(); i != ll["sem-setoppm"].end(); ++i) {
		i->second.second = SA_SETOPPM;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setoppt"].begin(); i != ll["sem-setoppt"].end(); ++i) {
		i->second.second = SA_SETOPPT;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopcl"].begin(); i != ll["sem-setopcl"].end(); ++i) {
		i->second.second = SA_SETOPCL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopix"].begin(); i != ll["sem-setopix"].end(); ++i) {
		i->second.second = SA_SETOPIX;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopqu"].begin(); i != ll["sem-setopqu"].end(); ++i) {
		i->second.second = SA_SETOPQU;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopst"].begin(); i != ll["sem-setopst"].end(); ++i) {
		i->second.second = SA_SETOPST;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopsz"].begin(); i != ll["sem-setopsz"].end(); ++i) {
		i->second.second = SA_SETOPSZ;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopat"].begin(); i != ll["sem-setopat"].end(); ++i) {
		i->second.second = SA_SETOPAT;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopaz"].begin(); i != ll["sem-setopaz"].end(); ++i) {
		i->second.second = SA_SETOPAZ;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopcv"].begin(); i != ll["sem-setopcv"].end(); ++i) {
		i->second.second = SA_SETOPCV;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setopxx"].begin(); i != ll["sem-setopxx"].end(); ++i) {
		i->second.second = SA_SETOPXX;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setnamef"].begin(); i != ll["sem-setnamef"].end(); ++i) {
		i->second.second = SA_SETNAMEF;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setnamevt"].begin(); i != ll["sem-setnamevt"].end(); ++i) {
		i->second.second = SA_SETNAMEVT;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-reversename"].begin(); i != ll["sem-reversename"].end(); ++i) {
		i->second.second = SA_REVERSENAME;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setprivate"].begin(); i != ll["sem-setprivate"].end(); ++i) {
		i->second.second = SA_SETPRIVATE;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setpublic"].begin(); i != ll["sem-setpublic"].end(); ++i) {
		i->second.second = SA_SETPUBLIC;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setprotected"].begin(); i != ll["sem-setprotected"].end(); ++i) {
		i->second.second = SA_SETPROTECTED;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setfcdecl"].begin(); i != ll["sem-setfcdecl"].end(); ++i) {
		i->second.second = SA_SETFCDECL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setfpascal"].begin(); i != ll["sem-setfpascal"].end(); ++i) {
		i->second.second = SA_SETFPASCAL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setffortran"].begin(); i != ll["sem-setffortran"].end(); ++i) {
		i->second.second = SA_SETFFORTRAN;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setfthiscall"].begin(); i != ll["sem-setfthiscall"].end(); ++i) {
		i->second.second = SA_SETFTHISCALL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setfstdcall"].begin(); i != ll["sem-setfstdcall"].end(); ++i) {
		i->second.second = SA_SETFSTDCALL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setffastcall"].begin(); i != ll["sem-setffastcall"].end(); ++i) {
		i->second.second = SA_SETFFASTCALL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setfinterrupt"].begin(); i != ll["sem-setfinterrupt"].end(); ++i) {
		i->second.second = SA_SETFINTERRUPT;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setunion"].begin(); i != ll["sem-setunion"].end(); ++i) {
		i->second.second = SA_SETUNION;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setstruct"].begin(); i != ll["sem-setstruct"].end(); ++i) {
		i->second.second = SA_SETSTRUCT;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setclass"].begin(); i != ll["sem-setclass"].end(); ++i) {
		i->second.second = SA_SETCLASS;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setenum"].begin(); i != ll["sem-setenum"].end(); ++i) {
		i->second.second = SA_SETENUM;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setstatic"].begin(); i != ll["sem-setstatic"].end(); ++i) {
		i->second.second = SA_SETSTATIC;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setvirtual"].begin(); i != ll["sem-setvirtual"].end(); ++i) {
		i->second.second = SA_SETVIRTUAL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-stclconst"].begin(); i != ll["sem-stclconst"].end(); ++i) {
		i->second.second = SA_STCLCONST;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-stclvol"].begin(); i != ll["sem-stclvol"].end(); ++i) {
		i->second.second = SA_STCLVOL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-stclfar"].begin(); i != ll["sem-stclfar"].end(); ++i) {
		i->second.second = SA_STCLFAR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-stclhuge"].begin(); i != ll["sem-stclhuge"].end(); ++i) {
		i->second.second = SA_STCLHUGE;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-savenamesub"].begin(); i != ll["sem-savenamesub"].end(); ++i) {
		i->second.second = SA_SAVENAMESUB;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-loadnamesub"].begin(); i != ll["sem-loadnamesub"].end(); ++i) {
		i->second.second = SA_LOADNAMESUB;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-mstemplsub"].begin(); i != ll["sem-mstemplsub"].end(); ++i) {
		i->second.second = SA_MSTEMPLPSUB;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setnamer0"].begin(); i != ll["sem-setnamer0"].end(); ++i) {
		i->second.second = SA_SETNAMER0;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setnamer1"].begin(); i != ll["sem-setnamer1"].end(); ++i) {
		i->second.second = SA_SETNAMER1;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setnamer2"].begin(); i != ll["sem-setnamer2"].end(); ++i) {
		i->second.second = SA_SETNAMER2;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setnamer3"].begin(); i != ll["sem-setnamer3"].end(); ++i) {
		i->second.second = SA_SETNAMER3;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setnamer4"].begin(); i != ll["sem-setnamer4"].end(); ++i) {
		i->second.second = SA_SETNAMER4;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_a"].begin(); i != ll["sem-setname_a"].end(); ++i) {
		i->second.second = SA_SETNAME_A;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_b"].begin(); i != ll["sem-setname_b"].end(); ++i) {
		i->second.second = SA_SETNAME_B;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_c"].begin(); i != ll["sem-setname_c"].end(); ++i) {
		i->second.second = SA_SETNAME_C;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_d"].begin(); i != ll["sem-setname_d"].end(); ++i) {
		i->second.second = SA_SETNAME_D;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_e"].begin(); i != ll["sem-setname_e"].end(); ++i) {
		i->second.second = SA_SETNAME_E;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_f"].begin(); i != ll["sem-setname_f"].end(); ++i) {
		i->second.second = SA_SETNAME_F;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_g"].begin(); i != ll["sem-setname_g"].end(); ++i) {
		i->second.second = SA_SETNAME_G;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_h"].begin(); i != ll["sem-setname_h"].end(); ++i) {
		i->second.second = SA_SETNAME_H;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_i"].begin(); i != ll["sem-setname_i"].end(); ++i) {
		i->second.second = SA_SETNAME_I;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_j"].begin(); i != ll["sem-setname_j"].end(); ++i) {
		i->second.second = SA_SETNAME_J;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_k"].begin(); i != ll["sem-setname_k"].end(); ++i) {
		i->second.second = SA_SETNAME_K;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_l"].begin(); i != ll["sem-setname_l"].end(); ++i) {
		i->second.second = SA_SETNAME_L;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_m"].begin(); i != ll["sem-setname_m"].end(); ++i) {
		i->second.second = SA_SETNAME_M;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_n"].begin(); i != ll["sem-setname_n"].end(); ++i) {
		i->second.second = SA_SETNAME_N;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_o"].begin(); i != ll["sem-setname_o"].end(); ++i) {
		i->second.second = SA_SETNAME_O;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_p"].begin(); i != ll["sem-setname_p"].end(); ++i) {
		i->second.second = SA_SETNAME_P;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_q"].begin(); i != ll["sem-setname_q"].end(); ++i) {
		i->second.second = SA_SETNAME_Q;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_r"].begin(); i != ll["sem-setname_r"].end(); ++i) {
		i->second.second = SA_SETNAME_R;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_s"].begin(); i != ll["sem-setname_s"].end(); ++i) {
		i->second.second = SA_SETNAME_S;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_t"].begin(); i != ll["sem-setname_t"].end(); ++i) {
		i->second.second = SA_SETNAME_T;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_u"].begin(); i != ll["sem-setname_u"].end(); ++i) {
		i->second.second = SA_SETNAME_U;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_v"].begin(); i != ll["sem-setname_v"].end(); ++i) {
		i->second.second = SA_SETNAME_V;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_w"].begin(); i != ll["sem-setname_w"].end(); ++i) {
		i->second.second = SA_SETNAME_W;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_x"].begin(); i != ll["sem-setname_x"].end(); ++i) {
		i->second.second = SA_SETNAME_X;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_y"].begin(); i != ll["sem-setname_y"].end(); ++i) {
		i->second.second = SA_SETNAME_Y;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-setname_z"].begin(); i != ll["sem-setname_z"].end(); ++i) {
		i->second.second = SA_SETNAME_Z;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-templ2tftpl"].begin(); i != ll["sem-templ2tftpl"].end(); ++i) {
		i->second.second = SA_TEMPL2TFTPL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-beginbsub"].begin(); i != ll["sem-beginbsub"].end(); ++i) {
		i->second.second = SA_BEGINBSUB;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-loadbsub"].begin(); i != ll["sem-loadbsub"].end(); ++i) {
		i->second.second = SA_LOADBSUB;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-addmconst"].begin(); i != ll["sem-addmconst"].end(); ++i) {
		i->second.second = SA_ADDMCONST;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-addmvol"].begin(); i != ll["sem-addmvol"].end(); ++i) {
		i->second.second = SA_ADDMVOL;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-addmfar"].begin(); i != ll["sem-addmfar"].end(); ++i) {
		i->second.second = SA_ADDMFAR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-addmhuge"].begin(); i != ll["sem-addmhuge"].end(); ++i) {
		i->second.second = SA_ADDMHUGE;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-loadmsnum"].begin(); i != ll["sem-loadmsnum"].end(); ++i) {
		i->second.second = SA_LOADMSNUM;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-numtorttibcd"].begin(); i != ll["sem-numtorttibcd"].end(); ++i) {
		i->second.second = SA_NUMTORTTIBCD;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-numtotype"].begin(); i != ll["sem-numtotype"].end(); ++i) {
		i->second.second = SA_NUMTOTYPE;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-normalizeparname"].begin(); i != ll["sem-normalizeparname"].end(); ++i) {
		i->second.second = SA_BORLANDNORMALIZEPARNAME;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-borlandid"].begin(); i != ll["sem-borlandid"].end(); ++i) {
		i->second.second = SA_BORLANDID;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-loadborlandsub"].begin(); i != ll["sem-loadborlandsub"].end(); ++i) {
		i->second.second = SA_LOADBORLANDSUB;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-borlandarr"].begin(); i != ll["sem-borlandarr"].end(); ++i) {
		i->second.second = SA_BORLANDARR;
	}

	for(map<char,pair<unsigned int, semact>>::iterator i=ll["sem-end"].begin(); i != ll["sem-end"].end(); ++i) {
		i->second.second = SA_END;
	}

}

/**
 * @brief Function which converts external grammar into internal grammar. No analysis may be done after using this function.
 * @param inputfilename The name of the file which contains grammar rules.
 * @param outputname The name of the output grammar.
 * @return Error code.
 */
cGram::errcode cGram::generateIgrammar(const string inputfilename, const string outputname) {
	errcode retvalue = ERROR_OK;

	//if the output files already exist, end with error. overwriting is not allowed
	if (fileExists("stgrammars/"+outputname+"ll.cpp")) {
		errString = string("") + "Igrammar file " + "stgrammars/"+outputname+"ll.cpp" + " already exists.";
		return ERROR_FILE;
	}
	if (fileExists("stgrammars/"+outputname+"ll.h")) {
		errString = string("") + "Igrammar file " + "stgrammars/"+outputname+"ll.h" + " already exists.";
		return ERROR_FILE;
	}

	//get current date/time
	sdate_t createDate = genTimeStruct();

	//set the variable that triggers igrammar construction during LL table creation (in getgrammar() and genconstll())
	createIGrammar = outputname;

	//get the rules for external grammar
	//sets newIG_ruleaddrs and newIG_ruleelements
	if ((retvalue = getgrammar(inputfilename)) != ERROR_OK) {return retvalue;}
	delete source;

	//standard steps to achieve LL table generation
	genempty();
	genfirst();
	genfollow();
	genpredict();

	//generate LL table into strings. sets newIG_terminal_static and newIG_llst
	if ((retvalue = genconstll()) != ERROR_OK) {return retvalue;}

	//generate newIG_root from left nt of first rule
	newIG_root = string("") + "cGram::GE_NONTERM,const_cast<char *>(\"" + string(rules[0].left.nt) + "\"),0,\'\\0\'";

	//output filestreams for the new .cpp and .h files
	ofstream ofsIgCpp;
	ofstream ofsIgH;
	//open .cpp for writing
	ofsIgCpp.open(("stgrammars/"+outputname+"ll.cpp").c_str());
	if (!ofsIgCpp.is_open()) {
		errString = string("") + "Could not open " + "stgrammars/"+outputname+"ll.cpp" + " for writing.";
		return ERROR_FILE;
	}
	//open .h for writing
	ofsIgH.open(("stgrammars/"+outputname+"ll.h").c_str());
	if (!ofsIgH.is_open()) {
		errString = string("") + "Could not open " + "stgrammars/"+outputname+"ll.h" + " for writing.";
		return ERROR_FILE;
	}

	stringstream date;
	date << createDate.y << "-" << setfill('0') << setw(2) << createDate.m << "-" << createDate.d;

	/*
	 * Generate .cpp header
	 */
	ofsIgCpp << "/**\n";
	ofsIgCpp << "* @file   demangler/stgrammars/" << outputname << "ll.cpp\n";
	ofsIgCpp << "* @author Internal Grammar Generator\n";
	ofsIgCpp << "* @brief  Internal LL grammar for demangler.\n";
	ofsIgCpp << "* @date   " << createDate.y << "-" << setfill('0') << setw(2) << createDate.m << "-" << createDate.d << "\n";
	ofsIgCpp << "* @copyright (c) 2017 Avast Software, licensed under the MIT license\n";
	ofsIgCpp << "*/\n";

	/*
	 * Generate .h header
	 */
	ofsIgH << "/**\n";
	ofsIgH << "* @file   demangler/stgrammars/" << outputname << "ll.h\n";
	ofsIgH << "* @author Internal Grammar Generator\n";
	ofsIgH << "* @brief  Internal LL grammar for demangler.\n";
	ofsIgH << "* @date   " << createDate.y << "-" << setfill('0') << setw(2) << createDate.m << "-" << createDate.d << "\n";
	ofsIgH << "* @copyright (c) 2017 Avast Software, licensed under the MIT license\n";
	ofsIgH << "*/\n";

	/*
	 * Generate .h content
	 */
	//generate <outputname>LL_H_
	string hDef = outputname;
	transform(hDef.begin(), hDef.end(), hDef.begin(), ::toupper);
	hDef += "LL_H_";

	//.h body
	ofsIgH << "\n";
	ofsIgH << "#ifndef " << hDef << "\n";
	ofsIgH << "#define " << hDef << "\n";
	ofsIgH << "\n";
	ofsIgH << "#include \"demangler/gparser.h\"\n";
	ofsIgH << "\n";
	ofsIgH << "namespace demangler\n";
	ofsIgH << "{\n";
	ofsIgH << "\n";
	ofsIgH << "class cIgram_" << outputname << "ll {\n";
	ofsIgH << "public:\n";
	ofsIgH << "\tstatic unsigned char terminal_static[" << newIG_terminal_static_x << "];\n";
	ofsIgH << "\tstatic cGram::llelem_t llst[" << newIG_llst_x << "][" << newIG_llst_y << "];\n";
	ofsIgH << "\tstatic cGram::ruleaddr_t ruleaddrs[" << newIG_ruleaddrs_x << "];\n";
	ofsIgH << "\tstatic cGram::gelem_t ruleelements[" << newIG_ruleelements_x << "];\n";
	ofsIgH << "\tstatic cGram::gelem_t root;\n";
	ofsIgH << "\tcGram::igram_t getInternalGrammar();\n";
	ofsIgH << "};\n";
	ofsIgH << "\n";
	ofsIgH << "} /* namespace demangler */\n";
	ofsIgH << "#endif /* " << hDef << " */\n";

	/*
	 * Generate .cpp content
	 */
	ofsIgCpp << "\n";
	ofsIgCpp << "#include <stdlib.h>\n";
	ofsIgCpp << "#include \"" << outputname << "ll.h\"\n";
	ofsIgCpp << "\n";
	ofsIgCpp << "namespace demangler\n";
	ofsIgCpp << "{\n";
	ofsIgCpp << "\n";
	ofsIgCpp << "/**\n";
	ofsIgCpp << " * @brief Static version of the root element.\n";
	ofsIgCpp << " */\n";
	ofsIgCpp << "cGram::gelem_t cIgram_" << outputname << "ll::root = {" << newIG_root << "};\n";
	ofsIgCpp << "\n";
	ofsIgCpp << "/**\n";
	ofsIgCpp << " * @brief Static table of used terminals. Used to reduce size of static LL table.\n";
	ofsIgCpp << " */\n";
	ofsIgCpp << "unsigned char cIgram_" << outputname << "ll::terminal_static[" << newIG_terminal_static_x << "] = {\n";
	ofsIgCpp << newIG_terminal_static << "};\n";
	ofsIgCpp << "\n";
	ofsIgCpp << "/**\n";
	ofsIgCpp << " * @brief Static adress table for grammar elements in rules. First value is offset from the start of ruleelements array, second value is the number of elements in the current rule.\n";
	ofsIgCpp << " */\n";
	ofsIgCpp << "cGram::ruleaddr_t cIgram_" << outputname << "ll::ruleaddrs[" << newIG_ruleaddrs_x << "] = {";
	ofsIgCpp << newIG_ruleaddrs;
	ofsIgCpp << "};\n";
	ofsIgCpp << "\n";
	ofsIgCpp << "/**\n";
	ofsIgCpp << " * @brief Static table of grammar elements in all rules.\n";
	ofsIgCpp << " */\n";
	ofsIgCpp << "cGram::gelem_t cIgram_" << outputname << "ll::ruleelements[" << newIG_ruleelements_x << "] = {\n";
	ofsIgCpp << newIG_ruleelements;
	ofsIgCpp << "\n};\n";
	ofsIgCpp << "\n";
	ofsIgCpp << "/**\n";
	ofsIgCpp << " * @brief Static LL table\n";
	ofsIgCpp << " */\n";
	ofsIgCpp << "cGram::llelem_t cIgram_" << outputname << "ll::llst[" << newIG_llst_x << "][" << newIG_llst_y << "] = {\n";
	ofsIgCpp << newIG_llst;
	ofsIgCpp << "\n};\n";
	ofsIgCpp << "\n";
	ofsIgCpp << "/**\n";
	ofsIgCpp << " * @brief Export internal grammar data for use in demangler.\n";
	ofsIgCpp << " * @return igram_t structure containing the internal grammar data.\n";
	ofsIgCpp << " */\n";
	ofsIgCpp << "cGram::igram_t cIgram_" << outputname << "ll::getInternalGrammar() {\n";
	ofsIgCpp << "\tcGram::igram_t retvalue = {\n";
	ofsIgCpp << "\t\t" << newIG_terminal_static_x << ", //terminal_static_x\n";
	ofsIgCpp << "\t\t" << newIG_ruleaddrs_x << ", //ruleaddrs_x\n";
	ofsIgCpp << "\t\t" << newIG_ruleelements_x << ", //ruleelements_x\n";
	ofsIgCpp << "\t\t" << newIG_llst_x << ", //llst_x\n";
	ofsIgCpp << "\t\t" << newIG_llst_y << ", //llst_y\n";
	ofsIgCpp << "\t\troot,\n";
	ofsIgCpp << "\t\tterminal_static,\n";
	ofsIgCpp << "\t\truleaddrs,\n";
	ofsIgCpp << "\t\truleelements,\n";
	ofsIgCpp << "\t\tNULL\n";
	ofsIgCpp << "\t};\n";
	ofsIgCpp << "\t\n";
	ofsIgCpp << "\t//dynamic allocation of the llst\n";
	ofsIgCpp << "\tretvalue.llst = static_cast<cGram::llelem_t**>(malloc(sizeof(cGram::llelem_t*) * retvalue.llst_x));\n";
	ofsIgCpp << "\tfor (unsigned int i=0; i<retvalue.llst_x; i++) {\n";
	ofsIgCpp << "\t\tretvalue.llst[i] = llst[i];\n";
	ofsIgCpp << "\t}\n";
	ofsIgCpp << "\t\n";
	ofsIgCpp << "\treturn retvalue;\n";
	ofsIgCpp << "}\n";
	ofsIgCpp << "\n";
	ofsIgCpp << "} /* namespace demangler */\n";

	//clean up the output file streams
	ofsIgCpp.close();
	ofsIgH.close();
	return retvalue;
}

/**
 * @brief Function which calls all the generating functions to transform input grammar file into an LL table.
 * @param filename The name of the file which contains grammar rules
 * @return Error code.
 */
cGram::errcode cGram::parse(const string filename) {
	errcode retvalue = ERROR_OK;

	//get the rules for external grammar
	if ((retvalue = getgrammar(filename)) != ERROR_OK) {return retvalue;}
	delete source;

	genempty();
	genfirst();
	genfollow();
	genpredict();
	retvalue = genll();
	if (retvalue != ERROR_OK) {
		return retvalue;
	}
	genllsem();

	return retvalue;
}

/**
 * @brief Function which creates template parameter sequence for SA_SUBSTRS.
 * @param pName Reference to any object of cName class, of which the type_t clear function is used.
 * @return Void type pointer to template of SA_SUBSTRS (vector of parameters).
 */
void *cGram::getbstpl(cName & pName) {
	vector<cName::type_t> return_vector;
	vector<cName::type_t> temp_type_vector;
	vector<cName::name_t> temp_name_vector;
	cName::name_t temp_name;
	cName::type_t temp_type;
	pName.type_t_clear(temp_type);

	//first parameter "char" is inserted into final vector and also into temp vector
	temp_type.type = cName::TT_BUILTIN;
	temp_type.b = cName::T_CHAR;
	return_vector.push_back(temp_type);
	temp_type_vector.push_back(temp_type);
	pName.type_t_clear(temp_type);

	//second parameter
	temp_name_vector.clear();
	temp_name.tpl = nullptr;
	temp_name.un = "std";
	temp_name_vector.push_back(temp_name);
	temp_name.un = "char_traits";
	temp_name.tpl = static_cast<void *>(new vector<cName::type_t>(temp_type_vector));
	temp_name_vector.push_back(temp_name);
	temp_type.type = cName::TT_NAME;
	temp_type.n = temp_name_vector;
	return_vector.push_back(temp_type);
	pName.type_t_clear(temp_type);

	//third parameter
	temp_name_vector.clear();
	temp_name.tpl = nullptr;
	temp_name.un = "std";
	temp_name_vector.push_back(temp_name);
	temp_name.un = "allocator";
	temp_name.tpl = static_cast<void *>(new vector<cName::type_t>(temp_type_vector));
	temp_name_vector.push_back(temp_name);
	temp_type.type = cName::TT_NAME;
	temp_type.n = temp_name_vector;
	return_vector.push_back(temp_type);
	pName.type_t_clear(temp_type);

	return static_cast<void *>(new vector<cName::type_t>(return_vector));
}

/**
 * @brief Function which creates template parameter sequence for basic stream template.
 * @param pName Reference to any object of cName class, of which the type_t clear function is used.
 * @return Void type pointer the template (vector of parameters).
 */
void *cGram::getstrtpl(cName & pName) {
	vector<cName::type_t> return_vector;
	vector<cName::type_t> temp_type_vector;
	vector<cName::name_t> temp_name_vector;
	cName::name_t temp_name;
	cName::type_t temp_type;
	pName.type_t_clear(temp_type);

	//first parameter "char" is inserted into final vector and also into temp vector
	temp_type.type = cName::TT_BUILTIN;
	temp_type.b = cName::T_CHAR;
	return_vector.push_back(temp_type);
	temp_type_vector.push_back(temp_type);
	pName.type_t_clear(temp_type);

	//second parameter
	temp_name_vector.clear();
	temp_name.tpl = nullptr;
	temp_name.un = "std";
	temp_name_vector.push_back(temp_name);
	temp_name.un = "char_traits";
	temp_name.tpl = static_cast<void *>(new vector<cName::type_t>(temp_type_vector));
	temp_name_vector.push_back(temp_name);
	temp_type.type = cName::TT_NAME;
	temp_type.n = temp_name_vector;
	return_vector.push_back(temp_type);
	pName.type_t_clear(temp_type);

	return static_cast<void *>(new vector<cName::type_t>(return_vector));
}

/**
 * @brief Function which determines whether a substitution already exists in given substitution vector.
 * @param candidate Candidate substitution string.
 * @param vec Vector of existing substitutions.
 * @return "Does 'candidate' already exist in 'vec'?"
 */
bool cGram::issub(string candidate,vector<string> & vec) {
	bool retvalue = false;
	if (candidate == "") {
		return true;
	}
	//TODO hacking for built-in substitutions
	if (candidate == "3std" || candidate == "3std9allocator" || candidate == "3std6string") {
		return true;
	}


	for(vector<string>::iterator i=vec.begin(); i != vec.end(); ++i) {
		if (candidate == (*i)) {
			retvalue = true;
			break;
		}
	}
	return retvalue;
}

/**
 * @brief Function which prints existing substitutions.
 * @param vec Vector of substitutions to be printed.
 */
void cGram::showsubs(vector<string> & vec) {
	unsigned int count = 0;
	for(vector<string>::iterator i=vec.begin(); i != vec.end(); ++i) {
		cout << "S";
		if (count != 0) {
			cout << count-1;
		}
		cout << "_: " << (*i) << endl;
		++count;
	}
}

/**
 * @brief Function which converts a Base36 number to long int.
 * @param x String containing the Base36 number (0-9, A-Z). Unknown symbols are ignored.
 * @return Long int converted from the Base36 number.
 * @retval -1 The Base36 string was empty.
 */
long int cGram::b36toint(string x) {
	long int retvalue = 0;
	//return -1 if string is empty
	if (x.empty()) {
		return -1;
	}
	//cycle through the string
	for (size_t i=0; i<x.length();++i) {
		retvalue *= 36;
		//0-9
		if (x[i] >= '0' && x[i] <= '9') {
			retvalue += x[i] - '0';
		}
		//A-Z
		else if (x[i] >= 'A' && x[i] <= 'Z') {
			retvalue += 10 + x[i] - 'A';
		}
		//ignore incorrect symbols
	}
	return retvalue;
}

cGram::llelem_t cGram::getllpair(string nt, unsigned int ntst, unsigned char t) {
	llelem_t retvalue;

	//internal grammar
	if (internalGrammar) {
		retvalue = internalGrammarStruct.llst[ntst][internalGrammarStruct.terminal_static[t]];
	}
	//external grammar
	else {
		retvalue.n = ll[nt][t].first;
		retvalue.s = ll[nt][t].second;
	}
	return retvalue;
}

/**
 * @brief The pre-analyzer which expands substitutions.
 * @param input The mangled name.
 * @param err Pointer to error code.
 * @return Input string with expanded substitutions.
 */
string cGram::subanalyze(const string input, cGram::errcode *err) {
	string retvalue;

	string current_part;
	bool last_rule = false;
	stack<gelem_t> elemstack;
	size_t position = 0;
	bool builtin = false;
	bool pexpr = false;
	subtype current_st = ST_NULL;
	unsigned int current_recursion = 0;
	bool fname = true;
	bool subbing = false; //if true, all other subs will be ignored
	bool subchange = true;
	unsigned int sub_recursion = 0; //used as temporary to determine in which level the sub is expanded
	string current_sub_id;
	string current_sub;
	string current_array;
	string current_templ_sub;
	string current_name;
	string current_unq_name;

	string current_param;
	string current_param_modifiers;
	string current_params;

	string current_retvalue;
	string current_input;

	stack<string> name_stack;
	stack<string> paramm_stack;
	stack<string> params_stack;
	stack<vector<string>> arrays_stack;
	vector<string> template_subs;
	vector<string> substitutions;

	vector<string> arrays;

	//grammar element from the top of stack
	gelem_t current_element;
	//vector of rules used during the analysis
	vector<unsigned int> used_rules;
	//currently used rule. contains the semantic action as well
	llelem_t current_rule;

	//id length used for loading IDs
	unsigned int current_id_length = 0;

	//current symbol of the analyzed string.
	char current_char = input[position];

	current_input = input;
	//the loop for multiple subanalyze passes
	while (subchange) {
		current_part.clear();
		last_rule = false;
		while (!elemstack.empty()) {elemstack.pop();}
		position = 0;
		builtin = false;
		fname = true;
		current_st = ST_NULL;
		current_recursion = 0;
		subbing = false;
		pexpr = false;
		sub_recursion = 0;
		current_id_length = 0;
		current_sub_id.clear();
		current_sub.clear();
		current_array.clear();
		current_name.clear();
		current_unq_name.clear();
		current_param.clear();
		current_param_modifiers.clear();
		current_param_modifiers.clear();
		current_params.clear();
		current_templ_sub.clear();
		template_subs.clear();
		while (!name_stack.empty()) {name_stack.pop();}
		while (!paramm_stack.empty()) {paramm_stack.pop();}
		while (!params_stack.empty()) {params_stack.pop();}
		while (!arrays_stack.empty()) {arrays_stack.pop();}
		arrays.clear();
		used_rules.clear();
		subchange = false;

		//insert the root NT into the stack
		if (internalGrammar) {
			elemstack.push(internalGrammarStruct.root);
		}
		else {
			elemstack.push(rules[0].left);
		}

		current_char = input[position];

		//the main loop of one pass
		while (!last_rule && *err == ERROR_OK) {
			if (elemstack.empty()) {
					errString = "cGram::subanalyze: Syntax error: elemstack empty ";
					*err = ERROR_SYN;
					break;
			}
			//load the current character of the input string
			if (position == current_input.length()) {
				current_char = '\0';
			}
			else {
				current_char = current_input[position];
			}
			current_element = elemstack.top();
			elemstack.pop();

			//top of stack is a non-terminal
			if (current_element.type == GE_NONTERM) {
				//load the rule number for current NT and T and check for syntax error
				if ((current_rule = getllpair(current_element.nt, current_element.ntst, current_char)).n == 0) {
					errString = string("cGram::subanalyze: Syntax error: No rule for NT ") + current_element.nt + " and T " + current_char + ".";
					*err = ERROR_SYN;
					break;
				}
				//add the currently used rule to the vector of used rules
				else {
					used_rules.push_back(current_rule.n);
				}

				//internal grammar
				if (internalGrammar) {
					for(unsigned int i=internalGrammarStruct.ruleaddrs[current_rule.n-1].size; i>0; i--) {
						elemstack.push(internalGrammarStruct.ruleelements[internalGrammarStruct.ruleaddrs[current_rule.n-1].offset + (i-1)]);
					}
				}
				//external grammar
				else {
					//push right side of the used rule into the stack
					for(vector<gelem_t>::reverse_iterator i = rules[current_rule.n-1].right.rbegin(); i != rules[current_rule.n-1].right.rend(); ++i) {
						elemstack.push(*i);
					}
				}


				//semantic action
				switch (current_rule.s) {
					//do nothing
					case SA_NULL:
						break;

					case SA_LOADARR:
						//remove the number terminal and "_" from element stack
						elemstack.pop();
						elemstack.pop();
						current_id_length = 0;
						current_array.clear();
						current_array += 'A';

						while (current_input[position] != '_') {
							if (position == current_input.length()) {
								errString = "cGram::subanalyze: Syntax error: Unexpected end of array.";
								*err = ERROR_SYN;
								break;
							}
							if (!isdigit(current_input[position])) {
								errString = string("") + "cGram::subanalyze: Syntax error: Unknown array symbol " + current_input[position] + ".";
								*err = ERROR_SYN;
								break;
							}
							current_id_length *= 10;
							current_id_length += current_input[position] - '0';
							current_array += current_input[position];
							current_part += current_input[position];
							++position;
						}
						if (*err != ERROR_OK) {
							break;
						}
						//_
						current_array += current_input[position];
						current_part += current_input[position];
						++position;

						if (!subbing) {
							arrays.push_back(current_array);
							current_params += current_array;
						}

						break;

					case SA_SETNAMEC:
					case SA_SETNAMED:
					case SA_SETNAMEO:
						//save previous names as sub
						if (!subbing && !issub(current_name,substitutions)) {
							substitutions.push_back(current_name);
						}
						break;

					case SA_LOADID:
						//remove the number terminal from element stack
						elemstack.pop();
						current_id_length = 0;

						//save previous names as sub
						if (!subbing && !issub(current_name,substitutions)) {
							substitutions.push_back(current_name);
						}

						//load the length of ID
						while (isdigit(current_input[position])) {
							if (position == current_input.length()) {
								errString = "cGram::subanalyze: Syntax error: Unexpected end of identifier length.";
								*err = ERROR_SYN;
								break;
							}
							current_unq_name += current_input[position];
							current_part += current_input[position];
							current_id_length *= 10;
							current_id_length += (current_input[position++] - '0');
						}
						if (*err != ERROR_OK) {
							break;
						}

						//load the ID
						for (unsigned int i = 0; i < current_id_length; ++i) {
							if (position == current_input.length()) {
								errString = string("") + "cGram::subanalyze: Syntax error: Unexpected end of identifier " + current_unq_name + ".";
								*err = ERROR_SYN;
								break;
							}
							current_part += current_input[position];
							current_unq_name += current_input[position++];
						}
						if (*err != ERROR_OK) {
							break;
						}

						current_name += current_unq_name;
						current_unq_name.clear();

						//synchronize current_char to current_input[position]
						if (position == current_input.length()) {
							current_char = '\0';
						}
						else {
							current_char = current_input[position];
						}
						break;

					//last rule - successfully end
					case SA_END:
						current_retvalue = current_part;
						last_rule = true;
						break;

					case SA_UNQ2F:
						fname = false;
						current_name.clear();
						current_param_modifiers.clear();
						current_params.clear();
						break;

					case SA_STOREPAR:
						if (!builtin) {
							//save sub
							if (!subbing && !issub(current_name,substitutions)) {
								substitutions.push_back(current_name);
							}
							current_params += current_param_modifiers;
							current_params += "N";
							current_params += current_name;
							current_params += "E";
							current_name = "N"+current_name+"E";
						}
						while (!subbing && !current_param_modifiers.empty()) {
							current_name = current_param_modifiers[current_param_modifiers.length()-1]+current_name;
							if (!current_param_modifiers.empty() && !issub('\0'+current_name,substitutions)) {
								substitutions.push_back('\0'+current_name);
							}
							current_param_modifiers = current_param_modifiers.substr(0,current_param_modifiers.length()-1);
						}

						while(!subbing && !arrays.empty()) {
							if (!issub('\0'+arrays.back()+current_name,substitutions)) {
								substitutions.push_back('\0'+arrays.back()+current_name);
							}
							current_name = arrays.back()+current_name;
							arrays.pop_back();
						}

						current_param_modifiers.clear();
						current_name.clear();
						builtin = false;


						break;

					case SA_SETOPNW:
					case SA_SETOPNA:
					case SA_SETOPDL:
					case SA_SETOPDA:
					case SA_SETOPPS:
					case SA_SETOPNG:
					case SA_SETOPAD:
					case SA_SETOPDE:
					case SA_SETOPCO:
					case SA_SETOPPL:
					case SA_SETOPMI:
					case SA_SETOPML:
					case SA_SETOPDV:
					case SA_SETOPRM:
					case SA_SETOPAN:
					case SA_SETOPOR:
					case SA_SETOPEO:
					case SA_SETOPASS:
					case SA_SETOPPLL:
					case SA_SETOPMII:
					case SA_SETOPMLL:
					case SA_SETOPDVV:
					case SA_SETOPRMM:
					case SA_SETOPANN:
					case SA_SETOPORR:
					case SA_SETOPEOO:
					case SA_SETOPLS:
					case SA_SETOPRS:
					case SA_SETOPLSS:
					case SA_SETOPRSS:
					case SA_SETOPEQ:
					case SA_SETOPNE:
					case SA_SETOPLT:
					case SA_SETOPGT:
					case SA_SETOPLE:
					case SA_SETOPGE:
					case SA_SETOPNT:
					case SA_SETOPAA:
					case SA_SETOPOO:
					case SA_SETOPPP:
					case SA_SETOPMM:
					case SA_SETOPCM:
					case SA_SETOPPM:
					case SA_SETOPPT:
					case SA_SETOPCL:
					case SA_SETOPIX:
					case SA_SETOPQU:
					case SA_SETOPST:
					case SA_SETOPSZ:
					case SA_SETOPAT:
					case SA_SETOPAZ:
					case SA_SETOPCV:
						//save previous names as sub
						if (!subbing && !issub(current_name,substitutions)) {
							substitutions.push_back(current_name);
						}
						current_name += current_part.substr(current_part.length()-1);
						current_name += current_char;
						template_subs.clear();
						break;

					case SA_SETTYPEV:
					case SA_SETTYPEW:
					case SA_SETTYPEB:
					case SA_SETTYPEC:
					case SA_SETTYPEA:
					case SA_SETTYPEH:
					case SA_SETTYPES:
					case SA_SETTYPET:
					case SA_SETTYPEI:
					case SA_SETTYPEJ:
					case SA_SETTYPEL:
					case SA_SETTYPEM:
					case SA_SETTYPEX:
					case SA_SETTYPEY:
					case SA_SETTYPEN:
					case SA_SETTYPEO:
					case SA_SETTYPEF:
					case SA_SETTYPED:
					case SA_SETTYPEE:
					case SA_SETTYPEG:
					case SA_SETTYPEZ:
						if (!pexpr) {
							current_params += current_param_modifiers;
							current_params += current_char;
						}
						current_name += current_char;
						builtin = true;
						break;
					case SA_BEGINTEMPL:
						++current_recursion;
						//clear current template substitutions
						if (current_recursion == 1 && fname) {
							template_subs.clear();
						}
						//save sub
						if (!subbing && !issub(current_name,substitutions)) {
							substitutions.push_back(current_name);
						}
						name_stack.push(current_name);
						current_name.clear();
						paramm_stack.push(current_param_modifiers);
						current_param_modifiers.clear();
						params_stack.push(current_params);
						current_params.clear();
						arrays_stack.push(arrays);
						arrays.clear();
						break;

					case SA_STORETEMPARG:
						if (!builtin) {
							//save sub
							if (!subbing && !issub(current_name,substitutions)) {
								substitutions.push_back(current_name);
							}
							current_params += current_param_modifiers;
							current_params += "N";
							current_params += current_name;
							current_params += "E";
							current_name = "N"+current_name+"E";
						}

						if (current_recursion == 1 && fname) {
							template_subs.push_back(current_param_modifiers+current_name);
						}

						while (!subbing && !current_param_modifiers.empty()) {
							current_name = current_param_modifiers[current_param_modifiers.length()-1]+current_name;
							if (!current_param_modifiers.empty() && !issub('\0'+current_name,substitutions)) {
								substitutions.push_back('\0'+current_name);
							}
							current_param_modifiers = current_param_modifiers.substr(0,current_param_modifiers.length()-1);
						}

						while(!subbing && !arrays.empty()) {
							if (!issub('\0'+arrays.back()+current_name,substitutions)) {
								substitutions.push_back('\0'+arrays.back()+current_name);
							}
							current_name = arrays.back()+current_name;
							arrays.pop_back();
						}

						current_param_modifiers.clear();
						current_name.clear();
						builtin = false;
						pexpr = false;
						break;

					case SA_TEMPL2TFTPL:
					case SA_STORETEMPLATE:
						if (!builtin) {
							//save sub
							if (!subbing && !issub(current_name,substitutions)) {
								substitutions.push_back(current_name);
							}
							current_params += current_param_modifiers;
							current_params += "N";
							current_params += current_name;
							current_params += "E";
							current_name = "N"+current_name+"E";
						}

						if (current_recursion == 1 && fname) {
							template_subs.push_back(current_param_modifiers+current_name);
						}

						while (!subbing && !current_param_modifiers.empty()) {
							current_name = current_param_modifiers[current_param_modifiers.length()-1]+current_name;
							if (!current_param_modifiers.empty() && !issub('\0'+current_name,substitutions)) {
								substitutions.push_back('\0'+current_name);
							}
							current_param_modifiers = current_param_modifiers.substr(0,current_param_modifiers.length()-1);
						}

						while(!subbing && !arrays.empty()) {
							if (!issub('\0'+arrays.back()+current_name,substitutions)) {
								substitutions.push_back('\0'+arrays.back()+current_name);
							}
							current_name = arrays.back()+current_name;
							arrays.pop_back();
						}

						current_param_modifiers.clear();
						current_name.clear();
						pexpr = false;
						builtin = false;

						current_name = name_stack.top();
						name_stack.pop();
						current_name += "I";
						current_name += current_params;
						current_name += "E";

						current_param_modifiers = paramm_stack.top();
						paramm_stack.pop();
						arrays = arrays_stack.top();
						arrays_stack.pop();
						current_params = params_stack.top();
						params_stack.pop();

						current_recursion--;
						if (subbing && (sub_recursion == current_recursion)) {
							switch(current_st) {
								case ST_STUNQ:
									current_part += "E";
									break;
								case ST_SSNO:
									current_part = "N" + current_part + "E";
									break;
								default:
									break;
							}
							subbing = false;
							current_st = ST_NULL;
							current_sub.clear();
							current_retvalue = current_retvalue + current_part + current_input.substr(position);
							last_rule = true;
							subchange = true;
						}

						break;

					case SA_SKIPTEMPL:
						if (subbing && (sub_recursion == current_recursion)) {
							switch(current_st) {
								case ST_STUNQ:
									current_part += "E";
									break;
								case ST_SSNO:
									if (current_sub[0] == '\0') {
										current_part = current_sub.substr(1);
									}
									else {
										current_part = "N" + current_part + "E";
									}
									break;
								default:
									break;
							}
							subbing = false;
							current_st = ST_NULL;
							current_sub.clear();
							current_retvalue = current_retvalue + current_part + current_input.substr(position);
							last_rule = true;
							subchange = true;
						}
						break;

					case SA_PAR2F:
						if (!builtin) {
							//save sub
							if (!subbing && !issub(current_name,substitutions)) {
								substitutions.push_back(current_name);
							}
							current_params += current_param_modifiers;
							current_params += "N";
							current_params += current_name;
							current_params += "E";
							current_name = "N"+current_name+"E";
						}
						while (!subbing && !current_param_modifiers.empty()) {
							current_name = current_param_modifiers[current_param_modifiers.length()-1]+current_name;
							if (!current_param_modifiers.empty() && !issub('\0'+current_name,substitutions)) {
								substitutions.push_back('\0'+current_name);
							}
							current_param_modifiers = current_param_modifiers.substr(0,current_param_modifiers.length()-1);
						}

						while(!subbing && !arrays.empty()) {
							if (!issub('\0'+arrays.back()+current_name,substitutions)) {
								substitutions.push_back('\0'+arrays.back()+current_name);
							}
							current_name = arrays.back()+current_name;
							arrays.pop_back();
						}

						current_param_modifiers.clear();
						current_name.clear();
						builtin = false;
						break;

					//parameter modifiers
					case SA_SETCONST:
					case SA_SETRESTRICT:
					case SA_SETVOLATILE:
					case SA_SETPTR:
					case SA_SETREF:
					case SA_SETRVAL:
					case SA_SETCPAIR:
					case SA_SETIM:
						current_param_modifiers += current_char;
						break;

					case SA_STUNQ:
						if (!subbing) {
							current_st = ST_STUNQ;
						}
						break;
					case SA_SSNEST:
						if (!subbing) {
							current_st = ST_SSNEST;
						}
						break;
					case SA_SSNO:
						if (!subbing) {
							current_st = ST_SSNO;
						}
						break;

					case SA_LOADTSUB:
						current_id_length = 0;
						while (!(elemstack.top().type == GE_TERM && elemstack.top().t == '_')) {
							elemstack.pop();
						}
						elemstack.pop();

						if (current_input[position] == '_') {
							current_templ_sub = template_subs[0];
						}
						else {
							while (current_input[position] != '_') {
								if (position == current_input.length()) {
									errString = "cGram::subanalyze: Syntax error: Unexpected end of template substitution.";
									*err = ERROR_SYN;
									break;
								}
								if (!isdigit(current_input[position])) {
									errString = string("") + "cGram::subanalyze: Syntax error: Unknown template sub character " + current_input[position] + ".";
									*err = ERROR_SYN;
									break;
								}

								if (subbing) {
									current_part += current_input[position];
								}
								current_id_length *= 10;
								current_id_length += current_input[position] - '0';
								++position;
							}
							if (*err != ERROR_OK) {
								break;
							}
							current_templ_sub = template_subs[current_id_length+1];
						}
						if (subbing) {
							current_part += '_';
						}
						++position;
						if (!subbing) {
							substitutions.push_back('\0'+current_templ_sub);
							current_retvalue = current_part.substr(0,current_part.length()-1) + current_templ_sub + current_input.substr(position);
							last_rule = true;
							subchange = true;
						}

						break;

					case SA_LOADSUB:
						current_sub_id.clear();
						while (!(elemstack.top().type == GE_TERM && elemstack.top().t == '_')) {
							elemstack.pop();
						}
						elemstack.pop();

						if (!subbing) {
							current_retvalue = current_part.substr(0,current_part.length()-1);
							current_part.clear();
						}

						while (current_input[position] != '_') {
							if (position == current_input.length()) {
								errString = "cGram::subanalyze: Syntax error: Unexpected end of substitution.";
								*err = ERROR_SYN;
								break;
							}
							if (!((current_input[position] >= '0' && current_input[position] <= '9') || (current_input[position] >= 'A' && current_input[position] <= 'Z'))) {
								errString = string("") + "cGram::subanalyze: Syntax error: Unknown sub ID symbol " + current_input[position] + ".";
								*err = ERROR_SYN;
								break;
							}

							if (subbing) {
								current_part += current_input[position++];
							}
							else {
								current_sub_id += current_input[position++];
							}
						}
						if (*err != ERROR_OK) {
							break;
						}

						if (subbing) {
							current_part += '_';
						}
						++position;

						if (!subbing) {
							subbing = true;
							sub_recursion = current_recursion;
#ifdef DEMANGLER_SUBDBG
							cout << "Iteration sub candidates:" << endl;
							showsubs(substitutions);
#endif

							//Fix of the bug #914
							unsigned tempPos = b36toint(current_sub_id);
							if ((tempPos+1) >= substitutions.size()) {
								errString = string("") + "cGram::subanalyze: Syntax error: Non-existent substitution " + current_sub_id + ".";
								*err = ERROR_SYN;
								break;
							}

							current_sub = substitutions[tempPos+1];
							switch(current_st) {
								case ST_STUNQ:
									current_part = "N" + current_sub;
									break;
								case ST_SSNO:
									current_part = current_sub;
									break;
								default:
									current_part = current_sub;
									subbing = false;
									current_st = ST_NULL;
									current_sub.clear();
									current_retvalue = current_retvalue + current_part + current_input.substr(position);
									last_rule = true;
									subchange = true;
									break;
							}
						}

						//synchronize current_char to current_input[position]
						if (position == current_input.length()) {
							current_char = '\0';
						}
						else {
							current_char = current_input[position];
						}
						break;

					case SA_SUBSTD:
						if (!subbing) {
							elemstack.pop();
							++position;
							subbing = true;
							current_retvalue = current_part.substr(0,current_part.length()-1);
							current_part.clear();
							sub_recursion = current_recursion;

							current_sub = "3std";
							switch(current_st) {
								case ST_STUNQ:
									current_part = "N" + current_sub;
									break;
								case ST_SSNO:
									current_part = current_sub;
									break;
								default:
									current_part = current_sub;
									subbing = false;
									current_st = ST_NULL;
									current_sub.clear();
									current_retvalue = current_retvalue + current_part + current_input.substr(position);
									last_rule = true;
									subchange = true;
									break;
							}
						}
						break;

					case SA_SUBALC:
						if (!subbing) {
							elemstack.pop();
							++position;
							subbing = true;
							current_retvalue = current_part.substr(0,current_part.length()-1);
							current_part.clear();
							sub_recursion = current_recursion;

							current_sub = "3std9allocator";
							switch(current_st) {
								case ST_STUNQ:
									current_part = "N" + current_sub;
									break;
								case ST_SSNO:
									current_part = current_sub;
									break;
								default:
									current_part = current_sub;
									subbing = false;
									current_st = ST_NULL;
									current_sub.clear();
									current_retvalue = current_retvalue + current_part + current_input.substr(position);
									last_rule = true;
									subchange = true;
									break;
							}
						}
						break;
					case SA_SUBSTR:
						if (!subbing) {
							//get rid of the second Substitution character
							++position;
							elemstack.pop();
							subbing = true;
							current_retvalue = current_part.substr(0,current_part.length()-1);
							current_part.clear();
							sub_recursion = current_recursion;

							current_sub = "3std6string";
							switch(current_st) {
								case ST_STUNQ:
									current_part = "N" + current_sub;
									break;
								case ST_SSNO:
									current_part = current_sub;
									break;
								default:
									current_part = current_sub;
									subbing = false;
									current_st = ST_NULL;
									current_sub.clear();
									current_retvalue = current_retvalue + current_part + current_input.substr(position);
									last_rule = true;
									subchange = true;
									break;
							}
						}
						break;
					case SA_SUBSTRS:
						if (!subbing) {
							elemstack.pop();
							++position;
							subbing = true;
							current_retvalue = current_part.substr(0,current_part.length()-1);
							current_part.clear();
							sub_recursion = current_recursion;

							if (current_input.length() >= position && (current_input[position] == 'C' || current_input[position] == 'D')) {
								current_sub = "3std12basic_stringIcN3std11char_traitsIcEEN3std9allocatorIcEEE";
							}
							else {
								current_sub = "3std6string";
							}
							switch(current_st) {
								case ST_STUNQ:
									current_part = "N" + current_sub;
									break;
								case ST_SSNO:
									current_part = current_sub;
									break;
								default:
									current_part = current_sub;
									subbing = false;
									current_st = ST_NULL;
									current_sub.clear();
									current_retvalue = current_retvalue + current_part + current_input.substr(position);
									last_rule = true;
									subchange = true;
									break;
							}
						}
						break;
					case SA_SUBISTR:
						if (!subbing) {
							elemstack.pop();
							++position;
							subbing = true;
							current_retvalue = current_part.substr(0,current_part.length()-1);
							current_part.clear();
							sub_recursion = current_recursion;

							current_sub = "3std7istream";
							switch(current_st) {
								case ST_STUNQ:
									current_part = "N" + current_sub;
									break;
								case ST_SSNO:
									current_part = current_sub;
									break;
								default:
									current_part = current_sub;
									subbing = false;
									current_st = ST_NULL;
									current_sub.clear();
									current_retvalue = current_retvalue + current_part + current_input.substr(position);
									last_rule = true;
									subchange = true;
									break;
							}
						}
						break;
					case SA_SUBOSTR:
						if (!subbing) {
							elemstack.pop();
							++position;
							subbing = true;
							current_retvalue = current_part.substr(0,current_part.length()-1);
							current_part.clear();
							sub_recursion = current_recursion;

							current_sub = "3std7ostream";
							switch(current_st) {
								case ST_STUNQ:
									current_part = "N" + current_sub;
									break;
								case ST_SSNO:
									current_part = current_sub;
									break;
								default:
									current_part = current_sub;
									subbing = false;
									current_st = ST_NULL;
									current_sub.clear();
									current_retvalue = current_retvalue + current_part + current_input.substr(position);
									last_rule = true;
									subchange = true;
									break;
							}
						}
						break;
					case SA_SUBIOSTR:
						if (!subbing) {
							elemstack.pop();
							++position;
							subbing = true;
							current_retvalue = current_part.substr(0,current_part.length()-1);
							current_part.clear();
							sub_recursion = current_recursion;

							current_sub = "3std8iostream";
							switch(current_st) {
								case ST_STUNQ:
									current_part = "N" + current_sub;
									break;
								case ST_SSNO:
									current_part = current_sub;
									break;
								default:
									current_part = current_sub;
									subbing = false;
									current_st = ST_NULL;
									current_sub.clear();
									current_retvalue = current_retvalue + current_part + current_input.substr(position);
									last_rule = true;
									subchange = true;
									break;
							}
						}
						break;
					case SA_BEGINPEXPR:
						current_name = 'L';
						pexpr = true;
						break;

					case SA_EXPRVAL:
						current_name += current_char;
						break;

					case SA_STOREPEXPR:
						current_name += 'E';
						current_params += current_name;
						current_name.clear();
						current_param_modifiers.clear();
						pexpr = false;
						break;



					//else do nothing
					default:
						break;
				}
			}
			//top of stack is a terminal
			else {
				if (current_element.t == current_char) {
					//just move to next char
					current_part += current_char;
					++position;
				}
				else {
					errString = string("") + "cGram::subanalyze: Syntax error: Unexpected terminal " + current_char + ". Expected was " + current_element.t + ".";
					*err = ERROR_SYN;
					break;
				}
			}
		} //while
#ifdef DEMANGLER_SUBDBG
		cout << current_retvalue << endl;
#endif
		current_input = current_retvalue;
	}
	retvalue = current_retvalue;






#ifdef DEMANGLER_SUBDBG
	showsubs(substitutions);
#endif
	return retvalue;
}

/**
 * @brief The main syntactical and semantical analyzer.
 * @param input The mangled name to be demangled.
 * @param pName Reference to an existing object of cName class into which the demangled name will be stored.
 * @return Error code. Anything else than ERROR_OK means an error has happened.
 */
cGram::errcode cGram::analyze(string input, cName & pName) {
	errcode retvalue = ERROR_OK;
	bool last_rule = false;
	bool rettype = false;
	bool btypesub = false;
	stack<gelem_t> elemstack;
	size_t position = 0;
	bool tempbool = false; //a bool variable for temporary use

	//grammar element from the top of stack
	gelem_t current_element;
	//vector of rules used during the analysis
	vector<unsigned int> used_rules;
	//currently used rule. contains the semantic action as well
	llelem_t current_rule;
	//currently built parameter
	cName::type_t current_param;
	pName.type_t_clear(current_param);
	//current vector of parameters
	vector<cName::type_t> current_param_vector;
	vector<cName::name_t> name_substitution_vector;
	vector<cName::type_t> type_substitution_vector;

	//stacks to store current state when entering a new level of recursion
	stack<cName::type_t> param_stack;
	stack<vector<cName::type_t>> param_vector_stack;
	stack<vector<cName::name_t>> name_vector_stack;
	stack<vector<cName::name_t>> name_substitution_stack;
	stack<vector<cName::type_t>> type_substitution_stack;
	stack<bool> btypesub_stack;


	//current unqualified name
	cName::name_t current_unq_name;
	current_unq_name.tpl = nullptr;
	current_unq_name.op = false;
	//current qualified name
	vector<cName::name_t> current_name;

	//id length used for loading IDs
	unsigned int current_id_length = 0;

	//integer used for adressing type substitutions (in Borland)
	unsigned int bsubtemp = 0;

	long int current_number = 0;

	//default name type is function
	pName.setnametype(cName::NT_FUNCTION);

	//current symbol of the analyzed string.
	char current_char = input[position];

	//insert the root NT into the stack
	if (internalGrammar) {
		elemstack.push(internalGrammarStruct.root);
	}
	else {
		elemstack.push(rules[0].left);
	}

	//the main loop
	while (!last_rule && retvalue == ERROR_OK) {
		if (elemstack.empty()) {
				errString = "cGram::analyze: Syntax error: elemstack empty ";
				retvalue = ERROR_SYN;
				break;
		}

		//load the current character of the input string
		if (position == input.length()) {
			current_char = '\0';
		}
		else {
			current_char = input[position];
		}
		current_element = elemstack.top();
		elemstack.pop();
		//top of stack is a non-terminal
		if (current_element.type == GE_NONTERM) {
			//load the rule number for current NT and T and check for syntax error
			if ((current_rule = getllpair(current_element.nt, current_element.ntst, current_char)).n == 0) {
				errString = string("") + "cGram::analyze: Syntax error: No rule for NT " + current_element.nt + " and T " + current_char + ".";
				retvalue = ERROR_SYN;
				break;
			}
			//add the currently used rule to the vector of used rules
			else {
				used_rules.push_back(current_rule.n);
			}

			//internal grammar
			if (internalGrammar) {
				for(unsigned int i=internalGrammarStruct.ruleaddrs[current_rule.n-1].size; i>0; i--) {
					elemstack.push(internalGrammarStruct.ruleelements[internalGrammarStruct.ruleaddrs[current_rule.n-1].offset + (i-1)]);
				}
			}
			//external grammar
			else {
				//push right side of the used rule into the stack
				for(vector<gelem_t>::reverse_iterator i = rules[current_rule.n-1].right.rbegin(); i != rules[current_rule.n-1].right.rend(); ++i) {
					elemstack.push(*i);
				}
			}

			//temporary name_t variable for storing template pointer into last element of current_name
			cName::name_t tempname;
			//semantic action
			switch (current_rule.s) {
				//do nothing
				case SA_NULL:
					break;

				//type of name modifiers
				//set name type to constructor
				case SA_SETNAMEC:
					pName.setnametype(cName::NT_CONSTRUCTOR);
					break;
				//set name type to destructor
				case SA_SETNAMED:
					pName.setnametype(cName::NT_DESTRUCTOR);
					break;
				//set name type to data
				case SA_SETNAMEX:
					pName.setnametype(cName::NT_DATA);
					break;
				//set name type to operator
				case SA_SETNAMEO:
					pName.setnametype(cName::NT_OPERATOR);
					current_unq_name.tpl = nullptr;
					current_unq_name.un = "operator";
					current_unq_name.op = true;
					break;

				//built-in types
				case SA_SETTYPEV:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_VOID;
					break;
				case SA_SETTYPEW:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_WCHAR;
					break;
				case SA_SETTYPEB:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_BOOL;
					break;
				case SA_SETTYPEC:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_CHAR;
					break;
				case SA_SETTYPEA:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_SCHAR;
					break;
				case SA_SETTYPEH:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_UCHAR;
					break;
				case SA_SETTYPES:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_SHORT;
					break;
				case SA_SETTYPET:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_USHORT;
					break;
				case SA_SETTYPEI:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_INT;
					break;
				case SA_SETTYPEJ:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_UINT;
					break;
				case SA_SETTYPEL:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_LONG;
					break;
				case SA_SETTYPEM:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_ULONG;
					break;
				case SA_SETTYPEX:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_LONGLONG;
					break;
				case SA_SETTYPEY:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_ULONGLONG;
					break;
				case SA_SETTYPEN:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_INT128;
					break;
				case SA_SETTYPEO:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_UINT128;
					break;
				case SA_SETTYPEF:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_FLOAT;
					break;
				case SA_SETTYPED:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_DOUBLE;
					break;
				case SA_SETTYPEE:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_LONGDOUBLE;
					break;
				case SA_SETTYPEG:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_FLOAT128;
					break;
				case SA_SETTYPEZ:
					current_param.type = cName::TT_BUILTIN;
					current_param.n.clear();
					current_param.b = cName::T_ELLIPSIS;
					break;



				//set operator type
				case SA_SETOPNW:
					pName.setop(cName::OT_NEW);
					current_unq_name.un += " new";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPNA:
					pName.setop(cName::OT_NEWARR);
					current_unq_name.un += " new[]";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPDL:
					pName.setop(cName::OT_DEL);
					current_unq_name.un += " delete";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPDA:
					pName.setop(cName::OT_DELARR);
					current_unq_name.un += " delete[]";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPPS:
					pName.setop(cName::OT_UPLUS);
					current_unq_name.un += "+";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPNG:
					pName.setop(cName::OT_UMINUS);
					current_unq_name.un += "-";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPAD:
					pName.setop(cName::OT_UAND);
					current_unq_name.un += "&";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPDE:
					pName.setop(cName::OT_UAST);
					current_unq_name.un += "*";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPCO:
					pName.setop(cName::OT_TILDA);
					current_unq_name.un += "~";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPPL:
					pName.setop(cName::OT_PLUS);
					current_unq_name.un += "+";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPMI:
					pName.setop(cName::OT_MINUS);
					current_unq_name.un += "-";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPML:
					pName.setop(cName::OT_AST);
					current_unq_name.un += "*";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPDV:
					pName.setop(cName::OT_DIV);
					current_unq_name.un += "/";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPRM:
					pName.setop(cName::OT_MOD);
					current_unq_name.un += "%";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPAN:
					pName.setop(cName::OT_AND);
					current_unq_name.un += "&";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPOR:
					pName.setop(cName::OT_OR);
					current_unq_name.un += "|";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPEO:
					pName.setop(cName::OT_EXP);
					current_unq_name.un += "^";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPASS:
					pName.setop(cName::OT_ASSIGN);
					current_unq_name.un += "=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPPLL:
					pName.setop(cName::OT_PLUSASS);
					current_unq_name.un += "+=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPMII:
					pName.setop(cName::OT_MINUSASS);
					current_unq_name.un += "-=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPMLL:
					pName.setop(cName::OT_ASTASS);
					current_unq_name.un += "*=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPDVV:
					pName.setop(cName::OT_DIVASS);
					current_unq_name.un += "/=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPRMM:
					pName.setop(cName::OT_MODASS);
					current_unq_name.un += "%=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPANN:
					pName.setop(cName::OT_ANDASS);
					current_unq_name.un += "&=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPORR:
					pName.setop(cName::OT_ORASS);
					current_unq_name.un += "|=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPEOO:
					pName.setop(cName::OT_EXPASS);
					current_unq_name.un += "^=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPLS:
					pName.setop(cName::OT_LSHIFT);
					current_unq_name.un += "<<";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPRS:
					pName.setop(cName::OT_RSHIFT);
					current_unq_name.un += ">>";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPLSS:
					pName.setop(cName::OT_LSHIFTASS);
					current_unq_name.un += "<<=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPRSS:
					pName.setop(cName::OT_RSHIFTASS);
					current_unq_name.un += ">>=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPEQ:
					pName.setop(cName::OT_EQ);
					current_unq_name.un += "==";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPNE:
					pName.setop(cName::OT_NEQ);
					current_unq_name.un += "!=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPLT:
					pName.setop(cName::OT_LT);
					current_unq_name.un += "<";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPGT:
					pName.setop(cName::OT_GT);
					current_unq_name.un += ">";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPLE:
					pName.setop(cName::OT_LE);
					current_unq_name.un += "<=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPGE:
					pName.setop(cName::OT_GE);
					current_unq_name.un += ">=";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPNT:
					pName.setop(cName::OT_NOT);
					current_unq_name.un += "!";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPAA:
					pName.setop(cName::OT_ANDAND);
					current_unq_name.un += "&&";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPOO:
					pName.setop(cName::OT_OROR);
					current_unq_name.un += "||";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPPP:
					pName.setop(cName::OT_PLUSPLUS);
					current_unq_name.un += "++";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPMM:
					pName.setop(cName::OT_MINUSMINUS);
					current_unq_name.un += "--";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPCM:
					pName.setop(cName::OT_COMMA);
					current_unq_name.un += ",";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPPM:
					pName.setop(cName::OT_PTAST);
					current_unq_name.un += "->*";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPPT:
					pName.setop(cName::OT_PT);
					current_unq_name.un += "->";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPCL:
					pName.setop(cName::OT_BRACKETS);
					current_unq_name.un += "()";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPIX:
					pName.setop(cName::OT_ARR);
					current_unq_name.un += "[]";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPQU:
					pName.setop(cName::OT_QUESTION);
					current_unq_name.un += "?";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPST:
					pName.setop(cName::OT_SIZEOFT);
					current_unq_name.un += " sizeof";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPSZ:
					pName.setop(cName::OT_SIZEOFE);
					current_unq_name.un += " sizeof";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPAT:
					pName.setop(cName::OT_ALIGNOFT);
					current_unq_name.un += " alignof";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPAZ:
					pName.setop(cName::OT_ALIGNOFE);
					current_unq_name.un += " alignof";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;
				case SA_SETOPCV:
					pName.setop(cName::OT_CAST);
					current_unq_name.un += " ";
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.op = false;
					break;

				//parameter modifiers
				case SA_SETCONST:
					current_param.modifiers = "K"+current_param.modifiers;
					break;
				case SA_SETRESTRICT:
					current_param.modifiers = "r"+current_param.modifiers;
					break;
				case SA_SETVOLATILE:
					current_param.modifiers = "V"+current_param.modifiers;
					break;
				case SA_SETPTR:
					current_param.modifiers = "P"+current_param.modifiers;
					break;
				case SA_SETREF:
					current_param.modifiers = "R"+current_param.modifiers;
					break;
				case SA_SETRVAL:
					current_param.modifiers = "O"+current_param.modifiers;
					break;
				case SA_SETCPAIR:
					current_param.modifiers = "C"+current_param.modifiers;
					break;
				case SA_SETIM:
					current_param.modifiers = "G"+current_param.modifiers;
					break;

				//start a template, enter a new level of recursion
				case SA_BEGINTEMPL:
					//push everything into stacks and clean up
					name_vector_stack.push(current_name);
					current_name.clear();

					name_substitution_stack.push(name_substitution_vector);
					name_substitution_vector.clear();
					type_substitution_stack.push(type_substitution_vector);
					type_substitution_vector.clear();

					btypesub_stack.push(btypesub);
					btypesub = false;

					param_stack.push(current_param);
					pName.type_t_clear(current_param);

					param_vector_stack.push(current_param_vector);
					current_param_vector.clear();
					break;

				//template ends, store all of its parameters and quit current level of recursion
				case SA_STORETEMPLATE:
					//if the param type was named, move the name to the param
					if (!current_name.empty()) {
						current_param.type = cName::TT_NAME;
						current_param.n = current_name;
						current_name.clear();
					}

					//save the current param's substitution
					if (btypesub) {
						if (type_substitution_vector.size() < BSUBMAX) {
							type_substitution_vector.push_back(current_param);
						}
						btypesub = false;
					}
					//move the param to current vector of params
					current_param_vector.push_back(current_param);
					pName.type_t_clear(current_param);

					//restore previous 'current_param' from the stack
					current_param = param_stack.top();
					param_stack.pop();

					//restore previous 'current qualified name' from the stack
					current_name = name_vector_stack.top();
					name_vector_stack.pop();

					//restore previous btypesub state
					btypesub = btypesub_stack.top();
					btypesub_stack.pop();

					//restore previous substitution name vector
					name_substitution_vector = name_substitution_stack.top();
					name_substitution_stack.pop();
					type_substitution_vector = type_substitution_stack.top();
					type_substitution_stack.pop();

					//insert current template into the last name element of current qualified name
					if (current_name.empty()) {
						errString = "Fatal error: Current name is empty!!";
						retvalue = ERROR_SYN;
						break;
					}
					else {
						tempname = current_name.back();
						current_name.pop_back();
						tempname.tpl = static_cast<void *>(new vector<cName::type_t>(current_param_vector));
						current_name.push_back(tempname);
					}

					//restore previous 'current vector of parameters' from the stack
					current_param_vector = param_vector_stack.top();
					param_vector_stack.pop();
					break;

				case SA_TEMPL2TFTPL:
					//if the param type was named, move the name to the param
					if (!current_name.empty()) {
						current_param.type = cName::TT_NAME;
						current_param.n = current_name;
						current_name.clear();
					}

					//save the current param's substitution
					if (btypesub) {
						if (type_substitution_vector.size() < BSUBMAX) {
							type_substitution_vector.push_back(current_param);
						}
						btypesub = false;
					}
					//move the param to current vector of params
					current_param_vector.push_back(current_param);
					pName.type_t_clear(current_param);

					//restore previous 'current_param' from the stack
					current_param = param_stack.top();
					param_stack.pop();

					//restore previous btypesub state
					btypesub = btypesub_stack.top();
					btypesub_stack.pop();

					//restore previous 'current qualified name' from the stack
					current_name = name_vector_stack.top();
					name_vector_stack.pop();

					//restore previous substitution name vector
					name_substitution_vector = name_substitution_stack.top();
					name_substitution_stack.pop();
					type_substitution_vector = type_substitution_stack.top();
					type_substitution_stack.pop();

					//insert current template into the last name element of current qualified name
					if (current_name.empty()) {
						errString = "Fatal error: Current name is empty!!";
						retvalue = ERROR_SYN;
						break;
					}
					else {
						tempname.tpl = static_cast<void *>(new vector<cName::type_t>(current_param_vector));
						pName.settftpl(tempname.tpl);
						tempname.tpl = nullptr;
					}

					//restore previous 'current vector of parameters' from the stack
					current_param_vector = param_vector_stack.top();
					param_vector_stack.pop();
					break;

				//store current template argument into the current vector of params
				case SA_STORETEMPARG:
					//if the param type was named, move the name to the param
					if (!current_name.empty()) {
						current_param.type = cName::TT_NAME;
						current_param.n = current_name;
						current_name.clear();
					}

					//save the current param's substitution
					if (btypesub) {
						if (type_substitution_vector.size() < BSUBMAX) {
							type_substitution_vector.push_back(current_param);
						}
						btypesub = false;
					}
					//move the param to current vector of params
					current_param_vector.push_back(current_param);
					pName.type_t_clear(current_param);
					break;

				//store current function parameter
				case SA_STOREPAR:
					//if the param type was named, move the name to the param
					if (!current_name.empty()) {
						current_param.type = cName::TT_NAME;
						current_param.n = current_name;
						current_name.clear();
					}
					//move the param to current vector of params
					if (rettype) {
						pName.setret(current_param);
						rettype = false;
					}
					else {
					//save the current param's substitution
					if (btypesub) {
						if (type_substitution_vector.size() < BSUBMAX) {
							type_substitution_vector.push_back(current_param);
						}
						btypesub = false;
					}
						current_param_vector.push_back(current_param);
					}
					pName.type_t_clear(current_param);
					break;

				case SA_LOADARR:
					//remove the number terminal and "_" from element stack
					elemstack.pop();
					elemstack.pop();
					current_id_length = 0;

					while (input[position] != '_') {
						if (position == input.length()) {
							errString = "cGram::analyze: Syntax error: Unexpected end of array.";
							retvalue = ERROR_SYN;
							break;
						}
						if (!isdigit(input[position])) {
							errString = string("") + "cGram::analyze: Syntax error: Unknown array symbol " + input[position] + ".";
							retvalue = ERROR_SYN;
							break;
						}
						current_id_length *= 10;
						current_id_length += input[position] - '0';
						++position;
					}
					if (retvalue != ERROR_OK) {
						break;
					}
					//_
					++position;

					current_param.is_array = true;
					current_param.array_dimensions.push_back(current_id_length);
					break;

				//loads one unqualified name into current qualified name
				case SA_LOADID:
					//remove the number terminal from element stack
					elemstack.pop();
					current_id_length = 0;
					//load the length of ID
					while (isdigit(input[position])) {
						if (position == input.length()) {
							errString = "cGram::analyze: Syntax error: Unexpected end of identifier length.";
							retvalue = ERROR_SYN;
							break;
						}
						current_id_length *= 10;
						current_id_length += (input[position++] - '0');
					}
					if (retvalue != ERROR_OK) {
						break;
					}

					//load the ID
					for (unsigned int i = 0; i < current_id_length; ++i) {
						if (position == input.length()) {
							errString = string("") + "cGram::analyze: Syntax error: Unexpected end of identifier " + current_unq_name.un + ".";
							retvalue = ERROR_SYN;
							break;
						}
						current_unq_name.un += input[position++];
					}
					if (retvalue != ERROR_OK) {
						break;
					}

					//move the ID into current qualified name
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.tpl = nullptr;

					//synchronize current_char to input[position]
					if (position == input.length()) {
						//if the ID was the last element in the mangled name, continue on to SA_PAR2F
						current_char = '\0';
					}
					else {
						current_char = input[position];
					}
					break;

				case SA_ADDCHARTONAME:
					current_unq_name.un += current_char;
				break;

				case SA_STORENAME:
					current_name.push_back(current_unq_name);
					current_unq_name.un.clear();
					current_unq_name.tpl = nullptr;
				break;

				//move the current vector of parameters into function parameter vector
				case SA_PAR2F:
					//if the param type was named, move the name to the param
					if (!current_name.empty()) {
						current_param.type = cName::TT_NAME;
						current_param.n = current_name;
						current_name.clear();
					}
					//save the current param's substitution
					if (btypesub) {
						if (type_substitution_vector.size() < BSUBMAX) {
							type_substitution_vector.push_back(current_param);
						}
						btypesub = false;
					}
					//move the param to current vector of params
					current_param_vector.push_back(current_param);
					pName.type_t_clear(current_param);

					//insert the param vector into the demangled name as the function parameters
					pName.addpar(current_param_vector);
					current_param_vector.clear();
					break;

				//save current qualified name into the function name
				case SA_UNQ2F:
					if (!current_name.empty()) {
						//if the name is a template function which is not a constructor or destructor, the first parameter will be the return type
						if (current_name.back().tpl != nullptr && pName.getnametype() != cName::NT_CONSTRUCTOR && pName.getnametype() != cName::NT_DESTRUCTOR) {
							rettype = true;
						}
						pName.addname(current_name);
						current_name.clear();
						if (!current_param.modifiers.empty()) {
							pName.setmodifiers(current_param.modifiers);
							current_param.modifiers.clear();
						}
					}
					break;

				case SA_UNQ2P:
					current_param.type = cName::TT_NAME;
					current_param.n = current_name;
					current_name.clear();
					break;

				//store current parameter as the return type
				case SA_PAR2SPEC:
					pName.setspec(current_param);
					pName.type_t_clear(current_param);
					btypesub = false;
					break;
				case SA_PAR2RET:
					pName.setret(current_param);
					pName.type_t_clear(current_param);
					btypesub = false;
					break;

				case SA_TYPE2EXPR:
					current_param.type = cName::TT_PEXPR;
					break;
				case SA_EXPRVAL:
					if (!isdigit(current_char)) {
						errString = string("") + "cGram::analyze: Syntax error: Unknown expression value symbol " + input[position] + ".";
						retvalue = ERROR_SYN;
						break;
					}
					if (current_param.b == cName::T_BOOL) {
						current_param.value = malloc(sizeof(bool));
						if (current_param.value == nullptr) {
							errString = string("") + "cGram::analyze: Syntax error: Couldn't allocate memory for bool expression value " + input[position] + ".";
							retvalue = ERROR_MEM;
							break;
						}
						*(static_cast<bool*>(current_param.value)) = current_char - '0';
					}
					elemstack.pop();
					++position;
					break;

				case SA_REVERSENAME:
					reverse(current_name.begin(), current_name.end());
					break;

				case SA_SETPRIVATE:
					pName.setmfacc(cName::MFM_PRIVATE);
					break;
				case SA_SETPUBLIC:
					pName.setmfacc(cName::MFM_PUBLIC);
					break;
				case SA_SETPROTECTED:
					pName.setmfacc(cName::MFM_PROTECTED);
					break;
				case SA_SETFCDECL:
					pName.setfcall(cName::FCC_CDECL);
					break;
				case SA_SETFPASCAL:
					pName.setfcall(cName::FCC_PASCAL);
					break;
				case SA_SETFFORTRAN:
					pName.setfcall(cName::FCC_FORTRAN);
					break;
				case SA_SETFTHISCALL:
					pName.setfcall(cName::FCC_THISCALL);
					break;
				case SA_SETFSTDCALL:
					pName.setfcall(cName::FCC_STDCALL);
					break;
				case SA_SETFFASTCALL:
					pName.setfcall(cName::FCC_FASTCALL);
					break;
				case SA_SETFINTERRUPT:
					pName.setfcall(cName::FCC_INTERRUPT);
					break;

				case SA_SETUNION:
					current_param.mst = cName::MST_UNION;
					break;
				case SA_SETSTRUCT:
					current_param.mst = cName::MST_STRUCT;
					break;
				case SA_SETCLASS:
					current_param.mst = cName::MST_CLASS;
					break;
				case SA_SETENUM:
					current_param.mst = cName::MST_ENUM;
					break;

				case SA_SETSTATIC:
					pName.setstatic();
					break;
				case SA_SETVIRTUAL:
					pName.setvirtual();
					break;

				case SA_STCLCONST:
					pName.addstcl('K');
					break;
				case SA_STCLVOL:
					pName.addstcl('V');
					break;
				case SA_STCLFAR:
					pName.addstcl('b');
					break;
				case SA_STCLHUGE:
					pName.addstcl('c');
					break;

				case SA_ADDMCONST:
					pName.addmodifier('K');
					break;
				case SA_ADDMVOL:
					pName.addmodifier('V');
					break;
				case SA_ADDMFAR:
					pName.addmodifier('b');
					break;
				case SA_ADDMHUGE:
					pName.addmodifier('c');
					break;

				case SA_SAVENAMESUB:
					if (name_substitution_vector.size() < 10 && !current_name.back().op) {
						name_substitution_vector.push_back(current_name.back());
					}
					rettype = false;
					break;

				case SA_LOADNAMESUB:
					if (static_cast<unsigned int>(current_char - '0') < name_substitution_vector.size()) {
						current_unq_name = name_substitution_vector[static_cast<unsigned int>(current_char - '0')];
						if (current_unq_name.tpl != nullptr) {
							current_unq_name.tpl = copynametpl(current_unq_name.tpl);
							if (current_unq_name.tpl == nullptr) {
								errString = string("") + "cGram::analyze: Error when copying template of name substitution " + current_unq_name.un + ".";
								retvalue = ERROR_MEM;
								break;
							}
						}
						current_name.push_back(current_unq_name);
						current_unq_name.un.clear();
						current_unq_name.tpl = nullptr;
					}
					else {
						errString = string("") + "cGram::analyze: name_substitution_vector does not contain substitution #" + current_char + ".";
						retvalue = ERROR_MEM;
						break;
					}

					break;

				case SA_MSTEMPLPSUB:
					if (name_vector_stack.top().back().op) {
						break;
					}
					name_substitution_vector.push_back(name_vector_stack.top().back());
					break;

				case SA_SETNAMEVT:
					pName.setnametype(cName::NT_VTABLE);
					break;

				case SA_SETNAMER0:
					pName.setnametype(cName::NT_R0);
					break;

				case SA_SETNAMER1:
					pName.setnametype(cName::NT_R1);
					break;

				case SA_SETNAMER2:
					pName.setnametype(cName::NT_R2);
					break;

				case SA_SETNAMER3:
					pName.setnametype(cName::NT_R3);
					break;

				case SA_SETNAMER4:
					pName.setnametype(cName::NT_R4);
					break;

				case SA_SETNAME_A:
					pName.setnametype(cName::NT__A);
					break;

				case SA_SETNAME_B:
					pName.setnametype(cName::NT__B);
					break;

				case SA_SETNAME_C:
					pName.setnametype(cName::NT__C);
					break;

				case SA_SETNAME_D:
					pName.setnametype(cName::NT__D);
					break;

				case SA_SETNAME_E:
					pName.setnametype(cName::NT__E);
					break;

				case SA_SETNAME_F:
					pName.setnametype(cName::NT__F);
					break;

				case SA_SETNAME_G:
					pName.setnametype(cName::NT__G);
					break;

				case SA_SETNAME_H:
					pName.setnametype(cName::NT__H);
					break;

				case SA_SETNAME_I:
					pName.setnametype(cName::NT__I);
					break;

				case SA_SETNAME_J:
					pName.setnametype(cName::NT__J);
					break;

				case SA_SETNAME_K:
					pName.setnametype(cName::NT__K);
					break;

				case SA_SETNAME_L:
					pName.setnametype(cName::NT__L);
					break;

				case SA_SETNAME_M:
					pName.setnametype(cName::NT__M);
					break;

				case SA_SETNAME_N:
					pName.setnametype(cName::NT__N);
					break;

				case SA_SETNAME_O:
					pName.setnametype(cName::NT__O);
					break;

				case SA_SETNAME_P:
					pName.setnametype(cName::NT__P);
					break;

				case SA_SETNAME_Q:
					pName.setnametype(cName::NT__Q);
					break;

				case SA_SETNAME_R:
					pName.setnametype(cName::NT__R);
					break;

				case SA_SETNAME_S:
					pName.setnametype(cName::NT__S);
					break;

				case SA_SETNAME_T:
					pName.setnametype(cName::NT__T);
					break;

				case SA_SETNAME_U:
					pName.setnametype(cName::NT__U);
					break;

				case SA_SETNAME_V:
					pName.setnametype(cName::NT__V);
					break;

				case SA_SETNAME_W:
					pName.setnametype(cName::NT__W);
					break;

				case SA_SETNAME_X:
					pName.setnametype(cName::NT__X);
					break;

				case SA_SETNAME_Y:
					pName.setnametype(cName::NT__Y);
					break;

				case SA_SETNAME_Z:
					pName.setnametype(cName::NT__Z);
					break;

				case SA_BEGINBSUB:
					btypesub = true;
					break;

				case SA_LOADBSUB:
					btypesub = false;
					if (static_cast<unsigned int>(current_char - '0') < type_substitution_vector.size()) {
						current_param = type_substitution_vector[static_cast<unsigned int>(current_char - '0')];
						for (vector<cName::name_t>::iterator i=current_param.n.begin(); i != current_param.n.end(); ++i) {
							if (i->tpl != nullptr) {
								i->tpl = copynametpl(i->tpl);
								//if there was an error during allocation, clean up
								if (i->tpl == nullptr) {
								errString = "cGram::analyze: Error when copying template of type substitution.";
								retvalue = ERROR_MEM;
								break;
								}
							}
						}
						break;
					}
					else {
						errString = string("") + "cGram::analyze: type_substitution_vector does not contain substitution #" + current_char + ".";
						retvalue = ERROR_MEM;
						break;
					}

				case SA_LOADBORLANDSUB:
					//bsub won't be disabled -> the newly loaded substitution will be stored as a new substitution again
					//because Borland works that way
					if (current_char >= '1' && current_char <= '9') {
						bsubtemp = static_cast<unsigned int>(current_char - '1');
					}
					else if (current_char >= 'a' && current_char <= 'z') {
						bsubtemp = static_cast<unsigned int>(current_char - 'a') + 9;
					}
					if (bsubtemp < type_substitution_vector.size()) {
						current_param = type_substitution_vector[bsubtemp];
						for (vector<cName::name_t>::iterator i=current_param.n.begin(); i != current_param.n.end(); ++i) {
							if (i->tpl != nullptr) {
								i->tpl = copynametpl(i->tpl);
								//if there was an error during allocation, clean up
								if (i->tpl == nullptr) {
								errString = "cGram::analyze: Error when copying template of type substitution.";
								retvalue = ERROR_MEM;
								break;
								}
							}
						}

						break;
					}
					else {
						errString = string("") + "cGram::analyze: type_substitution_vector does not contain substitution #" + current_char + ".";
						retvalue = ERROR_MEM;
						break;
					}

				case SA_LOADMSNUM:
					//remove the msnumber non-terminal from element stack
					elemstack.pop();
					current_number = 0;

					if (input[position] == '?') {
						tempbool = true;
						++position;
					}
					else {
						tempbool = false;
					}
					if (input[position] >= '0' && input[position] <= '9') {
						current_number = input[position] - '0' + 1;
						++position;
					}
					else if (input[position] >= 'A' && input[position] <= 'P') {
						while (input[position] != '@') {
							if (position == input.length()-1) {
								errString = "cGram::analyze: Syntax error: Unexpected end of MSVC++ number";
								retvalue = ERROR_SYN;
								break;
							}
							if (input[position] >= 'A' && input[position] <= 'P') {
								current_number *= 16;
								current_number += input[position] - 'A';
								++position;
							}
							else {
								errString = string("") + "cGram::analyze: Syntax error: Unexpected character " + input[position] + " instead of a MSVC++ number";
								retvalue = ERROR_SYN;
								break;
							}
						}
						if (retvalue != ERROR_OK) {
							break;
						}
						++position;
					}
					else {
							errString = string("") + "cGram::analyze: Syntax error: Unexpected character " + input[position] + " instead of a MSVC++ number";
							retvalue = ERROR_SYN;
							break;
					}

					if (tempbool) {
						current_number = -current_number;
					}

					break;

				case SA_NUMTORTTIBCD:
					pName.addrttinum(current_number);
					break;

				case SA_NUMTOTYPE:
					current_param.type = cName::TT_NUM;
					current_param.num = current_number;
					break;

				case SA_BORLANDNORMALIZEPARNAME:
					xreplace(current_name.back().un,"@","::");
					break;

				case SA_BORLANDID:
					//remove the number terminal from element stack
					elemstack.pop();
					current_id_length = 0;
					//load the length of ID
					while (isdigit(input[position])) {
						if (position == input.length()) {
							errString = "cGram::analyze: Syntax error: Unexpected end of identifier length.";
							retvalue = ERROR_SYN;
							break;
						}
						current_id_length *= 10;
						current_id_length += (input[position++] - '0');
					}
					if (retvalue != ERROR_OK) {
						break;
					}

					//check if input is long enough for the separator insertion
					if (input.length() < position + current_id_length) {
						errString = string("") + "cGram::analyze: Syntax error: Unexpected end of input when inserting a separator.";
						retvalue = ERROR_SYN;
						break;
					}

					//insert the separator
					input = input.substr(0,position + current_id_length) + "|" + input.substr(position + current_id_length);
					break;

				case SA_BORLANDARR:
					//remove the number terminal from element stack
					elemstack.pop();
					current_id_length = 0;

					while (isdigit(input[position])) {
						if (position == input.length()) {
							errString = "cGram::analyze: Syntax error: Unexpected end of array.";
							retvalue = ERROR_SYN;
							break;
						}
						current_id_length *= 10;
						current_id_length += input[position] - '0';
						++position;
					}
					if (retvalue != ERROR_OK) {
						break;
					}

					//store the array info to the current param
					current_param.is_array = true;
					current_param.array_dimensions.push_back(current_id_length);
					break;

				//last rule - successfully end
				case SA_END:
					last_rule = true;
					break;
				//else do nothing
				default:
					break;
			}
		}
		//top of stack is a terminal
		else {
			if (current_element.t == current_char) {
				//just move to next char
				++position;
			}
			else {
				errString = string("") + "cGram::analyze: Syntax error: Unexpected terminal " + current_char + ". Expected was " + current_element.t + ".";
				retvalue = ERROR_SYN;
				break;
			}
		}
	}

	return retvalue;
}

/**
 * Try to demangle string into class name.
 */
void cGram::demangleClassName(const std::string& input, cName* retvalue, cGram::errcode& err_i)
{
	//C++ class name demangler hack for gcc and msvc.
	if (err_i != ERROR_OK) {
		std::string className;

		//7Polygon
		//14PolygonPolygon
		if (compiler == "gcc") {
			static std::regex rgx("(\\d+)(\\w+)");
			std::smatch match;
			if (std::regex_match(input, match, rgx)) {
				std::string sLength = match[1];
				std::string name = match[2];

				unsigned long length = 0;

				bool ok = true;
				try {
					length = std::stoul(sLength);
				}
				catch (const std::invalid_argument&) {
					ok = false;
				}
				catch (const std::out_of_range&) {
					ok = false;
				}

				if (ok) {
					if (name.length() == length) {
						className = name;
					}
				}
			}
		}
		//.?AVPolygon@@
		//.?AVtype_info@@
		else if (compiler == "ms") {
			static std::regex rgx("\\.\\?AV(\\w+)\\@\\@");
			std::smatch match;
			if (std::regex_match(input, match, rgx)) {
				className = match[1];
			}
		}

		if (!className.empty()) {
			err_i = ERROR_OK;
			retvalue->name_type = cName::NT_CLASS;
			cName::name_t n;
			n.un = className;
			retvalue->name.push_back(n);
		}
	}
}

/**
 * @brief An envelope function for demangling of the input name. Calls the necessary analysis functions.
 * @param input The mangled name to be demangled.
 * @param err Pointer to an errcode into which the error code will be stored.
 * @return Pointer to an object of the cName class containing the demangled name.
 */
cName *cGram::perform(const string input, cGram::errcode *err) {
	cName *retvalue = new cName();
	errcode err_i = ERROR_OK;
	string temp = input;
	//substitution analysis (for now only in GCC)
	if (SubAnalyzeEnabled) {
		temp = subanalyze(temp,&err_i);
	}
#ifdef DEMANGLER_SUBDBG
	cout << temp << endl;
#else
	if (err_i == ERROR_OK) {
		err_i = analyze(temp, *retvalue);
	}
#endif

	demangleClassName(input, retvalue, err_i);

	*err = err_i;
	return retvalue;
}

/**
 * @brief Function which prints grammar rules on stdout.
 */
void cGram::showrules() {
	//for all rules
	for(vector<rule_t>::iterator i=rules.begin(); i != rules.end(); ++i) {
		//left
		cout << i->n << ": <" << i->left.nt << ">";
		cout << " ";
		cout << "::=";
		cout << " ";
		//right
		for(vector<gelem_t>::iterator j=i->right.begin(); j != i->right.end(); ++j) {
			if (j->type == GE_TERM) {
				cout << j->t;
			}
			else {
				cout << "<" << j->nt << ">";
			}
			cout << " ";
		}
		cout << endl;
	}
	cout << endl;
}

/**
 * @brief Function which prints "Empty" set on stdout.
 */
void cGram::showempty() {
	cout << "Empty:" << endl;
	for(map<string,bool>::iterator i=empty.begin(); i != empty.end(); ++i) {
		cout << "<" << i->first << ">" << ": ";
		if (i->second == false) {
			cout << "0";
		}
		else {
			cout << "{e}";
		}
		cout << endl;
	}
	cout << endl;
}

/**
 * @brief Function which prints "First" set on stdout.
 */
void cGram::showfirst() {
	cout << "First:" << endl;
	for(map<string,set<gelem_t,comparegelem_c>>::iterator i=first.begin(); i != first.end(); ++i) {
		cout << "<" << i->first << ">" << ": ";
		for(set<gelem_t,comparegelem_c>::iterator j=i->second.begin(); j != i->second.end(); ++j) {
			cout << j->t << " ";
		}
		cout << endl;
	}
	cout << endl;
}

/**
 * @brief Function which prints "Follow" set on stdout.
 */
void cGram::showfollow() {
	cout << "Follow:" << endl;
	for(map<string,set<gelem_t,comparegelem_c>>::iterator i=follow.begin(); i != follow.end(); ++i) {
		cout << "<" << i->first << ">" << ": ";
		for(set<gelem_t,comparegelem_c>::iterator j=i->second.begin(); j != i->second.end(); ++j) {
			if (j->t == '\0') {
				cout << "EOF";
			}
			else {
				cout << j->t;
			}
			cout << " ";
		}
		cout << endl;
	}
	cout << endl;
}

/**
 * @brief Function which prints "Predict" set on stdout.
 */
void cGram::showpredict() {
	cout << "Predict:" << endl;
	for(vector<rule_t>::iterator i=rules.begin(); i != rules.end(); ++i) {
		cout << i->n << ": ";
		for(set<gelem_t,comparegelem_c>::iterator j=predict[i->n].begin(); j != predict[i->n].end(); ++j) {
			if (j->t == '\0') {
				cout << "EOF";
			}
			else {
				cout << j->t;
			}
			cout << " ";
		}
		cout << endl;
	}
}

/**
 * @brief Function which prints LL table on stdout.
 */
void cGram::showll() {
	cout << "LL:" << endl;
	for(map<string,map<char,pair<unsigned int, semact>>>::iterator i=ll.begin(); i != ll.end(); ++i) {
		cout << "<" << i->first << ">" << ": ";
		for(map<char,pair<unsigned int, semact>>::iterator j=i->second.begin(); j != i->second.end(); ++j) {
			if (j->first == '\0') {
				cout << "EOF";
			}
			else {
				cout << j->first;
			}
			cout << "=";
			cout << j->second.first;
			cout << " ";
		}
		cout << endl;
	}
	cout << endl;
}

/**
 * @brief Function which sets qualified function name of the currently demangled name.
 * @param inname Qualified name consisting of unqualified names.
 */
void cName::addname(const vector<cName::name_t> & inname) {
	name = inname;
}

/**
 * @brief Function which sets parameters of the currently demangled name.
 * @param inpar Vector of parameters.
 */
void cName::addpar(const vector<cName::type_t> & inpar) {
	parameters = inpar;
}

/**
 * @brief Function which empties a type_t structure
 * @param x Reference to the structure to be cleared.
 */
void cName::type_t_clear(type_t &x) {
	x.type = TT_UNKNOWN;
	x.n.clear();
	x.value = nullptr;
	x.modifiers.clear();
	x.is_array = false;
	x.array_dimensions.clear();
	x.is_const = false;
	x.is_restrict = false;
	x.is_volatile = false;
	x.is_pointer = 0;
	x.is_reference = false;
	x.is_rvalue = false;
	x.is_cpair = false;
	x.is_imaginary = false;
	x.mst = MST_NULL;
}

/**
 * @brief Function which sets the type of the mangled name.
 * @param x The new type of name.
 */
void cName::setnametype(cName::ntype x) {
	name_type = x;
}

/**
 * @brief Function which sets the function call convention of the current name.
 * @param x The new type of name.
 */
void cName::setfcall(cName::fcall_t x) {
	function_call = x;
}

/**
 * @brief Function which sets member function access level.
 * @param x The new type of name.
 */
void cName::setmfacc(cName::memfacc_t x) {
	member_function_access = x;
}

/**
 * @brief Function which gets the type of the mangled name.
 * @return Current type of the mangled name
 */
cName::ntype cName::getnametype() {
	return name_type;
}

/**
 * @brief Function which sets the type of operator.
 * @param x The new type of operator.
 */
void cName::setop(cName::optype x) {
	operator_type = x;
}

/**
 * @brief Function which sets the return type.
 * @param x The type struct which will be assigned as return type of the name.
 */
void cName::setret(cName::type_t x) {
	return_type = x;
}

/**
 * @brief Function which sets the special type.
 * @param x The type struct which will be assigned as special type of the name.
 */
void cName::setspec(cName::type_t x) {
	special_type = x;
}

/**
 * @brief Function which sets the name's static flag.
 */
void cName::setstatic() {
	is_static = true;
}

/**
 * @brief Function which sets the name's virtual flag.
 */
void cName::setvirtual() {
	is_virtual = true;
}

/**
 * @brief Function which sets modifiers of the entire name.
 * @param x The string which will be assigned as type modifiers of the name.
 */
void cName::setmodifiers(string x) {
	modifiers = x;
}

/**
 * @brief Function which sets the template of the template function (for constructors or destructors).
 * @param x Pointer to the template (name_t vector).
 */
void cName::settftpl(void* x) {
	tf_tpl = x;
}

/**
 * @brief Function which adds a number to RTTI Base Class Descriptor.
 * @param x An unsigned integer to be added to the RTTI-BCD vector.
 */
void cName::addrttinum(long int x) {
	rttibcd.push_back(x);
}

/**
 * @brief Function which a modifier to the name.
 * @param x A char representing the modifier.
 */
void cName::addmodifier(char x) {
	modifiers += x;
}

/**
 * @brief Function which a storage class modifier.
 * @param x A char representing the modifier.
 */
void cName::addstcl(char x) {
	storage_class += x;
}

/**
 * @brief Function which recursively deletes parameters including the templates in their qualified names.
 * @param vec The vector of parameters.
 */
void cName::deleteparams(vector<cName::type_t> & vec) {
	for(vector<type_t>::iterator i=vec.begin(); i != vec.end(); ++i) {
		if (i->value != nullptr) {
			free(i->value);
		}
		for(vector<name_t>::iterator j=i->n.begin(); j != i->n.end(); ++j) {
			if (j->tpl != nullptr) {
				deleteparams(*(static_cast<vector<type_t>*>(j->tpl)));
				static_cast<vector<type_t>*>(j->tpl)->clear();
				delete static_cast<vector<type_t>*>(j->tpl);
			}
		}
	}
}

/**
 * @brief Destructor of cName class which deletes all dynamically allocated template arguments.
 */
cName::~cName() {
	for(vector<name_t>::iterator i=name.begin(); i != name.end(); ++i) {
		if (i->tpl != nullptr) {
			deleteparams(*(static_cast<vector<type_t>*>(i->tpl)));
			static_cast<vector<type_t>*>(i->tpl)->clear();
			delete static_cast<vector<type_t>*>(i->tpl);
		}
	}
	deleteparams(parameters);
	if (tf_tpl != nullptr) {
		deleteparams(*(static_cast<vector<type_t>*>(tf_tpl)));
		static_cast<vector<type_t>*>(tf_tpl)->clear();
		delete static_cast<vector<type_t>*>(tf_tpl);
	}
}

/**
 * @brief Function which shows the function name of the current demangled name.
 * @param vec The qualified name to be printed.
 * @param compiler Select the format of name according to compiler ("gcc", "ms", "borland"). Default is "gcc".
 */
string cName::printname(vector<cName::name_t> & vec, string compiler) {
	string retvalue;
	//for every name element...
	for(vector<name_t>::iterator i=vec.begin(); i != vec.end(); ++i) {
		last_shown_endtpl = false;
		//separate individual names with ::
		if (i != vec.begin()) {
			retvalue += "::";
		}
		//print the name
		retvalue += i->un;
		//if the name element contains pointer to a template, print the template
		if (i->tpl != nullptr) {
			retvalue += "<";
			retvalue += printparams(*(static_cast<vector<type_t>*>(i->tpl)), false, compiler);
			if (last_shown_endtpl) {
				retvalue += " ";
				last_shown_endtpl = false;
			}
			retvalue += ">";
			last_shown_endtpl = true;
		}
	}
	return retvalue;
}

/**
 * @brief Function which shows the modifiers of a type or name.
 * @param x The modifiers of a type or name.
 * @param space Indentation with space or no.
 * @return String containing modifiers.
 */
string cName::printmodifiers(string x, bool space) {
	string retvalue;
	//parameter modifiers
	for(size_t i=0; i<x.length(); ++i) {
		switch(x[i]) {
			case 'r':
				if (space) {retvalue += " ";}
				retvalue += "restrict";
				if (!space) {retvalue += " ";}
				break;
			case 'V':
				if (space) {retvalue += " ";}
				retvalue += "volatile";
				if (!space) {retvalue += " ";}
				break;
			case 'K':
				if (space) {retvalue += " ";}
				retvalue += "const";
				if (!space) {retvalue += " ";}
				break;
			case 'P':
				if (space) {retvalue += " ";}
				retvalue += "*";
				if (!space) {retvalue += " ";}
				break;
			case 'R':
				if (space) {retvalue += " ";}
				retvalue += "&";
				if (!space) {retvalue += " ";}
				break;
			case 'O':
				if (space) {retvalue += " ";}
				retvalue += "&&";
				if (!space) {retvalue += " ";}
				break;
			case 'C':
				if (space) {retvalue += " ";}
				retvalue += "complex pair";
				if (!space) {retvalue += " ";}
				break;
			case 'G':
				if (space) {retvalue += " ";}
				retvalue += "imaginary";
				if (!space) {retvalue += " ";}
				break;
			case 'a':
				if (space) {retvalue += " ";}
				retvalue += "near";
				if (!space) {retvalue += " ";}
				break;
			case 'c':
				if (space) {retvalue += " ";}
				retvalue += "huge";
				if (!space) {retvalue += " ";}
				break;
			case 'd':
				if (space) {retvalue += " ";}
				retvalue += "__unaligned";
				if (!space) {retvalue += " ";}
				break;
			case 'e':
				if (space) {retvalue += " ";}
				retvalue += "__restrict";
				if (!space) {retvalue += " ";}
				break;
			default:
				break;
		}
	}
	return retvalue;
}

/**
 * @brief Function which shows the modifiers of a type placed before the type.
 * @param x The modifiers of a type or name.
 * @param space Indentation with space or no.
 * @return String containing modifiers.
 */
string cName::printpremodifiers(string x, bool space) {
	string retvalue;
	//parameter modifiers
	for(size_t i=0; i<x.length(); ++i) {
		switch(x[i]) {
			case 'r':
				if (space) {retvalue += " ";}
				retvalue += "restrict";
				if (!space) {retvalue += " ";}
				break;
			case 'V':
				if (space) {retvalue += " ";}
				retvalue += "volatile";
				if (!space) {retvalue += " ";}
				break;
			case 'K':
				if (space) {retvalue += " ";}
				retvalue += "const";
				if (!space) {retvalue += " ";}
				break;
			case 'C':
				if (space) {retvalue += " ";}
				retvalue += "complex pair";
				if (!space) {retvalue += " ";}
				break;
			case 'G':
				if (space) {retvalue += " ";}
				retvalue += "imaginary";
				if (!space) {retvalue += " ";}
				break;
			case 'a':
				if (space) {retvalue += " ";}
				retvalue += "near";
				if (!space) {retvalue += " ";}
				break;
			case 'c':
				if (space) {retvalue += " ";}
				retvalue += "huge";
				if (!space) {retvalue += " ";}
				break;
			case 'd':
				if (space) {retvalue += " ";}
				retvalue += "__unaligned";
				if (!space) {retvalue += " ";}
				break;
			case 'e':
				if (space) {retvalue += " ";}
				retvalue += "__restrict";
				if (!space) {retvalue += " ";}
				break;
			default:
				break;
		}
	}
	return retvalue;
}

/**
 * @brief Function which shows the modifiers of a type or name placed after the type.
 * @param x The modifiers of a type or name.
 * @param space Indentation with space or no.
 * @return String containing modifiers.
 */
string cName::printpostmodifiers(string x, bool space) {
	string retvalue;
	//parameter modifiers
	for(size_t i=0; i<x.length(); ++i) {
		switch(x[i]) {
			case 'P':
				if (space) {retvalue += " ";}
				retvalue += "*";
				if (!space) {retvalue += " ";}
				break;
			case 'R':
				retvalue += "&";
				if (!space) {retvalue += " ";}
				break;
			case 'O':
				if (space) {retvalue += " ";}
				retvalue += "&&";
				if (!space) {retvalue += " ";}
				break;
			default:
				break;
		}
	}
	return retvalue;
}

/**
 * @brief Function which prints a primary expression type.
 * @param x The primary expression to be printed.
 */
string cName::printpexpr(type_t & x) {
	string retvalue;
	switch(x.b) {
		case T_BOOL:
			if (*(static_cast<bool*>(x.value))) {
				retvalue += "true";
			}
			else {
				retvalue += "false";
			}
			break;
		default:
			break;
	}
	return retvalue;
}

/**
 * @brief Function which prints a sequence of parameters into a string.
 * @param vec The vector of parameters to be printed.
 * @param ignorevoid Ignore void arguments.
 * @param compiler Select the format of name according to compiler ("gcc", "ms", "borland"). Default is "gcc".
 * @return String containing the sequence of parameters
 */
string cName::printparams(vector<cName::type_t> & vec, bool ignorevoid, string compiler) {
	string retvalue;
	//for every parameter...
	if ((ignorevoid == false) || !(vec.size() == 1 && vec.front().type == TT_BUILTIN && vec.front().b == T_VOID && vec.front().modifiers == "")) {
		for(vector<type_t>::iterator i=vec.begin(); i != vec.end(); ++i) {
			last_shown_endtpl = false;
			//separate individual parameters by comma
			if (i != vec.begin()) {
				retvalue += ", ";
			}

			//Borland
			if (compiler == "borland") {
				if (!i->modifiers.empty()) {
					last_shown_endtpl = false;
					retvalue += printpremodifiers(i->modifiers,false);
				}
			}

			if (i->mst != MST_NULL) {
				switch(i->mst) {
					case MST_UNION:
						retvalue += "union ";
						break;
					case MST_STRUCT:
						retvalue += "struct ";
						break;
					case MST_CLASS:
						retvalue += "class ";
						break;
					case MST_ENUM:
						retvalue += "enum ";
						break;
					default:
						break;
				}
			}

			//type of the parameter
			switch (i->type) {
				case TT_UNKNOWN:
					break;
				case TT_PEXPR:
					retvalue += printpexpr(*i);
					break;
				//built-in type
				case TT_BUILTIN:
					switch(i->b) {
						case T_VOID:
							retvalue += "void";
							break;
						case T_WCHAR:
							retvalue += "wchar_t";
							break;
						case T_BOOL:
							retvalue += "bool";
							break;
						case T_CHAR:
							retvalue += "char";
							break;
						case T_SCHAR:
							retvalue += "signed char";
							break;
						case T_UCHAR:
							retvalue += "unsigned char";
							break;
						case T_SHORT:
							retvalue += "short";
							break;
						case T_USHORT:
							retvalue += "unsigned short";
							break;
						case T_INT:
							retvalue += "int";
							break;
						case T_UINT:
							retvalue += "unsigned int";
							break;
						case T_LONG:
							retvalue += "long";
							break;
						case T_ULONG:
							retvalue += "unsigned long";
							break;
						case T_LONGLONG:
							retvalue += "long long";
							break;
						case T_ULONGLONG:
							retvalue += "unsigned long long";
							break;
						case T_INT128:
							retvalue += "__int128";
							break;
						case T_UINT128:
							retvalue += "unsigned __int128";
							break;
						case T_FLOAT:
							retvalue += "float";
							break;
						case T_DOUBLE:
							retvalue += "double";
							break;
						case T_LONGDOUBLE:
							retvalue += "long double";
							break;
						case T_FLOAT128:
							retvalue += "__float128";
							break;
						case T_ELLIPSIS:
							retvalue += "ellipsis";
							break;
						case T_DD:
							retvalue += "IEEE 754r decimal floating point (64 bits)";
							break;
						case T_DE:
							retvalue += "IEEE 754r decimal floating point (128 bits)";
							break;
						case T_DF:
							retvalue += "IEEE 754r decimal floating point (32 bits)";
							break;
						case T_DH:
							retvalue += "IEEE 754r half-precision floating point (16 bits)";
							break;
						case T_CHAR32:
							retvalue += "char32_t";
							break;
						case T_CHAR16:
							retvalue += "char16_t";
							break;
						case T_AUTO:
							retvalue += "auto";
							break;
						case T_NULLPTR:
							retvalue += "std::nullptr_t";
							break;
						default:
							break;
					}
					break;

				//number
				case TT_NUM:
					retvalue += std::to_string(i->num);
					break;
				//named type
				case TT_NAME:
					//print the name
					retvalue += printname(i->n, compiler);
					break;
				default:
					break;
			}

			//array
			if (i->is_array) {
				last_shown_endtpl = false;
				retvalue += " ";
				if (i->array_dimensions.empty()) {
					retvalue += "[]";
				}
				else {
					for(vector<unsigned int>::iterator j = i->array_dimensions.begin(); j != i->array_dimensions.end(); ++j) {
						retvalue += "[";
						retvalue += std::to_string(*j);
						retvalue += "]";
					}
				}
			}

			//Borland
			if (compiler == "borland") {
				if (!i->modifiers.empty()) {
					last_shown_endtpl = false;
					retvalue += printpostmodifiers(i->modifiers,true);
				}
			}

			else {
				if (!i->modifiers.empty()) {
					last_shown_endtpl = false;
					retvalue += printmodifiers(i->modifiers,true);
				}
			}
		}
	}
	return retvalue;
}

/**
 * @brief Function which converts optype enum to string.
 * @param x The optype to be converted.
 * @return The string containing name of the operator.
 */
string cName::optypetostr(optype x) {
	string retvalue;
	switch (x) {
		case OT_NEW:
			retvalue = "new";
			break;
		case OT_NEWARR:
			retvalue = "new[]";
			break;
		case OT_DEL:
			retvalue = "delete";
			break;
		case OT_DELARR:
			retvalue = "delete[]";
			break;
		case OT_UPLUS:
			retvalue = "+";
			break;
		case OT_UMINUS:
			retvalue = "-";
			break;
		case OT_UAND:
			retvalue = "&";
			break;
		case OT_UAST:
			retvalue = "*";
			break;
		case OT_TILDA:
			retvalue = "~";
			break;
		case OT_PLUS:
			retvalue = "+";
			break;
		case OT_MINUS:
			retvalue = "-";
			break;
		case OT_AST:
			retvalue = "*";
			break;
		case OT_DIV:
			retvalue = "/";
			break;
		case OT_MOD:
			retvalue = "%";
			break;
		case OT_AND:
			retvalue = "&";
			break;
		case OT_OR:
			retvalue = "|";
			break;
		case OT_EXP:
			retvalue = "^";
			break;
		case OT_ASSIGN:
			retvalue = "=";
			break;
		case OT_PLUSASS:
			retvalue = "+=";
			break;
		case OT_MINUSASS:
			retvalue = "-=";
			break;
		case OT_ASTASS:
			retvalue = "*=";
			break;
		case OT_DIVASS:
			retvalue = "/=";
			break;
		case OT_MODASS:
			retvalue = "%=";
			break;
		case OT_ANDASS:
			retvalue = "&=";
			break;
		case OT_ORASS:
			retvalue = "|=";
			break;
		case OT_EXPASS:
			retvalue = "^=";
			break;
		case OT_LSHIFT:
			retvalue = "<<";
			break;
		case OT_RSHIFT:
			retvalue = ">>";
			break;
		case OT_LSHIFTASS:
			retvalue = "<<=";
			break;
		case OT_RSHIFTASS:
			retvalue = ">>=";
			break;
		case OT_EQ:
			retvalue = "==";
			break;
		case OT_NEQ:
			retvalue = "!=";
			break;
		case OT_LT:
			retvalue = "<";
			break;
		case OT_GT:
			retvalue = ">";
			break;
		case OT_LE:
			retvalue = "<=";
			break;
		case OT_GE:
			retvalue = ">=";
			break;
		case OT_NOT:
			retvalue = "!";
			break;
		case OT_ANDAND:
			retvalue = "&&";
			break;
		case OT_OROR:
			retvalue = "||";
			break;
		case OT_PLUSPLUS:
			retvalue = "++";
			break;
		case OT_MINUSMINUS:
			retvalue = "--";
			break;
		case OT_COMMA:
			retvalue = ",";
			break;
		case OT_PTAST:
			retvalue = "->*";
			break;
		case OT_PT:
			retvalue = "->";
			break;
		case OT_BRACKETS:
			retvalue = "()";
			break;
		case OT_ARR:
			retvalue = "[]";
			break;
		case OT_QUESTION:
			retvalue = "?";
			break;
		case OT_SIZEOFT:
			retvalue = "sizeof";
			break;
		case OT_SIZEOFE:
			retvalue = "sizeof";
			break;
		case OT_ALIGNOFT:
			retvalue = "alignof";
			break;
		case OT_ALIGNOFE:
			retvalue = "alignof";
			break;
		default:
			break;
	}
	return retvalue;
}

/**
 * @brief Print the calling convention to a string.
 * @param callconv The calling convention to be printed.
 * @return String containing calling convention.
 */
string cName::printcallingconvention(fcall_t callconv) {
	string retvalue = "";
	if (callconv != FCC_NULL) {
		switch(callconv) {
			case FCC_CDECL:
				retvalue = "__cdecl";
				break;
			case FCC_PASCAL:
				retvalue = "__pascal";
				break;
			case FCC_FORTRAN:
				retvalue = "__fortran";
				break;
			case FCC_THISCALL:
				retvalue = "__thiscall";
				break;
			case FCC_STDCALL:
				retvalue = "__stdcall";
				break;
			case FCC_FASTCALL:
				retvalue = "__fastcall";
				break;
			case FCC_INTERRUPT:
				retvalue = "interrupt";
				break;
			default:
				break;
		}
	}
	return retvalue;
}

/**
 * @brief Function which prints everything the demangled name contains into a string.
 * @param compiler Select the format of name according to compiler ("gcc", "ms", "borland"). Default is "gcc".
 * @return String containing declaration of the demangled function.
 */
string cName::printall(string compiler) {
	string retvalue;
	last_shown_endtpl = false;
	vector<type_t> ret_vec;
	vector<name_t> nam_vec;

	if (member_function_access != MFM_NULL) {
		switch(member_function_access) {
			case MFM_PRIVATE:
				retvalue += "private: ";
				break;
			case MFM_PUBLIC:
				retvalue += "public: ";
				break;
			case MFM_PROTECTED:
				retvalue += "protected: ";
				break;
			default:
				break;
		}
	}

	if (is_static) {
		retvalue += "static ";
	}

	if (is_virtual) {
		retvalue += "virtual ";
	}

	if (return_type.type != TT_UNKNOWN) {
		ret_vec.push_back(return_type);
		retvalue += printparams(ret_vec, false, compiler);
		ret_vec.clear();
		retvalue += " ";
	}

	if (!storage_class.empty()) {
		retvalue += printmodifiers(storage_class,false);
	}

	//get calling convention
	retvalue += printcallingconvention(function_call);
	if (function_call != FCC_NULL) {
		retvalue += " ";
	}

	//print the function name
	retvalue += printname(name, compiler);

	if (name_type == NT_VTABLE) {
		retvalue += "::`vftable'";
	}
	else if (name_type == NT_R0) {
		retvalue += "`RTTI Type Descriptor'";
	}
	else if (name_type == NT_R1) {
		retvalue += "::`RTTI Base Class Descriptor at (";
		for(vector<long int>::iterator i=rttibcd.begin(); i != rttibcd.end(); ++i) {
			if (i != rttibcd.begin()) {
				retvalue += ", ";
			}
			retvalue += std::to_string(*i);
		}
		retvalue += ")'";
	}
	else if (name_type == NT_R2) {
		retvalue += "::`RTTI Base Class Array'";
	}
	else if (name_type == NT_R3) {
		retvalue += "::`RTTI Class Hierarchy Descriptor'";
	}
	else if (name_type == NT_R4) {
		retvalue += "::`RTTI Complete Object Locator'";
	}
	else if (name_type == NT__A) {
		retvalue += "::`typeof'";
	}
	else if (name_type == NT__B) {
		retvalue += "::`local static guard'";
	}
	else if (name_type == NT__C) {
		retvalue += "::";
	}
	else if (name_type == NT__D) {
		retvalue += "::`vbase destructor'";
	}
	else if (name_type == NT__E) {
		retvalue += "::`vector deleting destructor'";
	}
	else if (name_type == NT__F) {
		retvalue += "::`default constructor closure'";
	}
	else if (name_type == NT__G) {
		retvalue += "::`scalar deleting destructor'";
	}
	else if (name_type == NT__H) {
		retvalue += "::`vector constructor iterator'";
	}
	else if (name_type == NT__I) {
		retvalue += "::`vector destructor iterator'";
	}
	else if (name_type == NT__J) {
		retvalue += "::`vector vbase constructor iterator'";
	}
	else if (name_type == NT__K) {
		retvalue += "::`virtual displacement map'";
	}
	else if (name_type == NT__L) {
		retvalue += "::`eh vector constructor iterator'";
	}
	else if (name_type == NT__M) {
		retvalue += "::`eh vector destructor iterator'";
	}
	else if (name_type == NT__N) {
		retvalue += "::`eh vector vbase constructor iterator'";
	}
	else if (name_type == NT__O) {
		retvalue += "::`copy constructor closure'";
	}
	else if (name_type == NT__P) {
		retvalue += "::";
	}
	else if (name_type == NT__Q) {
		retvalue += "::";
	}
	else if (name_type == NT__R) {
		retvalue += "::";
	}
	else if (name_type == NT__S) {
		retvalue += "::`local vftable'";
	}
	else if (name_type == NT__T) {
		retvalue += "::`local vftable constructor closure'";
	}
	else if (name_type == NT__U) {
		retvalue += "::";
	}
	else if (name_type == NT__V) {
		retvalue += "::";
	}
	else if (name_type == NT__W) {
		retvalue += "::";
	}
	else if (name_type == NT__X) {
		retvalue += "::`placement delete closure'";
	}
	else if (name_type == NT__Y) {
		retvalue += "::`placement delete[] closure'";
	}
	else if (name_type == NT__Z) {
		retvalue += "::";
	}

	//if name is a constructor, duplicate the last name element
	if (name_type == NT_CONSTRUCTOR) {
		retvalue += "::";
		//MSVC++
		if (compiler == "ms") {
			nam_vec.push_back(name.back());
			retvalue += printname(nam_vec, compiler);
			nam_vec.clear();
		}

		//GCC
		else {
			nam_vec.push_back(name.back());
			nam_vec.back().tpl = tf_tpl;
			retvalue += printname(nam_vec, compiler);
			nam_vec.clear();
		}
	}
	//if name is a destructor, duplicate the last name element with a tilda
	else if (name_type == NT_DESTRUCTOR) {
		retvalue += "::~";

		//MSVC++
		if (compiler == "ms") {
			nam_vec.push_back(name.back());
			retvalue += printname(nam_vec, compiler);
			nam_vec.clear();
		}

		//GCC
		else {
			nam_vec.push_back(name.back());
			nam_vec.back().tpl = tf_tpl;
			retvalue += printname(nam_vec, compiler);
			nam_vec.clear();
		}
	}
	//if name is an operator, append "operator" and the operator name
	if (name_type == NT_OPERATOR) {
		if (operator_type == OT_CAST) {
			ret_vec.push_back(special_type);
			retvalue += printparams(ret_vec, false, compiler);
			ret_vec.clear();
		}
	}

	//print the parameters
	if (!parameters.empty()) {
		retvalue += "(";
		retvalue += printparams(parameters, true, compiler);
		retvalue += ")";
	}

	if (!modifiers.empty()) {
		retvalue += printmodifiers(modifiers,true);
	}

	return retvalue;
}

/**
 * @brief Function which prints everything the demangled name contains into a string. (overloaded for backward compatibility)
 * @param msvcpp Print the declaration in the MS VC++ style (Some differences in constructors / destructors)? - Default is false.
 * @return String containing declaration of the demangled function.
 */
string cName::printall_old(bool msvcpp) {
	if (msvcpp == true) {
		return printall(string("ms"));
	}
	else {
		return printall(string("gcc"));
	}

}

/**
 *
 */
string cName::type_t::getLlvmType() {
	if (llvmIr.empty()) {
		llvmIr = getLlvmTypePrivate();
		return llvmIr;
	} else {
		return llvmIr;
	}
}

string cName::type_t::getLlvmTypePrivate() {
	//array
	if (is_array && !array_dimensions.empty()) {
		unsigned int arrDim = array_dimensions[0];

		//reduce dimensions by one (if this was the last one, clear the isArray bool)
		array_dimensions.erase(array_dimensions.begin());
		if (array_dimensions.empty()) {
			is_array = false;
		}
		//get array type recursively from the rest of this type
		return "[" + to_string(arrDim) + " x " + getLlvmTypePrivate() + " ]";
	}

	//structure
	if (mst == MST_STRUCT) {
		//TODO: this is not OK, this method is not supported anymore, and it created only empty structure before.
		//If there are some info about structure, create new StructType, fill it with info and add to type manager
		//using TypeManager::addStructType(StructType *s).
		return "i32";
	}

	string typeName = modifiers + " ";
	for (unsigned k = 0; k < n.size(); ++k) {
		typeName += n[k].un + " ";
	}

	//named type
	if (type == cName::TT_NAME) {
		if (typeName.find("AnsiString") != string::npos) {
			return "i8*";
		} else if (typeName.find("WideString") != string::npos) {
			return "%wchar_t*";
		} else {
			return "i32*";
		}
	}

	//modifiers
	while (true) {
		if (modifiers.empty()) {
			break;
		}

		char modifier = modifiers[modifiers.size()-1] ;

		//modifier is a pointer, make a pointer of recursive call on the rest of the type
		if (modifier == 'P') {
			modifiers = modifiers.substr(0, modifiers.size()-1);
			return getLlvmTypePrivate() + "*";
		} else {
			modifiers = modifiers.substr(0,modifiers.size()-1);
		}
	}

	//switch the built-in type
	//TODO add support for named types, take modifiers (const, ppointer, reference) into account, etc.
	//See "struct type_t" in demangler/gparser.h
	switch (b) {
		case demangler::cName::T_VOID: return "void";
		case demangler::cName::T_FLOAT: return "float";
		case demangler::cName::T_DOUBLE: return "double";
		case demangler::cName::T_CHAR:
			//pointer to char is a string
			if (modifiers == "P") {
				return "i8*";
			}
			//normal signed char
			else {
				return "i8";
			}
			break;

		//char
		case demangler::cName::T_SCHAR:
			return "i8";
			break;
		case demangler::cName::T_UCHAR:
			return "i8";
			break;

		//short int
		case demangler::cName::T_SHORT:
			return "i16";
			break;
		case demangler::cName::T_USHORT:
			return "i16";
			break;

		//int
		case demangler::cName::T_INT:
			return "i32";
			break;
		case demangler::cName::T_UINT:
			return "i32";
			break;

		//long int (32-bit?)
		case demangler::cName::T_LONG:
			return "i32";
			break;
		case demangler::cName::T_ULONG:
			return "i32";
			break;

		//long long int (64-bit)
		case demangler::cName::T_LONGLONG:
			return "i64";
			break;
		case demangler::cName::T_ULONGLONG:
			return "i64";
			break;

		//int (128-bit)
		case demangler::cName::T_INT128:
			return "i128";
			break;
		case demangler::cName::T_UINT128:
			return "i128";
			break;

		//TODO unsupported built-in type
		default:
			return "i32";
			break;
	}

	//TODO: an unknown or unsupported type (or a named type, class, union,...)
	return "i32";
}

} // namespace demangler
