From 535c557cb44eca481ad82580109dfbcb7114db00 Mon Sep 17 00:00:00 2001 From: Garrett Dickinson Date: Sun, 19 Jun 2022 16:26:30 -0500 Subject: [PATCH] Add framework for expressions and generating AST, progress on Parser class --- cobalt/lang/Cobalt.class | Bin 0 -> 2711 bytes cobalt/{ => lang}/Cobalt.java | 4 +- cobalt/lang/Expr.java | 41 ++++++++ cobalt/lang/Parser.java | 159 +++++++++++++++++++++++++++++++ cobalt/lang/Scanner.class | Bin 0 -> 4982 bytes cobalt/{ => lang}/Scanner.java | 18 +++- cobalt/lang/Token.class | Bin 0 -> 1042 bytes cobalt/{ => lang}/Token.java | 2 +- cobalt/lang/TokenType.class | Bin 0 -> 2722 bytes cobalt/{ => lang}/TokenType.java | 2 +- cobalt/tool/GenerateAst.java | 67 +++++++++++++ 11 files changed, 284 insertions(+), 9 deletions(-) create mode 100644 cobalt/lang/Cobalt.class rename cobalt/{ => lang}/Cobalt.java (96%) create mode 100644 cobalt/lang/Expr.java create mode 100644 cobalt/lang/Parser.java create mode 100644 cobalt/lang/Scanner.class rename cobalt/{ => lang}/Scanner.java (94%) create mode 100644 cobalt/lang/Token.class rename cobalt/{ => lang}/Token.java (94%) create mode 100644 cobalt/lang/TokenType.class rename cobalt/{ => lang}/TokenType.java (95%) create mode 100644 cobalt/tool/GenerateAst.java diff --git a/cobalt/lang/Cobalt.class b/cobalt/lang/Cobalt.class new file mode 100644 index 0000000000000000000000000000000000000000..37e3b83bc371bd30b5a6706d8df714c3933a54ef GIT binary patch literal 2711 zcma)8Yf}?f7=AVhY*^NS;f|=-f`*HxUR#N^LaLP-kP2Rq+IC46SP8r7W@DlEtG(ZE z+S~L)XZ&0{gD6v{-#XLZ(O=dvrr{iv;E(j2k`RbnDP2Nc!Z7vG+`_bOPiWI=&IA@T!<5h* znM$13E@;uTX3j>(9osNxW5RLWxT7WKN3=}dUOBv&)H9A@nOPabl8Ls5ZUD$fYvgsA6WKr2Ccp#b1?_C+Ua&}Vp z@YTkxX)W#S5ZhF-4G|fp3JV$T{$y60)dxD0u0`imHfb9f=bVgZRoK|6A`3^w17frr z5efBoQzav(;sTzNv8ZAR&&zm0#fx}J#2gtHoPvucd%2%8zSVJcuPXaLmeGYYNjdTY!gct@wSS0 z@UDvY@V<%<@Sy}XZ#iMj>)g7Hk0e0FehjGiSnyCkWLZwuv9-*I?#x-KY+W@z3F1=~ zpW$CqAZ0PuaIDP=%`vjd0;dMk>7gY@e75FOteFV_u^vHBtILL1NX}_? zR(GOt?@i`X`iz!KJ8Kgik)9%myUYn?b7X4h-`52VK5^!YjFyOz?gfzLDs{U-Lmoeu zJW0BAS~@C5|k`O>dKE~gbOxnnqcyvu_lG)vh{*QXd};Q*-?Fw zC1Z1AuM3#&M|n<9k+d%BOZi#BfuGGs!58?FNT%2^^iJtqr`JITIwc7$3);LMx6GvG zoHm>}jNkp2Bi$h_)46mZzOcN<#yK$^8ycKzUg`ik7!Arh9w!_u?HqB zYd*)*sSbQn}`1c*pjrW~QjVF3&M`FN~6zGRKLfj9$UK^biyXyf;K# zOQ*+Ky_k&eC3N4jze?C7Z2E6IY~gL!#e0s*5FM1GAjj<9eD?5bBj4;|sOr52>1Wrl zm(OZ61yxtj#MvLA_WF0Np!y1`S0IaU zK8SFeAoE$zuPhv^4wV3+PdJ2%@+C?Xk2xQ%WZyUOzM za?0ns2tWUTJ*%jjyo&m3SbuC4;YqU8kPtg>Tt-vhGMY#Fe;3;g^%H^r7lf|-{bw=z zR`LgypcePJo9$o$wz%X+$Z8Mw*UJmB&qdS-|6gd7Q70pO8x1mox5z*#8@vU{izwP6 zu~qTCcNHxHQfq=x+D7`Xqg}%2_UqUn;WFAd=#cOmHVy;`xh=4aO=a-fSq~6I4QUAR zlI`Jdrk(t?w1>ag1WD`RyMrbfEi(4X*msNE1-bN49z_>Xh?)f3!<$)#&Z2r0cYc6H zk8uyYAM+l&Sq;G|wX>zYum1*V0TmP=Z$;;jYjTi?2C0-dQ^v~FN`F)ds*5h3TG!F> c02h6Dn7aovOZXb!;u6}B; tokens; + private int current = 0; + + Parser(List tokens) { + this.tokens = tokens; + } + + private Expr expression() { + return equality(); + } + + + // Parser definition for handling equalities + // + // equality -> comparison ( ( "!=" | "==" ) comparison )* ; + // + private Expr equality() { + Expr expr = comparison(); + while (match(BANG_EQUAL, EQUAL_EQUAL)) { + Token operator = previous(); + Expr right = comparison(); + expr = new Expr.Binary(expr, operator, right); + } + + return expr; + } + + // Parser definition for handling comparisons + // + // comparison -> term ( ( ">" | ">=" | "<" | "<=" ) term )* ; + // + private Expr comparison() { + Expr expr = term(); + + while (match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)) { + Token operator = previous(); + Expr right = term(); + expr = new Expr.Binary(expr, operator, right); + } + + return expr; + } + + + private Expr term() { + Expr expr = factor(); + + while (match(MINUS, PLUS)) { + Token operator = previous(); + Expr right = factor(); + expr = new Expr.Binary(expr, operator, right); + } + + return expr; + } + + + private Expr factor() { + Expr expr = unary(); + + while (match(SLASH, STAR)) { + Token operator = previous(); + Expr right = unary(); + expr = new Expr.Binary(expr, operator, right); + } + + return expr; + } + + + private Expr unary() { + if (match(BANG, MINUS)) { + Token operator = previous(); + Expr right = unary(); + return new Expr.Unary(operator, right); + } + + return primary(); + } + + + private Expr primary() { + if (match(FALSE)) return new Expr.Literal(false); + if (match(TRUE)) return new Expr.Literal(true); + if (match(NIL)) return new Expr.Literal(null); + + if (match(NUMBER, STRING)) { + return new Expr.Literal(previous().literal); + } + + if (match(LEFT_PAREN)) { + Expr expr = expression(); + consume(RIGHT_PAREN, "Expect ')' after expression."); + return new Expr.Grouping(expr); + } + } + + + // Checks to see if the current token has any of the given types. If so, + // it consumes the token and returns true. Otherwise, it returns false and leaves + // the current token alone. + private boolean match(TokenType... types) { + for (TokenType type : types) { + if (check(type)) { + advance(); + return true; + } + } + return false; + } + + + // Returns true if the current token is of the given type, false if otherwise + private boolean check(TokenType type) { + if (isAtEnd()) return false; + return peek().type == type; + } + + + // Consume the current token and return it + private Token advance() { + if (!isAtEnd()) current++; + return previous(); + } + + + // Check to see if we've run out of tokens to parse + private boolean isAtEnd() { + return peek().type == EOF; + } + + + // Return the current token we have yet to consume + private Token peek() { + return tokens.get(current); + } + + + // Return the previous token we just consumed + private Token previous() { + return tokens.get(current - 1); + } + +} + + diff --git a/cobalt/lang/Scanner.class b/cobalt/lang/Scanner.class new file mode 100644 index 0000000000000000000000000000000000000000..a1730f5c04d7e2e4f0a1c7b16d523b0e4ed518a7 GIT binary patch literal 4982 zcmbtYdwkQ?760Bme@%ZaA<%+Uu>wMy@=zzDP?1nl8rPRi3Ih>H+t3C=T9Xt(#y02N zbWWXnJ%qNZ3Wahb@MsrV-MzZn~ym+A9I^-zS;NO{MrP;{j+{P_dM=B_nh-P z=iGB|`p8H7?*TAJ9q+*kT;|4CJ+R@jBAkoM1$<4w6#}jlaFvJP>jJKJ;~PbogKrx6 zmIuZ7b}_z#?|QHq-;?F7vUZJ7-xul!06kJ8wK1XV2^;CMfk@;-6GUp0k;as3b;+c?FR1fU=TyX+-YE+IJjT79FUp21l%nj zc26;Wf_vTgsjS^6Qun*@fCqDNnSr0lSq8l%{@n;2B zy0<^o8B_2zu8nSp&Pqg+-Lt}(R6N-|kE@y9b+Kex!JAv{&%_h68sq5<*NVdN?qoF6 zpW^1qk1f|WcJ_8e6PZ~N**|YS#hvL)G?h_MAq975e<~GAl5-^DN$xk+#Wr2go9d#a zQ3a}+qJ7*s`|z3C99!t`eNFmt2UmDXXY!ERUNsddI^o!^>)$ts7CtG+`qmf zmWo6>5}fg*JEKYMIvw&=RTns5eSAqc6YX3_|EwL8ju1LZ2;A{>AQMb>DcGy3motJR zX7mg_qQs(jcU%HfRVN~@r1nelq;o>w;$4hfd`&!-a^qzR_QhiB^!_@=QJCh~Mk)|v ziN2nwLa9aZ6Jx1(CsVOLn(6GJNVIE1G%3k9qFr73dv&l1bEl&z!J1ZeWRwfS+S0K$ z)|r_nl16POv#KNS3G3>rk4sd|Fr%7`rDjT@nOKW{g)zG5bXf&bspuvNuZdMyZK4xh zCSn5C2sjTbO{9=Ek&%sNp_3;5f>#tEO8@G{-%PxU*G#;QH%xpHTTJ{Nub8+PUo!Eg zQ2!9{PXYfD@RoqL1^ipUe+0ZE;38aL;$5NM6Y#!(4+MND;3ENt2xytqXK6?&p*Er4 z#0J^BQK-)gb)h(2)}D-Q>|>l_U6q|Z(NvUFYNlJ+Ol4ON6Q9Etg|Y2P&aRL1Y*8$i z_L-(~DwnC;qW)rBV&V&;v>QEcWtgf+d3Zz%BO+-tRk1i{suG3K`8YR3(>*L)mP|C+ zWvWt<8Kt}|i_Szeoi^2IiKTN*G?AtXpD^|{y(v?T5z?`yKiO%ju|nJ8YfM$91(NZE zsmk@Fh>jD=*~c!DG1YisT&WmaMbcEq=%sXjUyO1SgmGkg;xt>4qf`B~daTg)4N;05 zr{yo`VF{UPqA;b_hg#*tFVA_+wip8;%O$_7zj5|!U}4g?)sdMrBb~qh4QLU zLDZ}`O3;eZ{T;c|)!o-Pc3Zg!x1hpXHD_zLyLmV zkx+do$jG?l;03tdQ5C9Y%d`iY7ZLXLbL#Y?XgpG3p|2x09Kpsg)8woVXl$=~!eg_YIP^L>#{m>C%fj@PD01#0l10H)l?;^GEJ3}D%X@rYMiEQq{a({cSs$hsW(VX(9}z$ zDm3*Bsbe+ugx~Kya}dXEMNt+L2jI%$c)c)GxKODVPS6Y26)sHD3zPN2C4~!9^umdH zA>sGV(k3dHfT>>3HZ0E-Q&rR;n&?!-(f@m#rG$wCGeu;>EVi#qJbB0PW~9!EV6VliGo171Uj zPX-=qG${|7c`LRmKh9Az5LR`FsCq0>E3j0p!ZOv5tZN+NVN{8BsxY~_% zYA+J%HuS0?^r`(wtNZv^co-Yhlh~vVVzYXlZwM*#^HKQ$_Nt{bV_AEL^P} zaIrD?#`|cKPcqAc-MU!-E48^v+i)TY_N9rM_iRT=zI?RxfkOvHVk;M4oTljy!kxwGP5$XT z9C@E6@EQSz7_)u!X+In40i2Gz*h}u#K9ta!??X&+V>9cCuJCPZxiXiWB(N+O6Q_NT z6$TnVBi~Tv$3AdByW;~|qaCV)}>b@b;4gbmW3L2D+%ftofFZuTr0EUn7g}kcirpU$R9-3#>vcvd?N38 zWmRuZIG4w_STAqu4VPFOLcE5>t7{~3)eb4&Hw-a literal 0 HcmV?d00001 diff --git a/cobalt/Scanner.java b/cobalt/lang/Scanner.java similarity index 94% rename from cobalt/Scanner.java rename to cobalt/lang/Scanner.java index add62c1..3213ae8 100644 --- a/cobalt/Scanner.java +++ b/cobalt/lang/Scanner.java @@ -1,11 +1,11 @@ -package cobalt; +package cobalt.lang; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import static cobalt.TokenType.*; +import static cobalt.lang.TokenType.*; class Scanner { private final String source; @@ -46,7 +46,7 @@ class Scanner { List scanTokens() { while (!isAtEnd()) { start = current; - scanTokens(); + scanToken(); } tokens.add(new Token(EOF, "", null, line)); @@ -107,6 +107,10 @@ class Scanner { line++; break; + case '"': + string(); + break; + default: if (isDigit(c)) { // Check to see if our incoming value is part of a number @@ -129,7 +133,7 @@ class Scanner { private void number() { - while (!isDigit(peek())) advance(); + while (isDigit(peek())) advance(); // Look for a decimal place. if (peek() == '.' && isDigit(peekNext())) { @@ -177,7 +181,11 @@ class Scanner { // add it to the Token list if it is valid private void identifier() { while (isAlphaNumeric(peek())) advance(); - addToken(IDENTIFIER); + + String text = source.substring(start, current); + TokenType type = keywords.get(text); + if (type == null) type = IDENTIFIER; + addToken(type); } diff --git a/cobalt/lang/Token.class b/cobalt/lang/Token.class new file mode 100644 index 0000000000000000000000000000000000000000..d359a8933b62f7c6fd90db0195d45bfaa5acc871 GIT binary patch literal 1042 zcmbVL%Wl&^6g`tTvE#br0j0D+36MhKKEh*xL=X!iMFgd=NK_WACdsIE<9H%_Qqe!* zGeAm&)CC{FMH8vaa|n#*y`epYpoPy`y?p`Hpu)R^4@!@VOfX zUz$JkPsBixS#uqaLRp(|UOAqlUNgL`F8?MG8$%~FFM}eX6MoJYZ zpHB{iI+nvApT)L;M;0EV%3vkiMc#imfFWJ29h#`YX2`HIeGKbkJ`zpo4Y>N~sAG}> z#XS`;y!+3_Tv4r#oiN8bL*+6l$2+CD#w(BcCn{WBd;X+Rw*N7D(F)%HB$H(eeC zg3|g@O>{HHShV9U_lB+rDvQ8I!fknoe*{LWwe0>Wp^Ar|tSyFUa@XzB)-`lIW7uA* z@2cDww*KvhGCc}|-h_rixC$f{!fl8+Ekwj=Cy1x$6j1`hb&^_i&k*0Vzk}I3bENFu zInwst9GbnR&5`*=R$9u?2U(&y`iDt$g-|WRSj7fO3~RU~7=p67Tf5kOuif{3 zv#nlRwOXw|_vzo%r{^3HX(f4{@6CJX&di;2?&Pn3Zr%Z~0e5rZ!fRfG|z3e;_z}XvW-uXitydg0mwUx8UvxMH03JS8te+YX^HmfoM2Nw{hEHzuirT z?6^NhX!l3DXeEsO!R-lun5N7yPk3+C(-jN)<3Tznf9BV{Bd&1Jrn}JUZ2R7rH`Eb~ z#6z7SJ|V}B$3lFYoJgXlokyJhNC(|0Kx^S;2E#Tzk-O6`baf^o0h-eprJ0UUC;g0s zXk89=cQ0Lv9x*2tj3;99K#5+yq$}PX;%miY3A&cEkDt!HqdOF43r6WGp87t2I1!{t zCA;SV%M*A*6l?sU(oQG+V^l`4f1y{1z?JQsdMGLDzR$q6{`_jr1QjT#TILySe@7s zoiDajtX>z0HHbCpLa}9HORh9*u%O^Y^~Tj zy+CZe*dw}JY=hXNdZE}xvBz|USgTl@t`yrOwpmw+Z4uk5sdv-@zgWAj5etZQ=tW{d zu})np)+N@h7mJ0&9@lkZ+r+|piCB+VL@yPKiuLMxvF&0p-5_R*#dV`tLNB{byTfVd z4?XEJ$g`7fgM2%gV^C-(JqAT~QW=!kNlim#1R5(R&|n22*Px0(8^4B-XHZM<8PpN- z4VDrL3>pZ92FnQApqWr)u!2x*u#!+>&_XCRSVJf?SW8%7u%1wEuz|49U?ZWzpp{T* zu!&G*u!T@<;3w1=1PF@^f`nRwF2Z7i5TVXs8)1n-4`Hc6lu&Q5ozP%l6B-Q?_ElK- v->yb_X#qFZ(Mx=he*X=xK7$@ literal 0 HcmV?d00001 diff --git a/cobalt/TokenType.java b/cobalt/lang/TokenType.java similarity index 95% rename from cobalt/TokenType.java rename to cobalt/lang/TokenType.java index b0c5ca4..2cd6a6f 100644 --- a/cobalt/TokenType.java +++ b/cobalt/lang/TokenType.java @@ -1,4 +1,4 @@ -package cobalt; +package cobalt.lang; enum TokenType { // Single character tokens. diff --git a/cobalt/tool/GenerateAst.java b/cobalt/tool/GenerateAst.java new file mode 100644 index 0000000..031add0 --- /dev/null +++ b/cobalt/tool/GenerateAst.java @@ -0,0 +1,67 @@ +package cobalt.tool; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.List; + +public class GenerateAst { + public static void main(String[] args) throws IOException { + if (args.length != 1) { + System.err.println("Usage: generate_ast "); + System.exit(64); + } + + String outputDir = args[0]; + + defineAst(outputDir, "Expr", Arrays.asList( + "Binary : Expr left, Token operator, Expr right", + "Grouping : Expr expression", + "Literal : Object value", + "Unary : Token operator, Expr right" + )); + } + + private static void defineAst(String outputDir, String baseName, List types) throws IOException { + String path = outputDir + "/" + baseName + ".java"; + PrintWriter writer = new PrintWriter(path, "UTF-8"); + + writer.println("package cobalt.lang"); + writer.println(); + writer.println("import java.util.List;"); + writer.println(); + writer.println("abstract class " + baseName + " {"); + + for (String type : types){ + String className = type.split(":")[0].trim(); + String fields = type.split(":")[1].trim(); + defineType(writer, baseName, className, fields); + } + + writer.println("}"); + writer.close(); + } + + private static void defineType(PrintWriter writer, String baseName, String className, String fieldList) { + writer.println(" static class " + className + " extends " + baseName + " {"); + + // Constructor + writer.println(" " + className + "(" + fieldList + ") {"); + + String[] fields = fieldList.split(", "); + for (String field : fields) { + String name = field.split(" ")[1]; + writer.println(" this." + name + " = " + name + ";"); + } + + writer.println(" }"); + + // Fields + writer.println(); + for (String field : fields) { + writer.println(" final " + field + ";"); + } + + writer.println(" }"); + } +} \ No newline at end of file