1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.utils.units;
18  
19  import org.hipparchus.fraction.Fraction;
20  import org.junit.jupiter.api.Assertions;
21  import org.junit.jupiter.api.Test;
22  import org.orekit.errors.OrekitException;
23  import org.orekit.errors.OrekitMessages;
24  
25  /**
26   * Unit tests for {@link Lexer}.
27   *
28   * @author Luc Maisonobe
29   */
30  public class LexerTest {
31  
32      @Test
33      public void testAllTypes() {
34          final Lexer lexer = new Lexer("√kg*km**  (3/2) /(µs^2*Ω⁻⁷)");
35          expect(lexer, "√",    TokenType.SQUARE_ROOT, 0, 1);
36          expect(lexer, "kg",   TokenType.IDENTIFIER, 0, 1);
37          expect(lexer, "*",    TokenType.MULTIPLICATION, 0, 1);
38          expect(lexer, "km",   TokenType.IDENTIFIER, 0, 1);
39          expect(lexer, "**",   TokenType.POWER, 0, 1);
40          expect(lexer, "(",    TokenType.OPEN, 0, 1);
41          expect(lexer, "3",    TokenType.INTEGER, 3, 1);
42          expect(lexer, "/",    TokenType.DIVISION, 0, 1);
43          expect(lexer, "2",    TokenType.INTEGER, 2, 1);
44          expect(lexer, ")",    TokenType.CLOSE, 0, 1);
45          expect(lexer, "/",    TokenType.DIVISION, 0, 1);
46          expect(lexer, "(",    TokenType.OPEN, 0, 1);
47          expect(lexer, "µs",   TokenType.IDENTIFIER, 0, 1);
48          expect(lexer, "^",    TokenType.POWER, 0, 1);
49          expect(lexer, "2",    TokenType.INTEGER, 2, 1);
50          expect(lexer, "*",    TokenType.MULTIPLICATION, 0, 1);
51          expect(lexer, "Ω",    TokenType.IDENTIFIER, 0, 1);
52          expect(lexer, "",     TokenType.POWER, 0, 1);
53          expect(lexer, "⁻⁷",   TokenType.INTEGER, -7, 1);
54          expect(lexer, ")",    TokenType.CLOSE, 0, 1);
55          Assertions.assertNull(lexer.next());
56      }
57  
58      @Test
59      public void testRegularExponent() {
60          final Lexer lexer = new Lexer("N^123450 × MHz^-98765");
61          expect(lexer, "N",      TokenType.IDENTIFIER, 0, 1);
62          expect(lexer, "^",      TokenType.POWER, 0, 1);
63          expect(lexer, "123450", TokenType.INTEGER, 123450, 1);
64          expect(lexer, "×",      TokenType.MULTIPLICATION, 0, 1);
65          expect(lexer, "MHz",    TokenType.IDENTIFIER, 0, 1);
66          expect(lexer, "^",      TokenType.POWER, 0, 1);
67          expect(lexer, "-98765", TokenType.INTEGER, -98765, 1);
68          Assertions.assertNull(lexer.next());
69      }
70  
71      @Test
72      public void testSuperscriptExponent() {
73          final Lexer lexer = new Lexer("SFU⁺¹²³⁴⁵⁰ ⁄ mas⁻⁹⁸⁷⁶⁵");
74          expect(lexer, "SFU",     TokenType.IDENTIFIER, 0, 1);
75          expect(lexer, "",        TokenType.POWER, 0, 1);
76          expect(lexer, "⁺¹²³⁴⁵⁰", TokenType.INTEGER, 123450, 1);
77          expect(lexer, "⁄",       TokenType.DIVISION, 0, 1);
78          expect(lexer, "mas",     TokenType.IDENTIFIER, 0, 1);
79          expect(lexer, "",        TokenType.POWER, 0, 1);
80          expect(lexer, "⁻⁹⁸⁷⁶⁵",  TokenType.INTEGER, -98765, 1);
81          Assertions.assertNull(lexer.next());
82      }
83  
84      @Test
85      public void testSignWithoutDigits() {
86          final Lexer lexer = new Lexer("Pa⁻");
87          expect(lexer, "Pa", TokenType.IDENTIFIER, 0, 1);
88          expect(lexer, "",  TokenType.POWER, 0, 1);
89          expectFailure(lexer);
90      }
91  
92      @Test
93      public void testMultipleSigns() {
94          final Lexer lexer = new Lexer("MJ⁻⁺²");
95          expect(lexer, "MJ", TokenType.IDENTIFIER, 0, 1);
96          expect(lexer, "",  TokenType.POWER, 0, 1);
97          expectFailure(lexer);
98      }
99  
100     @Test
101     public void testUnknownCharacter() {
102         final Lexer lexer = new Lexer("pW^2∇");
103         expect(lexer, "pW", TokenType.IDENTIFIER, 0, 1);
104         expect(lexer, "^",  TokenType.POWER, 0, 1);
105         expect(lexer, "2",  TokenType.INTEGER, 2, 1);
106         expectFailure(lexer);
107     }
108 
109     @Test
110     public void testPercentageCharacter() {
111         final Lexer lexer = new Lexer("%");
112         expect(lexer, "%", TokenType.IDENTIFIER, 0, 1);
113         Assertions.assertNull(lexer.next());
114     }
115 
116     @Test
117     public void testStartWithSuperscript() {
118         final Lexer lexer = new Lexer("³");
119         expect(lexer, "³", TokenType.INTEGER, 3, 1);
120         Assertions.assertNull(lexer.next());
121     }
122 
123     @Test
124     public void testCharacters() {
125         final Lexer lexer = new Lexer("d*°*min*◦*deg*′*hh*'*ad*″*a*\"*µ''");
126         expect(lexer, "d",   TokenType.IDENTIFIER, 0, 1);
127         expect(lexer, "*",   TokenType.MULTIPLICATION, 0, 1);
128         expect(lexer, "°",   TokenType.IDENTIFIER, 0, 1);
129         expect(lexer, "*",   TokenType.MULTIPLICATION, 0, 1);
130         expect(lexer, "min", TokenType.IDENTIFIER, 0, 1);
131         expect(lexer, "*",   TokenType.MULTIPLICATION, 0, 1);
132         expect(lexer, "◦",   TokenType.IDENTIFIER, 0, 1);
133         expect(lexer, "*",   TokenType.MULTIPLICATION, 0, 1);
134         expect(lexer, "deg", TokenType.IDENTIFIER, 0, 1);
135         expect(lexer, "*",   TokenType.MULTIPLICATION, 0, 1);
136         expect(lexer, "′",   TokenType.IDENTIFIER, 0, 1);
137         expect(lexer, "*",   TokenType.MULTIPLICATION, 0, 1);
138         expect(lexer, "hh",  TokenType.IDENTIFIER, 0, 1);
139         expect(lexer, "*",   TokenType.MULTIPLICATION, 0, 1);
140         expect(lexer, "'",   TokenType.IDENTIFIER, 0, 1);
141         expect(lexer, "*",   TokenType.MULTIPLICATION, 0, 1);
142         expect(lexer, "ad",  TokenType.IDENTIFIER, 0, 1);
143         expect(lexer, "*",   TokenType.MULTIPLICATION, 0, 1);
144         expect(lexer, "″",   TokenType.IDENTIFIER, 0, 1);
145         expect(lexer, "*",   TokenType.MULTIPLICATION, 0, 1);
146         expect(lexer, "a",   TokenType.IDENTIFIER, 0, 1);
147         expect(lexer, "*",   TokenType.MULTIPLICATION, 0, 1);
148         expect(lexer, "\"",  TokenType.IDENTIFIER, 0, 1);
149         expect(lexer, "*",   TokenType.MULTIPLICATION, 0, 1);
150         expect(lexer, "µ''", TokenType.IDENTIFIER, 0, 1);
151         Assertions.assertNull(lexer.next());
152     }
153 
154     @Test
155     public void testIdentifierUnicodeLetter() {
156         final String s = "αβγDEFghi";
157         final Lexer lexer = new Lexer(s);
158         expect(lexer, s, TokenType.IDENTIFIER, 0, 1);
159         Assertions.assertNull(lexer.next());
160     }
161 
162     @Test
163     public void testOneHalfAsDecimal() {
164         final Lexer lexer = new Lexer("0.5");
165         expect(lexer, "0.5", TokenType.FRACTION, 1, 2);
166         Assertions.assertNull(lexer.next());
167     }
168 
169     @Test
170     public void testThreeHalfAsDecimal() {
171         final Lexer lexer = new Lexer("1.5");
172         expect(lexer, "1.5", TokenType.FRACTION, 3, 2);
173         Assertions.assertNull(lexer.next());
174     }
175 
176     @Test
177     public void testUnicodeFractions() {
178         final Lexer lexer = new Lexer("¼½¾⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞");
179         expect(lexer, "¼", TokenType.FRACTION, 1, 4);
180         expect(lexer, "½", TokenType.FRACTION, 1, 2);
181         expect(lexer, "¾", TokenType.FRACTION, 3, 4);
182         expect(lexer, "⅐", TokenType.FRACTION, 1, 7);
183         expect(lexer, "⅑", TokenType.FRACTION, 1, 9);
184         expect(lexer, "⅒", TokenType.FRACTION, 1, 10);
185         expect(lexer, "⅓", TokenType.FRACTION, 1, 3);
186         expect(lexer, "⅔", TokenType.FRACTION, 2, 3);
187         expect(lexer, "⅕", TokenType.FRACTION, 1, 5);
188         expect(lexer, "⅖", TokenType.FRACTION, 2, 5);
189         expect(lexer, "⅗", TokenType.FRACTION, 3, 5);
190         expect(lexer, "⅘", TokenType.FRACTION, 4, 5);
191         expect(lexer, "⅙", TokenType.FRACTION, 1, 6);
192         expect(lexer, "⅚", TokenType.FRACTION, 5, 6);
193         expect(lexer, "⅛", TokenType.FRACTION, 1, 8);
194         expect(lexer, "⅜", TokenType.FRACTION, 3, 8);
195         expect(lexer, "⅝", TokenType.FRACTION, 5, 8);
196         expect(lexer, "⅞", TokenType.FRACTION, 7, 8);
197         Assertions.assertNull(lexer.next());
198     }
199 
200     @Test
201     public void testPushBack() {
202         final Lexer lexer = new Lexer("m/s");
203         expect(lexer, "m",   TokenType.IDENTIFIER, 0, 1);
204         expect(lexer, "/",   TokenType.DIVISION, 0, 1);
205         lexer.pushBack();
206         expect(lexer, "/",   TokenType.DIVISION, 0, 1);
207         expect(lexer, "s",   TokenType.IDENTIFIER, 0, 1);
208     }
209 
210     @Test
211     public void testPushBackBeforeVirtualExponent() {
212         final Lexer lexer = new Lexer("m²/s");
213         expect(lexer, "m",   TokenType.IDENTIFIER, 0, 1);
214         lexer.pushBack();
215         expect(lexer, "m",   TokenType.IDENTIFIER, 0, 1);
216         expect(lexer, "",    TokenType.POWER, 0, 1);
217         expect(lexer, "²",   TokenType.INTEGER, 2, 1);
218         expect(lexer, "/",   TokenType.DIVISION, 0, 1);
219         expect(lexer, "s",   TokenType.IDENTIFIER, 0, 1);
220     }
221 
222     @Test
223     public void testPushBackAtVirtualExponent() {
224         final Lexer lexer = new Lexer("m²/s");
225        expect(lexer, "m",   TokenType.IDENTIFIER, 0, 1);
226         expect(lexer, "",    TokenType.POWER, 0, 1);
227         lexer.pushBack();
228         expect(lexer, "",    TokenType.POWER, 0, 1);
229         expect(lexer, "²",   TokenType.INTEGER, 2, 1);
230         expect(lexer, "/",   TokenType.DIVISION, 0, 1);
231         expect(lexer, "s",   TokenType.IDENTIFIER, 0, 1);
232     }
233 
234     @Test
235     public void testPushBackAfterVirtualExponent() {
236         final Lexer lexer = new Lexer("m²/s");
237         expect(lexer, "m",   TokenType.IDENTIFIER, 0, 1);
238         expect(lexer, "",    TokenType.POWER, 0, 1);
239         expect(lexer, "²",   TokenType.INTEGER, 2, 1);
240         lexer.pushBack();
241         expect(lexer, "²",   TokenType.INTEGER, 2, 1);
242         expect(lexer, "/",   TokenType.DIVISION, 0, 1);
243         expect(lexer, "s",   TokenType.IDENTIFIER, 0, 1);
244     }
245 
246     @Test
247     public void testPushBackAtEnd() {
248         final Lexer lexer = new Lexer("m²/s");
249         expect(lexer, "m",   TokenType.IDENTIFIER, 0, 1);
250         expect(lexer, "",    TokenType.POWER, 0, 1);
251         expect(lexer, "²",   TokenType.INTEGER, 2, 1);
252         expect(lexer, "/",   TokenType.DIVISION, 0, 1);
253         expect(lexer, "s",   TokenType.IDENTIFIER, 0, 1);
254         Assertions.assertNull(lexer.next());
255         lexer.pushBack();
256         Assertions.assertNull(lexer.next());
257     }
258 
259     private void expect(Lexer lexer, String subString, TokenType type,
260                         int numerator, int denominator) {
261         Token t = lexer.next();
262         Assertions.assertEquals(subString, t.getSubString());
263         Assertions.assertEquals(type,      t.getType());
264         Assertions.assertEquals(numerator, t.getInt());
265         Assertions.assertEquals(type == TokenType.FRACTION ? new Fraction(numerator, denominator) : null,
266                             t.getFraction());
267     }
268 
269     private void expectFailure(Lexer lexer) {
270         try {
271             lexer.next();
272             Assertions.fail("an exception should have been thrown");
273         } catch (OrekitException oe) {
274             Assertions.assertEquals(OrekitMessages.UNKNOWN_UNIT, oe.getSpecifier());
275             Assertions.assertEquals(lexer.getUnitSpecification(), oe.getParts()[0]);
276         }
277     }
278 
279 }