From 9539af9dd327d21c4ba86b44407da235382851f7 Mon Sep 17 00:00:00 2001 From: Hugo Guerrier Date: Mon, 16 Sep 2024 14:45:07 +0200 Subject: [PATCH] Rewrite all built-in methods as specialized nodes --- .../built_ins/methods/ListMethods.java | 109 +++--- .../built_ins/methods/NodeMethods.java | 51 +-- .../built_ins/methods/StrMethods.java | 309 +++++++++--------- .../built_ins/methods/TokenMethods.java | 123 ++++--- .../interpreter/sublist_builtin/test.out | 4 +- 5 files changed, 325 insertions(+), 271 deletions(-) diff --git a/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/ListMethods.java b/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/ListMethods.java index 319660fe2..482074a2b 100644 --- a/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/ListMethods.java +++ b/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/ListMethods.java @@ -10,13 +10,14 @@ import com.adacore.lkql_jit.LKQLTypeSystemGen; import com.adacore.lkql_jit.built_ins.BuiltInMethodFactory; import com.adacore.lkql_jit.built_ins.BuiltInsHolder; +import com.adacore.lkql_jit.built_ins.SpecializedBuiltInBody; import com.adacore.lkql_jit.built_ins.functions.UniqueFunction; import com.adacore.lkql_jit.exception.LKQLRuntimeException; import com.adacore.lkql_jit.nodes.expressions.Expr; -import com.adacore.lkql_jit.nodes.expressions.FunCall; import com.adacore.lkql_jit.runtime.values.lists.LKQLList; import com.adacore.lkql_jit.utils.LKQLTypesHelper; -import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; import java.util.Arrays; import java.util.Map; @@ -27,51 +28,6 @@ */ public class ListMethods { - private static final Map.Entry sublistFunction = - createMethod( - "sublist", - "Return a sublist of `list` from `low_bound` to `high_bound`", - new String[] {"low_bound", "high_bound"}, - new Expr[] {null, null}, - (VirtualFrame frame, FunCall call) -> { - var args = frame.getArguments(); - - if (!LKQLTypeSystemGen.isLKQLList(args[0])) { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.LKQL_LIST, - LKQLTypesHelper.fromJava(args[0]), - call.getArgList().getArgs()[0]); - } - - if (!LKQLTypeSystemGen.isLong(args[1])) { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.LKQL_INTEGER, - LKQLTypesHelper.fromJava(args[1]), - call.getArgList().getArgs()[1]); - } - - if (!LKQLTypeSystemGen.isLong(args[2])) { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.LKQL_INTEGER, - LKQLTypesHelper.fromJava(args[2]), - call.getArgList().getArgs()[2]); - } - - LKQLList list = LKQLTypeSystemGen.asLKQLList(args[0]); - long lowBound = LKQLTypeSystemGen.asLong(args[1]); - long highBound = LKQLTypeSystemGen.asLong(args[2]); - - if (lowBound < 1) { - throw LKQLRuntimeException.invalidIndex((int) lowBound, call); - } else if (highBound > list.getContent().length) { - throw LKQLRuntimeException.invalidIndex((int) highBound, call); - } - - return new LKQLList( - Arrays.copyOfRange( - list.getContent(), (int) lowBound - 1, (int) highBound)); - }); - public static final Map methods = BuiltInsHolder.combine( Map.ofEntries( @@ -79,6 +35,63 @@ public class ListMethods { UniqueFunction.NAME, BuiltInMethodFactory.fromFunctionValue( UniqueFunction.getValue(), true)), - sublistFunction), + createMethod( + "sublist", + "Return a sublist of `list` from " + + "`low_bound` to `high_bound`", + new String[] {"low_bound", "high_bound"}, + new Expr[] {null, null}, + new SpecializedBuiltInBody<>( + ListMethodsFactory.SublistExprNodeGen.create()) { + @Override + protected Object dispatch(Object[] args) { + return this.specializedNode.executeSublist( + LKQLTypeSystemGen.asLKQLList(args[0]), + args[1], + args[2]); + } + })), IterableMethods.methods); + + // ----- Inner classes ----- + + /** Expression of the "sublist" method. */ + public abstract static class SublistExpr extends SpecializedBuiltInBody.SpecializedBuiltInNode { + + public abstract LKQLList executeSublist(LKQLList list, Object low, Object high); + + @Specialization + protected LKQLList onValid(LKQLList list, long low, long high) { + // Offset the low bound by 1 since LKQL is 1-indexed + low = low - 1; + + // Check bounds validity + if (low < 0) { + throw LKQLRuntimeException.invalidIndex((int) low + 1, body.argNode(0)); + } else if (high > list.getContent().length) { + throw LKQLRuntimeException.invalidIndex((int) high, body.argNode(1)); + } + + // Return the sublist + return new LKQLList(Arrays.copyOfRange(list.getContent(), (int) low, (int) high)); + } + + @Specialization + protected LKQLList onInvalidHigh( + @SuppressWarnings("unused") LKQLList list, + @SuppressWarnings("unused") long low, + Object high) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.LKQL_INTEGER, LKQLTypesHelper.fromJava(high), body.argNode(1)); + } + + @Fallback + protected LKQLList onInvalidLow( + @SuppressWarnings("unused") LKQLList list, + Object low, + @SuppressWarnings("unused") Object high) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.LKQL_INTEGER, LKQLTypesHelper.fromJava(low), body.argNode(0)); + } + } } diff --git a/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/NodeMethods.java b/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/NodeMethods.java index b2b785192..7829d1aec 100644 --- a/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/NodeMethods.java +++ b/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/NodeMethods.java @@ -13,6 +13,7 @@ import com.adacore.lkql_jit.LKQLTypeSystemGen; import com.adacore.lkql_jit.built_ins.AbstractBuiltInFunctionBody; import com.adacore.lkql_jit.built_ins.BuiltInMethodFactory; +import com.adacore.lkql_jit.built_ins.SpecializedBuiltInBody; import com.adacore.lkql_jit.exception.LKQLRuntimeException; import com.adacore.lkql_jit.nodes.expressions.Expr; import com.adacore.lkql_jit.runtime.values.LKQLNull; @@ -22,8 +23,9 @@ import com.adacore.lkql_jit.utils.functions.ObjectUtils; import com.adacore.lkql_jit.utils.functions.ReflectionUtils; import com.adacore.lkql_jit.utils.functions.StringUtils; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.UnexpectedResultException; import java.util.ArrayList; import java.util.Map; @@ -66,7 +68,16 @@ public final class NodeMethods { "Return whether two nodes have the same tokens, ignoring trivias", new String[] {"other"}, new Expr[] {null}, - new SameTokensExpr())); + new SpecializedBuiltInBody<>( + NodeMethodsFactory.SameTokensExprNodeGen.create()) { + @Override + protected Object dispatch(Object[] args) { + return this.specializedNode.executeSameTokens( + LKQLTypeSystemGen.asAdaNode(args[0]), args[1]); + } + })); + + // ----- Inner classes ----- /** Expression of the "children" method. */ public static final class ChildrenExpr extends AbstractBuiltInFunctionBody { @@ -171,21 +182,13 @@ public Object executeGeneric(VirtualFrame frame) { } /** Expression of the "same_tokens" method. */ - public static final class SameTokensExpr extends AbstractBuiltInFunctionBody { - @Override - public Object executeGeneric(VirtualFrame frame) { - // Get the nodes to compare - Libadalang.AdaNode leftNode = LKQLTypeSystemGen.asAdaNode(frame.getArguments()[0]); - Libadalang.AdaNode rightNode; - try { - rightNode = LKQLTypeSystemGen.expectAdaNode(frame.getArguments()[1]); - } catch (UnexpectedResultException e) { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.ADA_NODE, - LKQLTypesHelper.fromJava(e.getResult()), - this.callNode.getArgList().getArgs()[0]); - } + public abstract static class SameTokensExpr + extends SpecializedBuiltInBody.SpecializedBuiltInNode { + public abstract boolean executeSameTokens(Libadalang.AdaNode leftNode, Object rightNode); + + @Specialization + protected boolean onAdaNode(Libadalang.AdaNode leftNode, Libadalang.AdaNode rightNode) { // Get the tokens Libadalang.Token leftToken = leftNode.tokenStart(); Libadalang.Token rightToken = rightNode.tokenStart(); @@ -218,12 +221,16 @@ public Object executeGeneric(VirtualFrame frame) { return true; } - /** - * Get the next token from the given one ignoring the trivias - * - * @param t The token to get the next from - * @return The next token - */ + @Fallback + protected boolean onInvalid( + @SuppressWarnings("unused") Libadalang.AdaNode leftNode, Object rightValue) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.ADA_NODE, + LKQLTypesHelper.fromJava(rightValue), + body.argNode(0)); + } + + /** Get the next token from the given one ignoring the trivias. */ private static Libadalang.Token next(Libadalang.Token t) { Libadalang.Token res = t.next(); while (!res.isNone() && res.triviaIndex != 0) { diff --git a/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/StrMethods.java b/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/StrMethods.java index 4477d6709..8fb08facd 100644 --- a/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/StrMethods.java +++ b/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/StrMethods.java @@ -11,16 +11,17 @@ import com.adacore.lkql_jit.LKQLTypeSystemGen; import com.adacore.lkql_jit.built_ins.AbstractBuiltInFunctionBody; import com.adacore.lkql_jit.built_ins.BuiltInMethodFactory; +import com.adacore.lkql_jit.built_ins.SpecializedBuiltInBody; import com.adacore.lkql_jit.built_ins.functions.BaseNameFunction; import com.adacore.lkql_jit.exception.LKQLRuntimeException; import com.adacore.lkql_jit.nodes.expressions.Expr; import com.adacore.lkql_jit.runtime.values.LKQLPattern; import com.adacore.lkql_jit.runtime.values.lists.LKQLList; import com.adacore.lkql_jit.utils.LKQLTypesHelper; -import com.adacore.lkql_jit.utils.functions.BigIntegerUtils; import com.adacore.lkql_jit.utils.functions.StringUtils; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; -import java.math.BigInteger; import java.util.Map; /** @@ -68,40 +69,82 @@ public class StrMethods { + " contained between indices from and to (both included)", new String[] {"from", "to"}, new Expr[] {null, null}, - new SubstringExpr()), + new SpecializedBuiltInBody<>( + StrMethodsFactory.SubstringExprNodeGen.create()) { + @Override + protected Object dispatch(Object[] args) { + return this.specializedNode.executeSubstring( + LKQLTypeSystemGen.asString(args[0]), args[1], args[2]); + } + }), createMethod( "split", "Given a string, return an iterator on the words contained by str" + " separated by separator", new String[] {"separator"}, new Expr[] {null}, - new SplitExpr()), + new SpecializedBuiltInBody<>( + StrMethodsFactory.SplitExprNodeGen.create()) { + @Override + protected Object dispatch(Object[] args) { + return this.specializedNode.executeSplit( + LKQLTypeSystemGen.asString(args[0]), args[1]); + } + }), createMethod( "contains", "Search for to_find in the given string. Return whether a match is" + " found. to_find can be either a pattern or a string", new String[] {"to_find"}, new Expr[] {null}, - new ContainsExpr()), + new SpecializedBuiltInBody<>( + StrMethodsFactory.ContainsExprNodeGen.create()) { + @Override + protected Object dispatch(Object[] args) { + return this.specializedNode.executeContains( + LKQLTypeSystemGen.asString(args[0]), args[1]); + } + }), createMethod( "find", "Search for to_find in the given string. Return position of the match," + " or -1 if no match. to_find can be either a pattern or a string", new String[] {"to_find"}, new Expr[] {null}, - new FindExpr()), + new SpecializedBuiltInBody<>( + StrMethodsFactory.FindExprNodeGen.create()) { + @Override + protected Object dispatch(Object[] args) { + return this.specializedNode.executeFind( + LKQLTypeSystemGen.asString(args[0]), args[1]); + } + }), createMethod( "starts_with", "Given a string, returns whether it starts with the given prefix", new String[] {"prefix"}, new Expr[] {null}, - new StartsWithExpr()), + new SpecializedBuiltInBody<>( + StrMethodsFactory.StartsWithExprNodeGen.create()) { + @Override + protected Object dispatch(Object[] args) { + return this.specializedNode.executeStartsWith( + LKQLTypeSystemGen.asString(args[0]), args[1]); + } + }), createMethod( "ends_with", "Given a string, returns whether it ends with the given suffix", new String[] {"suffix"}, new Expr[] {null}, - new EndsWithExpr())); + new SpecializedBuiltInBody<>( + StrMethodsFactory.EndsWithExprNodeGen.create()) { + @Override + protected Object dispatch(Object[] args) { + return this.specializedNode.executeEndsWith( + LKQLTypeSystemGen.asString(args[0]), args[1]); + } + })); // ----- Inner classes ----- @@ -186,192 +229,154 @@ public Object executeGeneric(VirtualFrame frame) { } /** Expression of the "substring" method. */ - public static final class SubstringExpr extends AbstractBuiltInFunctionBody { - @Override - public Object executeGeneric(VirtualFrame frame) { - // Get the arguments - Object startObject = frame.getArguments()[1]; - Object endObject = frame.getArguments()[2]; - - // Verify the type of arguments - if (!LKQLTypeSystemGen.isImplicitBigInteger(startObject)) { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.LKQL_INTEGER, - LKQLTypesHelper.fromJava(startObject), - this.callNode.getArgList().getArgs()[0]); - } + abstract static class SubstringExpr extends SpecializedBuiltInBody.SpecializedBuiltInNode { - if (!LKQLTypeSystemGen.isImplicitBigInteger(endObject)) { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.LKQL_INTEGER, - LKQLTypesHelper.fromJava(endObject), - this.callNode.getArgList().getArgs()[1]); - } - - // Cast the arguments - BigInteger startBig = - BigIntegerUtils.subtract( - LKQLTypeSystemGen.asImplicitBigInteger(startObject), BigInteger.ONE); - BigInteger endBig = LKQLTypeSystemGen.asImplicitBigInteger(endObject); + public abstract String executeSubstring(String source, Object start, Object end); - int start = BigIntegerUtils.intValue(startBig); - int end = BigIntegerUtils.intValue(endBig); + @Specialization + protected String onValid(String source, long start, long end) { + // Offset the start index by 1 since LKQL is 1-indexed + start = start - 1; - // Verify the start and end + // Verify start and end bounds if (start < 0) { - throw LKQLRuntimeException.invalidIndex( - start, this.callNode.getArgList().getArgs()[0]); + throw LKQLRuntimeException.invalidIndex((int) start + 1, this.body.argNode(0)); } - if (end > LKQLTypeSystemGen.asString(frame.getArguments()[0]).length()) { - throw LKQLRuntimeException.invalidIndex( - end, this.callNode.getArgList().getArgs()[1]); + if (end > source.length()) { + throw LKQLRuntimeException.invalidIndex((int) end, this.body.argNode(1)); } // Return the substring - return LKQLTypeSystemGen.asString(frame.getArguments()[0]).substring(start, end); + return source.substring((int) start, (int) end); + } + + @Specialization + protected String invalidEnd( + @SuppressWarnings("unused") String source, + @SuppressWarnings("unused") long start, + Object end) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.LKQL_INTEGER, + LKQLTypesHelper.fromJava(end), + this.body.argNode(1)); + } + + @Fallback + protected String invalidStart( + @SuppressWarnings("unused") String source, + Object start, + @SuppressWarnings("unused") Object end) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.LKQL_INTEGER, + LKQLTypesHelper.fromJava(start), + this.body.argNode(0)); } } /** Expression of the "split" method. */ - public static final class SplitExpr extends AbstractBuiltInFunctionBody { - @Override - public Object executeGeneric(VirtualFrame frame) { - // Get the argument - Object toSplit = frame.getArguments()[0]; - Object separatorObject = frame.getArguments()[1]; - - // Verify the argument type - if (!LKQLTypeSystemGen.isString(separatorObject)) { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.LKQL_STRING, - LKQLTypesHelper.fromJava(separatorObject), - this.callNode.getArgList().getArgs()[0]); - } + abstract static class SplitExpr extends SpecializedBuiltInBody.SpecializedBuiltInNode { + + public abstract LKQLList executeSplit(String source, Object sep); - // Split the string - String[] separated = - StringUtils.split( - LKQLTypeSystemGen.asString(toSplit), - LKQLTypeSystemGen.asString(separatorObject)); + @Specialization + protected LKQLList onValid(String source, String sep) { + return new LKQLList(StringUtils.split(source, sep)); + } - // Return the list value of the split string - return new LKQLList(separated); + @Fallback + protected LKQLList onInvalid(@SuppressWarnings("unused") String source, Object sep) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.LKQL_STRING, + LKQLTypesHelper.fromJava(sep), + this.body.argNode(0)); } } /** Expression of the "contains" method. */ - public static final class ContainsExpr extends AbstractBuiltInFunctionBody { - @Override - public Object executeGeneric(VirtualFrame frame) { - // Get the arguments - String receiver = LKQLTypeSystemGen.asString(frame.getArguments()[0]); - Object toFindObject = frame.getArguments()[1]; - boolean contains; + abstract static class ContainsExpr extends SpecializedBuiltInBody.SpecializedBuiltInNode { - // If the argument is a string - if (LKQLTypeSystemGen.isString(toFindObject)) { - String toFind = LKQLTypeSystemGen.asString(toFindObject); - contains = StringUtils.contains(receiver, toFind); - } + public abstract boolean executeContains(String source, Object toFind); - // If the argument is a pattern - else if (LKQLTypeSystemGen.isLKQLPattern(toFindObject)) { - LKQLPattern pattern = LKQLTypeSystemGen.asLKQLPattern(toFindObject); - contains = pattern.contains(receiver); - } + @Specialization + protected boolean onString(String source, String toFind) { + return StringUtils.contains(source, toFind); + } - // Else, just thrown an error - else { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.LKQL_STRING, - LKQLTypesHelper.fromJava(toFindObject), - this.callNode.getArgList().getArgs()[0]); - } + @Specialization + protected boolean onPattern(String source, LKQLPattern toFind) { + return toFind.contains(source); + } - // Return if the receiver contains the to find - return contains; + @Fallback + protected boolean onInvalid(@SuppressWarnings("unused") String source, Object toFind) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.typeUnion( + LKQLTypesHelper.LKQL_STRING, LKQLTypesHelper.LKQL_PATTERN), + LKQLTypesHelper.fromJava(toFind), + this.body.argNode(0)); } } /** Expression of the "find" method. */ - public static final class FindExpr extends AbstractBuiltInFunctionBody { - @Override - public Object executeGeneric(VirtualFrame frame) { - // Get the arguments - String receiver = LKQLTypeSystemGen.asString(frame.getArguments()[0]); - Object toFindObject = frame.getArguments()[1]; - int index; + abstract static class FindExpr extends SpecializedBuiltInBody.SpecializedBuiltInNode { - // If the argument is a string - if (LKQLTypeSystemGen.isString(toFindObject)) { - String toFind = LKQLTypeSystemGen.asString(toFindObject); - index = StringUtils.indexOf(receiver, toFind); - } + public abstract long executeFind(String source, Object toFind); - // If the argument is a pattern - else if (LKQLTypeSystemGen.isLKQLPattern(toFindObject)) { - LKQLPattern pattern = LKQLTypeSystemGen.asLKQLPattern(toFindObject); - index = pattern.find(receiver); - } + @Specialization + protected long onString(String source, String toFind) { + return StringUtils.indexOf(source, toFind) + 1; + } - // Else, just throw an error - else { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.LKQL_STRING, - LKQLTypesHelper.fromJava(toFindObject), - this.callNode.getArgList().getArgs()[0]); - } + @Specialization + protected long onPattern(String source, LKQLPattern toFind) { + return toFind.find(source) + 1; + } - // Return the index - return (long) index + 1; + @Fallback + protected long onInvalid(@SuppressWarnings("unused") String source, Object toFind) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.typeUnion( + LKQLTypesHelper.LKQL_STRING, LKQLTypesHelper.LKQL_PATTERN), + LKQLTypesHelper.fromJava(toFind), + this.body.argNode(0)); } } /** Expression of the "starts_with" method. */ - public static final class StartsWithExpr extends AbstractBuiltInFunctionBody { - @Override - public Object executeGeneric(VirtualFrame frame) { - // Get the argument - Object prefixObject = frame.getArguments()[1]; - - // Verify the argument type - if (!LKQLTypeSystemGen.isString(prefixObject)) { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.LKQL_STRING, - LKQLTypesHelper.fromJava(prefixObject), - this.callNode.getArgList().getArgs()[0]); - } + abstract static class StartsWithExpr extends SpecializedBuiltInBody.SpecializedBuiltInNode { - // Cast the arguments - String receiver = LKQLTypeSystemGen.asString(frame.getArguments()[0]); - String prefix = LKQLTypeSystemGen.asString(prefixObject); + public abstract boolean executeStartsWith(String source, Object prefix); - // Return if the receiver has the prefix - return receiver.startsWith(prefix); + @Specialization + protected boolean onValid(String source, String prefix) { + return source.startsWith(prefix); + } + + @Fallback + protected boolean onInvalid(@SuppressWarnings("unused") String source, Object prefix) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.LKQL_STRING, + LKQLTypesHelper.fromJava(prefix), + this.body.argNode(0)); } } /** Expression of the "ends_with" method. */ - public static final class EndsWithExpr extends AbstractBuiltInFunctionBody { - @Override - public Object executeGeneric(VirtualFrame frame) { - // Get the argument - Object suffixObject = frame.getArguments()[1]; - - // Verify the argument type - if (!LKQLTypeSystemGen.isString(suffixObject)) { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.LKQL_STRING, - LKQLTypesHelper.fromJava(suffixObject), - this.callNode.getArgList().getArgs()[0]); - } + abstract static class EndsWithExpr extends SpecializedBuiltInBody.SpecializedBuiltInNode { - // Cast the arguments - String receiver = LKQLTypeSystemGen.asString(frame.getArguments()[0]); - String suffix = LKQLTypeSystemGen.asString(suffixObject); + public abstract boolean executeEndsWith(String source, Object suffix); + + @Specialization + protected boolean onValid(String source, String suffix) { + return source.endsWith(suffix); + } - // Return if the receiver has the prefix - return receiver.endsWith(suffix); + @Specialization + protected boolean onInvalid(@SuppressWarnings("unused") String source, Object suffix) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.LKQL_STRING, + LKQLTypesHelper.fromJava(suffix), + this.body.argNode(0)); } } } diff --git a/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/TokenMethods.java b/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/TokenMethods.java index 3860b0023..3f2c951e6 100644 --- a/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/TokenMethods.java +++ b/lkql_jit/language/src/main/java/com/adacore/lkql_jit/built_ins/methods/TokenMethods.java @@ -12,14 +12,16 @@ import com.adacore.lkql_jit.LKQLTypeSystemGen; import com.adacore.lkql_jit.built_ins.AbstractBuiltInFunctionBody; import com.adacore.lkql_jit.built_ins.BuiltInMethodFactory; +import com.adacore.lkql_jit.built_ins.SpecializedBuiltInBody; import com.adacore.lkql_jit.exception.LKQLRuntimeException; import com.adacore.lkql_jit.nodes.expressions.Expr; import com.adacore.lkql_jit.nodes.expressions.literals.BooleanLiteral; import com.adacore.lkql_jit.utils.LKQLTypesHelper; import com.adacore.lkql_jit.utils.functions.ObjectUtils; import com.adacore.lkql_jit.utils.functions.StringUtils; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; -import com.oracle.truffle.api.nodes.UnexpectedResultException; import java.util.Map; /** @@ -40,7 +42,14 @@ public final class TokenMethods { "Return whether two tokens are structurally equivalent", new String[] {"other"}, new Expr[] {null}, - new IsEquivalentExpr()), + new SpecializedBuiltInBody<>( + TokenMethodsFactory.IsEquivalentExprNodeGen.create()) { + @Override + protected Object dispatch(Object[] args) { + return this.specializedNode.executeIsEquivalent( + LKQLTypeSystemGen.asToken(args[0]), args[1]); + } + }), createAttribute( "is_trivia", "Return whether this token is a trivia", @@ -50,13 +59,27 @@ public final class TokenMethods { "Return the next token", new String[] {"exclude_trivia"}, new Expr[] {new BooleanLiteral(null, false)}, - new NextExpr()), + new SpecializedBuiltInBody<>( + TokenMethodsFactory.NextExprNodeGen.create()) { + @Override + protected Object dispatch(Object[] args) { + return this.specializedNode.executeNext( + LKQLTypeSystemGen.asToken(args[0]), args[1]); + } + }), createMethod( "previous", "Return the previous token", new String[] {"exclude_trivia"}, new Expr[] {new BooleanLiteral(null, false)}, - new PrevExpr()), + new SpecializedBuiltInBody<>( + TokenMethodsFactory.PrevExprNodeGen.create()) { + @Override + protected Object dispatch(Object[] args) { + return this.specializedNode.executePrev( + LKQLTypeSystemGen.asToken(args[0]), args[1]); + } + }), createAttribute("unit", "Return the unit for this token", new UnitExpr()), createAttribute("text", "Return the text of the token", new TextExpr()), createAttribute("kind", "Return the kind of the token", new KindExpr())); @@ -109,22 +132,20 @@ public Object executeGeneric(VirtualFrame frame) { } /** Expression of the "is_equivalent" method. */ - public static final class IsEquivalentExpr extends AbstractBuiltInFunctionBody { - @Override - public Object executeGeneric(VirtualFrame frame) { - // Get the other token to compare - Libadalang.Token other; - try { - other = LKQLTypeSystemGen.expectToken(frame.getArguments()[1]); - } catch (UnexpectedResultException e) { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.TOKEN, - LKQLTypesHelper.fromJava(e.getResult()), - this.callNode.getArgList().getArgs()[1]); - } + abstract static class IsEquivalentExpr extends SpecializedBuiltInBody.SpecializedBuiltInNode { - // Return the comparison - return LKQLTypeSystemGen.asToken(frame.getArguments()[0]).isEquivalent(other); + public abstract boolean executeIsEquivalent(Libadalang.Token receiver, Object other); + + @Specialization + protected boolean onValid(Libadalang.Token receiver, Libadalang.Token other) { + return receiver.isEquivalent(other); + } + + @Fallback + protected boolean onInvalid( + @SuppressWarnings("unused") Libadalang.Token receiver, Object other) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.TOKEN, LKQLTypesHelper.fromJava(other), this.body.argNode(0)); } } @@ -137,55 +158,63 @@ public Object executeGeneric(VirtualFrame frame) { } /** Expression of the "next" method. */ - public static final class NextExpr extends AbstractBuiltInFunctionBody { - @Override - public Object executeGeneric(VirtualFrame frame) { - // Get if the trivia tokens should be ignored - boolean ignoreTrivia; - try { - ignoreTrivia = LKQLTypeSystemGen.expectBoolean(frame.getArguments()[1]); - } catch (UnexpectedResultException e) { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.LKQL_BOOLEAN, - LKQLTypesHelper.fromJava(e.getResult()), - this.callNode.getArgList().getArgs()[1]); - } + abstract static class NextExpr extends SpecializedBuiltInBody.SpecializedBuiltInNode { - Libadalang.Token res = LKQLTypeSystemGen.asToken(frame.getArguments()[0]).next(); + public abstract Libadalang.Token executeNext( + Libadalang.Token receiver, Object ignoreTrivia); + + @Specialization + protected Libadalang.Token onValid(Libadalang.Token receiver, boolean ignoreTrivia) { + // Skip trivia if required + Libadalang.Token res = receiver.next(); if (ignoreTrivia) { while (!res.isNone() && res.triviaIndex != 0) { res = res.next(); } } + // Return the result return res; } + + @Fallback + protected Libadalang.Token onInvalid( + @SuppressWarnings("unused") Libadalang.Token receiver, Object ignoreTrivia) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.LKQL_BOOLEAN, + LKQLTypesHelper.fromJava(ignoreTrivia), + this.body.argNode(0)); + } } /** Expression of the "previous" method. */ - public static final class PrevExpr extends AbstractBuiltInFunctionBody { - @Override - public Object executeGeneric(VirtualFrame frame) { - // Get if the trivia tokens should be ignored - boolean ignoreTrivia; - try { - ignoreTrivia = LKQLTypeSystemGen.expectBoolean(frame.getArguments()[1]); - } catch (UnexpectedResultException e) { - throw LKQLRuntimeException.wrongType( - LKQLTypesHelper.LKQL_BOOLEAN, - LKQLTypesHelper.fromJava(e.getResult()), - this.callNode.getArgList().getArgs()[1]); - } + abstract static class PrevExpr extends SpecializedBuiltInBody.SpecializedBuiltInNode { + + public abstract Libadalang.Token executePrev( + Libadalang.Token receiver, Object ignoreTrivia); - Libadalang.Token res = LKQLTypeSystemGen.asToken(frame.getArguments()[0]).previous(); + @Specialization + protected Libadalang.Token onValid(Libadalang.Token receiver, boolean ignoreTrivia) { + // Skip trivia if required + Libadalang.Token res = receiver.previous(); if (ignoreTrivia) { while (!res.isNone() && res.triviaIndex != 0) { res = res.previous(); } } + // Return the result return res; } + + @Fallback + protected Libadalang.Token onInvalid( + @SuppressWarnings("unused") Libadalang.Token receiver, Object ignoreTrivia) { + throw LKQLRuntimeException.wrongType( + LKQLTypesHelper.LKQL_BOOLEAN, + LKQLTypesHelper.fromJava(ignoreTrivia), + this.body.argNode(0)); + } } /** Expression of the "unit" method. */ diff --git a/testsuite/tests/interpreter/sublist_builtin/test.out b/testsuite/tests/interpreter/sublist_builtin/test.out index b81307611..81aa71f74 100644 --- a/testsuite/tests/interpreter/sublist_builtin/test.out +++ b/testsuite/tests/interpreter/sublist_builtin/test.out @@ -2,7 +2,7 @@ 3 [2, 3] [1, 2, 3, 4, 5] -script.lkql:6:7: error: Invalid index: 0 +script.lkql:6:17: error: Invalid index: 0 6 | print(l.sublist(0, 5)) - | ^^^^^^^^^^^^^^^ + | ^ in sublist (called at script.lkql:6:7)