I have to visit parse tree and build symbol tables for functions by Antlr4.
I have to add some codes at SymbolTableVisitor class given behind with a skeleton code in B2CMain.cpp.

// B2CMain.cpp
#include <iostream>
#include <map>
#include <stack>
#include "antlr4-runtime.h"
#include "antlr4-cpp/BBaseVisitor.h"
#include "antlr4-cpp/BLexer.h"
#include "antlr4-cpp/BParser.h"

using namespace std;
using namespace antlr4;
using namespace antlr4::tree;

enum Types {tyAUTO, tyINT, tyDOUBLE, tySTRING, tyBOOL, tyCHAR, tyFUNCTION};
string mnemonicTypes[] = {"auto", "int", "double", "string", "bool", "char", "function"};

struct SymbolAttributes {
   Types type; // int, double, bool, char, string, function --- auto if unknown yet

   // if type == "function"
   vector<Types> retArgTypes; // first element is a return_type
};

class SymbolTable {
private:
    map<string, SymbolAttributes> table;  // symbol-name: string, symbol-typeInfo: SymbolAttributes

public:
    // Add a new symbol 
    void addSymbol(const string& name, const SymbolAttributes& attributes) {
		table[name] = attributes; 
    }

    // Check if a symbol exists
    bool symbolExists(const string& name) const {
        return table.find(name) != table.end();
    }

    // Get attributes of a symbol
    SymbolAttributes getSymbolAttributes(const string& name) const {
        if (symbolExists(name)) {
            return table.at(name);
        } else {
            cout << "Error: Symbol " << name << " not found" << endl;
        }
    }

    // Remove a symbol from the table
    void removeSymbol(const string& name) {
        table.erase(name);
    }

    // Print all symbols in the table (for debugging purposes)
    void printSymbols() const {
        for (const auto& pair : table) {
            cout << "(name) " << pair.first << ", (type) " << mnemonicTypes[pair.second.type];
			if (pair.second.type == tyFUNCTION) {
				cout << " | ";
				int n = pair.second.retArgTypes.size();
				if (n > 0) {
					cout << mnemonicTypes[pair.second.retArgTypes[0]] << "("; // return type
				}
				for (int i = 1; i < n-1; i++)
					cout << mnemonicTypes[pair.second.retArgTypes[i]] << ", ";
				if (n > 1) {
					cout << mnemonicTypes[pair.second.retArgTypes[n-1]]; // last arg type
				}
				cout << ")";
			} 
	    	cout << endl;
        }
	}
};



/*
 * STEP 1. build symbol table
 */
const string _GlobalFuncName_ = "$_global_$";

// collection of per-function symbol tables accessed by function name
// symbol table in global scope can be accessed with special name defined in _GlobalFuncName_
map<string, SymbolTable*> symTabs;


class SymbolTableVisitor : public BBaseVisitor {
private:
   int scopeLevel;
   string curFuncName;
   int blockCounter; // 추가 코드
   
public:
	SymbolTableVisitor(): scopeLevel(0), blockCounter(1) {}; // 추가 코드
	// Building symbol tables by visiting tree
	
	any visitProgram(BParser::ProgramContext *ctx) override {
		scopeLevel = 0; // global scope
			
		// prepare symbol table for global scope
		SymbolTable* globalSymTab = new SymbolTable();
		curFuncName = _GlobalFuncName_;
    	symTabs[curFuncName] = globalSymTab;

		// visit children
    	for (int i=0; i< ctx->children.size(); i++) {
    		visit(ctx->children[i]);
    	}

		// print all symbol tables
		for (auto& pair : symTabs) {
	    	cout << "--- Symbol Table --- " << pair.first << endl; // function name
	    	pair.second->printSymbols();					   // per-function symbol table
			cout << "";
		}

    	return nullptr;
	}

    any visitDefinition(BParser::DefinitionContext *ctx) override {
		visit(ctx->children[0]);
        return nullptr;
	}

