Index: cpp/frams/genetics/f4/f4_general.cpp
===================================================================
--- cpp/frams/genetics/f4/f4_general.cpp	(revision 1229)
+++ cpp/frams/genetics/f4/f4_general.cpp	(revision 1230)
@@ -31,38 +31,4 @@
 {
 	*v += 0.7853;  // 0.7853981  45 degrees
-}
-
-int scanRecur(const char* s, int slen, char stopchar)
-{
-	int i = 0;
-	//DB( printf("    scan('%s', '%c')\n", s, stopchar); )
-	while (1)
-	{
-		if (i >= slen)  // ran out the string, should never happen with a correct string
-			return 1; //TODO MacKo 2023-04: interesting: why was this situation made undistinguishable from s[1]==stopchar ? does this have any bad consequences or is "1" just used to tell "advance as little as possible"? Anyway, this function can be eliminated when parsing is simplified.
-		if (stopchar == s[i])  // bumped into stopchar
-			return int(i);
-		if (i < slen - 1) // s[i] is not the last char
-		{
-			if (s[i] == '(') //not an allowed char in f4, perhaps a remnant of old experiments with code
-			{
-				i += 2 + scanRecur(s + i + 1, slen - i - 1, ')');
-				continue;
-			}
-			if (s[i] == '<')
-			{
-				i += 2 + scanRecur(s + i + 1, slen - i - 1, '>');
-				continue;
-			}
-			if (s[i] == '#')
-			{
-				i += 2 + scanRecur(s + i + 1, slen - i - 1, '>');
-				continue;
-			}
-		}
-		// s[i] is a non-special character
-		i++;
-	}
-	return i;
 }
 
@@ -703,5 +669,6 @@
 	// transform geno from string to nodes
 	f4rootnode = new f4_Node();
