//TODO: rename with callable
        public void AddFunction(CallableSymbol funcSym)
        {
//            funcSym.Local = Pop();
//            funcSym.Parameters = Pop();
            _stack.Peek().RemoveCallable(funcSym.Ident, funcSym); // remove prepared callable
            _stack.Peek().AddCallable(funcSym.Ident, funcSym);    // add full callable
        }
        //TODO: rename with callable
        public bool PrepareFunction(CallableSymbol funcSym)
        {
            var existCallable = _stack.Peek().FindCallable(funcSym.Ident,
                                                           new List <SymType>(from type in funcSym.ParametersTypes select type.Item2));

            if (existCallable != null && existCallable.IsForward)
            {
                return(true); // function header already prepared by forward
            }

            return(_stack.Peek().AddCallable(funcSym.Ident, funcSym));
        }
        public bool Visit(CallableHeader node)
        {
            _symStack.Push(); //this will be function local namespace
            var callable = new CallableSymbol(node.Name.ToString())
            {
                ReturnSymType = null
            };

            if (node.ReturnType != null)
            {
                node.ReturnType.Accept(this);
                callable.ReturnSymType = _symStack.FindType(node.ReturnType.ToString());
                _symStack.AddVariable(true, "result", callable.ReturnSymType);
            }

            foreach (var paramSection in node.ParamList)
            {
                paramSection.Accept(this);
                foreach (var parameter in paramSection.ParamList)
                {
                    callable.ParametersTypes.Add(new Tuple <string, SymType>(paramSection.ParamModifier,
                                                                             _symStack.FindIdentInScope(parameter.ToString()).VarSymType));
                }
            }

            callable.IsForward  = true;
            callable.Local      = _symStack.Pop();
            node.CallableSymbol = callable;
//            if (!_symStack.PrepareFunction(callable))
//            {
//                throw new Exception(string.Format("({0}, {1}) semantic error: duplicate declaration of {2} '{3}' ",
//                    node.Name.Token.Line, node.Name.Token.Column,
//                    callable.ReturnSymType == null ? "procedure" : "function",
//                    node.GetSignature()));
//            }

            return(true);
        }
        public bool Visit(UserFunctionCall node)
        {
            if (!_symStack.IsFuncExist(node.Ident.ToString()))
            {
                throw new Exception(string.Format(
                                        "({0}, {1}) semantic error: function '{2}' is not found",
                                        node.Ident.Token.Line, node.Ident.Token.Column, node.Ident.ToString()));
            }

            var paramTypes = new List <Tuple <bool, SymType> >();

            foreach (var param in node.ParamList)
            {
                if (!WritableNodes.Contains(param.NodeType))
                {
                    throw new Exception(string.Format(
                                            "({0}, {1}) semantic error: expression '{2}' is not writable",
                                            param.Token.Line, param.Token.Column, param.ToString()));
                }

                param.Accept(this);
//                paramTypes.Add(new Tuple<bool, SymType>(param.IsLValue, param.SymType));
                paramTypes.Add(new Tuple <bool, SymType>(param.IsLValue,
                                                         (param.SymType as SymAliasType)?.GetBase() ?? param.SymType));
            }

            var paramTypesString = paramTypes[0].Item2.ToString();

            for (var i = 1; i < paramTypes.Count; ++i)
            {
                paramTypesString = string.Concat(paramTypesString, ", ", paramTypes[i].Item2.ToString());
            }

            var possibleCallables = _symStack.FindFunc(node.Ident.ToString());

            if (possibleCallables == null)
            {
                throw new Exception(string.Format(
                                        "({0}, {1}) semantic error: function '{2}({3})' is not found",
                                        node.Ident.Token.Line, node.Ident.Token.Column, node.Ident.ToString(),
                                        paramTypesString));
            }

            CallableSymbol calledCallable = null;

            foreach (var callable in possibleCallables)
            {
                if (callable == null || paramTypes.Count != callable.ParametersTypes.Count)
                {
                    throw new Exception(string.Format(
                                            "({0}, {1}) semantic error: function '{2}({3})' is not found",
                                            node.Ident.Token.Line, node.Ident.Token.Column, node.Ident.ToString(),
                                            paramTypesString));
                }

                var callableFound = true;

                for (var i = 0; i < paramTypes.Count; ++i)
                {
                    if (!((callable.ParametersTypes[i].Item2 as SymAliasType)?.GetBase() ??
                          callable.ParametersTypes[i].Item2).IsEquivalent(paramTypes[i].Item2))
                    {
                        callableFound = false;
                    }
                    else
                    //if got parameter is not lvalue, but it has 'var' modifier
                    if (callable.ParametersTypes[i].Item1 == "var" && !paramTypes[i].Item1)
                    {
                        throw new Exception(string.Format(
                                                "({0}, {1}) semantic error: parameter '{2}' is not lvalue",
                                                node.ParamList[i].Token.Line, node.ParamList[i].Token.Column,
                                                node.ParamList[i].ToString()));
                    }
                }

                if (callableFound)
                {
                    calledCallable = callable;
                    break;
                }
            }

            if (calledCallable == null)
            {
                throw new Exception(string.Format(
                                        "({0}, {1}) semantic error: function '{2}({3})' is not found",
                                        node.Ident.Token.Line, node.Ident.Token.Column, node.Ident.ToString(),
                                        paramTypesString));
            }

            node.CallableSymbol = calledCallable;
            node.SymType        = calledCallable.ReturnSymType;
            node.IsLValue       = false;
            return(true);
        }