From 0211c577e3af157043416a806dafe4adc4386f79 Mon Sep 17 00:00:00 2001 From: Pieter12345 Date: Wed, 27 Mar 2024 04:37:56 +0100 Subject: [PATCH] Support FQCN types in MethodScript code Support FQCN in `ms.lang.int @a = 1;`, `try {} catch (ms.lang.Exception @ex) {}` and `proc _a(ms.lang.int @a) {}` syntax. Note that `assign(ms.lang.int, @a, 1)` is not supported due to `assign.postParseRewrite()` running after bare string warning/error generation. --- .../laytonsmith/core/functions/Compiler.java | 105 ++++++++++++++---- .../core/functions/DataHandling.java | 10 ++ 2 files changed, 96 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/laytonsmith/core/functions/Compiler.java b/src/main/java/com/laytonsmith/core/functions/Compiler.java index 258821b07..b4a41c820 100644 --- a/src/main/java/com/laytonsmith/core/functions/Compiler.java +++ b/src/main/java/com/laytonsmith/core/functions/Compiler.java @@ -6,6 +6,7 @@ import com.laytonsmith.annotations.hide; import com.laytonsmith.annotations.noboilerplate; import com.laytonsmith.annotations.noprofile; +import com.laytonsmith.core.FullyQualifiedClassName; import com.laytonsmith.core.MSVersion; import com.laytonsmith.core.Optimizable; import com.laytonsmith.core.ParseTree; @@ -28,6 +29,7 @@ import com.laytonsmith.core.constructs.Target; import com.laytonsmith.core.constructs.Token; import com.laytonsmith.core.environments.Environment; +import com.laytonsmith.core.exceptions.CRE.CRECastException; import com.laytonsmith.core.exceptions.CRE.CRENotFoundException; import com.laytonsmith.core.exceptions.CRE.CREThrowable; import com.laytonsmith.core.functions.DataHandling._string; @@ -464,8 +466,9 @@ public static ParseTree rewrite(List list, boolean returnSConcat, // Look for typed assignments for(int k = 0; k < list.size(); k++) { if(list.get(k).getData().equals(CVoid.VOID) || list.get(k).getData().isInstanceOf(CClassType.TYPE) - || (list.get(k).getData().getClass().equals(CBareString.class) - && list.get(k).getData().val().matches("[a-zA-Z0-9\\-_\\.]+"))) { + || (list.get(k).getData().getClass().equals(CBareString.class)) + || (list.get(k).getData() instanceof CFunction + && list.get(k).getData().val().equals(concat.NAME))) { if(k == list.size() - 1) { // This is not a typed assignment break; @@ -484,17 +487,16 @@ public static ParseTree rewrite(List list, boolean returnSConcat, throw new ConfigCompileException("Variables may not be of type void", list.get(k + 1).getTarget()); } - ParseTree type = list.remove(k); + ParseTree typeNode = list.remove(k); - // Convert bare string to type reference as it is used like that in syntax. - // Type name regex is applied above. - if(type.getData().getClass().equals(CBareString.class)) { - type = __type_ref__.createASTNode( - type.getData().val(), type.getTarget(), type.getFileOptions()); + // Convert bare string or concat() to type reference as it is used like that in syntax. + ParseTree typeRefNode = __type_ref__.createFromBareStringOrConcats(typeNode); + if(typeRefNode != null) { + typeNode = typeRefNode; } List children = list.get(k).getChildren(); - children.add(0, type); + children.add(0, typeNode); list.get(k).setChildren(children); break; default: @@ -505,11 +507,10 @@ public static ParseTree rewrite(List list, boolean returnSConcat, ParseTree node = new ParseTree(new CFunction(assign.NAME, list.get(k + 1).getTarget()), list.get(k).getFileOptions()); ParseTree typeNode = list.get(k); - // Convert bare string to type reference as it is used like that in syntax. - // Type name regex is applied above. - if(typeNode.getData().getClass().equals(CBareString.class)) { - typeNode = __type_ref__.createASTNode( - typeNode.getData().val(), typeNode.getTarget(), typeNode.getFileOptions()); + // Convert bare string or concat() to type reference as it is used like that in syntax. + ParseTree typeRefNode = __type_ref__.createFromBareStringOrConcats(typeNode); + if(typeRefNode != null) { + typeNode = typeRefNode; } node.addChild(typeNode); @@ -521,11 +522,10 @@ public static ParseTree rewrite(List list, boolean returnSConcat, ParseTree node = new ParseTree(new CFunction(assign.NAME, list.get(k + 1).getTarget()), list.get(k).getFileOptions()); ParseTree typeNode = list.get(k); - // Convert bare string to type reference as it is used like that in syntax. - // Type name regex is applied above. - if(typeNode.getData().getClass().equals(CBareString.class)) { - typeNode = __type_ref__.createASTNode( - typeNode.getData().val(), typeNode.getTarget(), typeNode.getFileOptions()); + // Convert bare string or concat() to type reference as it is used like that in syntax. + ParseTree typeRefNode = __type_ref__.createFromBareStringOrConcats(typeNode); + if(typeRefNode != null) { + typeNode = typeRefNode; } ParseTree labelNode = new ParseTree(new CLabel(node.getData()), typeNode.getFileOptions()); @@ -750,6 +750,8 @@ public static class __type_ref__ extends DummyFunction implements Optimizable { public static final String NAME = "__type_ref__"; + public static final String TYPE_REGEX = "[a-zA-Z0-9\\-_\\.]+"; + @Override public String getName() { return NAME; @@ -788,6 +790,71 @@ public static ParseTree createASTNode(String typeName, Target t, FileOptions fil return node; } + public static ParseTree createFromBareStringOrConcats(ParseTree typeNode) { + + // Convert bare string types to __type_ref__. + if(typeNode.getData().getClass().equals(CBareString.class) + && typeNode.getData().val().matches(TYPE_REGEX)) { + return __type_ref__.createASTNode( + typeNode.getData().val(), typeNode.getTarget(), typeNode.getFileOptions()); + } + + // Convert concatenated bare string types such as "concat(concat(ms, lang), int)" to "ms.lang.int". + if(typeNode.getData() instanceof CFunction + && typeNode.getData().val().equals(concat.NAME) + && typeNode.getChildren().size() == 2) { + String typeName = null; + ParseTree node = typeNode; + while(true) { + ParseTree child1 = node.getChildAt(0); + ParseTree child2 = node.getChildAt(1); + + if(child2.getData() instanceof CBareString) { + typeName = child2.getData().val() + (typeName == null ? "" : "." + typeName); + } else if(child2.getData() instanceof CClassType) { + // "ms.lang.int" will have "int" parsed as CClassType. Convert to original string. + String[] cClassTypeStrSplit = child2.getData().val().split("\\."); + typeName = cClassTypeStrSplit[cClassTypeStrSplit.length - 1] + + (typeName == null ? "" : "." + typeName); + } else { + return null; + } + if(child1.getData() instanceof CBareString) { + typeName = child1.getData().val() + "." + typeName; + break; + } else if(child1.getData() instanceof CClassType) { + // "int.my.type" will have "int" parsed as CClassType. Convert to original string. + String[] cClassTypeStrSplit = child1.getData().val().split("\\."); + typeName = cClassTypeStrSplit[cClassTypeStrSplit.length - 1] + "." + typeName; + break; + } else if(!(child1.getData() instanceof CFunction) || !concat.NAME.equals(child1.getData().val()) + || child1.getChildren().size() != 2) { + return null; + } + node = child1; + } + if(typeName.matches(TYPE_REGEX)) { + return __type_ref__.createASTNode(typeName, typeNode.getTarget(), typeNode.getFileOptions()); + } + } + + return null; + } + + @Override + public ParseTree postParseRewrite(ParseTree ast, Environment env, + Set> envs, Set exceptions) { + + // Attempt to resolve type reference to CClassType. + String typeName = ast.getChildAt(0).getData().val(); + try { + CClassType classType = CClassType.get(FullyQualifiedClassName.forName(typeName, ast.getTarget(), env)); + return new ParseTree(classType, ast.getFileOptions()); + } catch (CRECastException | ClassNotFoundException e) { + return null; + } + } + @Override public Set optimizationOptions() { return EnumSet.of(OptimizationOption.OPTIMIZE_DYNAMIC); diff --git a/src/main/java/com/laytonsmith/core/functions/DataHandling.java b/src/main/java/com/laytonsmith/core/functions/DataHandling.java index 1d8771b44..12b35b2d5 100644 --- a/src/main/java/com/laytonsmith/core/functions/DataHandling.java +++ b/src/main/java/com/laytonsmith/core/functions/DataHandling.java @@ -694,6 +694,16 @@ public ParseTree postParseRewrite(ParseTree ast, Environment env, return tree; } } + + // Convert concatenated types such as "concat(concat(ms, lang), int)" to "ms.lang.int". + if(children.size() == 3) { + ParseTree typeRefNode = __type_ref__.createFromBareStringOrConcats(children.get(0)); + if(typeRefNode != null) { + children.set(0, typeRefNode); + return ast; + } + } + return null; }