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