From 08c1447967cf59b4b2aa265b48b604675d8cbc02 Mon Sep 17 00:00:00 2001 From: Astrash Date: Tue, 12 Sep 2023 14:21:21 +1000 Subject: [PATCH] Remove hardcoded print native java call --- .../terra/addons/terrascript/Environment.java | 22 +---- .../terrascript/codegen/NativeFunction.java | 86 +++++++++++++++++++ .../terrascript/codegen/TerraScript.java | 12 --- .../asm/TerraScriptClassGenerator.java | 14 ++- .../semanticanalysis/ScopeAnalyzer.java | 2 +- .../semanticanalysis/TypeChecker.java | 2 +- .../src/test/java/codegen/CodeGenTest.java | 1 + 7 files changed, 99 insertions(+), 40 deletions(-) create mode 100644 common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/NativeFunction.java diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/Environment.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/Environment.java index b0cc564fa..9760a10db 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/Environment.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/Environment.java @@ -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> parameters; + public final List parameters; public final Environment scope; - public Function(Type type, List> parameters, Environment scope) { + public Function(Type type, List parameters, Environment scope) { this.type = type; this.parameters = parameters; this.scope = scope; diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/NativeFunction.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/NativeFunction.java new file mode 100644 index 000000000..15218fbf6 --- /dev/null +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/NativeFunction.java @@ -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 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 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 parameters; + + // TODO - Use reflection to obtain these automatically + public StaticMethodOfStaticField(String fieldOwner, String fieldName, String className, String methodName, String methodDescriptor, + Type returnType, List 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 getParameterTypes() { + return parameters; + } + + } +} diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/TerraScript.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/TerraScript.java index 09db30350..e96f185d8 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/TerraScript.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/TerraScript.java @@ -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 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); - } - }}; } diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/asm/TerraScriptClassGenerator.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/asm/TerraScriptClassGenerator.java index 87675b0c0..2410c761f 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/asm/TerraScriptClassGenerator.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/codegen/asm/TerraScriptClassGenerator.java @@ -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)); diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/ScopeAnalyzer.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/ScopeAnalyzer.java index 5e45a94d6..260f1a0d9 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/ScopeAnalyzer.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/ScopeAnalyzer.java @@ -120,7 +120,7 @@ public class ScopeAnalyzer implements Visitor, Stmt.Visitor { 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) { diff --git a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/TypeChecker.java b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/TypeChecker.java index 06b668ba7..b30394432 100644 --- a/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/TypeChecker.java +++ b/common/addons/structure-terrascript-loader/src/main/java/com/dfsek/terra/addons/terrascript/semanticanalysis/TypeChecker.java @@ -113,7 +113,7 @@ public class TypeChecker implements Visitor, Stmt.Visitor Environment.Symbol.Function signature = expr.getSymbol(); List arguments = expr.arguments.stream().map(a -> a.accept(this)).toList(); - List parameters = signature.parameters.stream().map(Pair::getRight).toList(); + List parameters = signature.parameters; if(arguments.size() != parameters.size()) errorHandler.add(new ParseException( diff --git a/common/addons/structure-terrascript-loader/src/test/java/codegen/CodeGenTest.java b/common/addons/structure-terrascript-loader/src/test/java/codegen/CodeGenTest.java index 41819a608..9fb91389b 100644 --- a/common/addons/structure-terrascript-loader/src/test/java/codegen/CodeGenTest.java +++ b/common/addons/structure-terrascript-loader/src/test/java/codegen/CodeGenTest.java @@ -19,6 +19,7 @@ public class CodeGenTest { @Test public void test() { testValid(""" + printNum(12345); if (1 == 1) print("Dis is true");