    any visitAutostmt(BParser::AutostmtContext *ctx) override {
    	// get current symbol table
		SymbolTable *stab = symTabs[curFuncName];

		// You can retrieve the variable names and constants using ctx->name(i) and ctx->constant(i)
		for (int i=0, j=0; i < ctx->name().size(); i++) {
			
			string varName = ctx->name(i)->getText();
			enum Types varType = tyAUTO;				// default type

			// if initialized, get constant type
			int idx_assn = 1 + i*2 + j*2 + 1;  // auto name (= const)?, name (= const)?, ...
			if (ctx->children[idx_assn]->getText().compare("=") == 0) { 
				if (ctx->constant(j)) {  
					varType = any_cast<Types>( visit(ctx->constant(j)) );   // returns init constant type
					j++;
				}
			}

			stab->addSymbol(varName, {varType});
		}
    	return nullptr;
    }

	/* 추가 시작 ------------------------------------------------------------------------------------------ */
	any visitBlockstmt(BParser::BlockstmtContext *ctx) override {
		scopeLevel++;
		string blockName = curFuncName + "_$" + to_string(blockCounter++);
		SymbolTable* blockSymTab = new SymbolTable();
    	symTabs[blockName] = blockSymTab;
		
		for (auto stmt : ctx->statement())
			visit(stmt);

		scopeLevel--;
		return nullptr;
	}

	any visitIfstmt(BParser::IfstmtContext *ctx) override {
		visit(ctx->expr());
		visit(ctx->statement(0));

		if (ctx->ELSE())
			visit(ctx->statement(1));

		return nullptr;
	}

	any visitWhilestmt(BParser::WhilestmtContext *ctx) override {
		visit(ctx->expr());
		visit(ctx->statement());
		return nullptr;
	}

	any visitFuncdef(BParser::FuncdefContext *ctx) override {
		string functionName = ctx->name(0)->getText();
		curFuncName = functionName;	// 현재 함수 이름

		// Symbol Table 위한 type 저장하기
		vector<Types> retArgTypes;
		retArgTypes.push_back(tyAUTO);
		for (int i = 1; i < ctx->name().size(); i++)
			retArgTypes.push_back(tyAUTO);
		
		// global의 Symbol Table
		SymbolTable* globalSymTab = symTabs[_GlobalFuncName_];
		globalSymTab->addSymbol(functionName, {tyFUNCTION, retArgTypes});

		// 현재 함수의 Symbol Table
		SymbolTable* funcSymTab = new SymbolTable();
		symTabs[functionName] = funcSymTab;
		
		visit(ctx->blockstmt());	// visit blockstmt

		curFuncName = _GlobalFuncName_;	// 함수 종료, Global로 돌아가기
		return nullptr;
	}
	/* 추가 종료 ------------------------------------------------------------------------------------------ */

    any visitConstant(BParser::ConstantContext *ctx) override {
        
		if (ctx->INT()) return tyINT;
		else if (ctx->REAL()) return tyDOUBLE;
		else if (ctx->STRING()) return tySTRING;
		else if (ctx->BOOL()) return tyBOOL;
		else if (ctx->CHAR()) return tyCHAR;

		cout << "[ERROR] unrecognizable constant is used for initialization: " << ctx->children[0]->getText() << endl;
		exit(-1);
        return nullptr;
    }
};

/*
 * STEP 2. infer type
 */   
class TypeAnalysisVisitor : public BBaseVisitor {
public:
   // infer types for 'auto' variables and functions
   // ...
};

/*
 * STEP 3. print code
 */
class PrintTreeVisitor : public BBaseVisitor {
public:
    any visitProgram(BParser::ProgramContext *ctx) override {
    	// Perform some actions when visiting the program
    	for (int i=0; i< ctx->children.size(); i++) {
      	    visit(ctx->children[i]);
    	}
    	return nullptr;
    }
    
