• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

wurstscript / WurstScript / 228

29 Nov 2023 05:00PM UTC coverage: 62.48% (-0.09%) from 62.574%
228

push

circleci

web-flow
Show dialog for choosing game path, cleanup (#1083)

* show dialog for choosing game path

* cleanup code

* remove logs and refactor

* remove confusing mpq error, make some mpq loads readonly

17295 of 27681 relevant lines covered (62.48%)

0.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.21
de.peeeq.wurstscript/src/main/java/de/peeeq/wurstscript/translation/imtojass/ImToJassTranslator.java
1
package de.peeeq.wurstscript.translation.imtojass;
2

3
import com.google.common.collect.*;
4
import de.peeeq.wurstscript.attributes.CompileError;
5
import de.peeeq.wurstscript.jassAst.JassFunction;
6
import de.peeeq.wurstscript.jassAst.JassFunctions;
7
import de.peeeq.wurstscript.jassAst.JassInitializedVar;
8
import de.peeeq.wurstscript.jassAst.JassNative;
9
import de.peeeq.wurstscript.jassAst.JassProg;
10
import de.peeeq.wurstscript.jassAst.JassSimpleVar;
11
import de.peeeq.wurstscript.jassAst.JassVars;
12
import de.peeeq.wurstscript.jassAst.*;
13
import de.peeeq.wurstscript.jassIm.Element;
14
import de.peeeq.wurstscript.jassIm.*;
15
import de.peeeq.wurstscript.parser.WPos;
16
import de.peeeq.wurstscript.translation.imoptimizer.RestrictedCompressedNames;
17
import de.peeeq.wurstscript.translation.imtranslation.FunctionFlagEnum;
18
import de.peeeq.wurstscript.translation.imtranslation.ImHelper;
19
import de.peeeq.wurstscript.utils.Utils;
20
import org.eclipse.jdt.annotation.Nullable;
21

22
import java.util.*;
23
import java.util.regex.Pattern;
24
import java.util.stream.Collectors;
25

26
import static de.peeeq.wurstscript.jassAst.JassAst.*;
27

28
public class ImToJassTranslator {
29

30
    private final ImProg imProg;
31
    private final Multimap<ImFunction, ImFunction> calledFunctions;
32
    private final ImFunction mainFunc;
33
    private final ImFunction confFunction;
34
    private @Nullable JassProg prog;
35
    private final Stack<ImFunction> translatingFunctions = new Stack<>();
1✔
36
    private final Set<ImFunction> translatedFunctions = Sets.newLinkedHashSet();
1✔
37
    private final Set<String> usedNames = Sets.newLinkedHashSet();
1✔
38
    private final Multimap<ImFunction, String> usedLocalNames = HashMultimap.create();
1✔
39

40
    public ImToJassTranslator(ImProg imProg, Multimap<ImFunction, ImFunction> calledFunctions,
41
                              ImFunction mainFunc, ImFunction confFunction) {
1✔
42
        this.imProg = imProg;
1✔
43
        this.calledFunctions = calledFunctions;
1✔
44
        this.mainFunc = mainFunc;
1✔
45
        this.confFunction = confFunction;
1✔
46
    }
1✔
47

48
    public JassProg translate() {
49
        makeNamesUnique(imProg.getGlobals());
1✔
50
        makeNamesUnique(imProg.getFunctions());
1✔
51

52
        JassVars globals = JassVars();
1✔
53
        JassFunctions functions = JassFunctions();
1✔
54
        prog = JassProg(JassTypeDefs(), globals, JassNatives(), functions);
1✔
55

56
        collectGlobalVars();
1✔
57

58
        translateFunctionTransitive(mainFunc);
1✔
59
        translateFunctionTransitive(confFunction);
1✔
60

61
        return prog;
1✔
62
    }
63

64
    /**
65
     * makes names unique in a consistent way
66
     */
67
    private <T extends JassImElementWithName> void makeNamesUnique(List<T> list) {
68
        List<T> sorted = list.stream()
1✔
69
                .sorted(
1✔
70
                        Comparator.comparing(JassImElementWithName::getName)
1✔
71
                                .thenComparing(v -> v.getTrace().attrSource().getFile())
1✔
72
                                .thenComparing(v -> v.getTrace().attrSource().getLine())
1✔
73
                                .thenComparing(v -> v.getTrace().attrSource().getStartColumn()))
1✔
74
                .collect(Collectors.toList());
1✔
75

76
        for (int i = 0; i < sorted.size(); i++) {
1✔
77
            T vi = sorted.get(i);
1✔
78
            for (int j = i + 1; j < sorted.size(); j++) {
1✔
79
                T vj = sorted.get(j);
1✔
80
                if (vi.getName().equals(vj.getName())) {
1✔
81
                    vj.setName(vi.getName() + "_" + j);
1✔
82
                }
83
            }
84
        }
85
    }
1✔
86

87
    private void collectGlobalVars() {
88
        for (ImVar v : imProg.getGlobals()) {
1✔
89
            globalImVars.add(v);
1✔
90
            getJassVarFor(v);
1✔
91
        }
1✔
92
    }
1✔
93

94
    private void translateFunctionTransitive(ImFunction imFunc) {
95
        if (translatedFunctions.contains(imFunc)) {
1✔
96
            // already translated
97
            return;
1✔
98
        }
99
        if (translatingFunctions.contains(imFunc)) {
1✔
100
            // TODO extract method
101
            if (imFunc != translatingFunctions.peek()) {
×
102
                StringBuilder msg = new StringBuilder("cyclic dependency between functions: ");
×
103
                boolean start = false;
×
104
                for (ImFunction f : translatingFunctions) {
×
105
                    if (imFunc == f) {
×
106
                        start = true;
×
107
                    }
108
                    if (start) {
×
109
                        msg.append("\n - ").append(Utils.printElement(getTrace(f))).append("  ( ").append(f.attrTrace().attrSource().getFile()).append(" line" +
×
110
                                "  ").append(f.attrTrace().attrSource().getLine()).append(")");
×
111
                    }
112
                }
×
113
                WPos src = getTrace(imFunc).attrSource();
×
114
                throw new CompileError(src, msg.toString());
×
115
            }
116
            // already translating, recursive function
117
            return;
×
118
        }
119
        translatingFunctions.push(imFunc);
1✔
120
        for (ImFunction f : sorted(calledFunctions.get(imFunc))) {
1✔
121
            translateFunctionTransitive(f);
1✔
122
        }
1✔
123

124
        translateFunction(imFunc);
1✔
125

126
        // translation finished
127
        if (translatingFunctions.pop() != imFunc) {
1✔
128
            throw new Error("something went wrong...");
×
129
        }
130
        translatedFunctions.add(imFunc);
1✔
131
    }
1✔
132

133
    private List<ImFunction> sorted(Collection<ImFunction> collection) {
134
        List<ImFunction> r = Lists.newArrayList(collection);
1✔
135
        r.sort(Comparator.comparing(ImFunction::getName));
1✔
136
        return r;
1✔
137
    }
138

139
    private static de.peeeq.wurstscript.ast.Element getTrace(@Nullable Element elem) {
140
        while (elem != null) {
×
141
            if (elem instanceof ElementWithTrace) {
×
142
                ElementWithTrace ElementWithTrace = (ElementWithTrace) elem;
×
143
                de.peeeq.wurstscript.ast.Element t = ElementWithTrace.getTrace();
×
144
                if (t != null) {
×
145
                    return t;
×
146
                }
147
            }
148
            elem = elem.getParent();
×
149
        }
150
        throw new Error("Could not get trace to original program.");
×
151
    }
152

153
    private void translateFunction(ImFunction imFunc) {
154
        if (imFunc.isBj() || imFunc.hasFlag(FunctionFlagEnum.IS_VARARG)) {
1✔
155
            return;
1✔
156
        }
157
        // not a native
158
        JassFunctionOrNative f = getJassFuncFor(imFunc);
1✔
159

160
        f.setReturnType(imFunc.getReturnType().translateType());
1✔
161

162
        // translate parameters
163
        for (ImVar v : imFunc.getParameters()) {
1✔
164
            f.getParams().add((JassSimpleVar) getJassVarFor(v));
1✔
165
        }
1✔
166
        if (f instanceof JassFunction) {
1✔
167
            JassFunction jf = (JassFunction) f;
1✔
168
            // translate locals
169
            for (ImVar v : imFunc.getLocals()) {
1✔
170
                jf.getLocals().add(getJassVarFor(v));
1✔
171
            }
1✔
172
            imFunc.getBody().translate(jf.getBody(), jf, this);
1✔
173
        }
174

175
    }
1✔
176

177

178
    private String getUniqueGlobalName(String name) { // TODO find local names
179
        name = jassifyName(name);
1✔
180
        name = Utils.makeUniqueName(name, n -> !usedNames.contains(n));
1✔
181
        usedNames.add(name);
1✔
182
        return name;
1✔
183
    }
184

185
    private String getUniqueLocalName(ImFunction imFunction, String name) {
186
        name = jassifyName(name);
1✔
187
        name = Utils.makeUniqueName(name, n -> !usedNames.contains(n) && !usedLocalNames.containsEntry(imFunction, n));
1✔
188
        usedLocalNames.put(imFunction, name);
1✔
189
        return name;
1✔
190
    }
191

192

193
    private final Map<ImVar, JassVar> jassVars = Maps.newLinkedHashMap();
1✔
194
    private final Set<ImVar> globalImVars = Sets.newLinkedHashSet();
1✔
195

196
    JassVar getJassVarFor(ImVar v) {
197
        JassVar result = jassVars.get(v);
1✔
198
        if (result == null) {
1✔
199
            boolean isArray = v.getType() instanceof ImArrayType;
1✔
200
            String type = v.getType().translateType();
1✔
201
            String name = v.getName();
1✔
202
            if (v.getNearestFunc() != null) {
1✔
203
                name = getUniqueLocalName(v.getNearestFunc(), name);
1✔
204
            } else {
205
                name = getUniqueGlobalName(name);
1✔
206
            }
207
            if (isArray) {
1✔
208
                result = JassAst.JassArrayVar(type, name);
1✔
209
            } else {
210
                if (isGlobal(v) && v.getType() instanceof ImSimpleType) {
1✔
211
                    JassExpr initialVal = ImHelper.defaultValueForType((ImSimpleType) v.getType()).translate(this);
1✔
212
                    result = JassAst.JassInitializedVar(type, name, initialVal, v.getIsBJ());
1✔
213
                } else {
1✔
214
                    result = JassAst.JassSimpleVar(type, name);
1✔
215
                }
216
            }
217
            if (isGlobal(v) && (!v.getIsBJ() || result instanceof JassInitializedVar)) {
1✔
218
                prog.getGlobals().add(result);
1✔
219
            }
220
            jassVars.put(v, result);
1✔
221
        }
222
        return result;
1✔
223
    }
224

225
    private String jassifyName(String name) {
226
        name = filterInvalidSymbols(name);
1✔
227
        if (RestrictedCompressedNames.contains(name) || name.startsWith("_")) {
1✔
228
            name = "w" + name;
1✔
229
        }
230
        if (name.endsWith("_")) {
1✔
231
            name = name + "u";
1✔
232
        }
233
        if (name.isEmpty()) {
1✔
234
            name = "empty";
×
235
        }
236
        return name;
1✔
237
    }
238

239
    private final Pattern jassValidName = Pattern.compile("[a-zA-Z][a-zA-Z0-9_]*");
1✔
240

241
    /**
242
     * replaces all invalid characters with underscores
243
     */
244
    private String filterInvalidSymbols(String name) {
245
        if (jassValidName.matcher(name).matches()) {
1✔
246
            return name;
1✔
247
        }
248
        StringBuilder sb = new StringBuilder(name.length());
1✔
249
        for (int i = 0; i < name.length(); i++) {
1✔
250
            char c = name.charAt(i);
1✔
251
            if (!(c >= 'a' && c <= 'z'
1✔
252
                    || c >= 'A' && c <= 'Z'
253
                    || c >= '0' && c <= '9'
254
                    || c == '_')) {
255
                c = '_';
×
256
            }
257
            sb.append(c);
1✔
258
        }
259
        return sb.toString();
1✔
260
    }
261

262
    private boolean isGlobal(ImVar v) {
263
        return globalImVars.contains(v);
1✔
264
    }
265

266

267
    private final Map<ImFunction, JassFunctionOrNative> jassFuncs = Maps.newLinkedHashMap();
1✔
268

269
    public JassFunctionOrNative getJassFuncFor(ImFunction func) {
270
        JassFunctionOrNative f = jassFuncs.get(func);
1✔
271
        if (f == null) {
1✔
272
            if (func.isNative()) {
1✔
273
                f = JassAst.JassNative(func.getName(), JassSimpleVars(), "nothing");
1✔
274
                if (!func.isBj() && !func.isExtern()) {
1✔
275
                    prog.getNatives().add((JassNative) f);
1✔
276
                }
277
            } else {
278
                String name = func.getName();
1✔
279
                // find a unique name, but keep special names 'main' and 'config'
280
                if (!name.equals("main") && !name.equals("config")) {
1✔
281
                    name = getUniqueGlobalName(func.getName());
1✔
282
                }
283
                boolean isCompiletimeNative = func.hasFlag(FunctionFlagEnum.IS_COMPILETIME_NATIVE);
1✔
284
                f = JassFunction(name, JassSimpleVars(), "nothing", JassVars(), JassStatements(), isCompiletimeNative);
1✔
285
                if (!func.isBj() && !func.isExtern()) {
1✔
286
                    prog.getFunctions().add((JassFunction) f);
1✔
287
                }
288
            }
289
            jassFuncs.put(func, f);
1✔
290
        }
291
        return f;
1✔
292
    }
293

294

295
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc