|
6 | 6 | package com.adacore.lkql_jit.options.Refactorings; |
7 | 7 |
|
8 | 8 | import com.adacore.liblkqllang.Liblkqllang; |
9 | | -import java.io.PrintWriter; |
10 | | -import java.io.StringWriter; |
11 | | -import java.nio.charset.StandardCharsets; |
12 | | -import java.util.ArrayList; |
13 | | -import java.util.HashMap; |
14 | | -import java.util.List; |
15 | 9 | import java.util.function.Consumer; |
16 | 10 | import java.util.stream.Stream; |
17 | 11 |
|
| 12 | +/** Class offering an abstraction over all refactoring processes. */ |
| 13 | +@FunctionalInterface |
18 | 14 | public interface Refactoring { |
19 | | - class State { |
20 | | - |
21 | | - public final Liblkqllang.AnalysisUnit unit; |
22 | | - |
23 | | - /** Kind of actions that can be attached to a token. */ |
24 | | - enum ActionKind { |
25 | | - APPEND, |
26 | | - PREPEND, |
27 | | - REPLACE, |
28 | | - } |
29 | | - |
30 | | - /** |
31 | | - * Action record, encapsulating an action that can be attached to a token. When applied, the |
32 | | - * action will either append the given text, prepend the given text, or replace the token text |
33 | | - * with the given text, depending on the kind. |
34 | | - * |
35 | | - * <p>To remove a token, simply use a REPLACE action with an empty text. |
36 | | - */ |
37 | | - private record Action(ActionKind kind, String text) {} |
38 | | - |
39 | | - /** |
40 | | - * List of actions accumulated in this state object. |
41 | | - */ |
42 | | - public final HashMap<String, List<Action>> actions = new HashMap<>(); |
43 | | - |
44 | | - public State(Liblkqllang.AnalysisUnit unit) { |
45 | | - this.unit = unit; |
46 | | - } |
47 | | - |
48 | | - /** Add an action to the list of actions to apply */ |
49 | | - private void addAction(Liblkqllang.Token token, Action action) { |
50 | | - List<Action> actionList; |
51 | | - var tokenId = getTokenId(token); |
52 | | - if (actions.containsKey(tokenId)) { |
53 | | - actionList = actions.get(tokenId); |
54 | | - } else { |
55 | | - actionList = new ArrayList<>(); |
56 | | - actions.put(tokenId, actionList); |
57 | | - } |
58 | | - |
59 | | - actionList.add(action); |
60 | | - } |
61 | | - |
62 | | - public void delete(Liblkqllang.Token token) { |
63 | | - addAction(token, new Action(ActionKind.REPLACE, "")); |
64 | | - } |
65 | | - |
66 | | - public void replace(Liblkqllang.Token token, String text) { |
67 | | - addAction(token, new Action(ActionKind.REPLACE, text)); |
68 | | - } |
69 | | - |
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 | | - |
80 | | - public void append(Liblkqllang.Token token, String text) { |
81 | | - addAction(token, new Action(ActionKind.APPEND, text)); |
82 | | - } |
83 | | - |
84 | | - public void prepend(Liblkqllang.Token token, String text) { |
85 | | - addAction(token, new Action(ActionKind.PREPEND, text)); |
86 | | - } |
87 | | - |
88 | | - /** |
89 | | - * Internal method: rewrite a unit by printing all the tokens via the 'write' callback. Apply |
90 | | - * the actions at the same time. |
91 | | - */ |
92 | | - private void printAllTokens(Consumer<String> write) { |
93 | | - for (var tok = unit.getFirstToken(); !tok.isNone(); tok = tok.next()) { |
94 | | - var tokActions = actions.get(getTokenId(tok)); |
95 | | - if (tokActions != null) { |
96 | | - var replaceActions = tokActions |
97 | | - .stream() |
98 | | - .filter(c -> c.kind == ActionKind.REPLACE) |
99 | | - .toList(); |
100 | | - assert replaceActions.size() <= 1 : "Only one replace action per token"; |
101 | | - |
102 | | - var prependActions = tokActions |
103 | | - .stream() |
104 | | - .filter(c -> c.kind == ActionKind.PREPEND) |
105 | | - .toList(); |
106 | | - |
107 | | - var appendActions = tokActions |
108 | | - .stream() |
109 | | - .filter(c -> c.kind == ActionKind.APPEND) |
110 | | - .toList(); |
111 | | - |
112 | | - for (var action : prependActions) { |
113 | | - write.accept(action.text); |
114 | | - } |
115 | | - |
116 | | - if (!replaceActions.isEmpty()) { |
117 | | - write.accept(replaceActions.get(0).text); |
118 | | - } else { |
119 | | - write.accept(tok.getText()); |
120 | | - } |
121 | | - |
122 | | - for (var action : appendActions) { |
123 | | - write.accept(action.text); |
124 | | - } |
125 | | - } else { |
126 | | - write.accept(tok.getText()); |
127 | | - } |
128 | | - } |
129 | | - } |
130 | | - |
131 | | - /** |
132 | | - * Rewrite a unit by printing all the tokens, either to stdout, or to the |
133 | | - * original file, depending on the value of the '-i' command line flag. |
134 | | - */ |
135 | | - public void printAllTokens(boolean inPlace) { |
136 | | - try { |
137 | | - if (inPlace) { |
138 | | - var writer = new PrintWriter(unit.getFileName(), StandardCharsets.UTF_8); |
139 | | - printAllTokens(writer::print); |
140 | | - writer.close(); |
141 | | - } else { |
142 | | - printAllTokens(System.out::print); |
143 | | - } |
144 | | - } catch (Exception e) { |
145 | | - throw new RuntimeException(e); |
146 | | - } |
147 | | - } |
148 | | - |
149 | | - public String toString() { |
150 | | - var writer = new StringWriter(); |
151 | | - printAllTokens(writer::write); |
152 | | - return writer.toString(); |
153 | | - } |
154 | | - } |
155 | | - |
156 | | - /** |
157 | | - * Helper to create a unique Id for a token. TODO: This method exists only because the |
158 | | - * 'hashCode' method on Tokens is wrong. This should be fixed at the Langkit level. See |
159 | | - * eng/libadalang/langkit#857. |
160 | | - * |
161 | | - * @return the id as a string |
162 | | - */ |
163 | | - private static String getTokenId(Liblkqllang.Token token) { |
164 | | - return ( |
165 | | - token.unit.getFileName() + |
166 | | - (token.isTrivia() ? "trivia " + token.triviaIndex : token.tokenIndex) + |
167 | | - token.kind |
168 | | - ); |
169 | | - } |
| 15 | + /** Take a parsing tree root and return its source after refactoring actions. */ |
| 16 | + String apply(Liblkqllang.AnalysisUnit unit); |
170 | 17 |
|
171 | 18 | public static boolean isWhitespace(Liblkqllang.Token token) { |
172 | 19 | return token.kind == Liblkqllang.TokenKind.LKQL_WHITESPACE; |
@@ -198,6 +45,4 @@ public static Stream<Liblkqllang.LkqlNode> stream(Liblkqllang.LkqlNode root) { |
198 | 45 | public static Stream<Liblkqllang.Token> streamFrom(Liblkqllang.Token start) { |
199 | 46 | return Stream.iterate(start, t -> !t.isNone(), t -> t.next()); |
200 | 47 | } |
201 | | - |
202 | | - void applyRefactor(State state); |
203 | 48 | } |
0 commit comments