diff --git a/cobalt/lang/Cobalt.class b/cobalt/lang/Cobalt.class new file mode 100644 index 0000000..37e3b83 Binary files /dev/null and b/cobalt/lang/Cobalt.class differ diff --git a/cobalt/Cobalt.java b/cobalt/lang/Cobalt.java similarity index 96% rename from cobalt/Cobalt.java rename to cobalt/lang/Cobalt.java index dc164e9..bb0b27d 100644 --- a/cobalt/Cobalt.java +++ b/cobalt/lang/Cobalt.java @@ -17,9 +17,9 @@ public class Cobalt { System.out.println("Usage: cobalt [script]"); System.exit(64); } else if (args.length == 1) { - //runFile(args[0]); + runFile(args[0]); } else { - //runPrompt() + runPrompt(); } } diff --git a/cobalt/lang/Expr.java b/cobalt/lang/Expr.java new file mode 100644 index 0000000..d22d6dd --- /dev/null +++ b/cobalt/lang/Expr.java @@ -0,0 +1,41 @@ +package cobalt.lang; + +abstract class Expr { + static class Binary extends Expr { + Binary(Expr left, Token operator, Expr right) { + this.left = left; + this.operator = operator; + this.right = right; + } + + final Expr left; + final Token operator; + final Expr right; + } + + static class Unary extends Expr { + Unary(Token operator, Expr right) { + this.operator = operator; + this.right = right; + } + + final Token operator; + final Expr right; + } + + static class Grouping extends Expr { + Grouping(Expr expression) { + this.expression = expression; + } + + final Expr expression; + } + + static class Literal extends Expr { + Literal(Object value) { + this.value = value; + } + + final Object value; + } +} diff --git a/cobalt/lang/Parser.java b/cobalt/lang/Parser.java new file mode 100644 index 0000000..ceb098f --- /dev/null +++ b/cobalt/lang/Parser.java @@ -0,0 +1,159 @@ +/////////// +// +// Continue at page 89, Section 6.3 +// +/////////// + + +package cobalt.lang; + +import java.util.List; +import static cobalt.lang.TokenType.*; + +class Parser { + private final List 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 0000000..a1730f5 Binary files /dev/null and b/cobalt/lang/Scanner.class differ 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 0000000..d359a89 Binary files /dev/null and b/cobalt/lang/Token.class differ diff --git a/cobalt/Token.java b/cobalt/lang/Token.java similarity index 94% rename from cobalt/Token.java rename to cobalt/lang/Token.java index f5afaac..e911b3a 100644 --- a/cobalt/Token.java +++ b/cobalt/lang/Token.java @@ -1,4 +1,4 @@ -package cobalt; +package cobalt.lang; class Token { final TokenType type; diff --git a/cobalt/lang/TokenType.class b/cobalt/lang/TokenType.class new file mode 100644 index 0000000..ece27ed Binary files /dev/null and b/cobalt/lang/TokenType.class differ 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