From f1cff2024962c446422e99d992bc593f393b0aff Mon Sep 17 00:00:00 2001 From: phillvancejr Date: Mon, 24 Jan 2022 10:02:56 -0500 Subject: [PATCH 01/12] moved mac os glfw static lib to lib/darwin subdirectory --- vendor/glfw/bindings/bindings.odin | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/vendor/glfw/bindings/bindings.odin b/vendor/glfw/bindings/bindings.odin index 84905f603..52dc10a13 100644 --- a/vendor/glfw/bindings/bindings.odin +++ b/vendor/glfw/bindings/bindings.odin @@ -4,7 +4,14 @@ import "core:c" import vk "vendor:vulkan" when ODIN_OS == "linux" { foreign import glfw "system:glfw" } // TODO: Add the billion-or-so static libs to link to in linux -when ODIN_OS == "darwin" { foreign import glfw "system:glfw" } +when ODIN_OS == "darwin" { + foreign import glfw { + "../lib/darwin/libglfw3.a", + "system:Cocoa.framework", + "system:IOKit.framework", + "system:OpenGL.framework", + } +} when ODIN_OS == "windows" { foreign import glfw { "../lib/glfw3_mt.lib", From f28c268d97c80f840777007262a0bd43b3086652 Mon Sep 17 00:00:00 2001 From: phillvancejr Date: Mon, 24 Jan 2022 10:03:38 -0500 Subject: [PATCH 02/12] move libgflw3.a to lib/darwin --- vendor/glfw/lib/darwin/libglfw3.a | Bin 0 -> 281384 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 vendor/glfw/lib/darwin/libglfw3.a diff --git a/vendor/glfw/lib/darwin/libglfw3.a b/vendor/glfw/lib/darwin/libglfw3.a new file mode 100644 index 0000000000000000000000000000000000000000..242197e413e1cb6f476b4c8b9b03f7d5b3c1d5f3 GIT binary patch literal 281384 zcmeEv34B!5_5TZDiHc5er4?jURMZ4BSs*AVLlT&10x3bjHB6ETnVPN142x9=nWSM7 zqqys@`V(zy?Z1oGTEz{6Sb^47wEn8rRMnl7)HsbNybg%3}e}IBaHLKeZRP$ z7ThB^{!r$dBUmNaB;mTmeXF?d7WY$v4~hS7!MDZ#02k-OBY5;-oKBIrcO$#ad4}N| zjhD4a#?48Fk#ezNl!^PN;-+FPJHap-#ecnE-`5Re@7R&XJwV@{WTW9|#5ZlE@mmSE zQTSdH_x<9Ymjb@qlZ+$5PvJihdN1-d(oZYV{8|RBV1#%$w<>J0c@F79B(9aP3gZRHAIQDR^?G!=-KhFv-lKk8*<@;X=e*pBI z@-aoi6$#b~ULp8H!L5RS66}`vf)f8;@jv27&PNi`A^n*u?sEjcC3u72y@J0N>=hhy z6vuOdf;S5OUdnfaq&GpzbC1w> z2=)k$g1kxo#|oY$c#+_2DMyCr+qvSd6MRqfyh!Mir9IA+be~5)s9k?o+F?lA-)f29 zK)a>><6yzB3+|EjG9sDV=`o`B9TGl6+!KWE6ZbbI9~X%GG@(Z%zgI}QEy6cX=+hx@F~H+2<{a;@F4EjCJIg$oF{mpq*En$qvYdfff6 zm^&x0zOE{`Ts#Y#7W-?0mGBD%R)uV&g@LNZKvT7lyrEEIu)IlvmBLZCys)ldRVYx` z6s)gf4?`ss^9(mM)Hj9#mFzz+5XuWTHU{b_9ZJshE@`Z<@K#nLIV3=NE%sj$(0Sq@ z>|eUl-!La!RTXFyM)D{`WQ{BQHIg@7vbu(FsGza2zEM2Ns%rhKs$z~{o#|)_HCBcj z%S{TCRN}K6``wTZet+e zh=?*Y&tDs8@K-vR3rRqE)qX7>cB>Q%C~-;2;$2d&6H=b}jrEY3%C2(9rHuHspLbQz z7FPSu35P-?+xS>~eh4wJDLr0xUtRPie?s zSLtu8v{Lc|(17~YRxeW4nqWhDJy@+(%}R_~QNj=v5dqJKoJ}RIQxVb<3+_!|SJw~l~1ulQ)m*B-0L%$ktZYe}%KvVNuX6{QU|!O)zN z5>x$D0t@}MvCdv1mo651UK4AaLbEjn@hhob-4q0KY4FlGep4aPl!ZO6)?%b3^^KQ6 z9_ZPniAa!GV=ie7*7_S)i@(WdN-*9>!a_Avftf+OV#SA#L>i|HiSn&8xHw#MiN9`P zpa~<8?i&@21{)@d{LP_G`%)3}RL{)yR2puq@>j$pSXkGD{<$I$HzR<&w8--VD}uzK zdIrCxD=_7-5WP;YF;J;vGiM|s7tW2P1CiWgz?$2LPP|-AF}P}DB+hYO4Q5f40LBdK zh$K{++0=zZjhtcg{k8t(D6G?eZhb{qHv^^199dxnnp2f+++#wqe^sznDV{SXQ%9Tx zrISw2nDScdgvBT`3{H@yG$K10E7_k}RrakOsH`|SigGg>&nC_KV$Xz4kVz`GSZ)zN z`>DKY>7V@vT9h&O~ZJ7SmsO| zB(@}pxpmJ?vSeRdK^M$9JFhIS$Xi-kHo0tG(cC581`MVZs=v(-)C88Rd2Jk*FHqA! z(@I4sE?DFpS`Mo)()%Ia9FAllmp;(O1Wd zb{Lmuh)wblE4}p7W)h~b5rNH92B^IHih92}YQ?o4>2DVY1DL_aQFZh0hu~W44X2!jP|3v6^4h;yaPxSc$lU7f3`Ha2H_x8$&&zQa~{ zXK;H`x}7KJHmKZchK0pF##_y4Yw7VkRC!u6c&4Nmi8h5|lGDJDfJK+3Uy?a3wsAPj z6p3gFW46uDY@sX@ibak7x+YqfDXCaXj7Az~3bDmP46&84lVnMQ$4e)J&Ai|w4q?*o z3^LH{-^r`(8ob1FQdyd!o7OqKRtA)uc`Osu#F3bjl*HtOJsU@zKxo!UsVzRo?9@in zbE||ZVx6zrLOCa{He$a989Ns*mN;F@JlDZwoABCb=t~DyKw=Q9OKidtf(<+|!`zyB%&F?MLEe%*j)3RTiNl%$Nn&zh zoilTvu;831hPB|O{&Jd?CXPCRQkwA+P0w=^hn5MiooJcg+6b|Uts^rrf;r>02UO#d z>ipuw&&BlBuL!_Q;EY#=;;@T@O~IJ(RL@EiyYpyvV4VnXE+l!H*D0f^ zIfu}?fL=xsEY?(8K{Z{>sx}&rNyTC8w>XN)ltnepsgH~@iGm0zJ`Cj`E+WiyFG&;& z6eXVd+&Y?7TiIox<*lOmrSpm`a%8JzJ`Hw28Nqn3r5e#*)g6)at( zT`{~Hd!O`Bd@3x?WK@#JNTZoRKV@YV*yo|XmLF$)I-cXPcTCUII0IsRB)g^;;v-vj zMW`%Xw-TL3Sy@rONyN(wIQvD~xYoYf(jxp8Iy+x7I=qAV*5RF_qe0g{Q%{EbI= zdbUe^xi~9B@#zO!+SU4ldy|_2okz7j>uNn3 znOOI65(*QlUH9<_psVd#q}sV8rE|g9wrAH&YTL2u=*Y)O;Zc!~lGcukd_2O{_Bt5a zcDSy58R+ZEnSl-O-ua%F9y`!TGCUu6x_48ydwQ5U9x4J_GSP(cG$>{QhaiCiDS^`> zAB|db3gVm0Ax2t4+{$t7!B)S-)q8K`<7AZZ;}M**ZJrN$Q)b_v6!~c6y~b?Up8vCdr#<8B=L7d)O%!Gceol6to>SKZK@GE4E`s@lJ3dw z>+9=W;)clHB9V<~9NG44*xk8g^24MA@V-0deMIyHU*w6D3ohDf{5qIajv}nOo-sm5;6+DMn`aIp+3U<xnU>F6_7o+Fyj21uVqYOOhD~vqZ=W4|v1S4|!Q^uOnku_;X=ylX& zf#;>R9cxk|I8@F;1O7)^+y|FG7b7%0G2+Y5@Z0Cq3hh z>{T9f4~Iv0e$tlCvhfnZc#7b~&aw&Oo*-_wxZUEOEbht5?Tz%Q2-jpF!oK{Zj|$em zQIPanWNo$)8WUNQofLV-@I~J(nDHA|%k8M2@Fcj-ICrP3xVD$#`wbMd%ox=opVP<+ee}Y+tyBPo0@M?XF4fFao3}CuCOTDry5~l^hy2d zja+URq0=G{b8m1Obr45(c2X>7tf#-B6c04ghc{pJmA-4~rFX((Nvcivj)g97hW>IJ z=XUJtn)Mtys=jNhRhURFLD#I`S^Z{{Ut71Ul}^X`A`cUf;cD%}-^WHwLg1Z>dMv1g z(O(rs!`qS262#!$jFw|OM1OqIZK^QH@JvwW*{Ma*w>0$_P^T*@{umF@pBOcKaBgI~NevzW`+c_3c)D@xQvS2v7mFbw{y|t6z`0k zuCDLGBfnDxU#Emh)!^+cnhaTu`U#>zY|j(8^7o+A8e{3sc2TLia_+&2B2ZFd6kBDv zT7QW=6}Np5p5K#<#JuZfPNp;v{S63-2p%Nh=o3WZ>m=zJkr@#9B^;Bh^&$9pBb${H zxLTi}j6kkOc{{I6qzOgQ550XikScgy>d67bN{U=Ga4xPLaxVUcQusPA+U{$6DU_`> zF>ffEh~)35z*G~tc=5cXm9^(T$-gVd4N*gwGzRsw!V9gnv!J`v@Sc%Bd93S-g=l){ zPm-mbTs9G!T8E6A+M9vWsp!WeaIBcFw%;m=otf;q;!cuQSNOTdj%lHQq|2QJ&)t<2 zI&fR$a5xaqxNAgsJfe0ZYOR)sdLx%jKHMO#@xt{%!E-6(o*-^-P*DfVL?Orn>h@DWJnw<(g0qM{;4M?2k!6QpgL)Ye_p8QvbA*n`+0 zq26zS_=uetTCLJs++s;@)K=g#s2!sl&DN=-I;iy-Gua2#$RrENPJl9p~mnvWVB z$6GqKO(PInG%}fU>WyV4behV@C)SLhRENy^JLTfgO-Uul#i&gq0MW=44n7INxfUNY zX5)2d&;hCwD*=nGO@*eTJ%!HDEg+OXH0|#_O?QNo)aXx&(st=T&}qBTX+tV^ zVD@b78x>biGKkg@!BFG<;!`v4Q8y1C<#0l0 zhoyNQ;_356f3N%MKl-B2U*Ns?qP{z*o$7o}q+rK;`V?g&QIN~OqwXY^3JJ|7(Lh#}7=JA3 zkQb_9tJa>v=o7t@Hh5k47Z_vRGu@#J)F8omqFDgg4=;-Dp5K*ryl=+V&>xz=-RBPN z?0pg8k;pP665ZO1CY$qET>Fu^V{F`4%G0~sHHq~_&ksuqqwk{%e9;fJFbX^$P{QAE zV%BkrH09)woMXI7BmFv<`ubNP@~hb?h}GjPY9a9gxJtn zUlptg=t#w?;uh~FH+CP~^>sC?-Ial=U|pcn4Xc^k5pmYkOgBz&y8Y!a-WhZ28&~3l zX1ZF4hl1rnocCJo4mJ%TnhZ-cx_FXSL1{nb&?0KAFAq0`>Tq7!U5itZx z0GmbtRY=7yZ>+y0P^Y8RmBChzfjBKC9)S6T%Bkv~F~8mvP*r^xwGpgy){|IT2H=cK z(~uJ}sCY-ED{V@1FRu@o>9T^wg|I~0Zy+UCmSoVFS5)R*I0xJL<>BRJI5vSZVA{{7 zRN6CE_2dJ4RGHo99N$8ig_K>ctg0s5RIOY%my8O`gE}-CdH%XO6r>hs+}#y?y4}4p z7^-&r0d#oL9jf+++|~XjcU`@fM64m8gc84wsR@VR-F#*ZieI(w@dQF{6_+|1Od3Vm zf=m#j&Ztz(K-t03I4CFVl6*B_n2{8i;p$)rbK~4G39CGS*6I$>2`aZ}R*Z1~jTKo- zmMpDicqPMNWO?q!;Di+^4N1CNuk13>1mz8xLUmO@Lz!TI$)K z*;riA(*;VR@H@$nhleIYiRjC)bfzbpAuj%nLeJ}mPKGaf&d2{m9MsOcf8wS3TZCQol1I@8HpMB1&>;@XW{mk~f); z<$2+a53R{~iA~eZc%wKkDg+pYoB0ILCj;{8C?-x{5p`IPon4Uxx5W zy(Q$k417z4kLpi7IzExFDnIx(2p`pBBECCN&rr4W8-S>SBN~BY;jRTjMT^g4oHZ9n zeAz(a8wVu5u}m+fJ5%7F^$L*qo&^%$Z^ZpWp>8sekcsb*& z2Z{TAEGme;2T1%^iaRRqse&f}DSxzhCcYbh>9zwYA7P=_38n&xkM7bDUlNe` z-a>n#cy;UEbGW-4ZrR~AH%D<6B<_}Be>Vy-!;ZUaV7p~#+fBFabL&&X zJlQiZNMbgEf97U2=96-t5WW?3@dQ|>UurN}S1Ba8$XMFnB})}xfSa4no1RTgm_^~T zBkd}iv!>YX2ihO@$c8WWeBEyS-HrbCho?=?GWUmr*vr7UWEqJ z-?K*sFjyAV(;)hWZcFV86Vfx(ePMN{8~hX>x&UJXI(=#@a5NL>NA2U*;;6bjWo7x^ zMPBW~eyQZ6IxFTUSyUcYT_%sg_L+6QR!G3>q+RORogykLE62sy*yGIK3uiHHJQY%& zf%AvkU4G3;`L&n#4eB89NU@EvcyZ>5Ir#5j``~*ezGf-!c*IBf8-JLYhAsNR_Q6+) zy!J|bH(?O5mKWV#wKac};jr$5pDgZGNT0GsiPOG#Gu%1Ao!D1zK!ntN>V0v{2WU>9 zWP(ot7}&xe_9J%JLNOxxx>hAAJJ)WCiOhFoVEjN_FDR2}oSn%vwfoYsuBkgN9r>>7 zqAji|+g($(z`AAH#jNzQ1a#WxE%mK?;|1snHWIzjPg}b~xvrDjW`Ba+#FM*96kNYd z!L1Dne$cGoryCUfa-)Jr?*aIt*Si&TO#u`|+cFe%<&r1c!+f1cr(hQh%GeU7a(1<) zsvvDU3AzeWebKh<^ty@aC_{;&ty|F^-KC(5T$`y0!y>K3^JKv*Mr0Pvj$!Sk;Q_k| z<6#pvB?$v<8*SIalxG;5QxVi8F{dy3r1B~1nw5)y+Q5oT??TP{qRGDKM0(SN-i6+- z@AUP3iaq!PTVByT8hwQ`DT0GdRg?0gA7YO(CeSkqcDdRjD4TEH6I7GX z722vrqSQ28V&S^%2?3=y^Pppx~JaOi#$dQ!>9=$XQNU*oKP#98e(RL@7)`#`4pW!To% zhbZWTU(fFbWu+RTLwh~n8xkw*8%ED6u&O%SFakNh4gVj+|G(ltZL%MSsy-Y4SL6Tp z@&9T3k2PqvLzk8)JG)lqwv8w{lo@}G%{1Pq*gHq1z#UZu)mji$eu@l52_OvVv38$p zZ4jUrz-?~vz?~y>*tRJt6-@ovHVR_sqgJDwzUZiC9c}Lz<>h(FJZ2IrjM@L7cX2K0 zc8wdg6Ty6)v-VJHYU>M4ft{RVH2uPF^&V=C!KClzFyXf#6}%d2^08s~^{Nfn2j(cS z?c%E#r8k6G$VNXVXQO0$!>muLyV2XmkcfQPvmCu`G`~KJ*AZ9;A|i92hsHN&cjKc$x24P8f-R(Qs<|iO>%y3hjcsqrHrkk_#9s=%2F1np#+I?Z=s~>8VcFJ^ zeHgQOs3vsL)-;PH!5Vek zI-Wt>@Ewd^H%_0b>tgA22t8O8tZ)bDYbWmd3T3NR#j+$u2asV^c0@zzNqA&P%+F$Z z)*>iaxNyP3)7|IJKWF}eCG%OD%Fiq~3DaFon-Q|X!RtHfMd-hN(48f=YYGF+tMFKZ zD34|8uVM5@Zx;FmP%wWI9a6DBH2l-E1^*M-Eq4ihDMBW+yON&ck!~O^{$)Zxo7v*% z_Xz!@e(1Y|F4Nk$@Oy+_f$)j)zf2ZJVqc95FaG`7U8}{8N%f;1Z5I@K=VW;1g5&VB)FNP#MTbL`BuElQoSJ0Y2Vm_)z z_2~F4N6If1fz7b!FP}FNxdfIqwi_GAbe843Hj(S&@SPVdQQmq6Yy;p zKB{l^=yF)f`yBXa9!-y~U#ESz4tjx}ed2OKvI8R6#3O+yWBNEC)-)5xFxI{fJv#yZ zcLK>idNUAJbHoN9W{(r;Ta$#%f(w9Tzncq0Jhi6*p&RMP0gnYvWdDgr0*`}xoVX7Z zOcwu7F;%8~yd&5PoC^Ne#r?eCQ;c0t0#5;b3os41N$6y6q#;Hm=DB6Jv~d@Qv{C%X23s1+(|$W-0zL#^xhPF97ytc7 zcs$&9vb$>|@EEwi3&h$q{RSY_(^X8LNOn#){MU**B<^}~R{~+POD_}mBH)Q|FA%p6 zNcH9w_Zi}*i2&8tsp3D8vG#dXI@Y)8j{va-p7=AttAWtniL@I-SOtV>d*V{T0w4zK ziPHs77IXojViW1Y0O2!GN>u&Ct%7#~A;O6_30?t2Rwk|yYy=|c#7enUlrU0JQZ}ZpOQY@DR>+3B>3MbZrY0?`P1D6;#&hu$G-~T zWZ=c(U&2^B7nlwIw~-j-2XU!<(DH@Ck^KODYWgzp;os=lf^I?bB>!FDBkUI3DA+8x zOmMcKTM(ea{RzUE0*Ng|B!}Q#+n31BeFy}p?L#L(KIGn=M42mUN^A20EpBJLt_A1m&9agP`Gwc`FQ;y46~pzTk; zqWq!^yO3vkBKYrupSH(Q{gB-c-3~i~ASCFHYksaqIDw4u(>A zJzg)6@Opfv{XnAY`9z1fmqD57nW57c`Rvr`3;jG80c2_KOFA!9Q?Ts_hQ6k z4exf)(Kc=LKRet%a-?^VBYeO?zrzv!Oow}mBm4&r`YVq3COhc=aD-2Ve!$CoK6Hft zr6c?$j_?f*I>lv;?|ldV)sFa;slK8?e}G3{#~a{+T>LFI9jL#{aC}Ql?!A$LUShXh z4P?_jT^oyi$b($6$)M9Rggo43ryu zL&5gt!J@LMvAq%77b7f1$My}ruP0?wm-X!*WE7VwPq9B(x2PHi)+(*Ow8f%6=%{#f z22m92Hyz5#FAX%x7jjUb#riXk2&;BwR9N$7p8kMU8REu=5y~p6FDa|Ss!g(~QqOaK zKPG{1EG6FFaa3VHWc}^R6-{*wxWE=Nzg$^`FG*^LE+^FveSB{*e`OCVRQnjYd#i=v&ORj#Iu3x6D!(4-&l3p%vGX}sPv6t=dVEbEB-emUKGVb5B zhwEc|YLam}lWbWWY=5izJr1;#25>wtd}yy<%qy#z!hdNTO{G`4=ZQJ2a^G}ll0r&+ zJpSJWw;#A^e3C(DMQDCng8%eTozQ<0?HTxw^~10qvj467lvczm8>4%0Q!{i-+m7(! z=10b?r?^5jk+ls*Xu*@M%AMR>ER-biN%GRy|BY}?QRi(_keOoG{BrZ7-H}~SN7k;w zmBE7U{OAsEwEH4&-)rlyP_gu;NMOSo-4P2H-hRHd$%G=$-oqSQ>E}MNe^68kBGe}6$?U7eikz57aah>x3?>)(pwIL&% z?2Yb>?#W-@I;I(N@O8GQa8wtCM)d9C^1S0py>T}rT;-a&GkoZ!spQ_n?q|XyzCGz3 z*SL0~1Fs@~M3UNHekd!T(=?n8@W=(7i=t48GOY<}Sgj^=3Jsmk{1Teod53>3aC zZ;cH0y}f789)~!zyym6&Dt`B_Yuw27R$;j&&6aci^Tva|{_vEGwyOM8%ySo2Jb8B4 z$mZxX&95f;I#-TgKX!EVnVmh_RWv#&`b>Avh?si>xszk=WV)L?XZ_)1yms6RQ2V1L zk5MQISG@Z0ImtzM(f24k_%xJBZ^+tcpl)nn=rL5U0?B zLJ{dr6I0QoLCmFIND=8x6Wu9_=mRj3-ZU`-9SuUN{ziqQH%+8dLm*ZII3&GkVhQ>X z5LZ!GqC(P}CN6`~0mStHCeoWGHXN*ow0F-$deg*a3^@pS7mYkBT6)t&I>rIwCIA!Z zO%pewBLs0f^@l1Xy=me-;}r1)02Ap=6T5LG9wB$r&3F}(-ZXLNVT!mHz(jh}#9fC| zt>WUfT$t7OUPxcfd)3%;C%Zyn^aI)uiEZUZ3wHXVzpTb~A?`B1{wiXMwl$DzTiYrC z(!+k>s6Q})Zut7T+HWV%Xxn-Y^a#^7Fzp9SyNPLcFl{5#NUIUrumrG&9oAx z{TI`gGVRAqTgJ2(nO4oT8<^I>v_BKA>#8>uOV=vpdGK1rQrrx}+lo-U9y?tt*mr|3 zT5uEUx>h+0HbM|6IV!B+F8E%=zW2cQZ|u7XzVq3)8@@g4OIMfAg0J5A^hNf%U2W?? z^fs??r-d)|x>85u|NKRw3XSwt=xEzgaz$Up)n#rTNiXAb07*SWm5XoY#~CcG;EaNu zuC`kASJ+!#_XIT*%SBB(FH7Y?$(AE0DqidscEbxj@j`g%t7vouE$LmI@#rhsAK5KT zLY`DR^LC7G#r-`x{popW=BcjM5GrS8x~sJY=%pX>FnY()_){3!mRh*3uNsV@Yv*_V zWEM$A-{rI!C|DYo2-H>EUg~Nv~6(CB6Bb-=Echki~6(b+wW z5veir)X)*$$KZqH!|nplo9yUKF7WgfcFj-nMY||TZ^zuE!szd2`a`KRt3zvMmb==L z5ayl{y(8{P@~$5_nG=J2RQc4EbCo5C!@K^%$w}U3+}Y+&YUD?I@=yNgo)ND5yFd8I zyFP#Nh=Qbl!1IF_z3Yo6<6<(4sQJt_uC`l{_6IL8)B2jp$vvHvnI}h1PL>3VI6)j* zvZV=~U>({95m-rEUK{B`6!L2ucdnF{aD+So24tn?{awuOH*_*|uYuC(~vsC=%n_}r#^sPQUk zpzJ?XK37|O?p8k6SbQE(KG#}&9#cNoS$wuBpX)6?Pb;4rEI!XGpBpVcuPC3JEI$9O zd~UY*yrq2p!{YOy^7*dCr%(CZV)3Da2OTZ9T6}0Tp`+zLEj~vnpW95Il_^k~7ILdb zpw8FyY`|2Myh(*@YSX?0XLyLxq9_*iY2P9g^6O}^s8RcaLLtA77K=Ky|6C~KXH%>8 z9|?t2t)s=FUhTIFh5R~NENa$%vrx#dqs5|b?Nu9m4e|wTp$giWt(hS-^ z!l5kEQS!4jhxWIHLQTZhEZTd7LQSPZH3!{U{27#1b($5{F zAH_(&bdau$ksfrAnqs6!9V9w@Am!TPAkB)Ae&rw?7bER(kmy5365($hq&MIe(lZVc zxrOv!4$}QG(u)ofX`Y1ovxC$eBfV-Pg@O{|Z+7p6;@xBSK100Uw0lo3?wUMFNN-z6 zo*ix7g|339e9`T_Bjk9P<$D0;>RsClV(Tq@rwDeQqRtZ#8y0qtOjhe{Uo_n9i(bCn z)k^wF-fE@oYJE+dYHjUmr7{v#t)^YAm}4rZT0gs5|0qtiVs^EXgo#@s zuGZg(Q>}wttxt+mt$barTg0i>x~|qo#Hm)duGR;{sn)Zu)_cXNR$w*7Qbi z3cX9REw1SERlEe< zxh-~@o@j2+l#gDzkY@%w=SA;Jqq`FGqK~9a0p>+-Ny`P|qd+^)>6)2_K5QnO53NOF zK76%C^<+ZWP6Rg-x)s41I~i}(N6?z66&Wi!b@M=6IK zKMcJz zvqk6dAEPWMyCMb9-uxL&m$_PLV8D6$%Y0qG*i91bx>i{~^`bTUN`CYMI!sHwQS1XB zrVk3W;%PYq+w&3Mckx#~x`abTx+Qf%y`{s%`> zQ+(?_q7eXn0{*`1T64ug>ycoUE401JwdU6!Jf0MS!CLvjv;LVXSK~V=pgg)9sF2mZNE`|R0?Mi^H>aBddF%sg-{;Ud1DJ}#6??8`EtGT-`IV(Qu-cc zEOJ*X^8&x(Q+mPSHTlb}vG&(MUn>Ni>F2>)3y+kd-+m1`;;_ovnXJe#6f;5tj zE8eZUj^o#To3lC}Q7Br3Mi+AI24h5xF%b;*>XWNmcA%f2PkY@n0`(40`(R`7L)Rk_ zrWWtE_}j-5`+7{i8OUGIEXF|Tjeb}d{X67CH{9??Wf~^G!p=5zgC?3>7`>I>UYRlHG54getX3Vb9od;7`9u! z`?E^Yii+Y+x)TcN_-HY!u=G?i>Y>aB~k(%VM`O4Kk%r7W^ zF;x#8=!>o_ap)L3U6YzgKE!Wp|EWISSocLAn1UMWzv8LS<@DmhtG)2(0|fgNUTwKY zA0XJL@F}EP(FX|jDZJVZk3K-KPvO;WdGrB-eG0Em2Sgtr*r)JnH$M6R!BE0m^yQf7 z$C18N)%!2{mCSYI+n6*I+S?uZM4#~mcch|!i9C`*i!dYfysv8}rnuY4<@rGRVsi~1 zYd_ro%eJ}tqNFA9=btdOU6Suz8rLl5Mt2qk_fq0{kw?zt)H85l8OyLUB?Gb59wvE* z=XJK8NsGE@@;RNioJs3K*b81Vdouo%v6)pze>Px#Pm0g)H{ztN z6LHui#h^@2K~_Tt<#k?hCapocMy}@)xCbqPYTw)+519YuTR$gxTl25vO&R9g(z1FO z^7eI3qz)=_h7p>;T6qGx!Of4`wX(O!u9LkpO(D;TJaPsr#gwzVW}YD$fiF?Yz|z)z zc30~ew2Cis73?XB?ui+X_@=b}jwH)3aek(L-uc-+cz&$y)mQPfvjL{~qW7hABN|J4 z)qQ?^e}7DZ7$*2SZ&5My9v-K}mIi~9G4b{Sj2}0tV?NPM1Vib6V)nRoZ;+);%Ew;? z*lFn@T&*9VOT(8%JI2uN?4eM$qOKDj@1|Lsd}qGPx3l@90~?PW`Pj@8T0B6hW}r2x2_d~(}8*bD+}WT34fxSAOn7?v@V zFwADiU~n^}G8hbdaA}Kf`!nnW-~`8`pdn|*g9fV1*LCYoxG@{RwscYSe^gg6=gHP| z@&)x#6)5_$bP=xB-yvjC^ijGFR?xYb%<%Np`+3Ekcar73m?C*CHodCaP}udz2+ZSCZ6G0$ecH_EvX=?k}Is(<_qqj zuqrM&|70F}+Ch|}uE|MA;2^Af9#Zb;eliu>G`fba=YOFWI zQc*?Gj|!vP3X`6;`X%j!pM>}nt4EyA;a0!@)D#8{62h1M)RbB)qTcxE)CtJ4^XOE9 zZX|X*3I{{NhpR&&)O%P~Bucwt+OOxUdVP`u)x`P)-?FyMJT8KD9i5)lZdnr737%;V zGO?M*6w9;&AAQ$T`_Q4Dm(IcTgv`E%>-w!U0?G_#1JnP!ry8iphxdYDa@4C)AeDk}mrN3_NUX{}N?3y{P-QhD`Q@1x>-J7n* zqgY0E`nLSfNW@R)$z1mze&n%_fABamQw}H2%N&x_L&wKlQ@6NU7T~3~w`2XEDStR2 zo!5-tg=wX|?dFe?U2W(pQLgo5Ky=;zj5Ai)j}a>|d$-NSlE3*ZwT)BQd6g>I<_)h< zxFuA8U%T$#K(U}4W3P5?Zr-IR-JszA(k<`0QV;&=7^wqteBX3qWbdyOjX_F$2`USe*-r|@BzDsPT22vnhruP)ZR;CpQY?i6kbWl}ICiRmglg$Fr+hh6BT{Nu6EzslaUHCB#r*U5|eOi%Cws%?9Pea;I z5c{z?B3G-T`A&_{XOF6UuDb3jSNSNqabD%RaMalfr(iGdi0$vVmI6;M-lamAtVap! zq&U_o1-ssHUA%?Oy$*8=DoFfeQyRX%g84$^ja1u0iB2e0p}t_a#)$Ht$~AS=8fb*8 zeFI!%$7T~Ytikr&-3%rw$n6Sa1;KW4F)Z4tqh>SVnRi?l?tI7RN_*T#XJ6=P_Ib9D z&lV)R-RH{N;yZb3Ve~&qPRM$ynroPI0`0@bR%|!it4-rcHd3s#GCvt^{SBADW6aE? z(D<1nLMP5l4j=8he?bz|)YCmb$F#oNZv>E$!hR-Im+mA3ly@{F_w z`3BPI#r_KSf>QU=G(!ugA~&;A2?tV9j}i@plRGCE%CC3N&CAMgFl*Uf^fUbTzt zp;Ud2Cu3Tosz>SEa&13R(^wu3RtBguv3Cfp^cE!@v>;NGW{_-qL588jIf)zid~V6@^r|EtCHC290oIi5^v4xUVZxoQ@U;bMM4>A(#`H0o3g zb~2o%V*u2n>8a`+gK0YG&ZgrS934%1@RpE~sj`l2Cuj+k^={7l7d_Ttjxp3uK(`>r zScm>liRQC)=b3tp6X8%LQw^($z*k;6<}}H(Uw7gsk)90cG-J9$s$=R-shUO7a9QH7 zw9U4iQ%Yx>A41cR`R=gibeRci5)gfj<*}u3PW^1WBTB4al2-7lX$7ZzV1U)7i zXRhP=QniGkUU%>gdMK+2k4%~z!-LBD7u~~t*;W%7o@pW$OAo6x@ADDPK1{^^N#jt? zzRp)OG6z}N-VAky!Zg=O;$WZ2!tV}6iu4Pcbv|E)=W=;br=jXqk8eDDS zvaao_WF7mGXFV&Uf+nb$)9EiBs24$+7oRn(cjwWs;}!?&f+TT=L74OeD$Z667+hsB z7Bkv9S7+u3I7ExAU*}ERA-+U;gRb(jKO>v9R(Ge~(sN6#v*ypW54D-mr>o_%X%8EM zUBqrh&$U|yb^Rq?JgX|p9Mn!6sNzPfjq1=O)mf!^R|T77mafz&>vJ-m+a%(!jt+ZY zLCgnQq_CChjxCGdpyiqNel3GU;u*+(nmvP8%J`d2EepLG_uJlD|E2ih>mvM|gF6qu z35TEjtw}KMPSXpKq3*oMl$V8JZH?CP*S-ZKjo3b5)drg?JhXk{SR&cA2-7r0*FsCE zMrO;OPTLKGtI>3kkts91@EfAckf~t~dLlI0n(9YtLuheB6WXwv-n5~~tYTWG(l3u+ zPhLdN=Hz?b#S7+n-AhYy(wDkR@Eg*k91S@7s=Xe`2zA@s7RYgdNxC*k7q(PxbKwZjWLs zfM-z|vcazVt2vGJ^_RGd{f(;0DI4EcT0WjHnS8#Gvj>CgPi~g1eLOO$R1*cv!t=0s zdP%+8drn4fId$5fO@r(!yMfx(mqaeGe5qb17Y~U9e|b8Yx4Jygh-r4^@<2$3etpnwHGO}fUCXJTz3)v&6iS@7HHW#eKg7;g4@}w9JbIzXLlhYc9C~53 zZe{Z!9?_5T9GYlT?c+dFY#+8fM-DZWXWLg7eh&}lR^^CW0cScVdLB_1DsYCmrpVRm zk)`H(xoS<3InbiQUnxC>r%DgYq#qe+ROY5oeS>?>ob-(88SbT7(=rmb3m&Cs=y_i` z%A?Eg$-=9u81y)F+6(*8<(O0J5t{X3U{=}4NX?guBH#Iym zs#iMXW>c^KwW+=PDOIRZoH$f?Tq{Fu4q4 zz|?a2Tt>(Q`l6I3&9`7-VWL))4O3FqKU-V0q0Mgk9>%btBXC%}EgjG2(8OY@G`PsJ z_lpcd%9e8tN?!QGS~f3GOKW5IezhXG%!tiYEj?68{Uw(K>fDR!!;N)VH{$2G^RYjG zZQswus4&Q6&0$CRtaBLM=S+(=ASo&OoDy%o`wJSAbSLqp?q#OQi85$L0wMkL2dKnC z8pd#?BXD%ZpX=yl5fU{a?pH{Y=9ZMYONy|~|8Lv>ey(x~^w;9BrVIYtiYVczK3r3Q zURZgucqI%=i?p|J@NBHAN^#S|VIELcNxkBQxzJoRn&!EFu8BA;DOgK5x>%U<^f_o{ zH=9Ow#sTj{>V1i6F-r&MKR72`RpqatL*j8Iu$^SL6kxdbT;i;$ru3B+UF}^CXe3RL zdv3^P?&->FX1X6|Q{1`f<>62W=P@jnKu1Xu4QPX`#YEmi%<%MpoSu)6~22_s|s4W^#gYH&H=!k1AH_*b>x~`e3$J zZS2v7m@ab;r7_k*Ec_e_;na4!-7vwbSZYGq0R}gUW@cyF?Eu;$a`t(p%Btr)DvK7Y zcIUpht?w^n;i4MZfL+8ivF&-LRp>b;o5YIhm*YoiE2`c3VSkO=TM-J@*M0W&%D}2i zRK1e@LgX{J?IeMGcq?zed{k%bln*Scyfw35J_+TcJn<*Zl+Roo=+~o`drH3g0rbzo z#Psjm(sIq7ND0d#DcoV-FR6?Sv(3#AN#)4#f|yR>1m;({v##n_g&>%i3N4K_HrpKQ ztFf(Ts!^=L@#?@-BR-=-X1Gk+Jo;B_)EeiCNQ_Q;?(487BwPY!nP+)oTS^0-4K$^s z8=Lgtv2Mw{dZo7yzrvk3J};wgqM%38(@EB;Vkl9bKXfT?ACO9wEL`x#Y%^)~_)?nm zVgkz@qQJi5<~=R2+-xz*Xd9?q!%FF*qI*$+yY$rkT1H>QP|Y$o8%7m=5;aSQ(7xiD zrVl2X-%Zj+1e^z7QSY9&C>@(uG$xAiAoiIueS+RJTBymmT%FYx8gV*550k)b7W*pO zkPgFAVhQY8bY{^!vS`1pfVH-=HJWSh=>r{J)N;wrb{yC&_OGt0@i$eg36|sJre*G+ zPi(5AiIiDbUUDoa7U}e&IKq0N_JABZ;F%Rl9K(%}0ax`knaV?P*g zlgYVi$3G^KxFhnp(ivru5QcR}P>ovV%|L8sDGwgEvK_POOt#=#$DO#5Odpw-ekt48 zFX`jGyij$xraEj6czVOrY<)vLIB8e0xZ{$J9njBu$RsDg#%c3w33@-b zI9SnG-&9`}a+l)9){>w)8;YB@%iSm9!mWR`n$hTEHDW33ZE@eRdkx5ydf zR)kDM^w2WhBm0eN{G~p#ez{-u3Yl(SVLnw-V(ZptsU&H`L%33%xDF+}^MXxm!e*VT zN(V5^o79=ko7CnlYFvFyc#C>puLfs2C-JtfQg&>$0$?giT5s(stM{t>%H zJ;bZQ`oejp^A`0#O-ymeqyHVmG%X8r6Fk#%WOH9n|K}Ghm{+jCO{ZksbgU>g1e@I= zn%%x_D4Pq%A$@LPMkasDG`l>T4~AvQHA_0&k(u65-x$&ZTU^$LzeI_x69yQ9(G~HW z#S`E=TzYjdzhLR2M5j&?FA(M8XbCJ%^;>W53?j$(>>b6 z9Vc3z^Ro{WzrylGaiu^^4mV+DeArGFa>&n+t^^(#|>#|`8TH8-Khpxjqg8mz< z7zkh(G#9+XRP$-_5gJhkbY-Pkmf=n5f-?DG-OkH5(=33+gZ{<_4R)%pJ-wXto%XA; z%@1ry_Z2gpa>eV?srA3vT!-+p3Hl>_5`maT%oqmz*#z}XHSDj^cMb=%n(7YCA|Gig z`{hHrqUD2rM1SQ;%O~5ZKXjTru9N&{_9a8v9JSAjv`%R)N@!HF4<&>5C0a%m*`~2X zH8Zra{W6mNHGz!w>yhKf+)No&=%2XJldJuDq+6DjQDB;L0$Q5$ft$Q+Ezk@{_4Wf5_Nfh0l;-y18Gs zxF^@&m*iWW+@Y5S8dvyh;s!hOviC4oDg%D1o`2)iTxASl^V_jbk@K=<3YhX_c#VOENW45G^m&tcGxtZZ_+QI<+ z2t=^%l6|rJnnp6-;edfl6g_6uwy(wqH&MrBR9p698BOs02Zx={JmzHnY&d=PR~x_R zE5ge9m5%J%4cVVL`_u68S|wZ$J`Ti>43L2p^)+E-U?=;neb@7g-)8@k%qO(CV8kDy zxc*fi*ZPtOOdo)%3RF2dxq4h3%EJdD`P^RvF01jRLVb+PHWB-Z^mth^Ma?!R^V||k z{dFsG)DSyf3+mAE1^Sz>^6rm)w?~fQ40!o6*W*#A=FJ^_=?BtE132PX>JQ<|XEX|_ zK8rdib-uY=eRqd?Bgf4Wb2o}Nm18}tAKVYWbjzP4#m0c7t+<~FeUD2y6O2K>bp{cG8o9{IIw zvmVqB;^O-@%64PUxsg$Q0meQk3@2}}P(zYK_&IF;=4(ckO^z6k)!?Sax)L0LGxbDo z>zMj7{8MInifZogm;zNN2>6WpK(*hm@vgMKZngU4N@<|3NiF=ovIClyQANxMtkfgs1g>l?q?a;x;{n>91+ zc2AsosKoc(>@!PS@B7tRE9*-WxdHi#oOH8krJ;bo*1b5`7@{vk^*2lnYldVWEM!H& z7mj%3Fs%TIW)uVND#(!_GlE2CfXqRBh~G-n-vh+)FRee)?m~e;4Xg;kK$ClZu(sUx zlWlsILXwe53SA+Q(*(-2N5=^gTUB)ahjDMu3^L%^N*oRt(qZ$sGF#N)b13t`y|7u7 z1oMdgz7ArBNm%?fLo0PwE*I0R>HpiM=G~x-?7*}c_{EjX={eK0GWDScXgD=}M_u=G zqlV7L$ei4~CdG%vw7S!+M0MLcL(gaRMMHCPW$Op*Q}sm6pg%>B?M27TeUPuj;F9{V znuTZj09`?8s1Zw+Cil`J8h2$p(E|G1yMzAZFj#}o3z=bd2)g7jG^@s8Qm zr64?+nWiaFN`8LvdFdq!mJ}>>m*iPT#xH&@pQwQhZ~HxQ1JTRfA)dp`uHyYtPfoc$ zX2>JuqUy$QQ>fP8gx{=}Pfgb0!?erYSyR=dUNsUusUP+wh5_ai7-xxUHiq~$zZ^q| zSqbzHx0qk&2bAnH)GuDW zzAOtMQ7;_L8$iA0;^&<3<7$nytZ3@Gk=YSz9W&>vpXVkROd0#AY(vv7C^JT>!PTq0 zl5Yxna!mD-W>{Yv2=Ki;`lzy9t(0{Qx+QlR6E!NkX)kUfEa-po_x2-vtJFd(}-x_hwj2uP-9QD`QFAZZj_Yh=Jl`|cS zDq2=mP0yZ|lbMl;PqNXVl}4gi7^tleDYLKY7jmT2HLuoT_MnXhmLEB#r4_BAGW8P5 zX*+O^KhjTFy8@4%sVECJ+Bv2wGZLDx`!9zF-kE-Nc#!C+1p=*Ow| z3(7D*Tp{0d3&>hhhD;sy%L!7pMNqTqBbp8Zvb~b)|3s0stM=lNe>x+Z@ zxz_=Dt9>5}ha)XH0`yrHIbz7QjfLhllA$hguxC_3Qi7=TZ^g3l_WXi{3)C;^mSq2H zce%eL+mJCqdHNR=FK{f!<-T&ewuN=LeHg5C2kRQbA$M(kWx$=W>V%BkrG}go@E^tG-&p%O z)c8ZVL{e+`jdEj^vD%>BR735X8LB5WtkW37W~F8l%}B&wy^_h_kYj^DGPwXK7R4_X zY!0CvYWkzdtC_HhVYQKF=vkd%KGiw=+BS{xj_Hh#^`!9an@YM%6W`YAL9vyjHAwG>=yj?IqW{Fi18PK zzbbZxrf^kzN${NP|YZ+gxW4yVZ zaY+MX%C{JQD|k&KyB}#{{8NbW?_tIXD;ejmVr*E=7`>G7fp0VZ`7*|X)-cXo%UIjY zc*i=%=UW)xiZH(1%J_a8h~BIexGs04;a6HC*zhMGWOoZc*Or>oc3eJWj|rO=BJGJ{*3Y2yBXiQhw-ML zGcLK8@jJg@eBeIDSAWTP{{4(O4>0~kaP1~`XFbTcLaa2;<9-GJdp~@y*8= zU+ZRkaSP+ft&E=v?tOyY$3Mw<&NjxD?Tr6#2V=>v8C}0&^gP9Q{BH$+$2j$A#t#IK ze}>(2o@Kn`_l(y)$N0#9F@Ev~##3KlT=XL2<$q+n^CiaT{=_)qWyYzmFrL4QvE^09 zd;h|?>ovv${>G^PidrUVw?WA)@ipfPZNk&p!{L6$+^^=IcQRvi-CZcB?$@$xzY#7HHBjV!E_&U?4 zFvQW930;P@IC{6xFYYIO_t6|N4KIoEw?XJMOeLZl6FB@g8RF7!5IQy2MBz&&azN_# z6V=Zyp;L2CRR7(_aQIvLp)Z@n^phFl^0z_gGeJ+3znz27Q{5bKf8k38p>G(3zH<al~QKL~w;(5pC_IQbaIG2wPVBKhAVbQ*>d(Ho8*IJ|o@)2aRwg}-MI`s@=p{49pJ z{5K1IKIjSSSLprKU+Re*arwx^`q_L6)2{&}%D?+mre6+7)V?sH_Xz!ZM3boeC0QIl4Qq+=H(NUX{?cz4guYSeUQQ;ie225Yk#y-oBWYD? zQtH?-DI35=)jk3LR~^)+9vNQ^!+S{TcShtL3~{N4`10|8m+;Xzs~*iaH!|wVkyj-X zAN6rl#&UepEhmicI`FxLkNS1>%#Ba?5fE~PuRz!mUy1OE zcoUZ6Lhw}!-=PEKuN8bce=<%djPEDl^GSS*28i!D@HGn`joa#R)JGaT{|>&p4&nNs z@!Q5%5*c&F=q^=vaBN21A@X*0_+(y?knb|^y&!xvKTuDC{CyvMsmF7=)Q0-wdkTC@g^%V7{qem= z`4c{wHzds8gpr1^Tlk)J##g}gL3)u7zKxSPe>9G$N0-B)7bM3J_>B{oUzQ26^ez3x zE#TWEe7A6{3HhD?U&e_XpUg92d=ie*eFJ>C!bjtcdUQT5`gQmywC^GC%>-Zd5cn#= z*E|HiE5LWt5cuu`-#tU%`xE%K4}otaI`dsa;7bGFUg4{9%1_)R=SAR~eG=El?M^;R z{ayyX?ZQX%7xn1+u<(5!e9NW`9^X^o+bw)?dT5F7J@A#B%<&DxH(?C)PxxehlTfen z!M9WRXr5y$hefYyz_(ZU`qMvZPuGDj^%Tw@&3kO|Ir?Ri_Y>eRnacdv2)j8BTFU(f z__hll&4<*ZZAXFB^QubWYdX zPkb9NF_>)_=`BDXa3v69)vRUWzYtgm|9Rqnn)n|LM6*5OaB)9@`5}g}BYq4-w|&I- z1aAORyjKD#o;5(?yBJ96Ob1f>2LmbGD8ap$k5ah50LlN4f=>b|o=1Qbej|{=Ujxhs zwu^sA`~$#Z_%9Lvy&l6j7w&g~BN1;ea1q?Ei~C1{lYzuHN!&*PiSJNxKb67Zi-E-F z6ZdQ&@tq;=_fKQK6+q(qmbimJ;;R(*F@jG`W&T@$#DAl>uL2T(hq&KPXTB{!;(J8g z_W_CTZgFRzp_~Kv-cwmle-ZpWkm|1+NOD{UJO_N|0ZE>Pg0q0ccM6dBda;f<2lOpK z;=5b$b|CR}0g0~;h`uPj2zVx53xLF*2E=qO{XoGtPiFT{!Cwg8Ah=raLcy~Hj}sgv zxEl?I;{A=_&jha)3<(wk%fL4ScmePzAmuw1xD@U`on#p20UrfYIqnkN06ZW5J|Ld- z3?TZ(^bIF+Jf%R2CkII3P6AT7KRtoV{UVV39}#y{+^fYsPuxcVNxpBOp_9D+B)Ahe z3h_S*qUe1AKh`5pjLd_NGp5lDOyAn~05r1(BOj`{v3_&kvK zwgQPS3?#mJLeCLA2}pd00g3OOW0`L|kkY+h@c#jc??xc;tr7q8#C@i?zb5Wa-5mZ! zAn|V&_s!y7BkuFWeWtj-ChkusNqFE{6kgmni@QzSD}eKn&*kEOw)oEgQhqbUJpp(G z_)>v$fN#UDv>5nX;8}?OhrrozUjD@c%Jz1l+d@b_revqjo-qa2Es50L}(deNPvhD){l?+%LTY zycqNs1#bZ^hPz$x62Th}<@8E`NGCl<@EE}{f_ug>{l5es61+{YRWK+xUocB>yx>1l zIi9}=ZWH{8;MIaF1TPSrE$9NCk9$! z5&V0A7XohpmI2oQF95CpQaw}yiLXN3S->-qL>iFlD;2mD`05xA_aczOT`lgYxSIqo z5}YTPD|nn>-)Ij1n&39U9}8Y3*d(|_@C?C8g2{sajm%Jfej|9d;I)D)1uqbsBbW}P za=ty1_2)Uk2L%5^@N&Urf_Z``3LYT%Ml#3qwBWsh*9)!^yg=|w!O=iG>2E+8k)?@b ztDyGqUqI5|1KC~tVG{e#=KPP&70eKvBIp(zFG%|`_%}L5kR0URi+mIA5!@}fOYjB3 zor2p1y9GB1-XnO||7Y%9;G?R}weg8ujEW{!sPQOuR8&yGgqs1iW=sMT4G>8{ykHWN zfux2cCKE1RYA{5ZjuF7UYzqf%QCp?9HQ)2B z^{kmadu9UmJLmuX*DsT2zw3S1yWVx#Yp=c6P1rBoCfq9ABJ37!5Vi}OgmuCd!YW}{ zSRyPI77C{dgTew~zA#r95FSFloAe9$y@&L$aIbJkxJNiB+#wtgZWs0ow+Xijw+Oq1 z8-(q`CSjd$g|JE(7M2K$g@wYY!l1A~m@mu~284%feH(vJxKB7N+$$Us?hy_OcL)cB z+lBo?e&=I;ZWV43b_+KM+l5WSI^haol`t$U5f%#zg;Rw=VSzATm@5nj58-|?ko8GehfTev~kE^HFk30DZKgkfQcuvl0qoGJ_o z3xxT?Twy?X2#tB-|q$6z&iX2)7ISh1-N%gAVV$r<7!(GC!zdSAjvo~E3%iAN!V+On2r%JJLqBsOGNSj_Zk<|W)E+Ra0F2F4Te^B%+Am!QK(Y;@MwvTk( z_!)ho(Tz1Uz&Jh+$q!0jm%;}`=O}(R4`7C9H?IG6$T7T|2f%tv+Kqeu0kM#F^AOsR zS0=vmZT#;_-i@>VMEri`-zTNtjdPzW+Kp>}U*+M(q3>6GZe06P7vB^c-!|Fr#+}cQ zyqjk+UGi?;#z#cEc^Z7K(tbDZg7-UVH;#+XKZbVWu0K`!+&Jx8+3UuoZb@ZeB%~3m-J%fyV)~*UkI5T>P!#e_3?Z8CL!w zrO(ZS`GeDUw&mZd{B+~|Gn~HD72fHS{uf2Nc@ZZ`zZ>ViMfSUK{l7s!yb|tH$bT<= zmyiA{^m+M>2kJr%@`#1aK5BT!` zc%OZZKKf0cJoCnzKjVDzEj~I>+ZpX>ZNRkJ+O?6j25Z-}tX^Ni`SDjq;7w{_B;~pW z6U#dmbVP3IinKOB#K=@>mN{fI@~tMfU3(R_LyNRV71qS&SmAnjD^`zRr&qSrOwzAv zgC`}cBT>}6Ew$WPY%XuF!C1ZS|iCnSySK1MQ6&aPL zvZ&MgRBKhjD1kDHn4&K4=xFOm6rD@uvd&0HRa7{=cs6^}yj?QW7)WOOsx~aP!KSgeeUP}Ba!BxQUYvO{F>YAJ31q33<9;Qn_C-AX(oJ(g4)$<>o=^H86cWl9pP+(>Z=j9 zZGCm~&5=a)G%Yanb-~MTiI*qq)&AgyjYveSC{zJ9~C8SbWh%Gp%&p zv1{A#6l?34(J-UUD#QPuzVq6L z)^@dG>(0j7+R8GC8W@&7ey^abf<9hu$9q2W94D?TZaNgzzpfI%Q-J?kKUWW+7aZ`iB@ofiT zO}O`^yv@IYOl4nWsIqVE^9N2{JtOb*&Blzp(~-mB_^C$d-zYTimT=b}!m*dbeO-IP zeaoKSN4c_i`JS@)qNjI{WgY~;E_$ozxuT~G6I5mFca^dKE_$lGXwZ1YyN2S+o(IEF z2RdIRC>mVt5?wj(lB1*T z;hw?V@VqCwUO#YNIJP_7vpd(t6YiV%lS7!w6OKJ;ZHZf3Dr3cIu}5<&V9k z1YPtn!NNVe3a$%XA6gl@Aym6^XSm@hDyA(xkB`KRok;T0;hraR!#TUd)1CZsrDs>}bu0J7C$KZ`^b%uMIQHOH&@16#Xx88As!;5O z*qf-jkMXv6l;5dMWU*~qK$pb^?gsqucKkz$RL0&licRhr<*xh^$qvUald-qtx{G{s zVN5Q~&pUC9HEw4z_f~Ip_Y>`T3%M7Hz5OT~jzq!5#`x&cT)#(tS*)`lXDm?UGwt3F*@Pfp zO!_L~k0nZ=B4+f)7wrngq&4rJzeBh@zC5=)e#3P9Jc7ncbIYa;#U6kA=XsTTLOp*SiRQ&iCzMUw8+-EYU*%OE%y~W3Gh#gR%cdPl zc*e)XJI9`1z9;Y7gXecWo%ijxa-J-UcTOye9W0x+FSh&bm-8wQg?j!z5_dlQfzy-m0%pZ8?m9aXs4e=Yvm)P&>Zus7vCv`JH9 zO~ONuLf#~HGZKWxH1Jkdx7bG zBj@$_ta$a<^ShA5x6WUNBtDn(M*NnEu|rIt%^N20?HBXP-z}T>>yB90xUzX~blidK zu0C9KZ7G}gV%{BXOwi5vm#o38zww0=#w^SovoJrtc*2;)xnmaRPutUVMm(=9K0dy1 zY~HsYk1rn^%KP@vw4tu!%BDTul^b6;@%#l7SMH4MS?Q^)rhNZ5^!1Dccd)|dmG7=> z7_4YMw0zkz#B(V8_`b`+kN+{4vpY5~Hb3^kzOlE5rVW<&4t{t-&rvy{oAbW1ek2#ewLF@hxoYV|jScv86wdU-S|hmYe{dO@p>MK!r7JQ%Z_m zvzgR1sb>c@1)?7a$Ggo#BVKS|qF>vAql#W)jqqzU_Jw=jijL<52AJ6U94<{y#AEGv z+dw7^v}7XxvxM(`3aZ#x1{U=WMvo5d=A}0iGOJD2l|*}dIQvec1-bFNwm^SuJktPz z?Q!g=l{5> zVV@YU9O)CI{FPA5C=a2;RkLkprRb#te@OI~*gm9`&xzr_2fhfK!m(eO`)u?CC!YWA zc{o*_!BEd7Q_z3q0^z)?pURu@c;1~{Zk0D<+?R3p?$SI6%|G;iiz1K$ORpRs0L#FTp;xEr;zj-X;mSZFt?5jHre?a{5EaA;Z6Yxrn@*LFX zR99ts4rUH&#`N`zkGZ9EY2k;(XVu8WFUhrxt1P62?-xI-e({Tp@^t%y$5_Qvv*@o9 zpD)0f%5SUqmt^4&iT@SwGnH>QKX<@gieD!EgW|u4lxM2H{S!>^0AK7ft!ECZ;@+Hv zKhXwHTaSDJ{7=Jg_z&#AkBspCG0v@D%CMfa5(0wX`BTREAB z3Y@;)m^y-z3tMeTInO$)UEqZ!$NS7Ur_U4bt6|_36G$rd#@_03U{2iR5^H8t2mEN%t#|Ys#k2 zBR2tQ-6}cVPZ{H#4Y}=-(|wy!?s~`#OHTK1M!C(9EBLcb?`ZLU19F9u1l_ zJXjAu3%Q`=c>kx!WvGWwLw@LOtC#ITio9oCHP<6gf^F>;FsOG|F-wI^77U2y*#oR0WSc~2Et7~19%4LlYk8OUugfS=TRW(Pl;YH`Wn$? zqR$mQMl{zIF#L-^hTj96417fXeez!^dWPuJfu}=`YfDXdJZDXKAU>r}e*{SX9{FD* zdXDHk(Wj2J>C6K%okszg&I2eUrn8TA3COke#1`R&z-ge*5l$A41$*uiE7&_Mk z4?HD$SagqQ*KhoY^t*m2Klm`b>(||>_*_5q2FV9d|LIP0@}l`HV0hPW`!~ptcKy5! z;=6v^Zxr73!!D71uHVQ#Mi}1pL(hUfkO}u05MH{~N1p+GUjAQv^yhr~J`el6@^gLk z(?0nJeEQb-Xx?{TedqaTj_i8**ZRV@`{MtAk7m8|hOhC_xYtwoYkc7u)*IeTp0V@O z%%Hf2_RT=F9bDJIccAtrY}@K4lyQCwhS%5DhdMgyH@aC^W{OLSx8^@AvLoA0&dsgB zd;uFrVxk#lTS(S;rOYny@uYfrb7Q29^9}sUA@1dw;ogpj*UQcV;4pBizcg2MG_S4i z*eH8^5I>%&Ss<6?RI-g4dDO0vmGdhh0b4|G{#n*uJlz3Ru zrY2oY?TJMS?S+_I~L_#SuXA*TMJ+Plu+SJz8nXGIXrl2VA zOl~G7oXF6t+d6JUoy0_xEJpeSs@pNy;_|90+}rPU{>j|JV$4FJt2=gGSIdp{ttrz_ z(&nFVM8td}3gQ9|&;9_PiP_u$){W8?qMf31G#?-Q+3pkPV;tH1lXZFyQXj((pJ>(x zXPAF7`g{?mXP5MSS@AI4t&L#6m^-goIN6vv~iWBc&KH~*e(kmtmUNj{If%`Z`) zZw@yFawFA{-(lsWbzof&R?*1F5WENEy%C?JpD!9IdTwNhALk!~4veQB)iqgT-3Y~% zz>kf`LoytD!j0`=4gwUE7rlk)5MAf*{~%rzA-Nld!p{h&N}5>{1&}gU@8=$8kbe{M z*@XLGi4lgv{l9t_Bg{o_(XzbWXF(wF`RS4E{{dL>+n#@v+XkAz6_{LN5(wKzKEXue z!}F!dwC!^iB-7@;&*OW!&Fhi8-cHy{JC;L#W$ZRg$65RQqTbh{H&ynX{U1whc(p#uhvPexqNoN0@%?_&$jF??t`A?( zx8%5~$@IQbyYUierlV(PC-CnS1AMi!nI+0}(1v2j1nHT;R-M`v5zU9;hzbBys1%#G~I+uRSWn??@&qN<1n~^ z(bE0+7f6hAm;x>u^v&O~WAK%6^F=7sb6Frd69X)du_3boFjy14u%hQNvy>}(c4)Wx zCi{@79AiwQlW`o!jH%m^$CUX(-k2`O&?ttLl93^*64W>h6op3aH*E~&%b;1B&dD*T zjbJ48n$)F@pn691UF>CcJlnLFm~CfU%ASh@(TgxGq$2iqMeJ`WCR`HkG06)=aS#0- zla|V2Z*Y)|bH2P;dSC)>xl>S_-qwaAOjgu0L&1(`x%qxoVLSd`8OJ98wv4x-tqzA{ z|6Y;VoQj@DZ00Y(Fd*OH1JUJ(`FT`KGlHl_aR+a?11F`H_YT~)BR0>H)7JXtG%ze% zUl;Z-5IgXqO>SlEjd1Mesew$oTbfs?^HMaUaU-a}a?_WYAFOPvZ;W)9(D{UPUWb1GF|osyf;lLXC|be(}b(h&_QKvVjLs1TzO(cVl^L7gp+EQnyoBz5$bn zJDb~Dp}9TM5pBk6i8EyZmL1?Ii)dT0BZ8@{5gbwa4{N~6ppK0=WWy#Sh^f0a+F%0` z8J!R4Uh5D6^5vUU-EYwH`@I=!Bl6^xZk(fXEzXLVOY zL!`4a5DKowa-~SHts`h-YO1%HxG@riU2AcE1&;E7*!ueBXpq@cZ%*TIVhItX3#WZV zf}QP=hUV4H%rj1v548r7XRXm7&jD#((~4d^w0S(ckn_%)dEC{}8m#C0DPrtIzPANC z*Ww+;oH$SYSR#d$L@=nStrKUJ;COE2DFoUYl7?}uh%ve`vaT748&!&{keab%5Ss253?{T$jF_wGJ%4L^qAFUI83E6*iyd{cZiN$X}9af7*C4D zExeCdc*$_dO6NbWB}HsCmOgcLL|g=kGhIxwlG@fqI(XmEZhOO|1qiO%XsShH2Tu3x zpoNIH4F%b;zPS_nu?PaM^3mp1c)P1XVXv)k-KgA$+-egxy0JYHY^uktSKor{ZQN*c zt^wD!ZYB-li7xe~vSmJs#^yefsyoKoi$Z8!1MBM7q2koj6bP)ZZ*OR-N3Cvcb9|_7 zXhI`n>bbq;g6)Ym12;6LrnhXoAc(_kf>_@X#EFWixu`m3p+hoKmML^2PM)~~TN;{B z?(0$KqfNA432@OSvnaT}Iof2ScjlQAX>9InM~yJ?FvsgX<|mp(7-56rH5_J}rG`vw z5MNBzpt9C)Z0m~p3YfF(+txFeT-Gs;W`wFLkgb^T(2T`>tTeU?n+Be@T*NGYD_^>F$W@6HOvm z3o92~UAuTmEfbXL)*Y33e7hU|bQsH^V{ajrh-oaM)BLHacJcmJ}P7$e|O`p%D;^Ic0pgU#yy#j#yLAZk&2bF+ zbB|v7{}$y3_0#tN@!oRjCxPUD1W5kPzzLw211a}GAmt_jDK`#?Pd3wkhI&N)BS7*$ z10+9Y=|!u>uNHrf_~*+1WchPH4aWOj;n#&<5PnAZ31M8w-J==q7GbCGMqz{SI$^c& zN@1z+V&N>|`NFe=M+^Um$Qa*Gg%1ij&WBG$)9(T@AJzho2hH#L?*npN4@-rnp9jR> zq|-#7EczJHzeZ+~|9v3CeG|z1{Ji|{2f|Ii8Hi6y)0@PgSTCdFX24X8JekBM72V2KUzFUc>knVZM0d6>X_~m^M0@#G6%yah}7< zdIwRkjLrOV%g#f5}u!2XTMWIT{})%=dpH-I0F( z|Fae~QXd`xfx7eY9h(ZWtv@ZAOD^>L*0W;WDqW$4KoFBbsLwfles?6okG`I@Q2N+j z&~cwrZ?-7;mWlrfeZ|(xZ(p%b-wNpS>hRi14ToDFTOxgIAL;6#&+ELl(*JPd9d#T` zpi=>MpK9%^D?;DLa#B= zkV4ZKIk6Zt`(U~AQBNV7X}p8>@;3*+_v*}e$MR}3`8yoHYZ&q3yI}NaO!kib3WE$U z<@J6XS&lUh?=$oHv7jv{yy>qi;fGI_-4B0Y3u4Ua+Nb5KCK$#Y602JeJQM1Ew3{-~ z^|;!^tGvy<08n>W>XVl0vD8;Awb@cTEX4o__mrjXwA3#wb(f)f4*(GTqbyLaw@u7G zho@%+Zo-J(TkvI$0a&9F3v?4!^>W=D*(?b!+nU*Xjo7TzUbeNnH!L>mxtDDd(#zfN zseGJ|ZBy3ELn6pVr!^(CP3q=JV4FGh?)&szgDST%D@TUY)`Ov3_Z)`5?=bv>hv6SO z44?Nt>s2=U_Kp^Q_d@pOusY(dp%gK!Xt6cbi__%ZTlop>Q07F6Bd& zcJXtl=Yv>J!)_;*w1#4T!#d%6ehaFc7ypTiMX?`+V?PVW`nLRSWTYa#U`06ATY}3_ zUjBjt{98N~|CSfy-wolsX`d?uD{tC;K?8mH26_Wl{?2|_kvFZkN+cHnfUE=gW+<=i z&Oy+t^KS2Lhet*H&LP?y>+L4hw+)@Au5b#CY$Z zxhU9OdTS065$`QF^wiaP>jvMLQNj)swmCD{B z?=Pl$--~ifDhaoHuA`po4aw^*-6=t~@Hy?_Zb>Cst&-euP4arn))dwj)*x(H(KlxT zy4;xU%8@^NuWYTw^n(MaX7%{3#cv~iz4+aW-xu)vSNtBwk1{{O@8=j&T!k=e@N2>E zCj8dncME>q`1RuV5&S-i-@W+t1-u5y&E?#?D`nY&vskxaCk-R_0#nz+7CvIzNV=@NUe=;o~ z*U4vId7TVk#+yC!0gg=maO=r4Ye_sWJn;-slG0)oAtG6-v)vMxyV> zo(3b>@MGTKtwr;my>)sS!auX&oY>VXVhgX2EvYMuEo`(IfOYmgx90|;hpqGL zC&Tr~dTznG>=#q}T%*&*k0sMBkRbo6hAi>n|$V@&bCSH=DdRa|cI$J#T$(^oH2_TvKzqYBJuG zXmB~1(i8!wnp&ZG>nB8~#MV!+A^*vG#@6Q_$eXu*V)WS9`ib^#I2v8W^#v<;T7j(} zg{|Mk?-oD!JE7?E6 zRwC#AOh^mc^UkIaVa`}}O-h54pJxKV6!VlT_3I9-d z^R+1UAmJYhKWyVC{6pdQ+3~AFXaDe zH97>VvAs>l>G(g6Uxu@E32+B~6pxdX?m&9~F7xw?t)W~FG9jmqPZ8^g-XHoT9N$g+ z0^Gj@KQu*ff4tpoR-)-L+Nfj$_A2plny_zmuyDiKg~cl}vh41lStKx&*SxlCt=k7B zgL-TOd>Dy(&dZAi&7?)_r7}O=#5U|F+8o^&JbSg@!ulvCd}1nB#BX0m1Y?DrP1I^O zXmN?=gh%R0IDTc%w4cSGG9F*6=tNn9|A!C558xi4%N9O(7=FoN_;rWjcOQn|FFw|& z)1{?vP<)O8W#Y4qr27mo(|n_L^XeK1a4aO#ej`;JLV$Y?zfAl>4wt~4Z{ta`&n)N# z|Kmm~-Tr0B{X^nU$r9cSXn}Wj7JeZIyx_9&>%_m6)3d8Yz*+?-ze*mhhYmLzgZ6ZXH&(^tUVgVyh-C z{z37x<^RFM@Pn9qLpQ@hn*J*BtHIAyf82VzZ1E2tMt?rO1k+^;Un2hHuq;#jT%Skx z6$@$h4`3L9ZYF-2!XFgB!$_r4pWQsqi35nVGb%){iABI1C z7=BO)bM)-*QU>pW-&i>%Hz~(3aMag=U;Yuu-3z(gBanL-a)Bd|dj(>LetmfRxMAVJ zBaoX7xqU|MMiXA<5}El2NXOC9~h=%V_%Ugk-Itq35W)CJ400PJH(I&1K(6};9P}#SGU^lmgFx!LNc2?Tb>ROV z?=Q4(9k2oPjleQs9WVmC6u26A9$nbGsEwBejxogC~0=NWpnf%X{|B0fH0#<|n z_6atgUjrHbIUt^4(|61Nhw}d>km>pt(VqZb1GzYG4E)yvX>XJ0Iw18e1zrJMDE>wA zp9&1af3p1lfcJIEzYMGZJ`be6`{lm{SPuW&agCTF~5ok@0;+ z{`ZUC0=yFbx5@u1`Cksa8vYl_{|x!R4~RY3rXMZ;gXn-$?~6djw^Q_2Mc*TO6Oie= z8Q22(Hp^esAbN%9N+9)JD*u`CKicA@S7XSS{40Re*M+v3^hH3%{{hi2;8})l>!g1c zei~Q|xn9xN04aYNkm1h&zAchk*#Ui03LZ{2Jgq_}2kh z@0S2s?}Nbk;GYac(M&%c$awyOMiQ5k4gyL43W#Uf^e2F8fL{iNfPX~gZ31#aRtb=^ ztZoGU8Hr-NzXwwPSI{rP2khzG^N83iv3Q7BQrLm{l6Z#1<#GC!0+O|;5swfO?L&5bie4A(KjGnC;AfT zA>A$dcapcE@h*xB@&n?ZE&V~!m!mw$9~AvB$X`P<4BTas9~ONVkbL)j{O^j-eeeDv z!jSL2kF#ErcHaY^kv{kR^%ArfIe^>h4_tm3CyYI1EWv~1GH&^kw@2z|%q(1k3 zn&Gu@g zeXs4p{YSq0zV;{C9@rt;eZPN7bP)Y6x{IAY z(bu{7QUB;*nz7G)pMFs3b>H*HxcJduqMIz*eXqP)<>kg7mMQ<;_xx)mA4ETnj{8h9 zKiv2Hn_T(hxkdM&>~r61k5YcR@7I$Q-hEHxx!Kg`z8^M;cH=F7Qv7awrNq@QcSS z`JGSSwLbe#^x0S7<8%LGZ~W-9q|k36|GoVCeeu2Nv+sAl@E`K=7yI>ERwjUin_1{7t_6;PN-G{8K*r9`)r(DzBP*RIP=?0UMy4s~Ohpm!I| zbhZ}y+6Azg17R_(kBy=HY$v8$$qMWo(!AOojBXb9;^10qq7Lb{f+r1ER|BTRZJK&J}iyBfTQJW z+oKyzOuiWWOq-=QM$AU88+hJ)2h8&YTEGotS9Psk9qFj)sBi7$7QhK1k8P%~T*Wd6 z&s&>mo7So)Q|ODs%b-y_CfsI~PtePtVB$974!4!48;mORjg}_iu|2V=azbDFp312# zMxMAG(}hQ8)wDP(m)c1!Du9rg`{H;W`?T4u&E13{3E#{KPOwvKP57EXCXUoClWnP` zZHt^pQ3@weae8IB`rNqv*$QLe=W#Xlu za8C^;Gcp`8Z*5W&SDM(mIVDn4`?K&+J+g3I@n_cQ8ni5dS)tQdp1U<8-=@X1BWYp9 z^KAW`6v)>Ra9IN4`G>vwxJjE8@RW}!Sd@8#*(*FH)u}9hj-}VPbme9B z+$PQK6XMr^t_-5ZZg!2d%X3$+>8kI*A@?crHad4x`MHTEqAb$T#yN(50UO-h=UI4R zY_8ssg=bq2Y|t2}MLwC$NtjcKSZ&jvsBJbf=V_8riWV`_foC+%7N}@d&W5=4xm>pW z%;y$n-p^#_yjiosf~z~Wp{=2mj-KVu_jJCBjcSimC>iEGD&SlcD^i@G0(~4^3SVoTf z>FCxFoqB(?OJ9lMVjhI_)j^-CkT@2+<(tXA0iS(^82n&*yJa6PefF9A!|dPL+`78W zpWZ>~!)FKMN}$i@eEQ5*qYcs8uGaN_Ec43mk-l!#=dkSC3i~jALzhxz4!3_$ z81;iLU!nG3kVRF;_@g{rRW~$mZ)33b4`MJhNr8ii|Fgh>TC~Mzv)CL|;YW9!ei&Lb z%4XB^Jvz7k$}~6O_(O*#Vf?dqBzo#TD#Er7^w4GQ`|ah(C&Dwqiy0nwfjKK2|8AY} z+CB1#D$AoVdHHsU99PuO#yxr9Xk4tq#bdd6-lgn2kywB1UE}5KGmk(VHGL47h;xdg zCnxr|c-dMSKNa3x2c5>9;xjz=k4da~^5~eQtz)#)`hnxH-Xv`FL<@QzGUXlpBl4sh zg<*DDfAPSuNlkKtbF-c+xj#+Ms|9Yc%!UcrzTqxpSlI3upBk4{r$yFStT(3+f_=v? zCd77!MzCabrwhnE-d|*zuMEdo)X;d0LWXv5yr9zV_yIM4K*l0Gj4Vj*XfkrP36$ao z<#UX$iL4^FOFMjMr*$PFjO}%jmwOPw@h}j3pX1zMW7A-?j7K~ug|R=ly?91m;F${X zE*y37L}=uN&4ba?5V+6;rvIK1Q>!*(Lz_34puS1Z!*Z+GvRHNAi*6g|gmB+V`L10>Zlvsm$tr1*BL`x%tcQvitS4YAC7r5A@*{n*itO_2=q;w z%q+8p_);70n;f)Jt2j^=$g7IjUZcR?h<%ezlT7Rnj$=~wL$==LPDa|DY<@x(xp&rd z4D@Hnne4W{ju4q`g}Oh1iJ;aDr1X?I-lQ%*PjR9~ zRK|W?=|4SaWZ1i31fMC9Ue?Fd#^=tbO-H(R21$Y8!OV z{%FgqUBlb+sOg=3cxus0mXDpm4)Kv0zhtjX0rvZ=XtdUqgl_oPaFB%5lYHW+538Ae4=t*eOj=Cdc`?h=)ZYW(BnY9MX*iASXz7uwzH zN;whAh@1Ln+j~_^MTI!_K`)*od7JMs1%Gj}dL=_Szdln3f5;`QU?Ok<48++e`RH(L zMR1#7D!YTqR7U2$->u9*Z|u_FoJZ!9Mb8;OJ`=S0+M?c9qVLCjZTt^}P}c8G!94s` zsEsJ#Ar>&J4tnc(*(J;*7>|z3&l^*C@1*4z1I6#N#i;w&6i8(pn+>QWcb6AEMY+C59>vZ6 z!=*^zqx|k+Q?O%ZU{+vuU~T}peJl9k9dj^yYhK_Ydax-$p)V(3?qYaH8D>(2F+oF6xC5`>@&*8lF~<@r?#1O2OasM^XH)R~C6sq!ZwtJ_JFdlC z(^AY6MW|Ia)Ykylc1+^gZAVbG02;7n`eZ z;p#$j^&wpSv$=X0S7UM*Vh^qga>6^t<(!>!e9l=pP*#E`7+F=oTqE>(2<4b7J}m;~ z>I!hrM8I=$&dr&cGlc=)4=;rK0AO0qc}A)b*XNt7DY&}8T+PPS^c)CI#l?)ABI9`h zt}irK#keXoS1%y4nH1!6Z&uE1<9RIDbIjExxSC+D=HqItxvIp~(G2oq1X-T5%y>SD z>l$;#2h!E%Y9<7)H&?H~@1M+77=Fi@s}JJpSaY=)SL3O02x8afTw^?+!SxDr^#rc2 zGgl~%K+xpd4{&vq@mq?($C#^JTrH%U3AhO7R2a`;2rf2PdvSFI*{|Z_%A6%Rs2tpE zo|WSeyjUFn39K~IZ{TWC&Vl!%@!yQ%fWn@gM`3?gep`&r3gtbxc_r)V>fXT($E*&; zK3l#V9a!VNLf+5xT?tLGyA9{SAoj`7O@LD5(9Oie{)H{)mz2oAa0_5ML>ioF92Oe4$Ko{@ue_;UfAbj9i3`J~w|67Zve7 z^M2aeo({PVQO8B-4o$hV-Oxp(T?sadw6}x-g+pg4&Jd0pfelS{5 z5&ynkcta!i83SV`0N(}X7-h+S4>1c5%*K|xg~*I8XMi{b7pI(wi+o&64(he|N?c$I z9Q$Hy8=m+hd~E4j-2p!ShPo!2y({^+OTOvtG=j<9MzntdpXaeR6HVRAwtMkaesGAM zpLoQ(p112tuy5)bM|HAedwtynyI+%e|E;Muk+TT4AFwo6g-mZW0=f z7vqC55Jh*(*B4SJkx!2op~5^rU6<`ee6}#*5ELrd@CTwB{m=fh;fseDI+@RhFm(lw+aJ{v$sBo8*5yy3by>P5vM7`S;8J9H0NUv-l6n-=D7M?wH;1*#2UiYGkIQFAyL@!{=4cmxRC*jT4>CFC51#CS zn9_5nuN=N4|Bw0nACmtAKL2O4$nVP{e^k&~9g~j z^7qI4EBQa}lh4CrneL}P|0(kSh0p(T`TO&&JB$CLS^W3O-*4yp&^w^>$6F|Wf4o;` z@xM*}{`~y9{P+3NH7x&keEz4LW$p3Ddr21mcKQ3`-70^7dY_cPzuo^`7XNWmtUZ4D zf6C%NBa44|7XRzA_;1MKe{UB5?_}}+Nf!TCviQG~#ebsd#-u+l=V$T1A&Y-s7XRb$ zm4NPS;gwnF4*5I0+b91;AOBaP|0Zvnvmu66ux*?+|`l z=;WNfU&{agHRf2e)Be++T?k({B5*+SE7AgF@!)e{9DdoJ1KH})d%Z8S-GBd6&tWDn zS3h1m_g?eEh5P?2V&a84iV~BrR<&W8m6-}w-PYC75Gl3CYt9cewk^g{l$aSfKhV{V z=?0O&S{&+mIZhSE3^JTN+O(jp15?UOp!sd9YcMZlet-w3E^c167?VyK=eMnIjdaY9 zwzXH{B-<9-;qROu=!`@|4bkRx<~;$kftwpP!V_Zi1I?Y~H+9vw%nz8EI*@Ex9l*;4 z4=1fQCbxCWPxIi>o>uv&Jg-=DCl2A-uo3ZHiP8Ect4ld?gwZdGtX&o9NO)it1LN+j zX@h(e6RcobN2Ig8t+f$;Nt1B=F7gJf793(53DkFS61u&%tWq9S9JslyH4?xXrcD8y z3mfUMiENB?Hgq(%o2jymn5Df2^Y#-SM!4px3zm9A)SLy{8?2_}%*EAq!seM5EWMx< zOrIVclk48h?1aR`iC^;r9^QN&61Hqsz?`1E^BTKH8e!pqo{sejaN2DtDCTlg9%uK)`#=K zQo}T&0pb@JYiuW)`V_vcu_3fnIGmY>ZMSSRC+uOSHbl`3qk(Qgp`=#ZtC5cjFvqmB zsj|MSwZT3fJh=cPM`JTMm)4|Mf-q@*6kOKYltDCuFNH5h9atMN#hRu)D~|zDxp0hn z=73pw#ynGI{*>5~O!1{~=H?bDw~yQEh7KHQURBR^I=H)2^E@H7q@kgui^qni-%ETv znJ0P~>OnF;mbE5M=1wswMI8E-L+%uB$eQ`hoJ@>oNwmJ9X>nwIO%u-$PdxOZZ8h~B zYa&t8s=DWIC+lQmDEYME)82gxoF7=**2p97I~KImujvfj7{UF=W3nOC+GrLFA?8MV z(xVo+pmDIPh!*h(aWveE>N{^l+iF^l`GM9hKCgKbT;9fqb7M1l7HzHd9p=terL zz5#e!w5<7%Dw#EtUbrloIXh4?yD&+C6=+ZhDT5-Xmw}hB6Zz$s-NRxd*ryyzUI%^Sag+&c1)K#-x^1y?A4YLbjPcdzVJd6y`Um2jJIYl;U zmMXanX2&J;|OpmP@rEJAFVp*f{Bo;c7AzZREDt6J+ zl;jP1W@_f7g*15uU)DSrW%4Xd$P|?6X+d3P5yomIkO8A+6PtY|e^j9(RF!ClsL+*j zn)xMXRxGyNw!_9-ir!fVrBI`?C)C^ffpE!Xm#izfELjCU=&4j0jPaCcGS?Vnw7m62 zIht_km8QGpx>_d9$&&TPv7 zv+(P}2Zc`we=q#A@Lge^K1@v#ULd?eSTAf5-Xe?(9}s>+_^{B8pV`#?@BepC<3MZk zsz6Kgs&(c(?HNTg8aILplyd3T{2*V?B8|alTM*qr^xT8>$$nJOb?0rf!8PcpXeE~w zn%Zv2;s6`g2;Uz&L#>VBwwA{7R=#>Q24>BeHDhkjL=#+Gy|6M^j^7frPo4GYk>iU+ zM>D?I1kpnq^9XO{%3)xQYv4T2s`_R91xIs6WCtKFu6Z{#g+1p>=)^TIt#+r(1Ji^gMiEdPGp(|8ra-A)E z0Q}On_KjNI51uI(E8zKdM>#B4_4qigfyzZ-T)C=|FS}VRL*QVjsv?M!-&eI^dArB$ zIoc9#-BYfI$4kdQ5%YcM##l)Fcl=4>`#&!`ew+CGp6qpw|31v|r{fsA*E#+<;!p50 zaqalC#D8BF{sQrR>_qs5;^+GWocOzOp+Ji__mBzW;l^<446mE)mjR zIDYqG;*T9B{*N6d{!fTMK1=+cK1}>yIE?-;i9a@r{;!Fjt$ZI5KU@9#>0#o3R{Z0$ z#Q(DR$7JE}7e8D5Jy+`hgIU6d#XmO-zg>K;>-9QU{vQ$Fe}A0f&oFP`QLgJo;dxnO z{+IQ8EOPv~wYqRJd6u%t&Oy}z4tHBz`!XFg>BeB zjZI76z=@Wx`znn;(Xz9ii?D6C-S?X1V_b%PrOU|JW91Umb-DzPQ`E1 z54mE=vHVly7WRz4J8?>aJtyH+$ZUAg>P@t>POpMLi28CY_Bs6$@KczAbRY0zz^%ZK z1LMH^fwu!c!~Il&S76(}4&YqiRY1IoP3C^a6~Jl065uJoFfb3e4*Q?}74;V3FZ~lR z1pn87lzRrq@DGXqJs{?5PW~D&3VH5-+=}?u2$ut6@UH}Z8vdsP7eam#kor#mE&$Da zkQx51JZtxBK$Pv|p91efxNictfd6G6)BS1S-N3tn%n$B|OuSimldu-}QOI2j{0MLv z5ckUTTp;cI!3lQX-0uLtj41J?j&0qcM>fngxaxfoakx(HYZ zJP&vw@HAiy{0Ttx6(*0M{bfBl1jM6s^6S7E!2bfWKD`Ls0zUU(XFd6r=&t~o-d@q` zfu97u3dr<_fK9-8K&Ep#km))DxCQ!NMLVz+^pAnG=LbO8v1mIG_x9w^1DOu)pGg0Y z0?&tkulzRv??L!xAj4k_oCbc8_~!w;z~}z1jQ><%5d33+lYk>=FX?|s_&RVZ{PzM+ z0X`%D-9XAeBKilwb3uO@cq;Idz<&aM6v%MgrR={LVQ1!yor|rraLk1El^XKmzWj56xRp)_$o~)wrCvTSV9yErfYdWA z|GhvK)sX!6$iE-R^lSw(9ZkS`&?|rpUnL9+ON4xWkY6aADhvt>g!#f;VL-^|3Bw;0 z?h_6R_X>xEdxU%+V7MK^0pWIGzi^vyt8k03Tev~kE^HFk30DZKgkfQcuvl0qoGJ_o z3xxT?Twy?X2#H`i_6dhc;BQDY-JBXWt_Hu7AitH6VBzjQv4$<31_lw>tdW+}{qT5B+iC!T(EV@K=q3Efi z3qsW zt`l7)IxMFIUeQCM`+=3LCqNX*_zge=J)s@Qa7{pl zs{_KFfM>7quL9D)0LX9*e+~Fsf%gKp06zij2Hp;&-jzV=T@IvP%GCiWN4-5j>LrGO zi-09S$`u1CR|urOslXe6LEx3Z0wCq`ft1SyQV+mjzw(3mPrF_IZxP)sbmh=4n&n~A zFI*vHxzOL0ONr=Wp(~%MqJzQ$VZJa|7!V$Uz0`Y9xKB7N+$$Us?hy_OcL)cB+lBqY zZNja>Ey8Z$24TCfNmwUbA*>RHg(bpbVWDuUFeoe#<_mL$0GrRiTTyO|=N2I2=>{^M z+khp&JAjO5BarcY7|3{T1_puG0U6JFAmiBtWIWdc0f7y`Ayis+;RbqJ+HE)iWQIw(3{bU^e$v>%Lb7|8gBL=TD{5Zy0&tLSdg?V{^MSBWkWT_`#z zI$v}^^g+}w6F<Ymo0-fYm_iTMBFfqKL+afmeZMy@GC2 zpOyewj}`-SB}c3W4KUAF$~S;!{AIu(a5K=wPkGiyfT@4XKm3g^5e9|yr2jDFh=am@ zVYjeOSRxDx0Vdp!(XT%N3Y>cZ29Vw=k4EwPMc*g>u;}Ya)A(c~;&m0)^KUyXbF9-v-gI$ewP|E#hwxeW~siZq4)(#UsUM}ivEn`2PHp&@xgDq^wrT`&;z1NWnZzvKP~+wq7Nwi z4#}Ua_`>2}eS+0DD1J!!Q6>JTkGK3i;-BgAOZ+#`n4AC|Tj$#JaU6p74N3kP#kWE7 ze}*jid&R$=`cZ$n#s8bqH!S`XrEiP)ElS^3(Q&13pX6Vle(2jK{wbpSMgLOvZx_8A z`ONYehMjcZl>Qasb3B6j>O>byUz6x*s$V-qFH-#&6#by&_lW*Ho=+#>9mnmLe2(aL z=%L#rdb{Y^u#NHuMgJ#cNDodl;(FziahQ|FMX6x{s|xdZJ&J~NBX?sm-*!9`RLF3^fRnCe5a2-%SYeu(>KOv z&#;fKgL1F_?LK?UQBGd|Gd}tCK04$JKg*Y%qkQ@XeD+T9@mcM?@t^3c510Gs13r7$ zzw(C1yJHIdH?*BzeuqyV&!zM7pYzfCed*&lhFGyZ34BW3$P57a}K%eu?nkwOWW3B*(;`KdfMuEI&-)hsDpQdwKExVk2^xU{@>QF%>xNm+I6lB!YVtypc% zHC5%+?@4dc247;GX~rAsL&=!gmWxbK-3=T6dABRdb zH8yvom9KYdxt1a&aycynJ^L}G)}%CsH?dx(H`vl8sDtUuWY9DLH@g>eP3<&aF3729 zM6EH)zS0wCmTv`+o0wG_u!k0S7Ef1UCT&$47^XHtDOdMaZ^Q!FwM!%InCA?|WF`UVpdFyRD;k z*38<-%(=BWTos}T;5nBSCdp!|Ta1~)t(ZR^jVv^pE~oY^S=-gwgb-|2+}(^kUXJyL zW{n|dM6+nzia*SXUTc@lq-BWnPSI84)@G&L`f3(ftvrR~mIx+AGuG{lAyV$Lta{UK z@vM^GTfp7cY>kg7K;o9jXsT76;RYH-+BBx4Hkd~icxm|pwwgyK3=`2JCz{$(0%d3! zN0D~|E1ba0hhM2@p*)`rl!WD)?%Xj8m_pctZjPOTwsYZmxETd zVVS>KXl|Amcttq9eiSwrZfMI3Z?$0i@4RiRg$)m)8*A6u1q-;TQ|<&lBXyfMx30s| zd|2Ja18Hoi2CT1z*J`W*1_RdN4(1wFoxxSg%_(rq7!D?CZ7>pGqQz)vy0KP=BpGFE zJJ(cW9c|jpY1i$_ySluxytHP?(ppr9@}=c?`c~I^Lzri_hc~4*F#WnLHQ&2FCDDMU zEoELQ1$LjnU~*d#$H-6(yqdY1*}taz2!q0?e=wwP1Bm%Y9w8T^B*c|wyIwfb=pH+s z1sZOL@f*MTwE;xtjx|B6{n3c~P(K}2P33Y8~ZfVBC>*T^6BV$sP zFqMmsMyZK2issE5m0dhKd+zA$S);ON7CE-*1*YE#GiDYZPNHaD@u>D+G%9=M+|k){ zMr9WrhCSPb&X&BQSxzETD=}l{%u(5+GK=P2G zRQAj{j-A!IghWQ`X3lmBGP7rn&Mq94U3Af$QPVcZv9lUK+eu`!u4vY%?3qQQvWtpm zk80gTj-A!IxlSUpb+boh&zv zBR{`=uSnnD#jk-ruk&X)^o@G{;l7h>=s_u2fpTFQ2ZM?8@x-y9*Qc!K_ifksk1M|d z4&VH0{B!C+2vqgA*Z#5odVa6bQSe6U$QcJeou448|PutdB@;*Sy!H-zr>XUT2QUZw^d` zwRFiK)6}E6{zoom2qm7O-r$}?!$P5WBUPw?&)hwvY2BWsiO0!6*O+Kj;#gEP?kqAH z6KOvA*eJ!=;Z5e0b=B!DMi!zYbWBAJZjO(rqXN<^ysgynUJFeN!^@mn-QB4Zh z_I%OX%i?TrS!eyaQ+^rRytM~m&i&29kw`H9yI|g0uE_Q`XV9IuZY26LfAc3GfrD%0 z{mp;V4sIauH@~SJ+y&up?$C~TupR#9t=h2^>jcmswPWRxbo@d)*5Zr4zxht>ScgLU zn{UyM4KM|`BuYCrqR#!zQSI0Pef2kUZ8Wm82W`#Ye7Sb)gPrv^UkFETZ|=uZfx&po zT9nuA)6~ajC4338G)#fjG;|GZAhP-u-v zPUKEWC!688FN!s>nP%EtH-q@IQ_!)VymS|&pkpn%=n|^3TiS#1yXp}Zxjd01ua=#W zi;Db@wfi622H53)>?K^}m`E}@+<#E+-{XEyt|mfR*2DivbOgTKj|1^7xI*lbKw048 zz-(Q#*tYDhpChoyzqy5_1}1j_;oJ>?uor39%i6UR0GdZ%(YWbVjrYH%@mH?{{hROF zqn&sBLE~M21O{XKGm)+D_Q@9VR2G6^UHLLFF&;JjoE zM*rsNsL|rwce;;zSP7K>-YuYG1b^&Ic!JvBd0}000_CO*f@%0B4Y|w3d9hX(3o^gR(hB5I@>l-~fxMmwot;1xi zBwO>qbQ%8I|CRdh`;IXFF8>mL8$r_we&%l+-25}{|BG%}d==RkSsLW{)Y~UM2i34T z#NFDVJH)?ehwcy$Xov0)2U3CZ*6I%NXzkD);)&X!JH)fJLwAT%wL^D^v$aEah>NvD zcZdzzp*zIyX@~9*Z`BUnA^uD|bcgsb9P_v4?hID%+#$d`k(fvP)87ZVAjkI5F5mMvd2(gyO)I&ENPe}NdSq>rh_{ppIrH=(p?Vsw)} z5WQFWK#U&J2cpYQ8;CBe2zitxlmbEU74J9@+(DYqa z-_2`gb&!six99p-oF4eiuE1h?cJ5x4zWAr%XGal!mIvaoPf5j3_cgk2_2kcvBK#~5 z(>DV*4@4)$i|}^nw>m<+4&HfN z{mB+PK_k9U{5g=Z2M)I2e!Wm43MTp`8Uydb-1bnSG%J|5V>RwXEY1qXw}cXNM+Wmc z{5Mx(L<2ue2t3<055LWQlE`CM#NiGmR(B|&3#V%z>wXI5ALwsBp4x*qJ7=w|VA-P` z-RD^2K@2ocFB+4TySusFe-G|b_LR_$ows#$p0Kk^x6M(yM?;ATV@l!&RBl?@qxV}` z>%J3YR?YHe%A3EZ>LIWx6DUKd?sNUe7x*9&<|i0Cm>KdHJ{RzB9)3fgT-m&kt9)Ue zI#<7Dt}wx&g@kO~ZD;F_Kw^k$dx0wg^8*V43+Hdu@0+-T4klOO?Pua~-hgoscKegrEF<5Wr3oZUJ3#yed@=Haz+}AWNDJ31 zM-NBN#w%r4$(Z`5z9%ls3~q0S67=TH%xoC75y|>ij7VHK)bbsA?O^{scpGY0cNF@3 zP0xTtJ?yafuy5kS3UEH;#U77cBD*Nkv#g|J-@+&DVVpdP*r%^onA=#sBs>d$PPcEo zqtE5D4u7fQb1sliu7*D8ThrFp%=Qgf znKdA5=#b1-P>lmU*Jh9Z8{1iLFXf{ibIz9MTr$#^;BVI7H9h5}9z&nve6VXlQ2(K( z7y02(r=zlN7+A<{7wQ>`1$9S|y)b0u&n@G{`p zz;l48lF`SBe-@DO4g)gYK8)iS?-d~9Z3CVK{DHWmK+>NKB>mTbq(1=tB>i82xxhU@ zh;+mc#D6|;Jp4<5X8?U2yqV< z_XjZOFdd_LP8jLl1hO8U0U`@IJTC=XqjK&8!rtWk2uM2a>zM%j7=|DlxF2{t@C_ig zI_3NxcpQ-DdO?MAo(4jMIU9f~zTh>$k?_9~cq(ur5b_$a8I6JNyMc6H4n!A`GZlzW zZ8`Zs{6FGs@&BgyA1(ezi2ngZCOv1%;WgrIAlvV5ApRe*6aU~-We(57BHg_}(%k_> zRpi73zbkkfgf$K5Q9HvDrE+`jZQ>HHPQbUMZT zs<^iRna-~STY*gH1|ZY99>{bmfvo4tfRo@Z0aBjDz!TsPKb{9T1#OMb=o1BzH&y>_ zK-BZ#^}w-cVoZnbCUMt`dy%+jiMv4D?3}5d_?+g$9k39KhTxa}74?T3$6+Q^^bke`;p+C`6 zo-$8+{eqMKG7o*+6aF_(`0qUK0iN`ydgRyP2|v~o z-?uO{GZ-q;FSmL(g}RKuw-C#KYAfom=2~xc7Y;WFmDfbW0qm!$46l@k#Wmq&*o%e0 zlDZ|lOs=#XL2Bu@Gp4=;bKzCs#rws0W0YLCWi52a4HITLd$2s>$`==+n_Ivn!A9(w z)B8Yd4{+xt2lmcMthz5O#|?4VGXZ)DR)y*7ZO2ljbP%Y*sD=9`e}R?N;3sk^t837L zX~YURS&nsvUePZbC2`Yfjfr{&c5p<(eMmj){LhGTngMCpY0FkYRa>RywFqt~!{(97 zsJ<0*cC@ynjvMAwwJpOBJE}>0YBUn5!=5(msnOJ^HEwfBTa{LjB*;rT!5C?nT_}3z z8+_DVn^oMIk}75KGkeXP0zp02)HQ}p9))rmcEotRl;YTaPC=M^EwvW&no4Z2V+}4w zKCwlW7s%Pp^f8T&*|hp*H`{IYAVG44dN&dzuFFoj+De4|e@c(&t7mac`*CTu$5Tat zA2$i2f-Me8XR%ub6Gh_ZEQGyRN~dJIt-Vu<+-|GtE)d=FjV|%BK3>>nWKbHdiB#iS z6L+yR>r%O%#WVqruO~4zW!6W>?1?nl{)FZ!YAKe1+ff|Kioj5<_WQSXN>xA~>MAVl z{PO#1JjJ2xFhhs3qoRz%)!y|@kkhdzt-ic+`dkoWES_#1>{)5n!A>~)tO?nC%sSX* zb=cZd*Wg|T+h-jtx({Cy|H;N59@=|;TT1sV#mg2SjzjnuH(t}!I@qDg*9Wn2_pCSo z-gTiSq*1>8kRMrQo`g9W2BupF%YiZ;FTa(-2k&_58HtxkbZyq-Ez&}>jdp@2@$zdG zei&=3XFT|!YquUJZ+cq%aHp4cyVupW3qOv-`78oIXP)rR?0RI~Y6W-nHH51g7cMNq z5*VALNBCjbt)5mXZxAoD=)(DMh;Kc@50{26)tetZ>xd5uzugjJ9r!sPXW8j5{IFk* zxue^izYbY1y-1kUdc>OmurKyhsOj39rZ+ZJ&N4!D!HA()0;K%$GoTlZ6pROVw zY8~-dkynAl+L7&UA9y!i>dfyLI8=sZ9WkS^3f3cij*)vX`LGX-9`USo!5GT zigz;pzUt%pju~9De<{lE_#%X*!zIZ_CvajF%l^z)w!~fZ+06E277@k&tQH7ixsd)4 zSCW{>tOOEwv)Q%m_20~qawu{0K}a>2EbEQ$iFM*D*guCf4$0l!^-~1K{}g-oVD4`F z8#BMl1rw8Z1-IWg(o{?0+Fkev<#K0v+#@~iY>#`a$35QTp5%1Lp3MyAz2w@)rw z{B|%obQdydzKaDD6Og|fto+5E9f0qs(S0p@u~cv$786Cj6Rg;~nQCf86-qp&gs|o9 z!?32$N5)tmEo*K|<6p!!QM$h9aa}i|!gJe&$PV#0HzL5)QoN-N$=I$=my2th?67v1 zYlq9lC5MKR-yK_e;8C3W_mcRdW09ZkrHM_GfcsDqUrAyU{jiWM3vj@n)lxtV_=nxV z2970Y*$j^B^s~{Jo1rwMEq*FmfzU?{)MzRhXE3MV{ z(Gq`ACxqzG7vYsujPf!gLxYJ;%#tcHl3I!17bHgQgY=L^CScl9?`t~SOeUBdv1J6> zm-53Jc>Iv%ljkWxMW#`*rFr|KNh!EaC3UI6NtYDg$Xkv>l3bP1M9-3hs(n+Vi94v% zExW;niW~h#_k!HLL62|bg536Qzg|ZZjJ2adJR3PTm>Bwe5bI^%OKhU9p#KV}{(?M*<=W0=QSoCnK8h{xqZRz#5>_e`)7PxYw>{0wKRdLBFHJG+mF>;|z z&tnmC36d7Q^|!WVf*u*+yrV26=(@#SB(6P3PW*`x6LYv!m*?m3bLJ6s zbs<`Tm1j_Km5^Qxjxq<6D+j=WSYkojRm6HrfW2kK9oS6Ff_pvbsHk~A$lA=o4( z7dbJ`NcKQM#sTO0y7*Rf$yusPMq#iE%(8K)Yb`0P6f-}S1knWA(eEAwAr$MiOY|v< zhu0Owm)#kBA`>;mzhJy{J+^=GXT10!!@@4zC&5H?hvKL%U~L|B`q7m;Bu}jYWxQo8gXiU!rwT*2o!T z@Jr3$$a@rn-4=tQ_$N{a_@#1XDiVvqf(~7*^6ZO>?H?J6WADIcDiW$=2UeQeCEE_6 z#Ed|l+Gp59`^-;=K)u>m96zNvUYlVnUnqWIW?=g?brxnIK5e++fhBf3kmHedj=LyR z+d4}A)jB(@^;3%jvEcPV5TOv;i}#b9l4?OuiYnNza)tJQD%ekdUK_?LSO!d61^cxS zf{EX2yM&f~n8US% z*`fIBq44m*eXU*vNoACcs&wMOKku{IM6d1ApzoWbt}B zhZP_ZA7m5#Y4>k!*&2CsXB2lxQNoi-?7@I)6n3+vik!Tegv<|uV zLh6FE@NGyjdUN+}{^ONCIm;Jxx9<%BF8n~f#pkcrS}umeVN+qpH*@O3*jPxTqa?n% zmBX!@Plomt=k}H)m%*ye-_bqVDc&O5?Ck?e&RgB;Z~jcNz(Un8ROZ=@c#Sq*LyFf! zIF`{~XS!7wZTsgV=BHvr^K5#!b(T-MekG6?jHU0V1rzf-u%WH0752X*{+^14A=8Q6 z))uN5jCI)~v8(Ocx4OR$s^B{AaJwlNT|oEIV1URw#BVhGlEDFi_))=RMnN!9+8#{I zg=H*xHmiz?Tm|)gw(Ap&Fib~d*B&}3k2y|JU$DoYgyNs84ALH(-avV9>{t9sVe;B# zJ~erxJYf_JQBizfC^__o!emj7uQZu)LrJpqm7@52W=FySI42pQk6sjhd01eD{px@Od+mLoc0?%($%d z)HibX(x8sR?2xSjRju$Fdd~;`OLsuA->-^KxNK^2!s%$$SwkOl_+A=FPB^}g?>x0U zKJSg_la}p5hoB0%5o$-n>F64^hLS~NuzT&(0bBgHZb4Ry;(rgFWaw% z*v&ir>LrOIk{0g!LkSkmNKPn6i57?Qdi=L;4dw0h-}-DQ@1XzI_E6pn0spPfh4MZH z&rsfh0{^YggLU)E{+0u%)S~Qt{+2HMD9C=_-@@sK;`rNv*xR2MVI0N*mL!lBHc7O+?u^{s zx$WALQT3tqKPZa7Yc$^I5!BwTJY6V+PZX%jqd+HG3N#YGs6c9#hzc}BDNx1(Gn1p< zMdR-XC5PV}OioCW+Xcx950nh}IF$D>bP7`@P@o|7tGy)ekI*k_(eL~%&!NJyJNzx1 zY~A5(Fd%AA;B~||N4(N}6!7fN?_zxhG>xN(C@Jwb5XTS|AV748Q z%owKpAw4M0%h89G;!LIDJc55rf5?4O_`ZOF^dw&-GjR_`7~NU9d+~la09&19dLX_B zBFwy8b_S3fH+U2%u>R?J1~c9TKmO5=zT@*XoeyvPdEC9%PxtZ9JjQKsK*pfV!-pQp zzoAFsM0ewFP~tUK;0tzbqpzYaS{s>qb#ZMJXY%31+S(;N7`Kdz0P=BmBaYN-%vUt| zzDo7|wJ40J)%85Q(x(Zg)iq!tL%6amx>yJ0`=Ygt)k|u_mH1AqzXw+}@C3|?y4vO8 zhRAGiom0nE4@fTGx4gQsdT~v7nj+!ii<;_d!jqY zu3b2{%om!e>S%Ga3MYQLU0iuFy+kj(0ol~TXamj!jm)ZE8Lq*J-&p0)$Crwy>xloF z{SxN8oA>*q3BOam%kjI9^D*IDB^)zOuE&JGEa9$?W1uwQUzhK6n51z%Cj1l$SMA+F zVZ!qzT#~UUOn8}ut2tUL)K0%TMRwB*`R^h?vsCs?i-M1zskn9(Y4`W{iw(66Y=an8Hd0U{7sVm zDy%Q{82S|ZTdoDc+J9*NtUvXnp&x_5hwwMDN7J)D)nn)#`$k?tK)cXMz4qBR!u)=Y zzYRji`c;qNHzPLqra?Dn@csclq#p7dg@Dm^py7~&>WQgKhz)fnDWX#p(^MXI_;bga4Oba zzX$_>q5p_Uz?twr5m*BJCh%O~olrQ4bi@?k47kSvX90hRPX~;5i{RTZRE+;eAmcXy z8SiXhA>#p&r<^Y^*G@iNz;6S&XPW#T05bkvKrBhfITtt$$juFNfbXH*5xan=1OEg( z75Fj`b)C}&JPmk15K9Ph76O^hOdw2IP7x3?${7os1>A>r&h(xJhJe2XLVP1yfn{*t z2po#_=)7-<@>&YaMEFHO#(y7nWF~MUkn}$T9s&AogOu=m6i3mSN+?^;4 z)7uHm!T)yxDbN29YyncP*8nNkI^a0qCBTz`K_K(@Egp5y2~gLAZCL(kZVOfTMxG5%({|eKqhr_%{F%WyE4}&l7hMa3aEUf#h>4 zkbDjUGTx^{biDrs%!B(kKc;K&rGk|vkPXK-&mN56wgBi(C26m|6dK=N4!91Cm!27!x!)LX9Sr`(1C zOA(LvAR()xKSG~@tc?CE5J`;Yx_bnT{v8nikKTlT@Qh~Pbs^%{0a3=p%ogOhl<~3zGX(+K zANhjcU}hRGpGWn;pYBc|-5ug?7k8Vu*NeMV+)d)H7xyA@&k}cmxW|h-TijXV2B`EF zAs_hv|FwH5|BlK#3T@7;AB^KS-FxJH?@kDd?kpM5Z3t;hnUdZY5SAZ*X1x9?lu)^c zsqFdQfRfT}#^c+Pzlj&3ODt;cv#*ryxJ1HxKV?r`x<= zoF{JczH++6H{-9r<^LZ}x--JmEj|xL-u$bjJ7Y=lQ@BKE~tzBO*KF zU+Hmk|CclTDv$d_Pkwt)ekc6_PxwV1{@(phKk?*mji$I(@C;%t8lu^P+UE5%+HFCGrg&px~nv7)W$Q(FIMF{uk7L`DR+n}x#dM#q@<=NEl+z} znCvLC%0CzTOJv&B7K)zr?Bh*Y)fivJk~L5n#lACDEvc?@bLAcTqf`yeF1PloIqJs| zWMwbZ7-*~shmk_zblKM=H8Kr1+TflVUaO|2%|Q^T_-+&z)}$j)dBjz2dQQHsmK&OM zRM(YaW-=Ob@3ZI4u@kH$T)QNK`_!zgiY8rBT}khh0#O&RIe1vR$t)c)3c^;Tr{I;= ztgK!ZT~>w}d`!_7*H+Y3s=L~(r1k7M!x_(R+K4MV$NKI^vm#5rapM#k)qNT57z_EbnVQ}wIEn5w zPv@;Vpi^j!8?Q@hpCG17)$?Wd2|kCHdp_icA*iqYN0_6=*a(g${A)edKDm&HI&-bqKasT6xUj!RgDAPio~10MRhu6 zwlFAI?$$r?wm|Zy9bzRUfJN)eAqF{_)Kce)Zr-J;dXw55+I- z{WNPm`CAHpp2x$l|NCiLjr_zO)Jy)>B7awD#;z(p)V|W5CLO6q=Ce9(w92UhAU{`& z9s^&MVcA!Ds<=D1YkwwA+4aD^8Wqs~XLLC^?tP~3Ae?_w@yGN1E&+bkhwGBLUKR(- zz8s9N-X|-Dusp0MsJDt@x$nMxWXp|08Nv9xOts`{zI{G%FmZjRqR7ShqV^&u>-=K! z#||T$o_c-enrAXlv;m6_^F>GEAmb)DBpulUmROZ(@{qR?m#+DuhwIc;z%xN8k1K+I zGcvj=Gw<=G2#NUP3LP;XpT`(jXp(#TqZQPXOz}{wFFU8STMAx*uA3O+RrZ?wt&nYs zrRhIWwaQ%?k5bR9wdYvXD`(_d)zS)qthcH~RhiIOE3MnilKChU>UcEFH&E!H=dzVU(oGSuA_sIz&4?zLiZLASkBea$u&#apEaQ+eOmujRR^A zvSk8-rsAdeMk=vw^(oH#7JbH$i^|hPqd`*>%sUtzYSyN++1mSY=+dhmyBNtUPwXS0 z&6*dA_pZ7A+QSN9tR@5#qe)s4e~SEd-Nc^{B`o_g-PT*Z1)#e?3|Wa7vPf{d${hJX zLQP}A2)K4U&@ABYo~f7rt9=hR$=V?^Egc9nCK0)Vf=W6pra8E`2<7&Q0`=t)O5CHZ z;@-%qj$A|NtS;2vu+oi2MM|L)SODLB1oy<8Iv4qx+b#y!KXxvbx6a4zk1M$QWB3>- zs?H=14{yE`r3^rP10X&TULy7(%xm;J=tz#Uc7so(ICQk_DxB_6V)`uhH`t!M60O8R z(>)!V1fa|6%cCnq$L3(A`y_X`B#%wx(xt_><9BS^JSY=S`A${;vsC+xtqS@gXUA4$ z`64-H7YX;nxs*9~x6Zz)LrZCuim4=&nwfRE)GpbYg+>19czu$hx$AvWqVLlon2mk( zBbbzK}#=l$}>tL%^H_gI)>)7SE3f#Ob#0CpH3?hdg=0J!f* zuA##oy%yAN0=$fR$0r|VB5lt=iVSg9k8HgU^uCVP2uA#2?xf2&!m5)$D}QOl%+4M+ z8+0~%)V&>wPN@VG4hQ19yZqWk0_Qd-Ha$*1IJ z@?qi;pO0*5PYTSE&>octgpI8XTB)q@kD*Uw3~|@k{N9r(EnEF`{i;n7j`7u@+*+R` z`xhi8pL?i57HG~+plp-cZ4Oz2FHDPnuLE;A0h68Av$GhuCH-f-+MWD5yP&Hz- zb*m9GSzT%{IFqieZZ0X>Dk@V&gS4G$Rw^eGo42mL6btDTWcc^Y}P&wB32=9dg}AnSn9J!rZ@R@z=uD?6c-#zuXnOklqWb z(J)&wd{$XQ*>Tf>>}*U?pI=p;s^9lkp~D{jPuHJHFHIF1`@>y|c;nK|tRykP=%LhC zF#bB~OZ7Cy(82B1P1IXa{0mhwBQKA7s`25W%BS`f{?FwncXyi1nD)T*R3GLvS=V1X zP>r)pXAygbcE%U+bmgYe->5=CKwE}u()hw*_q4Xs7xz#u)pC7bGix+vD&Wyz5?>NFIW6a$uG#r0nRE_TxaIH<&51Y~6{bzJUhh5{R zbFFgj!W-pPv1fQSvo8_~Chl2;U)%&c1#j+-frscUwtyAm)nHiZ8&FlzIap6PTV3pZ^k@&U{rW{&_4Xyn$!Md)hXh-8M@}$yO?rP8^0iO`^{W~y5C3r~ zkqAh&XpHx$*L)3YGjBN(uTMQcc``l|ppggTf50t*D!Q=~X5{lqFg_peR%SqAEIPQ$ zBu8x9EK3y5{zLXjbKVE9IfjE827;d$9qAMOkGBV&=DPDKy~z2BEE&BO`JcHja4{yO zQi%gw_1xyFC^-%TR+Uj4GgX>^IC+wiN~w3Z|8eX^#;(tI)d;URE>lj}ndlZJ27x;i zi8cf#WyVx616%$vED-yvKM?;@V9R?$1F?6riVy!Hu!)kxu^SyYGE5J9 zTlV0@okYe_p^8^?_clG9wH8tx;J;&Q_Z(ZY4=Kr}VVO+wu#?!Qg)4rJ?+pW?Od~k| z9jqAoAHe~;m{#vw2*~htCvn45V)9YQsu8u)J|ph;pjq)AZOY80N2&`!>`Y%5Pm{r~ zEMM1D{cC_PuxTU;+jSP)+)6S2|DZpX`ELBZfxmb0=XE>#H=|D+o{8k4M_NO_baPUp zT{Sp+J#AaU7#6Q4pGX>b6++dbVWB1E?nE<6QMI=6JM00iowfy-n9|xUCX6GI*Eug> z*%q2RcHFg785Jv*I zLulxN$EywgJ?4^ z{E5k#7_yp6?{fE|T4gR42Q-!Beb#_e4gRW9cT^cqU>dIE01d#Tjj>evKH@(5Y{g`c ztqQB#9j*gKaqrj?-$7+*W{K?i5O(#Bp_-@&Uizh1!RzWszP{K#@n^CBx)(?44GnID zHQ;l-jQaHqfUyV8VCQ6$3AgkW{SOodBgT`NHKg`!BWf-GK<}&W+EyOAwW1H_OvtO4 zpK)mzyqfn$4i`q|8@-UoGsiL zK_;tUD8Ftokr`8-(_;7Rr z_?4oyJvc=~nyTnf31;qb>0n3qQ6lePZ1oObB)q-2gREhH@m5hZ1~+0~I|k>oW{jcl z6SSuR9^ZEpdJ?p2jO8%?)XIX)KZWr{yys+|vNpx{3EmAJWDAG7X5;9Fjfv7%wDr6l zI^{g1%9@XHwgKvwcYem}MzxIR%Y~etl3cyV@iC%@FBhLXKSmsmJu3XpgXG6Mld{sc z>wC@q4Jc|bj?WYExf??9k1!P0#wT99UQ9qBwz+2nsG`U8(FrjPy1qr?(lAQm?>eWO zanN2b#@RRW`WG0X$a$f}_3Fz{AefZX(Uec}(Up)YZ2z4q8~EbaLJwf+ose;&^pS3! zTJ!)`>)=3}uD4iGiDLYfW}pkjq>K9KxC`{?SBJICQ*nnS7iqj}sm43_)l1Zr8D;T& zPqNlSvFn4r=&^y=^+SD;qjPuSN2U)ZGngFy65d5%{*|@C!LnqHL;J|ldM`zm)Omxj z0+zm@(CSr@(|wA2q)3=Nc`O-XDqbaTMBH z%M!`g5_PoRNPb$uaVs}x1pzN`+ym7rlNC=gqXsr_T!A6lMd*PwHS@G&?hPTAu= zWT;DlN$1H1Tu&_UyD1Xk$hYO2p^}-5SLg984IgI9j{- z9YScec5z+~sxT=KzlAUDJaZ&=^<-buAU9Z(hNR9#7bb z|A|NPsF3ccli;^021+9t0j|U5^4o0jM$ECb zyR+MO>WD>CrMf<7j%w>3tV?W^0%HjrYq|l1jv=*kG+iX)es{J|&aNy6A(y&mI8S&!jw zz7%o{CSvtmrv%$Q2ujt9`61_82GQ=^-B4$Q(0JKz5A@p}qo1V8wxK>H(w`3T*S1%g za~+2se6+@>}ie2yiEnXI77BYeF%C31h^#X5}b(qC2 zwxInjxA8P9{^L|gJ%4MD-%#I{c%+oEjbBYj*;bFC2mE*uH)@kO-jb2L{uZ!FDfxWt zluxIT&#RPAwwei!?$GaJA&ZL<%hMD~3TgD!XS@&M{(3#^Ro{YK0|xz0h;f6;6AapW4@JruUXtZ>U@ zzrV-1EX{c+EQbet{ncyHqR(}|=shQR&3?@8V)XJ#fD`E2a4P$(%%H#dErc3V*zy;e z!c^#c`h zattLkWg|5Ovw3P&WA|85e9`$+6$|xood58Rtvz=y{#E+_3MLX?U>l(BqdnlDW-P^` zG@;MKFd>MKZ#eT*l>?KhTy;9O6g>E05dUV3L0+27S0(#nd@GY`5=}+4Cd6vB^B@1S zLp%-@rfxV!1}W@QG3G$xFUDUE&`$kJ*C%?aP+}7$8i;+-8#yiZS#RWI7Up@jc2{$; z=sxd_j_D3am=@S2J9U@rpottGh~Z%OV?{XIjM0#I-DXk+@*gzj1b!zRyTkPB%HrY^ zvCHH+=D+bh9)>O8I+Iy+3ddw3QI+)9S&nVWQl(1t@oZaqSsq$iAkJPhcHL8yT9$Q0ewzU)&~&MJF%PZ5Iv%D4uDN8~8%{ypDtitG*Duk=vs^w#U^YeDN(rPNTOQ*FtruJvCg~6m> zL4!Q5T_40eik_H+pI5wrA!smH@JMF+1ZG8m2=i!_uF&z(qDY`mr5;8ODjwLVg}&&d z*J`Q9wym`F1~a4TrW>|gEO2p@q9tECH3qEbKds2HzhwrzyDp}|IR5|p`ai4R`v31A z(rv9jc6J`l!Lez7$`5bihX<2toP}vDdBImYj*w%G3D*R{#CS7-q8icy-H25G+0Xlk zPOrH=ifLhIK2q_r=HL6_mnxp!o168RV)-zIs|S$@^J4Sl*Pz-W1AOh~sLL1wPBPiw zA9Q=t^O42z7mIoQLup5R?tSX`a#_mDsR~RR=Ka~Gvo67$jU^^}#K zMB}?npZAL%Gk$)BA_B{D2qkZX4R z&F{enmx8d+U254ow-@{`yvCK}Jw<`}TZW>p_*RenC@#(y`_)a#i&mkD-7l;OMzJ&? zG&6%<`=&jNkvE*=>J5%p`gXq3<8WwuiTu4*6!l6EW2BbB4Vw#-g*Dgg=iRULJa>Ep z{wdw)=-(Y3H7nY@*>@E{0I^j=O^GAJa@%98ICG2H|1r<$nOkRbzy~<>VM6?FQ>I!s zNcw9LexQWvjHae+oe?L31UD%tFN_&ejSdeX$^>SC^_U$KCt4W~CCYhLl(eFBH~#dZ zG#NMJ!Gll`3{#5lQ9puMdqh#Y0}z#@&@SH++Hq5#)<|~!Wrr-Dv`sbN+m#D)-KD9I^0=T$ zD=mi&O&knksA~;|9gQVXxHNVUgVDr{n%JkfWe7YVA_!V(x2~9-t{y2-I8S0+@ug!d zk{Fd=G6oyza5hr6y5eI?L7zsZ3Zr~~qI5%-8@5aNsdfm5GMTS;PxyCagY@G3R0Ok!2HUxtbW(Jh1)x z0DacuVWCN#D#ygfgDbuom=Q5vo}6Xre&SFw?XLAaE_u=x$+%0cLHkldbtf+(rm^UN z%3EW9j^ahMX2rC#gx0J%SD{6Q>ruaSWU`jGk93DQs9fTeS$xOKf^JW2g4QzQ^r@df zzDB^WJGe;AbU%J53HNCq*>(q~UJ4KWVZvFZw8~tVBTI5b*`M@7Q5nQr=5}L&XKs6O z?oNKBS8@&}@{A_MpVh0RU)F{uJB6)FF&~kw7N9xrCBsnE%ttwP`{?;7dxEip(;c2s zjIJK;${vg{qul$M>!_1fS>jUQ_*=u%EXZJWPkF`ZjNidL5 zn>C^{*lygKs`C`sZtWfkCiE7y3da^TqgSTj(cJDR_MG;|3Zqi!x`_~(W_8c@XOM2J z0B}~Yvx^X=R|_&NF3~5=R1*fa`jd%!n5grKRyTJo{#n+E;qRM}>>m&KkKT*FomKvM z__&f4-h+TQ5CDAs4+HTo6l=v09if=Z8m4|j3RsNP9+{lj$X8|1Z^Pe9N`WC;Zh($_ zP2DQ0yi5myuN-=cSgq4V3V5TLH{#hNR4s+^hC6KTI$Z{Q4Y1#7BADnM0fBM#8HVfF z#iGaT@;=k1(x5SEQ}xVWn3=;Ja`>Z9XZI+jZv3Z>{FSHqA^R|sn`YrP%YGwo;foxy z$j$Ea2JzHzvP3%qSB`Lsbv!ApzvU>9cYPiIIJL3JF<&zWd-$zs8ENX<+;8x@g5ns- z=oPJ4hlz!DIuc}PhBbZqL#KTn8@IQpVzHE%Y7D8?e8#;P{u_CiPkfK*<^0b)G@Uy# zLa~404&4_Jw!L^8{>4Sk#jGnIa+vuIDyz~tokJ{M4D|kR9=?nh`5(jU8NPrf=WZ-X z&fn&L{DYGCXS^3K{?{j=G`JuZ_GVUK_LUidGF51F>e*-zD{Fu00bb zzcR7ZZHJCTnd}R0>C7k@urD-VduYw}vB=buJUa!}8e>)a?qIl8obHiW4pw(M*S+=P zu-5^;_#fp0BTF4klKRS4+%?ScDfR%HIsdJQ+PzD0bM=S83M`~j2GsvdbKv4L^-*lw&-q?bgGzYB+IV5b$oqF<7Ekb}uW zFs{BPW2PLSY0 z-Qc0eraNcVb)VsJALnr&<#wAEq|%JsWD7?JJjI^DD5xxqHjBf3(_mhfs9e#FZHQ3d(5#A_|QBTYo?WE;8=(@91DT*%m0qYLYT^ec4DW2>e>3#hvWSp_d51k zw$5ds-Fo7f4PbjyCb1-uT@nxDMaHRs)&@7#4yCDaUI&W}t(Ga0Hm%h`g~>qG_GG!K z+aw-hFP!x~M1P+&s^ZR{G;_SSc+8P`j|wL6CC6nVUcuL$*;baZr09=YXF24!0Om(J zFLX^==!3+7nKB8fF>IJBiQh=F?vv5J?>bqv@59l)yK!^p#Le36Xzx~aI;Ii4^(obn zSld1VSG(_w939)Z1HXLgW7RLgvU@mukBe>GrhG@h0zC-dVDiZ832}cgK0VX1|H!+3 z-PwOk3dMh~wj_OMKA5ZZ7H$m~amKf;uSPWfpVHo8D`ZJLid~Y{ezN3b&J%D9UC%(qZCVJ7@K5{&jV_`5#Eqop7!MA zAh7ls`7KA4jDd(s;{WJv-3Z|!Cs>AxE(Dv8o1X(Eb|vw326IsuptLdW_Tu#$j=dY} z<8|uY_298)jI09hdXRCF6E_$SvFcBK3bRShU3X}Lj5Mi67Ntya<%4&s6jeFn(Ty0X ztY@Y>;#;AkV`T_}ga?AJ>Q2}oPccrh~6eFSct=P2bK=8iX2xN;{ez*Q$O$uTIg zwb8UcYrhTeZN$!;zO+CD)kx$PvxT_J*6XzRuI)vQt{Ns8FPeZRm z|4;n48i#xPqV+h8G3={uysWNnS-8^I->s2#6%{qnM%+TF?yB@LK5wp^jx)tCQ76mh z`}7I3Ww?H^rV^JBHinH?QFSFw6vi3U4v*5h_vXuT)!#D*oj9V}r z44J}`TDM=}((>9R;hCvext9vp`rY0mQEjaZQaP(5MF{K zCc_OlYP+bRe94k<`rqzuz>&tOFrEeL|KN=s z<;^@S17~MJ@_mc-$?>MLcoArRow`f3F~4p_ZMY#H8EW96|M|Z1%1U?StHQYY5(iyZ zBbkPJ9H$T27B9!a+bA$@*TiAcIBL8|7sKto)VH*}5r_C!R5ylk0J(#*(N{^qA!3P? z*z%ZF4$&^ID{rXu;UMisP92lR!-yt6&#s^TNw~kUsGNYUWPJBU}YUH zC0JHoTRHV=`@UD?t+c%1D(E&NHA1s1P%a&#FNsPk=q=Hx4K_<(xh*W* zV5`cCs^WbWFd32%lssBqBiY0awAGcCh#Nud;8s=L(5P;>WpyttM>~eL>WD5T6(&qo zIuopeVngli+-R}G2r6xxS%s6#BjvRf;WogR#CUdwAt?k7K zb&;jv21f*k+eEOv7(wNscx5&8$~b6a=vt0)qr1?CTdpjYt4ylH%6hC&R?G+Mq_bB= z)2itaAKpws+oMaChQl-;VseU2A~>773TN2UhT{NxUc+1maYP(_fT*w1YiG5Hb+)`p z(b&YVAzI6JeFKZD;4Kt(AH3Y+6hL`ZNXI}+-NNMXdb@tWDif~SK zqy|)MjcCuhYZX5nj?McICgRexd=L)?B!w;wt z7sd+>AL*zXV#CCp9^ufE+B%xqnN++$eLXU@>aZ)&mi3f)SPQ>J}B1vaGxw zMJ~rZ8@1?M>v?5%Bd%r%%g-RpU;g6iNaL(+vM5fedY6Vi^L%%0V)QUKmSZIHE^q zXiTQ(*F?7Y!`QdBvPLP{tn$cGT1t%KC|B5buovTsU%2;vDJssV{xKsiF=NW`+3j40 z&`@yPJ_D98DKO0y<&71%%VkboJ;eaqvrI+>U|xt}E36p|&(!eJ^5xZahz4(EM+4DF zU1RxjUD@>2!+mBoT;70~dW6pYYl*t?MR}?Cl0EkC${o;MwCX<9tAWy8ZB}7xG1lj} zQIX=xoW-a@)lgv})p($BPMtk;Q;h*lSsQ)zT5IlyIC-Xi%N-{;QSdy$iv&-arRh!; zoFX_^uvT!n;B|sG3f>|3Q^5^_zZPs4d`YlV@POdpi*))&3Z5lcC^$oKp7Quwz z9fCg-+#tAFaEIU80)uL|}E4xT6a zA$YFf48ggA6@ra|*9*o4e<-*^@KwRT34SD)aj8x(ORzw2hTx@wD+FH^+$Z?8%Ot+w z{eo?Rn+10X?i2h_(8mj7@ni{}B$y{yB3LFE7Q9;UCc#?;eEj1{|S%(nl%1N@qfuf{}b_l)#Lx7 z_{`o9;pJ_u7rYb9D|*^4ODO#otRmCXN4D zY5dJN%FE}xH1spl_%BG~Uz5iF+BE*Rr18HijsJsb{5OifSI)1C|L30eV8)?d`nS^X z|63aWgK7MG)A$dS@zp7HM${~i}_*Ly{I=h$7PNuVa>FnfJ?H95k zCiYS0>97AH&DkQ)T#>W0e`9!ogcqg=XaAQ?BFFPp?NK_r*79%IH6Fq=L-OmjM+?&U zGwUpe>C@@v^HtL?IBP<|`20x=RxRYPOi!syak*Akt7pOT>Qy9JUR{X^neY_tyIM$> z=^Iv!vqDWQNK8^*KB;2<)MO4=vF z#WNPp2+m&E7;UJ+tg;Wz;(2ox;vU%Y8mDI|0+y9m)HPb3XX@|QXO>hiZon7wGu4FU z3S1%Gc;-b>%$Z+PSl19fLwy;cZ{MO+;R|$sQoxycm@LSFn+4^ zvpA`M=Ob_~A8=S7{#jE0oNh}MUN7Mj5S}W3Z4#b`@KovdNcca~@b3{rer1|)bKf$S z0P^XR|5i@Q;o)2oo<8C04-wvWi12m^p92v4PdZPH*vY2y2i)Cs3cKU>1n<-cCS!)em*JOqE=Q9Av_ zY2w#Qcoo7^>2IfmSEmV|<=6agg=AC7r$+`DBQ#kb`DcmYcoX3%<4gEp#N0J+P@nj| zuWQDW(}d5Ggzp7e%J|>V@t+2yDu26#r_;X<3I8BX`W-T`8mp7(oBv~VKsx)eNW#nTJ0+a`Zz}%dCnEq4wgFQv+kf9Z4oB z#)sJ)IODl8P5jnrI=l;zDu3&z>+p2#r&Gd0 z(BT3|#r@2ZfFrbbpYTNzeqtK?*IKG0P6VWC|HCD@0oTm-4Op2qAZzH5%vPkrZebMu zdalzuVnofciT9|i8wM60Ik-4>7^9M|2!EN^YdRS&*>ohrL%JIN5IVk(sV4=$+Yzv| zN%LDKWLCOUb-Fa`Pvfsm==gr79>ed#*x;K6-JEfY%B4j8-Be{te z56h(I?M6*@6Xc6lB3aXaWX3Z0C*Z90cso(5f;&=q>~=V&MIfNqb_z3-)) z(YKup&p>%@)b%Ik%}(Eu&&iISqZuwLYJ=GY|l4=u1DzjzNsFgH`Dv(lc!vc zU#s((E`9QS2XteFZn3bn>N^GB;e%j*gucp4?`Vg-cc)G0s?*Rh{Yuas6gs}As>hTo zh5T=4`v0NJC9jXR{3Sa1J`K7>LdW-4^`zlT`u(8KN@%`(pLNlf#)jN9IB8i-(vKR9 z`Ve~BIv4!~ed$X;pLMh5f3J{Ra`w=VLHM1dBHf^NOg$@{cTC&{opAA)Y3(8;(mCEX|lHhl@YBG4@rI*#+y zW6C4r1lCUt=uH0jzU`t*Q9pNrzVlWsPgIqgUYsn?Hqh-5x^((My8WPQ`x11+4~Kk& zE+FYxdMs|1XENy8g^us%>M`Y>|4Z5r! zYkquxSC2{GBTv%r2Yts+H2q#7xA>-@ABDNiraLt~-;dN|_@~g*63}-DeY*N$J|dvo zD0F=9ck%U%+em*O==a>E^TBxl^_YBk>?i480sUBBOpAx}0xo(_y^uZwN;7G_rhiGu zExCBgN%{$(-y`%_dFeg<3F#|AKk}!VKj#tD(@*|y2mJ=2=e&Z8K1Drk1O3LIY5tsN zaM63@quM{{=iRI6Iq%@2caDFCqP+@TNk4Q&pxYsI{pocL=sJZ?Ui)pk;~1~p4!VOv z$9V}?{yg=k+CS)5KB&vnhp(eN`$5Je=2X(R=hul^^s6g`V>qE_!GAK{x&(&9_f`c8qI=W6pi8&;`Bx9CVXG z*Cuo!FP-B(sS(LX^?+1OG&~v^;JtiL>d9Zw=vLKJ&YW^dI+$x{A z$+rk}`-G13FzV@tZv^yXAJu%jgxuom(62i|m&F61@bqU_wvnIEah}GNzKkPP`9U}9 zam{bHH(gKpNk58{85=eIJ6?K^d?~LI(6a#XS~S2Kv`aG~Kg6@_S6&_Y0N_UIb)1Q-Qbpe4}%KF9WlHQ-Onl0pM;3><-{_ zz^g&O8F(b}@i-85HTq8NU-|>_zXiAp{%e69!0Ui3k&o{Hha%p&f+q+L0Wu%&T%gPQ z29WvwFCghR3;q&V3;%n8$Vcgqfxid;+kg%5zY$12DYkm*$dcfx-@km;2Hncj3D(-{F|I+?)X2=9fVXS{g7mrD5M5~#2 z3b+mK5#k;S+zR&~aeqF|=X)0JkARH7A4t9V6OelIB9QWWPQup%Ddz}~`7Q-AzY~F5 zfFptAlL>4G9xTS$NWf0ualk(SHv@MAmm~hez)f(k1mYcebT#ly$a5Z$^>q=D@h$*9 z1w0?f^uGqA+&?JN`t@Hx*86rK^Ysjn<@iq^%W*&O6u9pKjs<=f_-$Yq7(u+tfsMe) zzzg8dNnh5-KMQeACDMHX$n+itGW`dDOs^Tp^ddlXKRHXqy#UDg^MDT_{&XPYoiBI< zFbD3BVI*MsM!W?)4epnLafp~`)y%Tr__&o?j{3*8s zH^M&&{7>Kw+P}1k2={Uz>8pUu#}z>GEe4YB8G1S{J#$*{Z&BnDF=>6`WFM4pXos6zX13+!t;Q41JBa_rQ?Wj zp9m!V(Lm;Z2$1Q#Jw@mLMZqmV=Icq|-M~1I>DGxmEa3})%=bJZ!UI6^I~#Zx+*v@z z8w_NAKcB4g`4R9j_`e4v|961Q*PA-L^mQWqUlRXKK<4*hAo)jtly@bN=`R323i=`m zKSsg_OSljC2*Uqyo{s+tknx`dGX6&3bqHTC;WtS5cY#UxUkaprX9Ir$_i;exV*rqR zKFZg8{sCn8Q$U8_4P^K&5{?tIRC+ig#rJK{Ed-M8Tp;OA2jVi4oc(!Pe}4~TyWA@7 zCxO)GTY(P)Yk|K3E&;Xy=L4C~i-Es}dnS;2TnuD7qk&B4Xdufy6gU{+pW%g%;co&N zz8lE!7lFS*_)`*omxSLc;Wq&vK=>*NpD*FFCAbNp}m7bZf*v zEdH~B%iv!CWcf}5vV2DYsb?RYt@Y;{^&`M90fkoSsi2U7oiKW>4dzaPlSgY;h!w|OXu7A`V%1A(|3SO z?KM%YE_%IORt@sXbJLsF+im{dEA@?luA0Zu5Zj zH&66CfNU?bfNZyK0kYk`evWOouK?-)k3gpHcSO)X2h!iuK(^b*fDAVZWP9xgvb}Bv zG9K3oF9*_IEs*h931oa`2>*Gu<-ZK1{oer@uL&UI^#YLn$umIO*#~629wUMt1=8Q= zfsD^E@KfMl0h|w91!Q;^13wD-Bum%u09E!26HsD$o(IzZcYusP&raP5c@#+h9|qn7 zx(~?kI)K!#1ya8P$aq}@`~>vQm;C6vt^6R6`SUoC_45-z){mQktRFW3SwEV@uMmGB zko9vOkl~yLWW0`^W#jV_knuSHWPE-Jq~3Et#`_0A#^*uFKL@OZdLU$vL1gFmfp>06zx&GH@9986eB?LqN8R8-d$_*8>NDR|1ECWx(5k z^MNto>A;TwPXk7Q#{qW$kDM6@+y>kOyb1UX;H|(f0GS{60nY$^H*gSmhvnA{0vYZH zfeiP0;LS*1z2uhzKMej#(My3J1br@$_D&Z+8@LVh@4)N_{wI*(J_P&_=>Gs-3A^_J zXQ95_3BtOd*Al3x)8-Psz z1|aR$0O2)v74QSVi!Hxq84>gaK-Rl6fNO!L0$I*Ko@?v%13=d6uL4=GF9Ti<%m>1L z&5Nf80*7GdIbbjF9w7O50IvnTUGz=B2)JLn66w7Up+J?Qg*zXJv= zzvh*91_EuM4+C3)KL;}3UH~FXa-RS)-n_Ar@%|=|@%|E!c0UJ1SCM-Ukam|?e$9O0 z=|JX3w&VwMY`lLBB>yKs`g;^ujr_eASOdGC0CL`JQ1}7h`$1n1WcurX9iYpAjK@-7 z1Ni3w>w!7I8zBGdJJ4@|{w45g;17Xly1D-eWPW`Y$aL)nUJ1M#_ws4voMIs3u>g1}_&GrKD_Ou3Kp%a(jrZ??Oy4N50rLCBuLB;3 zaw`L_hyDe?_W^T(wEK2oE$BbZK|0{?2f#}}KMZ7kd=^N(dw{G@w*gtNyDYzE6Oj6+ zoo4<2G9L3Rza|Gr{gZ$U?`Nl=J;TnAf#`a2_W{u) zbH4?w0`35YfZKq~|6br~(5*m*(+Fh!em`&(uolSrTPpc7U?u3Yfd_#Zz$V!FTegkw zv%srBZwForyb`z)daHr7UkWS({hcfu{s=G=^q}aQfvgWI8j|Z}z{}z>${qOgIY-bMw&jSC8 zK(^x%AlvcBfxVCq0@?mP0HobbK-z5pvVC0vABI9=$h^`{{=RnRkzW`hZ{4XHW@f9HTz6hk=?Z7PX8-NVwav<%l1=4N_ zkm+0?c?K{GQ{gYqu=#TUNc%qo(*Ku$VbGrja$NsWAj9hcqN(Pt0`hsZ0LXFBJRrwG zr;7i#<1POHa5MN{2h!eWfbGCLfwa2_$n?wu(%uO`+ItNI?HvY^|0Mn}JzoN%3FLlG z^e2E!ANTw+ed~d(z;fVmz#<^iH6O@yy%WfEoC-u14qK%e^1SaP9#zoZBV8707U^fQ^u!3uOI98!+`7btHhG zat{K39SF>x0Cr^rW{(3qf%}2j%QSl*@DGrW0VjZ?K4crO*IrN5s z)Y}21UOzAd>;qD-3y830w*slx0HoeVU?s2?co^~!@OfYJ0O%9u!Up$A$Za`-Ee{QQ>akF5!r9r*K%fL)b6u6Qb^$c(e)| zgd2sm!jQ03SRyPC<_Uws9AUOFQy35)MSC*o7akN&2*-u{h5LkK!cpOF;V$8baHnus zxI@@4>=SkgTZIk6jlx=CNLVT?5f%vZgh64BFk6@@1ekP}V7&4x^d|*C^w%@9fr!)W zOd$TvE$}+0zV-B zQ9L9~x`B=0bDYKWPKZAa+yMT5@%M>82CM^rRQ%oI?*gs^e?XL^qS znGXl?=e^)hh(9j=e&7o5_lZ9y{wVMw@OO*9OZ*Ywa`1PGKP>(Z;4<+0#qSfp3y3y0 zyH)%K@izjOf?q3sNc>V@G596o7l@w+ECN3$evbIrK(yW2nc@e;KZ@t`Lhz3OnGXkn z5Y3zb=7T>j{(kZI0T+NjCjO}SyMgC}zf1fP@pl5>1OBl1JH+n?o(F!P_+8?+0`tIc z5PzfiwZL=04~btYehKj1;1`IWCw>r!skqrW;%AG05YJ=w8{v7w`pOE0E<=3Zz}{^Y{0V2f@eG%k%3u_M}+;t24Sf%C=3WEPLhA&h_GMSAS@LIg#qCN3g3h;91->l8-%68 zpfDhuKt>t=!VzJ=ut8WV3=!l&ONBvUKsbR$XZ#CCg#E$>VW}`E3VW}`E1Q>r8 zVR9@xV@BZS)tq;m5jYFdSJ@cU(Opa$WtVxirO#w%3;j|{2L6djIMNNG zZ#d8DXG{Opvn`z?`qZ;59Ta^e*V1{SKZwab+T-(u?)iCEK7Ng**GtalG2ML8W1|0> zXZ4Rr|5;4z(B2r*M8|JHNOOEdcgHza-zDHw(V<2wKlu#E(e@8^TKdJH)!)}(>5=)C z9uxg3Op-Fc_KSZ1`Iasb{YAxx<8!(f6yKbBOMhPJ*)9237FhoU%AeQe|A^!lGJlb_ z9F^A=mKWmNulR0N{*}moukyQ8^!GSug8mNazwsU$UP$t)b1hvf`gRvS`V+dUcU$>J z$@gNindxs3T_An-YjmGwc#yYBKF;`}JccDdLHZ-2e}hR|%6E%?LiCvEapjjg=j}Do z6X{XyK~(3D1BWD|L1rxQO^FJ?tG=UPx2SkJ{lx1k-j^(?L?)&U;1BC`ge$K zRCu5kIy%{mlYnLdvqU@zB{Mw^NRmY+2i^x<3Az&Q>Aa8q*x1;eRmGq zr$tYgCoR^Rm7ngMwUd?p5!pYi_PR^-7RAqeyYSE~MxiRS(n%J+$0mG77QX0^9*(cg3ViF!o$J;m3Z)An0hJxadDe&0*fWgkDbbo)hf?S z$zNpnkdH|ITh*^@$!}+QK)y@zAF_W!e&tBMo%TToMIS!T(s`oSsXR+WzwaC?FA)7M zm8UybZtWRXUMl%!wNDPl>8f4(7Tu@kL%--1D*xSz@9)*0g=CNW+!)_l(Ld4iVx#DP zsDB6|U39O^v-%B^KbvdmR?!zK{teQdsJV?8w?qE z7uyf&L#E{1AIAKgko*^-yYzfHU+wdteSFG~dr}!19{_hsOS#+NKw=;Z%S0MUag>|4{zNMXx#2=I4Hu7xytTefwqa z8!F!k(VdF#LD3(O{!!8QD8AghLsu*PY|(ehUZ&`|@*foae)-Q6{auw;spz-KUW4cy z+1n`kHP%1GzhCrU&bRaq(OYG2SoC4$2kO(9>n~N_J0<^n(8~8o&i!PxKO#Bz2b10< z`XbriExJkNIV$?Iv`;&?@>?+IuU;XNuqdD1W*n|GDh-iJmKa{i3=5nEAcK(Td-&=%uPZ zJ4OFe`8y)|T!nvB;eSZs?UMXamG5rRA5r{9MK8m+i<1`<*9MHcM)a`yhbL!Q`MBtN zM2|}UhsVo*oAv*|TP&T|Wa)LHOPVdcL-eTV&&z*F*velLT`PK-=$)cZ7Tu@tzImL& zyUzOOc^XVlmuRl*linrz#C*`%koQ>qCfLiu|E{%`Zh$`NgQEX}^pXyWUO5BwNmvU~ zejkM`(p{nt!wl(G(ODUmZVYtqN~UG|zTmcp=d*A-~Sd8VR&2zFZ$TbM{Ce zQ*=Ah#xUGDgSUxx=L-IW;p4wMr}g$MOE+9%)5~=y%H27Gn1eI)80rn(ZK4}QzX$Q5 zzB|`4|E-pG=LqgP!P0?J<2_Kx{DZzbr}W3OC9kydUCd9~Q+ytG{zZS0`2o2*hp|=u z-MOVLvhU704ZmTr&Qkula~e0OJlwgC+>b|l?wraYrDvqn zhW7>8cjt5th<4}3E>wKnIijPenuU;sxBT~EoX`9i!E=-DT-676F6C#S&pdGM{rKUfzl!vF>6yq+Fa0Ov zmzRDK>G9HkL-<~r`|rH;A|HJN(&Lq%;iKW#^^8i~8iH*ZSf!>9Zg9rFV}%KYaer!uyKXKKGM*X`U_U zrJqB6@Y39e=cUi{>2sf%SAHA%8!x@i7eDUX^vb{Llh4Gnz$+g|{r1v7^2Pr@eDqg* z`s2Rv4*B#S_tATO{@?Da58UtIjZdGizF+9m-{aGNk1v0o^wp1lgK@9@9lrQC`{DKFTLx1`4{tr|3M_v8y@%ldg;&l{Qu0CK7M!S)qkHa{53xRf%^JQ z?JZky{d@#h?QE_KM=r;;J)K){5qVQvWUEiSdWv}6$~EQn<<(_%b@lUceO5)Ax#=AD z1qJHMSJ?~P?Y%vhw?$gZTN}F@n{Z)z&)UxN&MxR|4sUKEnn;){e{JwpxDBqY1%kD8 ziF@KNU0=OE+}Mn3eOBROB~U%E1ONMEeVc;i)|-f*ifD_f7K`Ys(_Xz$yZ zs$-+$?kbL}C1qL#u2=8w+-g;QIi`f9l-0F%Zb>Ov)!EeBlO!=&Q)4gHOA@$(t7+_O z+l&jzQVLxOuwZ@hvsc5X_7-Yujtf{xQ2OR(qj(r5L+965;l{7BwdIxdHI*Ae>niF@ z<~pUMqF}Bk3E-p5)gG_VmX(#PU#IeNKCLj}b&5h#m}~2tKf_LVV|{fZQ=Z5ge~M69 zs>)VYSK2grZ>gTD>rH)R!~6;CzJ8>=ZA-pr_7ziz|2@^#g0ZfOn`K{TYjI+J&$0TX0^Go`}%M*@ALN6yUQEfB9%Ada_^oEZJWcLy-x1w1IpVw zd&29(H}s-;`Fx`$*0yf#X=`e1uL$4R))cPo4)?%VV+5Ca)%A9Db#}u``RaP7rI(oVq zyLnT2yjHk!n%X$l)owu6`L8&i>d{uG`VCjqqV~NR4^x`qw4SQVR#sNm$D_!ioF+c$ zb*?X8Ro33#+0+>6?5+s6H1@V52XGHvQ~lfqIbDlSjmFg>KN6cbg-C#dagZ?|>LCx}1oc$H%{`p-zu)Syr~rt>yc6n{Xb z6sZGG$V`fC%}g*|myXy?Ct^=*>hn}z5V6g)b%&d)bQxZyyY3DNiMNTBoxL5+E@hsa zRUD^Fa%!=jfHqE3y{6>bhf*3*o$g9>D!xkO*omP-Wp{UH_ms+3-q}uWLd|-t#BH-v zmDl!ezLso{HpVQXS(}@CyErH*>jET{D!MyCo0{L)i^hOk`x<+~wVj>qwG{YbzOpT{ zxv{HibK@p-Z_bXV)a^s5tfvcAe?7Z21?5p$-?+uJkD5;Qcs{xMOqq$`u0yL-zWVaU z?k?lc<^gI?n-i7w^mI10;o?OjFY9Pdsb`YxiH8k?gPZ_!r#%ex_6loz+uOYvRoB#w z#t06y8=J1LZ)&~1z6IB0dUb5T?QKohPm`s+J*~bJ#~bph_D*D2$0o_q80?*k80MgJ zs>3aulz9i08@6_Z9p9h2^7gi_YddjWXG#S&LcfBqq^G&|bV`lz--|j$!9~E0sTsE8 zC90t<4L20@=Bbsio0~4@Qpga<6o00t@I3rddpo9rwQJO@g-gpZa5qmw7z@J3G;NQZFg%5Wr=4QR&?@OC*rbWn}esdFU z;ccFh6N%U5~-<>Qg=zJI@%&hjB-5s!rit&t2$KK%5aacvTd5TmI!7k zuE&$YqnUUc@Lp~$Gj)~KmE{}Ot*>8SxvFw~C0>Q=>I3bbhF4uxx1ql7(poC;&COeC zM&|JdY5tXoUMYx%z7qMnIgDy-GwteW6lxmVI+8V%V!o#(*&)MO@#^$;@P&=<0;!~} zjXgE-_qkLG<|gd;v#e)TXZJDm5siwrW=sopYznWmd0va5RJc3R7Vb$Ef^DJRB1ofV z?w3wwWeXZ-&!wiZPpi||)D-TD^sH*@?!kb%r>nE0IozGfSX&R~%J7n!O4ft!8}nVw z=2H7q3ikbYT5)sORD9>t7Os0v1-F=)I%FU_=1m=_AIp!W8LvrkO>gnHq5W{4ZZ#%* zY*=wMU(jhZy#|}ygkKeMuMhRXynV-wdR3dd8@KRXFSQWUg_}3y>hzUc>r9q*cBj<4 zKD_nPu9T9^e4Cs`*4?(LHDX4c(nY%(;z7$(__Zpm$!z)?QyxUXp?Hy2g1ixs|asKZD@@2c4KsTOzoO~MHdf; zV;Z!b$GWDb_FmrTj+e$r!jrdxHJKM?+v?+1y$aqIZwfoEl`@Agd)wRIY$mDVie7WL ziXQzK`_kpz;jVT(;5XxGWSV+P{hn|n(OWK0>N(z=ffn0UHAT?Q;xp|wJWRUxHnvY6 zc4Gu@M%VVDr>g6{w#U3Bv9Y2n#uS|Gy_O%#hG}7(X=_fIJR`2`YeN_*U~Itr4k1F&5ZNHcex}(;m}kd75Ng%Z{KhCW?;+*VbuVg8rG4 z0#gO%g@-No3hG{e=?1=yVqwEOKEQBcZJptnAzIStXKmeT=o)?%`fd-Iq*j@vWvKGi z94%;mw5lS_KxSdlvSsPS#U*LQMd`(b>BZ@##Y@u%9T%I?+th*9!ni`J5!}M!CF#w_ z#nYKDUYypzqO{WD;`CxCoi6Yqr;s{rh3Un(XV*V|O&X&vEL>J{42`%#$_OlV-cw4K zq?HygPAe{oi>J$*xI)Unix#CfUz}E4T$EN^bPREN&xPs5*rj7jEoHh2(~F(o=`yWo z+0wMu(~Hwfi%XpCX+w`|q)uCUaa=l``QoK60x8WGr56`3alWUsUli9!X@9ZvpHf=9 zD6KfXv^cHb;==Ueg0$kIWlPdkiK3FU;^L)g#p$I*OVWByFD+i|JWpF*agCHEQnbh! zNG&c-FD^lhz96l*XxXB4#Zuy=(}up(X`~E2y|j2qTF*u4 z#p$KRi__XJUX)f`bPREEdf!Eh(oXO#ggniYxVSK_xG252Fuk~_Ag%Gj^y0LBiBYrM)BBE#r>o{gi_;oNFI|+@Z*h8QQF?I!#AaPYdnzy+AMP$v zF-)79WyjJeaSEwhwUbU4K=Bf%kneix#K1y(qo7IK8+iy|^%~xbPU_w4RF!(uxZq)^eTQ z8fSXF`uW(4{qDjGiVA`WSGc5jSwTt3q9u!h!NmoI5LhuxELj9zVbPKjtKD!?Q)g3W zV|@hs8y7Y$?6mvWpZWVA-{2N?d}d(wX=D4mIZ!qbZg z91WWR{x|l3_4N_VJmZI4ef@^YjT;=5z`OL5_ULyM5yO^o68!|{>g(Gvb=%$8uJ3sK z_I64U+u@BHg*~RxITtTS*Vki76RX~l`rZz%IMmlyS2!7Qmxu--&oQIDgZLx6$^olT zUmtG8@;`S&80U{4&)cLMwh-XCYO-TE_>7=cA_MdB*Q6yA8|HBs6a&kl1PNjOGywa7 z@W=D}lN{~3|4D*3L0lS&{!eJ&580uiTaF&2eys9ntmYpthqB(Y=jE&`-ohd!vG3)q zYrx1p`Fb!~vhw!7oORW+5G;5s^Z>oV^@j@gkTcejwQx@;t9(x=hL3~rsbh584xB;} z8h9f6>c<)0)rZJ#$vU$FcN{SM7(Nn$Lad*9S3gdIF8uV zE4nH2e#16rZ>N->CV9pO)(0~BvNN)0osc;UEAzqUr(@%9*n4eVIPv>}<&SzuP9SL2 zCKV4HhBiC<*5k@gHgz0w>b2v4ru1C6e!bhEgEC!lBX9t&7Yz1W*#^c%13>1@X`k@4tRcrc1s!bsC! zE>7KF78RGMdu=!gi%o5P^^sQW&uDf%=KpDb$^L@Q^_PzR|Jq-!Rpz_?lIMluld2T` zWs_Y;qYTd9A2~ZKtvJx3c#1Urjr&bJR)5L1VVrO`?9pT^;+JV9|5AU+b0O(AI(w+} z-YCA={&E+>$0vY+QDk^OY^CZi89)BVr%wN}V}Dh4SzRdldT3y8W@X{4g|8l@k-@RZ zoY2tX^AY5unP@SEV}^Zr*52cpqOwQ$w<3Dv%Cc*&e!OL2)|uPsD=H|$D_;4ywa=QV)R3?4`r$kO-ZMT-`5c4|4ICYyzwh9$U3Dwi2vD) z>)!HGrbuyi2{ipXaw;jlak_S0UDd|r!ByCi8*UCpI)hDUi;-}!$!@j^Zm6ycOsBIM z{X!o@G_DeV=kX+epQ{x;AD9uym?H0&9G}tAO_2{B2uEc?mF8_yw{! zUHGNPkh|x{bkD8Yf7%BP<7b?E9+eNwxC0H0`BH)ZebOUsoYT|2?d0=W(l0&5H|6u~ zF6gzs3BB(@@6E*T0Q6k^n2*M#h~F&a)$p4L7w3k??M;`CHPFGQj`=vNlx3gKR*Zf( z{|5*heMkPIKt6}R1;jHr|5Lzh;75SySMr;H=zH?d2U7n`AoW>Z7#rkI0AX_Gm~cec zFKiH&3WGv`@fQa2xjjM#+za^cea`2IcJ=R0(X~LjJ^1e(2h@mm_3_K1hkV)OXnjdA;cu>YK3z0^dk# z%;1J6`fdc<< z8(@NOwkhi)l;rSxO=A3c!UQIG*h7X~Y7cYwyYo$+?Sd0L>=WXbA#E*f*bwU!H+h(* z4ov&rx+L{DY-v#m-d^b%PU`5{)Q;CP?6c{MEZl70TvLt1c&2tBXo54xyYby|TBnB1 zbGnTa?Z#mo7tp=g_tppL5svm~vKHsKRgp2>dn=a3==|fbG1&ROP!74>{&vHr~7y^`s;pYn~&d`Y|edI}sai{X=lA zp17LvhKaCmo}&Tb=R>go4=%QIMzdBPPX+Qn>oSfzP*73{BJ7}3E3jQ9p_DhNd07c!@S zai{=x3D~2|!hzRj+*}&nGq5jX@Ae@oH-Wi#yYa_62M5M74(+0!p~WAx5jz2?Kh%JT zah8q#Zy7_hdV|v$LH@zW;g+)4y1aqcCL?pBj}3f3Bl_gt?cZj2Fber`(cAAPRTjHE zXW;LXkrHUnrS=77u`R)YHzp(Jl*K+Ye_(PllAG~t#-B4@i$1n@yYa#{Y7Hzh2Hrvc zmGD1<{x5?6O!_aTw}IEP9z-1vWUYI4@TmvPu&DC;L$!(6uw{8;vu+lRyvwV?%9x~* zJw=7hAW7x4DJtmsyeb19S`dh=Kqe32l@(R%VIRFn%V_h${himZ>0hqgVzT6AUE zRb^L~T~k(n_2Xp&A1Vn%vQZ3Ut}M~N1dtI04E@T%r+QB-GiCPZQt-_?X!Nne3x~Qg z;(1BIQ0;Ll8Uz$5GqvQ=68d0M&UiBVqv-cc##dyFMQ=A{7#(vp1{$H*3j7UJ$IiCE zh8(Dl{&ej2fX#&$Lqn`;flyZEzEEtL;uL3A zfLUnM{%9VuAauMbyioKP#!_|kZ|vU=(SmQDq740+nX7O?4m{M(3X-+`+xWY;@&NsW zVh@>UxN^VrzyrSk+Xgsr%Yi`F9k+p&iM<9}LRlUATCzIlyd8Pbl6CF(F~OYsF7x}U zAK>?%maI9LeBQXHr7ElA8LMCf&qDAl1o(f=W1OA-D=M%tN#Y;~OjBd(`ihfze;+Mb zA!e!VYirhoIPqDS1P@#=#ws#YB$*we?GDN_%w>XZj4Yj z_7EFXb#nWSzEL*$TxjnAMRdxfYR6{^=}c9*%H)u*y14vOBnQw-&Dr+o4EW1v!1D^N z{yrKx{7&?Hm=y^eJ^?>YVh8xDp0XkIcXsy6hGkF4E2wmoMR#gmLFFmNee7cL`?oJF z!zgHBV^>#ucp*OT>e$pC;9hm_OcP#)IMamBS2|W#2hB>(md@_$r6+b}U9hbu*wGmY zHr|LiDt?O^?Cc0)ZhBpvG&i({gOSE=ybxgq3m+HqqmW={i`5A>cZRM19(zcL3$J)# zL9nwsSiG<>h|eIPyfx@FiWe3I@#Rsl@mhS_@TLOk#HmEidruJC`?ugj&3rQsjPUzf zOs92+gI(R7EtqC=b8AL15uK@F*7384&LF-b#fND4P=Yzp9Pa84GvAu0eq@vx&K?0n6A2DM_%S!rdKh)&=Ha{dwd<(cd z;CT431W{PtgHOZ4=6e}@AQQk#tyyR2S&kUi;*==7AcVUO|8jn;w-VC__=yHl<$=wO*YoRLp2Q-{fhL^9zz;yZJk*VpfwkZhqSlq>OqYO;a0s~fkzZgu zO82smXO7aHE36h?Dr^ z4<^S+nXg$rmWOdp&oeK48gj8zdMcMG>7hN`^AyOF9`n~YXHW5Cx|^X_E4^l^#q($N zz|7m@skZ693;M&;*;PEhjQ(UEUNN@0GT;TbjgFsB_}a&_N;Un4xK(dbCRfSUtoa|2Pj}Z~}bNW5N+(zpz1ADhvt%#@|WE7oJP* z=F|QJW9A%fCKPTt@{=_0r>Fac^!K9>>Ha7=&l{%Wz1`Fw6WuF%pXj-w_lw>@dkE7# zFFq>TJs(CKZE_$G;Jx0|9}#_%XxHE8iDqWevHmbV6AJIGC^XVMAC->xijzJl`f}0k z9-cR75AvguUj&-#A6?dapk1`cz&AuUh_MQ2^c9~UgNFCG8vx;@`+RhnkIwV?@AT1M z@ag;4Pxkrb^L+jv^3hOFiXZMgPolrzlYh)d|IkOj==1MCXZaPM{<%K=U-{^heKaOg zz2}N@{Ur516yNDIc4+5DZ^j1v@?{~;^PVpiKXoppnOcr{?Re7{pJOlbco%b8+BpS1G-i1FqarPj;2b9G_aJ z^Q%&3t@i17&J6OzcU5nPIhfU)zkuyPo!vS(=$OaVq&RV=WpgLaWby7vPqSZDqG|S# z2i(G-N_V<1m@VIo@9nyjhV&l�StA-{aHurn2Vd?r=|!Rq5H%*kyECBLz; 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 literal 0 HcmV?d00001 From 8f600798ef201ce3f34e29d7cdbf07b9bfc5db21 Mon Sep 17 00:00:00 2001 From: phillvancejr Date: Fri, 4 Feb 2022 13:03:20 -0500 Subject: [PATCH 03/12] stb_image --- Makefile | 2 +- vendor/stb/image/stb_image.odin | 1 + vendor/stb/image/stb_image_resize.odin | 3 ++- vendor/stb/image/stb_image_write.odin | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index d3d3c6a2d..df5fe0605 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ ifeq ($(OS), Darwin) LLVM_VERSIONS = "13.%.%" else # allow for x86 / amd64 all llvm versions begining from 11 - LLVM_VERSIONS = "13.%.%" "12.0.1" "11.1.0" + LLVM_VERSIONS = "13.%.%" "12.0.1" "11.0.0" endif LLVM_VERSION_PATTERN_SEPERATOR = )|( diff --git a/vendor/stb/image/stb_image.odin b/vendor/stb/image/stb_image.odin index 9e72760ab..12f7aea9f 100644 --- a/vendor/stb/image/stb_image.odin +++ b/vendor/stb/image/stb_image.odin @@ -6,6 +6,7 @@ import c "core:c/libc" when ODIN_OS == "windows" { foreign import stbi "../lib/stb_image.lib" } when ODIN_OS == "linux" { foreign import stbi "../lib/stb_image.a" } +when ODIN_OS == "darwin" { foreign import stbi "../lib/darwin/stb_image.a" } #assert(size_of(b32) == size_of(c.int)) diff --git a/vendor/stb/image/stb_image_resize.odin b/vendor/stb/image/stb_image_resize.odin index bee29a15e..c75a95fc9 100644 --- a/vendor/stb/image/stb_image_resize.odin +++ b/vendor/stb/image/stb_image_resize.odin @@ -4,6 +4,7 @@ import c "core:c/libc" when ODIN_OS == "windows" { foreign import lib "../lib/stb_image_resize.lib" } when ODIN_OS == "linux" { foreign import lib "../lib/stb_image_resize.a" } +when ODIN_OS == "darwin" { foreign import lib "../lib/darwin/stb_image_resize.a" } ////////////////////////////////////////////////////////////////////////////// // @@ -184,4 +185,4 @@ foreign lib { space: colorspace, alloc_context: rawptr, s0, t0, s1, t1: f32) -> c.int --- -} \ No newline at end of file +} diff --git a/vendor/stb/image/stb_image_write.odin b/vendor/stb/image/stb_image_write.odin index 1f0cfce85..2a2ec240c 100644 --- a/vendor/stb/image/stb_image_write.odin +++ b/vendor/stb/image/stb_image_write.odin @@ -4,6 +4,7 @@ import c "core:c/libc" when ODIN_OS == "windows" { foreign import stbiw "../lib/stb_image_write.lib" } when ODIN_OS == "linux" { foreign import stbiw "../lib/stb_image_write.a" } +when ODIN_OS == "darwin" { foreign import stbiw "../lib/darwin/stb_image_write.a" } write_func :: proc "c" (ctx: rawptr, data: rawptr, size: c.int) From 42364f2fcee1f0f588b006bf2b7e9bc6f88acb93 Mon Sep 17 00:00:00 2001 From: phillvancejr Date: Fri, 4 Feb 2022 13:15:43 -0500 Subject: [PATCH 04/12] sync with main --- core/bindgen/c-parser-evaluate.odin | 266 +++++++ core/bindgen/c-parser-helpers.odin | 267 +++++++ core/bindgen/c-parser-nodes.odin | 132 ++++ core/bindgen/c-parser.odin | 840 +++++++++++++++++++++++ core/bindgen/errors.odin | 44 ++ core/bindgen/generator-clean.odin | 284 ++++++++ core/bindgen/generator-export.odin | 166 +++++ core/bindgen/generator-helpers.odin | 392 +++++++++++ core/bindgen/generator.odin | 205 ++++++ vendor/stb/lib/darwin/libstb_image.a | Bin 0 -> 55744 bytes vendor/stb/lib/darwin/stb_image.a | Bin 0 -> 97544 bytes vendor/stb/lib/darwin/stb_image_resize.a | Bin 0 -> 36824 bytes vendor/stb/lib/darwin/stb_image_write.a | Bin 0 -> 32896 bytes vendor/stb/lib/darwin/stb_rect_pack.a | Bin 0 -> 5064 bytes vendor/stb/lib/darwin/stb_truetype.a | Bin 0 -> 67008 bytes wasm-ld | 1 + 16 files changed, 2597 insertions(+) create mode 100644 core/bindgen/c-parser-evaluate.odin create mode 100644 core/bindgen/c-parser-helpers.odin create mode 100644 core/bindgen/c-parser-nodes.odin create mode 100644 core/bindgen/c-parser.odin create mode 100644 core/bindgen/errors.odin create mode 100644 core/bindgen/generator-clean.odin create mode 100644 core/bindgen/generator-export.odin create mode 100644 core/bindgen/generator-helpers.odin create mode 100644 core/bindgen/generator.odin create mode 100644 vendor/stb/lib/darwin/libstb_image.a create mode 100644 vendor/stb/lib/darwin/stb_image.a create mode 100644 vendor/stb/lib/darwin/stb_image_resize.a create mode 100644 vendor/stb/lib/darwin/stb_image_write.a create mode 100644 vendor/stb/lib/darwin/stb_rect_pack.a create mode 100644 vendor/stb/lib/darwin/stb_truetype.a create mode 120000 wasm-ld diff --git a/core/bindgen/c-parser-evaluate.odin b/core/bindgen/c-parser-evaluate.odin new file mode 100644 index 000000000..13cb5042c --- /dev/null +++ b/core/bindgen/c-parser-evaluate.odin @@ -0,0 +1,266 @@ +package bindgen + +import "core:fmt" +import "core:strconv" + +// Evaluates an expression to a i64, without checking. +evaluate_i64 :: proc(data : ^ParserData) -> i64 { + ok : bool; + value : LiteralValue; + + value, ok = evaluate(data); + return value.(i64); +} + +// Evaluate an expression, returns whether it succeeded. +evaluate :: proc(data : ^ParserData) -> (LiteralValue, bool) { + return evaluate_level_5(data); +} + +// @note Evaluate levels numbers are based on +// https://en.cppreference.com/w/c/language/operator_precedence. + +// Bitwise shift level. +evaluate_level_5 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { + value, ok = evaluate_level_4(data); + if !ok do return; + + invalid_value : LiteralValue; + token := peek_token(data); + + if token == "<<" { + v : LiteralValue; + eat_token(data); + + v, ok = evaluate_level_5(data); + if is_i64(v) do value = value.(i64) << cast(u64) v.(i64); + else do invalid_value = v; + } else if token == ">>" { + v : LiteralValue; + eat_token(data); + + v, ok = evaluate_level_5(data); + if is_i64(v) do value = value.(i64) >> cast(u64) v.(i64); + else do invalid_value = v; + } + + if invalid_value != nil { + print_warning("Invalid operand for bitwise shift ", invalid_value); + } + + return; +} + +// Additive level. +evaluate_level_4 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { + value, ok = evaluate_level_3(data); + if !ok do return; + + token := peek_token(data); + if token == "+" { + v : LiteralValue; + eat_token(data); + v, ok = evaluate_level_4(data); + if is_i64(v) do value = value.(i64) + v.(i64); + else if is_f64(v) do value = value.(f64) + v.(f64); + } + else if token == "-" { + v : LiteralValue; + eat_token(data); + v, ok = evaluate_level_4(data); + if is_i64(v) do value = value.(i64) - v.(i64); + else if is_f64(v) do value = value.(f64) - v.(f64); + } + + return; +} + +// Multiplicative level. +evaluate_level_3 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { + value, ok = evaluate_level_2(data); + if !ok do return; + + token := peek_token(data); + if token == "*" { + v : LiteralValue; + eat_token(data); + v, ok = evaluate_level_3(data); + if is_i64(v) do value = value.(i64) * v.(i64); + else if is_f64(v) do value = value.(f64) * v.(f64); + } + else if token == "/" { + v : LiteralValue; + eat_token(data); + v, ok = evaluate_level_3(data); + if is_i64(v) do value = value.(i64) / v.(i64); + else if is_f64(v) do value = value.(f64) / v.(f64); + } + + return; +} + +// Prefix level. +evaluate_level_2 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { + token := peek_token(data); + + // Bitwise not + if token == "~" { + check_and_eat_token(data, "~"); + value, ok = evaluate_level_2(data); + value = ~value.(i64); + } + else { + // @note Should call evaluate_level_1, but we don't have that because we do not dereferenciation. + value, ok = evaluate_level_0(data); + } + + return; +} + +// Does not try to compose with arithmetics, it just evaluates one single expression. +evaluate_level_0 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { + ok = true; + value = 0; + token := peek_token(data); + + // Parentheses + if token == "(" { + value, ok = evaluate_parentheses(data); + } // Number literal + else if (token[0] == '-') || (token[0] >= '0' && token[0] <= '9') { + value, ok = evaluate_number_literal(data); + } // String literal + else if token[0] == '"' { + value = evaluate_string_literal(data); + } // Function-like + else if token == "sizeof" { + value = evaluate_sizeof(data); + } // Knowned literal + else if token in data.knownedLiterals { + value = evaluate_knowned_literal(data); + } // Custom expression + else if token in data.options.customExpressionHandlers { + value = data.options.customExpressionHandlers[token](data); + } + else { + print_warning("Unknown token ", token, " for expression evaluation."); + ok = false; + } + + return; +} + +evaluate_sizeof :: proc(data : ^ParserData) -> LiteralValue { + print_warning("Using 'sizeof()'. Currently not able to precompute that. Please check generated code."); + + check_and_eat_token(data, "sizeof"); + check_and_eat_token(data, "("); + for data.bytes[data.offset] != ')' { + data.offset += 1; + } + check_and_eat_token(data, ")"); + return 1; +} + +evaluate_parentheses :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { + check_and_eat_token(data, "("); + + // Cast to int (via "(int)" syntax) + token := peek_token(data); + if token == "int" { + check_and_eat_token(data, "int"); + check_and_eat_token(data, ")"); + value, ok = evaluate(data); + return; + } // Cast to enum value (via "(enum XXX)" syntax) + else if token == "enum" { + check_and_eat_token(data, "enum"); + eat_token(data); + check_and_eat_token(data, ")"); + value, ok = evaluate(data); + return; + } + + value, ok = evaluate(data); + check_and_eat_token(data, ")"); + return; +} + +evaluate_number_literal :: proc(data : ^ParserData, loc := #caller_location) -> (value : LiteralValue, ok : bool) { + token := parse_any(data); + + // Unary - before numbers + numberLitteral := token; + for token == "-" { + token = parse_any(data); + numberLitteral = tcat(numberLitteral, token); + } + token = numberLitteral; + + // Check if any point or scientific notation in number + foundPointOrExp := false; + for c in token { + if c == '.' || c == 'e' || c == 'E' { + foundPointOrExp = true; + break; + } + } + + isHexadecimal := len(token) >= 2 && token[:2] == "0x"; + + // Computing postfix + tokenLength := len(token); + l := tokenLength - 1; + for l > 0 { + c := token[l]; + if c >= '0' && c <= '9' { break; } + if isHexadecimal && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { break; } + l -= 1; + } + + postfix : string; + if l != tokenLength - 1 { + postfix = token[l+1:]; + token = token[:l+1]; + } + + if postfix != "" && (postfix[0] == 'u' || postfix[0] == 'U') { + print_warning("Found number litteral '", token, "' with unsigned postfix, we cast it to an int64 internally."); + } + + // Floating point + if !isHexadecimal && (foundPointOrExp || postfix == "f") { + value, ok = strconv.parse_f64(token); + } // Integer + else { + value, ok = strconv.parse_i64(token); + } + + if !ok { + print_error(data, loc, "Expected number litteral but got '", token, "'."); + } + + return value, ok; +} + +evaluate_string_literal :: proc(data : ^ParserData) -> string { + token := parse_any(data); + return token; +} + +evaluate_knowned_literal :: proc(data : ^ParserData) -> LiteralValue { + token := parse_any(data); + return data.knownedLiterals[token]; +} + +is_i64 :: proc(value : LiteralValue) -> (ok : bool) { + v : i64; + v, ok = value.(i64); + return ok; +} + +is_f64 :: proc(value : LiteralValue) -> (ok : bool) { + v : f64; + v, ok = value.(f64); + return ok; +} diff --git a/core/bindgen/c-parser-helpers.odin b/core/bindgen/c-parser-helpers.odin new file mode 100644 index 000000000..a99d83dd2 --- /dev/null +++ b/core/bindgen/c-parser-helpers.odin @@ -0,0 +1,267 @@ +package bindgen + +import "core:os" +import "core:fmt" +import "core:strings" +import "core:strconv" + +// Extract from start (included) to end (excluded) offsets +extract_string :: proc(data : ^ParserData, startOffset : u32, endOffset : u32) -> string { + return strings.string_from_ptr(&data.bytes[startOffset], cast(int) (endOffset - startOffset)); +} + +// Peek the end offset of the next token +peek_token_end :: proc(data : ^ParserData) -> u32 { + offset : u32; + + for true { + eat_whitespaces_and_comments(data); + if data.offset >= data.bytesLength { + return data.bytesLength; + } + offset = data.offset; + + // Identifier + if (data.bytes[offset] >= 'a' && data.bytes[offset] <= 'z') || + (data.bytes[offset] >= 'A' && data.bytes[offset] <= 'Z') || + (data.bytes[offset] == '_') { + offset += 1; + for (data.bytes[offset] >= 'a' && data.bytes[offset] <= 'z') || + (data.bytes[offset] >= 'A' && data.bytes[offset] <= 'Z') || + (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') || + (data.bytes[offset] == '_') { + offset += 1; + } + } + if offset != data.offset { + // Nothing to do: we found an identifier + } // Number literal + else if (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') { + offset += 1; + // Hexademical literal + if data.bytes[offset - 1] == '0' && data.bytes[offset] == 'x' { + offset += 1; + for (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') || + (data.bytes[offset] >= 'a' && data.bytes[offset] <= 'f') || + (data.bytes[offset] >= 'A' && data.bytes[offset] <= 'F') { + offset += 1; + } + } // Basic number literal + else { + for (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') || + data.bytes[offset] == '.' { + offset += 1; + } + + if (data.bytes[offset] == 'e' || data.bytes[offset] == 'E') { + offset += 1; + if data.bytes[offset] == '-' { + offset += 1; + } + } + + for (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') { + offset += 1; + } + } + + // Number suffix? + for (data.bytes[offset] == 'u' || data.bytes[offset] == 'U') || + (data.bytes[offset] == 'l' || data.bytes[offset] == 'L') || + (data.bytes[offset] == 'f') { + offset += 1; + } + } // String literal + else if data.bytes[offset] == '"' { + offset += 1; + for data.bytes[offset-1] == '\\' || data.bytes[offset] != '"' { + offset += 1; + } + offset += 1; + } // Possible shifts + else if data.bytes[offset] == '<' || data.bytes[offset] == '>' { + offset += 1; + if data.bytes[offset] == data.bytes[offset-1] { + offset += 1; + } + } // Single character + else { + offset += 1; + } + + token := extract_string(data, data.offset, offset); + + // Ignore __attribute__ + if token == "__attribute__" { + print_warning("__attribute__ is ignored."); + + for data.bytes[offset] != '(' { + offset += 1; + } + + parenthesesCount := 1; + for true { + offset += 1; + if data.bytes[offset] == '(' do parenthesesCount += 1; + else if data.bytes[offset] == ')' do parenthesesCount -= 1; + if parenthesesCount == 0 do break; + } + offset += 1; + + data.offset = offset; + } // Ignore certain keywords + else if (token == "inline" || token == "__inline" || token == "static" + || token == "restrict" || token == "__restrict" + || token == "volatile" + || token == "__extension__") { + data.offset = offset; + } // Ignore ignored tokens ;) + else { + for ignoredToken in data.options.ignoredTokens { + if token == ignoredToken { + data.offset = offset; + break; + } + } + } + + if data.offset != offset { + break; + } + } + + return offset; +} + +// Peek the next token (just eating whitespaces and comment) +peek_token :: proc(data : ^ParserData) -> string { + tokenEnd := peek_token_end(data); + if tokenEnd == data.bytesLength { + return "EOF"; + } + return extract_string(data, data.offset, tokenEnd); +} + +// Find the end of the define directive (understanding endline backslashes) +// @note Tricky cases like comments hiding a backslash effect are not handled. +peek_define_end :: proc(data : ^ParserData) -> u32 { + defineEndOffset := data.offset; + for data.bytes[defineEndOffset-1] == '\\' || data.bytes[defineEndOffset] != '\n' { + defineEndOffset += 1; + } + return defineEndOffset; +} + +eat_comment :: proc(data : ^ParserData) { + if data.offset >= data.bytesLength || data.bytes[data.offset] != '/' { + return; + } + + // Line comment + if data.bytes[data.offset + 1] == '/' { + eat_line(data); + } // Range comment + else if data.bytes[data.offset + 1] == '*' { + data.offset += 2; + for data.bytes[data.offset] != '*' || data.bytes[data.offset + 1] != '/' { + data.offset += 1; + } + data.offset += 2; + } +} + +// Eat whitespaces +eat_whitespaces :: proc(data : ^ParserData) { + // Effective whitespace + for data.offset < data.bytesLength && + (data.bytes[data.offset] == ' ' || data.bytes[data.offset] == '\t' || + data.bytes[data.offset] == '\r' || data.bytes[data.offset] == '\n') { + if data.bytes[data.offset] == '\n' && data.bytes[data.offset] != '\\' { + data.foundFullReturn = true; + } + data.offset += 1; + } +} + +// Removes whitespaces and comments +eat_whitespaces_and_comments :: proc(data : ^ParserData) { + startOffset : u32 = 0xFFFFFFFF; + for startOffset != data.offset { + startOffset = data.offset; + eat_whitespaces(data); + eat_comment(data); + } +} + +// Eat full line +eat_line :: proc(data : ^ParserData) { + for ; data.bytes[data.offset] != '\n'; data.offset += 1 { + } +} + +// Eat a line, and repeat if it ends with a backslash +eat_define_lines :: proc(data : ^ParserData) { + for data.bytes[data.offset-1] == '\\' || data.bytes[data.offset] != '\n' { + data.offset += 1; + } +} + +// Eat next token +eat_token :: proc(data : ^ParserData) { + data.offset = peek_token_end(data); +} + +// Eat next token +check_and_eat_token :: proc(data : ^ParserData, expectedToken : string, loc := #caller_location) { + token := peek_token(data); + if token != expectedToken { + print_error(data, loc, "Expected ", expectedToken, " but found ", token, "."); + } + data.offset += cast(u32) len(token); +} + +// Check whether the next token is outside #define range +is_define_end :: proc(data : ^ParserData) -> bool { + defineEnd := peek_define_end(data); + tokenEnd := peek_token_end(data); + + return (defineEnd < tokenEnd); +} + +// Check if the current #define is a macro definition +is_define_macro :: proc(data : ^ParserData) -> bool { + startOffset := data.offset; + defer data.offset = startOffset; + + token := parse_any(data); + if token != "(" do return false; + + // Find the other parenthesis + parenthesesCount := 1; + for parenthesesCount != 0 { + token = parse_any(data); + if token == "(" do parenthesesCount += 1; + else if token == ")" do parenthesesCount -= 1; + } + + // Its a macro if after the parentheses, it's not the end + return !is_define_end(data); +} + +// @note Very slow function to get line number, +// use only for errors. +// @todo Well, this does not seem to work properly, UTF-8 problem? +get_line_column :: proc(data : ^ParserData) -> (u32, u32) { + line : u32 = 1; + column : u32 = 0; + for i : u32 = 0; i < data.offset; i += 1 { + if data.bytes[i] == '\n' { + column = 0; + line += 1; + } + else { + column += 1; + } + } + return line, column; +} diff --git a/core/bindgen/c-parser-nodes.odin b/core/bindgen/c-parser-nodes.odin new file mode 100644 index 000000000..0620e0187 --- /dev/null +++ b/core/bindgen/c-parser-nodes.odin @@ -0,0 +1,132 @@ +package bindgen + +DefineNode :: struct { + name : string, + value : LiteralValue, +} + +StructDefinitionNode :: struct { + name : string, + members : [dynamic]StructOrUnionMember, + forwardDeclared : bool, +} + +UnionDefinitionNode :: struct { + name : string, + members : [dynamic]StructOrUnionMember, +} + +EnumDefinitionNode :: struct { + name : string, + members : [dynamic]EnumMember, +} + +FunctionDeclarationNode :: struct { + name : string, + returnType : Type, + parameters : [dynamic]FunctionParameter, +} + +TypedefNode :: struct { + name : string, + type : Type, +} + +Nodes :: struct { + defines : [dynamic]DefineNode, + enumDefinitions : [dynamic]EnumDefinitionNode, + unionDefinitions : [dynamic]UnionDefinitionNode, + structDefinitions : [dynamic]StructDefinitionNode, + functionDeclarations : [dynamic]FunctionDeclarationNode, + typedefs : [dynamic]TypedefNode, +} + +LiteralValue :: union { + i64, + f64, + string, +} + +// Type, might be an array +Type :: struct { + base : BaseType, + dimensions : [dynamic]u64, // Array dimensions +} + +BaseType :: union { + BuiltinType, + PointerType, + IdentifierType, + FunctionType, + FunctionPointerType, +} + +BuiltinType :: enum { + Unknown, + Void, + Int, + UInt, + LongInt, + ULongInt, + LongLongInt, + ULongLongInt, + ShortInt, + UShortInt, + Char, + SChar, + UChar, + Float, + Double, + LongDouble, + + // Not defined by C language but in + Int8, + Int16, + Int32, + Int64, + UInt8, + UInt16, + UInt32, + UInt64, + Size, + SSize, + PtrDiff, + UIntPtr, + IntPtr, +} + +PointerType :: struct { + type : ^Type, // Pointer is there to prevent definition cycle. Null means void. +} + +IdentifierType :: struct { + name : string, + anonymous : bool, // An anonymous identifier can be hard-given a name in some contexts. +} + +FunctionType :: struct { + returnType : ^Type, // Pointer is there to prevent definition cycle. Null means void. + parameters : [dynamic]FunctionParameter, +} + +FunctionPointerType :: struct { + name : string, + returnType : ^Type, // Pointer is there to prevent definition cycle. Null means void. + parameters : [dynamic]FunctionParameter, +} + +EnumMember :: struct { + name : string, + value : i64, + hasValue : bool, +} + +StructOrUnionMember :: struct { + name : string, + type : Type, +} + +FunctionParameter :: struct { + name : string, + type : Type, +} diff --git a/core/bindgen/c-parser.odin b/core/bindgen/c-parser.odin new file mode 100644 index 000000000..c3ef4937f --- /dev/null +++ b/core/bindgen/c-parser.odin @@ -0,0 +1,840 @@ +package bindgen + +import "core:os" +import "core:fmt" +import "core:strings" +import "core:strconv" + +// Global counters +anonymousStructCount := 0; +anonymousUnionCount := 0; +anonymousEnumCount := 0; + +knownTypeAliases : map[string]Type; + +CustomHandler :: proc(data : ^ParserData); +CustomExpressionHandler :: proc(data : ^ParserData) -> LiteralValue; + +ParserOptions :: struct { + ignoredTokens : []string, + + // Handlers + customHandlers : map[string]CustomHandler, + customExpressionHandlers : map[string]CustomExpressionHandler, +} + +ParserData :: struct { + bytes : []u8, + bytesLength : u32, + offset : u32, + + // References + nodes : Nodes, + options : ^ParserOptions, + + // Knowned values + knownedLiterals : map[string]LiteralValue, + + // Whether we have eaten a '\n' character that has no backslash just before + foundFullReturn : bool, +} + +is_identifier :: proc(token : string) -> bool { + return (token[0] >= 'a' && token[0] <= 'z') || + (token[0] >= 'A' && token[0] <= 'Z') || + (token[0] == '_'); +} + +parse :: proc(bytes : []u8, options : ParserOptions, loc := #caller_location) -> Nodes { + options := options; + + data : ParserData; + data.bytes = bytes; + data.bytesLength = cast(u32) len(bytes); + data.options = &options; + + for data.offset = 0; data.offset < data.bytesLength; { + token := peek_token(&data); + if data.offset == data.bytesLength do break; + + if token in options.customHandlers { + options.customHandlers[token](&data); + } + else if token == "{" || token == "}" || token == ";" { + eat_token(&data); + } + else if token == "extern" { + check_and_eat_token(&data, "extern"); + } + else if token == "\"C\"" { + check_and_eat_token(&data, "\"C\""); + } + else if token == "#" { + parse_directive(&data); + } + else if token == "typedef" { + parse_typedef(&data); + } + else if is_identifier(token) { + parse_variable_or_function_declaration(&data); + } + else { + print_error(&data, loc, "Unexpected token: ", token, "."); + return data.nodes; + } + } + + return data.nodes; +} + +parse_any :: proc(data : ^ParserData) -> string { + offset := peek_token_end(data); + identifier := extract_string(data, data.offset, offset); + data.offset = offset; + return identifier; +} + +parse_identifier :: proc(data : ^ParserData, loc := #caller_location) -> string { + identifier := parse_any(data); + + if (identifier[0] < 'a' || identifier[0] > 'z') && + (identifier[0] < 'A' || identifier[0] > 'Z') && + (identifier[0] != '_') { + print_error(data, loc, "Expected identifier but found ", identifier, "."); + } + + return identifier; +} + +parse_type_dimensions :: proc(data : ^ParserData, type : ^Type) { + token := peek_token(data); + for token == "[" { + eat_token(data); + token = peek_token(data); + if token == "]" { + pointerType : PointerType; + pointerType.type = new(Type); + pointerType.type^ = type^; // Copy + type.base = pointerType; + delete(type.dimensions); + } else { + dimension := evaluate_i64(data); + append(&type.dimensions, cast(u64) dimension); + } + check_and_eat_token(data, "]"); + token = peek_token(data); + } +} + +// This will parse anything that look like a type: +// Builtin: char/int/float/... +// Struct-like: struct A/struct { ... }/enum E +// Function pointer: void (*f)(...) +// +// Definition permitted: If a struct-like definition is found, it will generate +// the according Node and return a corresponding type. +parse_type :: proc(data : ^ParserData, definitionPermitted := false) -> Type { + type : Type; + + // Eat qualifiers + token := peek_token(data); + if token == "const" { + eat_token(data); + token = peek_token(data); + } + + // Parse main type + if token == "struct" { + type.base = parse_struct_type(data, definitionPermitted); + } + else if token == "union" { + type.base = parse_union_type(data); + } + else if token == "enum" { + type.base = parse_enum_type(data); + } + else { + // Test builtin type + type.base = parse_builtin_type(data); + if type.base.(BuiltinType) == BuiltinType.Unknown { + // Basic identifier type + identifierType : IdentifierType; + identifierType.name = parse_identifier(data); + type.base = identifierType; + } + } + + // Eat qualifiers + token = peek_token(data); + if token == "const" { + eat_token(data); + token = peek_token(data); + } + + // Check if pointer + for token == "*" { + check_and_eat_token(data, "*"); + token = peek_token(data); + + pointerType : PointerType; + pointerType.type = new(Type); + pointerType.type^ = type; // Copy + + type.base = pointerType; + + // Eat qualifiers + if token == "const" { + eat_token(data); + token = peek_token(data); + } + } + + // Parse array dimensions if any. + parse_type_dimensions(data, &type); + + // ----- Function pointer type + + if token == "(" { + check_and_eat_token(data, "("); + check_and_eat_token(data, "*"); + + functionPointerType : FunctionPointerType; + functionPointerType.returnType = new(Type); + functionPointerType.returnType^ = type; + functionPointerType.name = parse_identifier(data); + + check_and_eat_token(data, ")"); + parse_function_parameters(data, &functionPointerType.parameters); + + type.base = functionPointerType; + } + + return type; +} + +parse_builtin_type :: proc(data : ^ParserData) -> BuiltinType { + previousBuiltinType := BuiltinType.Unknown; + intFound := false; + shortFound := false; + signedFound := false; + unsignedFound := false; + longCount := 0; + + for true { + token := peek_token(data); + + // Attribute + attributeFound := true; + if token == "long" do longCount += 1; + else if token == "short" do shortFound = true; + else if token == "unsigned" do unsignedFound = true; + else if token == "signed" do signedFound = true; + else do attributeFound = false; + if attributeFound { eat_token(data); continue; } + + // Known type alias + if token in knownTypeAliases { + builtinType, ok := knownTypeAliases[token].base.(BuiltinType); + if ok { + eat_token(data); + previousBuiltinType = builtinType; + } + break; + } + + // Classic type and standard types + if token == "void" { eat_token(data); return BuiltinType.Void; } + else if token == "int" { + eat_token(data); + intFound = true; + } + else if token == "float" { eat_token(data); return BuiltinType.Float; } + else if token == "double" { + eat_token(data); + if longCount == 0 do return BuiltinType.Double; + else do return BuiltinType.LongDouble; + } + else if token == "char" { + eat_token(data); + if signedFound do return BuiltinType.SChar; + else if unsignedFound do return BuiltinType.UChar; + else do return BuiltinType.Char; + } + else if token == "__int8" { + // @note :MicrosoftDumminess __intX are Microsoft's fixed-size integers + // https://docs.microsoft.com/fr-fr/cpp/cpp/int8-int16-int32-int64 + // and for unsigned version, they prefixed it with "unsigned"... + eat_token(data); + if unsignedFound do return BuiltinType.UInt8; + else do return BuiltinType.Int8; + } + else if token == "__int16" { + eat_token(data); + if unsignedFound do return BuiltinType.UInt16; + else do return BuiltinType.Int16; + } + else if token == "__int32" { + eat_token(data); + if unsignedFound do return BuiltinType.UInt32; + else do return BuiltinType.Int32; + } + else if token == "__int64" { + eat_token(data); + if unsignedFound do return BuiltinType.UInt64; + else do return BuiltinType.Int64; + } + else if token == "int8_t" { eat_token(data); return BuiltinType.Int8; } + else if token == "int16_t" { eat_token(data); return BuiltinType.Int16; } + else if token == "int32_t" { eat_token(data); return BuiltinType.Int32; } + else if token == "int64_t" { eat_token(data); return BuiltinType.Int64; } + else if token == "uint8_t" { eat_token(data); return BuiltinType.UInt8; } + else if token == "uint16_t" { eat_token(data); return BuiltinType.UInt16; } + else if token == "uint32_t" { eat_token(data); return BuiltinType.UInt32; } + else if token == "uint64_t" { eat_token(data); return BuiltinType.UInt64; } + else if token == "size_t" { eat_token(data); return BuiltinType.Size; } + else if token == "ssize_t" { eat_token(data); return BuiltinType.SSize; } + else if token == "ptrdiff_t" { eat_token(data); return BuiltinType.PtrDiff; } + else if token == "uintptr_t" { eat_token(data); return BuiltinType.UIntPtr; } + else if token == "intptr_t" { eat_token(data); return BuiltinType.IntPtr; } + + break; + } + + // Adapt previous builtin type + if previousBuiltinType == BuiltinType.ShortInt { + shortFound = true; + } + else if previousBuiltinType == BuiltinType.Int { + intFound = true; + } + else if previousBuiltinType == BuiltinType.LongInt { + longCount += 1; + } + else if previousBuiltinType == BuiltinType.LongLongInt { + longCount += 2; + } + else if previousBuiltinType == BuiltinType.UShortInt { + unsignedFound = true; + shortFound = true; + } + else if previousBuiltinType == BuiltinType.UInt { + unsignedFound = true; + } + else if previousBuiltinType == BuiltinType.ULongInt { + unsignedFound = true; + longCount += 1; + } + else if previousBuiltinType == BuiltinType.ULongLongInt { + unsignedFound = true; + longCount += 2; + } + else if (previousBuiltinType != BuiltinType.Unknown) { + return previousBuiltinType; // float, void, etc. + } + + // Implicit and explicit int + if intFound || shortFound || unsignedFound || signedFound || longCount > 0 { + if unsignedFound { + if shortFound do return BuiltinType.UShortInt; + if longCount == 0 do return BuiltinType.UInt; + if longCount == 1 do return BuiltinType.ULongInt; + if longCount == 2 do return BuiltinType.ULongLongInt; + } else { + if shortFound do return BuiltinType.ShortInt; + if longCount == 0 do return BuiltinType.Int; + if longCount == 1 do return BuiltinType.LongInt; + if longCount == 2 do return BuiltinType.LongLongInt; + } + } + + return BuiltinType.Unknown; +} + +parse_struct_type :: proc(data : ^ParserData, definitionPermitted : bool) -> IdentifierType { + check_and_eat_token(data, "struct"); + + type : IdentifierType; + token := peek_token(data); + + if !definitionPermitted || token != "{" { + type.name = parse_identifier(data); + token = peek_token(data); + } else { + type.name = tcat("AnonymousStruct", anonymousStructCount); + type.anonymous = true; + anonymousStructCount += 1; + } + + if token == "{" { + node := parse_struct_definition(data); + node.name = type.name; + } else if definitionPermitted { + // @note Whatever happens, we create a definition of the struct, + // as it might be used to forward declare it and then use it only with a pointer. + // This for instance the pattern for xcb_connection_t which definition + // is never known from user API. + node : StructDefinitionNode; + node.forwardDeclared = false; + node.name = type.name; + append(&data.nodes.structDefinitions, node); + } + + return type; +} + +parse_union_type :: proc(data : ^ParserData) -> IdentifierType { + check_and_eat_token(data, "union"); + + type : IdentifierType; + token := peek_token(data); + + if token != "{" { + type.name = parse_identifier(data); + token = peek_token(data); + } else { + type.name = tcat("AnonymousUnion", anonymousUnionCount); + type.anonymous = true; + anonymousUnionCount += 1; + } + + if token == "{" { + node := parse_union_definition(data); + node.name = type.name; + } + + return type; +} + +parse_enum_type :: proc(data : ^ParserData) -> IdentifierType { + check_and_eat_token(data, "enum"); + + type : IdentifierType; + token := peek_token(data); + + if token != "{" { + type.name = parse_identifier(data); + token = peek_token(data); + } else { + type.name = tcat("AnonymousEnum", anonymousEnumCount); + type.anonymous = true; + anonymousEnumCount += 1; + } + + if token == "{" { + node := parse_enum_definition(data); + node.name = type.name; + } + + return type; +} + +/** + * We only care about defines of some value + */ +parse_directive :: proc(data : ^ParserData) { + check_and_eat_token(data, "#"); + + token := peek_token(data); + if token == "define" { + parse_define(data); + } // We ignore all other directives + else { + eat_line(data); + } +} + +parse_define :: proc(data : ^ParserData) { + check_and_eat_token(data, "define"); + data.foundFullReturn = false; + + node : DefineNode; + node.name = parse_identifier(data); + + // Does it look like end? It might be a #define with no expression + if is_define_end(data) { + node.value = 1; + append(&data.nodes.defines, node); + data.knownedLiterals[node.name] = node.value; + } // Macros are ignored + else if is_define_macro(data) { + print_warning("Ignoring define macro for ", node.name, "."); + } + else { + literalValue, ok := evaluate(data); + if ok { + node.value = literalValue; + append(&data.nodes.defines, node); + data.knownedLiterals[node.name] = node.value; + } + else { + print_warning("Ignoring define expression for ", node.name, "."); + } + } + + // Evaluating the expression, we might have already eaten a full return, + // if so, do nothing. + if !data.foundFullReturn { + eat_define_lines(data); + } +} + +// @fixme Move +change_anonymous_node_name :: proc (data : ^ParserData, oldName : string, newName : string) -> bool { + for i := 0; i < len(data.nodes.structDefinitions); i += 1 { + if data.nodes.structDefinitions[i].name == oldName { + data.nodes.structDefinitions[i].name = newName; + return true; + } + } + + for i := 0; i < len(data.nodes.enumDefinitions); i += 1 { + if data.nodes.enumDefinitions[i].name == oldName { + data.nodes.enumDefinitions[i].name = newName; + return true; + } + } + + for i := 0; i < len(data.nodes.unionDefinitions); i += 1 { + if data.nodes.unionDefinitions[i].name == oldName { + data.nodes.unionDefinitions[i].name = newName; + return true; + } + } + + return false; +} + +/** + * Type aliasing. + * typedef ; + */ +parse_typedef :: proc(data : ^ParserData) { + check_and_eat_token(data, "typedef"); + + // @note Struct-like definitions (and such) + // are generated within type parsing. + // + // So that typedef struct { int foo; }* Ap; is valid. + + // Parsing type + node : TypedefNode; + node.type = parse_type(data, true); + + if sourceType, ok := node.type.base.(FunctionPointerType); ok { + node.name = sourceType.name; + } else { + node.name = parse_identifier(data); + } + + // Checking if function type + token := peek_token(data); + if token == "(" { + functionType : FunctionType; + functionType.returnType = new(Type); + functionType.returnType^ = node.type; + + parse_function_parameters(data, &functionType.parameters); + + node.type.base = functionType; + } + + // Checking if array + parse_type_dimensions(data, &node.type); + + // If the underlying type is anonymous, + // we just affect it the name. + addTypedefNode := true; + if identifierType, ok := node.type.base.(IdentifierType); ok { + if identifierType.anonymous { + addTypedefNode = !change_anonymous_node_name(data, identifierType.name, node.name); + } + } + + if addTypedefNode { + knownTypeAliases[node.name] = node.type; + append(&data.nodes.typedefs, node); + } + + check_and_eat_token(data, ";"); + + // @note Commented tool for debug + // fmt.println("Typedef: ", node.type, node.name); +} + +parse_struct_definition :: proc(data : ^ParserData) -> ^StructDefinitionNode { + node : StructDefinitionNode; + node.forwardDeclared = false; + parse_struct_or_union_members(data, &node.members); + + append(&data.nodes.structDefinitions, node); + return &data.nodes.structDefinitions[len(data.nodes.structDefinitions) - 1]; +} + +parse_union_definition :: proc(data : ^ParserData) -> ^UnionDefinitionNode { + node : UnionDefinitionNode; + parse_struct_or_union_members(data, &node.members); + + append(&data.nodes.unionDefinitions, node); + return &data.nodes.unionDefinitions[len(data.nodes.unionDefinitions) - 1]; +} + +parse_enum_definition :: proc(data : ^ParserData) -> ^EnumDefinitionNode { + node : EnumDefinitionNode; + parse_enum_members(data, &node.members); + + append(&data.nodes.enumDefinitions, node); + return &data.nodes.enumDefinitions[len(data.nodes.enumDefinitions) - 1]; +} + +/** + * { + * = , + * , + * } + */ +parse_enum_members :: proc(data : ^ParserData, members : ^[dynamic]EnumMember) { + check_and_eat_token(data, "{"); + + nextMemberValue : i64 = 0; + token := peek_token(data); + for token != "}" { + member : EnumMember; + member.name = parse_identifier(data); + member.hasValue = false; + + token = peek_token(data); + if token == "=" { + check_and_eat_token(data, "="); + + member.hasValue = true; + member.value = evaluate_i64(data); + nextMemberValue = member.value; + token = peek_token(data); + } else { + member.value = nextMemberValue; + } + + data.knownedLiterals[member.name] = member.value; + nextMemberValue += 1; + + // Eat until end, as this might be a complex expression that we couldn't understand + if token != "," && token != "}" { + print_warning("Parser cannot understand fully the expression of enum member ", member.name, "."); + for token != "," && token != "}" { + eat_token(data); + token = peek_token(data); + } + } + if token == "," { + check_and_eat_token(data, ","); + token = peek_token(data); + } + + append(members, member); + } + + check_and_eat_token(data, "}"); +} + +/** + * { + * ; + * , ; + * []; + * } + */ +parse_struct_or_union_members :: proc(data : ^ParserData, structOrUnionMembers : ^[dynamic]StructOrUnionMember) { + check_and_eat_token(data, "{"); + + // To ensure unique id + unamedCount := 0; + + token := peek_token(data); + for token != "}" { + member : StructOrUnionMember; + member.type = parse_type(data, true); + + for true { + // In the case of function pointer types, the name has been parsed + // during type inspection. + if type, ok := member.type.base.(FunctionPointerType); ok { + member.name = type.name; + } + else { + // Unamed (struct or union) + token = peek_token(data); + if !is_identifier(token) { + member.name = tcat("unamed", unamedCount); + unamedCount += 1; + } + else { + member.name = parse_identifier(data); + } + } + + parse_type_dimensions(data, &member.type); + + token = peek_token(data); + if token == ":" { + check_and_eat_token(data, ":"); + print_warning("Found bitfield in struct, which is not handled correctly."); + evaluate_i64(data); + token = peek_token(data); + } + + append(structOrUnionMembers, member); + + // Multiple declarations on one line + if token == "," { + check_and_eat_token(data, ","); + continue; + } + + break; + } + + check_and_eat_token(data, ";"); + token = peek_token(data); + } + + check_and_eat_token(data, "}"); +} + +parse_variable_or_function_declaration :: proc(data : ^ParserData) { + type := parse_type(data, true); + + // If it's just a type, it might be a struct definition + token := peek_token(data); + if token == ";" { + check_and_eat_token(data, ";"); + return; + } + + // Eat array declaration if any + // @fixme The return type of a function declaration will be wrong! + for data.bytes[data.offset] == '[' { + for data.bytes[data.offset] != ']' { + data.offset += 1; + } + data.offset += 1; + } + + name := parse_identifier(data); + + token = peek_token(data); + if token == "(" { + functionDeclarationNode := parse_function_declaration(data); + functionDeclarationNode.returnType = type; + functionDeclarationNode.name = name; + return; + } else if token == "[" { + // Eat whole array declaration + for data.bytes[data.offset] == '[' { + for data.bytes[data.offset] != ']' { + data.offset += 1; + } + data.offset += 1; + } + } + + // Global variable declaration (with possible multiple declarations) + token = peek_token(data); + + for true { + if token == "," { + print_warning("Found global variable declaration '", name, "', we won't generated any binding for it."); + check_and_eat_token(data, ","); + + name = parse_identifier(data); + token = peek_token(data); + continue; + } + else if token == ";" { + if name != "" { + print_warning("Found global variable declaration '", name, "', we won't generated any binding for it."); + } + check_and_eat_token(data, ";"); + break; + } + + // Global variable assignment, considered as constant define. + node : DefineNode; + + check_and_eat_token(data, "="); + literalValue, ok := evaluate(data); + if ok { + node.name = name; + node.value = literalValue; + append(&data.nodes.defines, node); + } + else { + print_warning("Ignoring global variable expression for '", name, "'."); + } + + name = ""; + token = peek_token(data); + } +} + +parse_function_declaration :: proc(data : ^ParserData) -> ^FunctionDeclarationNode { + node : FunctionDeclarationNode; + + parse_function_parameters(data, &node.parameters); + + // Function definition? Ignore it. + token := peek_token(data); + if token == "{" { + bracesCount := 1; + for true { + data.offset += 1; + if data.bytes[data.offset] == '{' do bracesCount += 1; + else if data.bytes[data.offset] == '}' do bracesCount -= 1; + if bracesCount == 0 do break; + } + data.offset += 1; + } // Function declaration + else { + check_and_eat_token(data, ";"); + } + + append(&data.nodes.functionDeclarations, node); + return &data.nodes.functionDeclarations[len(data.nodes.functionDeclarations) - 1]; +} + +parse_function_parameters :: proc(data : ^ParserData, parameters : ^[dynamic]FunctionParameter) { + check_and_eat_token(data, "("); + + token := peek_token(data); + for token != ")" { + parameter : FunctionParameter; + + token = peek_token(data); + if token == "." { + print_warning("A function accepts variadic arguments, this is currently not handled within generated code."); + + check_and_eat_token(data, "."); + check_and_eat_token(data, "."); + check_and_eat_token(data, "."); + break; + } else { + parameter.type = parse_type(data); + } + + // Check if named parameter + token = peek_token(data); + if token != ")" && token != "," { + parameter.name = parse_identifier(data); + parse_type_dimensions(data, ¶meter.type); + token = peek_token(data); + } + + if token == "," { + eat_token(data); + token = peek_token(data); + } + + append(parameters, parameter); + } + + check_and_eat_token(data, ")"); +} diff --git a/core/bindgen/errors.odin b/core/bindgen/errors.odin new file mode 100644 index 000000000..9564c5244 --- /dev/null +++ b/core/bindgen/errors.odin @@ -0,0 +1,44 @@ +package bindgen + +import "core:fmt" +import "core:os" + +seenWarnings : map[string]bool; + +print_warning :: proc(args : ..any) { + message := tcat(..args); + + if !seenWarnings[message] { + fmt.eprint("[bindgen] Warning: ", message, "\n"); + seenWarnings[message] = true; + } +} + +print_error :: proc(data : ^ParserData, loc := #caller_location, args : ..any) { + message := tcat(..args); + + min : u32 = 0; + for i := data.offset - 1; i > 0; i -= 1 { + if data.bytes[i] == '\n' { + min = i + 1; + break; + } + } + + max := min + 200; + for i := min + 1; i < max; i += 1 { + if data.bytes[i] == '\n' { + max = i; + break; + } + } + + line, _ := get_line_column(data); + + fmt.eprint("[bindgen] Error: ", message, "\n"); + fmt.eprint("[bindgen] ... from ", loc.procedure, "\n"); + fmt.eprint("[bindgen] ... at line ", line, " within this context:\n"); + fmt.eprint("> ", extract_string(data, min, max), "\n"); + + os.exit(1); +} diff --git a/core/bindgen/generator-clean.odin b/core/bindgen/generator-clean.odin new file mode 100644 index 000000000..8dd837b10 --- /dev/null +++ b/core/bindgen/generator-clean.odin @@ -0,0 +1,284 @@ +package bindgen + +import "core:fmt" + +// Prevent keywords clashes and other tricky cases +clean_identifier :: proc(name : string) -> string { + name := name; + + if name == "" { + return name; + } + + // Starting with _? Try removing that. + for true { + if name[0] == '_' { + name = name[1:]; + } + else { + break; + } + } + + // Number + if name[0] >= '0' && name[0] <= '9' { + return tcat("_", name); + } // Keywords clash + else if name == "map" || name == "proc" || name == "opaque" || name == "in" { + return tcat("_", name); + } // Jai keywords clash + else if name == "context" || + name == "float32" || name == "float64" || + name == "s8" || name == "s16" || name == "s32" || name == "s64" || + name == "u8" || name == "u16" || name == "u32" || name == "u64" { + return tcat("_", name); + } + + return name; +} + +clean_variable_name :: proc(name : string, options : ^GeneratorOptions) -> string { + name := name; + name = change_case(name, options.variableCase); + return clean_identifier(name); +} + +clean_pseudo_type_name :: proc(structName : string, options : ^GeneratorOptions) -> string { + structName := structName; + structName = remove_postfixes(structName, options.pseudoTypePostfixes, options.pseudoTypeTransparentPostfixes); + structName = remove_prefixes(structName, options.pseudoTypePrefixes, options.pseudoTypeTransparentPrefixes); + structName = change_case(structName, options.pseudoTypeCase); + return structName; +} + +// Clean up the enum name so that it can be used to remove the prefix from enum values. +clean_enum_name_for_prefix_removal :: proc(enumName : string, options : ^GeneratorOptions) -> (string, [dynamic]string) { + enumName := enumName; + + if !options.enumValueNameRemove { + return enumName, nil; + } + + // Remove postfix and use same case convention as the enum values + removedPostfixes : [dynamic]string; + enumName, removedPostfixes = remove_postfixes_with_removed(enumName, options.enumValueNameRemovePostfixes); + enumName = change_case(enumName, options.enumValueCase); + return enumName, removedPostfixes; +} + +clean_enum_value_name :: proc(valueName : string, enumName : string, postfixes : []string, options : ^GeneratorOptions) -> string { + valueName := valueName; + + valueName = remove_prefixes(valueName, options.enumValuePrefixes, options.enumValueTransparentPrefixes); + valueName = remove_postfixes(valueName, postfixes, options.enumValueTransparentPostfixes); + + if options.enumValueNameRemove { + valueName = remove_prefixes(valueName, []string{enumName}); + } + + valueName = change_case(valueName, options.enumValueCase); + + return clean_identifier(valueName); +} + +clean_function_name :: proc(functionName : string, options : ^GeneratorOptions) -> string { + functionName := functionName; + functionName = remove_prefixes(functionName, options.functionPrefixes, options.functionTransparentPrefixes); + functionName = remove_postfixes(functionName, options.definePostfixes, options.defineTransparentPostfixes); + functionName = change_case(functionName, options.functionCase); + return functionName; +} + +clean_define_name :: proc(defineName : string, options : ^GeneratorOptions) -> string { + defineName := defineName; + defineName = remove_prefixes(defineName, options.definePrefixes, options.defineTransparentPrefixes); + defineName = remove_postfixes(defineName, options.definePostfixes, options.defineTransparentPostfixes); + defineName = change_case(defineName, options.defineCase); + return defineName; +} + +// Convert to Odin's types +clean_type :: proc(data : ^GeneratorData, type : Type, baseTab : string = "", explicitSharpType := true) -> string { + output := ""; + + for dimension in type.dimensions { + output = tcat(output, "[", dimension, "]"); + } + output = tcat(output, clean_base_type(data, type.base, baseTab, explicitSharpType)); + + return output; +} + +clean_base_type :: proc(data : ^GeneratorData, baseType : BaseType, baseTab : string = "", explicitSharpType := true) -> string { + options := data.options; + + if _type, ok := baseType.(BuiltinType); ok { + if _type == BuiltinType.Void do return options.mode == "jai" ? "void" : ""; + else if _type == BuiltinType.Int do return options.mode == "jai" ? "s64" : "_c.int"; + else if _type == BuiltinType.UInt do return options.mode == "jai" ? "u64" :"_c.uint"; + else if _type == BuiltinType.LongInt do return options.mode == "jai" ? "s64" :"_c.long"; + else if _type == BuiltinType.ULongInt do return options.mode == "jai" ? "u64" :"_c.ulong"; + else if _type == BuiltinType.LongLongInt do return options.mode == "jai" ? "s64" :"_c.longlong"; + else if _type == BuiltinType.ULongLongInt do return options.mode == "jai" ? "u64" :"_c.ulonglong"; + else if _type == BuiltinType.ShortInt do return options.mode == "jai" ? "s16" :"_c.short"; + else if _type == BuiltinType.UShortInt do return options.mode == "jai" ? "u16" :"_c.ushort"; + else if _type == BuiltinType.Char do return options.mode == "jai" ? "u8" :"_c.char"; + else if _type == BuiltinType.SChar do return options.mode == "jai" ? "s8" :"_c.schar"; + else if _type == BuiltinType.UChar do return options.mode == "jai" ? "u8" :"_c.uchar"; + else if _type == BuiltinType.Float do return options.mode == "jai" ? "float32" :"_c.float"; + else if _type == BuiltinType.Double do return options.mode == "jai" ? "float64" :"_c.double"; + else if _type == BuiltinType.LongDouble { + print_warning("Found long double which is currently not supported. Fallback to double in generated code."); + return options.mode == "jai" ? "double" :"_c.double"; + } + else if _type == BuiltinType.Int8 do return options.mode == "jai" ? "s8" :"i8"; + else if _type == BuiltinType.Int16 do return options.mode == "jai" ? "s16" :"i16"; + else if _type == BuiltinType.Int32 do return options.mode == "jai" ? "s32" :"i32"; + else if _type == BuiltinType.Int64 do return options.mode == "jai" ? "s64" :"i64"; + else if _type == BuiltinType.UInt8 do return options.mode == "jai" ? "u8" :"u8"; + else if _type == BuiltinType.UInt16 do return options.mode == "jai" ? "u16" :"u16"; + else if _type == BuiltinType.UInt32 do return options.mode == "jai" ? "u32" :"u32"; + else if _type == BuiltinType.UInt64 do return options.mode == "jai" ? "u64" :"u64"; + else if _type == BuiltinType.Size do return options.mode == "jai" ? "u64" :"_c.size_t"; + else if _type == BuiltinType.SSize do return options.mode == "jai" ? "u64" :"_c.ssize_t"; + else if _type == BuiltinType.PtrDiff do return options.mode == "jai" ? "s64" :"_c.ptrdiff_t"; + else if _type == BuiltinType.UIntPtr do return options.mode == "jai" ? "u64" :"_c.uintptr_t"; + else if _type == BuiltinType.IntPtr do return options.mode == "jai" ? "s64" :"_c.intptr_t"; + } + else if _type, ok := baseType.(PointerType); ok { + if options.mode == "jai" { + // Hide pointers to types that were not declared. + if !is_known_base_type(data, _type.type.base) { + print_warning("*", _type.type.base.(IdentifierType).name, " replaced by *void as the pointed type is unknown."); + return "*void"; + } + } else { + if __type, ok := _type.type.base.(BuiltinType); ok { + if __type == BuiltinType.Void do return "rawptr"; + else if __type == BuiltinType.Char do return "cstring"; + } + } + name := clean_type(data, _type.type^, baseTab); + return tcat(options.mode == "jai" ? "*" :"^", name); + } + else if _type, ok := baseType.(IdentifierType); ok { + return clean_pseudo_type_name(_type.name, options); + } + else if _type, ok := baseType.(FunctionType); ok { + output : string; + if explicitSharpType { + output = "#type "; + } + output = tcat(output, options.mode == "jai" ? "(" :"proc("); + parameters := clean_function_parameters(data, _type.parameters, baseTab); + output = tcat(output, parameters, ")"); + + returnType := clean_type(data, _type.returnType^); + if len(returnType) > 0 && returnType != "void" { + output = tcat(output, " -> ", returnType); + } + return output; + } + else if _type, ok := baseType.(FunctionPointerType); ok { + output : string; + if explicitSharpType { + output = "#type "; + } + output = tcat(output, options.mode == "jai" ? "(" :"proc("); + parameters := clean_function_parameters(data, _type.parameters, baseTab); + output = tcat(output, parameters, ")"); + + returnType := clean_type(data, _type.returnType^); + if len(returnType) > 0 && returnType != "void" { + output = tcat(output, " -> ", returnType); + } + + if options.mode == "jai" { + output = tcat(output, " #foreign"); + } + return output; + } + + return ""; +} + +clean_function_parameters :: proc(data : ^GeneratorData, parameters : [dynamic]FunctionParameter, baseTab : string) -> string { + output := ""; + options := data.options; + + // Special case: function(void) does not really have a parameter + if len(parameters) == 1 { + if _type, ok := parameters[0].type.base.(BuiltinType); ok { + if _type == BuiltinType.Void { + return ""; + } + } + } + + tab := ""; + if options.mode == "jai" { // @note :OdinCodingStyle Odin forces a coding style, now. Ugh. + if (len(parameters) > 1) { + output = tcat(output, "\n"); + tab = tcat(baseTab, " "); + } + } + + unamedParametersCount := 0; + for parameter, i in parameters { + type := clean_type(data, parameter.type); + + name : string; + if len(parameter.name) != 0 { + name = clean_variable_name(parameter.name, options); + } else { + name = tcat("unamed", unamedParametersCount); + unamedParametersCount += 1; + } + + output = tcat(output, tab, name, " : ", type); + + if i != len(parameters) - 1 { + if options.mode == "jai" { // @note :OdinCodingStyle + output = tcat(output, ",\n"); + } else { + output = tcat(output, ", "); + } + } + } + + if (len(parameters) > 1) { + if options.mode == "jai" { // @note :OdinCodingStyle + output = tcat(output, "\n", baseTab); + } + } + + return output; +} + +is_known_base_type :: proc(data : ^GeneratorData, baseType : BaseType) -> bool { + if _type, ok := baseType.(IdentifierType); ok { + for it in data.nodes.typedefs { + if _type.name == it.name { + return true; + } + } + for it in data.nodes.structDefinitions { + if _type.name == it.name { + return true; + } + } + for it in data.nodes.enumDefinitions { + if _type.name == it.name { + return true; + } + } + for it in data.nodes.unionDefinitions { + if _type.name == it.name { + return true; + } + } + return false; + } + + return true; +} diff --git a/core/bindgen/generator-export.odin b/core/bindgen/generator-export.odin new file mode 100644 index 000000000..a04113ed9 --- /dev/null +++ b/core/bindgen/generator-export.odin @@ -0,0 +1,166 @@ +package bindgen + +import "core:os" +import "core:fmt" + +export_defines :: proc(data : ^GeneratorData) { + for node in data.nodes.defines { + defineName := clean_define_name(node.name, data.options); + + // @fixme fprint of float numbers are pretty badly handled, + // just has a 10^-3 precision. + fcat(data.handle, defineName, " :: ", node.value, ";\n"); + } + fcat(data.handle, "\n"); +} + +export_typedefs :: proc(data : ^GeneratorData) { + for node in data.nodes.typedefs { + name := clean_pseudo_type_name(node.name, data.options); + type := clean_type(data, node.type, "", true); + if name == type do continue; + fcat(data.handle, name, " :: ", type, ";\n"); + } + fcat(data.handle, "\n"); +} + +export_enums :: proc(data : ^GeneratorData) { + for node in data.nodes.enumDefinitions { + enumName := clean_pseudo_type_name(node.name, data.options); + + if data.options.mode == "jai" { + consideredFlags := false; + for postfix in data.options.enumConsideredFlagsPostfixes { + if ends_with(node.name, postfix) { + consideredFlags = true; + break; + } + } + + if consideredFlags { + fcat(data.handle, enumName, " :: enum_flags u32 {"); + } else { + fcat(data.handle, enumName, " :: enum s32 {"); + } + } else { + fcat(data.handle, enumName, " :: enum i32 {"); + } + + postfixes : [dynamic]string; + enumName, postfixes = clean_enum_name_for_prefix_removal(enumName, data.options); + + // Changing the case of postfixes to the enum value one, + // so that they can be removed. + enumValueCase := find_case(node.members[0].name); + for postfix, i in postfixes { + postfixes[i] = change_case(postfix, enumValueCase); + } + + // And changing the case of enumName to the enum value one + enumName = change_case(enumName, enumValueCase); + + // Merging enum value postfixes with postfixes that have been removed from the enum name. + for postfix in data.options.enumValuePostfixes { + append(&postfixes, postfix); + } + + export_enum_members(data, node.members, enumName, postfixes[:]); + fcat(data.handle, data.options.mode == "jai" ? "}\n" : "};\n"); + fcat(data.handle, "\n"); + } +} + +export_structs :: proc(data : ^GeneratorData) { + for node in data.nodes.structDefinitions { + structName := clean_pseudo_type_name(node.name, data.options); + fcat(data.handle, structName, " :: struct {"); + export_struct_or_union_members(data, node.members); + fcat(data.handle, data.options.mode == "jai" ? "}\n" : "};\n"); + fcat(data.handle, "\n"); + } +} + +export_unions :: proc(data : ^GeneratorData) { + for node in data.nodes.unionDefinitions { + unionName := clean_pseudo_type_name(node.name, data.options); + fcat(data.handle, unionName, data.options.mode == "jai" ? " :: union {" : " :: struct #raw_union {"); + export_struct_or_union_members(data, node.members); + fcat(data.handle, data.options.mode == "jai" ? "}\n" : "};\n"); + fcat(data.handle, "\n"); + } +} + +export_functions :: proc(data : ^GeneratorData) { + for node in data.nodes.functionDeclarations { + functionName := clean_function_name(node.name, data.options); + if data.options.mode == "jai" { + fcat(data.handle, functionName, " :: ("); + } else { + fcat(data.handle, " @(link_name=\"", node.name, "\")\n"); + fcat(data.handle, " ", functionName, " :: proc("); + } + parameters := clean_function_parameters(data, node.parameters, data.options.mode == "jai" ? "" : " "); + fcat(data.handle, parameters, ")"); + returnType := clean_type(data, node.returnType); + if len(returnType) > 0 { + fcat(data.handle, " -> ", returnType); + } + if data.options.mode == "jai" { + fcat(data.handle, " #foreign ", data.foreignLibrary, " \"", node.name ,"\";\n"); + } else { + fcat(data.handle, " ---;\n"); + } + fcat(data.handle, "\n"); + } +} + +export_enum_members :: proc(data : ^GeneratorData, members : [dynamic]EnumMember, enumName : string, postfixes : []string) { + if (len(members) > 0) { + fcat(data.handle, "\n"); + } + + cleanedMembers : [dynamic]EnumMember; + for member in members { + cleanedMember : EnumMember; + cleanedMember.hasValue = member.hasValue; + cleanedMember.value = member.value; + cleanedMember.name = clean_enum_value_name(member.name, enumName, postfixes, data.options); + + if len(cleanedMember.name) == 0 { + // print_warning("Enum member ", member.name, " resolves to an empty name. Ignoring it."); + continue; + } + + // Ensuring that we don't collide with an other enum member. + foundCopy := false; + for existingCleanedMember in cleanedMembers { + if cleanedMember.name == existingCleanedMember.name && + cleanedMember.hasValue == existingCleanedMember.hasValue && + cleanedMember.value == existingCleanedMember.value { + print_warning("Enum member ", member.name, " is duplicated once cleaned. Keeping only one copy."); + foundCopy = true; + break; + } + } + if foundCopy do continue; + + fcat(data.handle, " ", cleanedMember.name); + if member.hasValue { + fcat(data.handle, data.options.mode == "jai" ? " :: " : " = ", member.value); + } + fcat(data.handle, data.options.mode == "jai" ? ";\n" : ",\n"); + + append(&cleanedMembers, cleanedMember); + } +} + +export_struct_or_union_members :: proc(data : ^GeneratorData, members : [dynamic]StructOrUnionMember) { + if (len(members) > 0) { + fcat(data.handle, "\n"); + } + for member in members { + type := clean_type(data, member.type, " "); + name := clean_variable_name(member.name, data.options); + fcat(data.handle, " ", name, " : ", type, data.options.mode == "jai" ? ";\n" : ",\n"); + } +} diff --git a/core/bindgen/generator-helpers.odin b/core/bindgen/generator-helpers.odin new file mode 100644 index 000000000..a3b37f4f6 --- /dev/null +++ b/core/bindgen/generator-helpers.odin @@ -0,0 +1,392 @@ +package bindgen + +import "core:fmt" +import "core:os" +import "core:io" +import "core:strings" +import "core:unicode/utf8" + +Case :: enum { + Unknown, + Camel, + Constant, + Kebab, + Pascal, + Snake, +} + +WordCase :: enum { + Unknown, + Up, + Low, + FirstUp, + // When first upping, numbers are followed always by a capital + FirstUpNumberReset, +} + +// Change a character to a capital. +to_uppercase :: proc(c : rune) -> rune { + c := c; + if c >= 'a' && c <= 'z' { + c = c - 'a' + 'A'; + } + return c; +} + +// Change a character to lowercase. +to_lowercase :: proc(c : rune) -> rune { + c := c; + if c >= 'A' && c <= 'Z' { + c = c - 'A' + 'a'; + } + return c; +} + +// @note Stolen tprint and fprint from fmt package, because it was confusing due to args: ..any and sep default parameter. +tcat :: proc(args: ..any) -> string { + return fmt.tprint(args=args, sep=""); +} + +fcat :: proc(fd: os.Handle, args: ..any) -> int { + return fmt.fprint(fd=fd, args=args, sep=""); +} + +// Change the case convention of a word. +change_word_case :: proc(str : string, targetCase : WordCase) -> string { + newStr : string; + if targetCase == WordCase.Up { + for c in str { + newStr = tcat(newStr, to_uppercase(c)); + } + } + else if targetCase == WordCase.Low { + for c in str { + newStr = tcat(newStr, to_lowercase(c)); + } + } + else if targetCase == WordCase.FirstUp { + for c, i in str { + if i == 0 { + newStr = tcat(newStr, to_uppercase(c)); + } else { + newStr = tcat(newStr, to_lowercase(c)); + } + } + } + else if targetCase == WordCase.FirstUpNumberReset { + for c, i in str { + if i == 0 || (str[i - 1] >= '0' && str[i - 1] <= '9') { + newStr = tcat(newStr, to_uppercase(c)); + } else { + newStr = tcat(newStr, to_lowercase(c)); + } + } + } + return newStr; +} + +// Change the case convention of a string by detecting original convention, +// then splitting it into words. +change_case :: proc(str : string, targetCase : Case) -> string { + if targetCase == Case.Unknown { + return str; + } + + // Split + parts := autosplit_string(str); + + // Join + newStr : string; + if targetCase == Case.Pascal { + for part, i in parts { + newStr = tcat(newStr, change_word_case(part, WordCase.FirstUpNumberReset)); + } + } + else if targetCase == Case.Snake { + for part, i in parts { + newStr = tcat(newStr, change_word_case(part, WordCase.Low), (i != len(parts) - 1) ? "_" : ""); + } + } + else if targetCase == Case.Kebab { + for part, i in parts { + newStr = tcat(newStr, change_word_case(part, WordCase.Low), (i != len(parts) - 1) ? "-" : ""); + } + } + else if targetCase == Case.Camel { + for part, i in parts { + if i == 0 { + newStr = tcat(newStr, change_word_case(part, WordCase.Low)); + } else { + newStr = tcat(newStr, change_word_case(part, WordCase.FirstUpNumberReset)); + } + } + } + else if targetCase == Case.Constant { + for part, i in parts { + newStr = tcat(newStr, change_word_case(part, WordCase.Up), (i != len(parts) - 1) ? "_" : ""); + } + } + + return newStr; +} + +// Identify the case of the provided string. +// Full lowercase with no separator is identified as camelCase. +find_case :: proc(str : string) -> Case { + refuted : bool; + + // CONSTANT_CASE + refuted = false; + for c in str { + if (c != '_') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') { + refuted = true; + break; + } + } + if !refuted do return Case.Constant; + + for c in str { + // snake_case + if c == '_' { + return Case.Snake; + } // kebab-case + else if c == '-' { + return Case.Kebab; + } + } + + // PascalCase + if str[0] >= 'A' && str[0] <= 'Z' { + return Case.Pascal; + } + + // camelCase + return Case.Camel; +} + +// Splits the string according to detected case. +// HeyBuddy -> {"Hey", "Buddy"} +// hey-buddy -> {"hey", "buddy"} +// _hey_buddy -> {"", "hey", "buddy"} +// and such... +autosplit_string :: proc(str : string) -> [dynamic]string { + lowCount := 0; + upCount := 0; + for c in str { + // If any '_', split according to that (CONSTANT_CASE or snake_case) + if c == '_' { + return split_from_separator(str, '_'); + } // If any '-', split according to that (kebab-case) + else if c == '-' { + return split_from_separator(str, '-'); + } + else if c >= 'a' && c <= 'z' { + lowCount += 1; + } + else if c >= 'A' && c <= 'Z' { + upCount += 1; + } + } + + // If it seems to be only one word + if lowCount == 0 || upCount == 0 { + parts : [dynamic]string; + append(&parts, str); + return parts; + } + + // Split at each uppercase letter (PascalCase or camelCase) + return split_from_capital(str); +} + +split_from_separator :: proc(str : string, sep : rune) -> [dynamic]string { + parts : [dynamic]string; + + lastI := 0; + + // Empty strings for starting separators in string + for c in str { + if c == sep { + append(&parts, ""); + lastI += 1; + } else { + break; + } + } + + // Ignore non letter prefix + if lastI == 0 { + for c in str { + if (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') { + lastI += 1; + } + else { + break; + } + } + } + + for c, i in str { + if i > lastI + 1 && c == sep { + append(&parts, str[lastI:i]); + lastI = i + 1; + } + } + + append(&parts, str[lastI:]); + + return parts; +} + +split_from_capital :: proc(str : string) -> [dynamic]string { + parts : [dynamic]string; + + // Ignore non letter prefix + lastI := 0; + for c in str { + if (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') { + lastI += 1; + } + else { + break; + } + } + + // We want to handle: + // myBrainIsCRAZY -> my Brain Is Crazy + // myCRAZYBrain -> my CRAZY Brain + // SOLO -> SOLO + + // Do split + for i := 1; i < len(str); i += 1 { + if str[i] >= 'A' && str[i] <= 'Z' { + // Do not split too much if it seems to be a capitalized word + if (lastI == i - 1) && (str[lastI] >= 'A' && str[lastI] <= 'Z') { + for ; i + 1 < len(str); i += 1 { + if str[i + 1] < 'A' || str[i + 1] > 'Z' { + break; + } + } + if (i + 1 == len(str)) && (str[i] >= 'A' && str[i] <= 'Z') { + i += 1; + } + } + + append(&parts, str[lastI:i]); + lastI = i; + } + } + + if lastI != len(str) { + append(&parts, str[lastI:]); + } + + return parts; +} + +// Check if str if prefixed with any of the provided strings, +// even combinaisons of those, and remove them. +remove_prefixes :: proc(str : string, prefixes : []string, transparentPrefixes : []string = nil) -> string { + str := str; + transparentStr := ""; + + found := true; + for found { + found = false; + + // Remove effective prefixes + for prefix in prefixes { + if len(str) >= len(prefix) && + str[:len(prefix)] == prefix { + str = str[len(prefix):]; + if len(str) != 0 && (str[0] == '_' || str[0] == '-') { + str = str[1:]; + } + found = true; + break; + } + } + + if found do continue; + + // Remove transparent ones, only one by one, + // as we want effective ones to be fully removed. + for prefix in transparentPrefixes { + if len(str) >= len(prefix) && + str[:len(prefix)] == prefix { + str = str[len(prefix):]; + transparentStr = tcat(transparentStr, prefix); + if len(str) != 0 && (str[0] == '_' || str[0] == '-') { + str = str[1:]; + transparentStr = tcat(transparentStr, '_'); + } + found = true; + break; + } + } + } + + return tcat(transparentStr, str); +} + +// Check if str if postfixes with any of the provided strings, +// even combinaisons of those, and remove them. +remove_postfixes_with_removed :: proc( + str : string, + postfixes : []string, + transparentPostfixes : []string = nil) -> (string, [dynamic]string) { + str := str; + removedPostfixes : [dynamic]string; + transparentStr := ""; + + found := true; + for found { + found = false; + + // Remove effective postfixes + for postfix in postfixes { + if ends_with(str, postfix) { + str = str[:len(str) - len(postfix)]; + if len(str) != 0 && (str[len(str)-1] == '_' || str[len(str)-1] == '-') { + str = str[:len(str)-1]; + } + append(&removedPostfixes, postfix); + found = true; + break; + } + } + + if found do continue; + + // Remove transparent ones, only one by one, + // as we want effective ones to be fully removed. + for postfix in transparentPostfixes { + if ends_with(str, postfix) { + str = str[:len(str) - len(postfix)]; + transparentStr = tcat(postfix, transparentStr); + if len(str) != 0 && (str[len(str)-1] == '_' || str[len(str)-1] == '-') { + str = str[:len(str)-1]; + transparentStr = tcat('_', transparentStr); + } + found = true; + break; + } + } + } + + return tcat(str, transparentStr), removedPostfixes; +} + +remove_postfixes :: proc( + str : string, + postfixes : []string, + transparentPostfixes : []string = nil) -> string { + str := str; + removedPostfixes : [dynamic]string; + str, removedPostfixes = remove_postfixes_with_removed(str, postfixes, transparentPostfixes); + return str; +} + +ends_with :: proc(str : string, postfix : string) -> bool { + return len(str) >= len(postfix) && str[len(str) - len(postfix):] == postfix; +} diff --git a/core/bindgen/generator.odin b/core/bindgen/generator.odin new file mode 100644 index 000000000..3ef3d69c0 --- /dev/null +++ b/core/bindgen/generator.odin @@ -0,0 +1,205 @@ +/** + * Odin binding generator from C header data. + */ + +package bindgen + +import "core:os" +import "core:fmt" +import "core:runtime" + +GeneratorOptions :: struct { + mode : string, // "odin" or "jai" + + // Variable + variableCase : Case, + + // Defines + definePrefixes : []string, + defineTransparentPrefixes : []string, + definePostfixes : []string, + defineTransparentPostfixes : []string, + defineCase : Case, + + // Pseudo-types + pseudoTypePrefixes : []string, + pseudoTypeTransparentPrefixes : []string, + pseudoTypePostfixes : []string, + pseudoTypeTransparentPostfixes : []string, + pseudoTypeCase : Case, + + // Enums + enumConsideredFlagsPostfixes : []string, + + // Functions + functionPrefixes : []string, + functionTransparentPrefixes : []string, + functionPostfixes : []string, + functionTransparentPostfixes : []string, + functionCase : Case, + + // Enum values + enumValuePrefixes : []string, + enumValueTransparentPrefixes : []string, + enumValuePostfixes : []string, + enumValueTransparentPostfixes : []string, + enumValueCase : Case, + enumValueNameRemove : bool, + enumValueNameRemovePostfixes : []string, + + parserOptions : ParserOptions, +} + +GeneratorData :: struct { + handle : os.Handle, + nodes : Nodes, + + // References + foreignLibrary : string, + options : ^GeneratorOptions, +} + +generate :: proc( + packageName : string, + foreignLibrary : string, + outputFile : string, + headerFiles : []string, + options : GeneratorOptions, +) { + options := options; + data : GeneratorData; + data.options = &options; + data.foreignLibrary = foreignLibrary; + + if options.mode == "" { + options.mode = "odin"; + } + + // Outputing odin file + errno : os.Errno; + + // chmod 664 when creating file + mode: int = 0; + when os.OS == "linux" || os.OS == "darwin" { + mode = os.S_IRUSR | os.S_IWUSR | os.S_IRGRP | os.S_IWGRP | os.S_IROTH; + } + + data.handle, errno = os.open(outputFile, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, mode); + if errno != 0 { + fmt.eprint("[bindgen] Unable to write to output file ", outputFile, " (", errno ,")\n"); + return; + } + defer os.close(data.handle); + + if options.mode == "jai" { + fcat(data.handle, foreignLibrary, " :: #foreign_library \"", foreignLibrary, "\";\n"); + fcat(data.handle, "\n"); + } else { + fcat(data.handle, "package ", packageName, "\n"); + fcat(data.handle, "\n"); + fcat(data.handle, "foreign import \"", foreignLibrary, "\"\n"); + fcat(data.handle, "\n"); + fcat(data.handle, "import _c \"core:c\"\n"); + fcat(data.handle, "\n"); + } + + // Parsing header files + anonymousStructCount = 0; + anonymousUnionCount = 0; + anonymousEnumCount = 0; + + for headerFile in headerFiles { + bytes, ok := os.read_entire_file(headerFile); + if !ok { + fmt.eprint("[bindgen] Unable to read file ", headerFile, "\n"); + return; + } + + // We fuse the SOAs + headerNodes := parse(bytes, options.parserOptions); + merge_generic_nodes(&data.nodes.defines, &headerNodes.defines); + merge_generic_nodes(&data.nodes.enumDefinitions, &headerNodes.enumDefinitions); + merge_generic_nodes(&data.nodes.unionDefinitions, &headerNodes.unionDefinitions); + merge_forward_declared_nodes(&data.nodes.structDefinitions, &headerNodes.structDefinitions); + merge_generic_nodes(&data.nodes.functionDeclarations, &headerNodes.functionDeclarations); + merge_generic_nodes(&data.nodes.typedefs, &headerNodes.typedefs); + } + + // Exporting + export_defines(&data); + export_typedefs(&data); + export_enums(&data); + export_structs(&data); + export_unions(&data); + + // Foreign block for functions + if options.mode != "jai" { + foreignLibrarySimple := simplify_library_name(foreignLibrary); + fcat(data.handle, "@(default_calling_convention=\"c\")\n"); + fcat(data.handle, "foreign ", foreignLibrarySimple, " {\n"); + fcat(data.handle, "\n"); + } + + export_functions(&data); + + if options.mode != "jai" { + fcat(data.handle, "}\n"); + } +} + +// system:foo.lib -> foo +simplify_library_name :: proc(libraryName : string) -> string { + startOffset := 0; + endOffset := len(libraryName); + + for c, i in libraryName { + if startOffset == 0 && c == ':' { + startOffset = i + 1; + } + else if c == '.' { + endOffset = i; + break; + } + } + + return libraryName[startOffset:endOffset]; +} + +merge_generic_nodes :: proc(nodes : ^$T, headerNodes : ^T) { + for headerNode in headerNodes { + // Check that there are no duplicated nodes (due to forward declaration or such) + duplicatedIndex := -1; + for i := 0; i < len(nodes); i += 1 { + node := nodes[i]; + if node.name == headerNode.name { + duplicatedIndex = i; + break; + } + } + + if duplicatedIndex < 0 { + append(nodes, headerNode); + } + } +} + +merge_forward_declared_nodes :: proc(nodes : ^$T, headerNodes : ^T) { + for headerNode in headerNodes { + // Check that there are no duplicated nodes (due to forward declaration or such) + duplicatedIndex := -1; + for i := 0; i < len(nodes); i += 1 { + node := nodes[i]; + if node.name == headerNode.name { + duplicatedIndex = i; + break; + } + } + + if duplicatedIndex < 0 { + append(nodes, headerNode); + } + else if !headerNode.forwardDeclared && len(headerNode.members) > 0 { + nodes[duplicatedIndex] = headerNode; + } + } +} diff --git a/vendor/stb/lib/darwin/libstb_image.a b/vendor/stb/lib/darwin/libstb_image.a new file mode 100644 index 0000000000000000000000000000000000000000..06ce4432192e21268b40c487d33f54d0d4ec0007 GIT binary patch literal 55744 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 NM4g=;br$D{{|6{&o+bbQ literal 0 HcmV?d00001 diff --git a/vendor/stb/lib/darwin/stb_image.a b/vendor/stb/lib/darwin/stb_image.a new file mode 100644 index 0000000000000000000000000000000000000000..1379d6f9e70c8dd9a853de55e1f5346d8d17f244 GIT binary patch literal 97544 zcmeFa3wTsTwlChDZjufJDk^GJggC|-qK*7<+ylPZo?_bHaqpxe#O)um8qT^3#U%Lb(%-D;cteWer+~a zw#`;#J}H6T?=zp(=JQGO`3Lj)s`=byK0h#@$INFwU=y4|^Lc~${E_(#n9mjF^Iy&9 zM)SGHeE!XRo;061SrYEq=JP7^=`){|=JQeW`OoI_x8`$)`P^$hKQ^CP4hi>s^I2*> z=a|p1`TSS&x!HWaZ9ZL2S^iS zfV^=2gJ#*e_f!$E`|-GU_5%;hJ~et_#q9g%n9=hdn0@b@^2(|?sfY(F=UZtAB5D2u z9kPprHP%S)~;zk8m)@B3*$TiIoDI{eI(zLlWe zy)_UK`p0}>c`7Xvb}BjpFp&||q{MV;rbVYylX}`jLUek`wV7}I<78%)iJ7$%=k(hS zQAVVswIVYTWKXYMWIh%8z4IzhQ*mWlX){k>KA;NCq&{f$2Xx^ae|hEo^A?m7@6RJx zso`N(^$j^PIiE82JI+i%kK8}+?(%!)RLs9;PWjz|%E~zwZ=@$Y01W0=^ix76l;0be zvvA?Ud5>5_0}v_4os9c@f#x(`ohcqRK~l^as@|i891fGrYxYJm@LZgUbs-ctI9ORER2Um_X~TY zGT@z=7Ei$xyoYC35y0~DOn3z*e$ScZ3V|Q}O_^%9QCrUvZ@oX|7Gh>pUwBcIFY}BY_?d>q(6_^FP0gret3i|csJ<4(_yv+9IcS%6A%yCw5 z^4sxyV8QGP5{ZlEJ$O%fxlff4Gfx1wsVlpHa7(ffvfP3Xd_CofwpY?qd%fQq11l6-BySvAwl_E zP*j`ojND_ljRZ3To^1;L(O<~?cNw1R@sDKIfAw#=#{Z`2-_!Uu zjW_xDr#}9kk00~!4w2^NjT%2Bf^t~->O%ALwxN_n4J zwVIE+QHRRIANM4aUjCYwzp3$~Uf!eWf_cz7jeA<~MugT3V`MU!)VR`)7#GEGrOnF9 zl~#@aq(iHxeyWy<-Ay$VqCbuEr{p7ohtFu}K$Sk8lr7O$H6zx`SG7_iM;-6__(wi| zSk(n?A{UVXMsjy&|*glwXS!ySWlskcfClp%3QsukM*5N559(aZmV@fK$=)id43~1OGnvR`duEUcR01zrqvlTz0m~J4$xePP^HzM26;R z$(ZJD4s=-EnYhqX@3`%@A zI1c@lwD~V2oN_eX)2!+v75zFzKgY*E1g-)vA8!MfjQnQu{6AJXl~wj={0+$}G(0j` z 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 VgvCIU*OUwPCyoSGl89s9wlAP9^WFde literal 0 HcmV?d00001 diff --git a/vendor/stb/lib/darwin/stb_image_resize.a b/vendor/stb/lib/darwin/stb_image_resize.a new file mode 100644 index 0000000000000000000000000000000000000000..f39c507a6c92d777f16c781d44a9dd3b623c42f9 GIT binary patch literal 36824 zcmch=3tUvy+CM%6gQB7vOv_3W(+tuv$xBYa45KqT*kGO%#S4x&f>H<`VDgd|ibmOI zJ7}FKJEd{zc=A}Lz2zm#1SQ2=;iXcsqAZi6mAAa4Ilu3-*4o1`g3#xE|Ns4A@9$pg zdDgR@^{i)I_S$O?-Qv=6ozso2yT?5EP~XV9BW7U#eg?z9fiZ(3BM0=20fCca`t}{z zzkg(8zX1aU^W@eJN1svSk}TGtk)s}ae6-b~!7-99I96*lnsx&36=)PVO5hU$7Ylq< z;3k1p0xUc}SvhI>4aGU8rDvz-W;z3Cx#`m~bFu>>^ITJA zX3kE}3W#=PX6MHYY(x+d2Q`pj(5?6#dAZZ3G?<78j`XS1(r4sMO-GsOp!z{o_zqwF zplTYdpMm}Q^ZK0J8YXjO&Pbb^hs&hv=fD4aqoqcp4aPmHN)v)}H3cGZ;dr7S|EpXBijWZl)e#4}BS+zQ!fPJ$gvLC|{P-!#%vM-2d4hRT>JBDpta*$%**tob?U^%` z5RxQU$VrD2Wv@lCj#QFtHYGJunIEoLqvQ?KZ+N|ONfxda*;7D)t5*=S!lkGqbaOOZnt15E)c%+hb(q2j!s&WAlCEF&rRz=>^C^qj)mMtzBaN$+CRLr6 z!VgH34oMLul$R8>JwxiaUD|o@Cs?X&Rf0wnQzrE(mtHE{LJr@m%Dr2%mdEUt`W%p6 zDsOeb>Mk=&)=Haa>trOChfS0po6Ia|mUr6a%iNgmGU>Vg!LX{l!!FNXZ&7A+vhxIX z*~__=$Lxjk4{KaqloB$bwC;f7O0aq2sWPOE{f*MP^Rnx()%~-pvry=c13RVK807!lIIz^+4?bt>t0liNVCuD8iOQ|JXLr3`-2x~%mDsdt&|4)u~ivI+@YEd-LJJ(!~W8A)Lz ziP;^g-J}{bf$R=v1d)ZCvJiw0jie|qukETWKX6s{bZR7N9Y<&4@JxqLi{dT-D6Gwy zrFCWHH9@jFis){VbaR3NEO$g6PLS&!MAC6j#|8oG0qO!dh~pu1ge8-VJ|=5$YI zv?{xN-WHs^*=E?DKZ+SArn+_sx<@dR5YFr;nF(^1@YJLoiG;YUKyi{=ftX$HK_Jkf zl&HQ#?Ov@YUz1g~vT`dMsI*Fnf1QEXQ%bQO*3Bty>fo5JMBzqg`%)ZyCOWA=TD%hd zhDBLROx(T5Po=1G?t{Jr>6Bpc8T9%VWfiYWCSLoU`SNjUYf-5MQgJ+l!J~KBJiI9t z?1M#Q>tW=qzaJ9Nn+UFT?|4&Yzp&##nT%CFzDHw9;%x}=V6<6kKNh1%I36G>Cn+tnhiUs zibx6>n=0fbh@lE;$15hDP+B@2lK_jHzFJ|!BQjv^302)RrmO1<5N$5_9yiUR(Pj#wO2SZujUs)IjW>utcb|%vM-Ns+ErL^xw)F zj-0%Hut#^7ZMjWJ-V6;pJc}-PxkY>AmCN%FLo`XWIS(Rona!hH=9%OzycR4io`f+U zL(!9=0B7gdZQ5(z6BuZUpF@1iL3mTRy#!u4ZV!AZY#H1wY?;lw-DWu8I#D|rJj9#A zc;!R3@)JQUMHgQRjQ9~@ojl{UEPjPAez|9E5RsO%_^rNpXdV%^*;cqcI1yRAQ~L+X zMp^s@j5#QOWh?)QXA$p9GKi!j)u6yEn0(5I3gMT9XM8Zrr9pmVtxGgixn8V&mSw$Q z0W4NZ78BO^Le1D-bsb{pO7g$ZsYb%66cJV>jI{5x(Y<@YBX;?})|i7TPtX+FP(Xhw0&mNmvndF(EMXj!vmfS^d zq0Fejhz=j4CU5pum<{_RxAr=!m(M$tSv^>bsqNYV!#^AKs9SW1BNd;(6kdu-DuAlx7o_%OQnWh~6x^2Mwl8k2F<#N|FzJLL9@qsx z37NyP0e@7b6jee=q?A3^kB;IH&vRJL^Ue3`x}TU<}Ra zpeA~h#dwaC79B_Ljqa@gWiiXYiN3|Acz6$xw9L-CK8t)o@vH}voK&iKHseOSJJ_?2 zMHFBdW%COUbqD2FgXE;;iO$c_TOP2<$*YGdJ+9d0t<;bQE0!xd3=J3zix31yImT-0 zn=r~J01~Bjm#BwT+^loM{7hkUF>sOB*p;npEajaL`jO2F8$5AWsj!mE)(YSJ)icyv zcm;jMN%U?uPumAsNp;WIw3Xf(OKkdKi;`4|N09=XlC)Y{nog5p%>H^=D@dHpxjW_( zg)Q0|bVchi&0P>HtxH~R^Tf5YIk!`Hh319cDjD5NxNZyD8JY*~$5q0iQo>%wH6rX~ zG^j+w7p@aF?@m-)4=rVEmMiA{tM(#wx;Lru4JlivhTU2Is3(7h2F)-)nOsulQBPP< z1Iqjjea-<}`FH56!ox7D6e>wfCCM`!9Zz^zkuQEJ?`GY5z(`u>j#t zdjs8RN$o1w(WaD8E4o?LB1ZH%H@r&zG7;_UX15x7E4Q-CzsKx$S1;%iLz4;NDe(0z zGgX0(ZhUDA@_zk9u`zsDTS1~OxnkJz56poM@5=85K1^CYt)bq6E- zZ!XJ@UGsXwkVP6sspvnv-j$cV-qlyU-aSA)@YZV@ePZC*_0}|=2yS{&V|2YG5{oiF zik^d6fn&;G^^Vn8wW$+33X3Hpu`q`9kek*@CUQIY)=L@|t)vd(f*1J}Q~ zpUq@`UsNp_Clae+Be70_(I^2{MBgx(%oXCF$w>3#=24^gs+V~z7Pdw{p;4@zMk?0u zk<7`lKGvw&Y=vh!sd?EHOhc%zz}kAq8|Yox$rEm$`DfdQ439o?R*6e;gB=EQ5}t}Ck^(F z9)!B|Ur@rjO9idF6kdc#WKKEO<#UDQ!)+iY-dz-y!CpMxS7O2H2o|gkNy!JK(MO~) zSk>Hdt30kloK<_t5}X&RJZe|+CSon{oEc+|MSh@onl|m{sSjsJO zVJKo`B@9>hrTixgZ?wpJQpuZ)`%JpeWY&$j6hjL;|LvvGBuFpqlJ`hMzm$fa+x6Tv zl1B%s)T-TS34Wgp2w;T{exHhI)?PAezqH6_;ftYxzR2pS?n17K<`eGr=8rG*w#XZg zrvv#4TQKPpl7s9qwWRE9iX}93R!6L&paCUa_hRr)DTCpx*H5>ny0U;3!E#Q(BZ64v zg9_pPV-@9x?wv1lBVm`@kWlJ#G}nxPK8ONQ=P)t&O^P}xjSl&diN$)!*n?7^io83p z8uE+O=M*X`uMJ;3nS^zk!&pQXnUH))YFqXIiLJ~f<=SpS05YL^O1U2&n4wi`DOYD9U^jFntix_{k zYDoZB>k?jrNPV_Hb+1sX%T+|+{uos0a*p30MNT_>s#LNRT$PY*oP+7G$?txh?|vd4 zA%!a3CiChtFJYp#Kh;yUNvSVcO2~44&up>`_ow58Eu1XXo~Qbg703Ifc--%PxZnL? zc0Vb^2;^pMrgT5pZ1UEb6pE&m@eMRhlNO)Ab5o-HTcUj3 zBD)IYucRpJdO5kwEPu{>v>oNAyJ~k7p16VWq70KtEO_$)Mt0K@ZlYpo5lU(3sfwIG z!dgT@i@bF@$QJE2srUk#EJ~GB#5PMDyHuwVo|o8b{YDs??fjm5AMDywiH^lB5aJT$%xtA-)Bqwr{x zZOYi1;uU7{@#RF()lsD0N6_inl^K<0?-zFY6qTjQEPri{{)0*s*s{%rPoTS$2s9G&lKq(lV*28ufv7HrZMVRg<7^xRpxf_dWpnvbw_4 zIT;qq(sm`ry93YIRnofcSV5E7BXE*uo*S=Bexnr|^hC>scr5qPgb`-KnG!POelqsN za;%W7H+#Qi*&|Eq`U^v(2;Ez3J?DgLw_+`Im)Vt7WX?r%|xd{tUksoiUH?!fa^iruZjvxeR9yX$?a zPuuQ2;ZBQI!TXUqB!WVC>9*(<5pm@{B1|GLvsU?KKa^+xuqZ%NnTA{35Hk*SjR!p3 z3kBXYdv{u5T?LZ6D~!QQdjl&JP=)4e{)LdOWwe|HulxwCxZ&*B3II);=89iow%}P-_?rO7S`$oHWsHaCiJeiixpCFRgXC~b+ zqe^sE2ye40Q{8CKXjfE!{&I=cg2&6{Rv9e3J?^v^Tve`D=~+>6ElV_97OpF~Pm-r4 z7kKx?l$jNKh0bt5dTftb#+}VuW;5(`)zEj+?nnIYv^OEb*U zf?c>ga(ji=D*s`PrkMb`FzN;B8JK#m3Uta?exvqpk$W7m$n%cqEb{zPtKlbU$#ULf zTLWswCa0E3>vqs;K3nFsd*YtK%9&JDO1>0DB}-w3po!6XDk^dyOWT#cXvHzrWZ+87 zNxQQ*IuDTewlCbPrlmd|4H^3K78{{k>@IUf!-?mD#}zsE9;_=1ow2G{2EBQspk$S& zTQPvhO3x5DbE#S04-;4nr{x{<7M%`8mZIO!fW+&R>!JvFtQ>afl)-K2%*j-R2 zEm{d03OAdk<{`D+n1_^`JyvbZZZzg_T1{hXI#^f3l8DVv={iO1dW(Dto@e&%Coaq4 z>&WaQq;_?f+g=GfX3^Hl_G3DbZQja-$8G4~V^fPH_ZL`yz#0b)+fB?^S$`UEp(zn| zU(iI>EaRQ(HAaWxLoC#Row3Q^s&A$c$-;O|PIFUe*A3duYDIf5YLPp#7dK3(Z%R>k zW_F_dhs`<6ZkBghlvyJcM~d=Ts7+2wRY{+2k$;k1MNH{LB?;-0HrwSH%gjm%6N438PgrYu z878y$uJVD)N6m%Tb*{S#=NDb&OmP(%a>JzJ)yTW>T1#p1E7;1C==>4`OA(w_ zT8C+h*18$f4o}BIkZew@@M^2PHHM_aQgJVg*LG)ZlJh6KT!W?RPi?{0QurCxggZZz zSpGwhGQYwi|AH=mwQAnBe{>}$@h*sh>`G)2Y~75V8XG%=@@9|O3L8Rn3S1=7@VV=U zMDM3|?Foxv#$ng`+7B_nA!~GT1y~eVEfpVw3Q6(}TDwCNV-qM(51U24CapVqxtcP< zBI=GrngZCxTTRTc%-(%?@3j(XSvj1LYIlAI&9T>^5(TN?1);_5&d=ICy*)6Gh854EL%QKIwcnc!ExQq|C`LVMa` zNI&d)tCnVzOp#-_`O}KPKij@}k5Hs{Jw?ng?WPX4#!c%};_cc=suIb!R073g)__;8 zw|n#r7=|=pM(JX6R@#D-Hpd*q?wAO(lD|PC_y0RvvHi+*2DVyu$vis96=RpbW1|;L zyII<(3*zdQ!TXZX-%SeH(*=$foK%DwFCZfp!Faad)b-4I&(^NOG=N#}HF{&=JSVCV z$#?o!W1Zsc)d^}7GkV^63HPLgc;H6C@UAc0=A^eRXjY!yW=tK2NSiF_b<@U4A$_40 znZ6Sc&y9KaqaO{qjYUohkr>icI=pQ)?wR|O6PymE5}oRdw0K*ndlN-TGW_KFC(5?$ zQo?9BG1JL4z$TwnlGa!}`gCfE(^lmQ#uX4O7@(P+v z8(fDawvnWEQIIgteBAtR%tQ4G+pwyg>}Q>4XktACJ@+bRk5DM(vqvx$6qFu5#&o=EG z{-yt`f9YeY&5C6Oc?Pw1)cB;3b0`jP@!@T+-sNYGiwKZ8&NAfY_!!Iq=GaYSOn-H+ zVYEv$eCt{fXbwYT<`8P2!RTKVZ06CnxRv@Jw8D@z*!FCbsc9rt+O=%HVfSdvc;A_iWuFv!1sQ<| z-Y>;eCmO!VZ-;apR$@+qMVyEZD}|}?wx-OcyNo|W#O8Q~T-GV;q zRt*d?DEsloN0~$#WeT^_poB?z`sxLJ>yn;8(r+6ao-kv>2t}`~=6#F##_Yh!&7vz_ z??k-v`Rc0I+Y#?$MgSkeTxc|=_5JZ)CU6pB!#tu=85W5S8xOEjaZB`)iJ0;zF1*uS zfHzn+Wzlk!O<74Ll#fd5K1Y{>UJu>M(3wcJ+O8~^ZF4SKP2|+7%hiaZQO=DpO<2}r zB?LiM6$s+ti>8ejZLyDXxtxmHv#uWD>|8m*X{pp%4M(IU@1Q3#8>*!xo7fFvUZ=hZ zZ+Vr3&|u8Ju}{bIJfoFK9uICjgZZoLFVTwa@@@>#`>@-E_h`DsHtiK}&10U?EAX_q z7jtHsY`%^@$kjqp?ni1wj0^h*t|?)~s zv$S?PpGJG_lhd^Q+tPA^4`X1iWkCTh-}Vd9~6G<#eQP{1=R zh(FTSqcUYi7l(R=1vBb6Jl}IQ!?>DZiG@3Kct2UVOP6HWE-jfsJ?J*5W|5D;vYyUp ztfWuaVxi+v@&G3@IE=3v!l&cYT3�v~7pkgTJ;d*1^hn>h_iu?g+9QcFkLJdSa5i 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 literal 0 HcmV?d00001 diff --git a/vendor/stb/lib/darwin/stb_rect_pack.a b/vendor/stb/lib/darwin/stb_rect_pack.a new file mode 100644 index 0000000000000000000000000000000000000000..3b55ab8025d7dca76643a92399f45397e35f46e7 GIT binary patch literal 5064 zcma)A4{Q_H89x&fjB7XpX$g#Wx8k-~t3#ZG1TT=~;Ep`AJN_jeOe4L7V2DNjfPF5Z zvPmWr#M@y&TQ`k~Zb*|hG-(5EMd}#a9D+z`vNnON)!J&KU8@#cCQ7C>&<*ct z5G7B3_j}*>{l4#g@7=q1&-b-e_2Gub3j0#;@}ffSVeyuh6#IOorQVesx1!Js2E*(v zEG#W4;W+P#QcA9~D@sB2cVt0a$5n6KQX>is6pmb=+<>}(;xiQ2Qmg}1BI=ItUM18L zQWdp6ygR6xs=f6Mdz5gnL5-N2NKlRLRqC6XTK6lhQB`T(r8Ebd$6FhN(QqiDh8iF? zDU*(?=+WaUDO*XWq0SDe=?N$#m4a41-dp!?KbXZZmK=~QP_0mHGvLBOg|Z0h5n`C6 zha713H+H&P)Q+7Qpvp_xsFQ|Cf~=htfHs_o~0^1FPu0wgK17wxOOX2MLMrMh+!6E(6TRWq7Mqz*TvcJ16-s zMj*O`w=P4;s+9;=72^FXd#YA}OC@M4X}??fZ}S1ZrWz$aYeRSlHQepI#vVHd_s)<1 z%j0)Ya&4aXDz>3a7fPh^{l2%NcTsWVuh_V(3d)b<#qtw@R@Ocu? z=}kfCRF60a{Z{sfeFSd)jtrnj=+z(6n@@u)B^Yc8rzumykOz!gzNn86 zIm;!!yvAj1FDn-Z<4zWCv7d|%dM`1M|IE6lxjyBqcK?QPtP$2(GPhuiF+2d^ViUjwICzo zk40aJD{hyyW}E1XGO=Mvi=U7?Z?lIE1BcgY8Nody;SmXcz#f?oL-pf6Kkh+E-i9>4 z&9~^_zj@yh7km6mSXC1K1pmVr@hN@8Ft+xUWST8qFy@Z(_11Q`CZ7UzY+kwC7ju-? z_Qw}OOP!f^V!033_QUY>?&2wu)6IRb2#^*{q0V8X87o6GR)!YrkhJC#?4j>NqNI-; zd5|xMyV)L-HNnNnFbQFVU)KWtJa+Q9((2cc&5tD)hqSrAdGWbO+YIy91&iiQga;%o za74nnNZV%5i5qwXy*C2e?uc;6Lml=_gF99J8AqK!9`d)ek zdJQseClzvng$c`qL_nF8$YLKacC%a5dwV-{K_YJYf zf2!Z-Dx0s6J@!Lr3rTB{H^pCoCL{W~w>$N_g&}N4eyHhL*9K!V9#C$X93?x=Lqii zc+1~KJS5_Gczjv(U5Wif!1KsFWX%GZ0F+#Pi=>-eaZiUiR+#z=H=#W`M?mQ@C~q5- z^@MUei&6$ki1K@bvYk*)!)0ugJcDw=psXd7UuIE0*KI4B4GMYPU8u{V6oU*Mtv4v& zB$Vf}C{Ba&oI&|Ip%j=DLg9$+OsJ0tSP0W-mZyvcOy>AP^~_{;@>(XP&43C1uNr!WgG0)|wwV*`gXHi6 zML!GDW00XSu9lewlJhE*3I2}*n{uO#|oy@;t3VZz& z_Cr(He>H_Yj}F)J(iXD@l$H#jfI4?upPI=_M>&4{BiCIT;lQWN5kHcZe$n!Fwbl=F6u7e42 z$#_14x|#CmdYHf?{gdrz4dsz_p%=6NszmP5(cX>z1>pac^67n^5SR4d1iaU$!Fw5a z-P7P*1m2&g!TSh!f0zc(0f(bkr@<=$-q|U5S73pVzi1Z$f&Tb;iod1!3u-?}O0O7Bo=M_NsJ^5oQ9|nYeo?K03Hz53; zd>@psusyfno)IKF67jzS>xb|c1A@x)xV|VE&uI|gm)k+Hj$$Q6jv_pTxieJ7ez4(9~sfpG8zQ_hmozH}^?0P7>eT z7td4K+y}`Uo!HHN?@ekSyU($}G2bhtU?;Nq9@#@>b3gkTl>zip3s<0z7P1G9mCdg( z<6p|g`%>9Q-uWhDicdY{i;vP84($%L)Hgw6XD|#OLzMbRBp6neu-anAne1zj9-vV_ zU>M&h!ol$_qel)?{qRduK|^cP&Vpjo@QA?}j3Sef?K*16Ixl6pO&ZucS>RE_BLjPx F{{yO}#B2Zn literal 0 HcmV?d00001 diff --git a/vendor/stb/lib/darwin/stb_truetype.a b/vendor/stb/lib/darwin/stb_truetype.a new file mode 100644 index 0000000000000000000000000000000000000000..c4a895b54b69c674e69c31dc2464951a929cc3a4 GIT binary patch literal 67008 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 r6E3W Date: Fri, 4 Feb 2022 13:45:36 -0500 Subject: [PATCH 05/12] Reverted experimental changes that were meant for a branch, in sync with master --- Makefile | 2 +- core/bindgen/c-parser-evaluate.odin | 266 --------- core/bindgen/c-parser-helpers.odin | 267 --------- core/bindgen/c-parser-nodes.odin | 132 ----- core/bindgen/c-parser.odin | 840 ---------------------------- core/bindgen/errors.odin | 44 -- core/bindgen/generator-clean.odin | 284 ---------- core/bindgen/generator-export.odin | 166 ------ core/bindgen/generator-helpers.odin | 392 ------------- core/bindgen/generator.odin | 205 ------- wasm-ld | 1 - 11 files changed, 1 insertion(+), 2598 deletions(-) delete mode 100644 core/bindgen/c-parser-evaluate.odin delete mode 100644 core/bindgen/c-parser-helpers.odin delete mode 100644 core/bindgen/c-parser-nodes.odin delete mode 100644 core/bindgen/c-parser.odin delete mode 100644 core/bindgen/errors.odin delete mode 100644 core/bindgen/generator-clean.odin delete mode 100644 core/bindgen/generator-export.odin delete mode 100644 core/bindgen/generator-helpers.odin delete mode 100644 core/bindgen/generator.odin delete mode 120000 wasm-ld diff --git a/Makefile b/Makefile index df5fe0605..d3d3c6a2d 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ ifeq ($(OS), Darwin) LLVM_VERSIONS = "13.%.%" else # allow for x86 / amd64 all llvm versions begining from 11 - LLVM_VERSIONS = "13.%.%" "12.0.1" "11.0.0" + LLVM_VERSIONS = "13.%.%" "12.0.1" "11.1.0" endif LLVM_VERSION_PATTERN_SEPERATOR = )|( diff --git a/core/bindgen/c-parser-evaluate.odin b/core/bindgen/c-parser-evaluate.odin deleted file mode 100644 index 13cb5042c..000000000 --- a/core/bindgen/c-parser-evaluate.odin +++ /dev/null @@ -1,266 +0,0 @@ -package bindgen - -import "core:fmt" -import "core:strconv" - -// Evaluates an expression to a i64, without checking. -evaluate_i64 :: proc(data : ^ParserData) -> i64 { - ok : bool; - value : LiteralValue; - - value, ok = evaluate(data); - return value.(i64); -} - -// Evaluate an expression, returns whether it succeeded. -evaluate :: proc(data : ^ParserData) -> (LiteralValue, bool) { - return evaluate_level_5(data); -} - -// @note Evaluate levels numbers are based on -// https://en.cppreference.com/w/c/language/operator_precedence. - -// Bitwise shift level. -evaluate_level_5 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { - value, ok = evaluate_level_4(data); - if !ok do return; - - invalid_value : LiteralValue; - token := peek_token(data); - - if token == "<<" { - v : LiteralValue; - eat_token(data); - - v, ok = evaluate_level_5(data); - if is_i64(v) do value = value.(i64) << cast(u64) v.(i64); - else do invalid_value = v; - } else if token == ">>" { - v : LiteralValue; - eat_token(data); - - v, ok = evaluate_level_5(data); - if is_i64(v) do value = value.(i64) >> cast(u64) v.(i64); - else do invalid_value = v; - } - - if invalid_value != nil { - print_warning("Invalid operand for bitwise shift ", invalid_value); - } - - return; -} - -// Additive level. -evaluate_level_4 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { - value, ok = evaluate_level_3(data); - if !ok do return; - - token := peek_token(data); - if token == "+" { - v : LiteralValue; - eat_token(data); - v, ok = evaluate_level_4(data); - if is_i64(v) do value = value.(i64) + v.(i64); - else if is_f64(v) do value = value.(f64) + v.(f64); - } - else if token == "-" { - v : LiteralValue; - eat_token(data); - v, ok = evaluate_level_4(data); - if is_i64(v) do value = value.(i64) - v.(i64); - else if is_f64(v) do value = value.(f64) - v.(f64); - } - - return; -} - -// Multiplicative level. -evaluate_level_3 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { - value, ok = evaluate_level_2(data); - if !ok do return; - - token := peek_token(data); - if token == "*" { - v : LiteralValue; - eat_token(data); - v, ok = evaluate_level_3(data); - if is_i64(v) do value = value.(i64) * v.(i64); - else if is_f64(v) do value = value.(f64) * v.(f64); - } - else if token == "/" { - v : LiteralValue; - eat_token(data); - v, ok = evaluate_level_3(data); - if is_i64(v) do value = value.(i64) / v.(i64); - else if is_f64(v) do value = value.(f64) / v.(f64); - } - - return; -} - -// Prefix level. -evaluate_level_2 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { - token := peek_token(data); - - // Bitwise not - if token == "~" { - check_and_eat_token(data, "~"); - value, ok = evaluate_level_2(data); - value = ~value.(i64); - } - else { - // @note Should call evaluate_level_1, but we don't have that because we do not dereferenciation. - value, ok = evaluate_level_0(data); - } - - return; -} - -// Does not try to compose with arithmetics, it just evaluates one single expression. -evaluate_level_0 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { - ok = true; - value = 0; - token := peek_token(data); - - // Parentheses - if token == "(" { - value, ok = evaluate_parentheses(data); - } // Number literal - else if (token[0] == '-') || (token[0] >= '0' && token[0] <= '9') { - value, ok = evaluate_number_literal(data); - } // String literal - else if token[0] == '"' { - value = evaluate_string_literal(data); - } // Function-like - else if token == "sizeof" { - value = evaluate_sizeof(data); - } // Knowned literal - else if token in data.knownedLiterals { - value = evaluate_knowned_literal(data); - } // Custom expression - else if token in data.options.customExpressionHandlers { - value = data.options.customExpressionHandlers[token](data); - } - else { - print_warning("Unknown token ", token, " for expression evaluation."); - ok = false; - } - - return; -} - -evaluate_sizeof :: proc(data : ^ParserData) -> LiteralValue { - print_warning("Using 'sizeof()'. Currently not able to precompute that. Please check generated code."); - - check_and_eat_token(data, "sizeof"); - check_and_eat_token(data, "("); - for data.bytes[data.offset] != ')' { - data.offset += 1; - } - check_and_eat_token(data, ")"); - return 1; -} - -evaluate_parentheses :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { - check_and_eat_token(data, "("); - - // Cast to int (via "(int)" syntax) - token := peek_token(data); - if token == "int" { - check_and_eat_token(data, "int"); - check_and_eat_token(data, ")"); - value, ok = evaluate(data); - return; - } // Cast to enum value (via "(enum XXX)" syntax) - else if token == "enum" { - check_and_eat_token(data, "enum"); - eat_token(data); - check_and_eat_token(data, ")"); - value, ok = evaluate(data); - return; - } - - value, ok = evaluate(data); - check_and_eat_token(data, ")"); - return; -} - -evaluate_number_literal :: proc(data : ^ParserData, loc := #caller_location) -> (value : LiteralValue, ok : bool) { - token := parse_any(data); - - // Unary - before numbers - numberLitteral := token; - for token == "-" { - token = parse_any(data); - numberLitteral = tcat(numberLitteral, token); - } - token = numberLitteral; - - // Check if any point or scientific notation in number - foundPointOrExp := false; - for c in token { - if c == '.' || c == 'e' || c == 'E' { - foundPointOrExp = true; - break; - } - } - - isHexadecimal := len(token) >= 2 && token[:2] == "0x"; - - // Computing postfix - tokenLength := len(token); - l := tokenLength - 1; - for l > 0 { - c := token[l]; - if c >= '0' && c <= '9' { break; } - if isHexadecimal && ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) { break; } - l -= 1; - } - - postfix : string; - if l != tokenLength - 1 { - postfix = token[l+1:]; - token = token[:l+1]; - } - - if postfix != "" && (postfix[0] == 'u' || postfix[0] == 'U') { - print_warning("Found number litteral '", token, "' with unsigned postfix, we cast it to an int64 internally."); - } - - // Floating point - if !isHexadecimal && (foundPointOrExp || postfix == "f") { - value, ok = strconv.parse_f64(token); - } // Integer - else { - value, ok = strconv.parse_i64(token); - } - - if !ok { - print_error(data, loc, "Expected number litteral but got '", token, "'."); - } - - return value, ok; -} - -evaluate_string_literal :: proc(data : ^ParserData) -> string { - token := parse_any(data); - return token; -} - -evaluate_knowned_literal :: proc(data : ^ParserData) -> LiteralValue { - token := parse_any(data); - return data.knownedLiterals[token]; -} - -is_i64 :: proc(value : LiteralValue) -> (ok : bool) { - v : i64; - v, ok = value.(i64); - return ok; -} - -is_f64 :: proc(value : LiteralValue) -> (ok : bool) { - v : f64; - v, ok = value.(f64); - return ok; -} diff --git a/core/bindgen/c-parser-helpers.odin b/core/bindgen/c-parser-helpers.odin deleted file mode 100644 index a99d83dd2..000000000 --- a/core/bindgen/c-parser-helpers.odin +++ /dev/null @@ -1,267 +0,0 @@ -package bindgen - -import "core:os" -import "core:fmt" -import "core:strings" -import "core:strconv" - -// Extract from start (included) to end (excluded) offsets -extract_string :: proc(data : ^ParserData, startOffset : u32, endOffset : u32) -> string { - return strings.string_from_ptr(&data.bytes[startOffset], cast(int) (endOffset - startOffset)); -} - -// Peek the end offset of the next token -peek_token_end :: proc(data : ^ParserData) -> u32 { - offset : u32; - - for true { - eat_whitespaces_and_comments(data); - if data.offset >= data.bytesLength { - return data.bytesLength; - } - offset = data.offset; - - // Identifier - if (data.bytes[offset] >= 'a' && data.bytes[offset] <= 'z') || - (data.bytes[offset] >= 'A' && data.bytes[offset] <= 'Z') || - (data.bytes[offset] == '_') { - offset += 1; - for (data.bytes[offset] >= 'a' && data.bytes[offset] <= 'z') || - (data.bytes[offset] >= 'A' && data.bytes[offset] <= 'Z') || - (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') || - (data.bytes[offset] == '_') { - offset += 1; - } - } - if offset != data.offset { - // Nothing to do: we found an identifier - } // Number literal - else if (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') { - offset += 1; - // Hexademical literal - if data.bytes[offset - 1] == '0' && data.bytes[offset] == 'x' { - offset += 1; - for (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') || - (data.bytes[offset] >= 'a' && data.bytes[offset] <= 'f') || - (data.bytes[offset] >= 'A' && data.bytes[offset] <= 'F') { - offset += 1; - } - } // Basic number literal - else { - for (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') || - data.bytes[offset] == '.' { - offset += 1; - } - - if (data.bytes[offset] == 'e' || data.bytes[offset] == 'E') { - offset += 1; - if data.bytes[offset] == '-' { - offset += 1; - } - } - - for (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') { - offset += 1; - } - } - - // Number suffix? - for (data.bytes[offset] == 'u' || data.bytes[offset] == 'U') || - (data.bytes[offset] == 'l' || data.bytes[offset] == 'L') || - (data.bytes[offset] == 'f') { - offset += 1; - } - } // String literal - else if data.bytes[offset] == '"' { - offset += 1; - for data.bytes[offset-1] == '\\' || data.bytes[offset] != '"' { - offset += 1; - } - offset += 1; - } // Possible shifts - else if data.bytes[offset] == '<' || data.bytes[offset] == '>' { - offset += 1; - if data.bytes[offset] == data.bytes[offset-1] { - offset += 1; - } - } // Single character - else { - offset += 1; - } - - token := extract_string(data, data.offset, offset); - - // Ignore __attribute__ - if token == "__attribute__" { - print_warning("__attribute__ is ignored."); - - for data.bytes[offset] != '(' { - offset += 1; - } - - parenthesesCount := 1; - for true { - offset += 1; - if data.bytes[offset] == '(' do parenthesesCount += 1; - else if data.bytes[offset] == ')' do parenthesesCount -= 1; - if parenthesesCount == 0 do break; - } - offset += 1; - - data.offset = offset; - } // Ignore certain keywords - else if (token == "inline" || token == "__inline" || token == "static" - || token == "restrict" || token == "__restrict" - || token == "volatile" - || token == "__extension__") { - data.offset = offset; - } // Ignore ignored tokens ;) - else { - for ignoredToken in data.options.ignoredTokens { - if token == ignoredToken { - data.offset = offset; - break; - } - } - } - - if data.offset != offset { - break; - } - } - - return offset; -} - -// Peek the next token (just eating whitespaces and comment) -peek_token :: proc(data : ^ParserData) -> string { - tokenEnd := peek_token_end(data); - if tokenEnd == data.bytesLength { - return "EOF"; - } - return extract_string(data, data.offset, tokenEnd); -} - -// Find the end of the define directive (understanding endline backslashes) -// @note Tricky cases like comments hiding a backslash effect are not handled. -peek_define_end :: proc(data : ^ParserData) -> u32 { - defineEndOffset := data.offset; - for data.bytes[defineEndOffset-1] == '\\' || data.bytes[defineEndOffset] != '\n' { - defineEndOffset += 1; - } - return defineEndOffset; -} - -eat_comment :: proc(data : ^ParserData) { - if data.offset >= data.bytesLength || data.bytes[data.offset] != '/' { - return; - } - - // Line comment - if data.bytes[data.offset + 1] == '/' { - eat_line(data); - } // Range comment - else if data.bytes[data.offset + 1] == '*' { - data.offset += 2; - for data.bytes[data.offset] != '*' || data.bytes[data.offset + 1] != '/' { - data.offset += 1; - } - data.offset += 2; - } -} - -// Eat whitespaces -eat_whitespaces :: proc(data : ^ParserData) { - // Effective whitespace - for data.offset < data.bytesLength && - (data.bytes[data.offset] == ' ' || data.bytes[data.offset] == '\t' || - data.bytes[data.offset] == '\r' || data.bytes[data.offset] == '\n') { - if data.bytes[data.offset] == '\n' && data.bytes[data.offset] != '\\' { - data.foundFullReturn = true; - } - data.offset += 1; - } -} - -// Removes whitespaces and comments -eat_whitespaces_and_comments :: proc(data : ^ParserData) { - startOffset : u32 = 0xFFFFFFFF; - for startOffset != data.offset { - startOffset = data.offset; - eat_whitespaces(data); - eat_comment(data); - } -} - -// Eat full line -eat_line :: proc(data : ^ParserData) { - for ; data.bytes[data.offset] != '\n'; data.offset += 1 { - } -} - -// Eat a line, and repeat if it ends with a backslash -eat_define_lines :: proc(data : ^ParserData) { - for data.bytes[data.offset-1] == '\\' || data.bytes[data.offset] != '\n' { - data.offset += 1; - } -} - -// Eat next token -eat_token :: proc(data : ^ParserData) { - data.offset = peek_token_end(data); -} - -// Eat next token -check_and_eat_token :: proc(data : ^ParserData, expectedToken : string, loc := #caller_location) { - token := peek_token(data); - if token != expectedToken { - print_error(data, loc, "Expected ", expectedToken, " but found ", token, "."); - } - data.offset += cast(u32) len(token); -} - -// Check whether the next token is outside #define range -is_define_end :: proc(data : ^ParserData) -> bool { - defineEnd := peek_define_end(data); - tokenEnd := peek_token_end(data); - - return (defineEnd < tokenEnd); -} - -// Check if the current #define is a macro definition -is_define_macro :: proc(data : ^ParserData) -> bool { - startOffset := data.offset; - defer data.offset = startOffset; - - token := parse_any(data); - if token != "(" do return false; - - // Find the other parenthesis - parenthesesCount := 1; - for parenthesesCount != 0 { - token = parse_any(data); - if token == "(" do parenthesesCount += 1; - else if token == ")" do parenthesesCount -= 1; - } - - // Its a macro if after the parentheses, it's not the end - return !is_define_end(data); -} - -// @note Very slow function to get line number, -// use only for errors. -// @todo Well, this does not seem to work properly, UTF-8 problem? -get_line_column :: proc(data : ^ParserData) -> (u32, u32) { - line : u32 = 1; - column : u32 = 0; - for i : u32 = 0; i < data.offset; i += 1 { - if data.bytes[i] == '\n' { - column = 0; - line += 1; - } - else { - column += 1; - } - } - return line, column; -} diff --git a/core/bindgen/c-parser-nodes.odin b/core/bindgen/c-parser-nodes.odin deleted file mode 100644 index 0620e0187..000000000 --- a/core/bindgen/c-parser-nodes.odin +++ /dev/null @@ -1,132 +0,0 @@ -package bindgen - -DefineNode :: struct { - name : string, - value : LiteralValue, -} - -StructDefinitionNode :: struct { - name : string, - members : [dynamic]StructOrUnionMember, - forwardDeclared : bool, -} - -UnionDefinitionNode :: struct { - name : string, - members : [dynamic]StructOrUnionMember, -} - -EnumDefinitionNode :: struct { - name : string, - members : [dynamic]EnumMember, -} - -FunctionDeclarationNode :: struct { - name : string, - returnType : Type, - parameters : [dynamic]FunctionParameter, -} - -TypedefNode :: struct { - name : string, - type : Type, -} - -Nodes :: struct { - defines : [dynamic]DefineNode, - enumDefinitions : [dynamic]EnumDefinitionNode, - unionDefinitions : [dynamic]UnionDefinitionNode, - structDefinitions : [dynamic]StructDefinitionNode, - functionDeclarations : [dynamic]FunctionDeclarationNode, - typedefs : [dynamic]TypedefNode, -} - -LiteralValue :: union { - i64, - f64, - string, -} - -// Type, might be an array -Type :: struct { - base : BaseType, - dimensions : [dynamic]u64, // Array dimensions -} - -BaseType :: union { - BuiltinType, - PointerType, - IdentifierType, - FunctionType, - FunctionPointerType, -} - -BuiltinType :: enum { - Unknown, - Void, - Int, - UInt, - LongInt, - ULongInt, - LongLongInt, - ULongLongInt, - ShortInt, - UShortInt, - Char, - SChar, - UChar, - Float, - Double, - LongDouble, - - // Not defined by C language but in - Int8, - Int16, - Int32, - Int64, - UInt8, - UInt16, - UInt32, - UInt64, - Size, - SSize, - PtrDiff, - UIntPtr, - IntPtr, -} - -PointerType :: struct { - type : ^Type, // Pointer is there to prevent definition cycle. Null means void. -} - -IdentifierType :: struct { - name : string, - anonymous : bool, // An anonymous identifier can be hard-given a name in some contexts. -} - -FunctionType :: struct { - returnType : ^Type, // Pointer is there to prevent definition cycle. Null means void. - parameters : [dynamic]FunctionParameter, -} - -FunctionPointerType :: struct { - name : string, - returnType : ^Type, // Pointer is there to prevent definition cycle. Null means void. - parameters : [dynamic]FunctionParameter, -} - -EnumMember :: struct { - name : string, - value : i64, - hasValue : bool, -} - -StructOrUnionMember :: struct { - name : string, - type : Type, -} - -FunctionParameter :: struct { - name : string, - type : Type, -} diff --git a/core/bindgen/c-parser.odin b/core/bindgen/c-parser.odin deleted file mode 100644 index c3ef4937f..000000000 --- a/core/bindgen/c-parser.odin +++ /dev/null @@ -1,840 +0,0 @@ -package bindgen - -import "core:os" -import "core:fmt" -import "core:strings" -import "core:strconv" - -// Global counters -anonymousStructCount := 0; -anonymousUnionCount := 0; -anonymousEnumCount := 0; - -knownTypeAliases : map[string]Type; - -CustomHandler :: proc(data : ^ParserData); -CustomExpressionHandler :: proc(data : ^ParserData) -> LiteralValue; - -ParserOptions :: struct { - ignoredTokens : []string, - - // Handlers - customHandlers : map[string]CustomHandler, - customExpressionHandlers : map[string]CustomExpressionHandler, -} - -ParserData :: struct { - bytes : []u8, - bytesLength : u32, - offset : u32, - - // References - nodes : Nodes, - options : ^ParserOptions, - - // Knowned values - knownedLiterals : map[string]LiteralValue, - - // Whether we have eaten a '\n' character that has no backslash just before - foundFullReturn : bool, -} - -is_identifier :: proc(token : string) -> bool { - return (token[0] >= 'a' && token[0] <= 'z') || - (token[0] >= 'A' && token[0] <= 'Z') || - (token[0] == '_'); -} - -parse :: proc(bytes : []u8, options : ParserOptions, loc := #caller_location) -> Nodes { - options := options; - - data : ParserData; - data.bytes = bytes; - data.bytesLength = cast(u32) len(bytes); - data.options = &options; - - for data.offset = 0; data.offset < data.bytesLength; { - token := peek_token(&data); - if data.offset == data.bytesLength do break; - - if token in options.customHandlers { - options.customHandlers[token](&data); - } - else if token == "{" || token == "}" || token == ";" { - eat_token(&data); - } - else if token == "extern" { - check_and_eat_token(&data, "extern"); - } - else if token == "\"C\"" { - check_and_eat_token(&data, "\"C\""); - } - else if token == "#" { - parse_directive(&data); - } - else if token == "typedef" { - parse_typedef(&data); - } - else if is_identifier(token) { - parse_variable_or_function_declaration(&data); - } - else { - print_error(&data, loc, "Unexpected token: ", token, "."); - return data.nodes; - } - } - - return data.nodes; -} - -parse_any :: proc(data : ^ParserData) -> string { - offset := peek_token_end(data); - identifier := extract_string(data, data.offset, offset); - data.offset = offset; - return identifier; -} - -parse_identifier :: proc(data : ^ParserData, loc := #caller_location) -> string { - identifier := parse_any(data); - - if (identifier[0] < 'a' || identifier[0] > 'z') && - (identifier[0] < 'A' || identifier[0] > 'Z') && - (identifier[0] != '_') { - print_error(data, loc, "Expected identifier but found ", identifier, "."); - } - - return identifier; -} - -parse_type_dimensions :: proc(data : ^ParserData, type : ^Type) { - token := peek_token(data); - for token == "[" { - eat_token(data); - token = peek_token(data); - if token == "]" { - pointerType : PointerType; - pointerType.type = new(Type); - pointerType.type^ = type^; // Copy - type.base = pointerType; - delete(type.dimensions); - } else { - dimension := evaluate_i64(data); - append(&type.dimensions, cast(u64) dimension); - } - check_and_eat_token(data, "]"); - token = peek_token(data); - } -} - -// This will parse anything that look like a type: -// Builtin: char/int/float/... -// Struct-like: struct A/struct { ... }/enum E -// Function pointer: void (*f)(...) -// -// Definition permitted: If a struct-like definition is found, it will generate -// the according Node and return a corresponding type. -parse_type :: proc(data : ^ParserData, definitionPermitted := false) -> Type { - type : Type; - - // Eat qualifiers - token := peek_token(data); - if token == "const" { - eat_token(data); - token = peek_token(data); - } - - // Parse main type - if token == "struct" { - type.base = parse_struct_type(data, definitionPermitted); - } - else if token == "union" { - type.base = parse_union_type(data); - } - else if token == "enum" { - type.base = parse_enum_type(data); - } - else { - // Test builtin type - type.base = parse_builtin_type(data); - if type.base.(BuiltinType) == BuiltinType.Unknown { - // Basic identifier type - identifierType : IdentifierType; - identifierType.name = parse_identifier(data); - type.base = identifierType; - } - } - - // Eat qualifiers - token = peek_token(data); - if token == "const" { - eat_token(data); - token = peek_token(data); - } - - // Check if pointer - for token == "*" { - check_and_eat_token(data, "*"); - token = peek_token(data); - - pointerType : PointerType; - pointerType.type = new(Type); - pointerType.type^ = type; // Copy - - type.base = pointerType; - - // Eat qualifiers - if token == "const" { - eat_token(data); - token = peek_token(data); - } - } - - // Parse array dimensions if any. - parse_type_dimensions(data, &type); - - // ----- Function pointer type - - if token == "(" { - check_and_eat_token(data, "("); - check_and_eat_token(data, "*"); - - functionPointerType : FunctionPointerType; - functionPointerType.returnType = new(Type); - functionPointerType.returnType^ = type; - functionPointerType.name = parse_identifier(data); - - check_and_eat_token(data, ")"); - parse_function_parameters(data, &functionPointerType.parameters); - - type.base = functionPointerType; - } - - return type; -} - -parse_builtin_type :: proc(data : ^ParserData) -> BuiltinType { - previousBuiltinType := BuiltinType.Unknown; - intFound := false; - shortFound := false; - signedFound := false; - unsignedFound := false; - longCount := 0; - - for true { - token := peek_token(data); - - // Attribute - attributeFound := true; - if token == "long" do longCount += 1; - else if token == "short" do shortFound = true; - else if token == "unsigned" do unsignedFound = true; - else if token == "signed" do signedFound = true; - else do attributeFound = false; - if attributeFound { eat_token(data); continue; } - - // Known type alias - if token in knownTypeAliases { - builtinType, ok := knownTypeAliases[token].base.(BuiltinType); - if ok { - eat_token(data); - previousBuiltinType = builtinType; - } - break; - } - - // Classic type and standard types - if token == "void" { eat_token(data); return BuiltinType.Void; } - else if token == "int" { - eat_token(data); - intFound = true; - } - else if token == "float" { eat_token(data); return BuiltinType.Float; } - else if token == "double" { - eat_token(data); - if longCount == 0 do return BuiltinType.Double; - else do return BuiltinType.LongDouble; - } - else if token == "char" { - eat_token(data); - if signedFound do return BuiltinType.SChar; - else if unsignedFound do return BuiltinType.UChar; - else do return BuiltinType.Char; - } - else if token == "__int8" { - // @note :MicrosoftDumminess __intX are Microsoft's fixed-size integers - // https://docs.microsoft.com/fr-fr/cpp/cpp/int8-int16-int32-int64 - // and for unsigned version, they prefixed it with "unsigned"... - eat_token(data); - if unsignedFound do return BuiltinType.UInt8; - else do return BuiltinType.Int8; - } - else if token == "__int16" { - eat_token(data); - if unsignedFound do return BuiltinType.UInt16; - else do return BuiltinType.Int16; - } - else if token == "__int32" { - eat_token(data); - if unsignedFound do return BuiltinType.UInt32; - else do return BuiltinType.Int32; - } - else if token == "__int64" { - eat_token(data); - if unsignedFound do return BuiltinType.UInt64; - else do return BuiltinType.Int64; - } - else if token == "int8_t" { eat_token(data); return BuiltinType.Int8; } - else if token == "int16_t" { eat_token(data); return BuiltinType.Int16; } - else if token == "int32_t" { eat_token(data); return BuiltinType.Int32; } - else if token == "int64_t" { eat_token(data); return BuiltinType.Int64; } - else if token == "uint8_t" { eat_token(data); return BuiltinType.UInt8; } - else if token == "uint16_t" { eat_token(data); return BuiltinType.UInt16; } - else if token == "uint32_t" { eat_token(data); return BuiltinType.UInt32; } - else if token == "uint64_t" { eat_token(data); return BuiltinType.UInt64; } - else if token == "size_t" { eat_token(data); return BuiltinType.Size; } - else if token == "ssize_t" { eat_token(data); return BuiltinType.SSize; } - else if token == "ptrdiff_t" { eat_token(data); return BuiltinType.PtrDiff; } - else if token == "uintptr_t" { eat_token(data); return BuiltinType.UIntPtr; } - else if token == "intptr_t" { eat_token(data); return BuiltinType.IntPtr; } - - break; - } - - // Adapt previous builtin type - if previousBuiltinType == BuiltinType.ShortInt { - shortFound = true; - } - else if previousBuiltinType == BuiltinType.Int { - intFound = true; - } - else if previousBuiltinType == BuiltinType.LongInt { - longCount += 1; - } - else if previousBuiltinType == BuiltinType.LongLongInt { - longCount += 2; - } - else if previousBuiltinType == BuiltinType.UShortInt { - unsignedFound = true; - shortFound = true; - } - else if previousBuiltinType == BuiltinType.UInt { - unsignedFound = true; - } - else if previousBuiltinType == BuiltinType.ULongInt { - unsignedFound = true; - longCount += 1; - } - else if previousBuiltinType == BuiltinType.ULongLongInt { - unsignedFound = true; - longCount += 2; - } - else if (previousBuiltinType != BuiltinType.Unknown) { - return previousBuiltinType; // float, void, etc. - } - - // Implicit and explicit int - if intFound || shortFound || unsignedFound || signedFound || longCount > 0 { - if unsignedFound { - if shortFound do return BuiltinType.UShortInt; - if longCount == 0 do return BuiltinType.UInt; - if longCount == 1 do return BuiltinType.ULongInt; - if longCount == 2 do return BuiltinType.ULongLongInt; - } else { - if shortFound do return BuiltinType.ShortInt; - if longCount == 0 do return BuiltinType.Int; - if longCount == 1 do return BuiltinType.LongInt; - if longCount == 2 do return BuiltinType.LongLongInt; - } - } - - return BuiltinType.Unknown; -} - -parse_struct_type :: proc(data : ^ParserData, definitionPermitted : bool) -> IdentifierType { - check_and_eat_token(data, "struct"); - - type : IdentifierType; - token := peek_token(data); - - if !definitionPermitted || token != "{" { - type.name = parse_identifier(data); - token = peek_token(data); - } else { - type.name = tcat("AnonymousStruct", anonymousStructCount); - type.anonymous = true; - anonymousStructCount += 1; - } - - if token == "{" { - node := parse_struct_definition(data); - node.name = type.name; - } else if definitionPermitted { - // @note Whatever happens, we create a definition of the struct, - // as it might be used to forward declare it and then use it only with a pointer. - // This for instance the pattern for xcb_connection_t which definition - // is never known from user API. - node : StructDefinitionNode; - node.forwardDeclared = false; - node.name = type.name; - append(&data.nodes.structDefinitions, node); - } - - return type; -} - -parse_union_type :: proc(data : ^ParserData) -> IdentifierType { - check_and_eat_token(data, "union"); - - type : IdentifierType; - token := peek_token(data); - - if token != "{" { - type.name = parse_identifier(data); - token = peek_token(data); - } else { - type.name = tcat("AnonymousUnion", anonymousUnionCount); - type.anonymous = true; - anonymousUnionCount += 1; - } - - if token == "{" { - node := parse_union_definition(data); - node.name = type.name; - } - - return type; -} - -parse_enum_type :: proc(data : ^ParserData) -> IdentifierType { - check_and_eat_token(data, "enum"); - - type : IdentifierType; - token := peek_token(data); - - if token != "{" { - type.name = parse_identifier(data); - token = peek_token(data); - } else { - type.name = tcat("AnonymousEnum", anonymousEnumCount); - type.anonymous = true; - anonymousEnumCount += 1; - } - - if token == "{" { - node := parse_enum_definition(data); - node.name = type.name; - } - - return type; -} - -/** - * We only care about defines of some value - */ -parse_directive :: proc(data : ^ParserData) { - check_and_eat_token(data, "#"); - - token := peek_token(data); - if token == "define" { - parse_define(data); - } // We ignore all other directives - else { - eat_line(data); - } -} - -parse_define :: proc(data : ^ParserData) { - check_and_eat_token(data, "define"); - data.foundFullReturn = false; - - node : DefineNode; - node.name = parse_identifier(data); - - // Does it look like end? It might be a #define with no expression - if is_define_end(data) { - node.value = 1; - append(&data.nodes.defines, node); - data.knownedLiterals[node.name] = node.value; - } // Macros are ignored - else if is_define_macro(data) { - print_warning("Ignoring define macro for ", node.name, "."); - } - else { - literalValue, ok := evaluate(data); - if ok { - node.value = literalValue; - append(&data.nodes.defines, node); - data.knownedLiterals[node.name] = node.value; - } - else { - print_warning("Ignoring define expression for ", node.name, "."); - } - } - - // Evaluating the expression, we might have already eaten a full return, - // if so, do nothing. - if !data.foundFullReturn { - eat_define_lines(data); - } -} - -// @fixme Move -change_anonymous_node_name :: proc (data : ^ParserData, oldName : string, newName : string) -> bool { - for i := 0; i < len(data.nodes.structDefinitions); i += 1 { - if data.nodes.structDefinitions[i].name == oldName { - data.nodes.structDefinitions[i].name = newName; - return true; - } - } - - for i := 0; i < len(data.nodes.enumDefinitions); i += 1 { - if data.nodes.enumDefinitions[i].name == oldName { - data.nodes.enumDefinitions[i].name = newName; - return true; - } - } - - for i := 0; i < len(data.nodes.unionDefinitions); i += 1 { - if data.nodes.unionDefinitions[i].name == oldName { - data.nodes.unionDefinitions[i].name = newName; - return true; - } - } - - return false; -} - -/** - * Type aliasing. - * typedef ; - */ -parse_typedef :: proc(data : ^ParserData) { - check_and_eat_token(data, "typedef"); - - // @note Struct-like definitions (and such) - // are generated within type parsing. - // - // So that typedef struct { int foo; }* Ap; is valid. - - // Parsing type - node : TypedefNode; - node.type = parse_type(data, true); - - if sourceType, ok := node.type.base.(FunctionPointerType); ok { - node.name = sourceType.name; - } else { - node.name = parse_identifier(data); - } - - // Checking if function type - token := peek_token(data); - if token == "(" { - functionType : FunctionType; - functionType.returnType = new(Type); - functionType.returnType^ = node.type; - - parse_function_parameters(data, &functionType.parameters); - - node.type.base = functionType; - } - - // Checking if array - parse_type_dimensions(data, &node.type); - - // If the underlying type is anonymous, - // we just affect it the name. - addTypedefNode := true; - if identifierType, ok := node.type.base.(IdentifierType); ok { - if identifierType.anonymous { - addTypedefNode = !change_anonymous_node_name(data, identifierType.name, node.name); - } - } - - if addTypedefNode { - knownTypeAliases[node.name] = node.type; - append(&data.nodes.typedefs, node); - } - - check_and_eat_token(data, ";"); - - // @note Commented tool for debug - // fmt.println("Typedef: ", node.type, node.name); -} - -parse_struct_definition :: proc(data : ^ParserData) -> ^StructDefinitionNode { - node : StructDefinitionNode; - node.forwardDeclared = false; - parse_struct_or_union_members(data, &node.members); - - append(&data.nodes.structDefinitions, node); - return &data.nodes.structDefinitions[len(data.nodes.structDefinitions) - 1]; -} - -parse_union_definition :: proc(data : ^ParserData) -> ^UnionDefinitionNode { - node : UnionDefinitionNode; - parse_struct_or_union_members(data, &node.members); - - append(&data.nodes.unionDefinitions, node); - return &data.nodes.unionDefinitions[len(data.nodes.unionDefinitions) - 1]; -} - -parse_enum_definition :: proc(data : ^ParserData) -> ^EnumDefinitionNode { - node : EnumDefinitionNode; - parse_enum_members(data, &node.members); - - append(&data.nodes.enumDefinitions, node); - return &data.nodes.enumDefinitions[len(data.nodes.enumDefinitions) - 1]; -} - -/** - * { - * = , - * , - * } - */ -parse_enum_members :: proc(data : ^ParserData, members : ^[dynamic]EnumMember) { - check_and_eat_token(data, "{"); - - nextMemberValue : i64 = 0; - token := peek_token(data); - for token != "}" { - member : EnumMember; - member.name = parse_identifier(data); - member.hasValue = false; - - token = peek_token(data); - if token == "=" { - check_and_eat_token(data, "="); - - member.hasValue = true; - member.value = evaluate_i64(data); - nextMemberValue = member.value; - token = peek_token(data); - } else { - member.value = nextMemberValue; - } - - data.knownedLiterals[member.name] = member.value; - nextMemberValue += 1; - - // Eat until end, as this might be a complex expression that we couldn't understand - if token != "," && token != "}" { - print_warning("Parser cannot understand fully the expression of enum member ", member.name, "."); - for token != "," && token != "}" { - eat_token(data); - token = peek_token(data); - } - } - if token == "," { - check_and_eat_token(data, ","); - token = peek_token(data); - } - - append(members, member); - } - - check_and_eat_token(data, "}"); -} - -/** - * { - * ; - * , ; - * []; - * } - */ -parse_struct_or_union_members :: proc(data : ^ParserData, structOrUnionMembers : ^[dynamic]StructOrUnionMember) { - check_and_eat_token(data, "{"); - - // To ensure unique id - unamedCount := 0; - - token := peek_token(data); - for token != "}" { - member : StructOrUnionMember; - member.type = parse_type(data, true); - - for true { - // In the case of function pointer types, the name has been parsed - // during type inspection. - if type, ok := member.type.base.(FunctionPointerType); ok { - member.name = type.name; - } - else { - // Unamed (struct or union) - token = peek_token(data); - if !is_identifier(token) { - member.name = tcat("unamed", unamedCount); - unamedCount += 1; - } - else { - member.name = parse_identifier(data); - } - } - - parse_type_dimensions(data, &member.type); - - token = peek_token(data); - if token == ":" { - check_and_eat_token(data, ":"); - print_warning("Found bitfield in struct, which is not handled correctly."); - evaluate_i64(data); - token = peek_token(data); - } - - append(structOrUnionMembers, member); - - // Multiple declarations on one line - if token == "," { - check_and_eat_token(data, ","); - continue; - } - - break; - } - - check_and_eat_token(data, ";"); - token = peek_token(data); - } - - check_and_eat_token(data, "}"); -} - -parse_variable_or_function_declaration :: proc(data : ^ParserData) { - type := parse_type(data, true); - - // If it's just a type, it might be a struct definition - token := peek_token(data); - if token == ";" { - check_and_eat_token(data, ";"); - return; - } - - // Eat array declaration if any - // @fixme The return type of a function declaration will be wrong! - for data.bytes[data.offset] == '[' { - for data.bytes[data.offset] != ']' { - data.offset += 1; - } - data.offset += 1; - } - - name := parse_identifier(data); - - token = peek_token(data); - if token == "(" { - functionDeclarationNode := parse_function_declaration(data); - functionDeclarationNode.returnType = type; - functionDeclarationNode.name = name; - return; - } else if token == "[" { - // Eat whole array declaration - for data.bytes[data.offset] == '[' { - for data.bytes[data.offset] != ']' { - data.offset += 1; - } - data.offset += 1; - } - } - - // Global variable declaration (with possible multiple declarations) - token = peek_token(data); - - for true { - if token == "," { - print_warning("Found global variable declaration '", name, "', we won't generated any binding for it."); - check_and_eat_token(data, ","); - - name = parse_identifier(data); - token = peek_token(data); - continue; - } - else if token == ";" { - if name != "" { - print_warning("Found global variable declaration '", name, "', we won't generated any binding for it."); - } - check_and_eat_token(data, ";"); - break; - } - - // Global variable assignment, considered as constant define. - node : DefineNode; - - check_and_eat_token(data, "="); - literalValue, ok := evaluate(data); - if ok { - node.name = name; - node.value = literalValue; - append(&data.nodes.defines, node); - } - else { - print_warning("Ignoring global variable expression for '", name, "'."); - } - - name = ""; - token = peek_token(data); - } -} - -parse_function_declaration :: proc(data : ^ParserData) -> ^FunctionDeclarationNode { - node : FunctionDeclarationNode; - - parse_function_parameters(data, &node.parameters); - - // Function definition? Ignore it. - token := peek_token(data); - if token == "{" { - bracesCount := 1; - for true { - data.offset += 1; - if data.bytes[data.offset] == '{' do bracesCount += 1; - else if data.bytes[data.offset] == '}' do bracesCount -= 1; - if bracesCount == 0 do break; - } - data.offset += 1; - } // Function declaration - else { - check_and_eat_token(data, ";"); - } - - append(&data.nodes.functionDeclarations, node); - return &data.nodes.functionDeclarations[len(data.nodes.functionDeclarations) - 1]; -} - -parse_function_parameters :: proc(data : ^ParserData, parameters : ^[dynamic]FunctionParameter) { - check_and_eat_token(data, "("); - - token := peek_token(data); - for token != ")" { - parameter : FunctionParameter; - - token = peek_token(data); - if token == "." { - print_warning("A function accepts variadic arguments, this is currently not handled within generated code."); - - check_and_eat_token(data, "."); - check_and_eat_token(data, "."); - check_and_eat_token(data, "."); - break; - } else { - parameter.type = parse_type(data); - } - - // Check if named parameter - token = peek_token(data); - if token != ")" && token != "," { - parameter.name = parse_identifier(data); - parse_type_dimensions(data, ¶meter.type); - token = peek_token(data); - } - - if token == "," { - eat_token(data); - token = peek_token(data); - } - - append(parameters, parameter); - } - - check_and_eat_token(data, ")"); -} diff --git a/core/bindgen/errors.odin b/core/bindgen/errors.odin deleted file mode 100644 index 9564c5244..000000000 --- a/core/bindgen/errors.odin +++ /dev/null @@ -1,44 +0,0 @@ -package bindgen - -import "core:fmt" -import "core:os" - -seenWarnings : map[string]bool; - -print_warning :: proc(args : ..any) { - message := tcat(..args); - - if !seenWarnings[message] { - fmt.eprint("[bindgen] Warning: ", message, "\n"); - seenWarnings[message] = true; - } -} - -print_error :: proc(data : ^ParserData, loc := #caller_location, args : ..any) { - message := tcat(..args); - - min : u32 = 0; - for i := data.offset - 1; i > 0; i -= 1 { - if data.bytes[i] == '\n' { - min = i + 1; - break; - } - } - - max := min + 200; - for i := min + 1; i < max; i += 1 { - if data.bytes[i] == '\n' { - max = i; - break; - } - } - - line, _ := get_line_column(data); - - fmt.eprint("[bindgen] Error: ", message, "\n"); - fmt.eprint("[bindgen] ... from ", loc.procedure, "\n"); - fmt.eprint("[bindgen] ... at line ", line, " within this context:\n"); - fmt.eprint("> ", extract_string(data, min, max), "\n"); - - os.exit(1); -} diff --git a/core/bindgen/generator-clean.odin b/core/bindgen/generator-clean.odin deleted file mode 100644 index 8dd837b10..000000000 --- a/core/bindgen/generator-clean.odin +++ /dev/null @@ -1,284 +0,0 @@ -package bindgen - -import "core:fmt" - -// Prevent keywords clashes and other tricky cases -clean_identifier :: proc(name : string) -> string { - name := name; - - if name == "" { - return name; - } - - // Starting with _? Try removing that. - for true { - if name[0] == '_' { - name = name[1:]; - } - else { - break; - } - } - - // Number - if name[0] >= '0' && name[0] <= '9' { - return tcat("_", name); - } // Keywords clash - else if name == "map" || name == "proc" || name == "opaque" || name == "in" { - return tcat("_", name); - } // Jai keywords clash - else if name == "context" || - name == "float32" || name == "float64" || - name == "s8" || name == "s16" || name == "s32" || name == "s64" || - name == "u8" || name == "u16" || name == "u32" || name == "u64" { - return tcat("_", name); - } - - return name; -} - -clean_variable_name :: proc(name : string, options : ^GeneratorOptions) -> string { - name := name; - name = change_case(name, options.variableCase); - return clean_identifier(name); -} - -clean_pseudo_type_name :: proc(structName : string, options : ^GeneratorOptions) -> string { - structName := structName; - structName = remove_postfixes(structName, options.pseudoTypePostfixes, options.pseudoTypeTransparentPostfixes); - structName = remove_prefixes(structName, options.pseudoTypePrefixes, options.pseudoTypeTransparentPrefixes); - structName = change_case(structName, options.pseudoTypeCase); - return structName; -} - -// Clean up the enum name so that it can be used to remove the prefix from enum values. -clean_enum_name_for_prefix_removal :: proc(enumName : string, options : ^GeneratorOptions) -> (string, [dynamic]string) { - enumName := enumName; - - if !options.enumValueNameRemove { - return enumName, nil; - } - - // Remove postfix and use same case convention as the enum values - removedPostfixes : [dynamic]string; - enumName, removedPostfixes = remove_postfixes_with_removed(enumName, options.enumValueNameRemovePostfixes); - enumName = change_case(enumName, options.enumValueCase); - return enumName, removedPostfixes; -} - -clean_enum_value_name :: proc(valueName : string, enumName : string, postfixes : []string, options : ^GeneratorOptions) -> string { - valueName := valueName; - - valueName = remove_prefixes(valueName, options.enumValuePrefixes, options.enumValueTransparentPrefixes); - valueName = remove_postfixes(valueName, postfixes, options.enumValueTransparentPostfixes); - - if options.enumValueNameRemove { - valueName = remove_prefixes(valueName, []string{enumName}); - } - - valueName = change_case(valueName, options.enumValueCase); - - return clean_identifier(valueName); -} - -clean_function_name :: proc(functionName : string, options : ^GeneratorOptions) -> string { - functionName := functionName; - functionName = remove_prefixes(functionName, options.functionPrefixes, options.functionTransparentPrefixes); - functionName = remove_postfixes(functionName, options.definePostfixes, options.defineTransparentPostfixes); - functionName = change_case(functionName, options.functionCase); - return functionName; -} - -clean_define_name :: proc(defineName : string, options : ^GeneratorOptions) -> string { - defineName := defineName; - defineName = remove_prefixes(defineName, options.definePrefixes, options.defineTransparentPrefixes); - defineName = remove_postfixes(defineName, options.definePostfixes, options.defineTransparentPostfixes); - defineName = change_case(defineName, options.defineCase); - return defineName; -} - -// Convert to Odin's types -clean_type :: proc(data : ^GeneratorData, type : Type, baseTab : string = "", explicitSharpType := true) -> string { - output := ""; - - for dimension in type.dimensions { - output = tcat(output, "[", dimension, "]"); - } - output = tcat(output, clean_base_type(data, type.base, baseTab, explicitSharpType)); - - return output; -} - -clean_base_type :: proc(data : ^GeneratorData, baseType : BaseType, baseTab : string = "", explicitSharpType := true) -> string { - options := data.options; - - if _type, ok := baseType.(BuiltinType); ok { - if _type == BuiltinType.Void do return options.mode == "jai" ? "void" : ""; - else if _type == BuiltinType.Int do return options.mode == "jai" ? "s64" : "_c.int"; - else if _type == BuiltinType.UInt do return options.mode == "jai" ? "u64" :"_c.uint"; - else if _type == BuiltinType.LongInt do return options.mode == "jai" ? "s64" :"_c.long"; - else if _type == BuiltinType.ULongInt do return options.mode == "jai" ? "u64" :"_c.ulong"; - else if _type == BuiltinType.LongLongInt do return options.mode == "jai" ? "s64" :"_c.longlong"; - else if _type == BuiltinType.ULongLongInt do return options.mode == "jai" ? "u64" :"_c.ulonglong"; - else if _type == BuiltinType.ShortInt do return options.mode == "jai" ? "s16" :"_c.short"; - else if _type == BuiltinType.UShortInt do return options.mode == "jai" ? "u16" :"_c.ushort"; - else if _type == BuiltinType.Char do return options.mode == "jai" ? "u8" :"_c.char"; - else if _type == BuiltinType.SChar do return options.mode == "jai" ? "s8" :"_c.schar"; - else if _type == BuiltinType.UChar do return options.mode == "jai" ? "u8" :"_c.uchar"; - else if _type == BuiltinType.Float do return options.mode == "jai" ? "float32" :"_c.float"; - else if _type == BuiltinType.Double do return options.mode == "jai" ? "float64" :"_c.double"; - else if _type == BuiltinType.LongDouble { - print_warning("Found long double which is currently not supported. Fallback to double in generated code."); - return options.mode == "jai" ? "double" :"_c.double"; - } - else if _type == BuiltinType.Int8 do return options.mode == "jai" ? "s8" :"i8"; - else if _type == BuiltinType.Int16 do return options.mode == "jai" ? "s16" :"i16"; - else if _type == BuiltinType.Int32 do return options.mode == "jai" ? "s32" :"i32"; - else if _type == BuiltinType.Int64 do return options.mode == "jai" ? "s64" :"i64"; - else if _type == BuiltinType.UInt8 do return options.mode == "jai" ? "u8" :"u8"; - else if _type == BuiltinType.UInt16 do return options.mode == "jai" ? "u16" :"u16"; - else if _type == BuiltinType.UInt32 do return options.mode == "jai" ? "u32" :"u32"; - else if _type == BuiltinType.UInt64 do return options.mode == "jai" ? "u64" :"u64"; - else if _type == BuiltinType.Size do return options.mode == "jai" ? "u64" :"_c.size_t"; - else if _type == BuiltinType.SSize do return options.mode == "jai" ? "u64" :"_c.ssize_t"; - else if _type == BuiltinType.PtrDiff do return options.mode == "jai" ? "s64" :"_c.ptrdiff_t"; - else if _type == BuiltinType.UIntPtr do return options.mode == "jai" ? "u64" :"_c.uintptr_t"; - else if _type == BuiltinType.IntPtr do return options.mode == "jai" ? "s64" :"_c.intptr_t"; - } - else if _type, ok := baseType.(PointerType); ok { - if options.mode == "jai" { - // Hide pointers to types that were not declared. - if !is_known_base_type(data, _type.type.base) { - print_warning("*", _type.type.base.(IdentifierType).name, " replaced by *void as the pointed type is unknown."); - return "*void"; - } - } else { - if __type, ok := _type.type.base.(BuiltinType); ok { - if __type == BuiltinType.Void do return "rawptr"; - else if __type == BuiltinType.Char do return "cstring"; - } - } - name := clean_type(data, _type.type^, baseTab); - return tcat(options.mode == "jai" ? "*" :"^", name); - } - else if _type, ok := baseType.(IdentifierType); ok { - return clean_pseudo_type_name(_type.name, options); - } - else if _type, ok := baseType.(FunctionType); ok { - output : string; - if explicitSharpType { - output = "#type "; - } - output = tcat(output, options.mode == "jai" ? "(" :"proc("); - parameters := clean_function_parameters(data, _type.parameters, baseTab); - output = tcat(output, parameters, ")"); - - returnType := clean_type(data, _type.returnType^); - if len(returnType) > 0 && returnType != "void" { - output = tcat(output, " -> ", returnType); - } - return output; - } - else if _type, ok := baseType.(FunctionPointerType); ok { - output : string; - if explicitSharpType { - output = "#type "; - } - output = tcat(output, options.mode == "jai" ? "(" :"proc("); - parameters := clean_function_parameters(data, _type.parameters, baseTab); - output = tcat(output, parameters, ")"); - - returnType := clean_type(data, _type.returnType^); - if len(returnType) > 0 && returnType != "void" { - output = tcat(output, " -> ", returnType); - } - - if options.mode == "jai" { - output = tcat(output, " #foreign"); - } - return output; - } - - return ""; -} - -clean_function_parameters :: proc(data : ^GeneratorData, parameters : [dynamic]FunctionParameter, baseTab : string) -> string { - output := ""; - options := data.options; - - // Special case: function(void) does not really have a parameter - if len(parameters) == 1 { - if _type, ok := parameters[0].type.base.(BuiltinType); ok { - if _type == BuiltinType.Void { - return ""; - } - } - } - - tab := ""; - if options.mode == "jai" { // @note :OdinCodingStyle Odin forces a coding style, now. Ugh. - if (len(parameters) > 1) { - output = tcat(output, "\n"); - tab = tcat(baseTab, " "); - } - } - - unamedParametersCount := 0; - for parameter, i in parameters { - type := clean_type(data, parameter.type); - - name : string; - if len(parameter.name) != 0 { - name = clean_variable_name(parameter.name, options); - } else { - name = tcat("unamed", unamedParametersCount); - unamedParametersCount += 1; - } - - output = tcat(output, tab, name, " : ", type); - - if i != len(parameters) - 1 { - if options.mode == "jai" { // @note :OdinCodingStyle - output = tcat(output, ",\n"); - } else { - output = tcat(output, ", "); - } - } - } - - if (len(parameters) > 1) { - if options.mode == "jai" { // @note :OdinCodingStyle - output = tcat(output, "\n", baseTab); - } - } - - return output; -} - -is_known_base_type :: proc(data : ^GeneratorData, baseType : BaseType) -> bool { - if _type, ok := baseType.(IdentifierType); ok { - for it in data.nodes.typedefs { - if _type.name == it.name { - return true; - } - } - for it in data.nodes.structDefinitions { - if _type.name == it.name { - return true; - } - } - for it in data.nodes.enumDefinitions { - if _type.name == it.name { - return true; - } - } - for it in data.nodes.unionDefinitions { - if _type.name == it.name { - return true; - } - } - return false; - } - - return true; -} diff --git a/core/bindgen/generator-export.odin b/core/bindgen/generator-export.odin deleted file mode 100644 index a04113ed9..000000000 --- a/core/bindgen/generator-export.odin +++ /dev/null @@ -1,166 +0,0 @@ -package bindgen - -import "core:os" -import "core:fmt" - -export_defines :: proc(data : ^GeneratorData) { - for node in data.nodes.defines { - defineName := clean_define_name(node.name, data.options); - - // @fixme fprint of float numbers are pretty badly handled, - // just has a 10^-3 precision. - fcat(data.handle, defineName, " :: ", node.value, ";\n"); - } - fcat(data.handle, "\n"); -} - -export_typedefs :: proc(data : ^GeneratorData) { - for node in data.nodes.typedefs { - name := clean_pseudo_type_name(node.name, data.options); - type := clean_type(data, node.type, "", true); - if name == type do continue; - fcat(data.handle, name, " :: ", type, ";\n"); - } - fcat(data.handle, "\n"); -} - -export_enums :: proc(data : ^GeneratorData) { - for node in data.nodes.enumDefinitions { - enumName := clean_pseudo_type_name(node.name, data.options); - - if data.options.mode == "jai" { - consideredFlags := false; - for postfix in data.options.enumConsideredFlagsPostfixes { - if ends_with(node.name, postfix) { - consideredFlags = true; - break; - } - } - - if consideredFlags { - fcat(data.handle, enumName, " :: enum_flags u32 {"); - } else { - fcat(data.handle, enumName, " :: enum s32 {"); - } - } else { - fcat(data.handle, enumName, " :: enum i32 {"); - } - - postfixes : [dynamic]string; - enumName, postfixes = clean_enum_name_for_prefix_removal(enumName, data.options); - - // Changing the case of postfixes to the enum value one, - // so that they can be removed. - enumValueCase := find_case(node.members[0].name); - for postfix, i in postfixes { - postfixes[i] = change_case(postfix, enumValueCase); - } - - // And changing the case of enumName to the enum value one - enumName = change_case(enumName, enumValueCase); - - // Merging enum value postfixes with postfixes that have been removed from the enum name. - for postfix in data.options.enumValuePostfixes { - append(&postfixes, postfix); - } - - export_enum_members(data, node.members, enumName, postfixes[:]); - fcat(data.handle, data.options.mode == "jai" ? "}\n" : "};\n"); - fcat(data.handle, "\n"); - } -} - -export_structs :: proc(data : ^GeneratorData) { - for node in data.nodes.structDefinitions { - structName := clean_pseudo_type_name(node.name, data.options); - fcat(data.handle, structName, " :: struct {"); - export_struct_or_union_members(data, node.members); - fcat(data.handle, data.options.mode == "jai" ? "}\n" : "};\n"); - fcat(data.handle, "\n"); - } -} - -export_unions :: proc(data : ^GeneratorData) { - for node in data.nodes.unionDefinitions { - unionName := clean_pseudo_type_name(node.name, data.options); - fcat(data.handle, unionName, data.options.mode == "jai" ? " :: union {" : " :: struct #raw_union {"); - export_struct_or_union_members(data, node.members); - fcat(data.handle, data.options.mode == "jai" ? "}\n" : "};\n"); - fcat(data.handle, "\n"); - } -} - -export_functions :: proc(data : ^GeneratorData) { - for node in data.nodes.functionDeclarations { - functionName := clean_function_name(node.name, data.options); - if data.options.mode == "jai" { - fcat(data.handle, functionName, " :: ("); - } else { - fcat(data.handle, " @(link_name=\"", node.name, "\")\n"); - fcat(data.handle, " ", functionName, " :: proc("); - } - parameters := clean_function_parameters(data, node.parameters, data.options.mode == "jai" ? "" : " "); - fcat(data.handle, parameters, ")"); - returnType := clean_type(data, node.returnType); - if len(returnType) > 0 { - fcat(data.handle, " -> ", returnType); - } - if data.options.mode == "jai" { - fcat(data.handle, " #foreign ", data.foreignLibrary, " \"", node.name ,"\";\n"); - } else { - fcat(data.handle, " ---;\n"); - } - fcat(data.handle, "\n"); - } -} - -export_enum_members :: proc(data : ^GeneratorData, members : [dynamic]EnumMember, enumName : string, postfixes : []string) { - if (len(members) > 0) { - fcat(data.handle, "\n"); - } - - cleanedMembers : [dynamic]EnumMember; - for member in members { - cleanedMember : EnumMember; - cleanedMember.hasValue = member.hasValue; - cleanedMember.value = member.value; - cleanedMember.name = clean_enum_value_name(member.name, enumName, postfixes, data.options); - - if len(cleanedMember.name) == 0 { - // print_warning("Enum member ", member.name, " resolves to an empty name. Ignoring it."); - continue; - } - - // Ensuring that we don't collide with an other enum member. - foundCopy := false; - for existingCleanedMember in cleanedMembers { - if cleanedMember.name == existingCleanedMember.name && - cleanedMember.hasValue == existingCleanedMember.hasValue && - cleanedMember.value == existingCleanedMember.value { - print_warning("Enum member ", member.name, " is duplicated once cleaned. Keeping only one copy."); - foundCopy = true; - break; - } - } - if foundCopy do continue; - - fcat(data.handle, " ", cleanedMember.name); - if member.hasValue { - fcat(data.handle, data.options.mode == "jai" ? " :: " : " = ", member.value); - } - fcat(data.handle, data.options.mode == "jai" ? ";\n" : ",\n"); - - append(&cleanedMembers, cleanedMember); - } -} - -export_struct_or_union_members :: proc(data : ^GeneratorData, members : [dynamic]StructOrUnionMember) { - if (len(members) > 0) { - fcat(data.handle, "\n"); - } - for member in members { - type := clean_type(data, member.type, " "); - name := clean_variable_name(member.name, data.options); - fcat(data.handle, " ", name, " : ", type, data.options.mode == "jai" ? ";\n" : ",\n"); - } -} diff --git a/core/bindgen/generator-helpers.odin b/core/bindgen/generator-helpers.odin deleted file mode 100644 index a3b37f4f6..000000000 --- a/core/bindgen/generator-helpers.odin +++ /dev/null @@ -1,392 +0,0 @@ -package bindgen - -import "core:fmt" -import "core:os" -import "core:io" -import "core:strings" -import "core:unicode/utf8" - -Case :: enum { - Unknown, - Camel, - Constant, - Kebab, - Pascal, - Snake, -} - -WordCase :: enum { - Unknown, - Up, - Low, - FirstUp, - // When first upping, numbers are followed always by a capital - FirstUpNumberReset, -} - -// Change a character to a capital. -to_uppercase :: proc(c : rune) -> rune { - c := c; - if c >= 'a' && c <= 'z' { - c = c - 'a' + 'A'; - } - return c; -} - -// Change a character to lowercase. -to_lowercase :: proc(c : rune) -> rune { - c := c; - if c >= 'A' && c <= 'Z' { - c = c - 'A' + 'a'; - } - return c; -} - -// @note Stolen tprint and fprint from fmt package, because it was confusing due to args: ..any and sep default parameter. -tcat :: proc(args: ..any) -> string { - return fmt.tprint(args=args, sep=""); -} - -fcat :: proc(fd: os.Handle, args: ..any) -> int { - return fmt.fprint(fd=fd, args=args, sep=""); -} - -// Change the case convention of a word. -change_word_case :: proc(str : string, targetCase : WordCase) -> string { - newStr : string; - if targetCase == WordCase.Up { - for c in str { - newStr = tcat(newStr, to_uppercase(c)); - } - } - else if targetCase == WordCase.Low { - for c in str { - newStr = tcat(newStr, to_lowercase(c)); - } - } - else if targetCase == WordCase.FirstUp { - for c, i in str { - if i == 0 { - newStr = tcat(newStr, to_uppercase(c)); - } else { - newStr = tcat(newStr, to_lowercase(c)); - } - } - } - else if targetCase == WordCase.FirstUpNumberReset { - for c, i in str { - if i == 0 || (str[i - 1] >= '0' && str[i - 1] <= '9') { - newStr = tcat(newStr, to_uppercase(c)); - } else { - newStr = tcat(newStr, to_lowercase(c)); - } - } - } - return newStr; -} - -// Change the case convention of a string by detecting original convention, -// then splitting it into words. -change_case :: proc(str : string, targetCase : Case) -> string { - if targetCase == Case.Unknown { - return str; - } - - // Split - parts := autosplit_string(str); - - // Join - newStr : string; - if targetCase == Case.Pascal { - for part, i in parts { - newStr = tcat(newStr, change_word_case(part, WordCase.FirstUpNumberReset)); - } - } - else if targetCase == Case.Snake { - for part, i in parts { - newStr = tcat(newStr, change_word_case(part, WordCase.Low), (i != len(parts) - 1) ? "_" : ""); - } - } - else if targetCase == Case.Kebab { - for part, i in parts { - newStr = tcat(newStr, change_word_case(part, WordCase.Low), (i != len(parts) - 1) ? "-" : ""); - } - } - else if targetCase == Case.Camel { - for part, i in parts { - if i == 0 { - newStr = tcat(newStr, change_word_case(part, WordCase.Low)); - } else { - newStr = tcat(newStr, change_word_case(part, WordCase.FirstUpNumberReset)); - } - } - } - else if targetCase == Case.Constant { - for part, i in parts { - newStr = tcat(newStr, change_word_case(part, WordCase.Up), (i != len(parts) - 1) ? "_" : ""); - } - } - - return newStr; -} - -// Identify the case of the provided string. -// Full lowercase with no separator is identified as camelCase. -find_case :: proc(str : string) -> Case { - refuted : bool; - - // CONSTANT_CASE - refuted = false; - for c in str { - if (c != '_') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') { - refuted = true; - break; - } - } - if !refuted do return Case.Constant; - - for c in str { - // snake_case - if c == '_' { - return Case.Snake; - } // kebab-case - else if c == '-' { - return Case.Kebab; - } - } - - // PascalCase - if str[0] >= 'A' && str[0] <= 'Z' { - return Case.Pascal; - } - - // camelCase - return Case.Camel; -} - -// Splits the string according to detected case. -// HeyBuddy -> {"Hey", "Buddy"} -// hey-buddy -> {"hey", "buddy"} -// _hey_buddy -> {"", "hey", "buddy"} -// and such... -autosplit_string :: proc(str : string) -> [dynamic]string { - lowCount := 0; - upCount := 0; - for c in str { - // If any '_', split according to that (CONSTANT_CASE or snake_case) - if c == '_' { - return split_from_separator(str, '_'); - } // If any '-', split according to that (kebab-case) - else if c == '-' { - return split_from_separator(str, '-'); - } - else if c >= 'a' && c <= 'z' { - lowCount += 1; - } - else if c >= 'A' && c <= 'Z' { - upCount += 1; - } - } - - // If it seems to be only one word - if lowCount == 0 || upCount == 0 { - parts : [dynamic]string; - append(&parts, str); - return parts; - } - - // Split at each uppercase letter (PascalCase or camelCase) - return split_from_capital(str); -} - -split_from_separator :: proc(str : string, sep : rune) -> [dynamic]string { - parts : [dynamic]string; - - lastI := 0; - - // Empty strings for starting separators in string - for c in str { - if c == sep { - append(&parts, ""); - lastI += 1; - } else { - break; - } - } - - // Ignore non letter prefix - if lastI == 0 { - for c in str { - if (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') { - lastI += 1; - } - else { - break; - } - } - } - - for c, i in str { - if i > lastI + 1 && c == sep { - append(&parts, str[lastI:i]); - lastI = i + 1; - } - } - - append(&parts, str[lastI:]); - - return parts; -} - -split_from_capital :: proc(str : string) -> [dynamic]string { - parts : [dynamic]string; - - // Ignore non letter prefix - lastI := 0; - for c in str { - if (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') { - lastI += 1; - } - else { - break; - } - } - - // We want to handle: - // myBrainIsCRAZY -> my Brain Is Crazy - // myCRAZYBrain -> my CRAZY Brain - // SOLO -> SOLO - - // Do split - for i := 1; i < len(str); i += 1 { - if str[i] >= 'A' && str[i] <= 'Z' { - // Do not split too much if it seems to be a capitalized word - if (lastI == i - 1) && (str[lastI] >= 'A' && str[lastI] <= 'Z') { - for ; i + 1 < len(str); i += 1 { - if str[i + 1] < 'A' || str[i + 1] > 'Z' { - break; - } - } - if (i + 1 == len(str)) && (str[i] >= 'A' && str[i] <= 'Z') { - i += 1; - } - } - - append(&parts, str[lastI:i]); - lastI = i; - } - } - - if lastI != len(str) { - append(&parts, str[lastI:]); - } - - return parts; -} - -// Check if str if prefixed with any of the provided strings, -// even combinaisons of those, and remove them. -remove_prefixes :: proc(str : string, prefixes : []string, transparentPrefixes : []string = nil) -> string { - str := str; - transparentStr := ""; - - found := true; - for found { - found = false; - - // Remove effective prefixes - for prefix in prefixes { - if len(str) >= len(prefix) && - str[:len(prefix)] == prefix { - str = str[len(prefix):]; - if len(str) != 0 && (str[0] == '_' || str[0] == '-') { - str = str[1:]; - } - found = true; - break; - } - } - - if found do continue; - - // Remove transparent ones, only one by one, - // as we want effective ones to be fully removed. - for prefix in transparentPrefixes { - if len(str) >= len(prefix) && - str[:len(prefix)] == prefix { - str = str[len(prefix):]; - transparentStr = tcat(transparentStr, prefix); - if len(str) != 0 && (str[0] == '_' || str[0] == '-') { - str = str[1:]; - transparentStr = tcat(transparentStr, '_'); - } - found = true; - break; - } - } - } - - return tcat(transparentStr, str); -} - -// Check if str if postfixes with any of the provided strings, -// even combinaisons of those, and remove them. -remove_postfixes_with_removed :: proc( - str : string, - postfixes : []string, - transparentPostfixes : []string = nil) -> (string, [dynamic]string) { - str := str; - removedPostfixes : [dynamic]string; - transparentStr := ""; - - found := true; - for found { - found = false; - - // Remove effective postfixes - for postfix in postfixes { - if ends_with(str, postfix) { - str = str[:len(str) - len(postfix)]; - if len(str) != 0 && (str[len(str)-1] == '_' || str[len(str)-1] == '-') { - str = str[:len(str)-1]; - } - append(&removedPostfixes, postfix); - found = true; - break; - } - } - - if found do continue; - - // Remove transparent ones, only one by one, - // as we want effective ones to be fully removed. - for postfix in transparentPostfixes { - if ends_with(str, postfix) { - str = str[:len(str) - len(postfix)]; - transparentStr = tcat(postfix, transparentStr); - if len(str) != 0 && (str[len(str)-1] == '_' || str[len(str)-1] == '-') { - str = str[:len(str)-1]; - transparentStr = tcat('_', transparentStr); - } - found = true; - break; - } - } - } - - return tcat(str, transparentStr), removedPostfixes; -} - -remove_postfixes :: proc( - str : string, - postfixes : []string, - transparentPostfixes : []string = nil) -> string { - str := str; - removedPostfixes : [dynamic]string; - str, removedPostfixes = remove_postfixes_with_removed(str, postfixes, transparentPostfixes); - return str; -} - -ends_with :: proc(str : string, postfix : string) -> bool { - return len(str) >= len(postfix) && str[len(str) - len(postfix):] == postfix; -} diff --git a/core/bindgen/generator.odin b/core/bindgen/generator.odin deleted file mode 100644 index 3ef3d69c0..000000000 --- a/core/bindgen/generator.odin +++ /dev/null @@ -1,205 +0,0 @@ -/** - * Odin binding generator from C header data. - */ - -package bindgen - -import "core:os" -import "core:fmt" -import "core:runtime" - -GeneratorOptions :: struct { - mode : string, // "odin" or "jai" - - // Variable - variableCase : Case, - - // Defines - definePrefixes : []string, - defineTransparentPrefixes : []string, - definePostfixes : []string, - defineTransparentPostfixes : []string, - defineCase : Case, - - // Pseudo-types - pseudoTypePrefixes : []string, - pseudoTypeTransparentPrefixes : []string, - pseudoTypePostfixes : []string, - pseudoTypeTransparentPostfixes : []string, - pseudoTypeCase : Case, - - // Enums - enumConsideredFlagsPostfixes : []string, - - // Functions - functionPrefixes : []string, - functionTransparentPrefixes : []string, - functionPostfixes : []string, - functionTransparentPostfixes : []string, - functionCase : Case, - - // Enum values - enumValuePrefixes : []string, - enumValueTransparentPrefixes : []string, - enumValuePostfixes : []string, - enumValueTransparentPostfixes : []string, - enumValueCase : Case, - enumValueNameRemove : bool, - enumValueNameRemovePostfixes : []string, - - parserOptions : ParserOptions, -} - -GeneratorData :: struct { - handle : os.Handle, - nodes : Nodes, - - // References - foreignLibrary : string, - options : ^GeneratorOptions, -} - -generate :: proc( - packageName : string, - foreignLibrary : string, - outputFile : string, - headerFiles : []string, - options : GeneratorOptions, -) { - options := options; - data : GeneratorData; - data.options = &options; - data.foreignLibrary = foreignLibrary; - - if options.mode == "" { - options.mode = "odin"; - } - - // Outputing odin file - errno : os.Errno; - - // chmod 664 when creating file - mode: int = 0; - when os.OS == "linux" || os.OS == "darwin" { - mode = os.S_IRUSR | os.S_IWUSR | os.S_IRGRP | os.S_IWGRP | os.S_IROTH; - } - - data.handle, errno = os.open(outputFile, os.O_WRONLY | os.O_CREATE | os.O_TRUNC, mode); - if errno != 0 { - fmt.eprint("[bindgen] Unable to write to output file ", outputFile, " (", errno ,")\n"); - return; - } - defer os.close(data.handle); - - if options.mode == "jai" { - fcat(data.handle, foreignLibrary, " :: #foreign_library \"", foreignLibrary, "\";\n"); - fcat(data.handle, "\n"); - } else { - fcat(data.handle, "package ", packageName, "\n"); - fcat(data.handle, "\n"); - fcat(data.handle, "foreign import \"", foreignLibrary, "\"\n"); - fcat(data.handle, "\n"); - fcat(data.handle, "import _c \"core:c\"\n"); - fcat(data.handle, "\n"); - } - - // Parsing header files - anonymousStructCount = 0; - anonymousUnionCount = 0; - anonymousEnumCount = 0; - - for headerFile in headerFiles { - bytes, ok := os.read_entire_file(headerFile); - if !ok { - fmt.eprint("[bindgen] Unable to read file ", headerFile, "\n"); - return; - } - - // We fuse the SOAs - headerNodes := parse(bytes, options.parserOptions); - merge_generic_nodes(&data.nodes.defines, &headerNodes.defines); - merge_generic_nodes(&data.nodes.enumDefinitions, &headerNodes.enumDefinitions); - merge_generic_nodes(&data.nodes.unionDefinitions, &headerNodes.unionDefinitions); - merge_forward_declared_nodes(&data.nodes.structDefinitions, &headerNodes.structDefinitions); - merge_generic_nodes(&data.nodes.functionDeclarations, &headerNodes.functionDeclarations); - merge_generic_nodes(&data.nodes.typedefs, &headerNodes.typedefs); - } - - // Exporting - export_defines(&data); - export_typedefs(&data); - export_enums(&data); - export_structs(&data); - export_unions(&data); - - // Foreign block for functions - if options.mode != "jai" { - foreignLibrarySimple := simplify_library_name(foreignLibrary); - fcat(data.handle, "@(default_calling_convention=\"c\")\n"); - fcat(data.handle, "foreign ", foreignLibrarySimple, " {\n"); - fcat(data.handle, "\n"); - } - - export_functions(&data); - - if options.mode != "jai" { - fcat(data.handle, "}\n"); - } -} - -// system:foo.lib -> foo -simplify_library_name :: proc(libraryName : string) -> string { - startOffset := 0; - endOffset := len(libraryName); - - for c, i in libraryName { - if startOffset == 0 && c == ':' { - startOffset = i + 1; - } - else if c == '.' { - endOffset = i; - break; - } - } - - return libraryName[startOffset:endOffset]; -} - -merge_generic_nodes :: proc(nodes : ^$T, headerNodes : ^T) { - for headerNode in headerNodes { - // Check that there are no duplicated nodes (due to forward declaration or such) - duplicatedIndex := -1; - for i := 0; i < len(nodes); i += 1 { - node := nodes[i]; - if node.name == headerNode.name { - duplicatedIndex = i; - break; - } - } - - if duplicatedIndex < 0 { - append(nodes, headerNode); - } - } -} - -merge_forward_declared_nodes :: proc(nodes : ^$T, headerNodes : ^T) { - for headerNode in headerNodes { - // Check that there are no duplicated nodes (due to forward declaration or such) - duplicatedIndex := -1; - for i := 0; i < len(nodes); i += 1 { - node := nodes[i]; - if node.name == headerNode.name { - duplicatedIndex = i; - break; - } - } - - if duplicatedIndex < 0 { - append(nodes, headerNode); - } - else if !headerNode.forwardDeclared && len(headerNode.members) > 0 { - nodes[duplicatedIndex] = headerNode; - } - } -} diff --git a/wasm-ld b/wasm-ld deleted file mode 120000 index 01ef2a7e7..000000000 --- a/wasm-ld +++ /dev/null @@ -1 +0,0 @@ -/Volumes/Phill_Backup/pers/programming/sdk/emsdk/upstream/bin/lld \ No newline at end of file From 4aad835a6602ef95db3c4cc937c6637be0e7e34f Mon Sep 17 00:00:00 2001 From: pvance Date: Thu, 28 Jul 2022 04:19:28 -0400 Subject: [PATCH 06/12] replaced ODIN_OS string values with enums --- vendor/glfw/bindings/bindings.odin | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/vendor/glfw/bindings/bindings.odin b/vendor/glfw/bindings/bindings.odin index 52dc10a13..d1295dbf5 100644 --- a/vendor/glfw/bindings/bindings.odin +++ b/vendor/glfw/bindings/bindings.odin @@ -3,22 +3,25 @@ package glfw_bindings import "core:c" import vk "vendor:vulkan" -when ODIN_OS == "linux" { foreign import glfw "system:glfw" } // TODO: Add the billion-or-so static libs to link to in linux -when ODIN_OS == "darwin" { - foreign import glfw { - "../lib/darwin/libglfw3.a", - "system:Cocoa.framework", - "system:IOKit.framework", - "system:OpenGL.framework", - } -} -when ODIN_OS == "windows" { +when ODIN_OS == .Windows { foreign import glfw { "../lib/glfw3_mt.lib", "system:user32.lib", "system:gdi32.lib", "system:shell32.lib", } +} else when ODIN_OS == .Linux { + // TODO: Add the billion-or-so static libs to link to in linux + foreign import glfw "system:glfw" +} else when ODIN_OS == .Darwin { + foreign import glfw { + "../lib/darwin/libglfw3.a", + "system:Cocoa.framework", + "system:IOKit.framework", + "system:OpenGL.framework", + } +} else { + foreign import glfw "system:glfw" } #assert(size_of(c.int) == size_of(b32)) From f213622982d6662fe75a9ec0a19bee99527eff15 Mon Sep 17 00:00:00 2001 From: pvance Date: Thu, 28 Jul 2022 04:20:37 -0400 Subject: [PATCH 07/12] replace ODIN_OS string with enums --- vendor/stb/image/stb_image.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/stb/image/stb_image.odin b/vendor/stb/image/stb_image.odin index 12f7aea9f..27f4e7201 100644 --- a/vendor/stb/image/stb_image.odin +++ b/vendor/stb/image/stb_image.odin @@ -4,9 +4,9 @@ import c "core:c/libc" #assert(size_of(c.int) == size_of(b32)) -when ODIN_OS == "windows" { foreign import stbi "../lib/stb_image.lib" } -when ODIN_OS == "linux" { foreign import stbi "../lib/stb_image.a" } -when ODIN_OS == "darwin" { foreign import stbi "../lib/darwin/stb_image.a" } +when ODIN_OS == .Windows { foreign import stbi "../lib/stb_image.lib" } +when ODIN_OS == .Linux { foreign import stbi "../lib/stb_image.a" } +when ODIN_OS == .Darwin { foreign import stbi "../lib/stb_image.a" } #assert(size_of(b32) == size_of(c.int)) From 3f27cb230937607b29baddff3c7c3ebaa0b6aed0 Mon Sep 17 00:00:00 2001 From: pvance Date: Thu, 28 Jul 2022 04:22:00 -0400 Subject: [PATCH 08/12] replace ODIN_OS string with enums --- vendor/stb/image/stb_image_write.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/stb/image/stb_image_write.odin b/vendor/stb/image/stb_image_write.odin index 2a2ec240c..b9433e821 100644 --- a/vendor/stb/image/stb_image_write.odin +++ b/vendor/stb/image/stb_image_write.odin @@ -2,9 +2,9 @@ package stb_image import c "core:c/libc" -when ODIN_OS == "windows" { foreign import stbiw "../lib/stb_image_write.lib" } -when ODIN_OS == "linux" { foreign import stbiw "../lib/stb_image_write.a" } -when ODIN_OS == "darwin" { foreign import stbiw "../lib/darwin/stb_image_write.a" } +when ODIN_OS == .Windows { foreign import stbiw "../lib/stb_image_write.lib" } +when ODIN_OS == .Linux { foreign import stbiw "../lib/stb_image_write.a" } +when ODIN_OS == .Darwin { foreign import stbiw "../lib/stb_image_write.a" } write_func :: proc "c" (ctx: rawptr, data: rawptr, size: c.int) From 727a25f41f715b4c432504d6c53116916656e1f4 Mon Sep 17 00:00:00 2001 From: pvance Date: Thu, 28 Jul 2022 04:23:24 -0400 Subject: [PATCH 09/12] Replace ODIN_OS strings with enum values --- vendor/stb/image/stb_image_resize.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/stb/image/stb_image_resize.odin b/vendor/stb/image/stb_image_resize.odin index c75a95fc9..4268aeb34 100644 --- a/vendor/stb/image/stb_image_resize.odin +++ b/vendor/stb/image/stb_image_resize.odin @@ -2,9 +2,9 @@ package stb_image import c "core:c/libc" -when ODIN_OS == "windows" { foreign import lib "../lib/stb_image_resize.lib" } -when ODIN_OS == "linux" { foreign import lib "../lib/stb_image_resize.a" } -when ODIN_OS == "darwin" { foreign import lib "../lib/darwin/stb_image_resize.a" } +when ODIN_OS == .Windows { foreign import stbiw "../lib/stb_image_write.lib" } +when ODIN_OS == .Linux { foreign import stbiw "../lib/stb_image_write.a" } +when ODIN_OS == .Darwin { foreign import stbiw "../lib/stb_image_write.a" } ////////////////////////////////////////////////////////////////////////////// // From 21f2c06f4be9ac8aa0738e9dc71d1024cb148e07 Mon Sep 17 00:00:00 2001 From: pvance Date: Thu, 28 Jul 2022 04:25:18 -0400 Subject: [PATCH 10/12] Update stb_image_resize.odin --- vendor/stb/image/stb_image_resize.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/stb/image/stb_image_resize.odin b/vendor/stb/image/stb_image_resize.odin index 4268aeb34..be1f8dbd6 100644 --- a/vendor/stb/image/stb_image_resize.odin +++ b/vendor/stb/image/stb_image_resize.odin @@ -2,9 +2,9 @@ package stb_image import c "core:c/libc" -when ODIN_OS == .Windows { foreign import stbiw "../lib/stb_image_write.lib" } -when ODIN_OS == .Linux { foreign import stbiw "../lib/stb_image_write.a" } -when ODIN_OS == .Darwin { foreign import stbiw "../lib/stb_image_write.a" } +when ODIN_OS == .Windows { foreign import lib "../lib/stb_image_write.lib" } +when ODIN_OS == .Linux { foreign import lib "../lib/stb_image_write.a" } +when ODIN_OS == .Darwin { foreign import lib "../lib/stb_image_write.a" } ////////////////////////////////////////////////////////////////////////////// // From d26cfd2141a565dd14629c3b8c4c511f60499498 Mon Sep 17 00:00:00 2001 From: pvance Date: Thu, 28 Jul 2022 04:26:22 -0400 Subject: [PATCH 11/12] Update bindings.odin --- vendor/glfw/bindings/bindings.odin | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vendor/glfw/bindings/bindings.odin b/vendor/glfw/bindings/bindings.odin index d1295dbf5..aea09e31d 100644 --- a/vendor/glfw/bindings/bindings.odin +++ b/vendor/glfw/bindings/bindings.odin @@ -15,11 +15,11 @@ when ODIN_OS == .Windows { foreign import glfw "system:glfw" } else when ODIN_OS == .Darwin { foreign import glfw { - "../lib/darwin/libglfw3.a", - "system:Cocoa.framework", - "system:IOKit.framework", - "system:OpenGL.framework", - } + "../lib/darwin/libglfw3.a", + "system:Cocoa.framework", + "system:IOKit.framework", + "system:OpenGL.framework", + } } else { foreign import glfw "system:glfw" } From 9746e25784c208cf8a7883bcb2e1ac2317117aeb Mon Sep 17 00:00:00 2001 From: pvance Date: Thu, 28 Jul 2022 04:27:42 -0400 Subject: [PATCH 12/12] Update stb_image_resize.odin --- vendor/stb/image/stb_image_resize.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/stb/image/stb_image_resize.odin b/vendor/stb/image/stb_image_resize.odin index be1f8dbd6..5763e142a 100644 --- a/vendor/stb/image/stb_image_resize.odin +++ b/vendor/stb/image/stb_image_resize.odin @@ -2,9 +2,9 @@ package stb_image import c "core:c/libc" -when ODIN_OS == .Windows { foreign import lib "../lib/stb_image_write.lib" } -when ODIN_OS == .Linux { foreign import lib "../lib/stb_image_write.a" } -when ODIN_OS == .Darwin { foreign import lib "../lib/stb_image_write.a" } +when ODIN_OS == .Windows { foreign import lib "../lib/stb_image_resize.lib" } +when ODIN_OS == .Linux { foreign import lib "../lib/stb_image_resize.a" } +when ODIN_OS == .Darwin { foreign import lib "../lib/stb_image_resize.a" } ////////////////////////////////////////////////////////////////////////////// //