Skip to content

Commit efbaff3

Browse files
raph-amiardKillian Perlin
authored andcommitted
Introduce the Lkt syntax path
Start implementing pattern details
1 parent 395092e commit efbaff3

File tree

14 files changed

+890
-213
lines changed

14 files changed

+890
-213
lines changed

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>

lkql_jit/language/src/main/java/com/adacore/lkql_jit/LKQLLanguage.java

Lines changed: 153 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55

66
package com.adacore.lkql_jit;
77

8+
import com.adacore.langkit_support.LangkitSupport;
89
import com.adacore.liblkqllang.Liblkqllang;
10+
import com.adacore.liblktlang.Liblktlang;
911
import com.adacore.lkql_jit.checker.utils.CheckerUtils;
1012
import com.adacore.lkql_jit.exception.LKQLRuntimeException;
1113
import com.adacore.lkql_jit.langkit_translator.passes.FramingPass;
14+
import com.adacore.lkql_jit.langkit_translator.passes.LktPasses;
1215
import com.adacore.lkql_jit.langkit_translator.passes.ResolutionPass;
1316
import com.adacore.lkql_jit.langkit_translator.passes.TranslationPass;
1417
import com.adacore.lkql_jit.langkit_translator.passes.framing_utils.ScriptFrames;
@@ -26,6 +29,7 @@
2629
import com.oracle.truffle.api.source.Source;
2730
import java.io.PrintStream;
2831
import java.nio.charset.StandardCharsets;
32+
import java.util.Scanner;
2933
import org.graalvm.options.OptionCategory;
3034
import org.graalvm.options.OptionDescriptors;
3135
import org.graalvm.options.OptionKey;
@@ -103,6 +107,7 @@ public final class LKQLLanguage extends TruffleLanguage<LKQLContext> {
103107
static final OptionKey<String> options = new OptionKey<>("");
104108

105109
Liblkqllang.AnalysisContext lkqlAnalysisContext;
110+
Liblktlang.AnalysisContext lktAnalysisContext;
106111

107112
// ----- Constructors -----
108113

@@ -121,6 +126,14 @@ public LKQLLanguage() {
121126
1
122127
);
123128

129+
this.lktAnalysisContext = Liblktlang.AnalysisContext.create(
130+
null,
131+
null,
132+
null,
133+
null,
134+
true,
135+
1
136+
);
124137
// Set the color support flag
125138
SUPPORT_COLOR = System.getenv("TERM") != null && System.console() != null;
126139
}
@@ -205,44 +218,8 @@ protected CallTarget parse(ParsingRequest request) {
205218
final Liblkqllang.AnalysisUnit unit;
206219
TopLevelList result;
207220

208-
if (request.getSource().getPath() == null) {
209-
unit = lkqlAnalysisContext.getUnitFromBuffer(
210-
request.getSource().getCharacters().toString(),
211-
"<command-line>"
212-
);
213-
} else {
214-
unit = lkqlAnalysisContext.getUnitFromFile(request.getSource().getPath());
215-
}
216-
217-
// Verify the parsing result
218-
final var diagnostics = unit.getDiagnostics();
219-
if (diagnostics.length > 0) {
220-
var ctx = LKQLLanguage.getContext(null);
221-
222-
// Iterate over diagnostics
223-
for (Liblkqllang.Diagnostic diagnostic : diagnostics) {
224-
ctx
225-
.getDiagnosticEmitter()
226-
.emitDiagnostic(
227-
CheckerUtils.MessageKind.ERROR,
228-
diagnostic.message.toString(),
229-
null,
230-
SourceSectionWrapper.create(
231-
diagnostic.sourceLocationRange,
232-
request.getSource()
233-
)
234-
);
235-
}
236-
throw LKQLRuntimeException.fromMessage(
237-
"Syntax errors in " + unit.getFileName(false) + ": stopping interpreter"
238-
);
239-
}
240-
241-
// Get the LKQL langkit AST
242-
final Liblkqllang.TopLevelList lkqlLangkitRoot = (Liblkqllang.TopLevelList) unit.getRoot();
243-
244221
// Translate the LKQL AST from Langkit to a Truffle AST
245-
result = (TopLevelList) translate(lkqlLangkitRoot, request.getSource());
222+
result = (TopLevelList) translate(request.getSource());
246223

247224
// If the current parsing request is the root request
248225
if (!request.getSource().isInternal()) {
@@ -273,22 +250,14 @@ protected CallTarget parse(ParsingRequest request) {
273250
return new TopLevelRootNode(request.getSource().isInternal(), result, this).getCallTarget();
274251
}
275252

276-
/** Translate the given source from string. */
277-
public LKQLNode translate(String source, String sourceName) {
278-
Source src = Source.newBuilder(Constants.LKQL_ID, source, sourceName).build();
279-
var root = lkqlAnalysisContext.getUnitFromBuffer(source, sourceName).getRoot();
280-
return translate(root, src, false);
281-
}
282-
283253
/**
284-
* Translate the given source Langkit AST.
254+
* Private helper. Translate the given source Langkit AST to LKQLNode hierarchy.
285255
*
286-
* @param lkqlLangkitRoot The LKQL Langkit AST to translate.
287-
* @param source The Truffle source of the AST.
288-
* @return The translated LKQL Truffle AST.
256+
* <p>This method works on LKQL roots and Lkt roots, dispatching on the proper parser and
257+
* translation pass as needed.
289258
*/
290-
public LKQLNode translate(
291-
final Liblkqllang.LkqlNode lkqlLangkitRoot,
259+
private LKQLNode translate(
260+
final LangkitSupport.NodeInterface root,
292261
final Source source,
293262
boolean isPrelude
294263
) {
@@ -301,11 +270,11 @@ public LKQLNode translate(
301270
PRELUDE_SOURCE,
302271
"<prelude>"
303272
).build();
304-
var root = lkqlAnalysisContext
273+
var preludeRoot = lkqlAnalysisContext
305274
.getUnitFromBuffer(PRELUDE_SOURCE, "<prelude>")
306275
.getRoot();
307-
var preludeRoot = (TopLevelList) translate(root, preludeSource, true);
308-
var callTarget = new TopLevelRootNode(true, preludeRoot, this).getCallTarget();
276+
var lkqlPrelude = (TopLevelList) translate(preludeRoot, preludeSource, true);
277+
var callTarget = new TopLevelRootNode(true, lkqlPrelude, this).getCallTarget();
309278
global.prelude = (LKQLNamespace) callTarget.call();
310279
var preludeMap = global.prelude.asMap();
311280

@@ -320,27 +289,143 @@ public LKQLNode translate(
320289
}
321290
}
322291

323-
// Do the framing pass to create the script frame descriptions
324-
final FramingPass framingPass = new FramingPass(source);
325-
lkqlLangkitRoot.accept(framingPass);
326-
final ScriptFrames scriptFrames = framingPass
327-
.getScriptFramesBuilder()
328-
.build(CONTEXT_REFERENCE.get(null).getGlobal());
292+
if (root instanceof Liblkqllang.LkqlNode lkqlRoot) {
293+
// Do the framing pass to create the script frame descriptions
294+
final FramingPass framingPass = new FramingPass(source);
295+
lkqlRoot.accept(framingPass);
296+
final ScriptFrames scriptFrames = framingPass
297+
.getScriptFramesBuilder()
298+
.build(CONTEXT_REFERENCE.get(null).getGlobal());
299+
300+
// Do the translation pass and return the result
301+
final TranslationPass translationPass = new TranslationPass(source, scriptFrames);
302+
303+
final var ast = lkqlRoot.accept(translationPass);
304+
if (!isPrelude) {
305+
final var resolutionPass = new ResolutionPass();
306+
resolutionPass.passEntry(ast);
307+
}
308+
return ast;
309+
} else if (root instanceof Liblktlang.LangkitRoot lktRoot) {
310+
final ScriptFrames frames = LktPasses.Frames.buildFrames(lktRoot).build(
311+
CONTEXT_REFERENCE.get(null).getGlobal()
312+
);
329313

330-
// Do the translation pass and return the result
331-
final TranslationPass translationPass = new TranslationPass(source, scriptFrames);
314+
final var ast = LktPasses.buildLKQLNode(source, lktRoot, frames);
315+
if (!isPrelude) {
316+
final var resolutionPass = new ResolutionPass();
317+
resolutionPass.passEntry(ast);
318+
}
319+
return ast;
320+
} else {
321+
throw LKQLRuntimeException.fromMessage("Should not happen");
322+
}
323+
}
332324

333-
final var ast = lkqlLangkitRoot.accept(translationPass);
325+
/**
326+
* Translate the given source Langkit AST. The source can be either legacy LKQL syntax (LKQL V1)
327+
* or Lkt syntax (future LKQL v2).
328+
*
329+
* <p>The default is LKQL syntax, but either syntaxes can be triggered by a comment in the first
330+
* line of the file: # lkql version: 1/2
331+
*
332+
* <p>The trigger is a simple string match, so the comment needs to match exactly that. We might
333+
* relax those constraints at a later stage.
334+
*
335+
* @param source The Truffle source of the AST.
336+
* @return The translated LKQL Truffle AST.
337+
*/
338+
public LKQLNode translate(final Source source, String sourceName) {
339+
var firstLine = new Scanner(source.getReader()).nextLine();
340+
LangkitSupport.AnalysisContextInterface langkitCtx = null;
341+
var baseName = source.getName().replaceFirst("[.][^.]+$", "");
342+
Source src;
343+
344+
if (getContext(null).getOptions().autoTranslateUnits().contains(baseName)) {
345+
System.out.println("Translating " + source.getName() + " to lkt");
346+
String newSrc = source.getCharacters().toString();
347+
var lines = newSrc.split("\n");
348+
349+
if (firstLine.startsWith("# lkql version:")) {
350+
if (firstLine.equals("# lkql version: 1")) {
351+
// Translate LKQL to Lkt (LKQL v2)
352+
newSrc = String.join("\n", Arrays.stream(lines).skip(1).toList());
353+
} else {
354+
throw LKQLRuntimeException.fromMessage(
355+
"Invalid lkql version line for autoTranslateUnit unit"
356+
);
357+
}
358+
}
334359

335-
if (!isPrelude) {
336-
final var resolutionPass = new ResolutionPass();
337-
resolutionPass.passEntry(ast);
360+
var lktSrc = LKQLToLkt.lkqlToLkt(sourceName, newSrc);
361+
362+
if (getContext(null).isVerbose()) {
363+
System.out.println("Lkt source for " + sourceName);
364+
System.out.println("=============================");
365+
System.out.println(lktSrc);
366+
System.out.println();
367+
}
368+
369+
// Build a new source
370+
src = Source.newBuilder(Constants.LKQL_ID, lktSrc, source.getName()).build();
371+
372+
langkitCtx = lktAnalysisContext;
373+
} else if (firstLine.startsWith("# lkql version:")) {
374+
if (firstLine.equals("# lkql version: 1")) {
375+
// lkql V1 uses lkql syntax
376+
langkitCtx = lkqlAnalysisContext;
377+
} else if (firstLine.equals("# lkql version: 2")) {
378+
// lkql V2 uses Lkt syntax
379+
langkitCtx = lktAnalysisContext;
380+
} else {
381+
throw LKQLRuntimeException.fromMessage("Invalid lkql version");
382+
}
383+
} else {
384+
// By default, use lkql syntax
385+
langkitCtx = lkqlAnalysisContext;
386+
}
387+
388+
LangkitSupport.AnalysisUnit unit;
389+
if (source.getPath() == null) {
390+
unit = langkitCtx.getUnitFromBuffer(source.getCharacters().toString(), sourceName);
391+
} else {
392+
unit = langkitCtx.getUnitFromFile(source.getPath());
338393
}
339394

340-
return ast;
395+
final var diagnostics = unit.getDiagnostics();
396+
if (diagnostics.length > 0) {
397+
var ctx = LKQLLanguage.getContext(null);
398+
399+
// Iterate over diagnostics
400+
for (var diagnostic : diagnostics) {
401+
ctx
402+
.getDiagnosticEmitter()
403+
.emitDiagnostic(
404+
CheckerUtils.MessageKind.ERROR,
405+
diagnostic.getMessage().toString(),
406+
null,
407+
SourceSectionWrapper.create(diagnostic.getSourceLocationRange(), source)
408+
);
409+
}
410+
throw LKQLRuntimeException.fromMessage(
411+
"Syntax errors in " + unit.getFileName(false) + ": stopping interpreter"
412+
);
413+
}
414+
415+
return translate(unit.getRoot(), source, false);
416+
}
417+
418+
/**
419+
* Shortcut to translate a source. If it has no name, it will be given the name
420+
* "<command-line>".
421+
*/
422+
public LKQLNode translate(final Source source) {
423+
return translate(source, "<command-line>");
341424
}
342425

343-
public LKQLNode translate(final Liblkqllang.LkqlNode lkqlLangkitRoot, final Source source) {
344-
return translate(lkqlLangkitRoot, source, false);
426+
/** Shortcut to translate the given source from string. */
427+
public LKQLNode translate(String source, String sourceName) {
428+
Source src = Source.newBuilder(Constants.LKQL_ID, source, sourceName).build();
429+
return translate(src, sourceName);
345430
}
346431
}

lkql_jit/language/src/main/java/com/adacore/lkql_jit/checker/utils/CheckerUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ public static enum MessageKind {
5959
}
6060

6161
/**
62-
* Common interface for diagnostic emitters. All given parameters need not be used in the
63-
* output.
62+
* BaseTranslationPass interface for diagnostic emitters. All given parameters need not be used
63+
* in the output.
6464
*/
6565
public interface DiagnosticEmitter {
6666
/** Emit a rule violation TODO: Meld that into emitDiagnostic eventually */

0 commit comments

Comments
 (0)