Stuck on Alternation, could use help. Stage ##zm7

I’m stuck on Stage ##zm7

I’ve been trying for nearing a month now, and at this point, my code is of dubious quality at best.
The challenge is an absurd amount more difficult than all of the challenges before-hand, and ended up making me entirely refactor the entire codebase twice just to make it realistic. Not to say as a bad
thing per say, but I’m very much at my wit’s end with all the debugging. Right now, I think I’m in a spot where it’s nearly functional, but the code is rather strange.

I’ve got a regEx namespace containing all of the functions, and an Expression class containing the basic data: a string containing the original expression, and an int initialized with an enum to match an integer to each individual argument type. parenthesis-enclosed scopes and square bracket enclosed “any of” matches are both counted as a singular expression, as to allow me to iterate through the expressions without needing to search for the matching beginning. It’s ran linearly with a loop checking for matches by calling matchExpression(), unless either a postfix operator, scope operator, or “any of” operator is found, at which time it recursively runs the matchHere function. I wanted to mostly avoid recursion, but I for the life of me couldn’t figure out a way for those that weren’t far too verbose and complicated for my skill level.

I’ve noticed a primary issue for now: I can’t even begin to remember why as this has my brain fried at this point, but on the very last recursion before end of input, the iterator pointing to the current spot in input is not being incremented, leaving it to never reach the end, leaving it to never match the right-side anchor ($).

Sorry for being so verbose, but as I’ve said, I’m at my wit’s end here.

Here are my logs:

