Commit f6f16d28 authored by Luc Maisonobe's avatar Luc Maisonobe
Browse files

Fixed associativity in units parsing.

Fixes #776
parent cc4ef9ea
...@@ -21,6 +21,9 @@ ...@@ -21,6 +21,9 @@
</properties> </properties>
<body> <body>
<release version="11.0" date="TBD" description="TBD"> <release version="11.0" date="TBD" description="TBD">
<action dev="luc" type="fix" issue="776">
Fixed associativity in units parsing.
</action>
<action dev="bryan" type="update" issue="774"> <action dev="bryan" type="update" issue="774">
TimeStampedFieldPVCoordinates now implements FieldTimeStamped. TimeStampedFieldPVCoordinates now implements FieldTimeStamped.
</action> </action>
......
...@@ -26,14 +26,13 @@ import org.hipparchus.fraction.Fraction; ...@@ -26,14 +26,13 @@ import org.hipparchus.fraction.Fraction;
* The special case "n/a" corresponds to {@link PredefinedUnit#NONE}. * The special case "n/a" corresponds to {@link PredefinedUnit#NONE}.
* </p> * </p>
* <pre> * <pre>
* unit → "n/a" | chain * unit ::= "n/a" | chain
* chain → operand operation * chain ::= operand { ('*' | '/') operand }
* operand → '√' simple | simple power * operand ::= '√' simple | simple power
* operation → '*' chain | '/' chain | ε * power ::= '^' exponent | ε
* power → '^' exponent | ε * exponent ::= integer | '(' integer denominator ')'
* exponent → integer | '(' integer denominator ')' * denominator ::= '/' integer | ε
* denominator → '/' integer | ε * simple ::= predefined | '(' chain ')'
* simple → predefined | '(' chain ')'
* </pre> * </pre>
* <p> * <p>
* This parses correctly units like MHz, km/√d, kg.m.s⁻¹, µas^(2/5)/(h**(2)×m)³, km/√(kg.s), √kg*km** (3/2) /(µs^2*Ω⁻⁷). * This parses correctly units like MHz, km/√d, kg.m.s⁻¹, µas^(2/5)/(h**(2)×m)³, km/√(kg.s), √kg*km** (3/2) /(µs^2*Ω⁻⁷).
...@@ -73,7 +72,18 @@ class Parser { ...@@ -73,7 +72,18 @@ class Parser {
* @return chain unit * @return chain unit
*/ */
private static Unit chain(final Lexer lexer) { private static Unit chain(final Lexer lexer) {
return operation(operand(lexer), lexer); Unit chain = operand(lexer);
for (Token token = lexer.next(); token != null; token = lexer.next()) {
if (checkType(token, TokenType.MULTIPLICATION)) {
chain = chain.multiply(null, operand(lexer));
} else if (checkType(token, TokenType.DIVISION)) {
chain = chain.divide(null, operand(lexer));
} else {
lexer.pushBack();
break;
}
}
return chain;
} }
/** Parse an operand. /** Parse an operand.
...@@ -93,23 +103,6 @@ class Parser { ...@@ -93,23 +103,6 @@ class Parser {
} }
} }
/** Parse an operation.
* @param lhs left hand side unit
* @param lexer lexer providing tokens
* @return simple unit
*/
private static Unit operation(final Unit lhs, final Lexer lexer) {
final Token token = lexer.next();
if (checkType(token, TokenType.MULTIPLICATION)) {
return lhs.multiply(null, chain(lexer));
} else if (checkType(token, TokenType.DIVISION)) {
return lhs.divide(null, chain(lexer));
} else {
lexer.pushBack();
return lhs;
}
}
/** Parse a power operation. /** Parse a power operation.
* @param lexer lexer providing tokens * @param lexer lexer providing tokens
* @return exponent * @return exponent
......
...@@ -79,6 +79,19 @@ public class ParserTest { ...@@ -79,6 +79,19 @@ public class ParserTest {
new Fraction(-1, 2), Fraction.ONE, new Fraction(-1, 2), Fraction.ZERO); new Fraction(-1, 2), Fraction.ONE, new Fraction(-1, 2), Fraction.ZERO);
} }
@Test
public void testLeftAssociativity() {
checkReference("(kg/m)/s²",
1.0,
Fraction.ONE, Fraction.MINUS_ONE, new Fraction(-2), Fraction.ZERO);
checkReference("kg/(m/s²)",
1.0,
Fraction.ONE, Fraction.MINUS_ONE, Fraction.TWO, Fraction.ZERO);
checkReference("kg/m/s²",
1.0,
Fraction.ONE, Fraction.MINUS_ONE, new Fraction(-2), Fraction.ZERO);
}
@Test @Test
public void testEmpty() { public void testEmpty() {
expectFailure(""); expectFailure("");
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment