Skip to content

Commit 93c17fa

Browse files
committed
refactor: jvm compiler
1 parent 9b49c96 commit 93c17fa

File tree

54 files changed

+1131
-319
lines changed

Some content is hidden

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

54 files changed

+1131
-319
lines changed

backend/common/src/main/java/com/github/kayjamlang/backend/tree/KayJamExpressionVisitor.java renamed to backend/common/src/main/java/com/github/kayjamlang/backend/tree/KayJamStatementVisitor.java

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,53 @@
11
package com.github.kayjamlang.backend.tree;
22

3-
import com.github.kayjamlang.core.KayJamFile;
4-
import com.github.kayjamlang.core.statements.FunctionStatement;
53
import com.github.kayjamlang.core.expressions.*;
64
import com.github.kayjamlang.core.expressions.loops.*;
5+
import com.github.kayjamlang.core.statements.Statement;
76

8-
public abstract class KayJamExpressionVisitor<T> {
9-
public T visit(Expression expression) {
10-
if(expression instanceof AccessExpression) {
11-
return visitAccessExpression((AccessExpression) expression);
12-
}else if(expression instanceof ArrayExpression) {
13-
return visitArrayExpression((ArrayExpression) expression);
14-
}else if(expression instanceof AssertNullExpression) {
15-
return visitAssertNullExpression((AssertNullExpression) expression);
16-
}else if(expression instanceof CallOrCreateExpression) {
17-
return visitCallOrCreateExpression((CallOrCreateExpression) expression);
18-
}else if(expression instanceof CastExpression) {
19-
return visitCastExpression((CastExpression) expression);
20-
}else if(expression instanceof CompanionAccessExpression) {
21-
return visitCompanionAccessExpression((CompanionAccessExpression) expression);
22-
}else if(expression instanceof FunctionRefExpression) {
23-
return visitFunctionRefExpression((FunctionRefExpression) expression);
24-
}else if(expression instanceof GetExpression) {
25-
return visitGetExpression((GetExpression) expression);
26-
}else if(expression instanceof IfExpression) {
27-
return visitIfExpression((IfExpression) expression);
28-
}else if(expression instanceof IsExpression) {
29-
return visitIsExpression((IsExpression) expression);
30-
}else if(expression instanceof NegationExpression) {
31-
return visitNegationExpression((NegationExpression) expression);
32-
}else if(expression instanceof BitwiseComplementExpression) {
33-
return visitBitwiseComplementExpression((BitwiseComplementExpression) expression);
34-
}else if(expression instanceof OperationExpression) {
35-
return visitOperationExpression((OperationExpression) expression);
36-
}else if(expression instanceof ReturnExpression) {
37-
return visitReturnExpression((ReturnExpression) expression);
38-
}else if(expression instanceof ValueExpression) {
39-
return visitValueExpression((ValueExpression) expression);
40-
}else if(expression instanceof VariableExpression) {
41-
return visitVariableExpression((VariableExpression) expression);
42-
}else if(expression instanceof VariableLinkExpression) {
43-
return visitVariableLinkExpression((VariableLinkExpression) expression);
44-
}else if(expression instanceof ForExpression) {
45-
return visitForExpression((ForExpression) expression);
46-
}else if(expression instanceof WhileExpression) {
47-
return visitWhileExpression((WhileExpression) expression);
7+
public abstract class KayJamStatementVisitor<T> {
8+
public T visit(Statement statement) {
9+
if(statement instanceof AccessExpression) {
10+
return visitAccessExpression((AccessExpression) statement);
11+
}else if(statement instanceof ArrayExpression) {
12+
return visitArrayExpression((ArrayExpression) statement);
13+
}else if(statement instanceof AssertNullExpression) {
14+
return visitAssertNullExpression((AssertNullExpression) statement);
15+
}else if(statement instanceof CallOrCreateExpression) {
16+
return visitCallOrCreateExpression((CallOrCreateExpression) statement);
17+
}else if(statement instanceof CastExpression) {
18+
return visitCastExpression((CastExpression) statement);
19+
}else if(statement instanceof CompanionAccessExpression) {
20+
return visitCompanionAccessExpression((CompanionAccessExpression) statement);
21+
}else if(statement instanceof FunctionRefExpression) {
22+
return visitFunctionRefExpression((FunctionRefExpression) statement);
23+
}else if(statement instanceof GetExpression) {
24+
return visitGetExpression((GetExpression) statement);
25+
}else if(statement instanceof IfExpression) {
26+
return visitIfExpression((IfExpression) statement);
27+
}else if(statement instanceof IsExpression) {
28+
return visitIsExpression((IsExpression) statement);
29+
}else if(statement instanceof NegationExpression) {
30+
return visitNegationExpression((NegationExpression) statement);
31+
}else if(statement instanceof BitwiseComplementExpression) {
32+
return visitBitwiseComplementExpression((BitwiseComplementExpression) statement);
33+
}else if(statement instanceof OperationExpression) {
34+
return visitOperationExpression((OperationExpression) statement);
35+
}else if(statement instanceof ReturnExpression) {
36+
return visitReturnExpression((ReturnExpression) statement);
37+
}else if(statement instanceof ValueExpression) {
38+
return visitValueExpression((ValueExpression) statement);
39+
}else if(statement instanceof VariableExpression) {
40+
return visitVariableExpression((VariableExpression) statement);
41+
}else if(statement instanceof VariableLinkExpression) {
42+
return visitVariableLinkExpression((VariableLinkExpression) statement);
43+
}else if(statement instanceof ForExpression) {
44+
return visitForExpression((ForExpression) statement);
45+
}else if(statement instanceof WhileExpression) {
46+
return visitWhileExpression((WhileExpression) statement);
4847
}
4948

5049

51-
throw new IllegalStateException("Not implemented yet: "+expression.getClass().getSimpleName());
50+
throw new IllegalStateException("Not implemented yet: "+statement.getClass().getSimpleName());
5251
}
5352

5453
public abstract T visitAccessExpression(AccessExpression expression);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.github.kayjamlang.backend.jvm;
2+
3+
import com.github.kayjamlang.backend.jvm.types.ClassJVMType;
4+
5+
public interface ClasspathClassProvider {
6+
ClassJVMType loadClass(SymbolRegistry symbolRegistry, String javaFQN);
7+
}

backend/jvm/src/main/java/com/github/kayjamlang/backend/jvm/JVMBackendCompiler.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,24 @@
44
import com.github.kayjamlang.backend.IBackendCompiler;
55
import com.github.kayjamlang.backend.IOptions;
66
import com.github.kayjamlang.backend.jvm.generators.JVMCodeGenerator;
7+
import com.github.kayjamlang.backend.jvm.impl.ASMClasspathClassProviderImpl;
8+
import com.github.kayjamlang.backend.jvm.passes.SuperclassResolutionPass;
9+
import com.github.kayjamlang.backend.jvm.passes.TypeInferencePass;
710
import com.github.kayjamlang.backend.tree.KayJamFileTree;
811
import com.github.kayjamlang.core.KayJamFile;
912
import com.github.kayjamlang.core.exceptions.KayJamLexerException;
1013
import com.github.kayjamlang.core.exceptions.KayJamParserException;
14+
import com.github.kayjamlang.core.statements.ClassStatement;
1115
import org.objectweb.asm.ClassWriter;
1216

1317
import java.io.File;
1418
import java.io.FileOutputStream;
1519
import java.io.IOException;
20+
import java.util.ArrayList;
21+
import java.util.Arrays;
1622
import java.util.List;
1723
import java.util.Map;
24+
import java.util.stream.Collectors;
1825

1926
public class JVMBackendCompiler implements IBackendCompiler {
2027
public static final JVMBackendCompiler INSTANCE = new JVMBackendCompiler();
@@ -26,8 +33,27 @@ public IBackendOptions createOptionsClass() {
2633

2734
@Override
2835
public void compile(IOptions options) throws KayJamLexerException, KayJamParserException, IOException {
29-
JVMCodeGenerator codeGenerator = new JVMCodeGenerator();
36+
String classpath = System.getProperty("java.class.path");
37+
List<File> classpathFiles = classpath == null || classpath.isEmpty() ?
38+
List.of():
39+
Arrays.stream(classpath.split(File.pathSeparator))
40+
.map(File::new)
41+
.toList();
42+
3043
List<KayJamFile> files = KayJamFileTree.loadFilesFromPath(options.getInputDir().toPath());
44+
SymbolRegistry symbolRegistry = new SymbolRegistry(new ASMClasspathClassProviderImpl(classpathFiles));
45+
SignatureCollectorVisitor signatureCollectorVisitor = new SignatureCollectorVisitor(symbolRegistry);
46+
for (KayJamFile file : files) {
47+
signatureCollectorVisitor.visitKayJamFile(file);
48+
}
49+
50+
SuperclassResolutionPass superclassResolver = new SuperclassResolutionPass(symbolRegistry);
51+
superclassResolver.resolveSuperclassesAndInterfaces();
52+
53+
TypeInferencePass typeInference = new TypeInferencePass(symbolRegistry);
54+
typeInference.inferTypes();
55+
56+
JVMCodeGenerator codeGenerator = new JVMCodeGenerator(symbolRegistry);
3157
for (KayJamFile file : files) {
3258
for (Map.Entry<String, byte[]> entry : codeGenerator.generate(file).entrySet()) {
3359
File outputFile = getFile(options, entry);
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package com.github.kayjamlang.backend.jvm;
2+
3+
import com.github.kayjamlang.backend.jvm.types.ClassJVMType;
4+
import com.github.kayjamlang.backend.jvm.types.data.ConstInfo;
5+
import com.github.kayjamlang.backend.jvm.types.data.FunctionInfo;
6+
import com.github.kayjamlang.backend.jvm.types.data.PropertyInfo;
7+
import com.github.kayjamlang.backend.jvm.utils.NamesUtils;
8+
import com.github.kayjamlang.core.KayJamFile;
9+
import com.github.kayjamlang.core.Type;
10+
import com.github.kayjamlang.core.expressions.data.Constant;
11+
import com.github.kayjamlang.core.expressions.data.Property;
12+
import com.github.kayjamlang.core.statements.ClassStatement;
13+
import com.github.kayjamlang.core.statements.FunctionStatement;
14+
import com.github.kayjamlang.core.statements.ObjectStatement;
15+
16+
import java.util.List;
17+
import java.util.stream.Collectors;
18+
19+
public class SignatureCollectorVisitor {
20+
private final SymbolRegistry symbolRegistry;
21+
private ClassJVMType currentClass = null;
22+
private KayJamFile currentFileContext = null;
23+
24+
public SignatureCollectorVisitor(SymbolRegistry symbolRegistry) {
25+
this.symbolRegistry = symbolRegistry;
26+
}
27+
28+
public void visitKayJamFile(KayJamFile file) {
29+
this.currentFileContext = file;
30+
31+
for (ClassStatement classNode : file.classes) {
32+
visitClassStatement(classNode);
33+
}
34+
35+
// TODO: Обработать глобальные функции, константы и переменные, если они есть на уровне файла
36+
// ВАЖНО: Если есть глобальные переменные/константы, их типы также нужно будет разрешать
37+
// с использованием currentFileContext.
38+
39+
this.currentFileContext = null;
40+
}
41+
42+
public void visitClassStatement(ClassStatement classNode) {
43+
if (currentFileContext == null) {
44+
throw new IllegalStateException("ClassStatement visited without KayJamFile context.");
45+
}
46+
47+
String classFQN = NamesUtils.getFQNFromNamespaceAndSimpleName(currentFileContext.namespace, classNode.name);
48+
ClassJVMType classInfo = new ClassJVMType(
49+
classFQN,
50+
classNode instanceof ObjectStatement ?
51+
ClassJVMType.ClassType.OBJECT:
52+
ClassJVMType.ClassType.CLASS
53+
);
54+
classInfo.fileContext = currentFileContext;
55+
56+
if (classNode.parentClasses != null && !classNode.parentClasses.isEmpty()) {
57+
classInfo.parentTypes.addAll(classNode.parentClasses);
58+
}
59+
60+
this.symbolRegistry.registerClass(classInfo);
61+
this.currentClass = classInfo;
62+
63+
for (Constant constant : classNode.constants) {
64+
visitConstant(constant);
65+
}
66+
67+
for (Property property : classNode.properties) {
68+
visitProperty(property);
69+
}
70+
71+
for (FunctionStatement function : classNode.functions) {
72+
visitFunctionStatement(function);
73+
}
74+
75+
this.currentClass = null;
76+
}
77+
78+
public void visitConstant(Constant constant) {
79+
if (currentClass == null) {
80+
throw new IllegalStateException("Constant out class!");
81+
}
82+
83+
ConstInfo constInfo = new ConstInfo(
84+
constant.name, "any", constant.accessType
85+
);
86+
87+
currentClass.constants.put(constInfo.name, constInfo);
88+
}
89+
90+
91+
public void visitProperty(Property property) {
92+
if (currentClass == null) {
93+
throw new IllegalStateException("Property out class!");
94+
}
95+
96+
PropertyInfo propertyInfo = new PropertyInfo(
97+
property.name, null, property.accessType, property
98+
);
99+
100+
currentClass.properties.put(propertyInfo.name, propertyInfo);
101+
}
102+
103+
public void visitFunctionStatement(FunctionStatement funcNode) {
104+
if (currentClass == null) {
105+
throw new IllegalStateException("Method out class!");
106+
}
107+
108+
List<Type> arguments = funcNode.arguments.stream()
109+
.map(it -> it.type)
110+
.collect(Collectors.toList());
111+
112+
FunctionInfo methodInfo = new FunctionInfo(funcNode.name, arguments, funcNode.returnType, funcNode.accessType);
113+
currentClass.functions.put(methodInfo.name, methodInfo);
114+
}
115+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package com.github.kayjamlang.backend.jvm;
2+
3+
import com.github.kayjamlang.backend.jvm.types.*;
4+
import com.github.kayjamlang.backend.jvm.utils.NamesUtils;
5+
import com.github.kayjamlang.core.KayJamFile;
6+
import com.github.kayjamlang.core.Type;
7+
8+
import java.util.HashMap;
9+
import java.util.List;
10+
import java.util.Map;
11+
12+
public class SymbolRegistry {
13+
public final Map<String, ClassJVMType> classes = new HashMap<>();
14+
public final Map<String, ClassJVMType> externalClassesCache = new HashMap<>();
15+
16+
private final ClasspathClassProvider classpathClassProvider;
17+
18+
public SymbolRegistry(ClasspathClassProvider classpathClassProvider) {
19+
this.classpathClassProvider = classpathClassProvider;
20+
}
21+
22+
23+
public void registerClass(ClassJVMType classInfo) {
24+
if (classes.containsKey(classInfo.name)) {
25+
throw new IllegalStateException("Class with FQKN '" + classInfo.name + "' already registered.");
26+
}
27+
28+
classes.put(classInfo.name, classInfo);
29+
}
30+
31+
public ClassJVMType findClass(String name) {
32+
ClassJVMType classJVMType = classes.get(name);
33+
if(classJVMType == null) {
34+
classJVMType = externalClassesCache.get(name);
35+
}
36+
37+
if (classJVMType != null) {
38+
return classJVMType;
39+
}
40+
41+
String javaFQN = NamesUtils.convertSomeFQNToJavaFQN(name);
42+
ClassJVMType externalClassInfo = classpathClassProvider.loadClass(this, javaFQN);
43+
if (externalClassInfo != null) {
44+
externalClassesCache.put(name, externalClassInfo);
45+
return externalClassInfo;
46+
}
47+
48+
return null;
49+
}
50+
51+
public JVMType resolveType(Type type, KayJamFile fileContext) {
52+
if(type.equals(Type.INTEGER)) return IntJVMType.INSTANCE;
53+
else if(type.equals(Type.DOUBLE)) return DoubleJVMType.INSTANCE;
54+
else if(type.equals(Type.LONG)) return LongJVMType.INSTANCE;
55+
else if(type.equals(Type.STRING)) return StringJVMType.INSTANCE;
56+
else if(type.equals(Type.BOOLEAN)) return BooleanJVMType.INSTANCE;
57+
else if(type.equals(Type.ANY)) return AnyJVMType.INSTANCE;
58+
else if(type.equals(Type.VOID) || type.equals(Type.NOTHING)) return VoidJVMType.INSTANCE;
59+
60+
String resolvedKayJamFQN = resolveKayJamName(type.name, fileContext.namespace, fileContext.usages);
61+
ClassJVMType classJVMType = findClass(resolvedKayJamFQN);
62+
if(classJVMType == null) {
63+
throw new IllegalArgumentException(
64+
"Unknown class or type '" + type.name + "' (resolved to '" + resolvedKayJamFQN + "') " +
65+
"in file '" + fileContext.path + "' namespace '" + fileContext.namespace + "'."
66+
);
67+
}
68+
69+
return classJVMType;
70+
}
71+
72+
public String resolveKayJamName(String name, String currentNamespace, List<KayJamFile.Usage> usages) {
73+
if (name.startsWith("\\")) {
74+
if (findClass(name) != null) {
75+
return name;
76+
}
77+
}
78+
79+
for (KayJamFile.Usage usage : usages) {
80+
String aliasToMatch = usage.typealias;
81+
if (aliasToMatch == null || aliasToMatch.isEmpty()) {
82+
aliasToMatch = NamesUtils.getSimpleNameFromFQN(usage.usedImport.name);
83+
}
84+
85+
if (name.equals(aliasToMatch)) {
86+
String resolvedUsedImportFQN = resolveKayJamName(usage.usedImport.name, null, usages);
87+
if (resolvedUsedImportFQN != null && findClass(resolvedUsedImportFQN) != null) {
88+
return resolvedUsedImportFQN;
89+
}
90+
}
91+
}
92+
93+
String candidateFQN;
94+
if (currentNamespace != null && !currentNamespace.isEmpty() && !currentNamespace.equals("\\")) {
95+
candidateFQN = currentNamespace + name;
96+
} else {
97+
candidateFQN = name;
98+
}
99+
100+
if (findClass(candidateFQN) != null) {
101+
return candidateFQN;
102+
}
103+
104+
return name;
105+
}
106+
}

0 commit comments

Comments
 (0)