// Path: src/main/java/com/pidanic/saral/domain/ReturnStatement.java
// public class ReturnStatement implements Statement {
// 
//     private Expression expression;
// 
//     public ReturnStatement(Expression variable) {
//         this.expression = variable;
//     }
// 
//     @Override
//     public void accept(StatementGenerator generator) {
//         ((ReturnStatementGenerator) generator).generate(this);
//     }
// 
//     public Expression getExpression() {
//         return expression;
//     }
// }
// 
// Path: src/main/java/com/pidanic/saral/domain/Statement.java
// public interface Statement {
//     void accept(StatementGenerator generator);
// }
// 
// Path: src/main/java/com/pidanic/saral/generator/CallableStatementGenerator.java
// public class CallableStatementGenerator extends StatementGenerator {
// 
//     private final ClassWriter classWriter;
//     private final Scope scope;
// 
//     public CallableStatementGenerator(ClassWriter classWriter, Scope scope) {
//         super();
//         this.classWriter = classWriter;
//         this.scope = scope;
//     }
// 
//     public void generate(Function function) {
//         Scope scope = function.getScope();
//         String procedureName = function.getName();
// 
//         String descriptor = DescriptorFactory.getMethodDescriptor(function);
//         Collection<Statement> statements = function.getStatements();
//         int access = Opcodes.ACC_STATIC + Opcodes.ACC_PUBLIC;
//         MethodVisitor mw = classWriter.visitMethod(access, procedureName, descriptor, null, null);
//         mw.visitCode();
// 
//         StatementGenerator simpleStatementGenerator = new SimpleStatementGenerator(mw, scope);
//         StatementGenerator statementGenerator = new BlockStatementGenerator(mw, scope);
//         statements.forEach(statement -> {
//             if(statement instanceof SimpleStatement) {
//                 statement.accept(simpleStatementGenerator);
//             } else {
//                 statement.accept(statementGenerator);
//             }
//         });
// 
//         Optional<ReturnStatement> ret = function.getReturnStatement();
//         generateReturnStatement(scope, mw, ret);
//     }
// 
//     private void generateReturnStatement(Scope scope, MethodVisitor mw, Optional<ReturnStatement> returnStatement) {
//         ReturnStatementGenerator returnStatementGenerator = new ReturnStatementGenerator(scope, mw);
//         if(returnStatement.isPresent()) {
//             returnStatementGenerator.generate(returnStatement.get());
//         } else {
//             mw.visitInsn(Opcodes.RETURN);
//         }
//         mw.visitMaxs(-1, -1);
//         mw.visitEnd();
//     }
// }
// 
// Path: src/main/java/com/pidanic/saral/generator/StatementGenerator.java
// public class StatementGenerator {
// }
// 
// Path: src/main/java/com/pidanic/saral/scope/Scope.java
// public class Scope {
// 
//     private static final LocalVariable EMPTY = new LocalVariable("empty", BuiltInType.BOOLEAN, true);
// 
//     private List<LocalVariable> localVariables;
//     private List<Function> functions;
//     private String className;
// 
//     public Scope(String className) {
//         localVariables = new ArrayList<>();
//         functions = new ArrayList<>();
//         this.className = className;
//     }
// 
//     public Scope(Scope scope) {
//         localVariables = new ArrayList<>(scope.localVariables);
//         functions = new ArrayList<>(scope.functions);
//         className = scope.className;
//     }
// 
//     public List<LocalVariable> getLocalVariables() {
//         return Collections.unmodifiableList(localVariables);
//     }
// 
//     public void addLocalVariable(LocalVariable localVariable) {
//         localVariables.add(localVariable);
//         if(localVariable.type() == BuiltInType.LONG || localVariable.type() == BuiltInType.DOUBLE) {
//             localVariables.add(EMPTY);
//         }
//     }
// 
//     public boolean existsLocalVariable(String variableName) {
//         return localVariables.stream().anyMatch(variable -> variable.name().equals(variableName));
//     }
// 
//     public Optional<LocalVariable> getLocalVariable(String varName) {
//         return localVariables.stream()
//                 .filter(variable -> variable.name().equals(varName))
//                 .findFirst();
//     }
// 
//     public int getLocalVariableIndex(String varName) {
//         return localVariables.stream().map(LocalVariable::name).collect(Collectors.toList()).indexOf(varName);
//     }
// 
//     public Optional<Function> getFunction(String functionName) {
//         return functions.stream().filter(proc -> proc.getName().equals(functionName))
//                 .findFirst();
//     }
// 
//     public String getClassName() {
//         return className;
//     }
// 
//     public void addFunction(Function function) {
//         this.functions.add(function);
//     }
// 
//     public LocalVariable initializeLocalVariableAtIndex(int index) {
//         LocalVariable localVariable = this.localVariables.get(index);
//         LocalVariable initializedLocalVar = localVariable.initialize();
//         this.localVariables.set(index, initializedLocalVar);
//         return initializedLocalVar;
//     }
// 
//     public LocalVariable initializeLocalVariable(String name) {
//         LocalVariable localVariable = getLocalVariable(name).get();
//         int index = getLocalVariableIndex(name);
//         LocalVariable initializedLocalVar = localVariable.initialize();
//         this.localVariables.set(index, initializedLocalVar);
//         return initializedLocalVar;
//     }
// 
//     public int localVariablesCount() {
//         return localVariables.size();
//     }
// }
// 
// Path: src/main/java/com/pidanic/saral/util/Type.java
// public interface Type {
//     String getName();
//     Class<?> getTypeClass();
//     String getDescriptor();
//     String getInternalName();
//     TypeSpecificOpcodes getTypeSpecificOpcode();
// }


