std::string special_tag_a(const HtmlToMarkdown& htmlToMarkdown, CNode node) {
  std::string text = htmlToMarkdown(node);
  std::string href = node.attribute("href");
  translate_link_address(text);
  translate_link_address(href);
  return "[" + text + "](" + href + ")";
}
void CTagCn::operator ()(const CNode& node, CTreeNode& tree_node)
{
	(*this)(node);
	checkAttributes(node, { "type" });
	string attr = node.attribute("type").as_string();
	CNode child = node.first_child();
	if (attr == "real" || attr == "")
	{
		hasNChilds(node, 1);
		nodeIsReal(child, tree_node.Step("cn"));
		return;
	}
}
std::string special_tag_img(const HtmlToMarkdown& htmlToMarkdown, CNode node) {
  std::string alt = node.attribute("alt");
  std::string src = node.attribute("src");

  auto my_emoji = emoji.find(src);
  if (my_emoji == emoji.end()) {
    emoji_not_found[src]++;
    return "![" + alt + "](" + src + ")";
  }
  else {
    return my_emoji->second;
  }
}
void CTagCn::operator ()(const CNode& node)const
{
	checkAttributes(node, { "type" });
	string attr = node.attribute("type").as_string();
	CNode child = node.first_child();
	if (attr == "real" || attr == "")
	{
		hasNChilds(node, 1);
		nodeIsReal(child);
		return;
	}
	if (attr == "integer")
	{
		hasNChilds(node, 1);
		nodeIsInteger(child);
		return;
	};
	// syntax "real <sep/> real"
	if ((attr == "complex-cartesian") || (attr == "complex-polar"))
	{
		hasNChilds(node, 3);
		nodeIsReal(child);
		nodeIsReal(child.next_sibling().next_sibling());
		if (string(child.next_sibling().name()) != string("sep"))
			throwException(node, node.offset_debug(), INCORRECT_VALUE);
		return;
	};

	// syntax "int <sep/> imt"
	if (attr == "rational")
	{
		hasNChilds(node, 3);
		nodeIsInteger(child);
		nodeIsInteger(child.next_sibling().next_sibling());
		if (string(child.next_sibling().name()) != string("sep"))
			throwException(node, node.offset_debug(), INCORRECT_VALUE);
		return;
	};

	if (attr == "constant")
	{
		return;
	};

	throwException(node, node.offset_debug(), UNKNOWN_ATTRIBUTE);
}