Remove hardcoded print native java call

This commit is contained in:
Astrash
2023-09-12 14:21:21 +10:00
parent 37b5a2ec92
commit 08c1447967
7 changed files with 99 additions and 40 deletions

View File

@@ -1,25 +1,17 @@
package com.dfsek.terra.addons.terrascript;
import javax.annotation.Nullable;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import com.dfsek.terra.addons.terrascript.Environment.ScopeException.NonexistentSymbolException;
import com.dfsek.terra.addons.terrascript.Environment.ScopeException.SymbolAlreadyExistsException;
import com.dfsek.terra.addons.terrascript.Environment.ScopeException.SymbolTypeMismatchException;
import com.dfsek.terra.addons.terrascript.Environment.Symbol.Function;
import com.dfsek.terra.addons.terrascript.Environment.Symbol.Variable;
import com.dfsek.terra.addons.terrascript.codegen.TerraScript;
import com.dfsek.terra.api.util.generic.pair.Pair;
import com.dfsek.terra.addons.terrascript.codegen.NativeFunction;
public class Environment {
@@ -45,13 +37,7 @@ public class Environment {
this.index = index;
this.name = String.join("_", getNestedIndexes().stream().map(Object::toString).toList());
// Populate symbol tables with built-in Java implemented methods
TerraScript.BUILTIN_FUNCTIONS.forEach((name, method) -> symbolTable
.put(name,
new Function(
Type.from(method.getReturnType()).orElseThrow(() -> new RuntimeException("")),
// Map Java classes to TerraScript types
IntStream.range(0, method.getParameterCount()).mapToObj(i -> Pair.of("param" + i, Type.from(method.getParameterTypes()[i]).orElseThrow(() -> new RuntimeException("")))).toList(),
this)));
NativeFunction.BUILTIN_FUNCTIONS.forEach((name, function) -> symbolTable.put(name, new Symbol.Function(function.getReturnType(), function.getParameterTypes(), this)));
}
public static Environment global() {
@@ -133,11 +119,11 @@ public class Environment {
public final Type type;
public final List<Pair<String, Type>> parameters;
public final List<Type> parameters;
public final Environment scope;
public Function(Type type, List<Pair<String, Type>> parameters, Environment scope) {
public Function(Type type, List<Type> parameters, Environment scope) {
this.type = type;
this.parameters = parameters;
this.scope = scope;

View File

@@ -0,0 +1,86 @@
package com.dfsek.terra.addons.terrascript.codegen;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.dfsek.terra.addons.terrascript.Type;
public interface NativeFunction {
Map<String, NativeFunction> BUILTIN_FUNCTIONS = new HashMap<>() {{
put("print", new StaticMethodOfStaticField(
"java/lang/System",
"out",
"java/io/PrintStream",
"println",
"(Ljava/lang/String;)V",
Type.VOID,
List.of(Type.STRING))
);
put("printNum", new StaticMethodOfStaticField(
"java/lang/System",
"out",
"java/io/PrintStream",
"println",
"(D)V",
Type.VOID,
List.of(Type.NUMBER))
);
}};
void pushInstance(MethodVisitor method);
void callMethod(MethodVisitor method);
Type getReturnType();
List<Type> getParameterTypes();
class StaticMethodOfStaticField implements NativeFunction {
private final String fieldOwner;
private final String fieldName;
private final String className;
private final String methodName;
private final String methodDescriptor;
private final Type returnType;
private final List<Type> parameters;
// TODO - Use reflection to obtain these automatically
public StaticMethodOfStaticField(String fieldOwner, String fieldName, String className, String methodName, String methodDescriptor,
Type returnType, List<Type> parameters) {
this.fieldOwner = fieldOwner;
this.fieldName = fieldName;
this.className = className;
this.methodName = methodName;
this.methodDescriptor = methodDescriptor;
this.returnType = returnType;
this.parameters = parameters;
}
@Override
public void pushInstance(MethodVisitor method) {
method.visitFieldInsn(Opcodes.GETSTATIC, fieldOwner, fieldName, "L" + className + ";");
}
@Override
public void callMethod(MethodVisitor method) {
method.visitMethodInsn(Opcodes.INVOKEVIRTUAL, className, methodName, methodDescriptor, false);
}
@Override
public Type getReturnType() {
return returnType;
}
@Override
public List<Type> getParameterTypes() {
return parameters;
}
}
}

View File

@@ -1,20 +1,8 @@
package com.dfsek.terra.addons.terrascript.codegen;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public interface TerraScript {
void execute();
Map<String, Method> BUILTIN_FUNCTIONS = new HashMap<>() {{
try {
put("print", System.out.getClass().getMethod("println", String.class));
put("printNum", System.out.getClass().getMethod("println", double.class));
} catch(NoSuchMethodException e) {
throw new RuntimeException(e);
}
}};
}

View File

@@ -23,6 +23,7 @@ import com.dfsek.terra.addons.terrascript.ast.TypedStmt.Return;
import com.dfsek.terra.addons.terrascript.ast.TypedStmt.VariableDeclaration;
import com.dfsek.terra.addons.terrascript.ast.TypedStmt.While;
import com.dfsek.terra.addons.terrascript.codegen.NativeFunction;
import com.dfsek.terra.addons.terrascript.codegen.TerraScript;
import com.dfsek.terra.addons.terrascript.exception.CompilerBugException;
import com.dfsek.terra.addons.terrascript.util.ASMUtil;
@@ -39,7 +40,6 @@ import org.objectweb.asm.commons.LocalVariablesSorter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
@@ -217,13 +217,11 @@ public class TerraScriptClassGenerator {
@Override
public Void visitCallTypedExpr(Call expr) {
if (TerraScript.BUILTIN_FUNCTIONS.containsKey(expr.identifier)) {
Method m = TerraScript.BUILTIN_FUNCTIONS.get(expr.identifier);
if (expr.identifier.equals("print")) { // TODO - remove quick dirty print function call
method.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
expr.arguments.get(0).accept(this);
method.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
}
if (NativeFunction.BUILTIN_FUNCTIONS.containsKey(expr.identifier)) {
NativeFunction function = NativeFunction.BUILTIN_FUNCTIONS.get(expr.identifier);
function.pushInstance(method);
expr.arguments.forEach(a -> a.accept(this));
function.callMethod(method);
return null;
}
expr.arguments.forEach(a -> a.accept(this));

View File

@@ -120,7 +120,7 @@ public class ScopeAnalyzer implements Visitor<Void>, Stmt.Visitor<Void> {
stmt.body.accept(this);
currentScope = currentScope.outer();
try {
Symbol.Function symbol = new Symbol.Function(stmt.returnType, stmt.parameters, currentScope);
Symbol.Function symbol = new Symbol.Function(stmt.returnType, stmt.parameters.stream().map(Pair::getRight).toList(), currentScope);
stmt.setSymbol(symbol);
currentScope.put(stmt.identifier, symbol);
} catch(Environment.ScopeException.SymbolAlreadyExistsException e) {

View File

@@ -113,7 +113,7 @@ public class TypeChecker implements Visitor<TypedExpr>, Stmt.Visitor<TypedStmt>
Environment.Symbol.Function signature = expr.getSymbol();
List<TypedExpr> arguments = expr.arguments.stream().map(a -> a.accept(this)).toList();
List<Type> parameters = signature.parameters.stream().map(Pair::getRight).toList();
List<Type> parameters = signature.parameters;
if(arguments.size() != parameters.size())
errorHandler.add(new ParseException(

View File

@@ -19,6 +19,7 @@ public class CodeGenTest {
@Test
public void test() {
testValid("""
printNum(12345);
if (1 == 1) print("Dis is true");