From 378cae31a111c9ecbc195e9de6ebe13c3f8cf28b Mon Sep 17 00:00:00 2001 From: "Emilia(SleepeeSoftware)" Date: Thu, 5 Mar 2026 23:01:32 +0100 Subject: [PATCH] fixed issue with single char op, complex, num, and other fun things --- Makefile | 2 +- include/SterlingCompiler.h | 53 ++++- obj/main.o | Bin 21685 -> 33219 bytes source/main.c | 473 ++++++++++++++++++++++++++++--------- 4 files changed, 411 insertions(+), 117 deletions(-) diff --git a/Makefile b/Makefile index c2da10e..27d23d1 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ SRC := $(wildcard source/*.c) OBJ := $(SRC:source/%.c=obj/%.o) CC := gcc -CFLAG := -ggdb -Wall -Wextra -Werror -Wpedantic -I include -O0 -std=c99 +CFLAG := -ggdb -Wall -Wextra -Werror -Wpedantic -I include -O0 -std=c99 -fsignaling-nans LFLAG := all: $(NAME) diff --git a/include/SterlingCompiler.h b/include/SterlingCompiler.h index f409389..2301c91 100644 --- a/include/SterlingCompiler.h +++ b/include/SterlingCompiler.h @@ -15,14 +15,16 @@ # endif typedef enum { - TOK_NONE = 1 << 0, - TOK_RAW = 1 << 1, - TOK_STRING = 1 << 2, - TOK_OP = 1 << 3, - TOK_PREPROC = 1 << 4, - TOK_COMMENT = 1 << 5, - TOK_KEY = 1 << 6, - TOK_ID = 1 << 7 // New: For variable/function names + TOK_NONE = 1 << 0, + TOK_RAW = 1 << 1, + TOK_STRING = 1 << 2,//"asdfasf"; L"WideString" + TOK_LITERAL = 1 << 3,//INT: 42, 0xff, 0777, 100L; FLOAT:3.14, 1e-5, 2.0f; ENUM; char CONST 'a' '\n' L'x' + TOK_OP = 1 << 4, + TOK_PREPROC = 1 << 5, + TOK_COMMENT = 1 << 6, + TOK_KEY = 1 << 7, + TOK_ID = 1 << 8, + TOK_NUM = 1 << 9 } TKN_CTX; typedef struct Token_s { @@ -41,4 +43,39 @@ typedef struct { TKN_CTX ctx; } KeywordEntry; + +const char *SYMBOLS = ";(){}[]$%&*#@!?:,.<>|-+=~`^"; + +// Common C operators (Order matters: put longer ones first if you add 3-char ops) +static const MultiOp MUNCH_TABLE[] = { + {"%:%:", 4}, + {"<<=", 3}, {">>=", 3}, {"...", 3}, + {"\?\?=", 3}, {"\?\?/", 3}, {"\?\?'", 3}, {"\?\?(", 3}, + {"\?\?)", 3}, {"\?\?!", 3}, {"\?\?<", 3}, + {"\?\?>", 3}, {"\?\?-", 3}, //trigraph + {"==", 2}, {"!=", 2}, {"<=", 2}, {">=", 2}, {"##", 2}, + {"++", 2}, {"--", 2}, {"->", 2}, {"+=", 2}, {"%=",2}, + {"-=", 2}, {"*=", 2}, {"/=", 2}, {"&&", 2}, {"||", 2}, + {"^=", 2}, {"<<", 2}, {">>", 2}, {"|=", 2}, {"&=", 2},// + {"<:", 2}, {":>", 2}, {"<%", 2}, {"%>", 2}, {"%:", 2},//digraphs + {NULL, 0} +}; + +// This can be expanded at runtime if you use a dynamic array instead of a static one +static const KeywordEntry KEYWORD_TABLE[] = { + {"if", TOK_KEY}, + {"else", TOK_KEY}, + {"while", TOK_KEY}, + {"return", TOK_KEY}, + {"var", TOK_KEY}, + {"int", TOK_KEY}, + {"float", TOK_KEY}, + {"void", TOK_KEY}, + {"include", TOK_PREPROC}, + {"define", TOK_PREPROC}, + {"comptime",TOK_KEY}, + {"reflect", TOK_KEY}, + {NULL, TOK_NONE} +}; + #endif diff --git a/obj/main.o b/obj/main.o index 91ad0431ff331628396526b1c002b5716b4f995e..4c0aba5a7c53b6ff9f3fd376d280653b1dee38a7 100644 GIT binary patch literal 33219 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 literal 21685 zcmch9dw5jGm1o`CU9A?aN9&2ivjyUf9$<`l2{#WxO9G+r2*Spegw&Ec2uV?Q^RP`! zB*;Xc!Ww0~c6LI7v+=jdY`}5G`TX%p7A79Q#`7&s9Ggid6L!ZWvSBis!HLYQJlI(6#Q@)%W=XyfeL=;XjwC3WFO~0!MiXFnC69=POXUZX6)v-ch!rNo zgQ7B6-kXeT0PgfeA-=mtVYee;TwgM76#S2iQuQ?iE0!el(({D4SuDipdc|)*5)}Tm z6^oW80l$Ox{(fN+OX2RROkz^*WCi>eQfw4cxFz={;o7`Hya8OPgY#?Lz4s;IrXg3k zEQzJ`Elp;A2uOW0OW~F!H<;R2y*yPP{IWdRzJqQd{xfhdIrfq2pa`zr5gNQ68n_t< z4Q}>_1`hj0JnLK$WFw?9G+6Bq4dL@j5Y0ng(bP7kUm-$q2VM$1^b{>zgZI6YRNKJeA`zdaa1CD8 z0$D;i?~Ma07xbb5w?nT~kCK@;a=^VE8gjS;)uR>|MWKNaE3~|NG@5C|u;VS2Nz#w9 zY?rE&^Z=LvAPgNdct{*?AjSSc|B2AROF`q6O6`>-k3cCIwWCOR1lA_F+NmSUspB@B z;=SRxB}lb0Cqc@x6bmXH3#fzH)}YtedG2~G+L)r4_8nU0fl%N>l28H+-BTtxS8rQ2T zRRr9)4>`1rbE6zqYy()ZfjZ>!ou4vf@Un{Eq3Tg~$H3v+BL3H;D`5b<5;VgCHmJs+ zyC?>94-DK?{#K^4OaI;Y91X3kE{dkXJkPaG&;sJZ)S+6KiaFbQ zbIn;BZh_hn539$3;6ZVDK{NP++{#y~Z*vJ}F$f(mcv>moUSScnoQGKe(*Huz zc4l2Aj{H!(y)U!#Vp((vNI;ZYG!sP{A^ zYtFXgJF#9A4YgjuU_wi)M{5ROs~NnKG}bf#8m%iYI}XzIP*06_jGl~Czr!2VqwgI< z@RRizUaMxrvNbs;``9V6i0j`$U0<~390i0kr0b}gs7q)C9HbcMcpx=juKw+f0qbq8U%#=2T4ayIOwyS7 z@#{(E#NT!#P{fLD7NaGpIgxJrbRR z7#|Ta529z}hUDoLPNzWjJK@t#Qxpe*R?fpQ``8A|H7Amr*T921v3bXxm{oHa#fh?! zTYC-J5!Kr4V>=b{n$B-jc@$v}WFK4V01RW6L9koCT>W$CgH9*+_RH1ph?lEB6ndw* zhA6h2w9h*}8wA-XnkNxglIxjZ1={|NT``CjBV(Q2SGdnkr>&TxfXTws|$#cHx zsqc~KmFi1q3&k6HfUM(=Ad~ie$jijl(DBWeFopj>p{^!wI54z$uFXEWO;uwGJ32?l z+P33bXpjb^Qw>WdP!&TYVJE98?)VN=C#gEj$LR5Mq|40h@4b!OdD;*p?J2H6b7EyE zNR@$2ZDPqc{=k(%!j)Y?H&Ty|-=DmS68P)@k_sb6xI?keau(vD9h!!mJ9)%(GD)Zu zQl754N1Z#E(4TIaa^Kn1ToiK+$HCZKfth4x3iQi>4k;wO@TrrL(x@h*uV%kk{o%lrvTLV%b?zuyf}40dVY0zEttK=<9YMQjgF4=0w{*-T?CG3a86IRfwXhy9#t3|o zI^XG%vvBjB_dLpvMOg8@&K&j|>N^Cg8l<26j&tZJUjH$kFOU3+hn*UNjcQASV7NR| zi}eZSG5wU937L-kpE3S?UZoZ0OQWp&+Ktbm6ebba#sh3*69ugb<|=A-JRQcrO6^Yr z@iS@#=JBDe-q7*PzejHK;N_aZOKL)4O+2Ka5n9o$-@;rcZ;jcI_ODEHTToiiE(j|d zZHZ5uAo@>TQyihoNdqbQQYTLn5nXds!b=U2^HcI=Df!Bj{5>goeM+bGFHe!LO3B}! zl3$yWuTIH_Qu1{v`TCUnXHxQXK1r9hZ>P$dyp#RZiaZ&Uqadc2>sOr{9h%||4vzQ=4QvEl(xdb8BTf~H0=*4 z{SY4-W0VFFWD7x4+JVn9(3JiqK0(lwUc!fBn$my72i?MH2L^B>Xi8thrwufv|AbF3 zXiBTxLL3B5>7U_q6f~t*@HqjR(lngD(6K9}Yw`ImXiC41&ksRU`g?pXfu{7A9-M=N zru2g}A+Ca^^v!f3-UUtR^Am7B51P_LaKgu+DSZ@=GrVXwrH8YH$Old7_x*Tg0h-d^ z;d#bF&@1Q8d-U*w50%cEJ%47f4%U7*_tZYHm7n&ayt5#Ks)vK#SRh1~I5|pVD z3l@m7GEuf#ET|G?RbqaXsHhUNXN$)k6AuHk3I)Vt;8cl7y9jqh!{R_^q$@1?!m)T? zw}^DdM0;0HOHAzViL@cx+7)jLi?(okq&qBvtv&mCW08G;i)c^0uQgn;uO-r5-YVKj z2S3ROq-RbL^*sUN1fsp+)<}CK+$L()SJ!W^*-%rxRczlg#B&Xa5l3`jWBrc6y5-9Qi|#F7TwWO{+ZT&>hpRfm-Qm7SE3z$pt({c| zmn|(@x+GB6(b2XiP__lIXsoTOb@_6RJE0yOkvip6MWGVQ)7sh6Cki6qq(>uO$}Wwt>Mm| zuC{QWaL@D+Q-~68E)nS)YBp61cSSKhzAEhQX$v<+h2P5XpF(~jIp4};H6pE>p0in5 zIVAB4Pn1e?0dTWjl+7!$a(;_6jhrZ~+_aaernL4*Ulf?U2_%CLX7W;q{Av=xJE`|;XZ;Fy95vg1RHaQ*LcWwH*KI0#*;7! zx@k{4s2OQ)+G5C@tjj1i0WsKAzooufSWIqRvs2hiZQH)JrhcPvb7o6}@G{Y`wYp*J zmUY6nW0O#tecZncVHS5I@k-ojIzG>a|MjC} zwTQ9w^cNsWcLfwy6dyx&RD&moBDinomaXd@cwW{uh95yF(BW zAnYUbMT>&)kRX>N1aIBfE7I=gy8pY@!v-j6v=`Ov6#A7M2v7zJ#Y8P0uCL*WMRyX%p_C&HC>lNDG8EHA;PXsD!UQ7Vhd2?rPir zcgSuc9INuRN5kR0!o6PA(W3=6!IY~?n_8mLa9>P#i_4!NIr&z7(Qtw8o?dv;z0%Gh zm1hXTuA(pMf@m*h8R417UGNHtRTu0F?`!QnB+?f1eDgjDXyUP%I!3BJ{{rSLUu#QO zS5K=*f62-rK(+@UlJl3C4IhL>`rlaonIs^D@a=PoysU~;=^|ndtwzEwwImrq%P3v< zQz2Z|$-f11a!acywXz6c489<{wdM zWzocA4>!2OSflCstV%?7Of0r?uP_>W5)aL1o&q>(ZcSUbI~Hj_#N#+BY?tMs1)r|P z#=2o_A0~HUmULNDRh7a$=%A)KsO;8wAEt9FmnK!1=>7`_@CT)hY>>u=?Dn2`cbiDJ zvPic*+%ff>13%!vzX*PIMEI;+<&b8P-|V>Ur@4%gmff_rr>9GJ8~U*N?(BpYxYO&g zYM?3a(=LWCx|gTRI=LKfbWua>_h&i{vDj|=My6x8)!P@|FYKF6zS;eZqvds_L|7>O zFWQ&xpE=Sm8fh5g(*CysrLF;N?^fQUY;8H<7#-@zq)xRjY7aYx z{YV?;e#$9D$8&U%A(tIP8r(l{q;x{ZQn&&U_meytiqd8VWYhzQSn+77bO7xe^@JLEo69BkiJtdQqd$tZ$ky`#VQU zr_Xd*u|>i@rTfQ?H+0@{k=Tjg$PNvJkGG+hQT&mR`uD15I#9BLr{q0;Gl$#yA z9!G@$S`lGCTd0H5ea3-&OKC*?)c?;pjtViinAXcWl;w5E))BGo(7ruAU5L~n+LP1u zYn31%ehRb-h@mOk5x;U!f2&a{biZPJ@jD0beGNc|9CuvNZ1wh_sz3MdhtM9TI}>kB zcO({R>Dt!X(v6L)u)FC#fv#U^|D5bX^D=4jvAZWzBw=P!W_8a~W_g`B?cv291K*0q zS}<+fJ;e&&?7oM}|bG+i?;H-Z?GSl!t2AZuz167KkTJd0p|GG_$FuOY_@nSXtz$_m1cN%{=H6< zX${IhlaLYKP1po(qY=XaE9^SDy`t+kDnU2>Ckz-BuXTFdx@Zpy@O(8?ysk)hTlgRt z_bJc&Y20qgQXvU%#oOB>5N~lf&1&E@IK#e?2Nvce2JLj5jNM;DJgNqgus6^xAYJ-s z!#X)??AlP|gpbSd`G$GQQ(jjXGqAm3mCE{EwBNHg)X$hW!YqhJVJ00Mn4UWCKOtsdl)UIBk)N5iGM`&0rWe@RBR^mPb(vC$BtY40AN(+47} zCOWoy2z$PV#y@YlpryuWD#2Xu!Wf#bo0?A>FS`DjR-_zk%DUU)82vLiX6dG$u3DAA zl+QZ~W=z(;LQ`hUay*5BgE2T$ukjcJh`-G|C0S3fNpxgD*H}OQPtu4f-FjjwkrJv{9d=21f#!)*Ezp0`kD8r8iu9F6t#g~bpZ zThKL0C1}Y~tkzub&~#mIYiGE1Z*LFX6k0>~&gUrdiY!vpfX!Z=um;ko0y+<&YmZ8x z?7LdoKq-;bh`m5pIDX+(-#V&$+}-aeSf~|5gmo-Oftz7JRn22}1MSeP19T`u*Pu#J z;Vh`+QEMHTs7D8F=bGBFJfXzQN&xI(ud4YV-|Eqp=3_^b2`+UD`Uu}-VjqUP04}Ng zNVRA`!uL@+Afk(2K%~n$87Dia_oAlvXmeskD@XzZgy$o8jD5H!R*m6m^>852xf5Lz zRRUE%(5iZ7IzAk8eLdeO?^WJ+O0G2}Z|IVH=Zq^UtPU;}bV|)JrAn~FWYas6>s!;d zzoi@dZmXFK(jgjMV}mH73pNLw$l)2<+Qa7n@UpUdV>nhrn_6pkCIu=T%+aNGe&oAv zlOFcg?m5a^POsC%xVEJg5mZk{e024z1f_19QuULap6A)cs-MjxVAyN%^lfd?;if{V zhPOb%TDsX$Rp_X;PJSIWDMOYP>tKqs9RyYnY*oY3N>zj3so_2+tQy^KJ30?&GkFVY z1$}A->2&{ZN3c`_5b}W3W%qga_!J@~CEr8Pt6TmnnnTzZcl<}D|a=C9#aZTPT zhG!Tyj2vTU!osXX%d=8(s?JAPNjA5$#9j&2s-b>xlFH}Yy}YtH_da$@UY_Oiait~c z)Rg>?IBY!*qF^@53-c_O*FpFQFcFS1wUA?qv?1*A`-{ov#gEt9o zrk)b?B`TibwpROmaFOyXDW3@oMQO5g{>*|#63&_p;cO%1j#H93$;_I(#1NR9EKC&5 zE3l}ON%?#tiQsd4HElWDPw?oX^Xt1^&45W9P_;s6S=L8cnUv5l#|Ol8cLnGMe0Jh< z7$4ih8Hw)+P}WoQ^$q%ZmITky*Ngaay++iV^z{q+`X~Cji7z3ii*#ikGOh)dlow=P zTxMRC85b_d^i$HRlS@10WN($seCLczzaVA1oKz?C&daQRneiHdv-$_5@2bqcAYJ`3 zy+Yz%$L&5Rry;Q#u zlwJUL$&9PA7*!pW-U~8IgJus%Ye1G(!c6InIdyp&Wxl6E`eL#mE~mw0POZ#{Qw!#F z%IpDI7L!wzO=XnCJgbn{dyUQDq zadgaK9mO$eb;u=^G7skVODisyu9g#SN#6yTQ$gkyk__C3Q?lT^ z^aDL07w?yu5S@{-p9(Ah+BlOc_Iy*$@*E%?o*1_9y7D!haw=-5r3xEXOCLH6ekmnf zqpq@m;ND!BUMKyidcU-G;py94*YU^OW!5cedk18?w}Tuu<8x&1tO}VGlNqGrZHfC? zw_OVtPk@UvKKnch%PE}%n=l}4gn)HHPTfy!O7m9WDFrt&;|yuLzf-vDDsxbNw;Kv zzw|>1y1rk|ref_*aFqPHEb5oOTjadVfeX?)BWLf23=M;kUhKZzwkPKCUY9v&sJt$x zBFSl&_B&$|kN1|$d{x?26z#BnafB2i1bs(k<^wXriIG}4nQD%q9$A9W#E6(xEAvjt z3a~xpvYe8bTuY!@S&IY_l#j&SC?`>(qEXgzP9j=&Nq-HKRQ`vCdI_oFe#<_*hf19A>5@0Z2sdhhFU;u)%WGAg7B&dbShMS07Q z%83Kst8(@(ITNFkJMg@m8CR6ITm}Z@+~;H|WcMJ!bUh#g7ilb!cVOzwi`1YQ?Q-(4 zEOPaF-F)nlJ)bN?FqNtX~tOal;4K*m?Il5N$c}z~)B?Fj@ zS1Cnrb0Ne44Ro}HLd;$zS0i!NssQhoD=4>07S-X2)aPI22)C!IXe7%EPs^#U3H|R% zxl0xTinj9jd>8ZUBrHeSaKv?KU%hn!mn^tQGZ1Fu+*+9)mk6kI-2dxAaf1Cwb4@y! zm{z8EXUNi8Sy(Im8)QbUv|SmE=va(SYnSc8CV~#^aNe$-N7IQZ-)zM8b~n+TO!9;4 zbt`u7z`1L5clEwVSEOb4`fzk_tfzPPHry6CTM?MMInupxXW33XFL#WL zqna|DSw;?)MZ=v716&}m=TKlREa;2{wzc3~tuMMT(0A`V=Q%v>tFXbwhu%H2gmo~G zk>-8Ixm7D=>DZUPIa-tD|B}5f(_@ET23o!RK`1TPw|W}q%CQ)z^`+irYMgyISzqcs ztj29tFn;NT>P@|8);N28vcA-ta*f-QqK{r!p=+&5gvL4MnR-(X6*O*ZioVoy42|2B zf=fMj(K!2PaywEFgbePXWL)Ygl*a8ysW=sXkzLRsAi^~1w}{eZZQf<;XXFhG@Oj961zbu zedln}qj3Kh)Os%K!Es9qQ$?T@Z@Qr>K+)l`TZBOEVQM=ly61I^Zo~TmC{@P^P)f_w zhWZXDrR8-{ZCv&@pu$Ye#FHy}N7OAAfNE!|+VFOO>R?_NM+XWQ2i3{Erws2LDAl&* zc$A^2UxHG$ybmhECDJpsv;frSm{$u*;T`~`aDAYZ|N0H?$A&ixN8YN$y`WTyMo_BW zH8_-3)SIA`&DTLGn?E+ZnK(&R(g#32$hQ0sP`jCW1C*-cEl_)zH;A(c#rvwEo-x$- zKy@+h*PxUI$Z`ow1PoPasGy-54ApF?UPB!*)Cog9W2om1b;(dy40X*=qlTg*5^9Hq zSG6=1FjS?Xf`+1h9Z)5X80v(fo-x$(hPq^^D~7ses8K@+EO(AAhJtBg1wJlBrKYgb ziy-q5OperVDB45>@p18cp{nc@BMpEG;^X4QQ%UC{uc(DaT4{Jo47JQiDOOa8prOce z%DMQRQYGDFqzy*8-S8R>MR69y$Hg;>D%)nH6s<~1F{PxvhKd>KLBo5@P)CgPsNsFt zP+u|96NdMsp}uCMCk^izLp^Jx-!;6`hI-yee`t7T4Rz5-FB#rThI-XV-!Qx@hI-3L zuNvMz80wmlzH4|RhPr8_qlWi^p>7-L$A%{moyy~GP(kor{GP4iWf-a7Na@X6B`q@4 z6eA57Ua6tx8Y#VONRQu zP!q8>DhqZSie5fe`uYv;+lG4AP#+oG1gxB@#0pSK-(f?YHPUws?_EQ=GIWUuD5d4= zhG${DRQlQs^(ZKX`)`IfYN-4yU1E`;1`PF)p+Zt7+%v6b$pj3$m4DV4xoi-E#QEXuA zQLR&U2B-$+%{J70LzNq9nW3r-wboD@4MpQh>HCbKK5Ho2btq}Gp=e*Gc%L&=43uiy z6j0mo@ri)ptv9@o;dL2aui-sucwaNTi-vc}@MsoQb&MDu&3cMQv!1erW+}zfvy>%v z8(y=)(X66y^b0yw_O#(WZ+LGR-c`f<*zg4AAyu{%l+riX@HQDtTMcy;k6lFr{R6Y@J<-s4-N0E;r)Z*T{Ap4=2%s) z7nG`Zq2W~;-gd)lG`wyLM^o5yQ&{7VbP;%|ECgP%tuM>|#h2Bv~Qm2*){ zZN_IjhDMUQ=&3jzv5d!H`gN|ONa=WGJO2hQ5A<4;`6|#{@{s*ZcUUikbgTh9z!!VV#9eIk&Z^jV-^}c z+0aABBI7YM&#Q}Gn5H9<@t8^t;on!$amaYgVuPU>l8!>gW0n}qMqua|WISf6!O;9l zM~9zzn5^q+T7hxf$kDLbuWV^?{?-^n4QfvH3?eGC99@xSg6 ziohg^Ig|K5cL*V1$P>v|;(y$s>w#$ihGx96t32WV?P#`fSvSXo3DjOkJGvDZ`ggmA z1gRnqI2f9dT-JA}dE>5Q4hCHh4E6PR*pm*1W-XW126b?H%Jqzc!KMwEv2m9O(=UP3 z%;vH_OcWXVf};nUHDKxtKaqIZ!O-mIvgjY|R7_}${^1SfsWqxaKSQZN66e?89KmM{ z^UuiXOO>KG-Snk6WA8^$$)+Y9iG&J(5a#tI6W#gnD5rs;ju z1U6pUMxIAa=_8LkqDAd}^usN+;`0x7cp=p<;Za7v%SW_+KafFqEtKC)WC6e5NEBW> z#p{w9P{)*h6_vQ^mDr?Lb`#$L9(ysGzzOefr@Za1ewCr$!B6~QLdx$o?)vMJl;6$# i|8jHU57?4_pVrhA+1HEr_w;Lfl+!<<(lYg{wEqWgHRj3y diff --git a/source/main.c b/source/main.c index e437b22..771939c 100644 --- a/source/main.c +++ b/source/main.c @@ -3,33 +3,6 @@ #include "../include/SterlingCompiler.h" -const char *SYMBOLS = ";(){}[]$%&*#@!?:,.<>|-+=~`^"; - -// Common C operators (Order matters: put longer ones first if you add 3-char ops) -MultiOp MUNCH_TABLE[] = { - {"<<=", 3}, {">>=", 3}, - {"==", 2}, {"!=", 2}, {"<=", 2}, {">=", 2}, - {"++", 2}, {"--", 2}, {"->", 2}, {"+=", 2}, - {"-=", 2}, {"*=", 2}, {"/=", 2}, {"&&", 2}, {"||", 2}, - {"^=", 2}, {"<<", 2}, {">>", 2}, {"|=", 2}, {"&=", 2}, - {NULL, 0} -}; - -// This can be expanded at runtime if you use a dynamic array instead of a static one -KeywordEntry KEYWORD_TABLE[] = { - {"if", TOK_KEY}, - {"else", TOK_KEY}, - {"while", TOK_KEY}, - {"return", TOK_KEY}, - {"int", TOK_KEY}, - {"float", TOK_KEY}, - {"void", TOK_KEY}, - {"include", TOK_PREPROC}, - {"define", TOK_PREPROC}, - {"@comptime",TOK_KEY}, // Your custom identifier - {NULL, TOK_NONE} -}; - # ifndef strndup char *strndup(const char *s, size_t n) { char *str = calloc(n + 1, sizeof(char)); @@ -38,6 +11,21 @@ char *strndup(const char *s, size_t n) { } # endif +char *LoadFile(const char *filename) { + FILE *file = NULL; + char *data = NULL; + file = fopen(filename, "r"); + assert(file); + fseek(file, 0, SEEK_END); + long size = ftell(file); + fseek(file, 0, SEEK_SET); + data = (char *)malloc(size + 1); + assert(data); + fread(data, 1, size, file); + data[size] = 0x00; + fclose(file); + return (data); +} bool IsWhitespace(const char *s) { while (*s) { @@ -47,6 +35,90 @@ bool IsWhitespace(const char *s) { return true; } +bool IsNumeric(const char *s) { + if (!s || !*s) return false; + for (int i = 0; s[i]; i++) { + if (!isdigit((unsigned char)s[i])) return false; + } + return true; +} + +bool IsComplexNumeric(const char *s, size_t len) { + if (len == 0) return false; + + // Most numbers start with a digit + if (isdigit((unsigned char)s[0])) return true; + + // Floats can start with a dot (e.g., .5) + if (s[0] == '.' && len > 1 && isdigit((unsigned char)s[1])) return true; + + return false; +} + +void ResolveTrigraphs(char *data) { + char *src = data, *dst = data; + while (*src) { + if (src[0] == '?' && src[1] == '?' && src[2]) { + char c = 0; + switch (src[2]) { + case '=': c = '#'; break; case '/': c = '\\'; break; + case '\'': c = '^'; break; case '(': c = '['; break; + case ')': c = ']'; break; case '!': c = '|'; break; + case '<': c = '{'; break; case '>': c = '}'; break; + case '-': c = '~'; break; + } + if (c) { *dst++ = c; src += 3; continue; } + } + *dst++ = *src++; + } + *dst = '\0'; +} + +typedef struct { + list_iter_t iter; + list_t *tokens; + bool error; +} Parser_t; + +// Initialize the parser +Parser_t ParserInit(list_t *lst) { + return (Parser_t){ .iter = ListGetIter(lst), .tokens = lst, .error = false }; +} + +// Look at current token +Token_t* Peek(Parser_t *p) { + if (!p->iter.current) return NULL; + return (Token_t*)p->iter.current->data; +} + +// Move to next +void Advance(Parser_t *p) { + if (p->iter.current) p->iter.current = p->iter.current->next; +} + +// Check if current matches type (and optionally value) +bool Match(Parser_t *p, TKN_CTX ctx, const char *val) { + Token_t *t = Peek(p); + if (!t || (t->ctx != ctx)) return false; + if (val && strcmp(t->data, val) != 0) return false; + + Advance(p); + return true; +} + +// Required token; errors out if not found +Token_t* Expect(Parser_t *p, TKN_CTX ctx, const char *val) { + Token_t *t = Peek(p); + if (!t || (t->ctx != ctx) || (val && strcmp(t->data, val) != 0)) { + printf("Syntax Error: Expected '%s', but found '%s'\n", + val ? val : "specific type", t ? t->data : "EOF"); + p->error = true; + return NULL; + } + Advance(p); + return t; +} + void ClearTokens(void*arg) { Token_t *tok = arg; free(tok->data); @@ -72,6 +144,58 @@ void PushToken(list_t *lst, const char *start, const char *end, TKN_CTX ctx) { ListPushBack(lst, NewToken(start, end - start, ctx)); } +void IdentifyTokens(list_t *lst) { + for (node_t *curr = lst->first; curr; curr = curr->next) { + Token_t *t = (Token_t *)curr->data; + + // Skip nodes that were already identified (like TOK_STRING or munched TOK_OP) + if (t->ctx != TOK_RAW) continue; + + // 1. Check Keyword Registry (Highest Priority) + bool found = false; + for (int i = 0; KEYWORD_TABLE[i].name != NULL; i++) { + if (strcmp(t->data, KEYWORD_TABLE[i].name) == 0) { + t->ctx = KEYWORD_TABLE[i].ctx; + found = true; + break; + } + } + if (found) continue; + + // 2. Check for Numeric Literals (0x..., 3.14, 100L) + if (isdigit((unsigned char)t->data[0]) || (t->data[0] == '.' && t->size > 1 && isdigit(t->data[1]))) { + t->ctx = TOK_NUM; + continue; + } + + // 3. Check for Identifiers (my_var, @comptime) + if (isalpha((unsigned char)t->data[0]) || t->data[0] == '_' || t->data[0] == '@') { + t->ctx = TOK_ID; + continue; + } + + // 4. Check for Operators/Symbols (;, +, -, #) + // If it's in our SYMBOLS string, it's an operator or preprocessor trigger + if (strchr(SYMBOLS, t->data[0])) { + // Special case for '#' which is often its own thing + if (t->data[0] == '#') t->ctx = TOK_PREPROC; + else t->ctx = TOK_OP; + continue; + } + } +} + +void ApplyTypeAliases(list_t *lst) { + for (node_t *curr = lst->first; curr; curr = curr->next) { + Token_t *t = curr->data; + // If we see 'int', we could programmatically replace it + // with the sequence ': 4' during a transformation pass. + if (t->ctx == TOK_ID && strcmp(t->data, "int") == 0) { + // Logic to transform token... + } + } +} + void ListSplitToken(list_t *lst, node_t *node, size_t index) { Token_t *t = (Token_t *)node->data; @@ -90,37 +214,31 @@ void ListSplitToken(list_t *lst, node_t *node, size_t index) { t->size = index; } -char *LoadFile(const char *filename) { - FILE *file = NULL; - char *data = NULL; - file = fopen(filename, "r"); - assert(file); - fseek(file, 0, SEEK_END); - long size = ftell(file); - fseek(file, 0, SEEK_SET); - data = (char *)malloc(size + 1); - assert(data); - fread(data, 1, size, file); - data[size] = 0x00; - fclose(file); - return (data); -} - - void InitialScanner(char *data, list_t *tkn_lst) { char *curr = data, *start = data; while (*curr) { - // Handle Strings - if (*curr == '\"' || *curr == '\'') { + // 1. Handle Wide or Normal Strings/Chars + // Check for 'L' followed immediately by a quote + bool is_wide = (*curr == 'L' && (curr[1] == '\"' || curr[1] == '\'')); + if (*curr == '\"' || *curr == '\'' || is_wide) { PushToken(tkn_lst, start, curr, TOK_RAW); - char *s_start = curr++, q = *curr; - while (*curr && *curr != q) { if (*curr == '\\') curr++; curr++; } - if (*curr) curr++; + + char *s_start = curr; + if (is_wide) curr++; // Advance past 'L' + + char q = *curr; + curr++; // Skip opening quote + while (*curr && *curr != q) { + if (*curr == '\\' && curr[1]) curr++; // Skip escaped char + curr++; + } + if (*curr) curr++; // Skip closing quote + PushToken(tkn_lst, s_start, curr, TOK_STRING); start = curr; } - // Handle Comments + // 2. Handle Comments (Same as before) else if (*curr == '/' && (curr[1] == '/' || curr[1] == '*')) { PushToken(tkn_lst, start, curr, TOK_RAW); if (curr[1] == '/') { while (*curr && *curr != '\n') curr++; } @@ -139,7 +257,8 @@ void InitialScanner(char *data, list_t *tkn_lst) { void RefineSymbols(list_t *tkn_lst) { for (node_t *curr = tkn_lst->first; curr; ) { Token_t *t = curr->data; - if (t->ctx != TOK_RAW || (t->size == 1 && strchr(SYMBOLS, t->data[0]))) { + //IsComplexNumeric(t->data, t->size) || + if (t->ctx != TOK_RAW || (t->size == 1 && strchr(SYMBOLS, t->data[0]))) { curr = curr->next; continue; } @@ -151,39 +270,131 @@ void RefineSymbols(list_t *tkn_lst) { } else { curr = curr->next; } + } +} + +void MunchFloats(list_t *lst) { + for (node_t *n = lst->first; n && n->next && n->next->next; ) { + Token_t *t1 = n->data, *dot = n->next->data, *t2 = n->next->next->data; + + // Look for [Digit] [.] [Digit] + if (isdigit(t1->data[0]) && dot->data[0] == '.' && dot->size == 1 && isdigit(t2->data[0])) { + size_t new_size = t1->size + 1 + t2->size; + char *buf = malloc(new_size + 1); + sprintf(buf, "%s.%s", t1->data, t2->data); + + free(t1->data); + t1->data = buf; + t1->size = new_size; + t1->ctx = TOK_NUM; // Mark it now! + + // Remove '.' and '14' + for(int i=0; i<2; i++) { + node_t *rem = n->next; + n->next = rem->next; + if (lst->last == rem) lst->last = n; + ClearTokens(rem->data); free(rem); + lst->size--; + } + continue; + } + n = n->next; } } -void MunchTokens(list_t *lst) { - for (node_t *n = lst->first; n && n->next; ) { - Token_t *t1 = n->data, *t2 = n->next->data; +void MunchScientificNotation(list_t *lst) { + for (node_t *n = lst->first; n && n->next && n->next->next; ) { + Token_t *t1 = n->data; + Token_t *op = n->next->data; + Token_t *t2 = n->next->next->data; - if (t1->ctx == TOK_RAW && t2->ctx == TOK_RAW && t1->size == 1 && t2->size == 1) { - char op[3] = { t1->data[0], t2->data[0], '\0' }; - bool match = false; - for (int i = 0; MUNCH_TABLE[i].op; i++) { - if (strcmp(op, MUNCH_TABLE[i].op) == 0) { match = true; break; } - } - - if (match) { + // Check if t1 ends with 'e' or 'E' (and t1 is currently RAW) + if (t1->ctx == TOK_RAW && t1->size > 0) { + char last = tolower((unsigned char)t1->data[t1->size - 1]); + + if (last == 'e' && + (op->data[0] == '+' || op->data[0] == '-') && op->size == 1 && + isdigit((unsigned char)t2->data[0])) { + + // We found a match! (e.g., "1e" + "-" + "5") + size_t new_size = t1->size + op->size + t2->size; + char *new_data = malloc(new_size + 1); + + sprintf(new_data, "%s%s%s", t1->data, op->data, t2->data); + free(t1->data); - t1->data = strndup(op, 2); - t1->size = 2; - t1->ctx = TOK_OP; // Upgrade to Operator context + t1->data = new_data; + t1->size = new_size; - node_t *tmp = n->next; - n->next = tmp->next; - if (lst->last == tmp) lst->last = n; - ClearTokens(tmp->data); - free(tmp); - lst->size--; - continue; // Check if the next char can be munched too (e.g. >>=) + // Remove the op and t2 nodes + for (size_t i = 0; i < 2; i++) { + node_t *to_remove = n->next; + n->next = to_remove->next; + if (lst->last == to_remove) lst->last = n; + ClearTokens(to_remove->data); + free(to_remove); + lst->size--; + } + // Check this same node again (in case of weird nesting, though rare here) + continue; } } n = n->next; } } +void MunchTokens(list_t *lst) { + node_t *curr = lst->first; + + while (curr) { + Token_t *t1 = curr->data; + if (t1->ctx != TOK_RAW && t1->ctx != TOK_OP) { + curr = curr->next; + continue; + } + + bool matched = false; + for (size_t i = 0; MUNCH_TABLE[i].op; i++) { + size_t len = MUNCH_TABLE[i].len; + + // 1. Peek ahead to see if we have enough nodes + node_t *temp = curr; + char buffer[5] = {0}; // Max munch is 4 + size_t nodes_found = 0; + + for (size_t j = 0; j < len && temp; j++) { + Token_t *tk = temp->data; + if (tk->size != 1) break; // Multi-char tokens can't be part of a new munch + buffer[j] = tk->data[0]; + temp = temp->next; + nodes_found++; + } + + // 2. Compare buffer to table entry + if (nodes_found == len && strcmp(buffer, MUNCH_TABLE[i].op) == 0) { + // SUCCESS: Consolidate 'len' nodes into 'curr' + free(t1->data); + t1->data = strndup(MUNCH_TABLE[i].op, len); + t1->size = len; + t1->ctx = (MUNCH_TABLE[i].op[0] == '%') ? TOK_PREPROC : TOK_OP; + + // Remove the 'tail' nodes + for (size_t j = 1; j < len; j++) { + node_t *to_remove = curr->next; + curr->next = to_remove->next; + if (lst->last == to_remove) lst->last = curr; + ClearTokens(to_remove->data); + free(to_remove); + lst->size--; + } + matched = true; + break; + } + } + // If we munched, stay on 'curr' to see if a new sequence formed + if (!matched) curr = curr->next; + } +} void RefineRawNodes(list_t *tkn_lst) { node_t *curr = tkn_lst->first; @@ -256,64 +467,110 @@ void PruneWhitespaceNodes(list_t *lst) { } } -void IdentifyTokens(list_t *lst) { - for (node_t *curr = lst->first; curr; curr = curr->next) { - Token_t *t = (Token_t *)curr->data; +/* +// Modular function to register new identifiers +void RegisterIdentifier(const char *name, TKN_CTX type) { + //insert this into a Hash Map. + //this is where user-defined types go. +} +*/ - if (t->ctx != TOK_RAW) continue; +void ParseVarDeclaration(Parser_t *p) { + // 1. We already saw 'var' (the trigger) + + // 2. Expect an Identifier (the name) + Token_t *name = Expect(p, TOK_ID, NULL); + if (p->error) return; - bool found = false; - // 1. Check against Keyword Registry - for (int i = 0; KEYWORD_TABLE[i].name != NULL; i++) { - if (strcmp(t->data, KEYWORD_TABLE[i].name) == 0) { - t->ctx = KEYWORD_TABLE[i].ctx; - found = true; - break; - } - } + // 3. Expect the separator ':' + Expect(p, TOK_OP, ":"); + if (p->error) return; - // 2. If not a keyword, is it a valid Identifier? (e.g., my_var_1) - if (!found && t->size > 0) { - if (isalpha(t->data[0]) || t->data[0] == '_' || t->data[0] == '@') { - t->ctx = TOK_ID; - } + // 4. Expect the size (numeric) + Token_t *size = Expect(p, TOK_NUM, NULL); + if (p->error) return; + + printf("Defined variable '%s' with size %s bytes.\n", name->data, size->data); + + // 5. Finalize with semicolon + Expect(p, TOK_OP, ";"); +} + +void Parse(Parser_t *p) { + while (Peek(p) != NULL && !p->error) { + Token_t *t = Peek(p); + + if (t->ctx == TOK_KEY && strcmp(t->data, "var") == 0) { + Advance(p); // Consume 'var' + ParseVarDeclaration(p); + } + else { + printf("Unknown token: %s\n", t->data); + Advance(p); } } } -/* -// Modular function to register new identifiers -void RegisterIdentifier(const char *name, TKN_CTX type) { - // In a professional compiler, you'd insert this into a Hash Map. - // For now, it's enough to know this is where user-defined types go. +const char* CtxToString(TKN_CTX ctx) { + if (ctx & TOK_KEY) return "KEYWORD"; + if (ctx & TOK_ID) return "IDENTIFIER"; + if (ctx & TOK_NUM) return "NUMBER"; + if (ctx & TOK_OP) return "OPERATOR"; + if (ctx & TOK_STRING) return "STRING"; + if (ctx & TOK_PREPROC) return "PREPROCESS"; + if (ctx & TOK_COMMENT) return "COMMENT"; + if (ctx & TOK_RAW) return "RAW"; + if (ctx & TOK_LITERAL) return "LITERAL"; + if (ctx & TOK_NONE) return "NONE"; + return "UNKNOWN"; } -*/ +/* +pass on ";(){}[]$%&*$#@!?:,.<>|_-+=~`" +and give each token a context +let's replace preprocessor (include, define, etc) +let's do recursive parsing everywhere that need it +compile time reflection (@comptime or @reflect) +metaprogramming logic annotation if i do it lastly** may not be +*/ int main(int ac, char **av) { if (ac <= 1) return printf("No file specified\n"), -1; - char* data = LoadFile(av[1]); - list_t *tkn_lst = ListInit(NULL); + char* data = LoadFile(av[1]); + assert(data); + ResolveTrigraphs(data); + + list_t *tkn_lst = ListInit(NULL); + assert(tkn_lst); InitialScanner(data, tkn_lst); PruneWhitespaceNodes(tkn_lst); RefineRawNodes(tkn_lst); RefineSymbols(tkn_lst); + MunchFloats(tkn_lst); + MunchScientificNotation(tkn_lst); MunchTokens(tkn_lst); IdentifyTokens(tkn_lst); - + list_iter_t iter = ListGetIter(tkn_lst); - while (iter.current) { + printf("\n--- TOKEN STREAM ---\n"); + printf("%-6s | %-12s | %s\n", "HEX", "CONTEXT", "VALUE"); + printf("-------|--------------|----------\n"); + while (iter.current) { Token_t *t = (Token_t *)iter.current->data; - printf("[%02X] %-10s | %s\n", t->ctx, - (t->ctx == TOK_ID ? "IDENTIFIER" : "TOKEN"), t->data); + + // Use CtxToString for the middle column + printf("[0x%04X] | %-12s | %s\n", + t->ctx, + CtxToString(t->ctx), + t->data); + iter.current = iter.current->next; } - //pass on ";(){}[]$%&*$#@!?:,.<>|_-+=~`" - //and give each token a context - //let's replace preprocessor (include, define, etc) - //let's do recursive parsing everywhere that need it - //compile time reflection (@comptime or @reflect) - //metaprogramming logic annotation if i do it lastly** may not be + printf("--------------------\n"); + + //Parser_t p = ParserInit(tkn_lst); + //Parse(&p); + ListFree(tkn_lst, ClearTokens); free(data); return(0);