|
2 | 2 |
|
3 | 3 | import org.prop4j.Node; |
4 | 4 | import org.variantsync.diffdetective.error.UnparseableFormulaException; |
5 | | -import org.variantsync.diffdetective.feature.cpp.CPPDiffLineFormulaExtractor; |
6 | | -import org.variantsync.diffdetective.feature.jpp.JPPDiffLineFormulaExtractor; |
7 | | - |
| 5 | +import java.util.regex.Matcher; |
8 | 6 | import java.util.regex.Pattern; |
9 | 7 |
|
10 | 8 | /** |
11 | 9 | * A parser of preprocessor-like annotations. |
12 | 10 | * |
13 | 11 | * @author Paul Bittner, Alexander Schultheiß |
| 12 | + * @see org.variantsync.diffdetective.feature.cpp.CPPAnnotationParser |
| 13 | + * @see org.variantsync.diffdetective.feature.jpp.JPPAnnotationParser |
14 | 14 | */ |
15 | | -public class PreprocessorAnnotationParser implements AnnotationParser { |
16 | | - /** |
17 | | - * Matches the beginning or end of CPP conditional macros. |
18 | | - * It doesn't match the whole macro name, for example for {@code #ifdef} only {@code "#if"} is |
19 | | - * matched and only {@code "if"} is captured. |
20 | | - * <p> |
21 | | - * Note that this pattern doesn't handle comments between {@code #} and the macro name. |
22 | | - */ |
23 | | - protected final static Pattern CPP_PATTERN = |
24 | | - Pattern.compile("^[+-]?\\s*#\\s*(if|elif|else|endif)"); |
25 | | - |
| 15 | +public abstract class PreprocessorAnnotationParser implements AnnotationParser { |
26 | 16 | /** |
27 | | - * Matches the beginning or end of JPP conditional macros. |
28 | | - * It doesn't match the whole macro name, for example for {@code //#if defined(x)} only {@code "//#if"} is |
29 | | - * matched and only {@code "if"} is captured. |
| 17 | + * Pattern that is used to extract the {@link AnnotationType} and the associated {@link org.prop4j.Node formula}. |
30 | 18 | * <p> |
| 19 | + * The pattern needs to contain at least the two named capture groups {@code directive} and {@code formula}. |
| 20 | + * The {@code directive} group must match a string that can be processed by {@link #parseAnnotationType} |
| 21 | + * and whenever the resulting annotation type {@link AnnotationType#requiresFormula requires a formula}, |
| 22 | + * the capture group {@code formula} needs to match the formula that should be processed by {@link parseFormula}. |
31 | 23 | */ |
32 | | - protected final static Pattern JPP_PATTERN = |
33 | | - Pattern.compile("^[+-]?\\s*//\\s*#\\s*(if|elif|else|endif)"); |
34 | | - |
35 | | - /** |
36 | | - * Default parser for C preprocessor annotations. |
37 | | - * Created by invoking {@link #PreprocessorAnnotationParser(Pattern, DiffLineFormulaExtractor)}. |
38 | | - */ |
39 | | - public static final PreprocessorAnnotationParser CPPAnnotationParser = |
40 | | - new PreprocessorAnnotationParser(CPP_PATTERN, new CPPDiffLineFormulaExtractor()); |
41 | | - |
42 | | - /** |
43 | | - * Default parser for <a href="https://www.slashdev.ca/javapp/">JavaPP (Java PreProcessor)</a> annotations. |
44 | | - * Created by invoking {@link #PreprocessorAnnotationParser(Pattern, DiffLineFormulaExtractor)}. |
45 | | - */ |
46 | | - public static final PreprocessorAnnotationParser JPPAnnotationParser = |
47 | | - new PreprocessorAnnotationParser(JPP_PATTERN, new JPPDiffLineFormulaExtractor()); |
48 | | - |
49 | | - // Pattern that is used to identify the AnnotationType of a given annotation. |
50 | | - private final Pattern annotationPattern; |
51 | | - private final DiffLineFormulaExtractor extractor; |
| 24 | + protected final Pattern annotationPattern; |
52 | 25 |
|
53 | 26 | /** |
54 | 27 | * Creates a new preprocessor annotation parser. |
55 | 28 | * |
56 | | - * @param annotationPattern Pattern that is used to identify the AnnotationType of a given annotation; {@link #CPP_PATTERN} provides an example |
57 | | - * @param formulaExtractor An extractor that extracts the formula part of a preprocessor annotation |
| 29 | + * @param annotationPattern pattern that identifies the {@link AnnotationType} and the associated {@link org.prop4j.Node formula} of an annotation |
| 30 | + * @see #annotationPattern |
58 | 31 | */ |
59 | | - public PreprocessorAnnotationParser(final Pattern annotationPattern, DiffLineFormulaExtractor formulaExtractor) { |
| 32 | + public PreprocessorAnnotationParser(Pattern annotationPattern) { |
60 | 33 | this.annotationPattern = annotationPattern; |
61 | | - this.extractor = formulaExtractor; |
62 | 34 | } |
63 | 35 |
|
64 | | - /** |
65 | | - * Creates a new preprocessor annotation parser for C preprocessor annotations. |
66 | | - * |
67 | | - * @param formulaExtractor An extractor that extracts the formula part of a preprocessor annotation |
68 | | - */ |
69 | | - public static PreprocessorAnnotationParser CreateCppAnnotationParser(DiffLineFormulaExtractor formulaExtractor) { |
70 | | - return new PreprocessorAnnotationParser(CPP_PATTERN, formulaExtractor); |
| 36 | + @Override |
| 37 | + public Annotation parseAnnotation(final String line) throws UnparseableFormulaException { |
| 38 | + // Match the formula from the macro line |
| 39 | + final Matcher matcher = annotationPattern.matcher(line); |
| 40 | + if (!matcher.find()) { |
| 41 | + return new Annotation(AnnotationType.None); |
| 42 | + } |
| 43 | + String directive = matcher.group("directive"); |
| 44 | + String formula = matcher.group("formula"); |
| 45 | + AnnotationType annotationType = parseAnnotationType(directive); |
| 46 | + |
| 47 | + if (!annotationType.requiresFormula) { |
| 48 | + return new Annotation(annotationType); |
| 49 | + } |
| 50 | + |
| 51 | + if (annotationType.requiresFormula && formula == null) { |
| 52 | + throw new UnparseableFormulaException("Annotations of type " + annotationType.name + " require a formula but none was given"); |
| 53 | + } |
| 54 | + |
| 55 | + return new Annotation(annotationType, parseFormula(directive, formula)); |
71 | 56 | } |
72 | 57 |
|
73 | 58 | /** |
74 | | - * Creates a new preprocessor annotation parser for <a href="https://www.slashdev.ca/javapp/">JavaPP (Java PreProcessor)</a> annotations. |
75 | | - * |
76 | | - * @param formulaExtractor An extractor that extracts the formula part of a preprocessor annotation |
| 59 | + * Converts the string captured by the named capture group {@code directive} of {@link #annotationPattern} into an {@link AnnotationType}. |
77 | 60 | */ |
78 | | - public static PreprocessorAnnotationParser CreateJppAnnotationParser(DiffLineFormulaExtractor formulaExtractor) { |
79 | | - return new PreprocessorAnnotationParser(JPP_PATTERN, formulaExtractor); |
| 61 | + protected AnnotationType parseAnnotationType(String directive) { |
| 62 | + if (directive.startsWith("if")) { |
| 63 | + return AnnotationType.If; |
| 64 | + } else if (directive.startsWith("elif")) { |
| 65 | + return AnnotationType.Elif; |
| 66 | + } else if (directive.equals("else")) { |
| 67 | + return AnnotationType.Else; |
| 68 | + } else if (directive.equals("endif")) { |
| 69 | + return AnnotationType.Endif; |
| 70 | + } |
| 71 | + |
| 72 | + throw new IllegalArgumentException("The directive " + directive + " is not a valid conditional compilation directive"); |
80 | 73 | } |
81 | 74 |
|
82 | 75 | /** |
83 | | - * Parses the condition of the given line of source code that contains a preprocessor macro (i.e., IF, IFDEF, ELIF). |
| 76 | + * Parses the feature formula of a preprocessor annotation line. |
| 77 | + * It should abstract complex formulas (e.g., if they contain arithmetics or macro calls) as desired. |
| 78 | + * For example, for the line {@code "#if A && B == C"}, |
| 79 | + * this method is should be called like {@code parseFormula("if", "A && B == C")} |
| 80 | + * (the exact arguments are determined by {@link annotationPattern} |
| 81 | + * and it should return something like {@code and(var("A"), var("B==C"))}. |
| 82 | + * <p> |
| 83 | + * This method is only called if {@code directive} actually requires a formula as determined by {@link #parseAnnotationType}. |
84 | 84 | * |
85 | | - * @param line The line of code of a preprocessor annotation. |
86 | | - * @return The formula of the macro in the given line. |
87 | | - * If no such formula could be parsed, returns a Literal with the line's condition as name. |
88 | | - * @throws UnparseableFormulaException when {@link DiffLineFormulaExtractor#extractFormula(String)} throws. |
| 85 | + * @param directive as matched by the named capture group {@code directive} of {@link annotationPattern} |
| 86 | + * @param formula as matched by the named capture group {@code formula} of {@link annotationPattern} |
| 87 | + * @return the feature mapping |
| 88 | + * @throws UnparseableFormulaException if {@code formula} is ill-formed. |
89 | 89 | */ |
90 | | - @Override |
91 | | - public Node parseAnnotation(String line) throws UnparseableFormulaException { |
92 | | - return extractor.extractFormula(line); |
93 | | - } |
94 | | - |
95 | | - @Override |
96 | | - public AnnotationType determineAnnotationType(String text) { |
97 | | - var matcher = annotationPattern.matcher(text); |
98 | | - int nameId = 1; |
99 | | - if (matcher.find()) { |
100 | | - return AnnotationType.fromName(matcher.group(nameId)); |
101 | | - } else { |
102 | | - return AnnotationType.None; |
103 | | - } |
104 | | - } |
| 90 | + protected abstract Node parseFormula(String directive, String formula) throws UnparseableFormulaException; |
105 | 91 | } |
0 commit comments