    /*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package ast;

import API.Functions;
import API.Instruction;
import API.Tipos;
import API.Variaveis;
import static ast.Semantic.current;
import common.Log;
import java.util.ArrayList;
import java.util.Stack;

/**
 *
 * @author Eugenio
 */
public class Semantic extends Log {

    protected Stack<String> escopoStack = new Stack<>();
    protected AbstractSyntaxTree ast;
    protected static Node current;
//    protected HashMap<String, String> errosTemplate = new HashMap<>();
//    protected boolean temporalErro = false;

    public Semantic() {

    }

    public Semantic(AbstractSyntaxTree ast) {
        this.ast = ast;
    }

    /**
     * Percorre a AST em chama o metodo viSitNode que redireciona para o
     * tratamento de cada no
     *
     * @param as
     * @throws Exception
     */
    public Semantic Execute() throws Exception {
        _analise(ast.root);
        return this;
    }

    /**
     * Parte aux do metodo analiSe.
     *
     * @param n
     * @return
     * @throws Exception
     */
    protected void _analise(Node n) throws Exception {
        if (null == n) {
            return;
        }
        current = n;
        switch (current.G("class")) {
            default:
                visitNode();
                if (!n.childrens.isEmpty()) {
                    for (Node n2 : n.childrens) {
                        _analise(n2);
                    }
                }
        }
    }

    protected void visitNode() throws Exception {
        switch (current.G("class")) {
            case "type":
                CheckType();
                break;
            case "selector":
                CheckSelector();
                break;
            case "def.assign":
                CheckAssign();
                break;
            case "call":
                CheckCall();
                break;
            case "dec.const":
                break;
            case "dec.type":
                break;

//            case "_array":
////                chkArray();
//                break;
//            case "read":
//                chkRead();
//                break;
//            case "print":
//                chkPrint();
//                break;
//            case "dec::var":
//                verificarDecVariavel();
//                break;
//            case "return":
//                checkRetornoFuncao();
//                break;
//            case "var":
//                verificarVariavel(current);
//                break;
//            case "parametros::list":
//                testarListaParametros();
//                break;
//            case "op::unaria":
//                chkOperacaoUnario();
//                break;
//            case "expr::arithmetic":
//                exprArithmetic();
//                break;
//            case "exp::boolena":
//                chkOperacaoBooleano();
//                break;
//            case "dec::function":
//            case "main":
//                verificarFuncao();
//                break;
        }
    }

    protected void CheckType() {

        String type = current.G("type");

        if (!Tipos.Primitive(type) && !Tipos.Defined(type)) {
            AddError(new Instruction()
                    .S("class", "Type")
                    .S("msg", String.format("Tipo '%s' não definido", type))
                    .S("line", current.G("line"))
                    .S("col", current.G("col"))
            );
        }
    }

    protected void CheckSelector() {
        try {
            String type;

            switch (current.G("subclass")) {
                case "operand":
                    if (!current.eq("classType", "value")
                            && Variaveis.Get(current.getText()) == null) {
                        throw new Exception("Variavel " + current.getText() + " não definida");
                    }
                    break;
                case "selector":
//                    System.out.println("CheckSelector:" + current);
                    type = Variaveis.TypeOf(current.G("value"));
                    if (type.equals("undefined")) {
                        throw new Exception("Variavel " + current.getText() + " não definida");
                    }
                    break;
                case "index":
//                    System.out.println("CheckSeletor:" + current);
                    String varname = current.G("value");
                    type = Variaveis.TypeOf(varname);
                    if (type.equals("undefined")) {
                        throw new Exception("Variavel " + current.getText() + " não definida");
                    }

                    Node var = Variaveis.Get(varname);
                    Node ical;
                    ArrayList<Node> indexesCall = new ArrayList<>(current.childrens());
                    ArrayList<Node> indexesDecl = var.childrens();
                    if (indexesCall.size() > indexesDecl.size()) {
                        // todo exibir os indices
                        throw new Exception("Variavel " + current.getText() + " não aceita acesso indexado");
                    } else {
                        int idecval,
                                icallval;
                        for (Node idec : indexesDecl) {
                            if (indexesCall.isEmpty()) {
                                throw new Exception("Variavel " + current.getText() + " não aceita acesso indexado");
                            }
                            ical = indexesCall.remove(0);
                            // se não for um indice numerico salta
                            if (!ical.isNumber("value")) {
                                continue;
                            }
                            
                            icallval = ical.getInt("value");
                            idecval = idec.getInt("value");

                            if (idec.isNumber("value") && icallval >= idecval) {
                                throw new Exception(String.format("Index ['%s'] fora do intervalo ['%s'] em %s", icallval, idecval, current.getText()));
                            }
                        }
                    }

                    break;

            }
        } catch (Exception e) {
            AddError(new Instruction()
                    .S("class", "Type")
                    .S("msg", e.getMessage())
                    .S("line", current.G("line"))
                    .S("col", current.G("col"))
            );
        }
    }