remote: [tester::#ZM7] ^[[0m$ echo -n "I see 1 cat, 2 dogs and 3 cows" | ./your_program.sh -E "^I see (\d (cat|dog|cow)s?(, | and )?)+$"

...

remote: [your_program] matchScope ((, | and )) - subscope of: " and " did not match.d. Returning false.
remote: [your_program] matchHere - new match entered.
remote: [your_program] matchHere - Full match. Returning true.
remote: [your_program] postfixZeroOrOne ((, | and )) - Found to be a zero-match. Returning true.
remote: [your_program] matchHere - Match. Continueing...
remote: [your_program] matchHere - Full match. Returning true.
remote: [your_program] postfixZeroOrOne (s) - Found to be a one-match. Returning true.
remote: [your_program] matchHere - Match. Continueing...
remote: [your_program] matchHere - Full match. Returning true.
remote: [your_program] matchScope ((\d (cat|dog|cow)s?(, | and )?)) - subscope of: "\d (cat|dog|cow)s?(, | and )?" match found. Returning true with a current match of: 'postfixOneOrMore ((\d (cat|dog|cow)s?(, | and )?)) - checking possible match: 's'.

... (After the program tried incrementing the starting point forward a few times.)

remote: [your_program] matchHere - Matching expression: $ (8)
remote: [your_program] matchHere - with starting character: 1
remote: [your_program] matchExpression - No postfix found. Expression: $ (8)
remote: [your_program] matchHere - No match. Returning false.
remote: [your_program] matchHere - No match. Returning false.
remote: [tester::#ZM7] Expected exit code 0, got 1
remote: [tester::#ZM7] Test failed

Sorry for so much text in there, too. I ended up going mad with couts just to try and find what was wrong.

And here’s a snippet of my code:

bool regEx::matchPattern(const std::string& pattern, const std::string& text){
	const std::vector<Expression> expressions = parsePattern(pattern);
	std::vector<Expression>::const_iterator exp = expressions.cbegin();
	std::vector<Expression>::const_iterator end = expressions.cend();
	
	std::cout << "matchPattern - Match pattern called with a pattern of: \"" << pattern << "\" \nmatchPattern - And a string input of: \"" << text << "\"" << std::endl;
	
	std::string::const_iterator textBegin = text.begin();
	std::string::const_iterator match = text.begin();
	
	
	if((*exp).type == Expression::ANCHOR_START){
		std::cout << "Pattern is left-anchored. Testing from start..." << std::endl;
		++exp;
		return matchHere(exp, end, match);
	}
	
	std::cout << "matchPattern - Pattern is not left-anchored. Testing from each index..." << std::endl;
	
	int debugIndex = 0;
	while(*textBegin!= '\0'){
		std::cout << "matchPattern - Testing from character: '" << *match << "' at index: " << debugIndex++ << std::endl;
		if (matchHere(exp, end, match))
			return true;
	
		if(*textBegin == '\0')
			return false;
	
		++textBegin;
		match = textBegin;
		exp = expressions.cbegin();
	}
	
	return false;
}


bool regEx::matchHere(std::vector<Expression>::const_iterator& exp, const std::vector<Expression>::const_iterator& end, std::string::const_iterator& match){
	std::string::const_iterator textCurr = match;
	
	std::cout << "matchHere - new match entered." << std::endl;
	
	while(exp != end){
		
		std::cout << "matchHere - Matching expression: " << (*exp).typeString << " (" << (*exp).type << ")\n"
			<< "matchHere - with starting character: " << *textCurr << std::endl;
		
		if (matchExpression(exp, end, textCurr)){
			
			std::cout << "matchHere - Match. Continueing..." << std::endl;
			
			++exp;
			++textCurr;
			continue;
		}
		break;
	}


bool regEx::matchExpression(std::vector<Expression>::const_iterator& exp, const std::vector<Expression>::const_iterator& end, std::string::const_iterator& match){
	if (exp + 1 != end){
		switch((*(exp + 1)).type){
			
			case Expression::MATCH_ONE_OR_MORE:
				std::cout << "matchExpression - One or More (+) postfix found. Processing postfix." << std::endl;
				return postfixOneOrMore(exp, end, match);
				
			case Expression::MATCH_ZERO_OR_ONE:
				std::cout << "matchExpression - Zero or one (?) postfix found. Processing postfix." << std::endl;
				return postfixZeroOrOne(exp, end, match);
			
			default:
				break;
		}
	} 
	
	
	
	if(exp == end){ //If end of scope was reached, success. All match.
	
		std::cout << "matchHere - Full match. Returning true." << std::endl;
		
		match = textCurr;
		return true;
	}
	
	std::cout << "matchHere - No match. Returning false." << std::endl;
	
	return false;
}


	
	std::cout << "matchExpression - No postfix found. Expression: " << (*exp).typeString << " (" << (*exp).type << ")" << std::endl;
	
	
	switch((*exp).type){
		case Expression::EXACT:
			return (*exp).typeString[0] == *match;
			
		case Expression::DIGIT:
			return std::isdigit(*match);
			
		case Expression::WORD:
			return std::isalnum(*match) || *match == '_';
			
		case Expression::ANY:
			return (*match) != '\0';
			
		case Expression::ANY_OF:
			return matchAnyOf(*exp, match);
			
		case Expression::SCOPE:
			return matchScope(*exp, match);
			
		case Expression::ANCHOR_END:
			return (exp + 1) == end && *match == '\0';
			
		default:
			return false;
		
	}
	
}


bool regEx::postfixZeroOrOne(std::vector<Expression>::const_iterator& exp, const std::vector<Expression>::const_iterator& end, std::string::const_iterator& match){
	
	std::cout << "postfixZeroOrOne (" << (*exp).typeString << ") - entered." << std::endl;
	
	std::vector<Expression>::const_iterator nextExp = exp + 2;
	std::string::const_iterator currMatch = match;
	
	
	if(matchExpression(exp, exp + 1, currMatch)){
		std::string::const_iterator tempMatch = currMatch + 1;

		if (matchHere(nextExp, end, tempMatch)){
			
			
			std::cout << "postfixZeroOrOne (" << (*exp).typeString << ") - Found to be a one-match. Returning true." << std::endl;
			
			match = tempMatch - 1;
			exp = end - 1;
			return true;
		}
	}
	
	if (matchHere(nextExp, end, currMatch)){
		std::cout << "postfixZeroOrOne (" << (*exp).typeString << ") - Found to be a zero-match. Returning true." << std::endl;
		
		match = currMatch - 1;
		exp = end - 1;
		return true;
	} else {
		currMatch = match;
	}
	
	std::cout << "postfixZeroOrOne (" << (*exp).typeString << ") - No match found. Returning false." << std::endl;
	return false;
	
}

If you need any other functions just ask. The writing quality fell off drastically after the first week, and at this point nothing’s really even decent, but I’m determined to get this done.

Hey @Ch-byte-afk, could you upload your code to GitHub and share the link? It will be much easier to debug if I can run it directly.

Yessirie. I’ll try and get it down tonight, so if it’s not linked here by the time you get on tomorrow, give me a little nudge as I probably forgot.

Thanks for the help, by the way. I have noticed it’s almost purely you responding to people on here, so I do appreciate that you’re still taking the time.

Alright, did it.

If the codebase is too much of a mess for you to figure anything out you can just let me know, @andy1li . It’s alright. Going by what was said in another message, it seems like future tests require a system where multiple postfixes can be combined anyway, which this very much doesn’t support.

Though, I am very confused why CodeCrafters would have a challenge that expects so many systems never required or even implied in previous challenges? I really feel like there needs to be checks early on to make sure the system being developed can support all these more complicated systems later on, as since the challenge never directly requests it, the fact that you need to prepare for it is completely hidden until you reach this random challenge that all of a sudden expects your system to understand parenthesis-enclosed scopes, combined postfix operators, and postfix operators ran on scoped operators that take more than 1 total character. This is an absurd jump that feels like it should have been made evident in some way beforehand, as with how open-ended the original challenge was, it makes it seem like there’s a high likelihood of a codebase that just can’t realistically continue past this point.

@Ch-byte-afk Sorry for the delayed response!

We’ve simplified the combined test cases for #zm7 Alternation in this PR. Let us know if you’re still running into issues.

Though, I am very confused why CodeCrafters would have a challenge that expects so many systems never required or even implied in previous challenges?

Here’s a bit of background (not an excuse, as we should have handled it better):

  1. Originally, each stage only tested a narrow set of cases.
  2. Over time, some users requested more “realistic” tests that combined multiple features.
  3. We started adding combined tests, but in this case (#zm7), we went a little too far. The tests ended up being much more complex than intended.

I really feel like there needs to be checks early on to make sure the system being developed can support all these more complicated systems later on, as since the challenge never directly requests it, the fact that you need to prepare for it is completely hidden until you reach this random challenge that all of a sudden expects your system to understand parenthesis-enclosed scopes, combined postfix operators, and postfix operators ran on scoped operators that take more than 1 total character. This is an absurd jump that feels like it should have been made evident in some way beforehand, as with how open-ended the original challenge was, it makes it seem like there’s a high likelihood of a codebase that just can’t realistically continue past this point.

Totally fair point!

We’ll continue to iterate on this based on feedback like yours, and this kind of insight is incredibly valuable. Thanks again for taking the time to share it!

Hey, no worries. I’m glad you got back to me at all, and it’s nice to see you guys taking action like that. I had noticed this was a major sticking point for most people on the forum, so simplifying it a bit should help a lot both for completion rate and overall readability of the challenges.

I’m not sure if I’ll be finishing the challenge now, life and the like, but whether I do or not, if I need any more help I’ll let you know. For now, might as well take a crack at it again since I seemed to be close and got plenty of a break. Best of luck, Andy.

1 Like