diff options
Diffstat (limited to 'code/Interpreter2')
-rw-r--r-- | code/Interpreter2/.idea/misc.xml | 6 | ||||
-rw-r--r-- | code/Interpreter2/.idea/modules.xml | 8 | ||||
-rw-r--r-- | code/Interpreter2/.idea/uiDesigner.xml | 124 | ||||
-rw-r--r-- | code/Interpreter2/.idea/vcs.xml | 6 | ||||
-rw-r--r-- | code/Interpreter2/.idea/workspace.xml | 52 | ||||
-rw-r--r-- | code/Interpreter2/Interpreter2.iml | 11 | ||||
-rw-r--r-- | code/Interpreter2/src/Interpreter/Environment.java | 30 | ||||
-rw-r--r-- | code/Interpreter2/src/Interpreter/Expression.java | 84 | ||||
-rw-r--r-- | code/Interpreter2/src/Interpreter/Interpreter.java | 131 | ||||
-rw-r--r-- | code/Interpreter2/src/Interpreter/Language.java | 63 | ||||
-rw-r--r-- | code/Interpreter2/src/Interpreter/Parser.java | 179 | ||||
-rw-r--r-- | code/Interpreter2/src/Interpreter/Statement.java | 49 | ||||
-rw-r--r-- | code/Interpreter2/src/Interpreter/Token.java | 23 | ||||
-rw-r--r-- | code/Interpreter2/src/Interpreter/TokenScanner.java | 179 | ||||
-rw-r--r-- | code/Interpreter2/src/Interpreter/TokenType.java | 17 |
15 files changed, 962 insertions, 0 deletions
diff --git a/code/Interpreter2/.idea/misc.xml b/code/Interpreter2/.idea/misc.xml new file mode 100644 index 0000000..b1001d2 --- /dev/null +++ b/code/Interpreter2/.idea/misc.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_15" default="true" project-jdk-name="openjdk-15" project-jdk-type="JavaSDK"> + <output url="file://$PROJECT_DIR$/out" /> + </component> +</project>
\ No newline at end of file diff --git a/code/Interpreter2/.idea/modules.xml b/code/Interpreter2/.idea/modules.xml new file mode 100644 index 0000000..944dd7c --- /dev/null +++ b/code/Interpreter2/.idea/modules.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/Interpreter2.iml" filepath="$PROJECT_DIR$/Interpreter2.iml" /> + </modules> + </component> +</project>
\ No newline at end of file diff --git a/code/Interpreter2/.idea/uiDesigner.xml b/code/Interpreter2/.idea/uiDesigner.xml new file mode 100644 index 0000000..e96534f --- /dev/null +++ b/code/Interpreter2/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="Palette2"> + <group name="Swing"> + <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> + </item> + <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true"> + <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> + <initial-values> + <property name="text" value="Button" /> + </initial-values> + </item> + <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="RadioButton" /> + </initial-values> + </item> + <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="CheckBox" /> + </initial-values> + </item> + <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="Label" /> + </initial-values> + </item> + <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> + <preferred-size width="-1" height="20" /> + </default-constraints> + </item> + <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> + </item> + </group> + </component> +</project>
\ No newline at end of file diff --git a/code/Interpreter2/.idea/vcs.xml b/code/Interpreter2/.idea/vcs.xml new file mode 100644 index 0000000..b2bdec2 --- /dev/null +++ b/code/Interpreter2/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="$PROJECT_DIR$/../.." vcs="Git" /> + </component> +</project>
\ No newline at end of file diff --git a/code/Interpreter2/.idea/workspace.xml b/code/Interpreter2/.idea/workspace.xml new file mode 100644 index 0000000..4198910 --- /dev/null +++ b/code/Interpreter2/.idea/workspace.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ChangeListManager"> + <list default="true" id="d53ed2ac-d387-4e72-b30b-e936be78d097" name="Default Changelist" comment="" /> + <option name="SHOW_DIALOG" value="false" /> + <option name="HIGHLIGHT_CONFLICTS" value="true" /> + <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> + <option name="LAST_RESOLUTION" value="IGNORE" /> + </component> + <component name="Git.Settings"> + <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/../.." /> + </component> + <component name="ProjectId" id="20PtUsPCm3Avx9BljmzQRhs1xbs" /> + <component name="ProjectLevelVcsManager" settingsEditedManually="true" /> + <component name="ProjectViewState"> + <option name="hideEmptyMiddlePackages" value="true" /> + <option name="showLibraryContents" value="true" /> + </component> + <component name="PropertiesComponent"> + <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" /> + </component> + <component name="RunManager"> + <configuration name="Language" type="Application" factoryName="Application"> + <option name="MAIN_CLASS_NAME" value="Interpreter.Language" /> + <module name="Interpreter2" /> + <method v="2"> + <option name="Make" enabled="true" /> + </method> + </configuration> + </component> + <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" /> + <component name="TaskManager"> + <task active="true" id="Default" summary="Default task"> + <changelist id="d53ed2ac-d387-4e72-b30b-e936be78d097" name="Default Changelist" comment="" /> + <created>1635958069096</created> + <option name="number" value="Default" /> + <option name="presentableId" value="Default" /> + <updated>1635958069096</updated> + </task> + <servers /> + </component> + <component name="WindowStateProjectService"> + <state x="414" y="180" width="1092" height="714" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1635958280293"> + <screen x="0" y="0" width="1920" height="1050" /> + </state> + <state x="414" y="180" width="1092" height="714" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.0.1920.1050/-1920.0.1920.1050@0.0.1920.1050" timestamp="1635958280293" /> + <state x="689" y="327" key="#com.intellij.ide.util.TreeClassChooserDialog" timestamp="1635958256418"> + <screen x="0" y="0" width="1920" height="1050" /> + </state> + <state x="689" y="327" key="#com.intellij.ide.util.TreeClassChooserDialog/0.0.1920.1050/-1920.0.1920.1050@0.0.1920.1050" timestamp="1635958256418" /> + </component> +</project>
\ No newline at end of file diff --git a/code/Interpreter2/Interpreter2.iml b/code/Interpreter2/Interpreter2.iml new file mode 100644 index 0000000..c90834f --- /dev/null +++ b/code/Interpreter2/Interpreter2.iml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module>
\ No newline at end of file diff --git a/code/Interpreter2/src/Interpreter/Environment.java b/code/Interpreter2/src/Interpreter/Environment.java new file mode 100644 index 0000000..d191bde --- /dev/null +++ b/code/Interpreter2/src/Interpreter/Environment.java @@ -0,0 +1,30 @@ +package Interpreter; + +import java.util.HashMap; +import java.util.Map; + +public class Environment { + private final Map<String,Object> variableMap = new HashMap<>(); + + //Maybe check if variable is already defined? + public void defineVariable(String name,Object value){ + variableMap.put(name, value); + } + + public Object getVariable(String name){ + if(variableMap.containsKey(name)){ + return variableMap.get(name); + } + Language.displayError("Undefined Variable"); + throw new Error(); + } + + public void assignVariable(String name,Object value){ + if(variableMap.containsKey(name)){ + variableMap.put(name, value); + return; + } + Language.displayError("Variable undefined"); + throw new Error(); + } +} diff --git a/code/Interpreter2/src/Interpreter/Expression.java b/code/Interpreter2/src/Interpreter/Expression.java new file mode 100644 index 0000000..85ade48 --- /dev/null +++ b/code/Interpreter2/src/Interpreter/Expression.java @@ -0,0 +1,84 @@ +package Interpreter; + +abstract class Expression { + static class Binary extends Expression{ + + final Expression left; + final Expression right; + final Token op; + + Binary(Expression left, Token op, Expression right){ + this.left=left; + this.op=op; + this.right = right; + } + + @Override + public String getExpressionType() { + return "binary"; + } + + } + + static class Literal extends Expression{ + final Token value; + + Literal(Token value){ + this.value=value; + } + + + @Override + public String getExpressionType() { + return "literal"; + } + + } + + static class BracketedExpression extends Expression{ + final Expression expr; + + BracketedExpression(Expression expr){ + this.expr=expr; + } + + @Override + public String getExpressionType() { + return "bracket"; + } + + + } + + static class AssignmentExpression extends Expression{ + final Token name; + final Expression value; + + AssignmentExpression(Token name,Expression value){ + this.name=name; + this.value=value; + } + + + @Override + public String getExpressionType() { + return "assign"; + } + + } + + static class Variable extends Expression{ + + Variable(Token name){ + this.name=name; + + } + @Override + public String getExpressionType() { + return "var"; + } + final Token name; + + } + public abstract String getExpressionType(); +} diff --git a/code/Interpreter2/src/Interpreter/Interpreter.java b/code/Interpreter2/src/Interpreter/Interpreter.java new file mode 100644 index 0000000..65cdeb4 --- /dev/null +++ b/code/Interpreter2/src/Interpreter/Interpreter.java @@ -0,0 +1,131 @@ +package Interpreter; + +import java.util.List; + +import Interpreter.Expression.*; +import Interpreter.Statement.ExpressionStatement; +import Interpreter.Statement.PrintStatement; +import Interpreter.Statement.VariableDeclaration; + +public class Interpreter { + + private Environment environment = new Environment(); + + void interpret(List<Statement> statements){ + try{ + for (Statement statement: statements){ + evaluateStatement(statement); + } + } catch (Error e){ + + } + } + + private Object evaluateStatement(Statement statement){ + switch(statement.getStatmentType()){ + case "exprStmt": + return evalExpressionStatement((ExpressionStatement)statement); + case "vardec": + return evalVariableDeclaration((VariableDeclaration)statement); + case "print": + return evalPrintStatement((PrintStatement)statement); + default: + return null; + } + } + private Object evalExpressionStatement(ExpressionStatement stmt){ + return evaluateExpression(stmt.expr); + } + + private Object evalVariableDeclaration(VariableDeclaration vardec){ + environment.defineVariable(vardec.name.text, null); + return null; + } + + private Object evalPrintStatement(PrintStatement print){ + System.out.println(evaluateExpression(print.expr)); + return null; + } + + private Object evaluateExpression(Expression expression){ + switch(expression.getExpressionType()){ + case "binary": + return evaluateBinaryExpression((Binary)expression); + case "literal": + return evaluateLiteralExpression((Literal)expression); + case "bracket": + return evaluateBracketedExpression((BracketedExpression)expression); + case "assign": + return evaluateAssignmentExpression((AssignmentExpression)expression); + case "var": + return evaluateVariableExpression((Variable)expression); + default: + return null; + } + } + + private Object evaluateBinaryExpression(Binary expr){ + Object leftEval = evaluateExpression(expr.left); + Object rightEval = evaluateExpression(expr.right); + switch (expr.op.type){ + case PLUS: + if (checkOperandsNum(leftEval, leftEval)){ + return (double)leftEval + (double)rightEval; + } + case STAR: + if (checkOperandsNum(leftEval, leftEval)){ + return (double)leftEval * (double)rightEval; + } + case MINUS: + if (checkOperandsNum(leftEval, leftEval)){ + return (double)leftEval - (double)rightEval; + } + case SLASH: + if (checkOperandsNum(leftEval, leftEval)){ + return (double)leftEval / (double)rightEval; + } + + case GREATER: + if (checkOperandsNum(leftEval, leftEval)){ + return (double)leftEval > (double)rightEval; + } + case LESS: + if (checkOperandsNum(leftEval, leftEval)){ + return (double)leftEval < (double)rightEval; + } + + case EQUALITY: + return leftEval.equals(rightEval); + default: + break; + } + return null; + } + + private Object evaluateLiteralExpression(Literal expr){ + return expr.value.value; + } + + private Object evaluateBracketedExpression(BracketedExpression expr){ + return evaluateExpression(expr.expr); + } + + private Object evaluateAssignmentExpression(AssignmentExpression expr){ + Object assignedValue = evaluateExpression(expr.value); + environment.assignVariable(expr.name.text, assignedValue); + return null; + } + + private Object evaluateVariableExpression(Variable expr){ + return environment.getVariable(expr.name.text); + } + + private boolean checkOperandsNum(Object left, Object right){ + if (left instanceof Double && right instanceof Double){ + return true; + } else { + Language.displayError("Operands must be numbers"); + throw new Error(); + } + } +} diff --git a/code/Interpreter2/src/Interpreter/Language.java b/code/Interpreter2/src/Interpreter/Language.java new file mode 100644 index 0000000..80aa1e3 --- /dev/null +++ b/code/Interpreter2/src/Interpreter/Language.java @@ -0,0 +1,63 @@ +package Interpreter; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.Scanner; + +//Base class for the interpreter +public class Language { + static boolean hadError = false; + public static void main(String[] args){ + + //Allow users to input a single line of code + //Still needs some work to re-ask for input after each line + if (args.length < 1){ + Scanner input = new Scanner(System.in); + String sourceCode = "1"; + while (sourceCode!=""){ + System.out.print("Code: "); + sourceCode = input.nextLine(); + runInterpreter(sourceCode); + hadError=false; + } + input.close(); + + //Allow users to provide a path to a file as an argument + } else if (args.length==1){ + try { + String sourcecode = Files.readString(Paths.get(args[0])); //Maybe should set charset here + runInterpreter(sourcecode); + } catch (IOException exception){ + System.out.println("File not found"); + } + + } else { + System.out.println("Error, argument should be file path"); + System.exit(64); + } + } + + //Extract and print each token + private static void runInterpreter(String sourceCode){ + TokenScanner scanner = new TokenScanner(); + List<Token> tokens = scanner.extractTokens(sourceCode); + //for (Token token : tokens) { + // System.out.println(token); + //} + if (hadError) return; + //Parse into AST + Parser parser = new Parser(tokens); + List<Statement> ast = parser.parse(); + if (hadError) return; + Interpreter interpreter = new Interpreter(); + interpreter.interpret(ast); + } + + static void displayError(String message){ + hadError=true; + System.out.println("An error was encountered"); + System.out.println(message); + } +} diff --git a/code/Interpreter2/src/Interpreter/Parser.java b/code/Interpreter2/src/Interpreter/Parser.java new file mode 100644 index 0000000..6b55299 --- /dev/null +++ b/code/Interpreter2/src/Interpreter/Parser.java @@ -0,0 +1,179 @@ +package Interpreter; + +import java.util.ArrayList; +import java.util.List; + +public class Parser { + private final List<Token> tokens; + private int currentToken = 0; + + Parser(List<Token> tokens){ + this.tokens=tokens; + } + + List<Statement> parse(){ + List<Statement> statements = new ArrayList<>(); + while (!checkEOF()){ + statements.add(declaration()); + } + return statements; + + } + + private Statement declaration(){ + try{ + if (matchAndAdvance(TokenType.VAR)){ + if (matchOrError(TokenType.DEFINE, ":: Required for variable definition")){ + if (matchOrError(TokenType.IDENTIFIER,"Expected variable name.")){ + Token varName = getPreviousToken(); + return new Statement.VariableDeclaration(varName); + } + } + } + return statement(); + } catch (Error e){ + currentToken++; + return null; + } + } + + private Statement statement(){ + if (matchAndAdvance(TokenType.PRINT)){ + Expression expression = expression(); + return new Statement.PrintStatement(expression); + } + return expressionStatement(); + } + + + + private Statement expressionStatement(){ + Expression expression = assignment(); + return new Statement.ExpressionStatement(expression); + } + + private Expression assignment(){ + Expression variable = expression(); + if (matchAndAdvance(TokenType.EQUALS)){ + Expression assignedvalue = expression(); + + if (variable instanceof Expression.Variable){ + return new Expression.AssignmentExpression(((Expression.Variable)variable).name,assignedvalue); + } + throw error("Incorrect assignment operation"); + } + return variable; + } + + private Expression expression(){ + Expression createdExpression = equality(); + return createdExpression; + } + + private Expression equality(){ + Expression createdExpression = comparison(); + while (matchAndAdvance(TokenType.EQUALITY)){ + Token op = getPreviousToken(); + Expression right = comparison(); + createdExpression = new Expression.Binary(createdExpression, op, right); + } + return createdExpression; + } + + private Expression comparison(){ + Expression createdExpression = term(); + while (matchAndAdvance(TokenType.GREATER)||matchAndAdvance(TokenType.LESS)){ + Token op = getPreviousToken(); + Expression right = term(); + createdExpression = new Expression.Binary(createdExpression, op, right); + } + return createdExpression; + } + + private Expression term(){ + Expression createdExpression = factor(); + while (matchAndAdvance(TokenType.PLUS)||matchAndAdvance(TokenType.MINUS)){ + Token op = getPreviousToken(); + Expression right = factor(); + createdExpression = new Expression.Binary(createdExpression, op, right); + } + return createdExpression; + } + + private Expression factor(){ + Expression createdExpression = primary(); + while (matchAndAdvance(TokenType.STAR)||matchAndAdvance(TokenType.SLASH)){ + Token op = getPreviousToken(); + Expression right = primary(); + createdExpression = new Expression.Binary(createdExpression, op, right); + } + return createdExpression; + } + + private Expression primary(){ + if (matchAndAdvance(TokenType.NUMBER)){ + return new Expression.Literal(getPreviousToken()); + } + + if (matchAndAdvance(TokenType.IDENTIFIER)) { + + return new Expression.Variable(getPreviousToken()); + } + + if (matchAndAdvance(TokenType.LEFT_PAREN)){ + Expression expr = expression(); + if (matchAndAdvance(TokenType.RIGHT_PAREN)){ + return new Expression.BracketedExpression(expr); + } + else{ + throw error("Expected ')"); + } + } + throw error("Expected Expression"); + } + + private void advanceToken(){ + if (!checkEOF()) { + currentToken++; + }; + } + + private boolean matchAndAdvance(TokenType type){ + if (checkToken(type)) { + advanceToken(); + return true; + } + return false; + } + + private boolean matchOrError(TokenType type,String errorMessage){ + if (matchAndAdvance(type)){ + return true; + } + throw error(errorMessage); + } + + private boolean checkToken(TokenType type){ + if (checkEOF()) return false; + return getCurrentToken().type == type; + } + + private boolean checkEOF(){ + return tokens.get(currentToken).type==TokenType.EOF; + } + + private Token getCurrentToken(){ + return tokens.get(currentToken); + } + + private Token getPreviousToken(){ + return tokens.get(currentToken - 1); + } + + private Error error(String message){ + Language.displayError(message); + return new Error(); + } + + +} diff --git a/code/Interpreter2/src/Interpreter/Statement.java b/code/Interpreter2/src/Interpreter/Statement.java new file mode 100644 index 0000000..5a9aef7 --- /dev/null +++ b/code/Interpreter2/src/Interpreter/Statement.java @@ -0,0 +1,49 @@ +package Interpreter; + +abstract class Statement { + + static class ExpressionStatement extends Statement{ + ExpressionStatement(Expression expr){ + this.expr = expr; + } + + + final Expression expr; + + @Override + public String getStatmentType() { + return "exprStmt"; + } + } + + + static class VariableDeclaration extends Statement{ + VariableDeclaration(Token name){ + this.name=name; + } + + + final Token name; + + @Override + public String getStatmentType() { + return "vardec"; + } + + } + + static class PrintStatement extends Statement{ + PrintStatement(Expression expr){ + this.expr=expr; + } + final Expression expr; + + @Override + public String getStatmentType() { + return "print"; + } + } + + + public abstract String getStatmentType(); +} diff --git a/code/Interpreter2/src/Interpreter/Token.java b/code/Interpreter2/src/Interpreter/Token.java new file mode 100644 index 0000000..0129b78 --- /dev/null +++ b/code/Interpreter2/src/Interpreter/Token.java @@ -0,0 +1,23 @@ +package Interpreter; + +public class Token { + + + //Stores the token type, the actual text and the runtime object + public final TokenType type; + final String text; + final Object value; + + + Token(TokenType type, String text, Object value){ + this.type=type; + this.text=text; + this.value=value; + + } + + @Override + public String toString() { + return type + " " + text + " " + value; + } +} diff --git a/code/Interpreter2/src/Interpreter/TokenScanner.java b/code/Interpreter2/src/Interpreter/TokenScanner.java new file mode 100644 index 0000000..c9249a4 --- /dev/null +++ b/code/Interpreter2/src/Interpreter/TokenScanner.java @@ -0,0 +1,179 @@ +package Interpreter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TokenScanner { + private String sourceCode; + List<Token> tokens = new ArrayList<>(); + private int tokenStart=0; + private int currentLoc=0; + + //Extract tokens from the source code by reading character by character + List<Token> extractTokens(String sourceCode){ + this.sourceCode=sourceCode; + while (!checkEOF()){ + tokenStart=currentLoc; + readToken(); + } + tokens.add(new Token(TokenType.EOF, "", null)); + return tokens; + } + + //Extract a single token + private void readToken(){ + char checkChar = sourceCode.charAt(currentLoc); + switch(checkChar){ + + case ' ':break; + case '\n':break; + case '\r':break; + case '\t': + break; + + case '(': createTokenNull(TokenType.LEFT_PAREN); break; + case ')': createTokenNull(TokenType.RIGHT_PAREN); break; + case '+': createTokenNull(TokenType.PLUS); break; + case '-': createTokenNull(TokenType.MINUS); break; + case '*': createTokenNull(TokenType.STAR); break; + case '/': createTokenNull(TokenType.SLASH); break; + case ';': createTokenNull(TokenType.SEMI_COLON); break; + + //Some tokens are multiple characters long (==, <=) etc + //so need to check next char as well + case '=': + if (checkNextChar('=')){ + createTokenNull(TokenType.EQUALITY); + break; + } else { + createTokenNull(TokenType.EQUALS); + break; + } + case ':': + if (checkNextChar(':')){ + createTokenNull(TokenType.DEFINE); + break; + } else { + createTokenNull(TokenType.COLON); + break; + } + case '<': + if (checkNextChar('=')){ + createTokenNull(TokenType.LESS_EQUAL); + break; + } else { + createTokenNull(TokenType.LESS); + break; + } + case '>': + if (checkNextChar('=')){ + createTokenNull(TokenType.GREATER_EQUAL); + break; + } else { + createTokenNull(TokenType.GREATER); + break; + } + default: + + //Check for numer + if (checkIsDigit(checkChar)){ + while (checkIsDigit(lookAhead())){ + currentLoc++; + } + //Check if number contains a decimal point + if (lookAhead()=='.' && checkIsDigit(lookTwoAhead())){ + currentLoc++; + while (checkIsDigit(lookAhead())){ + currentLoc++; + } + } + createToken(TokenType.NUMBER, Double.parseDouble(sourceCode.substring(tokenStart, currentLoc+1))); + } + else if (checkIsAlpha(checkChar)){ + while (checkIsAlpha(lookAhead())){ + currentLoc++; + } + String text = sourceCode.substring(tokenStart, currentLoc+1); + TokenType type = keywords.get(text); + if(type == null){ + createToken(TokenType.IDENTIFIER, text); + } else{ + createToken(type, text); + } + + } else { + Language.displayError("Unexpected Character"); + } + } + currentLoc++; + + } + + //Test for end of file + private boolean checkEOF(){ + return currentLoc>=sourceCode.length(); + } + + //Create a token without a value + private void createTokenNull(TokenType type){ + createToken(type, null); + } + + //Create token + private void createToken(TokenType type, Object value){ + String tokenText = sourceCode.substring(tokenStart, currentLoc+1); + tokens.add(new Token(type, tokenText, value)); + } + + //Check if the next char matches a given char + private boolean checkNextChar(char matchChar){ + if (checkEOF()){ + return false; + } + if (sourceCode.charAt(currentLoc+1)==matchChar){ + currentLoc++; + return true; + } + return false; + } + + //Look at the next char in the source code + private char lookAhead(){ + if (currentLoc+1>=sourceCode.length()){ + return ' '; + + } else { + return sourceCode.charAt(currentLoc+1); + } + } + + //Look 2 chars ahead in the source code + private char lookTwoAhead(){ + if (currentLoc+2>=sourceCode.length()){ + return ' '; + + } else { + return sourceCode.charAt(currentLoc+2); + } + } + + //Check if a given char is a digit + private boolean checkIsDigit(char checkChar){ + return checkChar>='0' && checkChar<='9'; + } + + private boolean checkIsAlpha(char checkChar){ + return ('a'<=checkChar && checkChar<='z')|| + ('A'<=checkChar && checkChar<='Z'); + } + + private static final Map<String, TokenType> keywords; + + static { + keywords = new HashMap<>(); + keywords.put("var", TokenType.VAR); + keywords.put("print", TokenType.PRINT); + } +} diff --git a/code/Interpreter2/src/Interpreter/TokenType.java b/code/Interpreter2/src/Interpreter/TokenType.java new file mode 100644 index 0000000..756fab6 --- /dev/null +++ b/code/Interpreter2/src/Interpreter/TokenType.java @@ -0,0 +1,17 @@ +package Interpreter; + +public enum TokenType { + EQUALS, LEFT_PAREN, RIGHT_PAREN, + PLUS, MINUS, SLASH, STAR, SEMI_COLON, + COLON, + + EQUALITY, GREATER, LESS, + GREATER_EQUAL, LESS_EQUAL, + DEFINE, + + NUMBER,IDENTIFIER, + + VAR,PRINT, + + EOF +} |