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 @@
</properties>
<body>
<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">
TimeStampedFieldPVCoordinates now implements FieldTimeStamped.
</action>
......
......@@ -26,14 +26,13 @@ import org.hipparchus.fraction.Fraction;
* The special case "n/a" corresponds to {@link PredefinedUnit#NONE}.
* </p>
* <pre>
* unit → "n/a" | chain
* chain → operand operation
* operand → '√' simple | simple power
* operation → '*' chain | '/' chain | ε
* power → '^' exponent | ε
* exponent → integer | '(' integer denominator ')'
* denominator → '/' integer | ε
* simple → predefined | '(' chain ')'
* unit ::= "n/a" | chain
* chain ::= operand { ('*' | '/') operand }
* operand ::= '√' simple | simple power
* power ::= '^' exponent | ε
* exponent ::= integer | '(' integer denominator ')'
* denominator ::= '/' integer | ε
* simple ::= predefined | '(' chain ')'
* </pre>
* <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*Ω⁻⁷).
......@@ -73,7 +72,18 @@ class Parser {
* @return chain unit
*/
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.
......@@ -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.
* @param lexer lexer providing tokens
* @return exponent
......
......@@ -79,6 +79,19 @@ public class ParserTest {
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
public void testEmpty() {
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