package org.zwobble.couscous.interpreter; import org.zwobble.couscous.ast.*; import org.zwobble.couscous.ast.structure.NodeStructure; import org.zwobble.couscous.ast.visitors.NodeMapperWithDefault; import org.zwobble.couscous.ast.visitors.StatementNodeMapper; import org.zwobble.couscous.interpreter.values.BooleanInterpreterValue; 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; public class Executor implements StatementNodeMapper> { public static InterpreterValue callMethod(Environment environment, CallableNode method, Optional thisValue, PositionalArguments arguments) { final org.zwobble.couscous.interpreter.Environment innerEnvironment = buildEnvironment(environment, method, thisValue, arguments); for (final org.zwobble.couscous.ast.StatementNode statement : method.getBody()) { final java.util.Optional result = exec(innerEnvironment, statement); if (result.isPresent()) { return result.get(); } } return UnitInterpreterValue.UNIT; } private static Environment buildEnvironment(final Environment environment, final CallableNode method, Optional thisValue, PositionalArguments arguments) { final org.zwobble.couscous.interpreter.StackFrameBuilder stackFrame = new StackFrameBuilder(); for (int index = 0; index < method.getArguments().size(); index++) { stackFrame.declare(method.getArguments().get(index), arguments.get(index)); } findDeclarations(method.getBody()).forEach(declaration -> stackFrame.declare(declaration)); return environment.withStackFrame(thisValue, stackFrame.build()); } private static Stream findDeclarations(List body) { // TODO: add filter argument to descendantNodesAndSelf to allow recursion to be stopped early return body.stream() .flatMap(NodeStructure::descendantNodesAndSelf) .flatMap(node -> node.accept(new NodeMapperWithDefault>(Stream.empty()) { @Override public Stream visit(LocalVariableDeclarationNode localVariableDeclaration) { return Stream.of(localVariableDeclaration); } })); } 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(ifStatement.getCondition()) ? ifStatement.getTrueBranch() : ifStatement.getFalseBranch(); return exec(body); } @Override public Optional visit(WhileNode whileLoop) { while (evalCondition(whileLoop.getCondition())) { Optional result = exec(whileLoop.getBody()); if (result.isPresent()) { return result; } } return Optional.empty(); } private boolean evalCondition(ExpressionNode conditionExpression) { BooleanInterpreterValue condition = (BooleanInterpreterValue) eval(environment, conditionExpression); return condition.getValue(); } private Optional exec(List statements) { for (StatementNode statement : statements) { Optional result = exec(environment, statement); if (result.isPresent()) { return result; } } return Optional.empty(); } }