Skip to content

Commit

Permalink
feat(objectionary#329): add Return ast node
Browse files Browse the repository at this point in the history
  • Loading branch information
volodya-lombrozo committed Jul 9, 2024
1 parent 5b4e635 commit 31a800f
Show file tree
Hide file tree
Showing 4 changed files with 273 additions and 2 deletions.
2 changes: 1 addition & 1 deletion src/it/streams/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ SOFTWARE.
<goal>exec</goal>
</goals>
<configuration>
<executable>mvn</executable>
<executable>mvn</executable>
<arguments combine.children="append">
<argument>org.eolang:jeo-maven-plugin:${jeo.version}:assemble</argument>
<argument>-Djeo.assemble.sourcesDir=${project.build.directory}/generated-sources/opeo-compile-xmir</argument>
Expand Down
8 changes: 7 additions & 1 deletion src/main/java/org/eolang/opeo/ast/AstNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

import java.util.Collections;
import java.util.List;
import org.objectweb.asm.Type;
import org.xembly.Directive;

/**
Expand All @@ -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<AstNode> opcodes() {
Expand All @@ -54,5 +55,10 @@ public List<AstNode> opcodes() {
public Iterable<Directive> toXmir() {
return Collections.emptyList();
}

@Override
public Type type() {
return Type.VOID_TYPE;
}
}
}
132 changes: 132 additions & 0 deletions src/main/java/org/eolang/opeo/ast/Return.java
Original file line number Diff line number Diff line change
@@ -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<Directive> toXmir() {
return new Directives()
.add("o")
.attr("base", "return")
.append(this.value.toXmir())
.up();
}

@Override
public List<AstNode> opcodes() {
final List<AstNode> 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<XmlNode> children = node.children().collect(Collectors.toList());
if (children.isEmpty()) {
return new Empty();
} else {
return parser.parse(children.get(0));
}
}
}
133 changes: 133 additions & 0 deletions src/test/java/org/eolang/opeo/ast/ReturnTest.java
Original file line number Diff line number Diff line change
@@ -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("<o base='return'/>")
);
}

@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(
"<o base='return'><o base='int' data='bytes'>00 00 00 00 00 00 00 2A</o></o>"
)
);
}

@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(
"<o base='return'><o base='string' data='bytes'>66 6F 6F</o></o>"
)
);
}

@Test
void createsSimpleReturnFromXmir() {
MatcherAssert.assertThat(
"Can't create simple return",
new Return(new XmlNode("<o base='return'/>"), 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(
"<o base='return'><o base='int' data='bytes'>00 00 00 00 00 00 00 2A</o></o>"),
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(
"<o base='return'><o base='string' data='bytes'>66 6F 6F</o></o>"),
ReturnTest.PARSER
).opcodes(),
Matchers.hasItems(
new Opcode(Opcodes.LDC, "foo"),
new Opcode(Opcodes.ARETURN)
)
);
}

}

0 comments on commit 31a800f

Please sign in to comment.