11package net .marcellperger .first .parser ;
22
33import net .marcellperger .first .*;
4+ import net .marcellperger .first .util .Util ;
5+ import net .marcellperger .first .util .UtilCollectors ;
6+ import org .jetbrains .annotations .NotNull ;
47import org .junit .jupiter .api .Test ;
58
9+ import java .lang .reflect .Field ;
10+ import java .util .Arrays ;
11+ import java .util .HashMap ;
12+ import java .util .Map ;
13+ import java .util .Map .Entry ;
14+
615import static org .junit .jupiter .api .Assertions .*;
716
817class ParserTest {
18+ boolean nocache = false ;
19+
920 void assertInfixParsesTo (String src , int level , MathSymbol expected ) {
21+ // Expects it to be full parse
22+ if (!nocache ) assertInfixParsesToInner (src , level , expected );
23+ else try (WithSuppressingCache ignored = WithSuppressingCache .start ()) {
24+ assertInfixParsesToInner (src , level , expected );
25+ }
26+ }
27+
28+ void assertInfixParsesTo (Pair <MathSymbol , String > exprPair , int level ) {
29+ assertInfixParsesTo (exprPair .right (), level , exprPair .left ());
30+ }
31+
32+ void assertParsesToInner (String src , MathSymbol expected ) {
33+ // Expects it to be full parse
34+ Parser p = new Parser (src );
35+ MathSymbol actual = assertDoesNotThrow (p ::parse );
36+ // No need to check EOF as parse() checks that it consumed the whole string
37+ assertEquals (expected , actual );
38+ }
39+
40+ void assertParsesTo (String src , MathSymbol expected ) {
41+ // Expects it to be full parse
42+ if (!nocache ) assertParsesToInner (src , expected );
43+ else try (WithSuppressingCache ignored = WithSuppressingCache .start ()) {
44+ assertParsesToInner (src , expected );
45+ }
46+ }
47+
48+ void assertParsesTo (Pair <MathSymbol , String > exprPair ) {
49+ assertParsesTo (exprPair .right (), exprPair .left ());
50+ }
51+
52+ void assertInfixParsesToInner (String src , int level , MathSymbol expected ) {
1053 // Expects it to be full parse
1154 Parser p = new Parser (src );
1255 MathSymbol actual = assertDoesNotThrow (() -> p .parseInfixPrecedenceLevel (level ));
@@ -15,10 +58,6 @@ void assertInfixParsesTo(String src, int level, MathSymbol expected) {
1558 assertEquals (expected , actual );
1659 }
1760
18- void assertInfixParsesTo (Pair <MathSymbol , String > exprPair , int level ) {
19- assertInfixParsesTo (exprPair .right (), level , exprPair .left ());
20- }
21-
2261 @ Test
2362 void parseInfixPrecedenceLevel () {
2463 assertInfixParsesTo ("1.0/2.0" , 1 ,
@@ -44,4 +83,116 @@ void parseInfixPrecedenceLevel() {
4483 assertInfixParsesTo (".9/2./3.3" , 2 ,
4584 new DivOperation (new DivOperation (new BasicDoubleSymbol (.9 ), new BasicDoubleSymbol (2. )), new BasicDoubleSymbol (3.3 )));
4685 }
86+
87+ @ Test
88+ void parseInfixPrecedenceLevel_nocache () {
89+ boolean origNocache = nocache ;
90+ nocache = true ;
91+ try {
92+ parseInfixPrecedenceLevel ();
93+ } finally {
94+ nocache = origNocache ;
95+ }
96+ }
97+
98+ @ Test
99+ void parsePrecedenceLevel () {
100+ assertParsesTo ("1.0/2.0" ,
101+ new DivOperation (new BasicDoubleSymbol (1.0 ), new BasicDoubleSymbol (2.0 )));
102+ assertParsesTo (".3*6." ,
103+ new MulOperation (new BasicDoubleSymbol (.3 ), new BasicDoubleSymbol (6. )));
104+ assertParsesTo ("2.1*5.3+1.1" ,
105+ new AddOperation (new MulOperation (new BasicDoubleSymbol (2.1 ), new BasicDoubleSymbol (5.3 )), new BasicDoubleSymbol (1.1 )));
106+ assertParsesTo ("0.9-2.1/.3" ,
107+ new SubOperation (new BasicDoubleSymbol (0.9 ), new DivOperation (new BasicDoubleSymbol (2.1 ), new BasicDoubleSymbol (.3 ))));
108+ assertParsesTo ("(2.2+1.1)+3.7" ,
109+ new AddOperation (new AddOperation (new BasicDoubleSymbol (2.2 ), new BasicDoubleSymbol (1.1 )), new BasicDoubleSymbol (3.7 )));
110+ assertParsesTo (CommonData .getBigData1_groupingParens ());
111+ assertParsesTo (CommonData .getBigData2_groupingParens ());
112+ assertParsesTo ("2.2+1.1+3.7" ,
113+ new AddOperation (new AddOperation (new BasicDoubleSymbol (2.2 ), new BasicDoubleSymbol (1.1 )), new BasicDoubleSymbol (3.7 )));
114+ assertParsesTo ("2.2+1.1+3.7+0.2" ,
115+ new AddOperation (new AddOperation (new AddOperation (new BasicDoubleSymbol (2.2 ), new BasicDoubleSymbol (1.1 )), new BasicDoubleSymbol (3.7 )), new BasicDoubleSymbol (0.2 )));
116+ assertParsesTo (CommonData .getBigData1_minimumParens ());
117+ assertParsesTo (CommonData .getBigData2_minimumParens ());
118+ assertParsesTo (".9/2./3.3" ,
119+ new DivOperation (new DivOperation (new BasicDoubleSymbol (.9 ), new BasicDoubleSymbol (2. )), new BasicDoubleSymbol (3.3 )));
120+ assertParsesTo (".9/2./3.3" ,
121+ new DivOperation (new DivOperation (new BasicDoubleSymbol (.9 ), new BasicDoubleSymbol (2. )), new BasicDoubleSymbol (3.3 )));
122+ }
123+
124+ @ Test
125+ void parsePrecedenceLevel_nocache () {
126+ boolean origNocache = nocache ;
127+ nocache = true ;
128+ try {
129+ parsePrecedenceLevel ();
130+ } finally {
131+ nocache = origNocache ;
132+ }
133+ }
134+
135+ static class WithSuppressingCache implements AutoCloseable {
136+ public static WithSuppressingCache getInstance () {
137+ return INSTANCE ;
138+ }
139+ protected static WithSuppressingCache INSTANCE = new WithSuppressingCache ();
140+ protected WithSuppressingCache () {}
141+
142+ WithSuppressingCache startInstance () {
143+ clearCache ();
144+ return this ;
145+ }
146+ static WithSuppressingCache start () {
147+ return getInstance ().startInstance ();
148+ }
149+ WithSuppressingCache stop () {
150+ restoreCache ();
151+ return this ;
152+ }
153+
154+ @ Override
155+ public void close () {
156+ restoreCache ();
157+ }
158+
159+
160+ Map <SymbolInfo , BinOpBiConstructor <?>> origCache = new HashMap <>();
161+ void clearCache () {
162+ Field cacheField = getBiConstructorCache ();
163+
164+ origCache = Arrays .stream (SymbolInfo .values ()).<Entry <SymbolInfo , BinOpBiConstructor <?>>>map (sym -> {
165+ try {
166+ BinOpBiConstructor <?> cachedValue = (BinOpBiConstructor <?>)cacheField .get (sym );
167+ cacheField .set (sym , null );
168+ return Util .makeEntry (sym , cachedValue );
169+ } catch (IllegalAccessException e ) {
170+ throw Util .excToError (e );
171+ }
172+ }).collect (UtilCollectors .entriesToUnmodifiableMap ());
173+ }
174+
175+ void restoreCache () {
176+ Field cacheField = getBiConstructorCache ();
177+ origCache .forEach ((key , value ) -> {
178+ try {
179+ cacheField .set (key , value );
180+ } catch (IllegalAccessException ex ) {
181+ throw Util .excToError (ex );
182+ }
183+ });
184+ }
185+
186+ @ NotNull
187+ private static Field getBiConstructorCache () {
188+ Field cacheField ;
189+ try {
190+ cacheField = SymbolInfo .class .getDeclaredField ("biConstructorCache" );
191+ } catch (NoSuchFieldException e ) {
192+ throw Util .excToError (e );
193+ }
194+ cacheField .setAccessible (true );
195+ return cacheField ;
196+ }
197+ }
47198}
0 commit comments