    protected void CheckAssign() throws Exception {
        ArrayList<String> returns = new ArrayList<>();
        Node f, funcret;
        for (Node attrib : current.childrens().get(1).childrens()) {
            switch (attrib.Class()) {
                case "call":
                    f = Functions.Get(attrib.getText());
                    funcret = f.find("dec.return");
                    if (funcret != null) {
                        for (Node r : funcret.childrens()) {
                            returns.add(r.G("type"));
                        }
                    }
                    break;
                default:
                    returns.add(attrib.G("type"));
                    break;
            }
        }
        ArrayList<Node> dsts = current.childrens().get(0).childrens();
        if (dsts.size() != returns.size()) {
            throw new Exception(String.format("Atribuição incompativel. Esperando %d e atribuido %d", dsts.size(), returns.size()));
        } else {
            int i = 0;
            String attrib;
            for (Node dst : dsts) {
                attrib = returns.get(i++);
                if (!dst.eq("type", attrib)) {
                    AddError(new Instruction()
                            .S("class", "Assing")
                            .S("msg", String.format("Tipo atribuição '%s' diferente do esperado '%s'", attrib, dst.G("type")))
                            .S("line", current.G("line"))
                            .S("col", current.G("col"))
                    );
                }
            }
//            System.out.println("CheckAssign:" + returns);
            // A quantidade de retornos é igual a de destinos, verificar o tipo de cada atribuição
        }
    }

    protected void CheckCall() throws Exception {
//        System.out.println("CheckCall:" + current);

        Node function = Functions.Get(current.getText());

        // Verifica se a funcao foi definida
        if (function == null) {
            AddError(new Instruction()
                    .S("class", "Call")
                    .S("msg", String.format("A função '%s' não foi definida", current.getText()))
                    .S("line", current.G("line"))
                    .S("col", current.G("col"))
            );
            return;
        }

        // Se a definicao contem argumentos verifica se na chamada possui o mesmo 
        // numero de argumentos e se são do mesmo tipo
        ArrayList<Node> argumentsDelc = function.findAll("argument", "class");
        ArrayList<String> argsDelc = new ArrayList<>();
        String type, pointer;
        Node t;
        for (Node args : argumentsDelc) {
            t = args.find("type");
            type = t.G("type");
            pointer = t.G("pointer");
            for (int i = 0; i < args.find("ids").childrens().size(); i++) {
                argsDelc.add((pointer.equals("") ? "" : "&") + type);
            }
        }

        Node argumentsCall = current.find("arguments").find("exprs");
        ArrayList<String> argsCall = new ArrayList<>();
        if (argumentsCall != null) {
            String prefix = "";
            for (Node argCall : argumentsCall.childrens()) {
//                System.out.println("ArgCall:" + argCall);

                if (argCall.eq("subclass", "address") || argCall.eq("pointer", "*")) {
                    prefix = "&";
                }

                argsCall.add(prefix + argCall.G("type"));
            }
        }

        int argsDeclCount = argsDelc.size();
        int argsCallCount = argsCall.size();

        if (argsCallCount != argsDeclCount) {
            AddError(new Instruction()
                    .S("class", "Call")
                    .S("msg", String.format("Chamada incompativel. Numero de parametros de chamada é '%s' e o esperado é '%s'", argsCallCount, argsDeclCount))
                    .S("line", current.G("line"))
                    .S("col", current.G("col"))
            );

        } else {
            // todo check types
            int i = 0;
            String callType;
            for (String declType : argsDelc) {
                callType = argsCall.get(i++);
//                System.out.println("Call arg:" + callType + ":" + declType);

                if (!declType.equals(callType)) {
                    AddError(new Instruction()
                            .S("class", "Call")
                            .S("msg", String.format("Chamada incompativel. O parâmetro %s é do tipo '%s' e o esperado é '%s'", i, callType, declType))
                            .S("line", current.G("line"))
                            .S("col", current.G("col"))
                    );
                }
            }

        }

    }
}
//    /**
//     * Busca uma declaração de uma função, caso não exista adiciona erro ao Log
//     *
//     * @param nomeDaFuncao
//     * @param AddError
//     * @return Node | null
//     */
//    protected static Node encontreFuncao(String id, boolean AddError) {
//        Node func = null;
//        if (id.substring(0, 2).equals("__")) {
//
//        } else if (Functions.existe(id)) {
//            func = Functions.function(id);
//        }
//        if (func == null && AddError) {
//            AddError(Api.getFormatedPosition(current)
//                    + "Chamada de função. Tentativa de chamar uma função {"
//                    + id + "} não definida.");
//        }
//        return func;
//    }

