private void VisitAccessMethod(
            AccessMethodNode node,
            Func <FunctionToken, Node, ArgsListNode, MethodInfo, string, bool, AccessMethodNode> func)
        {
            var args = Nodes.Pop() as ArgsListNode;

            var groupArgs = new List <Type> {
                typeof(string)
            };

            groupArgs.AddRange(args.Args.Skip(1).Select(f => f.ReturnType));

            var alias = !string.IsNullOrEmpty(node.Alias) ? node.Alias : _identifier;

            var tableSymbol         = _currentScope.ScopeSymbolTable.GetSymbol <TableSymbol>(alias);
            var schemaTablePair     = tableSymbol.GetTableByAlias(alias);
            var canSkipInjectSource = false;

            if (!schemaTablePair.Schema.TryResolveAggreationMethod(node.Name, groupArgs.ToArray(), out var method))
            {
                if (!schemaTablePair.Schema.TryResolveMethod(node.Name, args.Args.Select(f => f.ReturnType).ToArray(), out method))
                {
                    if (!schemaTablePair.Schema.TryResolveRawMethod(node.Name, args.Args.Select(f => f.ReturnType).ToArray(), out method))
                    {
                        var types = args.Args.Length > 0
                            ? args.Args.Select(f => f.ReturnType.ToString()).Aggregate((a, b) => a + ", " + b)
                            : string.Empty;

                        throw new UnresolvableMethodException($"{node.Name}({types}) cannot be resolved.");
                    }

                    canSkipInjectSource = true;
                }
            }

            var isAggregateMethod = method.GetCustomAttribute <AggregationMethodAttribute>() != null;

            AccessMethodNode accessMethod;

            if (isAggregateMethod)
            {
                accessMethod = func(node.FToken, args, node.ExtraAggregateArguments, method, alias, false);
                var identifier = accessMethod.ToString();

                var newArgs = new List <Node> {
                    new WordNode(identifier)
                };
                newArgs.AddRange(args.Args.Skip(1));

                var newSetArgs = new List <Node> {
                    new WordNode(identifier)
                };
                newSetArgs.AddRange(args.Args);

                var setMethodName = $"Set{method.Name}";
                var argTypes      = newSetArgs.Select(f => f.ReturnType).ToArray();

                if (!schemaTablePair.Schema.TryResolveAggreationMethod(
                        setMethodName,
                        argTypes,
                        out var setMethod))
                {
                    var names = argTypes.Length == 0
                        ? string.Empty
                        : argTypes.Select(arg => arg.Name).Aggregate((a, b) => a + ", " + b);

                    throw new NotSupportedException($"Cannot resolve method {setMethodName} with parameters {names}");
                }

                if (setMethod.IsGenericMethodDefinition)
                {
                    var setParams                = setMethod.GetParameters();
                    var genericArguments         = setMethod.GetGenericArguments();
                    var genericArgumentsMap      = new Dictionary <int, Type>();
                    var genericArgumentsDistinct = new List <Type>();

                    foreach (var genericArgument in genericArguments)
                    {
                        for (int i = 0; i < setParams.Length; i++)
                        {
                            var setParam = setParams[i];

                            if (setParam.ParameterType == genericArgument)
                            {
                                genericArgumentsMap.Add(i, genericArgument);
                                genericArgumentsDistinct.Add(newSetArgs.Where((arg, index) => index == i - 1).Single().ReturnType);
                            }
                        }
                    }

                    var genericArgumentsConcreteTypes = genericArgumentsDistinct.Distinct().ToArray();

                    method    = method.MakeGenericMethod(genericArgumentsConcreteTypes);
                    setMethod = setMethod.MakeGenericMethod(genericArgumentsConcreteTypes);
                }

                var setMethodNode = func(new FunctionToken(setMethodName, TextSpan.Empty),
                                         new ArgsListNode(newSetArgs.ToArray()), null, setMethod,
                                         alias, false);

                _refreshMethods.Add(setMethodNode);

                accessMethod = func(node.FToken, new ArgsListNode(newArgs.ToArray()), null, method, alias, canSkipInjectSource);
            }
            else
            {
                accessMethod = func(node.FToken, args, new ArgsListNode(new Node[0]), method, alias, canSkipInjectSource);
            }

            AddAssembly(method.DeclaringType.Assembly);
            AddAssembly(method.ReturnType.Assembly);

            node.ChangeMethod(method);

            Nodes.Push(accessMethod);
        }
        private void VisitAccessMethod(AccessMethodNode node,
                                       Func <FunctionToken, Node, ArgsListNode, MethodInfo, string, AccessMethodNode> func)
        {
            var args = Nodes.Pop() as ArgsListNode;

            var groupArgs = new List <Type> {
                typeof(string)
            };

            groupArgs.AddRange(args.Args.Where((f, i) => i < args.Args.Length - 1).Select(f => f.ReturnType));

            var alias = !string.IsNullOrEmpty(node.Alias) ? node.Alias : _identifier;

            var tableSymbol     = _currentScope.ScopeSymbolTable.GetSymbol <TableSymbol>(alias);
            var schemaTablePair = tableSymbol.GetTableByAlias(alias);

            if (!schemaTablePair.Schema.TryResolveAggreationMethod(node.Name, groupArgs.ToArray(), out var method))
            {
                method = schemaTablePair.Schema.ResolveMethod(node.Name, args.Args.Select(f => f.ReturnType).ToArray());
            }

            var isAggregateMethod = method.GetCustomAttribute <AggregationMethodAttribute>() != null;

            AccessMethodNode accessMethod;

            if (isAggregateMethod)
            {
                accessMethod = func(node.FToken, args, node.ExtraAggregateArguments, method, alias);
                var identifier = accessMethod.ToString();

                var newArgs = new List <Node> {
                    new WordNode(identifier)
                };
                newArgs.AddRange(args.Args.Where((f, i) => i < args.Args.Length - 1));
                var newSetArgs = new List <Node> {
                    new WordNode(identifier)
                };
                newSetArgs.AddRange(args.Args);

                var setMethodName = $"Set{method.Name}";

                if (!schemaTablePair.Schema.TryResolveAggreationMethod(
                        setMethodName,
                        newSetArgs.Select(f => f.ReturnType).ToArray(),
                        out var setMethod))
                {
                    throw new NotSupportedException();
                }

                var setMethodNode = func(new FunctionToken(setMethodName, TextSpan.Empty),
                                         new ArgsListNode(newSetArgs.ToArray()), null, setMethod,
                                         alias);

                _refreshMethods.Add(setMethodNode);

                accessMethod = func(node.FToken, new ArgsListNode(newArgs.ToArray()), null, method, alias);
            }
            else
            {
                accessMethod = func(node.FToken, args, new ArgsListNode(new Node[0]), method, alias);
            }

            AddAssembly(method.DeclaringType.Assembly);
            AddAssembly(method.ReturnType.Assembly);

            node.ChangeMethod(method);

            Nodes.Push(accessMethod);
        }