From 6d950cf43c5278ee691838c9f8cd3f07167d5518 Mon Sep 17 00:00:00 2001 From: Yohan Beschi Date: Sun, 15 Dec 2013 19:00:31 +0100 Subject: [PATCH] Simple interpreter --- .../01_src/main/pjb/partnine/postfix.pjb | 14 ++ .../bytecode/partnine/BytecodeTest.java | 12 + .../{mathparser => math}/MathException.java | 5 +- .../isk/jvmhardcore/math/common/Operator.java | 5 + .../parser}/EventType.java | 2 +- .../jvmhardcore/math/parser/MathParser.java | 157 +++++++++++++ .../parser}/MathTokenizer.java | 51 ++-- .../parser}/Productions.java | 18 +- .../{mathparser => math/parser}/Symbols.java | 8 +- .../parser}/core/InputStreamReader.java | 4 +- .../parser}/core/Parser.java | 2 +- .../parser}/core/Production.java | 5 +- .../parser}/core/Reader.java | 2 +- .../parser}/core/ReaderException.java | 2 +- .../parser}/core/Tokenizer.java | 7 +- .../parser}/core/util/Ascii.java | 4 +- .../parser/core/util/StringGenerator.java | 74 ++++++ .../jvmhardcore/math/solver/MathSolver.java | 100 ++++++++ .../jvmhardcore/mathparser/MathParser.java | 75 ------ .../mathparser/core/util/StringGenerator.java | 78 ------- .../math/parser/MathParserTest.java | 217 ++++++++++++++++++ .../parser}/MathTokenizerTest.java | 83 ++++--- .../parser}/core/InputStreamReaderTest.java | 6 +- .../parser}/core/TokenizerTest.java | 9 +- .../math/solver/MathSolverTest.java | 217 ++++++++++++++++++ .../mathparser/MathParserTest.java | 161 ------------- README.md | 12 +- 27 files changed, 910 insertions(+), 420 deletions(-) create mode 100644 03_projects/bytecode/01_src/main/pjb/partnine/postfix.pjb create mode 100644 03_projects/bytecode/01_src/test/java/org/isk/jvmhardcore/bytecode/partnine/BytecodeTest.java rename 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/{mathparser => math}/MathException.java (91%) create mode 100644 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/common/Operator.java rename 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/{mathparser => math/parser}/EventType.java (72%) create mode 100644 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/MathParser.java rename 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/{mathparser => math/parser}/MathTokenizer.java (77%) rename 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/{mathparser => math/parser}/Productions.java (95%) rename 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/{mathparser => math/parser}/Symbols.java (93%) rename 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/{mathparser => math/parser}/core/InputStreamReader.java (97%) rename 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/{mathparser => math/parser}/core/Parser.java (96%) rename 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/{mathparser => math/parser}/core/Production.java (77%) rename 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/{mathparser => math/parser}/core/Reader.java (92%) rename 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/{mathparser => math/parser}/core/ReaderException.java (92%) rename 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/{mathparser => math/parser}/core/Tokenizer.java (93%) rename 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/{mathparser => math/parser}/core/util/Ascii.java (98%) create mode 100644 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/util/StringGenerator.java create mode 100644 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/solver/MathSolver.java delete mode 100644 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/MathParser.java delete mode 100644 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/util/StringGenerator.java create mode 100644 03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/MathParserTest.java rename 03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/{mathparser => math/parser}/MathTokenizerTest.java (83%) rename 03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/{mathparser => math/parser}/core/InputStreamReaderTest.java (98%) rename 03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/{mathparser => math/parser}/core/TokenizerTest.java (86%) create mode 100644 03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/solver/MathSolverTest.java delete mode 100644 03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/MathParserTest.java diff --git a/03_projects/bytecode/01_src/main/pjb/partnine/postfix.pjb b/03_projects/bytecode/01_src/main/pjb/partnine/postfix.pjb new file mode 100644 index 0000000..56d4b42 --- /dev/null +++ b/03_projects/bytecode/01_src/main/pjb/partnine/postfix.pjb @@ -0,0 +1,14 @@ +.class org/isk/jvmhardcore/bytecode/partnine/Postfix + .method compute()I + iconst_2 + bipush 7 + iconst_5 + isub + imul + bipush 8 + iconst_5 + isub + imul + ireturn + .methodend +.classend \ No newline at end of file diff --git a/03_projects/bytecode/01_src/test/java/org/isk/jvmhardcore/bytecode/partnine/BytecodeTest.java b/03_projects/bytecode/01_src/test/java/org/isk/jvmhardcore/bytecode/partnine/BytecodeTest.java new file mode 100644 index 0000000..fd28dfb --- /dev/null +++ b/03_projects/bytecode/01_src/test/java/org/isk/jvmhardcore/bytecode/partnine/BytecodeTest.java @@ -0,0 +1,12 @@ +package org.isk.jvmhardcore.bytecode.partnine; + +import org.junit.Assert; +import org.junit.Test; + +public class BytecodeTest { + @Test + public void compute() { + final int result = Postfix.compute(); + Assert.assertEquals(12, result); + } +} diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/MathException.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/MathException.java similarity index 91% rename from 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/MathException.java rename to 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/MathException.java index 1a33fec..0420c8f 100644 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/MathException.java +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/MathException.java @@ -1,10 +1,7 @@ -package org.isk.jvmhardcore.mathparser; +package org.isk.jvmhardcore.math; public class MathException extends RuntimeException { - /** - * - */ private static final long serialVersionUID = 2413076920261396393L; public MathException() { diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/common/Operator.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/common/Operator.java new file mode 100644 index 0000000..c89013e --- /dev/null +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/common/Operator.java @@ -0,0 +1,5 @@ +package org.isk.jvmhardcore.math.common; + +public enum Operator { + PLUS, MINUS, TIMES, DIVIDE; +} diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/EventType.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/EventType.java similarity index 72% rename from 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/EventType.java rename to 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/EventType.java index e38f0b6..4c45fdf 100644 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/EventType.java +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/EventType.java @@ -1,4 +1,4 @@ -package org.isk.jvmhardcore.mathparser; +package org.isk.jvmhardcore.math.parser; public enum EventType { EOF, diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/MathParser.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/MathParser.java new file mode 100644 index 0000000..cdee1fa --- /dev/null +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/MathParser.java @@ -0,0 +1,157 @@ +package org.isk.jvmhardcore.math.parser; + +import java.io.InputStream; +import java.util.LinkedList; +import java.util.Stack; + +import org.isk.jvmhardcore.math.MathException; +import org.isk.jvmhardcore.math.common.Operator; +import org.isk.jvmhardcore.math.parser.core.InputStreamReader; +import org.isk.jvmhardcore.math.parser.core.Parser; +import org.isk.jvmhardcore.math.parser.core.util.Ascii; + +public class MathParser extends Parser, EventType, MathTokenizer> { + + final private LinkedList postfixExpression; + final private Stack operatorStack; + + public MathParser(final InputStream inputStream) { + super(inputStream, Symbols.number()); + + this.postfixExpression = new LinkedList<>(); + this.operatorStack = new Stack<>(); + } + + @Override + public LinkedList parse() { + EventType eventType = null; + + boolean done = false; + while (!done) { + eventType = this.getNextEvent(); + + switch (eventType) { + case FLOAT: + this.postfixExpression.add(this.tokenizer.getFloat()); + break; + case INTEGER: + this.postfixExpression.add(this.tokenizer.getInteger()); + break; + case OPERATOR: + this.processOperator(this.tokenizer.getOperator()); + break; + case LEFT_PARENTHESIS: + this.operatorStack.push(this.tokenizer.getParenthesis()); + break; + case RIGHT_PARENTHESIS: + this.processRightParenthesis(this.tokenizer.getParenthesis()); + break; + case EOF: + this.tokenizer.checkEndOfFile(); + this.processEndOfFile(); + done = true; + break; + default: + System.err.println("Unexpected event."); + break; + } + } + + return this.postfixExpression; + } + + @Override + protected MathTokenizer newTokenizer(final String filename, final InputStream inputStream) { + return new MathTokenizer(filename, new InputStreamReader(inputStream)); + } + + @Override + protected void initProductionTable() { + this.table[Symbols.STREAM] = new Productions.Stream(); + this.table[Symbols.EOF] = new Productions.EndOfFile(); + this.table[Symbols.EXPRESSION] = new Productions.Expression(); + this.table[Symbols.OR_RIGHT_EXPRESSION] = new Productions.OrRightExpression(); + this.table[Symbols.OR_LEFT_PARENTHESIS] = new Productions.OrLeftParenthesis(); + this.table[Symbols.OR_RIGHT_PARENTHESIS] = new Productions.OrRightParenthesis(); + this.table[Symbols.LEFT_PARENTHESIS] = new Productions.LeftParenthesis(); + this.table[Symbols.RIGHT_PARENTHESIS] = new Productions.RightParenthesis(); + this.table[Symbols.NUMBER] = new Productions.Number(); + this.table[Symbols.OPERATOR] = new Productions.Operator(); + this.table[Symbols.WS] = new Productions.Whitespaces(); + } + + private void processOperator(ParsingOperator parsingOperator) { + // The current operator is of less priority than the previous one + if (!this.operatorStack.isEmpty() && parsingOperator.le(this.operatorStack.peek())) { + this.postfixExpression.add(ParsingOperator.getClean(this.operatorStack.pop())); + } + + this.operatorStack.push(parsingOperator); + } + + private void processRightParenthesis(ParsingOperator rightParenthsesis) { + // A closing parenthesis has been reached + // We add all operators until the opening parenthesis + while (!this.operatorStack.isEmpty() && this.operatorStack.peek() != ParsingOperator.LEFT_PARENTHESIS) { + this.postfixExpression.add(ParsingOperator.getClean(this.operatorStack.pop())); + } + + if (this.operatorStack.isEmpty() || this.operatorStack.pop() != ParsingOperator.LEFT_PARENTHESIS) { + throw new MathException("Missing left parenthesis."); + } + } + + private void processEndOfFile() { + while (!this.operatorStack.isEmpty()) { + this.postfixExpression.add(ParsingOperator.getClean(this.operatorStack.pop())); + } + } +} + +enum ParsingOperator { + PLUS(0), MINUS(0), TIMES(1), DIVIDE(1), LEFT_PARENTHESIS(100), RIGHT_PARENTHESIS(100); + + private int value; + + private ParsingOperator(int value) { + this.value = value; + } + + public boolean le(ParsingOperator po) { + return po.value != 100 && this.value <= po.value; + } + + public static ParsingOperator get(int operator) { + switch (operator) { + case Ascii.PLUS_SIGN: + return PLUS; + case Ascii.HYPHEN: + return MINUS; + case Ascii.ASTERISK: + return TIMES; + case Ascii.SLASH: + return DIVIDE; + case Ascii.LEFT_PARENTHESIS: + return LEFT_PARENTHESIS; + case Ascii.RIGHT_PARENTHESIS: + return RIGHT_PARENTHESIS; + default: + throw new MathException("Unexpected operator: " + String.valueOf(Character.toChars(operator))); + } + } + + public static Operator getClean(ParsingOperator operator) { + switch (operator) { + case PLUS: + return Operator.PLUS; + case MINUS: + return Operator.MINUS; + case TIMES: + return Operator.TIMES; + case DIVIDE: + return Operator.DIVIDE; + default: + throw new MathException("Unexpected operator: " + operator); + } + } +} diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/MathTokenizer.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/MathTokenizer.java similarity index 77% rename from 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/MathTokenizer.java rename to 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/MathTokenizer.java index d190ded..d494613 100644 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/MathTokenizer.java +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/MathTokenizer.java @@ -1,32 +1,32 @@ -package org.isk.jvmhardcore.mathparser; +package org.isk.jvmhardcore.math.parser; -import org.isk.jvmhardcore.mathparser.core.Reader; -import org.isk.jvmhardcore.mathparser.core.Tokenizer; -import org.isk.jvmhardcore.mathparser.core.util.Ascii; -import org.isk.jvmhardcore.mathparser.core.util.StringGenerator; +import org.isk.jvmhardcore.math.parser.core.Reader; +import org.isk.jvmhardcore.math.parser.core.Tokenizer; +import org.isk.jvmhardcore.math.parser.core.util.Ascii; +import org.isk.jvmhardcore.math.parser.core.util.StringGenerator; public class MathTokenizer extends Tokenizer { final private StringGenerator generator; - + public MathTokenizer(String filename, Reader reader) { super(filename, reader); - + this.generator = new StringGenerator(); } - public String getInteger() { + public Integer getInteger() { this.fillWithDigits(); if (!generator.isEmpty()) { this.rewind(); - return generator.toString(); + return Integer.valueOf(generator.toString()); } else { throw new ParserException("Expected: At least one Digit [0-9]."); } } - public String getFloat() { + public Double getFloat() { int character = this.fillWithDigits(); // getFloat() is called after isFloat() @@ -38,7 +38,7 @@ public String getFloat() { } this.rewind(); - return generator.toString(); + return Double.valueOf(generator.toString()); } private int fillWithDigits() { @@ -51,28 +51,29 @@ private int fillWithDigits() { } else { this.rewind(); } - + while (isDigit(character = this.next())) { generator.appendChar(character); } - + return character; } - public int getOperator() { + public ParsingOperator getOperator() { int character = this.next(); if (this.isOperator(character)) { - return character; + return ParsingOperator.get(character); } else { throw new ParserException("Expected: '+' or '-' or '*' or '/'."); } } - - public int getNext() { - return this.next(); + + public ParsingOperator getParenthesis() { + int character = this.next(); + return ParsingOperator.get(character); } - + public boolean isFloat() { this.mark(); @@ -122,18 +123,18 @@ public boolean isFloat() { } public boolean isOperator(int character) { - return character == Ascii.PLUS_SIGN - || character == Ascii.HYPHEN - || character == Ascii.ASTERIX - || character == Ascii.SLASH; + return character == Ascii.PLUS_SIGN + || character == Ascii.HYPHEN + || character == Ascii.ASTERISK + || character == Ascii.SLASH; } - + public boolean isOperator() { final int character = this.next(); this.rewind(); return isOperator(character); } - + public boolean isLeftParenthesis() { final int character = this.next(); this.rewind(); diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/Productions.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/Productions.java similarity index 95% rename from 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/Productions.java rename to 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/Productions.java index 42863ed..5dda9a6 100644 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/Productions.java +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/Productions.java @@ -1,11 +1,11 @@ -package org.isk.jvmhardcore.mathparser; +package org.isk.jvmhardcore.math.parser; import java.util.Stack; -import org.isk.jvmhardcore.mathparser.core.Production; +import org.isk.jvmhardcore.math.parser.core.Production; public class Productions { - + // stream = expression eof public static class Stream implements Production { public EventType produce(MathTokenizer tokenizer, @@ -32,7 +32,7 @@ public EventType produce(MathTokenizer tokenizer, return null; } } - + // orRightExpression = {ws operator orLeftParenthesis number orRightParenthesis ws} public static class OrRightExpression implements Production { public EventType produce(MathTokenizer tokenizer, @@ -52,7 +52,7 @@ public EventType produce(MathTokenizer tokenizer, return null; } } - + // number = integer | float // integer = repeatingDigit // float = oRepeatingDigit [dot] oRepeatingDigit @@ -132,9 +132,9 @@ public EventType produce(MathTokenizer tokenizer, } } - // ws = ? whitespaces ? + // ws = ? caractères d'espacement ? public static class Whitespaces implements Production { - public EventType produce(MathTokenizer tokenizer, + public EventType produce(MathTokenizer tokenizer, Production[] table, Stack> productionStack) { tokenizer.consumeUnprintables(); @@ -142,9 +142,9 @@ public EventType produce(MathTokenizer tokenizer, } } - // eof = ? end of stream ? + // eof = ? fin du flux ? public static class EndOfFile implements Production { - public EventType produce(MathTokenizer tokenizer, + public EventType produce(MathTokenizer tokenizer, Production[] table, Stack> productionStack) { return EventType.EOF; diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/Symbols.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/Symbols.java similarity index 93% rename from 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/Symbols.java rename to 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/Symbols.java index f4530f4..cd2bb53 100644 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/Symbols.java +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/Symbols.java @@ -1,4 +1,4 @@ -package org.isk.jvmhardcore.mathparser; +package org.isk.jvmhardcore.math.parser; public class Symbols { private static int number = 0; @@ -8,17 +8,17 @@ public class Symbols { final public static int EXPRESSION = number++; final public static int OR_RIGHT_EXPRESSION = number++; - + final public static int OR_LEFT_PARENTHESIS = number++; final public static int OR_RIGHT_PARENTHESIS = number++; final public static int LEFT_PARENTHESIS = number++; final public static int RIGHT_PARENTHESIS = number++; - + final public static int NUMBER = number++; final public static int OPERATOR = number++; final public static int WS = number++; - + private Symbols() { } diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/InputStreamReader.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/InputStreamReader.java similarity index 97% rename from 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/InputStreamReader.java rename to 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/InputStreamReader.java index 916e8a6..cb73f50 100644 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/InputStreamReader.java +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/InputStreamReader.java @@ -1,9 +1,9 @@ -package org.isk.jvmhardcore.mathparser.core; +package org.isk.jvmhardcore.math.parser.core; import java.io.IOException; import java.io.InputStream; -import org.isk.jvmhardcore.mathparser.core.util.Ascii; +import org.isk.jvmhardcore.math.parser.core.util.Ascii; public class InputStreamReader implements Reader { /** diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/Parser.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/Parser.java similarity index 96% rename from 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/Parser.java rename to 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/Parser.java index cbe46e9..b4fca96 100644 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/Parser.java +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/Parser.java @@ -1,4 +1,4 @@ -package org.isk.jvmhardcore.mathparser.core; +package org.isk.jvmhardcore.math.parser.core; import java.io.InputStream; import java.util.Stack; diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/Production.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/Production.java similarity index 77% rename from 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/Production.java rename to 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/Production.java index 3250717..95b3996 100644 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/Production.java +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/Production.java @@ -1,11 +1,10 @@ -package org.isk.jvmhardcore.mathparser.core; +package org.isk.jvmhardcore.math.parser.core; import java.util.Stack; public interface Production { /** - * Adds Productions to the productions table (non-terminal symbol) or returns - * an event (terminal symbol). + * Adds Productions to the productions table (non-terminal symbol) or returns an event (terminal symbol). * * @param tokenizer * @param table diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/Reader.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/Reader.java similarity index 92% rename from 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/Reader.java rename to 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/Reader.java index c9a30cb..bc76ea2 100644 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/Reader.java +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/Reader.java @@ -1,4 +1,4 @@ -package org.isk.jvmhardcore.mathparser.core; +package org.isk.jvmhardcore.math.parser.core; public interface Reader { /** diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/ReaderException.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/ReaderException.java similarity index 92% rename from 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/ReaderException.java rename to 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/ReaderException.java index 6387237..798b598 100644 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/ReaderException.java +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/ReaderException.java @@ -1,4 +1,4 @@ -package org.isk.jvmhardcore.mathparser.core; +package org.isk.jvmhardcore.math.parser.core; public class ReaderException extends RuntimeException { diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/Tokenizer.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/Tokenizer.java similarity index 93% rename from 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/Tokenizer.java rename to 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/Tokenizer.java index 78c227b..8dff5b3 100644 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/Tokenizer.java +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/Tokenizer.java @@ -1,6 +1,6 @@ -package org.isk.jvmhardcore.mathparser.core; +package org.isk.jvmhardcore.math.parser.core; -import org.isk.jvmhardcore.mathparser.core.util.Ascii; +import org.isk.jvmhardcore.math.parser.core.util.Ascii; public abstract class Tokenizer { final private String filename; @@ -32,8 +32,7 @@ public static boolean isAsciiLetter(int character) { } /** - * If the next character to be read is not the end of the file/stream a - * ParserException is thrown. + * If the next character to be read is not the end of the file/stream a ParserException is thrown. */ public void checkEndOfFile() { int character = this.next(); diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/util/Ascii.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/util/Ascii.java similarity index 98% rename from 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/util/Ascii.java rename to 03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/util/Ascii.java index e6e2ab5..7a59d74 100644 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/util/Ascii.java +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/util/Ascii.java @@ -1,4 +1,4 @@ -package org.isk.jvmhardcore.mathparser.core.util; +package org.isk.jvmhardcore.math.parser.core.util; public interface Ascii { public static final byte EOF = -1; @@ -16,7 +16,7 @@ public interface Ascii { public static final char SINGLE_QUOTE = 0x27; // ' public static final char LEFT_PARENTHESIS = 0x28; // ( public static final char RIGHT_PARENTHESIS = 0x29; // ) - public static final char ASTERIX = 0x2A; // * + public static final char ASTERISK = 0x2A; // * public static final char PLUS_SIGN = 0x2B; // + public static final char COMMA = 0x2C; // , public static final char HYPHEN = 0x2D; // - diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/util/StringGenerator.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/util/StringGenerator.java new file mode 100644 index 0000000..5a96535 --- /dev/null +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/parser/core/util/StringGenerator.java @@ -0,0 +1,74 @@ +package org.isk.jvmhardcore.math.parser.core.util; + +public class StringGenerator { + private StringBuilder builder; + + public StringGenerator() { + super(); + this.builder = new StringBuilder(); + } + + /** + * Appends the specified string to this character sequence.
+ * The characters of the String argument are appended, in order, increasing the length of this sequence by the length + * of the argument. If string is null, then the four characters "null" are appended.
+ * Let n be the length of this character sequence just prior to execution of the append method. Then the character at + * index k in the new character sequence is equal to the character at index k in the old character sequence, if k is + * less than n; otherwise, it is equal to the character at index k-n in the argument string. + * + * @param string + * is the string to append + */ + public void append(final String string) { + this.builder.append(string); + } + + /** + * Appends the string representation of the char argument to this sequence.
+ * The argument is appended to the contents of this sequence. The length of this sequence increases by 1.
+ * The overall effect is exactly as if the argument were converted to a string by the method String.valueOf(char), and + * the character in that string were then appended to this character sequence. + * + * @param character + * is the character to append + */ + public void append(final char character) { + this.builder.append(character); + } + + /** + * Appends the string representation of the integer argument to this sequence.
+ * The argument is appended to the contents of this sequence. The length of this sequence increases by 1.
+ * The overall effect is exactly as if the argument were converted to a string by the method + * String.valueOf((char)integer), and the character in that string were then appended to this character sequence. + * + * @param integer + * is the character to append + */ + public void appendChar(final int integer) { + this.builder.append((char) integer); + } + + /** + * Whether or not the sequence is null. + * + * @return true if the sequence is null, otherwise false. + */ + public boolean isEmpty() { + return this.builder.length() == 0; + } + + /** + * Returns a string representing the data in this sequence and reset the sequence. A new String object is allocated + * and initialized to contain the character sequence currently represented by this object. This String is then + * returned. Subsequent changes to this sequence do not affect the contents of the String. + * + * @return the string built. + */ + public String toString() { + String string = this.builder.toString(); + this.builder.setLength(0); + return string; + } + +} diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/solver/MathSolver.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/solver/MathSolver.java new file mode 100644 index 0000000..54da3b8 --- /dev/null +++ b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/math/solver/MathSolver.java @@ -0,0 +1,100 @@ +package org.isk.jvmhardcore.math.solver; + +import java.util.LinkedList; +import java.util.Stack; + +import org.isk.jvmhardcore.math.MathException; +import org.isk.jvmhardcore.math.common.Operator; + +public class MathSolver { + private final Stack processingStack; + + public MathSolver() { + this.processingStack = new Stack<>(); + } + + @SuppressWarnings("unchecked") + public T resolve(LinkedList postfixExpression) { + this.processingStack.clear(); + + for (Object object : postfixExpression) { + if (object instanceof Operator) { + this.compute((Operator) object); + } else { + this.processingStack.push((Number) object); + } + } + + return (T) this.processingStack.pop(); + } + + private void compute(Operator operator) { + final Number right = this.processingStack.pop(); + final Number left = this.processingStack.pop(); + + switch (operator) { + case PLUS: + this.add(left, right); + break; + case MINUS: + this.subtract(left, right); + break; + case TIMES: + this.multiply(left, right); + break; + case DIVIDE: + this.divide(left, right); + break; + default: + throw new MathException("Unknown operator: " + operator); + } + } + + private void add(Number left, Number right) { + Number n = null; + + if (left instanceof Integer && right instanceof Integer) { + n = left.intValue() + right.intValue(); + } else { + n = left.doubleValue() + right.doubleValue(); + } + + this.processingStack.push(n); + } + + private void subtract(Number left, Number right) { + Number n = null; + + if (left instanceof Integer && right instanceof Integer) { + n = left.intValue() - right.intValue(); + } else { + n = left.doubleValue() - right.doubleValue(); + } + + this.processingStack.push(n); + } + + private void multiply(Number left, Number right) { + Number n = null; + + if (left instanceof Integer && right instanceof Integer) { + n = left.intValue() * right.intValue(); + } else { + n = left.doubleValue() * right.doubleValue(); + } + + this.processingStack.push(n); + } + + private void divide(Number left, Number right) { + Number n = null; + + if (left instanceof Integer && right instanceof Integer && (left.intValue() % right.intValue()) == 0) { + n = left.intValue() / right.intValue(); + } else { + n = left.doubleValue() / right.doubleValue(); + } + + this.processingStack.push(n); + } +} diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/MathParser.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/MathParser.java deleted file mode 100644 index ccccd90..0000000 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/MathParser.java +++ /dev/null @@ -1,75 +0,0 @@ -package org.isk.jvmhardcore.mathparser; - -import java.io.InputStream; -import java.util.LinkedList; - -import org.isk.jvmhardcore.mathparser.core.InputStreamReader; -import org.isk.jvmhardcore.mathparser.core.Parser; - -public class MathParser extends Parser, EventType, MathTokenizer> { - - private LinkedList tokens; - - public MathParser(final InputStream inputStream) { - super(inputStream, Symbols.number()); - - this.tokens = new LinkedList<>(); - } - - @Override - public LinkedList parse() { - EventType eventType = null; - - boolean done = false; - while (!done) { - eventType = this.getNextEvent(); - - switch (eventType) { - case FLOAT: - this.tokens.add(this.tokenizer.getFloat()); - break; - case INTEGER: - this.tokens.add(this.tokenizer.getInteger()); - break; - case OPERATOR: - this.tokens.add(String.valueOf(Character.toChars(this.tokenizer.getOperator()))); - break; - case LEFT_PARENTHESIS: - this.tokens.add(String.valueOf(Character.toChars(this.tokenizer.getNext()))); - break; - case RIGHT_PARENTHESIS: - this.tokens.add(String.valueOf(Character.toChars(this.tokenizer.getNext()))); - break; - case EOF: - this.tokenizer.checkEndOfFile(); - done = true; - break; - default: - System.err.println("Unexpected event."); - break; - } - } - - return this.tokens; - } - - @Override - protected MathTokenizer newTokenizer(final String filename, final InputStream inputStream) { - return new MathTokenizer(filename, new InputStreamReader(inputStream)); - } - - @Override - protected void initProductionTable() { - this.table[Symbols.STREAM] = new Productions.Stream(); - this.table[Symbols.EOF] = new Productions.EndOfFile(); - this.table[Symbols.EXPRESSION] = new Productions.Expression(); - this.table[Symbols.OR_RIGHT_EXPRESSION] = new Productions.OrRightExpression(); - this.table[Symbols.OR_LEFT_PARENTHESIS] = new Productions.OrLeftParenthesis(); - this.table[Symbols.OR_RIGHT_PARENTHESIS] = new Productions.OrRightParenthesis(); - this.table[Symbols.LEFT_PARENTHESIS] = new Productions.LeftParenthesis(); - this.table[Symbols.RIGHT_PARENTHESIS] = new Productions.RightParenthesis(); - this.table[Symbols.NUMBER] = new Productions.Number(); - this.table[Symbols.OPERATOR] = new Productions.Operator(); - this.table[Symbols.WS] = new Productions.Whitespaces(); - } -} diff --git a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/util/StringGenerator.java b/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/util/StringGenerator.java deleted file mode 100644 index c0312fd..0000000 --- a/03_projects/mathparser/01_src/main/java/org/isk/jvmhardcore/mathparser/core/util/StringGenerator.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.isk.jvmhardcore.mathparser.core.util; - -public class StringGenerator { - private StringBuilder builder; - - public StringGenerator() { - super(); - this.builder = new StringBuilder(); - } - - /** - * Appends the specified string to this character sequence.
- * The characters of the String argument are appended, in order, increasing - * the length of this sequence by the length of the argument. If string is - * null, then the four characters "null" are appended.
- * Let n be the length of this character sequence just prior to execution of - * the append method. Then the character at index k in the new character - * sequence is equal to the character at index k in the old character - * sequence, if k is less than n; otherwise, it is equal to the character at - * index k-n in the argument string. - * - * @param string is the string to append - */ - public void append(final String string) { - this.builder.append(string); - } - - /** - * Appends the string representation of the char argument to this sequence.
- * The argument is appended to the contents of this sequence. The length of - * this sequence increases by 1.
- * The overall effect is exactly as if the argument were converted to a - * string by the method String.valueOf(char), and the character in that - * string were then appended to this character sequence. - * - * @param character is the character to append - */ - public void append(final char character) { - this.builder.append(character); - } - - /** - * Appends the string representation of the integer argument to this sequence.
- * The argument is appended to the contents of this sequence. The length of - * this sequence increases by 1.
- * The overall effect is exactly as if the argument were converted to a - * string by the method String.valueOf((char)integer), and the character in that - * string were then appended to this character sequence. - * - * @param integer is the character to append - */ - public void appendChar(final int integer) { - this.builder.append((char)integer); - } - - /** - * Whether or not the sequence is null. - * @return true if the sequence is null, otherwise false. - */ - public boolean isEmpty() { - return this.builder.length() == 0; - } - - /** - * Returns a string representing the data in this sequence and reset the - * sequence. A new String object is allocated and initialized to contain the - * character sequence currently represented by this object. This String is - * then returned. Subsequent changes to this sequence do not affect the - * contents of the String. - * - * @return the string built. - */ - public String toString() { - String string = this.builder.toString(); - this.builder.setLength(0); - return string; - } -} diff --git a/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/MathParserTest.java b/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/MathParserTest.java new file mode 100644 index 0000000..dff0542 --- /dev/null +++ b/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/MathParserTest.java @@ -0,0 +1,217 @@ +package org.isk.jvmhardcore.math.parser; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.LinkedList; + +import org.isk.jvmhardcore.math.MathException; +import org.isk.jvmhardcore.math.common.Operator; +import org.isk.jvmhardcore.math.parser.MathParser; +import org.isk.jvmhardcore.math.parser.core.Tokenizer.ParserException; +import org.junit.Assert; +import org.junit.Test; + +public class MathParserTest { + + private LinkedList expected(final Object ... objects) { + final LinkedList list = new LinkedList<>(); + + for (Object o : objects) { + list.add(o); + } + + return list; + } + + private void test(final String expression, final LinkedList expected) { + final InputStream inputStream = new ByteArrayInputStream(expression.getBytes()); + final MathParser parser = new MathParser(inputStream); + final LinkedList tokens = parser.parse(); + Assert.assertEquals(expected, tokens); + } + + @Test + public void addition() { + this.test("2+5", this.expected(2, 5, Operator.PLUS)); + } + + @Test + public void subtraction() { + this.test("2-5", this.expected(2, 5, Operator.MINUS)); + } + + @Test + public void multiplication() { + this.test("2*5", this.expected(2, 5, Operator.TIMES)); + } + + @Test + public void division() { + this.test("2/5", this.expected(2, 5, Operator.DIVIDE)); + } + @Test + public void operatorExpected() { + final String string = "3a+5"; + final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); + final MathParser parser = new MathParser(inputStream); + + try { + parser.parse(); + Assert.fail(); + } catch (ParserException e) { + Assert.assertEquals("Line 1, column 2 - Expected: End of file. Got: a", e.getMessage()); + } + } + + @Test + public void digitExpected() { + final String string = "2+,5"; + final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); + final MathParser parser = new MathParser(inputStream); + + try { + parser.parse(); + Assert.fail(); + } catch (ParserException e) { + Assert.assertEquals("Line 1, column 3 - Expected: At least one Digit [0-9]. Got: ,", e.getMessage()); + } + } + + @Test + public void getNumber0() { + this.test("23+5", this.expected(23, 5, Operator.PLUS)); + } + + @Test + public void getNumber1() { + this.test("2+54", this.expected(2, 54, Operator.PLUS)); + } + + @Test + public void getNumber2() { + this.test("23+54", this.expected(23, 54, Operator.PLUS)); + } + + @Test + public void getFloat0() { + this.test("1.2+5", this.expected(1.2, 5, Operator.PLUS)); + } + + @Test + public void getFloat1() { + this.test("1+56.32", this.expected(1, 56.32, Operator.PLUS)); + } + + @Test + public void getFloat2() { + this.test("1.2+56.32", this.expected(1.2, 56.32, Operator.PLUS)); + } + + @Test + public void getNegativeInteger() { + this.test("-23+-54", this.expected(-23, -54, Operator.PLUS)); + } + + @Test + public void getNegativeFloat() { + this.test("-1.2+-56.32", this.expected(-1.2, -56.32, Operator.PLUS)); + } + + @Test + public void expressionWithUnprintables() { + this.test(" \t1.2\u0003 \u0020+\n5 ", this.expected(1.2, 5, Operator.PLUS)); + } + + @Test + public void variableNumberOfOperands() { + this.test("2 + 3 - 4 * 5 / -17", + this.expected(2, 3, Operator.PLUS, 4, 5, Operator.TIMES, -17, Operator.DIVIDE, Operator.MINUS)); + } + + @Test + public void expressionWithParenthesis0() { + this.test("(2 + 5) - 2", this.expected(2, 5, Operator.PLUS, 2, Operator.MINUS)); + } + + @Test + public void expressionWithParenthesis1() { + this.test("((2 + 5) * 2) - 2", this.expected(2, 5, Operator.PLUS, 2, Operator.TIMES, 2, Operator.MINUS)); + } + + @Test + public void expressionWithParenthesis2() { + this.test("((2 + 5) * (10 - 8)) - 2", + this.expected(2, 5, Operator.PLUS, 10, 8, Operator.MINUS, Operator.TIMES, 2, Operator.MINUS)); + } + + @Test + public void expressionWithParenthesis3() { + this.test("((2 + 5) * (10 - 8) - 2) + 3", + this.expected(2, 5, Operator.PLUS, 10, 8, Operator.MINUS, Operator.TIMES, 2, Operator.MINUS, 3, Operator.PLUS)); + } + + @Test + public void expressionWithParenthesis4() { + this.test("((2 + 5) - 2 * (10 - 8)) + 3", + this.expected(2, 5, Operator.PLUS, 2, 10, 8, Operator.MINUS, Operator.TIMES, Operator.MINUS, 3, Operator.PLUS)); + } + + @Test + public void expressionWithParenthesis5() { + this.test("(2 * (2 + 5) - (10 - 8)) + 3", + this.expected(2, 2, 5, Operator.PLUS, Operator.TIMES, 10, 8, Operator.MINUS, Operator.MINUS, 3, Operator.PLUS)); + } + + @Test + public void expressionWithParenthesis6() { + this.test("4 - (2 + 5) - 2", this.expected(4, 2, 5, Operator.PLUS, Operator.MINUS, 2, Operator.MINUS)); + } + + @Test + public void wrongParensCount0() { + final String string = "2 * (3 + 4"; + + try { + this.test(string, null); + Assert.fail(); + } catch (MathException e) { + Assert.assertEquals("Unexpected operator: LEFT_PARENTHESIS", e.getMessage()); + } + } + + @Test + public void wrongParensCount1() { + final String string = "2 * (3 + 4))"; + + try { + this.test(string, null); + Assert.fail(); + } catch (MathException e) { + Assert.assertEquals("Missing left parenthesis.", e.getMessage()); + } + } + + @Test + public void wrongParensCount2() { + final String string = "2 * ((3 + 4)"; + + try { + this.test(string, null); + Assert.fail(); + } catch (MathException e) { + Assert.assertEquals("Unexpected operator: LEFT_PARENTHESIS", e.getMessage()); + } + } + + @Test + public void wrongParensCount3() { + final String string = "2 * (3 + 4))"; + + try { + this.test(string, null); + Assert.fail(); + } catch (MathException e) { + Assert.assertEquals("Missing left parenthesis.", e.getMessage()); + } + } +} diff --git a/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/MathTokenizerTest.java b/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/MathTokenizerTest.java similarity index 83% rename from 03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/MathTokenizerTest.java rename to 03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/MathTokenizerTest.java index 94a1168..0e1577d 100644 --- a/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/MathTokenizerTest.java +++ b/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/MathTokenizerTest.java @@ -1,13 +1,12 @@ -package org.isk.jvmhardcore.mathparser; +package org.isk.jvmhardcore.math.parser; import java.io.ByteArrayInputStream; import java.io.InputStream; import junit.framework.Assert; -import org.isk.jvmhardcore.mathparser.core.InputStreamReader; -import org.isk.jvmhardcore.mathparser.core.Tokenizer.ParserException; -import org.isk.jvmhardcore.mathparser.core.util.Ascii; +import org.isk.jvmhardcore.math.parser.core.InputStreamReader; +import org.isk.jvmhardcore.math.parser.core.Tokenizer.ParserException; import org.junit.Test; public class MathTokenizerTest { @@ -17,13 +16,13 @@ public void getEachToken() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String digitOne = tokenizer.getInteger(); - final int operator = tokenizer.getOperator(); - final String digitTwo = tokenizer.getInteger(); + final int digitOne = tokenizer.getInteger(); + final ParsingOperator operator = tokenizer.getOperator(); + final int digitTwo = tokenizer.getInteger(); - Assert.assertEquals("24", digitOne); - Assert.assertEquals(Ascii.PLUS_SIGN, operator); - Assert.assertEquals("53", digitTwo); + Assert.assertEquals(24, digitOne); + Assert.assertEquals(ParsingOperator.PLUS, operator); + Assert.assertEquals(53, digitTwo); } @Test @@ -66,8 +65,8 @@ public void addition() { final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); tokenizer.getInteger(); - final int operator = tokenizer.getOperator(); - Assert.assertEquals(Ascii.PLUS_SIGN, operator); + final ParsingOperator operator = tokenizer.getOperator(); + Assert.assertEquals(ParsingOperator.PLUS, operator); } @Test @@ -77,8 +76,8 @@ public void subtraction() { final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); tokenizer.getInteger(); - final int operator = tokenizer.getOperator(); - Assert.assertEquals(Ascii.HYPHEN, operator); + final ParsingOperator operator = tokenizer.getOperator(); + Assert.assertEquals(ParsingOperator.MINUS, operator); } @Test @@ -88,8 +87,8 @@ public void multiplication() { final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); tokenizer.getInteger(); - final int operator = tokenizer.getOperator(); - Assert.assertEquals(Ascii.ASTERIX, operator); + final ParsingOperator operator = tokenizer.getOperator(); + Assert.assertEquals(ParsingOperator.TIMES, operator); } @Test @@ -99,8 +98,8 @@ public void division() { final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); tokenizer.getInteger(); - final int operator = tokenizer.getOperator(); - Assert.assertEquals(Ascii.SLASH, operator); + final ParsingOperator operator = tokenizer.getOperator(); + Assert.assertEquals(ParsingOperator.DIVIDE, operator); } @Test @@ -109,9 +108,9 @@ public void getInteger0() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String num = tokenizer.getInteger(); + final int num = tokenizer.getInteger(); - Assert.assertEquals(string, num); + Assert.assertEquals(124, num); } @Test @@ -120,9 +119,9 @@ public void getInteger1() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String num = tokenizer.getInteger(); + final int num = tokenizer.getInteger(); - Assert.assertEquals("124", num); + Assert.assertEquals(124, num); } @Test @@ -131,9 +130,9 @@ public void getInteger2() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String num = tokenizer.getInteger(); + final int num = tokenizer.getInteger(); - Assert.assertEquals(string, num); + Assert.assertEquals(-124, num); } @Test @@ -263,9 +262,9 @@ public void getFloat0() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String num = tokenizer.getFloat(); + final double num = tokenizer.getFloat(); - Assert.assertEquals(string, num); + Assert.assertEquals(1.1, num); } @Test @@ -274,9 +273,9 @@ public void getFloat1() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String num = tokenizer.getFloat(); + final double num = tokenizer.getFloat(); - Assert.assertEquals(string, num); + Assert.assertEquals(.567, num); } @Test @@ -285,9 +284,9 @@ public void getFloat2() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String num = tokenizer.getFloat(); + final double num = tokenizer.getFloat(); - Assert.assertEquals(string, num); + Assert.assertEquals(23., num); } @Test @@ -296,9 +295,9 @@ public void getFloat3() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String num = tokenizer.getFloat(); + final double num = tokenizer.getFloat(); - Assert.assertEquals("1.1", num); + Assert.assertEquals(1.1, num); } @Test @@ -307,9 +306,9 @@ public void getFloat4() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String num = tokenizer.getFloat(); + final double num = tokenizer.getFloat(); - Assert.assertEquals(".567", num); + Assert.assertEquals(.567, num); } @Test @@ -318,9 +317,9 @@ public void getFloat5() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String num = tokenizer.getFloat(); + final double num = tokenizer.getFloat(); - Assert.assertEquals("23.", num); + Assert.assertEquals(23., num); } @Test @@ -329,9 +328,9 @@ public void getFloat6() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String num = tokenizer.getFloat(); + final double num = tokenizer.getFloat(); - Assert.assertEquals(string, num); + Assert.assertEquals(-1.1, num); } @Test @@ -340,9 +339,9 @@ public void getFloat7() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String num = tokenizer.getFloat(); + final double num = tokenizer.getFloat(); - Assert.assertEquals(string, num); + Assert.assertEquals(-.567, num); } @Test @@ -351,8 +350,8 @@ public void getFloat8() { final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); final MathTokenizer tokenizer = new MathTokenizer(null, new InputStreamReader(inputStream)); - final String num = tokenizer.getFloat(); + final double num = tokenizer.getFloat(); - Assert.assertEquals(string, num); + Assert.assertEquals(-23., num); } } diff --git a/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/core/InputStreamReaderTest.java b/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/core/InputStreamReaderTest.java similarity index 98% rename from 03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/core/InputStreamReaderTest.java rename to 03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/core/InputStreamReaderTest.java index 37c4cc6..7e63f7d 100644 --- a/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/core/InputStreamReaderTest.java +++ b/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/core/InputStreamReaderTest.java @@ -1,10 +1,12 @@ -package org.isk.jvmhardcore.mathparser.core; +package org.isk.jvmhardcore.math.parser.core; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import org.isk.jvmhardcore.mathparser.core.util.Ascii; +import org.isk.jvmhardcore.math.parser.core.InputStreamReader; +import org.isk.jvmhardcore.math.parser.core.ReaderException; +import org.isk.jvmhardcore.math.parser.core.util.Ascii; import org.isk.jvmhardcore.test.Reflection; import org.junit.Assert; import org.junit.Test; diff --git a/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/core/TokenizerTest.java b/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/core/TokenizerTest.java similarity index 86% rename from 03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/core/TokenizerTest.java rename to 03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/core/TokenizerTest.java index 05f5739..3b6a3a8 100644 --- a/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/core/TokenizerTest.java +++ b/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/parser/core/TokenizerTest.java @@ -1,10 +1,13 @@ -package org.isk.jvmhardcore.mathparser.core; +package org.isk.jvmhardcore.math.parser.core; import java.io.ByteArrayInputStream; import java.io.InputStream; -import org.isk.jvmhardcore.mathparser.core.Tokenizer.ParserException; -import org.isk.jvmhardcore.mathparser.core.util.Ascii; +import org.isk.jvmhardcore.math.parser.core.InputStreamReader; +import org.isk.jvmhardcore.math.parser.core.Reader; +import org.isk.jvmhardcore.math.parser.core.Tokenizer; +import org.isk.jvmhardcore.math.parser.core.Tokenizer.ParserException; +import org.isk.jvmhardcore.math.parser.core.util.Ascii; import org.junit.Assert; import org.junit.Test; diff --git a/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/solver/MathSolverTest.java b/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/solver/MathSolverTest.java new file mode 100644 index 0000000..a2c4c5f --- /dev/null +++ b/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/math/solver/MathSolverTest.java @@ -0,0 +1,217 @@ +package org.isk.jvmhardcore.math.solver; + +import java.util.LinkedList; + +import org.isk.jvmhardcore.math.common.Operator; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class MathSolverTest { + private static MathSolver SOLVER; + + @BeforeClass + public static void init() { + SOLVER = new MathSolver(); + } + + private LinkedList postfixExpression(final Object... objects) { + final LinkedList list = new LinkedList<>(); + + for (Object o : objects) { + list.add(o); + } + + return list; + } + + private void test(final LinkedList postfixExpression, final int expectedResult) { + final int result = SOLVER.resolve(postfixExpression); + Assert.assertEquals(expectedResult, result); + } + + private void test(final LinkedList postfixExpression, final double expectedResult) { + final double result = SOLVER.resolve(postfixExpression); + Assert.assertEquals(expectedResult, result, 0.001); + } + + @Test + public void resolve0() { + // 2 + 5 + this.test(this.postfixExpression(2, 5, Operator.PLUS), 7); + } + + @Test + public void resolve1() { + // 2.5 + 5 + this.test(this.postfixExpression(2.5, 5, Operator.PLUS), 7.5); + } + + @Test + public void resolve2() { + // 2 + 5.67 + this.test(this.postfixExpression(2, 5.67, Operator.PLUS), 7.67); + } + + @Test + public void resolve3() { + // 2.897 + 5.67 + this.test(this.postfixExpression(2.897, 5.67, Operator.PLUS), 8.567); + } + + @Test + public void resolve4() { + // 2 - 5 + this.test(this.postfixExpression(2, 5, Operator.MINUS), -3); + } + + @Test + public void resolve5() { + // 5 - 2 + this.test(this.postfixExpression(5, 2, Operator.MINUS), 3); + } + + @Test + public void resolve6() { + // 5.1 - 2 + this.test(this.postfixExpression(5.1, 2, Operator.MINUS), 3.1); + } + + @Test + public void resolve7() { + // 5 - 2.27 + this.test(this.postfixExpression(5, 2.27, Operator.MINUS), 2.73); + } + + @Test + public void resolve8() { + // 5.698 - 2.277 + this.test(this.postfixExpression(5.698, 2.277, Operator.MINUS), 3.421); + } + + @Test + public void resolve9() { + // 2 * 5 + this.test(this.postfixExpression(2, 5, Operator.TIMES), 10); + } + + @Test + public void resolve10() { + // 2.56 * 5 + this.test(this.postfixExpression(2.56, 5, Operator.TIMES), 12.8); + } + + @Test + public void resolve11() { + // 2 * 5.25 + this.test(this.postfixExpression(2, 5.25, Operator.TIMES), 10.50); + } + + @Test + public void resolve12() { + // 2.75 * 5.25 + this.test(this.postfixExpression(2.75, 5.25, Operator.TIMES), 14.4375); + } + + @Test + public void resolve13() { + // 4 / 2 + this.test(this.postfixExpression(4, 2, Operator.DIVIDE), 2); + } + + @Test + public void resolve14() { + // 5.5 / 2 + this.test(this.postfixExpression(5.5, 2, Operator.DIVIDE), 2.75); + } + + @Test + public void resolve15() { + // 5 / 2.5 + this.test(this.postfixExpression(5, 2.5, Operator.DIVIDE), 2.0); + } + + @Test + public void resolve16() { + // 5.5 / 2.5 + this.test(this.postfixExpression(5.5, 2.5, Operator.DIVIDE), 2.2); + } + + @Test + public void resolve17() { + // 0 / 2.5 + this.test(this.postfixExpression(0, 2.5, Operator.DIVIDE), 0.0); + } + + @Test + public void resolve18() { + // 10 / 0 + try { + SOLVER.resolve(this.postfixExpression(10, 0, Operator.DIVIDE)); + Assert.fail(); + } catch (ArithmeticException e) { + Assert.assertEquals("class java.lang.ArithmeticException: / by zero", e.getClass() + ": " + e.getMessage()); + } + } + + // ================================================================================================================== + + @Test + public void resolve19() { + // 0.5 + -50.0 * 32.756 + this.test(this.postfixExpression(.5, -50., 32.756, Operator.TIMES, Operator.PLUS), -1637.3); + } + + @Test + public void resolve20() { + // 2 + 3 - 4 * 5 / -17 + this.test(this.postfixExpression(2, 3, Operator.PLUS, 4, 5, Operator.TIMES, -17, Operator.DIVIDE, Operator.MINUS), + 6.1764); + } + + @Test + public void resolve21() { + // 2 + 5 - 2 + this.test(this.postfixExpression(2, 5, Operator.PLUS, 2, Operator.MINUS), 5); + } + + @Test + public void resolve22() { + // ((2 + 5) * 2) - 2 + this.test(this.postfixExpression(2, 5, Operator.PLUS, 2, Operator.TIMES, 2, Operator.MINUS), 12); + } + + @Test + public void resolve23() { + // ((2 + 5) * (10 - 8)) - 2 + this.test(this.postfixExpression(2, 5, Operator.PLUS, 10, 8, Operator.MINUS, Operator.TIMES, 2, Operator.MINUS), + 12); + } + + @Test + public void resolve24() { + // ((2 + 5) * (10 - 8) - 2) + 3 + this.test(this.postfixExpression(2, 5, Operator.PLUS, 10, 8, Operator.MINUS, Operator.TIMES, 2, Operator.MINUS, + 3, Operator.PLUS), 15); + } + + @Test + public void resolve25() { + // ((2 + 5) - 2 * (10 - 8)) + 3 + this.test(this.postfixExpression(2, 5, Operator.PLUS, 2, 10, 8, Operator.MINUS, Operator.TIMES, Operator.MINUS, + 3, Operator.PLUS), 6); + } + + @Test + public void resolve26() { + // (2 * (2 + 5) - (10 - 8)) + 3 + this.test(this.postfixExpression(2, 2, 5, Operator.PLUS, Operator.TIMES, 10, 8, Operator.MINUS, Operator.MINUS, + 3, Operator.PLUS), 15); + } + + @Test + public void resolve27() { + // 4 - (2 + 5) - 2 + this.test(this.postfixExpression(4, 2, 5, Operator.PLUS, Operator.MINUS, 2, Operator.MINUS), -5); + } +} \ No newline at end of file diff --git a/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/MathParserTest.java b/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/MathParserTest.java deleted file mode 100644 index 020cad7..0000000 --- a/03_projects/mathparser/01_src/test/java/org/isk/jvmhardcore/mathparser/MathParserTest.java +++ /dev/null @@ -1,161 +0,0 @@ -package org.isk.jvmhardcore.mathparser; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.util.LinkedList; - -import org.isk.jvmhardcore.mathparser.core.Tokenizer.ParserException; -import org.junit.Assert; -import org.junit.Test; - -public class MathParserTest { - - private LinkedList expected(final String ... strings) { - final LinkedList list = new LinkedList<>(); - - for (String s : strings) { - list.add(s); - } - - return list; - } - - private void test(final String expression, final LinkedList expected) { - final InputStream inputStream = new ByteArrayInputStream(expression.getBytes()); - final MathParser parser = new MathParser(inputStream); - final LinkedList tokens = parser.parse(); - Assert.assertEquals(expected, tokens); - } - - @Test - public void addition() { - this.test("2+5", this.expected("2", "+", "5")); - } - - @Test - public void subtraction() { - this.test("2-5", this.expected("2", "-", "5")); - } - - @Test - public void multiplication() { - this.test("2*5", this.expected("2", "*", "5")); - } - - @Test - public void division() { - this.test("2/5", this.expected("2", "/", "5")); - } - @Test - public void operatorExpected() { - final String string = "3a+5"; - final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); - final MathParser parser = new MathParser(inputStream); - - try { - parser.parse(); - Assert.fail(); - } catch (ParserException e) { - Assert.assertEquals("Line 1, column 2 - Expected: End of file. Got: a", e.getMessage()); - } - } - - @Test - public void digitExpected() { - final String string = "2+,5"; - final InputStream inputStream = new ByteArrayInputStream(string.getBytes()); - final MathParser parser = new MathParser(inputStream); - - try { - parser.parse(); - Assert.fail(); - } catch (ParserException e) { - Assert.assertEquals("Line 1, column 3 - Expected: At least one Digit [0-9]. Got: ,", e.getMessage()); - } - } - - @Test - public void getNumber0() { - this.test("23+5", this.expected("23", "+", "5")); - } - - @Test - public void getNumber1() { - this.test("2+54", this.expected("2", "+", "54")); - } - - @Test - public void getNumber2() { - this.test("23+54", this.expected("23", "+", "54")); - } - - @Test - public void getFloat0() { - this.test("1.2+5", this.expected("1.2", "+", "5")); - } - - @Test - public void getFloat1() { - this.test("1+56.32", this.expected("1", "+", "56.32")); - } - - @Test - public void getFloat2() { - this.test("1.2+56.32", this.expected("1.2", "+", "56.32")); - } - - @Test - public void getNegativeInteger() { - this.test("-23+-54", this.expected("-23", "+", "-54")); - } - - @Test - public void getNegativeFloat() { - this.test("-1.2+-56.32", this.expected("-1.2", "+", "-56.32")); - } - - @Test - public void expressionWithUnprintables() { - this.test(" \t1.2\u0003 \u0020+\n5 ", this.expected("1.2", "+", "5")); - } - - @Test - public void variableNumberOfOperands() { - this.test("2 + 3 - 4 * 5 / -17", this.expected("2", "+", "3", "-", "4", "*", "5", "/", "-17")); - } - - @Test - public void expressionWithParenthesis0() { - this.test("(2 + 5) - 2", this.expected("(", "2", "+", "5", ")", "-", "2")); - } - - @Test - public void expressionWithParenthesis1() { - this.test("((2 + 5) * 2) - 2", this.expected("(", "(", "2", "+", "5", ")", "*", "2", ")", "-", "2")); - } - - @Test - public void expressionWithParenthesis2() { - this.test("((2 + 5) * (10 - 8)) - 2", this.expected("(", "(", "2", "+", "5", ")", "*", "(", "10", "-", "8", ")", ")", "-", "2")); - } - - @Test - public void expressionWithParenthesis3() { - this.test("((2 + 5) * (10 - 8) - 2) + 3", this.expected("(", "(", "2", "+", "5", ")", "*", "(", "10", "-", "8", ")", "-", "2", ")", "+", "3")); - } - - @Test - public void expressionWithParenthesis4() { - this.test("((2 + 5) - 2 * (10 - 8)) + 3", this.expected("(", "(", "2", "+", "5", ")", "-", "2", "*", "(", "10", "-", "8", ")", ")", "+", "3")); - } - - @Test - public void expressionWithParenthesis5() { - this.test("(2 * (2 + 5) - (10 - 8)) + 3", this.expected("(", "2", "*", "(", "2", "+", "5", ")", "-", "(", "10", "-", "8", ")", ")", "+", "3")); - } - - @Test - public void expressionWithParenthesis6() { - this.test("4 - (2 + 5) - 2", this.expected("4", "-", "(", "2", "+", "5", ")", "-", "2")); - } -} diff --git a/README.md b/README.md index 8433464..2bc1f33 100644 --- a/README.md +++ b/README.md @@ -204,5 +204,13 @@ To see the evolution of the grammar look at branches `part08_01` to `part08_07`, ### Compile and execute The project can only be executed through unit tests. (See introduction) -### Blog post -[JVM Hardcore - Part 8 - Mon premier analyseur syntaxique - 2/2](http://blog.soat.fr/2013/12/09-jvm-hardcore-part-8-mon-premier-analyseur-syntaxique-22) \ No newline at end of file +Blog post: [JVM Hardcore - Part 8 - Mon premier analyseur syntaxique - 2/2](http://blog.soat.fr/2013/12/09-jvm-hardcore-part-8-mon-premier-analyseur-syntaxique-22) + +## part09 +### Summary +Introduction to a simple interpreter. + +### Compile and execute +The project can only be executed through unit tests. (See introduction) + +Blog post: [JVM Hardcore - Part 9 - Résoudre une expression arithmétique](http://blog.soat.fr/2013/12/10-jvm-hardcore-part-9-resoudre-une-expression-arithmetique) \ No newline at end of file