import com.pidanic.saral.domain.ReturnStatement;
import com.pidanic.saral.domain.Statement;
import com.pidanic.saral.generator.CallableStatementGenerator;
import com.pidanic.saral.generator.StatementGenerator;
import com.pidanic.saral.scope.Scope;
import com.pidanic.saral.util.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
package com.pidanic.saral.domain.block;



public class Function extends CallableStatement {
    private String name;
    private List<Argument> arguments;
    private List<Statement> statements;
// Path: src/main/java/com/pidanic/saral/domain/ReturnStatement.java
// public class ReturnStatement implements Statement {
// 
//     private Expression expression;
// 
//     public ReturnStatement(Expression variable) {
//         this.expression = variable;
//     }
// 
//     @Override
//     public void accept(StatementGenerator generator) {
//         ((ReturnStatementGenerator) generator).generate(this);
//     }
// 
//     public Expression getExpression() {
//         return expression;
//     }
// }
// 
// Path: src/main/java/com/pidanic/saral/domain/Statement.java
// public interface Statement {
//     void accept(StatementGenerator generator);
// }
// 
// Path: src/main/java/com/pidanic/saral/generator/CallableStatementGenerator.java
// public class CallableStatementGenerator extends StatementGenerator {
// 
//     private final ClassWriter classWriter;
//     private final Scope scope;
// 
//     public CallableStatementGenerator(ClassWriter classWriter, Scope scope) {
//         super();
//         this.classWriter = classWriter;
//         this.scope = scope;
//     }
// 
//     public void generate(Function function) {
//         Scope scope = function.getScope();
//         String procedureName = function.getName();
// 
//         String descriptor = DescriptorFactory.getMethodDescriptor(function);
//         Collection<Statement> statements = function.getStatements();
//         int access = Opcodes.ACC_STATIC + Opcodes.ACC_PUBLIC;
//         MethodVisitor mw = classWriter.visitMethod(access, procedureName, descriptor, null, null);
//         mw.visitCode();
// 
//         StatementGenerator simpleStatementGenerator = new SimpleStatementGenerator(mw, scope);
//         StatementGenerator statementGenerator = new BlockStatementGenerator(mw, scope);
//         statements.forEach(statement -> {
//             if(statement instanceof SimpleStatement) {
//                 statement.accept(simpleStatementGenerator);
//             } else {
//                 statement.accept(statementGenerator);
//             }
//         });
// 
//         Optional<ReturnStatement> ret = function.getReturnStatement();
//         generateReturnStatement(scope, mw, ret);
//     }
// 
//     private void generateReturnStatement(Scope scope, MethodVisitor mw, Optional<ReturnStatement> returnStatement) {
//         ReturnStatementGenerator returnStatementGenerator = new ReturnStatementGenerator(scope, mw);
//         if(returnStatement.isPresent()) {
//             returnStatementGenerator.generate(returnStatement.get());
//         } else {
//             mw.visitInsn(Opcodes.RETURN);
//         }
//         mw.visitMaxs(-1, -1);
//         mw.visitEnd();
//     }
// }
// 
// Path: src/main/java/com/pidanic/saral/generator/StatementGenerator.java
// public class StatementGenerator {
// }
// 
// Path: src/main/java/com/pidanic/saral/scope/Scope.java
// public class Scope {
// 
//     private static final LocalVariable EMPTY = new LocalVariable("empty", BuiltInType.BOOLEAN, true);
// 
//     private List<LocalVariable> localVariables;
//     private List<Function> functions;
//     private String className;
// 
//     public Scope(String className) {
//         localVariables = new ArrayList<>();
//         functions = new ArrayList<>();
//         this.className = className;
//     }
// 
//     public Scope(Scope scope) {
//         localVariables = new ArrayList<>(scope.localVariables);
//         functions = new ArrayList<>(scope.functions);
//         className = scope.className;
//     }
// 
//     public List<LocalVariable> getLocalVariables() {
//         return Collections.unmodifiableList(localVariables);
//     }
// 
//     public void addLocalVariable(LocalVariable localVariable) {
//         localVariables.add(localVariable);
//         if(localVariable.type() == BuiltInType.LONG || localVariable.type() == BuiltInType.DOUBLE) {
//             localVariables.add(EMPTY);
//         }
//     }
// 
//     public boolean existsLocalVariable(String variableName) {
//         return localVariables.stream().anyMatch(variable -> variable.name().equals(variableName));
//     }
// 
//     public Optional<LocalVariable> getLocalVariable(String varName) {
//         return localVariables.stream()
//                 .filter(variable -> variable.name().equals(varName))
//                 .findFirst();
//     }
// 
//     public int getLocalVariableIndex(String varName) {
//         return localVariables.stream().map(LocalVariable::name).collect(Collectors.toList()).indexOf(varName);
//     }
// 
//     public Optional<Function> getFunction(String functionName) {
//         return functions.stream().filter(proc -> proc.getName().equals(functionName))
//                 .findFirst();
//     }
// 
//     public String getClassName() {
//         return className;
//     }
// 
//     public void addFunction(Function function) {
//         this.functions.add(function);
//     }
// 
//     public LocalVariable initializeLocalVariableAtIndex(int index) {
//         LocalVariable localVariable = this.localVariables.get(index);
//         LocalVariable initializedLocalVar = localVariable.initialize();
//         this.localVariables.set(index, initializedLocalVar);
//         return initializedLocalVar;
//     }
// 
//     public LocalVariable initializeLocalVariable(String name) {
//         LocalVariable localVariable = getLocalVariable(name).get();
//         int index = getLocalVariableIndex(name);
//         LocalVariable initializedLocalVar = localVariable.initialize();
//         this.localVariables.set(index, initializedLocalVar);
//         return initializedLocalVar;
//     }
// 
//     public int localVariablesCount() {
//         return localVariables.size();
//     }
// }
// 
// Path: src/main/java/com/pidanic/saral/util/Type.java
// public interface Type {
//     String getName();
//     Class<?> getTypeClass();
//     String getDescriptor();
//     String getInternalName();
//     TypeSpecificOpcodes getTypeSpecificOpcode();
// }


// Path: src/main/java/com/pidanic/saral/domain/block/Function.java
import com.pidanic.saral.domain.ReturnStatement;
import com.pidanic.saral.domain.Statement;
import com.pidanic.saral.generator.CallableStatementGenerator;
import com.pidanic.saral.generator.StatementGenerator;
import com.pidanic.saral.scope.Scope;
import com.pidanic.saral.util.Type;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

package com.pidanic.saral.domain.block;



public class Function extends CallableStatement {
    private String name;
    private List<Argument> arguments;
    private List<Statement> statements;
    private ReturnStatement retStatement;