-	int res = f4_processRecur(genome.c_str(), 0, f4rootnode);
+	int _ = 0;
+	int res = f4_processRecur(genome.c_str(), _, f4rootnode);
 	if (res || (f4rootnode->childCount() != 1))
 	{
@@ -1329,29 +1296,31 @@
 // scan genotype string and build tree
 // return >1 for error (errorpos)
-int f4_processRecur(const char* genot, unsigned int pos0, f4_Node *parent)
-{
-	unsigned int gpos = pos0; //MacKo 2023-04 (TODO): these two variables are often updated before return which has no effect since they are local. Seems like a half step towards making them (or just gpos) in/out parameter which would solve many issues and simplify parsing (getting rid of scanRecur()) while making it more strict.
+int f4_processRecur(const char* genot, int &pos_inout, f4_Node *parent)
+{
 	f4_Node *par = parent;
 
-	if (gpos >= strlen(genot))
+	if (pos_inout >= (int)strlen(genot))
 		return (int)strlen(genot) + 1;
 
-	while (gpos < strlen(genot))
-	{
-		// first switch across cell dividers and old semantics
-		switch (genot[gpos])
+	while (pos_inout < (int)strlen(genot))
+	{
+		//#define PRINT_PARSING_LOCATION
+#ifdef PRINT_PARSING_LOCATION
+		printf("%s\n", genot);
+		for (int i = 0; i < pos_inout; i++) printf(" ");
+		printf("^\n");
+#endif
+		switch (genot[pos_inout])
 		{
 		case '<':
 		{
-			// find out genotype start for child
-			int stopchar_offset = scanRecur(genot + gpos + 1, (int)strlen(genot + gpos + 1), '>');
-
-			f4_Node *node = new f4_Node("<", par, gpos);
+			f4_Node *node = new f4_Node("<", par, pos_inout);
 			par = node;
-			int res = f4_processRecur(genot, gpos + 1, par);
+			pos_inout++; //move after '<'
+			int res = f4_processRecur(genot, pos_inout, par);
 			if (res) return res;
-			if (gpos + stopchar_offset + 2 < strlen(genot))
-			{
-				res = f4_processRecur(genot, gpos + stopchar_offset + 2, par);
+			if (pos_inout < (int)strlen(genot))
+			{
+				res = f4_processRecur(genot, pos_inout, par);
 				if (res) return res;
 			}
@@ -1359,17 +1328,14 @@
 			{
 				//MacKo 2023-04, more strict behavior: instead of silent repair (no visible effect to the user, genotype stays invalid but is interpreted and reported as valid), we now point out where the error is. For example <X> or <X><X or <X><N:N>
-				return gpos + 1; //the problem starts here, occurs because second child (branch) <1..>2..> is not completed
+				return (int)strlen(genot) + 1;
 				//old silent repair:
 				//node = new f4_Node(">", par, int(strlen(genot)) - 1);
-				//par = node;
-			}
-			gpos++;
+			}
 			return 0;  // OK
 		}
 		case '>':
 		{
-			f4_Node *node = new f4_Node(">", par, gpos);
-			par = node;
-			//gpos = (unsigned int)strlen(genot); //MacKo 2023-04: first of all, 'gpos' is a local variable so no effect; second, '>' may be internal (i.e., not the last one in the genotype), so it is a bad hint to assign strlen(). 'par' above is also local...
+			new f4_Node(">", par, pos_inout);
+			pos_inout++; //move after '>'
 			return 0;  // OK
 		}
@@ -1378,26 +1344,21 @@
 			// repetition marker, 1 by default
 			ExtValue val;
-			const char* end = val.parseNumber(genot + gpos + 1, ExtPType::TInt);
+			const char* end = val.parseNumber(genot + pos_inout + 1, ExtPType::TInt);
+			//TODO end==NULL? -> error!
 			int reps = (end == NULL) ? 1 : val.getInt();
-			// find out genotype start for continuation
-			int stopchar_offset = scanRecur(genot + gpos + 1, (int)strlen(genot + gpos + 1), '>');
+			f4_Node *node = new f4_Node("#", par, pos_inout);
+			node->reps = reps;
 			// skip number
-			unsigned int oldpos = gpos;
-			gpos += end - (genot + gpos);
-			//gpos++;
-			//while ((genot[gpos] >= '0') && (genot[gpos] <= '9')) gpos++; node1 = new f4_Node("#", par, oldpos);
-			f4_Node *node = new f4_Node("#", par, oldpos);
-			node->reps = reps;
-			par = node;
-			int res = f4_processRecur(genot, gpos, node);
+			pos_inout += end - (genot + pos_inout);
+			int res = f4_processRecur(genot, pos_inout, node);
 			if (res) return res;
-			if (oldpos + stopchar_offset + 2 < strlen(genot))
-			{
-				res = f4_processRecur(genot, oldpos + stopchar_offset + 2, node);
+			if (pos_inout < (int)strlen(genot))
+			{
+				res = f4_processRecur(genot, pos_inout, node);
 				if (res) return res;
 			}
 			else // ran out
 			{
-				return gpos; //MacKo 2023-04: report an error, better to be more strict instead of a silent repair (genotype stays invalid but is interpreted and reported as valid) with non-obvious consequences?
+				return (int)strlen(genot) + 1; //MacKo 2023-04: report an error, better to be more strict instead of a silent repair (genotype stays invalid but is interpreted and reported as valid) with non-obvious consequences?
 				//earlier apporach - silently treating this problem (we don't ever see where the error is because it gets corrected in some way here, while parsing the genotype, and error location in the genotype is never reported):
 				//node = new f4_Node(">", par, int(strlen(genot)) - 1); // check if needed and if this is really the best repair operation; seemed to happen too many times in succession for some genotypes even though they were only a result of f4 operators, not manually created... and the operators should not generate invalid genotypes, right? Or maybe crossover does? Seems like too many #N's for closing >'s; removing #N or adding > helped. Operators somehow don't do it properly sometimes? But F4_ADD_REP adds '>'... (TODO)
@@ -1411,20 +1372,20 @@
 		{
 			// whitespace: ignore
-			gpos++;
+			pos_inout++;
 			break;
 		}
 		case 'N':
 		{
-			int forgenorange = gpos;
-			if (genot[gpos + 1] != ':')
-				return gpos + 1; //error
-			gpos += 2; //skipping "N:"
-			unsigned int begin_index = gpos;
-			char* end = (char*)genot + begin_index;
-			NeuroClass *neuclass = GenoOperators::parseNeuroClass(end, ModelEnum::SHAPETYPE_BALL_AND_STICK);
+			int forgenorange = pos_inout;
+			if (genot[pos_inout + 1] != ':')
+				return pos_inout + 1; //error
+			pos_inout += 2; //skipping "N:"
+			unsigned int neuroclass_begin = pos_inout;
+			char* neuroclass_end = (char*)genot + neuroclass_begin;
+			NeuroClass *neuclass = GenoOperators::parseNeuroClass(neuroclass_end, ModelEnum::SHAPETYPE_BALL_AND_STICK); //advances neuroclass_end
 			if (neuclass == NULL)
-				return gpos + 1; //error
-			gpos += end - genot - begin_index;
-			string neutype = string(genot + begin_index, genot + gpos);
+				return pos_inout + 1; //error
+			pos_inout += neuroclass_end - genot - neuroclass_begin;
+			string neutype = string(genot + neuroclass_begin, genot + pos_inout);
 			f4_Node *node = new f4_Node(neutype, par, forgenorange);
 			node->neuclass = neuclass;
@@ -1438,21 +1399,20 @@
 			// in the future this could be generalized to all neuron properties, for example N:|:power:0.6:range:1.4, or can even use '=' or ',' instead of ':' if no ambiguity
 			char prop_dir, prop_symbol, prop_end[2]; // prop_end is only to ensure that neuron parameter definition is completed
-			if (sscanf(genot + gpos, ":%c%c%1[:]", &prop_dir, &prop_symbol, &prop_end) != 3)
+			if (sscanf(genot + pos_inout, ":%c%c%1[:]", &prop_dir, &prop_symbol, &prop_end) != 3)
 				// error: incorrect format
-				return gpos + 1 + 1;
+				return pos_inout + 1 + 1;
 			if (prop_dir != '-' && prop_dir != '+')
-				return gpos + 1 + 1; //error
+				return pos_inout + 1 + 1; //error
 			switch (prop_symbol)
 			{
 			case '!':  case '=':  case '/':  break;
 			default:
-				return gpos + 1 + 1; //error
-			}
-			f4_Node *node = new f4_Node(":", par, gpos);
+				return pos_inout + 1 + 1; //error
+			}
+			f4_Node *node = new f4_Node(":", par, pos_inout);
 			node->prop_symbol = prop_symbol;
 			node->prop_increase = prop_dir == '+' ? true : false; // + or -
 			par = node;
-			int stopchar_offset = scanRecur(genot + gpos + 1, (int)strlen(genot + gpos + 1), ':');
-			gpos += stopchar_offset + 2;
+			pos_inout += 4; //skipping :ds:
 			break;
 		}
@@ -1461,22 +1421,22 @@
 			double weight = 0;
 			int relfrom;
-			const char *end = parseConnection(genot + gpos, relfrom, weight);
+			const char *end = parseConnection(genot + pos_inout, relfrom, weight);
 			if (end == NULL)
-				return gpos + 1; //error
-
-			f4_Node *node = new f4_Node("[", par, gpos);
+				return pos_inout + 1; //error
+
+			f4_Node *node = new f4_Node("[", par, pos_inout);
 			node->conn_from = relfrom;
 			node->conn_weight = weight;
 			par = node;
-			int stopchar_offset = scanRecur(genot + gpos + 1, (int)strlen(genot + gpos + 1), ']');
-			gpos += stopchar_offset + 2;
+			pos_inout += end - (genot + pos_inout);
 			break;
 		}
 		default: // 'X' and ',' and all modifiers and also invalid symbols - add a node, for invalid symbols build will give the error or repair
 		{
-			//printf("any regular character '%c'\n", genot[gpos]);
-			f4_Node *node = new f4_Node(genot[gpos], par, gpos);
+			//printf("any regular character '%c'\n", genot[pos_inout]);
+			//TODO here: read a continuous sequence of modifiers, sort and optimize ("collapse") it like in f1, then add to tree
+			f4_Node *node = new f4_Node(genot[pos_inout], par, pos_inout);
 			par = node;
-			gpos++;
+			pos_inout++;
 			break;
 		}
@@ -1487,11 +1447,10 @@
 	if (par && par->name != ">")
 	{
-		//happens when gpos == strlen(genot)
-		//return gpos; //MacKo 2023-04: could report an error instead of silent repair, but repair operators only work in Cells (i.e., after the f4_Node tree has been parsed without errors and Cells can start developing) so we don't want to make a fatal error because of missing '>' here. Also after conversions from Cells to text, trailing '>' is deliberately removed... and also the simplest genotype is officially X, not X>.
-		f4_Node *node = new f4_Node('>', par, int(strlen(genot)) - 1);
-		par = node;
-	}
-
-	return 0;
+		//happens when pos_inout == strlen(genot)
+		//return pos_inout; //MacKo 2023-04: could report an error instead of silent repair, but repair operators only work in Cells (i.e., after the f4_Node tree has been parsed without errors and Cells can start developing) so we don't want to make a fatal error because of missing '>' here. Also after conversions from Cells to text, trailing '>' is deliberately removed... and also the simplest genotype is officially X, not X>.
+		new f4_Node('>', par, int(strlen(genot)) - 1);
+	}
+
+	return 0;  // OK
 }
 