    any visitDirective(BParser::DirectiveContext *ctx) override {
		cout << ctx->SHARP_DIRECTIVE()->getText();
		cout << endl;
        return nullptr;
    }

    any visitDefinition(BParser::DefinitionContext *ctx) override {
		visit(ctx->children[0]);
        return nullptr;
    }

    any visitFuncdef(BParser::FuncdefContext *ctx) override {
		// Handle function definition
        string functionName = ctx->name(0)->getText();
		cout << "auto " << functionName << "(" ;
        // You can retrieve and visit the parameter list using ctx->name(i)
		for (int i=1; i < ctx->name().size(); i++) {
			if (i != 1) cout << ", ";
			cout << "auto " << ctx->name(i)->getText();		
		}
		cout << ")";

		// visit blockstmt
		visit(ctx->blockstmt());
        return nullptr;
    }

    any visitStatement(BParser::StatementContext *ctx) override {
		visit(ctx->children[0]);
        return nullptr;
    }

    any visitAutostmt(BParser::AutostmtContext *ctx) override {
    	// You can retrieve the variable names and constants using ctx->name(i) and ctx->constant(i)
		cout << "auto ";
		for (int i=0, j=0; i < ctx->name().size(); i++) {
			if (i != 0) cout << " ,";
			cout << ctx->name(i)->getText();

			int idx_assn = 1 + i*2 + j*2 + 1;  // auto name (= const)?, name (= const)?, ...
			if (ctx->children[idx_assn]->getText().compare("=") == 0) { 
				if (ctx->constant(j)) {
					cout << " = ";    
					visit(ctx->constant(j));
					j++;
				}
			}
		}
		cout << ";" << endl;
    	return nullptr;
    }

    any visitDeclstmt(BParser::DeclstmtContext *ctx) override {
		// Handle function declaration
        string functionName = ctx->name()->getText();
		cout << "auto " << functionName << "(" ;
        
		// You can retrieve and visit the parameter type list
		for (int i=1; i < ctx->AUTO().size(); i++) {
			if (i != 1) cout << ", ";
			cout << "auto ";		
		}
		cout << ");" << endl;
        return nullptr;
    }

    any visitBlockstmt(BParser::BlockstmtContext *ctx) override {
    	// Perform some actions when visiting a block statement
		cout << "{" << endl;
    	for (auto stmt : ctx->statement()) {
      	    visit(stmt);
    	}
		cout << "}" << endl;
    	return nullptr;
    }

    any visitIfstmt(BParser::IfstmtContext *ctx) override {
		cout << "if (";
		visit(ctx->expr());
		cout << ") " ;

		visit(ctx->statement(0));
		if (ctx->ELSE()) {
	   		cout << endl << "else ";
	   		visit(ctx->statement(1));
		}
        return nullptr;
    }

    any visitWhilestmt(BParser::WhilestmtContext *ctx) override {
        cout << "while (";
        visit(ctx->expr());
        cout << ") ";
        visit(ctx->statement());
        return nullptr;
    }

    any visitExpressionstmt(BParser::ExpressionstmtContext *ctx) override {
		visit(ctx->expression());
		cout << ";" << endl;
        return nullptr;
    }

    any visitReturnstmt(BParser::ReturnstmtContext *ctx) override {
		cout << "return";
		if (ctx->expression()) {
			cout << " (";
			visit(ctx->expression());
			cout << ")";
		}
		cout << ";" << endl;
        return nullptr;
    }

    any visitNullstmt(BParser::NullstmtContext *ctx) override {
		cout << ";" << endl;
        return nullptr;
    }

