Scope

To acess other functions and variables they need to be in the scope:

publicclassScope{privateList<Identifier>identifiers;//think of it as a variables for nowprivateList<FunctionSignature>functionSignatures;privatefinalMetaDatametaData;//currently stores only class namepublicScope(MetaDatametaData){identifiers=newArrayList<>();functionSignatures=newArrayList<>();this.metaData=metaData;}publicScope(Scopescope){metaData=scope.metaData;identifiers=Lists.newArrayList(scope.identifiers);functionSignatures=Lists.newArrayList(scope.functionSignatures);}//some other methods that expose data to the outside}

The scope object is created during class creation and passed to the children (functions).
Children copy Scope (using one of the constructors) and add some other items to it.

Signatures

When calling a method there must be some kind of information about it available.
Let’s say you have the following psudocode:

FunctionCall:f2 //ERROR! f2??! What is that? It’s not yet been declared!!

Function:f2

So the problem is that during method invokation the method might not yet been visited. There is no information about f2 during parsing f1!

To fix that problem it is mandatory to visit all Method Declarations and store their signatures in the scope:

publicclassClassVisitorextendsEnkelBaseVisitor<ClassDeclaration>{privateScopescope;@OverridepublicClassDeclarationvisitClassDeclaration(@NotNullEnkelParser.ClassDeclarationContextctx){Stringname=ctx.className().getText();FunctionSignatureVisitorfunctionSignatureVisitor=newFunctionSignatureVisitor();List<EnkelParser.FunctionContext>methodsCtx=ctx.classBody().function();MetaDatametaData=newMetaData(ctx.className().getText());scope=newScope(metaData);//First find all signaturesList<FunctionSignature>signatures=methodsCtx.stream().map(method->method.functionDeclaration().accept(functionSignatureVisitor)).peek(scope::addSignature).collect(Collectors.toList());//Once the signatures are found start parsing methodsList<Function>methods=methodsCtx.stream().map(method->method.accept(newFunctionVisitor(scope))).collect(Collectors.toList());returnnewClassDeclaration(name,methods);}}

Invokestatic

Once all the information about the codes has been parsed It is time to convert it to bytecode.
Since I haven not yet implemented object creation, methods need to be called in a static context.

intaccess=Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC;

The bytecode instruction for static method invokation is called invokestatic.
This instruction has two parameters which require:

Values from operand stack are assumed to be parameters (amount and type must match method descriptor).

publicclassMethodGenerator{privatefinalClassWriterclassWriter;publicMethodGenerator(ClassWriterclassWriter){this.classWriter=classWriter;}publicvoidgenerate(Functionfunction){Scopescope=function.getScope();Stringname=function.getName();Stringdescription=DescriptorFactory.getMethodDescriptor(function);Collection<Statement>instructions=function.getStatements();intaccess=Opcodes.ACC_PUBLIC+Opcodes.ACC_STATIC;MethodVisitormv=classWriter.visitMethod(access,name,description,null,null);mv.visitCode();StatementGeneratorstatementScopeGenrator=newStatementGenerator(mv);instructions.forEach(instr->statementScopeGenrator.generate(instr,scope));mv.visitInsn(Opcodes.RETURN);mv.visitMaxs(-1,-1);//asm autmatically calculate those but the call is requiredmv.visitEnd();}}