diff --git a/src/main/java/com/ezylang/evalex/parser/Tokenizer.java b/src/main/java/com/ezylang/evalex/parser/Tokenizer.java index 08d2979d..de7eba33 100644 --- a/src/main/java/com/ezylang/evalex/parser/Tokenizer.java +++ b/src/main/java/com/ezylang/evalex/parser/Tokenizer.java @@ -109,6 +109,11 @@ private boolean implicitMultiplicationPossible(Token currentToken) { } private void validateToken(Token currentToken) throws ParseException { + + if (currentToken.getType() == STRUCTURE_SEPARATOR && getPreviousToken() == null) { + throw new ParseException(currentToken, "Misplaced structure operator"); + } + Token previousToken = getPreviousToken(); if (previousToken != null && previousToken.getType() == INFIX_OPERATOR @@ -365,7 +370,17 @@ private Token parseDecimalNumberLiteral() throws ParseException { int lastChar = -1; boolean scientificNotation = false; + boolean dotEncountered = false; while (currentChar != -1 && isAtNumberChar()) { + if (currentChar == '.' && dotEncountered) { + tokenValue.append((char) currentChar); + throw new ParseException( + new Token(tokenStartIndex, tokenValue.toString(), TokenType.NUMBER_LITERAL), + "Number contains more than one decimal point"); + } + if (currentChar == '.') { + dotEncountered = true; + } if (currentChar == 'e' || currentChar == 'E') { scientificNotation = true; } @@ -510,7 +525,7 @@ private boolean isAtNumberChar() { return Character.isDigit(currentChar) || currentChar == '+' || currentChar == '-'; } - if (previousChar == '.') { + if (previousChar == '.' && currentChar != '.') { return Character.isDigit(currentChar) || currentChar == 'e' || currentChar == 'E'; } diff --git a/src/test/java/com/ezylang/evalex/parser/TokenizerNumberLiteralTest.java b/src/test/java/com/ezylang/evalex/parser/TokenizerNumberLiteralTest.java index a27c2296..5b995e21 100644 --- a/src/test/java/com/ezylang/evalex/parser/TokenizerNumberLiteralTest.java +++ b/src/test/java/com/ezylang/evalex/parser/TokenizerNumberLiteralTest.java @@ -89,4 +89,19 @@ void testScientificLiteralsParseException(String expression) { .isInstanceOf(ParseException.class) .hasMessage("Illegal scientific format"); } + + @ParameterizedTest + @ValueSource(strings = {"1..0*2.7*195.0", "123.45.6", "2.1.2..4", ".2.4"}) + void testMoreThanOneDecimalPointThrowsException(String expression) { + assertThatThrownBy(() -> new Tokenizer(expression, configuration).parse()) + .isInstanceOf(ParseException.class) + .hasMessage("Number contains more than one decimal point"); + } + + @Test + void testMisplacedStructureOperator() { + assertThatThrownBy(() -> new Tokenizer("..3", configuration).parse()) + .isInstanceOf(ParseException.class) + .hasMessage("Misplaced structure operator"); + } }