    any visitExpr(BParser::ExprContext *ctx) override {
		// unary operator
        if(ctx->atom()) {
            if (ctx->PLUS()) cout << "+";
            else if (ctx->MINUS()) cout << "-";
	    	else if (ctx->NOT()) cout << "!";
	    	visit(ctx->atom()); 
        }
		// binary operator
		else if (ctx->MUL() || ctx->DIV() || ctx->PLUS() || ctx->MINUS() || 
		 		ctx->GT() || ctx->GTE() || ctx->LT() || ctx->LTE() || ctx->EQ() || ctx->NEQ() ||
		 		ctx->AND() || ctx->OR() ) {
	    	visit(ctx->expr(0));
	    	cout << " " << ctx->children[1]->getText() << " "; // print binary operator
	    	visit(ctx->expr(1));
		}
		// ternary operator
		else if (ctx->QUEST()) {
			visit(ctx->expr(0));
			cout << " ? ";
			visit(ctx->expr(1));
			cout << " : ";
			visit(ctx->expr(2));
		}
		else {
			int lineNum = ctx->getStart()->getLine();
			cerr << endl << "[ERROR] visitExpr: unrecognized ops in Line " << lineNum << " --" << ctx->children[1]->getText() << endl;
			exit(-1); // error
        }	
        return nullptr;
    }
   
    any visitAtom(BParser::AtomContext *ctx) override {
		if (ctx->expression()) { // ( expression )
			cout << "(";
			visit(ctx->expression());
			cout << ")";
		}
		else	// name | constant | funcinvocation
			visit(ctx->children[0]);
        return nullptr;
    }
    
    any visitExpression(BParser::ExpressionContext *ctx) override {
        if (ctx->ASSN()) { // assignment
	   		visit(ctx->name());
	  		 cout << " = ";
		}
		visit(ctx->expr());
        return nullptr;
    }

    any visitFuncinvocation(BParser::FuncinvocationContext *ctx) override {
		cout << ctx->name()->getText() << "(";
		for (int i=0; i < ctx->expr().size(); i++) {
			if (i != 0) cout << ", ";
			visit(ctx->expr(i));
		}
		cout << ")";
        return nullptr;
    }
    
    any visitConstant(BParser::ConstantContext *ctx) override {
        cout << ctx->children[0]->getText();
        return nullptr;
    }
    
    any visitName(BParser::NameContext *ctx) override {
		cout << ctx->NAME()->getText();
        return nullptr;
    }
};

int main(int argc, const char* argv[]) {
    if (argc < 2) {
        cerr << "[Usage] " << argv[0] << "  <input-file>\n";
        exit(0);
    }
    std::ifstream stream;
    stream.open(argv[1]);
    if (stream.fail()) {
        cerr << argv[1] << " : file open fail\n";
        exit(0);
    }

	//cout << "/*-- B2C ANTLR visitor --*/\n";

	ANTLRInputStream inputStream(stream);
	BLexer lexer(&inputStream);
	CommonTokenStream tokenStream(&lexer);
	BParser parser(&tokenStream);
	ParseTree* tree = parser.program();

	// STEP 1. visit parse tree and build symbol tables for functions (PA#1)
	cout << endl << "/*** STEP 1. BUILD SYM_TABS *************" << endl;
	SymbolTableVisitor SymtabTree;
	SymtabTree.visit(tree);
	cout <<         " ---    end of step 1       ------------*/" << endl;

	// STEP 2. visit parse tree and perform type inference for 'auto' typed variables and functions (PA#2)
	cout << endl << "/*** STEP 2. ANALYZE TYPES  *************" << endl;
	TypeAnalysisVisitor AnalyzeTree;
	AnalyzeTree.visit(tree);
	cout <<         " ---    end of step 2       ------------*/" << endl;

	// STEP 3. visit parse tree and print out C code with correct types
	cout << endl << "/*** STEP 3. TRANSFORM to C *************/" << endl;
	PrintTreeVisitor PrintTree;
	PrintTree.visit(tree);

	return 0;
}







And this is the code of BParser.h

// Generated from B.g4 by ANTLR 4.13.1
#pragma once
#include "antlr4-runtime.h"

