ParseNode gen_vardef_simple(const ParseNode & type, std::string name) {
	ParseNode newnode = ParseNode(gen_flex(Term{ TokenMeta::NT_VARIABLEDEFINE, name }), nullptr);
	newnode.addchild(new ParseNode(type)); // type
	newnode.addchild(new ParseNode(gen_flex(Term{ TokenMeta::NT_VOID, "" }), nullptr)); // variable_desc
	ParseNode kv = gen_keyvalue(gen_token(Term{ TokenMeta::UnknownVariant, name }));
	ParseNode pt = gen_paramtable(kv);
	newnode.addchild(new ParseNode(pt)); // type
	return newnode;
}
ParseNode gen_argtable(ParseNode & dimen_slice) {
	ParseNode newnode = ParseNode();
	bool isdimen = false;
	int sliceid = 0; /* if the array has 2 dimensions, sliceid is 0..1 */
	dimen_slice.fs.CurrentTerm.what = "";
	for (sliceid = 0; sliceid < dimen_slice.child.size(); sliceid++)
	{
		if (sliceid != 0) {
			dimen_slice.fs.CurrentTerm.what += ", ";
		}
		if (dimen_slice.fs.CurrentTerm.token == TokenMeta::NT_DIMENSLICE) {
			// dimen_slice
			// slice or slice/exp
			isdimen = true;
			newnode.addchild(new ParseNode(*dimen_slice.child[sliceid]));
			if (dimen_slice.child[sliceid]->fs.CurrentTerm.token == TokenMeta::NT_SLICE) {
				// slice
				if (dimen_slice.child[sliceid]->child.size() == 2) {
					/* from, to */
					sprintf(codegen_buf, "%s, %s", dimen_slice.child[sliceid]->child[0]->fs.CurrentTerm.what.c_str()
						, dimen_slice.child[sliceid]->child[1]->fs.CurrentTerm.what.c_str());
				}
				else {
					// size
					sprintf(codegen_buf, "%s", dimen_slice.child[sliceid]->child[0]->fs.CurrentTerm.what.c_str());
				}
			}
			else {
				// exp
				sprintf(codegen_buf, "%s", dimen_slice.child[sliceid]->fs.CurrentTerm.what.c_str());
			}
		}
		else if(dimen_slice.fs.CurrentTerm.token == TokenMeta::NT_ARGTABLE_PURE){
			// NT_ARGTABLE_PURE
			// exp
			isdimen = false;
			newnode.addchild(new ParseNode(*dimen_slice.child[sliceid]));
			sprintf(codegen_buf, "%s", dimen_slice.child[sliceid]->fs.CurrentTerm.what.c_str());
		}
		else {
			print_error("Illegal argtable", dimen_slice);
		}
		dimen_slice.fs.CurrentTerm.what += codegen_buf;
	}
	if (isdimen) {
		// %%s.slice(%s)
		// should not set codegen_buf here
		sprintf(codegen_buf, "/* deprecated */ slice(%%s, %s)", dimen_slice.fs.CurrentTerm.what.c_str());
		newnode.fs.CurrentTerm = Term{ TokenMeta::NT_PARAMTABLE_DIMENSLICE, string(codegen_buf) };
	}
	else {
		sprintf(codegen_buf, "%s", dimen_slice.fs.CurrentTerm.what.c_str());
		newnode.fs.CurrentTerm = Term{ TokenMeta::NT_ARGTABLE_PURE, string(codegen_buf) };
	}
	return newnode;
}
ParseNode gen_case(const ParseNode & dimen_slice, ParseNode & suite) {
	// one case
	ParseNode newnode = ParseNode();
	suite.fs.CurrentTerm.what = tabber(suite.fs.CurrentTerm.what);
	newnode.fs.CurrentTerm = Term{ TokenMeta::NT_CASE, "" };
	ParseNode select;
	newnode.addchild(new ParseNode(select)); // case
	newnode.addchild(new ParseNode(dimen_slice)); // dimen_slice
	newnode.addchild(new ParseNode(suite)); // suite
	return newnode;
}
ParseNode gen_keyvalue(const ParseNode & variable) {
	/* paramtable is used in function decl */
	/* this paramtable has only one value */

	sprintf(codegen_buf, "%s", variable.fs.CurrentTerm.what.c_str());
	ParseNode newnode = ParseNode(gen_flex(Term{ TokenMeta::NT_VARIABLEINITIAL, string(codegen_buf) }), nullptr);
	newnode.addchild(new ParseNode(variable)); // type
	newnode.addchild(new ParseNode(gen_flex(Term{ TokenMeta::NT_VARIABLEINITIALDUMMY, string("void") }), &newnode, nullptr)); // void is dummy initial

	// instead of return a NT_PARAMTABLE, now return NT_KEYVALUE node
	return newnode;
}
ParseNode gen_keyvalue_from_arraybuilder(const ParseNode & variable, const ParseNode & initial) {
	/* paramtable is used in function decl */
	/* this paramtable has only one value */
	/* 因为使用forarray作为数组, 故需要知道类型信息, 不在此处赋值, 在上层的var_def赋初值 */

	sprintf(codegen_buf, "%s.init(%s)", variable.fs.CurrentTerm.what.c_str(), initial.fs.CurrentTerm.what.c_str());
	ParseNode newnode = ParseNode(gen_flex(Term{ TokenMeta::NT_VARIABLEINITIAL, string(codegen_buf) }), nullptr);
	newnode.addchild(new ParseNode(variable)); // type
	newnode.addchild(new ParseNode(initial)); // void is dummy initial

	// instead of return a NT_PARAMTABLE, now return NT_KEYVALUE node
	return newnode;
}
ParseNode gen_paramtable(ParseNode & paramtable_elem) {
	ParseNode newnode = ParseNode();
	if (paramtable_elem.fs.CurrentTerm.token == TokenMeta::NT_DIMENSLICE 
		|| paramtable_elem.fs.CurrentTerm.token == TokenMeta::NT_ARGTABLE_PURE) {
		// promote dimen_slice to paramtable
		return gen_argtable(paramtable_elem);
	}
	else if(paramtable_elem.fs.CurrentTerm.token == TokenMeta::NT_KEYVALUE){
		newnode.addchild(new ParseNode(paramtable_elem)); // keyvalue
		sprintf(codegen_buf, "%s", paramtable_elem.fs.CurrentTerm.what.c_str());
		newnode.fs.CurrentTerm = Term{ TokenMeta::NT_PARAMTABLE, string(codegen_buf) };
		return newnode;
	}
	else if (paramtable_elem.fs.CurrentTerm.token == TokenMeta::NT_PARAMTABLE
		|| paramtable_elem.fs.CurrentTerm.token == TokenMeta::NT_PARAMTABLE_DIMENSLICE) {
		// get a paramtable from dimen_slice
		newnode = paramtable_elem;
		return newnode;
	}
	else {
		newnode = paramtable_elem;
		print_error("Illegal gen_paramtable arg", newnode);
		return newnode;
	}
}
ParseNode gen_empty_suite() {
	
	ParseNode * newnode = new ParseNode();
	ParseNode & stmt = ParseNode(gen_flex(Term{ TokenMeta::NT_SUITE, "\n" }), newnode);
	newnode->fs.CurrentTerm = Term{ TokenMeta::NT_SUITE, "\n" };
	newnode->addchild(new ParseNode(stmt)); // stmt
	newnode = flattern_bin(newnode);
	return *newnode;
}
ParseNode gen_array_generate_stmt(const ParseNode & _generate_stmt) {
	/* give generate stmt */
	ParseNode newnode = ParseNode();
	ParseNode * exp = _generate_stmt.child[0];
	ParseNode * index = _generate_stmt.child[1];
	ParseNode * from = _generate_stmt.child[2];
	ParseNode * to = _generate_stmt.child[3];
	//print_error("LBound don't agree", _generate_stmt);
	sprintf(codegen_buf, "for(int %s = %s; %s < %s; %s++){\n%s(%s) = %s;\n}", index->fs.CurrentTerm.what.c_str(), from->fs.CurrentTerm.what.c_str() /* exp_from */
		, index->fs.CurrentTerm.what.c_str(), to->fs.CurrentTerm.what.c_str() /* exp_to */, index->fs.CurrentTerm.what.c_str() /* index variable inc */
		, "\t%s" /* array variable name */, index->fs.CurrentTerm.what.c_str() /* index variable */, exp->fs.CurrentTerm.what.c_str());
	newnode.fs.CurrentTerm = Term{ TokenMeta::NT_ARRAYBUILDER_EXP, string(codegen_buf) };
	newnode.addchild(new ParseNode(*exp)); // exp
	newnode.addchild(new ParseNode(*index)); // index variable
	newnode.addchild(new ParseNode(*from)); // exp_from
	newnode.addchild(new ParseNode(*to)); // exp_to
	return newnode;

}
ParseNode gen_promote_paramtable(const ParseNode paramtable) {
	const ParseNode * pn = &paramtable;
	ParseNode newnode = ParseNode(gen_flex(Term{ TokenMeta::NT_PARAMTABLE, "" }), nullptr);
	do {
		// for all non-flatterned paramtable
		for (int i = 0; i < pn->child.size(); i++)
		{
			newnode.addchild(new ParseNode(gen_promote_exp_to_keyvalue(*pn->child[i])));
		}
		if (pn->child.size() >= 2)
		{
			/* if pn->child.size() == 0, this is an empty paramtable(this function takes no arguments) */
			/* if the paramtable is not flatterned pn->child[1] is a right-recursive paramtable node */
			pn = pn->child[1];
		}
	} while (pn->child.size() == 2 && pn->child[1]->fs.CurrentTerm.token == TokenMeta::NT_PARAMTABLE);
	return newnode;
}
ParseNode gen_select(const ParseNode & exp, const ParseNode & case_stmt) {

	ParseNode newnode = ParseNode();
	string codegen = "";
	for (int i = 0; i < case_stmt.child.size(); i++)
	{
		ParseNode & case_stmt_elem = *case_stmt.child[i];
		ParseNode & dimen_slice = *case_stmt_elem.child[1];
		/*
		0 -- case
		1 -- dimen_slice
		2 -- stmt(case body)
		*/
		if (dimen_slice.fs.CurrentTerm.token == TokenMeta::NT_DIMENSLICE) {
			// NT_DIMENSLICE
			string dsstr;
			for (int sliceid = 0; sliceid < dimen_slice.child.size(); sliceid++)
			{
				if (sliceid == 0) {
					sprintf(codegen_buf, "(%s >= %s && %s < %s)", exp.fs.CurrentTerm.what.c_str(), dimen_slice.child[sliceid]->child[0]->fs.CurrentTerm.what.c_str(), exp.fs.CurrentTerm.what.c_str(), dimen_slice.child[sliceid]->child[1]->fs.CurrentTerm.what.c_str());
				}
				else {
					sprintf(codegen_buf, " || (%s >= %s && %s < %s)", exp.fs.CurrentTerm.what.c_str(), dimen_slice.child[sliceid]->child[0]->fs.CurrentTerm.what.c_str(), exp.fs.CurrentTerm.what.c_str(), dimen_slice.child[sliceid]->child[1]->fs.CurrentTerm.what.c_str());
				}
				dsstr += string(codegen_buf);
			}
			if (i == 0) {
				sprintf(codegen_buf, "if(%s){\n%s}\n", dsstr.c_str(), case_stmt_elem.child[2]->fs.CurrentTerm.what.c_str());
			}
			else {
				sprintf(codegen_buf, "else if(%s){\n%s}\n", dsstr.c_str(), case_stmt_elem.child[2]->fs.CurrentTerm.what.c_str());
			}
		}
		else {
			// NT_ARGTABLE_PURE
			string choice = "";
			if (i == 0) {
				choice = "if(";
				sprintf(codegen_buf, "if(%s == %s){\n%s}\n", exp.fs.CurrentTerm.what.c_str(), dimen_slice.child[0]->fs.CurrentTerm.what.c_str(), case_stmt_elem.child[2]->fs.CurrentTerm.what.c_str());
			}
			else {
				choice = "else if(";
				sprintf(codegen_buf, "else if(%s == %s){\n%s}\n", exp.fs.CurrentTerm.what.c_str(), dimen_slice.child[0]->fs.CurrentTerm.what.c_str(), case_stmt_elem.child[2]->fs.CurrentTerm.what.c_str());
			}
			for (int j = 0; j < dimen_slice.child.size(); j++)
			{
				if (j == 0) {
					sprintf(codegen_buf, "%s == %s", exp.fs.CurrentTerm.what.c_str(), dimen_slice.child[j]->fs.CurrentTerm.what.c_str());
				}
				else {
					sprintf(codegen_buf, "|| (%s == %s)", exp.fs.CurrentTerm.what.c_str(), dimen_slice.child[j]->fs.CurrentTerm.what.c_str());
				}
				choice += codegen_buf;
			}
			sprintf(codegen_buf, "){\n%s}\n", case_stmt_elem.child[2]->fs.CurrentTerm.what.c_str());
			choice += codegen_buf;
			sprintf(codegen_buf, "%s", choice.c_str());
		}
		codegen += codegen_buf;
	}
	newnode.fs.CurrentTerm = Term{ TokenMeta::NT_SELECT, codegen };
	ParseNode select = ParseNode();
	newnode.addchild(new ParseNode(select)); // select
	newnode.addchild(new ParseNode(exp)); // exp
	newnode.addchild(new ParseNode(case_stmt)); // suite
	return newnode;
}
ParseNode gen_vardef(const ParseNode & type_spec, const ParseNode & variable_desc, const ParseNode & paramtable) {
	ParseNode newnode = ParseNode();
	string arr_decl = ""; string var_decl = ""; bool do_arr = false;
	VariableDescAttr * vardescattr = dynamic_cast<VariableDescAttr *>(variable_desc.attr);
	ParseNode * slice = vardescattr->desc.slice;
	ParseNode * spec_typename = promote_type(type_spec, vardescattr); // reset type according to kind
	if (slice == nullptr) {
		// slice == nullptr if this is not array
		/* must assure no ParseNode * is nullptr */
		slice = new ParseNode(gen_flex(Term{ TokenMeta::NT_VOID, "" }), nullptr);
	}
	else {
		do_arr = true;
	}
	newnode.addchild(spec_typename); // type
	newnode.addchild(slice); // variable_desc
	ParseNode * pn = new ParseNode(gen_promote_paramtable(paramtable));
	newnode.addchild(pn); // paramtable
	newnode.attr = new VariableDescAttr(*dynamic_cast<VariableDescAttr *>(variable_desc.attr)); // attr
	if (do_arr)
	{
		// ARRAY
		arr_decl = gen_vardef_array(pn, spec_typename, slice, vardescattr);
		newnode.fs.CurrentTerm = Term{ TokenMeta::NT_VARIABLEDEFINE, arr_decl };
	}
	else {
		// SCALAR
		sprintf(codegen_buf, gen_vardef_typestr(vardescattr).c_str(), spec_typename->fs.CurrentTerm.what.c_str());
		string typestr = string(codegen_buf);
		var_decl += typestr;
		bool hitted = false; // 是否至少有一个变量,因为有的变量定义可能是函数的声明,这在c++规范是不允许的,所以可能出现空int,空逗号的情况。
							 /* enumerate paramtable */
		// pn is flattened
		for (int i = 0; i < pn->child.size(); i++)
		{
			ParseNode * this_variable = new ParseNode(*pn->child[i]);
			// skip if it is a function
			// TODO no module currently
			if (get_function("", this_variable->fs.CurrentTerm.what)) {
				continue;
			}
			if (hitted) {
				var_decl += ", ";
			}
			hitted = true;

			sprintf(codegen_buf, "%s", this_variable->child[0]->fs.CurrentTerm.what.c_str());

			var_decl += codegen_buf;
			/* initial value */
			if (this_variable->child[1]->fs.CurrentTerm.token != TokenMeta::NT_VARIABLEINITIALDUMMY) {
				/* if initial value is not dummy but `exp` */
				var_decl += " = ";
				var_decl += this_variable->child[1]->fs.CurrentTerm.what;
			}
			/* desc */
			this_variable->attr = vardescattr->clone();
		}
		var_decl += ";";
		if (!hitted) {
			// all function declarations
			var_decl = "";
		}

		newnode.fs.CurrentTerm = Term{ TokenMeta::NT_VARIABLEDEFINE, var_decl };
	} // end if
	// set all elements' attr in paramtable 
	for (int i = 0; i < pn->child.size(); i++)
	{
		pn->child[i]->attr = newnode.attr->clone();
	}
	return newnode;
}
ParseNode gen_interface(const ParseNode & wrappers) {
	ParseNode newnode = ParseNode(gen_flex(Term{TokenMeta::NT_INTERFACE, ""/*wrappers.fs.CurrentTerm.what*/}), nullptr);

	newnode.addchild(new ParseNode(wrappers));
	return newnode;
}
ParseNode gen_array_generate_paramtable(const ParseNode & argtable) {
	/* give initial value */
	/* `B(1:2:3)` can be either a single-element argtable or a exp, this can probably lead to reduction conflicts, so merge rules */
	/* NOTE fortran use a 1d list to initialize a 2d(or higher) array, however, contrary to c++ and most other language does, it store them in a **conlumn - first order**. for a 2d array, it means you a order of a(1)(1)->a(2)(1)->a(lb_1)(1)->a(1)(2) */
	ParseNode newnode = ParseNode();
	vector<ParseNode *> slices;
	vector<ParseNode *> hiddens;
	std::vector<std::string> ans;
	string fi;
	for (int i = 0; i < argtable.child.size() + 1; i++)
	{
		int stat = 0;
		if (i == argtable.child.size()) {
			stat = 0;
		}else
		{
			if (argtable.child[i]->fs.CurrentTerm.token == TokenMeta::NT_EXPRESSION) {
				if (argtable.child[i]/* NT_EXPRESSION */->child[0]->fs.CurrentTerm.token == TokenMeta::NT_FUCNTIONARRAY) {
					// slice
					stat = 1;
					slices.push_back(argtable.child[i]->child[0]);
				}
				else if (argtable.child[i]/* NT_EXPRESSION */->child[0]->fs.CurrentTerm.token == TokenMeta::NT_HIDDENDO) {
					// hidden_do
					stat = 2;
					hiddens.push_back(argtable.child[i]->child[0]);
				}
			}
			else {
				/* for1array<_Container_value_type> & farr, const std::vector<int> & lower_bound
				, const std::vector<int> & size, const std::vector<T> & values */
				// set in gen_vardef.cpp
				sprintf(codegen_buf, "init_for1array(%%s, %%s, %%s, std::vector<%%s>{%s});\n", /* value */ argtable.fs.CurrentTerm.what.c_str());
				goto CAN_ONLY_GEN_ONE;
			}
		}
		if (stat != 1 && !slices.empty()) {
			for (int i = 0; i < slices.size(); i++)
			{
				ans.push_back(slices[i]->fs.CurrentTerm.what);
			}
			slices.clear();
		}
		if (stat != 2 && !hiddens.empty()) {
			for (int i = 0; i < hiddens.size(); i++)
			{
				ans.push_back(hiddens[i]->fs.CurrentTerm.what);
			}
			hiddens.clear();
		}
	}
	for (int i = 0; i < ans.size(); i++)
	{
		if (i != 0)
			fi += " + ";
		fi += ans[i];
	}
	sprintf(codegen_buf, "%%s = %s;\n", /* value */ fi.c_str());
CAN_ONLY_GEN_ONE:
	newnode.fs.CurrentTerm = Term{ TokenMeta::NT_ARRAYBUILDER_VALUE, string(codegen_buf) };
	newnode.addchild(new ParseNode(argtable)); // argtable
	return newnode;
}
ParseNode gen_stmt(const ParseNode & content, const std::string & rules) {
	sprintf(codegen_buf, rules.c_str() , content.fs.CurrentTerm.what.c_str());
	ParseNode newnode = ParseNode(gen_flex(Term{ TokenMeta::NT_STATEMENT, string(codegen_buf) }), nullptr);
	newnode.addchild(new ParseNode(content)); 
	return newnode;
}
ParseNode gen_paramtable(ParseNode & paramtable_elem, ParseNode & paramtable) {
	ParseNode newnode = ParseNode(gen_flex(Term{ TokenMeta::NT_PARAMTABLE, "" }), nullptr);
	bool dimen1 = false, dimen2 = false, arg1 = false, arg2 = false, va1 = false, va2 = false;;
	if (paramtable_elem.fs.CurrentTerm.token == TokenMeta::NT_DIMENSLICE) {
		dimen1 = true;
	}
	if (paramtable_elem.fs.CurrentTerm.token == TokenMeta::NT_ARGTABLE_PURE) {
		arg1 = true;
	}
	if (TokenMeta::iselement(paramtable_elem.fs.CurrentTerm.token)) {
		va1 = true;
	}
	if (paramtable.fs.CurrentTerm.token == TokenMeta::NT_PARAMTABLE_DIMENSLICE) {
		dimen2 = true;
	}
	if (paramtable.fs.CurrentTerm.token == TokenMeta::NT_ARGTABLE_PURE) {
		arg2 = true;
	}
	if (TokenMeta::iselement(paramtable.fs.CurrentTerm.token)) {
		va2 = true;
	}
	if (paramtable_elem.fs.CurrentTerm.token == TokenMeta::NT_KEYVALUE && paramtable.fs.CurrentTerm.token == TokenMeta::NT_PARAMTABLE) {
		// all keyvalue pair 
		newnode = gen_flattern(paramtable_elem, paramtable, "%s, %s", TokenMeta::NT_PARAMTABLE);
	}
	else if(paramtable.fs.CurrentTerm.token == TokenMeta::NT_PARAMTABLE){
		// there is keyvalue pair
		// this is possible because of rule `dimen_slice : exp ',' paramtable `
		// there is keyvalue
		if (dimen1) {
			// promote dimen_slice to paramtable
			for (int i = 0; i < paramtable_elem.child.size(); i++)
			{
				newnode.addchild(new ParseNode(*paramtable_elem.child[i]));
			}
		}
		else {
			// do not promote exp to keyvalue
			// TODO
			newnode.addchild(new ParseNode(paramtable_elem));
		}
		// assume paramtable is flatterned
		for (int i = 0; i < paramtable.child.size(); i++)
		{
			newnode.addchild(new ParseNode(*paramtable.child[i]));
		}
		sprintf(codegen_buf, "%s, %s", paramtable_elem.fs.CurrentTerm.what.c_str(), paramtable.fs.CurrentTerm.what.c_str());
		newnode.fs.CurrentTerm.what = string(codegen_buf);
	}
	else if ((dimen1 || arg1 || va1) && (dimen2 || arg2 || va2)) {
		// all dimen_slice or argument_pure or variable
		newnode = ParseNode(gen_flex(Term{ (dimen1 || dimen2) ? TokenMeta::NT_PARAMTABLE_DIMENSLICE : TokenMeta::NT_ARGTABLE_PURE, "" }), nullptr);
		if (dimen1 || arg1) {
			for (int i = 0; i < paramtable_elem.child.size(); i++)
			{
				newnode.addchild(new ParseNode(*paramtable_elem.child[i]));
			}
		}
		else {
			newnode.addchild(new ParseNode(paramtable_elem));
		}
		// assume paramtable is flatterned
		if (dimen2 || arg2) {
			for (int i = 0; i < paramtable.child.size(); i++)
			{
				newnode.addchild(new ParseNode(*paramtable.child[i]));
			}
		}
		else {
			newnode.addchild(new ParseNode(paramtable));
		}
		sprintf(codegen_buf, "%s, %s", paramtable_elem.fs.CurrentTerm.what.c_str(), paramtable.fs.CurrentTerm.what.c_str());
		newnode.fs.CurrentTerm.what = string(codegen_buf);
	}
	else {
		print_error("bad param table");
	}
	return newnode;
}