package org.zwobble.couscous.interpreter; import org.zwobble.couscous.ast.*; import org.zwobble.couscous.ast.visitors.DynamicNodeMapper; 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.function.Function; 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; import static org.zwobble.couscous.util.ExtraIterables.forEach; public class Executor implements StatementNodeMapper> { public static InterpreterValue callMethod( Environment environment, MethodNode method, Optional thisValue, Arguments 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, InterpreterValue thisValue, Arguments actualArguments) { return callMethod(environment, constructor.getArguments(), constructor.getBody(), Optional.of(thisValue), actualArguments); } private static InterpreterValue callMethod( Environment environment, List formalArguments, List statements, Optional thisValue, Arguments 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, Arguments arguments) { StackFrameBuilder stackFrame = new StackFrameBuilder(); forEach(formalArguments, arguments.getValues(), stackFrame::declare); 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(FindDirectDeclarations.VISITOR); } public static class FindDirectDeclarations { private static final Function> VISITOR = DynamicNodeMapper.instantiate(new FindDirectDeclarations(), "visit"); public Stream visit(Node node) { return Stream.empty(); } 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(ThrowNode throwNode) { throw new UnsupportedOperationException(); } @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(); } @Override public Optional visit(TryNode tryStatement) { throw new UnsupportedOperationException(); } private Optional exec(List statements) { for (StatementNode statement : statements) { Optional result = exec(environment, statement); if (result.isPresent()) { return result; } } return Optional.empty(); } }