�PNG  IHDR����Q�gAMA�� �a cHRMz&�����u0�`:�p��Q<�bKGD�������gmIDATx���w�U���ﹻ�& �^C��X(�����J I@� ���"% (** B�X� �+*�i��"]j(IH�{~�R)��[��~��>h��{�}g�y�)I�$I��j��� ������.I�$I�$�ʊ�y@�}x�.�: �$I�$I��i}��V�Z�����PC)I�$I��F� ����^��0ʐJ�$I�$�Q^���}{�"���r�=��OzI��$gR�ZeC.�IO����vH eK��X� �����$IM�px��sk�.��쒷/��&���r�[޳����<���v|�� ��.I���~�)@������$�up��dY�R�����a�$I �|�M�.�e ��Jaֶ�pS�Y�R��6j��>h�%IR��ز�� �i�f&���u�J)������M�����$I vL���i���=�H;�7UJ�,�]�,X��$I��1��AҒ�J����$ X�Y� XzI��@G����N��ҥR���T)E��@��;��]K*�M�w;#�5_�wO�n~\ DC&�$(A�5 �R�R��FkvIR���}�l��!�RytRl;��~^Ƿ�Jj� �اy�뷦BZ��Jr����&ӥ�8�P�j��w~�vn�����v ���X���^�(I;�4�R=�P[�3]J�,�]ȏ�~��:�3�?��[��� ��a��&e)`�e*����P[�4]�T��=Cq�6�R[ ~ޤ����r�XR Հg(�t�_HZ�-Hg �M�$�ãm�L5�R� �u��k�*`%C-�E6/����%[�t X.{��8�P9Z�������.vk����XŐKj����gKZ��Hg�(����aK9ڦ��mKj��Ѻm�_ \�#�$5�,)- � �61eJ�,��5m|� ��r�'��=��� ��&ڡd���%-]J �on�� X���m|�{ ��R�Ҟ����e $eڧY X��Y�����rԮ-a�7�RK�6h���>n$5A�V�ڴ�i��*�ֆ�K)���mѦ���tm�r�1p| �q:흺,)O�����i��*�ֺ�K)���ܬ�֦����K-5�r�3�>0ԔHj��Jئ�EZ��j�,%��r�e��~�/��z��%j�V��M��ڸ�mr��t)��3]J�,���T ��K֦O�vԒg��i�i��*�����bK�i�NO~�%�P��W���0=�d��i�i�������2�t�J9��J����ݕ�{�7�"I P��9�JK�Tb�u,%��r���"�6�RKU��}�Ij�2����HK�Z�XJ�,妝�� X��Y�����rP��� ެ�2�4�c��%i��^���IK|.H�,%��r�b���:XR�l��1X��4Pe/`����x��&����P��8�Pj��28��M��z���s��x���2���r�\���zR��P�z�4J����}�y���P[g=�L)��� .Q[�6Rj�Wgp ��FI�H�*-`I�����M�RaK9T��X��c�q�����*I� y���[j��E>cw%��gL�R���ԕi�F�Cj�-��ď�a`������#e~���I�� j�,%��r�,)?[gp �FI˨���mn��WX#��>mʔ X�A��� �DZf9,�nKҲz�����I��Z�XJ�,�L#��k�i��P�z�4JZF�����,�I,`���61%�2s �$���,�VO��Ϛ2��/U��FJ�fy��7����K�> X�+�6� S�TX����Ie�����JI���Lz�M�fKm ��L�RaK9�%|��4p9L�w�JI��!`N����sia���zĔ)������%-� X��M���q�>�pk�$-$�Q���2����x#�N� ؎�-�QR��}ᶦHZ�ډ��)�J�,�l#��i@y�n3������L��N`���;�nڔ X�����u����X5��p��F)��m|��^�0(��>B�H���F9(c�զE��er��JI rg��7 ��4I@z�0\�JI��������i�䵙���RR��0�s;�$�s6eJ�,�`n �䂦���0�a�)S)�A������ ���1eJ�,堌#�6�35R��I�gpN��Hu������TH���_S���ԕqV�����e `� ��&S)���>�p;S$魁eKI����uX��`I�����4��춒�o}`m�$1"��:�PI���<[�v9�^�\p��TJj�r�iRŭ ��P{#�{R2,`)e-`mgj�~�1�ϣ�L��Kam�7�&U\j�/�3mJ�,�`F��;M��'�䱀�� .KR#��)y�h�Tq�;p���cK9(���q!w�?����u�RR,n.yw�*UXj#�\�]ɱ���(q�v2=R���q����f����B#i��Jm�m�L����<]�Y����͙�#�$5� ��u�TU�7��Ӧ�X�R+q�,`I}����qL�����'���`��6�K�ͷ�6���r�,�]����0S$-���� ��[RKR3���o��iRE����|�nӦ�X�R.�(i�:�L��D�L��TJj�Y��%o�:����)�����6���r�x���zҒ��q��TJj��h㞦I���.�$Y�R.ʼ�n�GZ�\ֿ��f:%5�5 ��I�˼!�6����dK�x��m��4E�"mG�_�� �s? �.e*��?L�RfK9��%�����q�#�uh$�)�i���3U�����L�RfK9yx��m܌b�j�����8��4���$�i��1U��^@Wbm��4uJ�,�����Ҫ�A�>���_Ij�?1�v�3�2����[�gL�R��D��9�6�o�Ta�R���׿��N7%�����L�2�� NT�,`)7�&�Ɲ��L�*꽙��yp���_$���M�2�#�A�����S�,`)7�$r��k�TA���29�_���Iy�e"�|/0�t)�$�n X�T2���`Y���J���;�6��J�x"�����.e�<�`�����$)� P�I$��5�V4]���29���SRI>�~�=@j�]��l�p�2`K9Jaai�^" Ԋ��29�O�RI%��:X�V5]J��m��N9���]�H;1U���C39���NI%���Xe78�t)a��;���O��i Ҙ�>X�t�"~G>�_mn:%���|~ޅ_�+]�$�o���)�@��ǀ{hgN;�IK�6�G&�rp�)�T2�i�୦K�Ju���v*���T��=�T��O�SV�>(��~D�>d�����m�,I*��Ɛ�:���R�#��ۙNI%��D>G��.n��$�o��;�+#��R�R��!��.e�U��˽���TRI�2��8�t)1L��WϚ>IJ�a3�oF��b��u&���:�tJ*��(F7��y�0�Z�R ^�p���'Ii� ����L�24x�| X��RI%�ۄ>S1]J�y��[z��L�$�adB7��.eh4��%���%�누>W�E���Tf+3�����IR:�I�3Xה)3אO�ۦSR�O'�ٺ�)S�}"��q�O��r[B7�ϙ.edG�)^E���TR"R��t��R�ݜh���0}������<�S����ɧx�.6,)�&���)SI�p��j�'I��?���A�L�"���L����.\TZV�N���!�'I�Y.��pAS����5}� �TRbNL�3��”�d�����b�e��)��4]��Mg/S���Z{ni����,)=k����Д�d��p��ǦO��uLb7�ߛ2%lO�}u�)���K��]le�T�P��j�eS(I�Z���ִ��R�^eJ%%*�/\ Ke�̈́�-O�M�$����|.5eJ��o�s)^]oJ%%,�㚃R� <�p��LS��� �*����`GӦ�tdt<�5���o'��/�6�٧�� _��BIJ�kH��_�6%d ���rQ�b�gZ%%n�ڍ9o1mj�U�g���JR>�L�F�VӦD�B^k_���J�Dj��\����=�L���S(I������v─a���T�eZ%e�U����A�M-�0;�~˃@�i��|l�� �@S���4y���7��2��>���sX-vA�}�ϛBI���!ݎߨ����W�l�*)3{'�Y|�iS�lEڻ(��5�����K��t��SI�$��Uv0��2���,~��ԩ~�x��;�P��4��ց��C�r�O%ty�n4��25:���KM��l�D� ^���4JR������xS��ه�F_}شJ�T�S��6uj�+ﷸk�$e���Z�O%�G�*^�V2�����u3E�Mj�3��k%)ok��I]d�T����)�UR�K���DS� �7�~�m@�TJR�~�荪�f�T"�֛L�� �\���s��M��� �-0��T �K�f�J��z+��n�إK�r� �L�����&j��(��)�[�E&I����� ߴ>e �FW�_�kJR�|!���O�:5�/2跌3��T-�'|�zX�� r�yp0��J����S ~^�F�>-�2�< �`*%�ZFP�)����bS��n"���L�� :)���+pʷf(pO���3��TMW$~����>@~ū:����TA�IsV�1}�S2�<���%��ޟ�M�?@�iT ,E�ū�oz%i�~��g�|`wS(�]�oȤ��8��)�$�� �� ntu`өe�`6y�Pl� Iz�MI{�ʣ�z����ʨ�� �)IZ�2��= ld:5+���請M�$-ї���;�U�>_���g��sY��$Á����N�5��W���z�W�fIZ��)�-��y�u�XI�fp���~S*IZ��dt�;�t�>K�ū��KR�|$���#Lc�Ԁ+2�\�;kJ��`]�Y���ǔ��M1B)��U�bG"IRߊ���<x����ܾ��ӔJ�������0�Z����=��'�Y��嵤����� Le�v�e�g��)�$��z���n����V-º����^�3Ւ�o�f��#0�Tfk�^�Z�s[�*I꯳3{���)�ˬ����W�4Ւ4 �Odp��bZ��R���S��|�*I� �����5��5�#��"�&�-IvT&��/��윚Ye:��i�$ �9��{�Lk�u�R���e�[��I~�_�\��ؠ%�>�GL�$iY�8 �9ܕ��"�S���`kS.I�l���C;Ҏ4���x&�>�u_0J��Lr����<��J�2�(^��$5��L� s�=Mg�V� �~�,Ij�u��>�� 7��r2�)^=G���$�1:��3�G< �`J�3�~�&IR%�� 6���T�x�/�rIj���3�O< �ʔ&#f�_yX��J�i�ގN��Sz;�� T�x�(��i���8%���#���4 �~�AS+Ij��e���r�I�U�rIj�����3�62��v8��8�5�+Ij�A�h�K__5���X��%�n�V%Iͳ-y��|7��XV��2��v4���fzo��_6��8���"�S/I-qbf��;� Lk��F�)K���SM�$���� Ms�>K� W�N���V����}�^`��-�큧3����2Œ�Vؙ�G��d��u�,^�^m�%��6���~��N�n�&�͓��3Œ�V�Z����MsRpfE�W���%I�wd���ǀ�Lm[��7���W&�bIR��L�@Q�|�)*�� ����i ImsI�����MmKm�y��V`�i�$��G+R� 0�t�V'����!���V�)֏���28����v���U�7͒�v���H����ꦼt���x�ꗞ�T ;S���}��7�M�f���+��fIR���H��N��ZUk�U�x5��SA�Jㄌ�9��Mq��μ���AIRi|�j�5��)o����*^���'<$�T����w���I�1��hE�U�^c�_�j�?���Е$%d`z� �c�y�f��,X���O IJ�nTg����A�� �����U�XRD��������� ���}�������{�H�}��^�S,P5��V��2���\����Xx`p�Z����|Y�k:����$e�� ��~ ��@nW�L�.j�+��ϝ���Y��b퇪��bZ� �BV�u�)�u�������/IJ_ �1�[�p.p60�bC�� >|X����9�1P�:��N\�!�5�qUB}5�a5ja `ub��c�VxYt1N�0�Z����z���l4����]7­gKj�]�?�4ϻ� ���*���[��b��g$)+À���*x쳀ogO$~,5� �ز���U��S�����9���� lq3�+5�mgw@��n�p1��sso Ӻ=����|�N6 /�g(�Wv7U��;��zωM=��wk�,0���u��T��g�_��`_�P`�uz?�2�yI��!b��`�k���ĸSo��+Q���x%!\��ο����e����|�އ���ԁK��S-s6��pu���_����(ֿ�$�i+��+�T8=�e�Y;��� �צ��P�+p��h�x��WQ���v���*���|p1��. ��ά. XRk�IQ�Y�P,���d�r�Z�� |����� ��������B�%������w��P|�S5`��~́@�i�޾��� E�;��Չaw{o'�Q��?%�iL{u D��?���������N1��B��D������!�o��w�����PHRe��FZ�*�� ���k�_-~����{����E9�b-��~P�`��f��E{AܶB�J�A�FO��� wx6��R�ox�5 K5����=�W����we�������hS8 (��J���C���l�J���~ p+���F���i�;ŗo+��:�bD�#g(��C��"�wA^�� r.�F�8L;�dzd�IH�U�X��݆�Ϟ�X�g� �)I�F����q��e�m��%I�4�d�j&pp�T�{���'{�HO���x�( Rk���6^C�٫����O.�)�3�:s(��۳(Z�?~ٻ8�9�zmT"�PL�tw䥈��5��&b<8GZ-�Y��&�K�?e8,`I��6���e�����(֍x�b8�3 � `���r�����zX�j��)F�=l($I�j� �2*�(F�?h(/9ik:��I`m#�p3�Mg���L�aKj�c�/U��#�n5����S�# �������m(^)=y=đ�x8Ŭ����I���[U]����~S�цA�4�p���$-F �i(��R�,�7C�x�;X��=�c����I��>���{���Km�\ �o(T��v�2������v�x�2q�i����iDJ�N�,���Ҏ����!1f� �5quB�j��1��!�8 �r���D�Fd(���!���W���Ql��,g�S��k����L��1Bx��g'�'�՞�^���ǘ;�p����Q ���P(c���_ IRu����j�g(�W�z ����b�s�#�P�­rz�>���� k�� c&nB=�q+��ؔX���n#r5����)co���*Ũ�+G��?7��<�� |�P�����Q��ӣ'�G�����`uO�d>%M�ct�z�#�� Ԫ�����ڞ�&�7��CaQ��~N�'��-P�.�W`Oedp0�3C!IZc�I�AMP��U�ۀ5�J�<��\u~+�{�9�(Fb�b���y�A�e�B����hOS���ܳ�1� b��È���T�#��ŠyDžs�����,`5�}��D���C�-�`��̞%r&�ڙa��8�7Q����W����W����p6e7� ��Rϫ/�o����Y� ꇅ N��ܶ�ը��tc��!��L���A ���T�7�V4�J�sū� I-�0����P��x�z7�QN�F���_�i����Z�g�úW�k�G���83� ���0e�Wr9 X����]㾮݁#���Jˢ C�}0��=3�ݱ��tB��i�]�_ ��&�{�{[/�o[�~ \q�鯜�0��0��٩���|��cD��3�=4��B_b� ���RY��b$ó�BR���sf�&������l�L�X#M*��C����_�L܄:gx�)WΘs���GSb���u��L ���rF$9�'�;\4�Ɍ�q�'�n�[%p.�Q`�������u ��h���N�b`eCQyQ|���l�_���C>L���b꟟3h��Sb�� �#��x��N��xS���������s^�� 88�|�Mz�)��}:�](vbۢ�amŖ࿥� ��0)Q����7���@�0���=?^k(*�J�����}�3ib�kF�n H�jB׻���NO���� �z�� �x}�7p 0�t��f����D����X�.lw��gȔ��h�Ծ�Ų� }6�g� E� |�Lk��LZ���t��eu+=���q���\I�v0쮑�)��Q�ٵpH8�/2?Σ�o�>�J�vpp��h�������o~�f>%bM��M���}���\�//��":�PT��c(v���9v���!��g�ո��Q ��)��U�fV��G��+!� ��35{=�x\�2�+��k�i,y$���~A1��iC�6#)v��C�5�^>�+gǵ�@1�Hy٪7����u;p ps�ϰ�u���/S�� <��aʸ����Gu't�D1�ԝI��<��p��g|����6�j��'p:�tպ�h��X�{�o(7v],��*�}��6�a_����<�u`��Ȯ�r.E�;ˑ�q�io�p�R��"������26�2E�8j�� ]����U��鿍ǜ�v���D��,2���վ�8ϫ�:�e/^AQ����T�H{�WgRl���̊���2Yx���"1�Q�> �wX�Rk,O�]�Lܳ���~V<�����F���8��a��_g~�o.�XCD�?S�t���h���梫A�o�%���~K1ݵ��O1�LyZ�bJ�� E��Q���xpq�i�Cpv��a6��_�:�wejT����]����"����<��u`"���� 2>���o4��5rp"N5k��;�m���{���rZ�b������Φ${#)��`(��Ŵ�g�,;j���%�6�j���.�pyYT��?}-��kB������D���c3q����A`��N��WQ���ū2�0�/^A��Z�W%�N�Q��� ��MI�.��X#P��#����,^Eb�c&��?X�R tA�V�|Y���.�1����!�����؅�⨉ccww���>���i��v��l(J��T�~� �u`��ٵDm �q)���+���Ri�� x/�x��8cyFO�!�/���*�!/��&��,7�<.���N���,�������YDŽ�&ܑ�Q�F1�Bz��)F���P�ʛ�?5����d� �6`�����kQձ �λc�؎�%58�2��Y��&nD�_$Je4��>a��?��!� ��ͨ�|�Ȏ�WZ��S�s��v���8� �j����(�I��&��y�j� �Jb5��m��?��H������Wp��=����g}�G��3��#�|I��,5v珿�] H~�R3�@B��������[☉9Ox~��oMy�=J���;�xUVoj�� �b�U�s�l_��35�t-�(Ճɼ�RB7�U!�q��c��+�x�4�H�_�Q�o֮$[���GO<��4`��&č�\GO�c[�.[*�A�f%m��G/� ň�M�/�r ��W�/Nw~B1U3������J�?��P&���Y�� �)`�ѓ����Z�����1���p]�^l“��W#)lWZ�i����l�U�Q�u`��-����m|xĐ,������_�ƪ|9i:�_��{*(3G�ѧ}�Uo�D+�>m_�?V��Pۅ�15���&}2�|���/p�IOʵ�>���� G�Z9�cmíت�mnz��)yߐb���D�������� �>e}:�)� �r|@�R5q�V�S�����A�10�C%�E�_��'^�8c��������R��7O;�6�[���eKeP�������G������ϦX7�j���b}��OT�GO^j��n*媓����7n����GMC�� � ���t,�k31�R�b �(v�yܴ�ʭ�!��iTh8~�ZY�Z�p��(q��s���RL ?�b���}����c�Ũ�ʊGO^���!��rP�JO��1��5�MJ[��c&~������Z`"��ѓޔ����H1���C&����^|��Ш|�rʼ,�A�wĴ?�����b��5)�t��L��U��)F�|�� �&��g٣O]���oqSU����j���y(��x<��Ϳ3 ���.���FS�k���oYg�2� \_#w��j�{u'r�Q������>���o���;���%n�|�F�*�O�_��L�"�e�9um��Dds�����?.��fu�u����Qb��IW�z |4\0� s�b;�O�v��xOS�s�; G%����T4g��FR�u�rj���(֍ڑb �u�ԖK�D���u��1MK{���1^ q;�� �C=�6\8��F��R��艇�!���%\Y�Ô�U| �88�m��)֓��Nc��L�ve�� C�6z;��o&�X x5�9�:q���6�1�Z��(T����7���>C?�g�c�ļ�x�ѐ�� Z� ���o�o-�0�8j�ہ �x�,�`���'��� ��Ҕ���Oc��Rl��f��~���`�����jj�"�.N�v+���sM������_��]������Z�k��� �g( UOP���������y�εx%�pU����h�2�������(���@��il0���ݽ��QXxp�px-�N�S��( W�O+�轾 n��Fߢ����3M��<;z�)��FBZ�j����c�i��u�/�Q�oF�� �7R�¥ Z��F�L�F�~��#����ȣ��ߨ^<쩡�ݛк���v�џ)��)���M��E>ώ�x4�m#!-�m���!L;vv#~Y[��đ��K�����m����x�9.[,��U����FS �����C���VkZ ���+���ߟ�r�Y٧��IZd/�io�i$�%��͝ب_ֶX�3���ܫ��hNU �� Z����Z�g�k�=���]��=������b���b��JS[�w��j�U(��)���*I =ώ:}-蹞�l�Uj�:��1��}����M�W��m�=̛���� _�� ¾,8��{__�����m{_�P��V���K^n3�e����sw5�ӫh�#�$-�q=�A̟> ,^I}P�^�J$�qY~Q[ Xq���9�<�r�d�sߏǜs�#������%/���y����kKZ�������b��?� S�k�tc�񫝶L���&I ���W!�b �>{#�&�T.^����G��Vj�_���_R��K�p����n,b=`�ż����Y@�^՝��;z�{p�aV��Kk����QXj�/�)y� ��TI�c&F�;FB�G�7w����g� ZZD�G��!����x�� �r_�t��Ƣ!�}�i�/�V��=M����/��#��n��B8 Xx�Ы ^�@�CR�<{䤭����Y��CN��)�e���K��OSƟa $��&�g[i3�.C�6x�rOc���8�TI���;�o�� ��hH6�P�&L{�@�q��6�[���� �G��zp�^���71�j��(�l�`�J�}]���e6�X����☉#͕� ���׈$A�B1�Vj��h㭦IRs��qFBj�w�Q_7�Xk��>y"������N=�M�B0� ��,�C #�o6MR��c���0��|�$�)�ف����"1����!i���xY<���B��9mx�� ��`���,��t�A�>)5ػ�Q���?j��Q�?�cn�>Y�Z�e�� �Tis���v���h�#�� �GMމȇ���p�:���ԴVuږ�8ɼH��]C.���5C!UV;F`m�b��Bk��� ��L�TM�vP���ʍϤj�?��ԯ/Q�r1���N�B`9s"����s�� �TYs����z��� ��&�9S%U԰�> �{��<�ؿ���SM���xB��|H�\3�@!U�|�� �k']������$U�+>��� |�HHM����Lޢ�?��V9i�D!-�@��x�� �TI���î�%�6Z��*��9X�@HMW#�?��n�N� ,o�e6�?tQw��ڱ�.�]-����y����'�:mW�0#!�J82qF�jH -`�ѓ�&��M��0����u� Uγmxϵ��^-��_�\�]����)@0R���t.8�/?ٰ�C��Y]�x���}=sD3�o��j�ަ���Ы�N���uS%U��}Ԥw��HH�>ڗ�jܷ_3gN �����q7�[q���2�l���a���*����A�r���Ǔ��Ԗ+p�8���/��R��GM�� ]j�a����c�d(�JhWko�6��ڎb�j�]i���5���Bj�����3+�3�!\j��1�����U�Z���L��s��L�T�v8�HHmup<��U���\��GMމ�3�R+��w4R�����6�j� XW�M�T!��u(�*!��Pz�,����#���Sq���*�8?vww )kO���a��$�[&��?�*�bB�X� �@���%�����8�]�=���R�r)kO��w�0j��i��M�Tq�� ng$�2\�q�8f�:���e�N1�R�xr<��5 ������;��M��p^�@;��7]R��ꎾ�JtER�.�/(5�v3�R[�@=�h�l����?����l�@;���.���[]�Q*�Z\�4��"1P�'Y��w��x��# ���ǀg���{��5�i��_IUR���z�RɞsyS5q�E����=�@�Y���っ �����v�k��6 ��&��5�1E�o0�|�kp�c��#�j=`�D���WRU�����j̟���J'P� �����w2 �S� �v�:��p�g�3Rv�},�#�����8�b��Z~�&��(F�=�i�><�>gK�M���Jj����0�@H%��,����W�΃�7�R) "�>c�,����� x�ix������ј���^ ��aܖ>�H[�i.UI�Hc �U�1=y�W\���=��S*���G���R~�)AF���=�`�&�����2��h`�D�z���T��󑓶�����J+����?�W+}��C�%��P:|�0H���܆��}-�<;O����C[�~o.��$~��i�}��~�HQ�� �Tv�X������Έ�r=b}$��v����i�z�L��4�:�ȰT|4�~����*��!o�X�QR6��L�k+�#������t/g �lԁߖ����[��Jڶ_N$�k���������*"�.� ���x���s��xX���7jRVbA��A�ʯKҎ��U3����)�zS�NN �_�'��s�?f����)��6������X���!%s�s�A���kʱ>���qƷ�b ��h�g� %n���� �~p�1RE��GM���HH�=�������B�Jiy[<�5 ���ǁJҖ�����g�K���R��*�倳��e��~�HUy��)A���g,K)`�V�w6bRR:��q����L#\�r���cl��K��/�$�s�h�*$�� ������6�����덤�� ����KԖc� 3��Z�9��=�Ɣ�=o>�X � �Ώ�"1�� )a��`�S��JJ�6�k<��U�������-]�� b�m`��{r�y;����T���u��_GR5���*�%6�do�#XRg#���-!nl��$u�3��A�� L+Q{��9�x~�a-�|�H� �vbq[\�NJT%�]���rO8,��E�-F�����w)+?(Y{�Lz�n6����׀��?C������R�~�,)m�䎧�R�7���cww����qpW�ڳ=i.��U`Xf�F�b=�V��LJ��H^LI}��%�} ��|��w���� aG�$�,��^�R^�6 k2�^B�{7��t�������V�%@G�q� p�%R�zģN_ ��HHI[7�ֱ�>(��<�c e�{%kϊ����P�+��SL'�T�cM���J����WR���m ���ŏ�"�w)qc e�f�꒵i?��b7�b����(�'�"��2r%��������~�HUS�1���\<��(`�1�W����x��9�=�8HY9��m:X��1�8�b��g�����D1��u ���~|H��;K��-��U�ep�,,� C�1 RV.���M�R�5�άh����,�t��W�O8W���C�$ XRV�sQS]3G�J|�1��2����� [�v�M� ������:��k�#����~tH�3�0Rf-�����HYݺ-`I�9�%l�I��D�T�m\ ����S�{]��9�gO���ڒ�M���NCV\��G��*����2���J�R�Ũ;�R��ҏ^���ڽ�̱�mq�1E�u?�To�3I���)��y^��#�j�J�w���^�Ń�j��^�v����vl����B_��⋌�P�4x>0$�c>���K†A�ļ9s_V���jT��t0l�#������m��>E��-�,�,�x�,��-�W��)������سo&�9�6�R�E XR.6b���Xw�+)G���A�����E�v�L�)�͞K4�$p=�Ũ��i_ѱ�O�j��b�� HY����/���+@�θH9޼]�N�ԥ��%n�{����� �&zjT�?�� �Ty) s^�U��L�����lb�,�P�iTf�^���<À�]������� ��62R^V��7)S!nl�l��S�6~�͝�V�}�-=%*� ʻ>���G�� ���������D��nK��<��y��&>L����Py7'r=Hj���� ���9��V`[c"�*��^�8H��pc�����O�8�b�nU�`4���J��ȪA�Ƌ#��1_\ XϘH��PR���gi�k(�~G�~��0��D���A���A����_2�p�|�J�묭a����2���\N�C�r�]����M���_0 �^T��%e#����vD��^��%��x��y-n���}�-E�\�3�aS%�yN!�r_��{ �)s���A��w ���ڼp1pEAk�~v�<�:`'ӭ^�5 �����A�r���X�������OI驻��T �(��dk�)�_�\<��w���^��W�I��"�RFj3��V# ���M<,o�J��.H��#�\�SK���s]���� )��9> P��u�������A�*�B�Y�]����y�B�"�l�\�ey��� ��hH���*t��b�K)3��� IK�Z��򹞋X�jN�� �n� �*n>k�]��X�_��d�!�ry��BH� � ]��*R�� ��0(#'�7 ������%es9??��ښFC��,ՁQP��������j�����AR��J�\Ρw� �K��#��j���ah�g�w�;�2$�l*�)� ��%���Xq5�!U᢯�6Re] |�0����[�_�����_64�c�h�&�_}��i�L8K��Eg�Ҏ�7 M��/�\`|.p,�~`�a���=�BR?x�ܐrQ���8K� XR���2M�8�f ?�`s�gW�S%�"� ������Ԉ 7R%���$� N������}��?QL1|-э�ټwI�Z��%���pv�L���3Hk>,I����m�g�W���7{��E�� x�PHx�7�3R�����A�� ���@R�S�� CC���� �������!\ȟ���5I��XR^Z���xHл�$Q[��ŝ�40 (�>�+� �_C ���>���BR�t��<,T�r�T �������{���O�����/�H��+˟Pl6 I ���B)/�V���C��<6���a��2����~�����(�XwV4�g�n���XR� ϱ5�ǀHٻ?tw�똤Eyxp���{�#���WK��� �q����G%5���]�,���(�0ӈH����� HZ���])ג=K1j��&��G(FbM�@����)%�I` XR�����g ʔ�� KZ�G(v��P,�<`�[� K���n^ ��SJR���sAʠ�5xՅF`�0&R�b�V� ��t��x�:Ea�UE�/{�f��i�2;.I����A��wW8��/��t�T�x�A��GOo��N�?�G���}�l L�(���n�����`�Zv?���p�B��8K�_g�����I�+ܗ � #��i��?���ޙ�.��) p����$�u�tc �~DžfՈE�o3��l/)I-U�?a�ԅ�^��j�x�A�r����A�� ΧX��������}�DmZ@QLےbTXGd�.^|x�KHR{���|Ε�W_h]�� ���I��J`[�G9�{��)�.y�)�� ��<���D�*��zk�(ּ���Ya����O���8S����?��2-��� ������H13����#pK"���I`]`O� ��h�&=�S���F1Z�/Ie����D1R�W�a�"t'�x?!)Ou:��1 ��|��6��gt\s�����7�=�z_;�ؠ��>�0X Y��A1]q�p?�p�_���k+J*��Y�@HI>�^��?�g�t.06R�n ��,��`�� ��?)�;p pSF9����Z����X���L�����BJP�W���j���gQ|�&)7!�� Hj��Q��t���<| ؅��W�5 x �W��� �� HIz�Y���oV���M�G�P��� Hj��n`+�\�(d��N���W)F+I�rS�[���|��/a�����`K��|�ͻ�0Hj�{�R,���Q=��\� (F�}\�W�������R)A�g��SG`I��s�n���AR�=|�8�$}�G(v��C��$)s���� FBJ�?]�_�u XRv�ύ��6z�� �Ũ�G[��3��6-�T9�H��z��p����W�̞ú������� X�����g�큽�=�7C�u������fzI���$��)�k�i���^q��k��-) � ���0H*�N` �QZ��k�k]/���t���nn���sI�^Gu't=��7$�� Z;�{���8�^��jB��%� ��IItR�QS7�[�ϭ���3 �$�_���O�Q�J`7�!�]���W��"��W,)�����Iy �W��� �AJA�;K���WG��`IY�{8���k$I�$�^��%����9�.�^(`��N|���LJ�%�@�$I�}ֽp���=FB*�xN��=gI?Q{٥�4B)m���w �$I���gc~d��Z@G�9K��� X�?7)a�K�%�݅K�$IZ��-`I���p����C ������U��6�$I��\0��>!��9�k}��� Xa� ����II�S���0H�$I �H ?1R��.�Ч�j���:�4~R�w���@p�$I����r��A*�u��}��W�j�WFPJ����$I�➓/���6#!�� L�Ӿ��+ X36�x�8J �|+L;v���$I���o�4����3���0����1�R2�0��M� I�$-E}��@����,pS�^ޟR[���/����s¹'��0H�$IKyf��Ÿ���f�������VO�π�FT*�����a$I��>��H��e��~����V���Y/3�R�/�)��>d$I��>2��8`Cj���w�,n@�FU*�9tt�f$I��~<;��=�/4RD~����@��� X��-�ѕ�z��ἱI�$��:� ԍ��R �a�@��b X��{��+�Qx��u�q�$I�Л�z�o���� /~3\8����ڒ���4B������N7�$IҀ���j �V]n1�8H�$I��YFBj�3��̚�̵���ja ����p���p��� �$I���s/3R� Ӻ�-�Yj+L;�.0�R�́��I�$�A���v?� #!5�"��aʄ��j}���U���Km�ɽ��H�$Ij��C���Ys?h$I��Dl8�4��3���.��v�}���m���7�UiI=�&�=0L�g0$I��4���: ���emb�e��`���� e�Qbm�0u�? �$I�T!Sƍ'�-���s��v�)s#C��0�:�XB���2���a� w I�$�zbww�{�."p��Pz�O� �=�Ɔ�\����[� �����o($I������aw]�`���E���).K���v�i�:�L�*#gР7[��$I�����yG���PI=@��R� �4�y��R~�̮�´cg I�$I/<�t�P�ͽ ��h�Dg�o� 94����Z^k盇�΄8��I���56��^�W�$I�^�0���̜�N�?4*�H`237}g��+h���x�o��q)��SJ@p|�`�� �$I��%>������-�h���O�0e�O�>��\ԣNߌZ��D6��R�=K ����~n($I�$��y�3��D>o4�b#px�2���$��yڪt���z���W���~a�� �$I��~?�x<��e{W���g��ô�{�x$/�=�{t�G�0�7��e���a���B�$IҀ�yG��^S�卆�"puS��3��*�E=洣��,`9�>��'���Bww�pH�$IZ��ݑ�nC�㧄���Pc�_9��sO gw����J=l1�:mKB�>�����Ab<4L�p���$I�����b �o1Z���Q�@8�5�b�̍ S'�F���,�F��e���,^I�$Ij���E�dù{�l4� �8�Ys_�s� Z8.��x �m"+{~��?q,��Z D�!I�$��ϻ�'|X�h��B�)=��…'�]��M�>��5��� r�g���otԎ 獽�PH�$Ij����IP���hh)n#�cÔq���A'�ug5qw���U�&r�F|1��E%I�$%����]��!'�3�AFD/;C�k_`�9��� �v�!ٴt�PV�;��x`�'��*b�Qa� w I�$I�x�5� �����FC�3D����_��~��A�_�#O݆���Dv��V?<���q�w�+I�$I�{��=�Z�8"�.#RI���Y�yj���Ǫ����=f�D�l�9�%�M�,�����a8$I�$��Yw�i[�7�ݍFe�$�s��1��ՋBV�A?�`�]#!��oz����4zjLJ���o8$I�$%�@3j�A��a4��(�o�� �;�p,,dya�=��F9ً[��LS���PH�$IJ�Y�Љ+3��> 5"���3�9�aZ�<ñh!�{T�pB�G��k��j}��S�p��� �$I��lvF��.���F$I� ��z<� '\���K*qq��.f�<���2�Y�!�S"-\I�$I��Yw�č��jF$ w9��� \ߪB�.�1�v!Ʊ���?+��r�:�^�!I�$�BϹ�B� ����H��"�B�;L��'G[ 4�U�����#5>�੐�)|#�o0��aڱ���$I���>�}��k&�1`U#�V��?��Ys��V x���>�{t���1�[�I~D���&(I�$I/{��H�0fw�"�q"���y�%��4����� I�X�y�E~��M�3 8Xψ��L}q�������E�$I���[�> �nD�?~�s����f��� �����]o�΁� �cT��6"?'�_�Ἣ� �$I��>�~��.f�|'!������N�?�⟩����0��G KkX�Z�E��]�ޡ;�����/����&�?k�� O�ۘH�$IR��������ۀw�XӨ��<�7@��P��nS��04�a����Ӷ�p�.��:��@���\IWQ�J6�s�S%I�$���e��5��ڑ���v`�3:���x'�;��w��q_�vp�gHyX�Z� ��3�gЂ7{{���E�����uԹ�n�±��}�$I�$��������8t;b|��5��91n��ء����Q"�P������6���O�5�i���� }�i�R�̈́���%�Q�̄p!�I䮢�]��������O{�H�$IR�ϻ�9��s֧�� a=`-� aB\X��0"+5"C�1�H�b?߮����3x��3�&�g�ş�g��g����l��_���h�����Z^,`5�?���ߎ��vĸ%�̀M!�OZC2#0x ����LJ��0�� �G�w����$I�$I�}�<�{Eb�+y���;�iI,`����ܚ��F�����:�5��ܛ�A�8���-O�-|�8�K�7��s�|#�Z8�a&�>���<��a&����/V��tb�t��L��ʌI�$I�$I�$I�$I�$I�$IRj���D��D�%tEXtdate:create2022-05-31T04:40:26+00:00�!Î%tEXtdate:modify2022-05-31T04:40:26+00:00�|{2IEND�B`�Mini Shell

HOME


Mini Shell 1.0
DIR:/home/htlwork.com/www/dev/magento/vendor/webonyx/graphql-php/src/Language/
Upload File :
Current File : /home/htlwork.com/www/dev/magento/vendor/webonyx/graphql-php/src/Language/Parser.php
<?php

declare(strict_types=1);

namespace GraphQL\Language;

use GraphQL\Error\SyntaxError;
use GraphQL\Language\AST\ArgumentNode;
use GraphQL\Language\AST\BooleanValueNode;
use GraphQL\Language\AST\DefinitionNode;
use GraphQL\Language\AST\DirectiveDefinitionNode;
use GraphQL\Language\AST\DirectiveNode;
use GraphQL\Language\AST\DocumentNode;
use GraphQL\Language\AST\EnumTypeDefinitionNode;
use GraphQL\Language\AST\EnumTypeExtensionNode;
use GraphQL\Language\AST\EnumValueDefinitionNode;
use GraphQL\Language\AST\EnumValueNode;
use GraphQL\Language\AST\ExecutableDefinitionNode;
use GraphQL\Language\AST\FieldDefinitionNode;
use GraphQL\Language\AST\FieldNode;
use GraphQL\Language\AST\FloatValueNode;
use GraphQL\Language\AST\FragmentDefinitionNode;
use GraphQL\Language\AST\FragmentSpreadNode;
use GraphQL\Language\AST\InlineFragmentNode;
use GraphQL\Language\AST\InputObjectTypeDefinitionNode;
use GraphQL\Language\AST\InputObjectTypeExtensionNode;
use GraphQL\Language\AST\InputValueDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeDefinitionNode;
use GraphQL\Language\AST\InterfaceTypeExtensionNode;
use GraphQL\Language\AST\IntValueNode;
use GraphQL\Language\AST\ListTypeNode;
use GraphQL\Language\AST\ListValueNode;
use GraphQL\Language\AST\Location;
use GraphQL\Language\AST\NamedTypeNode;
use GraphQL\Language\AST\NameNode;
use GraphQL\Language\AST\Node;
use GraphQL\Language\AST\NodeList;
use GraphQL\Language\AST\NonNullTypeNode;
use GraphQL\Language\AST\NullValueNode;
use GraphQL\Language\AST\ObjectFieldNode;
use GraphQL\Language\AST\ObjectTypeDefinitionNode;
use GraphQL\Language\AST\ObjectTypeExtensionNode;
use GraphQL\Language\AST\ObjectValueNode;
use GraphQL\Language\AST\OperationDefinitionNode;
use GraphQL\Language\AST\OperationTypeDefinitionNode;
use GraphQL\Language\AST\ScalarTypeDefinitionNode;
use GraphQL\Language\AST\ScalarTypeExtensionNode;
use GraphQL\Language\AST\SchemaDefinitionNode;
use GraphQL\Language\AST\SchemaTypeExtensionNode;
use GraphQL\Language\AST\SelectionNode;
use GraphQL\Language\AST\SelectionSetNode;
use GraphQL\Language\AST\StringValueNode;
use GraphQL\Language\AST\TypeExtensionNode;
use GraphQL\Language\AST\TypeNode;
use GraphQL\Language\AST\TypeSystemDefinitionNode;
use GraphQL\Language\AST\UnionTypeDefinitionNode;
use GraphQL\Language\AST\UnionTypeExtensionNode;
use GraphQL\Language\AST\ValueNode;
use GraphQL\Language\AST\VariableDefinitionNode;
use GraphQL\Language\AST\VariableNode;
use function count;
use function sprintf;

/**
 * Parses string containing GraphQL query or [type definition](type-system/type-language.md) to Abstract Syntax Tree.
 *
 * Those magic functions allow partial parsing:
 *
 * @method static NameNode name(Source|string $source, bool[] $options = [])
 * @method static DocumentNode document(Source|string $source, bool[] $options = [])
 * @method static ExecutableDefinitionNode|TypeSystemDefinitionNode definition(Source|string $source, bool[] $options = [])
 * @method static ExecutableDefinitionNode executableDefinition(Source|string $source, bool[] $options = [])
 * @method static OperationDefinitionNode operationDefinition(Source|string $source, bool[] $options = [])
 * @method static string operationType(Source|string $source, bool[] $options = [])
 * @method static NodeList<VariableDefinitionNode> variableDefinitions(Source|string $source, bool[] $options = [])
 * @method static VariableDefinitionNode variableDefinition(Source|string $source, bool[] $options = [])
 * @method static VariableNode variable(Source|string $source, bool[] $options = [])
 * @method static SelectionSetNode selectionSet(Source|string $source, bool[] $options = [])
 * @method static mixed selection(Source|string $source, bool[] $options = [])
 * @method static FieldNode field(Source|string $source, bool[] $options = [])
 * @method static NodeList<ArgumentNode> arguments(Source|string $source, bool[] $options = [])
 * @method static NodeList<ArgumentNode> constArguments(Source|string $source, bool[] $options = [])
 * @method static ArgumentNode argument(Source|string $source, bool[] $options = [])
 * @method static ArgumentNode constArgument(Source|string $source, bool[] $options = [])
 * @method static FragmentSpreadNode|InlineFragmentNode fragment(Source|string $source, bool[] $options = [])
 * @method static FragmentDefinitionNode fragmentDefinition(Source|string $source, bool[] $options = [])
 * @method static NameNode fragmentName(Source|string $source, bool[] $options = [])
 * @method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|NullValueNode|ObjectValueNode|StringValueNode|VariableNode valueLiteral(Source|string $source, bool[] $options = [])
 * @method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|NullValueNode|ObjectValueNode|StringValueNode constValueLiteral(Source|string $source, bool[] $options = [])
 * @method static StringValueNode stringLiteral(Source|string $source, bool[] $options = [])
 * @method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|StringValueNode constValue(Source|string $source, bool[] $options = [])
 * @method static BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|ObjectValueNode|StringValueNode|VariableNode variableValue(Source|string $source, bool[] $options = [])
 * @method static ListValueNode array(Source|string $source, bool[] $options = [])
 * @method static ListValueNode constArray(Source|string $source, bool[] $options = [])
 * @method static ObjectValueNode object(Source|string $source, bool[] $options = [])
 * @method static ObjectValueNode constObject(Source|string $source, bool[] $options = [])
 * @method static ObjectFieldNode objectField(Source|string $source, bool[] $options = [])
 * @method static ObjectFieldNode constObjectField(Source|string $source, bool[] $options = [])
 * @method static NodeList<DirectiveNode> directives(Source|string $source, bool[] $options = [])
 * @method static NodeList<DirectiveNode> constDirectives(Source|string $source, bool[] $options = [])
 * @method static DirectiveNode directive(Source|string $source, bool[] $options = [])
 * @method static DirectiveNode constDirective(Source|string $source, bool[] $options = [])
 * @method static ListTypeNode|NamedTypeNode|NonNullTypeNode typeReference(Source|string $source, bool[] $options = [])
 * @method static NamedTypeNode namedType(Source|string $source, bool[] $options = [])
 * @method static TypeSystemDefinitionNode typeSystemDefinition(Source|string $source, bool[] $options = [])
 * @method static StringValueNode|null description(Source|string $source, bool[] $options = [])
 * @method static SchemaDefinitionNode schemaDefinition(Source|string $source, bool[] $options = [])
 * @method static OperationTypeDefinitionNode operationTypeDefinition(Source|string $source, bool[] $options = [])
 * @method static ScalarTypeDefinitionNode scalarTypeDefinition(Source|string $source, bool[] $options = [])
 * @method static ObjectTypeDefinitionNode objectTypeDefinition(Source|string $source, bool[] $options = [])
 * @method static NodeList<NamedTypeNode> implementsInterfaces(Source|string $source, bool[] $options = [])
 * @method static NodeList<FieldDefinitionNode> fieldsDefinition(Source|string $source, bool[] $options = [])
 * @method static FieldDefinitionNode fieldDefinition(Source|string $source, bool[] $options = [])
 * @method static NodeList<InputValueDefinitionNode> argumentsDefinition(Source|string $source, bool[] $options = [])
 * @method static InputValueDefinitionNode inputValueDefinition(Source|string $source, bool[] $options = [])
 * @method static InterfaceTypeDefinitionNode interfaceTypeDefinition(Source|string $source, bool[] $options = [])
 * @method static UnionTypeDefinitionNode unionTypeDefinition(Source|string $source, bool[] $options = [])
 * @method static NodeList<NamedTypeNode> unionMemberTypes(Source|string $source, bool[] $options = [])
 * @method static EnumTypeDefinitionNode enumTypeDefinition(Source|string $source, bool[] $options = [])
 * @method static NodeList<EnumValueDefinitionNode> enumValuesDefinition(Source|string $source, bool[] $options = [])
 * @method static EnumValueDefinitionNode enumValueDefinition(Source|string $source, bool[] $options = [])
 * @method static InputObjectTypeDefinitionNode inputObjectTypeDefinition(Source|string $source, bool[] $options = [])
 * @method static NodeList<InputValueDefinitionNode> inputFieldsDefinition(Source|string $source, bool[] $options = [])
 * @method static TypeExtensionNode typeExtension(Source|string $source, bool[] $options = [])
 * @method static SchemaTypeExtensionNode schemaTypeExtension(Source|string $source, bool[] $options = [])
 * @method static ScalarTypeExtensionNode scalarTypeExtension(Source|string $source, bool[] $options = [])
 * @method static ObjectTypeExtensionNode objectTypeExtension(Source|string $source, bool[] $options = [])
 * @method static InterfaceTypeExtensionNode interfaceTypeExtension(Source|string $source, bool[] $options = [])
 * @method static UnionTypeExtensionNode unionTypeExtension(Source|string $source, bool[] $options = [])
 * @method static EnumTypeExtensionNode enumTypeExtension(Source|string $source, bool[] $options = [])
 * @method static InputObjectTypeExtensionNode inputObjectTypeExtension(Source|string $source, bool[] $options = [])
 * @method static DirectiveDefinitionNode directiveDefinition(Source|string $source, bool[] $options = [])
 * @method static NodeList<NameNode> directiveLocations(Source|string $source, bool[] $options = [])
 * @method static NameNode directiveLocation(Source|string $source, bool[] $options = [])
 */
class Parser
{
    /**
     * Given a GraphQL source, parses it into a `GraphQL\Language\AST\DocumentNode`.
     * Throws `GraphQL\Error\SyntaxError` if a syntax error is encountered.
     *
     * Available options:
     *
     * noLocation: boolean,
     *   (By default, the parser creates AST nodes that know the location
     *   in the source that they correspond to. This configuration flag
     *   disables that behavior for performance or testing.)
     *
     * allowLegacySDLEmptyFields: boolean
     *   If enabled, the parser will parse empty fields sets in the Schema
     *   Definition Language. Otherwise, the parser will follow the current
     *   specification.
     *
     *   This option is provided to ease adoption of the final SDL specification
     *   and will be removed in a future major release.
     *
     * allowLegacySDLImplementsInterfaces: boolean
     *   If enabled, the parser will parse implemented interfaces with no `&`
     *   character between each interface. Otherwise, the parser will follow the
     *   current specification.
     *
     *   This option is provided to ease adoption of the final SDL specification
     *   and will be removed in a future major release.
     *
     * experimentalFragmentVariables: boolean,
     *   (If enabled, the parser will understand and parse variable definitions
     *   contained in a fragment definition. They'll be represented in the
     *   `variableDefinitions` field of the FragmentDefinitionNode.
     *
     *   The syntax is identical to normal, query-defined variables. For example:
     *
     *     fragment A($var: Boolean = false) on T  {
     *       ...
     *     }
     *
     *   Note: this feature is experimental and may change or be removed in the
     *   future.)
     *
     * @param Source|string $source
     * @param bool[]        $options
     *
     * @return DocumentNode
     *
     * @throws SyntaxError
     *
     * @api
     */
    public static function parse($source, array $options = [])
    {
        $parser = new self($source, $options);

        return $parser->parseDocument();
    }

    /**
     * Given a string containing a GraphQL value (ex. `[42]`), parse the AST for
     * that value.
     * Throws `GraphQL\Error\SyntaxError` if a syntax error is encountered.
     *
     * This is useful within tools that operate upon GraphQL Values directly and
     * in isolation of complete GraphQL documents.
     *
     * Consider providing the results to the utility function: `GraphQL\Utils\AST::valueFromAST()`.
     *
     * @param Source|string $source
     * @param bool[]        $options
     *
     * @return BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|ObjectValueNode|StringValueNode|VariableNode
     *
     * @api
     */
    public static function parseValue($source, array $options = [])
    {
        $parser = new Parser($source, $options);
        $parser->expect(Token::SOF);
        $value = $parser->parseValueLiteral(false);
        $parser->expect(Token::EOF);

        return $value;
    }

    /**
     * Given a string containing a GraphQL Type (ex. `[Int!]`), parse the AST for
     * that type.
     * Throws `GraphQL\Error\SyntaxError` if a syntax error is encountered.
     *
     * This is useful within tools that operate upon GraphQL Types directly and
     * in isolation of complete GraphQL documents.
     *
     * Consider providing the results to the utility function: `GraphQL\Utils\AST::typeFromAST()`.
     *
     * @param Source|string $source
     * @param bool[]        $options
     *
     * @return ListTypeNode|NamedTypeNode|NonNullTypeNode
     *
     * @api
     */
    public static function parseType($source, array $options = [])
    {
        $parser = new Parser($source, $options);
        $parser->expect(Token::SOF);
        $type = $parser->parseTypeReference();
        $parser->expect(Token::EOF);

        return $type;
    }

    /**
     * Parse partial source by delegating calls to the internal parseX methods.
     *
     * @param bool[] $arguments
     *
     * @throws SyntaxError
     */
    public static function __callStatic(string $name, array $arguments)
    {
        $parser = new Parser(...$arguments);
        $parser->expect(Token::SOF);

        switch ($name) {
            case 'arguments':
            case 'valueLiteral':
            case 'array':
            case 'object':
            case 'objectField':
            case 'directives':
            case 'directive':
                $type = $parser->{'parse' . $name}(false);
                break;
            case 'constArguments':
                $type = $parser->parseArguments(true);
                break;
            case 'constValueLiteral':
                $type = $parser->parseValueLiteral(true);
                break;
            case 'constArray':
                $type = $parser->parseArray(true);
                break;
            case 'constObject':
                $type = $parser->parseObject(true);
                break;
            case 'constObjectField':
                $type = $parser->parseObjectField(true);
                break;
            case 'constDirectives':
                $type = $parser->parseDirectives(true);
                break;
            case 'constDirective':
                $type = $parser->parseDirective(true);
                break;
            default:
                $type = $parser->{'parse' . $name}();
        }

        $parser->expect(Token::EOF);

        return $type;
    }

    /** @var Lexer */
    private $lexer;

    /**
     * @param Source|string $source
     * @param bool[]        $options
     */
    public function __construct($source, array $options = [])
    {
        $sourceObj   = $source instanceof Source ? $source : new Source($source);
        $this->lexer = new Lexer($sourceObj, $options);
    }

    /**
     * Returns a location object, used to identify the place in
     * the source that created a given parsed object.
     */
    private function loc(Token $startToken) : ?Location
    {
        if (! ($this->lexer->options['noLocation'] ?? false)) {
            return new Location($startToken, $this->lexer->lastToken, $this->lexer->source);
        }

        return null;
    }

    /**
     * Determines if the next token is of a given kind
     */
    private function peek(string $kind) : bool
    {
        return $this->lexer->token->kind === $kind;
    }

    /**
     * If the next token is of the given kind, return true after advancing
     * the parser. Otherwise, do not change the parser state and return false.
     */
    private function skip(string $kind) : bool
    {
        $match = $this->lexer->token->kind === $kind;

        if ($match) {
            $this->lexer->advance();
        }

        return $match;
    }

    /**
     * If the next token is of the given kind, return that token after advancing
     * the parser. Otherwise, do not change the parser state and return false.
     *
     * @throws SyntaxError
     */
    private function expect(string $kind) : Token
    {
        $token = $this->lexer->token;

        if ($token->kind === $kind) {
            $this->lexer->advance();

            return $token;
        }

        throw new SyntaxError(
            $this->lexer->source,
            $token->start,
            sprintf('Expected %s, found %s', $kind, $token->getDescription())
        );
    }

    /**
     * If the next token is a keyword with the given value, advance the lexer.
     * Otherwise, throw an error.
     *
     * @throws SyntaxError
     */
    private function expectKeyword(string $value) : void
    {
        $token = $this->lexer->token;
        if ($token->kind !== Token::NAME || $token->value !== $value) {
            throw new SyntaxError(
                $this->lexer->source,
                $token->start,
                'Expected "' . $value . '", found ' . $token->getDescription()
            );
        }

        $this->lexer->advance();
    }

    /**
     * If the next token is a given keyword, return "true" after advancing
     * the lexer. Otherwise, do not change the parser state and return "false".
     */
    private function expectOptionalKeyword(string $value) : bool
    {
        $token = $this->lexer->token;
        if ($token->kind === Token::NAME && $token->value === $value) {
            $this->lexer->advance();

            return true;
        }

        return false;
    }

    private function unexpected(?Token $atToken = null) : SyntaxError
    {
        $token = $atToken ?? $this->lexer->token;

        return new SyntaxError($this->lexer->source, $token->start, 'Unexpected ' . $token->getDescription());
    }

    /**
     * Returns a possibly empty list of parse nodes, determined by
     * the parseFn. This list begins with a lex token of openKind
     * and ends with a lex token of closeKind. Advances the parser
     * to the next lex token after the closing token.
     *
     * @throws SyntaxError
     */
    private function any(string $openKind, callable $parseFn, string $closeKind) : NodeList
    {
        $this->expect($openKind);

        $nodes = [];
        while (! $this->skip($closeKind)) {
            $nodes[] = $parseFn($this);
        }

        return new NodeList($nodes);
    }

    /**
     * Returns a non-empty list of parse nodes, determined by
     * the parseFn. This list begins with a lex token of openKind
     * and ends with a lex token of closeKind. Advances the parser
     * to the next lex token after the closing token.
     *
     * @throws SyntaxError
     */
    private function many(string $openKind, callable $parseFn, string $closeKind) : NodeList
    {
        $this->expect($openKind);

        $nodes = [$parseFn($this)];
        while (! $this->skip($closeKind)) {
            $nodes[] = $parseFn($this);
        }

        return new NodeList($nodes);
    }

    /**
     * Converts a name lex token into a name parse node.
     *
     * @throws SyntaxError
     */
    private function parseName() : NameNode
    {
        $token = $this->expect(Token::NAME);

        return new NameNode([
            'value' => $token->value,
            'loc'   => $this->loc($token),
        ]);
    }

    /**
     * Implements the parsing rules in the Document section.
     *
     * @throws SyntaxError
     */
    private function parseDocument() : DocumentNode
    {
        $start = $this->lexer->token;

        return new DocumentNode([
            'definitions' => $this->many(
                Token::SOF,
                function () {
                    return $this->parseDefinition();
                },
                Token::EOF
            ),
            'loc'         => $this->loc($start),
        ]);
    }

    /**
     * @return ExecutableDefinitionNode|TypeSystemDefinitionNode
     *
     * @throws SyntaxError
     */
    private function parseDefinition() : DefinitionNode
    {
        if ($this->peek(Token::NAME)) {
            switch ($this->lexer->token->value) {
                case 'query':
                case 'mutation':
                case 'subscription':
                case 'fragment':
                    return $this->parseExecutableDefinition();

                // Note: The schema definition language is an experimental addition.
                case 'schema':
                case 'scalar':
                case 'type':
                case 'interface':
                case 'union':
                case 'enum':
                case 'input':
                case 'extend':
                case 'directive':
                    // Note: The schema definition language is an experimental addition.
                    return $this->parseTypeSystemDefinition();
            }
        } elseif ($this->peek(Token::BRACE_L)) {
            return $this->parseExecutableDefinition();
        } elseif ($this->peekDescription()) {
            // Note: The schema definition language is an experimental addition.
            return $this->parseTypeSystemDefinition();
        }

        throw $this->unexpected();
    }

    /**
     * @throws SyntaxError
     */
    private function parseExecutableDefinition() : ExecutableDefinitionNode
    {
        if ($this->peek(Token::NAME)) {
            switch ($this->lexer->token->value) {
                case 'query':
                case 'mutation':
                case 'subscription':
                    return $this->parseOperationDefinition();
                case 'fragment':
                    return $this->parseFragmentDefinition();
            }
        } elseif ($this->peek(Token::BRACE_L)) {
            return $this->parseOperationDefinition();
        }

        throw $this->unexpected();
    }

    // Implements the parsing rules in the Operations section.

    /**
     * @throws SyntaxError
     */
    private function parseOperationDefinition() : OperationDefinitionNode
    {
        $start = $this->lexer->token;
        if ($this->peek(Token::BRACE_L)) {
            return new OperationDefinitionNode([
                'operation'           => 'query',
                'name'                => null,
                'variableDefinitions' => new NodeList([]),
                'directives'          => new NodeList([]),
                'selectionSet'        => $this->parseSelectionSet(),
                'loc'                 => $this->loc($start),
            ]);
        }

        $operation = $this->parseOperationType();

        $name = null;
        if ($this->peek(Token::NAME)) {
            $name = $this->parseName();
        }

        return new OperationDefinitionNode([
            'operation'           => $operation,
            'name'                => $name,
            'variableDefinitions' => $this->parseVariableDefinitions(),
            'directives'          => $this->parseDirectives(false),
            'selectionSet'        => $this->parseSelectionSet(),
            'loc'                 => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseOperationType() : string
    {
        $operationToken = $this->expect(Token::NAME);
        switch ($operationToken->value) {
            case 'query':
                return 'query';
            case 'mutation':
                return 'mutation';
            case 'subscription':
                return 'subscription';
        }

        throw $this->unexpected($operationToken);
    }

    private function parseVariableDefinitions() : NodeList
    {
        return $this->peek(Token::PAREN_L)
            ? $this->many(
                Token::PAREN_L,
                function () : VariableDefinitionNode {
                    return $this->parseVariableDefinition();
                },
                Token::PAREN_R
            )
            : new NodeList([]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseVariableDefinition() : VariableDefinitionNode
    {
        $start = $this->lexer->token;
        $var   = $this->parseVariable();

        $this->expect(Token::COLON);
        $type = $this->parseTypeReference();

        return new VariableDefinitionNode([
            'variable'     => $var,
            'type'         => $type,
            'defaultValue' => $this->skip(Token::EQUALS)
                ? $this->parseValueLiteral(true)
                : null,
            'directives'   => $this->parseDirectives(true),
            'loc'          => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseVariable() : VariableNode
    {
        $start = $this->lexer->token;
        $this->expect(Token::DOLLAR);

        return new VariableNode([
            'name' => $this->parseName(),
            'loc'  => $this->loc($start),
        ]);
    }

    private function parseSelectionSet() : SelectionSetNode
    {
        $start = $this->lexer->token;

        return new SelectionSetNode(
            [
                'selections' => $this->many(
                    Token::BRACE_L,
                    function () : SelectionNode {
                        return $this->parseSelection();
                    },
                    Token::BRACE_R
                ),
                'loc'        => $this->loc($start),
            ]
        );
    }

    /**
     *  Selection :
     *   - Field
     *   - FragmentSpread
     *   - InlineFragment
     */
    private function parseSelection() : SelectionNode
    {
        return $this->peek(Token::SPREAD)
            ? $this->parseFragment()
            : $this->parseField();
    }

    /**
     * @throws SyntaxError
     */
    private function parseField() : FieldNode
    {
        $start       = $this->lexer->token;
        $nameOrAlias = $this->parseName();

        if ($this->skip(Token::COLON)) {
            $alias = $nameOrAlias;
            $name  = $this->parseName();
        } else {
            $alias = null;
            $name  = $nameOrAlias;
        }

        return new FieldNode([
            'alias'        => $alias,
            'name'         => $name,
            'arguments'    => $this->parseArguments(false),
            'directives'   => $this->parseDirectives(false),
            'selectionSet' => $this->peek(Token::BRACE_L) ? $this->parseSelectionSet() : null,
            'loc'          => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseArguments(bool $isConst) : NodeList
    {
        $parseFn = $isConst
            ? function () : ArgumentNode {
                return $this->parseConstArgument();
            }
            : function () : ArgumentNode {
                return $this->parseArgument();
            };

        return $this->peek(Token::PAREN_L)
            ? $this->many(Token::PAREN_L, $parseFn, Token::PAREN_R)
            : new NodeList([]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseArgument() : ArgumentNode
    {
        $start = $this->lexer->token;
        $name  = $this->parseName();

        $this->expect(Token::COLON);
        $value = $this->parseValueLiteral(false);

        return new ArgumentNode([
            'name'  => $name,
            'value' => $value,
            'loc'   => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseConstArgument() : ArgumentNode
    {
        $start = $this->lexer->token;
        $name  = $this->parseName();

        $this->expect(Token::COLON);
        $value = $this->parseConstValue();

        return new ArgumentNode([
            'name'  => $name,
            'value' => $value,
            'loc'   => $this->loc($start),
        ]);
    }

    // Implements the parsing rules in the Fragments section.

    /**
     * @return FragmentSpreadNode|InlineFragmentNode
     *
     * @throws SyntaxError
     */
    private function parseFragment() : SelectionNode
    {
        $start = $this->lexer->token;
        $this->expect(Token::SPREAD);

        $hasTypeCondition = $this->expectOptionalKeyword('on');
        if (! $hasTypeCondition && $this->peek(Token::NAME)) {
            return new FragmentSpreadNode([
                'name'       => $this->parseFragmentName(),
                'directives' => $this->parseDirectives(false),
                'loc'        => $this->loc($start),
            ]);
        }

        return new InlineFragmentNode([
            'typeCondition' => $hasTypeCondition ? $this->parseNamedType() : null,
            'directives'    => $this->parseDirectives(false),
            'selectionSet'  => $this->parseSelectionSet(),
            'loc'           => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseFragmentDefinition() : FragmentDefinitionNode
    {
        $start = $this->lexer->token;
        $this->expectKeyword('fragment');

        $name = $this->parseFragmentName();

        // Experimental support for defining variables within fragments changes
        // the grammar of FragmentDefinition:
        //   - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet
        $variableDefinitions = null;
        if (isset($this->lexer->options['experimentalFragmentVariables'])) {
            $variableDefinitions = $this->parseVariableDefinitions();
        }
        $this->expectKeyword('on');
        $typeCondition = $this->parseNamedType();

        return new FragmentDefinitionNode([
            'name'                => $name,
            'variableDefinitions' => $variableDefinitions,
            'typeCondition'       => $typeCondition,
            'directives'          => $this->parseDirectives(false),
            'selectionSet'        => $this->parseSelectionSet(),
            'loc'                 => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseFragmentName() : NameNode
    {
        if ($this->lexer->token->value === 'on') {
            throw $this->unexpected();
        }

        return $this->parseName();
    }

    // Implements the parsing rules in the Values section.

    /**
     * Value[Const] :
     *   - [~Const] Variable
     *   - IntValue
     *   - FloatValue
     *   - StringValue
     *   - BooleanValue
     *   - NullValue
     *   - EnumValue
     *   - ListValue[?Const]
     *   - ObjectValue[?Const]
     *
     * BooleanValue : one of `true` `false`
     *
     * NullValue : `null`
     *
     * EnumValue : Name but not `true`, `false` or `null`
     *
     * @return BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|StringValueNode|VariableNode|ListValueNode|ObjectValueNode|NullValueNode
     *
     * @throws SyntaxError
     */
    private function parseValueLiteral(bool $isConst) : ValueNode
    {
        $token = $this->lexer->token;
        switch ($token->kind) {
            case Token::BRACKET_L:
                return $this->parseArray($isConst);
            case Token::BRACE_L:
                return $this->parseObject($isConst);
            case Token::INT:
                $this->lexer->advance();

                return new IntValueNode([
                    'value' => $token->value,
                    'loc'   => $this->loc($token),
                ]);
            case Token::FLOAT:
                $this->lexer->advance();

                return new FloatValueNode([
                    'value' => $token->value,
                    'loc'   => $this->loc($token),
                ]);
            case Token::STRING:
            case Token::BLOCK_STRING:
                return $this->parseStringLiteral();
            case Token::NAME:
                if ($token->value === 'true' || $token->value === 'false') {
                    $this->lexer->advance();

                    return new BooleanValueNode([
                        'value' => $token->value === 'true',
                        'loc'   => $this->loc($token),
                    ]);
                }

                if ($token->value === 'null') {
                    $this->lexer->advance();

                    return new NullValueNode([
                        'loc' => $this->loc($token),
                    ]);
                } else {
                    $this->lexer->advance();

                    return new EnumValueNode([
                        'value' => $token->value,
                        'loc'   => $this->loc($token),
                    ]);
                }
                break;

            case Token::DOLLAR:
                if (! $isConst) {
                    return $this->parseVariable();
                }
                break;
        }
        throw $this->unexpected();
    }

    private function parseStringLiteral() : StringValueNode
    {
        $token = $this->lexer->token;
        $this->lexer->advance();

        return new StringValueNode([
            'value' => $token->value,
            'block' => $token->kind === Token::BLOCK_STRING,
            'loc'   => $this->loc($token),
        ]);
    }

    /**
     * @return BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|StringValueNode|VariableNode
     *
     * @throws SyntaxError
     */
    private function parseConstValue() : ValueNode
    {
        return $this->parseValueLiteral(true);
    }

    /**
     * @return BooleanValueNode|EnumValueNode|FloatValueNode|IntValueNode|ListValueNode|ObjectValueNode|StringValueNode|VariableNode
     */
    private function parseVariableValue() : ValueNode
    {
        return $this->parseValueLiteral(false);
    }

    private function parseArray(bool $isConst) : ListValueNode
    {
        $start   = $this->lexer->token;
        $parseFn = $isConst
            ? function () {
                return $this->parseConstValue();
            }
            : function () {
                return $this->parseVariableValue();
            };

        return new ListValueNode(
            [
                'values' => $this->any(Token::BRACKET_L, $parseFn, Token::BRACKET_R),
                'loc'    => $this->loc($start),
            ]
        );
    }

    private function parseObject(bool $isConst) : ObjectValueNode
    {
        $start = $this->lexer->token;
        $this->expect(Token::BRACE_L);
        $fields = [];
        while (! $this->skip(Token::BRACE_R)) {
            $fields[] = $this->parseObjectField($isConst);
        }

        return new ObjectValueNode([
            'fields' => new NodeList($fields),
            'loc'    => $this->loc($start),
        ]);
    }

    private function parseObjectField(bool $isConst) : ObjectFieldNode
    {
        $start = $this->lexer->token;
        $name  = $this->parseName();

        $this->expect(Token::COLON);

        return new ObjectFieldNode([
            'name'  => $name,
            'value' => $this->parseValueLiteral($isConst),
            'loc'   => $this->loc($start),
        ]);
    }

    // Implements the parsing rules in the Directives section.

    /**
     * @throws SyntaxError
     */
    private function parseDirectives(bool $isConst) : NodeList
    {
        $directives = [];
        while ($this->peek(Token::AT)) {
            $directives[] = $this->parseDirective($isConst);
        }

        return new NodeList($directives);
    }

    /**
     * @throws SyntaxError
     */
    private function parseDirective(bool $isConst) : DirectiveNode
    {
        $start = $this->lexer->token;
        $this->expect(Token::AT);

        return new DirectiveNode([
            'name'      => $this->parseName(),
            'arguments' => $this->parseArguments($isConst),
            'loc'       => $this->loc($start),
        ]);
    }

    // Implements the parsing rules in the Types section.

    /**
     * Handles the Type: TypeName, ListType, and NonNullType parsing rules.
     *
     * @return ListTypeNode|NamedTypeNode|NonNullTypeNode
     *
     * @throws SyntaxError
     */
    private function parseTypeReference() : TypeNode
    {
        $start = $this->lexer->token;

        if ($this->skip(Token::BRACKET_L)) {
            $type = $this->parseTypeReference();
            $this->expect(Token::BRACKET_R);
            $type = new ListTypeNode([
                'type' => $type,
                'loc'  => $this->loc($start),
            ]);
        } else {
            $type = $this->parseNamedType();
        }
        if ($this->skip(Token::BANG)) {
            return new NonNullTypeNode([
                'type' => $type,
                'loc'  => $this->loc($start),
            ]);
        }

        return $type;
    }

    private function parseNamedType() : NamedTypeNode
    {
        $start = $this->lexer->token;

        return new NamedTypeNode([
            'name' => $this->parseName(),
            'loc'  => $this->loc($start),
        ]);
    }

    // Implements the parsing rules in the Type Definition section.

    /**
     * TypeSystemDefinition :
     *   - SchemaDefinition
     *   - TypeDefinition
     *   - TypeExtension
     *   - DirectiveDefinition
     *
     * TypeDefinition :
     *   - ScalarTypeDefinition
     *   - ObjectTypeDefinition
     *   - InterfaceTypeDefinition
     *   - UnionTypeDefinition
     *   - EnumTypeDefinition
     *   - InputObjectTypeDefinition
     *
     * @throws SyntaxError
     */
    private function parseTypeSystemDefinition() : TypeSystemDefinitionNode
    {
        // Many definitions begin with a description and require a lookahead.
        $keywordToken = $this->peekDescription()
            ? $this->lexer->lookahead()
            : $this->lexer->token;

        if ($keywordToken->kind === Token::NAME) {
            switch ($keywordToken->value) {
                case 'schema':
                    return $this->parseSchemaDefinition();
                case 'scalar':
                    return $this->parseScalarTypeDefinition();
                case 'type':
                    return $this->parseObjectTypeDefinition();
                case 'interface':
                    return $this->parseInterfaceTypeDefinition();
                case 'union':
                    return $this->parseUnionTypeDefinition();
                case 'enum':
                    return $this->parseEnumTypeDefinition();
                case 'input':
                    return $this->parseInputObjectTypeDefinition();
                case 'extend':
                    return $this->parseTypeExtension();
                case 'directive':
                    return $this->parseDirectiveDefinition();
            }
        }

        throw $this->unexpected($keywordToken);
    }

    private function peekDescription() : bool
    {
        return $this->peek(Token::STRING) || $this->peek(Token::BLOCK_STRING);
    }

    private function parseDescription() : ?StringValueNode
    {
        if ($this->peekDescription()) {
            return $this->parseStringLiteral();
        }

        return null;
    }

    /**
     * @throws SyntaxError
     */
    private function parseSchemaDefinition() : SchemaDefinitionNode
    {
        $start = $this->lexer->token;
        $this->expectKeyword('schema');
        $directives = $this->parseDirectives(true);

        $operationTypes = $this->many(
            Token::BRACE_L,
            function () : OperationTypeDefinitionNode {
                return $this->parseOperationTypeDefinition();
            },
            Token::BRACE_R
        );

        return new SchemaDefinitionNode([
            'directives'     => $directives,
            'operationTypes' => $operationTypes,
            'loc'            => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseOperationTypeDefinition() : OperationTypeDefinitionNode
    {
        $start     = $this->lexer->token;
        $operation = $this->parseOperationType();
        $this->expect(Token::COLON);
        $type = $this->parseNamedType();

        return new OperationTypeDefinitionNode([
            'operation' => $operation,
            'type'      => $type,
            'loc'       => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseScalarTypeDefinition() : ScalarTypeDefinitionNode
    {
        $start       = $this->lexer->token;
        $description = $this->parseDescription();
        $this->expectKeyword('scalar');
        $name       = $this->parseName();
        $directives = $this->parseDirectives(true);

        return new ScalarTypeDefinitionNode([
            'name'        => $name,
            'directives'  => $directives,
            'loc'         => $this->loc($start),
            'description' => $description,
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseObjectTypeDefinition() : ObjectTypeDefinitionNode
    {
        $start       = $this->lexer->token;
        $description = $this->parseDescription();
        $this->expectKeyword('type');
        $name       = $this->parseName();
        $interfaces = $this->parseImplementsInterfaces();
        $directives = $this->parseDirectives(true);
        $fields     = $this->parseFieldsDefinition();

        return new ObjectTypeDefinitionNode([
            'name'        => $name,
            'interfaces'  => $interfaces,
            'directives'  => $directives,
            'fields'      => $fields,
            'loc'         => $this->loc($start),
            'description' => $description,
        ]);
    }

    /**
     * ImplementsInterfaces :
     *   - implements `&`? NamedType
     *   - ImplementsInterfaces & NamedType
     */
    private function parseImplementsInterfaces() : NodeList
    {
        $types = [];
        if ($this->expectOptionalKeyword('implements')) {
            // Optional leading ampersand
            $this->skip(Token::AMP);
            do {
                $types[] = $this->parseNamedType();
            } while ($this->skip(Token::AMP) ||
                // Legacy support for the SDL?
                (($this->lexer->options['allowLegacySDLImplementsInterfaces'] ?? false) && $this->peek(Token::NAME))
            );
        }

        return new NodeList($types);
    }

    /**
     * @throws SyntaxError
     */
    private function parseFieldsDefinition() : NodeList
    {
        // Legacy support for the SDL?
        if (($this->lexer->options['allowLegacySDLEmptyFields'] ?? false)
            && $this->peek(Token::BRACE_L)
            && $this->lexer->lookahead()->kind === Token::BRACE_R
        ) {
            $this->lexer->advance();
            $this->lexer->advance();

            /** @phpstan-var NodeList<FieldDefinitionNode&Node> $nodeList */
            $nodeList = new NodeList([]);
        } else {
            /** @phpstan-var NodeList<FieldDefinitionNode&Node> $nodeList */
            $nodeList = $this->peek(Token::BRACE_L)
                ? $this->many(
                    Token::BRACE_L,
                    function () : FieldDefinitionNode {
                        return $this->parseFieldDefinition();
                    },
                    Token::BRACE_R
                )
                : new NodeList([]);
        }

        return $nodeList;
    }

    /**
     * @throws SyntaxError
     */
    private function parseFieldDefinition() : FieldDefinitionNode
    {
        $start       = $this->lexer->token;
        $description = $this->parseDescription();
        $name        = $this->parseName();
        $args        = $this->parseArgumentsDefinition();
        $this->expect(Token::COLON);
        $type       = $this->parseTypeReference();
        $directives = $this->parseDirectives(true);

        return new FieldDefinitionNode([
            'name'        => $name,
            'arguments'   => $args,
            'type'        => $type,
            'directives'  => $directives,
            'loc'         => $this->loc($start),
            'description' => $description,
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseArgumentsDefinition() : NodeList
    {
        /** @var NodeList<InputValueDefinitionNode&Node> $nodeList */
        $nodeList = $this->peek(Token::PAREN_L)
            ? $this->many(
                Token::PAREN_L,
                function () : InputValueDefinitionNode {
                    return $this->parseInputValueDefinition();
                },
                Token::PAREN_R
            )
            : new NodeList([]);

        return $nodeList;
    }

    /**
     * @throws SyntaxError
     */
    private function parseInputValueDefinition() : InputValueDefinitionNode
    {
        $start       = $this->lexer->token;
        $description = $this->parseDescription();
        $name        = $this->parseName();
        $this->expect(Token::COLON);
        $type         = $this->parseTypeReference();
        $defaultValue = null;
        if ($this->skip(Token::EQUALS)) {
            $defaultValue = $this->parseConstValue();
        }
        $directives = $this->parseDirectives(true);

        return new InputValueDefinitionNode([
            'name'         => $name,
            'type'         => $type,
            'defaultValue' => $defaultValue,
            'directives'   => $directives,
            'loc'          => $this->loc($start),
            'description'  => $description,
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseInterfaceTypeDefinition() : InterfaceTypeDefinitionNode
    {
        $start       = $this->lexer->token;
        $description = $this->parseDescription();
        $this->expectKeyword('interface');
        $name       = $this->parseName();
        $interfaces = $this->parseImplementsInterfaces();
        $directives = $this->parseDirectives(true);
        $fields     = $this->parseFieldsDefinition();

        return new InterfaceTypeDefinitionNode([
            'name'        => $name,
            'directives'  => $directives,
            'interfaces'  => $interfaces,
            'fields'      => $fields,
            'loc'         => $this->loc($start),
            'description' => $description,
        ]);
    }

    /**
     * UnionTypeDefinition :
     *   - Description? union Name Directives[Const]? UnionMemberTypes?
     *
     * @throws SyntaxError
     */
    private function parseUnionTypeDefinition() : UnionTypeDefinitionNode
    {
        $start       = $this->lexer->token;
        $description = $this->parseDescription();
        $this->expectKeyword('union');
        $name       = $this->parseName();
        $directives = $this->parseDirectives(true);
        $types      = $this->parseUnionMemberTypes();

        return new UnionTypeDefinitionNode([
            'name'        => $name,
            'directives'  => $directives,
            'types'       => $types,
            'loc'         => $this->loc($start),
            'description' => $description,
        ]);
    }

    /**
     * UnionMemberTypes :
     *   - = `|`? NamedType
     *   - UnionMemberTypes | NamedType
     */
    private function parseUnionMemberTypes() : NodeList
    {
        $types = [];
        if ($this->skip(Token::EQUALS)) {
            // Optional leading pipe
            $this->skip(Token::PIPE);
            do {
                $types[] = $this->parseNamedType();
            } while ($this->skip(Token::PIPE));
        }

        return new NodeList($types);
    }

    /**
     * @throws SyntaxError
     */
    private function parseEnumTypeDefinition() : EnumTypeDefinitionNode
    {
        $start       = $this->lexer->token;
        $description = $this->parseDescription();
        $this->expectKeyword('enum');
        $name       = $this->parseName();
        $directives = $this->parseDirectives(true);
        $values     = $this->parseEnumValuesDefinition();

        return new EnumTypeDefinitionNode([
            'name'        => $name,
            'directives'  => $directives,
            'values'      => $values,
            'loc'         => $this->loc($start),
            'description' => $description,
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseEnumValuesDefinition() : NodeList
    {
        /** @var NodeList<EnumValueDefinitionNode&Node> $nodeList */
        $nodeList = $this->peek(Token::BRACE_L)
            ? $this->many(
                Token::BRACE_L,
                function () : EnumValueDefinitionNode {
                    return $this->parseEnumValueDefinition();
                },
                Token::BRACE_R
            )
            : new NodeList([]);

        return $nodeList;
    }

    /**
     * @throws SyntaxError
     */
    private function parseEnumValueDefinition() : EnumValueDefinitionNode
    {
        $start       = $this->lexer->token;
        $description = $this->parseDescription();
        $name        = $this->parseName();
        $directives  = $this->parseDirectives(true);

        return new EnumValueDefinitionNode([
            'name'        => $name,
            'directives'  => $directives,
            'loc'         => $this->loc($start),
            'description' => $description,
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseInputObjectTypeDefinition() : InputObjectTypeDefinitionNode
    {
        $start       = $this->lexer->token;
        $description = $this->parseDescription();
        $this->expectKeyword('input');
        $name       = $this->parseName();
        $directives = $this->parseDirectives(true);
        $fields     = $this->parseInputFieldsDefinition();

        return new InputObjectTypeDefinitionNode([
            'name'        => $name,
            'directives'  => $directives,
            'fields'      => $fields,
            'loc'         => $this->loc($start),
            'description' => $description,
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseInputFieldsDefinition() : NodeList
    {
        /** @var NodeList<InputValueDefinitionNode&Node> $nodeList */
        $nodeList = $this->peek(Token::BRACE_L)
            ? $this->many(
                Token::BRACE_L,
                function () : InputValueDefinitionNode {
                    return $this->parseInputValueDefinition();
                },
                Token::BRACE_R
            )
            : new NodeList([]);

        return $nodeList;
    }

    /**
     * TypeExtension :
     *   - ScalarTypeExtension
     *   - ObjectTypeExtension
     *   - InterfaceTypeExtension
     *   - UnionTypeExtension
     *   - EnumTypeExtension
     *   - InputObjectTypeDefinition
     *
     * @throws SyntaxError
     */
    private function parseTypeExtension() : TypeExtensionNode
    {
        $keywordToken = $this->lexer->lookahead();

        if ($keywordToken->kind === Token::NAME) {
            switch ($keywordToken->value) {
                case 'schema':
                    return $this->parseSchemaTypeExtension();
                case 'scalar':
                    return $this->parseScalarTypeExtension();
                case 'type':
                    return $this->parseObjectTypeExtension();
                case 'interface':
                    return $this->parseInterfaceTypeExtension();
                case 'union':
                    return $this->parseUnionTypeExtension();
                case 'enum':
                    return $this->parseEnumTypeExtension();
                case 'input':
                    return $this->parseInputObjectTypeExtension();
            }
        }

        throw $this->unexpected($keywordToken);
    }

    /**
     * @throws SyntaxError
     */
    private function parseSchemaTypeExtension() : SchemaTypeExtensionNode
    {
        $start = $this->lexer->token;
        $this->expectKeyword('extend');
        $this->expectKeyword('schema');
        $directives     = $this->parseDirectives(true);
        $operationTypes = $this->peek(Token::BRACE_L)
            ? $this->many(
                Token::BRACE_L,
                [$this, 'parseOperationTypeDefinition'],
                Token::BRACE_R
            )
            : new NodeList([]);
        if (count($directives) === 0 && count($operationTypes) === 0) {
            $this->unexpected();
        }

        return new SchemaTypeExtensionNode([
            'directives' => $directives,
            'operationTypes' => $operationTypes,
            'loc' => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseScalarTypeExtension() : ScalarTypeExtensionNode
    {
        $start = $this->lexer->token;
        $this->expectKeyword('extend');
        $this->expectKeyword('scalar');
        $name       = $this->parseName();
        $directives = $this->parseDirectives(true);
        if (count($directives) === 0) {
            throw $this->unexpected();
        }

        return new ScalarTypeExtensionNode([
            'name'       => $name,
            'directives' => $directives,
            'loc'        => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseObjectTypeExtension() : ObjectTypeExtensionNode
    {
        $start = $this->lexer->token;
        $this->expectKeyword('extend');
        $this->expectKeyword('type');
        $name       = $this->parseName();
        $interfaces = $this->parseImplementsInterfaces();
        $directives = $this->parseDirectives(true);
        $fields     = $this->parseFieldsDefinition();

        if (count($interfaces) === 0 &&
            count($directives) === 0 &&
            count($fields) === 0
        ) {
            throw $this->unexpected();
        }

        return new ObjectTypeExtensionNode([
            'name'       => $name,
            'interfaces' => $interfaces,
            'directives' => $directives,
            'fields'     => $fields,
            'loc'        => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseInterfaceTypeExtension() : InterfaceTypeExtensionNode
    {
        $start = $this->lexer->token;
        $this->expectKeyword('extend');
        $this->expectKeyword('interface');
        $name       = $this->parseName();
        $interfaces = $this->parseImplementsInterfaces();
        $directives = $this->parseDirectives(true);
        $fields     = $this->parseFieldsDefinition();
        if (count($interfaces) === 0
            && count($directives) === 0
            && count($fields) === 0
        ) {
            throw $this->unexpected();
        }

        return new InterfaceTypeExtensionNode([
            'name'       => $name,
            'directives' => $directives,
            'interfaces' => $interfaces,
            'fields'     => $fields,
            'loc'        => $this->loc($start),
        ]);
    }

    /**
     * UnionTypeExtension :
     *   - extend union Name Directives[Const]? UnionMemberTypes
     *   - extend union Name Directives[Const]
     *
     * @throws SyntaxError
     */
    private function parseUnionTypeExtension() : UnionTypeExtensionNode
    {
        $start = $this->lexer->token;
        $this->expectKeyword('extend');
        $this->expectKeyword('union');
        $name       = $this->parseName();
        $directives = $this->parseDirectives(true);
        $types      = $this->parseUnionMemberTypes();
        if (count($directives) === 0 && count($types) === 0) {
            throw $this->unexpected();
        }

        return new UnionTypeExtensionNode([
            'name'       => $name,
            'directives' => $directives,
            'types'      => $types,
            'loc'        => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseEnumTypeExtension() : EnumTypeExtensionNode
    {
        $start = $this->lexer->token;
        $this->expectKeyword('extend');
        $this->expectKeyword('enum');
        $name       = $this->parseName();
        $directives = $this->parseDirectives(true);
        $values     = $this->parseEnumValuesDefinition();
        if (count($directives) === 0 &&
            count($values) === 0
        ) {
            throw $this->unexpected();
        }

        return new EnumTypeExtensionNode([
            'name'       => $name,
            'directives' => $directives,
            'values'     => $values,
            'loc'        => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseInputObjectTypeExtension() : InputObjectTypeExtensionNode
    {
        $start = $this->lexer->token;
        $this->expectKeyword('extend');
        $this->expectKeyword('input');
        $name       = $this->parseName();
        $directives = $this->parseDirectives(true);
        $fields     = $this->parseInputFieldsDefinition();
        if (count($directives) === 0 &&
            count($fields) === 0
        ) {
            throw $this->unexpected();
        }

        return new InputObjectTypeExtensionNode([
            'name'       => $name,
            'directives' => $directives,
            'fields'     => $fields,
            'loc'        => $this->loc($start),
        ]);
    }

    /**
     * DirectiveDefinition :
     *   - Description? directive @ Name ArgumentsDefinition? `repeatable`? on DirectiveLocations
     *
     * @throws SyntaxError
     */
    private function parseDirectiveDefinition() : DirectiveDefinitionNode
    {
        $start       = $this->lexer->token;
        $description = $this->parseDescription();
        $this->expectKeyword('directive');
        $this->expect(Token::AT);
        $name       = $this->parseName();
        $args       = $this->parseArgumentsDefinition();
        $repeatable = $this->expectOptionalKeyword('repeatable');
        $this->expectKeyword('on');
        $locations = $this->parseDirectiveLocations();

        return new DirectiveDefinitionNode([
            'name'        => $name,
            'description' => $description,
            'arguments'   => $args,
            'repeatable'  => $repeatable,
            'locations'   => $locations,
            'loc'         => $this->loc($start),
        ]);
    }

    /**
     * @throws SyntaxError
     */
    private function parseDirectiveLocations() : NodeList
    {
        // Optional leading pipe
        $this->skip(Token::PIPE);
        $locations = [];
        do {
            $locations[] = $this->parseDirectiveLocation();
        } while ($this->skip(Token::PIPE));

        return new NodeList($locations);
    }

    /**
     * @throws SyntaxError
     */
    private function parseDirectiveLocation() : NameNode
    {
        $start = $this->lexer->token;
        $name  = $this->parseName();
        if (DirectiveLocation::has($name->value)) {
            return $name;
        }

        throw $this->unexpected($start);
    }
}