From c0f11d84bf9ed80561c1da1f478d3628408657b3 Mon Sep 17 00:00:00 2001 From: Andre Masella Date: Fri, 9 Feb 2024 13:20:31 -0500 Subject: [PATCH] Add `Count` operator This expression returns the number of items in a set. --- changes/add_count_expression.md | 1 + language.md | 5 ++ .../gsi/shesmu/compiler/ExpressionNode.java | 1 + .../shesmu/compiler/ExpressionNodeCount.java | 73 +++++++++++++++++++ .../src/test/resources/run/count.shesmu | 4 + 5 files changed, 84 insertions(+) create mode 100644 changes/add_count_expression.md create mode 100644 shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/compiler/ExpressionNodeCount.java create mode 100644 shesmu-server/src/test/resources/run/count.shesmu diff --git a/changes/add_count_expression.md b/changes/add_count_expression.md new file mode 100644 index 000000000..500fd68ba --- /dev/null +++ b/changes/add_count_expression.md @@ -0,0 +1 @@ +* Add new `Count` operator for lists diff --git a/language.md b/language.md index d45ab33b1..d2c8335e4 100644 --- a/language.md +++ b/language.md @@ -894,6 +894,11 @@ Compute the logical complement of the expression, which must be a boolean. Computes the arithmetic additive inverse of the expression, which must be an integer. +#### List Size +- `Count` _expr_ + +Counts the number of elements _expr_, which must be a list. + #### WDL Pair Conversion - `ConvertWdlPair` _expr_ diff --git a/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/compiler/ExpressionNode.java b/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/compiler/ExpressionNode.java index 889aa5f3d..574f4c17e 100644 --- a/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/compiler/ExpressionNode.java +++ b/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/compiler/ExpressionNode.java @@ -658,6 +658,7 @@ private static Parser parse9(Parser input, Consumer output) { UNARY.addSymbol("!", just(ExpressionNodeLogicalNot::new)); UNARY.addSymbol("-", just(ExpressionNodeNegate::new)); UNARY.addKeyword("ConvertWdlPair", just(ExpressionNodeWdlPair::new)); + UNARY.addKeyword("Count", just(ExpressionNodeCount::new)); SUFFIX_TIGHT.addSymbol( "[", diff --git a/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/compiler/ExpressionNodeCount.java b/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/compiler/ExpressionNodeCount.java new file mode 100644 index 000000000..a42ce0e3c --- /dev/null +++ b/shesmu-server/src/main/java/ca/on/oicr/gsi/shesmu/compiler/ExpressionNodeCount.java @@ -0,0 +1,73 @@ +package ca.on.oicr.gsi.shesmu.compiler; + +import ca.on.oicr.gsi.shesmu.compiler.Target.Flavour; +import ca.on.oicr.gsi.shesmu.plugin.types.Imyhat; +import ca.on.oicr.gsi.shesmu.plugin.types.Imyhat.ListImyhat; +import java.nio.file.Path; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Predicate; +import org.objectweb.asm.Type; +import org.objectweb.asm.commons.Method; + +public class ExpressionNodeCount extends ExpressionNode { + + private static final Type A_SET_TYPE = Type.getType(Set.class); + private static final Method METHOD_SET__SIZE = new Method("size", Type.INT_TYPE, new Type[] {}); + private final ExpressionNode inner; + + public ExpressionNodeCount(int line, int column, ExpressionNode inner) { + super(line, column); + this.inner = inner; + } + + @Override + public void collectFreeVariables(Set names, Predicate predicate) { + inner.collectFreeVariables(names, predicate); + } + + @Override + public void collectPlugins(Set pluginFileNames) { + inner.collectPlugins(pluginFileNames); + } + + @Override + public void render(Renderer renderer) { + inner.render(renderer); + renderer.methodGen().invokeInterface(A_SET_TYPE, METHOD_SET__SIZE); + renderer.methodGen().cast(Type.INT_TYPE, Type.LONG_TYPE); + } + + @Override + public String renderEcma(EcmaScriptRenderer renderer) { + return "(" + inner.renderEcma(renderer) + ").length"; + } + + @Override + public boolean resolve(NameDefinitions defs, Consumer errorHandler) { + return inner.resolve(defs, errorHandler); + } + + @Override + public boolean resolveDefinitions( + ExpressionCompilerServices expressionCompilerServices, Consumer errorHandler) { + return inner.resolveDefinitions(expressionCompilerServices, errorHandler); + } + + @Override + public Imyhat type() { + return Imyhat.INTEGER; + } + + @Override + public boolean typeCheck(Consumer errorHandler) { + var ok = inner.typeCheck(errorHandler); + if (ok) { + ok = inner.type() instanceof ListImyhat || inner.type().equals(Imyhat.EMPTY); + if (!ok) { + typeError("list", inner.type(), errorHandler); + } + } + return ok; + } +} diff --git a/shesmu-server/src/test/resources/run/count.shesmu b/shesmu-server/src/test/resources/run/count.shesmu new file mode 100644 index 000000000..784076bf6 --- /dev/null +++ b/shesmu-server/src/test/resources/run/count.shesmu @@ -0,0 +1,4 @@ +Version 1; +Input test; + +Olive Run ok With ok = (Count [] == 0) && (Count [3] == 1);