Skip to content

Commit 037bf3f

Browse files
author
Killian Perlin
committed
Merge branch 'topic/selector_refactor' into 'master'
Implement selector translation See merge request eng/libadalang/langkit-query-language!582
2 parents ae1614c + 0c66ca7 commit 037bf3f

File tree

14 files changed

+584
-322
lines changed

14 files changed

+584
-322
lines changed

lkql_jit/cli/src/main/java/com/adacore/lkql_jit/cli/LKQLRefactor.java

Lines changed: 56 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,40 @@
99

1010
import com.adacore.lkql_jit.options.Refactorings.LKQLToLkt;
1111
import com.adacore.lkql_jit.options.Refactorings.Refactoring;
12+
import com.adacore.lkql_jit.options.Refactorings.TokenBasedRefactoring;
13+
import java.io.PrintWriter;
14+
import java.nio.charset.StandardCharsets;
1215
import java.util.ArrayList;
1316
import java.util.List;
1417
import java.util.concurrent.Callable;
1518
import picocli.CommandLine;
1619

1720
/**
18-
* Refactor command for LKQL. Allows to run automatic migrations, allowing the LKQL team to create
19-
* migrators to adapt to syntactic or semantic changes in LKQL.
21+
* Refactor command for LKQL. Allows to run automatic migrations, allowing the
22+
* LKQL team to create migrators to adapt to syntactic or semantic changes in
23+
* LKQL.
2024
*
21-
* <p>Migrators work on the stream of tokens directly. Migrator implementer will attach actions
22-
* (append/prepend/replace) on tokens, which allows to modify the output stream without working on
23-
* text directly, which simplifies the expression of refactorings.
25+
* <p>
26+
* Migrators are just an implementation of the functionnal interface
27+
* 'Refactoring'. It expects an LKQL unit as input and produces the new unit
28+
* content as a 'String'.
2429
*
25-
* <p>A migrator implementer will typically:
30+
* <p>
31+
* In practice, a 'Refactoring' implementation will be of one of 2 kinds:
32+
* 1. A 'TokenBasedRefactoring' working on the stream of tokens directly and
33+
* producing rewriting actions on each token of the stream.
34+
* 2. A 'TreeBasedRefactoring' working on the Liblkqllang syntax tree and
35+
* producing a new 'String' directly.
2636
*
27-
* <p>1. Find the nodes he wants to refactor in the LKQL tree 2. From those nodes, find the tokens
28-
* inside the nodes that need to be altered 3. Attach actions to those tokens
37+
* <p>
38+
* Then the rewriter will emit a new file for every LKQL unit, either inplace or
39+
* on stdout.
2940
*
30-
* <p>Then the rewriter will emit a new file for every LKQL unit, either inplace or on stdout.
41+
* <p>
42+
* To add a refactoring, you need to extend the 'RefactoringKind' enum to add a
43+
* new refactoring id, and then extend the 'getRefactoring' method to return the
44+
* corresponding implementation of 'Refactoring' to apply.
3145
*
32-
* <p>To add a refactoring, you need to extend the 'refactoringKind' enum to add a new refactoring
33-
* id, and then extend the 'getRefactoring' method to return an anonymous function that will add
34-
* actions to the list of actions.
3546
*/
3647
@CommandLine.Command(
3748
name = "refactor",
@@ -66,40 +77,49 @@ private enum RefactoringKind {
6677
*/
6778
public Refactoring getRefactoring(AnalysisUnit unit) {
6879
return switch (refactoring) {
69-
case IS_TO_COLON -> (Refactoring.State state) -> {
70-
for (var det : Refactoring.findAll(unit.getRoot(), n ->
71-
n instanceof NodePatternDetail
72-
)) {
73-
var tokIs = Refactoring.firstWithPred(
74-
det.tokenStart(),
75-
t -> t.kind == TokenKind.LKQL_IS
76-
);
80+
case IS_TO_COLON -> (TokenBasedRefactoring) state -> {
81+
Refactoring.stream(state.unit.getRoot())
82+
.filter(n -> n instanceof NodePatternDetail)
83+
.forEach(det -> {
84+
Refactoring.streamFrom(det.tokenStart())
85+
.filter(t -> t.kind == TokenKind.LKQL_IS)
86+
.findFirst()
87+
.ifPresent(tokIs -> {
88+
// Replace "is" -> ":"
89+
state.replace(tokIs, ":");
7790

78-
if (!tokIs.isNone()) {
79-
// Replace "is" -> ":"
80-
state.replace(tokIs, ":");
81-
82-
// Get rid of previous token if it is a whitespace
83-
var prev = tokIs.previous();
84-
if (Refactoring.isWhitespace(prev)) {
85-
state.delete(tokIs.previous());
86-
}
87-
}
88-
}
91+
// Get rid of previous token if it is a whitespace
92+
var prev = tokIs.previous();
93+
if (Refactoring.isWhitespace(prev)) {
94+
state.delete(prev);
95+
}
96+
});
97+
});
8998
};
90-
case TO_LKQL_V2 -> LKQLToLkt.instantiate();
99+
case TO_LKQL_V2 -> new LKQLToLkt();
91100
};
92101
}
93102

103+
/**
104+
* Rewrite a unit by printing refactored file, either to stdout, or to the
105+
* original file, depending on the value of the '-i' command line flag.
106+
*/
94107
@Override
95108
public Integer call() {
96109
var ctx = AnalysisContext.create();
97110
for (var file : files) {
98111
var unit = ctx.getUnitFromFile(file);
99-
var refactoring = getRefactoring(unit);
100-
var state = new Refactoring.State(unit);
101-
refactoring.applyRefactor(state);
102-
state.printAllTokens(this.inPlace);
112+
var result = getRefactoring(unit).apply(unit);
113+
114+
if (inPlace) {
115+
try (var writer = new PrintWriter(unit.getFileName(), StandardCharsets.UTF_8)) {
116+
writer.print(result);
117+
} catch (Exception e) {
118+
throw new RuntimeException(e);
119+
}
120+
} else {
121+
System.out.print(result);
122+
}
103123
}
104124
return 0;
105125
}

0 commit comments

Comments
 (0)