aboutsummaryrefslogtreecommitdiffstats
path: root/main
diff options
context:
space:
mode:
authorJan Tuomi <jans.tuomi@gmail.com>2016-06-15 21:56:03 +0300
committerJan Tuomi <jans.tuomi@gmail.com>2016-07-26 19:46:51 +0300
commitf441022881945db08846e09a8f3bfee2f8cfa95d (patch)
tree3b565c6902e5b5b6c351df89fe1d58b00d79f091 /main
parent12eeefcdba1ee392f033f51e6b9826eaae3c4de6 (diff)
Add interpreter and AST
Diffstat (limited to 'main')
-rw-r--r--main/Main.java32
-rw-r--r--main/core/CommandLineArgumentContainer.java8
-rw-r--r--main/core/parser/Parser.java155
-rw-r--r--main/core/parser/ast/AdditionNode.java4
-rw-r--r--main/core/parser/ast/IntegerLiteralNode.java3
-rw-r--r--main/core/parser/datatype/DataContainer.java5
-rw-r--r--main/core/parser/datatype/IntegerDataContainer.java26
-rw-r--r--main/core/runtime/Interpreter.java35
-rw-r--r--main/core/tokenizer/Tokenizer.java15
-rw-r--r--main/core/tokenizer/token/Token.java4
-rw-r--r--main/exception/InterpreterException.java12
-rw-r--r--main/utils/Counter.java23
12 files changed, 268 insertions, 54 deletions
diff --git a/main/Main.java b/main/Main.java
index 488b9fd..b2d29e9 100644
--- a/main/Main.java
+++ b/main/Main.java
@@ -2,10 +2,15 @@ package com.jantuomi.interpreter.main;
import com.jantuomi.interpreter.main.core.CommandLineArgumentContainer;
import com.jantuomi.interpreter.main.core.parser.Parser;
+import com.jantuomi.interpreter.main.core.parser.ast.ASTNode;
+import com.jantuomi.interpreter.main.core.runtime.Interpreter;
import com.jantuomi.interpreter.main.core.tokenizer.Tokenizer;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
+import java.util.List;
+import java.util.Scanner;
+
public class Main {
public static void main(String[] args) throws Exception {
@@ -14,10 +19,31 @@ public class Main {
throw new Exception("Argument files could not be parsed successfully.");
}
- String sourceFileContents = CommandLineArgumentContainer.getInstance().getSourceFileContents();
+ if (CommandLineArgumentContainer.getInstance().isInteractive()) {
+ repl();
+ } else {
+ String sourceFileContents = CommandLineArgumentContainer.getInstance().getSourceFileContents();
+ run(sourceFileContents);
+ }
+ }
+
+ public static void repl() {
+ Scanner scanner = new Scanner(System.in);
+ String input;
+ while (true) {
+ System.out.print(">> ");
+ input = scanner.nextLine();
+
+ run(input);
+ }
+ }
+
+ public static void run(String input) {
Tokenizer tokenizer = Tokenizer.getInstance();
- tokenizer.tokenize(sourceFileContents);
- Parser.getInstance().parse(tokenizer.getTokens());
+ tokenizer.tokenize(input);
+ List<ASTNode> nodes = Parser.getInstance().parse(tokenizer.getTokens());
+ String output = Interpreter.execute(nodes);
+ System.out.println(output);
}
public static boolean parseArguments(String[] args) {
diff --git a/main/core/CommandLineArgumentContainer.java b/main/core/CommandLineArgumentContainer.java
index 9ca0b5e..8984616 100644
--- a/main/core/CommandLineArgumentContainer.java
+++ b/main/core/CommandLineArgumentContainer.java
@@ -18,15 +18,23 @@ public class CommandLineArgumentContainer {
private static final CommandLineArgumentContainer instance = new CommandLineArgumentContainer();
private File srcFile;
+
@Option(name="-f", usage="Execute script in file FILE.")
public void setFile(File file) {
this.srcFile = file;
}
+ @Option(name="-i", usage="Run in interactive mode.")
+ public boolean interactiveModeActive = false;
+
public static CommandLineArgumentContainer getInstance() {
return instance;
}
+ public boolean isInteractive() {
+ return interactiveModeActive;
+ }
+
public String getSourceFileContents() {
if (srcFile == null) {
return null;
diff --git a/main/core/parser/Parser.java b/main/core/parser/Parser.java
index af62f7e..416c3f0 100644
--- a/main/core/parser/Parser.java
+++ b/main/core/parser/Parser.java
@@ -2,9 +2,12 @@ package com.jantuomi.interpreter.main.core.parser;
import com.jantuomi.interpreter.main.core.parser.ast.*;
import com.jantuomi.interpreter.main.core.tokenizer.token.Token;
+import com.jantuomi.interpreter.main.exception.ExceptionManager;
+import com.jantuomi.interpreter.main.exception.InterpreterException;
import com.jantuomi.interpreter.main.utils.Counter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -24,6 +27,18 @@ public class Parser {
}
+ private Token getTokenAt(Counter c) {
+ if (c.getValue() >= tokens.size()) {
+ return new Token(Token.Type.NotAToken);
+ }
+
+ return tokens.get(c.getValue());
+ }
+
+ private boolean isTooDeep(Counter c) {
+ return c.getRecursionDepth() > tokens.size();
+ }
+
public List<ASTNode> parse(List<Token> tokens) {
this.tokens = tokens;
this.statementSequence = new ArrayList<>();
@@ -40,6 +55,11 @@ public class Parser {
statementSequence.add(adn);
continue;
}
+
+ Token failed = tokens.get(c.getValue());
+ ExceptionManager.raise(InterpreterException.Exception.SyntaxError, failed.getLine(),
+ Arrays.asList(failed.toString()));
+ break;
}
return statementSequence;
@@ -47,8 +67,13 @@ public class Parser {
public AssignmentNode expectAssignmentRoutine(Counter c) {
Counter d = c.clone();
+ d.deeper();
- SymbolNode lhs = expectLHS(d);
+ if (isTooDeep(d)) {
+ return null;
+ }
+
+ SymbolNode lhs = expectAssignmentLHS(d);
if (lhs == null) {
return null;
}
@@ -56,27 +81,98 @@ public class Parser {
if (!isAssign) {
return null;
}
- ExpressionNode rhs = expectRHS(d);
+ ExpressionNode rhs = expectAssignmentRHS(d);
if (rhs == null) {
return null;
}
AssignmentNode an = new AssignmentNode(lhs, rhs);
- c.setValue(d.getValue());
+ d.shallower();
+ c.assign(d);
return an;
}
- public SymbolNode expectLHS(Counter c) {
- SymbolNode sn = parseSymbolAndAdvance(c);
+ private AdditionNode expectAdditionRoutine(Counter c) {
+ Counter d = c.clone();
+ d.deeper();
+
+ if (isTooDeep(d)) {
+ return null;
+ }
+
+ ExpressionNode lhs = expectExpression(d);
+ if (lhs == null) {
+ return null;
+ }
+ boolean isAddition = expectAdditionAndAdvance(d);
+ if (!isAddition) {
+ return null;
+ }
+ ExpressionNode rhs = expectExpression(d);
+ if (rhs == null) {
+ return null;
+ }
+
+ AdditionNode an = new AdditionNode(lhs, rhs);
+ d.shallower();
+ c.assign(d);
+ return an;
+ }
+
+// private ExpressionNode expectAdditionLHS(Counter c) {
+// Counter d = c.clone();
+// d.deeper();
+//
+// if (isTooDeep(d)) {
+// return null;
+// }
+//
+// IntegerLiteralNode in = parseIntegerLiteralAndAdvance(d);
+// if (in != null) {
+// d.shallower();
+// c.assign(d);
+// return in;
+// }
+// SymbolNode sn = parseSymbolAndAdvance(d);
+// if (sn != null) {
+// d.shallower();
+// c.assign(d);
+// return sn;
+// }
+//
+// return null;
+// }
+
+
+ public SymbolNode expectAssignmentLHS(Counter c) {
+ Counter d = c.clone();
+ d.deeper();
+
+ if (isTooDeep(d)) {
+ return null;
+ }
+
+ SymbolNode sn = parseSymbolAndAdvance(d);
if (sn != null) {
+ d.shallower();
+ c.assign(d);
return sn;
}
return null;
}
- public ExpressionNode expectRHS(Counter c) {
+ public ExpressionNode expectAssignmentRHS(Counter c) {
+ Counter d = c.clone();
+ d.deeper();
+
+ if (isTooDeep(d)) {
+ return null;
+ }
+
ExpressionNode en = expectExpression(c);
if (en != null) {
+ d.shallower();
+ c.assign(d);
return en;
}
return null;
@@ -84,49 +180,36 @@ public class Parser {
private ExpressionNode expectExpression(Counter c) {
Counter d = c.clone();
+ d.deeper();
+
+ if (isTooDeep(d)) {
+ return null;
+ }
AdditionNode an = expectAdditionRoutine(d);
if (an != null) {
- c.setValue(d.getValue());
+ d.shallower();
+ c.assign(d);
return an;
}
IntegerLiteralNode in = parseIntegerLiteralAndAdvance(d);
if (in != null) {
- c.setValue(d.getValue());
+ d.shallower();
+ c.assign(d);
return in;
}
SymbolNode sn = parseSymbolAndAdvance(d);
if (sn != null) {
- c.setValue(d.getValue());
+ d.shallower();
+ c.assign(d);
return sn;
}
return null;
}
- private AdditionNode expectAdditionRoutine(Counter c) {
- Counter d = c.clone();
-
- ExpressionNode lhs = expectLHS(d);
- if (lhs == null) {
- return null;
- }
- boolean isAddition = expectAdditionAndAdvance(d);
- if (!isAddition) {
- return null;
- }
- ExpressionNode rhs = expectRHS(d);
- if (rhs == null) {
- return null;
- }
-
- AdditionNode an = new AdditionNode(lhs, rhs);
- c.setValue(d.getValue());
- return an;
- }
-
private boolean expectAdditionAndAdvance(Counter c) {
- if (tokens.get(c.getValue()).is(Token.Type.AdditionToken)) {
+ if (getTokenAt(c).is(Token.Type.AdditionToken)) {
c.advance();
return true;
} else {
@@ -135,8 +218,8 @@ public class Parser {
}
private IntegerLiteralNode parseIntegerLiteralAndAdvance(Counter c) {
- if (tokens.get(c.getValue()).is(Token.Type.IntegerLiteralToken)) {
- IntegerLiteralNode in = new IntegerLiteralNode(tokens.get(c.getValue()));
+ if (getTokenAt(c).is(Token.Type.IntegerLiteralToken)) {
+ IntegerLiteralNode in = new IntegerLiteralNode(getTokenAt(c));
c.advance();
return in;
} else {
@@ -145,7 +228,7 @@ public class Parser {
}
public boolean expectAssignmentAndAdvance(Counter c) {
- if (tokens.get(c.getValue()).is(Token.Type.AssignmentToken)) {
+ if (getTokenAt(c).is(Token.Type.AssignmentToken)) {
c.advance();
return true;
} else {
@@ -154,8 +237,8 @@ public class Parser {
}
public SymbolNode parseSymbolAndAdvance(Counter c) {
- if (tokens.get(c.getValue()).is(Token.Type.SymbolToken)) {
- SymbolNode sn = new SymbolNode(tokens.get(c.getValue()));
+ if (getTokenAt(c).is(Token.Type.SymbolToken)) {
+ SymbolNode sn = new SymbolNode(getTokenAt(c));
c.advance();
return sn;
} else {
diff --git a/main/core/parser/ast/AdditionNode.java b/main/core/parser/ast/AdditionNode.java
index 3117e41..f6ebc84 100644
--- a/main/core/parser/ast/AdditionNode.java
+++ b/main/core/parser/ast/AdditionNode.java
@@ -29,6 +29,8 @@ public class AdditionNode extends ExpressionNode {
@Override
public DataContainer evaluate() {
- return null;
+ DataContainer lhs = this.lhs.evaluate();
+ DataContainer rhs = this.rhs.evaluate();
+ return lhs.add(rhs);
}
}
diff --git a/main/core/parser/ast/IntegerLiteralNode.java b/main/core/parser/ast/IntegerLiteralNode.java
index b68adf0..d2fceb1 100644
--- a/main/core/parser/ast/IntegerLiteralNode.java
+++ b/main/core/parser/ast/IntegerLiteralNode.java
@@ -1,6 +1,7 @@
package com.jantuomi.interpreter.main.core.parser.ast;
import com.jantuomi.interpreter.main.core.parser.datatype.DataContainer;
+import com.jantuomi.interpreter.main.core.parser.datatype.IntegerDataContainer;
import com.jantuomi.interpreter.main.core.tokenizer.token.Token;
import java.util.Arrays;
@@ -25,6 +26,6 @@ public class IntegerLiteralNode extends ExpressionNode {
@Override
public DataContainer evaluate() {
- return null;
+ return new IntegerDataContainer(data);
}
}
diff --git a/main/core/parser/datatype/DataContainer.java b/main/core/parser/datatype/DataContainer.java
index 48e72a3..45199e2 100644
--- a/main/core/parser/datatype/DataContainer.java
+++ b/main/core/parser/datatype/DataContainer.java
@@ -14,4 +14,9 @@ abstract public class DataContainer<T> {
}
private T data;
+
+ @Override
+ abstract public String toString();
+
+ public abstract DataContainer add(DataContainer rhs);
}
diff --git a/main/core/parser/datatype/IntegerDataContainer.java b/main/core/parser/datatype/IntegerDataContainer.java
index c5b9fb0..8566a1c 100644
--- a/main/core/parser/datatype/IntegerDataContainer.java
+++ b/main/core/parser/datatype/IntegerDataContainer.java
@@ -1,5 +1,11 @@
package com.jantuomi.interpreter.main.core.parser.datatype;
+
+import com.jantuomi.interpreter.main.exception.ExceptionManager;
+import com.jantuomi.interpreter.main.exception.InterpreterException;
+
+import java.util.Arrays;
+
/**
* Created by jan on 11.6.2016.
*/
@@ -7,4 +13,24 @@ public class IntegerDataContainer extends DataContainer<Integer> {
public IntegerDataContainer(Integer data) {
this.setData(data);
}
+
+ @Override
+ public String toString() {
+ return getData().toString();
+ }
+
+ @Override
+ public DataContainer add(DataContainer rhs) {
+ if (rhs instanceof IntegerDataContainer) {
+ return new IntegerDataContainer(
+ getData() + ((IntegerDataContainer) rhs).getData()
+ );
+ }
+ else {
+ ExceptionManager.raise(InterpreterException.Exception.TypeError, -1,
+ Arrays.asList(this.getClass().toString(), rhs.getClass().toString()));
+
+ return null;
+ }
+ }
}
diff --git a/main/core/runtime/Interpreter.java b/main/core/runtime/Interpreter.java
new file mode 100644
index 0000000..00294e2
--- /dev/null
+++ b/main/core/runtime/Interpreter.java
@@ -0,0 +1,35 @@
+package com.jantuomi.interpreter.main.core.runtime;
+
+import com.jantuomi.interpreter.main.core.parser.ast.ASTNode;
+import com.jantuomi.interpreter.main.core.parser.datatype.DataContainer;
+
+import java.util.List;
+
+/**
+ * Created by jan on 14.6.2016.
+ */
+public class Interpreter {
+ private static final Interpreter instance = new Interpreter();
+
+ public static Interpreter getInstance() {
+ return instance;
+ }
+
+ private Interpreter() {
+
+ }
+ public static String execute(List<ASTNode> sequence) {
+ StringBuilder output = new StringBuilder();
+ for (ASTNode node : sequence) {
+ DataContainer data = node.evaluate();
+
+ if (data != null) {
+ String out = data.toString();
+ output.append(out);
+ } else {
+ break;
+ }
+ }
+ return output.toString();
+ }
+}
diff --git a/main/core/tokenizer/Tokenizer.java b/main/core/tokenizer/Tokenizer.java
index 4a12014..c04db22 100644
--- a/main/core/tokenizer/Tokenizer.java
+++ b/main/core/tokenizer/Tokenizer.java
@@ -20,7 +20,8 @@ public class Tokenizer {
private List<Token> tokens = new ArrayList<>();
private static SortedMap<Token.Type, String> tokenRegexes = new TreeMap<>();
- private static final List<Token.Type>illegalTokenTypes = new ArrayList<>();
+ private static final List<Token.Type> discardedTokenTypes = new ArrayList<>();
+ private static final List<Token.Type> erroneousTokenTypes = new ArrayList<>();
private static final Tokenizer instance = new Tokenizer();
@@ -51,7 +52,9 @@ public class Tokenizer {
tokenRegexes.put(Token.Type.IntegerLiteralToken, "^(\\d+)");
tokenRegexes.put(Token.Type.SymbolToken, "^([a-zA-Z]+\\w*)");
- illegalTokenTypes.add(Token.Type.NotAToken);
+ discardedTokenTypes.add(Token.Type.WhitespaceToken);
+
+ erroneousTokenTypes.add(Token.Type.NotAToken);
}
public List<Token> tokenize(String string) {
@@ -71,10 +74,12 @@ public class Tokenizer {
line++;
}
- if (!illegalTokenTypes.contains(token.getTokenType())) {
+ if (!discardedTokenTypes.contains(token.getTokenType())) {
tokens.add(token);
- } else {
- ExceptionManager.raise(InterpreterException.Exception.IllegalTokenException, line, Arrays.asList(token.getText()));
+ }
+
+ if (erroneousTokenTypes.contains(token.getTokenType())) {
+ ExceptionManager.raise(InterpreterException.Exception.IllegalTokenError, line, Arrays.asList(token.getText()));
}
String tokenRawText = token.getRawText();
diff --git a/main/core/tokenizer/token/Token.java b/main/core/tokenizer/token/Token.java
index 6bb5f3b..99ae913 100644
--- a/main/core/tokenizer/token/Token.java
+++ b/main/core/tokenizer/token/Token.java
@@ -148,6 +148,10 @@ public class Token {
@Override
public String toString() {
+ return String.format(String.format("%s %s", getTokenType(), getText()));
+ }
+
+ public String toFormattedString() {
String textRepr = text;
return String.format("%-40s %s", getTokenType(), textRepr);
}
diff --git a/main/exception/InterpreterException.java b/main/exception/InterpreterException.java
index 8129012..6ba5a7b 100644
--- a/main/exception/InterpreterException.java
+++ b/main/exception/InterpreterException.java
@@ -10,15 +10,19 @@ import java.util.Map;
public class InterpreterException {
public enum Exception {
- IllegalTokenException,
- UnknownOperatorException
+ IllegalTokenError,
+ UnknownOperatorError,
+ SyntaxError,
+ TypeError
}
public static Map<Exception, String> errorTexts = new HashMap<>();
static {
- errorTexts.put(Exception.IllegalTokenException, "Illegal token %s found.");
- errorTexts.put(Exception.UnknownOperatorException, "Unknown operator %s found.");
+ errorTexts.put(Exception.IllegalTokenError, "Illegal token %s found.");
+ errorTexts.put(Exception.UnknownOperatorError, "Unexpected operator %s found.");
+ errorTexts.put(Exception.SyntaxError, "Unexpected %s.");
+ errorTexts.put(Exception.TypeError, "Incompatible types %s and %s.");
}
private Exception exception;
diff --git a/main/utils/Counter.java b/main/utils/Counter.java
index a0bf0fd..9783adf 100644
--- a/main/utils/Counter.java
+++ b/main/utils/Counter.java
@@ -6,22 +6,37 @@ package com.jantuomi.interpreter.main.utils;
public class Counter {
private int value = 0;
+ public int getRecursionDepth() {
+ return recursionDepth;
+ }
+
+ private int recursionDepth = 0;
+
public int advance() {
- value = value + 1;
- return value;
+ return ++value;
+ }
+
+ public int deeper() {
+ return ++recursionDepth;
+ }
+
+ public int shallower() {
+ return --recursionDepth;
}
public int getValue() {
return value;
}
- public void setValue(int value) {
- this.value = value;
+ public void assign(Counter other) {
+ value = other.value;
+ recursionDepth = other.recursionDepth;
}
public Counter clone() {
Counter c = new Counter();
c.value = value;
+ c.recursionDepth = recursionDepth;
return c;
}
}