Skip to content

Commit adba243

Browse files
author
Killian Perlin
committed
Refactor LKQLToLkt class
Removed unnecessary level of indirection to implement functional interface. Change LKQLToLkt to recursive rewrite - merged all loops - moved large refactors into methods
1 parent d033464 commit adba243

File tree

3 files changed

+187
-77
lines changed

3 files changed

+187
-77
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public Refactoring getRefactoring(AnalysisUnit unit) {
8787
}
8888
}
8989
};
90-
case TO_LKQL_V2 -> LKQLToLkt.instantiate();
90+
case TO_LKQL_V2 -> new LKQLToLkt();
9191
};
9292
}
9393

lkql_jit/options/src/main/java/com/adacore/lkql_jit/options/Refactorings/LKQLToLkt.java

Lines changed: 176 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -5,84 +5,184 @@
55

66
package com.adacore.lkql_jit.options.Refactorings;
77

8+
import static com.adacore.liblkqllang.Liblkqllang.Token.textRange;
9+
810
import com.adacore.liblkqllang.Liblkqllang;
911

10-
public class LKQLToLkt {
11-
12-
public static Refactoring instantiate() {
13-
return (Refactoring.State state) -> {
14-
state.prepend(state.unit.getRoot().tokenStart(), "# lkql version: 2\n\n");
15-
16-
// We need to handle:
17-
for (var fd : Refactoring.findAll(state.unit.getRoot(), n ->
18-
n instanceof Liblkqllang.NamedFunction
19-
)) {
20-
var namedFun = (Liblkqllang.NamedFunction) fd;
21-
22-
// Docstrings: Remove the nested docstring and put it before the function
23-
// declaration
24-
var doc = namedFun.fDocNode();
25-
if (!doc.isNone()) {
26-
var tok = doc.tokenStart();
27-
var afterEnd = doc.tokenEnd().next();
28-
while (!tok.equals(afterEnd)) {
29-
state.delete(tok);
30-
tok = tok.next();
31-
}
32-
33-
while (Refactoring.isWhitespace(afterEnd)) {
34-
state.delete(afterEnd);
35-
afterEnd = afterEnd.next();
36-
}
37-
38-
var funDecl = (Liblkqllang.FunDecl) fd.parent();
39-
state.prepend(funDecl.tokenStart(), doc.getText() + "\n");
40-
}
41-
42-
// Type annotations: Add "Any" type annotations in every place where a type
43-
// is mandatory in Lkt, so mainly in named function declarations (everything
44-
// else is supposedly inferred).
45-
for (var p : namedFun.fParameters().children()) {
46-
var param = (Liblkqllang.ParameterDecl) p;
47-
state.append(param.fParamIdentifier().tokenEnd(), ": Any");
48-
}
49-
50-
state.append(namedFun.fParameters().tokenEnd().next(), ": Any");
51-
}
52-
53-
// Match expressions
54-
for (var me : Refactoring.findAll(state.unit.getRoot(), n ->
55-
n instanceof Liblkqllang.Match
56-
)) {
57-
var matchExpr = (Liblkqllang.Match) me;
58-
state.append(matchExpr.fMatchedVal().tokenEnd(), " {");
59-
60-
for (var ma : matchExpr.fArms().children()) {
61-
var matchArm = (Liblkqllang.MatchArm) ma;
62-
state.replace(matchArm.tokenStart(), "case ");
63-
}
64-
65-
state.append(matchExpr.tokenEnd(), "\n}");
66-
}
67-
68-
// Naked expressions in decl blocks
69-
for (var bbe : Refactoring.findAll(state.unit.getRoot(), n ->
70-
n instanceof Liblkqllang.BlockBodyExpr
71-
)) {
72-
var blockBodyExpr = (Liblkqllang.BlockBodyExpr) bbe;
73-
74-
state.prepend(blockBodyExpr.tokenStart(), "var _ = ");
75-
}
76-
77-
// Naked expressions in the top-level
78-
for (var e : Refactoring.findAll(
79-
state.unit.getRoot(),
80-
n -> n instanceof Liblkqllang.Expr && n.parent() instanceof Liblkqllang.TopLevelList
81-
)) {
82-
var expr = (Liblkqllang.Expr) e;
83-
84-
state.prepend(expr.tokenStart(), "val _ = ");
85-
}
12+
public class LKQLToLkt implements Refactoring {
13+
14+
@Override
15+
public void applyRefactor(Refactoring.State state) {
16+
final var root = state.unit.getRoot();
17+
state.prepend(root.tokenStart(), "# lkql version: 2\n\n");
18+
19+
// Because this rewriting doesn't use the token/action API, this is a hack
20+
state.replaceRange(root.tokenStart(), root.tokenEnd(), refactorNode(root));
21+
}
22+
23+
/** Computes the text representing the refactored node. */
24+
private String refactorNode(Liblkqllang.LkqlNode node) {
25+
if (node.isNone() || node.isGhost()) return "";
26+
27+
return switch (node) {
28+
case Liblkqllang.FunDecl funDecl -> refactorFunDecl(funDecl);
29+
case Liblkqllang.NamedFunction namedFunction -> refactorNamedFunction(namedFunction);
30+
case Liblkqllang.ParameterDecl paramDecl -> refactorParamDecl(paramDecl);
31+
case Liblkqllang.Match match -> refactorMatch(match);
32+
case Liblkqllang.MatchArm arm -> refactorArm(arm, arm.fPattern(), arm.fExpr());
33+
case Liblkqllang.SelectorDecl selectorDecl -> refactorSelectorDecl(selectorDecl);
34+
case Liblkqllang.BlockBodyExpr bbe -> "var _ = " + refactorGeneric(bbe);
35+
case Liblkqllang.Expr expr when (
36+
expr.parent() instanceof Liblkqllang.TopLevelList
37+
) -> "val _ = " + refactorGeneric(expr);
38+
default -> refactorGeneric(node);
8639
};
8740
}
41+
42+
/**
43+
* Copy all the text belonging to a node in the input source,
44+
* but recursively refactor the code of its children.
45+
*/
46+
private String refactorGeneric(Liblkqllang.LkqlNode node) {
47+
if (node.isTokenNode()) return node.getText();
48+
var s = new StringBuilder();
49+
var cursor = node.tokenStart();
50+
51+
for (int i = 0; i < node.getChildrenCount(); i++) {
52+
final var child = node.getChild(i);
53+
if (child.isNone() || child.isGhost()) continue;
54+
// copy until child
55+
s.append(textRange(cursor, child.tokenStart().previous()));
56+
// copy child
57+
s.append(refactorNode(child));
58+
// fast forward token cursor after child
59+
cursor = child.tokenEnd().next();
60+
}
61+
62+
// copy until end
63+
s.append(textRange(cursor, node.tokenEnd()));
64+
65+
return s.toString();
66+
}
67+
68+
/*
69+
*
70+
* fun <name> <funexpr>
71+
*
72+
* <docstring>\n
73+
* fun <name> <funexpr>
74+
*
75+
*/
76+
private String refactorFunDecl(Liblkqllang.FunDecl funDecl) {
77+
var s =
78+
textRange(funDecl.tokenStart(), funDecl.fFunExpr().tokenStart().previous()) +
79+
refactorNode(funDecl.fFunExpr());
80+
81+
// pull docstring out of function declaration
82+
var docstring = funDecl.fFunExpr().fDocNode();
83+
if (!docstring.isNone()) s = refactorNode(docstring) + "\n" + s;
84+
85+
return s;
86+
}
87+
88+
/*
89+
*
90+
* (<params>) = <docstring> <body>
91+
*
92+
* (<params>) : Any = <body>
93+
*
94+
*/
95+
private String refactorNamedFunction(Liblkqllang.NamedFunction namedFunction) {
96+
var s =
97+
textRange(
98+
namedFunction.tokenStart(),
99+
namedFunction.fParameters().tokenStart().previous()
100+
) +
101+
refactorNode(namedFunction.fParameters());
102+
103+
var cursor = namedFunction.fParameters().tokenEnd().next();
104+
while (!cursor.getText().equals("=")) {
105+
s += cursor.getText();
106+
cursor = cursor.next();
107+
}
108+
109+
s +=
110+
": Any " +
111+
textRange(cursor, namedFunction.fBodyExpr().tokenStart().previous()) +
112+
refactorNode(namedFunction.fBodyExpr());
113+
return s;
114+
}
115+
116+
/*
117+
*
118+
* <id> [: <type>] [= <expr>]
119+
*
120+
* <id> : (Any|<type>) [= expr]
121+
*
122+
*/
123+
private String refactorParamDecl(Liblkqllang.ParameterDecl paramDecl) {
124+
var s = refactorNode(paramDecl.fParamIdentifier());
125+
126+
var cursor = paramDecl.fParamIdentifier().tokenEnd().next();
127+
128+
if (!paramDecl.fTypeAnnotation().isNone()) {
129+
s +=
130+
textRange(cursor, paramDecl.fTypeAnnotation().tokenStart().previous()) +
131+
refactorNode(paramDecl.fTypeAnnotation());
132+
cursor = paramDecl.fTypeAnnotation().tokenEnd().next();
133+
} else {
134+
s += " : Any"; // add type annotation if none
135+
}
136+
137+
if (!paramDecl.fDefaultExpr().isNone()) {
138+
s +=
139+
textRange(cursor, paramDecl.fDefaultExpr().tokenStart().previous()) +
140+
refactorNode(paramDecl.fDefaultExpr());
141+
}
142+
143+
return s;
144+
}
145+
146+
/*
147+
*
148+
* match <expr> <arms>
149+
*
150+
* match <expr> { <arms> }
151+
*
152+
*/
153+
private String refactorMatch(Liblkqllang.Match match) {
154+
return (
155+
textRange(match.tokenStart(), match.fMatchedVal().tokenStart().previous()) +
156+
refactorNode(match.fMatchedVal()) +
157+
" {" +
158+
textRange(
159+
match.fMatchedVal().tokenEnd().next(),
160+
match.fArms().tokenStart().previous()
161+
) +
162+
refactorNode(match.fArms()) +
163+
"\n}\n"
164+
);
165+
}
166+
167+
/*
168+
*
169+
* | <pattern> => <expr>
170+
*
171+
* case <pattern> => <expr>
172+
*
173+
*/
174+
private String refactorArm(
175+
Liblkqllang.LkqlNode arm,
176+
Liblkqllang.BasePattern pattern,
177+
Liblkqllang.Expr expr
178+
) {
179+
return (
180+
"case" +
181+
textRange(arm.tokenStart().next(), pattern.tokenStart().previous()) +
182+
refactorNode(pattern) +
183+
textRange(pattern.tokenEnd().next(), expr.tokenStart().previous()) +
184+
refactorNode(expr) +
185+
textRange(expr.tokenEnd().next(), arm.tokenEnd())
186+
);
187+
}
88188
}

lkql_jit/options/src/main/java/com/adacore/lkql_jit/options/Refactorings/Refactoring.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,16 @@ public void replace(Liblkqllang.Token token, String text) {
6767
addAction(token, new Action(ActionKind.REPLACE, text));
6868
}
6969

70+
public void replaceRange(Liblkqllang.Token start, Liblkqllang.Token end, String text) {
71+
var cur = start;
72+
while (cur.tokenIndex != end.tokenIndex) {
73+
delete(cur);
74+
cur = cur.next();
75+
}
76+
// append new code
77+
replace(end, text);
78+
}
79+
7080
public void append(Liblkqllang.Token token, String text) {
7181
addAction(token, new Action(ActionKind.APPEND, text));
7282
}

0 commit comments

Comments
 (0)