Skip to content

Commit

Permalink
[3-quell-the-fracus] [Bug Fix] - Ensure that users hand typing tex in…
Browse files Browse the repository at this point in the history
…to Numeric Inputs on Desktop do not cause an infinite loop. (#2182)

## Summary:
Third time's the charm on this one. This ticket is part of the Numeric Input project. 

Numeric Input has different experiences on Desktop versus Mobile: 

1. Desktop allows users to enter basic math commands using their regular keyboard. Desktop does not display / visually support TeX, but CAN parse it as an answer. 
2. Mobile allows users to enter basic math commands using a keypad that crafts TeX to display the output using MathQuill  / Math Input. 

This diverged experience resulted in the possibility to hit an infinite loop on Desktop if the user tries to hand type `\frac` or `\dfrac` TeX commands , as the parser was unable to locate the next symbols to parse. This has likely been a bug since inception, but has become far more noticeable as we're now parsing answers on the fly to provide AI support. As a result, the answers are constantly being evaluated and would hit the infinite loop as soon as the user started typing the expressions.

Issue: LEMS-198

## Test plan:
- Run tests
- New tex wrangler test

Author: SonicScrewdriver

Reviewers: mark-fitzgerald

Required Reviewers:

Approved By: mark-fitzgerald

Checks: ✅ Publish npm snapshot (ubuntu-latest, 20.x), ✅ Cypress (ubuntu-latest, 20.x), ✅ Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ✅ Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ✅ Check builds for changes in size (ubuntu-latest, 20.x), ⏹️  [cancelled] Publish npm snapshot (ubuntu-latest, 20.x), ⏹️  [cancelled] Lint, Typecheck, Format, and Test (ubuntu-latest, 20.x), ⏹️  [cancelled] Cypress (ubuntu-latest, 20.x), ⏹️  [cancelled] Check for .changeset entries for all changed files (ubuntu-latest, 20.x), ⏹️  [cancelled] Check builds for changes in size (ubuntu-latest, 20.x), ✅ Publish Storybook to Chromatic (ubuntu-latest, 20.x)

Pull Request URL: #2182
  • Loading branch information
SonicScrewdriver authored Feb 4, 2025
1 parent 985fb91 commit a48620f
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/pink-buttons-clap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@khanacademy/perseus-score": patch
---

Bugfix to ensure users cannot create infinite loop with incomplete tex in Numeric Input
19 changes: 19 additions & 0 deletions packages/perseus-score/src/util/tex-wrangler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,22 @@ describe("parseTex", () => {
assertParsed(undefined, "");
});
});
describe("parseTex", () => {
it("should replace single fractions", () => {
assertParsed("\\dfrac{3}{4}", "3 / 4");
});

it("should remove blackslash-escapes for percent signs", () => {
assertParsed("3\\%", "3%");
assertParsed("3.5\\%", "3.5%");
assertParsed("\\dfrac{3\\%}{4}", "3% / 4");
});

it("should not throw error when input is undefined", () => {
assertParsed(undefined, "");
});

it("should not cause an infinite loop if provided incomplete tex commands", () => {
assertParsed("\\frac", "/");
});
});
12 changes: 12 additions & 0 deletions packages/perseus-score/src/util/tex-wrangler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,23 @@ function parseNextExpression(
// Find the first '{' and grab subsequent TeX
// Ex) tex: '{3}{7}', and we want the '3'
const openBracketIndex = tex.indexOf("{", currentIndex);

// If there is no open bracket, set the endpoint to the end of the string
// and the expression to an empty string. This helps ensure we don't
// get stuck in an infinite loop when users handtype TeX.
if (openBracketIndex === -1) {
return {
endpoint: tex.length,
expression: "",
};
}

const nextExpIndex = openBracketIndex + 1;

// Truncate to only contain remaining TeX
const endpoint = findEndpoint(tex, nextExpIndex);
const expressionTeX = tex.substring(nextExpIndex, endpoint);

const parsedExp = walkTex(expressionTeX, handler);

return {
Expand Down

0 comments on commit a48620f

Please sign in to comment.