//
//    protected void chkOperacaoBooleano() {
//        Node n1 = getOperandoNaAst(current.getFilho(0), current);
//        Node n2 = getOperandoNaAst(current.getFilho(1), current);
//
//        if (n1 == null || n2 == null) {
//            return;
//        }
//
//        String esperado = "";
//        String tipo1 = tipo(n1);
//        String tipo2 = tipo(n2);
//
//        if (current.in("value", new String[]{"==", "!="})) {
//            if (!tipo1.equals(tipo2)) {
//                AddError(Api.getFormatedPosition(current) + " Comparação invalida. "
//                        + "Comparação entre tipos diferentes. Param[1] do tipo {" + tipo1 + "} Param[2] do tipo {" + tipo2 + "}");
//            }
//        } else {
//            if (current.in("value", new String[]{"<", ">", "<=", ">="})) {
//                esperado = "int";
//            } else if (current.in("value", new String[]{"&&", "||"})) {
//                esperado = "bool";
//            }
//            String op = current.getText();
//            tipoCheck(n1, tipo1, esperado, op);
//            tipoCheck(n2, tipo2, esperado, op);
//        }
//    }
//
//    /**
//     * Verifica:<br>
//     * -> Operação de atribuição. -> Se o endereço de destino é valido, seja
//     * variavel ou declarcao de uma -> Se o tipo atribuido corresponde com o
//     * esperado
//     *
//     * @throws Exception
//     */
//    protected void chkAssign(Node current) throws Exception {
//        Node parent = current.parent;
//
//        /**
//         * Se parent for igual assign::op entao deve se analisar a variavel,
//         * caso contrario sera dec::var e não precisa verificar o acesso a
//         * variavel
//         */
//        Node var;
//        boolean isDeclaracao = true;
//        if (parent.igual("class", "assign::op")) {
//            isDeclaracao = false;
//            if (Constantes.existe(parent.getText())) {
//                _addViolacaoDeConstante(parent.getText());
//                return;
//            }
//            var = parent.encontreByClass("var");
//            _analise(var);
//
//        } else {
//            var = parent;
//        }
//
//        /**
//         * Apos verificar variavel, vamos verificar se o tipo atribuido é igual
//         * ao esperado;
//         */
//        Node value = current.first();
//
//        /*Se valor for uma variavel e a mesma não existir*/
////        value.S("verificador", "0");
//        _analise(value);
//        /*Se ocorreu erro na verificação do valor atribuido*/
////        
//        String eTipo = parent.G("tipo"), aTipo = tipo(value);
//
//        if (var.igual("tipo", Tipos.REFERENCE)) {
//            if (isDeclaracao && value.diferente("acessoEndereco", "true")) {
//                AddError(Api.getFormatedPosition(value) + " Atribuição invalida. Reference ainda não aponta para uma área de memoria valida!");
//            }
//            return;
//        }
//
//        if (!aTipo.equals(eTipo)) {
//            AddError(Api.getFormatedPosition(value) + " Atribuição invalida. Tipo esperado {"
//                    + eTipo + "} enquanto o atribuido foi {" + aTipo + "}.");
//        }
//
//    }
//
//    //--------------------------------------------------//
//
//
//    //##################################################//
//    /**
//     * Verifica: <br>
//     * -> Se uma variavel foi redeclara.
//     *
//     * @param nome
//     */
//    protected boolean _$RedeclaracaoVar(String nome, Node current) throws Exception {
//        ArrayList<Node> declaracoes = current
//                .closest("atributos,dec::function,dec::thread,dec::globais,trap", "class")
//                .encontreTodos("dec::var", "class");
////                filtrarFilhosPorClass();
//        int cId = current.getInt("id");
//        int tId;
//        for (Node node : declaracoes) {
//            tId = node.getInt("id");
//            if (tId >= cId) {
//                break;
//            }
//            if (node.igual("value", nome)) {
//                AddErrorWithPosition(" Redeclaração [ Variavel {" + nome
//                        + "}]. Primeira definição na linha {" + node.G("line") + "}");
//                return true;
//            }
//        }
//        return false;
//
//    }
//
//    /**
//     * Verifica:<br>
//     * -> Redeclaração de uma função no mesmo escopo
//     *
//     * @throws Exception
//     */
//    protected void _checkRedeclaracaoFuncao() throws Exception {
//        String nomeFuncao = current.getText();
//        ArrayList<Node> funcoes = current.parent.filtrarFilhosPorClass("dec::function");
//        int cId = current.getInt("id");
//        int tId;
//        for (Node node : funcoes) {
//            tId = node.getInt("id");
//            if (tId >= cId) {
//                break;
//            }
//            if (node.igual("value", nomeFuncao)) {
//                AddError(Api.getFormatedPosition(current) + "Redeclaração de função. Previa declaração na linha {" + node.G("line") + "}");
//                return;
//            }
//        }
//    }
//
//    protected void _verificarAtribuicaoVetor(Node variavel, Node valor) throws Exception {
////        System.out.println("Var:" + variavel.encontre("indices"));
//
//        String tipoEsperado = variavel.G("tipo");
//        Node indices = variavel.encontre("indices");
//        int numeroIndices = indices.getChildrenCount();
//
////        System.out.println("\n\nAtribuicao:" + current);
////        Node valor = current.getPrimeiroFilho();
////        System.out.println("valor:" + valor);
//        if (!valor.igual("class", "lista")) {
//            String tipo = valor.G("tipo");
//            AddErrorWithPosition("Atribuição invalida. Aguardando uma lista de "
//                    + tipoEsperado + ". Um(a) {" + tipo + "} foi atribuido.");
//            return;
//        }
////        System.out.println("Valor era matrix:" + valor.igual("matrix", "true"));
//        Node visit;
//        if (numeroIndices > 1 && !valor.igual("matrix", "true")) //Caso tenha mais de uma linha de inicializacao
//        {
////            System.out.println("ATRIB:" + valor);
//            AddErrorWithPosition(" Numero de itens não corresponde ao esperado. Esperado {" + numeroIndices + "} atribuido {1}");
//            return;
//        }
//        if (valor.igual("matrix", "true")) {
//            visit = valor;
//        } else { //Caso seja apenas um vetor
////            valor:[value: lista, id: 17, _pos: 0, class: lista, tipo: array, line: 14, col: 16, file: program::exemplo, ][1,3,] sem ()
////            valor:[value: lista, id: 17, _pos: 0, class: lista, tipo: array, line: 14, col: 16, file: program::exemplo, ][lista,] ()
//
////            valor:[value: lista, id: 8, _pos: 0, class: lista, tipo: array, line: 14, col: 14, file: program::exemplo, ][1,2,]
////            valor:[value: lista, id: 8, _pos: 0, class: lista, tipo: array, line: 14, col: 14, file: program::exemplo, ][lista,]
////            System.out.println("valor:" + valor);
//            Node lista = valor.first();
//            if (lista.igual("class", "lista")) {
//                visit = lista;
//            } else {
//                visit = valor;
//            }
//        }
//        __conferirListaAtribuicao(indices, visit, 0, tipoEsperado, numeroIndices);
//    }
//
//    protected void __conferirListaAtribuicao(Node indices, Node valor, int ind, String tipo, int numeroIndices) throws Exception {
////        System.out.println("Valores:" + valor);
//        if (!indices.temFilho(ind)) {
//            return;
//        }
//        int numEle = indices.getFilho(ind).getInt("value");
//        int numAtrib = valor.getChildrenCount();
//
//        if (numeroIndices == 1) {  //diz que e um vetor;
//            if (numEle != numAtrib) {
//                AddErrorWithPosition(" Numero de itens não corresponde ao esperado. Esperado {"
//                        + numEle + "} atribuido {" + numAtrib + "}.");
//            }
//            checkTipo(tipo, valor.filhos(), "");
//        } else { //diz que é uma matrix
////            System.out.println("ASDASD:" + ind + ":" + (numeroIndices - 1));
//            if (numEle != numAtrib) {
//                AddErrorWithPosition(" Numero de itens não corresponde ao esperado. Esperado {"
//                        + numEle + "} atribuido {" + numAtrib + "}.");
//                return;
//            }
//            if (ind == numeroIndices - 1) {
//                checkTipo(tipo, valor.filhos(), "");
//            } else {
//                for (Node atribuicao : valor.filhos()) {
//                    __conferirListaAtribuicao(indices, atribuicao, ind + 1, tipo, numeroIndices);
//                }
//            }
//        }
//    }
//
//    protected Node getOperandoNaAst(Node n, Node parent) {
//        if (n.eNumerico("value") || n.igual("class", "valor")) {
//            return n;
//        }
//        return findVariavel(n);
//    }
//
//    /**
//     *
//     * @param n Sera extraido a poiscao e o texto
//     * @param tipo tipo do valor atribuido
//     * @param esperado tipo do valor esperado
//     * @param op operação [atribuição,]
//     */
//    protected void tipoCheck(Node n, String tipo, String esperado, String op) {
//        if (tipo == null) {
//            tipo = "undefined";
//        }
//        String acesso = (n.temAtributo("fullAccess")) ? n.G("fullAccess") : n.getText();
//        if (!tipo.equals(esperado)) {
//            AddError(Api.getFormatedPosition(n) + " Operação { " + op + " } espera um valor ["
//                    + esperado + "]. Parametro { " + acesso + " } é do tipo [" + tipo + "]");
//        }
//    }
//
//
//    protected static String tipoVar(Node n) {
//        Node nv = findVariavel(n);
//        String tipo = "undefined";
//        /*Se a variavel não existir*/
//        if (null == nv) {
//            return tipo;
//        }
//
//        Node atributo = n.encontreByClass("atributo");
//
//        /*Se não tem atributo*/
//        if (null == atributo) {
//            return n.G("tipo");
//        }
//
//        /*Se tem atributo*/
//        while (null != atributo) {
//            tipo = atributo.G("tipo");
//            atributo = atributo.encontreByClass("atributo");
//        }
//        return tipo;
//    }
//
//    protected static Node findVariavel(Node var) {
//        return findVariavel(var, true);
//    }
//
////    protected Node findVariavel(Node var, Node parent, boolean AddError) {
//    protected static Node findVariavel(Node var, boolean AddError) {
////        if (Constantes.existe(var.getText())) {
////            return Constantes.
////        }
//        Node dec = Variaveis.find(var.getText(), var.G("escopo"));
//        if (dec == null && AddError) {
////            System.out.println("Var:" + var);
////            new Exception().printStackTrace();
//            AddError(Api.getFormatedPosition(var) + " Tentativa de acesso identificador {" + var.getText() + "} não declarada no escopo.");
//        }
//        return dec;
//    }
//
//
//
//    /**
//     *
//     * @param n
//     * @param tipoEsperado
//     * @param onde
//     * @return true se iguais e false caso sejam diferentes
//     */
//    protected boolean checkTipo(String tipoEsperado, Node n, String onde) {
//        if (n.igual("tipo", "undefined")) {
//            switch (n.Class()) {
//                case "var":
//                    n = findVariavel(n);
////                    n = ast.encontreDeclaracao(n, n.parent);
//                    break;
//                case "function::call":
//                    n = encontreFuncao(n.getText(), true);
////                    n = ast.encontreFuncao(n.getText());
//                    break;
//            }
//        }
//
//        if (!n.igual("tipo", tipoEsperado)) {
//            switch (onde) {
//                case "retorno":
//                    AddErrorWithPosition(" Retorno invalido. Tipo esperado {" + tipoEsperado + "} enquanto o atribuido foi {" + n.G("tipo") + "}.");
//                    break;
//                default:
//                    AddErrorWithPosition(" Atribuição invalida. Tipo esperado {" + tipoEsperado + "} enquanto o atribuido foi {" + n.G("tipo") + "}.");
//            }
//            return false;
//        }
//        return true;
//    }
//
//    protected void checkTipo(String tipoEsperado, ArrayList<Node> nodes, String onde) {
//        for (Node v : nodes) {
//            checkTipo(tipoEsperado, v, onde);
//        }
//    }
//
//    public static boolean testExist(Node n) {
//        return testExist(n, true);
//    }
//
//    public static boolean testExist(Node n, boolean AddError) {
//        if (n == null) {
//            return false;
//        }
//        switch (n.G("class")) {
////            case "function::call":
////                Node func = encontreFuncao(n.getText(), AddError);
////                if (func != null) {
////                    return true;
////                }
////                break;
////            case "dec::var":
////                return true;
////            case "assign::op":
////                n = n.first();
////            case "var":
////                return findVariavel(n, AddError) != null;
////
////            case "const":
////                return Constantes.existe(n.getText());
////
////            case "op::unaria":
////            case "exp::boolena":
////            case "valor":
////            case "exp::aritimetica":
////                return true;
//        }
//        return false;
//    }
//
//    protected Node program() {
//        return Root().encontre("program");
//    }
//
//    protected boolean _$redeclaracao(String nome, String tipo, String onde, int id) throws Exception {
//        for (Node c : program().encontre(onde).filhos()) {
//            if (nome.equals(c.getText()) && id > c.getInt("id")) {
//                AddErrorWithPosition(" Redeclaração [ " + tipo + " {" + nome + "}]. Primeira definição na linha {" + c.G("line") + "}");
//                return true;
//            }
//        }
//        return false;
//    }
//
//  /**
//     * Verifica: <br>
//     * -> Se a constante é duplicada existe; <br>
//     * -> Se o tipo atribuido é igual ao esperado
//     */
//    protected void chkConstante() throws Exception {
//        /*Verificar se constante foi redeclarada*/
//
////        if (_$redeclaracao(current.getText(), "Constante", "constantes", current.getInt("id"))) {
////            return;
////        }
//    }
////
//    /**
//     * Verifica: <br>
//     * -> Se existe uma constante com mesmo nome <br>
//     * -> Se o tipo existe <br>
//     * -> Se ocorre redeclaração da variavel
//     *
//     * @throws Exception
//     */
//    protected void verificarDecVariavel() throws Exception {
//        String nome = current.getText();
//        if (_$RedeclaracaoVar(nome, current)) {
//            return;
//        }
//        if (Constantes.existe(nome)) {
//            AddErrorWithPosition(" Declaração invalida. Variavel {" + nome + "} com mesmo identificador de uma constante.");
//            return;
//        }
//        _testeTipoValido(current);
//        /*Se for matrix avalia os limites*/
//        if (current.igual("matrix", "true")
//                && !_$indicesValidos(current, current.encontre("indices"), false)) {
//        }
//        Node assign = current.encontreByClass("assign");
//        if (null != assign) {
//            chkAssign(assign);
//        }
//    }
//
//    /**
//     * Verifica se o tipo existe; Adiciona mensagem de erro caso contrario;
//     *
//     * @param n
//     * @return
//     * @throws Exception
//     */
//    protected boolean _testeTipoValido(Node n) throws Exception {
//        String tipo = n.G("tipo");
//        boolean exit = Tipos.existe(tipo);
//        if (!exit) {
//            AddError(Api.getFormatedPosition(n) + " Tipo invalido. Tipo {" + tipo + "} não definido.");
//        }
//        return exit;
//    }
//
//    /**
//     * Verifica:<br>
//     * -> Função com mesmo nome de uma variavel <br>
//     * -> Se função foi redeclarada <br>
//     *
//     * @throws Exception
//     */
//    protected void verificarFuncao() throws Exception {
//
//        String nomeFuncao = current.getText();
//        if (Variaveis.existe(current)) {
//            AddErrorWithPosition("Declaração invalida. Função com mesmo nome de uma variavel {" + nomeFuncao + "}.");
//        }
//        _$redeclaracao(nomeFuncao, "Funções", "funcoes", current.getInt("id"));
////        _checkRedeclaracaoFuncao();
//        //        _checkRetornoFuncao();
//    }
//
//    /**
//     * Verifica:<br>
//     * -> Se o retorno é o mesmo esperado pela função ;
//     */
//    protected void checkRetornoFuncao() {
//        Node parent = current.closest("dec::function,dec::thread", "class");
//        /*Se não estiver dentro de uma função ou thread*/
//        if (null == parent) {
//            return;
//        }
//        /*Se estiver dentro de uma thread não verifica*/
//        if (parent.igual("class", "dec::thread")) {
//            return;
//        }
//        String tipoEsperado = parent.G("tipo"),
//                tipoAtribuido = tipo(current.first());
//
//        if (!tipoAtribuido.equals(tipoEsperado) && !tipoAtribuido.equals(Tipos.REFERENCE)) {
//            AddErrorWithPosition(" Tipo de retorno invalido. Retornando {" + tipoAtribuido + "} enquanto o esperador é {" + tipoEsperado + "}");
//        }
//    }
//
//
//    /**
//     * Verifica:<br>
//     * -> Se função existe
//     */
//    protected void chkChamadaDeFuncao() {
//        if (current.in("value", new String[]{"print", "read"})) {
//            return;
//        }
//        String id = current.getText();
//
//        if (null == encontreFuncao(id, false)) {
//            AddErrorWithPosition(" Chamada de função invalida. Não foi encontrada declaração para a função ['" + id + "'].");
//        }
//    }
//
//    /**
//     * Verifica:<br>
//     * -> Se uma variavel existe.<br>
//     * -> Se for matrix avalia os limites
//     *
//     * @param nome
//     */
//    protected void verificarVariavel(Node var) throws Exception {
//        Node dec = findVariavel(var, true);
//        if (null == dec) {
//            var.S("verificador", "1");
//            return;
//        }
//        if (!_$checkAtributoAndIndices(dec, var)) {
//            var.S("verificador", "1");
//        }
//    }
//
//    /**
//     * Verifica:<br>
//     * -> Se uma atributo existe <br>
//     * -> Se for matrix avalia os limites
//     *
//     * @throws Exception
//     */
//    protected boolean checkAtributo(Node current) throws Exception {
//        if (current == null) {
//            return false;
//        }
//
//        String tipoParent = current.parent.G("tipo");
//
//        if (tipoParent.equals("undefined")) {
//            return false;
//        }
//
//        Node pTipo = Tipos.G(tipoParent);
//        String atributo = Variaveis.clearName(current.getText());
//        Node dec = pTipo.first().encontre(atributo);
//        /*Verifica se o atributo existe*/
//        if (null == dec) {
//            AddErrorWithPosition(" Acesso invalido de atributo. A variavel [ "
//                    + current.parent.getText() + " | tipo: " + pTipo.getText()
//                    + " ]  não possui o atributo {" + atributo + "}");
//            return false;
//        }
//        return _$checkAtributoAndIndices(dec, current);
//    }
//
//    /**
//     * Executa recursivamente a verificação de atributos e a verificação de
//     * indices
//     *
//     * @param dec
//     * @param current
//     * @throws Exception
//     */
//    protected boolean _$checkAtributoAndIndices(Node dec, Node current) throws Exception {
//        /*Verificar array limites*/
//        Node indices = current.encontre("indices");
//        if (null != indices && !_$indicesValidos(dec, indices, true)) {
//            current.S("verificador", "1");
//            return false;
//        }
//        Node atributo = current.encontreByClass("atributo");
//        /*Se tem um proxumo atributo e é array*/
//        if (null != atributo && dec.igual("matrix", "true")) {
//            Node decInd = dec.encontre("indices"),
//                    curInd = current.encontre("indices");
//
//            /*Isso indica que deve estar sendo accessado um array e uma variavel*/
//            if (null == curInd || decInd.getChildrenCount() > curInd.getChildrenCount()) {
//
//                AddErrorWithPosition(" Acesso invalido. Tentando acessar o atributo {" + atributo.getText() + "} de um array.");
//                current.S("verificador", "1");
//                return false;
//            }
//        }
//
//        /*Ativa recursao dos atributos*/
//        if (!checkAtributo(atributo)) {
//            current.S("verificador", "1");
//            return false;
//        }
//        return true;
//    }
//
//    /**
//     * Verifica:<br>
//     * -> Se os indices são do tipo inteiro <br>
//     * -> Se for valor verifica se o limite é valido
//     *
//     * @param dec
//     * @param indicesAccess
//     * @param chkLimite
//     * @throws Exception
//     */
//    protected boolean _$indicesValidos(Node dec, Node indicesAccess, boolean chkLimite) throws Exception {
//        boolean valid = true;
//        /*Caso o a variavel não seja um matriz*/
//        if (!dec.igual("matrix", "true")) {
//            AddErrorWithPosition(" Acesso invalido. Propriedade {" + dec.getText() + "} não é um vetor.");
//            return false;
//        }
//        ArrayList<Node> indices = indicesAccess.filhos();
//        ArrayList<Node> decIndices = dec.encontre("indices").filhos();
//
//        if (indices.size() > decIndices.size()) {
//            AddErrorWithPosition(" Acesso invalido. Tentando acessar indices fora do espaço da matriz.");
//            return false;
//        }
//
//        Node decIndice;
//        String tIndice;
//        Node indice;
//        for (int i = 0, valIndAce, valIndDec; i < indices.size(); i++) {
//            indice = indices.G(i);
//            tIndice = tipo(indice);
//
//            _analise(indice);
//
//            if (tIndice.equals("undefined")) {
//                valid = false;
//                continue;
//            }
//
//            /*Verifica se o indice é inteiro */
//            if (!tIndice.equals("int")) {
//                AddErrorWithPosition(" Indice erro. Indice [" + (i + 1) + "] deve ser inteiro, {" + tIndice + "} atribuido.");
//                valid = false;
//                continue;
//            }
//            /*Quando for uma declaração não deve conferir os limites, verificação 
//             apenas deve ser feita quando se tratar de acesso a variavel*/
//
//            /*Verificar limite apenas quando for numero inteiro*/
//            if (!chkLimite || !indice.in("class", new String[]{"valor", "indice"})) {
//                continue;
//            }
//
//            decIndice = decIndices.G(i);
//            if (!decIndice.in("class", new String[]{"valor", "indice"})) {
//                continue;
//            }
//
//            /* Quantidade de indices da chamada */
//            valIndAce = indice.getInt("value");
//            valIndDec = decIndice.getInt("value");
//
//            if (valIndAce >= valIndDec) {
//                AddErrorWithPosition(" Indice erro. Indice [" + (i + 1) + "], valor maximo {" + (valIndDec - 1) + "}, atribuido {" + valIndAce + "}.");
//                valid = false;
//            }
//        }
//        return valid;
//    }
//
//    /**
//     * Registra o erro caso a quantidade de parametros seja diferente da
//     * declarada
//     *
//     * @param numParametros
//     * @param sizedec
//     */
//    protected boolean _$verificarQuantidadeParametros(int numParametros, int sizedec) {
//        if (numParametros > sizedec) {
//            AddErrorWithPosition(" Chamada invalida. Função chamada com mais "
//                    + "parametros que o declarado. Esperando {"
//                    + sizedec + "}, atribuidos {" + numParametros + "}");
//            return false;
//        } else if (numParametros < sizedec) {
//            AddErrorWithPosition(" Chamada invalida. Função chamada com menos "
//                    + "parametros que o declarado. Esperando {"
//                    + sizedec + "}, atribuidos {" + numParametros + "}");
//            return false;
//        }
//        return true;
//    }
//
//    protected void _$verificarTipoParametro(ArrayList<Node> paramdec) {
//        Node funcao = current.parent;
//        ArrayList<Node> parampas = current.filhos();
//        int i = 0;
//        String tipoAtrib, tipoEsper;
//        try {
//            for (Node param : parampas) {
//                param = param.first();
//
//                tipoAtrib = Tipos.tipoDe(param, param.G("tipo"));
//                tipoEsper = paramdec.G(i++).G("tipo");
//
//                if (tipoEsper.equals(tipoAtrib)
//                        || tipoEsper.equals(Tipos.REFERENCE)) {
//                    continue;
//                }
//
//                AddError(Api.getFormatedPosition(funcao) + " Chamada de função {"
//                        + funcao.getText() + "} invalida. Parâmetro [" + (i) + "] espera {"
//                        + tipoEsper + "} não corresponde com a atribuição {"
//                        + tipoAtrib + "}");
//            }
//        } catch (Exception e) {
//            System.err.println(e.getMessage());
//            e.printStackTrace();
//        }
//    }
//
//    /**
//     * Deve ser chamada depois de analisar uma funcao ou depois da definicao de
//     * uma thread Verifica: <br>
//     * -> Se quantidade de parametros
//     */
//    protected void testarListaParametros() {
//        Node parent = current.parent;
//        ArrayList<Node> paramdec;
//        int numParametros = current.filhos().size();
//
//        if (parent.in("class", new String[]{"print", "read"})) {
//            return;
//        }
//
//        /*Se a lista de parametros for de uma thread*/
//        if (parent.igual("class", "dec::thread")) {
//            _$verificarQuantidadeParametros(numParametros, 1);
//            return;
//        }
//
//        /*Se a lista de parametros for de uma função*/
//        Node declaracao = Functions.function(parent.getText());
//
//        if (null == declaracao) {
//            return;
//        }
//        paramdec = declaracao.encontre("parametros").filhos();
//
//        /*Verifica a quantidade de parametros é igual na chamada e na declaração*/
//        if (_$verificarQuantidadeParametros(numParametros, paramdec.size())) {
//            _$verificarTipoParametro(paramdec);
//        }
//
//    }
//
//    /**
//     * Verfica: <br>
//     * -> Parametros da função print,se valores e variaveis existem.
//     *
//     */
//    protected void chkPrint() {
//        for (Node var : current.first().filhos()) {
//            testExist(var.first());
//        }
//    }
//
//    /**
//     * Verfica: <br>
//     * -> Parametros da função print,se valores e variaveis existem.
//     *
//     */
//    protected void chkRead() {
//        for (Node var : current.filhos()) {
//            if (!var.in("class", new String[]{"var"})) {
//                AddErrorWithPosition("A função read exige endereços de variaveis. Especifique uma variavel para armazenar a leitura");
//            }
//            testExist(var);
//        }
//    }
//
//    protected void _addViolacaoDeConstante(String constante) {
//        AddErrorWithPosition(" Violação de contante. Tentativa de atualizar a constante {" + constante + "}.");
//    }
//
//    /**
//     * Verfica: <br>
//     * -> Se a variavel existe. <br>
//     * -> Se o tipo da variavel é inteiro
//     */
//    protected void chkOperacaoUnario() {
//        /*Se tentar operação unaria com constate*/
//        if (Constantes.existe(current.getText())) {
//            _addViolacaoDeConstante(current.getText());
//            return;
//        }
//        Node var = ast.encontreDeclaracao(current, current.parent);
//        if (testExist(var, true) && !var.igual("tipo", "int")) {
//            AddError(Api.getFormatedPosition(current) + " Tipo { " + var.getText() + " | " + var.G("tipo") + " } não permite operação unária.");
//        }
//    }
////
//
//    /**
//     * Adiciona um erro a liSta de erroS SemanticoS
//     *
//     * @param erro
//     */
//    protected static void AddError(String erro) {
//        erros.add(erro);
//    }
//
//    /**
//     * Seta a pilha do eSopo
//     *
//     * @param escopo
//     */
//    protected void setEscopo(String escopo) {
//        String ant = escopoStack.empty() ? "" : "," + escopoStack.peek();
//        escopoStack.push(escopo + ant);
//    }//
//    protected void DecConst() {
//
//    }
//
//    /**
//     * Verifica: <br>
//     * -> Se os operandos existem <br>
//     * -> Se tipo dos operandos sao validos <br>
//     */
//    protected boolean exprArithmetic() {
//        int i = 1;
//        String type;
//        for (Node node : current.childrens()) {
//            type = tipo(node);
//
//            if (testExist(node, false) && (!type.equals(Tipos.REFERENCE) && !type.equals("int"))) {
//
//                AddError(Api.getFormatedPosition(node) + " Tipo esperado {int}. "
//                        + " Operando {" + i + "}  é do tipo {" + node.getText() + " | " + type + "}");
//                return false;
//            }
//            i++;
//        }
//        return true;
//    }
//    /*Functions de apoio */
//
//    protected String tipo(Node n) {
//        switch (n.G("class")) {
//
////            return n.G("type");
//            case "function::call":
//                Node func = encontreFuncao(n.getText(), false);
//                if (func != null) {
//                    return func.G("tipo");
//                }
//                break;
//
////            case "var":
////                tipo = tipoVar(n);
////                break;
//            case "indice":
//            case "dec::var":
//            case "const":
//                return n.G("type");
//
//            case "expr::or":
//            case "expr::and":
//            case "expr::comp":
//            case "value.boolean":
//                return Tipos.BOOLEAN;
//
//            case "expr::unary":
//            case "expr::negative":
//            case "expr::arithmetic":
//            case "value.integer":
//                return Tipos.INTEGER;
//        }
//        return Tipos.UNDEFINED;
//    }
