summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/parser/AST.java211
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/parser/CompilerException.java27
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/parser/Lexer.java151
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java262
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/parser/Token.java63
-rw-r--r--chapter01/src/test/java/com/seaofnodes/simple/parser/TestParser.java62
6 files changed, 0 insertions, 776 deletions
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/parser/AST.java b/chapter01/src/main/java/com/seaofnodes/simple/parser/AST.java
deleted file mode 100644
index 4e456416..00000000
--- a/chapter01/src/main/java/com/seaofnodes/simple/parser/AST.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- * <p>
- * Contributor(s):
- * <p>
- * The Initial Developer of the Original Software is Dibyendu Majumdar.
- * <p>
- * This file is part of the Sea Of Nodes Simple Programming language
- * implementation. See https://github.com/SeaOfNodes/Simple
- * <p>
- * The contents of this file are subject to the terms of the
- * Apache License Version 2 (the "APL"). You may not use this
- * file except in compliance with the License. A copy of the
- * APL may be obtained from:
- * http://www.apache.org/licenses/LICENSE-2.0
- */
-package com.seaofnodes.simple.parser;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * A simple AST definition.
- */
-public class AST {
-
- protected AST() {
- }
-
-
- public abstract static class Expr extends AST {
- public final Token op;
-
- protected Expr(Token op) {
- this.op = op;
- }
- }
-
- /**
- * A name in the language
- */
- public static class Identifier extends Expr {
- public final String name;
-
- public Identifier(Token op) {
- super(op);
- name = op.str;
- }
-
- @Override
- public String toString() {
- return name;
- }
- }
-
- /**
- * Reference to a name in the language
- */
- public static class Symbol extends Expr {
- public final String name;
-
- public Symbol(Token op) {
- super(op);
- name = op.str;
- }
-
- @Override
- public String toString() {
- return name;
- }
- }
-
- public static class Binary extends Expr {
- public final Expr expr1;
- public final Expr expr2;
-
- public Binary(Token op, Expr expr1, Expr expr2) {
- super(op);
- this.expr1 = expr1;
- this.expr2 = expr2;
- }
-
- @Override
- public String toString() {
- return "(" + expr1 + op.toString() + expr2 + ")";
- }
- }
-
- public static class Unary extends Expr {
- public final Expr expr;
-
- public Unary(Token op, Expr expr) {
- super(op);
- this.expr = expr;
- }
-
- @Override
- public String toString() {
- return "(" + op.toString() + "(" + expr + "))";
- }
- }
-
- public static class Constant extends Expr {
- public Constant(Token op) {
- super(op);
- }
-
- @Override
- public String toString() {
- return op.toString();
- }
- }
-
- public abstract static class Statement extends AST {
- }
-
- public static class IfElse extends Statement {
- public final Expr expr;
- public final Statement ifStmt;
- public final Statement elseStmt;
-
- public IfElse(Expr expr, Statement ifStmt, Statement elseStmt) {
- this.expr = expr;
- this.ifStmt = ifStmt;
- this.elseStmt = elseStmt;
- }
-
- @Override
- public String toString() {
- if (elseStmt != null)
- return "if(" + expr + ") " + ifStmt + " else " + elseStmt;
- return "if(" + expr + ") " + ifStmt;
- }
- }
-
- public static class While extends Statement {
- public final Expr expr;
- public final Statement stmt;
-
- public While(Expr expr, Statement stmt) {
- this.expr = expr;
- this.stmt = stmt;
- }
-
- @Override
- public String toString() {
- return "while(" + expr.toString() + ") " + stmt;
- }
- }
-
- public static class Break extends Statement {
- @Override
- public String toString() {
- return "break;";
- }
- }
-
- public static class Assign extends Statement {
- public final Identifier identifier;
- public final Expr expr;
-
- public Assign(Identifier identifier, Expr expr) {
- this.identifier = identifier;
- this.expr = expr;
- }
-
- @Override
- public String toString() {
- return identifier.toString() + "=" + expr.toString() + ";";
- }
- }
-
- public static class Type extends AST {
- public final String typeId;
-
- public Type(String typeId) {
- this.typeId = typeId;
- }
-
- @Override
- public String toString() {
- return typeId;
- }
- }
-
- public static class Declare extends Statement {
- public final Type type;
- public final Identifier identifier;
-
- public Declare(Type type, Identifier identifier) {
- this.type = type;
- this.identifier = identifier;
- }
-
- @Override
- public String toString() {
- return type.toString() + " " + identifier.toString() + ";";
- }
- }
-
- public static class Block extends Statement {
- public final List<Statement> stmtList = new ArrayList<>();
-
- @Override
- public String toString() {
- return "{" +
- stmtList.stream().map(Object::toString).collect(Collectors.joining()) + "}";
- }
- }
-}
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/parser/CompilerException.java b/chapter01/src/main/java/com/seaofnodes/simple/parser/CompilerException.java
deleted file mode 100644
index a62fa034..00000000
--- a/chapter01/src/main/java/com/seaofnodes/simple/parser/CompilerException.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- * <p>
- * Contributor(s):
- * <p>
- * The Initial Developer of the Original Software is Dibyendu Majumdar.
- * <p>
- * This file is part of the Sea Of Nodes Simple Programming language
- * implementation. See https://github.com/SeaOfNodes/Simple
- * <p>
- * The contents of this file are subject to the terms of the
- * Apache License Version 2 (the "APL"). You may not use this
- * file except in compliance with the License. A copy of the
- * APL may be obtained from:
- * http://www.apache.org/licenses/LICENSE-2.0
- */
-package com.seaofnodes.simple.parser;
-
-public class CompilerException extends RuntimeException {
- public CompilerException(String message) {
- super(message);
- }
-
- public CompilerException(String message, Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/parser/Lexer.java b/chapter01/src/main/java/com/seaofnodes/simple/parser/Lexer.java
deleted file mode 100644
index fbe8c239..00000000
--- a/chapter01/src/main/java/com/seaofnodes/simple/parser/Lexer.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- * <p>
- * Contributor(s):
- * <p>
- * The Initial Developer of the Original Software is Dibyendu Majumdar.
- * <p>
- * This file is part of the Sea Of Nodes Simple Programming language
- * implementation. See https://github.com/SeaOfNodes/Simple
- * <p>
- * The contents of this file are subject to the terms of the
- * Apache License Version 2 (the "APL"). You may not use this
- * file except in compliance with the License. A copy of the
- * APL may be obtained from:
- * http://www.apache.org/licenses/LICENSE-2.0
- */
-package com.seaofnodes.simple.parser;
-
-import java.text.NumberFormat;
-import java.text.ParseException;
-
-public class Lexer {
-
- private final char[] input;
- /**
- * Tracks current position in input buffer
- */
- private int position = 0;
- private char peek = ' ';
-
- private final NumberFormat numberFormat;
-
- public Lexer(String source) {
- input = source.toCharArray();
- numberFormat = NumberFormat.getInstance();
- numberFormat.setGroupingUsed(false);
- }
-
- private boolean isEOF() {
- return position >= input.length;
- }
-
- private char advance() {
- if (isEOF())
- return 0; // Special value that causes parsing to terminate
- return input[position++];
- }
-
- private boolean readch() {
- peek = advance();
- return true;
- }
-
- private boolean readch(char c) {
- if (readch() && peek != c)
- return false;
- peek = ' ';
- return true;
- }
-
- /**
- * Note that EOF is not whitespace!
- */
- private boolean isWhiteSpace(char c) {
- return c == ' ' ||
- c == '\t' ||
- c == '\n' ||
- c == '\r';
- }
-
- private void skipWhitespace() {
- while (isWhiteSpace(peek))
- readch();
- }
-
- /**
- * Parses number in format nnn[.nnn]
- * where n is a digit
- */
- private Token parseNumber() {
- assert Character.isDigit(peek);
- StringBuilder sb = new StringBuilder();
- sb.append(peek);
- while (readch() && Character.isDigit(peek))
- sb.append(peek);
- if (peek == '.') {
- sb.append(peek);
- while (readch() && Character.isDigit(peek))
- sb.append(peek);
- }
- String str = sb.toString();
- Number number = parseNumber(str);
- return Token.newNum(number, str);
- }
-
- private Number parseNumber(String str) {
- try {
- return numberFormat.parse(str);
- } catch (ParseException e) {
- throw new CompilerException("Failed to parse number " + str, e);
- }
- }
-
- private boolean isIdentifierStart(char ch) {
- return Character.isAlphabetic(ch) || ch == '_';
- }
-
- private boolean isIdentifierLetter(char ch) {
- return Character.isLetterOrDigit(ch) || ch == '_';
- }
-
- private Token parseIdentifier() {
- assert isIdentifierStart(peek);
- StringBuilder sb = new StringBuilder();
- sb.append(peek);
- while (readch() && isIdentifierLetter(peek))
- sb.append(peek);
- return Token.newIdent(sb.toString());
- }
-
- public Token scan() {
- skipWhitespace();
- switch (peek) {
- case 0:
- return Token.EOF;
- case '&':
- return readch('&') ? Token.newPunct("&&") : Token.newPunct("&");
- case '|':
- return readch('|') ? Token.newPunct("||") : Token.newPunct("|");
- case '=':
- return readch('=') ? Token.newPunct("==") : Token.newPunct("=");
- case '<':
- return readch('=') ? Token.newPunct("<=") : Token.newPunct("<");
- case '>':
- return readch('=') ? Token.newPunct(">=") : Token.newPunct(">");
- default: {
- return scanOthers();
- }
- }
- }
-
- private Token scanOthers() {
- if (Character.isDigit(peek))
- return parseNumber();
- else if (isIdentifierLetter(peek))
- return parseIdentifier();
- Token token = Token.newPunct(Character.toString(peek));
- peek = ' ';
- return token;
- }
-}
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java b/chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java
deleted file mode 100644
index d8f80090..00000000
--- a/chapter01/src/main/java/com/seaofnodes/simple/parser/Parser.java
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Contributor(s):
- *
- * The Initial Developer of the Original Software is Dibyendu Majumdar.
- *
- * This file is part of the Sea Of Nodes Simple Programming language
- * implementation. See https://github.com/SeaOfNodes/Simple
- *
- * The contents of this file are subject to the terms of the
- * Apache License Version 2 (the "APL"). You may not use this
- * file except in compliance with the License. A copy of the
- * APL may be obtained from:
- * http://www.apache.org/licenses/LICENSE-2.0
- */
-package com.seaofnodes.simple.parser;
-
-public class Parser {
- private Token currentToken;
-
- /**
- * Parse and return a top level Ast
- * At present this is an Ast.Block
- *
- * @see AST.Block
- */
- public AST parse(Lexer lexer) {
- nextToken(lexer);
- return parseProgram(lexer);
- }
-
- private void nextToken(Lexer lexer) {
- currentToken = lexer.scan();
- }
-
- private void error(Token t, String errorMessage) {
- throw new CompilerException(errorMessage + ": " + t.toString());
- }
-
- private void matchKind(Lexer lexer, Token.Kind kind) {
- if (currentToken.kind == kind) {
- nextToken(lexer);
- } else {
- error(currentToken, "syntax error, expected " + kind);
- }
- }
-
- private void matchPunctuation(Lexer lexer, String value) {
- if (currentToken.kind == Token.Kind.PUNCT && isToken(currentToken, value)) {
- nextToken(lexer);
- } else {
- error(currentToken, "syntax error, expected " + value + " got " + currentToken.str);
- }
- }
-
- private void matchIdentifier(Lexer lexer, String identifier) {
- if (currentToken.kind == Token.Kind.IDENT && isToken(currentToken, identifier)) {
- nextToken(lexer);
- } else {
- error(currentToken, "syntax error, expected " + identifier);
- }
- }
-
- private boolean isToken(Token token, String value) {
- return token.str.equals(value);
- }
-
- private AST.Statement parseProgram(Lexer lexer) {
- var block = new AST.Block();
- parseDeclarations(lexer, block);
- parseStatements(lexer, block);
- return block;
- }
-
- private boolean isTypeName(Token tok) {
- return tok.kind == Token.Kind.IDENT &&
- (isToken(tok, "int") ||
- isToken(tok, "char") ||
- isToken(tok, "float"));
- }
-
- private void parseDeclarations(Lexer lexer, AST.Block block) {
- while (isTypeName(currentToken)) {
- var type = new AST.Type(currentToken.str);
- nextToken(lexer);
- var tok = currentToken;
- matchKind(lexer, Token.Kind.IDENT);
- matchPunctuation(lexer, ";");
- var identifier = new AST.Identifier(tok);
- block.stmtList.add(new AST.Declare(type, identifier));
- }
- }
-
- private boolean isEOF(Token tok) {
- return tok.kind == Token.Kind.EOZ;
- }
-
- private boolean isPunct(Token tok, String str) {
- return tok.kind == Token.Kind.PUNCT && isToken(tok, str);
- }
-
- private void parseStatements(Lexer lexer, AST.Block block) {
- while (!isEOF(currentToken) && !isPunct(currentToken, "}")) {
- block.stmtList.add(parseStatement(lexer));
- }
- }
-
- private AST.Statement parseStatement(Lexer lexer) {
- AST.Expr x;
- AST.Statement s1;
- AST.Statement s2;
-
- switch (currentToken.str) {
- case "if" -> {
- matchIdentifier(lexer, "if");
- matchPunctuation(lexer, "(");
- x = parseBool(lexer);
- matchPunctuation(lexer, ")");
- s1 = parseStatement(lexer);
- if (!isToken(currentToken, "else")) {
- return new AST.IfElse(x, s1, null);
- }
- s2 = parseStatement(lexer);
- return new AST.IfElse(x, s1, s2);
- }
- case "while" -> {
- matchIdentifier(lexer, "while");
- matchPunctuation(lexer, "(");
- x = parseBool(lexer);
- matchPunctuation(lexer, ")");
- s1 = parseStatement(lexer);
- return new AST.While(x, s1);
- }
- case "break" -> {
- matchIdentifier(lexer, "break");
- matchPunctuation(lexer, ";");
- return new AST.Break();
- }
- case "{" -> {
- return parseBlock(lexer);
- }
- default -> {
- return parseAssign(lexer);
- }
- }
- }
-
- private AST.Statement parseBlock(Lexer lexer) {
- matchPunctuation(lexer, "{");
- var block = new AST.Block();
- parseDeclarations(lexer, block);
- parseStatements(lexer, block);
- matchPunctuation(lexer, "}");
- return block;
- }
-
- private AST.Statement parseAssign(Lexer lexer) {
- Token tok = currentToken;
- matchKind(lexer, Token.Kind.IDENT);
- AST.Identifier identifier = new AST.Identifier(tok);
- matchPunctuation(lexer, "=");
- var s = new AST.Assign(identifier, parseBool(lexer));
- matchPunctuation(lexer, ";");
- return s;
- }
-
- private AST.Expr parseBool(Lexer lexer) {
- var x = parseAnd(lexer);
- while (isToken(currentToken, "||")) {
- var tok = currentToken;
- nextToken(lexer);
- x = new AST.Binary(tok, x, parseAnd(lexer));
- }
- return x;
- }
-
- private AST.Expr parseAnd(Lexer lexer) {
- var x = parseRelational(lexer);
- while (isToken(currentToken, "&&")) {
- var tok = currentToken;
- nextToken(lexer);
- x = new AST.Binary(tok, x, parseRelational(lexer));
- }
- return x;
- }
-
- private AST.Expr parseRelational(Lexer lexer) {
- var x = parseAddition(lexer);
- while (isToken(currentToken, "==") ||
- isToken(currentToken, "!=") ||
- isToken(currentToken, "<=") ||
- isToken(currentToken, "<") ||
- isToken(currentToken, ">") ||
- isToken(currentToken, ">=")) {
- var tok = currentToken;
- nextToken(lexer);
- x = new AST.Binary(tok, x, parseAddition(lexer));
- }
- return x;
- }
-
- private AST.Expr parseAddition(Lexer lexer) {
- var x = parseMultiplication(lexer);
- while (isToken(currentToken, "-") ||
- isToken(currentToken, "+")) {
- var tok = currentToken;
- nextToken(lexer);
- x = new AST.Binary(tok, x, parseMultiplication(lexer));
- }
- return x;
- }
-
- private AST.Expr parseMultiplication(Lexer lexer) {
- var x = parseUnary(lexer);
- while (isToken(currentToken, "*") ||
- isToken(currentToken, "/")) {
- var tok = currentToken;
- nextToken(lexer);
- x = new AST.Binary(tok, x, parseUnary(lexer));
- }
- return x;
- }
-
- private AST.Expr parseUnary(Lexer lexer) {
- if (isToken(currentToken, "-") ||
- isToken(currentToken, "!")) {
- var tok = currentToken;
- nextToken(lexer);
- return new AST.Unary(tok, parseUnary(lexer));
- } else {
- return parsePrimary(lexer);
- }
- }
-
- private AST.Expr parsePrimary(Lexer lexer) {
- switch (currentToken.kind) {
- case PUNCT -> {
- /* Nested expression */
- matchPunctuation(lexer, "(");
- nextToken(lexer);
- var x = parseBool(lexer);
- matchPunctuation(lexer, ")");
- return x;
- }
- case NUM -> {
- var x = new AST.Constant(currentToken);
- nextToken(lexer);
- return x;
- }
- case IDENT -> {
- var x = new AST.Symbol(currentToken);
- nextToken(lexer);
- return x;
- }
- default -> {
- error(currentToken, "syntax error, expected nested expr, integer value or variable");
- return null;
- }
- }
- }
-}
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/parser/Token.java b/chapter01/src/main/java/com/seaofnodes/simple/parser/Token.java
deleted file mode 100644
index d45bef5a..00000000
--- a/chapter01/src/main/java/com/seaofnodes/simple/parser/Token.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- * <p>
- * Contributor(s):
- * <p>
- * The Initial Developer of the Original Software is Dibyendu Majumdar.
- * <p>
- * This file is part of the Sea Of Nodes Simple Programming language
- * implementation. See https://github.com/SeaOfNodes/Simple
- * <p>
- * The contents of this file are subject to the terms of the
- * Apache License Version 2 (the "APL"). You may not use this
- * file except in compliance with the License. A copy of the
- * APL may be obtained from:
- * http://www.apache.org/licenses/LICENSE-2.0
- */
-package com.seaofnodes.simple.parser;
-
-public class Token {
-
- enum Kind {
- IDENT,
- NUM,
- PUNCT,
- EOZ // Special kind to signal end of file
- }
-
- public final Kind kind;
- /**
- * String representation of a token - always
- * populated
- */
- public final String str;
- /**
- * The parsed number value, only populated for Kind.NUM
- */
- public final Number num;
-
- public Token(Kind kind, String str, Number num) {
- this.kind = kind;
- this.str = str;
- this.num = num;
- }
-
- public static Token newIdent(String str) {
- return new Token(Kind.IDENT, str.intern(), null);
- }
- public static Token newNum(Number num, String str) {
- return new Token(Kind.NUM, str, num);
- }
- public static Token newPunct(String str) {
- return new Token(Kind.PUNCT, str.intern(), null);
- }
-
- /**
- * Special token that indicates that source has been exhausted
- */
- public static Token EOF = new Token(Kind.EOZ, "", null);
-
- public String toString() {
- return str;
- }
-}
diff --git a/chapter01/src/test/java/com/seaofnodes/simple/parser/TestParser.java b/chapter01/src/test/java/com/seaofnodes/simple/parser/TestParser.java
deleted file mode 100644
index 356420eb..00000000
--- a/chapter01/src/test/java/com/seaofnodes/simple/parser/TestParser.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * DO NOT REMOVE COPYRIGHT NOTICES OR THIS HEADER.
- *
- * Contributor(s):
- *
- * This file is part of the Sea Of Nodes Simple Programming language
- * implementation. See https://github.com/SeaOfNodes/Simple
- *
- * The contents of this file are subject to the terms of the
- * Apache License Version 2 (the "APL"). You may not use this
- * file except in compliance with the License. A copy of the
- * APL may be obtained from:
- * http://www.apache.org/licenses/LICENSE-2.0
- */
-
-package com.seaofnodes.simple.parser;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-public class TestParser {
-
- @Test
- public void testParseSingleDecl()
- {
- final String source = "int i;";
- Lexer lexer = new Lexer(source);
- Parser parser = new Parser();
- var ast = parser.parse(lexer);
- Assert.assertEquals("{int i;}", ast.toString());
- }
-
- @Test
- public void testParseTwoDecls() {
- final String source = "int i;\nint j;";
- Lexer lexer = new Lexer(source);
- Parser parser = new Parser();
- var ast = parser.parse(lexer);
- Assert.assertEquals("{int i;int j;}", ast.toString());
- }
-
- @Test
- public void testIfAndWhile() {
- final String source =
- """
- int i;
- int j;
-
- i = 0;
- j = 100;
- while ( 1 ) {
- i = i + 1;
- j = j - 1;
- if ( i >= j ) break;
- }
- """;
- Lexer lexer = new Lexer(source);
- Parser parser = new Parser();
- var ast = parser.parse(lexer);
- Assert.assertEquals("{int i;int j;i=0;j=100;while(1) {i=(i+1);j=(j-1);if((i>=j)) break;}}", ast.toString());
- }
-}