diff options
| author | Jan Tuomi <jans.tuomi@gmail.com> | 2016-06-15 21:56:03 +0300 |
|---|---|---|
| committer | Jan Tuomi <jans.tuomi@gmail.com> | 2016-07-26 19:46:51 +0300 |
| commit | f441022881945db08846e09a8f3bfee2f8cfa95d (patch) | |
| tree | 3b565c6902e5b5b6c351df89fe1d58b00d79f091 /main | |
| parent | 12eeefcdba1ee392f033f51e6b9826eaae3c4de6 (diff) | |
Add interpreter and AST
Diffstat (limited to 'main')
| -rw-r--r-- | main/Main.java | 32 | ||||
| -rw-r--r-- | main/core/CommandLineArgumentContainer.java | 8 | ||||
| -rw-r--r-- | main/core/parser/Parser.java | 155 | ||||
| -rw-r--r-- | main/core/parser/ast/AdditionNode.java | 4 | ||||
| -rw-r--r-- | main/core/parser/ast/IntegerLiteralNode.java | 3 | ||||
| -rw-r--r-- | main/core/parser/datatype/DataContainer.java | 5 | ||||
| -rw-r--r-- | main/core/parser/datatype/IntegerDataContainer.java | 26 | ||||
| -rw-r--r-- | main/core/runtime/Interpreter.java | 35 | ||||
| -rw-r--r-- | main/core/tokenizer/Tokenizer.java | 15 | ||||
| -rw-r--r-- | main/core/tokenizer/token/Token.java | 4 | ||||
| -rw-r--r-- | main/exception/InterpreterException.java | 12 | ||||
| -rw-r--r-- | main/utils/Counter.java | 23 |
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; } } |
