From aa14b9effc96b4ac437f450e0edd2d8d09f48202 Mon Sep 17 00:00:00 2001 From: NoxAether <107390370+NoxAether@users.noreply.github.com> Date: Sat, 18 Oct 2025 22:48:42 +1100 Subject: [PATCH 1/6] cleaned --- LBS/a.out | Bin 0 -> 15880 bytes LBS/main.c | 66 +++++++++++++++++++++++++++++++++++++++++ mockups/file.c | 71 +++++++++++++++++++++++++++++++++++++++++++++ std/ffi/bindgen.lx | 0 std/ffi/c_types.lx | 0 std/ffi/stdlib.lx | 0 std/ffi/syscall.lx | 0 7 files changed, 137 insertions(+) create mode 100755 LBS/a.out create mode 100644 LBS/main.c create mode 100644 mockups/file.c delete mode 100644 std/ffi/bindgen.lx delete mode 100644 std/ffi/c_types.lx delete mode 100644 std/ffi/stdlib.lx delete mode 100644 std/ffi/syscall.lx diff --git a/LBS/a.out b/LBS/a.out new file mode 100755 index 0000000000000000000000000000000000000000..f527801958f12441e61d21800d499c8fead7aa81 GIT binary patch literal 15880 zcmeHOdu$Zv5&zD{!4UjNgv+yGMGS%>AGUdvLR{AP!8$cyz*LZ?T_1OQ_KEY|^=_R) zM2Zt*gyXt$TS!S&wN+n#RBA;lN~5GmCL~bON;Rs~&wB6Shw@dZyw+5?8D#Q&K+xMZ!Plq1XHp2q(EuBQXx7iPHv0SPM!$2Uck`uW$DA`nj(&xxizZ+$Xg|=e1f58B^EwL3 zob}Fp8ae}Ma_jKBBr?zT!`h73D1-5aTs~T)N;LT<=fC{q=GloQvm!;Rm9XVD)ew%dG|Q7XjDu*v~Ft-&FvwD}YxOz`G0JxWD=G z|2g0qeB87NfPD6A3gG1h@Yev>;Nzy<04#TdD2KZ(J1A0iGMG3b5>_%9PYTma*@568 zGuU&;3?1N~Yw9Vc?BnC+EaKH>lVu5JnX>bZg z<0(rdl98Al7HEv;#YVBcy=_~QS?^!(jMmKz>-`&q+19zk3|UF5JCd@kWao~iXgp?h z1`b9on4`Nl9-~=JWt!Jeh$1+1eXzy9S#rw~ay&1KMGQD!XY(VGGQ5))=6V*6=Lpk87GA+1a$N~{o!~jkRH~~H;(YHyq*BMN5QxkFUs4icp{@pq<32EH zx*8#lAEs5h8X?9tLZnv5t+IteJG4nxL$rU;K_UGtd|noAWZ`f?J@P8xRluu&R{^gA zUIqS-EAaPi*I;bnSD-&|Aj9&FPNLmb$UJtHwyf zAHgP5^=rQnx5NI5Wye8;9Uf^o00E4Vl0C>iamTKLQv3vruoQDL6BWz)(e4s)uwO0h zH!MfC`kpZ~ZCw28R^#HGV#9aYxOUH;4*{N`0Hv9UaK*Cb8Gp>LzhM&8qHoytSAneTLl3&8m_t{G``9dDL2+znEFWn8c`yht#?Ax_aiI6co zYsD%Vw&hOUm@!hdxE$sfDXBs>^m}9I%G9}gnT#log74k zQfKTrYB0C&4>Q5jc!&tM*F1Xxr@9Bwv1P+@+K~?5&}jsXGZp7gW3-c}N~R$+`;<}i ziop}$HXs9tFMeKm8)q&bm)1~hK`$m~qA!I`#TAj2wCR zxRi~N)5=>G^1Zv6%z*UpW!Hx5b!)ELJxE7n)uRfg%Be@i;46^PmBE`ZA==*BH7v)F z22DGB8g(N{pD|Kq!_pfvHHLos5*kmPha}wgxJNr>a_Hw%>Mb(Uqu%;ws(+oO^_GSHMX<@+#m}z^i~)0j~mH1-uG)74RzHRp7%`0emZy z6zzSzfrEXKXh^ie>nAIu*>MfuK51c)v>9!CBxY$VLYuV}DR>FuTT;A5gB`|)`SV@7=53F&m5R%KYrWLSYCOesHyCM;=M&M z1pw+j3I@gJR(zL=gkL=1$0M%-UIn}gcopy};8nn@fL8&p0$v5Y3Vhfr!2NLC|Ax;# zNHN8P)3Zelc-*U1O%G~|$d3E$mXNHTsDxNdGWXlza~Klq|8Xx9M|m6{3W2z1EkOhB zRhuR~JVr|T6LIb}TSzkYfo2oAi7D!@!$&`)X~mr9P40bLKzM=x#?MkBoc9>naSt5N z|6WGz52&Rnyrh%+mmq(5Ln>viGd#{pc_-=LC7JW4Y5wPo`^ViAZ=~~}mH4NLhlxK$ z`~>kAi2soIdE%FebNv0f8f|ZC+N`aDr|;ET-3EWXzfP;Kt=~|)scs!x=I2he} zF4S$2566S!6IH^!pQ;VCQgFXG$HMO!d}4uf9x=Wd2KoAeKUDzl0bGNRo94j|Xkwx0 z&~r7%LChEb>ws5+eV5DrTLtWYig;1E;OiDdNxy(?zW8qxz$XjfcM9NhVZSg>_kJyq z`wLDkS_$|=MGFO=53F8?xTtW7$KT0p13X{;`v9*Li$ssk5frbK_GQlRRaTPv39@(B zGfehZxj&e0AcrKL8ylr(3gGx17Veij{x<>F?hhl*1HL>Dehu)4i{P&3x_+SHI}-Cl z!*}z6osB!%nxF?8E+VtVFkAOD?r1TM<~_pP-o9&FW4pPlwRLYxr`g%Kt-ZyRz2iE! zd&|ylch9(^g%W;OHWrYHEfV#|cHPC_{p>FkDg{!&UQLd*hbqBx+WP5xL zVwyOmYCgZO6Wt7@;$}}E7Q!y|wq0Nqip0#mlog_m`P@)eECeAArcxA6cF@Z{`y4yf zmY)NeCUnG`Ejydd_v?SpZN|?H0OhfT@TZRS+JS?>?WE#8Jc`9_%ikUA^Cyz<-D}c5 zBK!^{znra!goGdZ`mJO_NW-2$sz>-kM`93EaXYCrPg%)SBp%BVOwc8*XaF5(l!)3G z5fby;)?wJngXmAj2kjB_eHvE3jV*(F!WjF_>a;*Z07j? zBAfVr#aJT@;8J$xFZ6phwny7+Y6Ah+HKdznFf12`V-cJ+a zvIK|h&+DAe0ERB?&+9ejygmaHN$&iEfZ|$+{dxVvTvKk&dXVjyCqR#DD@UgF6R)#$ z@^|M?us8+-JSObV>pSLSWazFR@Ba|_ZzDTiFEXFb^2ar$yMA8-3~NGZ&)m=HzRv4t z3Ul8(h%dYRHS%MAmK}+c+WEJEzsJg+pMSr}d^epB9ESZ_{k9If3on=l>7L zpX)C)vLwERL}qUPAAf2}GGu;~{Fw3aWBGL$WZyq-XiCGJ zLU@O-VB{``-cuo_l-_=wBFC5rz5?FD +#include +#include + +struct Builder { + char *output; + char *inputPath; + char **inputFiles; + char **importPaths; + char **importFiles; +}; + +int main(void) { + FILE *file = fopen("Lumabuild", "r"); + if (!file) { + perror("Failed to open file Lumabuild"); + return -1; + } + + char ch; + int startOfLine = 1; + char currentLine[256]; + int linePos = 0; + char *fileContents[256]; + int lineCount = 0; + + while ((ch = fgetc(file)) != EOF) { + if (startOfLine) { + if (ch == '#') { + // Skip the rest of the line + while ((ch = fgetc(file)) != EOF && ch != '\n') + ; + startOfLine = 1; + continue; + } + } + + // Store character in current line buffer + if (ch != '\n' && linePos < 255) { + currentLine[linePos++] = ch; + } + + // When we hit newline or EOF, store the completed line + if (ch == '\n' || ch == EOF) { + if (linePos > 0) { + currentLine[linePos] = '\0'; + fileContents[lineCount] = malloc(strlen(currentLine) + 1); + strcpy(fileContents[lineCount], currentLine); + lineCount++; + linePos = 0; + } + startOfLine = 1; + } else { + startOfLine = 0; + } + } + + // Print stored lines + for (int i = 0; i < lineCount; i++) { + printf("Line %d: %s\n", i + 1, fileContents[i]); + free(fileContents[i]); // Clean up + } + + fclose(file); + return 0; +} diff --git a/mockups/file.c b/mockups/file.c new file mode 100644 index 00000000..31afb04a --- /dev/null +++ b/mockups/file.c @@ -0,0 +1,71 @@ +#include +#include +#include + +// // what functions +enum { SUCCESS, ERROR }; + +typedef struct FILE { + // store the pointer to the file + FILE *fileptr; + char *file_name; + char *file_flag; // r, w, rw + // tmp buffer to edit or use as a medium + char *buffer; + // position in the file + int position; + // Can be user defined so that a custom function can be run on file contents + // or buffer flag 0 representing file flag 1 representing buffer + void *(*fptr)(FILE *, int flag); +} File; +// file return should return a file struct or error information +typedef struct FILE_RET { + File *file_core; + int type; + char *return_log; +} File_Re; + +// private used to init the values of the file object +File_Re *finit(File *file, char *file_name, char *file_flag) { + File_Re *fre; + // FLAGS r, w, rw, a, ab, r+, w+, + file = fre->file_core; + file->file_name = file_name; + file->file_flag = file_flag; + file->position = 0; + file->buffer = NULL; + file->fileptr = NULL; +} + +// used to open a specific file +File *file_open(char *file_name, char *file_flag, char *buffer) { + File_Re *file; + file->file_core = finit(file, file_name, file_flag); + if (!buffer) { + file->buffer = buffer; + } + if (!file->fileptr) + file->fileptr = fopen(file_name, file_flag); + // return what it is or what it inits as + return file; +} + +// used to close a file object and deinit +int file_close(File *file) { + if (file->fileptr) + fclose(file->fileptr); + if (file->buffer) + free(file->buffer); + // re-use init so we can reset the values. + finit(NULL, NULL, NULL); + return SUCCESS; +} + +int main(void) { + + File *file = file_open(); + + file = file_close(); + + return 0; +} diff --git a/std/ffi/bindgen.lx b/std/ffi/bindgen.lx deleted file mode 100644 index e69de29b..00000000 diff --git a/std/ffi/c_types.lx b/std/ffi/c_types.lx deleted file mode 100644 index e69de29b..00000000 diff --git a/std/ffi/stdlib.lx b/std/ffi/stdlib.lx deleted file mode 100644 index e69de29b..00000000 diff --git a/std/ffi/syscall.lx b/std/ffi/syscall.lx deleted file mode 100644 index e69de29b..00000000 From dd8ada69162f26a0d5d3d060b456848e027a8dac Mon Sep 17 00:00:00 2001 From: NoxAether <107390370+NoxAether@users.noreply.github.com> Date: Sun, 19 Oct 2025 01:26:43 +1100 Subject: [PATCH 2/6] mock update --- mockups/a.out | Bin 0 -> 15800 bytes mockups/file.c | 136 +++++++++++++++++++++++++++++++++++------------ mockups/text.txt | 1 + 3 files changed, 104 insertions(+), 33 deletions(-) create mode 100755 mockups/a.out create mode 100644 mockups/text.txt diff --git a/mockups/a.out b/mockups/a.out new file mode 100755 index 0000000000000000000000000000000000000000..2de10155e7a567eebcf6b4f9afd735462977c785 GIT binary patch literal 15800 zcmeHOYiu0V6~4QUiGd_Gk5C5+oWD@nN?Mirs3K{)&S-2l?Apl;h^=<14cK3$ zu2kniUZZu!ZW{utX?0bVw1)MhCfuWJ57PIupHRt=DB)IXx2-i6p^A@#aMfgDIY`fN zr$WW<$Fm5GX6e08YobOLT8^XI%r+YPY-=SQVcZx~!I4O~ImXQ~&Zm?R*_lc`DSg@w za6N<6hK-u7_euqE(~Jw!&Fn9fVo)PWTk!)Vy^~s6>zBztXT2|bDZ&Nw6WL~*P|5QE z<4#z{AxPU9CzN7PyA?R9=X-53JbF0ZYUi(s&zn#_9r`$%5RAs!-hBsE^thU#T*Mwx zkqS8)kjeK_Y;-yZ-~aqk@1C{uFnmer{YvZ?B@i4|)q*uw4Yj zO8sBBivD5fEo{Md0~A*Wl_ZZ8yiqlfPp8#DZX}&m!^xqcT)%Q%6usXa^^$qd9ZqJl z(DaWb-GNLtIh45<-u*+l(X<-LXR_XaB4;`wJJsH<&OL2zbF5{%&o;+)D7Uk(+fAkO z>BE^(FP-n}ZX3#F(|yT9LuvGUcsQ5kSgvth(GlrJF;XJf)1RTVr}H0GdqHJ9Kl+8Q z;1pM>TUl4QJaaywKMk3hT%Y9kcAN^0c9yR{qHkjTE?b-Z>xjrRHyb^TC;o|O#nWYa zzSL;_Y?+=f-CBRPOn-sanB82Np3b@7Y{O9>GzO*B*oGs$ToXuBXVYFO((C^VT2iXs zHVoZ zk8f4VxqrrsmS&ytCu>gX+)F#2fx2|3X>Oplp?w)o`5kYq238eX4m#tl zUxTM|CSLZ|JCm({f>7G{Aoj(69L!VI&x4KJadJ6dELwK6kEEawZas&iK(qRapB|=+wezAG65zV`E!_ZAld_aVFt5ajN(!sEuDQ zR7=j}LG*V3Cs^}y;OmG#Wz(&l(&|&h5xWTQC1-*h7EjrFo@>~3;&A#TCEANj>vg7+ ziJ~(}!=})PJc_DtfsQ@@py{xPUTAEf7qIJuT^J$-0Vj1fhP9DoH`q*^cPyE_k#-%@=8|zab3md@dhB-!Z$zt1L_!Ue5Ig%d z(@+>+{3jciEz2cDxg{M=W#Z_o<40dz)+I+d z^q8uJ@hWXRRlI}_Z>stl7T3tp;>ceS5c;3AYsQx%g&Qc=q`eN-S`2Py;t61vaKmO! ztsW!Axqp;O3lyWI4X27fghJOBux=UMQ`HM>o-Q+c;B_yjPb&W!=3lkw%!e(kKv;pW z0$~Nh3WOC1D-c#7tUy?SUq*4h4flzj&c1|U<85t;-d?qD z|GtFED=$6f#k?_3btE%G>6GQ=tV}lJSp$$%asV&2w9EmQo#?HacKVQ&&gXOa-PX0E z7pS-f=r?W#YhI_R$Q4yL;r3gcr}HPf(>p4J#Vh(5QFL;G6&%4{>(?Ic4# zLUco2$AhK@q=;{*dm!4j{=De75kV;ns?TA2 z26lbFBKE|1RrCkiMfY(6K6m0cCPA0`ctAT)j2UdFVLuAGY#;OOb5L)=ai6KMKWhR& zw+0&>7?p$GD^nvKZD9*55LO_pKv;pW0$~Nh3WOC1D-c%T|FZ(JZcorzYa-T=*^r*eh5cM<%7JUuNlJ^Yz$r?V1 zkJn84cwpA9cq^slvwS>wxus>PGj8*={42J<&9dapVgBnx){h4R@8ogN!Sv%y2bkW? z^Z}-Vm-U`|+uC+p*Wr%xdTZN`SaWQf)!fv)qiNT+7V%t}QoYuw*Gnr|SJG4QBWj}x z9*0?uTBlA}l|9k@djw}+#)(*WgIQ_)@W)os4?=HY3$_=auvEQjvMVk3L0qZ-FKhjJ z)yziyEC9N1Vyit`ka zU9a_mUZx*`zD{jYj=iiS?vL20ANfvY3!CHYAFO9L$9;p3OFt(8re~G7Dd_3Ag8hs@ zZ*i=0@(}b_mn)dR2KtMmxT*zyKZ+ic#jvit{geCR-JNY%^o<{pn{eEYo_Ke{b=rHB zySHoqo;V)Mb#(M5`rN+wp00$e*QVRDs$4H64=zpjtypvZZtKixsk`q>JyA}seg@~J zQ%Nt$i`pxEXvKSmm$_FMG}t_~b6rB41?*}7j|W}v$jCd_wY#a&oI9AzrfAK3=YII7 zGFi7UnogNa=y4*izpseorn4#3>(J;ZhtTWs^>Y16KNrjD-Ne3j_uUrJSFYo)=s?9r zj|_XsL!e&X&_Us{IWHYMoGrvg@~BweJECH~>QEtrk25kU6~o5>>HLV&j)Te3K^03K z$)Zz3y}Ys9oz9PDa@h*Og)N^RN)mv%ks*(IL{Vb&@Kwe1sfgus`dr1*gYG~+Ih=L} zQ^1PEX~p++HhRg!2Ja^G`6Lo1yzmH<9M1G3ha7swE~dkou2E_g>(31jr?cJ)gZw{G z(Y%8G7e^T@-~R};SuaHXPm4^euFCP#{0~+rUH{9QTz^Ct2ZNucAAhy)&%~d6%UdUe zzu|wovC9Xf&tUglRqWj+NL-Gce| zLrZfP!OMI_sKk-{#ZTx6@H7_^S>{7Rn@m{!4MlnWtX9Xs&^ZyjKOf_H*$g{M{NV9V zFn$mF$^1^}bQzxJfIi|NWDE?-x8?k%hhh zEM_-8Uhap^HbRLeenwiBMZW+8jXjAk^IHGAPbvjqyPDZbD14V> zh`$%S(6?bJ$ICpq$au#`8b)wJqX3uVWggkoWc(#h=|*tA3^3^=f7><|oAE(^idEj$ zb9{F9O@a^peg$7nW$gN3Fn4i>^_&=8$LSnMK5|_=A3Dh$tl!334Eu-#VoN~8+<0aD Ol{*aJvH&0;s(%CETYRzr literal 0 HcmV?d00001 diff --git a/mockups/file.c b/mockups/file.c index 31afb04a..7804f6d4 100644 --- a/mockups/file.c +++ b/mockups/file.c @@ -2,70 +2,140 @@ #include #include -// // what functions +// used to prototype options enum { SUCCESS, ERROR }; +enum { + FAIL_OPEN = 1, + FAIL_READ = 2, + FAIL_WRITE = 3, + FAIL_ACCESS = 4, + NONE = 0 +}; -typedef struct FILE { +typedef struct FILE_STRUCT { // store the pointer to the file FILE *fileptr; char *file_name; char *file_flag; // r, w, rw - // tmp buffer to edit or use as a medium - char *buffer; // position in the file int position; - // Can be user defined so that a custom function can be run on file contents - // or buffer flag 0 representing file flag 1 representing buffer - void *(*fptr)(FILE *, int flag); + } File; + // file return should return a file struct or error information typedef struct FILE_RET { File *file_core; - int type; - char *return_log; + int OK_ERR; + int FAIL_ON; } File_Re; // private used to init the values of the file object -File_Re *finit(File *file, char *file_name, char *file_flag) { - File_Re *fre; +File_Re *file_init(char *file_name, char *file_flag) { + File_Re *fre = (File_Re *)malloc(sizeof(File_Re)); + if (!fre) + return NULL; + + File *file = (File *)malloc(sizeof(File)); + if (!file) { + free(fre); + return NULL; + } + // FLAGS r, w, rw, a, ab, r+, w+, - file = fre->file_core; file->file_name = file_name; file->file_flag = file_flag; file->position = 0; - file->buffer = NULL; file->fileptr = NULL; + + fre->file_core = file; + fre->OK_ERR = SUCCESS; + fre->FAIL_ON = NONE; + + return fre; } -// used to open a specific file -File *file_open(char *file_name, char *file_flag, char *buffer) { - File_Re *file; - file->file_core = finit(file, file_name, file_flag); - if (!buffer) { - file->buffer = buffer; +// used to open a specific file. Strict error handling so if a empty buffer was +// passed in we say NO +File_Re *file_open(File_Re *fre) { + if (!fre->file_core) { + if (!fre) + return NULL; + fre->OK_ERR = ERROR; + fre->FAIL_ON = FAIL_OPEN; + return fre; + } + + // always try and open file + fre->file_core->fileptr = + fopen(fre->file_core->file_name, fre->file_core->file_flag); + if (!fre->file_core->fileptr) { + fre->OK_ERR = ERROR; + fre->FAIL_ON = FAIL_OPEN; + } else { + fre->OK_ERR = SUCCESS; + fre->FAIL_ON = NONE; } - if (!file->fileptr) - file->fileptr = fopen(file_name, file_flag); - // return what it is or what it inits as - return file; + return fre; } // used to close a file object and deinit -int file_close(File *file) { - if (file->fileptr) - fclose(file->fileptr); - if (file->buffer) - free(file->buffer); - // re-use init so we can reset the values. - finit(NULL, NULL, NULL); - return SUCCESS; +File_Re *file_close(File_Re *fre) { + if (!fre) + return NULL; + + if (fre->file_core) { + if (fre->file_core->fileptr) { + fclose(fre->file_core->fileptr); + fre->file_core->fileptr = NULL; + } + free(fre->file_core); + fre->file_core = NULL; + } + fre->OK_ERR = SUCCESS; + fre->FAIL_ON = NONE; + return fre; +} + +char *fail_type(int type) { + char *to_return; + switch (type) { + case FAIL_OPEN: + to_return = "FAIL_OPEN"; + break; + case FAIL_READ: + to_return = "FAIL_READ"; + break; + case FAIL_WRITE: + to_return = "FAIL_WRITE"; + break; + case FAIL_ACCESS: + to_return = "FAIL_ACCESS"; + break; + case NONE: + to_return = "NONE"; + break; + } + return to_return; } int main(void) { - File *file = file_open(); + File_Re *fre = file_init("text.txt", "r"); + if (!fre) { + printf("Failed to init file\n"); + } + printf("after init\n"); + + fre = file_open(fre); + if (fre->OK_ERR == ERROR) + printf("Failed to open file, error: %s\n", fail_type(fre->FAIL_ON)); + else + printf("After open\n"); + + fre = file_close(fre); + printf("after close\n"); - file = file_close(); + free(fre); return 0; } diff --git a/mockups/text.txt b/mockups/text.txt new file mode 100644 index 00000000..b5fd817d --- /dev/null +++ b/mockups/text.txt @@ -0,0 +1 @@ +some stuff From 809c8cf37f4728dfb2bb427af001069ecf003ee4 Mon Sep 17 00:00:00 2001 From: NoxAether <107390370+NoxAether@users.noreply.github.com> Date: Sun, 19 Oct 2025 05:23:33 +1100 Subject: [PATCH 3/6] tried to implement impl with the master but segfaulted --- src/ast/ast.h | 884 ++++++++++----------- src/ast/ast_definistions/stmt.c | 228 +++--- src/lexer/lexer.c | 669 ++++++++-------- src/lexer/lexer.h | 235 +++--- src/parser/parser.c | 585 +++++++------- src/parser/parser.h | 53 +- src/parser/stmt.c | 1267 +++++++++++++++++-------------- tests/test.lx | 16 +- tests/tetris.lx | 2 +- 9 files changed, 2048 insertions(+), 1891 deletions(-) diff --git a/src/ast/ast.h b/src/ast/ast.h index 20eab7ee..4ab2cbd1 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -11,451 +11,467 @@ typedef struct AstNode AstNode; // Node type enumeration typedef enum { - // Preprocessor nodes - AST_PREPROCESSOR_MODULE, // Module declaration - AST_PREPROCESSOR_USE, // Use/import statement - - // Expression nodes - AST_EXPR_LITERAL, // Literal values (numbers, strings, booleans) - AST_EXPR_IDENTIFIER, // Variable/function names - AST_EXPR_BINARY, // Binary operations (+, -, *, /, etc.) - AST_EXPR_UNARY, // Unary operations (!, -, ++, --) - AST_EXPR_CALL, // Function calls - AST_EXPR_ASSIGNMENT, // Assignment expressions - AST_EXPR_TERNARY, // Conditional expressions (? :) - AST_EXPR_MEMBER, // Member access (obj.field) - AST_EXPR_INDEX, // Array/object indexing (obj[index]) - AST_EXPR_GROUPING, // Parenthesized expressions - AST_EXPR_RANGE, // range expressions '..' - AST_EXPR_ARRAY, // [ ... ] array expressions - AST_EXPR_DEREF, // *object - AST_EXPR_ADDR, // &object - AST_EXPR_ALLOC, - AST_EXPR_MEMCPY, - AST_EXPR_FREE, - AST_EXPR_CAST, - AST_EXPR_INPUT, - AST_EXPR_SIZEOF, - AST_EXPR_SYSTEM, // System Statement - - // Statement nodes - AST_PROGRAM, // Program root node - AST_STMT_EXPRESSION, // Expression statements - AST_STMT_VAR_DECL, // Variable declarations - AST_STMT_CONST_DECL, // Constant declarations - AST_STMT_FUNCTION, // Function definitions - AST_STMT_IF, // If statements - AST_STMT_LOOP, // Loop statements (while, for) - AST_STMT_BREAK_CONTINUE, // Break and continue statements - AST_STMT_RETURN, // Return statements - AST_STMT_BLOCK, // Block statements - AST_STMT_PRINT, // Print statements - AST_STMT_MODULE, // Module declarations - AST_STMT_ENUM, // Enum declarations - AST_STMT_STRUCT, // Struct declarations - AST_STMT_FIELD_DECL, // Field declarations (for structs) - AST_STMT_DEFER, // Defer statements - AST_STMT_SWITCH, // Switch statement - AST_STMT_CASE, - AST_STMT_DEFAULT, - - // Type nodes - AST_TYPE_BASIC, // Basic types (int, float, string, etc.) - AST_TYPE_POINTER, // Pointer types - AST_TYPE_ARRAY, // Array types - AST_TYPE_FUNCTION, // Function types - AST_TYPE_STRUCT, // Struct types - AST_TYPE_ENUM, // Enum types + // Preprocessor nodes + AST_PREPROCESSOR_MODULE, // Module declaration + AST_PREPROCESSOR_USE, // Use/import statement + + // Expression nodes + AST_EXPR_LITERAL, // Literal values (numbers, strings, booleans) + AST_EXPR_IDENTIFIER, // Variable/function names + AST_EXPR_BINARY, // Binary operations (+, -, *, /, etc.) + AST_EXPR_UNARY, // Unary operations (!, -, ++, --) + AST_EXPR_CALL, // Function calls + AST_EXPR_ASSIGNMENT, // Assignment expressions + AST_EXPR_TERNARY, // Conditional expressions (? :) + AST_EXPR_MEMBER, // Member access (obj.field) + AST_EXPR_INDEX, // Array/object indexing (obj[index]) + AST_EXPR_GROUPING, // Parenthesized expressions + AST_EXPR_RANGE, // range expressions '..' + AST_EXPR_ARRAY, // [ ... ] array expressions + AST_EXPR_DEREF, // *object + AST_EXPR_ADDR, // &object + AST_EXPR_ALLOC, + AST_EXPR_MEMCPY, + AST_EXPR_FREE, + AST_EXPR_CAST, + AST_EXPR_INPUT, + AST_EXPR_SIZEOF, + AST_EXPR_SYSTEM, // System Statement + + // Statement nodes + AST_PROGRAM, // Program root node + AST_STMT_EXPRESSION, // Expression statements + AST_STMT_VAR_DECL, // Variable declarations + AST_STMT_CONST_DECL, // Constant declarations + AST_STMT_FUNCTION, // Function definitions + AST_STMT_IF, // If statements + AST_STMT_LOOP, // Loop statements (while, for) + AST_STMT_BREAK_CONTINUE, // Break and continue statements + AST_STMT_RETURN, // Return statements + AST_STMT_BLOCK, // Block statements + AST_STMT_PRINT, // Print statements + AST_STMT_MODULE, // Module declarations + AST_STMT_ENUM, // Enum declarations + AST_STMT_STRUCT, // Struct declarations + AST_STMT_FIELD_DECL, // Field declarations (for structs) + AST_STMT_DEFER, // Defer statements + AST_STMT_SWITCH, // Switch statement + AST_STMT_IMPL, // impl statement + AST_STMT_CASE, + AST_STMT_DEFAULT, + + // Type nodes + AST_TYPE_BASIC, // Basic types (int, float, string, etc.) + AST_TYPE_POINTER, // Pointer types + AST_TYPE_ARRAY, // Array types + AST_TYPE_FUNCTION, // Function types + AST_TYPE_STRUCT, // Struct types + AST_TYPE_ENUM, // Enum types } NodeType; // Literal types typedef enum { - LITERAL_IDENT, - LITERAL_INT, - LITERAL_FLOAT, - LITERAL_DOUBLE, - LITERAL_STRING, - LITERAL_CHAR, - LITERAL_BOOL, - LITERAL_NULL + LITERAL_IDENT, + LITERAL_INT, + LITERAL_FLOAT, + LITERAL_DOUBLE, + LITERAL_STRING, + LITERAL_CHAR, + LITERAL_BOOL, + LITERAL_NULL } LiteralType; // Binary operators typedef enum { - BINOP_ADD, // + - BINOP_SUB, // - - BINOP_MUL, // * - BINOP_DIV, // / - BINOP_MOD, // % - BINOP_POW, // ** - BINOP_EQ, // == - BINOP_NE, // != - BINOP_LT, // < - BINOP_LE, // <= - BINOP_GT, // > - BINOP_GE, // >= - BINOP_AND, // && - BINOP_OR, // || - BINOP_BIT_AND, // & - BINOP_BIT_OR, // | - BINOP_BIT_XOR, // ^ - BINOP_SHL, // << - BINOP_SHR, // >> - BINOP_RANGE, // .. + BINOP_ADD, // + + BINOP_SUB, // - + BINOP_MUL, // * + BINOP_DIV, // / + BINOP_MOD, // % + BINOP_POW, // ** + BINOP_EQ, // == + BINOP_NE, // != + BINOP_LT, // < + BINOP_LE, // <= + BINOP_GT, // > + BINOP_GE, // >= + BINOP_AND, // && + BINOP_OR, // || + BINOP_BIT_AND, // & + BINOP_BIT_OR, // | + BINOP_BIT_XOR, // ^ + BINOP_SHL, // << + BINOP_SHR, // >> + BINOP_RANGE, // .. } BinaryOp; // Unary operators typedef enum { - UNOP_NOT, // ! - UNOP_NEG, // - - UNOP_POS, // + - UNOP_BIT_NOT, // ~ - UNOP_PRE_INC, // ++x - UNOP_PRE_DEC, // --x - UNOP_POST_INC, // x++ - UNOP_POST_DEC, // x-- - UNOP_DEREF, // *x - UNOP_ADDR, // &x + UNOP_NOT, // ! + UNOP_NEG, // - + UNOP_POS, // + + UNOP_BIT_NOT, // ~ + UNOP_PRE_INC, // ++x + UNOP_PRE_DEC, // --x + UNOP_POST_INC, // x++ + UNOP_POST_DEC, // x-- + UNOP_DEREF, // *x + UNOP_ADDR, // &x } UnaryOp; typedef enum { - Node_Category_EXPR, - Node_Category_STMT, - Node_Category_TYPE, - Node_Category_PREPROCESSOR + Node_Category_EXPR, + Node_Category_STMT, + Node_Category_TYPE, + Node_Category_PREPROCESSOR } NodeCategory; // Base AST node structure struct AstNode { - NodeType type; - size_t line; - size_t column; - NodeCategory category; // Category of the node (expression, statement, type) - - union { - struct { - union { - // Preprocessor-specific data - struct { - char *name; - int potions; - AstNode **body; - size_t body_count; - const char *file_path; - Token *tokens; - size_t token_count; - void *scope; // NEW: Add this field - } module; - - // @use "module_name" as module; - struct { - const char *module_name; - const char *alias; - } use; - }; - } preprocessor; - - struct { - // Expression-specific data - union { - // Literal expression - struct { - LiteralType lit_type; - union { - long long int_val; - double float_val; - char *string_val; - char char_val; - bool bool_val; - } value; - } literal; - - // Identifier expression - struct { - char *name; - } identifier; - - // Binary expression - struct { - BinaryOp op; - AstNode *left; // Changed from Expr* to AstNode* - AstNode *right; // Changed from Expr* to AstNode* - } binary; - - // Unary expression - struct { - UnaryOp op; - AstNode *operand; // Changed from Expr* to AstNode* - } unary; - - // Function call expression - struct { - AstNode *callee; // Changed from Expr* to AstNode* - AstNode **args; // Changed from Expr** to AstNode** - size_t arg_count; - } call; - - // Assignment expression - struct { - AstNode *target; // Changed from Expr* to AstNode* - AstNode *value; // Changed from Expr* to AstNode* - } assignment; - - // Ternary expression - struct { - AstNode *condition; // Changed from Expr* to AstNode* - AstNode *then_expr; // Changed from Expr* to AstNode* - AstNode *else_expr; // Changed from Expr* to AstNode* - } ternary; - - // Member access expression - struct { - bool is_compiletime; - AstNode *object; // Changed from Expr* to AstNode* - char *member; - } member; - - // Index expression - struct { - AstNode *object; // Changed from Expr* to AstNode* - AstNode *index; // Changed from Expr* to AstNode* - } index; - - // Grouping expression - struct { - AstNode *expr; // Changed from Expr* to AstNode* - } grouping; - - // Array expression - struct { - AstNode **elements; // Changed from Expr** to AstNode** - size_t element_count; - } array; - - // Deref expression - struct { - AstNode *object; - } deref; - - // Address experssion - struct { - AstNode *object; - } addr; - - // alloc expression - struct { - AstNode *size; - } alloc; - - // memcpy expression - struct { - AstNode *to; - AstNode *from; - AstNode *size; - } memcpy; - - // free expression - struct { - AstNode *ptr; - } free; - - // cast expression - struct { - AstNode *type; - AstNode *castee; - } cast; - - // input expression - struct { - AstNode *type; - AstNode *msg; - } input; - - // system expression - struct { - AstNode *command; - } _system; - - // sizeof expression - struct { - AstNode *object; - bool is_type; - } size_of; - }; - } expr; - - struct { - // Statement-specific data - union { - // Program root node - struct { - AstNode **modules; // Changed from Stmt** to AstNode** - size_t module_count; - } program; - - // Expression statement - struct { - AstNode *expression; // Changed from Expr* to AstNode* - } expr_stmt; - - // Variable declaration - struct { - const char *name; - AstNode *var_type; // Changed from Type* to AstNode* - AstNode *initializer; // Changed from Expr* to AstNode* - bool is_mutable; // Whether the variable is mutable - bool is_public; - } var_decl; - - // const Persion = struct { - // pub: - // name: str; - // age: int; - // priv: - // ssn: str; - // }; - // Struct declaration - struct { - const char *name; - AstNode **public_members; // Changed from Stmt** to AstNode** - size_t public_count; - AstNode **private_members; // Changed from Stmt** to AstNode** - size_t private_count; - bool is_public; // Whether the struct is public (which is true by - // default) - } struct_decl; - - struct { - const char *name; - AstNode *type; // Changed from Type* to AstNode* - AstNode *function; // Changed from Stmt* to AstNode* - bool is_public; // Whether the field is public - } field_decl; - - // Enumeration declaration - struct { - const char *name; - char **members; // Changed from char** to AstNode** - size_t member_count; - bool is_public; // same as struct, enums are public by default - } enum_decl; - - // Function declaration - struct { - const char *name; - char **param_names; - AstNode **param_types; // Changed from Type** to AstNode** - size_t param_count; - AstNode *return_type; // Changed from Type* to AstNode* - bool is_public; - AstNode *body; // Changed from Stmt* to AstNode* - bool returns_ownership; - bool takes_ownership; - } func_decl; - - // If statement - struct { - AstNode *condition; // Changed from Expr* to AstNode* - AstNode *then_stmt; // Changed from Stmt* to AstNode* - AstNode **elif_stmts; // Changed from Stmt* to AstNode* - int elif_count; // Number of elif statements - AstNode *else_stmt; // Changed from Stmt* to AstNode* - } if_stmt; - - // Loop statement (Combined while and for) - struct { - AstNode *condition; // Changed from Expr* to AstNode* - AstNode *optional; // Optional expression (e.g., for loop initializer) - AstNode *body; // Changed from Stmt* to AstNode* - // For loops can be represented as a while loop with an initializer - // and increment - AstNode **initializer; // Optional initializer for for loops (Changed - // from Stmt* to AstNode*) - size_t init_count; // Number of initializers - } loop_stmt; - - // Return statement - struct { - AstNode *value; // Changed from Expr* to AstNode* - } return_stmt; - - // Block statement - struct { - AstNode **statements; // Changed from Stmt** to AstNode** - size_t stmt_count; - } block; - - // Print statement - struct { - AstNode **expressions; // Changed from Expr** to AstNode** - size_t expr_count; - bool ln; // Whether to print with a newline - } print_stmt; - - struct { - bool is_continue; // true for continue, false for break - } break_continue; - - struct { - AstNode *statement; - } defer_stmt; - - struct { - AstNode *condition; // The switch expression - struct AstNode **cases; // Array of case clauses - size_t case_count; - struct AstNode *default_case; // Optional default case - } switch_stmt; - - // Case clause node - struct { - AstNode **values; // Array of expressions (0, 1, 2, 3, ...) - size_t value_count; // Number of values in this case - AstNode *body; // Block statement for this case - } case_clause; - - struct { - AstNode *body; // Block statement for default case - } default_clause; - }; - } stmt; - - struct { - // Type-specific data - union { - // Basic type - struct { - const char *name; - } basic; - - // Pointer type - struct { - AstNode *pointee_type; // Changed from Type* to AstNode* - } pointer; - - // Array type - struct { - AstNode *element_type; // Changed from Type* to AstNode* - AstNode *size; // Changed from Expr* to AstNode* - } array; - - // Function type - struct { - AstNode **param_types; // Changed from Type** to AstNode** - size_t param_count; - AstNode *return_type; // Changed from Type* to AstNode* - } function; - - // AST_TYPE_STRUCT - ADD THIS STRUCT - struct { - const char *name; // Name of the struct type - AstNode **member_types; // Array of member types - const char **member_names; // Array of member names - size_t member_count; // Number of members - } struct_type; - }; - } type_data; - }; + NodeType type; + size_t line; + size_t column; + NodeCategory category; // Category of the node (expression, statement, type) + + union { + struct { + union { + // Preprocessor-specific data + struct { + char *name; + int potions; + AstNode **body; + size_t body_count; + const char *file_path; + Token *tokens; + size_t token_count; + void *scope; // NEW: Add this field + } module; + + // @use "module_name" as module; + struct { + const char *module_name; + const char *alias; + } use; + }; + } preprocessor; + + struct { + // Expression-specific data + union { + // Literal expression + struct { + LiteralType lit_type; + union { + long long int_val; + double float_val; + char *string_val; + char char_val; + bool bool_val; + } value; + } literal; + + // Identifier expression + struct { + char *name; + } identifier; + + // Binary expression + struct { + BinaryOp op; + AstNode *left; // Changed from Expr* to AstNode* + AstNode *right; // Changed from Expr* to AstNode* + } binary; + + // Unary expression + struct { + UnaryOp op; + AstNode *operand; // Changed from Expr* to AstNode* + } unary; + + // Function call expression + struct { + AstNode *callee; // Changed from Expr* to AstNode* + AstNode **args; // Changed from Expr** to AstNode** + size_t arg_count; + } call; + + // Assignment expression + struct { + AstNode *target; // Changed from Expr* to AstNode* + AstNode *value; // Changed from Expr* to AstNode* + } assignment; + + // Ternary expression + struct { + AstNode *condition; // Changed from Expr* to AstNode* + AstNode *then_expr; // Changed from Expr* to AstNode* + AstNode *else_expr; // Changed from Expr* to AstNode* + } ternary; + + // Member access expression + struct { + bool is_compiletime; + AstNode *object; // Changed from Expr* to AstNode* + char *member; + } member; + + // Index expression + struct { + AstNode *object; // Changed from Expr* to AstNode* + AstNode *index; // Changed from Expr* to AstNode* + } index; + + // Grouping expression + struct { + AstNode *expr; // Changed from Expr* to AstNode* + } grouping; + + // Array expression + struct { + AstNode **elements; // Changed from Expr** to AstNode** + size_t element_count; + } array; + + // Deref expression + struct { + AstNode *object; + } deref; + + // Address experssion + struct { + AstNode *object; + } addr; + + // alloc expression + struct { + AstNode *size; + } alloc; + + // memcpy expression + struct { + AstNode *to; + AstNode *from; + AstNode *size; + } memcpy; + + // free expression + struct { + AstNode *ptr; + } free; + + // cast expression + struct { + AstNode *type; + AstNode *castee; + } cast; + + // input expression + struct { + AstNode *type; + AstNode *msg; + } input; + + // system expression + struct { + AstNode *command; + } _system; + + // sizeof expression + struct { + AstNode *object; + bool is_type; + } size_of; + }; + } expr; + + struct { + // Statement-specific data + union { + // Program root node + struct { + AstNode **modules; // Changed from Stmt** to AstNode** + size_t module_count; + } program; + + // Expression statement + struct { + AstNode *expression; // Changed from Expr* to AstNode* + } expr_stmt; + + // Variable declaration + struct { + const char *name; + AstNode *var_type; // Changed from Type* to AstNode* + AstNode *initializer; // Changed from Expr* to AstNode* + bool is_mutable; // Whether the variable is mutable + bool is_public; + } var_decl; + + // const Persion = struct { + // pub: + // name: str; + // age: int; + // priv: + // ssn: str; + // }; + // Struct declaration + struct { + const char *name; + AstNode * + *public_members; // Changed from Stmt** to AstNode** + size_t public_count; + AstNode * + *private_members; // Changed from Stmt** to AstNode** + size_t private_count; + bool is_public; // Whether the struct is public (which is + // true by default) + } struct_decl; + + struct { + const char *name; + AstNode *type; // Changed from Type* to AstNode* + AstNode *function; // Changed from Stmt* to AstNode* + bool is_public; // Whether the field is public + } field_decl; + + // Enumeration declaration + struct { + const char *name; + char **members; // Changed from char** to AstNode** + size_t member_count; + bool is_public; // same as struct, enums are public by + // default + } enum_decl; + + // Function declaration + struct { + const char *name; + char **param_names; + AstNode **param_types; // Changed from Type** to AstNode** + size_t param_count; + AstNode *return_type; // Changed from Type* to AstNode* + bool is_public; + AstNode *body; // Changed from Stmt* to AstNode* + bool returns_ownership; + bool takes_ownership; + } func_decl; + + // If statement + struct { + AstNode *condition; // Changed from Expr* to AstNode* + AstNode *then_stmt; // Changed from Stmt* to AstNode* + AstNode **elif_stmts; // Changed from Stmt* to AstNode* + int elif_count; // Number of elif statements + AstNode *else_stmt; // Changed from Stmt* to AstNode* + } if_stmt; + + // Loop statement (Combined while and for) + struct { + AstNode *condition; // Changed from Expr* to AstNode* + AstNode *optional; // Optional expression (e.g., for loop + // initializer) + AstNode *body; // Changed from Stmt* to AstNode* + // For loops can be represented as a while loop with an + // initializer and increment + AstNode **initializer; // Optional initializer for for loops + // (Changed from Stmt* to AstNode*) + size_t init_count; // Number of initializers + } loop_stmt; + + // Return statement + struct { + AstNode *value; // Changed from Expr* to AstNode* + } return_stmt; + + // Block statement + struct { + AstNode **statements; // Changed from Stmt** to AstNode** + size_t stmt_count; + } block; + + // Print statement + struct { + AstNode **expressions; // Changed from Expr** to AstNode** + size_t expr_count; + bool ln; // Whether to print with a newline + } print_stmt; + + struct { + bool is_continue; // true for continue, false for break + } break_continue; + + struct { + AstNode *statement; + } defer_stmt; + + struct { + AstNode *condition; // The switch expression + struct AstNode **cases; // Array of case clauses + size_t case_count; + struct AstNode *default_case; // Optional default case + } switch_stmt; + + // impl [fun1, fun2, ...] -> [struct1, struct2, ...] {} + struct { + char **function_name_list; + AstNode **function_type_list; + char **struct_name_list; + size_t function_name_count; + size_t struct_name_count; + + AstNode *body; + } impl_stmt; + + // Case clause node + struct { + AstNode **values; // Array of expressions (0, 1, 2, 3, ...) + size_t value_count; // Number of values in this case + AstNode *body; // Block statement for this case + } case_clause; + + struct { + AstNode *body; // Block statement for default case + } default_clause; + }; + } stmt; + + struct { + // Type-specific data + union { + // Basic type + struct { + const char *name; + } basic; + + // Pointer type + struct { + AstNode *pointee_type; // Changed from Type* to AstNode* + } pointer; + + // Array type + struct { + AstNode *element_type; // Changed from Type* to AstNode* + AstNode *size; // Changed from Expr* to AstNode* + } array; + + // Function type + struct { + AstNode **param_types; // Changed from Type** to AstNode** + size_t param_count; + AstNode *return_type; // Changed from Type* to AstNode* + } function; + + // AST_TYPE_STRUCT - ADD THIS STRUCT + struct { + const char *name; // Name of the struct type + AstNode **member_types; // Array of member types + const char **member_names; // Array of member names + size_t member_count; // Number of members + } struct_type; + }; + } type_data; + }; }; // Type aliases for cleaner code (defined AFTER the struct) @@ -476,14 +492,14 @@ AstNode *create_type_node(ArenaAllocator *arena, NodeType type, size_t line, // Helper macros for creating nodes #define create_preprocessor(arena, type, line, column) \ - create_preprocessor_node(arena, type, Node_Category_PREPROCESSOR, line, \ - column) + create_preprocessor_node(arena, type, Node_Category_PREPROCESSOR, line, \ + column) #define create_expr(arena, type, line, column) \ - create_expr_node(arena, type, line, column) + create_expr_node(arena, type, line, column) #define create_stmt(arena, type, line, column) \ - create_stmt_node(arena, type, line, column) + create_stmt_node(arena, type, line, column) #define create_type(arena, type, line, column) \ - create_type_node(arena, type, line, column) + create_type_node(arena, type, line, column) // Create the AstNode AstNode *create_ast_node(ArenaAllocator *arena, NodeType type, @@ -595,6 +611,12 @@ AstNode *create_defer_stmt(ArenaAllocator *arena, AstNode *statement, AstNode *create_switch_stmt(ArenaAllocator *arena, AstNode *condition, AstNode **cases, size_t case_count, AstNode *default_case, size_t line, size_t column); + +AstNode *create_impl_stmt(ArenaAllocator *arena, char **function_name_list, + AstNode **function_type_list, AstNode *body, + char **struct_name_list, size_t function_name_count, + size_t struct_name_count, size_t line, size_t column); + AstNode *create_case_stmt(ArenaAllocator *arena, AstNode **values, size_t value_count, AstNode *body, size_t line, size_t column); diff --git a/src/ast/ast_definistions/stmt.c b/src/ast/ast_definistions/stmt.c index fd206191..1c8cf43a 100644 --- a/src/ast/ast_definistions/stmt.c +++ b/src/ast/ast_definistions/stmt.c @@ -4,30 +4,30 @@ AstNode *create_program_node(ArenaAllocator *arena, AstNode **statements, size_t stmt_count, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_PROGRAM, line, column); - node->stmt.program.modules = statements; - node->stmt.program.module_count = stmt_count; - return node; + AstNode *node = create_stmt_node(arena, AST_PROGRAM, line, column); + node->stmt.program.modules = statements; + node->stmt.program.module_count = stmt_count; + return node; } AstNode *create_expr_stmt(ArenaAllocator *arena, Expr *expression, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_EXPRESSION, line, column); - node->stmt.expr_stmt.expression = expression; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_EXPRESSION, line, column); + node->stmt.expr_stmt.expression = expression; + return node; } AstNode *create_var_decl_stmt(ArenaAllocator *arena, const char *name, AstNode *var_type, Expr *initializer, bool is_mutable, bool is_public, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_VAR_DECL, line, column); - node->stmt.var_decl.name = name; - node->stmt.var_decl.var_type = var_type; - node->stmt.var_decl.initializer = initializer; - node->stmt.var_decl.is_mutable = is_mutable; - node->stmt.var_decl.is_public = is_public; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_VAR_DECL, line, column); + node->stmt.var_decl.name = name; + node->stmt.var_decl.var_type = var_type; + node->stmt.var_decl.initializer = initializer; + node->stmt.var_decl.is_mutable = is_mutable; + node->stmt.var_decl.is_public = is_public; + return node; } AstNode *create_func_decl_stmt(ArenaAllocator *arena, const char *name, @@ -36,17 +36,17 @@ AstNode *create_func_decl_stmt(ArenaAllocator *arena, const char *name, bool is_public, bool returns_ownership, bool takes_ownership, AstNode *body, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_FUNCTION, line, column); - node->stmt.func_decl.name = name; - node->stmt.func_decl.param_names = param_names; - node->stmt.func_decl.param_types = param_types; - node->stmt.func_decl.param_count = param_count; - node->stmt.func_decl.return_type = return_type; - node->stmt.func_decl.is_public = is_public; - node->stmt.func_decl.returns_ownership = returns_ownership; - node->stmt.func_decl.takes_ownership = takes_ownership; - node->stmt.func_decl.body = body; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_FUNCTION, line, column); + node->stmt.func_decl.name = name; + node->stmt.func_decl.param_names = param_names; + node->stmt.func_decl.param_types = param_types; + node->stmt.func_decl.param_count = param_count; + node->stmt.func_decl.return_type = return_type; + node->stmt.func_decl.is_public = is_public; + node->stmt.func_decl.returns_ownership = returns_ownership; + node->stmt.func_decl.takes_ownership = takes_ownership; + node->stmt.func_decl.body = body; + return node; } AstNode *create_struct_decl_stmt(ArenaAllocator *arena, const char *name, @@ -54,151 +54,167 @@ AstNode *create_struct_decl_stmt(ArenaAllocator *arena, const char *name, AstNode **private_members, size_t private_count, bool is_public, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_STRUCT, line, column); - node->stmt.struct_decl.name = name; - node->stmt.struct_decl.public_members = public_members; - node->stmt.struct_decl.public_count = public_count; - node->stmt.struct_decl.private_members = private_members; - node->stmt.struct_decl.private_count = private_count; - node->stmt.struct_decl.is_public = is_public; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_STRUCT, line, column); + node->stmt.struct_decl.name = name; + node->stmt.struct_decl.public_members = public_members; + node->stmt.struct_decl.public_count = public_count; + node->stmt.struct_decl.private_members = private_members; + node->stmt.struct_decl.private_count = private_count; + node->stmt.struct_decl.is_public = is_public; + return node; } AstNode *create_field_decl_stmt(ArenaAllocator *arena, const char *name, AstNode *type, AstNode *function, bool is_public, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_FIELD_DECL, line, column); - node->stmt.field_decl.name = name; - node->stmt.field_decl.type = type; - node->stmt.field_decl.function = function; - node->stmt.field_decl.is_public = is_public; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_FIELD_DECL, line, column); + node->stmt.field_decl.name = name; + node->stmt.field_decl.type = type; + node->stmt.field_decl.function = function; + node->stmt.field_decl.is_public = is_public; + return node; } AstNode *create_enum_decl_stmt(ArenaAllocator *arena, const char *name, char **members, size_t member_count, bool is_public, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_ENUM, line, column); - node->stmt.enum_decl.name = name; - node->stmt.enum_decl.members = members; - node->stmt.enum_decl.member_count = member_count; - node->stmt.enum_decl.is_public = is_public; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_ENUM, line, column); + node->stmt.enum_decl.name = name; + node->stmt.enum_decl.members = members; + node->stmt.enum_decl.member_count = member_count; + node->stmt.enum_decl.is_public = is_public; + return node; } AstNode *create_if_stmt(ArenaAllocator *arena, Expr *condition, AstNode *then_stmt, AstNode **elif_stmts, int elif_count, AstNode *else_stmt, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_IF, line, column); - node->stmt.if_stmt.condition = condition; - node->stmt.if_stmt.then_stmt = then_stmt; - node->stmt.if_stmt.elif_stmts = elif_stmts; - node->stmt.if_stmt.elif_count = elif_count; - node->stmt.if_stmt.else_stmt = else_stmt; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_IF, line, column); + node->stmt.if_stmt.condition = condition; + node->stmt.if_stmt.then_stmt = then_stmt; + node->stmt.if_stmt.elif_stmts = elif_stmts; + node->stmt.if_stmt.elif_count = elif_count; + node->stmt.if_stmt.else_stmt = else_stmt; + return node; } AstNode *create_infinite_loop_stmt(ArenaAllocator *arena, AstNode *body, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_LOOP, line, column); - node->stmt.loop_stmt.condition = NULL; - node->stmt.loop_stmt.optional = NULL; - node->stmt.loop_stmt.initializer = NULL; - node->stmt.loop_stmt.init_count = 0; - node->stmt.loop_stmt.body = body; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_LOOP, line, column); + node->stmt.loop_stmt.condition = NULL; + node->stmt.loop_stmt.optional = NULL; + node->stmt.loop_stmt.initializer = NULL; + node->stmt.loop_stmt.init_count = 0; + node->stmt.loop_stmt.body = body; + return node; } AstNode *create_for_loop_stmt(ArenaAllocator *arena, AstNode **initializers, size_t init_count, Expr *condition, Expr *optional, AstNode *body, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_LOOP, line, column); - node->stmt.loop_stmt.condition = condition; - node->stmt.loop_stmt.optional = optional; - node->stmt.loop_stmt.body = body; - node->stmt.loop_stmt.initializer = initializers; - node->stmt.loop_stmt.init_count = init_count; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_LOOP, line, column); + node->stmt.loop_stmt.condition = condition; + node->stmt.loop_stmt.optional = optional; + node->stmt.loop_stmt.body = body; + node->stmt.loop_stmt.initializer = initializers; + node->stmt.loop_stmt.init_count = init_count; + return node; } AstNode *create_loop_stmt(ArenaAllocator *arena, Expr *condition, Expr *optional, AstNode *body, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_LOOP, line, column); - node->stmt.loop_stmt.condition = condition; - node->stmt.loop_stmt.optional = optional; - node->stmt.loop_stmt.initializer = NULL; // No initializers for standard loops - node->stmt.loop_stmt.init_count = 0; - node->stmt.loop_stmt.body = body; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_LOOP, line, column); + node->stmt.loop_stmt.condition = condition; + node->stmt.loop_stmt.optional = optional; + node->stmt.loop_stmt.initializer = + NULL; // No initializers for standard loops + node->stmt.loop_stmt.init_count = 0; + node->stmt.loop_stmt.body = body; + return node; } AstNode *create_return_stmt(ArenaAllocator *arena, Expr *value, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_RETURN, line, column); - node->stmt.return_stmt.value = value; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_RETURN, line, column); + node->stmt.return_stmt.value = value; + return node; } AstNode *create_block_stmt(ArenaAllocator *arena, AstNode **statements, size_t stmt_count, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_BLOCK, line, column); - node->stmt.block.statements = statements; - node->stmt.block.stmt_count = stmt_count; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_BLOCK, line, column); + node->stmt.block.statements = statements; + node->stmt.block.stmt_count = stmt_count; + return node; } AstNode *create_print_stmt(ArenaAllocator *arena, Expr **expressions, size_t expr_count, bool ln, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_PRINT, line, column); - node->stmt.print_stmt.expressions = expressions; - node->stmt.print_stmt.expr_count = expr_count; - node->stmt.print_stmt.ln = ln; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_PRINT, line, column); + node->stmt.print_stmt.expressions = expressions; + node->stmt.print_stmt.expr_count = expr_count; + node->stmt.print_stmt.ln = ln; + return node; } AstNode *create_break_continue_stmt(ArenaAllocator *arena, bool is_continue, size_t line, size_t column) { - AstNode *node = - create_stmt_node(arena, AST_STMT_BREAK_CONTINUE, line, column); - node->stmt.break_continue.is_continue = is_continue; - return node; + AstNode *node = + create_stmt_node(arena, AST_STMT_BREAK_CONTINUE, line, column); + node->stmt.break_continue.is_continue = is_continue; + return node; } AstNode *create_defer_stmt(ArenaAllocator *arena, AstNode *statement, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_DEFER, line, column); - node->stmt.defer_stmt.statement = statement; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_DEFER, line, column); + node->stmt.defer_stmt.statement = statement; + return node; } AstNode *create_switch_stmt(ArenaAllocator *arena, AstNode *condition, AstNode **cases, size_t case_count, AstNode *default_case, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_SWITCH, line, column); - node->stmt.switch_stmt.condition = condition; - node->stmt.switch_stmt.case_count = case_count; - node->stmt.switch_stmt.cases = cases; - node->stmt.switch_stmt.default_case = default_case; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_SWITCH, line, column); + node->stmt.switch_stmt.condition = condition; + node->stmt.switch_stmt.case_count = case_count; + node->stmt.switch_stmt.cases = cases; + node->stmt.switch_stmt.default_case = default_case; + return node; +} + +AstNode *create_impl_stmt(ArenaAllocator *arena, char **function_name_list, + AstNode **function_type_list, AstNode *body, + char **struct_name_list, size_t function_name_count, + size_t struct_name_count, size_t line, + size_t column) { + AstNode *node = create_stmt_node(arena, AST_STMT_IMPL, line, column); + node->stmt.impl_stmt.function_name_list = function_name_list; + node->stmt.impl_stmt.function_type_list = function_type_list; + node->stmt.impl_stmt.struct_name_list = struct_name_list; + node->stmt.impl_stmt.function_name_count = function_name_count; + node->stmt.impl_stmt.struct_name_count = struct_name_count; + node->stmt.impl_stmt.body = body; + return node; } AstNode *create_case_stmt(ArenaAllocator *arena, AstNode **values, size_t value_count, AstNode *body, size_t line, size_t column) { - AstNode *node = create_stmt_node(arena, AST_STMT_CASE, line, column); - node->stmt.case_clause.values = values; - node->stmt.case_clause.value_count = value_count; - node->stmt.case_clause.body = body; - return node; + AstNode *node = create_stmt_node(arena, AST_STMT_CASE, line, column); + node->stmt.case_clause.values = values; + node->stmt.case_clause.value_count = value_count; + node->stmt.case_clause.body = body; + return node; } AstNode *create_default_stmt(ArenaAllocator *arena, AstNode *body, size_t line, size_t column) { - AstNode *node = create_stmt(arena, AST_STMT_DEFAULT, line, column); - node->stmt.default_clause.body = body; - return node; + AstNode *node = create_stmt(arena, AST_STMT_DEFAULT, line, column); + node->stmt.default_clause.body = body; + return node; } diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c index a1e25e5f..078255fc 100644 --- a/src/lexer/lexer.c +++ b/src/lexer/lexer.c @@ -17,14 +17,14 @@ /** @internal Macro to compare string to a key of known length */ #define STR_EQUALS_LEN(str, key, len) \ - (strncmp(str, key, len) == 0 && key[len] == '\0') + (strncmp(str, key, len) == 0 && key[len] == '\0') /** @internal Macro to check if next two chars match given pair */ #define MATCH_NEXT(lx, a, b) (peek(lx, 0) == (a) && peek(lx, 1) == (b)) /** @internal Macro to construct a token */ #define MAKE_TOKEN(type, start, lx, length, whitespace_len) \ - make_token(type, start, lx->line, lx->col - 1, length, whitespace_len) + make_token(type, start, lx->line, lx->col - 1, length, whitespace_len) /** @internal Symbol to token type mapping */ static const SymbolEntry symbols[] = { @@ -81,6 +81,7 @@ static const KeywordEntry keywords[] = { {"defer", TOK_DEFER}, {"in", TOK_IN}, {"switch", TOK_SWITCH}, + {"impl", TOK_IMPL}, {"input", TOK_INPUT}, {"system", TOK_SYSTEM}, }; @@ -110,19 +111,19 @@ static const KeywordEntry function_attributes[] = { void report_lexer_error(Lexer *lx, const char *error_type, const char *file, const char *msg, const char *line_text, int line, int col, int tk_length) { - ErrorInformation err = { - .error_type = error_type, - .file_path = file, - .message = msg, - .line = line, - .col = col, - .line_text = arena_strdup(lx->arena, line_text), - .token_length = tk_length, - .label = "Undefined Token", - .note = NULL, - .help = NULL, - }; - error_add(err); + ErrorInformation err = { + .error_type = error_type, + .file_path = file, + .message = msg, + .line = line, + .col = col, + .line_text = arena_strdup(lx->arena, line_text), + .token_length = tk_length, + .label = "Undefined Token", + .note = NULL, + .help = NULL, + }; + error_add(err); } /** @@ -133,29 +134,30 @@ void report_lexer_error(Lexer *lx, const char *error_type, const char *file, * @return Pointer to static buffer containing the line text */ const char *get_line_text_from_source(const char *source, int target_line) { - static char line_buffer[1024]; - const char *start = source; - int current_line = 1; + static char line_buffer[1024]; + const char *start = source; + int current_line = 1; - while (current_line < target_line && *start != '\0') { - if (*start == '\n') { - current_line++; + while (current_line < target_line && *start != '\0') { + if (*start == '\n') { + current_line++; + } + start++; } - start++; - } - if (current_line != target_line) { - line_buffer[0] = '\0'; - return line_buffer; - } + if (current_line != target_line) { + line_buffer[0] = '\0'; + return line_buffer; + } - int i = 0; - while (*start != '\0' && *start != '\n' && i < (int)sizeof(line_buffer) - 1) { - line_buffer[i++] = *start++; - } - line_buffer[i] = '\0'; + int i = 0; + while (*start != '\0' && *start != '\n' && + i < (int)sizeof(line_buffer) - 1) { + line_buffer[i++] = *start++; + } + line_buffer[i] = '\0'; - return line_buffer; + return line_buffer; } /** @@ -167,12 +169,12 @@ const char *get_line_text_from_source(const char *source, int target_line) { * @return LumaTokenType keyword token if found, else TOK_IDENTIFIER */ static LumaTokenType lookup_keyword(const char *str, int length) { - for (int i = 0; i < (int)(sizeof(keywords) / sizeof(*keywords)); ++i) { - if (STR_EQUALS_LEN(str, keywords[i].text, length)) { - return keywords[i].type; + for (int i = 0; i < (int)(sizeof(keywords) / sizeof(*keywords)); ++i) { + if (STR_EQUALS_LEN(str, keywords[i].text, length)) { + return keywords[i].type; + } } - } - return TOK_IDENTIFIER; + return TOK_IDENTIFIER; } /** @@ -184,14 +186,14 @@ static LumaTokenType lookup_keyword(const char *str, int length) { * @return LumaTokenType preprocessor token if found, else TOK_SYMBOL */ static LumaTokenType lookup_preprocessor(const char *str, int length) { - for (int i = 0; i < (int)(sizeof(preprocessor_directives) / - sizeof(*preprocessor_directives)); - ++i) { - if (STR_EQUALS_LEN(str, preprocessor_directives[i].text, length)) { - return preprocessor_directives[i].type; + for (int i = 0; i < (int)(sizeof(preprocessor_directives) / + sizeof(*preprocessor_directives)); + ++i) { + if (STR_EQUALS_LEN(str, preprocessor_directives[i].text, length)) { + return preprocessor_directives[i].type; + } } - } - return TOK_SYMBOL; + return TOK_SYMBOL; } /** @@ -203,12 +205,12 @@ static LumaTokenType lookup_preprocessor(const char *str, int length) { * @return LumaTokenType symbol token if found, else TOK_SYMBOL */ static LumaTokenType lookup_symbol(const char *str, int length) { - for (int i = 0; i < (int)(sizeof(symbols) / sizeof(*symbols)); ++i) { - if (STR_EQUALS_LEN(str, symbols[i].text, length)) { - return symbols[i].type; + for (int i = 0; i < (int)(sizeof(symbols) / sizeof(*symbols)); ++i) { + if (STR_EQUALS_LEN(str, symbols[i].text, length)) { + return symbols[i].type; + } } - } - return TOK_SYMBOL; + return TOK_SYMBOL; } /** @@ -219,11 +221,11 @@ static LumaTokenType lookup_symbol(const char *str, int length) { * @param arena Arena allocator to allocate tokens and strings */ void init_lexer(Lexer *lexer, const char *source, ArenaAllocator *arena) { - lexer->arena = arena; - lexer->src = source; - lexer->current = source; - lexer->line = 1; - lexer->col = 0; + lexer->arena = arena; + lexer->src = source; + lexer->current = source; + lexer->line = 1; + lexer->col = 0; } /** @@ -254,14 +256,14 @@ bool is_at_end(Lexer *lx) { return *lx->current == '\0'; } * @return The character that was advanced past */ char advance(Lexer *lx) { - char c = *lx->current++; - if (c == '\n') { - lx->line++; - lx->col = 0; - } else if (c != '\0') { - lx->col++; - } - return c; + char c = *lx->current++; + if (c == '\n') { + lx->line++; + lx->col = 0; + } else if (c != '\0') { + lx->col++; + } + return c; } /** @@ -277,7 +279,7 @@ char advance(Lexer *lx) { */ Token make_token(LumaTokenType type, const char *start, int line, int col, int length, int whitespace_len) { - return (Token){type, start, line, col, length, whitespace_len}; + return (Token){type, start, line, col, length, whitespace_len}; } /** @@ -288,19 +290,19 @@ Token make_token(LumaTokenType type, const char *start, int line, int col, * @return Number of characters skipped */ int skip_multiline_comment(Lexer *lx) { - int count = 0; - advance(lx); // skip '/' - advance(lx); // skip '*' - count += 2; - while (!is_at_end(lx) && !MATCH_NEXT(lx, '*', '/')) { - advance(lx); - count++; - } - if (!is_at_end(lx)) { - advance(lx); // skip '*' + int count = 0; advance(lx); // skip '/' - } - return count + 2; + advance(lx); // skip '*' + count += 2; + while (!is_at_end(lx) && !MATCH_NEXT(lx, '*', '/')) { + advance(lx); + count++; + } + if (!is_at_end(lx)) { + advance(lx); // skip '*' + advance(lx); // skip '/' + } + return count + 2; } /** @@ -311,37 +313,37 @@ int skip_multiline_comment(Lexer *lx) { * @return Total count of skipped characters */ int skip_whitespace(Lexer *lx) { - int whitespace_count = 0; - - while (!is_at_end(lx)) { - char c = peek(lx, 0); - - if (c == ' ' || c == '\t') { - // Count spaces and tabs as whitespace - advance(lx); - whitespace_count++; - } else if (c == '\n' || c == '\r') { - // Newline resets whitespace count - we only want leading whitespace on - // the current line - advance(lx); - whitespace_count = 0; // Reset because we're on a new line - } else if (c == '/' && peek(lx, 1) == '/') { - // Skip single-line comment - while (!is_at_end(lx) && peek(lx, 0) != '\n') { - advance(lx); - } - // Don't count comment characters as whitespace - } else if (c == '/' && peek(lx, 1) == '*') { - // Skip multiline comment - skip_multiline_comment(lx); - // Don't count comment characters as whitespace - } else { - // Not whitespace or comment, stop skipping - break; + int whitespace_count = 0; + + while (!is_at_end(lx)) { + char c = peek(lx, 0); + + if (c == ' ' || c == '\t') { + // Count spaces and tabs as whitespace + advance(lx); + whitespace_count++; + } else if (c == '\n' || c == '\r') { + // Newline resets whitespace count - we only want leading whitespace + // on the current line + advance(lx); + whitespace_count = 0; // Reset because we're on a new line + } else if (c == '/' && peek(lx, 1) == '/') { + // Skip single-line comment + while (!is_at_end(lx) && peek(lx, 0) != '\n') { + advance(lx); + } + // Don't count comment characters as whitespace + } else if (c == '/' && peek(lx, 1) == '*') { + // Skip multiline comment + skip_multiline_comment(lx); + // Don't count comment characters as whitespace + } else { + // Not whitespace or comment, stop skipping + break; + } } - } - return whitespace_count; + return whitespace_count; } /** @@ -351,254 +353,255 @@ int skip_whitespace(Lexer *lx) { * @return Next token found in the input */ Token next_token(Lexer *lx) { - int wh_count = skip_whitespace(lx); - if (is_at_end(lx)) { - return MAKE_TOKEN(TOK_EOF, lx->current, lx, 0, wh_count); - } - - const char *start = lx->current; - char c = advance(lx); - - // Preprocessor directives (starting with @) - if (c == '@') { - if (isalpha(peek(lx, 0))) { - // Read the rest of the directive - while (isalnum(peek(lx, 0)) || peek(lx, 0) == '_') { - advance(lx); - } - int len = (int)(lx->current - start); - LumaTokenType type = lookup_preprocessor(start, len); - if (type != TOK_SYMBOL) { - return MAKE_TOKEN(type, start, lx, len, wh_count); - } - // If not a known preprocessor directive, treat as error or symbol - static char error_msg[64]; - snprintf(error_msg, sizeof(error_msg), - "Unknown preprocessor directive: '%.*s'", len, start); - report_lexer_error(lx, "LexerError", "unknown_file", error_msg, - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col - len, len); - return MAKE_TOKEN(TOK_ERROR, start, lx, len, wh_count); + int wh_count = skip_whitespace(lx); + if (is_at_end(lx)) { + return MAKE_TOKEN(TOK_EOF, lx->current, lx, 0, wh_count); } - // Just @ by itself - treat as symbol - LumaTokenType single_type = lookup_symbol(start, 1); - return MAKE_TOKEN(single_type, start, lx, 1, wh_count); - } - if (c == '#') { - if (isalpha(peek(lx, 0))) { - // Read the rest of the attribute - while (isalnum(peek(lx, 0)) || peek(lx, 0) == '_') { - advance(lx); - } - int len = (int)(lx->current - start); - for (int i = 0; i < (int)(sizeof(function_attributes) / - sizeof(*function_attributes)); - ++i) { - if (STR_EQUALS_LEN(start, function_attributes[i].text, len)) { - return MAKE_TOKEN(function_attributes[i].type, start, lx, len, - wh_count); + const char *start = lx->current; + char c = advance(lx); + + // Preprocessor directives (starting with @) + if (c == '@') { + if (isalpha(peek(lx, 0))) { + // Read the rest of the directive + while (isalnum(peek(lx, 0)) || peek(lx, 0) == '_') { + advance(lx); + } + int len = (int)(lx->current - start); + LumaTokenType type = lookup_preprocessor(start, len); + if (type != TOK_SYMBOL) { + return MAKE_TOKEN(type, start, lx, len, wh_count); + } + // If not a known preprocessor directive, treat as error or symbol + static char error_msg[64]; + snprintf(error_msg, sizeof(error_msg), + "Unknown preprocessor directive: '%.*s'", len, start); + report_lexer_error(lx, "LexerError", "unknown_file", error_msg, + get_line_text_from_source(lx->src, lx->line), + lx->line, lx->col - len, len); + return MAKE_TOKEN(TOK_ERROR, start, lx, len, wh_count); } - } - // If not a known function attribute, treat as error - static char error_msg[64]; - snprintf(error_msg, sizeof(error_msg), - "Unknown function attribute: '%.*s'", len, start); - report_lexer_error(lx, "LexerError", "unknown_file", error_msg, - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col - len, len); - return MAKE_TOKEN(TOK_ERROR, start, lx, len, wh_count); + // Just @ by itself - treat as symbol + LumaTokenType single_type = lookup_symbol(start, 1); + return MAKE_TOKEN(single_type, start, lx, 1, wh_count); } - // Just # by itself - treat as symbol - LumaTokenType single_type = lookup_symbol(start, 1); - return MAKE_TOKEN(single_type, start, lx, 1, wh_count); - } - // Identifiers and keywords - if (isalpha(c) || c == '_') { - while (isalnum(peek(lx, 0)) || peek(lx, 0) == '_') { - advance(lx); - } - int len = (int)(lx->current - start); - LumaTokenType type = lookup_keyword(start, len); - return MAKE_TOKEN(type, start, lx, len, wh_count); - } - - // Numbers - if (isdigit(c)) { - // Read the integer part - while (isdigit(peek(lx, 0))) { - advance(lx); + if (c == '#') { + if (isalpha(peek(lx, 0))) { + // Read the rest of the attribute + while (isalnum(peek(lx, 0)) || peek(lx, 0) == '_') { + advance(lx); + } + int len = (int)(lx->current - start); + for (int i = 0; i < (int)(sizeof(function_attributes) / + sizeof(*function_attributes)); + ++i) { + if (STR_EQUALS_LEN(start, function_attributes[i].text, len)) { + return MAKE_TOKEN(function_attributes[i].type, start, lx, + len, wh_count); + } + } + // If not a known function attribute, treat as error + static char error_msg[64]; + snprintf(error_msg, sizeof(error_msg), + "Unknown function attribute: '%.*s'", len, start); + report_lexer_error(lx, "LexerError", "unknown_file", error_msg, + get_line_text_from_source(lx->src, lx->line), + lx->line, lx->col - len, len); + return MAKE_TOKEN(TOK_ERROR, start, lx, len, wh_count); + } + // Just # by itself - treat as symbol + LumaTokenType single_type = lookup_symbol(start, 1); + return MAKE_TOKEN(single_type, start, lx, 1, wh_count); } - // Check for decimal point - if (peek(lx, 0) == '.' && isdigit(peek(lx, 1))) { - advance(lx); // consume the '.' + // Identifiers and keywords + if (isalpha(c) || c == '_') { + while (isalnum(peek(lx, 0)) || peek(lx, 0) == '_') { + advance(lx); + } + int len = (int)(lx->current - start); + LumaTokenType type = lookup_keyword(start, len); + return MAKE_TOKEN(type, start, lx, len, wh_count); + } - // Read the fractional part - while (isdigit(peek(lx, 0))) { - advance(lx); - } + // Numbers + if (isdigit(c)) { + // Read the integer part + while (isdigit(peek(lx, 0))) { + advance(lx); + } - int len = (int)(lx->current - start); - return MAKE_TOKEN(TOK_NUM_FLOAT, start, lx, len, - wh_count); // Make sure this is TOK_FLOAT_LITERAL - } + // Check for decimal point + if (peek(lx, 0) == '.' && isdigit(peek(lx, 1))) { + advance(lx); // consume the '.' - int len = (int)(lx->current - start); - return MAKE_TOKEN(TOK_NUMBER, start, lx, len, - wh_count); // Make sure this is TOK_NUMBER - } + // Read the fractional part + while (isdigit(peek(lx, 0))) { + advance(lx); + } - if (c == '\'') { - const char *char_start = lx->current; // Start after opening quote - char actual_char = 0; // The actual character value we'll store - int char_count = 0; // How many characters we consumed + int len = (int)(lx->current - start); + return MAKE_TOKEN(TOK_NUM_FLOAT, start, lx, len, + wh_count); // Make sure this is TOK_FLOAT_LITERAL + } - if (is_at_end(lx)) { - // Unclosed character literal - report_lexer_error(lx, "LexerError", "unknown_file", - "Unclosed character literal", - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col - 1, 1); - return MAKE_TOKEN(TOK_ERROR, start, lx, 1, wh_count); + int len = (int)(lx->current - start); + return MAKE_TOKEN(TOK_NUMBER, start, lx, len, + wh_count); // Make sure this is TOK_NUMBER } - char ch = peek(lx, 0); - - // Handle escape sequences - if (ch == '\\') { - advance(lx); // consume backslash - char_count++; - - if (is_at_end(lx)) { - report_lexer_error(lx, "LexerError", "unknown_file", - "Incomplete escape sequence in character literal", - get_line_text_from_source(lx->src, lx->line), - lx->line, lx->col - 2, 2); - return MAKE_TOKEN(TOK_ERROR, start, lx, 2, wh_count); - } - - char escaped = peek(lx, 0); - - // Convert escape sequence to actual character value - switch (escaped) { - case 'n': - actual_char = '\n'; - break; - case 't': - actual_char = '\t'; - break; - case 'r': - actual_char = '\r'; - break; - case '\\': - actual_char = '\\'; - break; - case '\'': - actual_char = '\''; - break; - case '\"': - actual_char = '\"'; - break; - case '0': - actual_char = '\0'; - break; - default: { - static char error_msg[64]; - snprintf(error_msg, sizeof(error_msg), - "Invalid escape sequence '\\%c' in character literal", - escaped); - report_lexer_error(lx, "LexerError", "unknown_file", error_msg, - get_line_text_from_source(lx->src, lx->line), - lx->line, lx->col - 2, 3); - return MAKE_TOKEN(TOK_ERROR, start, lx, 3, wh_count); - } - } - - advance(lx); // consume escaped character - char_count++; - - } else if (ch == '\'') { - // Empty character literal - report_lexer_error(lx, "LexerError", "unknown_file", - "Empty character literal", - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col - 1, 2); - advance(lx); // consume closing quote - return MAKE_TOKEN(TOK_ERROR, start, lx, 2, wh_count); - - } else if (ch == '\n' || ch == '\r') { - // Newline in character literal - report_lexer_error(lx, "LexerError", "unknown_file", - "Newline in character literal", - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col - 1, 1); - return MAKE_TOKEN(TOK_ERROR, start, lx, 1, wh_count); - - } else { - // Regular character - actual_char = ch; - advance(lx); // consume the character - char_count = 1; - } + if (c == '\'') { + const char *char_start = lx->current; // Start after opening quote + char actual_char = 0; // The actual character value we'll store + int char_count = 0; // How many characters we consumed + + if (is_at_end(lx)) { + // Unclosed character literal + report_lexer_error(lx, "LexerError", "unknown_file", + "Unclosed character literal", + get_line_text_from_source(lx->src, lx->line), + lx->line, lx->col - 1, 1); + return MAKE_TOKEN(TOK_ERROR, start, lx, 1, wh_count); + } - // Check for closing quote - if (is_at_end(lx) || peek(lx, 0) != '\'') { - report_lexer_error(lx, "LexerError", "unknown_file", - "Unclosed character literal", - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col - 1, (int)(lx->current - start)); - return MAKE_TOKEN(TOK_ERROR, start, lx, (int)(lx->current - start), - wh_count); - } + char ch = peek(lx, 0); + + // Handle escape sequences + if (ch == '\\') { + advance(lx); // consume backslash + char_count++; + + if (is_at_end(lx)) { + report_lexer_error( + lx, "LexerError", "unknown_file", + "Incomplete escape sequence in character literal", + get_line_text_from_source(lx->src, lx->line), lx->line, + lx->col - 2, 2); + return MAKE_TOKEN(TOK_ERROR, start, lx, 2, wh_count); + } + + char escaped = peek(lx, 0); + + // Convert escape sequence to actual character value + switch (escaped) { + case 'n': + actual_char = '\n'; + break; + case 't': + actual_char = '\t'; + break; + case 'r': + actual_char = '\r'; + break; + case '\\': + actual_char = '\\'; + break; + case '\'': + actual_char = '\''; + break; + case '\"': + actual_char = '\"'; + break; + case '0': + actual_char = '\0'; + break; + default: { + static char error_msg[64]; + snprintf(error_msg, sizeof(error_msg), + "Invalid escape sequence '\\%c' in character literal", + escaped); + report_lexer_error(lx, "LexerError", "unknown_file", error_msg, + get_line_text_from_source(lx->src, lx->line), + lx->line, lx->col - 2, 3); + return MAKE_TOKEN(TOK_ERROR, start, lx, 3, wh_count); + } + } + + advance(lx); // consume escaped character + char_count++; + + } else if (ch == '\'') { + // Empty character literal + report_lexer_error(lx, "LexerError", "unknown_file", + "Empty character literal", + get_line_text_from_source(lx->src, lx->line), + lx->line, lx->col - 1, 2); + advance(lx); // consume closing quote + return MAKE_TOKEN(TOK_ERROR, start, lx, 2, wh_count); + + } else if (ch == '\n' || ch == '\r') { + // Newline in character literal + report_lexer_error(lx, "LexerError", "unknown_file", + "Newline in character literal", + get_line_text_from_source(lx->src, lx->line), + lx->line, lx->col - 1, 1); + return MAKE_TOKEN(TOK_ERROR, start, lx, 1, wh_count); + + } else { + // Regular character + actual_char = ch; + advance(lx); // consume the character + char_count = 1; + } - advance(lx); // consume closing quote - int total_len = (int)(lx->current - start); + // Check for closing quote + if (is_at_end(lx) || peek(lx, 0) != '\'') { + report_lexer_error( + lx, "LexerError", "unknown_file", "Unclosed character literal", + get_line_text_from_source(lx->src, lx->line), lx->line, + lx->col - 1, (int)(lx->current - start)); + return MAKE_TOKEN(TOK_ERROR, start, lx, (int)(lx->current - start), + wh_count); + } - // CRITICAL: Store the actual character value, not the escape sequence - // We'll create a single-character string with the resolved value - char *char_storage = (char *)arena_alloc(lx->arena, 2, 1); - char_storage[0] = actual_char; - char_storage[1] = '\0'; + advance(lx); // consume closing quote + int total_len = (int)(lx->current - start); - return make_token(TOK_CHAR_LITERAL, char_storage, lx->line, - lx->col - total_len, 1, - wh_count); // length = 1 (single char) - } + // CRITICAL: Store the actual character value, not the escape sequence + // We'll create a single-character string with the resolved value + char *char_storage = (char *)arena_alloc(lx->arena, 2, 1); + char_storage[0] = actual_char; + char_storage[1] = '\0'; - // Strings - if (c == '"') { - while (!is_at_end(lx) && peek(lx, 0) != '"') { - advance(lx); + return make_token(TOK_CHAR_LITERAL, char_storage, lx->line, + lx->col - total_len, 1, + wh_count); // length = 1 (single char) } - if (!is_at_end(lx)) { - advance(lx); // Skip closing quote + + // Strings + if (c == '"') { + while (!is_at_end(lx) && peek(lx, 0) != '"') { + advance(lx); + } + if (!is_at_end(lx)) { + advance(lx); // Skip closing quote + } + int len = (int)(lx->current - start - 2); + return MAKE_TOKEN(TOK_STRING, start + 1, lx, len, wh_count); } - int len = (int)(lx->current - start - 2); - return MAKE_TOKEN(TOK_STRING, start + 1, lx, len, wh_count); - } - - // Try to match two-character symbol - if (!is_at_end(lx)) { - char two[3] = {start[0], peek(lx, 0), '\0'}; - LumaTokenType ttype = lookup_symbol(two, 2); - if (ttype != TOK_SYMBOL) { - advance(lx); - return MAKE_TOKEN(ttype, start, lx, 2, wh_count); + + // Try to match two-character symbol + if (!is_at_end(lx)) { + char two[3] = {start[0], peek(lx, 0), '\0'}; + LumaTokenType ttype = lookup_symbol(two, 2); + if (ttype != TOK_SYMBOL) { + advance(lx); + return MAKE_TOKEN(ttype, start, lx, 2, wh_count); + } } - } - - // Single-character symbol fallback - LumaTokenType single_type = lookup_symbol(start, 1); - if (single_type != TOK_SYMBOL) - return MAKE_TOKEN(single_type, start, lx, 1, wh_count); - - // Error token if none matched - static char error_msg[64]; - snprintf(error_msg, sizeof(error_msg), "Token not found: '%c'", c); - report_lexer_error(lx, "LexerError", "unknown_file", error_msg, - get_line_text_from_source(lx->src, lx->line), lx->line, - lx->col, 1); - return MAKE_TOKEN(TOK_ERROR, start, lx, 1, wh_count); + + // Single-character symbol fallback + LumaTokenType single_type = lookup_symbol(start, 1); + if (single_type != TOK_SYMBOL) + return MAKE_TOKEN(single_type, start, lx, 1, wh_count); + + // Error token if none matched + static char error_msg[64]; + snprintf(error_msg, sizeof(error_msg), "Token not found: '%c'", c); + report_lexer_error(lx, "LexerError", "unknown_file", error_msg, + get_line_text_from_source(lx->src, lx->line), lx->line, + lx->col, 1); + return MAKE_TOKEN(TOK_ERROR, start, lx, 1, wh_count); } diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h index eea4f578..d14af9f7 100644 --- a/src/lexer/lexer.h +++ b/src/lexer/lexer.h @@ -17,108 +17,109 @@ * @brief Enumeration of all possible token types recognized by the lexer. */ typedef enum { - TOK_EOF, /**< End of file/input */ - TOK_ERROR, /**< Error token */ - TOK_IDENTIFIER, /**< Identifier (variable/function names) */ - TOK_KEYWORD, /**< Reserved keyword */ - TOK_NUMBER, /**< Numeric literal */ - TOK_NUM_FLOAT, /**< Floating point numeric literal */ - TOK_STRING, /**< String literal */ - TOK_CHAR_LITERAL, /**< Character literal */ - - // Primitive Types - TOK_INT, /**< int */ - TOK_DOUBLE, /**< double */ - TOK_UINT, /**< unsigned int */ - TOK_FLOAT, /**< float */ - TOK_BOOL, /**< bool */ - TOK_STRINGT, /**< str (string type) */ - TOK_VOID, /**< void */ - TOK_CHAR, /**< char */ - - // Keywords - TOK_IF, /**< if keyword */ - TOK_ELIF, /**< elif keyword */ - TOK_ELSE, /**< else keyword */ - TOK_LOOP, /**< loop keyword */ - TOK_RETURN, /**< return keyword */ - TOK_BREAK, /**< break keyword */ - TOK_CONTINUE, /**< continue keyword */ - TOK_STRUCT, /**< struct keyword */ - TOK_ENUM, /**< enum keyword */ - TOK_MOD, /**< mod keyword */ - TOK_IMPORT, /**< import keyword */ - TOK_TRUE, /**< true keyword */ - TOK_FALSE, /**< false keyword */ - TOK_PUBLIC, /**< pub keyword */ - TOK_PRIVATE, /**< private keyword */ - TOK_VAR, /**< let keyword */ - TOK_CONST, /**< const keyword */ - TOK_FN, /**< fn keyword */ - TOK_PRINT, /**< output keyword */ - TOK_PRINTLN, /**< println keyword */ - TOK_INPUT, /**< input keyword */ - TOK_ALLOC, /**< alloc(size_t size) */ - TOK_FREE, /**< free(void *ptr, size_t size) */ - TOK_CAST, /**< cast(value you want to cast too) */ - TOK_SIZE_OF, /**< size_of */ - TOK_AS, /**< as keyword (for use in modules) */ - TOK_DEFER, /**< defer keyword */ - TOK_IN, /**< in keyword */ - TOK_SWITCH, /**< switch keyword */ - TOK_SYSTEM, /**< system keyword */ - - // prepocessor directives - TOK_MODULE, /**< @module */ - TOK_USE, /**< @use */ - - // function attibutes - TOK_RETURNES_OWNERSHIP, /** #returns_ownership */ - TOK_TAKES_OWNERSHIP, /** #takes_ownership */ - - // Symbols - TOK_SYMBOL, /**< Fallback symbol */ - TOK_LPAREN, /**< ( */ - TOK_RPAREN, /**< ) */ - TOK_LBRACE, /**< { */ - TOK_RBRACE, /**< } */ - TOK_LBRACKET, /**< [ */ - TOK_RBRACKET, /**< ] */ - TOK_SEMICOLON, /**< ; */ - TOK_COMMA, /**< , */ - TOK_DOT, /**< . */ - TOK_AT, /**< @ */ - TOK_EQUAL, /**< = */ - TOK_PLUS, /**< + */ - TOK_MINUS, /**< - */ - TOK_STAR, /**< * */ - TOK_SLASH, /**< / */ - TOK_LT, /**< < */ - TOK_GT, /**< > */ - TOK_LE, /**< <= */ - TOK_GE, /**< >= */ - TOK_EQEQ, /**< == */ - TOK_NEQ, /**< != */ - TOK_AMP, /**< & */ - TOK_PIPE, /**< | */ - TOK_CARET, /**< ^ */ - TOK_TILDE, /**< ~ */ - TOK_AND, /**< && */ - TOK_OR, /**< || */ - TOK_RESOLVE, /**< :: */ - TOK_COLON, /**< : */ - TOK_BANG, /**< ! */ - TOK_QUESTION, /**< ? */ - TOK_PLUSPLUS, /**< ++ */ - TOK_MINUSMINUS, /**< -- */ - TOK_SHIFT_LEFT, /**< << */ - TOK_SHIFT_RIGHT, /**< >> */ - TOK_RANGE, /**< .. */ - TOK_RIGHT_ARROW, /**< -> */ - TOK_LEFT_ARROW, /**< <- */ - TOK_MODL, /**< % */ - TOK_WHITESPACE, /**< whitespace */ - TOK_COMMENT /**< comment */ + TOK_EOF, /**< End of file/input */ + TOK_ERROR, /**< Error token */ + TOK_IDENTIFIER, /**< Identifier (variable/function names) */ + TOK_KEYWORD, /**< Reserved keyword */ + TOK_NUMBER, /**< Numeric literal */ + TOK_NUM_FLOAT, /**< Floating point numeric literal */ + TOK_STRING, /**< String literal */ + TOK_CHAR_LITERAL, /**< Character literal */ + + // Primitive Types + TOK_INT, /**< int */ + TOK_DOUBLE, /**< double */ + TOK_UINT, /**< unsigned int */ + TOK_FLOAT, /**< float */ + TOK_BOOL, /**< bool */ + TOK_STRINGT, /**< str (string type) */ + TOK_VOID, /**< void */ + TOK_CHAR, /**< char */ + + // Keywords + TOK_IF, /**< if keyword */ + TOK_ELIF, /**< elif keyword */ + TOK_ELSE, /**< else keyword */ + TOK_LOOP, /**< loop keyword */ + TOK_RETURN, /**< return keyword */ + TOK_BREAK, /**< break keyword */ + TOK_CONTINUE, /**< continue keyword */ + TOK_STRUCT, /**< struct keyword */ + TOK_ENUM, /**< enum keyword */ + TOK_MOD, /**< mod keyword */ + TOK_IMPORT, /**< import keyword */ + TOK_TRUE, /**< true keyword */ + TOK_FALSE, /**< false keyword */ + TOK_PUBLIC, /**< pub keyword */ + TOK_PRIVATE, /**< private keyword */ + TOK_VAR, /**< let keyword */ + TOK_CONST, /**< const keyword */ + TOK_FN, /**< fn keyword */ + TOK_PRINT, /**< output keyword */ + TOK_PRINTLN, /**< println keyword */ + TOK_INPUT, /**< input keyword */ + TOK_ALLOC, /**< alloc(size_t size) */ + TOK_FREE, /**< free(void *ptr, size_t size) */ + TOK_CAST, /**< cast(value you want to cast too) */ + TOK_SIZE_OF, /**< size_of */ + TOK_AS, /**< as keyword (for use in modules) */ + TOK_DEFER, /**< defer keyword */ + TOK_IN, /**< in keyword */ + TOK_SWITCH, /**< switch keyword */ + TOK_SYSTEM, /**< system keyword */ + TOK_IMPL, /**< implement keyword */ + + // prepocessor directives + TOK_MODULE, /**< @module */ + TOK_USE, /**< @use */ + + // function attibutes + TOK_RETURNES_OWNERSHIP, /** #returns_ownership */ + TOK_TAKES_OWNERSHIP, /** #takes_ownership */ + + // Symbols + TOK_SYMBOL, /**< Fallback symbol */ + TOK_LPAREN, /**< ( */ + TOK_RPAREN, /**< ) */ + TOK_LBRACE, /**< { */ + TOK_RBRACE, /**< } */ + TOK_LBRACKET, /**< [ */ + TOK_RBRACKET, /**< ] */ + TOK_SEMICOLON, /**< ; */ + TOK_COMMA, /**< , */ + TOK_DOT, /**< . */ + TOK_AT, /**< @ */ + TOK_EQUAL, /**< = */ + TOK_PLUS, /**< + */ + TOK_MINUS, /**< - */ + TOK_STAR, /**< * */ + TOK_SLASH, /**< / */ + TOK_LT, /**< < */ + TOK_GT, /**< > */ + TOK_LE, /**< <= */ + TOK_GE, /**< >= */ + TOK_EQEQ, /**< == */ + TOK_NEQ, /**< != */ + TOK_AMP, /**< & */ + TOK_PIPE, /**< | */ + TOK_CARET, /**< ^ */ + TOK_TILDE, /**< ~ */ + TOK_AND, /**< && */ + TOK_OR, /**< || */ + TOK_RESOLVE, /**< :: */ + TOK_COLON, /**< : */ + TOK_BANG, /**< ! */ + TOK_QUESTION, /**< ? */ + TOK_PLUSPLUS, /**< ++ */ + TOK_MINUSMINUS, /**< -- */ + TOK_SHIFT_LEFT, /**< << */ + TOK_SHIFT_RIGHT, /**< >> */ + TOK_RANGE, /**< .. */ + TOK_RIGHT_ARROW, /**< -> */ + TOK_LEFT_ARROW, /**< <- */ + TOK_MODL, /**< % */ + TOK_WHITESPACE, /**< whitespace */ + TOK_COMMENT /**< comment */ } LumaTokenType; /** @@ -126,11 +127,11 @@ typedef enum { * @brief Lexer state object for scanning source code. */ typedef struct { - ArenaAllocator *arena; /**< Arena allocator for token storage */ - const char *src; /**< Pointer to the source code string */ - const char *current; /**< Current scanning position in source */ - int line; /**< Current line number */ - int col; /**< Current column number */ + ArenaAllocator *arena; /**< Arena allocator for token storage */ + const char *src; /**< Pointer to the source code string */ + const char *current; /**< Current scanning position in source */ + int line; /**< Current line number */ + int col; /**< Current column number */ } Lexer; /** @@ -138,12 +139,12 @@ typedef struct { * @brief Represents a single token extracted by the lexer. */ typedef struct { - LumaTokenType type_; /**< Token type */ - const char *value; /**< Pointer to token text start */ - int line; /**< Line number of token */ - int col; /**< Column number of token */ - int length; /**< Length of the token text */ - int whitespace_len; /**< Leading whitespace length before token */ + LumaTokenType type_; /**< Token type */ + const char *value; /**< Pointer to token text start */ + int line; /**< Line number of token */ + int col; /**< Column number of token */ + int length; /**< Length of the token text */ + int whitespace_len; /**< Leading whitespace length before token */ } Token; /** @@ -151,8 +152,8 @@ typedef struct { * @brief Maps symbol text to token type for quick lookup. */ typedef struct { - const char *text; /**< Symbol text */ - LumaTokenType type; /**< Corresponding token type */ + const char *text; /**< Symbol text */ + LumaTokenType type; /**< Corresponding token type */ } SymbolEntry; /** @@ -160,8 +161,8 @@ typedef struct { * @brief Maps keyword text to token type for quick lookup. */ typedef struct { - const char *text; /**< Keyword text */ - LumaTokenType type; /**< Corresponding token type */ + const char *text; /**< Keyword text */ + LumaTokenType type; /**< Corresponding token type */ } KeywordEntry; /** diff --git a/src/parser/parser.c b/src/parser/parser.c index 6bf074eb..9ec7a5ab 100644 --- a/src/parser/parser.c +++ b/src/parser/parser.c @@ -48,20 +48,21 @@ */ void parser_error(Parser *psr, const char *error_type, const char *file, const char *msg, int line, int col, int tk_length) { - (void)file; - ErrorInformation err = { - .error_type = error_type, - .file_path = psr->file_path, - .message = msg, - .line = line, - .col = col, - .line_text = generate_line(psr->arena, psr->tks, psr->tk_count, err.line), - .token_length = tk_length, - .label = "Parser Error", - .note = NULL, - .help = NULL, - }; - error_add(err); + (void)file; + ErrorInformation err = { + .error_type = error_type, + .file_path = psr->file_path, + .message = msg, + .line = line, + .col = col, + .line_text = + generate_line(psr->arena, psr->tks, psr->tk_count, err.line), + .token_length = tk_length, + .label = "Parser Error", + .note = NULL, + .help = NULL, + }; + error_add(err); } /** @@ -85,69 +86,69 @@ void parser_error(Parser *psr, const char *error_type, const char *file, */ Stmt *parse(GrowableArray *tks, ArenaAllocator *arena, BuildConfig *config) { - // Initialize parser - Parser parser = { - .file_path = config->filepath, - .arena = arena, - .tks = (Token *)tks->data, - .tk_count = tks->count, - .capacity = (tks->count / 4) + 10, - .pos = 0, - }; - - if (!parser.tks) { - fprintf(stderr, "Failed to get tokens from GrowableArray\n"); - return NULL; - } - - // Initialize arrays - GrowableArray stmts, modules; - if (!init_parser_arrays(&parser, &stmts, &modules)) { - return NULL; - } - - // Parse module declaration - Token module_tok = p_current(&parser); - const char *module_name = parse_module_declaration(&parser); - if (!module_name) { - return NULL; - } - - // Create initial module node and add to modules array - Stmt *module_stmt = create_module_node(parser.arena, module_name, 0, NULL, 0, - module_tok.line, module_tok.col); - - Stmt **module_slot = (Stmt **)growable_array_push(&modules); - if (!module_slot) { - fprintf(stderr, "Out of memory while growing modules array\n"); - return NULL; - } - *module_slot = module_stmt; - - // Parse all statements - while (p_current(&parser).type_ != TOK_EOF) { - Stmt *stmt = parse_stmt(&parser); - if (!stmt) { - // Error already reported in parse_stmt - return NULL; + // Initialize parser + Parser parser = { + .file_path = config->filepath, + .arena = arena, + .tks = (Token *)tks->data, + .tk_count = tks->count, + .capacity = (tks->count / 4) + 10, + .pos = 0, + }; + + if (!parser.tks) { + fprintf(stderr, "Failed to get tokens from GrowableArray\n"); + return NULL; } - Stmt **slot = (Stmt **)growable_array_push(&stmts); - if (!slot) { - fprintf(stderr, "Out of memory while growing statements array\n"); - return NULL; + // Initialize arrays + GrowableArray stmts, modules; + if (!init_parser_arrays(&parser, &stmts, &modules)) { + return NULL; } - *slot = stmt; - } - // Update module with parsed statements - *module_slot = - create_module_node(parser.arena, module_name, 0, (Stmt **)stmts.data, - stmts.count, module_tok.line, module_tok.col); + // Parse module declaration + Token module_tok = p_current(&parser); + const char *module_name = parse_module_declaration(&parser); + if (!module_name) { + return NULL; + } + + // Create initial module node and add to modules array + Stmt *module_stmt = create_module_node(parser.arena, module_name, 0, NULL, + 0, module_tok.line, module_tok.col); + + Stmt **module_slot = (Stmt **)growable_array_push(&modules); + if (!module_slot) { + fprintf(stderr, "Out of memory while growing modules array\n"); + return NULL; + } + *module_slot = module_stmt; + + // Parse all statements + while (p_current(&parser).type_ != TOK_EOF) { + Stmt *stmt = parse_stmt(&parser); + if (!stmt) { + // Error already reported in parse_stmt + return NULL; + } + + Stmt **slot = (Stmt **)growable_array_push(&stmts); + if (!slot) { + fprintf(stderr, "Out of memory while growing statements array\n"); + return NULL; + } + *slot = stmt; + } + + // Update module with parsed statements + *module_slot = + create_module_node(parser.arena, module_name, 0, (Stmt **)stmts.data, + stmts.count, module_tok.line, module_tok.col); - // Create and return program node - return create_program_node(parser.arena, (AstNode **)modules.data, - modules.count, 0, 0); + // Create and return program node + return create_program_node(parser.arena, (AstNode **)modules.data, + modules.count, 0, 0); } /** * @brief Gets the binding power (precedence) for a given token type @@ -178,68 +179,68 @@ Stmt *parse(GrowableArray *tks, ArenaAllocator *arena, BuildConfig *config) { * @see BindingPower, LumaTokenType */ BindingPower get_bp(LumaTokenType kind) { - switch (kind) { - // Assignment - case TOK_EQUAL: - return BP_ASSIGN; - - // Ternary - case TOK_QUESTION: - return BP_TERNARY; - - // Logical - case TOK_OR: - return BP_LOGICAL_OR; - case TOK_AND: - return BP_LOGICAL_AND; - - // Bitwise - case TOK_PIPE: - return BP_BITWISE_OR; - case TOK_CARET: - return BP_BITWISE_XOR; - case TOK_AMP: - return BP_BITWISE_AND; - - // Equality - case TOK_EQEQ: - case TOK_NEQ: - return BP_EQUALITY; - - // Relational - case TOK_LT: - case TOK_LE: - case TOK_GT: - case TOK_GE: - return BP_RELATIONAL; - - // Arithmetic - case TOK_PLUS: - case TOK_MINUS: - return BP_SUM; - case TOK_STAR: - case TOK_SLASH: - case TOK_MODL: - return BP_PRODUCT; - - // Postfix - case TOK_PLUSPLUS: - case TOK_MINUSMINUS: - return BP_POSTFIX; - - // Call/indexing/member access - case TOK_LPAREN: - case TOK_LBRACKET: - case TOK_DOT: - case TOK_RESOLVE: - return BP_CALL; - - case TOK_RANGE: - return BP_RANGE; - - default: - return BP_NONE; - } + switch (kind) { + // Assignment + case TOK_EQUAL: + return BP_ASSIGN; + + // Ternary + case TOK_QUESTION: + return BP_TERNARY; + + // Logical + case TOK_OR: + return BP_LOGICAL_OR; + case TOK_AND: + return BP_LOGICAL_AND; + + // Bitwise + case TOK_PIPE: + return BP_BITWISE_OR; + case TOK_CARET: + return BP_BITWISE_XOR; + case TOK_AMP: + return BP_BITWISE_AND; + + // Equality + case TOK_EQEQ: + case TOK_NEQ: + return BP_EQUALITY; + + // Relational + case TOK_LT: + case TOK_LE: + case TOK_GT: + case TOK_GE: + return BP_RELATIONAL; + + // Arithmetic + case TOK_PLUS: + case TOK_MINUS: + return BP_SUM; + case TOK_STAR: + case TOK_SLASH: + case TOK_MODL: + return BP_PRODUCT; + + // Postfix + case TOK_PLUSPLUS: + case TOK_MINUSMINUS: + return BP_POSTFIX; + + // Call/indexing/member access + case TOK_LPAREN: + case TOK_LBRACKET: + case TOK_DOT: + case TOK_RESOLVE: + return BP_CALL; + + case TOK_RANGE: + return BP_RANGE; + + default: + return BP_NONE; + } } /** @@ -262,45 +263,45 @@ BindingPower get_bp(LumaTokenType kind) { * @see led(), parse_expr(), primary(), unary(), grouping(), array_expr() */ Expr *nud(Parser *parser) { - switch (p_current(parser).type_) { - case TOK_NUMBER: - case TOK_NUM_FLOAT: - case TOK_STRING: - case TOK_CHAR_LITERAL: - case TOK_IDENTIFIER: - return primary(parser); - case TOK_MINUS: - case TOK_PLUS: - case TOK_BANG: - case TOK_PLUSPLUS: - case TOK_MINUSMINUS: - return unary(parser); - case TOK_LPAREN: - return grouping(parser); - case TOK_LBRACKET: - return array_expr(parser); - case TOK_STAR: - return deref_expr(parser); - case TOK_AMP: - return addr_expr(parser); - case TOK_ALLOC: - return alloc_expr(parser); - case TOK_FREE: - return free_expr(parser); - case TOK_CAST: - return cast_expr(parser); - case TOK_INPUT: - return input_expr(parser); - case TOK_SYSTEM: - return system_expr(parser); - - // Compile time - case TOK_SIZE_OF: - return sizeof_expr(parser); - default: - p_advance(parser); - return NULL; - } + switch (p_current(parser).type_) { + case TOK_NUMBER: + case TOK_NUM_FLOAT: + case TOK_STRING: + case TOK_CHAR_LITERAL: + case TOK_IDENTIFIER: + return primary(parser); + case TOK_MINUS: + case TOK_PLUS: + case TOK_BANG: + case TOK_PLUSPLUS: + case TOK_MINUSMINUS: + return unary(parser); + case TOK_LPAREN: + return grouping(parser); + case TOK_LBRACKET: + return array_expr(parser); + case TOK_STAR: + return deref_expr(parser); + case TOK_AMP: + return addr_expr(parser); + case TOK_ALLOC: + return alloc_expr(parser); + case TOK_FREE: + return free_expr(parser); + case TOK_CAST: + return cast_expr(parser); + case TOK_INPUT: + return input_expr(parser); + case TOK_SYSTEM: + return system_expr(parser); + + // Compile time + case TOK_SIZE_OF: + return sizeof_expr(parser); + default: + p_advance(parser); + return NULL; + } } /** @@ -328,40 +329,40 @@ Expr *nud(Parser *parser) { * @see nud(), parse_expr(), binary(), call_expr(), assign_expr(), prefix_expr() */ Expr *led(Parser *parser, Expr *left, BindingPower bp) { - switch (p_current(parser).type_) { - case TOK_PLUS: - case TOK_MINUS: - case TOK_STAR: - case TOK_SLASH: - case TOK_MODL: - case TOK_EQEQ: - case TOK_NEQ: - case TOK_LT: - case TOK_LE: - case TOK_GT: - case TOK_GE: - case TOK_AMP: - case TOK_PIPE: - case TOK_CARET: - case TOK_AND: // Add logical AND - case TOK_OR: // Add logical OR - case TOK_RANGE: - return binary(parser, left, bp); - case TOK_LPAREN: - return call_expr(parser, left, bp); - case TOK_EQUAL: - return assign_expr(parser, left, bp); - case TOK_DOT: - case TOK_RESOLVE: - case TOK_PLUSPLUS: - case TOK_MINUSMINUS: - case TOK_LBRACKET: - // Handle member access, postfix increment/decrement, and indexing - return prefix_expr(parser, left, bp); - default: - p_advance(parser); - return left; // No valid LED found, return left expression - } + switch (p_current(parser).type_) { + case TOK_PLUS: + case TOK_MINUS: + case TOK_STAR: + case TOK_SLASH: + case TOK_MODL: + case TOK_EQEQ: + case TOK_NEQ: + case TOK_LT: + case TOK_LE: + case TOK_GT: + case TOK_GE: + case TOK_AMP: + case TOK_PIPE: + case TOK_CARET: + case TOK_AND: // Add logical AND + case TOK_OR: // Add logical OR + case TOK_RANGE: + return binary(parser, left, bp); + case TOK_LPAREN: + return call_expr(parser, left, bp); + case TOK_EQUAL: + return assign_expr(parser, left, bp); + case TOK_DOT: + case TOK_RESOLVE: + case TOK_PLUSPLUS: + case TOK_MINUSMINUS: + case TOK_LBRACKET: + // Handle member access, postfix increment/decrement, and indexing + return prefix_expr(parser, left, bp); + default: + p_advance(parser); + return left; // No valid LED found, return left expression + } } /** @@ -386,13 +387,13 @@ Expr *led(Parser *parser, Expr *left, BindingPower bp) { * @see nud(), led(), get_bp(), BindingPower */ Expr *parse_expr(Parser *parser, BindingPower bp) { - Expr *left = nud(parser); + Expr *left = nud(parser); - while (p_has_tokens(parser) && get_bp(p_current(parser).type_) > bp) { - left = led(parser, left, get_bp(p_current(parser).type_)); - } + while (p_has_tokens(parser) && get_bp(p_current(parser).type_) > bp) { + left = led(parser, left, get_bp(p_current(parser).type_)); + } - return left; + return left; } /** @@ -418,61 +419,65 @@ Expr *parse_expr(Parser *parser, BindingPower bp) { * loop_stmt(), print_stmt(), break_continue_stmt(), expr_stmt() */ Stmt *parse_stmt(Parser *parser) { - bool returns_ownership = false; - bool takes_ownership = false; - bool is_public = false; - - // Check for ownership modifiers - while (p_current(parser).type_ == TOK_RETURNES_OWNERSHIP || - p_current(parser).type_ == TOK_TAKES_OWNERSHIP) { - if (p_current(parser).type_ == TOK_RETURNES_OWNERSHIP) { - returns_ownership = true; - p_advance(parser); - } else if (p_current(parser).type_ == TOK_TAKES_OWNERSHIP) { - takes_ownership = true; - p_advance(parser); + bool returns_ownership = false; + bool takes_ownership = false; + bool is_public = false; + + // Check for ownership modifiers + while (p_current(parser).type_ == TOK_RETURNES_OWNERSHIP || + p_current(parser).type_ == TOK_TAKES_OWNERSHIP) { + if (p_current(parser).type_ == TOK_RETURNES_OWNERSHIP) { + returns_ownership = true; + p_advance(parser); + } else if (p_current(parser).type_ == TOK_TAKES_OWNERSHIP) { + takes_ownership = true; + p_advance(parser); + } + } + + // Check for visibility modifiers + if (p_current(parser).type_ == TOK_PUBLIC) { + is_public = true; + p_advance(parser); // Advance past the public token + } else if (p_current(parser).type_ == TOK_PRIVATE) { + is_public = false; + p_advance(parser); // Advance past the private token + } + + switch (p_current(parser).type_) { + case TOK_USE: + return use_stmt(parser); + case TOK_CONST: + return const_stmt(parser, is_public, returns_ownership, + takes_ownership); + case TOK_VAR: + return var_stmt(parser, is_public); + case TOK_RETURN: + return return_stmt(parser); + case TOK_LBRACE: + return block_stmt(parser); + case TOK_IF: + return if_stmt(parser); + case TOK_LOOP: + return loop_stmt(parser); + case TOK_PRINT: + return print_stmt(parser, false); + case TOK_PRINTLN: + return print_stmt(parser, true); + case TOK_CONTINUE: + case TOK_BREAK: + return break_continue_stmt(parser, + p_current(parser).type_ == TOK_CONTINUE); + case TOK_DEFER: + return defer_stmt(parser); + case TOK_SWITCH: + return switch_stmt(parser); + case TOK_IMPL: + return impl_stmt(parser); + default: + return expr_stmt( + parser); // expression statements handle their own semicolon } - } - - // Check for visibility modifiers - if (p_current(parser).type_ == TOK_PUBLIC) { - is_public = true; - p_advance(parser); // Advance past the public token - } else if (p_current(parser).type_ == TOK_PRIVATE) { - is_public = false; - p_advance(parser); // Advance past the private token - } - - switch (p_current(parser).type_) { - case TOK_USE: - return use_stmt(parser); - case TOK_CONST: - return const_stmt(parser, is_public, returns_ownership, takes_ownership); - case TOK_VAR: - return var_stmt(parser, is_public); - case TOK_RETURN: - return return_stmt(parser); - case TOK_LBRACE: - return block_stmt(parser); - case TOK_IF: - return if_stmt(parser); - case TOK_LOOP: - return loop_stmt(parser); - case TOK_PRINT: - return print_stmt(parser, false); - case TOK_PRINTLN: - return print_stmt(parser, true); - case TOK_CONTINUE: - case TOK_BREAK: - return break_continue_stmt(parser, p_current(parser).type_ == TOK_CONTINUE); - case TOK_DEFER: - return defer_stmt(parser); - case TOK_SWITCH: - return switch_stmt(parser); - default: - return expr_stmt( - parser); // expression statements handle their own semicolon - } } /** @@ -497,28 +502,28 @@ Stmt *parse_stmt(Parser *parser) { * @see tnud(), tled(), LumaTokenType */ Type *parse_type(Parser *parser) { - LumaTokenType tok = p_current(parser).type_; - - switch (tok) { - case TOK_INT: - case TOK_UINT: - case TOK_DOUBLE: - case TOK_FLOAT: - case TOK_BOOL: - case TOK_STRINGT: - case TOK_VOID: - case TOK_CHAR: - case TOK_STAR: // Pointer type - case TOK_LBRACKET: // Array type - return tnud(parser); - - // Optionally: handle identifiers like 'MyStruct' or user-defined types - case TOK_IDENTIFIER: - return create_basic_type(parser->arena, get_name(parser), parser->tks->line, - parser->tks->col); - - default: - fprintf(stderr, "[parse_type] Unexpected token for type: %d\n", tok); - return NULL; - } + LumaTokenType tok = p_current(parser).type_; + + switch (tok) { + case TOK_INT: + case TOK_UINT: + case TOK_DOUBLE: + case TOK_FLOAT: + case TOK_BOOL: + case TOK_STRINGT: + case TOK_VOID: + case TOK_CHAR: + case TOK_STAR: // Pointer type + case TOK_LBRACKET: // Array type + return tnud(parser); + + // Optionally: handle identifiers like 'MyStruct' or user-defined types + case TOK_IDENTIFIER: + return create_basic_type(parser->arena, get_name(parser), + parser->tks->line, parser->tks->col); + + default: + fprintf(stderr, "[parse_type] Unexpected token for type: %d\n", tok); + return NULL; + } } diff --git a/src/parser/parser.h b/src/parser/parser.h index 1beb2a83..47dfaa93 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -51,26 +51,26 @@ * Used to control operator precedence and associativity in Pratt parsing. */ typedef enum { - BP_NONE = 0, /**< No binding power */ - BP_LOWEST, /**< Lowest binding power */ - BP_ASSIGN, /**< Assignment operators (=, +=, etc.) */ - BP_TERNARY, /**< Ternary conditional operator (? :) */ - BP_LOGICAL_OR, /**< Logical OR operator (||) */ - BP_LOGICAL_AND, /**< Logical AND operator (&&) */ - BP_BITWISE_OR, /**< Bitwise OR operator (|) */ - BP_BITWISE_XOR, /**< Bitwise XOR operator (^) */ - BP_BITWISE_AND, /**< Bitwise AND operator (&) */ - BP_EQUALITY, /**< Equality operators (==, !=) */ - BP_RELATIONAL, /**< Relational operators (<, >, <=, >=) */ - BP_RANGE, /**< Range operations (..) */ - BP_SHIFT, /**< Shift operators (<<, >>) */ - BP_SUM, /**< Addition and subtraction (+, -) */ - BP_PRODUCT, /**< Multiplication, division, modulo (*, /, %) */ - BP_EXPONENT, /**< Exponentiation operator (**) */ - BP_UNARY, /**< Unary operators (!, ~, +, -, prefix ++/--) */ - BP_POSTFIX, /**< Postfix operators (++/-- postfix) */ - BP_CALL, /**< Function call or indexing */ - BP_PRIMARY /**< Primary expressions (literals, variables) */ + BP_NONE = 0, /**< No binding power */ + BP_LOWEST, /**< Lowest binding power */ + BP_ASSIGN, /**< Assignment operators (=, +=, etc.) */ + BP_TERNARY, /**< Ternary conditional operator (? :) */ + BP_LOGICAL_OR, /**< Logical OR operator (||) */ + BP_LOGICAL_AND, /**< Logical AND operator (&&) */ + BP_BITWISE_OR, /**< Bitwise OR operator (|) */ + BP_BITWISE_XOR, /**< Bitwise XOR operator (^) */ + BP_BITWISE_AND, /**< Bitwise AND operator (&) */ + BP_EQUALITY, /**< Equality operators (==, !=) */ + BP_RELATIONAL, /**< Relational operators (<, >, <=, >=) */ + BP_RANGE, /**< Range operations (..) */ + BP_SHIFT, /**< Shift operators (<<, >>) */ + BP_SUM, /**< Addition and subtraction (+, -) */ + BP_PRODUCT, /**< Multiplication, division, modulo (*, /, %) */ + BP_EXPONENT, /**< Exponentiation operator (**) */ + BP_UNARY, /**< Unary operators (!, ~, +, -, prefix ++/--) */ + BP_POSTFIX, /**< Postfix operators (++/-- postfix) */ + BP_CALL, /**< Function call or indexing */ + BP_PRIMARY /**< Primary expressions (literals, variables) */ } BindingPower; /** @@ -113,12 +113,12 @@ static const UnaryOp TOKEN_TO_UNOP_MAP[] = { * @brief Parser state holding token stream and current position. */ typedef struct { - const char *file_path; - ArenaAllocator *arena; /**< Memory arena for AST node allocations */ - Token *tks; /**< Array of tokens to parse */ - size_t tk_count; /**< Number of tokens in the array */ - size_t capacity; /**< Capacity for statements and expressions */ - size_t pos; /**< Current token position */ + const char *file_path; + ArenaAllocator *arena; /**< Memory arena for AST node allocations */ + Token *tks; /**< Array of tokens to parse */ + size_t tk_count; /**< Number of tokens in the array */ + size_t capacity; /**< Capacity for statements and expressions */ + size_t pos; /**< Current token position */ } Parser; /** @@ -217,3 +217,4 @@ Stmt *if_stmt(Parser *parser); Stmt *break_continue_stmt(Parser *parser, bool is_continue); Stmt *defer_stmt(Parser *parser); Stmt *switch_stmt(Parser *parser); +Stmt *impl_stmt(Parser *parser); diff --git a/src/parser/stmt.c b/src/parser/stmt.c index 18603895..ac81eb55 100644 --- a/src/parser/stmt.c +++ b/src/parser/stmt.c @@ -47,32 +47,32 @@ * @see parse_expr(), create_expr_stmt() */ Stmt *expr_stmt(Parser *parser) { - // Capture line/col info at the beginning - int line = p_current(parser).line; - int col = p_current(parser).col; - - Expr *expr = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after expression statement"); - return create_expr_stmt(parser->arena, expr, line, col); + // Capture line/col info at the beginning + int line = p_current(parser).line; + int col = p_current(parser).col; + + Expr *expr = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after expression statement"); + return create_expr_stmt(parser->arena, expr, line, col); } // @use "module_name" as module_alias Stmt *use_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - p_consume(parser, TOK_USE, "Expected '@use' keyword"); - const char *module_name = get_name(parser); - p_advance(parser); // Advance past the identifier token - const char *module_alias = NULL; - if (p_current(parser).type_ == TOK_AS) { - p_consume(parser, TOK_AS, "Expected 'as' keyword for module alias"); - module_alias = get_name(parser); - p_advance(parser); // Advance past the alias identifier token - } - - return create_use_node(parser->arena, module_name, module_alias, line, col); + int line = p_current(parser).line; + int col = p_current(parser).col; + + p_consume(parser, TOK_USE, "Expected '@use' keyword"); + const char *module_name = get_name(parser); + p_advance(parser); // Advance past the identifier token + const char *module_alias = NULL; + if (p_current(parser).type_ == TOK_AS) { + p_consume(parser, TOK_AS, "Expected 'as' keyword for module alias"); + module_alias = get_name(parser); + p_advance(parser); // Advance past the alias identifier token + } + + return create_use_node(parser->arena, module_name, module_alias, line, col); } /** @@ -100,45 +100,46 @@ Stmt *use_stmt(Parser *parser) { */ Stmt *const_stmt(Parser *parser, bool is_public, bool returns_ownership, bool takes_ownership) { - int line = p_current(parser).line; - int col = p_current(parser).col; + int line = p_current(parser).line; + int col = p_current(parser).col; + + p_consume(parser, TOK_CONST, "Expected 'const' keyword"); + const char *name = get_name(parser); + p_advance(parser); // Advance past the identifier token - p_consume(parser, TOK_CONST, "Expected 'const' keyword"); - const char *name = get_name(parser); - p_advance(parser); // Advance past the identifier token + // Handle explicit type annotation: const name: Type = value + if (p_current(parser).type_ == TOK_COLON) { + p_consume(parser, TOK_COLON, "Expected ':' after const name"); - // Handle explicit type annotation: const name: Type = value - if (p_current(parser).type_ == TOK_COLON) { - p_consume(parser, TOK_COLON, "Expected ':' after const name"); + Type *type = parse_type(parser); + p_advance(parser); // Advance past the type token - Type *type = parse_type(parser); - p_advance(parser); // Advance past the type token + p_consume(parser, TOK_EQUAL, "Expected '=' after const type"); + Expr *value = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_EQUAL, "Expected '=' after const type"); - Expr *value = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after const declaration"); + return create_var_decl_stmt(parser->arena, name, type, value, false, + is_public, line, col); + } - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after const declaration"); - return create_var_decl_stmt(parser->arena, name, type, value, false, - is_public, line, col); - } - - p_consume(parser, TOK_RIGHT_ARROW, "Expected '->' after const name"); - - // Handle complex constant types (functions, structs, enums) - switch (p_current(parser).type_) { - case TOK_FN: - return fn_stmt(parser, name, is_public, returns_ownership, takes_ownership); - case TOK_STRUCT: - return struct_stmt(parser, name, is_public); - case TOK_ENUM: - return enum_stmt(parser, name, is_public); - default: { - fprintf(stderr, "Expected function, struct, or enum after const '%s'\n", - name); - return NULL; - } - } + p_consume(parser, TOK_RIGHT_ARROW, "Expected '->' after const name"); + + // Handle complex constant types (functions, structs, enums) + switch (p_current(parser).type_) { + case TOK_FN: + return fn_stmt(parser, name, is_public, returns_ownership, + takes_ownership); + case TOK_STRUCT: + return struct_stmt(parser, name, is_public); + case TOK_ENUM: + return enum_stmt(parser, name, is_public); + default: { + fprintf(stderr, "Expected function, struct, or enum after const '%s'\n", + name); + return NULL; + } + } } /** @@ -162,65 +163,66 @@ Stmt *const_stmt(Parser *parser, bool is_public, bool returns_ownership, */ Stmt *fn_stmt(Parser *parser, const char *name, bool is_public, bool returns_ownership, bool takes_ownership) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - GrowableArray param_names, param_types; - if (!growable_array_init(¶m_names, parser->arena, 4, sizeof(char *)) || - !growable_array_init(¶m_types, parser->arena, 4, sizeof(Type *))) { - fprintf(stderr, "Failed to initialize parameter arrays.\n"); - return NULL; - } - - p_consume(parser, TOK_FN, "Expected 'fn' keyword"); - p_consume(parser, TOK_LPAREN, "Expected '(' after function name"); - - // Parse parameter list: param_name: param_type, ... - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RPAREN) { - if (p_current(parser).type_ != TOK_IDENTIFIER) { - fprintf(stderr, "Expected identifier for function parameter\n"); - return NULL; - } - - char *param_name = get_name(parser); - p_advance(parser); // Advance past the identifier token + int line = p_current(parser).line; + int col = p_current(parser).col; - p_consume(parser, TOK_COLON, "Expected ':' after parameter name"); - - Type *param_type = parse_type(parser); - if (!param_type) { - fprintf(stderr, "Failed to parse type for parameter '%s'\n", param_name); - return NULL; - } - p_advance(parser); // Advance past the type token - - // Store parameter name and type - char **name_slot = (char **)growable_array_push(¶m_names); - Type **type_slot = (Type **)growable_array_push(¶m_types); - if (!name_slot || !type_slot) { - fprintf(stderr, "Out of memory while growing parameter arrays\n"); - return NULL; + GrowableArray param_names, param_types; + if (!growable_array_init(¶m_names, parser->arena, 4, sizeof(char *)) || + !growable_array_init(¶m_types, parser->arena, 4, sizeof(Type *))) { + fprintf(stderr, "Failed to initialize parameter arrays.\n"); + return NULL; } - *name_slot = param_name; - *type_slot = param_type; - - if (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); // Advance past the comma + p_consume(parser, TOK_FN, "Expected 'fn' keyword"); + p_consume(parser, TOK_LPAREN, "Expected '(' after function name"); + + // Parse parameter list: param_name: param_type, ... + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RPAREN) { + if (p_current(parser).type_ != TOK_IDENTIFIER) { + fprintf(stderr, "Expected identifier for function parameter\n"); + return NULL; + } + + char *param_name = get_name(parser); + p_advance(parser); // Advance past the identifier token + + p_consume(parser, TOK_COLON, "Expected ':' after parameter name"); + + Type *param_type = parse_type(parser); + if (!param_type) { + fprintf(stderr, "Failed to parse type for parameter '%s'\n", + param_name); + return NULL; + } + p_advance(parser); // Advance past the type token + + // Store parameter name and type + char **name_slot = (char **)growable_array_push(¶m_names); + Type **type_slot = (Type **)growable_array_push(¶m_types); + if (!name_slot || !type_slot) { + fprintf(stderr, "Out of memory while growing parameter arrays\n"); + return NULL; + } + + *name_slot = param_name; + *type_slot = param_type; + + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); // Advance past the comma + } } - } - p_consume(parser, TOK_RPAREN, "Expected ')' after function parameters"); + p_consume(parser, TOK_RPAREN, "Expected ')' after function parameters"); - Type *return_type = parse_type(parser); - p_advance(parser); // Advance past the return type token + Type *return_type = parse_type(parser); + p_advance(parser); // Advance past the return type token - Stmt *body = block_stmt(parser); + Stmt *body = block_stmt(parser); - return create_func_decl_stmt(parser->arena, name, (char **)param_names.data, - (AstNode **)param_types.data, param_names.count, - return_type, is_public, returns_ownership, - takes_ownership, body, line, col); + return create_func_decl_stmt( + parser->arena, name, (char **)param_names.data, + (AstNode **)param_types.data, param_names.count, return_type, is_public, + returns_ownership, takes_ownership, body, line, col); } /** @@ -242,45 +244,46 @@ Stmt *fn_stmt(Parser *parser, const char *name, bool is_public, * @see create_enum_decl_stmt() */ Stmt *enum_stmt(Parser *parser, const char *name, bool is_public) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - GrowableArray members; - if (!growable_array_init(&members, parser->arena, 4, sizeof(char *))) { - fprintf(stderr, "Failed to initialize enum members array.\n"); - return NULL; - } + int line = p_current(parser).line; + int col = p_current(parser).col; - p_consume(parser, TOK_ENUM, "Expected 'enum' keyword"); - p_consume(parser, TOK_LBRACE, "Expected '{' after enum name"); - - // Parse enum members: member1, member2, ... - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { - if (p_current(parser).type_ != TOK_IDENTIFIER) { - fprintf(stderr, "Expected identifier for enum member\n"); - return NULL; - } - - char *member_name = get_name(parser); - p_advance(parser); // Advance past the identifier token - - char **slot = (char **)growable_array_push(&members); - if (!slot) { - fprintf(stderr, "Out of memory while growing enum members array\n"); - return NULL; + GrowableArray members; + if (!growable_array_init(&members, parser->arena, 4, sizeof(char *))) { + fprintf(stderr, "Failed to initialize enum members array.\n"); + return NULL; } - *slot = member_name; - if (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); // Advance past the comma + p_consume(parser, TOK_ENUM, "Expected 'enum' keyword"); + p_consume(parser, TOK_LBRACE, "Expected '{' after enum name"); + + // Parse enum members: member1, member2, ... + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { + if (p_current(parser).type_ != TOK_IDENTIFIER) { + fprintf(stderr, "Expected identifier for enum member\n"); + return NULL; + } + + char *member_name = get_name(parser); + p_advance(parser); // Advance past the identifier token + + char **slot = (char **)growable_array_push(&members); + if (!slot) { + fprintf(stderr, "Out of memory while growing enum members array\n"); + return NULL; + } + *slot = member_name; + + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); // Advance past the comma + } } - } - p_consume(parser, TOK_RBRACE, "Expected '}' to end enum declaration"); - p_consume(parser, TOK_SEMICOLON, "Expected semicolon after enum declaration"); + p_consume(parser, TOK_RBRACE, "Expected '}' to end enum declaration"); + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after enum declaration"); - return create_enum_decl_stmt(parser->arena, name, (char **)members.data, - members.count, is_public, line, col); + return create_enum_decl_stmt(parser->arena, name, (char **)members.data, + members.count, is_public, line, col); } /** @@ -311,83 +314,89 @@ Stmt *enum_stmt(Parser *parser, const char *name, bool is_public) { * @see fn_stmt(), create_field_decl_stmt(), create_struct_decl_stmt() */ Stmt *struct_stmt(Parser *parser, const char *name, bool is_public) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - p_consume(parser, TOK_STRUCT, "Expected 'struct' keyword"); - p_consume(parser, TOK_LBRACE, "Expected '{' after struct name"); - - GrowableArray public_fields; - GrowableArray private_fields; - if (!growable_array_init(&public_fields, parser->arena, 4, sizeof(Stmt *)) || - !growable_array_init(&private_fields, parser->arena, 4, sizeof(Stmt *))) { - fprintf(stderr, "Failed to initialize field arrays.\n"); - return NULL; - } - - bool public_member = true; // default: everything is public - - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { - LumaTokenType tok = p_current(parser).type_; - - // Handle visibility modifiers - if (tok == TOK_PUBLIC || tok == TOK_PRIVATE) { - public_member = (tok == TOK_PUBLIC); - p_advance(parser); - p_consume(parser, TOK_COLON, "Expected ':' after visibility keyword"); - continue; - } - - // Parse a field or method - int field_line = p_current(parser).line; - int field_col = p_current(parser).col; - - char *field_name = get_name(parser); - Stmt *field_function = NULL; - Type *field_type = NULL; - p_advance(parser); - - // TODO: Add in a check to see if we have any function modifiers like - // returns_ownership or takes_ownership - - // Method: field_name = fn(...) - if (p_current(parser).type_ == TOK_EQUAL) { - p_consume(parser, TOK_EQUAL, "Expected '=' after field name"); - field_function = fn_stmt(parser, field_name, public_member, false, false); - } else { - // Data field: field_name: Type - p_consume(parser, TOK_COLON, "Expected ':' after field name"); - field_type = parse_type(parser); - p_advance(parser); - } - - // Handle field separators - if (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); - } else if (p_current(parser).type_ != TOK_RBRACE) { - parser_error(parser, "Unexpected token", __FILE__, - "Expected ',' to separate struct fields", field_line, - field_col, 1); - return NULL; - } - - // Create field declaration and add to appropriate visibility list - Stmt *field_decl = create_field_decl_stmt( - parser->arena, field_name, field_type, field_function, public_member, - field_line, field_col); - Stmt **slot = public_member ? (Stmt **)growable_array_push(&public_fields) - : (Stmt **)growable_array_push(&private_fields); - - *slot = field_decl; - } - - p_consume(parser, TOK_RBRACE, "Expected '}' to end struct declaration"); - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after struct declaration"); - - return create_struct_decl_stmt( - parser->arena, name, (Stmt **)public_fields.data, public_fields.count, - (Stmt **)private_fields.data, private_fields.count, is_public, line, col); + int line = p_current(parser).line; + int col = p_current(parser).col; + + p_consume(parser, TOK_STRUCT, "Expected 'struct' keyword"); + p_consume(parser, TOK_LBRACE, "Expected '{' after struct name"); + + GrowableArray public_fields; + GrowableArray private_fields; + if (!growable_array_init(&public_fields, parser->arena, 4, + sizeof(Stmt *)) || + !growable_array_init(&private_fields, parser->arena, 4, + sizeof(Stmt *))) { + fprintf(stderr, "Failed to initialize field arrays.\n"); + return NULL; + } + + bool public_member = true; // default: everything is public + + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { + LumaTokenType tok = p_current(parser).type_; + + // Handle visibility modifiers + if (tok == TOK_PUBLIC || tok == TOK_PRIVATE) { + public_member = (tok == TOK_PUBLIC); + p_advance(parser); + p_consume(parser, TOK_COLON, + "Expected ':' after visibility keyword"); + continue; + } + + // Parse a field or method + int field_line = p_current(parser).line; + int field_col = p_current(parser).col; + + char *field_name = get_name(parser); + Stmt *field_function = NULL; + Type *field_type = NULL; + p_advance(parser); + + // TODO: Add in a check to see if we have any function modifiers like + // returns_ownership or takes_ownership + + // Method: field_name = fn(...) + if (p_current(parser).type_ == TOK_EQUAL) { + p_consume(parser, TOK_EQUAL, "Expected '=' after field name"); + field_function = + fn_stmt(parser, field_name, public_member, false, false); + } else { + // Data field: field_name: Type + p_consume(parser, TOK_COLON, "Expected ':' after field name"); + field_type = parse_type(parser); + p_advance(parser); + } + + // Handle field separators + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); + } else if (p_current(parser).type_ != TOK_RBRACE) { + parser_error(parser, "Unexpected token", __FILE__, + "Expected ',' to separate struct fields", field_line, + field_col, 1); + return NULL; + } + + // Create field declaration and add to appropriate visibility list + Stmt *field_decl = create_field_decl_stmt( + parser->arena, field_name, field_type, field_function, + public_member, field_line, field_col); + Stmt **slot = public_member + ? (Stmt **)growable_array_push(&public_fields) + : (Stmt **)growable_array_push(&private_fields); + + *slot = field_decl; + } + + p_consume(parser, TOK_RBRACE, "Expected '}' to end struct declaration"); + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after struct declaration"); + + return create_struct_decl_stmt( + parser->arena, name, (Stmt **)public_fields.data, public_fields.count, + (Stmt **)private_fields.data, private_fields.count, is_public, line, + col); } /** @@ -409,34 +418,34 @@ Stmt *struct_stmt(Parser *parser, const char *name, bool is_public) { * @see parse_type(), parse_expr(), create_var_decl_stmt() */ Stmt *var_stmt(Parser *parser, bool is_public) { - int line = p_current(parser).line; - int col = p_current(parser).col; + int line = p_current(parser).line; + int col = p_current(parser).col; - p_consume(parser, TOK_VAR, "Expected 'let' keyword"); - const char *name = get_name(parser); - p_advance(parser); // Advance past the identifier token + p_consume(parser, TOK_VAR, "Expected 'let' keyword"); + const char *name = get_name(parser); + p_advance(parser); // Advance past the identifier token - p_consume(parser, TOK_COLON, "Expected ':' after variable name"); - Type *type = parse_type(parser); - p_advance(parser); // Advance past the type token + p_consume(parser, TOK_COLON, "Expected ':' after variable name"); + Type *type = parse_type(parser); + p_advance(parser); // Advance past the type token - if (p_current(parser).type_ != TOK_EQUAL) { - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after variable declaration"); - return create_var_decl_stmt(parser->arena, name, type, NULL, true, - is_public, line, col); - } + if (p_current(parser).type_ != TOK_EQUAL) { + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after variable declaration"); + return create_var_decl_stmt(parser->arena, name, type, NULL, true, + is_public, line, col); + } - p_consume(parser, TOK_EQUAL, "Expected '=' after variable declaration"); + p_consume(parser, TOK_EQUAL, "Expected '=' after variable declaration"); - Expr *value = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after variable declaration"); + Expr *value = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after variable declaration"); - // const are not changable aka immutable and vars are mutable so we set - // is_mutable to true - return create_var_decl_stmt(parser->arena, name, type, value, true, is_public, - line, col); + // const are not changable aka immutable and vars are mutable so we set + // is_mutable to true + return create_var_decl_stmt(parser->arena, name, type, value, true, + is_public, line, col); } /** @@ -456,17 +465,18 @@ Stmt *var_stmt(Parser *parser, bool is_public) { * @see parse_expr(), create_return_stmt() */ Stmt *return_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; + int line = p_current(parser).line; + int col = p_current(parser).col; - p_consume(parser, TOK_RETURN, "Expected 'return' keyword"); - Expr *value = NULL; - if (p_current(parser).type_ != TOK_SEMICOLON) { - value = parse_expr(parser, BP_LOWEST); - } - p_consume(parser, TOK_SEMICOLON, "Expected semicolon after return statement"); + p_consume(parser, TOK_RETURN, "Expected 'return' keyword"); + Expr *value = NULL; + if (p_current(parser).type_ != TOK_SEMICOLON) { + value = parse_expr(parser, BP_LOWEST); + } + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after return statement"); - return create_return_stmt(parser->arena, value, line, col); + return create_return_stmt(parser->arena, value, line, col); } /** @@ -488,40 +498,41 @@ Stmt *return_stmt(Parser *parser) { * @see parse_stmt(), create_block_stmt() */ Stmt *block_stmt(Parser *parser) { - p_consume(parser, TOK_LBRACE, "Expected '{' to start block statement"); - - GrowableArray block; - if (!growable_array_init(&block, parser->arena, 4, sizeof(Stmt *))) { - fprintf(stderr, "Failed to initialize block statement array.\n"); - return NULL; - } + p_consume(parser, TOK_LBRACE, "Expected '{' to start block statement"); - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { - Stmt *stmt = parse_stmt(parser); - if (!stmt) { - fprintf(stderr, "parse_stmt returned NULL inside block\n"); - continue; // or return NULL to fail the entire block + GrowableArray block; + if (!growable_array_init(&block, parser->arena, 4, sizeof(Stmt *))) { + fprintf(stderr, "Failed to initialize block statement array.\n"); + return NULL; } - Stmt **slot = (Stmt **)growable_array_push(&block); - if (!slot) { - fprintf(stderr, "Out of memory while growing block statement array\n"); - return NULL; + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { + Stmt *stmt = parse_stmt(parser); + if (!stmt) { + fprintf(stderr, "parse_stmt returned NULL inside block\n"); + continue; // or return NULL to fail the entire block + } + + Stmt **slot = (Stmt **)growable_array_push(&block); + if (!slot) { + fprintf(stderr, + "Out of memory while growing block statement array\n"); + return NULL; + } + + *slot = stmt; } - *slot = stmt; - } + p_consume(parser, TOK_RBRACE, "Expected '}' to end block statement"); - p_consume(parser, TOK_RBRACE, "Expected '}' to end block statement"); - - Stmt **stmts = (Stmt **)block.data; - if (block.count == 0) { - return create_block_stmt(parser->arena, NULL, 0, p_current(parser).line, - p_current(parser).col); - } + Stmt **stmts = (Stmt **)block.data; + if (block.count == 0) { + return create_block_stmt(parser->arena, NULL, 0, p_current(parser).line, + p_current(parser).col); + } - return create_block_stmt(parser->arena, stmts, block.count, - p_current(parser).line, p_current(parser).col); + return create_block_stmt(parser->arena, stmts, block.count, + p_current(parser).line, p_current(parser).col); } /** @@ -549,54 +560,55 @@ Stmt *block_stmt(Parser *parser) { * @see parse_expr(), block_stmt(), create_if_stmt() */ Stmt *if_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - if (p_current(parser).type_ != TOK_IF && - p_current(parser).type_ != TOK_ELIF) { - fprintf(stderr, "Expected 'if' or 'elif' keyword\n"); - return NULL; - } - p_consume(parser, p_current(parser).type_, "Expected 'if' or 'elif' keyword"); - - p_consume(parser, TOK_LPAREN, "Expected '(' after 'if' keyword"); - Expr *condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, "Expected ')' after if condition"); - - Stmt *then_stmt = block_stmt(parser); - - // Collect all elif statements in a list/array instead of recursing - Stmt **elif_stmts = - (Stmt **)arena_alloc(parser->arena, sizeof(Stmt *) * 4, alignof(Stmt *)); - int elif_count = 0; - - while (p_has_tokens(parser) && p_current(parser).type_ == TOK_ELIF) { - int elif_line = p_current(parser).line; - int elif_col = p_current(parser).col; - - p_consume(parser, TOK_ELIF, "Expected 'elif' keyword"); - p_consume(parser, TOK_LPAREN, "Expected '(' after 'elif' keyword"); - - Expr *elif_condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, "Expected ')' after elif condition"); - - Stmt *elif_stmt = block_stmt(parser); - - // Store the elif condition and statement - elif_stmts[elif_count] = - create_if_stmt(parser->arena, elif_condition, elif_stmt, NULL, - elif_count, NULL, elif_line, elif_col); - elif_count++; - } - - Stmt *else_stmt = NULL; - if (p_current(parser).type_ == TOK_ELSE) { - p_consume(parser, TOK_ELSE, "Expected 'else' keyword"); - else_stmt = block_stmt(parser); - } - - return create_if_stmt(parser->arena, condition, then_stmt, elif_stmts, - elif_count, else_stmt, line, col); + int line = p_current(parser).line; + int col = p_current(parser).col; + + if (p_current(parser).type_ != TOK_IF && + p_current(parser).type_ != TOK_ELIF) { + fprintf(stderr, "Expected 'if' or 'elif' keyword\n"); + return NULL; + } + p_consume(parser, p_current(parser).type_, + "Expected 'if' or 'elif' keyword"); + + p_consume(parser, TOK_LPAREN, "Expected '(' after 'if' keyword"); + Expr *condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, "Expected ')' after if condition"); + + Stmt *then_stmt = block_stmt(parser); + + // Collect all elif statements in a list/array instead of recursing + Stmt **elif_stmts = (Stmt **)arena_alloc(parser->arena, sizeof(Stmt *) * 4, + alignof(Stmt *)); + int elif_count = 0; + + while (p_has_tokens(parser) && p_current(parser).type_ == TOK_ELIF) { + int elif_line = p_current(parser).line; + int elif_col = p_current(parser).col; + + p_consume(parser, TOK_ELIF, "Expected 'elif' keyword"); + p_consume(parser, TOK_LPAREN, "Expected '(' after 'elif' keyword"); + + Expr *elif_condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, "Expected ')' after elif condition"); + + Stmt *elif_stmt = block_stmt(parser); + + // Store the elif condition and statement + elif_stmts[elif_count] = + create_if_stmt(parser->arena, elif_condition, elif_stmt, NULL, + elif_count, NULL, elif_line, elif_col); + elif_count++; + } + + Stmt *else_stmt = NULL; + if (p_current(parser).type_ == TOK_ELSE) { + p_consume(parser, TOK_ELSE, "Expected 'else' keyword"); + else_stmt = block_stmt(parser); + } + + return create_if_stmt(parser->arena, condition, then_stmt, elif_stmts, + elif_count, else_stmt, line, col); } /** @@ -618,13 +630,13 @@ Stmt *if_stmt(Parser *parser) { * @see block_stmt(), create_infinite_loop_stmt() */ Stmt *infinite_loop_stmt(Parser *parser, int line, int col) { - Stmt *body = block_stmt(parser); - if (!body) { - parser_error(parser, "Syntax Error", __FILE__, "Expected block statement", - line, col, 1); - return NULL; - } - return create_infinite_loop_stmt(parser->arena, body, line, col); + Stmt *body = block_stmt(parser); + if (!body) { + parser_error(parser, "Syntax Error", __FILE__, + "Expected block statement", line, col, 1); + return NULL; + } + return create_infinite_loop_stmt(parser->arena, body, line, col); } /** @@ -645,17 +657,17 @@ Stmt *infinite_loop_stmt(Parser *parser, int line, int col) { * @see create_var_decl_stmt() */ Stmt *loop_init(Parser *parser, int line, int col) { - const char *name = get_name(parser); - p_advance(parser); // Advance past the identifier token + const char *name = get_name(parser); + p_advance(parser); // Advance past the identifier token - p_consume(parser, TOK_COLON, "Expected ':' after loop initializer"); - Type *type = parse_type(parser); - p_advance(parser); // Advance past the type token + p_consume(parser, TOK_COLON, "Expected ':' after loop initializer"); + Type *type = parse_type(parser); + p_advance(parser); // Advance past the type token - p_consume(parser, TOK_EQUAL, "Expected '=' after loop initializer"); - Expr *initializer = parse_expr(parser, BP_LOWEST); - return create_var_decl_stmt(parser->arena, name, type, initializer, true, - false, line, col); + p_consume(parser, TOK_EQUAL, "Expected '=' after loop initializer"); + Expr *initializer = parse_expr(parser, BP_LOWEST); + return create_var_decl_stmt(parser->arena, name, type, initializer, true, + false, line, col); } /** @@ -681,53 +693,55 @@ Stmt *loop_init(Parser *parser, int line, int col) { * @see loop_init(), parse_expr(), block_stmt(), create_for_loop_stmt() */ Stmt *for_loop_stmt(Parser *parser, int line, int col) { - GrowableArray intializers; - if (!growable_array_init(&intializers, parser->arena, 4, sizeof(Expr *))) { - fprintf(stderr, "Failed to initialize loop initializers array.\n"); - return NULL; - } - - p_consume(parser, TOK_LBRACKET, "Expected '[' after 'loop' keyword"); - // Parse initializers: i: int = 0, j: int = 1 - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACKET) { - Expr *init = loop_init(parser, line, col); - if (!init) { - fprintf(stderr, "Failed to parse loop initializer\n"); - return NULL; - } - - Expr **slot = (Expr **)growable_array_push(&intializers); - if (!slot) { - fprintf(stderr, "Out of memory while growing loop initializers array\n"); - return NULL; - } - - *slot = init; - - if (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); // Advance past the comma - } - } - p_consume(parser, TOK_RBRACKET, "Expected ']' after loop initializer"); - - p_consume(parser, TOK_LPAREN, "Expected '(' after loop initializer"); - Expr *condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, "Expected ')' after loop initializer"); - - // Check for the optional condition - Expr *optional_condition = NULL; - if (p_current(parser).type_ == TOK_COLON) { - p_consume(parser, TOK_COLON, "Expected ':' after loop condition"); - p_consume(parser, TOK_LPAREN, "Expected '(' after ':' in loop statement"); - optional_condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, - "Expected ')' after optional condition in loop statement"); - } - - Stmt *body = block_stmt(parser); - return create_for_loop_stmt(parser->arena, (AstNode **)intializers.data, - intializers.count, condition, optional_condition, - body, line, col); + GrowableArray intializers; + if (!growable_array_init(&intializers, parser->arena, 4, sizeof(Expr *))) { + fprintf(stderr, "Failed to initialize loop initializers array.\n"); + return NULL; + } + + p_consume(parser, TOK_LBRACKET, "Expected '[' after 'loop' keyword"); + // Parse initializers: i: int = 0, j: int = 1 + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACKET) { + Expr *init = loop_init(parser, line, col); + if (!init) { + fprintf(stderr, "Failed to parse loop initializer\n"); + return NULL; + } + + Expr **slot = (Expr **)growable_array_push(&intializers); + if (!slot) { + fprintf(stderr, + "Out of memory while growing loop initializers array\n"); + return NULL; + } + + *slot = init; + + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); // Advance past the comma + } + } + p_consume(parser, TOK_RBRACKET, "Expected ']' after loop initializer"); + + p_consume(parser, TOK_LPAREN, "Expected '(' after loop initializer"); + Expr *condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, "Expected ')' after loop initializer"); + + // Check for the optional condition + Expr *optional_condition = NULL; + if (p_current(parser).type_ == TOK_COLON) { + p_consume(parser, TOK_COLON, "Expected ':' after loop condition"); + p_consume(parser, TOK_LPAREN, + "Expected '(' after ':' in loop statement"); + optional_condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, + "Expected ')' after optional condition in loop statement"); + } + + Stmt *body = block_stmt(parser); + return create_for_loop_stmt(parser->arena, (AstNode **)intializers.data, + intializers.count, condition, + optional_condition, body, line, col); } /** @@ -754,40 +768,41 @@ Stmt *for_loop_stmt(Parser *parser, int line, int col) { * create_loop_stmt() */ Stmt *loop_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - p_consume(parser, TOK_LOOP, "Expected 'loop' keyword"); - - if (p_current(parser).type_ == - TOK_LBRACE) { // Aka we have a infinite loop 'loop { ... }' - return infinite_loop_stmt(parser, line, col); - } - - if (p_current(parser).type_ == - TOK_LBRACKET) { // Aka we have a for loop 'loop [ ... ] { ... }' - return for_loop_stmt(parser, line, col); - } - - // else we have a standard while loop 'loop (condition) { ... }' - p_consume(parser, TOK_LPAREN, "Expected '(' after 'loop' keyword"); - Expr *condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, "Expected ')' after loop condition"); - - // check if there is an optional condition 'loop (condition) : - // (optional_condition) { ... }' - Expr *optional_condition = NULL; - if (p_current(parser).type_ == TOK_COLON) { - p_advance(parser); // Advance past the colon - p_consume(parser, TOK_LPAREN, "Expected '(' after ':' in loop statement"); - optional_condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, - "Expected ')' after optional condition in loop statement"); - } - - Stmt *body = block_stmt(parser); - return create_loop_stmt(parser->arena, condition, optional_condition, body, - line, col); + int line = p_current(parser).line; + int col = p_current(parser).col; + + p_consume(parser, TOK_LOOP, "Expected 'loop' keyword"); + + if (p_current(parser).type_ == + TOK_LBRACE) { // Aka we have a infinite loop 'loop { ... }' + return infinite_loop_stmt(parser, line, col); + } + + if (p_current(parser).type_ == + TOK_LBRACKET) { // Aka we have a for loop 'loop [ ... ] { ... }' + return for_loop_stmt(parser, line, col); + } + + // else we have a standard while loop 'loop (condition) { ... }' + p_consume(parser, TOK_LPAREN, "Expected '(' after 'loop' keyword"); + Expr *condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, "Expected ')' after loop condition"); + + // check if there is an optional condition 'loop (condition) : + // (optional_condition) { ... }' + Expr *optional_condition = NULL; + if (p_current(parser).type_ == TOK_COLON) { + p_advance(parser); // Advance past the colon + p_consume(parser, TOK_LPAREN, + "Expected '(' after ':' in loop statement"); + optional_condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, + "Expected ')' after optional condition in loop statement"); + } + + Stmt *body = block_stmt(parser); + return create_loop_stmt(parser->arena, condition, optional_condition, body, + line, col); } /** @@ -810,43 +825,45 @@ Stmt *loop_stmt(Parser *parser) { * @see parse_expr(), create_print_stmt() */ Stmt *print_stmt(Parser *parser, bool ln) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - p_consume(parser, ln ? TOK_PRINTLN : TOK_PRINT, - "Expected 'output' or 'outputln' keyword"); - p_consume(parser, TOK_LPAREN, "Expected '(' after print statement"); + int line = p_current(parser).line; + int col = p_current(parser).col; - GrowableArray expressions; - if (!growable_array_init(&expressions, parser->arena, 4, sizeof(Expr *))) { - fprintf(stderr, "Failed to initialize print expressions array.\n"); - return NULL; - } + p_consume(parser, ln ? TOK_PRINTLN : TOK_PRINT, + "Expected 'output' or 'outputln' keyword"); + p_consume(parser, TOK_LPAREN, "Expected '(' after print statement"); - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RPAREN) { - Expr *expr = parse_expr(parser, BP_LOWEST); - if (!expr) { - fprintf(stderr, "Failed to parse expression in print statement\n"); - return NULL; - } - - Expr **slot = (Expr **)growable_array_push(&expressions); - if (!slot) { - fprintf(stderr, "Out of memory while growing print expressions array\n"); - return NULL; + GrowableArray expressions; + if (!growable_array_init(&expressions, parser->arena, 4, sizeof(Expr *))) { + fprintf(stderr, "Failed to initialize print expressions array.\n"); + return NULL; } - *slot = expr; - - if (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); // Advance past the comma + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RPAREN) { + Expr *expr = parse_expr(parser, BP_LOWEST); + if (!expr) { + fprintf(stderr, "Failed to parse expression in print statement\n"); + return NULL; + } + + Expr **slot = (Expr **)growable_array_push(&expressions); + if (!slot) { + fprintf(stderr, + "Out of memory while growing print expressions array\n"); + return NULL; + } + + *slot = expr; + + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); // Advance past the comma + } } - } - p_consume(parser, TOK_RPAREN, "Expected ')' to end print statement"); - p_consume(parser, TOK_SEMICOLON, "Expected semicolon after print statement"); + p_consume(parser, TOK_RPAREN, "Expected ')' to end print statement"); + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after print statement"); - return create_print_stmt(parser->arena, (Expr **)expressions.data, - expressions.count, ln, line, col); + return create_print_stmt(parser->arena, (Expr **)expressions.data, + expressions.count, ln, line, col); } /** @@ -871,146 +888,246 @@ Stmt *print_stmt(Parser *parser, bool ln) { * @see create_break_continue_stmt() */ Stmt *break_continue_stmt(Parser *parser, bool is_continue) { - int line = p_current(parser).line; - int col = p_current(parser).col; + int line = p_current(parser).line; + int col = p_current(parser).col; - p_consume(parser, is_continue ? TOK_CONTINUE : TOK_BREAK, - is_continue ? "Expected 'continue' keyword" - : "Expected 'break' keyword"); - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after break/continue statement"); + p_consume(parser, is_continue ? TOK_CONTINUE : TOK_BREAK, + is_continue ? "Expected 'continue' keyword" + : "Expected 'break' keyword"); + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after break/continue statement"); - return create_break_continue_stmt(parser->arena, is_continue, line, col); + return create_break_continue_stmt(parser->arena, is_continue, line, col); } Stmt *defer_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - p_consume(parser, TOK_DEFER, "Expected 'defer' keyword"); - Stmt *stmt = parse_stmt(parser); - if (!stmt) { - parser_error(parser, "Syntax Error", __FILE__, - "Expected statement after 'defer'", line, col, 1); - return NULL; - } - - return create_defer_stmt(parser->arena, stmt, line, col); + int line = p_current(parser).line; + int col = p_current(parser).col; + + p_consume(parser, TOK_DEFER, "Expected 'defer' keyword"); + Stmt *stmt = parse_stmt(parser); + if (!stmt) { + parser_error(parser, "Syntax Error", __FILE__, + "Expected statement after 'defer'", line, col, 1); + return NULL; + } + + return create_defer_stmt(parser->arena, stmt, line, col); } Stmt *switch_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - p_consume(parser, TOK_SWITCH, "Expected 'switch' keyword"); - p_consume(parser, TOK_LPAREN, "Expected '(' after 'switch' keyword"); - Expr *condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, "Expected ')' after switch condition"); - p_consume(parser, TOK_LBRACE, "Expected '{' to start switch body"); - - GrowableArray cases; - if (!growable_array_init(&cases, parser->arena, 4, sizeof(Stmt *))) { - fprintf(stderr, "Failed to initialize switch cases array.\n"); - return NULL; - } - - Stmt *default_case = NULL; - - // Parse all cases and default - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { - int case_line = p_current(parser).line; - int case_col = p_current(parser).col; - - // Check if this is the default case - if (p_current(parser).type_ == TOK_IDENTIFIER && - strcmp(get_name(parser), "_") == 0) { - // Handle default case: _: { ... } - p_advance(parser); // consume '_' - p_consume(parser, TOK_RIGHT_ARROW, - "Expected '=>' after default case '_'"); - - Stmt *default_body; - if (p_current(parser).type_ == TOK_LBRACE) { - default_body = block_stmt(parser); - } else { - default_body = parse_stmt(parser); - } - - default_case = - create_default_stmt(parser->arena, default_body, case_line, case_col); - continue; - } - - // Parse case values: can be single value or comma-separated list - GrowableArray case_values; - if (!growable_array_init(&case_values, parser->arena, 4, sizeof(Expr *))) { - fprintf(stderr, "Failed to initialize case values array.\n"); - return NULL; - } - - // Parse first case value - Expr *case_expr = parse_expr(parser, BP_LOWEST); - if (!case_expr) { - fprintf(stderr, "Failed to parse case expression\n"); - return NULL; - } - - Expr **value_slot = (Expr **)growable_array_push(&case_values); - if (!value_slot) { - fprintf(stderr, "Out of memory while growing case values array\n"); - return NULL; - } - *value_slot = case_expr; - - // Parse additional case values if comma-separated - while (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); // consume comma - - Expr *additional_case = parse_expr(parser, BP_LOWEST); - if (!additional_case) { - fprintf(stderr, "Failed to parse additional case expression\n"); + int line = p_current(parser).line; + int col = p_current(parser).col; + + p_consume(parser, TOK_SWITCH, "Expected 'switch' keyword"); + p_consume(parser, TOK_LPAREN, "Expected '(' after 'switch' keyword"); + Expr *condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, "Expected ')' after switch condition"); + p_consume(parser, TOK_LBRACE, "Expected '{' to start switch body"); + + GrowableArray cases; + if (!growable_array_init(&cases, parser->arena, 4, sizeof(Stmt *))) { + fprintf(stderr, "Failed to initialize switch cases array.\n"); return NULL; - } + } - Expr **additional_slot = (Expr **)growable_array_push(&case_values); - if (!additional_slot) { - fprintf(stderr, "Out of memory while growing case values array\n"); - return NULL; - } - *additional_slot = additional_case; + Stmt *default_case = NULL; + + // Parse all cases and default + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { + int case_line = p_current(parser).line; + int case_col = p_current(parser).col; + + // Check if this is the default case + if (p_current(parser).type_ == TOK_IDENTIFIER && + strcmp(get_name(parser), "_") == 0) { + // Handle default case: _: { ... } + p_advance(parser); // consume '_' + p_consume(parser, TOK_RIGHT_ARROW, + "Expected '=>' after default case '_'"); + + Stmt *default_body; + if (p_current(parser).type_ == TOK_LBRACE) { + default_body = block_stmt(parser); + } else { + default_body = parse_stmt(parser); + } + + default_case = create_default_stmt(parser->arena, default_body, + case_line, case_col); + continue; + } + + // Parse case values: can be single value or comma-separated list + GrowableArray case_values; + if (!growable_array_init(&case_values, parser->arena, 4, + sizeof(Expr *))) { + fprintf(stderr, "Failed to initialize case values array.\n"); + return NULL; + } + + // Parse first case value + Expr *case_expr = parse_expr(parser, BP_LOWEST); + if (!case_expr) { + fprintf(stderr, "Failed to parse case expression\n"); + return NULL; + } + + Expr **value_slot = (Expr **)growable_array_push(&case_values); + if (!value_slot) { + fprintf(stderr, "Out of memory while growing case values array\n"); + return NULL; + } + *value_slot = case_expr; + + // Parse additional case values if comma-separated + while (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); // consume comma + + Expr *additional_case = parse_expr(parser, BP_LOWEST); + if (!additional_case) { + fprintf(stderr, "Failed to parse additional case expression\n"); + return NULL; + } + + Expr **additional_slot = (Expr **)growable_array_push(&case_values); + if (!additional_slot) { + fprintf(stderr, + "Out of memory while growing case values array\n"); + return NULL; + } + *additional_slot = additional_case; + } + + p_consume(parser, TOK_RIGHT_ARROW, "Expected '=>' after case value(s)"); + + // Parse case body (either block or single statement) + Stmt *case_body; + if (p_current(parser).type_ == TOK_LBRACE) { + case_body = block_stmt(parser); + } else { + // Single statement + case_body = parse_stmt(parser); + } + + if (!case_body) { + fprintf(stderr, "Failed to parse case body\n"); + return NULL; + } + + // Create case statement and add to cases array + Stmt *case_stmt = + create_case_stmt(parser->arena, (AstNode **)case_values.data, + case_values.count, case_body, case_line, case_col); + + Stmt **case_slot = (Stmt **)growable_array_push(&cases); + if (!case_slot) { + fprintf(stderr, "Out of memory while growing cases array\n"); + return NULL; + } + *case_slot = case_stmt; } - p_consume(parser, TOK_RIGHT_ARROW, "Expected '=>' after case value(s)"); + p_consume(parser, TOK_RBRACE, "Expected '}' to end switch statement"); - // Parse case body (either block or single statement) - Stmt *case_body; - if (p_current(parser).type_ == TOK_LBRACE) { - case_body = block_stmt(parser); - } else { - // Single statement - case_body = parse_stmt(parser); - } + return create_switch_stmt(parser->arena, condition, (AstNode **)cases.data, + cases.count, (AstNode *)default_case, line, col); +} +// Impl [fun1: void, fun2: void, ...] -> [struct1, struct2, ...] +Stmt *impl_stmt(Parser *parser) { + int line = p_current(parser).line; + int col = p_current(parser).col; - if (!case_body) { - fprintf(stderr, "Failed to parse case body\n"); - return NULL; - } + GrowableArray function_name_list, function_name_types; + GrowableArray struct_name_list; - // Create case statement and add to cases array - Stmt *case_stmt = - create_case_stmt(parser->arena, (AstNode **)case_values.data, - case_values.count, case_body, case_line, case_col); + p_consume(parser, TOK_IMPL, "Expected 'impl' keyword"); + p_consume(parser, TOK_LBRACKET, "Expected '[' after 'impl' keyword"); - Stmt **case_slot = (Stmt **)growable_array_push(&cases); - if (!case_slot) { - fprintf(stderr, "Out of memory while growing cases array\n"); - return NULL; + if (!growable_array_init(&function_name_list, parser->arena, 4, + sizeof(Stmt *))) { + fprintf(stderr, "Failed to initialize impl function array list"); + return NULL; + } + if (!growable_array_init(&struct_name_list, parser->arena, 4, + sizeof(Stmt *))) { + fprintf(stderr, "Failed to initialize impl struct array list"); + return NULL; } - *case_slot = case_stmt; - } - - p_consume(parser, TOK_RBRACE, "Expected '}' to end switch statement"); - return create_switch_stmt(parser->arena, condition, (AstNode **)cases.data, - cases.count, (AstNode *)default_case, line, col); + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACKET) { + int list_line = p_current(parser).line; + int list_col = p_current(parser).col; + + if (p_current(parser).type_ != TOK_IDENTIFIER) { + parser_error(parser, "SyntaxError", parser->file_path, + "Required identifier", list_line, list_col, + p_current(parser).length); + return NULL; + } + char *function_list_name = get_name(parser); + p_advance(parser); + p_consume(parser, TOK_COLON, "Expected ':' after parameter name"); + + Type *function_list_type = parse_type(parser); + if (!function_list_type) { + fprintf(stderr, "Failed to parse type for parameter '%s'\n", + function_list_name); + return NULL; + } + p_advance(parser); + + char **name_identifier = + (char **)growable_array_push(&function_name_list); + Type **type_specifier = + (Type **)growable_array_push(&function_name_types); + + if (!name_identifier || !type_specifier) { + fprintf(stderr, + "Out of memory while growing function list for impl\n"); + return NULL; + } + *name_identifier = function_list_name; + *type_specifier = function_list_type; + + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); + } + } + p_consume(parser, TOK_RBRACKET, "Expected ']' after function parameters"); + p_consume(parser, TOK_RIGHT_ARROW, "Expected '->' after function list"); + p_consume(parser, TOK_LBRACKET, "Expected '[' after '->'"); + + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACKET) { + int list_line = p_current(parser).line; + int list_col = p_current(parser).col; + + if (p_current(parser).type_ != TOK_IDENTIFIER) { + parser_error(parser, "SyntaxError", parser->file_path, + "Required identifier", list_line, list_col, + p_current(parser).length); + return NULL; + } + char *struct_list_name = get_name(parser); + p_advance(parser); + char **struct_identifier = + (char **)growable_array_push(&struct_name_list); + + if (!struct_identifier) { + fprintf(stderr, + "Out of memory while growing struct list for impl\n"); + return NULL; + } + *struct_identifier = struct_list_name; + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); + } + } + p_consume(parser, TOK_RBRACKET, "Expected ']' after struct list"); + Stmt *body = block_stmt(parser); + return create_impl_stmt(parser->arena, (char **)function_name_list.data, + (AstNode **)function_name_types.data, body, + (char **)struct_name_list.data, + function_name_list.count, struct_name_list.count, + line, col); } diff --git a/tests/test.lx b/tests/test.lx index cb35f662..4a33641c 100644 --- a/tests/test.lx +++ b/tests/test.lx @@ -1,20 +1,12 @@ @module "main" -@use "stack" as st +impl [func1: int, func2: char] -> [theStruct] { + +} pub const main -> fn () int { - let stack: *Stack = st::createStack(20); - st::push(stack, 20); - st::push(stack, 30); - st::push(stack, 50); - st::push(stack, 12); - st::push(stack, 311); - output("Peeked: ", st::peek(stack), "\n"); - st::pop(stack); - st::pop(stack); - st::freeStack(stack); - return 0; +return 0; } diff --git a/tests/tetris.lx b/tests/tetris.lx index b15c5b7c..26c70258 100644 --- a/tests/tetris.lx +++ b/tests/tetris.lx @@ -342,4 +342,4 @@ pub const main -> fn () int { term::disable_raw_mode(); output(tx::BRIGHT_CYAN, "\n\nGame Over! Final Score: ", score, "\n", tx::RESET); return 0; -} \ No newline at end of file +} From 884414cc6fd5f2c8a41ee2ace8279d393c0964e5 Mon Sep 17 00:00:00 2001 From: NoxAether <107390370+NoxAether@users.noreply.github.com> Date: Sun, 19 Oct 2025 05:55:09 +1100 Subject: [PATCH 4/6] nooo --- tests/terminal_fps.lx | 156 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 tests/terminal_fps.lx diff --git a/tests/terminal_fps.lx b/tests/terminal_fps.lx new file mode 100644 index 00000000..216337f9 --- /dev/null +++ b/tests/terminal_fps.lx @@ -0,0 +1,156 @@ +@module "main" + +@use "math" as math +@use "memory" as mem +@use "string" as string +@use "termfx" as fx + +let A: double; +let B: double; +let C: double; + +let zBuffer: *double; +let _colorBuffer: **char; +let _buffer: *char; + +let cube_width: double; +const width: int = 160; +const height: int = 44; + +const distance_from_cam: int = 100; + +const increment_speed: double = 0.6; +let horizontal_offset: double; +const k1: double = 40.0; + +let cube_gradient_size: int = 12; + +const calculateX -> fn (i: int, j: int, k: int) double { + let sA: double = math::sin(A); + let cA: double = math::cos(A); + let sB: double = math::sin(B); + let cB: double = math::cos(B); + let sC: double = math::sin(C); + let cC: double = math::cos(C); + + return j * sA * sB * cC - k * cA * sB * cC + + j * cA * sC + k * sA * sC + i * cB * cC; +} + +const calculateY -> fn (i: int, j: int, k: int) double { + let sA: double = math::sin(A); + let cA: double = math::cos(A); + let sB: double = math::sin(B); + let cB: double = math::cos(B); + let sC: double = math::sin(C); + let cC: double = math::cos(C); + + return j * cA * cC + k * sA * cC - + j * sA * sB * sC + k * cA * sB * sC - + i * cB * sC; +} + +const calculateZ -> fn (i: int, j: int, k: int) double { + let sA: double = math::sin(A); + let cA: double = math::cos(A); + let sB: double = math::sin(B); + let cB: double = math::cos(B); + + return k * cA * cB - j * sA * cB + i * sB; +} + +const calculateForSurface -> fn (cX: double, cY: double, cZ: double, + ch: char, color: *char) void { + let x: double = calculateX(cast(cX), cast(cY), cast(cZ)); + let y: double = calculateY(cast(cX), cast(cY), cast(cZ)); + let z: double = calculateZ(cast(cX), cast(cY), cast(cZ)) + distance_from_cam; + + if (z <= 0.001) { return; } + + let ooz: double = 1.0 / z; + + if (ooz > 1000.0 || ooz < -1000.0) { return; } + + let xp: int = cast(width / 2 + horizontal_offset + k1 * ooz * x * 2); + let yp: int = cast(height / 2 + k1 * ooz * y); + + if (xp >= 0 && xp < width && yp >= 0 && yp < height) { + let idx: int = xp + yp * width; + if (ooz > zBuffer[idx]) { + zBuffer[idx] = ooz; + _buffer[idx] = ch; + _colorBuffer[idx] = color; + } + } +} + +const print_cube -> fn () void { + loop [cubeX: double = -cube_width](cubeX < cube_width) : (cubeX = cubeX + increment_speed) { + loop [cubeY: double = -cube_width](cubeY < cube_width) : (cubeY = cubeY + increment_speed) { + calculateForSurface(cubeX, cubeY, -cube_width, '@', fx::BRIGHT_RED); + calculateForSurface(cube_width, cubeY, cubeX, '+', fx::BRIGHT_GREEN); + calculateForSurface(-cube_width, cubeY, -cubeX, '~', fx::BRIGHT_YELLOW); + calculateForSurface(-cubeX, cubeY, cube_width, '#', fx::BRIGHT_BLUE); + calculateForSurface(cubeX, -cube_width, -cubeY, '*', fx::BRIGHT_CYAN); + calculateForSurface(cubeX, cube_width, cubeY, '-', fx::BRIGHT_MAGENTA); + } + } +} + +pub const main -> fn () int { + zBuffer = cast<*double>(alloc(160 * 44 * sizeof)); + _colorBuffer = cast<**char>(alloc(160 * 44 * 8)); + _buffer = cast<*char>(alloc(160 * 44)); + + defer { free(zBuffer); free(_colorBuffer); free(_buffer); } + + output(fx::CLEAR_SCREEN, fx::CURSOR_HOME, fx::BRIGHT_MAGENTA, "3D", fx::RESET, + fx::BRIGHT_CYAN, " Spinning Cubes in Luma\n", fx::RESET, fx::CURSOR_HIDE); + + let frame_count: int = 0; + + loop { + mem::memset(cast<*void>(_buffer), 32, width * height); + mem::memset(cast<*void>(_colorBuffer), 0, width * height * 8); + mem::memset(cast<*void>(zBuffer), 0, width * height * sizeof); + + cube_width = 20.0; + horizontal_offset = -2 * cube_width; + print_cube(); + + cube_width = 10.0; + horizontal_offset = 1 * cube_width; + print_cube(); + + cube_width = 5.0; + horizontal_offset = 8 * cube_width; + print_cube(); + + output(fx::CURSOR_HOME); + loop [k: int = 0](k < width * height) : (++k) { + if (k % width == 0) { output("\n"); } + + if (_colorBuffer[k] != cast<*char>(0)) { + output(_colorBuffer[k]); + } + output(string::from_char(_buffer[k]), fx::RESET); + } + + output( + fx::move_cursor(1, 29), + fx::BOLD, fx::WHITE, " Frame: ", + fx::BRIGHT_GREEN, frame_count, " ", + fx::RESET + ); + + // Rotate cube + A = (A + 0.05) % math::TWO_PI; + B = (B + 0.05) % math::TWO_PI; + C = (C + 0.01) % math::TWO_PI; + + // Increment frame count + ++frame_count; + } + + return 0; +} From aec8516f00217f5927c9c43ab916cac71bb6cc3a Mon Sep 17 00:00:00 2001 From: TheDevConnor Date: Sun, 19 Oct 2025 03:11:06 -0400 Subject: [PATCH 5/6] implemnted impl in the parser --- src/parser/stmt.c | 1330 ++++++++++++++++++++++----------------------- tests/test.lx | 93 +++- 2 files changed, 743 insertions(+), 680 deletions(-) diff --git a/src/parser/stmt.c b/src/parser/stmt.c index ac81eb55..3c43bbf3 100644 --- a/src/parser/stmt.c +++ b/src/parser/stmt.c @@ -47,32 +47,32 @@ * @see parse_expr(), create_expr_stmt() */ Stmt *expr_stmt(Parser *parser) { - // Capture line/col info at the beginning - int line = p_current(parser).line; - int col = p_current(parser).col; - - Expr *expr = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after expression statement"); - return create_expr_stmt(parser->arena, expr, line, col); + // Capture line/col info at the beginning + int line = p_current(parser).line; + int col = p_current(parser).col; + + Expr *expr = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after expression statement"); + return create_expr_stmt(parser->arena, expr, line, col); } // @use "module_name" as module_alias Stmt *use_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - p_consume(parser, TOK_USE, "Expected '@use' keyword"); - const char *module_name = get_name(parser); - p_advance(parser); // Advance past the identifier token - const char *module_alias = NULL; - if (p_current(parser).type_ == TOK_AS) { - p_consume(parser, TOK_AS, "Expected 'as' keyword for module alias"); - module_alias = get_name(parser); - p_advance(parser); // Advance past the alias identifier token - } - - return create_use_node(parser->arena, module_name, module_alias, line, col); + int line = p_current(parser).line; + int col = p_current(parser).col; + + p_consume(parser, TOK_USE, "Expected '@use' keyword"); + const char *module_name = get_name(parser); + p_advance(parser); // Advance past the identifier token + const char *module_alias = NULL; + if (p_current(parser).type_ == TOK_AS) { + p_consume(parser, TOK_AS, "Expected 'as' keyword for module alias"); + module_alias = get_name(parser); + p_advance(parser); // Advance past the alias identifier token + } + + return create_use_node(parser->arena, module_name, module_alias, line, col); } /** @@ -100,46 +100,45 @@ Stmt *use_stmt(Parser *parser) { */ Stmt *const_stmt(Parser *parser, bool is_public, bool returns_ownership, bool takes_ownership) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - p_consume(parser, TOK_CONST, "Expected 'const' keyword"); - const char *name = get_name(parser); - p_advance(parser); // Advance past the identifier token + int line = p_current(parser).line; + int col = p_current(parser).col; - // Handle explicit type annotation: const name: Type = value - if (p_current(parser).type_ == TOK_COLON) { - p_consume(parser, TOK_COLON, "Expected ':' after const name"); + p_consume(parser, TOK_CONST, "Expected 'const' keyword"); + const char *name = get_name(parser); + p_advance(parser); // Advance past the identifier token - Type *type = parse_type(parser); - p_advance(parser); // Advance past the type token + // Handle explicit type annotation: const name: Type = value + if (p_current(parser).type_ == TOK_COLON) { + p_consume(parser, TOK_COLON, "Expected ':' after const name"); - p_consume(parser, TOK_EQUAL, "Expected '=' after const type"); - Expr *value = parse_expr(parser, BP_LOWEST); + Type *type = parse_type(parser); + p_advance(parser); // Advance past the type token - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after const declaration"); - return create_var_decl_stmt(parser->arena, name, type, value, false, - is_public, line, col); - } + p_consume(parser, TOK_EQUAL, "Expected '=' after const type"); + Expr *value = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RIGHT_ARROW, "Expected '->' after const name"); - - // Handle complex constant types (functions, structs, enums) - switch (p_current(parser).type_) { - case TOK_FN: - return fn_stmt(parser, name, is_public, returns_ownership, - takes_ownership); - case TOK_STRUCT: - return struct_stmt(parser, name, is_public); - case TOK_ENUM: - return enum_stmt(parser, name, is_public); - default: { - fprintf(stderr, "Expected function, struct, or enum after const '%s'\n", - name); - return NULL; - } - } + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after const declaration"); + return create_var_decl_stmt(parser->arena, name, type, value, false, + is_public, line, col); + } + + p_consume(parser, TOK_RIGHT_ARROW, "Expected '->' after const name"); + + // Handle complex constant types (functions, structs, enums) + switch (p_current(parser).type_) { + case TOK_FN: + return fn_stmt(parser, name, is_public, returns_ownership, takes_ownership); + case TOK_STRUCT: + return struct_stmt(parser, name, is_public); + case TOK_ENUM: + return enum_stmt(parser, name, is_public); + default: { + fprintf(stderr, "Expected function, struct, or enum after const '%s'\n", + name); + return NULL; + } + } } /** @@ -163,66 +162,65 @@ Stmt *const_stmt(Parser *parser, bool is_public, bool returns_ownership, */ Stmt *fn_stmt(Parser *parser, const char *name, bool is_public, bool returns_ownership, bool takes_ownership) { - int line = p_current(parser).line; - int col = p_current(parser).col; + int line = p_current(parser).line; + int col = p_current(parser).col; + + GrowableArray param_names, param_types; + if (!growable_array_init(¶m_names, parser->arena, 4, sizeof(char *)) || + !growable_array_init(¶m_types, parser->arena, 4, sizeof(Type *))) { + fprintf(stderr, "Failed to initialize parameter arrays.\n"); + return NULL; + } + + p_consume(parser, TOK_FN, "Expected 'fn' keyword"); + p_consume(parser, TOK_LPAREN, "Expected '(' after function name"); + + // Parse parameter list: param_name: param_type, ... + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RPAREN) { + if (p_current(parser).type_ != TOK_IDENTIFIER) { + fprintf(stderr, "Expected identifier for function parameter\n"); + return NULL; + } - GrowableArray param_names, param_types; - if (!growable_array_init(¶m_names, parser->arena, 4, sizeof(char *)) || - !growable_array_init(¶m_types, parser->arena, 4, sizeof(Type *))) { - fprintf(stderr, "Failed to initialize parameter arrays.\n"); - return NULL; + char *param_name = get_name(parser); + p_advance(parser); // Advance past the identifier token + + p_consume(parser, TOK_COLON, "Expected ':' after parameter name"); + + Type *param_type = parse_type(parser); + if (!param_type) { + fprintf(stderr, "Failed to parse type for parameter '%s'\n", param_name); + return NULL; + } + p_advance(parser); // Advance past the type token + + // Store parameter name and type + char **name_slot = (char **)growable_array_push(¶m_names); + Type **type_slot = (Type **)growable_array_push(¶m_types); + if (!name_slot || !type_slot) { + fprintf(stderr, "Out of memory while growing parameter arrays\n"); + return NULL; } - p_consume(parser, TOK_FN, "Expected 'fn' keyword"); - p_consume(parser, TOK_LPAREN, "Expected '(' after function name"); - - // Parse parameter list: param_name: param_type, ... - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RPAREN) { - if (p_current(parser).type_ != TOK_IDENTIFIER) { - fprintf(stderr, "Expected identifier for function parameter\n"); - return NULL; - } - - char *param_name = get_name(parser); - p_advance(parser); // Advance past the identifier token - - p_consume(parser, TOK_COLON, "Expected ':' after parameter name"); - - Type *param_type = parse_type(parser); - if (!param_type) { - fprintf(stderr, "Failed to parse type for parameter '%s'\n", - param_name); - return NULL; - } - p_advance(parser); // Advance past the type token - - // Store parameter name and type - char **name_slot = (char **)growable_array_push(¶m_names); - Type **type_slot = (Type **)growable_array_push(¶m_types); - if (!name_slot || !type_slot) { - fprintf(stderr, "Out of memory while growing parameter arrays\n"); - return NULL; - } - - *name_slot = param_name; - *type_slot = param_type; - - if (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); // Advance past the comma - } + *name_slot = param_name; + *type_slot = param_type; + + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); // Advance past the comma } + } - p_consume(parser, TOK_RPAREN, "Expected ')' after function parameters"); + p_consume(parser, TOK_RPAREN, "Expected ')' after function parameters"); - Type *return_type = parse_type(parser); - p_advance(parser); // Advance past the return type token + Type *return_type = parse_type(parser); + p_advance(parser); // Advance past the return type token - Stmt *body = block_stmt(parser); + Stmt *body = block_stmt(parser); - return create_func_decl_stmt( - parser->arena, name, (char **)param_names.data, - (AstNode **)param_types.data, param_names.count, return_type, is_public, - returns_ownership, takes_ownership, body, line, col); + return create_func_decl_stmt(parser->arena, name, (char **)param_names.data, + (AstNode **)param_types.data, param_names.count, + return_type, is_public, returns_ownership, + takes_ownership, body, line, col); } /** @@ -244,46 +242,45 @@ Stmt *fn_stmt(Parser *parser, const char *name, bool is_public, * @see create_enum_decl_stmt() */ Stmt *enum_stmt(Parser *parser, const char *name, bool is_public) { - int line = p_current(parser).line; - int col = p_current(parser).col; + int line = p_current(parser).line; + int col = p_current(parser).col; + + GrowableArray members; + if (!growable_array_init(&members, parser->arena, 4, sizeof(char *))) { + fprintf(stderr, "Failed to initialize enum members array.\n"); + return NULL; + } + + p_consume(parser, TOK_ENUM, "Expected 'enum' keyword"); + p_consume(parser, TOK_LBRACE, "Expected '{' after enum name"); + + // Parse enum members: member1, member2, ... + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { + if (p_current(parser).type_ != TOK_IDENTIFIER) { + fprintf(stderr, "Expected identifier for enum member\n"); + return NULL; + } - GrowableArray members; - if (!growable_array_init(&members, parser->arena, 4, sizeof(char *))) { - fprintf(stderr, "Failed to initialize enum members array.\n"); - return NULL; + char *member_name = get_name(parser); + p_advance(parser); // Advance past the identifier token + + char **slot = (char **)growable_array_push(&members); + if (!slot) { + fprintf(stderr, "Out of memory while growing enum members array\n"); + return NULL; } + *slot = member_name; - p_consume(parser, TOK_ENUM, "Expected 'enum' keyword"); - p_consume(parser, TOK_LBRACE, "Expected '{' after enum name"); - - // Parse enum members: member1, member2, ... - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { - if (p_current(parser).type_ != TOK_IDENTIFIER) { - fprintf(stderr, "Expected identifier for enum member\n"); - return NULL; - } - - char *member_name = get_name(parser); - p_advance(parser); // Advance past the identifier token - - char **slot = (char **)growable_array_push(&members); - if (!slot) { - fprintf(stderr, "Out of memory while growing enum members array\n"); - return NULL; - } - *slot = member_name; - - if (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); // Advance past the comma - } + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); // Advance past the comma } + } - p_consume(parser, TOK_RBRACE, "Expected '}' to end enum declaration"); - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after enum declaration"); + p_consume(parser, TOK_RBRACE, "Expected '}' to end enum declaration"); + p_consume(parser, TOK_SEMICOLON, "Expected semicolon after enum declaration"); - return create_enum_decl_stmt(parser->arena, name, (char **)members.data, - members.count, is_public, line, col); + return create_enum_decl_stmt(parser->arena, name, (char **)members.data, + members.count, is_public, line, col); } /** @@ -314,89 +311,83 @@ Stmt *enum_stmt(Parser *parser, const char *name, bool is_public) { * @see fn_stmt(), create_field_decl_stmt(), create_struct_decl_stmt() */ Stmt *struct_stmt(Parser *parser, const char *name, bool is_public) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - p_consume(parser, TOK_STRUCT, "Expected 'struct' keyword"); - p_consume(parser, TOK_LBRACE, "Expected '{' after struct name"); - - GrowableArray public_fields; - GrowableArray private_fields; - if (!growable_array_init(&public_fields, parser->arena, 4, - sizeof(Stmt *)) || - !growable_array_init(&private_fields, parser->arena, 4, - sizeof(Stmt *))) { - fprintf(stderr, "Failed to initialize field arrays.\n"); - return NULL; + int line = p_current(parser).line; + int col = p_current(parser).col; + + p_consume(parser, TOK_STRUCT, "Expected 'struct' keyword"); + p_consume(parser, TOK_LBRACE, "Expected '{' after struct name"); + + GrowableArray public_fields; + GrowableArray private_fields; + if (!growable_array_init(&public_fields, parser->arena, 4, sizeof(Stmt *)) || + !growable_array_init(&private_fields, parser->arena, 4, sizeof(Stmt *))) { + fprintf(stderr, "Failed to initialize field arrays.\n"); + return NULL; + } + + bool public_member = true; // default: everything is public + + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { + LumaTokenType tok = p_current(parser).type_; + + // Handle visibility modifiers + if (tok == TOK_PUBLIC || tok == TOK_PRIVATE) { + public_member = (tok == TOK_PUBLIC); + p_advance(parser); + p_consume(parser, TOK_COLON, "Expected ':' after visibility keyword"); + continue; } - bool public_member = true; // default: everything is public - - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { - LumaTokenType tok = p_current(parser).type_; - - // Handle visibility modifiers - if (tok == TOK_PUBLIC || tok == TOK_PRIVATE) { - public_member = (tok == TOK_PUBLIC); - p_advance(parser); - p_consume(parser, TOK_COLON, - "Expected ':' after visibility keyword"); - continue; - } - - // Parse a field or method - int field_line = p_current(parser).line; - int field_col = p_current(parser).col; - - char *field_name = get_name(parser); - Stmt *field_function = NULL; - Type *field_type = NULL; - p_advance(parser); - - // TODO: Add in a check to see if we have any function modifiers like - // returns_ownership or takes_ownership - - // Method: field_name = fn(...) - if (p_current(parser).type_ == TOK_EQUAL) { - p_consume(parser, TOK_EQUAL, "Expected '=' after field name"); - field_function = - fn_stmt(parser, field_name, public_member, false, false); - } else { - // Data field: field_name: Type - p_consume(parser, TOK_COLON, "Expected ':' after field name"); - field_type = parse_type(parser); - p_advance(parser); - } - - // Handle field separators - if (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); - } else if (p_current(parser).type_ != TOK_RBRACE) { - parser_error(parser, "Unexpected token", __FILE__, - "Expected ',' to separate struct fields", field_line, - field_col, 1); - return NULL; - } - - // Create field declaration and add to appropriate visibility list - Stmt *field_decl = create_field_decl_stmt( - parser->arena, field_name, field_type, field_function, - public_member, field_line, field_col); - Stmt **slot = public_member - ? (Stmt **)growable_array_push(&public_fields) - : (Stmt **)growable_array_push(&private_fields); - - *slot = field_decl; + // Parse a field or method + int field_line = p_current(parser).line; + int field_col = p_current(parser).col; + + char *field_name = get_name(parser); + Stmt *field_function = NULL; + Type *field_type = NULL; + p_advance(parser); + + // TODO: Add in a check to see if we have any function modifiers like + // returns_ownership or takes_ownership + + // Method: field_name = fn(...) + if (p_current(parser).type_ == TOK_EQUAL) { + p_consume(parser, TOK_EQUAL, "Expected '=' after field name"); + field_function = fn_stmt(parser, field_name, public_member, false, false); + } else { + // Data field: field_name: Type + p_consume(parser, TOK_COLON, "Expected ':' after field name"); + field_type = parse_type(parser); + p_advance(parser); } - p_consume(parser, TOK_RBRACE, "Expected '}' to end struct declaration"); - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after struct declaration"); + // Handle field separators + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); + } else if (p_current(parser).type_ != TOK_RBRACE) { + parser_error(parser, "Unexpected token", __FILE__, + "Expected ',' to separate struct fields", field_line, + field_col, 1); + return NULL; + } + + // Create field declaration and add to appropriate visibility list + Stmt *field_decl = create_field_decl_stmt( + parser->arena, field_name, field_type, field_function, public_member, + field_line, field_col); + Stmt **slot = public_member ? (Stmt **)growable_array_push(&public_fields) + : (Stmt **)growable_array_push(&private_fields); - return create_struct_decl_stmt( - parser->arena, name, (Stmt **)public_fields.data, public_fields.count, - (Stmt **)private_fields.data, private_fields.count, is_public, line, - col); + *slot = field_decl; + } + + p_consume(parser, TOK_RBRACE, "Expected '}' to end struct declaration"); + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after struct declaration"); + + return create_struct_decl_stmt( + parser->arena, name, (Stmt **)public_fields.data, public_fields.count, + (Stmt **)private_fields.data, private_fields.count, is_public, line, col); } /** @@ -418,34 +409,34 @@ Stmt *struct_stmt(Parser *parser, const char *name, bool is_public) { * @see parse_type(), parse_expr(), create_var_decl_stmt() */ Stmt *var_stmt(Parser *parser, bool is_public) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - p_consume(parser, TOK_VAR, "Expected 'let' keyword"); - const char *name = get_name(parser); - p_advance(parser); // Advance past the identifier token + int line = p_current(parser).line; + int col = p_current(parser).col; - p_consume(parser, TOK_COLON, "Expected ':' after variable name"); - Type *type = parse_type(parser); - p_advance(parser); // Advance past the type token - - if (p_current(parser).type_ != TOK_EQUAL) { - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after variable declaration"); - return create_var_decl_stmt(parser->arena, name, type, NULL, true, - is_public, line, col); - } + p_consume(parser, TOK_VAR, "Expected 'let' keyword"); + const char *name = get_name(parser); + p_advance(parser); // Advance past the identifier token - p_consume(parser, TOK_EQUAL, "Expected '=' after variable declaration"); + p_consume(parser, TOK_COLON, "Expected ':' after variable name"); + Type *type = parse_type(parser); + p_advance(parser); // Advance past the type token - Expr *value = parse_expr(parser, BP_LOWEST); + if (p_current(parser).type_ != TOK_EQUAL) { p_consume(parser, TOK_SEMICOLON, "Expected semicolon after variable declaration"); - - // const are not changable aka immutable and vars are mutable so we set - // is_mutable to true - return create_var_decl_stmt(parser->arena, name, type, value, true, + return create_var_decl_stmt(parser->arena, name, type, NULL, true, is_public, line, col); + } + + p_consume(parser, TOK_EQUAL, "Expected '=' after variable declaration"); + + Expr *value = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after variable declaration"); + + // const are not changable aka immutable and vars are mutable so we set + // is_mutable to true + return create_var_decl_stmt(parser->arena, name, type, value, true, is_public, + line, col); } /** @@ -465,18 +456,17 @@ Stmt *var_stmt(Parser *parser, bool is_public) { * @see parse_expr(), create_return_stmt() */ Stmt *return_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; + int line = p_current(parser).line; + int col = p_current(parser).col; - p_consume(parser, TOK_RETURN, "Expected 'return' keyword"); - Expr *value = NULL; - if (p_current(parser).type_ != TOK_SEMICOLON) { - value = parse_expr(parser, BP_LOWEST); - } - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after return statement"); + p_consume(parser, TOK_RETURN, "Expected 'return' keyword"); + Expr *value = NULL; + if (p_current(parser).type_ != TOK_SEMICOLON) { + value = parse_expr(parser, BP_LOWEST); + } + p_consume(parser, TOK_SEMICOLON, "Expected semicolon after return statement"); - return create_return_stmt(parser->arena, value, line, col); + return create_return_stmt(parser->arena, value, line, col); } /** @@ -498,41 +488,40 @@ Stmt *return_stmt(Parser *parser) { * @see parse_stmt(), create_block_stmt() */ Stmt *block_stmt(Parser *parser) { - p_consume(parser, TOK_LBRACE, "Expected '{' to start block statement"); + p_consume(parser, TOK_LBRACE, "Expected '{' to start block statement"); - GrowableArray block; - if (!growable_array_init(&block, parser->arena, 4, sizeof(Stmt *))) { - fprintf(stderr, "Failed to initialize block statement array.\n"); - return NULL; + GrowableArray block; + if (!growable_array_init(&block, parser->arena, 4, sizeof(Stmt *))) { + fprintf(stderr, "Failed to initialize block statement array.\n"); + return NULL; + } + + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { + Stmt *stmt = parse_stmt(parser); + if (!stmt) { + fprintf(stderr, "parse_stmt returned NULL inside block\n"); + continue; // or return NULL to fail the entire block } - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { - Stmt *stmt = parse_stmt(parser); - if (!stmt) { - fprintf(stderr, "parse_stmt returned NULL inside block\n"); - continue; // or return NULL to fail the entire block - } - - Stmt **slot = (Stmt **)growable_array_push(&block); - if (!slot) { - fprintf(stderr, - "Out of memory while growing block statement array\n"); - return NULL; - } - - *slot = stmt; + Stmt **slot = (Stmt **)growable_array_push(&block); + if (!slot) { + fprintf(stderr, "Out of memory while growing block statement array\n"); + return NULL; } - p_consume(parser, TOK_RBRACE, "Expected '}' to end block statement"); + *slot = stmt; + } - Stmt **stmts = (Stmt **)block.data; - if (block.count == 0) { - return create_block_stmt(parser->arena, NULL, 0, p_current(parser).line, - p_current(parser).col); - } + p_consume(parser, TOK_RBRACE, "Expected '}' to end block statement"); - return create_block_stmt(parser->arena, stmts, block.count, - p_current(parser).line, p_current(parser).col); + Stmt **stmts = (Stmt **)block.data; + if (block.count == 0) { + return create_block_stmt(parser->arena, NULL, 0, p_current(parser).line, + p_current(parser).col); + } + + return create_block_stmt(parser->arena, stmts, block.count, + p_current(parser).line, p_current(parser).col); } /** @@ -560,55 +549,54 @@ Stmt *block_stmt(Parser *parser) { * @see parse_expr(), block_stmt(), create_if_stmt() */ Stmt *if_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - if (p_current(parser).type_ != TOK_IF && - p_current(parser).type_ != TOK_ELIF) { - fprintf(stderr, "Expected 'if' or 'elif' keyword\n"); - return NULL; - } - p_consume(parser, p_current(parser).type_, - "Expected 'if' or 'elif' keyword"); - - p_consume(parser, TOK_LPAREN, "Expected '(' after 'if' keyword"); - Expr *condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, "Expected ')' after if condition"); - - Stmt *then_stmt = block_stmt(parser); - - // Collect all elif statements in a list/array instead of recursing - Stmt **elif_stmts = (Stmt **)arena_alloc(parser->arena, sizeof(Stmt *) * 4, - alignof(Stmt *)); - int elif_count = 0; - - while (p_has_tokens(parser) && p_current(parser).type_ == TOK_ELIF) { - int elif_line = p_current(parser).line; - int elif_col = p_current(parser).col; - - p_consume(parser, TOK_ELIF, "Expected 'elif' keyword"); - p_consume(parser, TOK_LPAREN, "Expected '(' after 'elif' keyword"); - - Expr *elif_condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, "Expected ')' after elif condition"); - - Stmt *elif_stmt = block_stmt(parser); - - // Store the elif condition and statement - elif_stmts[elif_count] = - create_if_stmt(parser->arena, elif_condition, elif_stmt, NULL, - elif_count, NULL, elif_line, elif_col); - elif_count++; - } - - Stmt *else_stmt = NULL; - if (p_current(parser).type_ == TOK_ELSE) { - p_consume(parser, TOK_ELSE, "Expected 'else' keyword"); - else_stmt = block_stmt(parser); - } - - return create_if_stmt(parser->arena, condition, then_stmt, elif_stmts, - elif_count, else_stmt, line, col); + int line = p_current(parser).line; + int col = p_current(parser).col; + + if (p_current(parser).type_ != TOK_IF && + p_current(parser).type_ != TOK_ELIF) { + fprintf(stderr, "Expected 'if' or 'elif' keyword\n"); + return NULL; + } + p_consume(parser, p_current(parser).type_, "Expected 'if' or 'elif' keyword"); + + p_consume(parser, TOK_LPAREN, "Expected '(' after 'if' keyword"); + Expr *condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, "Expected ')' after if condition"); + + Stmt *then_stmt = block_stmt(parser); + + // Collect all elif statements in a list/array instead of recursing + Stmt **elif_stmts = + (Stmt **)arena_alloc(parser->arena, sizeof(Stmt *) * 4, alignof(Stmt *)); + int elif_count = 0; + + while (p_has_tokens(parser) && p_current(parser).type_ == TOK_ELIF) { + int elif_line = p_current(parser).line; + int elif_col = p_current(parser).col; + + p_consume(parser, TOK_ELIF, "Expected 'elif' keyword"); + p_consume(parser, TOK_LPAREN, "Expected '(' after 'elif' keyword"); + + Expr *elif_condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, "Expected ')' after elif condition"); + + Stmt *elif_stmt = block_stmt(parser); + + // Store the elif condition and statement + elif_stmts[elif_count] = + create_if_stmt(parser->arena, elif_condition, elif_stmt, NULL, + elif_count, NULL, elif_line, elif_col); + elif_count++; + } + + Stmt *else_stmt = NULL; + if (p_current(parser).type_ == TOK_ELSE) { + p_consume(parser, TOK_ELSE, "Expected 'else' keyword"); + else_stmt = block_stmt(parser); + } + + return create_if_stmt(parser->arena, condition, then_stmt, elif_stmts, + elif_count, else_stmt, line, col); } /** @@ -630,13 +618,13 @@ Stmt *if_stmt(Parser *parser) { * @see block_stmt(), create_infinite_loop_stmt() */ Stmt *infinite_loop_stmt(Parser *parser, int line, int col) { - Stmt *body = block_stmt(parser); - if (!body) { - parser_error(parser, "Syntax Error", __FILE__, - "Expected block statement", line, col, 1); - return NULL; - } - return create_infinite_loop_stmt(parser->arena, body, line, col); + Stmt *body = block_stmt(parser); + if (!body) { + parser_error(parser, "Syntax Error", __FILE__, "Expected block statement", + line, col, 1); + return NULL; + } + return create_infinite_loop_stmt(parser->arena, body, line, col); } /** @@ -657,17 +645,17 @@ Stmt *infinite_loop_stmt(Parser *parser, int line, int col) { * @see create_var_decl_stmt() */ Stmt *loop_init(Parser *parser, int line, int col) { - const char *name = get_name(parser); - p_advance(parser); // Advance past the identifier token + const char *name = get_name(parser); + p_advance(parser); // Advance past the identifier token - p_consume(parser, TOK_COLON, "Expected ':' after loop initializer"); - Type *type = parse_type(parser); - p_advance(parser); // Advance past the type token + p_consume(parser, TOK_COLON, "Expected ':' after loop initializer"); + Type *type = parse_type(parser); + p_advance(parser); // Advance past the type token - p_consume(parser, TOK_EQUAL, "Expected '=' after loop initializer"); - Expr *initializer = parse_expr(parser, BP_LOWEST); - return create_var_decl_stmt(parser->arena, name, type, initializer, true, - false, line, col); + p_consume(parser, TOK_EQUAL, "Expected '=' after loop initializer"); + Expr *initializer = parse_expr(parser, BP_LOWEST); + return create_var_decl_stmt(parser->arena, name, type, initializer, true, + false, line, col); } /** @@ -693,55 +681,53 @@ Stmt *loop_init(Parser *parser, int line, int col) { * @see loop_init(), parse_expr(), block_stmt(), create_for_loop_stmt() */ Stmt *for_loop_stmt(Parser *parser, int line, int col) { - GrowableArray intializers; - if (!growable_array_init(&intializers, parser->arena, 4, sizeof(Expr *))) { - fprintf(stderr, "Failed to initialize loop initializers array.\n"); - return NULL; + GrowableArray intializers; + if (!growable_array_init(&intializers, parser->arena, 4, sizeof(Expr *))) { + fprintf(stderr, "Failed to initialize loop initializers array.\n"); + return NULL; + } + + p_consume(parser, TOK_LBRACKET, "Expected '[' after 'loop' keyword"); + // Parse initializers: i: int = 0, j: int = 1 + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACKET) { + Expr *init = loop_init(parser, line, col); + if (!init) { + fprintf(stderr, "Failed to parse loop initializer\n"); + return NULL; } - p_consume(parser, TOK_LBRACKET, "Expected '[' after 'loop' keyword"); - // Parse initializers: i: int = 0, j: int = 1 - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACKET) { - Expr *init = loop_init(parser, line, col); - if (!init) { - fprintf(stderr, "Failed to parse loop initializer\n"); - return NULL; - } - - Expr **slot = (Expr **)growable_array_push(&intializers); - if (!slot) { - fprintf(stderr, - "Out of memory while growing loop initializers array\n"); - return NULL; - } - - *slot = init; - - if (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); // Advance past the comma - } - } - p_consume(parser, TOK_RBRACKET, "Expected ']' after loop initializer"); - - p_consume(parser, TOK_LPAREN, "Expected '(' after loop initializer"); - Expr *condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, "Expected ')' after loop initializer"); - - // Check for the optional condition - Expr *optional_condition = NULL; - if (p_current(parser).type_ == TOK_COLON) { - p_consume(parser, TOK_COLON, "Expected ':' after loop condition"); - p_consume(parser, TOK_LPAREN, - "Expected '(' after ':' in loop statement"); - optional_condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, - "Expected ')' after optional condition in loop statement"); + Expr **slot = (Expr **)growable_array_push(&intializers); + if (!slot) { + fprintf(stderr, "Out of memory while growing loop initializers array\n"); + return NULL; } - Stmt *body = block_stmt(parser); - return create_for_loop_stmt(parser->arena, (AstNode **)intializers.data, - intializers.count, condition, - optional_condition, body, line, col); + *slot = init; + + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); // Advance past the comma + } + } + p_consume(parser, TOK_RBRACKET, "Expected ']' after loop initializer"); + + p_consume(parser, TOK_LPAREN, "Expected '(' after loop initializer"); + Expr *condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, "Expected ')' after loop initializer"); + + // Check for the optional condition + Expr *optional_condition = NULL; + if (p_current(parser).type_ == TOK_COLON) { + p_consume(parser, TOK_COLON, "Expected ':' after loop condition"); + p_consume(parser, TOK_LPAREN, "Expected '(' after ':' in loop statement"); + optional_condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, + "Expected ')' after optional condition in loop statement"); + } + + Stmt *body = block_stmt(parser); + return create_for_loop_stmt(parser->arena, (AstNode **)intializers.data, + intializers.count, condition, optional_condition, + body, line, col); } /** @@ -768,41 +754,40 @@ Stmt *for_loop_stmt(Parser *parser, int line, int col) { * create_loop_stmt() */ Stmt *loop_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - p_consume(parser, TOK_LOOP, "Expected 'loop' keyword"); - - if (p_current(parser).type_ == - TOK_LBRACE) { // Aka we have a infinite loop 'loop { ... }' - return infinite_loop_stmt(parser, line, col); - } - - if (p_current(parser).type_ == - TOK_LBRACKET) { // Aka we have a for loop 'loop [ ... ] { ... }' - return for_loop_stmt(parser, line, col); - } - - // else we have a standard while loop 'loop (condition) { ... }' - p_consume(parser, TOK_LPAREN, "Expected '(' after 'loop' keyword"); - Expr *condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, "Expected ')' after loop condition"); - - // check if there is an optional condition 'loop (condition) : - // (optional_condition) { ... }' - Expr *optional_condition = NULL; - if (p_current(parser).type_ == TOK_COLON) { - p_advance(parser); // Advance past the colon - p_consume(parser, TOK_LPAREN, - "Expected '(' after ':' in loop statement"); - optional_condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, - "Expected ')' after optional condition in loop statement"); - } - - Stmt *body = block_stmt(parser); - return create_loop_stmt(parser->arena, condition, optional_condition, body, - line, col); + int line = p_current(parser).line; + int col = p_current(parser).col; + + p_consume(parser, TOK_LOOP, "Expected 'loop' keyword"); + + if (p_current(parser).type_ == + TOK_LBRACE) { // Aka we have a infinite loop 'loop { ... }' + return infinite_loop_stmt(parser, line, col); + } + + if (p_current(parser).type_ == + TOK_LBRACKET) { // Aka we have a for loop 'loop [ ... ] { ... }' + return for_loop_stmt(parser, line, col); + } + + // else we have a standard while loop 'loop (condition) { ... }' + p_consume(parser, TOK_LPAREN, "Expected '(' after 'loop' keyword"); + Expr *condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, "Expected ')' after loop condition"); + + // check if there is an optional condition 'loop (condition) : + // (optional_condition) { ... }' + Expr *optional_condition = NULL; + if (p_current(parser).type_ == TOK_COLON) { + p_advance(parser); // Advance past the colon + p_consume(parser, TOK_LPAREN, "Expected '(' after ':' in loop statement"); + optional_condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, + "Expected ')' after optional condition in loop statement"); + } + + Stmt *body = block_stmt(parser); + return create_loop_stmt(parser->arena, condition, optional_condition, body, + line, col); } /** @@ -825,45 +810,43 @@ Stmt *loop_stmt(Parser *parser) { * @see parse_expr(), create_print_stmt() */ Stmt *print_stmt(Parser *parser, bool ln) { - int line = p_current(parser).line; - int col = p_current(parser).col; + int line = p_current(parser).line; + int col = p_current(parser).col; - p_consume(parser, ln ? TOK_PRINTLN : TOK_PRINT, - "Expected 'output' or 'outputln' keyword"); - p_consume(parser, TOK_LPAREN, "Expected '(' after print statement"); + p_consume(parser, ln ? TOK_PRINTLN : TOK_PRINT, + "Expected 'output' or 'outputln' keyword"); + p_consume(parser, TOK_LPAREN, "Expected '(' after print statement"); - GrowableArray expressions; - if (!growable_array_init(&expressions, parser->arena, 4, sizeof(Expr *))) { - fprintf(stderr, "Failed to initialize print expressions array.\n"); - return NULL; + GrowableArray expressions; + if (!growable_array_init(&expressions, parser->arena, 4, sizeof(Expr *))) { + fprintf(stderr, "Failed to initialize print expressions array.\n"); + return NULL; + } + + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RPAREN) { + Expr *expr = parse_expr(parser, BP_LOWEST); + if (!expr) { + fprintf(stderr, "Failed to parse expression in print statement\n"); + return NULL; } - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RPAREN) { - Expr *expr = parse_expr(parser, BP_LOWEST); - if (!expr) { - fprintf(stderr, "Failed to parse expression in print statement\n"); - return NULL; - } - - Expr **slot = (Expr **)growable_array_push(&expressions); - if (!slot) { - fprintf(stderr, - "Out of memory while growing print expressions array\n"); - return NULL; - } - - *slot = expr; - - if (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); // Advance past the comma - } + Expr **slot = (Expr **)growable_array_push(&expressions); + if (!slot) { + fprintf(stderr, "Out of memory while growing print expressions array\n"); + return NULL; } - p_consume(parser, TOK_RPAREN, "Expected ')' to end print statement"); - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after print statement"); - return create_print_stmt(parser->arena, (Expr **)expressions.data, - expressions.count, ln, line, col); + *slot = expr; + + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); // Advance past the comma + } + } + p_consume(parser, TOK_RPAREN, "Expected ')' to end print statement"); + p_consume(parser, TOK_SEMICOLON, "Expected semicolon after print statement"); + + return create_print_stmt(parser->arena, (Expr **)expressions.data, + expressions.count, ln, line, col); } /** @@ -888,246 +871,243 @@ Stmt *print_stmt(Parser *parser, bool ln) { * @see create_break_continue_stmt() */ Stmt *break_continue_stmt(Parser *parser, bool is_continue) { - int line = p_current(parser).line; - int col = p_current(parser).col; + int line = p_current(parser).line; + int col = p_current(parser).col; - p_consume(parser, is_continue ? TOK_CONTINUE : TOK_BREAK, - is_continue ? "Expected 'continue' keyword" - : "Expected 'break' keyword"); - p_consume(parser, TOK_SEMICOLON, - "Expected semicolon after break/continue statement"); + p_consume(parser, is_continue ? TOK_CONTINUE : TOK_BREAK, + is_continue ? "Expected 'continue' keyword" + : "Expected 'break' keyword"); + p_consume(parser, TOK_SEMICOLON, + "Expected semicolon after break/continue statement"); - return create_break_continue_stmt(parser->arena, is_continue, line, col); + return create_break_continue_stmt(parser->arena, is_continue, line, col); } Stmt *defer_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; + int line = p_current(parser).line; + int col = p_current(parser).col; + + p_consume(parser, TOK_DEFER, "Expected 'defer' keyword"); + Stmt *stmt = parse_stmt(parser); + if (!stmt) { + parser_error(parser, "Syntax Error", __FILE__, + "Expected statement after 'defer'", line, col, 1); + return NULL; + } + + return create_defer_stmt(parser->arena, stmt, line, col); +} - p_consume(parser, TOK_DEFER, "Expected 'defer' keyword"); - Stmt *stmt = parse_stmt(parser); - if (!stmt) { - parser_error(parser, "Syntax Error", __FILE__, - "Expected statement after 'defer'", line, col, 1); - return NULL; +Stmt *switch_stmt(Parser *parser) { + int line = p_current(parser).line; + int col = p_current(parser).col; + + p_consume(parser, TOK_SWITCH, "Expected 'switch' keyword"); + p_consume(parser, TOK_LPAREN, "Expected '(' after 'switch' keyword"); + Expr *condition = parse_expr(parser, BP_LOWEST); + p_consume(parser, TOK_RPAREN, "Expected ')' after switch condition"); + p_consume(parser, TOK_LBRACE, "Expected '{' to start switch body"); + + GrowableArray cases; + if (!growable_array_init(&cases, parser->arena, 4, sizeof(Stmt *))) { + fprintf(stderr, "Failed to initialize switch cases array.\n"); + return NULL; + } + + Stmt *default_case = NULL; + + // Parse all cases and default + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { + int case_line = p_current(parser).line; + int case_col = p_current(parser).col; + + // Check if this is the default case + if (p_current(parser).type_ == TOK_IDENTIFIER && + strcmp(get_name(parser), "_") == 0) { + // Handle default case: _: { ... } + p_advance(parser); // consume '_' + p_consume(parser, TOK_RIGHT_ARROW, + "Expected '=>' after default case '_'"); + + Stmt *default_body; + if (p_current(parser).type_ == TOK_LBRACE) { + default_body = block_stmt(parser); + } else { + default_body = parse_stmt(parser); + } + + default_case = + create_default_stmt(parser->arena, default_body, case_line, case_col); + continue; } - return create_defer_stmt(parser->arena, stmt, line, col); -} + // Parse case values: can be single value or comma-separated list + GrowableArray case_values; + if (!growable_array_init(&case_values, parser->arena, 4, sizeof(Expr *))) { + fprintf(stderr, "Failed to initialize case values array.\n"); + return NULL; + } -Stmt *switch_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; - - p_consume(parser, TOK_SWITCH, "Expected 'switch' keyword"); - p_consume(parser, TOK_LPAREN, "Expected '(' after 'switch' keyword"); - Expr *condition = parse_expr(parser, BP_LOWEST); - p_consume(parser, TOK_RPAREN, "Expected ')' after switch condition"); - p_consume(parser, TOK_LBRACE, "Expected '{' to start switch body"); - - GrowableArray cases; - if (!growable_array_init(&cases, parser->arena, 4, sizeof(Stmt *))) { - fprintf(stderr, "Failed to initialize switch cases array.\n"); + // Parse first case value + Expr *case_expr = parse_expr(parser, BP_LOWEST); + if (!case_expr) { + fprintf(stderr, "Failed to parse case expression\n"); + return NULL; + } + + Expr **value_slot = (Expr **)growable_array_push(&case_values); + if (!value_slot) { + fprintf(stderr, "Out of memory while growing case values array\n"); + return NULL; + } + *value_slot = case_expr; + + // Parse additional case values if comma-separated + while (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); // consume comma + + Expr *additional_case = parse_expr(parser, BP_LOWEST); + if (!additional_case) { + fprintf(stderr, "Failed to parse additional case expression\n"); return NULL; + } + + Expr **additional_slot = (Expr **)growable_array_push(&case_values); + if (!additional_slot) { + fprintf(stderr, "Out of memory while growing case values array\n"); + return NULL; + } + *additional_slot = additional_case; + } + + p_consume(parser, TOK_RIGHT_ARROW, "Expected '=>' after case value(s)"); + + // Parse case body (either block or single statement) + Stmt *case_body; + if (p_current(parser).type_ == TOK_LBRACE) { + case_body = block_stmt(parser); + } else { + // Single statement + case_body = parse_stmt(parser); + } + + if (!case_body) { + fprintf(stderr, "Failed to parse case body\n"); + return NULL; } - Stmt *default_case = NULL; - - // Parse all cases and default - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACE) { - int case_line = p_current(parser).line; - int case_col = p_current(parser).col; - - // Check if this is the default case - if (p_current(parser).type_ == TOK_IDENTIFIER && - strcmp(get_name(parser), "_") == 0) { - // Handle default case: _: { ... } - p_advance(parser); // consume '_' - p_consume(parser, TOK_RIGHT_ARROW, - "Expected '=>' after default case '_'"); - - Stmt *default_body; - if (p_current(parser).type_ == TOK_LBRACE) { - default_body = block_stmt(parser); - } else { - default_body = parse_stmt(parser); - } - - default_case = create_default_stmt(parser->arena, default_body, - case_line, case_col); - continue; - } - - // Parse case values: can be single value or comma-separated list - GrowableArray case_values; - if (!growable_array_init(&case_values, parser->arena, 4, - sizeof(Expr *))) { - fprintf(stderr, "Failed to initialize case values array.\n"); - return NULL; - } - - // Parse first case value - Expr *case_expr = parse_expr(parser, BP_LOWEST); - if (!case_expr) { - fprintf(stderr, "Failed to parse case expression\n"); - return NULL; - } - - Expr **value_slot = (Expr **)growable_array_push(&case_values); - if (!value_slot) { - fprintf(stderr, "Out of memory while growing case values array\n"); - return NULL; - } - *value_slot = case_expr; - - // Parse additional case values if comma-separated - while (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); // consume comma - - Expr *additional_case = parse_expr(parser, BP_LOWEST); - if (!additional_case) { - fprintf(stderr, "Failed to parse additional case expression\n"); - return NULL; - } - - Expr **additional_slot = (Expr **)growable_array_push(&case_values); - if (!additional_slot) { - fprintf(stderr, - "Out of memory while growing case values array\n"); - return NULL; - } - *additional_slot = additional_case; - } - - p_consume(parser, TOK_RIGHT_ARROW, "Expected '=>' after case value(s)"); - - // Parse case body (either block or single statement) - Stmt *case_body; - if (p_current(parser).type_ == TOK_LBRACE) { - case_body = block_stmt(parser); - } else { - // Single statement - case_body = parse_stmt(parser); - } - - if (!case_body) { - fprintf(stderr, "Failed to parse case body\n"); - return NULL; - } - - // Create case statement and add to cases array - Stmt *case_stmt = - create_case_stmt(parser->arena, (AstNode **)case_values.data, - case_values.count, case_body, case_line, case_col); - - Stmt **case_slot = (Stmt **)growable_array_push(&cases); - if (!case_slot) { - fprintf(stderr, "Out of memory while growing cases array\n"); - return NULL; - } - *case_slot = case_stmt; + // Create case statement and add to cases array + Stmt *case_stmt = + create_case_stmt(parser->arena, (AstNode **)case_values.data, + case_values.count, case_body, case_line, case_col); + + Stmt **case_slot = (Stmt **)growable_array_push(&cases); + if (!case_slot) { + fprintf(stderr, "Out of memory while growing cases array\n"); + return NULL; } + *case_slot = case_stmt; + } - p_consume(parser, TOK_RBRACE, "Expected '}' to end switch statement"); + p_consume(parser, TOK_RBRACE, "Expected '}' to end switch statement"); - return create_switch_stmt(parser->arena, condition, (AstNode **)cases.data, - cases.count, (AstNode *)default_case, line, col); + return create_switch_stmt(parser->arena, condition, (AstNode **)cases.data, + cases.count, (AstNode *)default_case, line, col); } // Impl [fun1: void, fun2: void, ...] -> [struct1, struct2, ...] Stmt *impl_stmt(Parser *parser) { - int line = p_current(parser).line; - int col = p_current(parser).col; + int line = p_current(parser).line; + int col = p_current(parser).col; + + GrowableArray function_name_list, function_name_types; + GrowableArray struct_name_list; + + p_consume(parser, TOK_IMPL, "Expected 'impl' keyword"); + p_consume(parser, TOK_LBRACKET, "Expected '[' after 'impl' keyword"); + + if (!growable_array_init(&function_name_list, parser->arena, 4, + sizeof(Stmt *)) || + !growable_array_init(&function_name_types, parser->arena, 4, + sizeof(Stmt *))) { + fprintf(stderr, "Failed to initialize impl function array list\n"); + return NULL; + } + + if (!growable_array_init(&struct_name_list, parser->arena, 4, + sizeof(Stmt *))) { + fprintf(stderr, "Failed to initialize impl struct array list"); + return NULL; + } + + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACKET) { + int list_line = p_current(parser).line; + int list_col = p_current(parser).col; + + if (p_current(parser).type_ != TOK_IDENTIFIER) { + parser_error(parser, "SyntaxError", parser->file_path, + "Required identifier", list_line, list_col, + p_current(parser).length); + return NULL; + } + + char *function_list_name = get_name(parser); + p_advance(parser); + p_consume(parser, TOK_COLON, "Expected ':' after parameter name"); - GrowableArray function_name_list, function_name_types; - GrowableArray struct_name_list; + Type *function_list_type = parse_type(parser); + if (!function_list_type) { + fprintf(stderr, "Failed to parse type for parameter '%s'\n", + function_list_name); + return NULL; + } + p_advance(parser); - p_consume(parser, TOK_IMPL, "Expected 'impl' keyword"); - p_consume(parser, TOK_LBRACKET, "Expected '[' after 'impl' keyword"); + char **name_identifier = (char **)growable_array_push(&function_name_list); + Type **type_specifier = (Type **)growable_array_push(&function_name_types); - if (!growable_array_init(&function_name_list, parser->arena, 4, - sizeof(Stmt *))) { - fprintf(stderr, "Failed to initialize impl function array list"); - return NULL; + if (!name_identifier || !type_specifier) { + fprintf(stderr, "Out of memory while growing function list for impl\n"); + return NULL; } - if (!growable_array_init(&struct_name_list, parser->arena, 4, - sizeof(Stmt *))) { - fprintf(stderr, "Failed to initialize impl struct array list"); - return NULL; + *name_identifier = function_list_name; + *type_specifier = function_list_type; + + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); + } + } + p_consume(parser, TOK_RBRACKET, "Expected ']' after function parameters"); + p_consume(parser, TOK_RIGHT_ARROW, "Expected '->' after function list"); + p_consume(parser, TOK_LBRACKET, "Expected '[' after '->'"); + + while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACKET) { + int list_line = p_current(parser).line; + int list_col = p_current(parser).col; + + if (p_current(parser).type_ != TOK_IDENTIFIER) { + parser_error(parser, "SyntaxError", parser->file_path, + "Required identifier", list_line, list_col, + p_current(parser).length); + return NULL; } + char *struct_list_name = get_name(parser); + p_advance(parser); + char **struct_identifier = (char **)growable_array_push(&struct_name_list); - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACKET) { - int list_line = p_current(parser).line; - int list_col = p_current(parser).col; - - if (p_current(parser).type_ != TOK_IDENTIFIER) { - parser_error(parser, "SyntaxError", parser->file_path, - "Required identifier", list_line, list_col, - p_current(parser).length); - return NULL; - } - char *function_list_name = get_name(parser); - p_advance(parser); - p_consume(parser, TOK_COLON, "Expected ':' after parameter name"); - - Type *function_list_type = parse_type(parser); - if (!function_list_type) { - fprintf(stderr, "Failed to parse type for parameter '%s'\n", - function_list_name); - return NULL; - } - p_advance(parser); - - char **name_identifier = - (char **)growable_array_push(&function_name_list); - Type **type_specifier = - (Type **)growable_array_push(&function_name_types); - - if (!name_identifier || !type_specifier) { - fprintf(stderr, - "Out of memory while growing function list for impl\n"); - return NULL; - } - *name_identifier = function_list_name; - *type_specifier = function_list_type; - - if (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); - } + if (!struct_identifier) { + fprintf(stderr, "Out of memory while growing struct list for impl\n"); + return NULL; } - p_consume(parser, TOK_RBRACKET, "Expected ']' after function parameters"); - p_consume(parser, TOK_RIGHT_ARROW, "Expected '->' after function list"); - p_consume(parser, TOK_LBRACKET, "Expected '[' after '->'"); - - while (p_has_tokens(parser) && p_current(parser).type_ != TOK_RBRACKET) { - int list_line = p_current(parser).line; - int list_col = p_current(parser).col; - - if (p_current(parser).type_ != TOK_IDENTIFIER) { - parser_error(parser, "SyntaxError", parser->file_path, - "Required identifier", list_line, list_col, - p_current(parser).length); - return NULL; - } - char *struct_list_name = get_name(parser); - p_advance(parser); - char **struct_identifier = - (char **)growable_array_push(&struct_name_list); - - if (!struct_identifier) { - fprintf(stderr, - "Out of memory while growing struct list for impl\n"); - return NULL; - } - *struct_identifier = struct_list_name; - if (p_current(parser).type_ == TOK_COMMA) { - p_advance(parser); - } + *struct_identifier = struct_list_name; + if (p_current(parser).type_ == TOK_COMMA) { + p_advance(parser); } - p_consume(parser, TOK_RBRACKET, "Expected ']' after struct list"); - Stmt *body = block_stmt(parser); - return create_impl_stmt(parser->arena, (char **)function_name_list.data, - (AstNode **)function_name_types.data, body, - (char **)struct_name_list.data, - function_name_list.count, struct_name_list.count, - line, col); + } + p_consume(parser, TOK_RBRACKET, "Expected ']' after struct list"); + Stmt *body = block_stmt(parser); + return create_impl_stmt(parser->arena, (char **)function_name_list.data, + (AstNode **)function_name_types.data, body, + (char **)struct_name_list.data, + function_name_list.count, struct_name_list.count, + line, col); } diff --git a/tests/test.lx b/tests/test.lx index 4a33641c..73936da6 100644 --- a/tests/test.lx +++ b/tests/test.lx @@ -1,12 +1,95 @@ @module "main" -impl [func1: int, func2: char] -> [theStruct] { +// Define a struct +const Point -> struct { +pub: + x: int, + y: int +}; -} - -pub const main -> fn () int { +// Define another struct +const Rectangle -> struct { +pub: + top_left: Point, + bottom_right: Point +}; +// Implement methods for Point +impl [distance: float, add: Point, scale: Point] -> [Point] { + const distance -> fn (p1: Point, p2: Point) float { + let dx: int = p2.x - p1.x; + let dy: int = p2.y - p1.y; + return cast(dx * dx + dy * dy); + } + + const add -> fn (p1: Point, p2: Point) Point { + let result: Point; + result.x = p1.x + p2.x; + result.y = p1.y + p2.y; + return result; + } + + const scale -> fn (p: Point, factor: int) Point { + let result: Point; + result.x = p.x * factor; + result.y = p.y * factor; + return result; + } +} +// Implement methods for Rectangle +impl [area: int, contains: bool] -> [Rectangle] { + const area -> fn (rect: Rectangle) int { + let width: int = rect.bottom_right.x - rect.top_left.x; + let height: int = rect.bottom_right.y - rect.top_left.y; + return width * height; + } + + const contains -> fn (rect: Rectangle, p: Point) bool { + return p.x >= rect.top_left.x && + p.x <= rect.bottom_right.x && + p.y >= rect.top_left.y && + p.y <= rect.bottom_right.y; + } +} -return 0; +// Implement shared methods for both structs +impl [print: void] -> [Point, Rectangle] { + const print -> fn (p: Point) void { + output("Point(", p.x, ", ", p.y, ")\n"); + } + + const print -> fn (rect: Rectangle) void { + output("Rectangle(", rect.top_left.x, ", ", rect.top_left.y, + " to ", rect.bottom_right.x, ", ", rect.bottom_right.y, ")\n"); + } } + +pub const main -> fn () int { + let p1: Point; + p1.x = 10; + p1.y = 20; + + let p2: Point; + p2.x = 30; + p2.y = 40; + + let p3: Point = add(p1, p2); + let dist: float = distance(p1, p2); + + print(p1); + print(p2); + print(p3); + + println("Distance: ", dist); + + let rect: Rectangle; + rect.top_left = p1; + rect.bottom_right = p2; + + print(rect); + println("Area: ", area(rect)); + println("Contains p3: ", contains(rect, p3)); + + return 0; +} \ No newline at end of file From e95f6702348f4a8cc780f1895c4fcc300a3196e1 Mon Sep 17 00:00:00 2001 From: TheDevConnor Date: Sun, 19 Oct 2025 04:44:32 -0400 Subject: [PATCH 6/6] Added in function for structs into the tc and codegen! --- src/llvm/expr.c | 79 +++++++-- src/llvm/llvm.h | 5 +- src/llvm/struct.c | 269 ++++++++++++++++++++++++------ src/parser/stmt.c | 20 ++- src/typechecker/expr.c | 132 +++++++++++++-- src/typechecker/stmt.c | 369 ++++++++++++++++++++++++++++++----------- tests/test.lx | 99 ++--------- 7 files changed, 701 insertions(+), 272 deletions(-) diff --git a/src/llvm/expr.c b/src/llvm/expr.c index e4ec2d1e..a44f1084 100644 --- a/src/llvm/expr.c +++ b/src/llvm/expr.c @@ -433,35 +433,82 @@ LLVMValueRef codegen_expr_unary(CodeGenContext *ctx, AstNode *node) { } LLVMValueRef codegen_expr_call(CodeGenContext *ctx, AstNode *node) { - LLVMValueRef callee = codegen_expr(ctx, node->expr.call.callee); - if (!callee) - return NULL; - - LLVMValueRef *args = (LLVMValueRef *)arena_alloc( - ctx->arena, sizeof(LLVMValueRef) * node->expr.call.arg_count, - alignof(LLVMValueRef)); - - for (size_t i = 0; i < node->expr.call.arg_count; i++) { - args[i] = codegen_expr(ctx, node->expr.call.args[i]); - if (!args[i]) + AstNode *callee = node->expr.call.callee; + LLVMValueRef callee_value = NULL; + LLVMValueRef *args = NULL; + size_t arg_count = node->expr.call.arg_count; + + // Check if this is a method call (obj.method()) + if (callee->type == AST_EXPR_MEMBER && !callee->expr.member.is_compiletime) { + // Method call: obj.method(arg1, arg2) + // The typechecker has already injected 'self' as the first argument + // So we just need to codegen all arguments as-is + + // Get the method function + const char *member_name = callee->expr.member.member; + + // Look up the method in the current module + LLVMModuleRef current_llvm_module = + ctx->current_module ? ctx->current_module->module : ctx->module; + LLVMValueRef method_func = LLVMGetNamedFunction(current_llvm_module, member_name); + + if (!method_func) { + fprintf(stderr, "Error: Method '%s' not found\n", member_name); return NULL; + } + + callee_value = method_func; + + // Allocate space for all arguments (including injected self) + args = (LLVMValueRef *)arena_alloc( + ctx->arena, sizeof(LLVMValueRef) * arg_count, + alignof(LLVMValueRef)); + + // Codegen all arguments (self is already in args[0] from typechecker) + for (size_t i = 0; i < arg_count; i++) { + args[i] = codegen_expr(ctx, node->expr.call.args[i]); + if (!args[i]) { + fprintf(stderr, "Error: Failed to generate argument %zu for method '%s'\n", + i, member_name); + return NULL; + } + } + + } else { + // Regular function call or compile-time member access (module::func) + callee_value = codegen_expr(ctx, callee); + if (!callee_value) { + return NULL; + } + + // Allocate space for arguments + args = (LLVMValueRef *)arena_alloc( + ctx->arena, sizeof(LLVMValueRef) * arg_count, + alignof(LLVMValueRef)); + + for (size_t i = 0; i < arg_count; i++) { + args[i] = codegen_expr(ctx, node->expr.call.args[i]); + if (!args[i]) { + return NULL; + } + } } // Get the function type to check return type - LLVMTypeRef func_type = LLVMGlobalGetValueType(callee); + LLVMTypeRef func_type = LLVMGlobalGetValueType(callee_value); LLVMTypeRef return_type = LLVMGetReturnType(func_type); // Check if return type is void if (LLVMGetTypeKind(return_type) == LLVMVoidTypeKind) { // For void functions, don't assign a name to the call - LLVMBuildCall2(ctx->builder, func_type, callee, args, - node->expr.call.arg_count, ""); + LLVMBuildCall2(ctx->builder, func_type, callee_value, args, + arg_count, ""); // Return a void constant since we can't return NULL return LLVMConstNull(LLVMVoidTypeInContext(ctx->context)); } else { // For non-void functions, assign a name as usual - return LLVMBuildCall2(ctx->builder, func_type, callee, args, - node->expr.call.arg_count, "call"); + return LLVMBuildCall2(ctx->builder, func_type, callee_value, args, + arg_count, "call"); } } diff --git a/src/llvm/llvm.h b/src/llvm/llvm.h index dabb9bd3..47679748 100644 --- a/src/llvm/llvm.h +++ b/src/llvm/llvm.h @@ -200,7 +200,9 @@ void add_struct_type(CodeGenContext *ctx, StructInfo *struct_info); int get_field_index(StructInfo *struct_info, const char *field_name); bool is_field_access_allowed(CodeGenContext *ctx, StructInfo *struct_info, int field_index); - +LLVMValueRef codegen_struct_method(CodeGenContext *ctx, AstNode *func_node, + StructInfo *struct_info, const char *method_name, + bool is_public); // Enhanced member access (handles both struct.field and module.symbol) LLVMValueRef codegen_expr_member_access_enhanced(CodeGenContext *ctx, AstNode *node); @@ -284,6 +286,7 @@ LLVMValueRef codegen_expr_identifier(CodeGenContext *ctx, AstNode *node); LLVMValueRef codegen_expr_binary(CodeGenContext *ctx, AstNode *node); LLVMValueRef codegen_expr_unary(CodeGenContext *ctx, AstNode *node); LLVMValueRef codegen_expr_call(CodeGenContext *ctx, AstNode *node); +// LLVMValueRef codegen_method_call(CodeGenContext *ctx, AstNode *call_node); LLVMValueRef codegen_expr_assignment(CodeGenContext *ctx, AstNode *node); LLVMValueRef codegen_expr_array(CodeGenContext *ctx, AstNode *node); LLVMValueRef codegen_expr_index(CodeGenContext *ctx, AstNode *node); diff --git a/src/llvm/struct.c b/src/llvm/struct.c index d299a78e..0f234b57 100644 --- a/src/llvm/struct.c +++ b/src/llvm/struct.c @@ -36,18 +36,32 @@ bool is_field_access_allowed(CodeGenContext *ctx, StructInfo *struct_info, return struct_info->field_is_public[field_index]; } -// Handle struct declaration LLVMValueRef codegen_stmt_struct(CodeGenContext *ctx, AstNode *node) { if (!node || node->type != AST_STMT_STRUCT) { return NULL; } const char *struct_name = node->stmt.struct_decl.name; - size_t total_members = node->stmt.struct_decl.public_count + - node->stmt.struct_decl.private_count; + size_t public_count = node->stmt.struct_decl.public_count; + size_t private_count = node->stmt.struct_decl.private_count; + + // Separate data fields from methods + size_t data_field_count = 0; + for (size_t i = 0; i < public_count; i++) { + AstNode *member = node->stmt.struct_decl.public_members[i]; + if (member->type == AST_STMT_FIELD_DECL && !member->stmt.field_decl.function) { + data_field_count++; + } + } + for (size_t i = 0; i < private_count; i++) { + AstNode *member = node->stmt.struct_decl.private_members[i]; + if (member->type == AST_STMT_FIELD_DECL && !member->stmt.field_decl.function) { + data_field_count++; + } + } - if (total_members == 0) { - fprintf(stderr, "Error: Struct %s cannot be empty\n", struct_name); + if (data_field_count == 0) { + fprintf(stderr, "Error: Struct %s must have at least one data field\n", struct_name); return NULL; } @@ -57,35 +71,34 @@ LLVMValueRef codegen_stmt_struct(CodeGenContext *ctx, AstNode *node) { return NULL; } - // Create StructInfo + // Create StructInfo for data fields only StructInfo *struct_info = (StructInfo *)arena_alloc( ctx->arena, sizeof(StructInfo), alignof(StructInfo)); struct_info->name = arena_strdup(ctx->arena, struct_name); - struct_info->field_count = total_members; + struct_info->field_count = data_field_count; struct_info->is_public = node->stmt.struct_decl.is_public; // Allocate arrays for field information struct_info->field_names = (char **)arena_alloc( - ctx->arena, sizeof(char *) * total_members, alignof(char *)); + ctx->arena, sizeof(char *) * data_field_count, alignof(char *)); struct_info->field_types = (LLVMTypeRef *)arena_alloc( - ctx->arena, sizeof(LLVMTypeRef) * total_members, alignof(LLVMTypeRef)); - struct_info->field_element_types = (LLVMTypeRef *)arena_alloc( // NEW - ctx->arena, sizeof(LLVMTypeRef) * total_members, alignof(LLVMTypeRef)); + ctx->arena, sizeof(LLVMTypeRef) * data_field_count, alignof(LLVMTypeRef)); + struct_info->field_element_types = (LLVMTypeRef *)arena_alloc( + ctx->arena, sizeof(LLVMTypeRef) * data_field_count, alignof(LLVMTypeRef)); struct_info->field_is_public = (bool *)arena_alloc( - ctx->arena, sizeof(bool) * total_members, alignof(bool)); + ctx->arena, sizeof(bool) * data_field_count, alignof(bool)); - // Process public members first + // Process public data fields size_t field_index = 0; - for (size_t i = 0; i < node->stmt.struct_decl.public_count; i++) { - AstNode *field = node->stmt.struct_decl.public_members[i]; - if (field->type != AST_STMT_FIELD_DECL) { - fprintf(stderr, "Error: Expected field declaration in struct %s\n", - struct_name); - return NULL; - } + for (size_t i = 0; i < public_count; i++) { + AstNode *member = node->stmt.struct_decl.public_members[i]; + if (member->type != AST_STMT_FIELD_DECL) continue; + + // Skip methods for now, we'll process them after the struct type is created + if (member->stmt.field_decl.function) continue; - const char *field_name = field->stmt.field_decl.name; + const char *field_name = member->stmt.field_decl.name; // Check for duplicate field names for (size_t j = 0; j < field_index; j++) { @@ -97,31 +110,27 @@ LLVMValueRef codegen_stmt_struct(CodeGenContext *ctx, AstNode *node) { } struct_info->field_names[field_index] = arena_strdup(ctx->arena, field_name); - struct_info->field_types[field_index] = codegen_type(ctx, field->stmt.field_decl.type); - struct_info->field_element_types[field_index] = extract_element_type_from_ast(ctx, field->stmt.field_decl.type); + struct_info->field_types[field_index] = codegen_type(ctx, member->stmt.field_decl.type); + struct_info->field_element_types[field_index] = extract_element_type_from_ast(ctx, member->stmt.field_decl.type); struct_info->field_is_public[field_index] = true; if (!struct_info->field_types[field_index]) { - fprintf(stderr, - "Error: Failed to resolve type for field %s in struct %s\n", + fprintf(stderr, "Error: Failed to resolve type for field %s in struct %s\n", field_name, struct_name); return NULL; } field_index++; } - // Process private members - for (size_t i = 0; i < node->stmt.struct_decl.private_count; i++) { - AstNode *field = node->stmt.struct_decl.private_members[i]; - if (field->type != AST_STMT_FIELD_DECL) { - fprintf(stderr, "Error: Expected field declaration in struct %s\n", - struct_name); - return NULL; - } + // Process private data fields + for (size_t i = 0; i < private_count; i++) { + AstNode *member = node->stmt.struct_decl.private_members[i]; + if (member->type != AST_STMT_FIELD_DECL) continue; + + if (member->stmt.field_decl.function) continue; - const char *field_name = field->stmt.field_decl.name; + const char *field_name = member->stmt.field_decl.name; - // Check for duplicate field names (including public fields) for (size_t j = 0; j < field_index; j++) { if (strcmp(struct_info->field_names[j], field_name) == 0) { fprintf(stderr, "Error: Duplicate field name '%s' in struct %s\n", @@ -131,13 +140,12 @@ LLVMValueRef codegen_stmt_struct(CodeGenContext *ctx, AstNode *node) { } struct_info->field_names[field_index] = arena_strdup(ctx->arena, field_name); - struct_info->field_types[field_index] = codegen_type(ctx, field->stmt.field_decl.type); - struct_info->field_element_types[field_index] = extract_element_type_from_ast(ctx, field->stmt.field_decl.type); - struct_info->field_is_public[field_index] = field->stmt.field_decl.is_public; + struct_info->field_types[field_index] = codegen_type(ctx, member->stmt.field_decl.type); + struct_info->field_element_types[field_index] = extract_element_type_from_ast(ctx, member->stmt.field_decl.type); + struct_info->field_is_public[field_index] = member->stmt.field_decl.is_public; if (!struct_info->field_types[field_index]) { - fprintf(stderr, - "Error: Failed to resolve type for field %s in struct %s\n", + fprintf(stderr, "Error: Failed to resolve type for field %s in struct %s\n", field_name, struct_name); return NULL; } @@ -146,29 +154,182 @@ LLVMValueRef codegen_stmt_struct(CodeGenContext *ctx, AstNode *node) { // Create LLVM struct type struct_info->llvm_type = LLVMStructTypeInContext( - ctx->context, struct_info->field_types, total_members, false); + ctx->context, struct_info->field_types, data_field_count, false); - // Note: LLVMStructSetName is not available in all LLVM versions - // The struct will still work without an explicit name - - // Add to context + // Add to context BEFORE processing methods add_struct_type(ctx, struct_info); - - // Add struct type to symbol table as a type symbol add_symbol(ctx, struct_name, NULL, struct_info->llvm_type, false); -// Debug output (can be removed in production) -#ifdef DEBUG_STRUCTS - printf("Defined struct %s with %zu fields:\n", struct_name, total_members); - for (size_t i = 0; i < total_members; i++) { - printf(" - %s: %s (%s)\n", struct_info->field_names[i], "type", - struct_info->field_is_public[i] ? "public" : "private"); + // NOW process methods with access to the struct type + for (size_t i = 0; i < public_count; i++) { + AstNode *member = node->stmt.struct_decl.public_members[i]; + if (member->type != AST_STMT_FIELD_DECL) continue; + + // Only process methods + if (!member->stmt.field_decl.function) continue; + + AstNode *func_node = member->stmt.field_decl.function; + const char *method_name = member->stmt.field_decl.name; + + // Generate the method with implicit 'self' parameter + if (!codegen_struct_method(ctx, func_node, struct_info, method_name, true)) { + fprintf(stderr, "Error: Failed to generate method '%s' for struct '%s'\n", + method_name, struct_name); + return NULL; + } + } + + // Process private methods + for (size_t i = 0; i < private_count; i++) { + AstNode *member = node->stmt.struct_decl.private_members[i]; + if (member->type != AST_STMT_FIELD_DECL) continue; + + if (!member->stmt.field_decl.function) continue; + + AstNode *func_node = member->stmt.field_decl.function; + const char *method_name = member->stmt.field_decl.name; + + if (!codegen_struct_method(ctx, func_node, struct_info, method_name, false)) { + fprintf(stderr, "Error: Failed to generate private method '%s' for struct '%s'\n", + method_name, struct_name); + return NULL; + } } -#endif return NULL; } +LLVMValueRef codegen_struct_method(CodeGenContext *ctx, AstNode *func_node, + StructInfo *struct_info, const char *method_name, + bool is_public) { + if (!func_node || func_node->type != AST_STMT_FUNCTION) { + fprintf(stderr, "Error: Invalid function node for method '%s'\n", method_name); + return NULL; + } + + // Get method signature + AstNode *return_type_node = func_node->stmt.func_decl.return_type; + size_t original_param_count = func_node->stmt.func_decl.param_count; + AstNode **original_param_type_nodes = func_node->stmt.func_decl.param_types; + char **original_param_names = func_node->stmt.func_decl.param_names; + + // CRITICAL: Methods need an implicit 'self' parameter as the FIRST parameter + // The typechecker injects 'self' when calling methods, so the method definition must match + size_t param_count = original_param_count + 1; // +1 for 'self' + + // Allocate arrays for ALL parameters (including self) + LLVMTypeRef *llvm_param_types = (LLVMTypeRef *)arena_alloc( + ctx->arena, sizeof(LLVMTypeRef) * param_count, alignof(LLVMTypeRef)); + + char **param_names = (char **)arena_alloc( + ctx->arena, sizeof(char *) * param_count, alignof(char *)); + + AstNode **param_type_nodes = (AstNode **)arena_alloc( + ctx->arena, sizeof(AstNode *) * param_count, alignof(AstNode *)); + + // First parameter is 'self' - a pointer to the struct + llvm_param_types[0] = LLVMPointerType(struct_info->llvm_type, 0); + param_names[0] = "self"; + param_type_nodes[0] = NULL; // We'll handle this specially for element type extraction + + // Copy the rest of the original parameters (shifted by 1) + for (size_t i = 0; i < original_param_count; i++) { + llvm_param_types[i + 1] = codegen_type(ctx, original_param_type_nodes[i]); + if (!llvm_param_types[i + 1]) { + fprintf(stderr, "Error: Failed to resolve parameter type %zu for method '%s'\n", + i, method_name); + return NULL; + } + param_names[i + 1] = original_param_names[i]; + param_type_nodes[i + 1] = original_param_type_nodes[i]; + } + + // Create function type + LLVMTypeRef llvm_return_type = codegen_type(ctx, return_type_node); + if (!llvm_return_type) { + fprintf(stderr, "Error: Failed to resolve return type for method '%s'\n", method_name); + return NULL; + } + + LLVMTypeRef func_type = LLVMFunctionType(llvm_return_type, llvm_param_types, + param_count, 0); + + // Get the current LLVM module + LLVMModuleRef current_llvm_module = + ctx->current_module ? ctx->current_module->module : ctx->module; + + // Create the function in the current module + LLVMValueRef func = LLVMAddFunction(current_llvm_module, method_name, func_type); + + if (!func) { + fprintf(stderr, "Error: Failed to create LLVM function for method '%s'\n", method_name); + return NULL; + } + + // Set linkage + if (is_public) { + LLVMSetLinkage(func, LLVMExternalLinkage); + } else { + LLVMSetLinkage(func, LLVMInternalLinkage); + } + + // CRITICAL: Save the old function context before starting method generation + LLVMValueRef old_function = ctx->current_function; + + // Set current function context + ctx->current_function = func; + + // Create entry basic block + LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext( + ctx->context, func, "entry"); + LLVMPositionBuilderAtEnd(ctx->builder, entry); + + // Add all parameters to symbol table (including self at index 0) + for (size_t i = 0; i < param_count; i++) { + LLVMValueRef param = LLVMGetParam(func, i); + const char *param_name = param_names[i]; + + LLVMSetValueName2(param, param_name, strlen(param_name)); + + // Allocate stack space and store parameter + LLVMValueRef alloca = LLVMBuildAlloca(ctx->builder, llvm_param_types[i], param_name); + LLVMBuildStore(ctx->builder, param, alloca); + + // Extract element type for pointer parameters (needed for self which is *Person) + LLVMTypeRef element_type = extract_element_type_from_ast(ctx, param_type_nodes[i]); + + // Add to symbol table with element type information + add_symbol_with_element_type(ctx, param_name, alloca, llvm_param_types[i], + element_type, false); + } + + // Generate method body + AstNode *body = func_node->stmt.func_decl.body; + if (body) { + codegen_stmt(ctx, body); + } + + // Add return if missing for void functions + if (LLVMGetTypeKind(llvm_return_type) == LLVMVoidTypeKind) { + if (!LLVMGetBasicBlockTerminator(LLVMGetInsertBlock(ctx->builder))) { + LLVMBuildRetVoid(ctx->builder); + } + } + + // Verify the function + if (LLVMVerifyFunction(func, LLVMReturnStatusAction)) { + fprintf(stderr, "Error: Function verification failed for method '%s'\n", method_name); + LLVMDumpValue(func); + // Restore context even on error + ctx->current_function = old_function; + return NULL; + } + + // CRITICAL: Restore the old function context + ctx->current_function = old_function; + + return func; +} // Handle individual field declarations (mainly for completeness) LLVMValueRef codegen_stmt_field(CodeGenContext *ctx, AstNode *node) { // Field declarations are handled by the parent struct diff --git a/src/parser/stmt.c b/src/parser/stmt.c index 3c43bbf3..09ba8fe1 100644 --- a/src/parser/stmt.c +++ b/src/parser/stmt.c @@ -350,10 +350,22 @@ Stmt *struct_stmt(Parser *parser, const char *name, bool is_public) { // TODO: Add in a check to see if we have any function modifiers like // returns_ownership or takes_ownership - // Method: field_name = fn(...) - if (p_current(parser).type_ == TOK_EQUAL) { - p_consume(parser, TOK_EQUAL, "Expected '=' after field name"); - field_function = fn_stmt(parser, field_name, public_member, false, false); + bool takes_ownership, returns_ownership = false; + while (p_current(parser).type_ == TOK_RETURNES_OWNERSHIP || + p_current(parser).type_ == TOK_TAKES_OWNERSHIP) { + if (p_current(parser).type_ == TOK_RETURNES_OWNERSHIP) { + returns_ownership = true; + p_advance(parser); + } else if (p_current(parser).type_ == TOK_TAKES_OWNERSHIP) { + takes_ownership = true; + p_advance(parser); + } + } + + // Method: field_name -> fn(...) + if (p_current(parser).type_ == TOK_RIGHT_ARROW) { + p_consume(parser, TOK_RIGHT_ARROW, "Expected '->' after field name"); + field_function = fn_stmt(parser, field_name, public_member, returns_ownership, takes_ownership); } else { // Data field: field_name: Type p_consume(parser, TOK_COLON, "Expected ':' after field name"); diff --git a/src/typechecker/expr.c b/src/typechecker/expr.c index 884e3e27..8115c1df 100644 --- a/src/typechecker/expr.c +++ b/src/typechecker/expr.c @@ -200,6 +200,7 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, Symbol *func_symbol = NULL; const char *func_name = NULL; + bool is_method_call = false; // Track if this is a method call with injected self if (callee->type == AST_EXPR_IDENTIFIER) { // Simple function call: func() @@ -207,6 +208,7 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, func_symbol = scope_lookup(scope, func_name); } else if (callee->type == AST_EXPR_MEMBER) { + // Member function call: could be module::func() or obj.method() const char *base_name = callee->expr.member.object->expr.identifier.name; const char *member_name = callee->expr.member.member; @@ -272,7 +274,6 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, } AstNode *base_type = base_symbol->type; - // Handle pointer dereference: if we have a pointer to struct, // automatically dereference it if (base_type->type == AST_TYPE_POINTER) { @@ -304,17 +305,98 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, // Now check if it's a struct type and get the member if (base_type->type == AST_TYPE_STRUCT) { AstNode *member_type = get_struct_member_type(base_type, member_name); + if (member_type && member_type->type == AST_TYPE_FUNCTION) { // This is a method call on a struct instance func_symbol = arena_alloc(arena, sizeof(Symbol), alignof(Symbol)); + if (!func_symbol) { + tc_error(expr, "Memory Error", "Failed to allocate symbol for method '%s'", member_name); + return NULL; + } + func_symbol->name = member_name; + func_symbol->type = member_type; + func_symbol->is_public = true; func_symbol->is_mutable = false; func_symbol->scope_depth = 0; - func_symbol->returns_ownership = false; // ADD THIS - func_symbol->takes_ownership = false; // ADD THIS + func_symbol->returns_ownership = false; + func_symbol->takes_ownership = false; func_name = member_name; + + // CRITICAL: For method calls, we need to inject 'self' as the first argument + // Verify we have a valid object before injectin + if (!callee->expr.member.object) { + tc_error(expr, "Internal Error", "Method call has no object"); + return NULL; + } + + // Create a new arguments array with 'self' prepended + size_t new_arg_count = arg_count + 1; + AstNode **new_arguments = arena_alloc(arena, new_arg_count * sizeof(AstNode *), + alignof(AstNode *)); + + if (!new_arguments) { + tc_error(expr, "Memory Error", "Failed to allocate arguments array for method call"); + return NULL; + } + + // CRITICAL: Check if we need to take the address of the object + // The method expects a pointer to the struct (self is Person*) + // But obj.method() gives us the struct value (Person) + // We need to automatically inject &obj instead of just obj + + // Get the method's parameter types to check what self expects + if (!member_type || member_type->type != AST_TYPE_FUNCTION) { + tc_error(expr, "Internal Error", "Method type is not a function"); + return NULL; + } + + AstNode **method_param_types = member_type->type_data.function.param_types; + if (!method_param_types || member_type->type_data.function.param_count == 0) { + tc_error(expr, "Internal Error", "Method has no parameters (missing self?)"); + return NULL; + } + + AstNode *self_param_type = method_param_types[0]; // First param is always self + AstNode *object_node = callee->expr.member.object; + + // Check what the method expects for self + bool expects_pointer = (self_param_type->type == AST_TYPE_POINTER); + + // Check what we have + Symbol *obj_symbol = scope_lookup(scope, base_name); + bool have_pointer = (obj_symbol && obj_symbol->type && + obj_symbol->type->type == AST_TYPE_POINTER); + + if (expects_pointer && !have_pointer) { + // Method expects pointer but we have value - take address + AstNode *addr_expr = arena_alloc(arena, sizeof(AstNode), alignof(AstNode)); + addr_expr->type = AST_EXPR_ADDR; + addr_expr->category = Node_Category_EXPR; + addr_expr->line = object_node->line; + addr_expr->column = object_node->column; + addr_expr->expr.addr.object = object_node; + new_arguments[0] = addr_expr; + } else { + // Either method expects value, or we already have pointer + new_arguments[0] = object_node; + } + + // Copy the rest of the user-provided arguments + for (size_t i = 0; i < arg_count; i++) { + new_arguments[i + 1] = arguments[i]; + } + + // Update the arguments and count for the rest of the function + arguments = new_arguments; + arg_count = new_arg_count; + is_method_call = true; // Mark that we injected self + + expr->expr.call.args = new_arguments; + expr->expr.call.arg_count = new_arg_count; + } else if (member_type) { tc_error(expr, "Runtime Call Error", "Cannot call non-function member '%s' on struct '%s'", @@ -338,7 +420,7 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, tc_error(expr, "Call Error", "Unsupported callee type for function call"); return NULL; } - + if (!func_symbol) { tc_error(expr, "Call Error", "Undefined function '%s'", func_name ? func_name : "unknown"); @@ -362,22 +444,51 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, return NULL; } - for (size_t i = 0; i < arg_count; i++) { + // For method calls, start from index 1 to skip the injected 'self' parameter + size_t start_index = is_method_call ? 1 : 0; + + for (size_t i = start_index; i < arg_count; i++) { + // Validate the argument pointer before dereferencing + if (!arguments) { + tc_error(expr, "Call Error", + "Arguments array is NULL for call to '%s'", func_name); + return NULL; + } + + if (!arguments[i]) { + tc_error(expr, "Call Error", + "Argument %zu in call to '%s' is NULL", i + 1, func_name); + return NULL; + } + AstNode *arg_type = typecheck_expression(arguments[i], scope, arena); + if (!arg_type) { tc_error(expr, "Call Error", "Failed to type-check argument %zu in call to '%s'", i + 1, func_name); return NULL; } - + + // Validate param_types array + if (!param_types || !param_types[i]) { + tc_error(expr, "Call Error", + "Parameter %zu type in function '%s' is NULL", i + 1, func_name); + return NULL; + } + TypeMatchResult match = types_match(param_types[i], arg_type); + if (match == TYPE_MATCH_NONE) { + fprintf(stderr, "DEBUG: Type mismatch - NONE - about to report error\n"); + + const char *param_str = type_to_string(param_types[i], arena); + const char *arg_str = type_to_string(arg_type, arena); + tc_error_help(expr, "Call Error", "Argument %zu to function '%s' has wrong type.", "Expected '%s', got '%s'", i + 1, func_name, - type_to_string(param_types[i], arena), - type_to_string(arg_type, arena)); + param_str, arg_str); return NULL; } } @@ -385,7 +496,7 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, // Handle ownership transfer from caller to function if (func_symbol->takes_ownership) { for (size_t i = 0; i < arg_count; i++) { - if (is_pointer_type(param_types[i])) { + if (param_types[i] && is_pointer_type(param_types[i])) { const char *arg_var = extract_variable_name(arguments[i]); if (arg_var) { StaticMemoryAnalyzer *analyzer = get_static_analyzer(scope); @@ -397,8 +508,7 @@ AstNode *typecheck_call_expr(AstNode *expr, Scope *scope, } } } - // ========== NEW CODE ENDS HERE ========== - + return return_type; } diff --git a/src/typechecker/stmt.c b/src/typechecker/stmt.c index 979448c2..8fec9082 100644 --- a/src/typechecker/stmt.c +++ b/src/typechecker/stmt.c @@ -354,11 +354,6 @@ bool typecheck_struct_decl(AstNode *node, Scope *scope, ArenaAllocator *arena) { size_t private_count = node->stmt.struct_decl.private_count; bool is_public = node->stmt.struct_decl.is_public; - // printf("DEBUG: Processing struct '%s' with %zu public members, %zu private - // " - // "members\n", - // struct_name, public_count, private_count); - // Validate struct name if (!struct_name) { tc_error(node, "Struct Error", "Struct declaration missing name"); @@ -385,47 +380,19 @@ bool typecheck_struct_decl(AstNode *node, Scope *scope, ArenaAllocator *arena) { GrowableArray seen_names; growable_array_init(&seen_names, arena, total_members, sizeof(char *)); - // Process public members + // FIRST PASS: Collect all data fields (non-methods) to create the struct type for (size_t i = 0; i < public_count; i++) { AstNode *member = public_members[i]; - // printf("DEBUG: Processing public member %zu: %p\n", i, (void *)member); - - if (!member) { - tc_error(node, "Struct Error", "Public member %zu is NULL in struct '%s'", + if (!member || member->type != AST_STMT_FIELD_DECL) { + tc_error(node, "Struct Error", "Invalid public member %zu in struct '%s'", i, struct_name); return false; } - // printf("DEBUG: Member type: %d (expected AST_STMT_FIELD_DECL)\n", - // member->type); - - if (member->type != AST_STMT_FIELD_DECL) { - tc_error(node, "Struct Error", - "Invalid public member %zu in struct '%s' (type=%d)", i, - struct_name, member->type); - return false; - } - const char *field_name = member->stmt.field_decl.name; AstNode *field_type = member->stmt.field_decl.type; AstNode *field_function = member->stmt.field_decl.function; - // printf("DEBUG: Field name: '%s', type: %p, function: %p\n", - // field_name ? field_name : "NULL", (void *)field_type, - // (void *)field_function); - - // Additional debugging for the type - // if (field_type) { - // printf("DEBUG: Field type category: %d, type: %d\n", - // field_type->category, - // field_type->type); - // if (field_type->type == AST_TYPE_BASIC) { - // printf("DEBUG: Basic type name: '%s'\n", - // field_type->type_data.basic.name); - // } - // } - - // Validate field name if (!field_name) { tc_error(node, "Struct Field Error", "Field %zu in struct '%s' has no name", i, struct_name); @@ -444,85 +411,77 @@ bool typecheck_struct_decl(AstNode *node, Scope *scope, ArenaAllocator *arena) { } } - // Add to seen names char **name_slot = (char **)growable_array_push(&seen_names); *name_slot = (char *)field_name; - // Handle method vs data field - if (field_function) { - // printf("DEBUG: Processing method '%s'\n", field_name); - // This is a method - validate the function - if (!typecheck_statement(field_function, scope, arena)) { - tc_error(node, "Struct Method Error", - "Method '%s' in struct '%s' failed type checking", field_name, - struct_name); - return false; - } - - // Get the function's type from the symbol table - Symbol *method_symbol = scope_lookup_current_only(scope, field_name); - if (method_symbol && method_symbol->type) { - all_member_types[member_index] = method_symbol->type; - // printf("DEBUG: Method type found: %p\n", (void - // *)method_symbol->type); - } else { - tc_error(node, "Struct Method Error", - "Could not find type for method '%s' in struct '%s'", - field_name, struct_name); - return false; - } - } else if (field_type) { - // printf("DEBUG: Processing data field '%s'\n", field_name); - // This is a data field - validate the type + // If it's a data field (not a method), add it now + if (field_type && !field_function) { if (field_type->category != Node_Category_TYPE) { tc_error(node, "Struct Field Error", - "Field '%s' in struct '%s' has invalid type category (%d, " - "expected %d)", - field_name, struct_name, field_type->category, - Node_Category_TYPE); + "Field '%s' in struct '%s' has invalid type category", + field_name, struct_name); return false; } + all_member_types[member_index] = field_type; - // printf("DEBUG: Data field type assigned: %p\n", (void *)field_type); - } else { - tc_error(node, "Struct Field Error", - "Field '%s' in struct '%s' has neither type nor function", - field_name, struct_name); + all_member_names[member_index] = field_name; + member_index++; + } + } + + // Process private data fields + for (size_t i = 0; i < private_count; i++) { + AstNode *member = private_members[i]; + if (!member || member->type != AST_STMT_FIELD_DECL) { + tc_error(node, "Struct Error", "Invalid private member %zu in struct '%s'", + i, struct_name); return false; } - // Validate that we actually got a valid type - if (!all_member_types[member_index]) { + const char *field_name = member->stmt.field_decl.name; + AstNode *field_type = member->stmt.field_decl.type; + AstNode *field_function = member->stmt.field_decl.function; + + if (!field_name) { tc_error(node, "Struct Field Error", - "Failed to determine type for field '%s' in struct '%s'", - field_name, struct_name); + "Field %zu in struct '%s' has no name", i, struct_name); return false; } - // printf("DEBUG: Final type for member %zu ('%s'): %p\n", member_index, - // field_name, (void *)all_member_types[member_index]); + // Check for duplicate member names + for (size_t j = 0; j < seen_names.count; j++) { + char **existing_name = + (char **)((char *)seen_names.data + j * sizeof(char *)); + if (strcmp(*existing_name, field_name) == 0) { + tc_error_id(node, field_name, "Duplicate Member", + "Struct member '%s' is already declared in struct '%s'", + field_name, struct_name); + return false; + } + } - all_member_names[member_index] = field_name; - member_index++; - } + char **name_slot = (char **)growable_array_push(&seen_names); + *name_slot = (char *)field_name; - // Process private members (similar logic, but abbreviated for brevity) - for (size_t i = 0; i < private_count; i++) { - AstNode *member = private_members[i]; - // ... similar processing with debug prints ... - // (You can add the same debug logic here) - } + if (field_type && !field_function) { + if (field_type->category != Node_Category_TYPE) { + tc_error(node, "Struct Field Error", + "Field '%s' in struct '%s' has invalid type category", + field_name, struct_name); + return false; + } - // printf("DEBUG: Creating struct type with %zu members\n", total_members); - for (size_t i = 0; i < total_members; i++) { - // printf("DEBUG: Member %zu: '%s' -> type %p\n", i, all_member_names[i], - // (void *)all_member_types[i]); + all_member_types[member_index] = field_type; + all_member_names[member_index] = field_name; + member_index++; + } } - // Create struct type + // Create the struct type with just data fields + size_t data_field_count = member_index; AstNode *struct_type = create_struct_type(arena, struct_name, all_member_types, all_member_names, - total_members, node->line, node->column); + data_field_count, node->line, node->column); if (!struct_type) { tc_error(node, "Struct Creation Error", @@ -530,22 +489,230 @@ bool typecheck_struct_decl(AstNode *node, Scope *scope, ArenaAllocator *arena) { return false; } - // printf("DEBUG: Struct type created: %p\n", (void *)struct_type); - - // Add struct type to scope - if (!scope_add_symbol(scope, struct_name, struct_type, is_public, false, - arena)) { + // Add struct type to scope BEFORE processing methods + if (!scope_add_symbol(scope, struct_name, struct_type, is_public, false, arena)) { tc_error_id(node, struct_name, "Symbol Error", "Failed to add struct '%s' to scope", struct_name); return false; } - // printf("DEBUG: About to print struct type:\n"); - // debug_print_struct_type(struct_type, 0); + // Now we need to reserve space for methods in the arrays BEFORE processing them + // Count total fields (data + methods) + size_t method_count = 0; + for (size_t i = 0; i < public_count; i++) { + if (public_members[i]->stmt.field_decl.function) { + method_count++; + } + } + for (size_t i = 0; i < private_count; i++) { + if (private_members[i]->stmt.field_decl.function) { + method_count++; + } + } + + size_t total_member_count = data_field_count + method_count; + + // Reallocate the arrays to hold all members (data + methods) + AstNode **full_member_types = arena_alloc(arena, total_member_count * sizeof(AstNode *), + alignof(AstNode *)); + const char **full_member_names = arena_alloc(arena, total_member_count * sizeof(char *), + alignof(char *)); + + // Copy existing data fields + for (size_t i = 0; i < data_field_count; i++) { + full_member_types[i] = all_member_types[i]; + full_member_names[i] = all_member_names[i]; + } + + // Update struct type to use new arrays + struct_type->type_data.struct_type.member_types = full_member_types; + struct_type->type_data.struct_type.member_names = full_member_names; + struct_type->type_data.struct_type.member_count = data_field_count; // Start with data fields + + member_index = data_field_count; // Continue from where we left off + + // SECOND PASS: Process methods with explicit 'self' parameter + for (size_t i = 0; i < public_count; i++) { + AstNode *member = public_members[i]; + const char *field_name = member->stmt.field_decl.name; + AstNode *field_function = member->stmt.field_decl.function; + + if (field_function) { + // This is a method - typecheck it with 'self' parameter + + AstNode *func_node = field_function; + if (func_node->type != AST_STMT_FUNCTION) { + tc_error(node, "Internal Error", "Expected function node for method"); + return false; + } + + // Create the method's scope + Scope *method_scope = create_child_scope(scope, field_name, arena); + method_scope->is_function_scope = true; + method_scope->associated_node = func_node; + + // **KEY ADDITION**: Add 'self' as the first parameter + // 'self' is a reference to the struct instance (could be pointer or value) + // For simplicity, let's make it a pointer to the struct + AstNode *self_type = create_pointer_type(arena, struct_type, + func_node->line, func_node->column); + + if (!scope_add_symbol(method_scope, "self", self_type, false, true, arena)) { + tc_error(func_node, "Method Error", + "Failed to add 'self' parameter to method '%s'", field_name); + return false; + } + + // Add method parameters to the scope + size_t param_count = func_node->stmt.func_decl.param_count; + char **param_names = func_node->stmt.func_decl.param_names; + AstNode **param_types = func_node->stmt.func_decl.param_types; + + for (size_t j = 0; j < param_count; j++) { + if (!scope_add_symbol(method_scope, param_names[j], param_types[j], + false, true, arena)) { + tc_error(func_node, "Method Parameter Error", + "Could not add parameter '%s' to method '%s' scope", + param_names[j], field_name); + return false; + } + } + + // Typecheck the method body + AstNode *body = func_node->stmt.func_decl.body; + if (body) { + if (!typecheck_statement(body, method_scope, arena)) { + tc_error(node, "Struct Method Error", + "Method '%s' in struct '%s' failed type checking", + field_name, struct_name); + return false; + } + } + + // Register the method in the parent scope + // CRITICAL: Include 'self' as the first parameter in the method type + AstNode *return_type = func_node->stmt.func_decl.return_type; + + size_t method_param_count = param_count + 1; // +1 for self + AstNode **method_param_types = arena_alloc(arena, method_param_count * sizeof(AstNode *), + alignof(AstNode *)); + + // First parameter is self + method_param_types[0] = self_type; + + // Copy the rest of the parameters + for (size_t j = 0; j < param_count; j++) { + method_param_types[j + 1] = param_types[j]; + } + + AstNode *method_type = create_function_type( + arena, method_param_types, method_param_count, return_type, + func_node->line, func_node->column); + + if (!scope_add_symbol(scope, field_name, method_type, is_public, false, arena)) { + tc_error(func_node, "Method Registration Error", + "Failed to register method '%s' in scope", field_name); + return false; + } + + // Add method to struct type's member list + full_member_types[member_index] = method_type; + full_member_names[member_index] = field_name; + member_index++; + + // Update the struct's member count to include this method + struct_type->type_data.struct_type.member_count = member_index; + } + } + + // Process private methods similarly + for (size_t i = 0; i < private_count; i++) { + AstNode *member = private_members[i]; + const char *field_name = member->stmt.field_decl.name; + AstNode *field_function = member->stmt.field_decl.function; + + if (field_function) { + AstNode *func_node = field_function; + if (func_node->type != AST_STMT_FUNCTION) { + tc_error(node, "Internal Error", "Expected function node for method"); + return false; + } + + Scope *method_scope = create_child_scope(scope, field_name, arena); + method_scope->is_function_scope = true; + method_scope->associated_node = func_node; + + // Add 'self' parameter + AstNode *self_type = create_pointer_type(arena, struct_type, + func_node->line, func_node->column); + + if (!scope_add_symbol(method_scope, "self", self_type, false, true, arena)) { + tc_error(func_node, "Method Error", + "Failed to add 'self' parameter to method '%s'", field_name); + return false; + } + + size_t param_count = func_node->stmt.func_decl.param_count; + char **param_names = func_node->stmt.func_decl.param_names; + AstNode **param_types = func_node->stmt.func_decl.param_types; + + for (size_t j = 0; j < param_count; j++) { + if (!scope_add_symbol(method_scope, param_names[j], param_types[j], + false, true, arena)) { + tc_error(func_node, "Method Parameter Error", + "Could not add parameter '%s' to method '%s' scope", + param_names[j], field_name); + return false; + } + } + + AstNode *body = func_node->stmt.func_decl.body; + if (body) { + if (!typecheck_statement(body, method_scope, arena)) { + tc_error(node, "Struct Method Error", + "Method '%s' in struct '%s' failed type checking", + field_name, struct_name); + return false; + } + } + + AstNode *return_type = func_node->stmt.func_decl.return_type; + + // CRITICAL: Include 'self' as the first parameter in the method type + size_t method_param_count = param_count + 1; // +1 for self + AstNode **method_param_types = arena_alloc(arena, method_param_count * sizeof(AstNode *), + alignof(AstNode *)); + + // First parameter is self + method_param_types[0] = self_type; + + // Copy the rest of the parameters + for (size_t j = 0; j < param_count; j++) { + method_param_types[j + 1] = param_types[j]; + } + + AstNode *method_type = create_function_type( + arena, method_param_types, method_param_count, return_type, + func_node->line, func_node->column); + + if (!scope_add_symbol(scope, field_name, method_type, false, false, arena)) { + tc_error(func_node, "Method Registration Error", + "Failed to register method '%s' in scope", field_name); + return false; + } + + // Add method to struct type's member list + full_member_types[member_index] = method_type; + full_member_names[member_index] = field_name; + member_index++; + + // Update the struct's member count to include this method + struct_type->type_data.struct_type.member_count = member_index; + } + } return true; } - bool typecheck_enum_decl(AstNode *node, Scope *scope, ArenaAllocator *arena) { const char *enum_name = node->stmt.enum_decl.name; char **member_names = node->stmt.enum_decl.members; diff --git a/tests/test.lx b/tests/test.lx index 73936da6..b25eaba8 100644 --- a/tests/test.lx +++ b/tests/test.lx @@ -1,95 +1,24 @@ @module "main" -// Define a struct -const Point -> struct { -pub: - x: int, - y: int -}; +const Person -> struct { + name: *char, + age: int, -// Define another struct -const Rectangle -> struct { -pub: - top_left: Point, - bottom_right: Point -}; + init -> fn (age: int, name: *char) void { + self.name = name; + self.age = age; + }, -// Implement methods for Point -impl [distance: float, add: Point, scale: Point] -> [Point] { - const distance -> fn (p1: Point, p2: Point) float { - let dx: int = p2.x - p1.x; - let dy: int = p2.y - p1.y; - return cast(dx * dx + dy * dy); - } - - const add -> fn (p1: Point, p2: Point) Point { - let result: Point; - result.x = p1.x + p2.x; - result.y = p1.y + p2.y; - return result; + greet -> fn () void { + output("I am ", self.name, " and I am ", self.age, " years old.\n"); } - - const scale -> fn (p: Point, factor: int) Point { - let result: Point; - result.x = p.x * factor; - result.y = p.y * factor; - return result; - } -} +}; -// Implement methods for Rectangle -impl [area: int, contains: bool] -> [Rectangle] { - const area -> fn (rect: Rectangle) int { - let width: int = rect.bottom_right.x - rect.top_left.x; - let height: int = rect.bottom_right.y - rect.top_left.y; - return width * height; - } - - const contains -> fn (rect: Rectangle, p: Point) bool { - return p.x >= rect.top_left.x && - p.x <= rect.bottom_right.x && - p.y >= rect.top_left.y && - p.y <= rect.bottom_right.y; - } -} +pub const main -> fn () int { + let p: Person; -// Implement shared methods for both structs -impl [print: void] -> [Point, Rectangle] { - const print -> fn (p: Point) void { - output("Point(", p.x, ", ", p.y, ")\n"); - } - - const print -> fn (rect: Rectangle) void { - output("Rectangle(", rect.top_left.x, ", ", rect.top_left.y, - " to ", rect.bottom_right.x, ", ", rect.bottom_right.y, ")\n"); - } -} + p.init(20, "Connor"); + p.greet(); -pub const main -> fn () int { - let p1: Point; - p1.x = 10; - p1.y = 20; - - let p2: Point; - p2.x = 30; - p2.y = 40; - - let p3: Point = add(p1, p2); - let dist: float = distance(p1, p2); - - print(p1); - print(p2); - print(p3); - - println("Distance: ", dist); - - let rect: Rectangle; - rect.top_left = p1; - rect.bottom_right = p2; - - print(rect); - println("Area: ", area(rect)); - println("Contains p3: ", contains(rect, p3)); - return 0; } \ No newline at end of file