From 396b51b3a719a6a920b40c790fedae7a0ca68441 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Mon, 24 Feb 2025 22:12:13 -0600 Subject: [PATCH] add ast_print --- ast.h | 6 +-- ast_print.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++ ast_print.h | 12 ++++++ compiler.mk | 1 + lexer.c | 13 +++++-- lexer.h | 4 +- main_hosted.c | 18 +-------- print_class | Bin 37420 -> 44916 bytes 8 files changed, 135 insertions(+), 25 deletions(-) create mode 100644 ast_print.c create mode 100644 ast_print.h diff --git a/ast.h b/ast.h index e0150e2..e0ca053 100644 --- a/ast.h +++ b/ast.h @@ -1,6 +1,6 @@ #pragma once -struct token; +#include "lexer.h" struct expression { struct token constant; @@ -20,7 +20,7 @@ struct statement_return { struct statement_if_else { struct expression * expression; struct statement * statement_if; - struct statement * statement_else; + struct statement * statement_else; // might be nullptr }; struct statement { @@ -32,7 +32,7 @@ struct statement { }; struct function_definition { - struct token * name; + struct token name; struct statement * statements; }; diff --git a/ast_print.c b/ast_print.c new file mode 100644 index 0000000..51e57d3 --- /dev/null +++ b/ast_print.c @@ -0,0 +1,106 @@ +#include "assert.h" +#include "printf.h" +#include "ast_print.h" + +static void print_indent(int indent) +{ + for (int i = 0; i < indent; i++) { + print_char(' '); + print_char(' '); + } +} + +#define printi(indent, ...) \ + do { \ + print_indent(indent); \ + _printf(__VA_ARGS__); \ + } while (0); + +const char * statement_type_str(enum statement_type type) +{ + static const char * map[] = { + [STATEMENT_RETURN] = "STATEMENT_RETURN", + [STATEMENT_IF_ELSE] = "STATEMENT_IF_ELSE", + }; + assert(((int)type) < ((sizeof (map)) / (sizeof (map[0])))); + return map[(int)type]; +} + +const char * token_type_str(enum token_type type) { + static const char * map[] = { + [TOKEN_INVALID] = "TOKEN_INVALID", + [TOKEN_EOF] = "TOKEN_EOF", + [TOKEN_IDENTIFIER] = "TOKEN_IDENTIFIER", + [TOKEN_CONSTANT] = "TOKEN_CONSTANT", + [TOKEN_INT] = "TOKEN_INT", + [TOKEN_VOID] = "TOKEN_VOID", + [TOKEN_RETURN] = "TOKEN_RETURN", + [TOKEN_LPAREN] = "TOKEN_LPAREN", + [TOKEN_RPAREN] = "TOKEN_RPAREN", + [TOKEN_LBRACE] = "TOKEN_LBRACE", + [TOKEN_RBRACE] = "TOKEN_RBRACE", + [TOKEN_SEMICOLON] = "TOKEN_SEMICOLON", + }; + assert(((int)type) < ((sizeof (map)) / (sizeof (map[0])))); + return map[(int)type]; +} + +void print_token(int indent, struct token token) +{ + printi(indent, "type=%s str=", token_type_str(token.type)); + const uint8_t * bufi = token.start; + while (bufi < token.end) { + print_char((char)*bufi++); + } + print_char('\n'); +} + +void print_expression(int indent, struct expression * expression) +{ + printi(indent, "token=(\n"); + print_token(indent + 1, expression->constant); + printi(indent, ")\n"); +} + +void print_return(int indent, struct statement_return * statement_return) +{ + printi(indent, "expression=(\n"); + print_expression(indent + 1, statement_return->expression); + printi(indent, ")\n"); +} + +void print_if_else(int indent, struct statement_if_else * statement_if_else) +{ + printi(indent, "expression=(\n"); + print_expression(indent + 1, statement_if_else->expression); + printi(indent, ")\n"); + printi(indent, "statement_if=(\n"); + print_statement(indent + 1, statement_if_else->statement_if); + printi(indent, ")\n"); + printi(indent, "statement_else=(\n"); + if (statement_if_else->statement_else == nullptr) { + printi(indent + 1, "nullptr\n"); + } else { + print_statement(indent + 1, statement_if_else->statement_else); + } + printi(indent, ")\n"); +} + +void print_statement(int indent, struct statement * statement) +{ + printi(indent, "statement type=%s (\n", statement_type_str(statement->type)); + switch (statement->type) { + case STATEMENT_RETURN: print_return(indent + 1, statement->statement_return); break; + case STATEMENT_IF_ELSE: print_if_else(indent + 1, statement->statement_if_else); break; + default: + assert(false); + break; + } + printi(indent, ")\n"); +} + +void print_function_definition(int indent, struct statement * statement) +{ + printi(indent, "function_definition name="); + print_token(statement->name); +} diff --git a/ast_print.h b/ast_print.h new file mode 100644 index 0000000..e24f843 --- /dev/null +++ b/ast_print.h @@ -0,0 +1,12 @@ +#pragma once + +#include "ast.h" + +const char * statement_type_str(enum statement_type type); +const char * token_type_str(enum token_type type); + +void print_token(int indent, struct token token); +void print_expression(int indent, struct expression * expression); +void print_return(int indent, struct statement_return * statement_return); +void print_if_else(int indent, struct statement_if_else * statement_if_else); +void print_statement(int indent, struct statement * statement); diff --git a/compiler.mk b/compiler.mk index 65cb20d..44bf0dd 100644 --- a/compiler.mk +++ b/compiler.mk @@ -5,4 +5,5 @@ OBJ = \ printf.o \ string_parse.o \ string_unparse.o \ + ast_print.o \ main_hosted.o diff --git a/lexer.c b/lexer.c index 3251349..5922298 100644 --- a/lexer.c +++ b/lexer.c @@ -80,12 +80,14 @@ static inline enum token_type find_keyword(const uint8_t * buf, int start, int e struct token lexer_next_token(struct lexer_state * state) { struct token token; + int token_start; + int token_end; while (state->offset < state->size && is_whitespace(state->buf[state->offset])) { state->offset += 1; } - token.start = state->offset; + token_start = state->offset; uint8_t c = state->buf[state->offset++]; @@ -108,7 +110,7 @@ struct token lexer_next_token(struct lexer_state * state) break; state->offset += 1; } - token.type = find_keyword(state->buf, token.start, state->offset); + token.type = find_keyword(state->buf, token_start, state->offset); } else if (is_decimal_digit(c)) { while (state->offset < state->size) { uint8_t c = state->buf[state->offset]; @@ -128,10 +130,13 @@ struct token lexer_next_token(struct lexer_state * state) break; } - token.end = state->offset; + token_end = state->offset; - if (token.start >= state->size) + if (token_start >= state->size) token.type = TOKEN_EOF; + token.start = &state->buf[token_start]; + token.end = &state->buf[token_end]; + return token; } diff --git a/lexer.h b/lexer.h index 62b5f94..7155895 100644 --- a/lexer.h +++ b/lexer.h @@ -21,8 +21,8 @@ enum token_type { struct token { enum token_type type; - int start; - int end; + const uint8_t * start; + const uint8_t * end; int value; }; diff --git a/main_hosted.c b/main_hosted.c index 47c9b95..ee5a8cb 100644 --- a/main_hosted.c +++ b/main_hosted.c @@ -4,6 +4,7 @@ #include #include "lexer.h" +#include "ast_print.h" int read_file(const char * filename, uint8_t ** buf) { @@ -50,21 +51,6 @@ int read_file(const char * filename, uint8_t ** buf) return size; } -const char * token_str[] = { - [TOKEN_INVALID] = "TOKEN_INVALID", - [TOKEN_EOF] = "TOKEN_EOF", - [TOKEN_IDENTIFIER] = "TOKEN_IDENTIFIER", - [TOKEN_CONSTANT] = "TOKEN_CONSTANT", - [TOKEN_INT] = "TOKEN_INT", - [TOKEN_VOID] = "TOKEN_VOID", - [TOKEN_RETURN] = "TOKEN_RETURN", - [TOKEN_LPAREN] = "TOKEN_LPAREN", - [TOKEN_RPAREN] = "TOKEN_RPAREN", - [TOKEN_LBRACE] = "TOKEN_LBRACE", - [TOKEN_RBRACE] = "TOKEN_RBRACE", - [TOKEN_SEMICOLON] = "TOKEN_SEMICOLON", -}; - int main(int argc, char * argv[]) { if (argc != 3) { @@ -89,7 +75,7 @@ int main(int argc, char * argv[]) while (true) { struct token token = lexer_next_token(&lexer_state); - printf("%s\n", token_str[token.type]); + printf("%s\n", token_type_str(token.type)); if (token.type == TOKEN_INVALID) return EXIT_FAILURE; if (token.type == TOKEN_EOF) diff --git a/print_class b/print_class index 7acecf966086a812dd8e863cb57c34a6fdf128bf..7923c88f08e37ad09decc0b4a3a09140b82c1c11 100755 GIT binary patch delta 17142 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 HoZ^22*yb=5 delta 11810 zcmb7Kd3aPswy(NN-`nX1q~2c5_Ull2v0?6AgEwu z3FU}rU=&RwNcfYUd)LH7(sZ-mj z>i*$VWyk%>`fO$D?JYU=`W^>kds; z6B_DlIN64CY%KM3}zFDq3I7na9 zkFnDU;=SYy(bM0VpA{4RA^lXs?y6H_slPZp63*FQbp}0Vrv_SO^qQUOWtGu$cB+$A zhFjUG43w#IP5WcNMj}o7w3ROFVYxce9tWN{t^(*v(c|ordJ;n)a2JWYbx` z@})(7i}tid6J3(K+Vg%Rz3$ajXHMM;y;frr#ggRA^sj+@UDI9` zt1s80eI}kx9-aJyt*FxM^U93|`UPv5xG2z3sqG`i2a5PA(H!Wfd|f8q2$U(0mkA{} z*zELkB(nU~X=P$ma3o(TT7&t@cctRh;2?9>dEz`!Dm*EbN`0xQNqNe=`W(?7>Mh)< zdx8`G5s5Tcoov49mp5OO;?`jCC;bzhQo`}hA4e|iBlcZGnV{B zeekTZAKPWO zS67{Ey6Gfq?9=`O2+a_3*R(%u@tVa53{`Uy$**bpo@!p!zPVkz&^1lz&@Lu+%P`|U z2eIAVE+Sn6%9wT$1T9U{-u+6Hcgt2be?@W?*^;*EE7|6r2)q{}Pd&hOUDUa`Mr(RSYmSR{@6t%onx^0h*}HvTS{BkAJsrlX zX?n>drZMw+YIn^!iiM15&{pD#sR zk2LSgdq7}orkxu%;i@%LetEU6qKB$xy@f`Xzv{@5W7EC^R%7y+?QN3$U5Bza5E;1N^_z_ZRTB7})~5d>}?988q!cBr+7~Hl+VS`W~t0yOGE>Nb8Xj z4n`t@gOLuA3>5N^x*!!J^+Bpc8j3UuNfZrQEqv>f#T^%~4o_JyqanLP|LmUG`BSs= z7sfFjQu|GYr@Fueb)Q5c+flCjG!l6Sa1P*&fSW#xM2-O-0i<&@<5(n;4OjzM33vc- zDqsa>;VQs+@X-#yg@7*so&anI%s3g2L`-bEGfqY#g@B&`jsWcVMIv4fNubLzedA=mjIernSTkN ziA1`hQ1T5L2HXcY2k_2sBayp^{v8?yJPCLN@Zi}<460QzZ$;S%8rl4>s2{{ zRYzf)#bxP$W00QsMEq8jsT}-7WDdzxe*8oX05Crl6Nls|^`D9rLvs4=!q^nSr);3d zyji(8ULV|~ss+kxuevo2$1Kancs+-4LGR_4hr9%|M~{h5hZHE^9}|wDnM(X|Q2|CR1RCkqbUA-QhW(!_7}o2JX7iSg(w)FqcnXbs(~H*O3VhRX&1K* z@2vdPE_MT3bV_^(pnWZV0yy}!2vlb(N6v`e)j7&z--z+R6228T0EE94A6=5^^kaZV zV4w~QJ|Zpn%FAxf2EID*wL$PiIeI+|?6YS@@rb^TRVZfvT`U;UH~9?|=VLVG$iRn9 z$|#Sw?L9|Ft#1v&5{6_JVSrI27rId*DJat@5FRQL8%7nR-QSI|RG<`j zh5)Hr8dBXT@&2faLc}{%;W{L08=(*jB2_{Im1m*_YX6AnGrE)q#I>Wl@=;>T=qu{? zbOJf%qZo1-k_=~K=u}KDW-J$}8`1!zkw{k})gwi}c_`nAv>NG7q;17wam}#shty^J zqxqlYDM=Ir3#6c)3EtbT}T5M^sNVnaZ?OiI{33k-}*bb2HNgLARDM2O6C9jnkQy>7z1Jm%yD`mZd?xAG@2DZE0X} z&EU(;%*~S@h*snQyP|@MJZM)WNAMJBl*x>1A)G`t!p|b}N&hh85tLmy5DeuISy$> zM2pzKJhvw^Hf|tTATO}V^>8owFz^gAJ|v5LxB=7gT0q|VXXwz#H$%$FHA)L}^if3d zn2QR>11$}}06&Cj>hM;=q;P3*jBxXqRIlR&lAe(P0j0(5Qp8hZ3hNGn7Dpa&H=w$g zY9Gg(?J0H9Jw|I_-fouhib(9Q)*1I_%0l@b2`E)Bz88keI5#a3I1nhI`pP$i1SNvy)*p3DJ&jn(LeY3-hcLC0ku?svhZJUJPehS9$Q;KpY9|5I zY=>hbRnbs(7$%jOLhf{!vL~8CzHvAM$wOt36f^mtTY&(^LhXcs+){&s;$;9YOMXWTn25_Y8kkV4;r4><+ zq^s+T#OB(}@K_-I)2SETq2)QLWj_g!mj2bTS}HP0Olg^{7K1lXx({21N)8-kw=h#B z4-U4mIn4PU&|V>O*y$j|q<$ARIH|wauyC{iQdw%NoGFbi%b;3H3zRHJp<6Ny=Y3E@ z8#`|u9aYk;JXS{!TSuKr4lk1ahRMvi0XlL$G?BVM$6~2ttA;f%Ey*pilxTX8&rCS?v!Q7?Cnm8+38%f(IYq4%%kAzbEnN^dOQ`G z`+h^rpU@-!I(-9ss?N^k;YA639AOXv@iB#6e zzp8zj`~dsZ9$oJO-C_CKVV^Ab5a+JcCy8VEUCVNSv?+8uNI^}1hd9=;lSpMPf(ErT z-%Kk1gxXrj7baP488Y39&=Sw0tkJzsPxVuPl*rK|jfPE6^OK;K45p(n)EV>?tpvW1 z%F~HDBPA15-<2reVO;b&jY>BN7(ET@7~|W}s?poIpVaU~aKc8Z#7y1^wi{&gKDFnL{j+2q^?{d>3j%@^_NIC-%UuRMDqAz(l$^cg}juIizU*P z&kd8h!GX_UX%T;%)D7{U1X9ACq+yt}yNsVEWVl4i`C>wp|ANFWO*ju^7{7s#aq-VUEIfiwA#6fWh4>hLJ!z;*jR#W8 zZy{5!lJ?Z`dq`}e^EFhO%!^5EQgRZ+rt~Tl5EmOC0(janU)#F{aeI0^&!W*aPXy<2v0+(h}f5Qin1p_Q4eZu7Q5VS z5$C}1zkrl*94%FwVq8NgOqHrTMk}TKQE9{joo z=5$;V3x^T|<}saSlK)%OOecZ0*m{@An!BPk(=WO~6W=!^o7LzaPw-zMyi>EoY1Hj) zkj$V93GK2wZsn#TRXCW6L zxpP^f$F%$s5=^xP=Nq|F5Tp)Ht1djRQOUI(M6T;pULsaa%L=~SzTsO^aH9js@8 zb>w2Jd-5_5>_6S*75hMErwx2HkIHnEX!c#_Wgppllw)(QgScsCPw5Z;z^JN|;>DSv{3~)|jPCG9<%H9y%pKC&@gg!a82$`)QDpN0 zy5X0Tr`Dc4(Kn-f{Ep}747T!NuowpE*3{9hAx)sYO}FNjZUsINprr$Wjo>-#h^LC= ztO=HEO)3W_QkVjnTZv@_GP<>@9CjdcA91We#$g9CH0=VK^C#i7AuF)ZN+n{&6Pgu+T1+2m%YZS{odg1VgR3LL1@_Q}k*D7Fujzp~VIkTD6Pb;jF+y z8<{i$h!t39qY}mgu>uQiv^fjftiVFMG~s$6R$!rxi{B10E3VKc1e?II;)<|Vm%0X+ z61K}e+=ouN?^hVJO4!jf{7nMQ{e*TR^aidF#`RePNfO_OhlntU+PUSQ{RMY_w$gVB>fW4)Jjxx z5+Us(2=iW^oha)n{d*!Yo|??{kQ=^3QYd38*_=4sVA;&vBhT$PG^vm~nK27G%p*!d z0gxi$T9g{j1;u%<;-N)1lmzAQ8Ml$3p(fr3GR2E_{eDE|s#F_}k`)qC$O{O&Nnje6 z0GYS%Loe*1M!EC24Hl29bt`QO!9h-bh^@*St}oX$I6jb z8tXC!cE*o-P$FNGCy2_%`D$Pl^v6Lj7B3!Vh|j}e z@yZQJUH+{J%o3{Ql?po;R@Tq9qDk3=-Ps9Zr&uwV$cRQ<cC)9)!oGd%OgBlJg}mt85pG}OxOJb#3w8I z@g|YlG>WefGn=OR{S|urblp8b50&eV>ALq4J#Mi$(=?Blw$-hi!1?oH&&_-J6>W{T zoacOrn78T-AKzBDdZ?;g!o~TU^Tge2lihAQ4Y4~Sn=YPPn~~W8MQt8s*HN~dGM(dO zqj66YzpicSr6$$t4)d^HGEUcFfcc!BexvT04%#KUdxq}2P7ln`{f&CsEIrrkq3h+k z$Lt|?uj}6yS$8FGo7{509-KR6&YXD-Y~idMrcGMJ7R|eQ+BK6FE?UsGX5)S?CT|Lg zrcE8qEqc4I{jC2}Jh`ctXx~(YAH=%fT|qy?xnVEI-@Rx;;@xlQ-I+q+k1pY(}eu@;xpQ=!RLEj=j5rcmZya2Z<>+`U4C^W|4zXD$u zgDX6jF}k2xpGW@1i>_Z$+>PPwD!#cVEBt5-H5=3?WAJ?7&&1&L3i476PJY`TgZBXc zKn&g!_y;j~De$8)ct4JxK2O9DDj@K+*xZ`McZ=s+L!x%GH#{G-_1KrD1JYn!56HSQ zcAJf_0Hi@Bcs6i4UMDAFxuBe+cvuDP)N~WTm(5a`AKPH`0hJoa0v`QPP5z!0 z{auUr1HnI(&R9S2l33WoMuR{%o-LB#Ve|l4Yd7FU0~At3KQgcpIQ=M1Uy_o*mH>~w z6jB3gFOa8a@90x9@$W^Mj0dVO;E%fir(ecOv5(0fK_A`7 zDTtsAf;w<>jC?j|cL3iG%g@k4CIjd#kA8orFA@7Sy`J5XYu$>7PcK2ypB)JQAK=lS z2?#$6Tr0BXNFw&H2=M5EN_={Ni+<#R5OC|gJZ!lr_X2^Q7vO_{FRSrN9bW8&!!G2b z!6M8yEF9UP2I9vUM2}d}pCrg2I;AI5htWRlO=Y)&5Dhv=;CA3W@ib2#jrmsK(T{pS zzb4Ro#Z4%a{A<9Y56^^u3Vb~#sh{|$|0D2AV&(+LvOI@LKmKuBK`IE*Sx-X{`@e#0 z$W(wI{r!j<7!7=HzV%8&_!QvuM`8Ms6c?MJx20{J&&6xo8bt5=cZ>7)SBo(Z>~{Ws zCJE<*8^wzcekq=QXmVTL_FcT~(8FUmKO#Cknx$9b5U&&?AKlhz5sZ$0wt99S2pkDQ zawXDg^oztte4^`)_@W|Ek@m%`3Upq6^dkyYB-ZbLO%}6uWOXkr!E8hFl1}@J*4hu; zgM=}*{{K8ce7Ykb-q{haV`Ho%EZ0P7@&No(Ni5X`o8t95|rzBAXv?$}xZwm81S zC+6&or?t&0p$oEE4w?qFmEysj1q#4{odu5Dz&pmo&pUG*vj;NPJ456=mgBg0B4f8Z zMKy|DXEL_PEfx?JXY#{_xcjjjefW*|^ND!*v0UZ-o5Yuo6~w!j;-?)0zfz&O-Xe8Z Z$Z_M%m~p!3yDLZk=WUG55>Gth`(LB;=70bI