diff --git a/iosMath/lib/MTMathListBuilder.m b/iosMath/lib/MTMathListBuilder.m index f3a9943..ab492d4 100644 --- a/iosMath/lib/MTMathListBuilder.m +++ b/iosMath/lib/MTMathListBuilder.m @@ -746,15 +746,18 @@ - (MTMathAtom*) atomForCommand:(NSString*) command } else if ([command isEqualToString:@"sqrt"]) { // A sqrt command with one argument MTRadical* rad = [MTRadical new]; - unichar ch = [self getNextCharacter]; - if (ch == '[') { - // special handling for sqrt[degree]{radicand} - rad.degree = [self buildInternal:false stopChar:']']; - rad.radicand = [self buildInternal:true]; - } else { - [self unlookCharacter]; - rad.radicand = [self buildInternal:true]; + // Guard against a lone "\sqrt" at the end of input: only read a + // character if one is available. + if ([self hasCharacters]) { + unichar ch = [self getNextCharacter]; + if (ch == '[') { + // special handling for sqrt[degree]{radicand} + rad.degree = [self buildInternal:false stopChar:']']; + } else { + [self unlookCharacter]; + } } + rad.radicand = [self buildInternal:true]; return rad; } else if ([command isEqualToString:@"left"]) { // Save the current inner while a new one gets built. diff --git a/iosMathTests/MTMathListBuilderTest.m b/iosMathTests/MTMathListBuilderTest.m index 36a571c..573db44 100644 --- a/iosMathTests/MTMathListBuilderTest.m +++ b/iosMathTests/MTMathListBuilderTest.m @@ -378,6 +378,55 @@ - (void) testSqrt XCTAssertEqualObjects(latex, @"\\sqrt{2}", @"%@", desc); } +- (void) testSqrtAtEnd +{ + // A lone \sqrt with no argument at the end of the input must not crash + // (it previously asserted in getNextCharacter). It should parse as a + // radical with an empty radicand, matching \sqrt{}. + NSString *str = @"\\sqrt"; + MTMathList* list = [MTMathListBuilder buildFromString:str]; + NSString* desc = [NSString stringWithFormat:@"Error for string:%@", str]; + + XCTAssertNotNil(list, @"%@", desc); + XCTAssertEqualObjects(@(list.atoms.count), @1, @"%@", desc); + MTRadical* rad = list.atoms[0]; + XCTAssertEqual(rad.type, kMTMathAtomRadical, @"%@", desc); + XCTAssertEqualObjects(rad.nucleus, @"", @"%@", desc); + + MTMathList *subList = rad.radicand; + XCTAssertNotNil(subList, @"%@", desc); + XCTAssertEqualObjects(@(subList.atoms.count), @0, @"%@", desc); + XCTAssertNil(rad.degree, @"%@", desc); + + // convert it back to latex + NSString* latex = [MTMathListBuilder mathListToString:list]; + XCTAssertEqualObjects(latex, @"\\sqrt{}", @"%@", desc); +} + +- (void) testSqrtInGroup +{ + // A \sqrt with no argument inside a group exercises a different path + // (empty radicand via the oneCharOnly stop-char guard) than the + // end-of-input case, and must also not crash. + NSString *str = @"{\\sqrt}"; + MTMathList* list = [MTMathListBuilder buildFromString:str]; + NSString* desc = [NSString stringWithFormat:@"Error for string:%@", str]; + + XCTAssertNotNil(list, @"%@", desc); + XCTAssertEqualObjects(@(list.atoms.count), @1, @"%@", desc); + MTRadical* rad = list.atoms[0]; + XCTAssertEqual(rad.type, kMTMathAtomRadical, @"%@", desc); + + MTMathList *subList = rad.radicand; + XCTAssertNotNil(subList, @"%@", desc); + XCTAssertEqualObjects(@(subList.atoms.count), @0, @"%@", desc); + XCTAssertNil(rad.degree, @"%@", desc); + + // convert it back to latex + NSString* latex = [MTMathListBuilder mathListToString:list]; + XCTAssertEqualObjects(latex, @"\\sqrt{}", @"%@", desc); +} + - (void) testSqrtInSqrt { NSString *str = @"\\sqrt\\sqrt2";