package org.zwobble.couscous.interpreter; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.IntStream; import org.zwobble.couscous.Project; import org.zwobble.couscous.ast.ExpressionStatementNode; import org.zwobble.couscous.ast.MethodNode; import org.zwobble.couscous.ast.ReturnNode; import org.zwobble.couscous.ast.visitors.StatementNodeVisitor; import org.zwobble.couscous.values.InterpreterValue; import static com.google.common.collect.Iterables.find; import static java.util.stream.Collectors.toMap; import static org.zwobble.couscous.values.UnitValue.UNIT; import lombok.val; public class Interpreter { private Project project; public Interpreter(Project project) { this.project = project; } public InterpreterValue run(String className, String methodName, List arguments) { val clazz = project.findClass(className); val method = find(clazz.getMethods(), method -> method.getName().equals(methodName)); val stackFrame = buildStackFrame(method, arguments); for (val statement : method.getBody()) { val result = statement.accept(new Executor(new Evaluator(stackFrame))); if (result.isPresent()) { return result.get(); } } return UNIT; } private Map buildStackFrame( final MethodNode method, List arguments) { if (method.getArguments().size() != arguments.size()) { throw new WrongNumberOfArguments(method.getArguments().size(), arguments.size()); } return IntStream.range(0, method.getArguments().size()) .boxed() .collect(toMap( index -> method.getArguments().get(index).getId(), index -> arguments.get(index))); } private class Executor implements StatementNodeVisitor> { private Evaluator evaluator; public Executor(Evaluator evaluator) { this.evaluator = evaluator; } @Override public Optional visit(ReturnNode returnNode) { return Optional.of(evaluator.eval(returnNode.getValue())); } @Override public Optional visit(ExpressionStatementNode expressionStatement) { evaluator.eval(expressionStatement.getExpression()); return Optional.empty(); } } }