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
13 changes: 13 additions & 0 deletions BREAKING-CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ A not-equal written `<>` is read as `not (a = b)`, and printed that way.

## Unreleased — since 2.6.0

### A subscript may have a subscript

**Not silent: what was a parse error is a name.** A name took at most one `_`, so `x_1_2` and `X_a_beta` were lexer
errors. A subscript may now have one of its own: `X_a_beta` is one variable, `X` with the subscript `a_beta`, printed
in LaTeX as `X_{a_{\beta}}`. `_` still neither begins nor ends a name, and two do not stand together
([#524](https://github.com/asc-community/AngouriMath/issues/524)).

| Input | Was (2.6.0) | Now |
|---|---|---|
| `"X_a_beta + 10".ToEntity()` | an `UnhandledParseException` | `X_a_beta + 10` |
| `MathS.Var("X_a_beta").Latexize()` | an `UnhandledParseException` | `X_{a_{\beta}}` |
| `"x__1".ToEntity()` | an `UnhandledParseException` | an `UnhandledParseException` |

### A numerical definite integral is Gauss–Legendre, with the digits it knows

**Silent: wrong answers, and values where there was NaN.** `Entity.DefiniteIntegral(x, a, b, steps)` was the mean of
Expand Down
4 changes: 3 additions & 1 deletion Sources/AngouriMath/Core/Antlr/AngouriMath.g
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,9 @@ BOOLEAN: ('true' | 'True' | 'false' | 'False') ;
// otherwise be an identifier. https://github.com/asc-community/AngouriMath/issues/906
NAN: 'NaN' ;

VARIABLE: ('a'..'z'|'A'..'Z'|'\u0370'..'\u03FF'|'\u1F00'..'\u1FFF'|'\u0400'..'\u04FF'|'\u212F')+ ('_' ('a'..'z'|'A'..'Z'|'0'..'9'|'\u0370'..'\u03FF'|'\u1F00'..'\u1FFF'|'\u0400'..'\u04FF')+)? ;
/* A subscript may have a subscript of its own: X_a_beta is X with the subscript a_beta, and prints as X_{a_{\beta}}.
https://github.com/asc-community/AngouriMath/issues/524 */
VARIABLE: ('a'..'z'|'A'..'Z'|'\u0370'..'\u03FF'|'\u1F00'..'\u1FFF'|'\u0400'..'\u04FF'|'\u212F')+ ('_' ('a'..'z'|'A'..'Z'|'0'..'9'|'\u0370'..'\u03FF'|'\u1F00'..'\u1FFF'|'\u0400'..'\u04FF')+)* ;

COMMENT: ('//' ~[\r\n]* ('\r'? '\n')? | '/*' .*? '*/') -> skip ;

Expand Down
889 changes: 445 additions & 444 deletions Sources/AngouriMath/Core/Antlr/AngouriMathLexer.cs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Sources/AngouriMath/Core/Antlr/AngouriMathLexer.interp

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions Sources/AngouriMath/Docs/Usage/Syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,14 @@ is `pi` and the script `ℯ` is `e`, so `sin(π)` is `0` and `ln(ℯ)` is `1`. T
they are variables that print like `e` and `x`, and `х + x` has two
([#1260](https://github.com/asc-community/AngouriMath/issues/1260)).

Four things that look like names and are not:
A subscript may have one of its own: `X_a_beta` is one name, `X` with the subscript `a_beta`, and in
LaTeX it is `X_{a_{\beta}}` ([#524](https://github.com/asc-community/AngouriMath/issues/524)).

Three things that look like names and are not:

| written | what it is |
|---|---|
| `_x`, `x_` | a lexer error. `_` may neither begin a name nor end one |
| `x_1_2` | a lexer error. At most one `_`, so subscripts do not nest ([#524](https://github.com/asc-community/AngouriMath/issues/524)) |
| `_x`, `x_`, `x__1` | a lexer error. `_` may neither begin a name nor end one, and two do not stand together |
| `x1` | `x ^ 1`. A digit is not a letter, and a name beside a number is a power — see below |
| `sinx` | one name, spelled `sinx`. Letters written together are a single name, never a product of one-letter ones |

Expand Down
10 changes: 8 additions & 2 deletions Sources/AngouriMath/Functions/Output/Latex/Latex.Classes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,15 @@ static string LatexizePart(string symbol, bool upright)
}
// For variables with subscripts (e.g., "pi_2", "x_e", "e_pi")
// Both the main part and subscript are processed through LatexizePart,
// which handles upright formatting for "pi" and "e" consistently
// which handles upright formatting for "pi" and "e" consistently.
// A subscript may have one of its own, so X_a_beta is X_{a_{\beta}}.
// https://github.com/asc-community/AngouriMath/issues/524
static string LatexizeIndex(string index)
=> index.IndexOf('_') is var underscore && underscore == -1
? LatexizePart(index, IsNameLatexUprightFormatted(index))
: $"{LatexizePart(index.Substring(0, underscore), IsNameLatexUprightFormatted(index.Substring(0, underscore)))}_{{{LatexizeIndex(index.Substring(underscore + 1))}}}";
return SplitIndex() is var (prefix, index)
? $"{LatexizePart(prefix, IsNameLatexUprightFormatted(prefix))}_{{{LatexizePart(index, IsNameLatexUprightFormatted(index))}}}"
? $"{LatexizePart(prefix, IsNameLatexUprightFormatted(prefix))}_{{{LatexizeIndex(index)}}}"
: LatexizePart(Name, IsOwnNameUpright);
}
}
Expand Down
4 changes: 4 additions & 0 deletions Sources/Tests/UnitTests/Convenience/LatexTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ void TestSimplify(string expected, Entity actual) =>
[Fact] public void SubscriptSingle() => Test(@"c_{\mathrm{e}}", MathS.Var("c_e"));
[Fact] public void SubscriptDuo() => Test(@"\Delta_{\mathrm{a2}}", MathS.Var("Delta_a2"));
[Fact] public void SubscriptDuoPlus() => TestSimplify(@"6+\Delta_{\mathrm{a2}}", "Delta_a2+6");
// A subscript of a subscript. https://github.com/asc-community/AngouriMath/issues/524
[Fact] public void SubscriptNested() => Test(@"X_{a_{\beta}}", MathS.Var("X_a_beta"));
[Fact] public void SubscriptNestedThrice() => Test(@"x_{1_{2_{\mathrm{e}}}}", MathS.Var("x_1_2_e"));
[Fact] public void SubscriptNestedParsed() => Test(@"X_{a_{\beta}}+10", MathS.FromString("X_a_beta + 10"));
[Fact] public void Square() => Test(@"{x}^{2}", MathS.Sqr(x));
[Fact] public void TwoXSquare() => Test(@"2 {x}^{2}", 2 * MathS.Sqr(x));
[Fact] public void MTwoXSquare() => Test(@"-2 \cdot {x}^{2}", -2 * MathS.Sqr(x));
Expand Down
5 changes: 4 additions & 1 deletion Sources/Tests/UnitTests/Convenience/SyntaxDocumentedTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ public void ALongerNameContainingNaNIsAVariable(string written) =>
[InlineData("альфа")]
[InlineData("x_ω")]
[InlineData("sinx")]
// A subscript may have a subscript. https://github.com/asc-community/AngouriMath/issues/524
[InlineData("x_1_2")]
[InlineData("X_a_beta")]
public void AName(string written)
{
var variable = Assert.IsType<Entity.Variable>(written.ToEntity());
Expand All @@ -154,7 +157,7 @@ public void AName(string written)
[Theory]
[InlineData("_x")]
[InlineData("x_")]
[InlineData("x_1_2")]
[InlineData("x__1")]
[InlineData("fi")]
public void WhatIsNotAName(string written) =>
Assert.Throws<UnhandledParseException>(() => written.ToEntity());
Expand Down
Loading