class  BParser : public antlr4::Parser {
public:
  enum {
    T__0 = 1, T__1 = 2, T__2 = 3, T__3 = 4, T__4 = 5, AUTO = 6, PLUS = 7, 
    MINUS = 8, MUL = 9, DIV = 10, NOT = 11, GT = 12, GTE = 13, LT = 14, 
    LTE = 15, EQ = 16, NEQ = 17, AND = 18, OR = 19, QUEST = 20, COLON = 21, 
    SEMI = 22, IF = 23, ELSE = 24, WHILE = 25, RETURN = 26, ASSN = 27, BOOL = 28, 
    NAME = 29, INT = 30, REAL = 31, STRING = 32, CHAR = 33, SHARP_DIRECTIVE = 34, 
    BLOCKCOMMENT = 35, LINECOMMENT = 36, WS = 37
  };

  enum {
    RuleProgram = 0, RuleDirective = 1, RuleDefinition = 2, RuleAutostmt = 3, 
    RuleDeclstmt = 4, RuleFuncdef = 5, RuleBlockstmt = 6, RuleStatement = 7, 
    RuleIfstmt = 8, RuleWhilestmt = 9, RuleExpressionstmt = 10, RuleReturnstmt = 11, 
    RuleNullstmt = 12, RuleExpr = 13, RuleAtom = 14, RuleExpression = 15, 
    RuleFuncinvocation = 16, RuleConstant = 17, RuleName = 18
  };

  explicit BParser(antlr4::TokenStream *input);

  BParser(antlr4::TokenStream *input, const antlr4::atn::ParserATNSimulatorOptions &options);

  ~BParser() override;

  std::string getGrammarFileName() const override;

  const antlr4::atn::ATN& getATN() const override;

  const std::vector<std::string>& getRuleNames() const override;

  const antlr4::dfa::Vocabulary& getVocabulary() const override;

  antlr4::atn::SerializedATNView getSerializedATN() const override;


  class ProgramContext;
  class DirectiveContext;
  class DefinitionContext;
  class AutostmtContext;
  class DeclstmtContext;
  class FuncdefContext;
  class BlockstmtContext;
  class StatementContext;
  class IfstmtContext;
  class WhilestmtContext;
  class ExpressionstmtContext;
  class ReturnstmtContext;
  class NullstmtContext;
  class ExprContext;
  class AtomContext;
  class ExpressionContext;
  class FuncinvocationContext;
  class ConstantContext;
  class NameContext; 

  class  ProgramContext : public antlr4::ParserRuleContext {
  public:
    ProgramContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    antlr4::tree::TerminalNode *EOF();
    std::vector<DirectiveContext *> directive();
    DirectiveContext* directive(size_t i);
    std::vector<DefinitionContext *> definition();
    DefinitionContext* definition(size_t i);


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  ProgramContext* program();

  class  DirectiveContext : public antlr4::ParserRuleContext {
  public:
    DirectiveContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    antlr4::tree::TerminalNode *SHARP_DIRECTIVE();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  DirectiveContext* directive();

  class  DefinitionContext : public antlr4::ParserRuleContext {
  public:
    DefinitionContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    AutostmtContext *autostmt();
    DeclstmtContext *declstmt();
    FuncdefContext *funcdef();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  DefinitionContext* definition();

  class  AutostmtContext : public antlr4::ParserRuleContext {
  public:
    AutostmtContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    antlr4::tree::TerminalNode *AUTO();
    std::vector<NameContext *> name();
    NameContext* name(size_t i);
    antlr4::tree::TerminalNode *SEMI();
    std::vector<antlr4::tree::TerminalNode *> ASSN();
    antlr4::tree::TerminalNode* ASSN(size_t i);
    std::vector<ConstantContext *> constant();
    ConstantContext* constant(size_t i);


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  AutostmtContext* autostmt();

