1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.themis.lethe.analysis;
18
19 import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20 import io.github.tonywasher.joceanus.themis.exc.ThemisDataException;
21 import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisDataMap.ThemisAnalysisDataType;
22 import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisEmbedded.ThemisAnalysisEmbedType;
23 import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisGeneric.ThemisAnalysisGenericBase;
24 import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisScanner.ThemisAnalysisSource;
25
26 import java.util.ArrayList;
27 import java.util.Deque;
28 import java.util.HashMap;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Map;
32
33
34
35
36 public class ThemisAnalysisParser
37 implements ThemisAnalysisSource {
38
39
40
41 static final Map<String, Object> KEYWORDS = createKeyWordMap();
42
43
44
45
46 private final ThemisAnalysisContainer theParent;
47
48
49
50
51 private final Deque<ThemisAnalysisElement> theLines;
52
53
54
55
56 private final Deque<ThemisAnalysisElement> theContents;
57
58
59
60
61 private final ThemisAnalysisDataMap theDataMap;
62
63
64
65
66 private final boolean isTemporary;
67
68
69
70
71
72
73
74
75 ThemisAnalysisParser(final Deque<ThemisAnalysisElement> pLines,
76 final Deque<ThemisAnalysisElement> pContents,
77 final ThemisAnalysisContainer pContainer) {
78
79 theLines = pLines;
80 theContents = pContents;
81 theParent = pContainer;
82
83
84 theDataMap = pContainer.getDataMap();
85 isTemporary = false;
86 }
87
88
89
90
91
92
93
94 ThemisAnalysisParser(final ThemisAnalysisParser pParser,
95 final Deque<ThemisAnalysisElement> pProcessed) {
96 this(pParser.theLines, pProcessed, pParser.getParent());
97 }
98
99
100
101
102
103
104 ThemisAnalysisParser(final ThemisAnalysisParser pParser) {
105
106 theLines = pParser.theLines;
107 theContents = pParser.theContents;
108 theParent = pParser.theParent;
109
110
111 theDataMap = new ThemisAnalysisDataMap(pParser.getDataMap());
112 isTemporary = true;
113 }
114
115 @Override
116 public boolean hasLines() {
117 return !theLines.isEmpty();
118 }
119
120
121
122
123
124
125 boolean isTemporary() {
126 return isTemporary;
127 }
128
129
130
131
132
133
134 ThemisAnalysisContainer getParent() {
135 return theParent;
136 }
137
138
139
140
141
142
143 ThemisAnalysisDataMap getDataMap() {
144 return theDataMap;
145 }
146
147 @Override
148 public ThemisAnalysisElement popNextLine() throws OceanusException {
149
150 if (theLines.isEmpty()) {
151 throw new ThemisDataException("No more lines");
152 }
153
154
155 return theLines.removeFirst();
156 }
157
158
159
160
161
162
163
164 ThemisAnalysisElement peekNextLine() throws OceanusException {
165
166 if (theLines.isEmpty()) {
167 throw new ThemisDataException("No more lines");
168 }
169
170
171 return theLines.getFirst();
172 }
173
174 @Override
175 public void pushLine(final ThemisAnalysisElement pLine) {
176
177 theLines.offerFirst(pLine);
178 }
179
180
181
182
183
184
185
186
187 boolean processCommentsAndBlanks(final ThemisAnalysisLine pLine) throws OceanusException {
188
189 if (ThemisAnalysisComment.isStartComment(pLine)) {
190
191 final ThemisAnalysisComment myComment = new ThemisAnalysisComment(this, pLine);
192 theContents.add(myComment);
193 return true;
194 }
195
196
197 pLine.stripTrailingComments();
198 pLine.stripModifiers();
199
200
201 if (ThemisAnalysisBlank.isBlank(pLine)) {
202
203 final ThemisAnalysisBlank myBlank = new ThemisAnalysisBlank(this, pLine);
204 theContents.add(myBlank);
205 return true;
206 }
207
208
209 if (ThemisAnalysisAnnotation.isAnnotation(pLine)) {
210
211 final ThemisAnalysisAnnotation myAnnotation = new ThemisAnalysisAnnotation(this, pLine);
212 theContents.add(myAnnotation);
213 return true;
214 }
215
216
217 return false;
218 }
219
220
221
222
223
224
225
226
227 boolean processImports(final ThemisAnalysisLine pLine) throws OceanusException {
228
229 if (ThemisAnalysisImports.isImport(pLine)) {
230
231 final ThemisAnalysisImports myImports = new ThemisAnalysisImports(this, pLine);
232 theContents.add(myImports);
233 return true;
234 }
235
236
237 return false;
238 }
239
240
241
242
243
244
245
246
247 boolean processClass(final ThemisAnalysisLine pLine) throws OceanusException {
248
249 final String myToken = pLine.peekNextToken();
250 final Object myType = KEYWORDS.get(myToken);
251
252
253 if (myType instanceof ThemisAnalysisKeyWord myKeyWord) {
254
255 switch (myKeyWord) {
256 case CLASS:
257
258 pLine.stripStartSequence(myToken);
259 theContents.add(new ThemisAnalysisClass(this, pLine));
260 return true;
261
262
263 case INTERFACE:
264 case ANNOTATION:
265
266 pLine.stripStartSequence(myToken);
267 theContents.add(new ThemisAnalysisInterface(this, myType.equals(ThemisAnalysisKeyWord.ANNOTATION), pLine));
268 return true;
269
270
271 case ENUM:
272
273 pLine.stripStartSequence(myToken);
274 theContents.add(new ThemisAnalysisEnum(this, pLine));
275 return true;
276
277 default:
278 break;
279 }
280 }
281
282
283 return false;
284 }
285
286
287
288
289
290
291
292
293 boolean processLanguage(final ThemisAnalysisLine pLine) throws OceanusException {
294
295 final String myToken = pLine.peekNextToken();
296 final Object myType = KEYWORDS.get(myToken);
297
298
299 if (myType instanceof ThemisAnalysisKeyWord myKeyWord) {
300
301 switch (myKeyWord) {
302
303 case WHILE:
304
305 pLine.stripStartSequence(myToken);
306 theContents.add(new ThemisAnalysisWhile(this, pLine));
307 return true;
308
309
310 case DO:
311
312 pLine.stripStartSequence(myToken);
313 theContents.add(new ThemisAnalysisDoWhile(this));
314 return true;
315
316
317 case SWITCH:
318
319 pLine.stripStartSequence(myToken);
320 theContents.add(new ThemisAnalysisSwitch(this, pLine));
321 return true;
322
323
324 case FOR:
325
326 pLine.stripStartSequence(myToken);
327 theContents.add(new ThemisAnalysisFor(this, pLine));
328 return true;
329
330
331 case IF:
332
333 pLine.stripStartSequence(myToken);
334 theContents.add(new ThemisAnalysisIf(this, pLine));
335 return true;
336
337
338 case TRY:
339
340 pLine.stripStartSequence(myToken);
341 theContents.add(new ThemisAnalysisTry(this, pLine));
342 return true;
343
344 default:
345 break;
346 }
347 }
348
349
350 return false;
351 }
352
353
354
355
356
357
358
359
360 boolean processBlocks(final ThemisAnalysisLine pLine) throws OceanusException {
361
362 if (ThemisAnalysisBlock.checkBlock(pLine)) {
363 theContents.add(new ThemisAnalysisBlock(this, pLine));
364 return true;
365 }
366
367
368 final ThemisAnalysisEmbedType myEmbed = ThemisAnalysisEmbedded.checkForEmbedded(pLine);
369 switch (myEmbed) {
370 case LAMBDA:
371 case ANON:
372 case ARRAY:
373
374 theContents.add(new ThemisAnalysisEmbedded(this, myEmbed, pLine));
375 return true;
376 case METHOD:
377
378 theContents.add(new ThemisAnalysisMethodBody(this, pLine));
379 return true;
380 default:
381 break;
382 }
383
384
385 return false;
386 }
387
388
389
390
391
392
393
394
395
396 boolean processCase(final ThemisAnalysisContainer pOwner,
397 final ThemisAnalysisLine pLine) throws OceanusException {
398
399 final Object myCase = parseCase(pLine);
400
401
402 if (myCase != null) {
403 theContents.add(new ThemisAnalysisCase(this, pOwner, myCase));
404 return true;
405 }
406
407
408 return false;
409 }
410
411
412
413
414
415
416
417 static Object parseCase(final ThemisAnalysisLine pLine) {
418
419 if (pLine.getProperties().hasModifier(ThemisAnalysisModifier.DEFAULT)) {
420 return ThemisAnalysisKeyWord.DEFAULT;
421 }
422
423
424 final String myToken = pLine.peekNextToken();
425 final Object myType = KEYWORDS.get(myToken);
426
427
428 if (myType instanceof ThemisAnalysisKeyWord myKeyWord) {
429
430 switch (myKeyWord) {
431 case CASE:
432 pLine.stripStartSequence(myToken);
433 return pLine.stripNextToken();
434
435 case DEFAULT:
436 pLine.stripStartSequence(myToken);
437 return myKeyWord;
438
439 default:
440 return null;
441
442 }
443 }
444
445
446 return null;
447 }
448
449
450
451
452
453
454
455
456
457 ThemisAnalysisElement processExtra(final ThemisAnalysisContainer pOwner,
458 final ThemisAnalysisKeyWord pKeyWord) throws OceanusException {
459
460 if (!hasLines()) {
461 return null;
462 }
463
464
465 final ThemisAnalysisLine myLine = (ThemisAnalysisLine) popNextLine();
466 final String myToken = myLine.peekNextToken();
467 final Object myType = KEYWORDS.get(myToken);
468
469
470 if (pKeyWord.equals(myType)) {
471
472 switch ((ThemisAnalysisKeyWord) myType) {
473
474 case ELSE:
475
476 myLine.stripStartSequence(myToken);
477 return new ThemisAnalysisElse(this, pOwner, myLine);
478
479
480 case CATCH:
481
482 myLine.stripStartSequence(myToken);
483 return new ThemisAnalysisCatch(this, pOwner, myLine);
484
485
486 case FINALLY:
487
488 myLine.stripStartSequence(myToken);
489 return new ThemisAnalysisFinally(this, pOwner, myLine);
490
491 default:
492 break;
493 }
494 }
495
496
497 pushLine(myLine);
498 return null;
499 }
500
501
502
503
504
505
506
507
508 ThemisAnalysisElement processEmbedded(final ThemisAnalysisEmbedded pEmbedded) throws OceanusException {
509
510 final ThemisAnalysisLine myLine = pEmbedded.getHeader();
511 final ThemisAnalysisReference myReference = parsePotentialDataType(myLine);
512
513
514 if (myReference != null) {
515
516 final String myName = myLine.stripNextToken();
517 return new ThemisAnalysisField(this, myName, myReference, pEmbedded);
518 }
519
520
521 final ThemisAnalysisKeyWord myKeyWord = determineStatementKeyWord(myLine);
522 return new ThemisAnalysisStatement(myKeyWord, pEmbedded);
523 }
524
525
526
527
528
529
530
531
532 ThemisAnalysisElement processMethodBody(final ThemisAnalysisMethodBody pMethod) throws OceanusException {
533
534 final ThemisAnalysisLine myLine = pMethod.getHeader();
535 final ThemisAnalysisReference myReference = parsePotentialDataType(myLine);
536
537
538 final String myName = myLine.stripNextToken();
539 final boolean isMethod = myLine.startsWithChar(ThemisAnalysisChar.PARENTHESIS_OPEN);
540
541
542 if (myReference == null || !isMethod) {
543 throw new ThemisDataException("Invalid Method Body");
544 }
545
546
547 return new ThemisAnalysisMethod(this, myName, myReference, pMethod);
548 }
549
550
551
552
553
554
555
556
557 ThemisAnalysisElement processFieldsAndMethods(final ThemisAnalysisLine pLine) throws OceanusException {
558
559 final ThemisAnalysisReference myReference = parsePotentialDataType(pLine);
560 if (myReference != null) {
561
562 final String myName = pLine.stripNextToken();
563 final boolean isMethod = pLine.startsWithChar(ThemisAnalysisChar.PARENTHESIS_OPEN);
564 if (!isMethod) {
565 myReference.resolveGeneric(this);
566 return new ThemisAnalysisField(this, myName, myReference, pLine);
567 } else {
568 return new ThemisAnalysisMethod(this, myName, myReference, pLine);
569 }
570 }
571
572
573 return null;
574 }
575
576
577
578
579
580
581
582
583 ThemisAnalysisElement processStatement(final ThemisAnalysisLine pLine) throws OceanusException {
584
585 final ThemisAnalysisKeyWord myKeyWord = determineStatementKeyWord(pLine);
586 return new ThemisAnalysisStatement(this, myKeyWord, pLine);
587 }
588
589
590
591
592
593
594
595 private static ThemisAnalysisKeyWord determineStatementKeyWord(final ThemisAnalysisLine pLine) {
596
597 final String myToken = pLine.peekNextToken();
598 final Object myType = KEYWORDS.get(myToken);
599
600
601 if (myType instanceof ThemisAnalysisKeyWord myKeyWord) {
602
603 switch (myKeyWord) {
604 case RETURN:
605 case THROW:
606 case BREAK:
607 case CONTINUE:
608 case YIELD:
609 pLine.stripNextToken();
610 return myKeyWord;
611 default:
612 break;
613 }
614 }
615
616
617 return null;
618 }
619
620
621
622
623
624
625
626
627 private ThemisAnalysisReference parsePotentialDataType(final ThemisAnalysisLine pLine) throws OceanusException {
628
629 final String myToken = pLine.peekNextToken();
630 if (KEYWORDS.get(myToken) != null) {
631 return null;
632 }
633
634
635 final boolean isMethod = pLine.startsWithSequence(myToken + ThemisAnalysisChar.PARENTHESIS_OPEN);
636
637
638 ThemisAnalysisDataType myType = theDataMap.lookUpDataType(myToken, isMethod);
639 if (myType == null) {
640
641 final ThemisAnalysisProperties myProps = pLine.getProperties();
642 if (myProps.hasGeneric()) {
643
644 final ThemisAnalysisParser myTempParser = new ThemisAnalysisParser(this);
645 myProps.resolveGeneric(myTempParser);
646 myType = myTempParser.getDataMap().lookUpTheDataType(myToken);
647 }
648
649
650 if (myType == null) {
651 return null;
652 }
653 }
654 pLine.stripStartSequence(myToken);
655
656
657 return buildReference(theDataMap, pLine, myType);
658 }
659
660
661
662
663
664
665
666
667
668 static ThemisAnalysisReference parseDataType(final ThemisAnalysisDataMap pDataMap,
669 final ThemisAnalysisLine pLine) throws OceanusException {
670
671 String myToken = pLine.peekNextToken();
672 if (KEYWORDS.get(myToken) != null) {
673 throw new ThemisDataException("DataType required but keyWord found");
674 }
675
676
677 if (myToken.endsWith(ThemisAnalysisArray.VARARGS)) {
678
679 myToken = myToken.substring(0, myToken.length() - ThemisAnalysisArray.VARARGS.length());
680 }
681
682
683 ThemisAnalysisDataType myType = pDataMap.lookUpDataType(myToken, false);
684 if (myType == null) {
685
686 myType = pDataMap.declareUnknown(myToken);
687 }
688 pLine.stripStartSequence(myToken);
689
690
691 return buildReference(pDataMap, pLine, myType);
692 }
693
694
695
696
697
698
699
700
701
702
703 private static ThemisAnalysisReference buildReference(final ThemisAnalysisDataMap pDataMap,
704 final ThemisAnalysisLine pLine,
705 final ThemisAnalysisDataType pType) throws OceanusException {
706
707 final ThemisAnalysisGeneric myGeneric = ThemisAnalysisGeneric.isGeneric(pLine)
708 ? new ThemisAnalysisGenericBase(pLine)
709 : null;
710 final ThemisAnalysisArray myArray = ThemisAnalysisArray.isArray(pLine)
711 ? new ThemisAnalysisArray(pLine)
712 : null;
713
714
715 final ThemisAnalysisReference myRef = new ThemisAnalysisReference(pType, myGeneric, myArray);
716 pDataMap.declareReference(myRef);
717 return myRef;
718 }
719
720
721
722
723
724
725 void processLines() throws OceanusException {
726
727 while (hasLines()) {
728
729 final ThemisAnalysisLine myLine = (ThemisAnalysisLine) popNextLine();
730
731
732 final boolean processed = processCommentsAndBlanks(myLine)
733 || processClass(myLine)
734 || processLanguage(myLine)
735 || processBlocks(myLine);
736
737
738 if (!processed) {
739
740 theContents.add(myLine);
741 }
742 }
743 }
744
745
746
747
748
749
750 private static Map<String, Object> createKeyWordMap() {
751
752 final Map<String, Object> myMap = new HashMap<>();
753
754
755 for (ThemisAnalysisModifier myModifier : ThemisAnalysisModifier.values()) {
756 myMap.put(myModifier.toString(), myModifier);
757 }
758
759
760 for (ThemisAnalysisKeyWord myKeyWord : ThemisAnalysisKeyWord.values()) {
761 myMap.put(myKeyWord.toString(), myKeyWord);
762 }
763
764
765 return myMap;
766 }
767
768
769
770
771
772
773
774
775 List<ThemisAnalysisReference> parseAncestors(final Deque<ThemisAnalysisElement> pHeaders) throws OceanusException {
776
777 final List<ThemisAnalysisReference> myAncestors = new ArrayList<>();
778 final ThemisAnalysisLine myHeader = new ThemisAnalysisLine(pHeaders);
779
780
781 while (true) {
782
783 if (myHeader.startsWithChar(ThemisAnalysisChar.COMMA)) {
784 myHeader.stripStartChar(ThemisAnalysisChar.COMMA);
785 }
786
787
788 final String myToken = myHeader.peekNextToken();
789 if (myToken.length() == 0) {
790 return myAncestors;
791 }
792
793
794 if (KEYWORDS.get(myToken) != null) {
795
796 myHeader.stripNextToken();
797 } else {
798
799 final ThemisAnalysisReference myReference = parseDataType(theDataMap, myHeader);
800 myReference.resolveGeneric(this);
801 myAncestors.add(myReference);
802 }
803 }
804 }
805
806
807
808
809
810
811
812
813 Map<String, ThemisAnalysisReference> parseParameters(final ThemisAnalysisLine pParams) throws OceanusException {
814
815 final Map<String, ThemisAnalysisReference> myParams = new LinkedHashMap<>();
816
817
818 while (true) {
819
820 if (pParams.startsWithChar(ThemisAnalysisChar.COMMA)) {
821 pParams.stripStartChar(ThemisAnalysisChar.COMMA);
822 }
823
824
825 final String myToken = pParams.peekNextToken();
826 if (myToken.length() == 0) {
827 return myParams;
828 }
829
830
831 if (KEYWORDS.get(myToken) != null) {
832
833 pParams.stripNextToken();
834 } else {
835
836 final ThemisAnalysisReference myReference = parseDataType(theDataMap, pParams);
837 myReference.resolveGeneric(this);
838 final String myVar = pParams.stripNextToken();
839 myParams.put(myVar, myReference);
840 }
841 }
842 }
843 }