Skip to content

Commit f71d229

Browse files
author
Killian Perlin
committed
Merge branch 'topic/lktql_frontend' into 'master'
Introduce the Lkt frontend to LKQL See merge request eng/libadalang/langkit-query-language!563
2 parents d9b5a09 + dc99ec1 commit f71d229

File tree

60 files changed

+1490
-763
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1490
-763
lines changed

lkql/lkql.lkt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ lexer lkql_lexer {
1010
Coma <- ","
1111
SemiCol <- ";"
1212
Colon <- ":"
13-
UnderScore <- "_"
1413
Eq <- "="
1514
EqEq <- "=="
1615
Neq <- "!="
@@ -65,7 +64,7 @@ lexer lkql_lexer {
6564
@symbol() Pass <- "pass"
6665
@symbol() RunPass <- "run_pass"
6766
Integer <- p"[0-9]+"
68-
@symbol() Identifier <- p"[a-z][A-Za-z0-9_]*"
67+
@symbol() Identifier <- p"[a-z_][A-Za-z0-9_]*"
6968
@symbol() UpperIdentifier <- p"[A-Za-z][A-Za-z0-9_]*"
7069
String <- p"\"(\\\\.|[^\"])*\""
7170
@trivia()

lkql_jit/builtins_annotations/src/main/java/com/adacore/lkql_jit/annotations/BuiltInProcessor.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,35 +103,47 @@ public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
103103
stream.println("import com.adacore.lkql_jit.utils.LKQLTypesHelper;");
104104
stream.println("import com.adacore.lkql_jit.built_ins.BuiltInFunctionValue;");
105105
stream.println("import com.adacore.lkql_jit.built_ins.BuiltInMethodFactory;");
106+
stream.println("import org.graalvm.collections.Pair;");
106107

107108
for (var p : builtInPackages) {
108109
stream.println("import " + p.getKey() + "." + p.getValue() + ";");
109110
}
110111
stream.println("public class AllBuiltIns {");
111112

112113
stream.println(
113-
" static private List<BuiltInFunctionValue> allFunctionsCache = null;"
114+
" static private Map<String, Pair<Integer, BuiltInFunctionValue>>" +
115+
" allFunctionsCache = null;"
114116
);
115117
stream.println(
116118
" static private Map<String, Map<String, BuiltInMethodFactory>>" +
117119
" allMethodsCache = null;"
118120
);
119121

120122
// allFunctions method
121-
stream.println(" public static List<BuiltInFunctionValue> allFunctions() {");
122-
stream.println(" if (allFunctionsCache != null) {");
123-
stream.println(" return allFunctionsCache;");
124-
stream.println(" }");
125-
stream.println(" allFunctionsCache = Stream.of(");
123+
stream.println(
124+
"public static Map<String, Pair<Integer, BuiltInFunctionValue>> functions() {"
125+
);
126+
stream.println(" if (allFunctionsCache != null) {");
127+
stream.println(" return allFunctionsCache;");
128+
stream.println(" }");
129+
stream.println(" allFunctionsCache = new HashMap<>();");
130+
stream.println(" var idx = 0;");
131+
stream.println(" var fns = Stream.of(");
126132
stream.println(
127133
builtInPackages
128134
.stream()
129135
.map(p -> " Arrays.stream(" + p.getValue() + ".getFunctions())")
130136
.collect(Collectors.joining(",\n")) +
131137
")"
132138
);
133-
stream.println(" .reduce(Stream::concat).orElseGet(Stream::empty)");
134-
stream.println(" .collect(Collectors.toList());");
139+
140+
stream.println(".reduce(Stream::concat).orElseGet(Stream::empty).toList();");
141+
stream.println(" for (var fn : fns) {");
142+
stream.println(
143+
" allFunctionsCache.put(fn.getExecutableName(), Pair.create(idx, fn));"
144+
);
145+
stream.println(" idx += 1;");
146+
stream.println(" }");
135147
stream.println(" return allFunctionsCache;");
136148
stream.println(" }");
137149

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ protected int executeScript(Context.Builder contextBuilder) {
172172
// Create the context and run the script in it
173173
try (Context context = contextBuilder.build()) {
174174
if (this.args.script != null) {
175-
final Source source = Source.newBuilder("lkql", new File(this.args.script)).build();
175+
Source source = Source.newBuilder("lkql", new File(this.args.script)).build();
176176
context.eval(source);
177177
}
178178

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

Lines changed: 32 additions & 190 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,11 @@
77

88
import static com.adacore.liblkqllang.Liblkqllang.*;
99

10-
import java.io.PrintWriter;
11-
import java.nio.charset.StandardCharsets;
10+
import com.adacore.lkql_jit.options.Refactorings.LKQLToLkt;
11+
import com.adacore.lkql_jit.options.Refactorings.Refactoring;
1212
import java.util.ArrayList;
13-
import java.util.HashMap;
1413
import java.util.List;
1514
import java.util.concurrent.Callable;
16-
import java.util.function.Consumer;
17-
import java.util.function.Predicate;
1815
import picocli.CommandLine;
1916

2017
/**
@@ -51,213 +48,58 @@ public class LKQLRefactor implements Callable<Integer> {
5148
@CommandLine.Option(names = { "-i", "--inplace" }, description = "Rewrite the files in place")
5249
public boolean inPlace;
5350

54-
private enum refactoringKind {
51+
private enum RefactoringKind {
5552
IS_TO_COLON,
53+
TO_LKQL_V2,
5654
}
5755

5856
@CommandLine.Option(
5957
names = { "-r", "--refactoring" },
6058
description = "Refactoring to run. Valid values: ${COMPLETION-CANDIDATES}",
6159
required = true
6260
)
63-
private refactoringKind refactoring;
64-
65-
/** Kind of actions that can be attached to a token. */
66-
private enum actionKind {
67-
APPEND,
68-
PREPEND,
69-
REPLACE,
70-
}
71-
72-
/**
73-
* Action record, encapsulating an action that can be attached to a token. When applied, the
74-
* action will either append the given text, prepend the given text, or replace the token text
75-
* with the given text, depending on the kind.
76-
*
77-
* <p>To remove a token, simply use a REPLACE action with an empty text.
78-
*/
79-
private record Action(actionKind kind, String text) {}
80-
81-
/**
82-
* Global map of actions that will be applied on tokens. To append an action, use the addAction
83-
* method.
84-
*/
85-
private final HashMap<String, List<Action>> actions = new HashMap<>();
86-
87-
/**
88-
* Helper to create a unique Id for a token. TODO: This method exists only because the
89-
* 'hashCode' method on Tokens is wrong. This should be fixed at the Langkit level. See
90-
* eng/libadalang/langkit#857.
91-
*
92-
* @return the id as a string
93-
*/
94-
private String getTokenId(Token token) {
95-
return (
96-
token.unit.getFileName() +
97-
(token.isTrivia() ? "trivia " + token.triviaIndex : token.tokenIndex) +
98-
token.kind
99-
);
100-
}
101-
102-
/** Add an action to the list of actions to apply */
103-
public void addAction(Token token, Action action) {
104-
List<Action> actionList;
105-
var tokenId = getTokenId(token);
106-
if (actions.containsKey(tokenId)) {
107-
actionList = actions.get(tokenId);
108-
} else {
109-
actionList = new ArrayList<>();
110-
actions.put(tokenId, actionList);
111-
}
112-
113-
actionList.add(action);
114-
}
115-
116-
/**
117-
* Helper for refactor writers: Returns the first token that satisfies the predicate 'pred',
118-
* iterating on tokens from 'fromTok' (including 'fromTok').
119-
*
120-
* <p>If no token is found, return the null token.
121-
*/
122-
public static Token firstWithPred(Token fromTok, Predicate<Token> pred) {
123-
var curTok = fromTok;
124-
while (!curTok.isNone() && !pred.test(curTok)) {
125-
curTok = curTok.next();
126-
}
127-
return curTok;
128-
}
129-
130-
/**
131-
* Internal method: rewrite a unit by printing all the tokens via the 'write' callback. Apply
132-
* the actions at the same time.
133-
*/
134-
private void printAllTokens(AnalysisUnit unit, Consumer<String> write) {
135-
for (var tok = unit.getFirstToken(); !tok.isNone(); tok = tok.next()) {
136-
var tokActions = actions.get(getTokenId(tok));
137-
if (tokActions != null) {
138-
var replaceActions = tokActions
139-
.stream()
140-
.filter(c -> c.kind == actionKind.REPLACE)
141-
.toList();
142-
assert replaceActions.size() <= 1 : "Only one replace action per token";
143-
144-
var prependActions = tokActions
145-
.stream()
146-
.filter(c -> c.kind == actionKind.PREPEND)
147-
.toList();
148-
149-
var appendActions = tokActions
150-
.stream()
151-
.filter(c -> c.kind == actionKind.APPEND)
152-
.toList();
153-
154-
for (var action : prependActions) {
155-
write.accept(action.text);
156-
}
157-
158-
if (!replaceActions.isEmpty()) {
159-
write.accept(replaceActions.get(0).text);
160-
} else {
161-
write.accept(tok.getText());
162-
}
163-
164-
for (var action : appendActions) {
165-
write.accept(action.text);
166-
}
167-
} else {
168-
write.accept(tok.getText());
169-
}
170-
}
171-
}
172-
173-
/**
174-
* Internal method: rewrite a unit by printing all the tokens, either to stdout, or to the
175-
* original file, depending on the value of the '-i' command line flag.
176-
*/
177-
private void printAllTokens(AnalysisUnit unit) {
178-
try {
179-
if (this.inPlace) {
180-
var writer = new PrintWriter(unit.getFileName(), StandardCharsets.UTF_8);
181-
printAllTokens(unit, writer::print);
182-
writer.close();
183-
} else {
184-
printAllTokens(unit, System.out::print);
185-
}
186-
} catch (Exception e) {
187-
throw new RuntimeException(e);
188-
}
189-
}
190-
191-
/**
192-
* Helper for findAll. Visit all children of 'node', calling 'cons' on each of them. TODO: Hoist
193-
* in Java bindings (see eng/libadalang/langkit#859)
194-
*/
195-
private static void visitChildren(LkqlNode node, Consumer<LkqlNode> cons) {
196-
if (node == null || node.isNone()) {
197-
return;
198-
}
199-
200-
for (var c : node.children()) {
201-
if (c != null && !c.isNone()) {
202-
cons.accept(c);
203-
visitChildren(c, cons);
204-
}
205-
}
206-
}
207-
208-
/**
209-
* Helper for refactor writers: Find all nodes that are children of root and which satisfies the
210-
* predicate 'pred'. TODO: Hoist in Java bindings (see eng/libadalang/langkit#859)
211-
*/
212-
public static List<LkqlNode> findAll(LkqlNode root, Predicate<LkqlNode> pred) {
213-
var result = new ArrayList<LkqlNode>();
214-
visitChildren(root, c -> {
215-
if (pred.test(c)) {
216-
result.add(c);
217-
}
218-
});
219-
return result;
220-
}
61+
private RefactoringKind refactoring;
22162

22263
/**
22364
* Return the refactoring corresponding to enum value passed on command line. This is where
22465
* concrete refactorings are implemented.
22566
*/
226-
public Consumer<AnalysisUnit> getRefactoring() {
227-
switch (refactoring) {
228-
case IS_TO_COLON:
229-
return unit -> {
230-
for (var det : findAll(unit.getRoot(), n -> n instanceof NodePatternDetail)) {
231-
var tokIs = firstWithPred(
232-
det.tokenStart(),
233-
t -> t.kind == TokenKind.LKQL_IS
234-
);
235-
236-
if (!tokIs.isNone()) {
237-
// Replace "is" -> ":"
238-
addAction(tokIs, new Action(actionKind.REPLACE, ":"));
239-
240-
// Get rid of previous token if it is a whitespace
241-
var prev = tokIs.previous();
242-
if (prev.kind == TokenKind.LKQL_WHITESPACE) {
243-
addAction(tokIs.previous(), new Action(actionKind.REPLACE, ""));
244-
}
67+
public Refactoring getRefactoring(AnalysisUnit unit) {
68+
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+
);
77+
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());
24586
}
24687
}
247-
};
248-
}
249-
return null;
88+
}
89+
};
90+
case TO_LKQL_V2 -> LKQLToLkt.instantiate();
91+
};
25092
}
25193

25294
@Override
25395
public Integer call() {
254-
var refactoring = getRefactoring();
25596
var ctx = AnalysisContext.create();
25697
for (var file : files) {
25798
var unit = ctx.getUnitFromFile(file);
258-
refactoring.accept(unit);
259-
printAllTokens(unit);
260-
actions.clear();
99+
var refactoring = getRefactoring(unit);
100+
var state = new Refactoring.State(unit);
101+
refactoring.applyRefactor(state);
102+
state.printAllTokens(this.inPlace);
261103
}
262104
return 0;
263105
}

lkql_jit/language/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@
9494
<version>${config.liblkqllangVersion}</version>
9595
</dependency>
9696

97+
<dependency>
98+
<groupId>com.adacore</groupId>
99+
<artifactId>liblktlang</artifactId>
100+
<version>${config.liblktlangVersion}</version>
101+
</dependency>
102+
97103
<dependency>
98104
<groupId>com.adacore</groupId>
99105
<artifactId>libadalang</artifactId>

0 commit comments

Comments
 (0)