  class  DeclstmtContext : public antlr4::ParserRuleContext {
  public:
    DeclstmtContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    std::vector<antlr4::tree::TerminalNode *> AUTO();
    antlr4::tree::TerminalNode* AUTO(size_t i);
    NameContext *name();
    antlr4::tree::TerminalNode *SEMI();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  DeclstmtContext* declstmt();

  class  FuncdefContext : public antlr4::ParserRuleContext {
  public:
    FuncdefContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    std::vector<antlr4::tree::TerminalNode *> AUTO();
    antlr4::tree::TerminalNode* AUTO(size_t i);
    std::vector<NameContext *> name();
    NameContext* name(size_t i);
    BlockstmtContext *blockstmt();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  FuncdefContext* funcdef();

  class  BlockstmtContext : public antlr4::ParserRuleContext {
  public:
    BlockstmtContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    std::vector<StatementContext *> statement();
    StatementContext* statement(size_t i);


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  BlockstmtContext* blockstmt();

  class  StatementContext : public antlr4::ParserRuleContext {
  public:
    StatementContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    AutostmtContext *autostmt();
    DeclstmtContext *declstmt();
    BlockstmtContext *blockstmt();
    IfstmtContext *ifstmt();
    WhilestmtContext *whilestmt();
    ExpressionstmtContext *expressionstmt();
    ReturnstmtContext *returnstmt();
    NullstmtContext *nullstmt();
    DirectiveContext *directive();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  StatementContext* statement();

  class  IfstmtContext : public antlr4::ParserRuleContext {
  public:
    IfstmtContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    antlr4::tree::TerminalNode *IF();
    ExprContext *expr();
    std::vector<StatementContext *> statement();
    StatementContext* statement(size_t i);
    antlr4::tree::TerminalNode *ELSE();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  IfstmtContext* ifstmt();

  class  WhilestmtContext : public antlr4::ParserRuleContext {
  public:
    WhilestmtContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    antlr4::tree::TerminalNode *WHILE();
    ExprContext *expr();
    StatementContext *statement();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  WhilestmtContext* whilestmt();

  class  ExpressionstmtContext : public antlr4::ParserRuleContext {
  public:
    ExpressionstmtContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    ExpressionContext *expression();
    antlr4::tree::TerminalNode *SEMI();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  ExpressionstmtContext* expressionstmt();

  class  ReturnstmtContext : public antlr4::ParserRuleContext {
  public:
    ReturnstmtContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    antlr4::tree::TerminalNode *RETURN();
    antlr4::tree::TerminalNode *SEMI();
    ExpressionContext *expression();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  ReturnstmtContext* returnstmt();

  class  NullstmtContext : public antlr4::ParserRuleContext {
  public:
    NullstmtContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    antlr4::tree::TerminalNode *SEMI();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  NullstmtContext* nullstmt();

  class  ExprContext : public antlr4::ParserRuleContext {
  public:
    ExprContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    AtomContext *atom();
    antlr4::tree::TerminalNode *PLUS();
    antlr4::tree::TerminalNode *MINUS();
    antlr4::tree::TerminalNode *NOT();
    std::vector<ExprContext *> expr();
    ExprContext* expr(size_t i);
    antlr4::tree::TerminalNode *MUL();
    antlr4::tree::TerminalNode *DIV();
    antlr4::tree::TerminalNode *GT();
    antlr4::tree::TerminalNode *GTE();
    antlr4::tree::TerminalNode *LT();
    antlr4::tree::TerminalNode *LTE();
    antlr4::tree::TerminalNode *EQ();
    antlr4::tree::TerminalNode *NEQ();
    antlr4::tree::TerminalNode *AND();
    antlr4::tree::TerminalNode *OR();
    antlr4::tree::TerminalNode *QUEST();
    antlr4::tree::TerminalNode *COLON();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  ExprContext* expr();
  ExprContext* expr(int precedence);
  class  AtomContext : public antlr4::ParserRuleContext {
  public:
    AtomContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    NameContext *name();
    ConstantContext *constant();
    ExpressionContext *expression();
    FuncinvocationContext *funcinvocation();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  AtomContext* atom();

