private DesignatorNode ParseMemberAccess(SymTable symTable, DesignatorNode record)
        {
            while (true)
            {
                if (Current.SubType != Dot)
                {
                    return(record);
                }
                Next();
                var rt = (RecordTypeSymbol)record.Type;
                var c  = Current;
                Require(Identifier);
                if (!rt.Fields.Contains(c.Value.ToString()))
                {
                    throw new ParserException("Illegal identifier", c.Line, c.Position);
                }
                var f = new MemberAccessOperator(new List <Node> {
                    record, new IdentNode(null, c)
                }, "Member access",
                                                 c.Line, c.Position)
                {
                    Type = ((VarSymbol)rt.Fields[c.Value.ToString()]).Type
                };
                switch (f.Type)
                {
                case RecordTypeSymbol _:
                    record = f;
                    continue;

                case ArrayTypeSymbol _:
                    return(ParseIndex(symTable, f));
                }
                return(f);
            }
        }
        private DesignatorNode ParseIndex(SymTable symTable, DesignatorNode array)
        {
            while (true)
            {
                if (Current.SubType != LBracket)
                {
                    return(array);
                }
                var ars = (ArrayTypeSymbol)array.Type;
                var p   = ParseIndexParam(symTable);
                CheckImplicitTypeCompatibility((TypeSymbol)p.Type, TypeSymbol.IntTypeSymbol);
                var i = new IndexOperator(new List <Node> {
                    array, p
                }, "Index", p.Line, p.Position)
                {
                    Type = ars.ElementType
                };
                switch (ars.ElementType)
                {
                case RecordTypeSymbol _:
                    return(ParseMemberAccess(symTable, i));

                case ArrayTypeSymbol _:
                    array = i;
                    continue;
                }
                return(i);
            }
        }
        private DesignatorNode ParseProcedureCall(SymTable symTable, DesignatorNode procedure)
        {
            var ps = (ProcedureSymbol)procedure.Type;
            var p  = ParseActualParameters(symTable);

            CheckParameters(ps.Parameters, p);
            return(new CallOperator(p.Childs, "Call", p.Line, p.Position)
            {
                Subprogram = ps
            });
        }
        private DesignatorNode ParseFunctionCall(SymTable symTable, DesignatorNode function)
        {
            var fs = (FunctionSymbol)function.Type;
            var p  = ParseActualParameters(symTable);

            CheckParameters(fs.Parameters, p);
            var f = new CallOperator(p.Childs, "Call", p.Line, p.Position)
            {
                Subprogram = fs,
                Type       = fs.ReturnType
            };

            switch (fs.ReturnType)
            {
            case RecordTypeSymbol _:
                return(ParseMemberAccess(symTable, f));

            case ArrayTypeSymbol _:
                return(ParseIndex(symTable, f));
            }
            return(f);
        }
        private DesignatorNode ParseCast(SymTable symTable, DesignatorNode type)
        {
            var ts = (TypeSymbol)type.Type;
            var p  = ParseCastParam(symTable);

            CheckExplicitTypeCompatibility((TypeSymbol)p.Type, ts);
            var c = new CastOperator(new List <Node> {
                p
            }, "Cast", p.Line, p.Position)
            {
                Type = ts
            };

            switch (ts)
            {
            case RecordTypeSymbol _:
                return(ParseMemberAccess(symTable, c));

            case ArrayTypeSymbol _:
                return(ParseIndex(symTable, c));
            }
            return(c);
        }