from Grammar.DecafVisitor import DecafVisitor
from Grammar.DecafParser import DecafParser
import inspect
from stack import DecafStack
from errors import genericError
from evaluador import Evaluator
from symbolTable import *

class CustomVisitor(DecafVisitor):

    def __init__(self):
        self.scope = DecafStack()
        self.saved = {}
        self.errors = []
        self.anonCounter = 0
        self.offset = 0
        self.TypeValidator = Evaluator(scopes=self.scope)

    def error(self):
        self.flag = True
    
    def enterScope(self, name, t='scope'):
        parent = self.scope.peek()
        st = STable(name, parent=parent, stype=t, tt=TypeTable(), entrys={})
        self.scope.push(st)

    # pops the current scope off the stack
    def exitScope(self):
        if self.scope.isEmpty():
            pass
        else:
            popped = self.scope.pop()
            self.saved[popped.name] = popped
            return popped

    # getError method to return error message as string
    def getError(self, name):
        return "DECLARATION ERROR " + name


    #Inicio de los metodos que si hacen algo

    # Visit a parse tree produced by DecafParser#program.
    def visitProgram(self, ctx:DecafParser.ProgramContext):
        self.enterScope('GLOBAL')
        return self.visitChildren(ctx)

    # Visit a parse tree produced by DecafParser#singleVar.
    def visitSingleVar(self, ctx:DecafParser.SingleVarContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        type_text = ctx.vType.getText()

        vartype = scope.typeExists(type_text.replace('struct', '')).name

        # if is inside struct
        if scope.scopeType == 'struct':
            # add to type table under structs name
            tt = scope.parent.typeTable
            param = Symbol(name, vartype, 0)

            # Add size and parameter to struct type in parent scope
            tt.addSize(name, tt.getSize(type_text))
            tt.addParam(scope.name, param)
            return self.visitChildren(ctx)

        # if is struct declaration
        elif 'struct' in type_text:
            # add struct to symbol table
            sName = type_text.replace('struct', '')

            struct = Symbol(name, sName, self.offset)
            scope.add(struct)

            structParams = scope.typeTable.getParams(sName)
            for param in structParams.values():
                s = Symbol(sName + param.name, param.stype, self.offset)
                self.offset += scope.typeTable.getSize(sName)
                scope.add(s)
            
            return self.visitChildren(ctx)

        #else is just normal var, add symbol to table
        s = Symbol(name, vartype, self.offset)
        self.offset += scope.typeTable.getSize(type_text)
        scope.add(s)

        return self.visitChildren(ctx)


    # Visit a parse tree produced by DecafParser#listVar.
    def visitListVar(self, ctx:DecafParser.ListVarContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        vartype = ctx.vType.getText().replace('struct', '')

        size = int(str(ctx.NUM()))

        if scope.scopeType == 'struct':
            t = scope.parent.typeExists(vartype)
            tt = scope.parent.typeTable
            param = Symbol(name, t.name, 0, listSize=size)
            
            tt.addSize(name, t.size * size)
            tt.addParam(scope.name, param)
            return self.visitChildren(ctx)

        s = Symbol(name, vartype, self.offset, listSize=size)
        self.offset += (scope.typeExists(vartype).size * size)
        scope.add(s)

        return self.visitChildren(ctx)


    # Visit a parse tree produced by DecafParser#structDeclaration.
    def visitStructDeclaration(self, ctx:DecafParser.StructDeclarationContext):
        name = str(ctx.ID())
        scope = self.scope.peek()

        self.enterScope(name, 'struct')

        s = TypeItem(name, 0, type_enum.Struct, {})
        scope.addType(s)
        visited = self.visitChildren(ctx)

        self.exitScope()
        return visited


    # Visit a parse tree produced by DecafParser#methodDeclaration.
    def visitMethodDeclaration(self, ctx:DecafParser.MethodDeclarationContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        returnType = ctx.returnType.getText()
        s = TypeItem(name, 0, 'method', {}, scope.typeExists(returnType).name)
        
        for param in ctx.parameter():
            values, size = self.visitParameter(param)
            s.addParam(values)
            # size = scope.typeTable.getSize(values.stype)
            s.size += size

        scope.addType(s)

        # enter method scope
        self.enterScope(name, 'method')

        # visit
        visit = self.visitChildren(ctx)

        self.exitScope()
        
        return visit
    
    # Visit a parse tree produced by DecafParser#parameter.
    def visitParameter(self, ctx:DecafParser.ParameterContext):
        name = str(ctx.ID())
        scope = self.scope.peek()

        type_text = ctx.vType.getText()

        vartype = scope.typeExists(type_text)
        
        s = Symbol(name, vartype.name, self.offset, param=True)
        self.offset += vartype.size

        scope.add(s)

        return s, vartype.size

    # Visit a parse tree produced by DecafParser#ifStmt.
    def visitIfStmt(self, ctx:DecafParser.IfStmtContext):
        self.enterScope('ifblock' + str(self.anonCounter), 'if')
        self.anonCounter += 1

        expt = self.TypeValidator.visit(ctx.expression())
        if expt != type_enum.Boolean:
            error = genericError('Expected boolean expression for if', ctx.start.line)
            self.TypeValidator.errors.append(error)

        visit = self.visitChildren(ctx)
        
        self.exitScope()
        return visit

    # Visit a parse tree produced by DecafParser#whileStmt.
    def visitWhileStmt(self, ctx:DecafParser.WhileStmtContext):
        self.enterScope('whileblock' + str(self.anonCounter), 'if')
        self.anonCounter += 1

        expt = self.TypeValidator.visit(ctx.expression())

        if expt != type_enum.Boolean:
            error = genericError('Expected boolean expression for while', ctx.start.line)
            self.TypeValidator.errors.append(error)

        visit = self.visitChildren(ctx)
        
        self.exitScope()
        return visit
    
    #Operations
    def visitRelationOp(self, ctx:DecafParser.RelationOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)
    
    def visitConditionalOp(self, ctx:DecafParser.ConditionalOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)
    
    def visitEqualityOp(self, ctx:DecafParser.EqualityOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)
    
    def visitHigherArithOp(self, ctx:DecafParser.HigherArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitArithOp(self, ctx:DecafParser.ArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)
    
    #Assign
    def visitAssignStmt(self, ctx:DecafParser.ArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)
7120720d764af0e1cd47372339a4514bcc02848d
<mask token>


class CustomVisitor(DecafVisitor):
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>
    <mask token>

    def visitStructDeclaration(self, ctx: DecafParser.StructDeclarationContext
        ):
        name = str(ctx.ID())
        scope = self.scope.peek()
        self.enterScope(name, 'struct')
        s = TypeItem(name, 0, type_enum.Struct, {})
        scope.addType(s)
        visited = self.visitChildren(ctx)
        self.exitScope()
        return visited
    <mask token>
    <mask token>
    <mask token>

    def visitWhileStmt(self, ctx: DecafParser.WhileStmtContext):
        self.enterScope('whileblock' + str(self.anonCounter), 'if')
        self.anonCounter += 1
        expt = self.TypeValidator.visit(ctx.expression())
        if expt != type_enum.Boolean:
            error = genericError('Expected boolean expression for while',
                ctx.start.line)
            self.TypeValidator.errors.append(error)
        visit = self.visitChildren(ctx)
        self.exitScope()
        return visit
    <mask token>
    <mask token>

    def visitEqualityOp(self, ctx: DecafParser.EqualityOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitHigherArithOp(self, ctx: DecafParser.HigherArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)
    <mask token>

    def visitAssignStmt(self, ctx: DecafParser.ArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

<mask token>


class CustomVisitor(DecafVisitor):
    <mask token>
    <mask token>

    def enterScope(self, name, t='scope'):
        parent = self.scope.peek()
        st = STable(name, parent=parent, stype=t, tt=TypeTable(), entrys={})
        self.scope.push(st)
    <mask token>
    <mask token>

    def visitProgram(self, ctx: DecafParser.ProgramContext):
        self.enterScope('GLOBAL')
        return self.visitChildren(ctx)

    def visitSingleVar(self, ctx: DecafParser.SingleVarContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        type_text = ctx.vType.getText()
        vartype = scope.typeExists(type_text.replace('struct', '')).name
        if scope.scopeType == 'struct':
            tt = scope.parent.typeTable
            param = Symbol(name, vartype, 0)
            tt.addSize(name, tt.getSize(type_text))
            tt.addParam(scope.name, param)
            return self.visitChildren(ctx)
        elif 'struct' in type_text:
            sName = type_text.replace('struct', '')
            struct = Symbol(name, sName, self.offset)
            scope.add(struct)
            structParams = scope.typeTable.getParams(sName)
            for param in structParams.values():
                s = Symbol(sName + param.name, param.stype, self.offset)
                self.offset += scope.typeTable.getSize(sName)
                scope.add(s)
            return self.visitChildren(ctx)
        s = Symbol(name, vartype, self.offset)
        self.offset += scope.typeTable.getSize(type_text)
        scope.add(s)
        return self.visitChildren(ctx)
    <mask token>

    def visitStructDeclaration(self, ctx: DecafParser.StructDeclarationContext
        ):
        name = str(ctx.ID())
        scope = self.scope.peek()
        self.enterScope(name, 'struct')
        s = TypeItem(name, 0, type_enum.Struct, {})
        scope.addType(s)
        visited = self.visitChildren(ctx)
        self.exitScope()
        return visited
    <mask token>

    def visitParameter(self, ctx: DecafParser.ParameterContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        type_text = ctx.vType.getText()
        vartype = scope.typeExists(type_text)
        s = Symbol(name, vartype.name, self.offset, param=True)
        self.offset += vartype.size
        scope.add(s)
        return s, vartype.size
    <mask token>

    def visitWhileStmt(self, ctx: DecafParser.WhileStmtContext):
        self.enterScope('whileblock' + str(self.anonCounter), 'if')
        self.anonCounter += 1
        expt = self.TypeValidator.visit(ctx.expression())
        if expt != type_enum.Boolean:
            error = genericError('Expected boolean expression for while',
                ctx.start.line)
            self.TypeValidator.errors.append(error)
        visit = self.visitChildren(ctx)
        self.exitScope()
        return visit
    <mask token>

    def visitConditionalOp(self, ctx: DecafParser.ConditionalOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitEqualityOp(self, ctx: DecafParser.EqualityOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitHigherArithOp(self, ctx: DecafParser.HigherArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)
    <mask token>

    def visitAssignStmt(self, ctx: DecafParser.ArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

<mask token>


class CustomVisitor(DecafVisitor):

    def __init__(self):
        self.scope = DecafStack()
        self.saved = {}
        self.errors = []
        self.anonCounter = 0
        self.offset = 0
        self.TypeValidator = Evaluator(scopes=self.scope)

    def error(self):
        self.flag = True

    def enterScope(self, name, t='scope'):
        parent = self.scope.peek()
        st = STable(name, parent=parent, stype=t, tt=TypeTable(), entrys={})
        self.scope.push(st)

    def exitScope(self):
        if self.scope.isEmpty():
            pass
        else:
            popped = self.scope.pop()
            self.saved[popped.name] = popped
            return popped

    def getError(self, name):
        return 'DECLARATION ERROR ' + name

    def visitProgram(self, ctx: DecafParser.ProgramContext):
        self.enterScope('GLOBAL')
        return self.visitChildren(ctx)

    def visitSingleVar(self, ctx: DecafParser.SingleVarContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        type_text = ctx.vType.getText()
        vartype = scope.typeExists(type_text.replace('struct', '')).name
        if scope.scopeType == 'struct':
            tt = scope.parent.typeTable
            param = Symbol(name, vartype, 0)
            tt.addSize(name, tt.getSize(type_text))
            tt.addParam(scope.name, param)
            return self.visitChildren(ctx)
        elif 'struct' in type_text:
            sName = type_text.replace('struct', '')
            struct = Symbol(name, sName, self.offset)
            scope.add(struct)
            structParams = scope.typeTable.getParams(sName)
            for param in structParams.values():
                s = Symbol(sName + param.name, param.stype, self.offset)
                self.offset += scope.typeTable.getSize(sName)
                scope.add(s)
            return self.visitChildren(ctx)
        s = Symbol(name, vartype, self.offset)
        self.offset += scope.typeTable.getSize(type_text)
        scope.add(s)
        return self.visitChildren(ctx)

    def visitListVar(self, ctx: DecafParser.ListVarContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        vartype = ctx.vType.getText().replace('struct', '')
        size = int(str(ctx.NUM()))
        if scope.scopeType == 'struct':
            t = scope.parent.typeExists(vartype)
            tt = scope.parent.typeTable
            param = Symbol(name, t.name, 0, listSize=size)
            tt.addSize(name, t.size * size)
            tt.addParam(scope.name, param)
            return self.visitChildren(ctx)
        s = Symbol(name, vartype, self.offset, listSize=size)
        self.offset += scope.typeExists(vartype).size * size
        scope.add(s)
        return self.visitChildren(ctx)

    def visitStructDeclaration(self, ctx: DecafParser.StructDeclarationContext
        ):
        name = str(ctx.ID())
        scope = self.scope.peek()
        self.enterScope(name, 'struct')
        s = TypeItem(name, 0, type_enum.Struct, {})
        scope.addType(s)
        visited = self.visitChildren(ctx)
        self.exitScope()
        return visited

    def visitMethodDeclaration(self, ctx: DecafParser.MethodDeclarationContext
        ):
        name = str(ctx.ID())
        scope = self.scope.peek()
        returnType = ctx.returnType.getText()
        s = TypeItem(name, 0, 'method', {}, scope.typeExists(returnType).name)
        for param in ctx.parameter():
            values, size = self.visitParameter(param)
            s.addParam(values)
            s.size += size
        scope.addType(s)
        self.enterScope(name, 'method')
        visit = self.visitChildren(ctx)
        self.exitScope()
        return visit

    def visitParameter(self, ctx: DecafParser.ParameterContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        type_text = ctx.vType.getText()
        vartype = scope.typeExists(type_text)
        s = Symbol(name, vartype.name, self.offset, param=True)
        self.offset += vartype.size
        scope.add(s)
        return s, vartype.size
    <mask token>

    def visitWhileStmt(self, ctx: DecafParser.WhileStmtContext):
        self.enterScope('whileblock' + str(self.anonCounter), 'if')
        self.anonCounter += 1
        expt = self.TypeValidator.visit(ctx.expression())
        if expt != type_enum.Boolean:
            error = genericError('Expected boolean expression for while',
                ctx.start.line)
            self.TypeValidator.errors.append(error)
        visit = self.visitChildren(ctx)
        self.exitScope()
        return visit

    def visitRelationOp(self, ctx: DecafParser.RelationOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitConditionalOp(self, ctx: DecafParser.ConditionalOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitEqualityOp(self, ctx: DecafParser.EqualityOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitHigherArithOp(self, ctx: DecafParser.HigherArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitArithOp(self, ctx: DecafParser.ArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitAssignStmt(self, ctx: DecafParser.ArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

<mask token>


class CustomVisitor(DecafVisitor):

    def __init__(self):
        self.scope = DecafStack()
        self.saved = {}
        self.errors = []
        self.anonCounter = 0
        self.offset = 0
        self.TypeValidator = Evaluator(scopes=self.scope)

    def error(self):
        self.flag = True

    def enterScope(self, name, t='scope'):
        parent = self.scope.peek()
        st = STable(name, parent=parent, stype=t, tt=TypeTable(), entrys={})
        self.scope.push(st)

    def exitScope(self):
        if self.scope.isEmpty():
            pass
        else:
            popped = self.scope.pop()
            self.saved[popped.name] = popped
            return popped

    def getError(self, name):
        return 'DECLARATION ERROR ' + name

    def visitProgram(self, ctx: DecafParser.ProgramContext):
        self.enterScope('GLOBAL')
        return self.visitChildren(ctx)

    def visitSingleVar(self, ctx: DecafParser.SingleVarContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        type_text = ctx.vType.getText()
        vartype = scope.typeExists(type_text.replace('struct', '')).name
        if scope.scopeType == 'struct':
            tt = scope.parent.typeTable
            param = Symbol(name, vartype, 0)
            tt.addSize(name, tt.getSize(type_text))
            tt.addParam(scope.name, param)
            return self.visitChildren(ctx)
        elif 'struct' in type_text:
            sName = type_text.replace('struct', '')
            struct = Symbol(name, sName, self.offset)
            scope.add(struct)
            structParams = scope.typeTable.getParams(sName)
            for param in structParams.values():
                s = Symbol(sName + param.name, param.stype, self.offset)
                self.offset += scope.typeTable.getSize(sName)
                scope.add(s)
            return self.visitChildren(ctx)
        s = Symbol(name, vartype, self.offset)
        self.offset += scope.typeTable.getSize(type_text)
        scope.add(s)
        return self.visitChildren(ctx)

    def visitListVar(self, ctx: DecafParser.ListVarContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        vartype = ctx.vType.getText().replace('struct', '')
        size = int(str(ctx.NUM()))
        if scope.scopeType == 'struct':
            t = scope.parent.typeExists(vartype)
            tt = scope.parent.typeTable
            param = Symbol(name, t.name, 0, listSize=size)
            tt.addSize(name, t.size * size)
            tt.addParam(scope.name, param)
            return self.visitChildren(ctx)
        s = Symbol(name, vartype, self.offset, listSize=size)
        self.offset += scope.typeExists(vartype).size * size
        scope.add(s)
        return self.visitChildren(ctx)

    def visitStructDeclaration(self, ctx: DecafParser.StructDeclarationContext
        ):
        name = str(ctx.ID())
        scope = self.scope.peek()
        self.enterScope(name, 'struct')
        s = TypeItem(name, 0, type_enum.Struct, {})
        scope.addType(s)
        visited = self.visitChildren(ctx)
        self.exitScope()
        return visited

    def visitMethodDeclaration(self, ctx: DecafParser.MethodDeclarationContext
        ):
        name = str(ctx.ID())
        scope = self.scope.peek()
        returnType = ctx.returnType.getText()
        s = TypeItem(name, 0, 'method', {}, scope.typeExists(returnType).name)
        for param in ctx.parameter():
            values, size = self.visitParameter(param)
            s.addParam(values)
            s.size += size
        scope.addType(s)
        self.enterScope(name, 'method')
        visit = self.visitChildren(ctx)
        self.exitScope()
        return visit

    def visitParameter(self, ctx: DecafParser.ParameterContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        type_text = ctx.vType.getText()
        vartype = scope.typeExists(type_text)
        s = Symbol(name, vartype.name, self.offset, param=True)
        self.offset += vartype.size
        scope.add(s)
        return s, vartype.size

    def visitIfStmt(self, ctx: DecafParser.IfStmtContext):
        self.enterScope('ifblock' + str(self.anonCounter), 'if')
        self.anonCounter += 1
        expt = self.TypeValidator.visit(ctx.expression())
        if expt != type_enum.Boolean:
            error = genericError('Expected boolean expression for if', ctx.
                start.line)
            self.TypeValidator.errors.append(error)
        visit = self.visitChildren(ctx)
        self.exitScope()
        return visit

    def visitWhileStmt(self, ctx: DecafParser.WhileStmtContext):
        self.enterScope('whileblock' + str(self.anonCounter), 'if')
        self.anonCounter += 1
        expt = self.TypeValidator.visit(ctx.expression())
        if expt != type_enum.Boolean:
            error = genericError('Expected boolean expression for while',
                ctx.start.line)
            self.TypeValidator.errors.append(error)
        visit = self.visitChildren(ctx)
        self.exitScope()
        return visit

    def visitRelationOp(self, ctx: DecafParser.RelationOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitConditionalOp(self, ctx: DecafParser.ConditionalOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitEqualityOp(self, ctx: DecafParser.EqualityOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitHigherArithOp(self, ctx: DecafParser.HigherArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitArithOp(self, ctx: DecafParser.ArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitAssignStmt(self, ctx: DecafParser.ArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

from Grammar.DecafVisitor import DecafVisitor
from Grammar.DecafParser import DecafParser
import inspect
from stack import DecafStack
from errors import genericError
from evaluador import Evaluator
from symbolTable import *

class CustomVisitor(DecafVisitor):

    def __init__(self):
        self.scope = DecafStack()
        self.saved = {}
        self.errors = []
        self.anonCounter = 0
        self.offset = 0
        self.TypeValidator = Evaluator(scopes=self.scope)

    def error(self):
        self.flag = True
    
    def enterScope(self, name, t='scope'):
        parent = self.scope.peek()
        st = STable(name, parent=parent, stype=t, tt=TypeTable(), entrys={})
        self.scope.push(st)

    # pops the current scope off the stack
    def exitScope(self):
        if self.scope.isEmpty():
            pass
        else:
            popped = self.scope.pop()
            self.saved[popped.name] = popped
            return popped

    # getError method to return error message as string
    def getError(self, name):
        return "DECLARATION ERROR " + name


    #Inicio de los metodos que si hacen algo

    # Visit a parse tree produced by DecafParser#program.
    def visitProgram(self, ctx:DecafParser.ProgramContext):
        self.enterScope('GLOBAL')
        return self.visitChildren(ctx)

    # Visit a parse tree produced by DecafParser#singleVar.
    def visitSingleVar(self, ctx:DecafParser.SingleVarContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        type_text = ctx.vType.getText()

        vartype = scope.typeExists(type_text.replace('struct', '')).name

        # if is inside struct
        if scope.scopeType == 'struct':
            # add to type table under structs name
            tt = scope.parent.typeTable
            param = Symbol(name, vartype, 0)

            # Add size and parameter to struct type in parent scope
            tt.addSize(name, tt.getSize(type_text))
            tt.addParam(scope.name, param)
            return self.visitChildren(ctx)

        # if is struct declaration
        elif 'struct' in type_text:
            # add struct to symbol table
            sName = type_text.replace('struct', '')

            struct = Symbol(name, sName, self.offset)
            scope.add(struct)

            structParams = scope.typeTable.getParams(sName)
            for param in structParams.values():
                s = Symbol(sName + param.name, param.stype, self.offset)
                self.offset += scope.typeTable.getSize(sName)
                scope.add(s)
            
            return self.visitChildren(ctx)

        #else is just normal var, add symbol to table
        s = Symbol(name, vartype, self.offset)
        self.offset += scope.typeTable.getSize(type_text)
        scope.add(s)

        return self.visitChildren(ctx)


    # Visit a parse tree produced by DecafParser#listVar.
    def visitListVar(self, ctx:DecafParser.ListVarContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        vartype = ctx.vType.getText().replace('struct', '')

        size = int(str(ctx.NUM()))

        if scope.scopeType == 'struct':
            t = scope.parent.typeExists(vartype)
            tt = scope.parent.typeTable
            param = Symbol(name, t.name, 0, listSize=size)
            
            tt.addSize(name, t.size * size)
            tt.addParam(scope.name, param)
            return self.visitChildren(ctx)

        s = Symbol(name, vartype, self.offset, listSize=size)
        self.offset += (scope.typeExists(vartype).size * size)
        scope.add(s)

        return self.visitChildren(ctx)


    # Visit a parse tree produced by DecafParser#structDeclaration.
    def visitStructDeclaration(self, ctx:DecafParser.StructDeclarationContext):
        name = str(ctx.ID())
        scope = self.scope.peek()

        self.enterScope(name, 'struct')

        s = TypeItem(name, 0, type_enum.Struct, {})
        scope.addType(s)
        visited = self.visitChildren(ctx)

        self.exitScope()
        return visited


    # Visit a parse tree produced by DecafParser#methodDeclaration.
    def visitMethodDeclaration(self, ctx:DecafParser.MethodDeclarationContext):
        name = str(ctx.ID())
        scope = self.scope.peek()
        returnType = ctx.returnType.getText()
        s = TypeItem(name, 0, 'method', {}, scope.typeExists(returnType).name)
        
        for param in ctx.parameter():
            values, size = self.visitParameter(param)
            s.addParam(values)
            # size = scope.typeTable.getSize(values.stype)
            s.size += size

        scope.addType(s)

        # enter method scope
        self.enterScope(name, 'method')

        # visit
        visit = self.visitChildren(ctx)

        self.exitScope()
        
        return visit
    
    # Visit a parse tree produced by DecafParser#parameter.
    def visitParameter(self, ctx:DecafParser.ParameterContext):
        name = str(ctx.ID())
        scope = self.scope.peek()

        type_text = ctx.vType.getText()

        vartype = scope.typeExists(type_text)
        
        s = Symbol(name, vartype.name, self.offset, param=True)
        self.offset += vartype.size

        scope.add(s)

        return s, vartype.size

    # Visit a parse tree produced by DecafParser#ifStmt.
    def visitIfStmt(self, ctx:DecafParser.IfStmtContext):
        self.enterScope('ifblock' + str(self.anonCounter), 'if')
        self.anonCounter += 1

        expt = self.TypeValidator.visit(ctx.expression())
        if expt != type_enum.Boolean:
            error = genericError('Expected boolean expression for if', ctx.start.line)
            self.TypeValidator.errors.append(error)

        visit = self.visitChildren(ctx)
        
        self.exitScope()
        return visit

    # Visit a parse tree produced by DecafParser#whileStmt.
    def visitWhileStmt(self, ctx:DecafParser.WhileStmtContext):
        self.enterScope('whileblock' + str(self.anonCounter), 'if')
        self.anonCounter += 1

        expt = self.TypeValidator.visit(ctx.expression())

        if expt != type_enum.Boolean:
            error = genericError('Expected boolean expression for while', ctx.start.line)
            self.TypeValidator.errors.append(error)

        visit = self.visitChildren(ctx)
        
        self.exitScope()
        return visit
    
    #Operations
    def visitRelationOp(self, ctx:DecafParser.RelationOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)
    
    def visitConditionalOp(self, ctx:DecafParser.ConditionalOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)
    
    def visitEqualityOp(self, ctx:DecafParser.EqualityOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)
    
    def visitHigherArithOp(self, ctx:DecafParser.HigherArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)

    def visitArithOp(self, ctx:DecafParser.ArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)
    
    #Assign
    def visitAssignStmt(self, ctx:DecafParser.ArithOpContext):
        self.TypeValidator.visit(ctx)
        return self.visitChildren(ctx)