diff --git a/wk2/Java/counting.bb b/wk2/Java/counting.bb new file mode 100644 index 0000000000000000000000000000000000000000..7c0ee2323b5ab0fcae6e378a910eef855a214961 --- /dev/null +++ b/wk2/Java/counting.bb @@ -0,0 +1,7 @@ +clear X; +incr X; +incr X; +incr X; +while X not 0 do; + decr X; +end; diff --git a/wk2/Java/multiply.bb b/wk2/Java/multiply.bb new file mode 100644 index 0000000000000000000000000000000000000000..2ab092c4c8491b6a9a22fcf059414a047b650c43 --- /dev/null +++ b/wk2/Java/multiply.bb @@ -0,0 +1,21 @@ +clear X; +incr X; +incr X; +clear Y; +incr Y; +incr Y; +incr Y; +clear Z; +while X not 0 do; + clear W; + while Y not 0 do; + incr Z; + incr W; + decr Y; + end; + while W not 0 do; + incr Y; + decr W; + end; + decr X; +end; diff --git a/wk2/Java/src/jrr1g18/bb/Interpreter.java b/wk2/Java/src/jrr1g18/bb/Interpreter.java new file mode 100644 index 0000000000000000000000000000000000000000..a20774007e75884a40a0ffeaa0df77021d62ea19 --- /dev/null +++ b/wk2/Java/src/jrr1g18/bb/Interpreter.java @@ -0,0 +1,266 @@ +package jrr1g18.bb; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Interpreter { + + private Map<String, Integer> m_vars = new HashMap<>(); + private Map<Integer, String> m_code = new HashMap<>(); + private Map<Integer, Integer> whiles = new HashMap<>(); + private int m_lineCount = 0; + private int m_whileDepth = 0; + + private static final Map<InstructionType, String> INSTRUCTION_NAMES; + + static { + Map<InstructionType, String> tempMap = new HashMap<>(); + tempMap.put(InstructionType.CLEAR, "clear"); + tempMap.put(InstructionType.DECR, "decr"); + tempMap.put(InstructionType.INCR, "incr"); + tempMap.put(InstructionType.WHILE, "while"); + tempMap.put(InstructionType.END, "end;"); + INSTRUCTION_NAMES = Collections.unmodifiableMap(tempMap); + } + + private enum InstructionType { + INCR, DECR, CLEAR, WHILE, END, + } + + @SuppressWarnings("unused") + private String instructionTypeToString(InstructionType it) { + return INSTRUCTION_NAMES.get(it); + } + + private InstructionType stringToInstructionType(String instruction) { + for(Entry<InstructionType, String> entry : INSTRUCTION_NAMES + .entrySet()) { + if(Objects.equals(instruction, entry.getValue())) { + return entry.getKey(); + } + } + return null; + } + + private static String getVarFromLine(String line) { + Matcher m = + Pattern.compile("(?<=incr|clear|decr)(.+)(?=;)").matcher(line); + if(!m.find()) { + System.err.println(line); + System.exit(1); + } + return stripWhitespace(m.group(1)); + } + + public void run(String fileName) { + readCode(fileName); + } + + public String variablesToString() { + return m_vars.toString(); + } + + private String getLine(int line) { + return m_code.get(line); + } + + private String[] splitLine(String line) { + return stripWhitespace(line).split("\\s+"); + } + + private String[] getSplitLine(int line) { + return splitLine(getLine(line)); + } + + public static String stripWhitespace(String str) { + return str.replaceAll("(^\\s+|\\s+$)", ""); + } + + private boolean isVariableNull(String name) { + return m_vars.get(name) == null; + } + + private void codeCheck() { + String line; + String[] splitLine; + int whileDepth = 0; + + for(int idx = 0; idx < m_lineCount; ++idx) { + line = stripWhitespace(getLine(idx)); + splitLine = splitLine(line); + + if(line.equals("")) { + continue; + } + if(!line.matches(".*;$")) { + System.err.println("missing ; on line " + (idx + 1)); + System.exit(1); + } + + if(!splitLine[0].equals("incr") && !splitLine[0].equals("decr") + && !splitLine[0].equals("end;") + && !splitLine[0].equals("clear") + && !splitLine[0].equals("while")) { + System.err.println("Syntax error on line " + (idx + 1)); + System.exit(1); + + } + + InstructionType instruction = stringToInstructionType(splitLine[0]); + if((instruction == InstructionType.CLEAR + || instruction == InstructionType.DECR + || instruction == InstructionType.INCR) + && splitLine.length > 2) { + System.err.println("Too many arguments on line " + (idx + 1)); + System.exit(1); + } + + switch (instruction) { + case WHILE: + whileDepth++; + splitLine[4] = splitLine[1].replace( + splitLine[1].substring(splitLine[1].length() - 1), ""); + + if(!splitLine[2].equals("not") || splitLine[4].equals("do") + || splitLine.length > 5) { + System.err.println("Syntax error on line " + (idx + 1)); + System.exit(1); + } + break; + case END: + whileDepth--; + if(splitLine[0].equals("end;") && splitLine.length > 1) { + System.err.println("Syntax error on line " + (idx + 1)); + System.exit(1); + } + break; + default: + break; + } + + } + + if(whileDepth > 0) { + System.err.println( + "There are more while statements than end statements"); + } else if(whileDepth < 0) { + System.err.println( + "There are more end statements than while statements"); + } + } + + private void readCode(String nameOfTheFile) { + try(BufferedReader br = + new BufferedReader(new FileReader(nameOfTheFile))) { + String line; + while((line = br.readLine()) != null) { + m_code.put(m_lineCount, line); + m_lineCount++; + } + } catch(FileNotFoundException e) { + e.printStackTrace(); + } catch(IOException e) { + e.printStackTrace(); + } + + codeCheck(); + exec(); + } + + private void exec() { + String[] splitLine; + + for(int idx = 0; idx < m_lineCount; ++idx) { + splitLine = getSplitLine(idx); + InstructionType instruction = stringToInstructionType(splitLine[0]); + switch (instruction) { + case WHILE: + case END: + idx = loop(idx, splitLine); + continue; + default: + break; + } + String var = getVarFromLine(getLine(idx)); + switch (instruction) { + case CLEAR: + clear(var); + break; + case INCR: + incr(var); + break; + case DECR: + decr(var); + break; + default: + break; + } + } + } + + private void incr(String name) { + if(isVariableNull(name)) { + System.err.println("Error: Variable " + name + " doesn't exist"); + System.exit(1); + } else { + m_vars.put(name, m_vars.get(name) + 1); + } + } + + private void decr(String name) { + if(isVariableNull(name)) { + System.err.println("Error: Variable " + name + " doesn't exist"); + System.exit(1); + } else if(m_vars.get(name) == 0) { + System.err + .println("Error: Variable " + name + " cannot be negative"); + System.exit(1); + } else { + m_vars.put(name, m_vars.get(name) - 1); + } + } + + private void clear(String name) { + m_vars.put(name, 0); + } + + private int loop(int idx, String[] splitLine) { + String line; + if(splitLine[0].equals("while")) { + m_whileDepth++; + whiles.put(m_whileDepth, idx); + + int condition = Integer.parseInt(splitLine[3]); + if(isVariableNull(splitLine[1]) + && m_vars.get(splitLine[1]) == condition) { + while(!splitLine[0].equals("end;")) { + idx++; + line = getLine(idx); + splitLine = splitLine(line); + } + } + } else { + line = m_code.get(whiles.get(m_whileDepth)); + splitLine = splitLine(line); + int condition = Integer.parseInt(splitLine[3]); + + if(m_vars.get(splitLine[1]) != condition) + idx = whiles.get(m_whileDepth); + else { + m_whileDepth--; + } + + } + return idx; + } + +} diff --git a/wk2/Java/src/jrr1g18/bb/Main.java b/wk2/Java/src/jrr1g18/bb/Main.java new file mode 100644 index 0000000000000000000000000000000000000000..c19932fc21aa1c4831543f05807494c55e6453dc --- /dev/null +++ b/wk2/Java/src/jrr1g18/bb/Main.java @@ -0,0 +1,11 @@ +package jrr1g18.bb; + +public class Main { + + public static void main(String[] args) { + Interpreter interpreter = new Interpreter(); + interpreter.run("multiply.bb"); + System.out.println(interpreter.variablesToString()); + } + +} diff --git a/wk2/Java/src/jrr1g18/boring/Interpreter.java b/wk2/Java/src/jrr1g18/boring/Interpreter.java deleted file mode 100644 index 703acea4ebbba5a51c94ae96a0d9e4797a8797e2..0000000000000000000000000000000000000000 --- a/wk2/Java/src/jrr1g18/boring/Interpreter.java +++ /dev/null @@ -1,112 +0,0 @@ -package jrr1g18.boring; - -import jrr1g18.boring.Token.TokenType; -import jrr1g18.boring.exceptions.IllegalCharacterException; -import jrr1g18.boring.exceptions.IllegalTokenException; - -/** - * Interpreter for Boring - * - * @author jrr1g18 - * - */ -public class Interpreter { - private Lexer m_lexer; - private Token m_currentToken; - - public Interpreter(Lexer lexer) { - m_lexer = lexer; - try { - m_currentToken = m_lexer.getNextToken(); - } catch(IllegalCharacterException e) { - e.printStackTrace(); - } - } - - /** - * Compare current token with tt. If they match, then advance the Lexer. - * - * @param tt The expected TokenType - * @throws IllegalTokenException When the token types do not match - */ - public void eat(TokenType tt) throws IllegalTokenException { - if(m_currentToken.getType() == tt) { - try { - m_currentToken = m_lexer.getNextToken(); - } catch(IllegalCharacterException e) { - e.printStackTrace(); - } - } else { - throw new IllegalTokenException( - "Expected: " + Lib.tokenTypeToString(tt) + " got: " - + Lib.tokenTypeToString(m_currentToken.getType())); - } - } - - /** - * number : INTEGER - * - * @return An integer token value. - * @throws IllegalTokenException - */ - public int number() throws IllegalTokenException { - Integer value = ((ValueToken) m_currentToken).getValue(); - eat(TokenType.INTEGER); - return value; - } - - /** - * mulDiv : number ((MUL|DIV) number)* - * - * @return The expression result. - * @throws IllegalTokenException When an illegal token is enountered. - */ - public int mulDiv() throws IllegalTokenException { - int result = number(); - while(m_currentToken.getType() == TokenType.MUL - || m_currentToken.getType() == TokenType.DIV) { - ValueToken token = (ValueToken) m_currentToken; - switch (token.getType()) { - case MUL: - eat(TokenType.MUL); - result *= number(); - break; - case DIV: - eat(TokenType.DIV); - result /= number(); - break; - default: - break; - } - } - return result; - } - - /** - * addSub : number ((ADD|SUB) number)* - * - * @return The expression result. - * @throws IllegalTokenException When an illegal token is enountered. - */ - public int addSub() throws IllegalTokenException { - int result = number(); - while(m_currentToken.getType() == TokenType.ADD - || m_currentToken.getType() == TokenType.SUB) { - ValueToken token = (ValueToken) m_currentToken; - switch (token.getType()) { - case ADD: - eat(TokenType.ADD); - result += number(); - break; - case SUB: - eat(TokenType.SUB); - result -= number(); - break; - default: - break; - } - } - return result; - } - -} diff --git a/wk2/Java/src/jrr1g18/boring/Lexer.java b/wk2/Java/src/jrr1g18/boring/Lexer.java deleted file mode 100644 index 38771f0d88e7841dbaaeebc9c13d7846a8cbff17..0000000000000000000000000000000000000000 --- a/wk2/Java/src/jrr1g18/boring/Lexer.java +++ /dev/null @@ -1,91 +0,0 @@ -package jrr1g18.boring; - -import jrr1g18.boring.Token.TokenType; -import jrr1g18.boring.exceptions.IllegalCharacterException; - -public class Lexer { - private String m_text; - private int m_pos; - private String m_currentChar; - - public Lexer(String text) { - m_text = text; - } - - /** - * Advance m_pos and set m_currentChar - */ - private void nextChar() { - m_pos += 1; - if(m_pos > m_text.length() - 1) { - m_currentChar = null; - } else { - m_currentChar = "" + m_text.charAt(m_pos); - } - } - - /** - * Advance until EOF or end of whitespace - */ - public void skipWhitespace() { - while(m_currentChar != null && Lib.isWhitespace(m_currentChar)) { - nextChar(); - } - } - - /** - * @return A multidigit integer generated from input - */ - public int integer() { - String ret = ""; - while(m_currentChar != null && Lib.isInt(m_currentChar)) { - ret += m_currentChar; - nextChar(); - } - return Integer.parseInt(ret); - } - - /** - * Tokeniser - * - * @return The next token - * @throws IllegalCharacterException When an unknown character is found - */ - public Token getNextToken() throws IllegalCharacterException { - while(m_currentChar != null) { - if(Lib.isWhitespace(m_currentChar)) { - skipWhitespace(); - continue; - } - - if(Lib.isInt(m_currentChar)) { - return new ValueToken(TokenType.INTEGER, integer()); - } else if(m_currentChar - .equals(Lib.tokenTypeToString(TokenType.ADD))) { - nextChar(); - return new NamedToken(TokenType.ADD, - Lib.tokenTypeToString(TokenType.ADD)); - } else if(m_currentChar - .equals(Lib.tokenTypeToString(TokenType.SUB))) { - nextChar(); - return new NamedToken(TokenType.SUB, - Lib.tokenTypeToString(TokenType.SUB)); - } else if(m_currentChar - .equals(Lib.tokenTypeToString(TokenType.MUL))) { - nextChar(); - return new NamedToken(TokenType.MUL, - Lib.tokenTypeToString(TokenType.MUL)); - } else if(m_currentChar - .equals(Lib.tokenTypeToString(TokenType.DIV))) { - nextChar(); - return new NamedToken(TokenType.DIV, - Lib.tokenTypeToString(TokenType.DIV)); - } else { - throw new IllegalCharacterException( - "Unrecognised thing: " + m_currentChar); - } - } - return new Token(TokenType.EOF); - } - -} diff --git a/wk2/Java/src/jrr1g18/boring/Lib.java b/wk2/Java/src/jrr1g18/boring/Lib.java deleted file mode 100644 index 0b806927584c31620de8cf348a76482cb96ede52..0000000000000000000000000000000000000000 --- a/wk2/Java/src/jrr1g18/boring/Lib.java +++ /dev/null @@ -1,46 +0,0 @@ -package jrr1g18.boring; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import jrr1g18.boring.Token.TokenType; - -public abstract class Lib { - /** - * Checks if a string is an integer - * - * @param string String to check - * @return True if an integer, false otherwise - */ - public static boolean isInt(String string) { - return string.matches("\\d+"); - } - - /** - * Checks if a string is only whitespace - * - * @param string String to check - * @return True if an integer, false otherwise - */ - public static boolean isWhitespace(String string) { - return string.matches("\\s+"); - } - - private static final Map<TokenType, String> TOKEN_MAP; - static { - HashMap<TokenType, String> tempMap = new HashMap<TokenType, String>(); - tempMap.put(TokenType.EOF, "EOF"); - tempMap.put(TokenType.INTEGER, "INTEGER"); - tempMap.put(TokenType.SEMICOLON, "SEMICOLON"); - tempMap.put(TokenType.DIV, "/"); - tempMap.put(TokenType.SUB, "-"); - tempMap.put(TokenType.ADD, "+"); - tempMap.put(TokenType.MUL, "*"); - TOKEN_MAP = Collections.unmodifiableMap(tempMap); - } - - public static String tokenTypeToString(final TokenType tt) { - return TOKEN_MAP.get(tt); - } -} diff --git a/wk2/Java/src/jrr1g18/boring/Main.java b/wk2/Java/src/jrr1g18/boring/Main.java deleted file mode 100644 index 6559edd848abc3546f058da9aaf35a7073c79e29..0000000000000000000000000000000000000000 --- a/wk2/Java/src/jrr1g18/boring/Main.java +++ /dev/null @@ -1,11 +0,0 @@ -package jrr1g18.boring; - -public class Main { - - public static void main(String[] args) throws Exception { - Lexer lexer = new Lexer("1+2"); - Interpreter interpreter = new Interpreter(lexer); - System.out.println(interpreter.addSub()); - } - -} diff --git a/wk2/Java/src/jrr1g18/boring/NamedToken.java b/wk2/Java/src/jrr1g18/boring/NamedToken.java deleted file mode 100644 index d40a44780b264e1a167ae11c5dff530a91a40207..0000000000000000000000000000000000000000 --- a/wk2/Java/src/jrr1g18/boring/NamedToken.java +++ /dev/null @@ -1,33 +0,0 @@ -package jrr1g18.boring; - -public class NamedToken extends Token { - private String m_name; - - public NamedToken(TokenType type, String value) { - super(type); - m_name = new String(value); - } - - public NamedToken(TokenType type) { - super(type); - m_name = null; - } - - @Override - public String toString() { - return "ValueToken(" + Lib.tokenTypeToString(getType()) + ", " - + getName() + ")"; - } - - public String getName() { - return m_name; - } - - public void setName(final NamedToken value) { - m_name = new String(value.getName()); - } - - public void setName(final String value) { - m_name = new String(value); - } -} diff --git a/wk2/Java/src/jrr1g18/boring/Token.java b/wk2/Java/src/jrr1g18/boring/Token.java deleted file mode 100644 index 48df78484e635ab751bf8bface3394f9e7ebc9a1..0000000000000000000000000000000000000000 --- a/wk2/Java/src/jrr1g18/boring/Token.java +++ /dev/null @@ -1,23 +0,0 @@ -package jrr1g18.boring; - -public class Token { - - public enum TokenType { - EOF, INTEGER, SEMICOLON, ADD, SUB, MUL, DIV, - } - - private final TokenType m_type; - - public Token(TokenType type) { - m_type = type; - } - - public TokenType getType() { - return m_type; - } - - public String toString() { - return "Token(" + Lib.tokenTypeToString(m_type) + ")"; - } - -} \ No newline at end of file diff --git a/wk2/Java/src/jrr1g18/boring/ValueToken.java b/wk2/Java/src/jrr1g18/boring/ValueToken.java deleted file mode 100644 index 04efae29725ea9c405fcd8d247e814df70625b31..0000000000000000000000000000000000000000 --- a/wk2/Java/src/jrr1g18/boring/ValueToken.java +++ /dev/null @@ -1,38 +0,0 @@ -package jrr1g18.boring; - -public class ValueToken extends Token { - private Integer m_value; - - public ValueToken(TokenType type, final Integer name) { - super(type); - m_value = new Integer(name); - } - - public ValueToken(TokenType type, final int name) { - super(type); - m_value = new Integer(name); - } - - public ValueToken(TokenType type) { - super(type); - m_value = null; - } - - @Override - public String toString() { - return "ValueToken(" + Lib.tokenTypeToString(getType()) + ", " - + getValue() + ")"; - } - - public Integer getValue() { - return m_value; - } - - public void setValue(ValueToken value) { - m_value = value.getValue(); - } - - public void setValue(final Integer value) { - m_value = new Integer(value); - } -} diff --git a/wk2/Java/src/jrr1g18/boring/exceptions/BoringException.java b/wk2/Java/src/jrr1g18/boring/exceptions/BoringException.java deleted file mode 100644 index 644425d8d3e72230d6d41e3986285d1e4a56fcbf..0000000000000000000000000000000000000000 --- a/wk2/Java/src/jrr1g18/boring/exceptions/BoringException.java +++ /dev/null @@ -1,22 +0,0 @@ -package jrr1g18.boring.exceptions; - -public class BoringException extends Exception { - - public BoringException(String message) { - super(message); - } - - public BoringException(Throwable cause) { - super(cause); - } - - public BoringException(String message, Throwable cause) { - super(message, cause); - } - - public BoringException(String message, Throwable cause, - boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - -} diff --git a/wk2/Java/src/jrr1g18/boring/exceptions/IllegalCharacterException.java b/wk2/Java/src/jrr1g18/boring/exceptions/IllegalCharacterException.java deleted file mode 100644 index f01f3448073b390378645c25710fb43b39651fa3..0000000000000000000000000000000000000000 --- a/wk2/Java/src/jrr1g18/boring/exceptions/IllegalCharacterException.java +++ /dev/null @@ -1,22 +0,0 @@ -package jrr1g18.boring.exceptions; - -public class IllegalCharacterException extends BoringException { - - public IllegalCharacterException(String message) { - super(message); - } - - public IllegalCharacterException(Throwable cause) { - super(cause); - } - - public IllegalCharacterException(String message, Throwable cause) { - super(message, cause); - } - - public IllegalCharacterException(String message, Throwable cause, - boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - -} diff --git a/wk2/Java/src/jrr1g18/boring/exceptions/IllegalTokenException.java b/wk2/Java/src/jrr1g18/boring/exceptions/IllegalTokenException.java deleted file mode 100644 index 849e8c47010c527ebf0a05ad6f137c92cd89a559..0000000000000000000000000000000000000000 --- a/wk2/Java/src/jrr1g18/boring/exceptions/IllegalTokenException.java +++ /dev/null @@ -1,22 +0,0 @@ -package jrr1g18.boring.exceptions; - -public class IllegalTokenException extends BoringException { - - public IllegalTokenException(String message) { - super(message); - } - - public IllegalTokenException(Throwable cause) { - super(cause); - } - - public IllegalTokenException(String message, Throwable cause) { - super(message, cause); - } - - public IllegalTokenException(String message, Throwable cause, - boolean enableSuppression, boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - -}