package org.zwobble.couscous.interpreter; import org.zwobble.couscous.ast.*; import org.zwobble.couscous.ast.visitors.NodeMapperWithDefault; import org.zwobble.couscous.ast.visitors.StatementNodeMapper; import org.zwobble.couscous.interpreter.values.InterpreterValue; import org.zwobble.couscous.interpreter.values.UnitInterpreterValue; import java.util.List; import java.util.Optional; import java.util.stream.Stream; import static org.zwobble.couscous.ast.structure.NodeStructure.descendantNodesAndSelf; import static org.zwobble.couscous.interpreter.Evaluator.eval; import static org.zwobble.couscous.interpreter.Evaluator.evalCondition; public class Executor implements StatementNodeMapper> { public static InterpreterValue callMethod( Environment environment, MethodNode method, Optional thisValue, PositionalArguments actualArguments) { List body = method.getBody().orElseThrow(() -> new RuntimeException("Cannot call abstract method")); return callMethod(environment, method.getArguments(), body, thisValue, actualArguments); } public static InterpreterValue callConstructor( Environment environment, ConstructorNode constructor, Optional thisValue, PositionalArguments actualArguments) { return callMethod(environment, constructor.getArguments(), constructor.getBody(), thisValue, actualArguments); } private static InterpreterValue callMethod( Environment environment, List formalArguments, List statements, Optional thisValue, PositionalArguments actualArguments) { Environment innerEnvironment = buildEnvironment(environment, formalArguments, statements, thisValue, actualArguments); return exec(innerEnvironment, statements) .orElse(UnitInterpreterValue.UNIT); } private static Environment buildEnvironment( Environment environment, List formalArguments, List statements, Optional thisValue, PositionalArguments arguments) { // TODO use map with multiple iterables final org.zwobble.couscous.interpreter.StackFrameBuilder stackFrame = new StackFrameBuilder(); for (int index = 0; index < formalArguments.size(); index++) { stackFrame.declare(formalArguments.get(index), arguments.get(index)); } findDeclarations(statements).forEach(declaration -> stackFrame.declare(declaration)); return environment.withStackFrame(thisValue, stackFrame.build()); } private static Stream findDeclarations(List body) { return body.stream() .flatMap(statement -> descendantNodesAndSelf(statement, node -> node instanceof StatementNode)) .flatMap(node -> node.accept(new NodeMapperWithDefault>(Stream.empty()) { @Override public Stream visit(LocalVariableDeclarationNode localVariableDeclaration) { return Stream.of(localVariableDeclaration); } })); } public static Optional exec(Environment environment, Iterable statements) { for (StatementNode statement : statements) { Optional result = exec(environment, statement); if (result.isPresent()) { return result; } } return Optional.empty(); } public static Optional exec(Environment environment, StatementNode statement) { return statement.accept(new Executor(environment)); } private final Environment environment; private Executor(Environment environment) { this.environment = environment; } @Override public Optional visit(ReturnNode returnNode) { return Optional.of(eval(environment, returnNode.getValue())); } @Override public Optional visit(ExpressionStatementNode expressionStatement) { eval(environment, expressionStatement.getExpression()); return Optional.empty(); } @Override public Optional visit(LocalVariableDeclarationNode localVariableDeclaration) { final org.zwobble.couscous.interpreter.values.InterpreterValue value = eval(environment, localVariableDeclaration.getInitialValue()); environment.put(localVariableDeclaration, value); return Optional.empty(); } @Override public Optional visit(IfStatementNode ifStatement) { List body = evalCondition(environment, ifStatement.getCondition()) ? ifStatement.getTrueBranch() : ifStatement.getFalseBranch(); return exec(body); } @Override public Optional visit(WhileNode whileLoop) { while (evalCondition(environment, whileLoop.getCondition())) { Optional result = exec(whileLoop.getBody()); if (result.isPresent()) { return result; } } return Optional.empty(); } private Optional exec(List statements) { for (StatementNode statement : statements) { Optional result = exec(environment, statement); if (result.isPresent()) { return result; } } return Optional.empty(); } }