From c83f6d59942078f37163970fa76bbe5352fe0c4c Mon Sep 17 00:00:00 2001 From: dimgigov Date: Tue, 9 Jun 2026 20:24:10 +0300 Subject: [PATCH] feat: capture-less anonymous functions (closures) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement MVP closures — anonymous functions without captures. Syntax: |a: int, b: int| -> int { return a + b; } Changes: - ast.bux + ast.nim: add ekClosure AST node - parser.bux + parser.nim: parse |params| -> Ret { body } - sema.bux + sema.nim: type-check closure params/body, return tyFunc - hir_lower.bux + hir_lower.nim: generate __closure_N function + hAddrOf - lir_c_backend.nim: fix function-pointer variable declaration (cParamDecl) - C backend: closures compile to global functions with unique names Test: _test_closure/src/Main.bux - Closure as variable - Closure passed to higher-order function - Address of named function as function pointer Both bootstrap and selfhost compilers build and pass the test. --- _test_closure/build/closure_test | Bin 0 -> 221104 bytes _test_closure/build/io.c | 54 + _test_closure/build/main | Bin 0 -> 62984 bytes _test_closure/build/main.c | 67 ++ _test_closure/build/runtime.c | 1760 ++++++++++++++++++++++++++++++ _test_closure/bux.toml | 7 + _test_closure/src/Main.bux | 34 + bootstrap/ast.nim | 5 + bootstrap/hir_lower.nim | 30 + bootstrap/lir_c_backend.nim | 16 +- bootstrap/parser.nim | 20 + bootstrap/sema.nim | 20 + src/ast.bux | 3 + src/hir_lower.bux | 104 ++ src/parser.bux | 79 ++ src/sema.bux | 99 ++ 16 files changed, 2290 insertions(+), 8 deletions(-) create mode 100755 _test_closure/build/closure_test create mode 100644 _test_closure/build/io.c create mode 100755 _test_closure/build/main create mode 100644 _test_closure/build/main.c create mode 100644 _test_closure/build/runtime.c create mode 100644 _test_closure/bux.toml create mode 100644 _test_closure/src/Main.bux diff --git a/_test_closure/build/closure_test b/_test_closure/build/closure_test new file mode 100755 index 0000000000000000000000000000000000000000..4637bfd0c0794e227b6f9a78698c6900195ef4e7 GIT binary patch literal 221104 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 literal 0 HcmV?d00001 diff --git a/_test_closure/build/io.c b/_test_closure/build/io.c new file mode 100644 index 0000000..a13bbb7 --- /dev/null +++ b/_test_closure/build/io.c @@ -0,0 +1,54 @@ +/* Bux Standard Library - I/O functions */ + +#include +#include +#include + +/* PrintLine - print string with newline */ +void PrintLine(const char* s) { + if (s != NULL) { + puts(s); + } else { + puts(""); + } +} + +/* Print - print string without newline */ +void Print(const char* s) { + if (s != NULL) { + printf("%s", s); + } +} + +/* PrintInt - print integer */ +void PrintInt(int n) { + printf("%d", n); +} + +/* PrintInt64 - print 64-bit integer */ +void PrintInt64(int64_t n) { + printf("%lld", (long long)n); +} + +/* PrintFloat - print float */ +void PrintFloat(double f) { + printf("%g", f); +} + +/* PrintBool - print boolean */ +void PrintBool(int b) { + printf("%s", b ? "true" : "false"); +} + +/* ReadLine - read line from stdin (simplified) */ +const char* ReadLine(void) { + static char buffer[1024]; + if (fgets(buffer, sizeof(buffer), stdin) != NULL) { + size_t len = strlen(buffer); + if (len > 0 && buffer[len-1] == '\n') { + buffer[len-1] = '\0'; + } + return buffer; + } + return ""; +} diff --git a/_test_closure/build/main b/_test_closure/build/main new file mode 100755 index 0000000000000000000000000000000000000000..91e1ee8064f1a9a4c0f346f767b4425fdd7c1ef2 GIT binary patch literal 62984 zcmeFa3wTu3)i-`}!w8s(cT}jOf(8Xl01*^wh9q!;i9~LqVuxfhfk;T=WWq%$MnfoL z7)|?HOReu$db7SQZE0%}t(R~y!Io-7rBtb)RXk%*BU+$V&G-AQz0c&#kYm56U!U*! zKhNLO$~xo(9TtLwM5<9jai%rnZHoEa!I3zgRu7sgbRCmdoI6 zuHutv%C!o&sk5Ba*QCoO(FGSN+@{WQ^v&j9sOOuXN~C3ch->p#6t&CIU3}E8%eCur zHZ`eemz%H4CDTGJZ&TL6YxdjW%73T6GF>jcS?Nip zmC{LuVN<)l>rqa-OcneW|LSfxX6gD|`$7A&OjA3~F0ZMsm@xkGn#$2N)wPW)Mz5GS zVf2LYV;btljFp;c7we$8)32Lrq&6At#O(6CKLh{Kt$beh{gE%vTsmy;uRfd8dD_SC zKe#Vi_?fMPWhldo@^%;7JY7W@X@ANOjHZ{m>VK&o8Z-Tb@1XNUJ?$ z)Wf8I{|tjqOTx%*hB5u&x1rMh@H<`fd<};7CqKajpNYErlYbIU=np^HMNgfJJ!4$j zb;L!_0T(>g#hz6z`e(Y}KXkG4A#{9y?S0Wj-s93QFS*qFR~J1$c9H+Y1)t&KpBG&4 zc`ka+ayDD_!Kj>w@3zg8z>T{%aTi zx44Y2AujQd>C)aaUD}oBqGzm&o?p7im%G?`wM+j_bCI9vQtzcMxap$5&IKRng1_bB zACHUtw_W6yy7c2P7yD0liHE~3{`tO({s&#~M_lmtT@4MJ}kBj~}E_!yl$QQWizsLpmyXfcQslWN~o3Fzq{0X#6|y&F7ne|+MDKL|F>M^54q?c;UXV#vGYC`{7)|a`EM8h zq`2sL*aaWq(%uFa{YPBnpLNln=Yl`(qJM--d%6AUFFr9W3@`pmzD{-NmnU58IooA? z{nf>uoi2VD21C5b#(=-uB_2k($ggv$_jfM#M_t-`i%Y#yFAG&KL&0lE7g!=iOAAF63MUyncPd&VA}GS9iw?V~s_|6aXF$9?Nd|~p z(7HvzTEi^$6&O>CXDS!bv1o3dsz&^oPz#=on>b#c&I^XBt5znogGSlzr%F0tX>h5( zqOr=MP!$xQpt^o>Fk}~DryTmqgAIOkus=`{P*(MID0_irl>UCoED9_Y0o`lyN*qBE$F$vXE932r2TEex zK|Uce`ns4+muk@;SLBVATSuq*tL^8ii1N4;s)BC=2LEj^8p5GK{Ys;@ytb~PCK#+Y z8fxo9)wSU&JgKa%HR>B7+5ky}W?el-UU;z#pU`p!D@HRE)UBkNTIvZ13eDl_r9lI& zF0V8Kiz}-`MsNkpg`!~XGDw%7pea--(GLX;LCRND*H%((D6lw08m_A}s_NX=6CJBHn_fOIYjD@+BCGwRq5H zpr~xXpu%9a%LdRgWSmeiz}8gOHTH=swbB>})!?SD6pgD0)s45x*UCg)y_N=(^dnOZ?-;)Ux74_J-(yTb* zw*5A)p73AR%>NCVTVD-)%Kxd0(wnf>;aMz?_9NeX0Ng<11&uYHTYWN5 zPG1GjD+T}CqMv!edoQa8mu?)2KObN`tDjT;JEHGo<718eR`^V#SM&D^f08lqBxS>+ z!erXShnpgIu#_h4+3)t(bvPrDfFt+M*e;d!%`0*X;xsBU=`!fn>GuEhE z`FEECzubX$IdJX;>}#I`UulD&2Oanw4%}WBVtHpYJ8*mbN4&*>+v`8#YaO`Jt{Aup zJ=Aw)wyS&McApc;Zs)%SB2}H43xj`#w5vPf>FQSg^|mX9IP12roOX3boW8TK+;(+G zod4O^#CCN@%we2BW0f7{y?L;M_dEC23lR}69NV&Yd0k6+_&sZq>ir6d`_r1J88e&iz)l z1HZ^2Kg59#ci>(JKEi?LIPi-dc&-D##DPzA;5iOF-+_;G;HCq=)Pa{d@XH+ddm2wv2j1qu$2;&14*UuSzR`hC zaNv(P@GBkoW(Tfc#mm#J4qQ(Og10;Hs~mcEIq<6;c$Wj8?7;Uq@F@=bpacJ^1GgOb zH4ePTf#*B$;||>Cz>V#SVf;^Z;F%7*z=3Bw@InVZ#DNz%aIXWO=D>3t_;d%J>%dJ1 zKGA^}JMer5KEr{V4qSg?AWzF2c!@)Pz5_3H;0qo2bq;*71LwO``>J=~Wi|-9!hv7! zz?&TSEC;^MfzNi}Z4P{n1K;4l)f;W~aH9jC=a7HIf#2Z3H#_k84t%QvztMrWJMfzv z_$~*2vjgvP;0ql1J_mk_13&1%zvjR#2kv*^Jq~=K13&J-%N@9(*L95l3J0F)zyl6E z+ksa)@F5O7=)k=Wyvl*+IPgUdJlBCQcHk2oc(nu1ci^`=aMOV=ao}YRyvBjgci>AM z_(BI>>%bQ~@Hz)x@4)LF_zDMpn*(oh;2{UT&Vh#=c$))nbl@8t_%a8+(Sa{_;Ey=) z6%Kr}1HavYZ*}0S9C*6}|GER;<-k`v@Gb{_rvu;Tz?&TSK?i=91GgOb-46WA+b=!v zr3b$Bz?UBQ(gR<5;7bpD>4E=mdcbTxoN2aZ{OLZ!Fk9Ngsl8oh^Uh4YsDsJ9SNs8w zdPnTWzn=5+aYtTu1hIp?xJGOzp90ZXm+()JPbJ?j{BOvok>4!*&&cy=HnvgtACl*h zY^+WA2gwg0-z5CE$a7m1s~7$r@|olp3V%0w9=XQKguk6Uk6L5-!iUK7h&7fg{H^49 zv>NjYUqPNnsL7qpav4g@-CC?+%SeNiuk>^oqtX=rA zdDIxo7d}LuM~tyt;cq3+qs5q4_zLnoQjBE_e-rsJ z%F&-y%Pqe7*4ZkT=OM6#j1V#pKI` zzn%OH^7+Dt$n)qemMi?N?RwtX=rAV+Rjo=0M_g~F$j=TTU!O!!aMgXa-gEMNE| zkoO8-L4GOuY~gPrUrXK)eir#U@;%3;|H<=6DRxl!spNT-6zdZHD)Kx+inR+rmV5*G z&B9+wo<~Wsjly3@o<~TrHsQ}D&!eMQlklgJ=aEsYUig9Jc~lfzD10jUmE_BW|D+W> zkBnmZ!XF`j2l-s#-zCqZqnKCtH_3mUe75jE`-WjuwSkvw_A@7en$cl37;@J880Ti> zfb{_AGafv!_G@Gfkt`yA&`1b0ul?p7RbxEouMu5bKjghv9C`6;X5>w?`K_L^IYoKx zdC!^ADc^=?d(XTQRezi)k7qZ+Kr@;#i)7OA@R?YyoCQHHS5EFd=sCZM%6DiA`H>=j z#f2mzKQ|*i=C=2*F}EF0GgCUu7e5c50R?ZbRSGhD4_0~3M>ev{f8_bfCrwjML0O~m zvbkpSlm{V+HoqMnY_?A6xCagV3#LV@683gvybV6(7L-jWA6t%x+MeD+Rn7(((e!I} zIFPnz`jtuioeqyxwfS!F&GXIm%_)w2I(N2t*Ws;VO?dbaGcsp*wpEH04M%v^j7y=F zJBsp-mPDsqhV;p9^4m|lP^xLg>0?Twi%)Mn$=CGt6eIjPfkqFRF^!oZsYdt@Z6%R+ ztgXj-drKl8n2~3#U*Oh^hBH#l$T2e#9-e8Phop!n^!`I;D-bi%SsJnO+DjsDltezU zZf2d)@Nlmg`M~-rMo>$8WMb)h(!?Vpv+ZxU>NA9Aw zA;r;E8HTkNnWy=9im!Pu+VO#P30Su0KGu!aws0j!;Bx|Z?W9u*BRx0Y;#=Um#rHLz ze+N2M#hlp+H;HB{`X#y!)R)p>y#&*`e+GYbKke)f`nx!?*EhE)^6ZIT&o(0^!-rUZ zrakofQR$5xH02{3do}Ls72@kVwccUfw$J|nzryFsl6<}lHCV6zy|=e`Qb)s$B_n&NEW0>5`6O$x z>ZL;ZYDjV5Sv1Gj{A`LD8MWOSs+-j^Qes-dY8^4#>z2do7zgz73gnTP1y_?t4-b*5 ziXs*smPC$N8+0$Ay`>a=N<>wE%{E(yZT=ny=WF51%+}_E*f}uPurcXu-UlXCTz=qY zGkQh)caXG}d@S`K&xk*LH>8VC!)>0Yk<5CTJnd z8(bPWUK05btuBENhnkU&ygk-i=m=OWL4CxGypN}yg~PdQHmt=TK|uf<8UvjDWfVtt zS{vz?$brJN%Hcz;kJyvVJ5lFBhlgZ57e|nljxorJBY*PEDUIx&TNrtk7G_!{)Q$Gu znfF}Yp1h+NTr_p>5z$;6>9m$WZ)r_8!b}!=WMCHf4_qyL^zLx!AA`nosoprMDLW*Q?rTdVaJ$G}yDHAjNFnkuur4 zj6H2_fxn+YMm3^~F$~_fI>dGFvbxhyJ-vOA_TA05?0|D#LG;2k_sX1SU5TEtjz9_YTe^Wh=Zf)CLUrjp;Hws@a2n#Q6|WCv!2<*cjaJ; zd6xZt4>gJGJ3e@|BR$QqW@v>WmTlgNiD#ePHAu9+PA=UI<7s+J>a)HZucYyXCkVqo zziSr%)aEmWtg{iR)*iGR1E~8AM|V}VwPySVqK^C>%{YS1M@#RDvnEaX4j!$}!t}kC z>H>(D^yALi6Zr<+jluO#Wr>HDFnQd}KFVXE$gAk4cc9Vw7Tjo+!ia?WQT`*VlOqW^ zGy_?vk%bWwA7NgD{@C~6ZAS_ArP1dU1c|JcXT7qG(1wX!kIVtENv*4hNb(8Q3_lsxUI3 zxii&l-j*`ykE<{DdA6J}*bE%Q;-=}7Q^J?8ex>P?3&Up9Ckq-cqYT8)g7~CA-ua4* z=Rj{q`a~IA7+Cp6Rb&jFcK>=Egi^YH)&}t`0vq-E1^idYw`tzd&%b80z)Qbn{NH65 zs;{G0M6h|Z#=}k42OmJ9b;?#eG$ZfA@wI$(4j;XYN34H#apbs(YX4tOg3-~^q5!-@CTQR145!$`s^O~+P!e^P0V&vVhTjh z03!a3e9oje0gB5==I5>HVN^ew?{ zUVH4R#1gQ#^@?H`mhXAGH`W6i?EKt9N6*9)Xe(0MN<>>>7Y5wJXqZ+0es3?@dz2aT zxcOc4*ylUSP}6vbS-*qtRQvE889_ZaBV8hLAw|B$oY^)zZx0hHypn0Pp}A&cI}<{@ zKP2Bt*!$*{M8{`gjzg+1j=aK|csC}6za!UhszV+r#B#C*MsbMxF^O?n!;pKgacXJH zTj7jS+<7q-j6)|%+CjE(WFEUeCSO((c@vYK^-ZYie!=dy$h*jpOpmt{{w3EoIOv=* z>Q3?W+gD2>x)*U{6|>juc$!;fdTP<6&!i2=1}{~4YT8ZDuU=O+zwQ~CKHS>Emc#0^ zt2}jshjRilBbMjX(o9;aW`rH9QITYum8i=a&YBJ+_NK(gq+<;!aI#FX_eq%#WtROO zOTCX#V*L+G+4ix&t+Rn}-rj+Dm~;&B3&#y*&z>54NRKPTx%8(5-VQZ3bbaCTC_kL? z=S23zKIw+~_#Umla*lK-<7E(w4v2jdPH01@I0sZ)tQ!q=6hH-kVFhvfQT3p_qp_W6 z0_~~te2_3AWxOfuf8_Yco*n%@zli0(BAyxz9q1^}x7+D2&lY+GgM^*0 z`~zPjd*2_&$*sY<(y%vOk-yK5yd=7Q09})wZ73RPNo-8oXL_dXD~v8lVetJJjbubr z6i3I8V!tEu479N$eF(x}8*4WsyHxn1X6Jla&7u%ND&2<&y=PTlD*Y zqWUVf7cy0lDUpmiR7pHbi6&_*^Wgx@+0sJEU&}E`A);qn2V$t{aeDz2*?~3oZ0(O| z0f(|@OJRnAMNnR0G=OFul7?ElloP{arCnGcby_1}Gn>W+p*i_TTR++sdM@%3Cd@~D zPcqweKcj4Y;ttySCej;!rx03m4sCrIrm$Rmea4Q@v`V>Z=5qFcH5K^=lF+Uk|?F3@A(&q?_0vO|q9Bz3p zJSwsTi!trnn!d+rNTi(wnbfA;igmB=$$aon@t^KaSXzUVohMiW{OMJeK8L=r)>x>f zbxJ87D`%(Sq4i5Xs>Y+l@u$Yu^_W-NoZgiFz+}w)bLM+ElAf)d%LQ}}&Oo@Xyn}s# zz!{`AMb;qB&e-R`ky2iYNY;UpUr>o!fZOG-PAdOLnZ)Ad>E8h26~nIe1`L^hw$8-C z%QexdsaXGC7@e9DDag!UH|p%x0%Y^kJJM5?uj1`1j1E7!FcP4HW&EM@Ij}I9yw!+# z?dsOqnca`0A9rxQdp@W2zT78q9L=~JM$?ReX6s32Cy&3J>rp&7Pny(vjddS8FLL1K z*jn7c1oT(3Kbc16KE?4M`#b8rx;XMNdthACSB>x(dM9$sDm;XpdF0vdEFDkSf6s^x z%|SCb7OLK+)7u+kj9PoQCjM6YEnnniHISISB8(2qvsjC+`8w`Vjc3iH0v@yWLlf#L zYI)1E<{&U0fSdt;_?kPU!lFqZd)9P{u{<42F4bUYt&U)U%}p5=nO3XTo$_q=&pBSzZg>Qnp-!!)m?QsM5)Gz^HmZp! z=Z3H38dYuXrYR*I={ZS8LEiyugHqQ0xJq;wZ?RA02!rGG2F!@6Q_v$^=A7K4&WVa6 zpP}P%wv_i8qu?a#dZ^)|2R+dNhch6*N~myndURA;XZmn}Mkxa2loABUMSPS#JmuMF zkun|YD7AzdDP77i_pJF)io6#-CGx7A19Vz<;AzWio^NlbrVG~W2@k`;P&#B=egN6L zqZ}9)b?4gt=F00LoD8KydT?v{a5H7!nmt&P?lU7ta9DIc9hL)?zK-NMY-! z;i_KjZr`2cXyyPd08D zFC%1L=oCpiQ>Ebzp7nj?bBr*Y@?k}bY}$)s`X{IlCjJ%zFmVA*JR2tJaq8&md~~a4 z&G#_YO}Mze(6i+dGcq2lLkw^(KGCr`e6oUvh^V7*c&^#HW_T0ymw*{k0@zD7q{2B%E zAk`M_{v*Z#86ENOTgTpHPjpDcoKAZq+q=(G`Kxf_JNo;Zs6~xA&zhgYCe&Ef#&Hf8=a=*y z*Ge8UJx{lrt&@=|xD1%^1`JGj*0ZH34UJ8cH}gtAw&v=pb;{>>kAuE{ z46lFD_X`n4)_Ex9m@_25+o|2N2Jhlnz(4`398tnZL&U9og2$F}oM(L^ZaHB6=5-$9 z5Iex*p1%Pujn)kB>!s_ZS#r`|Y)1ZqG$Y1y`85HS=yI2#+|-W35#m+^nLf&cQ=3pF zeVv*29x|&XnUzsJ5{*)e#{*-eUF%?|RdA3#l8J8#=aP@O=H)b1iuR0ypw-u(;Ymz` z-#kDgu)I=p10BB_nE^e4R$KoC>$$i_>XEg#ULS}(?ZSwCSR*>TsrKXJr0VDuPmD&b z=ZU-Iq|al06-w2JMYDE5J&BBaS?zvd{>505i5Fv(zeLE|1OLNNM3HCBbpRMiPXjBA zrl)a+#IYHstvb9{Q1cE3W_mp5{tj&~WFkfHBBy!QTnP=4*D%}QT(wY%zH0T_QaIo& zigeid(*j)3k$xpR2cteAe_|cpgf}3Uhri*A=BKJWW+(c5lv4>*P0@CJSe9G&;~8@D z`In+zCoiH9#;>8=++WB`_y~OL@Q4$Du!oAj2@Ihbx<=W(tzuKpv^D*_lZfzCM zy{b$JKQ3#OPU|$Nk6qf!C_+EL1a185*181wh)l_y)|)_O-jwtmtCNyO{5oQbs2D&hgqI1r~i;X+8x{Dd7&F`i(cc+B!Y`W_5@DVeB$AuZa-6xyPmf(w;CSKmy zho5TCZ}#))cpg52b?jHU)zO*pko8yA#)%X!_KXO2aop{gf!D0@uvYKo^fE=o-P0V{ zQr!9h%gR13Jje4`#=VH%st6n=yFPn7I`YqLarwqgc7H@`r9Y-!CjIdNTx$JnKhH_u z?0!RXNcXFb;Q~L%A_L>JH$3WT3{tr4pW5G&IL>|=nF!zktAU+>RPLR2x9a*wg4MmON@s{M8m7pj-4AlW@YHMVSI`M(EK$p1Rn8x z@LZK=%&z4bM?BZJ?}+V2Sx1Z7V&6j^asWJvI)rsuj2_1xx5HGjl&%Qk1mIpXmr@bu(mOnYO@l+U+joJorr&ynpW4oJ7XnUT`v z*>Xc~N*4}Z8sC-k2YD`{`aA~ZdvX2%CFd}cSu0-U8#x>@FkPuL2uvr+K6O6RH{Lu? zpKV5GrieN$*1Q<|IK!u5!pw%PArc%Jc(9v5{%?)OB^0Vw0s{lQwX2UQK)RZ`0&m%OfcaVemJ|J<@ZeqE-cI+UM zpE>$Zwn}#$ZiA?arQ>LX4%UCC@lE4hZ}N@sU7r~FcqM$w)9=Ybc>Jk|pZ;+@X;?fqJ%@sah+5(BcmEg{7!D_W|@AYz$4BKyD3oyZU zVK*8^IqUZlnFp+9bPB4;&So`z{)gTT+Sa=u5!s&CjxER;NcBVdUSOju(TylgQD+a} zeGiT~<=xja+{n+i4x_;w7&Gu`NR!2t|9pns?OtZ;1r#SYJlt z7%xuiW&hjKyZTyl>kG$b|LrH=+v!F84#5)T7y{=wKB2&86rY@Aj>P%s8NfZdR9`x@ju5#b8ti3Q8QyU_@Km8K&zB#66X6IZCl4siKC@d7!#O|5d!3cen z-2=Y`)pDZP^Q%v-jl?2pgzL@7l1!c*n$bIQ>DoZH*;zo37@f`BQu6YgS}?@=CksXI zn2*Nyu<_B~ufrp7Z;C5$0E81Pxb$>B-9ZGs?A|^5J4g zAl1Yr+9}a0$q(@ab=K!-xg+?QFS#e^L?I4KypFc>_0jMxCDBE>NY_|*n9-lpD9b}z zd9pF7AgAHn?rTwACf5(F7RQs&xK?Q*-0%i73KXavSnL`Y>R9(FROK1FNF%~$Ha>{B z!^{1l=U1PYX;)h}NCOMJINNV7$id?Oj`@6VVjgQif6n$=XTOZNyI=BLKk?$yzG5?4 z$M&HEOQM1K)>!(x`HmbTJgnP?q^jecE2ej>pV$u@FSacWzs0$xM#WMlz9do$Si61F ziUus7K0qsum}%pzZ&I|iUNYTv!9tr`(6=Yr+xoLpK;&apM7U37h)bxd~Dk{ z;a}K4PnqOWUfKVqI0*JX{zd)8u6o8uN8ZEff|VuBD3oIb&ze*O!ZZB!CDu`>P;Z4E)+V!|ZI1*M!_L%#3UxFD99h zjHwjC;RoO4<;T(SK)d%N`+XEhnsP3@5d9dX(^`@Dt!DI}^|{n`N+)HVxwPW~$d*Ia z+7Agw59592lx;jZ#56Src0R@X&WxXdD35%@Y4U|;WUH7Qy<#W6s5B!}q7aNN?qb?g z;{n)Rz`FPAJdqqPtr@qXB#*yN!^$0}b1v;+gIO+4!WDwIJ@{cb*^YezN-uwwvZB&{_0ut6zh(3b93G@2m7whjO%eGzzYF@ zll;EV=-QxBOvE$w{T2O!#UW>{$gly>XvLnVxx)rZ{TUzNM2}bO$mi(kHr-zI>FfG| zrzHbbqOV`%*`L$k_V8_XOh$(N7Efb0KvLx$RQ5lVj|_WI$ctELAhC_yW=8&CZhIH+ zPP&SZy@R`SvwFL78I~w`T>YY2o8Sb_nyD;ZNY%4pp6q9a6_YI$_Qds&JzrxMABv`P zK8uc?ep|dF!*dbAJSUWi{$Vs6EjvcOGvh4~US_8=ial#K5Yv&&?b4J^2wFe<6TW#= z(b4#GD0zZPT7NEW!En9QY;BW%oV26S6KjDAnUBp$JHptIZZ}&cjUdr99yVKl--W=i zdN3}K6fBEWrXw5<>F?xPGb-xUI2Z>XTW^1g&G<<6OJs@<6~_X&S9zdSMbAA#uFg0Q zPaFT+jGTEIZo2Qs&8fJ_+td9woKk+5?}t72J_hT=arp9Y^qUwp68_(mW@9~|Uw!4h zz%VF5^4w~VDYJU~aA|O7#!pd+hqL}iLGQU~J7i+Prb|a?Z=gWMooTBmvl3$b56&XDVrMn+@BrE#EH>ad^ zga=RN&gS_e4#MPPh&=VeB|MVnCCAyY2w&)iPwjiBa0LAev^cU}n#fVc{UQ?IAhY!w zY&F-*{jERn9S_zVv#1p+PhnR=ROh>B#-4MfmBjLK_?WRBeBX2E$M*q8!&$PokByRC znQy2s0`NTtKJA|WHT~rcgXKK92h~8&?bPcj+L16N)835OGT5Rt$OZxJI%HL#eR{hz zQhlgeJy>;SU-uq+-`w^&HxbBTN07r#rTd*Jx~)&&Y5nPwvD~*iiR;6r#6{nKU_UeD z4{(Y5eg^)S0H-N`*xzeA^2Yxlo)CV&AA%wI*XQhAwr35;gDfSiPBfKA*IXqu0nK~Q z_axUC-6yyC)-4!~nc*|O0@{r971_~zyf-|uB>MG~(n;@x4wk08g(WcG%ABU-V>C2% z@3P~=*HMUbD3<;f;}4-zVjmI9htm8Kdl#&yL_H+QC~fB(q^SN05Gh%N^j1Nxisx@>YI&)p6KOEJl3MDifp$njhkK- zL7*2%)vM3PYjSNYj>4={bm+wMuCnZKe0|JTp4YNx_1Q2i(+up%d(K*^Y*FJmIUn@B zZ)iEXdX^b@-KW1YEYTmLoQDY0Z#rk-U>HAL5$Pzz4y(D7_;$qD>hn7Al?M}J!s`N^ zPl!fGuRe=89TQ96Yl9;@0RiS{BhO4UVELDkpE(r zCAKMgU8--c8Q8Y%ALsk#`0(LCb^E-zxca_$*=Bk1vlFPVFgmJ*Bd#>?9=}LBjm^M( zF`|&Cd2)`8*D`}?+y;KwtC}e1A3pgA=G6Ii2X~$!j;@E}tjjtQd>l<*hP86!sBdmd zyYfKXlIZ+2Pw~EO|4ilk!@z57>b)pT8>SXUjwl3-F|M4}z<2KIizbKfHzC-k)6(tr^WPv?LThhb--x z&OOwW2T#We=)KjKN!)n03_c_8waD|$Z+PN^_s+C1B9--J30i>qkV-*+-%knhxaY+poLe}qeL&T3tR?!k(m(TGFy zPU}ZBEgp~=a1mlv1>_Ju!p>T|BDNL%63_2)-s{kRGxSpb4%Fk+Uyz{xCo(3q{;e?9 zsek?#>F?I|2a@#vjY{J77diFMPtt!J9_q9Ii^fwf$5R9~ebIQTX3Ls)_CKD!c)aFQ z|2h9c|H)sZKRPcJo5;ZSZJ(U)o2Lh-8e4BOi_I!k0;Uc)rEfaLTUR0n0h* z^RHSJW5pQRhAr8>hhP;D_)K^O|SN@)QokZJ(#(kiKxz zORF>R!NK%YU*y?s2h;Q3D~c{kEj+o$x9zjk$Tnnzf_p|?*8Ijb_*#7Mpoq0@cjVdP zKxdUNGAac{Z!L_pZ#$G)8hFds+?lrR_<)l;nh#MneO$r1^b4r{3}5rXYfuCNDFvP_ z>_|4#COwSlpZzF`Pjoa`VVlFcfPg3zAP#V(U zD{>;kyhpFLv=@4c5Bj!!n(AxbnSut<&6(S^o1ceNF@H#IPZ55a;$F^prGY=mESZPz z4gP`$-503+W^*hRKVT8qj)tYY^f?-fFA27%6i<3Fe68o{v4zoT&qR8BO~=n!R*U=Z z%N;D5*45mR4(8-#GbWD?-~9A7sA00dap$&o21I=WPCnLr{G>a7LPgjIOnR~Lcc`Ew zWnTvyyRR@Z*O^-MeAE2?^p>M5n!BHh^FDs*Vm$p-sP6tLe09?-+gI5h@zvvqDl6R1 zF^2sLe*WYI{UdGSw$jMXxzXASW~-k{fX}cy!ugGwP+vzW=Etk$Oh^4(RAJ0+An#xwXH$i<$N{_VL=+cPoIo+7Sl z#2Bux{)I7D#f|AXMU43_!oivYV{HHQ>L1OK_hy)5=axkN#HB+HKg-FqA`oT6uSX+F zBC#TTq`{MNyqSv}D2?=Ba9L+T1apjX;&&+zCx$Tf3?Et)Ifx&@l3b3j%kXZa0}F+r z=sd&9ehPvC{Ky%XRrtZIMx!LMtqE1omyTC!x9cc_OyS+1yhh%!a8wf8>B_)u||nSBq8w%rpayY%_||5vS5U_))|N z-dDn<_>yq-NS057V**|H*x@V|JkbfNogD*^H@aVkM<&g*J@UaI34jgooi!WUF^>A5 zAFRK_8m!;cPXMwd{Jbze0bhcs`@;30^$HAjFaM5AuuW*WTm3CxSpKJu@;4N5e7j7d znbs(}RV_x*7~c)D3{}ms>bXLc^N;v}AI9ew>-&^F?xN58Z|HmI3-oo##LV*j#d-*FJ<^P3$p5)+n@sH78d;W#K^&A8)`ZlYDNuqyb^?OY^ z4ybS>m3NeR%*&l>CZo+dMI}_v8qR&(nqZvYh$^rvT*{vy#*XlSbsR$+zyjWHV*l`f zpPS}v%%;?p?PY1)4B?GsI%O~$yl<`jy)I=GMbGE@L@7Q}6b${FnTM zwGMX}d+{SN&#)cx;uPN)pkccoXMO5q(VE3NkY@3YafkH^81>VW#gTo|u4gUZ<8l}< zf-n49SQV=L1u%wuVZv8e$OCL%W@!LvE5=4*^(Dz%JeSW#7CeUYkViQ_lzq>OE%LEN z+)|kMao$mN^ke;yRoV7k(T{x};w%7%6=L7pD%V-3DEl%`uuqxx1Wi%)!3^sf*u)=` z_uSu!gG%wAoF~SXp$9dO7uf@_Y^(&r`Hs)e!_P?Kvxw-F0eo`P7U%lRxf!LrmV5=_ z6U&99UG#aJAjHmR(Z02v=#Ng>ibvAjvEJXE@Ekv<7R~rBj~}8}e1}B+xEnrsL%NPV zpIiVR2DF~Z-|m8A%3t5YV@gB>JMvNXK2HTQUZR$kJ#5th>ob}Nr=QfcDy?@Qj$d(% ziDG>}pVm>B)@yWIvRFhQIDXR=Jp1MKr3b$Bz?UBQ|B(my_j5)xTt1?~7*SJGX^dE8 zghP$^BVFY+4MC%^c1dmB@>;L@BTf~KE4=mPwbg;EydxS08kN<{svGbJl)M!yy|)KL zb;i=V%Ep?yI2)=kmRA^~YnB>;01D{;kFL`z@MfQZt8dTkcD%lg{B?}qs|-lU>p(czv3}VMS^~NgbU!HnpwI%W(?)@E-M<3#A<*@pyFoXC z4#qjy9tK^7eaFy~4dY4B$)LMI%RoN`tppv4h5riB$)M{&7l3XAT?P6m z=qAuzpihGC2R#hh13DP{$8%A@wRn5&fC_5)`0E?T~8ig4DADb65sHgfwRjR ze6__h%=MsUpbvpof^Gs`0lFJ>J?MVWji84?9|ipsbQkDgd{(s|v;?#VbUkP$&P;ZL z4h3ECN^dWJ`{+ZU>p%zZMO=ZFfbIg_5Bf3jKlk=#a*LVJe*2Q2}82y`Lns910BL!i4s z4}&gv7kbZuzd$QNhjydiL05t92i*@k93NgxK8&~m-Si&n0p0&T`Vq9|gWlf!^WYEA z^`M7)(0=kCq8yGDCm%t*psPS10(}y6AL!tZQ139*54r+$6X-6`!=UM3ft?>iFX$@J zHqhOm2SL~W9sPGco*#uhpu0hz1U(Ge1v>N?{Kn^?$3aU#2YX>Bs2B7h(D9(?V!2Xo zpJk-1$WA$L@JX3%DJNwUhk1tetKQxVm5BV?zr0}#*Yb;hivC94G_ta%Weu6(8MHjJ z$++g+t1lflT-309A^yvSUZltPg&|}tz<-N3_xAGbJ}+YR_^%QewwH1dTZ8`=CSeUWnJELaCIa(i|Z=ED|6-`9~wa)uE+C7@cjFHF7_AE{-pW}X@6nX z!qjko#+xYmGVT2>#&2AY_+dt(-C9q6>N0qt-~=yBA0++dMdwvQ*9Fv7n3X*dWIV7Y zVC*;Ac`yAOrJvWQ7iQ(G&6u8*+dSa*fmt~|hg+u&%7J)2%D#-Ut63I(xHi?7HKaLh zdX_hJ{=lpuK;RAEAcO6A2(mek_V!-H_J}`bB>KbW=+Cm$-}c*^(8U^4_#u1Ykn}XBf`<*=%`6T_&ED;2D-Ia*e{j z3$*-XZU1_~~(o763aAY`h=}s|Pk5YgRk& zQf>{fTwow_5!(P}6fkGJa=n}b4AXC1t{vETU|0&oF|Mcc9T@AffRzBdj8fNSWzW=h zb8TG-97|BSnD@BOUI?t6$hBG7QI7BRX@yzdwdn;}In5c>7~Vh-(dZFGap9m9 zS(!UJcK->fq&Z`4`uemehL`xE68gHJ56eBd7~dr*iWu1zy(%ksF1(*{ZZLw4{JePfAsE&w(J*kqQM`b!yW zC0Y4?JJZQLy9RQ(339VkPISo8XPY30->cYr8};@1OnifxgvnE;uf^grNi%erw9i7> z^(c!alw2HVp90$eOmh1)&ZjeXaL_Schk$Ovvjvn!+jR^;>Du&Zi~-D>sYv#yJRSm! zoidbr8Rc4a&O2dlK+bL+kh)z(PkhcT9+Y}_5>z@Uo!PaB2RLoPhvoa2fH(Fb#&qYV4OKjA*vchI4H*OD}T%t3`f;LbvOL@_j_FMbSt zUC`G+edwpYcu&1nbvq)yXb=h(Nbs9GI%K`U{pAqsFRx~~;;ifnnXRU=i)DWO6wfE( zx%?sq$*W)J+l_9%Wo(+T}jH5g7G3u@Ydk@2gU)T8l3PHV4=QVzaZduhV&#d+$}i^R%o9x*pggz*>l* zKlIqa`fq)@#Gb6HQp*r0&Y&{~l_mn5g@1LVeH=ElA*E|zsu zC1ejm_5#)=dFFb_Gt}RNXCLF)^?Wuv%exZ(8HHP(F-(58xA#rrGnji3n_7P=mXMhQ91h-DVlju(df2>+{73 z_Z{$khm29?=#7vqgshKsi|>|-@A&LVJX?Wh7xJ0(@j@xbc;AO-JcBuv&u|et3~Yk~ zqYeWLnI8k|>zk6e{-Job)kXhAJlpNiMg1kf_9bBpfxYFxSZ5em53uAqy?EA!XX!W_ zDog<*Z7M+;cm+vXa-6i|QOM-t(~NS6@S21Fb^%)t?1EGRQNNt9y1PGc}rqf-`n>n3xaK)}}Hpc&?R+&$_R}y;^UW@%_S`cEsZmeZ3X3qab@a zWpQ!L?*=vz*hB)z_tzoc7Z18N9utT0d_JDbx*H#5Yy``QzXNgkp?J0e&sZNX zj@Mi;VPJ&>;DtLOP22>&sULhH@V9_F$2axyOl}m;_;}At@*kKSU@s7m`AGJy{m=FJ zsrUCgAL;$xKIqy6U3UD_#>2q20=ta*VS5#9_u@7kLS4X@5*HuL%<^J^#~$J&MH_P< zGxUYNd{2FofeiuX#Xa=p-VA;0$2q{gz!wpRzO|_{IKHn{t7hHxh#Bf=f@~#ZWp7og z<_G4S4Zv3czn?mi)?e{_#uWqF5zjEilFN`s64wy4--14#o!avi>;4qjE?~Te{n?BZ z%ASF9$}Zqo9tbY&2gdSw#E^%^gG>g-pTWA3nAj*e7Z&M#0(`PE(Ly=r2&0_0tG9QK zE_W|;dz9xK`uu=fhyl~H@~_V_=VX&||lWao+`OJTRG;inwmY z7+MeC!zSz%n=@u)BWjCPgD4cQ6*JYh+0Xbt4AFWC>1+pyr zGRl_yn`ODl4gVWunGZ@(b_2@(-}Ec>+z&mQ|1b40pYXhU7xdIKccVY=V=UauHHtnD zlsTc4=LWO0^3w)$PB`&6&^L%a&B3Po@E+{%sZ07&;tDbA=*t~ReTn2jJ1SB3;7h%| z_u-yp^&Y0bbG;eZ!}wgx5M7(0Yxv7Jlm0)_wc1UUTK_}WV(9Ar*SdJ#_#-!EY@^Au z{a3KZ*Z%o}Hd5EtesxV#vCsDgUC=d2+tq)5xT)XR??Ty^|IM<4u{qxNZ|6gq zKO5K({-^YD4Opi2{a(kxdags)rePn@jGUjJ+B`7Ji&G@53a|s7KBy$iTVQiCCfiWH za_5bC7Qr-h#%ebEBcyV3%?z&i< z^X(|S7t8!};u&&2urq*NKp|X|n*%Ht81H$J@-HQh0){1rT*OuZo0x>P0V@P1dm6}a zUHfBT3xVChlCnNaohD0L@?TzGdf-bBeCdHNJ@BOmzVyJC9{AD&UwYs_;DNG9N=de+ z+}85qx2C*oeoGSntmX$RF)W1D4UXRBI)1yNSenbV`6)&1_s*Fb$C6jBt@=6F!MyC} zfB(Fj5b}i7WdE2fY&2Q2b z|0C(SvgZH&zwNrel53uz<6xSm^E9o}beX1WHGM$Shc(@zX@{nJH9e&1QB4QP0j*)2 zr71rCmurHi(=?r@X_cnSG+nFd1DZap=@v~pG~KJ|Ax)2JI$*4-?<`F()^viV(=_E@ zqTp4f=`u~%YWjet4{N$b(+*AdYI;c1qnZvFr}Uns>BX8(&~%!n^E9o}beX1WHGM$S zhc(@zX@{nJH9e&1QB4Po*Y#_9v8EF=ou=tLO{+9rrs-NuAJFt+O}A*;q3K>t4{3T- z(*alL`Zc{+(+QeR({!GuRhll-bgiZjX!@|GTQu#^bg!m|G(D>6fC;*OO)u7Tf~M0n zou_G)rpq*4tLX!pKCI~$O*=H*tLY(4k7_#LN?pIE7i&5}(`lN{)3i#{Wty(l^Z`vD z)^v-e9h&ad^pK`UH61Wf)o=fMe22Q5UQlqAH>WUIQC(i^%^N#r?3moqdE+mu3|8cg zZOj{~A-mLxlxZ{F_^>zm3G-+Qo~+V|(W?bc2klRu>A63A1n^YDV{A;a{}PRNCE-_T z+@2r7%2kGcQ;c(soSlld=g9?vpJ~|hnR<#}?}!`LYtOef&a?6qydksaM;reoZu+x- zD{!%2&)4z|Ft9xu&z6s1@ZM1y^uEU1cxZ=4nYK@FVyWy*ZTL^xb8BjsYX~hoGpZ*kCn4D&$GmsqD8NgGFvy9~Vc$nZr;_Di_$T;Aq z*^Gl0x!^&;Jw|?`64qg7tkih@*A#xC*0s(>&zqF-7fh1z)wXy z7`h+rcuqn55;rxjyT!-^ej4JrDXCpU1=o#nkjYxUr$B-BI+ou<_vg<=E_kyG{v8+m z5y3r1y>`59|F3L(p0ei*ZR=JSJ$qg7V=nkvsEGc_zCkJ0!_pX`@q@bG(XDb_DY#}F zWQLaK?+WH6?~OngYJ78n!1k~+>VRkSWyMZ~+y5>Td7JB_5v?ctMuq9+iLpWBxhoa^ zpf2)r7d<;{dA|~r?{aYalE!hip4UKzmQTX+W7$3RlKN%Yr`m^j-C1~$s&J|o2 zc92n8zHN>I<$FWiPS*HA-S77PhTmoP7l$|7dS)m+x9K{9Xowen?%AOMO=?~A+^O~C z29=^1AZ$0XEZ56xJ#}M7x~v+@DyzP`fKk+F1Sx{4;JF8 zz7e|K5{;MX_-WDj&A>0z_fE1*4MMK zdKSvZNBBNVmnnCV54+&&w4Q^yzb?>v9&nL=1o)|`=NR_-)Hd~ZF7kV|{+=QwIbIid zQ{xAN3iyh~-_v-$PXY2XFt|;@1VlgQ>i*Tk*f>>iUD!d!Yx(+{6!@loR;clOE-Z1q zq=+$B;|r%L0A|Ql?xO!L7ySD!`0rit{eoj&s8;nY()E7eBG2#4`x~bd1jl$xnn#LU z-;oAwX)3A`d2@6z+aPz4+RW$T%w06Q;zU*qjM9!6>TpAk>Z zHtcgQ4I7)aeD1XhvE%bejW5)Jua~n%m*95kKKh9Zel`xc7+39Dzx@0Gid`yrUwQlP zS{M9QttbC_CHW^^V1>qw1_g9$yv;?=W-Z^eTnXCqdxwksU$nee_m}Pew>5t7Hl^o2 zUGHZuda?%T@l~Y+v$gyX;9T!z?^FQ7Qm#w2eABIphkNB3C%9%Dgg>m-U)l*LMSC!i9mTw5|Fht)zsO7V@;$a$pSmXJN6>yox zAJh0k9nYy6e@f#Arz_wgjq|)y=7mB9Jf`tCHSP^4K)w&g?GcR|x*yNc@}CN>83#E7 z2gUvQlfR_9KYWS{{x!j|j+&vAPu6vWT;yA{e6G#|_By#u<88B)0=;cEHoEBfqn6*S z^FWracdy2~bo~Eby7;LmD3Jqwi{`S}3czT_g` z1Dxxk>(}j?psF$6 zxACNL_YW8SXCq+f=cZbvz+N|AqVa6)=d-o`@wU9qOP6T8NaOi+3XpGuaeI^CTF5~f zw0!$g1={QL)h_bialyB0JsX!OJ$C>8LF1dN6@I_2`&Ab`?`ipbJ-_R4H2$ISHl1M7 zwVn(lG|v0poeHq${UJKvZcN%Q57&5`&bN75&*g$^#zAIj`KF)(?e$~;IL|AR_tEuQ zzFm)Nm@U^G8ZXoNJVWDaMbEj$!u=Z1q*cr3&s2c?ye4iR6g*Lu+%L3zyPlUX*CLO( z$p1mhZ`S@nMv!Z-#>;g6|BlAraMAO3EuX8`Cw99&caa~4a>Vlt)fRXwAI;BSs17jHQY{$8!cC#^3&H-!R^}m=xi9?a$(4qMAG+p*YqSeKQ*f*+ z^f`f8l*C}vpfBv~ZaLh+KVC}fd(|Fk;RqqSBiG?nDsPur zJX_n}p!Ix1<4p?{P_FUsxaj|#md{V>_Z_zWqC`C}YCLzj!fijiU$@t)$-X|f4hFj-#yh|f8FPTKkS06rTRP#7+t znGF^G^7{Hk9pTczpVGbNxl8Ux8-fEW=-T z<8{8$;sV1zy=3N8Ux|O_v}v=8=J@CMrj``>jjC|4rY3)SN%7PIKR-aZ!ifK#)4JM9 zF}}R-`M5D-mECr`8Wxw2n>b!!%WLWaOZ@o#rZ5A)w6?a+51>4Z-%^{PzbsT;8=hKM zS7Q{-EAtmtFA6q5Kuth4K;;x5@SLBVA%8FEX&#kX44+oR9v2*cTb^X;7#*1u0b^YRCs9#aq zO^c^h*TOf6W;cXG;kqP`R^a#1CYV`R*T|lfc)BeVHl`NOq~HAbUAlhsc#=wSQ&0l6 zm}4loqCWxg<2;Gqq{pmgae2dHzx-NWXJ0kevdawEjQ=NC$TE%rp?yfSp55oiZ>J3z z{viBVZZtqgFtHyfSXmu{sYo;pQf07m>=jq!P0~ZmXjooeAE>Jh2Una}Qht@KUw#*_ z@{v8}gwwwmmzk>}Xw;V1)-}`wgY_qvugub7^>rAtCy3OShZp-XxREVH_0sYc(*Naw zK(PKqyW(SCH6K}O*$FC@?V(_}F;uIJ7w`C$O1fHppL0SvKe`{mTwUvLYzS5&WiJW# z=bD6pSrMu$uMCto^jXQV=?E1G&i-RXMM;0r%YIuF3}Fk0N@9JXVxm_UcsCaYpWyLaW6 zF-u8?Ww;KG3<;})h@l~qpaMgSmKhDm-U)^z<{^%;ent?6K%Sv|lTcU;tY4Xc(LnLt zt##G4MtMbDsIQxtVlmq`)L~54Rw4_qcAc2|AKubjb!r-XIg22%_mtaqvLqgTdkoK<)uIxwi(ne$= z&R0vMkNZhX0a*&1(9J3xR&YAg{loy2Ij)2Y5{w(MK~L>a8e~I#2s=k zW5i!wh2{q&6j&NxR`rFSdt?U#m3<4Z zdM+klYPc?OgwnO*=^B+(?aH{0v)6D9m`UR%vI#5{Sd6R#jYzI1Jj3J^Hkhd#qc?9n z7QGT(3F5M@P(#5U8;oJqX_z#suxMa)5+g>#O02GzCMe)T_^SpHCW1PBg@VZVhOfT9 zW~GAdg=DCD(PA|x!5bLaSRU76Eu|{qZ+iEaFE5AramU1alqn}(HF^pm(dU+N;|zo& zR%u9N{4M@*!MrQu3UrT$Dk&Hju*1lniv3kJb+Cuw>~Np$b_I)d3=n%A5RYe6ui{ya z$$kQw4TBa;ETY9;Js|Sy%h99a?7l?fuT<+J!FBG_8vJ!tRmhm~#O^!J07jK8AmR7U)-X$b1^whSS>0fLCB||3>PJS6e~IO!4BU@7Xm2R6IB^n(Z-aeN;&h@RJ3jF{ zb~AqbZ3m;OzA@a8;BnQ01W3A8LL9}!_R>O^Il=FrQ(B<6J}ibmf+6cSxyN$K9+vDV zmHcFv84q>U`bum-LUkw3;IxgaSflEM#iKaKPKS2T%hn@dk(QV>_1=lbaFC8Qn1B+R zj$whBJpp1pb;$GmtEUnExo<7j($33_4bT5W+^PAKiFBwOR(ZNiw!ibY@AKZFFW)RWh3=1-UFj zmi9mUGKcw>^7z7@WydkuV~Kr&6U!?a;;~WQ&=ABVnB+By*Cf;F40}o6&{z}Z(hYka zHPNbUBgtOfA{inlq78aGkAADF3oX@oGHF3f)$$izjQSHZk<<%|;}KjT8|x~W1glPH zoD5*w&3$W`1UZ=s6OdUx92&^dJE_%ljAJntKpsw#NUSR%&N&=lPx*;6c;EhsDbrS= zqhfiezxl?QC>diMiOKs2wH{7r8Qmunl-?0=ZS40~G&Jarv+KsJTIuIR8ZXUV6pc@= zgc>mpF(3K$`GQ&)DYK1PC?e}UnL;EQmLdW1Fo7kR00RM|iu;FnRpKZhi5XE>M8nk- z%g(UZJ6K#Lbp*vFC>+wV#*R<1Zc9e$)%y;Y<+E7cw>WdmtMzqiqo9{D>Q59k)Vf5c zmf|r8mmk>U_Olaou0+LWGJVMgDn3pN=FE4SCKC@U7%FkNn>So5GhuB8hLH1#Q2HXS@j@Cg;Ml>>aUH3n!hON_X_z z8RAqq1MGKWPVI6Ai0YXP5$TDEC?O-B5cEijRTAKF-`I;DHV0W0C)%|Q66osO^b0Zy zVw1-g@mMXGBins{b!9)%seDRNFWP z9bJzZex(DF)z0YZN)7p@7LVrp5fP_nj4_ofYf*)wh$?x47XthzLweRPwWntdXc^ja% zXtnRAb({}BKD0EN?~q0^<6NoZu`0L8dGyme^8bqKcMm@2i_enJ#Q68)f?uBFfpng~ zj{n_O`o7LlU_YR{!Uu2EQ}W5ra+UlC)r7+Xr$jFK?9cr3C0r6G0l-6^5!pK8@AgT5 z4U08?=t*c2fw}a*gaSQW-_Ui8GZpZtCkvd@7$S0#SJ^6#UIJxtr{n~M-G XG0Z;vVmhbB|M)L|hsD#1P>H=;4 +#include +#include +#include +#include + +typedef const char* String; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; +typedef signed char int8; +typedef short int16; +typedef long long int64; +typedef float float32; +typedef double float64; +typedef char char8; + +void* bux_alloc(unsigned int size); +void bux_free(void* ptr); + + +int Apply(int x, int (*op)(int)); +int Double(int x); +int __closure_2(int a, int b); +int __closure_3(int x); +int main(); + +void PrintInt(int x); +void PrintLine(String msg); + +int Apply(int x, int (*op)(int)) { + return op(x); + +} + +int Double(int x) { + return x * 2; + +} + +int __closure_2(int a, int b) { + return a + b; + +} + +int __closure_3(int x) { + return x * 3; + +} + +int main() { + int (*add)(int, int) = &__closure_2; + int sum = add(3, 4); + PrintInt(sum); + PrintLine(""); + int result = Apply(5, &__closure_3); + PrintInt(result); + PrintLine(""); + int (*d)(int) = &Double; + PrintInt(d(7)); + PrintLine(""); + return 0; + +} + diff --git a/_test_closure/build/runtime.c b/_test_closure/build/runtime.c new file mode 100644 index 0000000..b46be93 --- /dev/null +++ b/_test_closure/build/runtime.c @@ -0,0 +1,1760 @@ +/* Bux Runtime - Minimal C runtime for Bux programs */ +/* This is linked with every Bux program compiled via the C backend */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Command-line argument storage */ +int g_argc = 0; +char** g_argv = NULL; + +int bux_argc(void) { return g_argc; } +char* bux_argv(int index) { + if (index < 0 || index >= g_argc) return ""; + return g_argv[index]; +} + +/* Memory allocation */ +void* bux_alloc(size_t size) { + void* ptr = calloc(1, size); + if (ptr == NULL) { + fprintf(stderr, "bux runtime: out of memory (alloc %zu bytes)\n", size); + abort(); + } + return ptr; +} + +void* bux_realloc(void* ptr, size_t size) { + void* new_ptr = realloc(ptr, size); + if (new_ptr == NULL && size > 0) { + fprintf(stderr, "bux runtime: out of memory (realloc %zu bytes)\n", size); + abort(); + } + return new_ptr; +} + +void bux_free(void* ptr) { + free(ptr); +} + +/* Escape a string for C output (newlines -> \n, quotes -> \", etc.) */ +/* Returns escaped content WITHOUT surrounding quotes */ +char* bux_escape_c_string(const char* s, int len) { + if (s == NULL || len <= 0) { + char* empty = (char*)bux_alloc(1); + empty[0] = '\0'; + return empty; + } + /* Worst case: every char needs escaping (e.g., all newlines -> 2 chars) */ + char* buf = (char*)bux_alloc(len * 2 + 1); + int j = 0; + for (int i = 0; i < len; i++) { + char c = s[i]; + switch (c) { + case '\n': buf[j++] = '\\'; buf[j++] = 'n'; break; + case '\r': buf[j++] = '\\'; buf[j++] = 'r'; break; + case '\t': buf[j++] = '\\'; buf[j++] = 't'; break; + case '\\': buf[j++] = '\\'; buf[j++] = '\\'; break; + case '"': buf[j++] = '\\'; buf[j++] = '"'; break; + default: buf[j++] = c; break; + } + } + buf[j] = '\0'; + return buf; +} + +int bux_run_nim(const char* nim_file, const char* out_bin) { + char cmd[4096]; + snprintf(cmd, sizeof(cmd), "nim c -o:%s -d:release --gc:orc %s 2>&1", out_bin, nim_file); + return system(cmd); +} + +int bux_system(const char* cmd) { + if (cmd == NULL) return -1; + return system(cmd); +} + +int bux_strlen_c(const char* s) { + if (s == NULL) return 0; + const char* p = s; + while (*p) p++; + return (int)(p - s); +} + +/* I/O */ +void bux_print(const char* s) { + if (s != NULL) { + fputs(s, stdout); + } +} + +void bux_println(const char* s) { + if (s != NULL) { + puts(s); + } else { + puts(""); + } +} + +void bux_print_int(int64_t n) { + printf("%lld", (long long)n); +} + +void bux_print_float(double f) { + printf("%g", f); +} + +void bux_print_bool(bool b) { + printf("%s", b ? "true" : "false"); +} + +void bux_print_char(char c) { + putchar(c); +} + +/* Panic */ +void bux_panic(const char* msg) { + fprintf(stderr, "bux panic: %s\n", msg ? msg : "unknown error"); + abort(); +} + +/* Division by zero check */ +int64_t bux_div_i64(int64_t a, int64_t b) { + if (b == 0) { + bux_panic("division by zero"); + } + return a / b; +} + +int64_t bux_mod_i64(int64_t a, int64_t b) { + if (b == 0) { + bux_panic("modulo by zero"); + } + return a % b; +} + +/* String operations */ +typedef struct { + const char* data; + size_t len; +} BuxString; + +BuxString bux_string_from_cstr(const char* s) { + BuxString result; + result.data = s; + result.len = s ? strlen(s) : 0; + return result; +} + +BuxString bux_string_concat(BuxString a, BuxString b) { + BuxString result; + result.len = a.len + b.len; + char* buf = (char*)bux_alloc(result.len + 1); + if (a.data && a.len > 0) memcpy(buf, a.data, a.len); + if (b.data && b.len > 0) memcpy(buf + a.len, b.data, b.len); + buf[result.len] = '\0'; + result.data = buf; + return result; +} + +/* Slice operations */ +typedef struct { + void* data; + size_t len; + size_t cap; +} BuxSlice; + +BuxSlice bux_slice_new(size_t elem_size, size_t len) { + BuxSlice result; + result.len = len; + result.cap = len; + result.data = bux_alloc(elem_size * len); + return result; +} + +void bux_bounds_check(size_t index, size_t len) { + if (index >= len) { + fprintf(stderr, "bux panic: index out of bounds (index %zu, len %zu)\n", index, len); + abort(); + } +} + +/* String wrappers with Bux-compatible signatures */ +unsigned int bux_strlen(const char* s) { + return (unsigned int)strlen(s); +} + +int bux_strcmp(const char* a, const char* b) { + if (a == b) return 0; + if (!a) return -1; + if (!b) return 1; + return strcmp(a, b); +} + +int bux_strncmp(const char* a, const char* b, unsigned int n) { + if (a == b) return 0; + if (!a) return -1; + if (!b) return 1; + return strncmp(a, b, (size_t)n); +} + +char* bux_strcpy(char* dest, const char* src) { + if (!dest || !src) return dest; + return strcpy(dest, src); +} + +char* bux_strcat(char* dest, const char* src) { + return strcat(dest, src); +} + +char* bux_strncpy(char* dest, const char* src, unsigned int n) { + return strncpy(dest, src, (size_t)n); +} + +double bux_str_to_float(const char* s) { + if (!s) return 0.0; + return strtod(s, NULL); +} + +/* String find: returns pointer to first occurrence of needle in haystack, or NULL */ +const char* bux_strstr(const char* haystack, const char* needle) { + if (!haystack || !needle) return NULL; + return strstr(haystack, needle); +} + +/* String offset: byte offset of pos within base (both must point into same string) */ +unsigned int bux_str_offset(const char* pos, const char* base) { + if (!pos || !base) return 0; + return (unsigned int)(pos - base); +} + +/* String contains: returns 1 if haystack contains needle, 0 otherwise */ +int bux_str_contains(const char* haystack, const char* needle) { + return bux_strstr(haystack, needle) != NULL; +} + +/* String is null: returns 1 if string is NULL */ +int bux_str_is_null(const char* s) { + return s == NULL; +} + +/* String slice: extract substring from start, length len */ +char* bux_str_slice(const char* s, unsigned int start, unsigned int len) { + if (!s) return NULL; + unsigned int s_len = (unsigned int)strlen(s); + if (start >= s_len) { + char* empty = (char*)bux_alloc(1); + empty[0] = '\0'; + return empty; + } + unsigned int avail = s_len - start; + if (len > avail) len = avail; + char* result = (char*)bux_alloc(len + 1); + memcpy(result, s + start, len); + result[len] = '\0'; + return result; +} + +/* String trim left: remove leading whitespace */ +char* bux_str_trim_left(const char* s) { + if (!s) return NULL; + while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') s++; + unsigned int len = (unsigned int)strlen(s); + char* result = (char*)bux_alloc(len + 1); + memcpy(result, s, len + 1); + return result; +} + +/* String trim right: remove trailing whitespace */ +char* bux_str_trim_right(const char* s) { + if (!s) return NULL; + unsigned int len = (unsigned int)strlen(s); + while (len > 0 && (s[len-1] == ' ' || s[len-1] == '\t' || s[len-1] == '\n' || s[len-1] == '\r')) { + len--; + } + char* result = (char*)bux_alloc(len + 1); + memcpy(result, s, len); + result[len] = '\0'; + return result; +} + +/* String trim: remove both leading and trailing whitespace */ +char* bux_str_trim(const char* s) { + char* left = bux_str_trim_left(s); + char* result = bux_str_trim_right(left); + bux_free(left); + return result; +} + +/* Int to string conversion */ +char* bux_int_to_str(int64_t n) { + char* result = (char*)bux_alloc(32); + snprintf(result, 32, "%lld", (long long)n); + return result; +} + +/* String to int conversion */ +int64_t bux_str_to_int(const char* s) { + if (!s) return 0; + return (int64_t)atoll(s); +} + +/* String builder */ +typedef struct { + char* buf; + unsigned int len; + unsigned int cap; +} BuxStringBuilder; + +BuxStringBuilder* bux_sb_new(unsigned int initial_cap) { + BuxStringBuilder* sb = (BuxStringBuilder*)bux_alloc(sizeof(BuxStringBuilder)); + sb->cap = initial_cap > 0 ? initial_cap : 64; + sb->buf = (char*)bux_alloc(sb->cap); + sb->buf[0] = '\0'; + sb->len = 0; + return sb; +} + +void bux_sb_append(BuxStringBuilder* sb, const char* s) { + if (!sb || !s) return; + unsigned int s_len = (unsigned int)strlen(s); + unsigned int new_len = sb->len + s_len; + if (new_len + 1 > sb->cap) { + while (sb->cap < new_len + 1) sb->cap *= 2; + sb->buf = (char*)bux_realloc(sb->buf, sb->cap); + } + memcpy(sb->buf + sb->len, s, s_len); + sb->len = new_len; + sb->buf[sb->len] = '\0'; +} + +void bux_sb_append_int(BuxStringBuilder* sb, int64_t n) { + char tmp[32]; + snprintf(tmp, sizeof(tmp), "%lld", (long long)n); + bux_sb_append(sb, tmp); +} + +void bux_sb_append_float(BuxStringBuilder* sb, double f) { + char tmp[32]; + snprintf(tmp, sizeof(tmp), "%g", f); + bux_sb_append(sb, tmp); +} + +void bux_sb_append_char(BuxStringBuilder* sb, char c) { + if (!sb) return; + if (sb->len + 2 > sb->cap) { + sb->cap *= 2; + sb->buf = (char*)bux_realloc(sb->buf, sb->cap); + } + sb->buf[sb->len++] = c; + sb->buf[sb->len] = '\0'; +} + +const char* bux_sb_build(BuxStringBuilder* sb) { + if (!sb) return ""; + const char* buf = sb->buf; + sb->buf = NULL; // detach — ownership transferred to caller + return buf; +} + +void bux_sb_free(BuxStringBuilder* sb) { + if (!sb) return; + bux_free(sb->buf); // safe: NULL if bux_sb_build already detached + bux_free(sb); +} + +/* String split: count parts separated by delimiter */ +unsigned int bux_str_split_count(const char* s, const char* delim) { + if (!s || !delim || !*delim) return 1; + unsigned int count = 1; + size_t delim_len = strlen(delim); + const char* p = s; + while ((p = strstr(p, delim)) != NULL) { + count++; + p += delim_len; + } + return count; +} + +/* String split: get the n-th part (0-indexed) */ +char* bux_str_split_part(const char* s, const char* delim, unsigned int index) { + if (!s || !delim || !*delim) { + if (index == 0) { + unsigned int len = s ? (unsigned int)strlen(s) : 0; + char* result = (char*)bux_alloc(len + 1); + if (s) memcpy(result, s, len); + result[len] = '\0'; + return result; + } + char* empty = (char*)bux_alloc(1); + empty[0] = '\0'; + return empty; + } + size_t delim_len = strlen(delim); + const char* start = s; + const char* end; + unsigned int current = 0; + while (current < index) { + end = strstr(start, delim); + if (!end) { + char* empty = (char*)bux_alloc(1); + empty[0] = '\0'; + return empty; + } + start = end + delim_len; + current++; + } + end = strstr(start, delim); + size_t part_len; + if (end) { + part_len = (size_t)(end - start); + } else { + part_len = strlen(start); + } + char* result = (char*)bux_alloc(part_len + 1); + memcpy(result, start, part_len); + result[part_len] = '\0'; + return result; +} + +/* String join: join two strings with separator */ +char* bux_str_join2(const char* a, const char* b, const char* sep) { + if (!a && !b) { + char* empty = (char*)bux_alloc(1); + empty[0] = '\0'; + return empty; + } + unsigned int len_a = a ? (unsigned int)strlen(a) : 0; + unsigned int len_b = b ? (unsigned int)strlen(b) : 0; + unsigned int len_sep = sep ? (unsigned int)strlen(sep) : 0; + unsigned int total = len_a + len_sep + len_b; + char* result = (char*)bux_alloc(total + 1); + if (a) memcpy(result, a, len_a); + if (sep && len_a > 0 && len_b > 0) memcpy(result + len_a, sep, len_sep); + if (b) memcpy(result + len_a + (len_a > 0 && len_b > 0 ? len_sep : 0), b, len_b); + result[total] = '\0'; + return result; +} + +/* Simple string format: replace {0}, {1}, ... with string arguments. + Returns formatted string. Supports up to 8 arguments. */ +char* bux_float_to_string(double f) { + char* buf = (char*)bux_alloc(64); + snprintf(buf, 64, "%g", f); + return buf; +} + +char* bux_str_format(const char* pattern, + const char* a0, const char* a1, const char* a2, const char* a3, + const char* a4, const char* a5, const char* a6, const char* a7) { + if (!pattern) { + char* empty = (char*)bux_alloc(1); + empty[0] = '\0'; + return empty; + } + const char* args[8] = { a0, a1, a2, a3, a4, a5, a6, a7 }; + + /* Calculate result size */ + size_t total = 0; + const char* p = pattern; + while (*p) { + if (*p == '{' && p[1] >= '0' && p[1] <= '7' && p[2] == '}') { + int idx = p[1] - '0'; + if (args[idx]) total += strlen(args[idx]); + p += 3; + } else { + total++; + p++; + } + } + + char* result = (char*)bux_alloc(total + 1); + char* w = result; + p = pattern; + while (*p) { + if (*p == '{' && p[1] >= '0' && p[1] <= '7' && p[2] == '}') { + int idx = p[1] - '0'; + if (args[idx]) { + size_t len = strlen(args[idx]); + memcpy(w, args[idx], len); + w += len; + } + p += 3; + } else { + *w++ = *p++; + } + } + *w = '\0'; + return result; +} + +/* File I/O — read entire file into string */ +char* bux_read_file(const char* path) { + if (!path) return NULL; + FILE* f = fopen(path, "rb"); + if (!f) return NULL; + fseek(f, 0, SEEK_END); + long size = ftell(f); + fseek(f, 0, SEEK_SET); + char* buf = (char*)bux_alloc((size_t)size + 1); + if (!buf) { fclose(f); return NULL; } + size_t read = fread(buf, 1, (size_t)size, f); + fclose(f); + buf[read] = '\0'; + return buf; +} + +/* File I/O — write string to file */ +int bux_write_file(const char* path, const char* content) { + if (!path || !content) return 0; + FILE* f = fopen(path, "wb"); + if (!f) return 0; + size_t len = strlen(content); + size_t written = fwrite(content, 1, len, f); + fclose(f); + return written > 0 ? 1 : 0; +} + +/* File I/O — check if file exists */ +int bux_file_exists(const char* path) { + if (!path) return 0; + FILE* f = fopen(path, "rb"); + if (f) { + fclose(f); + return 1; + } + return 0; +} + +/* Path operations */ +char* bux_path_join(const char* a, const char* b) { + if (!a && !b) { + char* empty = (char*)bux_alloc(1); + empty[0] = '\0'; + return empty; + } + if (!a) { + unsigned int len = (unsigned int)strlen(b); + char* result = (char*)bux_alloc(len + 1); + memcpy(result, b, len + 1); + return result; + } + if (!b) { + unsigned int len = (unsigned int)strlen(a); + char* result = (char*)bux_alloc(len + 1); + memcpy(result, a, len + 1); + return result; + } + unsigned int len_a = (unsigned int)strlen(a); + unsigned int len_b = (unsigned int)strlen(b); + int need_sep = (len_a > 0 && a[len_a-1] != '/') ? 1 : 0; + unsigned int total = len_a + (need_sep ? 1 : 0) + len_b; + char* result = (char*)bux_alloc(total + 1); + memcpy(result, a, len_a); + if (need_sep) result[len_a] = '/'; + memcpy(result + len_a + (need_sep ? 1 : 0), b, len_b); + result[total] = '\0'; + return result; +} + +char* bux_path_parent(const char* path) { + if (!path) { + char* empty = (char*)bux_alloc(1); + empty[0] = '\0'; + return empty; + } + int len = (int)strlen(path); + while (len > 0 && path[len-1] == '/') len--; + while (len > 0 && path[len-1] != '/') len--; + while (len > 0 && path[len-1] == '/') len--; + if (len == 0) { + char* dot = (char*)bux_alloc(2); + dot[0] = '.'; + dot[1] = '\0'; + return dot; + } + char* result = (char*)bux_alloc((unsigned int)len + 1); + memcpy(result, path, (unsigned int)len); + result[len] = '\0'; + return result; +} + +char* bux_path_ext(const char* path) { + if (!path) { + char* empty = (char*)bux_alloc(1); + empty[0] = '\0'; + return empty; + } + const char* dot = strrchr(path, '.'); + if (!dot) { + char* empty = (char*)bux_alloc(1); + empty[0] = '\0'; + return empty; + } + const char* slash = strrchr(path, '/'); + if (slash && slash > dot) { + char* empty = (char*)bux_alloc(1); + empty[0] = '\0'; + return empty; + } + unsigned int len = (unsigned int)strlen(dot); + char* result = (char*)bux_alloc(len + 1); + memcpy(result, dot, len + 1); + return result; +} + +/* Math functions — wrap C math.h */ +#include + +double bux_sqrt(double x) { return sqrt(x); } +double bux_pow(double x, double y) { return pow(x, y); } +int64_t bux_abs_i64(int64_t x) { return x < 0 ? -x : x; } +double bux_abs_f64(double x) { return x < 0 ? -x : x; } +int64_t bux_min_i64(int64_t a, int64_t b) { return a < b ? a : b; } +int64_t bux_max_i64(int64_t a, int64_t b) { return a > b ? a : b; } +double bux_min_f64(double a, double b) { return a < b ? a : b; } +double bux_max_f64(double a, double b) { return a > b ? a : b; } + +/* Hash function (djb2) over raw bytes — for generic key types */ +unsigned int bux_hash_bytes(const void* ptr, size_t size) { + if (!ptr) return 0; + unsigned int hash = 5381; + const unsigned char* bytes = (const unsigned char*)ptr; + for (size_t i = 0; i < size; i++) { + hash = ((hash << 5) + hash) + bytes[i]; /* hash * 33 + byte */ + } + return hash; +} + +/* Byte equality check — for generic key comparison */ +int bux_mem_eq(const void* a, const void* b, size_t size) { + if (a == b) return 1; + if (!a || !b) return 0; + return memcmp(a, b, size) == 0; +} + +/* Hash function (djb2) for string keys */ +unsigned int bux_hash_string(const char* s) { + unsigned int hash = 5381; + int c; + while ((c = *s++)) { + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + } + return hash; +} + +/* --------------------------------------------------------------------------- + * Directory listing and build tools (for self-hosting compiler) + * --------------------------------------------------------------------------- */ + +#include +#include + +/* bux_list_dir: returns array of file paths in dir (recursively) matching ext suffix. + * Result is malloc'd array of malloc'd strings. Caller must free. + * Sets *out_count to number of files found. */ +static int bux_count_files_recursive(const char* dir, const char* ext, size_t ext_len) { + DIR* d = opendir(dir); + if (!d) return 0; + int count = 0; + struct dirent* entry; + while ((entry = readdir(d)) != NULL) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; + size_t path_len = strlen(dir) + 1 + strlen(entry->d_name) + 1; + char* path = (char*)malloc(path_len); + if (!path) continue; + snprintf(path, path_len, "%s/%s", dir, entry->d_name); + struct stat st; + if (stat(path, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + count += bux_count_files_recursive(path, ext, ext_len); + } else { + size_t name_len = strlen(entry->d_name); + if (name_len > ext_len && strcmp(entry->d_name + name_len - ext_len, ext) == 0) { + count++; + } + } + } + free(path); + } + closedir(d); + return count; +} + +static int bux_collect_files_recursive(const char* dir, const char* ext, size_t ext_len, char** result, int idx) { + DIR* d = opendir(dir); + if (!d) return idx; + struct dirent* entry; + while ((entry = readdir(d)) != NULL) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; + size_t path_len = strlen(dir) + 1 + strlen(entry->d_name) + 1; + char* path = (char*)malloc(path_len); + if (!path) continue; + snprintf(path, path_len, "%s/%s", dir, entry->d_name); + struct stat st; + if (stat(path, &st) == 0) { + if (S_ISDIR(st.st_mode)) { + idx = bux_collect_files_recursive(path, ext, ext_len, result, idx); + free(path); + } else { + size_t name_len = strlen(entry->d_name); + if (name_len > ext_len && strcmp(entry->d_name + name_len - ext_len, ext) == 0) { + result[idx++] = path; + } else { + free(path); + } + } + } else { + free(path); + } + } + closedir(d); + return idx; +} + +char** bux_list_dir(const char* dir, const char* ext, int* out_count) { + size_t ext_len = strlen(ext); + int count = bux_count_files_recursive(dir, ext, ext_len); + if (count == 0) { *out_count = 0; return NULL; } + char** result = (char**)malloc(count * sizeof(char*)); + if (!result) { *out_count = 0; return NULL; } + int idx = bux_collect_files_recursive(dir, ext, ext_len, result, 0); + *out_count = idx; + return result; +} + +/* bux_mkdir_if_needed: create directory if it doesn't exist */ +int bux_mkdir_if_needed(const char* path) { + struct stat st; + if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) return 0; + return mkdir(path, 0755); +} + +/* bux_run_cc: invoke C compiler to compile c_file into out_bin, + * linking runtime.c and io.c. Returns exit code. */ +int bux_run_cc(const char* c_file, const char* out_bin, + const char* runtime_c, const char* io_c, + const char* math_lib) { + char cmd[4096]; + snprintf(cmd, sizeof(cmd), + "cc %s %s %s -o %s %s 2>&1", + c_file, + runtime_c ? runtime_c : "", + io_c ? io_c : "", + out_bin, + math_lib ? "-lm" : ""); + return system(cmd); +} + +/* bux_dir_exists: check if directory exists */ +int bux_dir_exists(const char* path) { + struct stat st; + return (stat(path, &st) == 0 && S_ISDIR(st.st_mode)); +} + +/* ============================================================================ + * Concurrency primitives (Phase 8.3) + * ============================================================================ */ + +typedef struct { + pthread_t thread; +} BuxTask; + +typedef struct { + uint8_t* buffer; + size_t capacity; + size_t elem_size; + size_t head; + size_t tail; + size_t count; + pthread_mutex_t mutex; + pthread_cond_t not_empty; + pthread_cond_t not_full; + int closed; +} BuxChannel; + +/* Task / thread spawning */ +void* bux_task_spawn(void* (*func)(void*), void* arg) { + BuxTask* task = (BuxTask*)malloc(sizeof(BuxTask)); + if (!task) { + fprintf(stderr, "bux runtime: out of memory (task spawn)\n"); + abort(); + } + int rc = pthread_create(&task->thread, NULL, func, arg); + if (rc != 0) { + fprintf(stderr, "bux runtime: pthread_create failed (%d)\n", rc); + free(task); + return NULL; + } + return task; +} + +void bux_task_join(void* handle) { + if (!handle) return; + BuxTask* task = (BuxTask*)handle; + pthread_join(task->thread, NULL); + free(task); +} + +void bux_task_sleep(int64_t ms) { + if (ms <= 0) return; + struct timespec ts; + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000; + nanosleep(&ts, NULL); +} + +/* Channel implementation */ +void* bux_channel_new(int64_t capacity, int64_t elem_size) { + if (capacity <= 0) capacity = 1; + if (elem_size <= 0) elem_size = 1; + BuxChannel* ch = (BuxChannel*)malloc(sizeof(BuxChannel)); + if (!ch) { + fprintf(stderr, "bux runtime: out of memory (channel new)\n"); + abort(); + } + ch->buffer = (uint8_t*)malloc((size_t)capacity * (size_t)elem_size); + if (!ch->buffer) { + fprintf(stderr, "bux runtime: out of memory (channel buffer)\n"); + free(ch); + abort(); + } + ch->capacity = (size_t)capacity; + ch->elem_size = (size_t)elem_size; + ch->head = 0; + ch->tail = 0; + ch->count = 0; + ch->closed = 0; + pthread_mutex_init(&ch->mutex, NULL); + pthread_cond_init(&ch->not_empty, NULL); + pthread_cond_init(&ch->not_full, NULL); + return ch; +} + +void bux_channel_send(void* handle, void* elem) { + if (!handle || !elem) return; + BuxChannel* ch = (BuxChannel*)handle; + pthread_mutex_lock(&ch->mutex); + while (ch->count >= ch->capacity && !ch->closed) { + pthread_cond_wait(&ch->not_full, &ch->mutex); + } + if (ch->closed) { + pthread_mutex_unlock(&ch->mutex); + return; + } + uint8_t* dst = ch->buffer + ch->tail * ch->elem_size; + memcpy(dst, elem, ch->elem_size); + ch->tail = (ch->tail + 1) % ch->capacity; + ch->count++; + pthread_cond_signal(&ch->not_empty); + pthread_mutex_unlock(&ch->mutex); +} + +int bux_channel_recv(void* handle, void* out) { + if (!handle || !out) return 0; + BuxChannel* ch = (BuxChannel*)handle; + pthread_mutex_lock(&ch->mutex); + while (ch->count == 0 && !ch->closed) { + pthread_cond_wait(&ch->not_empty, &ch->mutex); + } + if (ch->count == 0) { + pthread_mutex_unlock(&ch->mutex); + return 0; /* channel empty and closed */ + } + uint8_t* src = ch->buffer + ch->head * ch->elem_size; + memcpy(out, src, ch->elem_size); + ch->head = (ch->head + 1) % ch->capacity; + ch->count--; + pthread_cond_signal(&ch->not_full); + pthread_mutex_unlock(&ch->mutex); + return 1; +} + +void bux_channel_close(void* handle) { + if (!handle) return; + BuxChannel* ch = (BuxChannel*)handle; + pthread_mutex_lock(&ch->mutex); + ch->closed = 1; + pthread_cond_broadcast(&ch->not_empty); + pthread_cond_broadcast(&ch->not_full); + pthread_mutex_unlock(&ch->mutex); +} + +void bux_channel_free(void* handle) { + if (!handle) return; + BuxChannel* ch = (BuxChannel*)handle; + pthread_mutex_destroy(&ch->mutex); + pthread_cond_destroy(&ch->not_empty); + pthread_cond_destroy(&ch->not_full); + free(ch->buffer); + free(ch); +} + +/* ============================================================================ + * Synchronization primitives (Mutex, RwLock) + * ============================================================================ */ + +typedef struct bux_mutex { + pthread_mutex_t mtx; +} bux_mutex_t; + +typedef struct bux_rwlock { + pthread_rwlock_t rwl; +} bux_rwlock_t; + +void* bux_mutex_new(void) { + bux_mutex_t* m = (bux_mutex_t*)malloc(sizeof(bux_mutex_t)); + if (!m) return NULL; + pthread_mutex_init(&m->mtx, NULL); + return m; +} + +void bux_mutex_lock(void* handle) { + if (!handle) return; + bux_mutex_t* m = (bux_mutex_t*)handle; + pthread_mutex_lock(&m->mtx); +} + +void bux_mutex_unlock(void* handle) { + if (!handle) return; + bux_mutex_t* m = (bux_mutex_t*)handle; + pthread_mutex_unlock(&m->mtx); +} + +void bux_mutex_free(void* handle) { + if (!handle) return; + bux_mutex_t* m = (bux_mutex_t*)handle; + pthread_mutex_destroy(&m->mtx); + free(m); +} + +void* bux_rwlock_new(void) { + bux_rwlock_t* rw = (bux_rwlock_t*)malloc(sizeof(bux_rwlock_t)); + if (!rw) return NULL; + pthread_rwlock_init(&rw->rwl, NULL); + return rw; +} + +void bux_rwlock_rdlock(void* handle) { + if (!handle) return; + bux_rwlock_t* rw = (bux_rwlock_t*)handle; + pthread_rwlock_rdlock(&rw->rwl); +} + +void bux_rwlock_wrlock(void* handle) { + if (!handle) return; + bux_rwlock_t* rw = (bux_rwlock_t*)handle; + pthread_rwlock_wrlock(&rw->rwl); +} + +void bux_rwlock_unlock(void* handle) { + if (!handle) return; + bux_rwlock_t* rw = (bux_rwlock_t*)handle; + pthread_rwlock_unlock(&rw->rwl); +} + +void bux_rwlock_free(void* handle) { + if (!handle) return; + bux_rwlock_t* rw = (bux_rwlock_t*)handle; + pthread_rwlock_destroy(&rw->rwl); + free(rw); +} + +/* ============================================================================ + * Stackful Coroutines + Async Scheduler (Phase 8.3 true async) + * ============================================================================ */ + +#define BUX_CORO_STACK_SIZE (64 * 1024) + +typedef struct bux_async_task { + ucontext_t ctx; + ucontext_t* caller_ctx; + uint8_t* stack; + int state; /* 0 = ready, 1 = running, 2 = done */ + void (*entry)(void); + void* result; /* pointer to heap-allocated result */ + int64_t sleep_until_ms; + struct bux_async_task* next; +} bux_async_task_t; + +static bux_async_task_t* bux_ready_head = NULL; +static bux_async_task_t* bux_ready_tail = NULL; +static bux_async_task_t* bux_current_task = NULL; +static ucontext_t bux_scheduler_ctx; +static int bux_scheduler_running = 0; + +static void bux_enqueue_ready(bux_async_task_t* task) { + task->next = NULL; + if (bux_ready_tail) { + bux_ready_tail->next = task; + } else { + bux_ready_head = task; + } + bux_ready_tail = task; +} + +static bux_async_task_t* bux_dequeue_ready(void) { + bux_async_task_t* task = bux_ready_head; + if (task) { + bux_ready_head = task->next; + if (!bux_ready_head) bux_ready_tail = NULL; + } + return task; +} + +static void bux_remove_from_ready(bux_async_task_t* target) { + bux_async_task_t* prev = NULL; + bux_async_task_t* curr = bux_ready_head; + while (curr) { + if (curr == target) { + if (prev) { + prev->next = curr->next; + } else { + bux_ready_head = curr->next; + } + if (bux_ready_tail == curr) { + bux_ready_tail = prev; + } + return; + } + prev = curr; + curr = curr->next; + } +} + +static void bux_coro_trampoline(void) { + bux_async_task_t* self = bux_current_task; + if (self != NULL && self->entry != NULL) { + self->entry(); + } + if (self != NULL) { + self->state = 2; /* done */ + swapcontext(&self->ctx, &bux_scheduler_ctx); + } +} + +void* bux_async_spawn(void (*func)(void)) { + bux_async_task_t* task = (bux_async_task_t*)malloc(sizeof(bux_async_task_t)); + if (!task) { + fprintf(stderr, "bux runtime: out of memory (async spawn)\n"); + abort(); + } + task->stack = (uint8_t*)malloc(BUX_CORO_STACK_SIZE); + if (!task->stack) { + fprintf(stderr, "bux runtime: out of memory (coro stack)\n"); + free(task); + abort(); + } + task->state = 0; + task->next = NULL; + task->caller_ctx = NULL; + task->entry = func; + task->sleep_until_ms = 0; + + getcontext(&task->ctx); + task->ctx.uc_stack.ss_sp = task->stack; + task->ctx.uc_stack.ss_size = BUX_CORO_STACK_SIZE; + task->ctx.uc_link = &bux_scheduler_ctx; + makecontext(&task->ctx, bux_coro_trampoline, 0); + + bux_enqueue_ready(task); + return task; +} + +void bux_async_yield(void) { + if (bux_current_task != NULL) { + bux_async_task_t* task = bux_current_task; + bux_current_task = NULL; + bux_enqueue_ready(task); + swapcontext(&task->ctx, &bux_scheduler_ctx); + } +} + + +void bux_async_run(void); + +void* bux_async_await(void* handle) { + if (!handle) return NULL; + bux_async_task_t* target = (bux_async_task_t*)handle; + while (target->state != 2) { + if (bux_current_task != NULL) { + bux_async_yield(); + } else { + if (!bux_scheduler_running) { + bux_async_run(); + } + } + } + void* result = target->result; + bux_remove_from_ready(target); + free(target->stack); + free(target); + return result; +} +static int64_t bux_now_ms(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; +} + +void bux_async_run(void) { + if (bux_scheduler_running) return; + bux_scheduler_running = 1; + getcontext(&bux_scheduler_ctx); + while (bux_ready_head != NULL) { + bux_async_task_t* task = bux_dequeue_ready(); + if (!task) break; + if (task->state == 2) { + /* Leave completed tasks in queue for await to clean up */ + bux_enqueue_ready(task); + continue; + } + /* Check if task is still sleeping */ + if (task->sleep_until_ms > 0) { + int64_t now = bux_now_ms(); + if (now < task->sleep_until_ms) { + /* Re-queue and check next task */ + bux_enqueue_ready(task); + /* If all tasks are sleeping, sleep the thread */ + if (bux_ready_head == task && bux_ready_tail == task) { + int64_t delay = task->sleep_until_ms - now; + struct timespec ts; + ts.tv_sec = delay / 1000; + ts.tv_nsec = (delay % 1000) * 1000000; + nanosleep(&ts, NULL); + } + continue; + } + task->sleep_until_ms = 0; + } + task->state = 1; + bux_current_task = task; + swapcontext(&bux_scheduler_ctx, &task->ctx); + bux_current_task = NULL; + } + bux_scheduler_running = 0; +} + +void bux_async_sleep(int64_t ms) { + if (ms > 0 && bux_current_task != NULL) { + bux_current_task->sleep_until_ms = bux_now_ms() + ms; + bux_async_yield(); + } +} + +void bux_async_return(void* value, size_t size) { + if (bux_current_task != NULL && value != NULL && size > 0) { + void* copy = malloc(size); + if (copy) memcpy(copy, value, size); + bux_current_task->result = copy; + } +} + +void* bux_async_result(void* handle) { + if (!handle) return NULL; + bux_async_task_t* task = (bux_async_task_t*)handle; + return task->result; +} + +/* ============================================================================ + * OS primitives + * ============================================================================ */ + +#include + +const char* bux_getenv(const char* name) { + if (!name) return ""; + const char* val = getenv(name); + return val ? val : ""; +} + +int bux_setenv(const char* name, const char* value) { + if (!name || !value) return -1; + return setenv(name, value, 1); +} + +const char* bux_getcwd(void) { + static char buf[4096]; + if (getcwd(buf, sizeof(buf))) { + return buf; + } + return ""; +} + +int bux_chdir(const char* path) { + if (!path) return -1; + return chdir(path); +} + +/* ============================================================================ + * Time primitives + * ============================================================================ */ + +int64_t bux_time_ms(void) { + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { + return (int64_t)ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + } + return 0; +} + +int64_t bux_time_us(void) { + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { + return (int64_t)ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + } + return 0; +} + +void bux_sleep_ms(int64_t ms) { + if (ms <= 0) return; + struct timespec ts; + ts.tv_sec = ms / 1000; + ts.tv_nsec = (ms % 1000) * 1000000; + nanosleep(&ts, NULL); +} + +/* ============================================================================ + * Process primitives + * ============================================================================ */ + +#include + +int bux_process_run(const char* cmd) { + if (!cmd) return -1; + return system(cmd); +} + +char* bux_process_output(const char* cmd) { + if (!cmd) return NULL; + FILE* pipe = popen(cmd, "r"); + if (!pipe) return NULL; + + size_t cap = 1024; + size_t len = 0; + char* buf = (char*)malloc(cap); + if (!buf) { pclose(pipe); return NULL; } + + int c; + while ((c = fgetc(pipe)) != EOF) { + if (len + 1 >= cap) { + cap *= 2; + char* new_buf = (char*)realloc(buf, cap); + if (!new_buf) { free(buf); pclose(pipe); return NULL; } + buf = new_buf; + } + buf[len++] = (char)c; + } + buf[len] = '\0'; + pclose(pipe); + return buf; +} + +/* ============================================================================ + * Network / Socket primitives + * ============================================================================ */ + +int bux_socket_create(void) { + int fd = socket(AF_INET, SOCK_STREAM, 0); + return fd; +} + +int bux_socket_reuse(int fd) { + int opt = 1; + return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); +} + +int bux_socket_bind(int fd, const char* addr, int port) { + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons((uint16_t)port); + if (addr == NULL || addr[0] == '\0') { + sa.sin_addr.s_addr = INADDR_ANY; + } else { + if (inet_pton(AF_INET, addr, &sa.sin_addr) <= 0) { + return -1; + } + } + return bind(fd, (struct sockaddr*)&sa, sizeof(sa)); +} + +int bux_socket_listen(int fd, int backlog) { + return listen(fd, backlog); +} + +int bux_socket_accept(int fd) { + struct sockaddr_in sa; + socklen_t len = sizeof(sa); + return accept(fd, (struct sockaddr*)&sa, &len); +} + +int bux_socket_connect(int fd, const char* addr, int port) { + struct sockaddr_in sa; + memset(&sa, 0, sizeof(sa)); + sa.sin_family = AF_INET; + sa.sin_port = htons((uint16_t)port); + if (inet_pton(AF_INET, addr, &sa.sin_addr) <= 0) { + return -1; + } + return connect(fd, (struct sockaddr*)&sa, sizeof(sa)); +} + +int bux_socket_send(int fd, const char* data, int len) { + if (!data || len <= 0) return 0; + return (int)send(fd, data, (size_t)len, 0); +} + +BuxString bux_socket_recv(int fd, int max_len) { + BuxString result; + if (max_len <= 0) { + result.data = ""; + result.len = 0; + return result; + } + char* buf = (char*)bux_alloc((size_t)max_len + 1); + ssize_t n = recv(fd, buf, (size_t)max_len, 0); + if (n <= 0) { + result.data = ""; + result.len = 0; + return result; + } + buf[n] = '\0'; + result.data = buf; + result.len = (size_t)n; + return result; +} + +int bux_socket_close(int fd) { + return close(fd); +} + +const char* bux_socket_error(void) { + return strerror(errno); +} + +/* ============================================================================ + * Test / Assert primitives + * ============================================================================ */ + +void bux_exit(int code) { + exit(code); +} + +void bux_assert(int cond, const char* file, int line, const char* expr) { + if (!cond) { + fprintf(stderr, "ASSERT FAILED: %s at %s:%d\n", expr, file, line); + exit(1); + } +} + +/* ============================================================================ + * Cryptography primitives (OpenSSL) + * ============================================================================ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void bux_sha256(const char* data, int len, unsigned char* out) { + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + EVP_DigestInit_ex(ctx, EVP_sha256(), NULL); + EVP_DigestUpdate(ctx, data, (size_t)len); + EVP_DigestFinal_ex(ctx, out, NULL); + EVP_MD_CTX_free(ctx); +} + +void bux_hmac_sha256(const char* key, int keylen, const char* msg, int msglen, unsigned char* out) { + unsigned int outlen = 32; + HMAC(EVP_sha256(), key, keylen, (const unsigned char*)msg, (size_t)msglen, out, &outlen); +} + +int bux_random_bytes(unsigned char* buf, int len) { + return RAND_bytes(buf, len); +} + +char* bux_base64_encode(const unsigned char* in, int inlen) { + int outlen = 4 * ((inlen + 2) / 3); + char* out = (char*)bux_alloc(outlen + 1); + int elen = EVP_EncodeBlock((unsigned char*)out, in, inlen); + out[elen] = '\0'; + return out; +} + +char* bux_base64_decode(const char* in, int inlen, int* outlen) { + int maxlen = 3 * inlen / 4; + char* out = (char*)bux_alloc(maxlen + 1); + *outlen = EVP_DecodeBlock((unsigned char*)out, (const unsigned char*)in, inlen); + if (*outlen < 0) { + *outlen = 0; + out[0] = '\0'; + } + return out; +} + +char* bux_bytes_to_hex(const unsigned char* data, int len) { + char* out = (char*)bux_alloc((size_t)len * 2 + 1); + const char* hex = "0123456789abcdef"; + for (int i = 0; i < len; i++) { + out[i * 2] = hex[(data[i] >> 4) & 0x0F]; + out[i * 2 + 1] = hex[data[i] & 0x0F]; + } + out[len * 2] = '\0'; + return out; +} + +/* ============================================================================ + * Extended cryptography primitives (OpenSSL) + * ============================================================================ */ + +static void bux_digest(const EVP_MD* md, const char* data, int len, unsigned char* out) { + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + EVP_DigestInit_ex(ctx, md, NULL); + EVP_DigestUpdate(ctx, data, (size_t)len); + EVP_DigestFinal_ex(ctx, out, NULL); + EVP_MD_CTX_free(ctx); +} + +void bux_sha1(const char* data, int len, unsigned char* out) { + bux_digest(EVP_sha1(), data, len, out); +} + +void bux_sha384(const char* data, int len, unsigned char* out) { + bux_digest(EVP_sha384(), data, len, out); +} + +void bux_sha512(const char* data, int len, unsigned char* out) { + bux_digest(EVP_sha512(), data, len, out); +} + +/* --- HMAC --- */ + +void bux_hmac_sha384(const char* key, int keylen, const char* msg, int msglen, unsigned char* out) { + unsigned int outlen = 48; + HMAC(EVP_sha384(), key, keylen, (const unsigned char*)msg, (size_t)msglen, out, &outlen); +} + +void bux_hmac_sha512(const char* key, int keylen, const char* msg, int msglen, unsigned char* out) { + unsigned int outlen = 64; + HMAC(EVP_sha512(), key, keylen, (const unsigned char*)msg, (size_t)msglen, out, &outlen); +} + +/* --- Base64URL --- */ + +static const char b64url_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; + +char* bux_base64url_encode(const unsigned char* in, int inlen) { + int outlen = 4 * ((inlen + 2) / 3); + char* out = (char*)bux_alloc(outlen + 1); + int j = 0; + for (int i = 0; i < inlen; i += 3) { + int a = in[i]; + int b = (i + 1 < inlen) ? in[i + 1] : 0; + int c = (i + 2 < inlen) ? in[i + 2] : 0; + out[j++] = b64url_table[(a >> 2) & 0x3F]; + out[j++] = b64url_table[((a << 4) | (b >> 4)) & 0x3F]; + if (i + 1 < inlen) + out[j++] = b64url_table[((b << 2) | (c >> 6)) & 0x3F]; + if (i + 2 < inlen) + out[j++] = b64url_table[c & 0x3F]; + } + out[j] = '\0'; + return out; +} + +static int b64url_char_val(char c) { + if (c >= 'A' && c <= 'Z') return c - 'A'; + if (c >= 'a' && c <= 'z') return c - 'a' + 26; + if (c >= '0' && c <= '9') return c - '0' + 52; + if (c == '-') return 62; + if (c == '_') return 63; + return -1; +} + +char* bux_base64url_decode(const char* in, int inlen, int* outlen) { + int maxlen = 3 * inlen / 4 + 1; + char* out = (char*)bux_alloc(maxlen); + int j = 0; + int buf = 0, bits = 0; + for (int i = 0; i < inlen; i++) { + int val = b64url_char_val(in[i]); + if (val < 0) continue; + buf = (buf << 6) | val; + bits += 6; + if (bits >= 8) { + bits -= 8; + out[j++] = (buf >> bits) & 0xFF; + } + } + out[j] = '\0'; + *outlen = j; + return out; +} + +/* --- AES-256-CBC --- */ + +static int aes_cbc_ctx_encrypt(const unsigned char* in, int inlen, + unsigned char* out, const unsigned char* key, + const unsigned char* iv, int encrypt) { + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + EVP_CipherInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv, encrypt); + int outlen = 0, tmplen = 0; + EVP_CipherUpdate(ctx, out, &tmplen, in, inlen); + outlen = tmplen; + EVP_CipherFinal_ex(ctx, out + outlen, &tmplen); + outlen += tmplen; + EVP_CIPHER_CTX_free(ctx); + return outlen; +} + +char* bux_aes_256_cbc_encrypt(const char* plain, int plainlen, + const char* key, const char* iv, int* outlen) { + int maxlen = plainlen + EVP_MAX_BLOCK_LENGTH; + char* out = (char*)bux_alloc(maxlen + 1); + *outlen = aes_cbc_ctx_encrypt((const unsigned char*)plain, plainlen, + (unsigned char*)out, + (const unsigned char*)key, + (const unsigned char*)iv, 1); + out[*outlen] = '\0'; + return out; +} + +char* bux_aes_256_cbc_decrypt(const char* cipher, int cipherlen, + const char* key, const char* iv, int* outlen) { + char* out = (char*)bux_alloc(cipherlen + 1); + *outlen = aes_cbc_ctx_encrypt((const unsigned char*)cipher, cipherlen, + (unsigned char*)out, + (const unsigned char*)key, + (const unsigned char*)iv, 0); + out[*outlen] = '\0'; + return out; +} + +/* --- AES-256-GCM --- */ + +char* bux_aes_256_gcm_encrypt(const char* plain, int plainlen, + const char* key, const char* iv, + unsigned char* tag, int* outlen) { + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, (const unsigned char*)key, + (const unsigned char*)iv); + int maxlen = plainlen + EVP_MAX_BLOCK_LENGTH; + char* out = (char*)bux_alloc(maxlen); + int tmplen = 0; + EVP_EncryptUpdate(ctx, (unsigned char*)out, &tmplen, + (const unsigned char*)plain, plainlen); + *outlen = tmplen; + EVP_EncryptFinal_ex(ctx, (unsigned char*)out + *outlen, &tmplen); + *outlen += tmplen; + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag); + EVP_CIPHER_CTX_free(ctx); + return out; +} + +char* bux_aes_256_gcm_decrypt(const char* cipher, int cipherlen, + const char* key, const char* iv, + const char* tag, int* outlen) { + EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); + EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, (const unsigned char*)key, + (const unsigned char*)iv); + char* out = (char*)bux_alloc(cipherlen); + int tmplen = 0; + EVP_DecryptUpdate(ctx, (unsigned char*)out, &tmplen, + (const unsigned char*)cipher, cipherlen); + *outlen = tmplen; + EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, (void*)tag); + int ret = EVP_DecryptFinal_ex(ctx, (unsigned char*)out + *outlen, &tmplen); + EVP_CIPHER_CTX_free(ctx); + if (ret <= 0) { *outlen = 0; out[0] = '\0'; return out; } + *outlen += tmplen; + out[*outlen] = '\0'; + return out; +} + +/* --- RSA PKCS#1 v1.5 sign / verify --- */ + +static EVP_PKEY* bux_load_private_key(const char* pem, int len) { + BIO* bio = BIO_new_mem_buf(pem, len); + if (!bio) return NULL; + EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + return pkey; +} + +static EVP_PKEY* bux_load_public_key(const char* pem, int len) { + BIO* bio = BIO_new_mem_buf(pem, len); + if (!bio) return NULL; + EVP_PKEY* pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); + BIO_free(bio); + return pkey; +} + +static char* bux_rsa_sign_evp(const EVP_MD* md, const char* pem_key, int keylen, + const char* data, int datalen, int* siglen) { + EVP_PKEY* pkey = bux_load_private_key(pem_key, keylen); + if (!pkey) { *siglen = 0; return (char*)bux_alloc(1); } + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + EVP_SignInit_ex(ctx, md, NULL); + EVP_SignUpdate(ctx, data, (size_t)datalen); + unsigned int slen = (unsigned int)EVP_PKEY_size(pkey); + unsigned char* sig = (unsigned char*)bux_alloc(slen + 1); + EVP_SignFinal(ctx, sig, &slen, pkey); + *siglen = (int)slen; + sig[*siglen] = '\0'; + EVP_MD_CTX_free(ctx); + EVP_PKEY_free(pkey); + return (char*)sig; +} + +static int bux_rsa_verify_evp(const EVP_MD* md, const char* pem_key, int keylen, + const char* data, int datalen, + const char* sig, int siglen) { + EVP_PKEY* pkey = bux_load_public_key(pem_key, keylen); + if (!pkey) return 0; + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + EVP_VerifyInit_ex(ctx, md, NULL); + EVP_VerifyUpdate(ctx, data, (size_t)datalen); + int ret = EVP_VerifyFinal(ctx, (const unsigned char*)sig, (size_t)siglen, pkey); + EVP_MD_CTX_free(ctx); + EVP_PKEY_free(pkey); + return ret; +} + +char* bux_rsa_sign_sha256(const char* pem, int keylen, const char* data, int datalen, int* siglen) { + return bux_rsa_sign_evp(EVP_sha256(), pem, keylen, data, datalen, siglen); +} +char* bux_rsa_sign_sha384(const char* pem, int keylen, const char* data, int datalen, int* siglen) { + return bux_rsa_sign_evp(EVP_sha384(), pem, keylen, data, datalen, siglen); +} +char* bux_rsa_sign_sha512(const char* pem, int keylen, const char* data, int datalen, int* siglen) { + return bux_rsa_sign_evp(EVP_sha512(), pem, keylen, data, datalen, siglen); +} + +int bux_rsa_verify_sha256(const char* pem, int keylen, const char* data, int datalen, + const char* sig, int siglen) { + return bux_rsa_verify_evp(EVP_sha256(), pem, keylen, data, datalen, sig, siglen); +} +int bux_rsa_verify_sha384(const char* pem, int keylen, const char* data, int datalen, + const char* sig, int siglen) { + return bux_rsa_verify_evp(EVP_sha384(), pem, keylen, data, datalen, sig, siglen); +} +int bux_rsa_verify_sha512(const char* pem, int keylen, const char* data, int datalen, + const char* sig, int siglen) { + return bux_rsa_verify_evp(EVP_sha512(), pem, keylen, data, datalen, sig, siglen); +} + +/* --- ECDSA P-256 / P-384 --- */ + +static char* bux_ecdsa_sign_evp(const EVP_MD* md, const char* pem, int keylen, + const char* data, int datalen, int* siglen) { + EVP_PKEY* pkey = bux_load_private_key(pem, keylen); + if (!pkey) { *siglen = 0; return (char*)bux_alloc(1); } + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + EVP_SignInit_ex(ctx, md, NULL); + EVP_SignUpdate(ctx, data, (size_t)datalen); + unsigned int slen = (unsigned int)EVP_PKEY_size(pkey); + unsigned char* sig = (unsigned char*)bux_alloc(slen + 1); + EVP_SignFinal(ctx, sig, &slen, pkey); + *siglen = (int)slen; + sig[*siglen] = '\0'; + EVP_MD_CTX_free(ctx); + EVP_PKEY_free(pkey); + return (char*)sig; +} + +static int bux_ecdsa_verify_evp(const EVP_MD* md, const char* pem, int keylen, + const char* data, int datalen, + const char* sig, int siglen) { + EVP_PKEY* pkey = bux_load_public_key(pem, keylen); + if (!pkey) return 0; + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + EVP_VerifyInit_ex(ctx, md, NULL); + EVP_VerifyUpdate(ctx, data, (size_t)datalen); + int ret = EVP_VerifyFinal(ctx, (const unsigned char*)sig, (size_t)siglen, pkey); + EVP_MD_CTX_free(ctx); + EVP_PKEY_free(pkey); + return ret; +} + +char* bux_ecdsa_sign_p256(const char* pem, int keylen, const char* data, int datalen, int* siglen) { + return bux_ecdsa_sign_evp(EVP_sha256(), pem, keylen, data, datalen, siglen); +} +char* bux_ecdsa_sign_p384(const char* pem, int keylen, const char* data, int datalen, int* siglen) { + return bux_ecdsa_sign_evp(EVP_sha384(), pem, keylen, data, datalen, siglen); +} +int bux_ecdsa_verify_p256(const char* pem, int keylen, const char* data, int datalen, + const char* sig, int siglen) { + return bux_ecdsa_verify_evp(EVP_sha256(), pem, keylen, data, datalen, sig, siglen); +} +int bux_ecdsa_verify_p384(const char* pem, int keylen, const char* data, int datalen, + const char* sig, int siglen) { + return bux_ecdsa_verify_evp(EVP_sha384(), pem, keylen, data, datalen, sig, siglen); +} + +/* --- Ed25519 (OpenSSL 1.1.1+) --- */ + +int bux_ed25519_keypair(unsigned char* pub, unsigned char* priv) { + EVP_PKEY* pkey = NULL; + EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, NULL); + if (!pctx) return 0; + if (EVP_PKEY_keygen_init(pctx) <= 0 || + EVP_PKEY_keygen(pctx, &pkey) <= 0) { + EVP_PKEY_CTX_free(pctx); + return 0; + } + EVP_PKEY_CTX_free(pctx); + size_t pub_len = 32, priv_len = 32; + EVP_PKEY_get_raw_public_key(pkey, pub, &pub_len); + EVP_PKEY_get_raw_private_key(pkey, priv, &priv_len); + EVP_PKEY_free(pkey); + return 1; +} + +int bux_ed25519_sign(const char* priv, const char* data, int datalen, unsigned char* sig) { + EVP_PKEY* pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_ED25519, NULL, + (const unsigned char*)priv, 32); + if (!pkey) return 0; + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + int ret = 0; + if (EVP_DigestSignInit(ctx, NULL, NULL, NULL, pkey) > 0) { + size_t siglen = 64; + EVP_DigestSign(ctx, sig, &siglen, (const unsigned char*)data, (size_t)datalen); + ret = 1; + } + EVP_MD_CTX_free(ctx); + EVP_PKEY_free(pkey); + return ret; +} + +int bux_ed25519_verify(const char* pub, const char* sig, const char* data, int datalen) { + EVP_PKEY* pkey = EVP_PKEY_new_raw_public_key(EVP_PKEY_ED25519, NULL, + (const unsigned char*)pub, 32); + if (!pkey) return 0; + EVP_MD_CTX* ctx = EVP_MD_CTX_new(); + int ret = 0; + if (EVP_DigestVerifyInit(ctx, NULL, NULL, NULL, pkey) > 0) { + ret = EVP_DigestVerify(ctx, (const unsigned char*)sig, 64, + (const unsigned char*)data, (size_t)datalen); + ret = (ret == 1) ? 1 : 0; + } + EVP_MD_CTX_free(ctx); + EVP_PKEY_free(pkey); + return ret; +} diff --git a/_test_closure/bux.toml b/_test_closure/bux.toml new file mode 100644 index 0000000..8f016f2 --- /dev/null +++ b/_test_closure/bux.toml @@ -0,0 +1,7 @@ +[Package] +Name = "closure_test" +Version = "0.1.0" +Type = "bin" + +[Build] +Output = "Bin" diff --git a/_test_closure/src/Main.bux b/_test_closure/src/Main.bux new file mode 100644 index 0000000..b8b4f73 --- /dev/null +++ b/_test_closure/src/Main.bux @@ -0,0 +1,34 @@ +module Main { + +extern func PrintInt(x: int); +extern func PrintLine(msg: String); + +func Apply(x: int, op: func(int) -> int) -> int { + return op(x); +} + +func Double(x: int) -> int { + return x * 2; +} + +func main() -> int { + // Closure as variable + let add: func(int, int) -> int = |a: int, b: int| -> int { return a + b; }; + let sum: int = add(3, 4); + PrintInt(sum); + PrintLine(""); + + // Closure passed to function + let result: int = Apply(5, |x: int| -> int { return x * 3; }); + PrintInt(result); + PrintLine(""); + + // Address of named function as function pointer + let d: func(int) -> int = &Double; + PrintInt(d(7)); + PrintLine(""); + + return 0; +} + +} diff --git a/bootstrap/ast.nim b/bootstrap/ast.nim index 4fe20c5..4e512bb 100644 --- a/bootstrap/ast.nim +++ b/bootstrap/ast.nim @@ -132,6 +132,7 @@ type ekBlock ekMatch ekStringInterp + ekClosure MatchArm* = object loc*: SourceLocation @@ -228,6 +229,10 @@ type of ekStringInterp: exprInterpTexts*: seq[string] exprInterpExprs*: seq[Expr] + of ekClosure: + exprClosureParams*: seq[Param] + exprClosureBody*: Block + exprClosureReturnType*: TypeExpr # --------------------------------------------------------------------------- # Statements diff --git a/bootstrap/hir_lower.nim b/bootstrap/hir_lower.nim index 187641d..6d6feee 100644 --- a/bootstrap/hir_lower.nim +++ b/bootstrap/hir_lower.nim @@ -276,6 +276,7 @@ proc resolveTypeExpr(ctx: var LowerCtx, te: TypeExpr): Type = proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode proc lowerBlock(ctx: var LowerCtx, blk: Block): HirNode +proc lowerClosureFunc(ctx: var LowerCtx, expr: Expr): HirFunc proc resolveExprType(ctx: var LowerCtx, expr: Expr): Type = if expr == nil: return makeUnknown() @@ -1139,6 +1140,10 @@ proc lowerExpr(ctx: var LowerCtx, expr: Expr): HirNode = resultNode = hirCall("String_Concat", @[resultNode, lastTextNode], makeStr(), loc) return resultNode + of ekClosure: + let f = ctx.lowerClosureFunc(expr) + return hirUnary(tkAmp, hirVar(f.name, makeFunc(@[], makeVoid()), loc), typ, loc) + else: return HirNode(kind: hLit, litToken: Token(kind: tkIntLiteral, text: "0", loc: loc), typ: makeVoid(), loc: loc) @@ -1167,6 +1172,12 @@ proc lowerStmt(ctx: var LowerCtx, stmt: Stmt): HirNode = of tekSlice: let elemType = ctx.resolveTypeExpr(stmt.stmtLetType.sliceElement) makeSlice(elemType) + of tekFunc: + var params: seq[Type] = @[] + for p in stmt.stmtLetType.funcParams: + params.add(ctx.resolveTypeExpr(p)) + let ret = if stmt.stmtLetType.funcRet != nil: ctx.resolveTypeExpr(stmt.stmtLetType.funcRet) else: makeVoid() + makeFunc(params, ret) else: makeUnknown() elif stmt.stmtLetInit != nil: ctx.resolveExprType(stmt.stmtLetInit) @@ -1475,6 +1486,25 @@ proc generateMethodInstance(ctx: var LowerCtx, baseMethodName: string, typeArgs: ctx.generatedFuncInsts[mangledName] = true return mangledName +proc lowerClosureFunc(ctx: var LowerCtx, expr: Expr): HirFunc = + let loc = expr.loc + let name = "__closure_" & $ctx.varCounter + inc ctx.varCounter + var f = HirFunc(name: name, isPublic: false) + # Params + for p in expr.exprClosureParams: + f.params.add((name: p.name, typ: if p.ptype != nil: ctx.resolveTypeExpr(p.ptype) else: makeUnknown())) + # Return type + if expr.exprClosureReturnType != nil: + f.retType = ctx.resolveTypeExpr(expr.exprClosureReturnType) + else: + f.retType = makeVoid() + # Body + if expr.exprClosureBody != nil: + f.body = ctx.lowerBlock(expr.exprClosureBody) + ctx.extraFuncs.add(f) + return f + proc lowerModule*(module: Module, sema: Sema): HirModule = var ctx = initLowerCtx(module, sema) var funcs: seq[HirFunc] = @[] diff --git a/bootstrap/lir_c_backend.nim b/bootstrap/lir_c_backend.nim index af16321..2141cd9 100644 --- a/bootstrap/lir_c_backend.nim +++ b/bootstrap/lir_c_backend.nim @@ -58,6 +58,13 @@ proc typeFromValue(be: var LirCBackend, v: LirValue): string = proc setTempType(be: var LirCBackend, temp: string, cType: string) = be.tempTypes[temp] = cType +proc cParamDecl(cType, name: string): string = + ## Emit a C parameter declaration, handling function-pointer syntax. + if cType.contains("(*)"): + return cType.replace("(*)", "(*" & name & ")") + else: + return cType & " " & name + # ── Per-instruction emission ── proc emitInstr(be: var LirCBackend, instr: LirInstr) = @@ -183,7 +190,7 @@ proc emitInstr(be: var LirCBackend, instr: LirInstr) = let inferred = be.tempTypes[instr.dst.strVal] if inferred != "" and inferred != ct: ct = inferred - be.emitLine(&"{ct} {v(instr.dst)};") + be.emitLine(cParamDecl(ct, v(instr.dst)) & ";") # ── Pointers ── of lirAddrOf: @@ -241,13 +248,6 @@ proc emitInstr(be: var LirCBackend, instr: LirInstr) = # ── Function emission ── -proc cParamDecl(cType, name: string): string = - ## Emit a C parameter declaration, handling function-pointer syntax. - if cType.contains("(*)"): - return cType.replace("(*)", "(*" & name & ")") - else: - return cType & " " & name - proc emitFunc(be: var LirCBackend, f: LirFunc, funcRetTypes: Table[string, string], funcPtrTypes: Table[string, string]) = var paramsStr = "" for i, p in f.params: diff --git a/bootstrap/parser.nim b/bootstrap/parser.nim index ad34094..c661479 100644 --- a/bootstrap/parser.nim +++ b/bootstrap/parser.nim @@ -587,6 +587,26 @@ proc parsePrimary(p: var Parser): Expr = of tkNull: discard p.advance() return newLiteralExpr(Token(kind: tkNull, text: "null", loc: loc)) + of tkPipe: + # Closure: |params| -> Ret { body } + discard p.advance() # | + var params: seq[Param] = @[] + while not p.check(tkPipe) and not p.isAtEnd: + let nameTok = p.expect(tkIdent, "expected parameter name in closure") + discard p.expect(tkColon, "expected ':' in closure parameter") + let ptype = p.parseType() + params.add(Param(name: nameTok.text, ptype: ptype)) + if p.check(tkComma): + discard p.advance() + else: + break + discard p.expect(tkPipe, "expected '|' to close closure params") + var retType: TypeExpr = nil + if p.check(tkArrow): + discard p.advance() # -> + retType = p.parseType() + let body = p.parseBlock() + return Expr(kind: ekClosure, loc: loc, exprClosureParams: params, exprClosureBody: body, exprClosureReturnType: retType) else: p.emitError(loc, "expected expression") discard p.advance() diff --git a/bootstrap/sema.nim b/bootstrap/sema.nim index 29825b5..84ad050 100644 --- a/bootstrap/sema.nim +++ b/bootstrap/sema.nim @@ -44,6 +44,7 @@ type checkedFunc*: bool ## true inside @[Checked] function currentFuncIsAsync*: bool ## true inside async func movedVars*: seq[string] ## variables moved in current checked function + currentRetType*: Type ## return type of the function being checked # --------------------------------------------------------------------------- # Helpers @@ -1337,6 +1338,25 @@ proc checkExpr(sema: var Sema, expr: Expr, scope: Scope): Type = for e in expr.exprInterpExprs: discard sema.checkExpr(e, scope) return makeStr() + of ekClosure: + let savedRetType = sema.currentRetType + let childScope = Scope(parent: scope) + sema.currentRetType = if expr.exprClosureReturnType != nil: sema.resolveType(expr.exprClosureReturnType) else: makeUnknown() + # Register params + for p in expr.exprClosureParams: + let ptype = if p.ptype != nil: sema.resolveType(p.ptype) else: makeUnknown() + discard childScope.define(Symbol(kind: skVar, name: p.name, typ: ptype)) + # Check body + if expr.exprClosureBody != nil: + for stmt in expr.exprClosureBody.stmts: + discard sema.checkStmt(stmt, childScope) + sema.currentRetType = savedRetType + # Build function type + var params: seq[Type] = @[] + for p in expr.exprClosureParams: + params.add(if p.ptype != nil: sema.resolveType(p.ptype) else: makeUnknown()) + let retType = if expr.exprClosureReturnType != nil: sema.resolveType(expr.exprClosureReturnType) else: makeVoid() + return makeFunc(params, retType) # --------------------------------------------------------------------------- # Statement type checking diff --git a/src/ast.bux b/src/ast.bux index ccbe55d..cb1c092 100644 --- a/src/ast.bux +++ b/src/ast.bux @@ -107,6 +107,7 @@ const ekMatch: int = 22; const ekSpawn: int = 24; const ekAwait: int = 25; const ekStringInterp: int = 26; +const ekClosure: int = 27; struct ExprList { expr: *Expr, @@ -139,6 +140,8 @@ struct Expr { // Struct init fields structName: String, structFieldCount: int, + // Closure params (for ekClosure) + closureParams: *Decl, // Call arguments (linked list for multi-arg support) callArgs: *ExprList, callArgCount: int, diff --git a/src/hir_lower.bux b/src/hir_lower.bux index f6e687c..2c25ece 100644 --- a/src/hir_lower.bux +++ b/src/hir_lower.bux @@ -384,6 +384,23 @@ func Lcx_LowerExpr(ctx: *LowerCtx, expr: *Expr) -> *HirNode { return n; } + // Closure: generate function and return address-of + if kind == ekClosure { + let f: *HirFunc = Lcx_LowerClosureFunc(ctx, expr); + n.kind = hUnary; + n.intValue = tkAmp; + let varNode: *HirNode = bux_alloc(sizeof(HirNode)) as *HirNode; + varNode.kind = hVar; + varNode.strValue = f.name; + varNode.typeKind = tyFunc; + n.child1 = varNode; + n.typeKind = tyFunc; + if expr.refType != null as *TypeExpr { + n.typeName = Lcx_BuildFuncTypeName(expr.refType); + } + return n; + } + // Cast if kind == ekCast { n.kind = hCast; @@ -779,6 +796,93 @@ func Lcx_LowerFunc(ctx: *LowerCtx, decl: *Decl) -> *HirFunc { return f; } +// --------------------------------------------------------------------------- +// Closure lowering — generate a global function for a closure expression +// --------------------------------------------------------------------------- + +func Lcx_LowerClosureFunc(ctx: *LowerCtx, expr: *Expr) -> *HirFunc { + let f: *HirFunc = bux_alloc(sizeof(HirFunc)) as *HirFunc; + + // Generate unique name + let numStr: String = String_FromInt(ctx.funcCount); + f.name = String_Concat("__closure_", numStr); + f.isPublic = false; + + let params: *Decl = expr.closureParams; + if params != null as *Decl { + f.paramCount = params.paramCount; + if params.paramCount > 0 { f.param0 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param0, ¶ms.param0); } + if params.paramCount > 1 { f.param1 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param1, ¶ms.param1); } + if params.paramCount > 2 { f.param2 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param2, ¶ms.param2); } + if params.paramCount > 3 { f.param3 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param3, ¶ms.param3); } + if params.paramCount > 4 { f.param4 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param4, ¶ms.param4); } + if params.paramCount > 5 { f.param5 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param5, ¶ms.param5); } + if params.paramCount > 6 { f.param6 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param6, ¶ms.param6); } + if params.paramCount > 7 { f.param7 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param7, ¶ms.param7); } + if params.paramCount > 8 { f.param8 = bux_alloc(sizeof(HirParam)) as *HirParam; Lcx_LowerParam(f.param8, ¶ms.param8); } + } + + if expr.refType != null as *TypeExpr && expr.refType.kind == tekFunc && expr.refType.funcRet != null as *TypeExpr { + f.retTypeName = expr.refType.funcRet.typeName; + f.retTypeKind = Lcx_ResolveTypeKind(expr.refType.funcRet); + } else { + f.retTypeName = ""; + f.retTypeKind = 0; + } + + // Create function scope + var funcScope: Scope = Scope_NewChild(ctx.scope); + var pi: int = 0; + while pi < params.paramCount { + var p: *Param = null as *Param; + if pi == 0 { p = ¶ms.param0; } + else if pi == 1 { p = ¶ms.param1; } + else if pi == 2 { p = ¶ms.param2; } + else if pi == 3 { p = ¶ms.param3; } + else if pi == 4 { p = ¶ms.param4; } + else if pi == 5 { p = ¶ms.param5; } + else if pi == 6 { p = ¶ms.param6; } + else if pi == 7 { p = ¶ms.param7; } + else if pi == 8 { p = ¶ms.param8; } + if p != null as *Param && p.refParamType != null as *TypeExpr { + var sym: Symbol; + sym.kind = skVar; + sym.name = p.name; + sym.typeKind = Lcx_ResolveTypeKind(p.refParamType); + sym.refType = p.refParamType; + if p.refParamType.kind == tekFunc { + sym.typeName = Lcx_BuildFuncTypeName(p.refParamType); + } else if !String_Eq(p.refParamType.typeName, "") { + sym.typeName = p.refParamType.typeName; + } else if p.refParamType.kind == tekPointer && p.refParamType.pointerPointee != null as *TypeExpr { + sym.typeName = String_Concat(p.refParamType.pointerPointee.typeName, "*"); + } else { + sym.typeName = ""; + } + sym.isMutable = false; + sym.isPublic = false; + sym.decl = null as *Decl; + discard Scope_Define(&funcScope, sym); + } + pi = pi + 1; + } + + let prevScope: *Scope = ctx.scope; + ctx.scope = &funcScope; + if expr.refBlock != null as *Block { + f.body = Lcx_LowerBlock(ctx, expr.refBlock, -1); + } else { + f.body = null as *HirNode; + } + ctx.scope = prevScope; + + // Add to module functions + ctx.funcs[ctx.funcCount] = *f; + ctx.funcCount = ctx.funcCount + 1; + + return f; +} + // --------------------------------------------------------------------------- // Module lowering — main entry point // --------------------------------------------------------------------------- diff --git a/src/parser.bux b/src/parser.bux index 40e204c..439e9a4 100644 --- a/src/parser.bux +++ b/src/parser.bux @@ -384,10 +384,89 @@ func parserParsePrimary(p: *Parser) -> *Expr { return e; } + // Closure: |params| -> Ret { body } + if kind == tkPipe { + return parserParseClosure(p); + } + parserEmitDiag(p, line, col, "expected expression"); return parserMakeExpr(ekLiteral, line, col); } +// --------------------------------------------------------------------------- +// Closure: |params| -> Ret { body } +// --------------------------------------------------------------------------- + +func parserParseClosure(p: *Parser) -> *Expr { + let line: uint32 = parserCurToken(p).line; + let col: uint32 = parserCurToken(p).column; + discard parserExpect(p, tkPipe, "expected '|' to start closure params"); + + let e: *Expr = parserMakeExpr(ekClosure, line, col); + let params: *Decl = bux_alloc(sizeof(Decl)) as *Decl; + params.kind = dkFunc; + params.paramCount = 0; + + // Parse params: name: Type + while !parserCheck(p, tkPipe) && parserPeek(p, 0) != tkEndOfFile { + while parserCheck(p, tkNewLine) || parserCheck(p, tkSemicolon) { + discard parserAdvance(p); + } + if parserCheck(p, tkPipe) || parserPeek(p, 0) == tkEndOfFile { + break; + } + if params.paramCount >= 9 { break; } + let nameTok: LexToken = parserExpectIdentOrKeyword(p, "expected parameter name in closure"); + discard parserExpect(p, tkColon, "expected ':' in closure parameter"); + let ptype: *TypeExpr = parserParseType(p); + + let idx: int = params.paramCount; + if idx == 0 { + params.param0.name = nameTok.text; + params.param0.refParamType = ptype; + } else if idx == 1 { + params.param1.name = nameTok.text; + params.param1.refParamType = ptype; + } else if idx == 2 { + params.param2.name = nameTok.text; + params.param2.refParamType = ptype; + } else if idx == 3 { + params.param3.name = nameTok.text; + params.param3.refParamType = ptype; + } else if idx == 4 { + params.param4.name = nameTok.text; + params.param4.refParamType = ptype; + } else if idx == 5 { + params.param5.name = nameTok.text; + params.param5.refParamType = ptype; + } else if idx == 6 { + params.param6.name = nameTok.text; + params.param6.refParamType = ptype; + } else if idx == 7 { + params.param7.name = nameTok.text; + params.param7.refParamType = ptype; + } else if idx == 8 { + params.param8.name = nameTok.text; + params.param8.refParamType = ptype; + } + params.paramCount = params.paramCount + 1; + if parserMatch(p, tkComma) { continue; } + break; + } + + discard parserExpect(p, tkPipe, "expected '|' to close closure params"); + e.closureParams = params; + + // Optional return type: -> Type + if parserMatch(p, tkArrow) { + e.refType = parserParseType(p); + } + + // Body: { ... } + e.refBlock = parserParseBlock(p); + return e; +} + // --------------------------------------------------------------------------- // Postfix: call, index, field access, as, is, ? // --------------------------------------------------------------------------- diff --git a/src/sema.bux b/src/sema.bux index 40ba0c6..6b6c312 100644 --- a/src/sema.bux +++ b/src/sema.bux @@ -564,6 +564,105 @@ func Sema_CheckExpr(sema: *Sema, expr: *Expr) -> int { return tyVoid; } + // Closure: |params| -> Ret { body } + if kind == ekClosure { + let savedRetType: int = sema.currentRetType; + let savedScope: *Scope = sema.scope; + let childScope: Scope = Scope_NewChild(sema.scope); + sema.scope = &childScope; + + // Set return type from annotation, or unknown for inference + var closureRetType: int = tyUnknown; + if expr.refType != null as *TypeExpr { + closureRetType = Sema_ResolveType(sema, expr.refType); + } + sema.currentRetType = closureRetType; + + // Register params in child scope + let params: *Decl = expr.closureParams; + if params != null as *Decl { + var i: int = 0; + while i < params.paramCount { + var p: Param; + if i == 0 { p = params.param0; } + else if i == 1 { p = params.param1; } + else if i == 2 { p = params.param2; } + else if i == 3 { p = params.param3; } + else if i == 4 { p = params.param4; } + else if i == 5 { p = params.param5; } + else if i == 6 { p = params.param6; } + else if i == 7 { p = params.param7; } + else if i == 8 { p = params.param8; } + + if !String_Eq(p.name, "") { + var sym: Symbol; + sym.kind = skVar; + sym.name = p.name; + sym.typeKind = tyUnknown; + if p.refParamType != null as *TypeExpr { + sym.typeKind = Sema_ResolveType(sema, p.refParamType); + sym.refType = p.refParamType; + } + sym.typeName = ""; + sym.isMutable = false; + sym.isPublic = false; + sym.decl = null as *Decl; + discard Scope_Define(sema.scope, sym); + } + i = i + 1; + } + } + + // Check body + if expr.refBlock != null as *Block { + Sema_CheckBlock(sema, expr.refBlock); + } + + // Restore + sema.scope = savedScope; + sema.currentRetType = savedRetType; + + // Build function type expression for later use + let funcType: *TypeExpr = bux_alloc(sizeof(TypeExpr)) as *TypeExpr; + funcType.kind = tekFunc; + funcType.funcRet = expr.refType; + funcType.funcParamCount = params.paramCount; + // Build param type list + if params.paramCount > 0 { + var head: *TypeExprList = null as *TypeExprList; + var tail: *TypeExprList = null as *TypeExprList; + var i: int = 0; + while i < params.paramCount { + var p: Param; + if i == 0 { p = params.param0; } + else if i == 1 { p = params.param1; } + else if i == 2 { p = params.param2; } + else if i == 3 { p = params.param3; } + else if i == 4 { p = params.param4; } + else if i == 5 { p = params.param5; } + else if i == 6 { p = params.param6; } + else if i == 7 { p = params.param7; } + else if i == 8 { p = params.param8; } + + let node: *TypeExprList = bux_alloc(sizeof(TypeExprList)) as *TypeExprList; + node.te = p.refParamType; + node.next = null as *TypeExprList; + if head == null as *TypeExprList { + head = node; + tail = node; + } else { + tail.next = node; + tail = node; + } + i = i + 1; + } + funcType.funcParams = head; + } + expr.refType = funcType; + + return tyFunc; + } + return tyUnknown; }