Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions expression_parsing/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ The first public release. Further tidy-up after the 0.2.0 extraction: previewing
### Added

- `SympyParsingConfig.replace` also takes `extra_unsplittable_symbols` and `extra_symbol_assumptions`, alongside parameter changes (`config.replace(simplify=False, extra_unsplittable_symbols=(...))`); they needed `dataclasses.replace` before.
- `notation_warnings(response, strict_syntax)` returns the `NotationWarning`s for a response: `EXPONENT` (`^`) and `FACTORIAL` (`!`) under strict syntax, and `TRIPLE_FACTORIAL` (`!!!`) always. `NotationWarning` is a `StrEnum` whose values are compareExpressions' feedback tags (`NOTATION_WARNING_EXPONENT`, ...), so a tag lookup keeps working. Wording the warnings is still up to the caller. It was `notation_warnings` in compareExpressions' `evaluation.py`, and behaves the same: the checks are substring tests, so under strict syntax `!=` also warns `FACTORIAL`, and `!!!` warns both `FACTORIAL` and `TRIPLE_FACTORIAL`.
- DEBUG logging through module loggers (`lambdafeedback.expression_parsing.*`), with a `NullHandler` on the package logger; nothing is logged at INFO or above. See the repository README. Preprocessing, parsing and `parse_latex` log each step's result.

### Security
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
UnknownConventionError,
)
from .latex import MAX_LATEX_NESTING, extract_latex, latex_symbols, parse_latex, sanitise_latex, sympy_to_latex
from .notation import NotationWarning, notation_warnings
from .numbers import (
compute_relative_tolerance_from_significant_decimals,
generate_arbitrary_number_pattern_matcher,
Expand Down Expand Up @@ -82,6 +83,7 @@
"FunctionName",
"InvalidParameterError",
"LatexParseError",
"NotationWarning",
"SymbolAssumptionError",
"SymbolSpec",
"SympyParsingConfig",
Expand All @@ -103,6 +105,7 @@
"is_number",
"latex_symbols",
"normalise_unicode_operators",
"notation_warnings",
"parse_expression",
"parse_expressions",
"parse_latex",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Notation in a response that is likely to be misread, such as ``^`` for a power under strict syntax.

Only the detection lives here: wording the warnings for a learner is up to
the caller, keyed by :class:`NotationWarning`.
"""

from __future__ import annotations

from enum import StrEnum


class NotationWarning(StrEnum):
"""A notation warning; the values are compareExpressions' feedback tags."""

EXPONENT = "NOTATION_WARNING_EXPONENT"
"""``^`` under strict syntax, which doesn't read it as a power."""
FACTORIAL = "NOTATION_WARNING_FACTORIAL"
"""``!`` under strict syntax, which doesn't read it as a factorial."""
TRIPLE_FACTORIAL = "NOTATION_WARNING_TRIPLE_FACTORIAL"
"""``!!!``, which is never supported."""


def notation_warnings(response: str, strict_syntax: bool) -> list[NotationWarning]:
"""The notation warnings for ``response``, in the order of :class:`NotationWarning`.

The checks look for the characters anywhere in ``response``, so under
strict syntax ``!`` also warns for ``!=`` and ``!!!`` (which gets both
factorial warnings).
"""
warnings = []
if strict_syntax and "^" in response:
warnings.append(NotationWarning.EXPONENT)
if strict_syntax and "!" in response:
warnings.append(NotationWarning.FACTORIAL)
if "!!!" in response:
warnings.append(NotationWarning.TRIPLE_FACTORIAL)
return warnings
28 changes: 28 additions & 0 deletions expression_parsing/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
ExpressionParsingError,
InvalidParameterError,
LatexParseError,
NotationWarning,
SymbolAssumptionError,
SymbolSpec,
SympyParsingConfig,
Expand All @@ -17,6 +18,7 @@
is_complex_number_on_cartesian_form,
is_complex_number_on_exponential_form,
is_number,
notation_warnings,
parse_expression,
parse_expressions,
parse_latex,
Expand Down Expand Up @@ -185,3 +187,29 @@ def test_number_forms():
assert is_complex_number_on_cartesian_form("1+2*I")
assert is_complex_number_on_exponential_form("2*exp(3*I)")
assert not is_number("x")


class TestNotationWarnings:
@pytest.mark.parametrize(
"response, strict, expected",
[
("x**2", True, []),
("x^2", True, [NotationWarning.EXPONENT]),
("x^2", False, []),
("n!", True, [NotationWarning.FACTORIAL]),
("n!", False, []),
("x != y", True, [NotationWarning.FACTORIAL]),
("n!!!", False, [NotationWarning.TRIPLE_FACTORIAL]),
("n!!!", True, [NotationWarning.FACTORIAL, NotationWarning.TRIPLE_FACTORIAL]),
("x^n!", True, [NotationWarning.EXPONENT, NotationWarning.FACTORIAL]),
],
)
def test_warnings(self, response, strict, expected):
assert notation_warnings(response, strict) == expected

def test_values_are_the_compare_expressions_tags(self):
assert [str(warning) for warning in NotationWarning] == [
"NOTATION_WARNING_EXPONENT",
"NOTATION_WARNING_FACTORIAL",
"NOTATION_WARNING_TRIPLE_FACTORIAL",
]
Loading