  class  ExpressionContext : public antlr4::ParserRuleContext {
  public:
    ExpressionContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    ExprContext *expr();
    NameContext *name();
    antlr4::tree::TerminalNode *ASSN();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  ExpressionContext* expression();

  class  FuncinvocationContext : public antlr4::ParserRuleContext {
  public:
    FuncinvocationContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    NameContext *name();
    std::vector<ExprContext *> expr();
    ExprContext* expr(size_t i);


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  FuncinvocationContext* funcinvocation();

  class  ConstantContext : public antlr4::ParserRuleContext {
  public:
    ConstantContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    antlr4::tree::TerminalNode *INT();
    antlr4::tree::TerminalNode *REAL();
    antlr4::tree::TerminalNode *STRING();
    antlr4::tree::TerminalNode *BOOL();
    antlr4::tree::TerminalNode *CHAR();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  ConstantContext* constant();

  class  NameContext : public antlr4::ParserRuleContext {
  public:
    NameContext(antlr4::ParserRuleContext *parent, size_t invokingState);
    virtual size_t getRuleIndex() const override;
    antlr4::tree::TerminalNode *NAME();


    virtual std::any accept(antlr4::tree::ParseTreeVisitor *visitor) override;
   
  };

  NameContext* name();


  bool sempred(antlr4::RuleContext *_localctx, size_t ruleIndex, size_t predicateIndex) override;

  bool exprSempred(ExprContext *_localctx, size_t predicateIndex);

  // By default the static state used to implement the parser is lazily initialized during the first
  // call to the constructor. You can call this function if you wish to initialize the static state
  // ahead of time.
  static void initialize();

private:
};






This is a content of input0.b .
/* input0.b */

auto a, b = 10;

auto fn(auto, auto);

auto main() 
{
    auto i, j, phi = 3.14;
    auto a;

    {
        auto x;
    }

    while(true) {
        auto y;

        if (y) {
            auto z;
        } else {

        }
    }
}

auto fn(auto x, auto y) {
    
}




When I execute this code with ./b2c input0.b ,
--- Symbol Table --- $_global_$
(name) a, (type) auto
(name) b, (type) int
(name) fn, (type) function | auto(auto, auto)
(name) main, (type) function | auto()
--- Symbol Table --- fn
--- Symbol Table --- fn_$6
--- Symbol Table --- main
(name) a, (type) auto
(name) i, (type) auto
(name) j, (type) auto
(name) phi, (type) double
(name) x, (type) auto
(name) y, (type) auto
(name) z, (type) auto
--- Symbol Table --- main_$1
--- Symbol Table --- main_$2
--- Symbol Table --- main_$3
--- Symbol Table --- main_$4
--- Symbol Table --- main_$5

is printed in Step 1.



I should modify the code to print the output 
--- Symbol Table --- $_global_$
(name) a, (type) auto
(name) b, (type) int
(name) fn, (type) function | auto (auto, auto)
(name) main, (type) function | auto ()

--- Symbol Table --- main
(name) i, (type) auto
(name) j, (type) auto
(name) phi, (type) double
(name) a, (type) auto

--- Symbol Table --- main_$1
(name) x, (type) auto

--- Symbol Table --- main_$2
(name) y, (type) auto

--- Symbol Table --- main_$2_1
(name) z, (type) auto

--- Symbol Table --- main_$2_2

--- Symbol Table --- fn
(name) x, (type) auto
(name) y, (type) auto 

to be printed in Step 1.




I think I have to control all the scopes (such as if statement, while statement, {}, ...) in making symbol table. How can I modify this SymbolTableVisitor class code to activate the output in Step 1 correctly?