From a33ae225732c1205a2ce06978fd1862bdcd0fd43 Mon Sep 17 00:00:00 2001 From: IgorCielniak Date: Thu, 11 Dec 2025 20:07:10 +0100 Subject: [PATCH] commit --- __pycache__/main.cpython-314.pyc | Bin 0 -> 146648 bytes a.out | Bin 0 -> 6928 bytes a.sl | 5 + build/call_syntax_parens.asm | 289 ++++++++++++++++++++ build/call_syntax_parens.o | Bin 0 -> 3056 bytes build/loops_and_cmp.asm | 298 +++++++++++++++++++++ build/loops_and_cmp.o | Bin 0 -> 3120 bytes build/main.asm | 289 ++++++++++++++++++++ build/main.o | Bin 0 -> 3040 bytes build/override_dup_compile_time.asm | 262 ++++++++++++++++++ build/override_dup_compile_time.o | Bin 0 -> 2912 bytes build/test.asm | 52 ++-- build/test.o | Bin 6240 -> 6352 bytes fn.sl | 155 +++++++++++ main | Bin 0 -> 6832 bytes main.bin | Bin 0 -> 6928 bytes main.py | 398 ++++++++++++++++++++++++---- main.sl | 6 +- stdlib.sl | 221 ++------------- test.bin | Bin 10112 -> 10224 bytes test.sl | 5 +- tests/run_tests.py | 153 +++++++++++ 22 files changed, 1855 insertions(+), 278 deletions(-) create mode 100644 __pycache__/main.cpython-314.pyc create mode 100755 a.out create mode 100644 a.sl create mode 100644 build/call_syntax_parens.asm create mode 100644 build/call_syntax_parens.o create mode 100644 build/loops_and_cmp.asm create mode 100644 build/loops_and_cmp.o create mode 100644 build/main.asm create mode 100644 build/main.o create mode 100644 build/override_dup_compile_time.asm create mode 100644 build/override_dup_compile_time.o create mode 100644 fn.sl create mode 100755 main create mode 100755 main.bin create mode 100644 tests/run_tests.py diff --git a/__pycache__/main.cpython-314.pyc b/__pycache__/main.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8c2e145e55052fc0f3849d4388b87c850fb32098 GIT binary patch literal 146648 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 z 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%0Df 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 E0uM;x(*OVf literal 0 HcmV?d00001 diff --git a/a.out b/a.out new file mode 100755 index 0000000000000000000000000000000000000000..9a54c1f663bd689403674acbda0efa18814cd1ca GIT binary patch literal 6928 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 QYCtYM@ZG?If zKFlVb?o!5CL5V1i$8TOG7OgryLvgy`YqK31Ztp=kPf-7lBvc)%JK=+4#b<{q#Jy1R zv18MZ-O6y5b;zpXey~v&DuCW6HYs6J9X%M!`yZdX4;>TXjq$kwRL#>coq5UjtOQA` zo=91>3rRD)I6n-uw<{uz5$Q8)%i`5;bLq;+JoaGD?Z)UY@ip>Q54_P(-DI{F7d8x< zt@FS1o0-dI__MY2=j=BC7LRWSYK9H-*p=#atM-=&x}|vl@IvCw9w=nyb25E+$qH|d zhl{;k4KB(J->8>(+uLijy}w4=M{BfwxJKItQJYy8G!wP8 z7LF_2A=huBzFE$Bf>vgtzKzyEm%FGwoSMfY3gHQ`CJPEpM{q;BW}JH1DL5?`20TMSLIQgT!sdA0=L9e3bYs6~8_wjboY zfQodfY!_zSz&@Ott`@|Rvgg>9YTyfe*||Ik$35t3I9)DRRI~3MEGaeRmR$dkO*Wau zsa8}v#{<7a<--L}DgQ{0m_3wN$R2XE$Z0X9#cmQ=$IiBGnMh|RwSnefXD4H=g-Guj z)HyRs6pMN&#cHXoaZTDOY0#dXrR-ibr;0oIqB44s#I;s%s$3QhEI61{VF$1po+7G{ tq~f`yz|L2vL|fT~l7suFj~|{A)GKu3-+_(rL|h&aq3+d)zrybPe*iSS7##or literal 0 HcmV?d00001 diff --git a/build/override_dup_compile_time.asm b/build/override_dup_compile_time.asm new file mode 100644 index 0000000..1fc2fbb --- /dev/null +++ b/build/override_dup_compile_time.asm @@ -0,0 +1,262 @@ +section .text +%define DSTK_BYTES 65536 +%define RSTK_BYTES 65536 +%define PRINT_BUF_BYTES 128 +global _start +_start: + ; initialize data/return stack pointers + lea r12, [rel dstack_top] + mov r15, r12 + lea r13, [rel rstack_top] + call word_main + mov rax, 0 + cmp r12, r15 + je .no_exit_value + mov rax, [r12] + add r12, 8 +.no_exit_value: + mov rdi, rax + mov rax, 60 + syscall +word_puts: + mov rax, [r12] + add r12, 8 + mov rbx, rax + mov r8, 0 + cmp rbx, 0 + jge puts_abs + neg rbx + mov r8, 1 +puts_abs: + lea rsi, [rel print_buf_end] + mov rcx, 0 + mov r10, 10 + cmp rbx, 0 + jne puts_digits + dec rsi + mov byte [rsi], '0' + inc rcx + jmp puts_sign +puts_digits: +puts_loop: + xor rdx, rdx + mov rax, rbx + div r10 + add dl, '0' + dec rsi + mov [rsi], dl + inc rcx + mov rbx, rax + test rbx, rbx + jne puts_loop +puts_sign: + cmp r8, 0 + je puts_finish_digits + dec rsi + mov byte [rsi], '-' + inc rcx +puts_finish_digits: + mov byte [rsi + rcx], 10 + inc rcx + mov rax, 1 + mov rdi, 1 + mov rdx, rcx + mov r9, rsi + mov rsi, r9 + syscall + ret +word_dup: + mov rax, [r12] + sub r12, 8 + mov [r12], rax + ret +word_drop: + add r12, 8 + ret +word_over: + mov rax, [r12 + 8] + sub r12, 8 + mov [r12], rax + ret +word_swap: + mov rax, [r12] + mov rbx, [r12 + 8] + mov [r12], rbx + mov [r12 + 8], rax + ret +word__2b: + mov rax, [r12] + add r12, 8 + add qword [r12], rax + ret +word__2d: + mov rax, [r12] + add r12, 8 + sub qword [r12], rax + ret +word__2a: + mov rax, [r12] + add r12, 8 + imul qword [r12] + mov [r12], rax + ret +word__2f: + mov rbx, [r12] + add r12, 8 + mov rax, [r12] + cqo + idiv rbx + mov [r12], rax + ret +word__25: + mov rbx, [r12] + add r12, 8 + mov rax, [r12] + cqo + idiv rbx + mov [r12], rdx + ret +word__3d_3d: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + cmp rbx, rax + mov rbx, 0 + sete bl + mov [r12], rbx + ret +word__21_3d: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + cmp rbx, rax + mov rbx, 0 + setne bl + mov [r12], rbx + ret +word__3c: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + cmp rbx, rax + mov rbx, 0 + setl bl + mov [r12], rbx + ret +word__3e: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + cmp rbx, rax + mov rbx, 0 + setg bl + mov [r12], rbx + ret +word__3c_3d: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + cmp rbx, rax + mov rbx, 0 + setle bl + mov [r12], rbx + ret +word__3e_3d: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + cmp rbx, rax + mov rbx, 0 + setge bl + mov [r12], rbx + ret +word__40: + mov rax, [r12] + mov rax, [rax] + mov [r12], rax + ret +word__21: + mov rax, [r12] + add r12, 8 + mov rbx, [r12] + mov [rax], rbx + add r12, 8 + ret +word_mmap: + mov r9, [r12] + add r12, 8 + mov r8, [r12] + add r12, 8 + mov r10, [r12] + add r12, 8 + mov rdx, [r12] + add r12, 8 + mov rsi, [r12] + add r12, 8 + mov rdi, [r12] + mov rax, 9 + syscall + mov [r12], rax + ret +word_munmap: + mov rsi, [r12] + add r12, 8 + mov rdi, [r12] + mov rax, 11 + syscall + mov [r12], rax + ret +word_exit: + mov rdi, [r12] + add r12, 8 + mov rax, 60 + syscall + ret +word__3er: + mov rax, [r12] + add r12, 8 + sub r13, 8 + mov [r13], rax + ret +word_r_3e: + mov rax, [r13] + add r13, 8 + sub r12, 8 + mov [r12], rax + ret +word_rdrop: + add r13, 8 + ret +word_pick: + mov rcx, [r12] + add r12, 8 + mov rax, [r12 + rcx * 8] + sub r12, 8 + mov [r12], rax + ret +word_rpick: + mov rcx, [r12] + add r12, 8 + mov rax, [r13 + rcx * 8] + sub r12, 8 + mov [r12], rax + ret +word_main: + ; push 6 + sub r12, 8 + mov qword [r12], 6 + call word_puts + ; push 0 + sub r12, 8 + mov qword [r12], 0 + ret +section .bss +align 16 +dstack: resb DSTK_BYTES +dstack_top: +align 16 +rstack: resb RSTK_BYTES +rstack_top: +align 16 +print_buf: resb PRINT_BUF_BYTES +print_buf_end: \ No newline at end of file diff --git a/build/override_dup_compile_time.o b/build/override_dup_compile_time.o new file mode 100644 index 0000000000000000000000000000000000000000..14b8148689c784e590e1372a5b9e36115629b7bb GIT binary patch literal 2912 zcmbW3U1%It6vywjn|6~{6QPtsQ}JOfO51HW+dc$BNuaSam>`Hg2*T~`&ZHgPFXp3- zpfs?E9hQKFD)?C+#1}#EsnH@@w;#0B2=zq>q7Om~Dq4L=At~E??mc&Aa!i6auyg+R zcRud9cka&a>G6rDHe@nPUm13oB_mB48%-qYQMQ?FOws=*VJ`S=qUjqH_!+Hs4`_jQ zv#>ewp`HAaRRJ?>WdWMMb(LJm)cHkKCce|X*)BXQXHcP!^`ON*V7NzopjWtdD8n8V zmi7*=(OwmBolHoEY_|x*DO3X92ev3-;22^cm(D*i`#3~QMz<$s_kp_esaVdOWW&or z&TMD%X6tg!h_20z2-@G1kj|KlGg?dRwO(U!VQda&FlG-y_7`v)`?62mAt2mhbmy;b zI$(4!{WfS6ZyC{#=HlNo-wH5)X1kz9)G=OP2!AzO_gK&?gZBx1HT!U%FtYO3vV3&I zjP6cE^Zh*?)ynTVGs@;W(4`G&P7;!|G5s#o6M5-y|s?-{dIij*YSP0j_-rSXS5lv zL_Jy)`igeQ{hMrWm1~~FotbQJ!#&XUAdW}!b8tk(c~E?C{9`a?XF zaVEexGh0_u&rA#iRc(l^znOZP8XoyhR3{B&7X^3yxxz-R-Uxx^dMvD16WV{ifI}3uM?6q|g}h zA;OZwg!I*`B@SlApeN)H;{it_?yUY5dJRm?+O1H`A>v@hWszWFCf29 z_&3P=;6bFW>r!XfPQq^?FA~0h{3zkKkyi-+2l;8jmyy3mc#j-s*agBLLH+~byO94$ z_-^D&gzrb*Pmb0w@}q>0Bj{(SNoD-X9=%k{M&?2BZps?^wmOsiSSv; z2^5LvVlG8o>iin~lbElp_4yk7B69k_0e&S1R{LI;L$_-0Z=7^I&$S(HhfQ8-)S7P9 z;elIo3YK4E+z%`-KvSqUxHIhr{G?S49d@eW*}NGBK7$*#$|!92xT|5c(P%2S?;fu! zHRaY_{{+V{Sw+mP%GsWXmk9b&%0Fcx<3nYI_>irIrNxvMhftJkUW&tti7r)i0Iq?T zDk-m%=z|Az&7u;unr@{Q)?*EO5|_lLJzm1NthiIR^+D4A#o)AnUdA{4Ll}^7c~IE%guhDO@ecr}Nasrc literal 0 HcmV?d00001 diff --git a/build/test.asm b/build/test.asm index bc3c542..81c4db2 100644 --- a/build/test.asm +++ b/build/test.asm @@ -73,6 +73,11 @@ word_dup: word_drop: add r12, 8 ret +word_over: + mov rax, [r12 + 8] + sub r12, 8 + mov [r12], rax + ret word_swap: mov rax, [r12] mov rbx, [r12 + 8] @@ -222,6 +227,13 @@ word_r_3e: word_rdrop: add r13, 8 ret +word_pick: + mov rcx, [r12] + add r12, 8 + mov rax, [r12 + rcx * 8] + sub r12, 8 + mov [r12], rax + ret word_rpick: mov rcx, [r12] add r12, 8 @@ -461,18 +473,18 @@ word_test_2dif: mov rax, [r12] add r12, 8 test rax, rax - jz L_if_false_0 + jz L_if_false_24 ; push 111 sub r12, 8 mov qword [r12], 111 call word_puts - jmp L_if_end_1 -L_if_false_0: + jmp L_if_end_25 +L_if_false_24: ; push 222 sub r12, 8 mov qword [r12], 222 call word_puts -L_if_end_1: +L_if_end_25: ret word_test_2delse_2dif: ; push 2 @@ -486,13 +498,13 @@ word_test_2delse_2dif: mov rax, [r12] add r12, 8 test rax, rax - jz L_if_false_2 + jz L_if_false_26 ; push 50 sub r12, 8 mov qword [r12], 50 call word_puts - jmp L_if_end_3 -L_if_false_2: + jmp L_if_end_27 +L_if_false_26: call word_dup ; push 2 sub r12, 8 @@ -501,19 +513,19 @@ L_if_false_2: mov rax, [r12] add r12, 8 test rax, rax - jz L_if_false_4 + jz L_if_false_28 ; push 60 sub r12, 8 mov qword [r12], 60 call word_puts - jmp L_if_end_5 -L_if_false_4: + jmp L_if_end_29 +L_if_false_28: ; push 70 sub r12, 8 mov qword [r12], 70 call word_puts -L_if_end_5: -L_if_end_3: +L_if_end_29: +L_if_end_27: call word_drop ret word_test_2dfor: @@ -526,10 +538,10 @@ word_test_2dfor: mov rax, [r12] add r12, 8 cmp rax, 0 - jle L_for_end_7 + jle L_for_end_31 sub r13, 8 mov [r13], rax -L_for_loop_6: +L_for_loop_30: ; push 1 sub r12, 8 mov qword [r12], 1 @@ -537,9 +549,9 @@ L_for_loop_6: mov rax, [r13] dec rax mov [r13], rax - jg L_for_loop_6 + jg L_for_loop_30 add r13, 8 -L_for_end_7: +L_for_end_31: call word_puts ret word_test_2dfor_2dzero: @@ -552,17 +564,17 @@ word_test_2dfor_2dzero: mov rax, [r12] add r12, 8 cmp rax, 0 - jle L_for_end_9 + jle L_for_end_33 sub r13, 8 mov [r13], rax -L_for_loop_8: +L_for_loop_32: call word_drop mov rax, [r13] dec rax mov [r13], rax - jg L_for_loop_8 + jg L_for_loop_32 add r13, 8 -L_for_end_9: +L_for_end_33: call word_puts ret word_test_2dstruct: diff --git a/build/test.o b/build/test.o index 92599760b610482f185dcfcdab07c44c678508cd..03b5da4bd1e078463d7837c5793d2d3f969ea25a 100644 GIT binary patch literal 6352 zcmbW5e{9re9mk)8vK82%1nd^AOkJH$M``b1QbUwVfje!H8RtS^`Fgz{>|F1zxgRZ^ zj$$q(yHqkahY4{Tn|~}Z7)|_E3@6s&-?j)p6~a$ z&)UZLP44-Ap4apKe4fwq^LicI(zkU%D5T62QlC>n%}}K_1t!~@)U|4%MgKn&ec^MB zbYI|s$;FKgEsa&mL8)f?$V`5>)D!Mk9Ekm-|8=wYLTdcG*w4J=+_X|dV-KFP1%c_g ziUjFdM4zwH2f5<8(R-=Y^p?4!sb!TX*xls4O)2}og&KrJONY3Fud^dG@Rj}d9sD>H z{nbl-2iL%I@1Pk!rdjt)sW~>@)E=vxXpUA-9otBB`J#Z-dbBQDnO65Nj!vEHIffph z2iL>nd1g4i8H7}Ldn`70c_8PJz(%aq~s~Cjj$9EH{ zwzbEq)Wj|)nqwij@Ze&%+Lfu>NUH%kQBbI&hoe)|bJ0~+r17@)Xm{;d41W0|HS`s+K$ZA_V_$)j|FYfam6F?mNtgGsyFKS>mR>PkGY>`rhj}D&w+Wbp_$AH2D;IF6prH)=iaonS61muXS9C<0qltnz}jW>g{Sr9o5wlC_!!_WqW9c#We6 zakPTEZaCG{o*+njf>cz2#Mn3i?!Vuy*Zo%934R65euZpRbFb9vb+<+y?!xFGL^1Ng^7U<-IWyI0Bm`80XqBwxi!$&;k*^WUW8Sn z!tdT5Fm52X3A&ycOM;VW;^78zuTZPI+Lqu_HWD4>EF{y@RutT64U&_PoV6u)O386Z zerZeWTyXKZYaw~qmS9u4OM$yl4BKtEfK3t@xw{Y&48`3Jo%<(@ev(Y1`X*H83Ak@V zbrHqQc1UXXLn_)J#t}1R?2lPCt&krazV6Qua+lR*H)ZC216@|ov0@#tX5blvSwX=Jt4cv&D?g;1HUP)G-Jpk@~!0i+6sd% z0JYlTFu(4DM2tl5%I3LZh9UpsI}~%9yVLU0Va@>X?b475zFpiS@bWX+3iKU9?oD<& zMEy0;8{LGXua146it*Lbkn1Ad>lPKQzldvR>~FYp+%5|3&l$Fa@zFilQCFL&?jiEQ zrDY;r(UC4)#Wowl;_QKd?;Sn6!x+FRH1%0cdX{&g=~Xl#6}{ozM=6y1JlMmq?t=9I z!keIa$kxT?j=?uYlGzma;&<*#qCOS$Vjl&)$<{(~-cs0^ z>MQ1YD4v~9aV->wEyeqKxkyR6BbLJMMd$GsDDJcr_C2N*6Hr7f#rtAB4aEveVb6z- zaR`bx-nJITPE}u*ZM5s$mmqNyFu#rB2#N_5=TN+Z;sz{o2Z}pTjG$1s1wM_UjG~TW z2TuCGP<$6fMO->HTC1s1qj*oTR4By<$QsRhZ>|?ogKL+XhDX{*3|LU7y?h;v%-1I%9*{6XgD1wYLEAAJt1U>$eGhhIvA8TvHhDnBbR~|3GkWJR$Y8;4`d$Met@_52-fmEzIv0d;{~W;9q2Zzu-HW zzbtr$`CEdIGXDVXZR@Er-yry5=DP&{5%Yb5Kg0Yv@m) zS;M?d@NVWG7W~W19l^cz3#mhb-^==^1Rt4$SD61;=)cGO z&w@Y6d`9q}FkgT#TI+d%d8gp7FyAWpUzzs_{!ivd1z)JwFQj_K|D{@)?-BeXn(M{1 zvQ+95KGz4zZwt@CyM5jmt9K5*o%wA4|7w}z`&uV4P|ge{*OpSnQb)X)RZg)KFO>Mz zk;^-&y_u3T93L#F)JVRNbcV{MqC(-s2iQnv_GAnk%;$%^=3-_~&a={)T&8%B!!jKu zSswDjB@3k2C_kJkcvf*F&ddo9c*qGSjS)9S+8FEEh$NkeFY5~INWuts3{E6rwWb1l zU8jlJ5nfo}dA$#Yf`E27zrwW0mz{nU|H@v*p1CeKIrLP|xNY;{_AU0sQU6^C0d2g+&XTMi|xuNQr-l7(9g_6+}`xL&u~+`AXOzuN=;E&fe$ Lg0I0Kj;H@Gk?4tb literal 6240 zcmbW5e~esJ702(iTiCYLR$5ENwb6ELMCrOe+RBm!S=}u&xGO)Tl7R6!J3nA#XJ&ab zL%V=&byBnAX8qBWq!29fj}c11Xuwq<)LqIpM3RHZ{ZTr5foYd-AKLzHpD?L>wpMFPSA4QWM|GaporHW;HqLTkw_t;`WcC zgLEyT&&~8fUvaJRZ)z>wW#8dCywVq3)8OAtDcj#b4k7WS+`zXv5E{hF_IpM?14VE3 z&7P5su)JoP@lnk>W=f6m;f83u@?vAGIx)JH=<-D&8Sm7(SmlEH?&8?giOx~95F6Qy z=-;8}{QVLtAt*16)h1uMs3lhW!&^rdonS8N2&O<32CiH2yBAT2nl zr~-+xvAw+|AJl4r7Y}?;fjg*>t;W4~YPFho{Cz5pA-qv@dY??wIW*|4Bx(Tmv!`XZQdk{*o8r9Nk?b{C$C1$p>;dC=@jeb+YQ~b_ zu`_ZUFWy2(I&BF~TqDt0PLtPLYfEmCk`s_zxL`H3T}mb)Ic`hrzN|uDZieJ(TY~-L zH3NSQ#e=pFr>p5FM((YFq#ecG(0PBw(9V!)RQEx3mVmbuu4zcO*(u2%q*Sy)Odw{; z*y}8tmMln)SZ@L;cUet#P|v*GXz~DDNm$>9H92jCQd8cGpf}pR!9(S>BH}I-4^xD< z5*^OqQG-qbb=c`JzuvEj7>VAMpPVaa z8|3HCQ_kzW`>iOQ=1l-TNa}imSBE!-sGuj?fSw}cecA2~(O?bqMmOo`y|C+ZaXv-r z`g*x;wOhq%M{&aJejUe)cMGKs<_ufH#ON06tZPkHZo_Q87L92H}*o`!WRtV3i49o0j&4mNK;Vuwg#nza>p zt9vdKnfVm`P;^-e#F>0L$MsOGvlRBDpf_0;6v|TA{m@6uIw(#~&E8Y3XoBK7OYxyr zrl|q%AxmMmqWke46gw@2eU53xF(|%aDL$0rk5Fu~6!v`R9FIeBDJg=5vAe2|%O=`& z-n;+7)=0wq9*Rd$Jdff8it{Kg!y-4MxCzA$6bh%n=TVeV)byc%C;dGXKS5Cuht4?m zty0aU^v;rM?kg5mbMek%$t@-N$QsP~cdonX{)DgeqyA@!hrU6bsm&%_IO{J0d~d}= zUw>Vus}c3tdaSNwj^o3=w&+Ske>b-ImzbArNcLCf;CK(=8}3i_JuDn;M%2$kq%D{E z^Md1`K%SQc-^=_J!GFyBb-@oXe^>BFna>FRIP+y#2k)NY@DVhdf)yhHE}%x@CBg?U2otC_ok zcQLOBegpF%!Ea-JQ1I_Ce^T%=^JfL$!~9jj_c1>u_%E6NTkylo<&VZ=%va!?vaUCn ze@gIa<{JgSRJRvVR|_6xe!bvZnBOLNH}fY1-_HCM!PCso3SMUZF&xp>wU_x-ft!3Fc#h`|C&hS@3_d{yw}utZRn(A;FjGaYmFU_!{Qt1z*p6TJR3$ zTk#sSu5HX+!N1PDBKWuF;92HXp&w-aTfu+8{8hpCG5?$3k1=1y|C5*@Ji~mw;A70M z5d1I9hXnrz^G}QakZ*p9pw;t_^eW7Ur}4U$uhX zM{{0%|3qvewXUPZ|OD+-Xl*-gIbhYB4!meIu7MUCEX6fsL66Qo6q7I_;d@4M*Y*WRrJs zrgV-5QS2{pWN+tn@vr!lm?Bj{`y2OXJ_l$$R`IK8iqdzY#|z}tQX6?nC#D$`|I?n>^*h$T%kT+IIWx@ z*g9Mf_l*Iig3Hrr+6>mvfJD+Q)HR#Us2(SqaWaYiV%ll(E9rd7;lXlX8s4^4y}Hc~ z-4upy4ozNY&XOs(nGaIr!A${7_{4)vA}6@3iS9j6rR8U6*Z ZjBH#X_s<~zdv?h0=idO2@-Zj%{{>~(bol@P diff --git a/fn.sl b/fn.sl new file mode 100644 index 0000000..45b0c4b --- /dev/null +++ b/fn.sl @@ -0,0 +1,155 @@ +: call-syntax-rewrite # ( fnameToken -- handled ) + dup token-lexeme identifier? 0 == if drop 0 exit then + peek-token dup nil? if drop drop 0 exit then + dup token-lexeme "(" string= 0 == if drop drop 0 exit then + swap >r # stash fnameTok + drop # discard peeked '(' + next-token drop # consume '(' + list-new # out + list-new # out cur +begin + next-token dup nil? if "unterminated call expression" parse-error then + dup token-lexeme ")" string= if + drop + # flush current arg + list-extend # out' + r> list-append # out'' + inject-tokens + 1 exit + then + dup token-lexeme "," string= if + drop + list-extend # out' + list-new # out' cur + continue + then + # default: append tok to cur + list-append +again +; +immediate +compile-only + +: extend-syntax + "call-syntax-rewrite" set-token-hook +; +immediate +compile-only + + +: fn-op-prec + dup "+" string= if drop 1 exit then + dup "-" string= if drop 1 exit then + dup "*" string= if drop 2 exit then + dup "/" string= if drop 2 exit then + dup "%" string= if drop 2 exit then + drop 0 +; +compile-only + +: fn-operator? + fn-op-prec 0 > +; +compile-only + +: fn-check-dup + >r # params (r: name) + 0 # params idx +begin + over list-length swap >= if # params flag + r> exit + then + dup >r # params idx (r: idx name) + over swap list-get # params elem + 1 rpick string= if "duplicate parameter names in fn definition" parse-error then + drop # drop comparison flag when no error + r> 1 + # params idx+1 +again +; +compile-only + +: fn-params + list-new # lexer params + swap # params lexer + >r # params (r: lexer) +begin + 0 rpick lexer-pop token-lexeme # params lex + swap drop # params lex (drop returned lexer) + dup ")" string= if drop r> exit then + dup "int" string= 0 == if "only 'int' parameters are supported in fn definitions" parse-error then + drop # params + 0 rpick lexer-pop token-lexeme # params lexer pname + swap drop # params pname + dup identifier? 0 == if "invalid parameter name in fn definition" parse-error then + fn-check-dup # params pname + list-append # params + 0 rpick lexer-pop token-lexeme # params lexer sep + swap drop # params sep + dup "," string= if drop continue then + dup ")" string= if drop r> exit then + "expected ',' or ')' in parameter list" parse-error +again +; +compile-only + +: fn-collect-body + "{" lexer-expect drop # consume opening brace, keep lexer + lexer-collect-brace # lexer bodyTokens + swap drop # bodyTokens +; +compile-only + +: fn-lexemes-from-tokens + list-new >r # tokens (r: acc) + 0 # tokens idx +begin + over list-length over swap >= if # stop when idx >= len + drop drop # drop idx and tokens (flag consumed by if) + r> exit # return acc + then + over over list-get token-lexeme # tokens idx lex + r> swap list-append >r # tokens idx + 1 + # tokens idx+1 +again +; +compile-only + +: fn-validate-body + dup list-length 0 == if "empty function body" parse-error then + dup >r 0 r> swap list-get "return" string= 0 == if "function body must start with 'return'" parse-error then + dup list-last ";" string= 0 == if "function body must terminate with ';'" parse-error then + list-clone # work on a copy + list-pop drop # drop trailing ';' + list-pop-front drop # drop leading 'return' + dup list-length 0 == if "missing return expression" parse-error then +; +compile-only + +: fn-build-body + fn-translate-postfix # words +; +compile-only + +: fn + "(),{};+-*/%," lexer-new # lexer + dup lexer-pop # lexer nameTok + dup >r # save nameTok + token-lexeme # lexer name + dup identifier? 0 == if "invalid function name for 'fn'" parse-error then + >r # save name string + drop # leave lexer only for params + "(" lexer-expect drop # consume '(' keep lexer + fn-params # params lexer + fn-collect-body # params bodyTokens + swap >r # bodyTokens (r: params) + fn-lexemes-from-tokens # lexemes + fn-validate-body # expr + shunt # postfix + r> # postfix params + fn-build-body # body + r> drop # drop name string + r> # name token + swap emit-definition +; +immediate +compile-only diff --git a/main b/main new file mode 100755 index 0000000000000000000000000000000000000000..922fbf70060df67ee400c326bfc29e8faed51ce0 GIT binary patch literal 6832 zcmeI1UuauZ9LIm@l9Fy}vOnm=f)O*fq3haagB7Zz(#pNi$W#Oc^>WjjtYOU`Z*E+D za4Q6%*U*Q-d{EpJeGox>8&pMPi``(b3O-{HpteoxtX~=& z)jAih4{KxJY3>ht`}d`b2-cVHMXI???agboU-bGP;0$r`0mPTW_iRPOGk?v~yH|Ag z=B&FOi!{Zp{1(;SaI(qnQjo?=Nkqd*-JMPn!k8XnB3h2V-`{CJhFT7A5R;-xdq->E z>PL6^GBt*i+C=k9w%pj6CY!fAi$C`ECb9Q6iM_u`?A#`?v)!2109uK7m^!;JcQ>zC zrg0ZvbA~FDY3!mJNPCF-lgZT~w#W$O^6_ncKFRg8Te`KV-@u150|;h7udhY*y7%DL z*i#a>p*NQd#OtRJ?33@eNU(?75R5DQAn|7v zeuQ{U;nRKi)5K3J`WJ|Qpz!0wzf!nE{EEU)68~1=%fx?H_-W!B3V)aQZH0eC{O;{( z?#bp;;twnQJn;#IUm*Ux!mkp4RpH+gUs3pv#6MB^b>eFZZxjDX;o{|L2yQ5Rh;1MY zhIXX+NZITpKB4dji9e(8{lra$r-<7Mf1G$-;TrK}g+E99oWfrwzM=3u=lF?C^scMu z@hQ1fZWsITQyzb^NB>qIeunr`qF4Vh@;G}4%6|Kh^E>bJvp)PRaWy|=YlQ{tV9_j; zM$BpvjH+YW&Pb_j;S zf(~<-Vc)cDE_Uq}2H1Z9-rU5PIeMSyoM(&kT}l_WW9;9?N9cU%5#Lzms^k9w3MUi- literal 0 HcmV?d00001 diff --git a/main.bin b/main.bin new file mode 100755 index 0000000000000000000000000000000000000000..9a54c1f663bd689403674acbda0efa18814cd1ca GIT binary patch literal 6928 zcmeI1UuauZ7{I?|Nl7~~Svz#H3KcTl#CB`5!3I@QY3;qx%rRf~Fu2?_X?I!k$K+<4 zf?FjByTpR?AINm>!3SaN!I#-NWNeumPOYF13PbQgFrqRiNR>L{ch31v?#t#&#{TAr-Fe1$R`L-0Pn?;9Eld-l?S8;KoWMs8f?2D%zZew zuU?60&b7r+gwe1|>SNqbtFFKc-P-bvu|@XpTwB=jNGu5FS&FfbdKf{6%37_tbhT@n z*1Ys{zc&1p=6tI!|2}^i!P4>jkZMj{d+A2y2fg|y*xlTD5b@Q}y*(&+=8HVNb6t0C zO*u=^a9vF0mx%6!;&nDlB;)ajUX_SVzP2BU(;wFzG*QTvh#p`ev^af>x3l~h`f`9l zn3Q>GZ)wXbo#=W`rdoeo+f~2779V}R&c<~|@!H;4#rF0pws%*tom$2Aj%(9uKr0bj ztFw7I8+iRvwR`xQQ&gE$Z9UaM%I(x2jW2eyMTRMthp+YWQSMJ?$E`*6SKO2dKrjJ% z^%KvYVF#(02A2MF&u&UM{sqR!J!ZKUZH2!_yk}jKYqB{@e51n85g%6gMdFhR|BU#w!oMMYOyS=XKdJDah<~W?KZt*= zaPcZO05=uh!wwL>ZCz1#AMt2!l4q37gTyBlzJ<7<@C5OK!XGC-ukcCYCltPy_?rsf zPyCF+XNg}@__M@&``Xv_Jm>iJ9cbq-w)mJV0hcd#;IFm#lWqQ|I`H?2&kwZwUqBvX zZ^4!Gd5-ft=XtpU|A4rfAJP?TF0(afTKOTfoCBk5n%?!%8D%DOrZc7tTZ|aVc1xWk|08GQY=|{+elYtprskvd None: self.line = 1 self.column = 0 + self.custom_tokens: Set[str] = {"(", ")", "{", "}", ";", ",", "[", "]"} + self._token_order: List[str] = sorted(self.custom_tokens, key=len, reverse=True) + + def add_tokens(self, tokens: Iterable[str]) -> None: + updated = False + for tok in tokens: + if not tok: + continue + if tok not in self.custom_tokens: + self.custom_tokens.add(tok) + updated = True + if updated: + self._token_order = sorted(self.custom_tokens, key=len, reverse=True) + + def add_token_chars(self, chars: str) -> None: + self.add_tokens(chars) def tokenize(self, source: str) -> Iterable[Token]: self.line = 1 @@ -62,19 +78,78 @@ class Reader: source_len = len(source) while index < source_len: char = source[index] + if char == '"': + if lexeme: + yield Token("".join(lexeme), token_line, token_column, token_start, index) + lexeme.clear() + token_start = index + token_line = self.line + token_column = self.column + index += 1 + self.column += 1 + string_parts = ['"'] + while True: + if index >= source_len: + raise ParseError("unterminated string literal") + ch = source[index] + string_parts.append(ch) + index += 1 + if ch == "\n": + self.line += 1 + self.column = 0 + else: + self.column += 1 + if ch == "\\": + if index >= source_len: + raise ParseError("unterminated string literal") + next_ch = source[index] + string_parts.append(next_ch) + index += 1 + if next_ch == "\n": + self.line += 1 + self.column = 0 + else: + self.column += 1 + continue + if ch == '"': + yield Token("".join(string_parts), token_line, token_column, token_start, index) + break + continue if char == "#": while index < source_len and source[index] != "\n": index += 1 continue + if char == ";" and index + 1 < source_len and source[index + 1].isalpha(): + if not lexeme: + token_start = index + token_line = self.line + token_column = self.column + lexeme.append(";") + index += 1 + self.column += 1 + continue + matched_token: Optional[str] = None + for tok in self._token_order: + if source.startswith(tok, index): + matched_token = tok + break + if matched_token is not None: + if lexeme: + yield Token("".join(lexeme), token_line, token_column, token_start, index) + lexeme.clear() + token_start = index + token_line = self.line + token_column = self.column + yield Token(matched_token, self.line, self.column, index, index + len(matched_token)) + index += len(matched_token) + self.column += len(matched_token) + token_start = index + token_line = self.line + token_column = self.column + continue if char.isspace(): if lexeme: - yield Token( - "".join(lexeme), - token_line, - token_column, - token_start, - index, - ) + yield Token("".join(lexeme), token_line, token_column, token_start, index) lexeme.clear() if char == "\n": self.line += 1 @@ -82,6 +157,9 @@ class Reader: else: self.column += 1 index += 1 + token_start = index + token_line = self.line + token_column = self.column continue if not lexeme: token_start = index @@ -91,7 +169,7 @@ class Reader: self.column += 1 index += 1 if lexeme: - yield Token("".join(lexeme), token_line, token_column, token_start, index) + yield Token("".join(lexeme), token_line, token_column, token_start, source_len) # --------------------------------------------------------------------------- @@ -218,11 +296,8 @@ class MacroContext: def inject_token_objects(self, tokens: Sequence[Token]) -> None: self._parser.tokens[self._parser.pos:self._parser.pos] = list(tokens) - def enable_call_syntax(self) -> None: - self._parser.call_syntax_enabled = True - - def disable_call_syntax(self) -> None: - self._parser.call_syntax_enabled = False + def set_token_hook(self, handler: Optional[str]) -> None: + self._parser.token_hook = handler def new_label(self, prefix: str) -> str: return self._parser._new_label(prefix) @@ -247,6 +322,7 @@ class Word: macro_params: int = 0 compile_time_intrinsic: Optional[Callable[["CompileTimeVM"], None]] = None compile_only: bool = False + compile_time_override: bool = False @dataclass @@ -271,9 +347,12 @@ Context = Union[Module, Definition] class Parser: - def __init__(self, dictionary: Dictionary) -> None: + def __init__(self, dictionary: Dictionary, reader: Optional[Reader] = None) -> None: self.dictionary = dictionary + self.reader = reader or Reader() self.tokens: List[Token] = [] + self._token_iter: Optional[Iterable[Token]] = None + self._token_iter_exhausted = True self.pos = 0 self.context_stack: List[Context] = [] self.definition_stack: List[Word] = [] @@ -282,14 +361,20 @@ class Parser: self.macro_recording: Optional[MacroDefinition] = None self.control_stack: List[Dict[str, str]] = [] self.label_counter = 0 - self.call_syntax_enabled = False + self.token_hook: Optional[str] = None + self._last_token: Optional[Token] = None self.compile_time_vm = CompileTimeVM(self) + def inject_token_objects(self, tokens: Sequence[Token]) -> None: + """Insert tokens at the current parse position.""" + self.tokens[self.pos:self.pos] = list(tokens) + # Public helpers for macros ------------------------------------------------ def next_token(self) -> Token: return self._consume() def peek_token(self) -> Optional[Token]: + self._ensure_tokens(self.pos) return None if self._eof() else self.tokens[self.pos] def emit_node(self, node: ASTNode) -> None: @@ -300,7 +385,9 @@ class Parser: # Parsing ------------------------------------------------------------------ def parse(self, tokens: Iterable[Token], source: str) -> Module: - self.tokens = list(tokens) + self.tokens = [] + self._token_iter = iter(tokens) + self._token_iter_exhausted = False self.source = source self.pos = 0 self.context_stack = [Module(forms=[])] @@ -308,10 +395,14 @@ class Parser: self.last_defined = None self.control_stack = [] self.label_counter = 0 - self.call_syntax_enabled = False + self.token_hook = None + self._last_token = None while not self._eof(): token = self._consume() + self._last_token = token + if self._run_token_hook(token): + continue if self._handle_macro_recording(token): continue lexeme = token.lexeme @@ -358,11 +449,6 @@ class Parser: # Internal helpers --------------------------------------------------------- def _handle_token(self, token: Token) -> None: - if self.call_syntax_enabled: - call_target = self._maybe_call_form(token.lexeme) - if call_target is not None: - self._append_node(WordRef(name=call_target)) - return if self._try_literal(token): return @@ -444,6 +530,12 @@ class Parser: self.dictionary.register(word) def _push_control(self, entry: Dict[str, str]) -> None: + if "line" not in entry or "column" not in entry: + tok = self._last_token + if tok is not None: + entry = dict(entry) + entry["line"] = tok.line + entry["column"] = tok.column self.control_stack.append(entry) def _pop_control(self, expected: Tuple[str, ...]) -> Dict[str, str]: @@ -451,7 +543,14 @@ class Parser: raise ParseError("control stack underflow") entry = self.control_stack.pop() if entry.get("type") not in expected: - raise ParseError(f"mismatched control word '{entry.get('type')}'") + tok = self._last_token + location = "" + if tok is not None: + location = f" at {tok.line}:{tok.column} near '{tok.lexeme}'" + origin = "" + if "line" in entry and "column" in entry: + origin = f" (opened at {entry['line']}:{entry['column']})" + raise ParseError(f"mismatched control word '{entry.get('type')}'" + origin + location) return entry def _new_label(self, prefix: str) -> str: @@ -459,13 +558,16 @@ class Parser: self.label_counter += 1 return label - def _maybe_call_form(self, lexeme: str) -> Optional[str]: - if len(lexeme) <= 2 or not lexeme.endswith("()"): - return None - name = lexeme[:-2] - if not name or not _is_identifier(name): - return None - return name + def _run_token_hook(self, token: Token) -> bool: + if not self.token_hook: + return False + hook_word = self.dictionary.lookup(self.token_hook) + if hook_word is None: + raise ParseError(f"token hook '{self.token_hook}' not defined") + self.compile_time_vm.invoke_with_args(hook_word, [token]) + # Convention: hook leaves handled flag on stack (int truthy means consumed) + handled = self.compile_time_vm.pop() + return bool(handled) def _handle_if_control(self) -> None: false_label = self._new_label("if_false") @@ -518,6 +620,9 @@ class Parser: word = self.definition_stack.pop() ctx.immediate = word.immediate ctx.compile_only = word.compile_only + if word.compile_only or word.immediate: + word.compile_time_override = True + word.compile_time_intrinsic = None module = self.context_stack[-1] if not isinstance(module, Module): raise ParseError("nested definitions are not supported yet") @@ -626,6 +731,7 @@ class Parser: return True def _consume(self) -> Token: + self._ensure_tokens(self.pos) if self._eof(): raise ParseError("unexpected EOF") token = self.tokens[self.pos] @@ -633,8 +739,23 @@ class Parser: return token def _eof(self) -> bool: + self._ensure_tokens(self.pos) return self.pos >= len(self.tokens) + def _ensure_tokens(self, upto: int) -> None: + if self._token_iter_exhausted: + return + if self._token_iter is None: + self._token_iter_exhausted = True + return + while len(self.tokens) <= upto and not self._token_iter_exhausted: + try: + next_tok = next(self._token_iter) + except StopIteration: + self._token_iter_exhausted = True + break + self.tokens.append(next_tok) + class CompileTimeVM: def __init__(self, parser: Parser) -> None: @@ -642,10 +763,12 @@ class CompileTimeVM: self.dictionary = parser.dictionary self.stack: List[Any] = [] self.return_stack: List[Any] = [] + self.loop_stack: List[Dict[str, Any]] = [] def reset(self) -> None: self.stack.clear() self.return_stack.clear() + self.loop_stack.clear() def push(self, value: Any) -> None: self.stack.append(value) @@ -688,11 +811,18 @@ class CompileTimeVM: self.reset() self._call_word(word) + def invoke_with_args(self, word: Word, args: Sequence[Any]) -> None: + self.reset() + for value in args: + self.push(value) + self._call_word(word) + def _call_word(self, word: Word) -> None: - if word.compile_time_intrinsic is not None: + definition = word.definition + prefer_definition = word.compile_time_override or (isinstance(definition, Definition) and (word.immediate or word.compile_only)) + if not prefer_definition and word.compile_time_intrinsic is not None: word.compile_time_intrinsic(self) return - definition = word.definition if definition is None: raise ParseError(f"word '{word.name}' has no compile-time definition") if isinstance(definition, AsmDefinition): @@ -708,7 +838,9 @@ class CompileTimeVM: def _execute_nodes(self, nodes: Sequence[ASTNode]) -> None: label_positions = self._label_positions(nodes) loop_pairs = self._for_pairs(nodes) - loop_stack: List[Dict[str, Any]] = [] + begin_pairs = self._begin_pairs(nodes) + self.loop_stack = [] + begin_stack: List[Dict[str, int]] = [] ip = 0 while ip < len(nodes): node = nodes[ip] @@ -717,7 +849,31 @@ class CompileTimeVM: ip += 1 continue if isinstance(node, WordRef): - self._call_word_by_name(node.name) + name = node.name + if name == "begin": + end_idx = begin_pairs.get(ip) + if end_idx is None: + raise ParseError("'begin' without matching 'again'") + begin_stack.append({"begin": ip, "end": end_idx}) + ip += 1 + continue + if name == "again": + if not begin_stack or begin_stack[-1]["end"] != ip: + raise ParseError("'again' without matching 'begin'") + ip = begin_stack[-1]["begin"] + 1 + continue + if name == "continue": + if not begin_stack: + raise ParseError("'continue' outside begin/again loop") + ip = begin_stack[-1]["begin"] + 1 + continue + if name == "exit": + if begin_stack: + frame = begin_stack.pop() + ip = frame["end"] + 1 + continue + return + self._call_word_by_name(name) ip += 1 continue if isinstance(node, BranchZero): @@ -748,18 +904,18 @@ class CompileTimeVM: raise ParseError("internal loop bookkeeping error") ip = match + 1 continue - loop_stack.append({"remaining": count, "begin": ip}) + self.loop_stack.append({"remaining": count, "begin": ip, "initial": count}) ip += 1 continue if isinstance(node, ForNext): - if not loop_stack: + if not self.loop_stack: raise ParseError("'next' without matching 'for'") - frame = loop_stack[-1] + frame = self.loop_stack[-1] frame["remaining"] -= 1 if frame["remaining"] > 0: ip = frame["begin"] + 1 continue - loop_stack.pop() + self.loop_stack.pop() ip += 1 continue raise ParseError(f"unsupported compile-time AST node {node!r}") @@ -787,6 +943,22 @@ class CompileTimeVM: raise ParseError("'for' without matching 'next'") return pairs + def _begin_pairs(self, nodes: Sequence[ASTNode]) -> Dict[int, int]: + stack: List[int] = [] + pairs: Dict[int, int] = {} + for idx, node in enumerate(nodes): + if isinstance(node, WordRef) and node.name == "begin": + stack.append(idx) + elif isinstance(node, WordRef) and node.name == "again": + if not stack: + raise ParseError("'again' without matching 'begin'") + begin_idx = stack.pop() + pairs[begin_idx] = idx + pairs[idx] = begin_idx + if stack: + raise ParseError("'begin' without matching 'again'") + return pairs + def _jump_to_label(self, labels: Dict[str, int], target: str) -> int: if target not in labels: raise ParseError(f"unknown label '{target}' during compile-time execution") @@ -1457,6 +1629,13 @@ def _ct_rpick(vm: CompileTimeVM) -> None: vm.push(vm.return_stack[-1 - index]) +def _ct_pick(vm: CompileTimeVM) -> None: + index = vm.pop_int() + if index < 0 or index >= len(vm.stack): + raise ParseError("pick index out of range") + vm.push(vm.stack[-1 - index]) + + def _ct_nil(vm: CompileTimeVM) -> None: vm.push(None) @@ -1509,6 +1688,14 @@ def _ct_list_empty(vm: CompileTimeVM) -> None: vm.push(1 if not lst else 0) +def _ct_loop_index(vm: CompileTimeVM) -> None: + if not vm.loop_stack: + raise ParseError("'i' used outside of a for loop") + frame = vm.loop_stack[-1] + idx = frame["initial"] - frame["remaining"] + vm.push(idx) + + def _ct_list_get(vm: CompileTimeVM) -> None: index = vm.pop_int() lst = _ensure_list(vm.pop()) @@ -1608,11 +1795,121 @@ def _ct_string_to_number(vm: CompileTimeVM) -> None: vm.push(0) +def _ct_set_token_hook(vm: CompileTimeVM) -> None: + hook_name = vm.pop_str() + vm.parser.token_hook = hook_name + + +def _ct_clear_token_hook(vm: CompileTimeVM) -> None: + vm.parser.token_hook = None + + +def _ct_use_l2_compile_time(vm: CompileTimeVM) -> None: + if vm.stack: + name = vm.pop_str() + word = vm.dictionary.lookup(name) + else: + word = vm.parser.most_recent_definition() + if word is None: + raise ParseError("use-l2-ct with empty stack and no recent definition") + name = word.name + if word is None: + raise ParseError(f"unknown word '{name}' for use-l2-ct") + word.compile_time_intrinsic = None + word.compile_time_override = True + + +def _ct_add_token(vm: CompileTimeVM) -> None: + tok = vm.pop_str() + vm.parser.reader.add_tokens([tok]) + + +def _ct_add_token_chars(vm: CompileTimeVM) -> None: + chars = vm.pop_str() + vm.parser.reader.add_token_chars(chars) + + +def _ct_fn_param_index(vm: CompileTimeVM) -> None: + name = vm.pop_str() + params = _ensure_list(vm.pop()) + try: + idx = params.index(name) + vm.push(params) + vm.push(idx) + vm.push(1) + except ValueError: + vm.push(params) + vm.push(-1) + vm.push(0) + + +def _ct_fn_translate_postfix(vm: CompileTimeVM) -> None: + params = _ensure_list(vm.pop()) + postfix = _ensure_list(vm.pop()) + prologue: List[Any] = [">r"] * len(params) + translated: List[Any] = [] + for tok in postfix: + if isinstance(tok, int): + translated.append(tok) + continue + if isinstance(tok, str): + try: + num_value = int(tok, 0) + translated.append(num_value) + continue + except ValueError: + pass + if isinstance(tok, str) and tok in params: + idx = params.index(tok) + translated.append(idx) + translated.append("rpick") + else: + translated.append(tok) + epilogue: List[Any] = ["rdrop"] * len(params) + out: List[Any] = prologue + translated + epilogue + vm.push(out) + + +def _ct_shunt(vm: CompileTimeVM) -> None: + """Convert an infix token list (strings) to postfix using +,-,*,/,%.""" + ops: List[str] = [] + output: List[str] = [] + prec = {"+": 1, "-": 1, "*": 2, "/": 2, "%": 2} + tokens = _ensure_list(vm.pop()) + for tok in tokens: + if not isinstance(tok, str): + raise ParseError("shunt expects list of strings") + if tok == "(": + ops.append(tok) + continue + if tok == ")": + while ops and ops[-1] != "(": + output.append(ops.pop()) + if not ops: + raise ParseError("mismatched parentheses in expression") + ops.pop() + continue + if tok in prec: + while ops and ops[-1] in prec and prec[ops[-1]] >= prec[tok]: + output.append(ops.pop()) + ops.append(tok) + continue + output.append(tok) + while ops: + top = ops.pop() + if top == "(": + raise ParseError("mismatched parentheses in expression") + output.append(top) + vm.push(output) + + def _ct_int_to_string(vm: CompileTimeVM) -> None: value = vm.pop_int() vm.push(str(value)) + + def _ct_identifier_p(vm: CompileTimeVM) -> None: value = vm.pop_str() vm.push(1 if _is_identifier(value) else 0) @@ -1677,12 +1974,6 @@ def _ct_parse_error(vm: CompileTimeVM) -> None: raise ParseError(message) -def _ct_enable_call_syntax(vm: CompileTimeVM) -> None: - vm.parser.call_syntax_enabled = True - - -def _ct_disable_call_syntax(vm: CompileTimeVM) -> None: - vm.parser.call_syntax_enabled = False def _ct_lexer_new(vm: CompileTimeVM) -> None: @@ -1763,6 +2054,7 @@ def _register_compile_time_primitives(dictionary: Dictionary) -> None: register("r>", _ct_r_from) register("rdrop", _ct_rdrop) register("rpick", _ct_rpick) + register("pick", _ct_pick) register("nil", _ct_nil, compile_only=True) register("nil?", _ct_nil_p, compile_only=True) @@ -1778,6 +2070,7 @@ def _register_compile_time_primitives(dictionary: Dictionary) -> None: register("list-clear", _ct_list_clear, compile_only=True) register("list-extend", _ct_list_extend, compile_only=True) register("list-last", _ct_list_last, compile_only=True) + register("i", _ct_loop_index, compile_only=True) register("map-new", _ct_map_new, compile_only=True) register("map-set", _ct_map_set, compile_only=True) @@ -1788,18 +2081,27 @@ def _register_compile_time_primitives(dictionary: Dictionary) -> None: register("string-length", _ct_string_length, compile_only=True) register("string-append", _ct_string_append, compile_only=True) register("string>number", _ct_string_to_number, compile_only=True) + register("fn-param-index", _ct_fn_param_index, compile_only=True) + register("fn-translate-postfix", _ct_fn_translate_postfix, compile_only=True) register("int>string", _ct_int_to_string, compile_only=True) register("identifier?", _ct_identifier_p, compile_only=True) + register("shunt", _ct_shunt, compile_only=True) register("token-lexeme", _ct_token_lexeme, compile_only=True) register("token-from-lexeme", _ct_token_from_lexeme, compile_only=True) register("next-token", _ct_next_token, compile_only=True) register("peek-token", _ct_peek_token, compile_only=True) register("inject-tokens", _ct_inject_tokens, compile_only=True) + register("add-token", _ct_add_token, compile_only=True) + register("add-token-chars", _ct_add_token_chars, compile_only=True) + register("set-token-hook", _ct_set_token_hook, compile_only=True) + register("clear-token-hook", _ct_clear_token_hook, compile_only=True) + register("use-l2-ct", _ct_use_l2_compile_time, compile_only=True) + word_use_l2 = dictionary.lookup("use-l2-ct") + if word_use_l2: + word_use_l2.immediate = True register("emit-definition", _ct_emit_definition, compile_only=True) register("parse-error", _ct_parse_error, compile_only=True) - register("enable-call-syntax", _ct_enable_call_syntax, compile_only=True) - register("disable-call-syntax", _ct_disable_call_syntax, compile_only=True) register("lexer-new", _ct_lexer_new, compile_only=True) register("lexer-pop", _ct_lexer_pop, compile_only=True) @@ -1910,11 +2212,11 @@ class Compiler: def __init__(self) -> None: self.reader = Reader() self.dictionary = bootstrap_dictionary() - self.parser = Parser(self.dictionary) + self.parser = Parser(self.dictionary, self.reader) self.assembler = Assembler(self.dictionary) def compile_source(self, source: str) -> Emission: - tokens = list(self.reader.tokenize(source)) + tokens = self.reader.tokenize(source) module = self.parser.parse(tokens, source) return self.assembler.emit(module) diff --git a/main.sl b/main.sl index 44cc4e8..7548779 100644 --- a/main.sl +++ b/main.sl @@ -1,15 +1,15 @@ import stdlib.sl +import fn.sl : main 2 40 + puts extend-syntax - 1 - 2 - foo() + foo(1, 2) puts 0 ; + fn foo(int a, int b){ return a + b; } \ No newline at end of file diff --git a/stdlib.sl b/stdlib.sl index 4cfa60e..28381c8 100644 --- a/stdlib.sl +++ b/stdlib.sl @@ -47,211 +47,6 @@ puts_finish_digits: } ; -: extend-syntax - enable-call-syntax -; -immediate -compile-only - -:py fn { - FN_SPLIT_CHARS = set("(),{};+-*/%,") - - def split_token(token): - lex = token.lexeme - parts = [] - idx = 0 - while idx < len(lex): - char = lex[idx] - if char in FN_SPLIT_CHARS: - parts.append(Token( - lexeme=char, - line=token.line, - column=token.column + idx, - start=token.start + idx, - end=token.start + idx + 1, - )) - idx += 1 - continue - start_idx = idx - while idx < len(lex) and lex[idx] not in FN_SPLIT_CHARS: - idx += 1 - segment = lex[start_idx:idx] - if segment: - parts.append(Token( - lexeme=segment, - line=token.line, - column=token.column + start_idx, - start=token.start + start_idx, - end=token.start + idx, - )) - return [part for part in parts if part.lexeme] - - class FnLexer: - def __init__(self, parser): - self.parser = parser - self.buffer = [] - - def _fill(self): - while not self.buffer: - if self.parser._eof(): - raise ParseError("unexpected EOF inside fn definition") - token = self.parser.next_token() - split = split_token(token) - if not split: - continue - self.buffer.extend(split) - - def peek(self): - self._fill() - return self.buffer[0] - - def pop(self): - token = self.peek() - self.buffer.pop(0) - return token - - def expect(self, lexeme): - token = self.pop() - if token.lexeme != lexeme: - raise ParseError(f"expected '{lexeme}' but found '{token.lexeme}'") - return token - - def push_back_remaining(self): - if not self.buffer: - return - self.parser.tokens[self.parser.pos:self.parser.pos] = self.buffer - self.buffer = [] - - def collect_block_tokens(self): - depth = 1 - collected = [] - while depth > 0: - token = self.pop() - if token.lexeme == "{": - depth += 1 - collected.append(token) - continue - if token.lexeme == "}": - depth -= 1 - if depth == 0: - break - collected.append(token) - continue - collected.append(token) - return collected - - OP_PRECEDENCE = {} - OP_PRECEDENCE["+"] = 1 - OP_PRECEDENCE["-"] = 1 - OP_PRECEDENCE["*"] = 2 - OP_PRECEDENCE["/"] = 2 - OP_PRECEDENCE["%"] = 2 - - def parse_fn_body(tokens): - if not tokens: - raise ParseError("empty function body") - lexemes = [tok.lexeme for tok in tokens if tok.lexeme] - if not lexemes or lexemes[0] != "return": - raise ParseError("function body must start with 'return'") - if lexemes[-1] != ";": - raise ParseError("function body must terminate with ';'") - extra = lexemes[1:-1] - if not extra: - raise ParseError("missing return expression") - return extra - - def shunting_yard(tokens): - output = [] - stack = [] - for token in tokens: - if token == "(": - stack.append(token) - continue - if token == ")": - while stack and stack[-1] != "(": - output.append(stack.pop()) - if not stack: - raise ParseError("mismatched parentheses in return expression") - stack.pop() - continue - if token in OP_PRECEDENCE: - while stack and stack[-1] in OP_PRECEDENCE and OP_PRECEDENCE[stack[-1]] >= OP_PRECEDENCE[token]: - output.append(stack.pop()) - stack.append(token) - continue - output.append(token) - while stack: - top = stack.pop() - if top == "(": - raise ParseError("mismatched parentheses in return expression") - output.append(top) - return output - - def is_int_literal(text): - try: - int(text, 0) - return True - except ValueError: - return False - - def translate_postfix(postfix, params): - indices = {name: idx for idx, name in enumerate(params)} - translated = [] - for token in postfix: - if token in indices: - translated.append(str(indices[token])) - translated.append("rpick") - continue - if is_int_literal(token): - translated.append(token) - continue - translated.append(token) - return translated - - def macro(ctx): - parser = ctx.parser - if not isinstance(parser.context_stack[-1], Module): - raise ParseError("'fn' definitions must be top-level") - lexer = FnLexer(parser) - name_token = lexer.pop() - name = name_token.lexeme - if not is_identifier(name): - raise ParseError("invalid function name for 'fn'") - lexer.expect("(") - params = [] - if lexer.peek().lexeme != ")": - while True: - type_token = lexer.pop() - if type_token.lexeme != "int": - raise ParseError("only 'int' parameters are supported in fn definitions") - param_token = lexer.pop() - if not is_identifier(param_token.lexeme): - raise ParseError("invalid parameter name in fn definition") - params.append(param_token.lexeme) - if lexer.peek().lexeme == ",": - lexer.pop() - continue - break - lexer.expect(")") - lexer.expect("{") - body_tokens = lexer.collect_block_tokens() - lexer.push_back_remaining() - if len(params) != len(set(params)): - raise ParseError("duplicate parameter names in fn definition") - return_tokens = parse_fn_body(body_tokens) - postfix = shunting_yard(return_tokens) - body_words = [] - for _ in reversed(params): - body_words.append(">r") - body_words.extend(translate_postfix(postfix, params)) - for _ in params: - body_words.append("rdrop") - generated = [] - emit_definition(generated, name_token, name, body_words) - ctx.inject_token_objects(generated) -} -; - :asm dup { mov rax, [r12] sub r12, 8 @@ -264,6 +59,13 @@ compile-only } ; +:asm over { + mov rax, [r12 + 8] + sub r12, 8 + mov [r12], rax +} +; + :asm swap { mov rax, [r12] mov rbx, [r12 + 8] @@ -453,6 +255,15 @@ compile-only } ; +:asm pick { + mov rcx, [r12] + add r12, 8 + mov rax, [r12 + rcx * 8] + sub r12, 8 + mov [r12], rax +} +; + :asm rpick { mov rcx, [r12] add r12, 8 diff --git a/test.bin b/test.bin index b883df02a0990299fe76974eba4d991b2dacafc9..792e00c324aee10fee36391d3c75537ed8273c47 100755 GIT binary patch literal 10224 zcmeI2e{38_701Vpoj3`t9h|y}>b5R*1EIu;?-H=36xq!W-${0rMx+IjYIVLJ&QYJw zy4$nkLgP@YQckW_8WE(@(f~s6M?gqar3sZO?Uke|sS5?vK$NsXq+QfBAr-9~>~g&C z?3=yWiS2**lUpgXZ$9&VZ)V=i&N+KGx~+SAWkrQhSEcx(khQuFiQH0%!8J-DM2Bb- zi>O>Is-?E7)I^r@aPJPcsk^9i0s5$h$#uc~sr%GYSEueu2fxK`lUp`*`0w0yKvcSx ziqE`!D`%jbfpP}Q87OC2^7Uk~L^ho|2^@FIb!gAGJO`9kR7Gr7Nq z$37DFQYl_X_(avU%jhN}KQF`QUkT?gcju>Ss|)T>F4TncRm}yAlE~KPn(&xH82tDi zBKgMVaGsjj<)xZ%1zdP?;k9~Y`g5d}fSf2ORH4U1(=+qYm0G0n#^z9a;WY731Eo{77(VT>!kO6IR;{1bA{J)D`I@s*qdb>GPK z&CSQ~iiY-sn=6ek*ZF3t=e<+P#n^kaDBA>qZ6ZAOTW!s#Lew1wrT&z*nhI`xm3(-O zLlf0)*Ho73vf*S^bCd=_dxBI{!J8&!*O2Xe@Nu!|wc-_#S3$E^AzKuji^XEmDUgRV z`5$s~&Y}J^>c>&n1N$qkXCBJ2lIIBOqq6=Um5xJ&ih&NDdWx?Aw!Bk`4(}v4AH4wQ z?FjZVtO6BY_xgmFAVd{(owJ$*Po|2u2$GA`>fEGD@S;sgWR%m8%*<#}zGO;HKyq4_ z>@p?CA$dlZ=wn%lv8;#WkS@Wda+U+PqZrcL@TyOZ6Ek-fL4v6`cR}a8f!R-ysZ?Kw z>MQ~0L#WQ9xK$5H?OsT{dsL3dl(Iji*|b7laQHgEK*-%%m+dq%=WFP43{4%dQm~Hx zTXUtZoS%cP(?^3R&AApH_n;Ud52qe;O2cY^6(?)X<(CQBLvHG{3qAZByI##9jmT@s z_uUl=odasn!y&)UqePTM?#i0^Vg?}p^A(EO<$Oi+l3`v4;M2u75`4NiN8sg+WINC! zgq)l8;SlxKKyGvujy^i}e>TiVi*Kyeve#`QRD2o7&gkE8<~Xes+RGWXgz}L+=uvM{ zQJur&gF{P2x+o*P^AWb$AQq<&0zP+Sc84&5lW6MJnq-!Dqv>rlVJLFLJ5N$5=S8qX zuUsD=?rTMy%#x-H=)?2DfDwpDkh-_YKp63JPXAN zO`+#Q#yAYcr4O}*(T6IJ%SPID&M8QoD9mr7ID%pl#TgV=P+X5iZb5N3ieVH2r@-wf zawv)@cHl|>7m9D87&8u?0`~3P7oONEak*%=x?hh}h|j3+Us!OPyjO^T!Phh2Y4A4Y zIfHMVhu_8gF+=}l=HEB?{mdr~ZZm(y;QN`sX7Gc|&l>y?^M4xr3FaRg{71}}t>}=J zDt^ZN27~{Sd8@&nXWnJ--!YFG{0-)#27inB69)eW^JfkI0rNi^-2E!BLR>QVGHf9F zwr|GZ^~`HO*&!pU;zs6O24BnEGI)@A+TeFEKVa}K=8qYCC-d(Zd=K-Z2Jd73g29KG z|H0r7F+XSU3Ffl~Kf-)zy+1$CFmEvUtITgT_?ygw2EWXFi@}%R!7k4eh>4*29GiSvB3xD z;oo5XQ$zm<^S>JWyUb?|{uAbv*Zb%DYvvmaev0{aga3tjx4|zkKWgw<=37_$`>n<2 zHLXjZ!BJ3TFoJLchC%nvmA^ zBlGYb%+2_sH<#>>t+(S@yCsrM2`g(yGIq;A+KTT>+SX8{KNlCn=}gQT%-LCi!iw~= z5li+Z73@!^2i@juvTwk(63KyNcCW=U86}n*bi>6mq?af?6wkO;b~wV!3iP_j3dEEV zQAR=;o7f1(te_`rE!n}S67U?XU{q_3m+XxjRLoY_NTt+JQn>-oMozqtJm`!Slcs!wk<6^KMLXiYd^7u6v+U346a to%D~ZVih1=c$4GxEs0yag@Mtv7mnJ6x$4&ns?6mdTH~tt=DC>u{{?!^ry2kN literal 10112 zcmeI2e{fXQ6~}KvLIad0n269ujTmidOCTGlq?6i21MV^jgH}tYboyqqKXixeZrpt< zMB6}{aVG0d9Chf7G8U@+tKvwfGmNp+QHh9SYnh!EVp@zH{H+ zcgg0D{@Z;s+;{Hhe$To0o_pV2_GNc%=-XIbRVCC_Ep8RE*3d1HTatORtTJ0K=80}G zU(`x%O{s~jZmzj^himFC8eD)rYGHC+aDVE4V1cVsccp{he7DIhTe|&sZrd%YT}#Dh zUb$5=P{}|g1CKqUi}3{*1k|CoWk@tZEzSBd`oM}6a~&em58 zr;WaOPaqZjotN#pP@$$ZH2Qj7Fn?^Ki(q|iiA?rLU2t?p{B%KZ`ee@pzMTih*L}62 zTD*i1J-=TFLW10)U~%e=`D=p3*G@MF+m8qHCqmQbcOM}zHMW#!Fh3c5_+;*%q0tM% zULeJ*2)|MDrG<2pkzbMF^KXUn=lb$f^|h1kP(H2;m4KWeB~|dL;Pgy6+N4D)G`9vjCl6z~^?N4KZ>bl(?H6;jJv~R; z{yEzA&C&MDQd_VfcqZ;*hA^)DGMTad!WZS7`#CfHg(l8{y07JWSL*~`%FqsQbEWY` zI`5Kt-YunEjGm!I*&qmP5TVhdb)ivj;f`XJ++lC-9HwDLQw=J$N)n&u6n$`&Q zM+;6Ws^B$}vg^op7XGJL^jh)$$E%>(tB@@wollCzqI2SNDvra=IfeS0s88u(e4eu6 zdhUbr$&%-zsPB>WYDD^+Of@)k=rj^l0Q=dIvaRIiqaTx>vkk%CgLMKCl|Y~H7Z74O zbkWP21dpAP&x2$hBt5zWZ_$)QMma;R&UL!vZc}m+l9?H;qkBxr6eK5fi9VJljO8v! z4(Sr?AEym?Gm5A5HoV7E(RIy>1Y};+r^Oc71^3#kre8dpX0FP(HE;J?eETsxv`8IGSLK136S;yeNCl?zlHfprMhR#*qf@&+oWtQ~C59{7%t zM0Kkx@aEmgKoOryF$hJkrhuP{Cu3}aVuhyA9|gI|dZ7@SLLY}bVpc$LYI^pON<}ji zf6x?H^m2(haQ12ny%#x-&!E_;DfDwpDvm?(eNAyij6XrKR#WKtkTITv;wn;j3!@KJ z9+%Cu>zuRy#@2|y{27Yz?1$NipNll z8i&p#_HEe<4{eP&S2P9gpFgU^*VNxvSa6fPSBZeZS2MrM;Om*^4BlIYZ)X0uq5lE% zUm1Km^Op^7Gk?qABh241_!#pK4E{Ltvj*SG{IbD+&3y5aZfU9FdFD47{1Ed_gTKc7 z9)rKl{CzsJ1A;GZ&IW$;D#T%~pSw!yDuzRBRLng7t>w=#d;;G3DhWAGm{KX339 z^Q)Wu^BrNn-r$ci-)8Xr%ts7D`RzV^h{|cnW02vJ4cEoX%gANG<){<+!4CnZ3;)D z?H#VUC7npwRv?y5Jjl@$*;$sRk0u`Q)sty|yi!^@fVXwHUfMSb*y#+XQT6Gqp#tGZ zChhAs5f^<{B5uXQgV~t1#;wFsQHv+bjxn?bqJDKicU@a@T~{)Bp*c!CouMWwEj%vP zx444tZt;x@l0fvq7!6j_uh;aQK7H2C;l?*3RB1(@sZCCeZ z+4MMOT;kp&8OmUT4&>s(vn(2|u&jY>);%kP6^n-Lun=w8;iMfNpu3%M@AoOoj_tHX z8@c$-s{f_(6>`FVUA*69ssBeexY4}dZ*g$OTod93vhX@h9`m5;{A@u*2=8}XZdLsZ zjB9S+bn0Gl{dT#F{)X>rbkT7uw$MNHg=?966L|UJ_00^o$1pIuHo;N5FjxIrLY3M4 Q|LT6h(tkE5-Aw=g0vB14YXATM diff --git a/test.sl b/test.sl index c26daa1..5dd56e1 100644 --- a/test.sl +++ b/test.sl @@ -1,4 +1,5 @@ import stdlib.sl +import fn.sl :asm mem-slot { lea rax, [rel print_buf] @@ -33,8 +34,8 @@ struct: Point extend-syntax -fn fancy_add(int left, int right){ - return (left + right) * right; +fn fancy_add(int a, int b){ + return (a + b) * b; } : test-add diff --git a/tests/run_tests.py b/tests/run_tests.py new file mode 100644 index 0000000..68a6af3 --- /dev/null +++ b/tests/run_tests.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python3 +"""Simple end-to-end test runner for L2. + +Each test case provides an L2 program source and an expected stdout. The runner +invokes the bootstrap compiler on the fly and executes the produced binary. +""" + +from __future__ import annotations + +import subprocess +import sys +import tempfile +from dataclasses import dataclass +from pathlib import Path +from typing import List + +ROOT = Path(__file__).resolve().parents[1] +COMPILER = ROOT / "main.py" +PYTHON = Path(sys.executable) + + +@dataclass +class TestCase: + name: str + source: str + expected_stdout: str + + +CASES: List[TestCase] = [ + TestCase( + name="call_syntax_parens", + source=f""" +import {ROOT / 'stdlib.sl'} +import {ROOT / 'fn.sl'} + +: main + 2 40 + + puts + extend-syntax + foo(1, 2) + puts + 0 +; + +fn foo(int a, int b){{ + return a + b; +}} +""", + expected_stdout="42\n3\n", + ), + TestCase( + name="loops_and_cmp", + source=f""" +import {ROOT / 'stdlib.sl'} + +: main + 0 + 5 for + 1 + + next + puts + 5 5 == puts + 5 4 == puts + 0 +; +""", + expected_stdout="5\n1\n0\n", + ), + TestCase( + name="override_dup_compile_time", + source=f""" +import {ROOT / 'stdlib.sl'} + +: dup + 6 +; +compile-only + +: emit-overridden + "dup" use-l2-ct + 42 + dup + int>string + nil + token-from-lexeme + list-new + swap + list-append + inject-tokens +; +immediate +compile-only + +: main + emit-overridden + puts + 0 +; +""", + expected_stdout="6\n", + ), +] + + +def run_case(case: TestCase) -> None: + print(f"[run] {case.name}") + with tempfile.TemporaryDirectory() as tmp: + src_path = Path(tmp) / f"{case.name}.sl" + exe_path = Path(tmp) / f"{case.name}.out" + src_path.write_text(case.source.strip() + "\n", encoding="utf-8") + + compile_cmd = [str(PYTHON), str(COMPILER), str(src_path), "-o", str(exe_path)] + compile_result = subprocess.run( + compile_cmd, + capture_output=True, + text=True, + cwd=ROOT, + ) + if compile_result.returncode != 0: + sys.stderr.write("[fail] compile error\n") + sys.stderr.write(compile_result.stdout) + sys.stderr.write(compile_result.stderr) + raise SystemExit(compile_result.returncode) + + run_result = subprocess.run( + [str(exe_path)], + capture_output=True, + text=True, + cwd=ROOT, + ) + if run_result.returncode != 0: + sys.stderr.write("[fail] execution error\n") + sys.stderr.write(run_result.stdout) + sys.stderr.write(run_result.stderr) + raise SystemExit(run_result.returncode) + + if run_result.stdout != case.expected_stdout: + sys.stderr.write(f"[fail] output mismatch for {case.name}\n") + sys.stderr.write("expected:\n" + case.expected_stdout) + sys.stderr.write("got:\n" + run_result.stdout) + raise SystemExit(1) + + print(f"[ok] {case.name}") + + +def main() -> None: + for case in CASES: + run_case(case) + print("[all tests passed]") + + +if __name__ == "__main__": + main()