diff --git a/src/it/streams/pom.xml b/src/it/streams/pom.xml
index 48e28027..48ff0eaa 100644
--- a/src/it/streams/pom.xml
+++ b/src/it/streams/pom.xml
@@ -120,7 +120,7 @@ SOFTWARE.
exec
- mvn
+ mvn
org.eolang:jeo-maven-plugin:${jeo.version}:assemble
-Djeo.assemble.sourcesDir=${project.build.directory}/generated-sources/opeo-compile-xmir
diff --git a/src/main/java/org/eolang/opeo/ast/AstNode.java b/src/main/java/org/eolang/opeo/ast/AstNode.java
index ee0a3b78..3a2ce6c5 100644
--- a/src/main/java/org/eolang/opeo/ast/AstNode.java
+++ b/src/main/java/org/eolang/opeo/ast/AstNode.java
@@ -25,6 +25,7 @@
import java.util.Collections;
import java.util.List;
+import org.objectweb.asm.Type;
import org.xembly.Directive;
/**
@@ -43,7 +44,7 @@ public interface AstNode extends Xmir {
* Empty node that does nothing.
* @since 0.2
*/
- final class Empty implements AstNode {
+ final class Empty implements AstNode, Typed {
@Override
public List opcodes() {
@@ -54,5 +55,10 @@ public List opcodes() {
public Iterable toXmir() {
return Collections.emptyList();
}
+
+ @Override
+ public Type type() {
+ return Type.VOID_TYPE;
+ }
}
}
diff --git a/src/main/java/org/eolang/opeo/ast/Return.java b/src/main/java/org/eolang/opeo/ast/Return.java
new file mode 100644
index 00000000..22584b9e
--- /dev/null
+++ b/src/main/java/org/eolang/opeo/ast/Return.java
@@ -0,0 +1,132 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016-2023 Objectionary.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.eolang.opeo.ast;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.eolang.jeo.representation.xmir.XmlNode;
+import org.eolang.opeo.compilation.Parser;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.xembly.Directive;
+import org.xembly.Directives;
+
+/**
+ * Return statement.
+ *
+ * @since 0.5
+ */
+public final class Return implements AstNode {
+
+ /**
+ * Value to return.
+ */
+ private final AstNode value;
+
+ /**
+ * Default constructor.
+ */
+ public Return() {
+ this(new Empty());
+ }
+
+ /**
+ * Constructor.
+ * @param node XML node to parse.
+ * @param parser Parser, that is used to parse child nodes.
+ */
+ public Return(final XmlNode node, final Parser parser) {
+ this(Return.xtype(node, parser));
+ }
+
+ /**
+ * Constructor.
+ * @param typed Value to return.
+ */
+ public Return(final AstNode typed) {
+ this.value = typed;
+ }
+
+ @Override
+ public Iterable toXmir() {
+ return new Directives()
+ .add("o")
+ .attr("base", "return")
+ .append(this.value.toXmir())
+ .up();
+ }
+
+ @Override
+ public List opcodes() {
+ final List res = new ArrayList<>(1);
+ res.addAll(this.value.opcodes());
+ res.add(this.opcode());
+ return res;
+ }
+
+ /**
+ * Get opcode.
+ * @return Opcode.
+ */
+ private Opcode opcode() {
+ final Type type = this.type();
+ if (type.equals(Type.VOID_TYPE)) {
+ return new Opcode(Opcodes.RETURN);
+ } else if (type.equals(Type.INT_TYPE)) {
+ return new Opcode(Opcodes.IRETURN);
+ } else if (type.equals(Type.LONG_TYPE)) {
+ return new Opcode(Opcodes.LRETURN);
+ } else if (type.equals(Type.FLOAT_TYPE)) {
+ return new Opcode(Opcodes.FRETURN);
+ } else if (type.equals(Type.DOUBLE_TYPE)) {
+ return new Opcode(Opcodes.DRETURN);
+ } else {
+ return new Opcode(Opcodes.ARETURN);
+ }
+ }
+
+ /**
+ * Get a type of the value.
+ * @return Type of the value.
+ */
+ private Type type() {
+ return Typed.class.cast(this.value).type();
+ }
+
+ /**
+ * Parse type.
+ * @param node XML node.
+ * @param parser Parser to parse children.
+ * @return Parsed typed value.
+ */
+ private static AstNode xtype(final XmlNode node, final Parser parser) {
+ final List children = node.children().collect(Collectors.toList());
+ if (children.isEmpty()) {
+ return new Empty();
+ } else {
+ return parser.parse(children.get(0));
+ }
+ }
+}
diff --git a/src/test/java/org/eolang/opeo/ast/ReturnTest.java b/src/test/java/org/eolang/opeo/ast/ReturnTest.java
new file mode 100644
index 00000000..e781171e
--- /dev/null
+++ b/src/test/java/org/eolang/opeo/ast/ReturnTest.java
@@ -0,0 +1,133 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016-2023 Objectionary.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+package org.eolang.opeo.ast;
+
+import org.eolang.jeo.representation.xmir.XmlNode;
+import org.eolang.opeo.SameXml;
+import org.eolang.opeo.compilation.Parser;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.junit.jupiter.api.Test;
+import org.objectweb.asm.Opcodes;
+import org.xembly.ImpossibleModificationException;
+import org.xembly.Xembler;
+
+
+/**
+ * Test case for {@link Return}.
+ * @since 0.5
+ */
+class ReturnTest {
+
+ /**
+ * Parser for all unit tests in this class.
+ */
+ private static final Parser PARSER = node -> {
+ final String base = node.attribute("base").orElseThrow(
+ () -> new IllegalArgumentException("No base attribute")
+ );
+ AstNode result;
+ if (base.startsWith("int")) {
+ result = new Literal(42);
+ } else if (base.startsWith("string")) {
+ result = new Literal("foo");
+ } else {
+ throw new IllegalArgumentException(String.format("Unknown base: %s", base));
+ }
+ return result;
+ };
+
+ @Test
+ void convertsSimpleReturnToXml() throws ImpossibleModificationException {
+ MatcherAssert.assertThat(
+ "Can't convert simple return to XML",
+ new Xembler(new Return().toXmir()).xml(),
+ new SameXml("")
+ );
+ }
+
+ @Test
+ void convertsIntReturnToXml() throws ImpossibleModificationException {
+ MatcherAssert.assertThat(
+ "Can't convert typed return with int to XML",
+ new Xembler(new Return(new Literal(42)).toXmir()).xml(),
+ new SameXml(
+ "00 00 00 00 00 00 00 2A"
+ )
+ );
+ }
+
+ @Test
+ void convertsStringReturnToXml() throws ImpossibleModificationException {
+ MatcherAssert.assertThat(
+ "Can't convert typed return with string to XML",
+ new Xembler(new Return(new Literal("foo")).toXmir()).xml(),
+ new SameXml(
+ "66 6F 6F"
+ )
+ );
+ }
+
+ @Test
+ void createsSimpleReturnFromXmir() {
+ MatcherAssert.assertThat(
+ "Can't create simple return",
+ new Return(new XmlNode(""), ReturnTest.PARSER).opcodes(),
+ Matchers.hasItem(new Opcode(Opcodes.RETURN))
+ );
+ }
+
+ @Test
+ void createsIntReturnFromXmir() {
+ MatcherAssert.assertThat(
+ "Can't create typed return with int",
+ new Return(
+ new XmlNode(
+ "00 00 00 00 00 00 00 2A"),
+ ReturnTest.PARSER
+ ).opcodes(),
+ Matchers.hasItems(
+ new Opcode(Opcodes.BIPUSH, 42),
+ new Opcode(Opcodes.IRETURN)
+ )
+ );
+ }
+
+ @Test
+ void createsStringReturnFromXmir() {
+ MatcherAssert.assertThat(
+ "Can't create typed return with string",
+ new Return(
+ new XmlNode(
+ "66 6F 6F"),
+ ReturnTest.PARSER
+ ).opcodes(),
+ Matchers.hasItems(
+ new Opcode(Opcodes.LDC, "foo"),
+ new Opcode(Opcodes.ARETURN)
+ )
+ );
+ }
+
+}
\ No newline at end of file